Skip to content

Instantly share code, notes, and snippets.

@mattsgithub
Created July 1, 2018 22:20
Show Gist options
  • Save mattsgithub/6b38ee5c70286ae47221da99fe9ee99f to your computer and use it in GitHub Desktop.
Save mattsgithub/6b38ee5c70286ae47221da99fe9ee99f to your computer and use it in GitHub Desktop.
This code demonstrates how to use Tensorflow to build a basic neural network
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<b>Author:</b> Robert Matthew Johnson\n",
"\n",
"<b>Website:</b> https://mattshomepage.com\n",
"\n",
"This code demonstrates how to use Tensorflow to build a basic neural network"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Import libraries"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from datetime import datetime\n",
"import os\n",
"\n",
"import tensorflow as tf\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from sklearn.datasets import make_moons\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Seed for reproducibility\n",
"seed = 37\n",
"\n",
"# Number of training examples\n",
"m = 1000"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Let's load a dataset"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# The data\n",
"X, y = make_moons(n_samples=m, noise=0.1, random_state=seed)\n",
"n_features = X.shape[1]"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Plot the data\n",
"plt.plot(X[y == 1, 0], X[y == 1, 1], 'go', label='Positive')\n",
"plt.plot(X[y == 0, 0], X[y == 0, 1], 'r^', label='Negative')\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 2.03835815, 0.44859342],\n",
" [ 0.86844054, -0.46508814],\n",
" [ 1.1045931 , -0.00597881],\n",
" ...,\n",
" [ 1.39099029, -0.21741412],\n",
" [-0.60142004, 0.81973929],\n",
" [ 1.93134606, -0.05077904]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"y = y.reshape(-1, 1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Training/Test Data"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"test_ratio = 0.2\n",
"test_size = int(m * test_ratio)\n",
"X_train = X[:-test_size]\n",
"X_test = X[-test_size:]\n",
"y_train = y[:-test_size]\n",
"y_test = y[-test_size:]\n",
"\n",
"\n",
"def get_minibatch(X, y, batch_size):\n",
" \"\"\"Yield minibatches of data\"\"\"\n",
" \n",
" assert X.shape[0] == y.shape[0]\n",
" \n",
" # Shuffle\n",
" indx = np.arange(X.shape[0])\n",
" np.random.shuffle(indx)\n",
"\n",
" # Yield the mini-batches\n",
" i = 0\n",
" while(i < X.shape[0]):\n",
" j = indx[i:(i + batch_size)]\n",
" yield X[j,:], y[j,:]\n",
" i += batch_size"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tensorflow"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Logging"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def get_log_dir(model_name):\n",
" now = datetime.utcnow().strftime('%Y-%m-%d-%H:%M:%S')\n",
" root_logdir = 'tf_logs'\n",
" log_dir = '{}/{}/run-{}/'.format(root_logdir, model_name, now)\n",
" return log_dir\n",
"\n",
"def get_file_writers_dict(log_dir):\n",
" g = tf.get_default_graph()\n",
" names = ['cost_train',\n",
" 'cost_test',\n",
" 'acc_train',\n",
" 'acc_test']\n",
" return {n: tf.summary.FileWriter(os.path.join(log_dir, n), g) for n in names}\n",
"\n",
"def fw_write(fw, cost_summary, acc_summary,\n",
" epoch, X, y, X_train, y_train,\n",
" X_test, y_test):\n",
" \n",
" fw['cost_train'].add_summary(cost_summary.eval(feed_dict={X: X_train, y: y_train}),\n",
" global_step=epoch)\n",
" \n",
" fw['cost_test'].add_summary(cost_summary.eval(feed_dict={X: X_test, y: y_test}),\n",
" global_step=epoch)\n",
" \n",
" fw['acc_train'].add_summary(acc_summary.eval(feed_dict={X: X_train, y: y_train}),\n",
" global_step=epoch)\n",
" \n",
" fw['acc_test'].add_summary(acc_summary.eval(feed_dict={X: X_test, y: y_test}),\n",
" global_step=epoch)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Main inputs"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"def get_placeholders(n_features):\n",
" X = tf.placeholder(tf.float32, shape=(None, n_features), name=\"X\")\n",
" y = tf.placeholder(tf.float32, shape=(None, 1), name=\"y\")\n",
" return X, y"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Model Helper Function"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"def _get_layer(X, n_units):\n",
" n_inputs = int(X.get_shape()[1])\n",
" stddev = 2. / np.sqrt(n_inputs)\n",
" W = tf.Variable(tf.truncated_normal((n_inputs, n_units), stddev=stddev))\n",
" b = tf.Variable(tf.zeros([n_units]))\n",
" z = tf.matmul(X, W) + b\n",
" return z\n",
"\n",
"def hidden_layer(X, n_units):\n",
" z = _get_layer(X, n_units)\n",
" a = tf.nn.relu(z)\n",
" return a\n",
"\n",
"def output_layer(X):\n",
" z = _get_layer(X, 1)\n",
" y_hat = tf.sigmoid(z)\n",
" return y_hat"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Models"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"def get_lr_output(X, params):\n",
" w = tf.Variable(tf.random_uniform([params['n_features'], 1], -0.1, 0.1), name=\"weights\")\n",
" b = tf.Variable(0., name=\"bias\")\n",
" z = tf.matmul(X, w) + b\n",
" return tf.sigmoid(z)\n",
"\n",
"def get_mlp_output(l, layers):\n",
" for n_units in layers:\n",
" l = hidden_layer(l, n_units)\n",
" return output_layer(l)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Parameters"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"# Default parameters\n",
"params = {'learning_rate': 0.01,\n",
" 'n_epochs': 150,\n",
" 'batch_size': 20}\n",
"\n",
"params['n_batches'] = int(m / params['batch_size'])\n",
"params['n_features'] = n_features"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"def _fit(model_name,\n",
" X, y,\n",
" X_train, y_train,\n",
" y_hat, cost,\n",
" params):\n",
" \n",
" # Accuracy to report after every epoch\n",
" y_pred = tf.round(y_hat)\n",
" correct = tf.equal(y, y_pred)\n",
" acc = tf.reduce_mean(tf.cast(tf.equal(y, y_pred), tf.float32))\n",
"\n",
" # Gradient Descent\n",
" training_op = tf.train.GradientDescentOptimizer(params['learning_rate']).minimize(cost)\n",
"\n",
" # Used to save model\n",
" saver = tf.train.Saver()\n",
"\n",
" # Summaries to track\n",
" cost_summary = tf.summary.scalar('cost', cost)\n",
" acc_summary = tf.summary.scalar('acc', acc)\n",
"\n",
" fw = get_file_writers_dict(get_log_dir(model_name))\n",
"\n",
" with tf.Session() as sess:\n",
"\n",
" sess.run(tf.global_variables_initializer())\n",
" sess.run(tf.local_variables_initializer())\n",
"\n",
" for epoch in range(params['n_epochs']):\n",
" for X_batch, y_batch in get_minibatch(X_train, y_train, params['batch_size']):\n",
" sess.run(training_op, feed_dict={X: X_batch, y: y_batch})\n",
"\n",
" fw_write(fw, cost_summary, acc_summary,\n",
" epoch, X, y, X_train, y_train,\n",
" X_test, y_test)\n",
"\n",
" save_path = saver.save(sess, '/tmp/{}.ckpt'.format(model_name))\n",
"\n",
" for k, v in fw.items():\n",
" v.close()\n",
" \n",
"def run_lr(X_train, y_train, params): \n",
" \n",
" tf.reset_default_graph()\n",
" X, y = get_placeholders(params['n_features'])\n",
" \n",
" w = tf.Variable(tf.random_uniform([params['n_features'], 1], -0.1, 0.1), name=\"weights\")\n",
" b = tf.Variable(0., name=\"bias\")\n",
"\n",
" y_hat = get_lr_output(X, params)\n",
" \n",
" cost = tf.losses.log_loss(y, y_hat)\n",
"\n",
" _fit('lr', X, y, X_train, y_train, y_hat, cost, params)\n",
" \n",
"def run_mlp(X_train, y_train, layers, params): \n",
" \n",
" tf.reset_default_graph()\n",
" X, y = get_placeholders(params['n_features'])\n",
"\n",
" y_hat = get_mlp_output(X, layers)\n",
" \n",
" cost = tf.losses.log_loss(y, y_hat)\n",
"\n",
" _fit('mlp', X, y, X_train, y_train, y_hat, cost, params)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Run models"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"run_lr(X_train, y_train, params)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"run_mlp(X_train, y_train, [30, 20], params)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# How does sklearn compare?"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Final Test Acc: 0.84\n",
"Weights: [[ 1.16127587 -5.67308681]]\n",
"Bias: [0.82481367]\n"
]
},
{
"data": {
"image/png": "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aSxevLhblvf8889zxRVXYLGc2K/l5eTk8MQTT3T5/Z5J/8svv+zysm677TYefvjhLr+/PX5XJbbVSpu+EJ1x/3+2kVts69ZlZqda+cMlo485T1VVFWvWrGHlypVccskl3H///c7XHnroIV555RWCgoKYOXMmDz74IAUFBdx6662UlpZisVh44403GDp0qNsyFy1axKuvvgqYGvL111/P7NmzAZg/fz4XX3wxOTk5XH/99VRXVwPw5JNPtqmJf/bZZzz66KO8//77lJeXM2/ePIqKijjttNPQWjvnu+yyy9i/fz91dXXceeed3HLLLSxYsIDa2lomTJjA6NGjWbRoEdHR0VRVVaG15je/+Q3Lli1DKcU999zD1VdfzWeffcZ9991HYmIiW7duZdKkSbzyyisopTjzzDOZP38+TU1NBAd3X6r2q6Tf3KKprG+S0TtC9GHvvfceM2bMYMSIESQkJLBu3TomTZrEsmXLeO+99/jmm2+IjIzk8OHDAFx77bUsWLCAyy+/nLq6OlpaWtyW19DQwK5du8jIyADg6quv5vXXX2f27Nk0NDTwySef8Mwzz6C15uOPPyY8PJz8/HzmzZvH2rVr243z/vvvZ9q0adx777188MEHPPfcc87Xnn/+eeLj46mtrWXy5MlceeWVPPjggzz55JNs3LixzbLefvttNm7cyKZNmygrK2Py5MmcddZZAGzYsIFt27aRmprKGWecwRdffMG0adMICgpi2LBhbNq0iUmTJp1osTv5VXasst+CQWr6QnSsoxp5T1m8eDF33nknYGrlixcvZtKkSaxYsYIbbriByMhIAOLj46msrKSoqIjLL78cMBcmeSorKyMuLs75fObMmdx5553U19fz4YcfctZZZxEREUFFRQW33347GzduxGKxsHPnzmPGuWrVKt5++20AZs+eTb9+/ZyvPfHEE7zzzjsA7N+/n/z8fBISEtpd1po1a5g3bx4Wi4Xk5GTOPvtsvvvuO6xWK1OmTCE9PR2ACRMmsGfPHqZNmwZA//79KS4ulqTfHuctGKRNX4g+6fDhw3z66ads2bIFpRTNzc0opXjkkUe6vMyIiAi3cevh4eGcc845LF++nH//+9/MnTsXgMcff5zk5GQ2bdpES0uL1wNIZ3z22WesWLGCr776isjISM4555wTGjcfFtb62x8Wi4Wmpibn87q6OiIi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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from sklearn.linear_model import SGDClassifier\n",
"from sklearn.metrics import accuracy_score\n",
"\n",
"clf = SGDClassifier(loss='log',\n",
" penalty=None,\n",
" shuffle=True,\n",
" tol=None,\n",
" max_iter=params['n_epochs'],\n",
" learning_rate='constant',\n",
" eta0=params['learning_rate'],\n",
" fit_intercept=True)\n",
"\n",
"acc_valid = []\n",
"acc_train = []\n",
"for epoch in range(params['n_epochs']):\n",
" for X_batch, y_batch in get_minibatch(X_train, y_train, params['batch_size']):\n",
" clf.partial_fit(X_batch, y_batch.flatten(), classes=[0, 1])\n",
" acc_train.append(accuracy_score(y_train, clf.predict(X_train)))\n",
" acc_valid.append(accuracy_score(y_test, clf.predict(X_test)))\n",
"\n",
"print('Final Test Acc: {}'.format(accuracy_score(y_test, clf.predict(X_test))))\n",
"print('Weights: {}'.format(clf.coef_))\n",
"print('Bias: {}'.format(clf.intercept_))\n",
"plt.plot(range(len(acc_valid)), acc_valid, label='Acc (validation)')\n",
"plt.plot(range(len(acc_train)), acc_train, label='Acc (train)')\n",
"plt.legend()\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment