From 34cc0f248a26bfa2c592b745505a7e2c92a5ba40 Mon Sep 17 00:00:00 2001 From: aymericdamien Date: Wed, 3 Apr 2019 23:23:42 -0700 Subject: [PATCH 1/4] add tf v2 examples --- tensorflow_v2/README.md | 0 .../0_Prerequisite/ml_introduction.ipynb | 50 ++ .../0_Prerequisite/mnist_dataset_intro.ipynb | 96 +++ .../1_Introduction/basic_operations.ipynb | 172 ++++++ .../notebooks/1_Introduction/helloworld.ipynb | 83 +++ .../2_BasicModels/linear_regression.ipynb | 204 +++++++ .../2_BasicModels/logistic_regression.ipynb | 344 +++++++++++ .../3_NeuralNetworks/autoencoder.ipynb | 338 +++++++++++ .../convolutional_network.ipynb | 400 ++++++++++++ .../convolutional_network_raw.ipynb | 429 +++++++++++++ .../notebooks/3_NeuralNetworks/dcgan.ipynb | 381 ++++++++++++ .../3_NeuralNetworks/neural_network.ipynb | 381 ++++++++++++ .../3_NeuralNetworks/neural_network_raw.ipynb | 402 ++++++++++++ .../4_Utils/build_custom_layers.ipynb | 304 ++++++++++ .../4_Utils/save_restore_model.ipynb | 573 ++++++++++++++++++ 15 files changed, 4157 insertions(+) create mode 100644 tensorflow_v2/README.md create mode 100644 tensorflow_v2/notebooks/0_Prerequisite/ml_introduction.ipynb create mode 100644 tensorflow_v2/notebooks/0_Prerequisite/mnist_dataset_intro.ipynb create mode 100644 tensorflow_v2/notebooks/1_Introduction/basic_operations.ipynb create mode 100644 tensorflow_v2/notebooks/1_Introduction/helloworld.ipynb create mode 100644 tensorflow_v2/notebooks/2_BasicModels/linear_regression.ipynb create mode 100644 tensorflow_v2/notebooks/2_BasicModels/logistic_regression.ipynb create mode 100644 tensorflow_v2/notebooks/3_NeuralNetworks/autoencoder.ipynb create mode 100644 tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network.ipynb create mode 100644 tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network_raw.ipynb create mode 100644 tensorflow_v2/notebooks/3_NeuralNetworks/dcgan.ipynb create mode 100644 tensorflow_v2/notebooks/3_NeuralNetworks/neural_network.ipynb create mode 100644 tensorflow_v2/notebooks/3_NeuralNetworks/neural_network_raw.ipynb create mode 100644 tensorflow_v2/notebooks/4_Utils/build_custom_layers.ipynb create mode 100644 tensorflow_v2/notebooks/4_Utils/save_restore_model.ipynb diff --git a/tensorflow_v2/README.md b/tensorflow_v2/README.md new file mode 100644 index 00000000..e69de29b diff --git a/tensorflow_v2/notebooks/0_Prerequisite/ml_introduction.ipynb b/tensorflow_v2/notebooks/0_Prerequisite/ml_introduction.ipynb new file mode 100644 index 00000000..aa271e04 --- /dev/null +++ b/tensorflow_v2/notebooks/0_Prerequisite/ml_introduction.ipynb @@ -0,0 +1,50 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Machine Learning\n", + "\n", + "Prior to start browsing the examples, it may be useful that you get familiar with machine learning, as TensorFlow is mostly used for machine learning tasks (especially Neural Networks). You can find below a list of useful links, that can give you the basic knowledge required for this TensorFlow Tutorial.\n", + "\n", + "## Machine Learning\n", + "\n", + "- [An Introduction to Machine Learning Theory and Its Applications: A Visual Tutorial with Examples](https://www.toptal.com/machine-learning/machine-learning-theory-an-introductory-primer)\n", + "- [A Gentle Guide to Machine Learning](https://blog.monkeylearn.com/a-gentle-guide-to-machine-learning/)\n", + "- [A Visual Introduction to Machine Learning](http://www.r2d3.us/visual-intro-to-machine-learning-part-1/)\n", + "- [Introduction to Machine Learning](http://alex.smola.org/drafts/thebook.pdf)\n", + "\n", + "## Deep Learning & Neural Networks\n", + "\n", + "- [An Introduction to Neural Networks](http://www.cs.stir.ac.uk/~lss/NNIntro/InvSlides.html)\n", + "- [An Introduction to Image Recognition with Deep Learning](https://medium.com/@ageitgey/machine-learning-is-fun-part-3-deep-learning-and-convolutional-neural-networks-f40359318721)\n", + "- [Neural Networks and Deep Learning](http://neuralnetworksanddeeplearning.com/index.html)\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "IPython (Python 2.7)", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.11" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/tensorflow_v2/notebooks/0_Prerequisite/mnist_dataset_intro.ipynb b/tensorflow_v2/notebooks/0_Prerequisite/mnist_dataset_intro.ipynb new file mode 100644 index 00000000..f1813c85 --- /dev/null +++ b/tensorflow_v2/notebooks/0_Prerequisite/mnist_dataset_intro.ipynb @@ -0,0 +1,96 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# MNIST Dataset Introduction\n", + "\n", + "Most examples are using MNIST dataset of handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 1. For simplicity, each image has been flatten and converted to a 1-D numpy array of 784 features (28*28).\n", + "\n", + "## Overview\n", + "\n", + "![MNIST Digits](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", + "\n", + "## Usage\n", + "In our examples, we are using TensorFlow [input_data.py](https://github.com/tensorflow/tensorflow/blob/r0.7/tensorflow/examples/tutorials/mnist/input_data.py) script to load that dataset.\n", + "It is quite useful for managing our data, and handle:\n", + "\n", + "- Dataset downloading\n", + "\n", + "- Loading the entire dataset into numpy array: \n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Import MNIST\n", + "from tensorflow.examples.tutorials.mnist import input_data\n", + "mnist = input_data.read_data_sets(\"/tmp/data/\", one_hot=True)\n", + "\n", + "# Load data\n", + "X_train = mnist.train.images\n", + "Y_train = mnist.train.labels\n", + "X_test = mnist.test.images\n", + "Y_test = mnist.test.labels" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- A `next_batch` function that can iterate over the whole dataset and return only the desired fraction of the dataset samples (in order to save memory and avoid to load the entire dataset)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Get the next 64 images array and labels\n", + "batch_X, batch_Y = mnist.train.next_batch(64)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Link: http://yann.lecun.com/exdb/mnist/" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.13" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/tensorflow_v2/notebooks/1_Introduction/basic_operations.ipynb b/tensorflow_v2/notebooks/1_Introduction/basic_operations.ipynb new file mode 100644 index 00000000..769cb600 --- /dev/null +++ b/tensorflow_v2/notebooks/1_Introduction/basic_operations.ipynb @@ -0,0 +1,172 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Basic Tensor Operations\n", + "\n", + "Basic tensor operations using TensorFlow v2.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "import tensorflow as tf" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Define tensor constants.\n", + "a = tf.constant(2)\n", + "b = tf.constant(3)\n", + "c = tf.constant(5)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "add = 5\n", + "sub = -1\n", + "mul = 6\n", + "div = 0.6666666666666666\n" + ] + } + ], + "source": [ + "# Various tensor operations.\n", + "# Note: Tensors also support python operators (+, *, ...)\n", + "add = tf.add(a, b)\n", + "sub = tf.subtract(a, b)\n", + "mul = tf.multiply(a, b)\n", + "div = tf.divide(a, b)\n", + "\n", + "# Access tensors value.\n", + "print(\"add =\", add.numpy())\n", + "print(\"sub =\", sub.numpy())\n", + "print(\"mul =\", mul.numpy())\n", + "print(\"div =\", div.numpy())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mean = 3\n", + "sum = 10\n" + ] + } + ], + "source": [ + "# Some more operations.\n", + "mean = tf.reduce_mean([a, b, c])\n", + "sum = tf.reduce_sum([a, b, c])\n", + "\n", + "# Access tensors value.\n", + "print(\"mean =\", mean.numpy())\n", + "print(\"sum =\", sum.numpy())" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Matrix multiplications.\n", + "matrix1 = tf.constant([[1., 2.], [3., 4.]])\n", + "matrix2 = tf.constant([[5., 6.], [7., 8.]])\n", + "\n", + "product = tf.matmul(matrix1, matrix2)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Display Tensor.\n", + "product" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[19., 22.],\n", + " [43., 50.]], dtype=float32)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Convert Tensor to Numpy.\n", + "product.numpy()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/1_Introduction/helloworld.ipynb b/tensorflow_v2/notebooks/1_Introduction/helloworld.ipynb new file mode 100644 index 00000000..8a9479c0 --- /dev/null +++ b/tensorflow_v2/notebooks/1_Introduction/helloworld.ipynb @@ -0,0 +1,83 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Hello World\n", + "\n", + "A very simple \"hello world\" using TensorFlow v2 tensors.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import tensorflow as tf" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tf.Tensor(hello world, shape=(), dtype=string)\n" + ] + } + ], + "source": [ + "# Create a Tensor.\n", + "hello = tf.constant(\"hello world\")\n", + "print hello" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello world\n" + ] + } + ], + "source": [ + "# To access a Tensor value, call numpy().\n", + "print hello.numpy()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/2_BasicModels/linear_regression.ipynb b/tensorflow_v2/notebooks/2_BasicModels/linear_regression.ipynb new file mode 100644 index 00000000..17b57b8a --- /dev/null +++ b/tensorflow_v2/notebooks/2_BasicModels/linear_regression.ipynb @@ -0,0 +1,204 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Linear Regression Example\n", + "\n", + "Linear regression implementation with TensorFlow v2 library.\n", + "\n", + "This example is using a low-level approach to better understand all mechanics behind the training process.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import tensorflow as tf\n", + "import numpy as np\n", + "rng = np.random" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Parameters.\n", + "learning_rate = 0.01\n", + "training_steps = 1000\n", + "display_step = 50" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Training Data.\n", + "X = np.array([3.3,4.4,5.5,6.71,6.93,4.168,9.779,6.182,7.59,2.167,\n", + " 7.042,10.791,5.313,7.997,5.654,9.27,3.1])\n", + "Y = np.array([1.7,2.76,2.09,3.19,1.694,1.573,3.366,2.596,2.53,1.221,\n", + " 2.827,3.465,1.65,2.904,2.42,2.94,1.3])\n", + "n_samples = X.shape[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Weight and Bias, initialized randomly.\n", + "W = tf.Variable(rng.randn(), name=\"weight\")\n", + "b = tf.Variable(rng.randn(), name=\"bias\")\n", + "\n", + "# Linear regression (Wx + b).\n", + "def linear_regression(x):\n", + " return W * x + b\n", + "\n", + "# Mean square error.\n", + "def mean_square(y_pred, y_true):\n", + " return tf.reduce_sum(tf.pow(y_pred-y_true, 2)) / (2 * n_samples)\n", + "\n", + "# Stochastic Gradient Descent Optimizer.\n", + "optimizer = tf.optimizers.SGD(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization():\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " pred = linear_regression(X)\n", + " loss = mean_square(pred, Y)\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, [W, b])\n", + " \n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, [W, b]))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 50, loss: 0.210631, W: 0.458940, b: -0.670898\n", + "step: 100, loss: 0.195340, W: 0.446725, b: -0.584301\n", + "step: 150, loss: 0.181797, W: 0.435230, b: -0.502807\n", + "step: 200, loss: 0.169803, W: 0.424413, b: -0.426115\n", + "step: 250, loss: 0.159181, W: 0.414232, b: -0.353942\n", + "step: 300, loss: 0.149774, W: 0.404652, b: -0.286021\n", + "step: 350, loss: 0.141443, W: 0.395636, b: -0.222102\n", + "step: 400, loss: 0.134064, W: 0.387151, b: -0.161949\n", + "step: 450, loss: 0.127530, W: 0.379167, b: -0.105341\n", + "step: 500, loss: 0.121742, W: 0.371652, b: -0.052068\n", + "step: 550, loss: 0.116617, W: 0.364581, b: -0.001933\n", + "step: 600, loss: 0.112078, W: 0.357926, b: 0.045247\n", + "step: 650, loss: 0.108058, W: 0.351663, b: 0.089647\n", + "step: 700, loss: 0.104498, W: 0.345769, b: 0.131431\n", + "step: 750, loss: 0.101345, W: 0.340223, b: 0.170753\n", + "step: 800, loss: 0.098552, W: 0.335003, b: 0.207759\n", + "step: 850, loss: 0.096079, W: 0.330091, b: 0.242583\n", + "step: 900, loss: 0.093889, W: 0.325468, b: 0.275356\n", + "step: 950, loss: 0.091949, W: 0.321118, b: 0.306198\n", + "step: 1000, loss: 0.090231, W: 0.317024, b: 0.335223\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step in range(1, training_steps + 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization()\n", + " \n", + " if step % display_step == 0:\n", + " pred = linear_regression(X)\n", + " loss = mean_square(pred, Y)\n", + " print(\"step: %i, loss: %f, W: %f, b: %f\" % (step, loss, W.numpy(), b.numpy()))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAD8CAYAAACMwORRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xt8VNW5//HPAwTCVRSxIhgGEQVECBIURD0qoAh4KYpiqVaPFS+00nMUReMF0ShWK7XHC40Hi/5M9XhDqKD1ggiIIkRBrgUjASJeAAsSIxLI+v0xYcgMEzIhk+w9M9/365XXZK9ZM/thCE8Wa6/9LHPOISIiyaWe1wGIiEj8KbmLiCQhJXcRkSSk5C4ikoSU3EVEkpCSu4hIElJyFxFJQkruIiJJSMldRCQJNfDqxIcffrgLBAJenV5EJCHl5+dvcc61rqqfZ8k9EAiwePFir04vIpKQzGx9LP00LSMikoSU3EVEkpCSu4hIEvJszj2a0tJSioqK2Llzp9ehCJCenk67du1IS0vzOhQRqSZfJfeioiKaN29OIBDAzLwOJ6U559i6dStFRUV06NDB63BEpJp8NS2zc+dOWrVqpcTuA2ZGq1at9L8okQTlq+QOKLH7iP4uRBKX75K7iEiy2lm6h0ffWcOmbT/V+rmU3CMUFRVx4YUX0qlTJzp27MiYMWPYtWtX1L6bNm3ikksuqfI9Bw8ezLZt2w4qnvHjx/PII49U2a9Zs2YHfH7btm08+eSTBxWDiNTcS4s30vmut/jLe2uZu2ZzrZ8vsZN7Xh4EAlCvXvAxL69Gb+ecY9iwYVx00UWsXbuWNWvWUFxcTHZ29n59d+/ezVFHHcUrr7xS5fvOmjWLli1b1ii2mlJyF/HG9p9KCYybya2vfA7ARZlHMeLkjFo/b+Im97w8GDUK1q8H54KPo0bVKMHPnj2b9PR0rr76agDq16/PpEmTeOaZZygpKWHq1KkMHz6c888/n3POOYfCwkK6desGQElJCZdeeindu3fnsssu45RTTgmVVwgEAmzZsoXCwkK6dOnCtddeywknnMA555zDTz8F/3v29NNP07t3b3r06MHFF19MSUnJAWNdt24dffv2pXfv3tx1112h9uLiYvr3789JJ53EiSeeyPTp0wEYN24cBQUFZGZmMnbs2Er7iUj8TP6ggB73vh06njv2LP48omednDtxk3t2NkQmwJKSYPtBWrFiBb169Qpra9GiBRkZGXzxxRcAfPTRRzz77LPMnj07rN+TTz7JoYceyueff85dd91Ffn5+1HOsXbuW0aNHs2LFClq2bMmrr74KwLBhw1i0aBFLly6lS5cuTJky5YCxjhkzhhtuuIFFixZx5JFHhtrT09OZNm0an376Ke+//z4333wzzjkmTpxIx44dWbJkCQ8//HCl/USk5r77YSeBcTOZ+OZqAK474xgKJw4ho1WTOovBV+vcq2XDhuq1x8A5F3WFSMX2gQMHcthhh+3XZ/78+YwZMwaAbt260b1796jn6NChA5mZmQD06tWLwsJCAJYvX86dd97Jtm3bKC4u5txzzz1grB9++GHoF8MVV1zBbbfdFor1jjvuYO7cudSrV4+vvvqKb7/9NuqfKVq/ir8oRKT67ntjJVPmrwsdL8oeQOvmjeo8jsRN7hkZwamYaO0H6YQTTgglzL1++OEHNm7cSMeOHcnPz6dp06ZRXxvrqLdRo31/yfXr1w9Ny1x11VW8/vrr9OjRg6lTpzJnzpwq3yvaL6K8vDw2b95Mfn4+aWlpBAKBqGvVY+0nIrEp3PIjZz4yJ3ScPbgL155xjGfxJO60TE4ONIn4L06TJsH2g9S/f39KSkp47rnnANizZw8333wzV111FU0izxXhtNNO46WXXgJg5cqVLFu2rFrn3rFjB23atKG0tJS8GK4b9OvXjxdffBEgrP/27ds54ogjSEtL4/3332d9+S/A5s2bs2PHjir7iUj1/f6Fz8IS++fjz/E0sUMiJ/eRIyE3F9q3B7PgY25usP0gmRnTpk3j5ZdfplOnThx33HGkp6fzwAMPVPnaG2+8kc2bN9O9e3ceeughunfvziGHHBLzue+77z5OOeUUBg4cSOfOnavs/9hjj/HEE0/Qu3dvtm/fHmofOXIkixcvJisri7y8vNB7tWrVin79+tGtWzfGjh1baT8Rid3yr7YTGDeTfyzdBMAjw3tQOHEILdK9r8dkXl1Ey8rKcpGbdaxatYouXbp4Ek9N7dmzh9LSUtLT0ykoKKB///6sWbOGhg0beh1ajSTy34lIbSkrc4zI/ZhPCr8H4NAmaXx0e3/S0+rX+rnNLN85l1VVv8Sdc/eZkpISzjrrLEpLS3HO8dRTTyV8YheR/S0o2MKvnl4YOn7mqizO7vwLDyOKTsk9Tpo3b65tA0WSWOmeMgY8+gHrtwaXYHc+sjkzbzqd+vX8WYNJyV1EpApvLf+a65//NHT8yvV9yQrsvyTaT5TcRUQq8dOuPfS87212lpYBcMZxrXn26t4JUTFVyV1EJIq/L9zAHdP2LWn+5x/O4Pgjm3sYUfUouYuIVLCtZBeZE94JHQ/v1Y6Hh/fwMKKDU+U6dzNLN7NPzGypma0ws3uj9LnKzDab2ZLyr9/WTri1r379+mRmZoa+CgsLWbx4MTfddBMAc+bMYcGCBaH+r7/+OitXrqz2eSor0bu3PdZywiISP4/PXhuW2OfdelZCJnaIbeT+M3C2c67YzNKA+Wb2pnPu44h+/+ec+138Q6xbjRs3ZsmSJWFtgUCArKzgstI5c+bQrFkzTj31VCCY3IcOHUrXrl3jGkes5YRFpOa+2b6TPg++FzoefVZHxp6b2Df2VTlyd0HF5Ydp5V8pVT5wzpw5DB06lMLCQiZPnsykSZPIzMzkgw8+YMaMGYwdO5bMzEwKCgooKChg0KBB9OrVi9NPP53Vq4NV4Sor0VuZiuWEp06dyrBhwxg0aBCdOnXi1ltvDfV7++236du3LyeddBLDhw+nuLi4srcUkSjumb48LLHn3zkg4RM7xDjnbmb1gXzgWOAJ59zCKN0uNrMzgDXAfznnNtYksHv/sYKVm36oyVvsp+tRLbjn/BMO2Oenn34KVW3s0KED06ZNCz0XCAS4/vrradasGbfccgsAF1xwAUOHDg1NofTv35/JkyfTqVMnFi5cyI033sjs2bNDJXqvvPJKnnjiiWrHvmTJEj777DMaNWrE8ccfz+9//3saN27M/fffz7vvvkvTpk156KGHePTRR7n77rur/f4iqaZgczH9//RB6PjuoV35z9M61O5J8/KCZck3bAgWOczJqVHJlAOJKbk75/YAmWbWEphmZt2cc8srdPkH8IJz7mczux54Fjg78n3MbBQwCiCjBtUba1O0aZlYFRcXs2DBAoYPHx5q+/nnn4HKS/TGqn///qFaNV27dmX9+vVs27aNlStX0q9fPwB27dpF3759Dyp2kVThnOOG5z/lrRXfhNqW33suzRrV8vqSvRsM7d2HYu8GQ1ArCb5afxrn3DYzmwMMApZXaN9aodvTwEOVvD4XyIVgbZkDnauqEbYflZWV0bJly0p/OdRkbWxkqeDdu3fjnGPgwIG88MILB/2+Iqnk86JtXPD4h6Hjx0ZkcmFm27o5+YE2GKqF5B7LapnW5SN2zKwxMABYHdGnTYXDC4BV8QzSTyJL51Y8btGiBR06dODll18GgiOEpUuXApWX6K2JPn368OGHH4Z2iSopKWHNmjVxeW+RZFJW5rjoiQ9Dif2I5o341/2D6i6xQ61sMHQgsZT8bQO8b2afA4uAd5xzb5jZBDO7oLzPTeXLJJcCNwFX1Uq0PnD++eczbdo0MjMzmTdvHiNGjODhhx+mZ8+eFBQUkJeXx5QpU+jRowcnnHBCaG/Sykr01kTr1q2ZOnUql19+Od27d6dPnz6hC7giEvT3hRs45o5ZLNm4DYCpV/fmk+wBNGpQ+xUcw1Q2FV1LU9Qq+SsHpL8TSVQlu3bT9e5/ho5PbHsIr4/u512hr8g5dwhuMFTNfShU8ldEUtaNefnMWrbvgun487tyVb9aXglTlb0J3E+rZUREEsGW4p/Juv/dsLZ1L47G/lj7yTQmI0fW2fl9l9ydcwlRcS0VeDVlJ3IwBv15Lqu/2bfY4amMHzlv7NV1tvTQb3yV3NPT09m6dSutWrVSgveYc46tW7eSnp7udSgiB/Tl5mLOrnAzEkDhxCEQCNTp0kO/8VVyb9euHUVFRWzevNnrUITgL9t27dp5HYZIpQLjZoYdv3pDX3q1L99Eo46XHvqNr5J7WloaHTp4fNFDRHwvf/33XPzUR2FthROHhHfKyAhOxUTy6d3x8ear5C4iUpXI0fp7N/8HHVtHKaGdkxN96WFOTi1H6A+x3MQkIuK5t5Z/HZbYOx3RjMKJQ6IndgjOq+fmQvv2YBZ8rOaa8kSmkbuI+Jpzjg63zwprW5Q9gNbNG1XyigrqcOmh3yi5i4hv/e3Dddz7j307nZ3X7Uie+nUvDyNKHEruIuI7P+/ew/F3vhXWtnLCuTRpqJQVK31SIuIr/f80h4LNP4aOr/+Pjow7L/F3RqprSu4i4gv//nEXPe97J6xtbc55pNXXuo+DoeQuIp6LXN54aVY7/nhJD4+iSQ76lShSHXl5wdva69ULPsZp45VU9eXm4v0S+7oHByuxx4FG7iKxquM9MJNdZFLPHtyFa884xqNoko+vNusQ8bVAIPrt7O3bQ2FhXUeTsD7+cisjcj8Oa9uvdIBUSpt1iMRbiheiiofI0fpfr+jFuScc6VE0yU3JXSRWKV6IqiZezS/i5peXhrVptF67lNxFYpXihagOVuRofcbv+tG9XUuPokkdSu4isarjPTAT3SP//BePv/9FWJtG63VHyV2kOlK4EFWsysocx9wRXujrw3Fn07ZlY48iSk1K7iISN9c+t5h3Vn4bOm6cVp9V9w3yMKLUpeQuIjW2s3QPne8KL/S1bPw5NE9P8ygiUXIXkRo59cH32LR9Z+j45A6H8dJ1fT2MSEDJXUQO0uYdP9M7592wti9yzqOBCn35gpK7iFRb5PLGK/u2Z8KF3TyKRqKpMrmbWTowF2hU3v8V59w9EX0aAc8BvYCtwGXOucK4Rysinlrz7Q7OmTQ3rE3LG/0plpH7z8DZzrliM0sD5pvZm865isUhrgH+7Zw71sxGAA8Bl9VCvCLikcjR+r0XnMBvTg14E4xUqcrk7oKVxYrLD9PKvyKrjV0IjC///hXgcTMz51VVMhGJm3lrN3PFlE/C2jRa97+Y5tzNrD6QDxwLPOGcWxjRpS2wEcA5t9vMtgOtgC1xjFVE6ljkaP1vV/XmrM5HeBSNVEdMyd05twfINLOWwDQz6+acW16hi0V7WWSDmY0CRgFkqNiSiG+98MkGbn9tWVibRuuJpVprlpxz24A5QOQtZ0XA0QBm1gA4BPg+yutznXNZzrms1q1bH1TAIlK7AuNmhiX2WTedXveJXTte1Vgsq2VaA6XOuW1m1hgYQPCCaUUzgN8AHwGXALM13y6SWHJmruTpeevC2jwZrWvHq7iocicmM+sOPAvUJzjSf8k5N8HMJgCLnXMzypdL/j+gJ8ER+wjn3JcHel/txCTiD3vKHB0jCn0tvKM/v2iR7k1A2vHqgGLdiUnb7Ikku7y8SssUXzFlIfPW7lv3cFjThnx610CvIg2qVw+i5SUzKCur+3h8RtvsiUilUxwlZdB1RfiGGSsnnEuThj5ICdrxKi5UBEIkmWVnh+8cBfS85umwxH7Gca0pnDjEH4kdgv+zaNIkvE07XlWbT/42RaRWVNi8+5tmregz+tmwpwseGEz9etFWMntIO17FhZK7SDIrn+II3PZGWPN1q97h9ul/9iioGGjHqxrTtIxIbfHBWu2Pb5+4X2Iv/J9Luf3S3nUei9QtjdxFaoMP1moHSwc0Dx2P/uglxq7/AHJzNSpOAVoKKVIbPFyr/eInGxin0gFJS0shRbxU4UJmTO1xElno67ERmVyY2bZWzyn+pOQuUhvqeK32+BkrmLqgMKxNo/XUpguqqcIHF/dSSh2t1XbOERg3Myyxv3bjqUrsopF7SvDBxb2UUwdrtX/55Id8tmFbWJuSuuylC6qpQIWYkkrpnjI6Zb8Z1rZg3Nkc1bKxRxFJXdIFVdnHo4t7En+RF0xBo3WJTsk9FagQU8LbvONneue8G9a24t5zadpI/4QlOv1kpIKcnPA5d1AhpgSi0bocDCX3VKBCTAlp+VfbGfo/88PafFnoS3xJyT1VqBBTQokcrR/Tuimzbz7Tm2AkISm5i/jIjKWbuOmFz8LaNAUjB0PJXcQnIkfrl598NA8O6+5RNJLolNxFPPbQW6t5ak5BWJtG61JTSu4iHoocref8shsjT2nvUTSSTFRbRpKfD+vqXDr5o/0Se+HEIUrsEjcauUty81ldHeccHW6fFdb20nV9ObnDYXUeiyQ31ZaR5Oajujq6GUniQbVlRMAXdXV2lu6h811vhbXNv+0s2h3apJJXiNSckrskN4/r6mi0Ll7RBVVJbnW0aUakr7f/tF9iXznh3Joldh9eGBb/qnLkbmZHA88BRwJlQK5z7rGIPmcC04F15U2vOecmxDdUkYPgQV2dWhmt++zCsPhflRdUzawN0MY596mZNQfygYuccysr9DkTuMU5NzTWE+uCqiSb+Wu38OspC8Pa1j04GLM4FPry0YVh8VbcLqg6574Gvi7/foeZrQLaAisP+EKRFBI5Wu/WtgVv/P70+J3ABxeGJbFU64KqmQWAnsDCKE/3NbOlwCaCo/gVNY5OxOdy5xbwwKzVYW21csFUG65INcWc3M2sGfAq8Afn3A8RT38KtHfOFZvZYOB1oFOU9xgFjALI0A+lJLjI0fqQE9vwxMiTaudk2nBFqimmm5jMLA14A/inc+7RGPoXAlnOuS2V9dGcuySq3z67mHdXfRvWVifLG/PytOGKxG/O3YJXg6YAqypL7GZ2JPCtc86Z2ckEl1hurWbMIr4XOVq/e2hX/vO0DnVzcm24ItUQy7RMP+AKYJmZLSlvuwPIAHDOTQYuAW4ws93AT8AI51VdA0kOPhuldsqeReme8B9p3YwkfhbLapn5wAHXcjnnHgcej1dQkuJ8tKa7rMxxzB3hhb7+fu0pnNrx8DqNQ6S6VDhM/Mcna7pVOkD8SIXDJHF5vKb7h52ldB//dlibCn1JolFyF//xcE23RuuSLFQ4TPzHg2JfX3xXvF9iXzVhkBK7JCyN3MV/6rjYl0brkoyU3MWf6mBN97srv+W3z4Vf1I9boS8Rjym5S0qKHK23OSSdj27v71E0IvGn5C4pZdI7a3jsvbVhbZqCkWSk5C4pI3K0fmlWO/54SQ+PohGpXUrukvRueXkpr+QXhbVptC7JTsldklrkaP3BYSdy+ckqNy3JT8ldktLpf5zNxu9/CmvTaF1SiZK7JJU9ZY6OEYW+Zt10Ol2PauFRRCLeUHKXpKGbkUT2UXKXhLf9p1J63Bte6Cv/zgG0atbIo4hEvKfkLglNo3WR6JTcJSEVbC6m/58+CGtbc/95NGygWngioOQuCShytN6sUQOW33uuR9GI+JOSuySMOf/6jqv+tiisTVMwItEpuUtCiBytn9P1F+ReWeVOYyIpS8ldfO2vHxTw4Jurw9o0WhepmpK7+FbkaH3succz+qxjPYpGJLEouYv38vLCdl168Hd/4q9b0sO6aLQuUj1K7uKtvDwYNQpKSgAIjHgCtux7+qXr+nJyh8M8Ck4kcSm5i7eys6GkhF9dlsOCQHhtdY3WRQ6ekrt4avfGIo697Y2wtnmTr+HoH76DiWUeRSWS+JTcxTPH3jGL3WOnh7UVPjQ0+E379h5EJJI8lNylzkUr9LVs0nCa7yqvv96kCeTkeBCZSPKoshCHmR1tZu+b2SozW2FmY6L0MTP7i5l9YWafm9lJtROuJLrAuJlhib1ZowYUnriN5m2OALPgiD03F0aO9DBKkcQXy8h9N3Czc+5TM2sO5JvZO865lRX6nAd0Kv86BXiq/FEEgG+276TPg++FtRU8MJj69Sx4oGQuEldVJnfn3NfA1+Xf7zCzVUBboGJyvxB4zjnngI/NrKWZtSl/raS4yJuRzjy+NVOvPtmjaERSQ7Xm3M0sAPQEFkY81RbYWOG4qLwtLLmb2ShgFEBGhjYpTnYrNm1nyF/mh7VpeaNI3Yg5uZtZM+BV4A/OuR8in47yErdfg3O5QC5AVlbWfs9L8ogcrT908Ylc1lu/0EXqSkzJ3czSCCb2POfca1G6FAFHVzhuB2yqeXiSaN5b9S3XPLs4rE2jdZG6V2VyNzMDpgCrnHOPVtJtBvA7M3uR4IXU7ZpvTz2Ro/W8355Cv2MP9ygakdQWy8i9H3AFsMzMlpS33QFkADjnJgOzgMHAF0AJcHX8QxW/+tuH67j3HyvD2jRaF/FWLKtl5hN9Tr1iHweMjldQkhicc3S4fVZY27v/fQbHHtHco4hEZC/doSoH5c7Xl/H8xxvC2jRaF/EPJXeplt17yjg2+82wtsV3DuDwZo08ikhEolFyl5hd/NQC8tf/O3R89GGNmXfr2R5GJCKVUXKXKu3YWcqJ48MLfa2+bxDpafU9ikhEqqLkLgfUKXsWpXv23W92XrcjeerXvTyMSERioeQuURX9u4TTHno/rO3LBwZTr94BF06JiE8ouct+Im9Guql/J/574HEeRSMiB0PJXUKWbtzGhU98GNam5Y0iiUnJXYD9R+t/viyTi3q29SgaEakpJfcU99byr7n++U/D2jRaF0l8Su4pLHK0/tJ1fTm5w2EeRSMi8aTknoImf1DAxDdXh7VptC6SXJTcU0i0Ql/v33ImHQ5v6lFEIlJb6nkdQFLJy4NAAOrVCz7m5XkdUcjNLy3dL7EXThyixB5vPv4ZkNSikXu85OXBqFFQUhI8Xr8+eAwwcqRnYe3aXcZxd4YX+lpy90BaNmnoUURJzKc/A5KaLFiKve5lZWW5xYsXV90xUQQCwX/Mkdq3h8LCuo4GgPMem8eqr/dtd9v5yOa89YczPIklJfjwZ0CSj5nlO+eyquqnkXu8bNhQvfZatL2klB4Twgt9/ev+QTRqoEJftcpHPwMimnOPl4yM6rXXksC4mWGJ/Zc921I4cYj3iT0V5qJ98jMgAkru8ZOTA02ahLc1aRJsrwPf7di537r1dQ8OZtJlmXVy/gPaOxe9fj04t28uOtkSvMc/AyIVKbnHy8iRkJsbnF81Cz7m5tbJhbT+f5rDyTnvhY5vHXQ8hROHYOaTCo7Z2fsuMu5VUhJsTyYe/gyIRNIF1QT2xXfFDHj0g7A2X96MVK9ecMQeyQzKyuo+HpEEpguqSS5yCubVG06lV/tDPYqmChkZ0VeRaC5apNZoWibBLCr8PiyxmwVH673mz/LvBUvNRYvUOY3cE0jkaD1UOsDvN8/sjSE7O7gsMCMjmNj9EJtIktKcewKY+fnXjP77vrK8+92MpJtnRFKG5tyTQLRCX4vvHMDhzRqFd9TNMyISQXPuPvW/874MS+xDTmxD4cQh+yd20M0zIrKfKkfuZvYMMBT4zjnXLcrzZwLTgXXlTa855ybEM8hUUrqnjE7Z4YW+Vk44lyYND/BXlZMTPucOumApkuJimZaZCjwOPHeAPvOcc0PjElEKGz9jBVMXFIaObzyzI7cO6lz1C3XBUkQiVJncnXNzzSxQ+6Gkrh07SzlxfHihr4IHBlO/XjXuMB05UslcRELidUG1r5ktBTYBtzjnVkTrZGajgFEAGZoPBuA3z3zCB2s2h44f+OWJ/OoUfTYiUjPxSO6fAu2dc8VmNhh4HegUraNzLhfIheBSyDicO2F9s30nfR58L6xt3YOD/VMPRkQSWo2Tu3PuhwrfzzKzJ83scOfclpq+d7I67aHZFP37p9DxlN9k0b/LLzyMSESSTY2Tu5kdCXzrnHNmdjLB5ZVbaxxZElrz7Q7OmTQ3rM2Xhb5EJOHFshTyBeBM4HAzKwLuAdIAnHOTgUuAG8xsN/ATMMJ5ddurj0WWDpg+uh89jm7pUTQikuxiWS1zeRXPP05wqaREsaBgC796emHouGnD+qyYMMjDiEQkFaj8QC2KHK3PHXsWGa2aVNJbRCR+lNxrwfQlXzHmxSWh4x5Ht2T66H4eRiQiqUbJPY6iFfr67K6BHNq0oUcRiUiqUuGwOJm+5KuwxD6sZ1sKJw5RYhcRT2jkXkPRCn396/5BNGpQ36OIRESU3Gskd24BD8xaHTp++JLuDM862sOIRESClNyrKy+PH++ZwAmXPBrW/OUDg6lXnUJfIiK1SHPu1ZGXxyuP5oUl9r/NeIDCE7cpsYuIr2jkHqMfdpbSfVlLGDgagMa7drJq0iXBJ7O/UrldEfEVJfcYRM6tz/nrtQS2fb2vg/YqFRGfUXI/gO927OTknH1lea9Z/R53TZ+0f0fVphcRn1Fyr0TOzJU8PW9d6PiTO/pzxD+2wTtNtFepiPheYl1QzcuDQADq1Qs+5uXF/RTrt/5IYNzMUGK/bVBnCicO4YgW6cF59dxcaN8ezIKPubmabxcR30mckXteHowatW/UvH598BjillzHvPgZ05dsCh0vveccDmmcFt5Je5WKSAIwr0qvZ2VlucWLF8f+gkAgmNAjtW8PhYU1imXFpu0M+cv80PEfL+nOpboZSUR8yMzynXNZVfVLnJF7ZStSarBSxTnHiNyPWbjuewCapzdgUfYA0tNUOkBEElviJPeMjOgj94NcqfLxl1sZkftx6PjpK7MY2FX7mIpIckic5J6TEz7nDge1UmX3njIGTprLui0/AnDsEc14a8zpNKifWNeWRUQOJHGS+96LmNnZwamYjIxgYq/Gxc23ln/D9c/nh45fuq4vJ3c4LN6Rioh4LnGSOxz0SpWdpXs46b53KNm1B4B+x7bi+WtOwUz1YEQkOSVWcj8I/7doA7e9uix0/OaY0+nSpoWHEYmI1L6kTe7bS0rpMeHt0PGwk9ry6KWZHkYkIlJ3kjK5P/H+Fzz8z3+FjufdehZHH9bEw4hEROpWUiX3b3/YySkP7Cv0df1/dGTceZ09jEhExBvTo48KAAAEk0lEQVRJk9zHz1jB1AWFoeNF2QNo3byRdwGJiHgo4ZP7ui0/ctYjc0LHdw7pwm9PP8a7gEREfCBhk7tzjt/9/TNmLtu3acay8efQPD3tAK8SEUkNVSZ3M3sGGAp855zrFuV5Ax4DBgMlwFXOuU/jHWhFy4q2c/7j+wp9PXppD4ad1K42TykiklBiued+KjDoAM+fB3Qq/xoFPFXzsCq38fuSUGJv1bQhq+8bpMQuIhKhypG7c26umQUO0OVC4DkXrB38sZm1NLM2zrmvD/Cag9asUQP6HduKa07rwNmdVehLRCSaeMy5twU2VjguKm+rleR+aNOG5P22T228tYhI0ohHKcRoBVqi7gBiZqPMbLGZLd68eXMcTi0iItHEI7kXARW3LWoHbIrW0TmX65zLcs5ltW7dOg6nFhGRaOKR3GcAV1pQH2B7bc23i4hIbGJZCvkCcCZwuJkVAfcAaQDOucnALILLIL8guBTy6toKVkREYhPLapnLq3jeAaPjFpGIiNSY9pYTEUlCSu4iIklIyV1EJAlZcMrcgxObbQbWx9D1cGBLLYeTiPS5VE6fTXT6XCqXSJ9Ne+dclWvJPUvusTKzxc65LK/j8Bt9LpXTZxOdPpfKJeNno2kZEZEkpOQuIpKEEiG553odgE/pc6mcPpvo9LlULuk+G9/PuYuISPUlwshdRESqyZfJ3cyONrP3zWyVma0wszFex+QnZlbfzD4zsze8jsVPyjeKecXMVpf/7PT1Oia/MLP/Kv+3tNzMXjCzdK9j8oqZPWNm35nZ8gpth5nZO2a2tvzxUC9jjAdfJndgN3Czc64L0AcYbWZdPY7JT8YAq7wOwoceA95yznUGeqDPCAAzawvcBGSV74NcHxjhbVSemsr+W4eOA95zznUC3is/Tmi+TO7Oua/3brLtnNtB8B9pW2+j8gczawcMAf7X61j8xMxaAGcAUwCcc7ucc9u8jcpXGgCNzawB0IRK9lxIBc65ucD3Ec0XAs+Wf/8scFGdBlULfJncKyrfv7UnsNDbSHzjz8CtQJnXgfjMMcBm4G/lU1b/a2ZNvQ7KD5xzXwGPABsIbn+53Tn3trdR+c4v9u5DUf54hMfx1Jivk7uZNQNeBf7gnPvB63i8ZmZDge+cc/lex+JDDYCTgKeccz2BH0mC/1rHQ/n88YVAB+AooKmZ/drbqKS2+Ta5m1kawcSe55x7zet4fKIfcIGZFQIvAmeb2fPehuQbRUCRc27v//BeIZjsBQYA65xzm51zpcBrwKkex+Q335pZG4Dyx+88jqfGfJnczcwIzp2ucs496nU8fuGcu9051845FyB4QWy2c04jMMA59w2w0cyOL2/qD6z0MCQ/2QD0MbMm5f+2+qOLzZFmAL8p//43wHQPY4mLKndi8kg/4ApgmZktKW+7wzk3y8OYxP9+D+SZWUPgS7TlIwDOuYVm9grwKcGVaJ+RhHdkxqqSrUMnAi+Z2TUEfxkO9y7C+NAdqiIiSciX0zIiIlIzSu4iIklIyV1EJAkpuYuIJCEldxGRJKTkLiKShJTcRUSSkJK7iEgS+v8notqtBX8tmgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Graphic display\n", + "plt.plot(X, Y, 'ro', label='Original data')\n", + "plt.plot(X, np.array(W * X + b), label='Fitted line')\n", + "plt.legend()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/2_BasicModels/logistic_regression.ipynb b/tensorflow_v2/notebooks/2_BasicModels/logistic_regression.ipynb new file mode 100644 index 00000000..c29c42b9 --- /dev/null +++ b/tensorflow_v2/notebooks/2_BasicModels/logistic_regression.ipynb @@ -0,0 +1,344 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Logistic Regression Example\n", + "\n", + "Logistic regression implementation with TensorFlow v2 library.\n", + "\n", + "This example is using a low-level approach to better understand all mechanics behind the training process.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## MNIST Dataset Overview\n", + "\n", + "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n", + "\n", + "In this example, each image will be converted to float32, normalized to [0, 1] and flattened to a 1-D array of 784 features (28*28).\n", + "\n", + "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", + "\n", + "More info: http://yann.lecun.com/exdb/mnist/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST dataset parameters.\n", + "num_classes = 10 # 0 to 9 digits\n", + "num_features = 784 # 28*28\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.01\n", + "training_steps = 1000\n", + "batch_size = 256\n", + "display_step = 50" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", + "# Flatten images to 1-D vector of 784 features (28*28).\n", + "x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Weight of shape [784, 10], the 28*28 image features, and total number of classes.\n", + "W = tf.Variable(tf.ones([num_features, num_classes]), name=\"weight\")\n", + "# Bias of shape [10], the total number of classes.\n", + "b = tf.Variable(tf.zeros([num_classes]), name=\"bias\")\n", + "\n", + "# Logistic regression (Wx + b).\n", + "def logistic_regression(x):\n", + " # Apply softmax to normalize the logits to a probability distribution.\n", + " return tf.nn.softmax(tf.matmul(x, W) + b)\n", + "\n", + "# Cross-Entropy loss function.\n", + "def cross_entropy(y_pred, y_true):\n", + " # Encode label to a one hot vector.\n", + " y_true = tf.one_hot(y_true, depth=num_classes)\n", + " # Clip prediction values to avoid log(0) error.\n", + " y_pred = tf.clip_by_value(y_pred, 1e-9, 1.)\n", + " # Compute cross-entropy.\n", + " return tf.reduce_mean(-tf.reduce_sum(y_true * tf.math.log(y_pred)))\n", + "\n", + "# Accuracy metric.\n", + "def accuracy(y_pred, y_true):\n", + " # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n", + " correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n", + " return tf.reduce_mean(tf.cast(correct_prediction, tf.float32))\n", + "\n", + "# Stochastic gradient descent optimizer.\n", + "optimizer = tf.optimizers.SGD(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x, y):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " pred = logistic_regression(x)\n", + " loss = cross_entropy(pred, y)\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, [W, b])\n", + " \n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, [W, b]))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 50, loss: 608.584717, accuracy: 0.824219\n", + "step: 100, loss: 828.206482, accuracy: 0.765625\n", + "step: 150, loss: 716.329407, accuracy: 0.746094\n", + "step: 200, loss: 584.887634, accuracy: 0.820312\n", + "step: 250, loss: 472.098114, accuracy: 0.871094\n", + "step: 300, loss: 621.834595, accuracy: 0.832031\n", + "step: 350, loss: 567.288818, accuracy: 0.714844\n", + "step: 400, loss: 489.062988, accuracy: 0.847656\n", + "step: 450, loss: 496.466675, accuracy: 0.843750\n", + "step: 500, loss: 465.342224, accuracy: 0.875000\n", + "step: 550, loss: 586.347168, accuracy: 0.855469\n", + "step: 600, loss: 95.233109, accuracy: 0.906250\n", + "step: 650, loss: 88.136490, accuracy: 0.910156\n", + "step: 700, loss: 67.170349, accuracy: 0.937500\n", + "step: 750, loss: 79.673691, accuracy: 0.921875\n", + "step: 800, loss: 112.844872, accuracy: 0.914062\n", + "step: 850, loss: 92.789581, accuracy: 0.894531\n", + "step: 900, loss: 80.116165, accuracy: 0.921875\n", + "step: 950, loss: 45.706650, accuracy: 0.925781\n", + "step: 1000, loss: 72.986969, accuracy: 0.925781\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization(batch_x, batch_y)\n", + " \n", + " if step % display_step == 0:\n", + " pred = logistic_regression(batch_x)\n", + " loss = cross_entropy(pred, batch_y)\n", + " acc = accuracy(pred, batch_y)\n", + " print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Accuracy: 0.915100\n" + ] + } + ], + "source": [ + "# Test model on validation set.\n", + "pred = logistic_regression(x_test)\n", + "print(\"Test Accuracy: %f\" % accuracy(pred, y_test))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize predictions.\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 7\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 2\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 1\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAP8AAAD8CAYAAAC4nHJkAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAADbVJREFUeJzt3W2IXPUVx/HfSWzfpH2hZE3jU9I2EitCTVljoRKtxZKUStIX0YhIiqUbJRoLfVFJwEaKINqmLRgSthi6BbUK0bqE0KaINBWCuJFaNVtblTVNs2yMEWsI0picvti7siY7/zuZuU+b8/2AzMOZuXO8+tt7Z/733r+5uwDEM6PuBgDUg/ADQRF+ICjCDwRF+IGgCD8QFOEHgiL8QFCEHwjqnCo/zMw4nBAombtbO6/rastvZkvN7A0ze9PM7u1mWQCqZZ0e229mMyX9U9INkg5IeknSLe6+L/EetvxAyarY8i+W9Ka7v+3u/5P0e0nLu1gegAp1E/4LJf170uMD2XOfYmZ9ZjZkZkNdfBaAgnXzg99Uuxan7da7e7+kfondfqBJutnyH5B08aTHF0k62F07AKrSTfhfknSpmX3RzD4raZWkwWLaAlC2jnf73f1jM7tL0p8kzZS0zd1fL6wzAKXqeKivow/jOz9QukoO8gEwfRF+ICjCDwRF+IGgCD8QFOEHgiL8QFCEHwiK8ANBEX4gKMIPBEX4gaAIPxAU4QeCIvxAUIQfCIrwA0ERfiAowg8ERfiBoAg/EFSlU3SjerNmzUrWH3744WR9zZo1yfrevXuT9ZUrV7asvfPOO8n3olxs+YGgCD8QFOEHgiL8QFCEHwiK8ANBEX4gqK5m6TWzEUkfSjoh6WN37815PbP0VmzBggXJ+vDwcFfLnzEjvf1Yt25dy9rmzZu7+mxMrd1Zeos4yOeb7n64gOUAqBC7/UBQ3YbfJe0ys71m1ldEQwCq0e1u/zfc/aCZnS/pz2b2D3ffPfkF2R8F/jAADdPVlt/dD2a3hyQ9I2nxFK/pd/fevB8DAVSr4/Cb2Swz+/zEfUnflvRaUY0BKFc3u/1zJD1jZhPLedzd/1hIVwBK13H43f1tSV8tsBd0qKenp2VtYGCgwk4wnTDUBwRF+IGgCD8QFOEHgiL8QFCEHwiKS3dPA6nTYiVpxYoVLWuLF5920GWllixZ0rKWdzrwK6+8kqzv3r07WUcaW34gKMIPBEX4gaAIPxAU4QeCIvxAUIQfCKqrS3ef8Ydx6e6OnDhxIlk/efJkRZ2cLm+svpve8qbwvvnmm5P1vOnDz1btXrqbLT8QFOEHgiL8QFCEHwiK8ANBEX4gKMIPBMU4fwPs3LkzWV+2bFmyXuc4/3vvvZesHz16tGVt3rx5RbfzKTNnzix1+U3FOD+AJMIPBEX4gaAIPxAU4QeCIvxAUIQfCCr3uv1mtk3SdyUdcvcrsufOk/SkpPmSRiTd5O7vl9fm9Hbttdcm6wsXLkzW88bxyxzn37p1a7K+a9euZP2DDz5oWbv++uuT792wYUOynufOO+9sWduyZUtXyz4btLPl/62kpac8d6+k59z9UknPZY8BTCO54Xf33ZKOnPL0ckkD2f0BSa2njAHQSJ1+55/j7qOSlN2eX1xLAKpQ+lx9ZtYnqa/szwFwZjrd8o+Z2VxJym4PtXqhu/e7e6+793b4WQBK0Gn4ByWtzu6vlvRsMe0AqEpu+M3sCUl7JC00swNm9gNJD0q6wcz+JemG7DGAaYTz+Qswf/78ZH3Pnj3J+uzZs5P1bq6Nn3ft++3btyfr999/f7J+7NixZD0l73z+vPXW09OTrH/00Ucta/fdd1/yvY888kiyfvz48WS9TpzPDyCJ8ANBEX4gKMIPBEX4gaAIPxAUQ30FWLBgQbI+PDzc1fLzhvqef/75lrVVq1Yl33v48OGOeqrC3Xffnaxv2rQpWU+tt7zToC+77LJk/a233krW68RQH4Akwg8ERfiBoAg/EBThB4Ii/EBQhB8IqvTLeKF7Q0NDyfrtt9/estbkcfw8g4ODyfqtt96arF911VVFtnPWYcsPBEX4gaAIPxAU4QeCIvxAUIQfCIrwA0Exzl+BvPPx81x99dUFdTK9mKVPS89br92s940bNybrt912W8fLbgq2/EBQhB8IivADQRF+ICjCDwRF+IGgCD8QVO44v5ltk/RdSYfc/YrsuY2Sfijp3exl6919Z1lNNt0dd9yRrOddIx5Tu/HGG5P1RYsWJeup9Z733yRvnP9s0M6W/7eSlk7x/C/d/crsn7DBB6ar3PC7+25JRyroBUCFuvnOf5eZ/d3MtpnZuYV1BKASnYZ/i6QvS7pS0qikX7R6oZn1mdmQmaUvRAegUh2F393H3P2Eu5+U9BtJixOv7Xf3Xnfv7bRJAMXrKPxmNnfSw+9Jeq2YdgBUpZ2hvickXSdptpkdkPRTSdeZ2ZWSXNKIpDUl9gigBLnhd/dbpnj60RJ6mbbyxqMj6+npaVm7/PLLk+9dv3590e184t13303Wjx8/XtpnNwVH+AFBEX4gKMIPBEX4gaAIPxAU4QeC4tLdKNWGDRta1tauXVvqZ4+MjLSsrV69Ovne/fv3F9xN87DlB4Ii/EBQhB8IivADQRF+ICjCDwRF+IGgGOdHV3buTF+4eeHChRV1crp9+/a1rL3wwgsVdtJMbPmBoAg/EBThB4Ii/EBQhB8IivADQRF+ICjG+QtgZsn6jBnd/Y1dtmxZx+/t7+9P1i+44IKOly3l/7vVOT05l1RPY8sPBEX4gaAIPxAU4QeCIvxAUIQfCIrwA0HljvOb2cWSfifpC5JOSup391+b2XmSnpQ0X9KIpJvc/f3yWm2uLVu2JOsPPfRQV8vfsWNHst7NWHrZ4/BlLn/r1q2lLTuCdrb8H0v6sbt/RdLXJa01s8sl3SvpOXe/VNJz2WMA00Ru+N191N1fzu5/KGlY0oWSlksayF42IGlFWU0CKN4Zfec3s/mSFkl6UdIcdx+Vxv9ASDq/6OYAlKftY/vN7HOStkv6kbv/N+949knv65PU11l7AMrS1pbfzD6j8eA/5u5PZ0+PmdncrD5X0qGp3uvu/e7e6+69RTQMoBi54bfxTfyjkobdfdOk0qCkialOV0t6tvj2AJTF3D39ArNrJP1V0qsaH+qTpPUa/97/lKRLJO2XtNLdj+QsK/1h09S8efOS9T179iTrPT09yXqTT5vN621sbKxlbXh4OPnevr70t8XR0dFk/dixY8n62crd2/pOnvud391fkNRqYd86k6YANAdH+AFBEX4gKMIPBEX4gaAIPxAU4QeCyh3nL/TDztJx/jxLlixJ1lesSJ8Tdc899yTrTR7nX7duXcva5s2bi24Han+cny0/EBThB4Ii/EBQhB8IivADQRF+ICjCDwTFOP80sHTp0mQ9dd573jTVg4ODyXreFN95l3Pbt29fy9r+/fuT70VnGOcHkET4gaAIPxAU4QeCIvxAUIQfCIrwA0Exzg+cZRjnB5BE+IGgCD8QFOEHgiL8QFCEHwiK8ANB5YbfzC42s+fNbNjMXjeze7LnN5rZf8zsb9k/3ym/XQBFyT3Ix8zmSprr7i+b2ecl7ZW0QtJNko66+8/b/jAO8gFK1+5BPue0saBRSaPZ/Q/NbFjShd21B6BuZ/Sd38zmS1ok6cXsqbvM7O9mts3Mzm3xnj4zGzKzoa46BVCoto/tN7PPSfqLpAfc/WkzmyPpsCSX9DONfzW4PWcZ7PYDJWt3t7+t8JvZZyTtkPQnd980RX2+pB3ufkXOcgg/ULLCTuyx8cuzPippeHLwsx8CJ3xP0mtn2iSA+rTza/81kv4q6VVJE3NBr5d0i6QrNb7bPyJpTfbjYGpZbPmBkhW6218Uwg+Uj/P5ASQRfiAowg8ERfiBoAg/EBThB4Ii/EBQhB8IivADQRF+ICjCDwRF+IGgCD8QFOEHgsq9gGfBDkt6Z9Lj2dlzTdTU3pral0RvnSqyt3ntvrDS8/lP+3CzIXfvra2BhKb21tS+JHrrVF29sdsPBEX4gaDqDn9/zZ+f0tTemtqXRG+dqqW3Wr/zA6hP3Vt+ADWpJfxmttTM3jCzN83s3jp6aMXMRszs1Wzm4VqnGMumQTtkZq9Neu48M/uzmf0ru51ymrSaemvEzM2JmaVrXXdNm/G68t1+M5sp6Z+SbpB0QNJLkm5x932VNtKCmY1I6nX32seEzWyJpKOSfjcxG5KZPSTpiLs/mP3hPNfdf9KQ3jbqDGduLqm3VjNLf181rrsiZ7wuQh1b/sWS3nT3t939f5J+L2l5DX00nrvvlnTklKeXSxrI7g9o/H+eyrXorRHcfdTdX87ufyhpYmbpWtddoq9a1BH+CyX9e9LjA2rWlN8uaZeZ7TWzvrqbmcKciZmRstvza+7nVLkzN1fplJmlG7PuOpnxumh1hH+q2USaNOTwDXf/mqRlktZmu7dozxZJX9b4NG6jkn5RZzPZzNLbJf3I3f9bZy+TTdFXLeutjvAfkHTxpMcXSTpYQx9TcveD2e0hSc9o/GtKk4xNTJKa3R6quZ9PuPuYu59w95OSfqMa1102s/R2SY+5+9PZ07Wvu6n6qmu91RH+lyRdamZfNLPPSlolabCGPk5jZrOyH2JkZrMkfVvNm314UNLq7P5qSc/W2MunNGXm5lYzS6vmdde0Ga9rOcgnG8r4laSZkra5+wOVNzEFM/uSxrf20vgZj4/X2ZuZPSHpOo2f9TUm6aeS/iDpKUmXSNovaaW7V/7DW4vertMZztxcUm+tZpZ+UTWuuyJnvC6kH47wA2LiCD8gKMIPBEX4gaAIPxAU4QeCIvxAUIQfCIrwA0H9HwAENgeMtPBpAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 4\n" + ] + } + ], + "source": [ + "# Predict 5 images from validation set.\n", + "n_images = 5\n", + "test_images = x_test[:n_images]\n", + "predictions = logistic_regression(test_images)\n", + "\n", + "# Display image and model prediction.\n", + "for i in range(n_images):\n", + " plt.imshow(np.reshape(test_images[i], [28, 28]), cmap='gray')\n", + " plt.show()\n", + " print(\"Model prediction: %i\" % np.argmax(predictions.numpy()[i]))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/3_NeuralNetworks/autoencoder.ipynb b/tensorflow_v2/notebooks/3_NeuralNetworks/autoencoder.ipynb new file mode 100644 index 00000000..b7c22279 --- /dev/null +++ b/tensorflow_v2/notebooks/3_NeuralNetworks/autoencoder.ipynb @@ -0,0 +1,338 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Auto-Encoder Example\n", + "\n", + "Build a 2 layers auto-encoder with TensorFlow v2 to compress images to a lower latent space and then reconstruct them.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Auto-Encoder Overview\n", + "\n", + "\"ae\"\n", + "\n", + "References:\n", + "- [Gradient-based learning applied to document recognition](http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf). Y. LeCun, L. Bottou, Y. Bengio, and P. Haffner. Proceedings of the IEEE, 86(11):2278-2324, November 1998.\n", + "\n", + "## MNIST Dataset Overview\n", + "\n", + "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n", + "\n", + "In this example, each image will be converted to float32, normalized to [0, 1] and flattened to a 1-D array of 784 features (28*28).\n", + "\n", + "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", + "\n", + "More info: http://yann.lecun.com/exdb/mnist/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST Dataset parameters.\n", + "num_features = 784 # data features (img shape: 28*28).\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.01\n", + "training_steps = 20000\n", + "batch_size = 256\n", + "display_step = 1000\n", + "\n", + "# Network Parameters\n", + "num_hidden_1 = 128 # 1st layer num features.\n", + "num_hidden_2 = 64 # 2nd layer num features (the latent dim)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = x_train.astype(np.float32), x_test.astype(np.float32)\n", + "# Flatten images to 1-D vector of 784 features (28*28).\n", + "x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(10000).batch(batch_size).prefetch(1)\n", + "\n", + "test_data = tf.data.Dataset.from_tensor_slices((x_test, y_test))\n", + "test_data = test_data.repeat().batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Store layers weight & bias\n", + "\n", + "# A random value generator to initialize weights.\n", + "random_normal = tf.initializers.RandomNormal()\n", + "\n", + "weights = {\n", + " 'encoder_h1': tf.Variable(random_normal([num_features, num_hidden_1])),\n", + " 'encoder_h2': tf.Variable(random_normal([num_hidden_1, num_hidden_2])),\n", + " 'decoder_h1': tf.Variable(random_normal([num_hidden_2, num_hidden_1])),\n", + " 'decoder_h2': tf.Variable(random_normal([num_hidden_1, num_features])),\n", + "}\n", + "biases = {\n", + " 'encoder_b1': tf.Variable(random_normal([num_hidden_1])),\n", + " 'encoder_b2': tf.Variable(random_normal([num_hidden_2])),\n", + " 'decoder_b1': tf.Variable(random_normal([num_hidden_1])),\n", + " 'decoder_b2': tf.Variable(random_normal([num_features])),\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Building the encoder.\n", + "def encoder(x):\n", + " # Encoder Hidden layer with sigmoid activation.\n", + " layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['encoder_h1']),\n", + " biases['encoder_b1']))\n", + " # Encoder Hidden layer with sigmoid activation.\n", + " layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['encoder_h2']),\n", + " biases['encoder_b2']))\n", + " return layer_2\n", + "\n", + "\n", + "# Building the decoder.\n", + "def decoder(x):\n", + " # Decoder Hidden layer with sigmoid activation.\n", + " layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['decoder_h1']),\n", + " biases['decoder_b1']))\n", + " # Decoder Hidden layer with sigmoid activation.\n", + " layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['decoder_h2']),\n", + " biases['decoder_b2']))\n", + " return layer_2" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Mean square loss between original images and reconstructed ones.\n", + "def mean_square(reconstructed, original):\n", + " return tf.reduce_mean(tf.pow(original - reconstructed, 2))\n", + "\n", + "# Adam optimizer.\n", + "optimizer = tf.optimizers.Adam(learning_rate=learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " reconstructed_image = decoder(encoder(x))\n", + " loss = mean_square(reconstructed_image, x)\n", + "\n", + " # Variables to update, i.e. trainable variables.\n", + " trainable_variables = weights.values() + biases.values()\n", + " \n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, trainable_variables)\n", + " \n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, trainable_variables))\n", + " \n", + " return loss" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 0, loss: 0.234978\n", + "step: 1000, loss: 0.014881\n", + "step: 2000, loss: 0.010402\n", + "step: 3000, loss: 0.008817\n", + "step: 4000, loss: 0.007337\n", + "step: 5000, loss: 0.006399\n", + "step: 6000, loss: 0.006039\n", + "step: 7000, loss: 0.005042\n", + "step: 8000, loss: 0.005235\n", + "step: 9000, loss: 0.004838\n", + "step: 10000, loss: 0.004552\n", + "step: 11000, loss: 0.004717\n", + "step: 12000, loss: 0.004550\n", + "step: 13000, loss: 0.004633\n", + "step: 14000, loss: 0.004469\n", + "step: 15000, loss: 0.004503\n", + "step: 16000, loss: 0.003971\n", + "step: 17000, loss: 0.004258\n", + "step: 18000, loss: 0.004012\n", + "step: 19000, loss: 0.003703\n", + "step: 20000, loss: 0.003933\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, _) in enumerate(train_data.take(training_steps + 1)):\n", + " \n", + " # Run the optimization.\n", + " loss = run_optimization(batch_x)\n", + " \n", + " if step % display_step == 0:\n", + " print(\"step: %i, loss: %f\" % (step, loss))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# Testing and Visualization.\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Images\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQUAAAD8CAYAAAB+fLH0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJztnXm8VXP3x99LKTI0UPQkMmVMKZSpkAwViVIUCWUuT6YKjzKVKeFJZCxTkilj0pOZKDSX0oMuKfWQRL/E9/fHOevuu/c9p3Pvmfa+567363Ve557v2WefdfY59/v9fNd3fdcS5xyGYRjKZmEbYBhGtLBOwTAMH9YpGIbhwzoFwzB8WKdgGIYP6xQMw/BhnYJhGD5y0imIyAkiskhElojIwFy8h2EYuUGyHbwkIlWAr4B2QBHwGXCGc25+Vt/IMIycUDUH5zwEWOKcWwogIuOBTkDSTkFELKzSMHLPKudc3VQH5WL60ABYVuJxUbzNh4j0FZEZIjIjBzYYhlGab8tyUC6UgiRoK6UEnHNjgDFgSsEwokQulEIR0LDE452AH3LwPoZh5IBcdAqfAXuKyK4iUg3oDkzKwfsYhpEDsj59cM5tFJFLgclAFeBR59y8bL+PYRi5IetLkmkZYT6FMnHllVcCsOWWWwJwwAEHANClSxffcaNHjwbg448/BuCJJ57Il4lGtJnpnDso1UEW0WgYhg9TChWAZ599FiitCFLx9ddfA3DssccC8N1332XXsJBo3LgxAAsXLgSgf//+ANx3332h2VRWttpqKwDuuOMOAC644AIAZs6cCUDXrl0B+PbbMq0elhdTCoZhlJ9cxCkYWSKVQtCRcvLkyQDstttuAJx00kkA7L777gD06NEDgGHDhuXO2Dxy4IEHAvD3338DUFRUFKY55aJ+/foA9OnTB/A+Q4sWLQDo2LEjAKNGjQrBuhimFAzD8GFKIYIcdFBs2te5c2df+7x5sZXdk08+GYBVq1YB8NtvvwFQrVo1AD755BMAmjZtCsB2222XY4vzS7NmzQBYt24dAC+++GKY5pSJunVjWw7Gjh0bsiWpMaVgGIaPCqkUdI6t87IffohFUa9fv56nnnoKgB9//BGAJUuWhGBhZui8UyS2jUQVwvHHHw/A8uXLE77uiiuuAGDffff1tb/22ms5sTPf7L///gBceumlQMWIv+jXrx8Ap5xyCgCHHHLIJo9v3bo1AJttFhuvZ82axXvvvZdDC0tjSsEwDB8VMk5h6dKlADRq1CjpMWvXrgW8UTZd1LN9++23AzBjRv52eu+yyy6A91n+97//bfL4WbNmAd6IqmicwrRp07JtYl5RhThhwgQAjj76aADefffd0GxKxV9//QV4qwzJUGUQPO7bb7+lW7dugBfLkAEWp2AYRvmpkD4F9SVo7P+CBQsA2GeffWjevDkARx11FACtWrUCYNmyWN6Xhg1L7ur22LhxIwA//fQT4M3rFY0GzKdSKGtU21VXXQV4kX7K9OnTffcVnauvvhrwrks+v4vy8vrrrwOeAkjF6tWrAW8lSVXirrvuyqeffgpAlSpVsm1mQipkpzB16lTfvfLmm28W/127dm3AW75S6XXwwQcnPOf69esB+OqrrwCvo6lTpw7ghQxHCQ10ufHGGwFvSXLlypUADBo0CIDff/89BOuyh04TdalWvyNdkowSbdq0AWCvvfYCvOlAsunDAw88AMBbb70FwJo1awA45phjALj22muLj73ooosAb8NbrrDpg2EYPiqkUigLP//8M1DauRZUF0FOO+00wFMac+bMAbyQ4yihI6cqBEVtjbIDrjzo6KvoFC9KqJoZP348ANtvv33C43Tq8/zzzwMwdOhQoLSa0+P69u1bHPikzu4tttgCgH//+98A/Pnnn1n5DIopBcMwfBSsUigv9erVA+D+++8HPAeRztdTLQfmk5deegmA4447ztc+btw4AK677rq825RLmjRp4nusI2aUqFo19q+UTCGoauvevTvghagnQ5XCsGHDGDFiBAA1atQAvM8/aVIsy2G2/V2mFAzD8GFKIc4ll1wCeBtX1CexaNGi0GwKosukhx12GADVq1cHvFHn5ptvBrxlrYqOLif37t0bgC+++AKAKVOmhGZTedFl03PPPRdIrRCCTJo0qXjre7KVs2xjSsEwDB+VXikcfvjhAAwc6K+DqxtY5s6dm3ebkqEe6+BW6CeffBKIZixFJmh4tsaKaByKxpREkWCwUsuWLTM6n4gUnzN47iFDhgBw1llnZfQeQUwpGIbho9Irhfbt2wOw+eabA14cg6ZHjwKaVEVDuJV33nkHgBtuuCHfJuUFTRKjm/YmTpwYpjmb5MILLwRSb3wqLyeddFKp9HN6r0oh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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reconstructed Images\n" + ] + }, + { + "data": { + "image/png": "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VO30hBRGvnyFYtBn9unTBwjt8KRSRHz/6L7U/bjuuusCMGbMGCDkzZx44okVv2amFAzDSNDslYJmcrU+j1uCq6lpTGxd0qQU5EtQE1jF3f/yl78knouaLevTdByQySmQQlBG5vz584GQmZmvOYyORXkaRxxxBFB7d6ga8KolXpwHUcw5kSK46qqrEs/1mWoQqya4+nmHDh0SY5eak3LQscyaNYurr7664PEVgikFwzASNHulIKuw3nrrJV6XElCbc1EK61EuZEXuu+8+IKyl33nnHSBk7mnscS5Gmo5JXvm77747l0MiSz5o0CAgqLg46qDj0LW95pprAGjbtm3ifbLSF1xwQeJzSpmboexR5YjoXKst3J///GcgRBOEomHyfwwePBiA/v37A+Fan3322Tk/Q6VayplSMAwjQbNVCrKSq6++OhBmXqFYdU3PfNrZa69Mq07F4WWVlJcgC6hj0hpdr3/99ddVb94q6/70008DGesuy//4448D8MorrwAhUpJP4Wy55ZZAiPULHa8yOt977z0gnK+G+Ika6kvSfSXFqfcri3LcuHFAyGTU5+oY9VyqSPs2dI1btWrF9ttvD8DDDz+8zLGUClMKhmEkaDpmskBkNeLGnrHFTNN6O0ZjVzxfY5WXXmvno446CoA//OEPQFBJyleYOHEixx13HFC7FX2l+OUvfwmEvBHnXG7fyfHHHw8Eqxr7RoQ89o899hhQ+9q+//77QMh7KORaN/R+ULak1JnGGucW5NtrorEvXboUCPUtpBSWW245tttuOyDsjyj3vWpKwTCMBM1OKcRWRTNznOWWrzV4GpEVind4tmrVCoDrrrsOgLXXXjvxfuUraB2/77775tauZ5xxBlA5haQxa09AzboCDz2UaUy+ZMmSxO/E+1Rk8Q8//HAgWFOhXaDa8/H111+X9iDqYIMNNgBq10tYa621gHDfxWOJ369rJdVX832KVFQqZ8aUgmEYCZqVUqipEuLZVDO23iNvsWLkaUZRBMXEpQSkFGRRFy5cCMDRRx8NwOuvvw4Ey3r66aez+eabA5XP1NRYldMvvv32W2bOnAlA586dAejVqxcQYvbKSNT75DMRUoPTp08HYMaMGYmfx1a5lLko2pcRZ8BKGcg/kg/9nq6hakDUROpBiq/cNTDqVQrOuVucc4udc9NqvNbWOfeEc25W9rFN9nXn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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Encode and decode images from test set and visualize their reconstruction.\n", + "n = 4\n", + "canvas_orig = np.empty((28 * n, 28 * n))\n", + "canvas_recon = np.empty((28 * n, 28 * n))\n", + "for i, (batch_x, _) in enumerate(test_data.take(n)):\n", + " # Encode and decode the digit image.\n", + " reconstructed_images = decoder(encoder(batch_x))\n", + " # Display original images.\n", + " for j in range(n):\n", + " # Draw the generated digits.\n", + " img = batch_x[j].numpy().reshape([28, 28])\n", + " canvas_orig[i * 28:(i + 1) * 28, j * 28:(j + 1) * 28] = img\n", + " # Display reconstructed images.\n", + " for j in range(n):\n", + " # Draw the generated digits.\n", + " reconstr_img = reconstructed_images[j].numpy().reshape([28, 28])\n", + " canvas_recon[i * 28:(i + 1) * 28, j * 28:(j + 1) * 28] = reconstr_img\n", + "\n", + "print(\"Original Images\") \n", + "plt.figure(figsize=(n, n))\n", + "plt.imshow(canvas_orig, origin=\"upper\", cmap=\"gray\")\n", + "plt.show()\n", + "\n", + "print(\"Reconstructed Images\")\n", + "plt.figure(figsize=(n, n))\n", + "plt.imshow(canvas_recon, origin=\"upper\", cmap=\"gray\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network.ipynb b/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network.ipynb new file mode 100644 index 00000000..0bf52a43 --- /dev/null +++ b/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network.ipynb @@ -0,0 +1,400 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Convolutional Neural Network Example\n", + "\n", + "Build a convolutional neural network with TensorFlow v2.\n", + "\n", + "This example is using a low-level approach to better understand all mechanics behind building convolutional neural networks and the training process.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## CNN Overview\n", + "\n", + "![CNN](http://personal.ie.cuhk.edu.hk/~ccloy/project_target_code/images/fig3.png)\n", + "\n", + "## MNIST Dataset Overview\n", + "\n", + "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n", + "\n", + "In this example, each image will be converted to float32 and normalized to [0, 1].\n", + "\n", + "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", + "\n", + "More info: http://yann.lecun.com/exdb/mnist/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "from tensorflow.keras import Model, layers\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST dataset parameters.\n", + "num_classes = 10 # total classes (0-9 digits).\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.001\n", + "training_steps = 200\n", + "batch_size = 128\n", + "display_step = 10\n", + "\n", + "# Network parameters.\n", + "conv1_filters = 32 # number of filters for 1st conv layer.\n", + "conv2_filters = 64 # number of filters for 2nd conv layer.\n", + "fc1_units = 1024 # number of neurons for 1st fully-connected layer." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Create TF Model.\n", + "class ConvNet(Model):\n", + " # Set layers.\n", + " def __init__(self):\n", + " super(ConvNet, self).__init__()\n", + " # Convolution Layer with 32 filters and a kernel size of 5.\n", + " self.conv1 = layers.Conv2D(32, kernel_size=5, activation=tf.nn.relu)\n", + " # Max Pooling (down-sampling) with kernel size of 2 and strides of 2. \n", + " self.maxpool1 = layers.MaxPool2D(2, strides=2)\n", + "\n", + " # Convolution Layer with 64 filters and a kernel size of 3.\n", + " self.conv2 = layers.Conv2D(64, kernel_size=3, activation=tf.nn.relu)\n", + " # Max Pooling (down-sampling) with kernel size of 2 and strides of 2. \n", + " self.maxpool2 = layers.MaxPool2D(2, strides=2)\n", + "\n", + " # Flatten the data to a 1-D vector for the fully connected layer.\n", + " self.flatten = layers.Flatten()\n", + "\n", + " # Fully connected layer.\n", + " self.fc1 = layers.Dense(1024)\n", + " # Apply Dropout (if is_training is False, dropout is not applied).\n", + " self.dropout = layers.Dropout(rate=0.5)\n", + "\n", + " # Output layer, class prediction.\n", + " self.out = layers.Dense(num_classes)\n", + "\n", + " # Set forward pass.\n", + " def call(self, x, is_training=False):\n", + " x = tf.reshape(x, [-1, 28, 28, 1])\n", + " x = self.conv1(x)\n", + " x = self.maxpool1(x)\n", + " x = self.conv2(x)\n", + " x = self.maxpool2(x)\n", + " x = self.flatten(x)\n", + " x = self.fc1(x)\n", + " x = self.dropout(x, training=is_training)\n", + " x = self.out(x)\n", + " if not is_training:\n", + " # tf cross entropy expect logits without softmax, so only\n", + " # apply softmax when not training.\n", + " x = tf.nn.softmax(x)\n", + " return x\n", + "\n", + "# Build neural network model.\n", + "conv_net = ConvNet()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Cross-Entropy Loss.\n", + "# Note that this will apply 'softmax' to the logits.\n", + "def cross_entropy_loss(x, y):\n", + " # Convert labels to int 64 for tf cross-entropy function.\n", + " y = tf.cast(y, tf.int64)\n", + " # Apply softmax to logits and compute cross-entropy.\n", + " loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=x)\n", + " # Average loss across the batch.\n", + " return tf.reduce_mean(loss)\n", + "\n", + "# Accuracy metric.\n", + "def accuracy(y_pred, y_true):\n", + " # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n", + " correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n", + " return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis=-1)\n", + "\n", + "# Stochastic gradient descent optimizer.\n", + "optimizer = tf.optimizers.Adam(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x, y):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " # Forward pass.\n", + " pred = conv_net(x, is_training=True)\n", + " # Compute loss.\n", + " loss = cross_entropy_loss(pred, y)\n", + " \n", + " # Variables to update, i.e. trainable variables.\n", + " trainable_variables = conv_net.trainable_variables\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, trainable_variables)\n", + " \n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, trainable_variables))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 10, loss: 1.877234, accuracy: 0.789062\n", + "step: 20, loss: 1.609390, accuracy: 0.898438\n", + "step: 30, loss: 1.555458, accuracy: 0.960938\n", + "step: 40, loss: 1.588296, accuracy: 0.921875\n", + "step: 50, loss: 1.561057, accuracy: 0.929688\n", + "step: 60, loss: 1.539851, accuracy: 0.945312\n", + "step: 70, loss: 1.527458, accuracy: 0.976562\n", + "step: 80, loss: 1.526701, accuracy: 0.945312\n", + "step: 90, loss: 1.522610, accuracy: 0.968750\n", + "step: 100, loss: 1.514970, accuracy: 0.968750\n", + "step: 110, loss: 1.489902, accuracy: 0.976562\n", + "step: 120, loss: 1.520697, accuracy: 0.953125\n", + "step: 130, loss: 1.494326, accuracy: 0.968750\n", + "step: 140, loss: 1.501781, accuracy: 0.984375\n", + "step: 150, loss: 1.506588, accuracy: 0.976562\n", + "step: 160, loss: 1.493378, accuracy: 0.984375\n", + "step: 170, loss: 1.494006, accuracy: 0.984375\n", + "step: 180, loss: 1.509782, accuracy: 0.953125\n", + "step: 190, loss: 1.516123, accuracy: 0.953125\n", + "step: 200, loss: 1.515508, accuracy: 0.953125\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization(batch_x, batch_y)\n", + " \n", + " if step % display_step == 0:\n", + " pred = conv_net(batch_x)\n", + " loss = cross_entropy_loss(pred, batch_y)\n", + " acc = accuracy(pred, batch_y)\n", + " print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Accuracy: 0.977700\n" + ] + } + ], + "source": [ + "# Test model on validation set.\n", + "pred = conv_net(x_test)\n", + "print(\"Test Accuracy: %f\" % accuracy(pred, y_test))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize predictions.\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 7\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 2\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 1\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 4\n" + ] + } + ], + "source": [ + "# Predict 5 images from validation set.\n", + "n_images = 5\n", + "test_images = x_test[:n_images]\n", + "predictions = conv_net(test_images)\n", + "\n", + "# Display image and model prediction.\n", + "for i in range(n_images):\n", + " plt.imshow(np.reshape(test_images[i], [28, 28]), cmap='gray')\n", + " plt.show()\n", + " print(\"Model prediction: %i\" % np.argmax(predictions.numpy()[i]))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network_raw.ipynb b/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network_raw.ipynb new file mode 100644 index 00000000..80adb3f0 --- /dev/null +++ b/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network_raw.ipynb @@ -0,0 +1,429 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Convolutional Neural Network Example\n", + "\n", + "Build a convolutional neural network with TensorFlow v2.\n", + "\n", + "This example is using a low-level approach to better understand all mechanics behind building convolutional neural networks and the training process.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## CNN Overview\n", + "\n", + "![CNN](http://personal.ie.cuhk.edu.hk/~ccloy/project_target_code/images/fig3.png)\n", + "\n", + "## MNIST Dataset Overview\n", + "\n", + "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n", + "\n", + "In this example, each image will be converted to float32 and normalized to [0, 1].\n", + "\n", + "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", + "\n", + "More info: http://yann.lecun.com/exdb/mnist/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST dataset parameters.\n", + "num_classes = 10 # total classes (0-9 digits).\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.001\n", + "training_steps = 200\n", + "batch_size = 128\n", + "display_step = 10\n", + "\n", + "# Network parameters.\n", + "conv1_filters = 32 # number of filters for 1st conv layer.\n", + "conv2_filters = 64 # number of filters for 2nd conv layer.\n", + "fc1_units = 1024 # number of neurons for 1st fully-connected layer." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Create some wrappers for simplicity.\n", + "def conv2d(x, W, b, strides=1):\n", + " # Conv2D wrapper, with bias and relu activation.\n", + " x = tf.nn.conv2d(x, W, strides=[1, strides, strides, 1], padding='SAME')\n", + " x = tf.nn.bias_add(x, b)\n", + " return tf.nn.relu(x)\n", + "\n", + "def maxpool2d(x, k=2):\n", + " # MaxPool2D wrapper.\n", + " return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, k, k, 1], padding='SAME')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Store layers weight & bias\n", + "\n", + "# A random value generator to initialize weights.\n", + "random_normal = tf.initializers.RandomNormal()\n", + "\n", + "weights = {\n", + " # Conv Layer 1: 5x5 conv, 1 input, 32 filters (MNIST has 1 color channel only).\n", + " 'wc1': tf.Variable(random_normal([5, 5, 1, conv1_filters])),\n", + " # Conv Layer 2: 5x5 conv, 32 inputs, 64 filters.\n", + " 'wc2': tf.Variable(random_normal([5, 5, conv1_filters, conv2_filters])),\n", + " # FC Layer 1: 7*7*64 inputs, 1024 units.\n", + " 'wd1': tf.Variable(random_normal([7*7*64, fc1_units])),\n", + " # FC Out Layer: 1024 inputs, 10 units (total number of classes)\n", + " 'out': tf.Variable(random_normal([fc1_units, num_classes]))\n", + "}\n", + "\n", + "biases = {\n", + " 'bc1': tf.Variable(tf.zeros([conv1_filters])),\n", + " 'bc2': tf.Variable(tf.zeros([conv2_filters])),\n", + " 'bd1': tf.Variable(tf.zeros([fc1_units])),\n", + " 'out': tf.Variable(tf.zeros([num_classes]))\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Create model\n", + "def conv_net(x):\n", + " \n", + " # Input shape: [-1, 28, 28, 1]. A batch of 28x28x1 (grayscale) images.\n", + " x = tf.reshape(x, [-1, 28, 28, 1])\n", + "\n", + " # Convolution Layer. Output shape: [-1, 28, 28, 32].\n", + " conv1 = conv2d(x, weights['wc1'], biases['bc1'])\n", + " \n", + " # Max Pooling (down-sampling). Output shape: [-1, 14, 14, 32].\n", + " conv1 = maxpool2d(conv1, k=2)\n", + "\n", + " # Convolution Layer. Output shape: [-1, 14, 14, 64].\n", + " conv2 = conv2d(conv1, weights['wc2'], biases['bc2'])\n", + " \n", + " # Max Pooling (down-sampling). Output shape: [-1, 7, 7, 64].\n", + " conv2 = maxpool2d(conv2, k=2)\n", + "\n", + " # Reshape conv2 output to fit fully connected layer input, Output shape: [-1, 7*7*64].\n", + " fc1 = tf.reshape(conv2, [-1, weights['wd1'].get_shape().as_list()[0]])\n", + " \n", + " # Fully connected layer, Output shape: [-1, 1024].\n", + " fc1 = tf.add(tf.matmul(fc1, weights['wd1']), biases['bd1'])\n", + " # Apply ReLU to fc1 output for non-linearity.\n", + " fc1 = tf.nn.relu(fc1)\n", + "\n", + " # Fully connected layer, Output shape: [-1, 10].\n", + " out = tf.add(tf.matmul(fc1, weights['out']), biases['out'])\n", + " # Apply softmax to normalize the logits to a probability distribution.\n", + " return tf.nn.softmax(out)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Cross-Entropy loss function.\n", + "def cross_entropy(y_pred, y_true):\n", + " # Encode label to a one hot vector.\n", + " y_true = tf.one_hot(y_true, depth=num_classes)\n", + " # Clip prediction values to avoid log(0) error.\n", + " y_pred = tf.clip_by_value(y_pred, 1e-9, 1.)\n", + " # Compute cross-entropy.\n", + " return tf.reduce_mean(-tf.reduce_sum(y_true * tf.math.log(y_pred)))\n", + "\n", + "# Accuracy metric.\n", + "def accuracy(y_pred, y_true):\n", + " # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n", + " correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n", + " return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis=-1)\n", + "\n", + "# ADAM optimizer.\n", + "optimizer = tf.optimizers.Adam(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x, y):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " pred = conv_net(x)\n", + " loss = cross_entropy(pred, y)\n", + " \n", + " # Variables to update, i.e. trainable variables.\n", + " trainable_variables = weights.values() + biases.values()\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, trainable_variables)\n", + " \n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, trainable_variables))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 10, loss: 72.370056, accuracy: 0.851562\n", + "step: 20, loss: 53.936745, accuracy: 0.882812\n", + "step: 30, loss: 29.929554, accuracy: 0.921875\n", + "step: 40, loss: 28.075102, accuracy: 0.953125\n", + "step: 50, loss: 19.366310, accuracy: 0.960938\n", + "step: 60, loss: 20.398090, accuracy: 0.945312\n", + "step: 70, loss: 29.320951, accuracy: 0.960938\n", + "step: 80, loss: 9.121045, accuracy: 0.984375\n", + "step: 90, loss: 11.680668, accuracy: 0.976562\n", + "step: 100, loss: 12.413654, accuracy: 0.976562\n", + "step: 110, loss: 6.675493, accuracy: 0.984375\n", + "step: 120, loss: 8.730624, accuracy: 0.984375\n", + "step: 130, loss: 13.608270, accuracy: 0.960938\n", + "step: 140, loss: 12.859011, accuracy: 0.968750\n", + "step: 150, loss: 9.110849, accuracy: 0.976562\n", + "step: 160, loss: 5.832032, accuracy: 0.984375\n", + "step: 170, loss: 6.996647, accuracy: 0.968750\n", + "step: 180, loss: 5.325038, accuracy: 0.992188\n", + "step: 190, loss: 8.866342, accuracy: 0.984375\n", + "step: 200, loss: 2.626245, accuracy: 1.000000\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization(batch_x, batch_y)\n", + " \n", + " if step % display_step == 0:\n", + " pred = conv_net(batch_x)\n", + " loss = cross_entropy(pred, batch_y)\n", + " acc = accuracy(pred, batch_y)\n", + " print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Accuracy: 0.980000\n" + ] + } + ], + "source": [ + "# Test model on validation set.\n", + "pred = conv_net(x_test)\n", + "print(\"Test Accuracy: %f\" % accuracy(pred, y_test))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize predictions.\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 7\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 2\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 1\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 4\n" + ] + } + ], + "source": [ + "# Predict 5 images from validation set.\n", + "n_images = 5\n", + "test_images = x_test[:n_images]\n", + "predictions = conv_net(test_images)\n", + "\n", + "# Display image and model prediction.\n", + "for i in range(n_images):\n", + " plt.imshow(np.reshape(test_images[i], [28, 28]), cmap='gray')\n", + " plt.show()\n", + " print(\"Model prediction: %i\" % np.argmax(predictions.numpy()[i]))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/3_NeuralNetworks/dcgan.ipynb b/tensorflow_v2/notebooks/3_NeuralNetworks/dcgan.ipynb new file mode 100644 index 00000000..763b210f --- /dev/null +++ b/tensorflow_v2/notebooks/3_NeuralNetworks/dcgan.ipynb @@ -0,0 +1,381 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Deep Convolutional Generative Adversarial Network Example\n", + "\n", + "Build a deep convolutional generative adversarial network (DCGAN) to generate digit images from a noise distribution with TensorFlow v2.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## DCGAN Overview\n", + "\n", + "\"dcgan\"\n", + "\n", + "References:\n", + "- [Unsupervised representation learning with deep convolutional generative adversarial networks](https://arxiv.org/pdf/1511.06434). A Radford, L Metz, S Chintala, 2016.\n", + "- [Understanding the difficulty of training deep feedforward neural networks](http://proceedings.mlr.press/v9/glorot10a.html). X Glorot, Y Bengio. Aistats 9, 249-256\n", + "- [Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift](https://arxiv.org/abs/1502.03167). Sergey Ioffe, Christian Szegedy. 2015.\n", + "\n", + "## MNIST Dataset Overview\n", + "\n", + "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n", + "\n", + "In this example, each image will be converted to float32 and normalized from [0, 255] to [0, 1].\n", + "\n", + "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", + "\n", + "More info: http://yann.lecun.com/exdb/mnist/" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "from tensorflow.keras import Model, layers\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST Dataset parameters.\n", + "num_features = 784 # data features (img shape: 28*28).\n", + "\n", + "# Training parameters.\n", + "lr_generator = 0.0002\n", + "lr_discriminator = 0.0002\n", + "training_steps = 20000\n", + "batch_size = 128\n", + "display_step = 500\n", + "\n", + "# Network parameters.\n", + "noise_dim = 100 # Noise data points." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(10000).batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# Create TF Model.\n", + "class Generator(Model):\n", + " # Set layers.\n", + " def __init__(self):\n", + " super(Generator, self).__init__()\n", + " self.fc1 = layers.Dense(7 * 7 * 128)\n", + " self.bn1 = layers.BatchNormalization()\n", + " self.conv2tr1 = layers.Conv2DTranspose(64, 5, strides=2, padding='SAME')\n", + " self.bn2 = layers.BatchNormalization()\n", + " self.conv2tr2 = layers.Conv2DTranspose(1, 5, strides=2, padding='SAME')\n", + "\n", + " # Set forward pass.\n", + " def call(self, x, is_training=False):\n", + " x = self.fc1(x)\n", + " x = self.bn1(x, training=is_training)\n", + " x = tf.nn.leaky_relu(x)\n", + " # Reshape to a 4-D array of images: (batch, height, width, channels)\n", + " # New shape: (batch, 7, 7, 128)\n", + " x = tf.reshape(x, shape=[-1, 7, 7, 128])\n", + " # Deconvolution, image shape: (batch, 14, 14, 64)\n", + " x = self.conv2tr1(x)\n", + " x = self.bn2(x, training=is_training)\n", + " x = tf.nn.leaky_relu(x)\n", + " # Deconvolution, image shape: (batch, 28, 28, 1)\n", + " x = self.conv2tr2(x)\n", + " x = tf.nn.tanh(x)\n", + " return x\n", + "\n", + "# Generator Network\n", + "# Input: Noise, Output: Image\n", + "# Note that batch normalization has different behavior at training and inference time,\n", + "# we then use a placeholder to indicates the layer if we are training or not.\n", + "class Discriminator(Model):\n", + " # Set layers.\n", + " def __init__(self):\n", + " super(Discriminator, self).__init__()\n", + " self.conv1 = layers.Conv2D(64, 5, strides=2, padding='SAME')\n", + " self.bn1 = layers.BatchNormalization()\n", + " self.conv2 = layers.Conv2D(128, 5, strides=2, padding='SAME')\n", + " self.bn2 = layers.BatchNormalization()\n", + " self.flatten = layers.Flatten()\n", + " self.fc1 = layers.Dense(1024)\n", + " self.bn3 = layers.BatchNormalization()\n", + " self.fc2 = layers.Dense(2)\n", + "\n", + " # Set forward pass.\n", + " def call(self, x, is_training=False):\n", + " x = tf.reshape(x, [-1, 28, 28, 1])\n", + " x = self.conv1(x)\n", + " x = self.bn1(x, training=is_training)\n", + " x = tf.nn.leaky_relu(x)\n", + " x = self.conv2(x)\n", + " x = self.bn2(x, training=is_training)\n", + " x = tf.nn.leaky_relu(x)\n", + " x = self.flatten(x)\n", + " x = self.fc1(x)\n", + " x = self.bn3(x, training=is_training)\n", + " x = tf.nn.leaky_relu(x)\n", + " return self.fc2(x)\n", + "\n", + "# Build neural network model.\n", + "generator = Generator()\n", + "discriminator = Discriminator()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# Losses.\n", + "def generator_loss(reconstructed_image):\n", + " gen_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n", + " logits=reconstructed_image, labels=tf.ones([batch_size], dtype=tf.int32)))\n", + " return gen_loss\n", + "\n", + "def discriminator_loss(disc_fake, disc_real):\n", + " disc_loss_real = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n", + " logits=disc_real, labels=tf.ones([batch_size], dtype=tf.int32)))\n", + " disc_loss_fake = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n", + " logits=disc_fake, labels=tf.zeros([batch_size], dtype=tf.int32)))\n", + " return disc_loss_real + disc_loss_fake\n", + "\n", + "# Optimizers.\n", + "optimizer_gen = tf.optimizers.Adam(learning_rate=lr_generator)#, beta_1=0.5, beta_2=0.999)\n", + "optimizer_disc = tf.optimizers.Adam(learning_rate=lr_discriminator)#, beta_1=0.5, beta_2=0.999)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. Inputs: real image and noise.\n", + "def run_optimization(real_images):\n", + " \n", + " # Rescale to [-1, 1], the input range of the discriminator\n", + " real_images = real_images * 2. - 1.\n", + "\n", + " # Generate noise.\n", + " noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n", + " \n", + " with tf.GradientTape() as g:\n", + " \n", + " fake_images = generator(noise, is_training=True)\n", + " disc_fake = discriminator(fake_images, is_training=True)\n", + " disc_real = discriminator(real_images, is_training=True)\n", + "\n", + " disc_loss = discriminator_loss(disc_fake, disc_real)\n", + " \n", + " # Training Variables for each optimizer\n", + " gradients_disc = g.gradient(disc_loss, discriminator.trainable_variables)\n", + " optimizer_disc.apply_gradients(zip(gradients_disc, discriminator.trainable_variables))\n", + " \n", + " # Generate noise.\n", + " noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n", + " \n", + " with tf.GradientTape() as g:\n", + " \n", + " fake_images = generator(noise, is_training=True)\n", + " disc_fake = discriminator(fake_images, is_training=True)\n", + "\n", + " gen_loss = generator_loss(disc_fake)\n", + " \n", + " gradients_gen = g.gradient(gen_loss, generator.trainable_variables)\n", + " optimizer_gen.apply_gradients(zip(gradients_gen, generator.trainable_variables))\n", + " \n", + " return gen_loss, disc_loss" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "initial: gen_loss: 0.694535, disc_loss: 1.403878\n", + "step: 500, gen_loss: 2.154825, disc_loss: 0.429040\n", + "step: 1000, gen_loss: 2.103464, disc_loss: 0.502638\n", + "step: 1500, gen_loss: 2.282369, disc_loss: 0.572065\n", + "step: 2000, gen_loss: 2.711531, disc_loss: 0.341791\n", + "step: 2500, gen_loss: 2.835576, disc_loss: 0.322356\n", + "step: 3000, gen_loss: 2.970127, disc_loss: 0.275483\n", + "step: 3500, gen_loss: 2.836321, disc_loss: 0.190680\n", + "step: 4000, gen_loss: 2.892855, disc_loss: 0.337681\n", + "step: 4500, gen_loss: 2.948962, disc_loss: 0.329322\n", + "step: 5000, gen_loss: 3.799061, disc_loss: 0.327288\n", + "step: 5500, gen_loss: 4.090328, disc_loss: 0.274685\n", + "step: 6000, gen_loss: 4.343777, disc_loss: 0.155223\n", + "step: 6500, gen_loss: 3.806556, disc_loss: 0.155855\n", + "step: 7000, gen_loss: 4.827947, disc_loss: 0.078372\n", + "step: 7500, gen_loss: 3.708949, disc_loss: 0.140979\n", + "step: 8000, gen_loss: 5.250406, disc_loss: 0.164736\n", + "step: 8500, gen_loss: 5.491106, disc_loss: 0.110080\n", + "step: 9000, gen_loss: 4.391072, disc_loss: 0.100240\n", + "step: 9500, gen_loss: 5.074200, disc_loss: 0.105567\n", + "step: 10000, gen_loss: 6.077592, disc_loss: 0.215981\n", + "step: 10500, gen_loss: 4.468120, disc_loss: 0.099412\n", + "step: 11000, gen_loss: 5.887744, disc_loss: 0.093534\n", + "step: 11500, gen_loss: 5.656942, disc_loss: 0.075079\n", + "step: 12000, gen_loss: 4.752551, disc_loss: 0.092505\n", + "step: 12500, gen_loss: 5.682284, disc_loss: 0.077406\n", + "step: 13000, gen_loss: 5.386811, disc_loss: 0.101259\n", + "step: 13500, gen_loss: 4.646158, disc_loss: 0.039680\n", + "step: 14000, gen_loss: 5.698145, disc_loss: 0.083461\n", + "step: 14500, gen_loss: 4.513465, disc_loss: 0.077946\n", + "step: 15000, gen_loss: 5.586041, disc_loss: 0.039668\n", + "step: 15500, gen_loss: 6.316034, disc_loss: 0.084526\n", + "step: 16000, gen_loss: 7.120735, disc_loss: 0.070267\n", + "step: 16500, gen_loss: 5.511638, disc_loss: 0.053082\n", + "step: 17000, gen_loss: 8.176781, disc_loss: 0.029212\n", + "step: 17500, gen_loss: 6.144678, disc_loss: 0.140460\n", + "step: 18000, gen_loss: 5.583275, disc_loss: 0.077311\n", + "step: 18500, gen_loss: 5.840647, disc_loss: 0.042103\n", + "step: 19000, gen_loss: 7.111396, disc_loss: 0.030435\n", + "step: 19500, gen_loss: 4.391251, disc_loss: 0.043656\n", + "step: 20000, gen_loss: 6.271616, disc_loss: 0.040568\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, _) in enumerate(train_data.take(training_steps + 1)):\n", + " \n", + " if step == 0:\n", + " # Generate noise.\n", + " noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n", + " gen_loss = generator_loss(discriminator(generator(noise)))\n", + " disc_loss = discriminator_loss(discriminator(batch_x), discriminator(generator(noise)))\n", + " print(\"initial: gen_loss: %f, disc_loss: %f\" % (gen_loss, disc_loss))\n", + " continue\n", + " \n", + " # Run the optimization.\n", + " gen_loss, disc_loss = run_optimization(batch_x)\n", + " \n", + " if step % display_step == 0:\n", + " print(\"step: %i, gen_loss: %f, disc_loss: %f\" % (step, gen_loss, disc_loss))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize predictions.\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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G6BUAfeJPv300/o8Wuzu//7v/4Dc/uzNN98E8vnj+1XelljedNNNATjhhBMAYg+nl4QiD4IgaHMmeUVuO6ddd90VgBtvvBHISsJmzPrKq4RqbqWVVgKyAlf17LbbbgAcddRRQDWaFGy00UYAXHXV+EUxyw2zxdZ2+pOfffZZAIYNGwZki6NKOAc2zn744YeBbOltu+22QFa5a6655jjvO+uss8Z5fxXw3Nh+++3rUTeipdFVWexyqQOtrssvvxyAPfbYA8jNGoKJIxR5EARBm9OnVm/9RTMbS7h7fvPNNwNw4YUXAnDXXXcB49dt2HDDDQH43e9+B1SjFocqbuuttwZyyzfbhtmqytZvVcAuULPPPjuQFRx0X5jKn6tytSz0kV988cVANVva3XTTTUCO3thggwm3sB00aBCQfeq29asCWqkzzjhj3RrUYvW582qmthaJDY4936644gogn2//7//9PwB22mmnxh5EL/CYbrjhBgCeeuopIFsT7vfYCm6uueaqr0nbwun7n5gibM1svhwEQRC0mAHvI/duusMOOwBw6aWXAtkPazSKu+i77LILAD/+8Y+BHD9eBbSeVAb6U7UejByokhIX49c7o6y4xefOoXOm+nvwwQeB7G/VIqkSK664IpDVWndoTc0333wAvP322/W12WpUlY888khdhWod6fM3WsfolXINpaWXrrUquOaaa4A8l1Wsl6K1YEPs008/Hchj9rG8p/PGG2/UI628frz11lsALLnkkgBMO+20APzmN78BYO655+7TWEORB0EQtDkDVpHrR15mmWWA7NdS1arE999/f6C2Ew/VjIAQ/cxmbI4cORLIfshNNtmk81+sAPqzVSjGSU8xxRR1dVL2H/q6Mdb6aI2YeP311wF4+eWXAVhkkUUaNfxeow+5pxx00EFAXqc9VfLN5Fvf+tZ4Vt+///1voPsMzddeew3IVpTHWYWIKtEC1F9vfoPWhhbS8ssvD9QsFMjr8u23366v1eeffx7I0XFa035PPv75z38Get/IJBR5EARBmzPgFLmxxXvttReQfXUqo9133x2AQw89FIAZZpgBqHZLL31wxotrXXg3Nya7ysdQjgGXCY3Z47777rsBWGeddQD44IMPgOzD1Fe+4IILAuP7ZVuJirO7uTn++OMBuOWWWwAYPHgwUI0oqZ7QnRJ3roy0cm5Vt+uu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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Testing\n", + "# Generate images from noise, using the generator network.\n", + "n = 6\n", + "canvas = np.empty((28 * n, 28 * n))\n", + "for i in range(n):\n", + " # Noise input.\n", + " z = np.random.normal(-1., 1., size=[n, noise_dim]).astype(np.float32)\n", + " # Generate image from noise.\n", + " g = generator(z).numpy()\n", + " # Rescale to original [0, 1]\n", + " g = (g + 1.) / 2\n", + " # Reverse colours for better display\n", + " g = -1 * (g - 1)\n", + " for j in range(n):\n", + " # Draw the generated digits\n", + " canvas[i * 28:(i + 1) * 28, j * 28:(j + 1) * 28] = g[j].reshape([28, 28])\n", + "\n", + "plt.figure(figsize=(n, n))\n", + "plt.imshow(canvas, origin=\"upper\", cmap=\"gray\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network.ipynb b/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network.ipynb new file mode 100644 index 00000000..2bcd1860 --- /dev/null +++ b/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network.ipynb @@ -0,0 +1,381 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Neural Network Example\n", + "\n", + "Build a 2-hidden layers fully connected neural network (a.k.a multilayer perceptron) with TensorFlow v2.\n", + "\n", + "This example is using a low-level approach to better understand all mechanics behind building neural networks and the training process.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Neural Network Overview\n", + "\n", + "\"nn\"\n", + "\n", + "## MNIST Dataset Overview\n", + "\n", + "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n", + "\n", + "In this example, each image will be converted to float32, normalized to [0, 1] and flattened to a 1-D array of 784 features (28*28).\n", + "\n", + "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", + "\n", + "More info: http://yann.lecun.com/exdb/mnist/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "from tensorflow.keras import Model, layers\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST dataset parameters.\n", + "num_classes = 10 # total classes (0-9 digits).\n", + "num_features = 784 # data features (img shape: 28*28).\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.1\n", + "training_steps = 2000\n", + "batch_size = 256\n", + "display_step = 100\n", + "\n", + "# Network parameters.\n", + "n_hidden_1 = 128 # 1st layer number of neurons.\n", + "n_hidden_2 = 256 # 2nd layer number of neurons." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", + "# Flatten images to 1-D vector of 784 features (28*28).\n", + "x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Create TF Model.\n", + "class NeuralNet(Model):\n", + " # Set layers.\n", + " def __init__(self):\n", + " super(NeuralNet, self).__init__()\n", + " # First fully-connected hidden layer.\n", + " self.fc1 = layers.Dense(n_hidden_1, activation=tf.nn.relu)\n", + " # First fully-connected hidden layer.\n", + " self.fc2 = layers.Dense(n_hidden_2, activation=tf.nn.relu)\n", + " # Second fully-connecter hidden layer.\n", + " self.out = layers.Dense(num_classes, activation=tf.nn.softmax)\n", + "\n", + " # Set forward pass.\n", + " def call(self, x, is_training=False):\n", + " x = self.fc1(x)\n", + " x = self.out(x)\n", + " if not is_training:\n", + " # tf cross entropy expect logits without softmax, so only\n", + " # apply softmax when not training.\n", + " x = tf.nn.softmax(x)\n", + " return x\n", + "\n", + "# Build neural network model.\n", + "neural_net = NeuralNet()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Cross-Entropy Loss.\n", + "# Note that this will apply 'softmax' to the logits.\n", + "def cross_entropy_loss(x, y):\n", + " # Convert labels to int 64 for tf cross-entropy function.\n", + " y = tf.cast(y, tf.int64)\n", + " # Apply softmax to logits and compute cross-entropy.\n", + " loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=x)\n", + " # Average loss across the batch.\n", + " return tf.reduce_mean(loss)\n", + "\n", + "# Accuracy metric.\n", + "def accuracy(y_pred, y_true):\n", + " # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n", + " correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n", + " return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis=-1)\n", + "\n", + "# Stochastic gradient descent optimizer.\n", + "optimizer = tf.optimizers.SGD(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x, y):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " # Forward pass.\n", + " pred = neural_net(x, is_training=True)\n", + " # Compute loss.\n", + " loss = cross_entropy_loss(pred, y)\n", + " \n", + " # Variables to update, i.e. trainable variables.\n", + " trainable_variables = neural_net.trainable_variables\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, trainable_variables)\n", + " \n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, trainable_variables))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 100, loss: 2.031049, accuracy: 0.535156\n", + "step: 200, loss: 1.821917, accuracy: 0.722656\n", + "step: 300, loss: 1.764789, accuracy: 0.753906\n", + "step: 400, loss: 1.677593, accuracy: 0.859375\n", + "step: 500, loss: 1.643402, accuracy: 0.867188\n", + "step: 600, loss: 1.645116, accuracy: 0.859375\n", + "step: 700, loss: 1.618012, accuracy: 0.878906\n", + "step: 800, loss: 1.618097, accuracy: 0.878906\n", + "step: 900, loss: 1.616565, accuracy: 0.875000\n", + "step: 1000, loss: 1.599962, accuracy: 0.894531\n", + "step: 1100, loss: 1.593849, accuracy: 0.910156\n", + "step: 1200, loss: 1.594491, accuracy: 0.886719\n", + "step: 1300, loss: 1.622147, accuracy: 0.859375\n", + "step: 1400, loss: 1.547483, accuracy: 0.937500\n", + "step: 1500, loss: 1.581775, accuracy: 0.898438\n", + "step: 1600, loss: 1.555893, accuracy: 0.929688\n", + "step: 1700, loss: 1.578076, accuracy: 0.898438\n", + "step: 1800, loss: 1.584776, accuracy: 0.882812\n", + "step: 1900, loss: 1.563029, accuracy: 0.921875\n", + "step: 2000, loss: 1.569637, accuracy: 0.902344\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization(batch_x, batch_y)\n", + " \n", + " if step % display_step == 0:\n", + " pred = neural_net(batch_x, is_training=True)\n", + " loss = cross_entropy_loss(pred, batch_y)\n", + " acc = accuracy(pred, batch_y)\n", + " print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Accuracy: 0.920700\n" + ] + } + ], + "source": [ + "# Test model on validation set.\n", + "pred = neural_net(x_test, is_training=False)\n", + "print(\"Test Accuracy: %f\" % accuracy(pred, y_test))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize predictions.\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 7\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 2\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 1\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 4\n" + ] + } + ], + "source": [ + "# Predict 5 images from validation set.\n", + "n_images = 5\n", + "test_images = x_test[:n_images]\n", + "predictions = neural_net(test_images)\n", + "\n", + "# Display image and model prediction.\n", + "for i in range(n_images):\n", + " plt.imshow(np.reshape(test_images[i], [28, 28]), cmap='gray')\n", + " plt.show()\n", + " print(\"Model prediction: %i\" % np.argmax(predictions.numpy()[i]))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network_raw.ipynb b/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network_raw.ipynb new file mode 100644 index 00000000..bbec2f13 --- /dev/null +++ b/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network_raw.ipynb @@ -0,0 +1,402 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Neural Network Example\n", + "\n", + "Build a 2-hidden layers fully connected neural network (a.k.a multilayer perceptron) with TensorFlow v2.\n", + "\n", + "This example is using a low-level approach to better understand all mechanics behind building neural networks and the training process.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Neural Network Overview\n", + "\n", + "\"nn\"\n", + "\n", + "## MNIST Dataset Overview\n", + "\n", + "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n", + "\n", + "In this example, each image will be converted to float32, normalized to [0, 1] and flattened to a 1-D array of 784 features (28*28).\n", + "\n", + "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", + "\n", + "More info: http://yann.lecun.com/exdb/mnist/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST dataset parameters.\n", + "num_classes = 10 # total classes (0-9 digits).\n", + "num_features = 784 # data features (img shape: 28*28).\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.001\n", + "training_steps = 3000\n", + "batch_size = 256\n", + "display_step = 100\n", + "\n", + "# Network parameters.\n", + "n_hidden_1 = 128 # 1st layer number of neurons.\n", + "n_hidden_2 = 256 # 2nd layer number of neurons." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", + "# Flatten images to 1-D vector of 784 features (28*28).\n", + "x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Store layers weight & bias\n", + "\n", + "# A random value generator to initialize weights.\n", + "random_normal = tf.initializers.RandomNormal()\n", + "\n", + "weights = {\n", + " 'h1': tf.Variable(random_normal([num_features, n_hidden_1])),\n", + " 'h2': tf.Variable(random_normal([n_hidden_1, n_hidden_2])),\n", + " 'out': tf.Variable(random_normal([n_hidden_2, num_classes]))\n", + "}\n", + "biases = {\n", + " 'b1': tf.Variable(tf.zeros([n_hidden_1])),\n", + " 'b2': tf.Variable(tf.zeros([n_hidden_2])),\n", + " 'out': tf.Variable(tf.zeros([num_classes]))\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Create model.\n", + "def neural_net(x):\n", + " # Hidden fully connected layer with 128 neurons.\n", + " layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])\n", + " # Apply sigmoid to layer_1 output for non-linearity.\n", + " layer_1 = tf.nn.sigmoid(layer_1)\n", + " \n", + " # Hidden fully connected layer with 256 neurons.\n", + " layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])\n", + " # Apply sigmoid to layer_2 output for non-linearity.\n", + " layer_2 = tf.nn.sigmoid(layer_2)\n", + " \n", + " # Output fully connected layer with a neuron for each class.\n", + " out_layer = tf.matmul(layer_2, weights['out']) + biases['out']\n", + " # Apply softmax to normalize the logits to a probability distribution.\n", + " return tf.nn.softmax(out_layer)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Cross-Entropy loss function.\n", + "def cross_entropy(y_pred, y_true):\n", + " # Encode label to a one hot vector.\n", + " y_true = tf.one_hot(y_true, depth=num_classes)\n", + " # Clip prediction values to avoid log(0) error.\n", + " y_pred = tf.clip_by_value(y_pred, 1e-9, 1.)\n", + " # Compute cross-entropy.\n", + " return tf.reduce_mean(-tf.reduce_sum(y_true * tf.math.log(y_pred)))\n", + "\n", + "# Accuracy metric.\n", + "def accuracy(y_pred, y_true):\n", + " # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n", + " correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n", + " return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis=-1)\n", + "\n", + "# Stochastic gradient descent optimizer.\n", + "optimizer = tf.optimizers.SGD(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x, y):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " pred = neural_net(x)\n", + " loss = cross_entropy(pred, y)\n", + " \n", + " # Variables to update, i.e. trainable variables.\n", + " trainable_variables = weights.values() + biases.values()\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, trainable_variables)\n", + " \n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, trainable_variables))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 100, loss: 567.292969, accuracy: 0.136719\n", + "step: 200, loss: 398.614929, accuracy: 0.562500\n", + "step: 300, loss: 226.743774, accuracy: 0.753906\n", + "step: 400, loss: 193.384521, accuracy: 0.777344\n", + "step: 500, loss: 138.649963, accuracy: 0.886719\n", + "step: 600, loss: 109.713669, accuracy: 0.898438\n", + "step: 700, loss: 90.397217, accuracy: 0.906250\n", + "step: 800, loss: 104.545380, accuracy: 0.894531\n", + "step: 900, loss: 94.204697, accuracy: 0.890625\n", + "step: 1000, loss: 81.660645, accuracy: 0.906250\n", + "step: 1100, loss: 81.237137, accuracy: 0.902344\n", + "step: 1200, loss: 65.776703, accuracy: 0.925781\n", + "step: 1300, loss: 94.195862, accuracy: 0.910156\n", + "step: 1400, loss: 79.425507, accuracy: 0.917969\n", + "step: 1500, loss: 93.508163, accuracy: 0.914062\n", + "step: 1600, loss: 88.912506, accuracy: 0.917969\n", + "step: 1700, loss: 79.033607, accuracy: 0.929688\n", + "step: 1800, loss: 65.788315, accuracy: 0.898438\n", + "step: 1900, loss: 73.462387, accuracy: 0.937500\n", + "step: 2000, loss: 59.309540, accuracy: 0.917969\n", + "step: 2100, loss: 67.014008, accuracy: 0.917969\n", + "step: 2200, loss: 48.297115, accuracy: 0.949219\n", + "step: 2300, loss: 64.523148, accuracy: 0.910156\n", + "step: 2400, loss: 72.989517, accuracy: 0.925781\n", + "step: 2500, loss: 57.588585, accuracy: 0.929688\n", + "step: 2600, loss: 44.957100, accuracy: 0.960938\n", + "step: 2700, loss: 59.788242, accuracy: 0.937500\n", + "step: 2800, loss: 63.581337, accuracy: 0.937500\n", + "step: 2900, loss: 53.471252, accuracy: 0.941406\n", + "step: 3000, loss: 43.869728, accuracy: 0.949219\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization(batch_x, batch_y)\n", + " \n", + " if step % display_step == 0:\n", + " pred = neural_net(batch_x)\n", + " loss = cross_entropy(pred, batch_y)\n", + " acc = accuracy(pred, batch_y)\n", + " print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Accuracy: 0.936800\n" + ] + } + ], + "source": [ + "# Test model on validation set.\n", + "pred = neural_net(x_test)\n", + "print(\"Test Accuracy: %f\" % accuracy(pred, y_test))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize predictions.\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 7\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 2\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 1\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model prediction: 4\n" + ] + } + ], + "source": [ + "# Predict 5 images from validation set.\n", + "n_images = 5\n", + "test_images = x_test[:n_images]\n", + "predictions = neural_net(test_images)\n", + "\n", + "# Display image and model prediction.\n", + "for i in range(n_images):\n", + " plt.imshow(np.reshape(test_images[i], [28, 28]), cmap='gray')\n", + " plt.show()\n", + " print(\"Model prediction: %i\" % np.argmax(predictions.numpy()[i]))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/4_Utils/build_custom_layers.ipynb b/tensorflow_v2/notebooks/4_Utils/build_custom_layers.ipynb new file mode 100644 index 00000000..760a1c9c --- /dev/null +++ b/tensorflow_v2/notebooks/4_Utils/build_custom_layers.ipynb @@ -0,0 +1,304 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Build Custom Layers & Modules\n", + "\n", + "Build custom layers and modules with TensorFlow v2.\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "from tensorflow.keras import Model, layers\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST dataset parameters.\n", + "num_classes = 10 # 0 to 9 digits\n", + "num_features = 784 # 28*28\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.01\n", + "training_steps = 500\n", + "batch_size = 256\n", + "display_step = 50" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", + "# Flatten images to 1-D vector of 784 features (28*28).\n", + "x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create a custom layer\n", + "\n", + "Build a custom layer with inner-variables." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a custom layer, extending TF 'Layer' class.\n", + "# Layer compute: y = relu(W * x + b)\n", + "class CustomLayer1(layers.Layer):\n", + " \n", + " # Layer arguments.\n", + " def __init__(self, num_units, **kwargs):\n", + " # Store the number of units (neurons).\n", + " self.num_units = num_units\n", + " super(CustomLayer1, self).__init__(**kwargs)\n", + " \n", + " def build(self, input_shape):\n", + " # Note: a custom layer can also include any other TF 'layers'.\n", + " shape = tf.TensorShape((input_shape[1], self.num_units))\n", + " # Create weight variables for this layer.\n", + " self.weight = self.add_weight(name='W',\n", + " shape=shape,\n", + " initializer=tf.initializers.RandomNormal,\n", + " trainable=True)\n", + " self.bias = self.add_weight(name='b',\n", + " shape=[self.num_units])\n", + " # Make sure to call the `build` method at the end\n", + " super(CustomLayer1, self).build(input_shape)\n", + "\n", + " def call(self, inputs):\n", + " x = tf.matmul(inputs, self.weight)\n", + " x = x + self.bias\n", + " return tf.nn.relu(x)\n", + "\n", + " def get_config(self):\n", + " base_config = super(CustomLayer1, self).get_config()\n", + " base_config['num_units'] = self.num_units\n", + " return base_config" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create another custom layer\n", + "\n", + "Build another custom layer with inner TF 'layers'." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a custom layer, extending TF 'Layer' class.\n", + "# Custom layer: 2 Fully-Connected layers with residual connection.\n", + "class CustomLayer2(layers.Layer):\n", + " \n", + " # Layer arguments.\n", + " def __init__(self, num_units, **kwargs):\n", + " self.num_units = num_units\n", + " super(CustomLayer2, self).__init__(**kwargs)\n", + " \n", + " def build(self, input_shape):\n", + " shape = tf.TensorShape((input_shape[1], self.num_units))\n", + " \n", + " self.inner_layer1 = layers.Dense(1)\n", + " self.inner_layer2 = layers.Dense(self.num_units)\n", + " \n", + " # Make sure to call the `build` method at the end\n", + " super(CustomLayer2, self).build(input_shape)\n", + "\n", + " def call(self, inputs):\n", + " x = self.inner_layer1(inputs)\n", + " x = tf.nn.relu(x)\n", + " x = self.inner_layer2(x)\n", + " return x + inputs\n", + "\n", + " def get_config(self):\n", + " base_config = super(CustomLayer2, self).get_config()\n", + " base_config['num_units'] = self.num_units\n", + " return base_config" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Build Model" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Create TF Model.\n", + "class CustomNet(Model):\n", + " \n", + " def __init__(self):\n", + " super(CustomNet, self).__init__()\n", + " # Use custom layers created above.\n", + " self.layer1 = CustomLayer1(64)\n", + " self.layer2 = CustomLayer2(64)\n", + " self.out = layers.Dense(num_classes, activation=tf.nn.softmax)\n", + "\n", + " # Set forward pass.\n", + " def __call__(self, x, is_training=False):\n", + " x = self.layer1(x)\n", + " x = tf.nn.relu(x)\n", + " x = self.layer2(x)\n", + " if not is_training:\n", + " # tf cross entropy expect logits without softmax, so only\n", + " # apply softmax when not training.\n", + " x = tf.nn.softmax(x)\n", + " return x\n", + "\n", + "# Build neural network model.\n", + "custom_net = CustomNet()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Cross-Entropy loss function.\n", + "def cross_entropy(y_pred, y_true):\n", + " y_true = tf.cast(y_true, tf.int64)\n", + " crossentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y_true, logits=y_pred)\n", + " return tf.reduce_mean(crossentropy)\n", + "\n", + "# Accuracy metric.\n", + "def accuracy(y_pred, y_true):\n", + " # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n", + " correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n", + " return tf.reduce_mean(tf.cast(correct_prediction, tf.float32))\n", + "\n", + "# Adam optimizer.\n", + "optimizer = tf.optimizers.Adam(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x, y):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " pred = custom_net(x, is_training=True)\n", + " loss = cross_entropy(pred, y)\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, custom_net.trainable_variables)\n", + "\n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, custom_net.trainable_variables))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 50, loss: 3.363096, accuracy: 0.902344\n", + "step: 100, loss: 3.344931, accuracy: 0.910156\n", + "step: 150, loss: 3.336300, accuracy: 0.914062\n", + "step: 200, loss: 3.318396, accuracy: 0.925781\n", + "step: 250, loss: 3.300045, accuracy: 0.937500\n", + "step: 300, loss: 3.335487, accuracy: 0.898438\n", + "step: 350, loss: 3.330979, accuracy: 0.914062\n", + "step: 400, loss: 3.298509, accuracy: 0.921875\n", + "step: 450, loss: 3.278253, accuracy: 0.953125\n", + "step: 500, loss: 3.285335, accuracy: 0.945312\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization(batch_x, batch_y)\n", + " \n", + " if step % display_step == 0:\n", + " pred = custom_net(batch_x, is_training=False)\n", + " loss = cross_entropy(pred, batch_y)\n", + " acc = accuracy(pred, batch_y)\n", + " print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tensorflow_v2/notebooks/4_Utils/save_restore_model.ipynb b/tensorflow_v2/notebooks/4_Utils/save_restore_model.ipynb new file mode 100644 index 00000000..6235bbfe --- /dev/null +++ b/tensorflow_v2/notebooks/4_Utils/save_restore_model.ipynb @@ -0,0 +1,573 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Save & Restore a Model\n", + "\n", + "Save and Restore a model using TensorFlow v2. In this example, we will go over both low and high-level approaches: \n", + "- Low-level: TF Checkpoint.\n", + "- High-level: TF Module/Model saver.\n", + "\n", + "This example is using the MNIST database of handwritten digits as toy dataset\n", + "(http://yann.lecun.com/exdb/mnist/).\n", + "\n", + "- Author: Aymeric Damien\n", + "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import tensorflow as tf\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST dataset parameters.\n", + "num_classes = 10 # 0 to 9 digits\n", + "num_features = 784 # 28*28\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.01\n", + "training_steps = 1000\n", + "batch_size = 256\n", + "display_step = 50" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare MNIST data.\n", + "from tensorflow.keras.datasets import mnist\n", + "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", + "# Convert to float32.\n", + "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", + "# Flatten images to 1-D vector of 784 features (28*28).\n", + "x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])\n", + "# Normalize images value from [0, 255] to [0, 1].\n", + "x_train, x_test = x_train / 255., x_test / 255." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Use tf.data API to shuffle and batch data.\n", + "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", + "train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1) TF Checkpoint\n", + "\n", + "Basic logistic regression" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Weight of shape [784, 10], the 28*28 image features, and total number of classes.\n", + "W = tf.Variable(tf.random.normal([num_features, num_classes]), name=\"weight\")\n", + "# Bias of shape [10], the total number of classes.\n", + "b = tf.Variable(tf.zeros([num_classes]), name=\"bias\")\n", + "\n", + "# Logistic regression (Wx + b).\n", + "def logistic_regression(x):\n", + " # Apply softmax to normalize the logits to a probability distribution.\n", + " return tf.nn.softmax(tf.matmul(x, W) + b)\n", + "\n", + "# Cross-Entropy loss function.\n", + "def cross_entropy(y_pred, y_true):\n", + " # Encode label to a one hot vector.\n", + " y_true = tf.one_hot(y_true, depth=num_classes)\n", + " # Clip prediction values to avoid log(0) error.\n", + " y_pred = tf.clip_by_value(y_pred, 1e-9, 1.)\n", + " # Compute cross-entropy.\n", + " return tf.reduce_mean(-tf.reduce_sum(y_true * tf.math.log(y_pred)))\n", + "\n", + "# Accuracy metric.\n", + "def accuracy(y_pred, y_true):\n", + " # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n", + " correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n", + " return tf.reduce_mean(tf.cast(correct_prediction, tf.float32))\n", + "\n", + "# Adam optimizer.\n", + "optimizer = tf.optimizers.Adam(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x, y):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " pred = logistic_regression(x)\n", + " loss = cross_entropy(pred, y)\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, [W, b])\n", + "\n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, [W, b]))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 50, loss: 535.380981, accuracy: 0.656250\n", + "step: 100, loss: 354.681152, accuracy: 0.765625\n", + "step: 150, loss: 225.300934, accuracy: 0.785156\n", + "step: 200, loss: 163.948761, accuracy: 0.859375\n", + "step: 250, loss: 129.653534, accuracy: 0.878906\n", + "step: 300, loss: 170.743576, accuracy: 0.859375\n", + "step: 350, loss: 97.912575, accuracy: 0.910156\n", + "step: 400, loss: 144.119141, accuracy: 0.906250\n", + "step: 450, loss: 164.991943, accuracy: 0.875000\n", + "step: 500, loss: 145.191666, accuracy: 0.871094\n", + "step: 550, loss: 82.272644, accuracy: 0.925781\n", + "step: 600, loss: 149.180237, accuracy: 0.878906\n", + "step: 650, loss: 127.171280, accuracy: 0.871094\n", + "step: 700, loss: 116.045761, accuracy: 0.910156\n", + "step: 750, loss: 92.582680, accuracy: 0.906250\n", + "step: 800, loss: 108.238007, accuracy: 0.894531\n", + "step: 850, loss: 92.755638, accuracy: 0.894531\n", + "step: 900, loss: 69.131119, accuracy: 0.902344\n", + "step: 950, loss: 67.176285, accuracy: 0.921875\n", + "step: 1000, loss: 104.205658, accuracy: 0.890625\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization(batch_x, batch_y)\n", + " \n", + " if step % display_step == 0:\n", + " pred = logistic_regression(batch_x)\n", + " loss = cross_entropy(pred, batch_y)\n", + " acc = accuracy(pred, batch_y)\n", + " print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Save and Load with TF Checkpoint" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Save weights and optimizer variables.\n", + "# Create a dict of variables to save.\n", + "vars_to_save = {\"W\": W, \"b\": b, \"optimizer\": optimizer}\n", + "# TF Checkpoint, pass the dict as **kwargs.\n", + "checkpoint = tf.train.Checkpoint(**vars_to_save)\n", + "# TF CheckpointManager to manage saving parameters.\n", + "saver = tf.train.CheckpointManager(\n", + " checkpoint, directory=\"./tf-example\", max_to_keep=5)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'./tf-example/ckpt-1'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Save variables.\n", + "saver.save()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-0.09673191" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Check weight value.\n", + "np.mean(W.numpy())" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# Reset variables to test restore.\n", + "W = tf.Variable(tf.random.normal([num_features, num_classes]), name=\"weight\")\n", + "b = tf.Variable(tf.zeros([num_classes]), name=\"bias\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-0.0083419625" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Check resetted weight value.\n", + "np.mean(W.numpy())" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Set checkpoint to load data.\n", + "vars_to_load = {\"W\": W, \"b\": b, \"optimizer\": optimizer}\n", + "checkpoint = tf.train.Checkpoint(**vars_to_load)\n", + "# Restore variables from latest checkpoint.\n", + "latest_ckpt = tf.train.latest_checkpoint(\"./tf-example\")\n", + "checkpoint.restore(latest_ckpt)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-0.09673191" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Confirm that W has been correctly restored.\n", + "np.mean(W.numpy())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2) TF Model\n", + "\n", + "Basic neural network with TF Model" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "from tensorflow.keras import Model, layers" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# MNIST dataset parameters.\n", + "num_classes = 10 # 0 to 9 digits\n", + "num_features = 784 # 28*28\n", + "\n", + "# Training parameters.\n", + "learning_rate = 0.01\n", + "training_steps = 1000\n", + "batch_size = 256\n", + "display_step = 100" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "# Create TF Model.\n", + "class NeuralNet(Model):\n", + " # Set layers.\n", + " def __init__(self):\n", + " super(NeuralNet, self).__init__(name=\"NeuralNet\")\n", + " # First fully-connected hidden layer.\n", + " self.fc1 = layers.Dense(64, activation=tf.nn.relu)\n", + " # Second fully-connected hidden layer.\n", + " self.fc2 = layers.Dense(128, activation=tf.nn.relu)\n", + " # Third fully-connecter hidden layer.\n", + " self.out = layers.Dense(num_classes, activation=tf.nn.softmax)\n", + "\n", + " # Set forward pass.\n", + " def __call__(self, x, is_training=False):\n", + " x = self.fc1(x)\n", + " x = self.out(x)\n", + " if not is_training:\n", + " # tf cross entropy expect logits without softmax, so only\n", + " # apply softmax when not training.\n", + " x = tf.nn.softmax(x)\n", + " return x\n", + "\n", + "# Build neural network model.\n", + "neural_net = NeuralNet()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "# Cross-Entropy loss function.\n", + "def cross_entropy(y_pred, y_true):\n", + " y_true = tf.cast(y_true, tf.int64)\n", + " crossentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y_true, logits=y_pred)\n", + " return tf.reduce_mean(crossentropy)\n", + "\n", + "# Accuracy metric.\n", + "def accuracy(y_pred, y_true):\n", + " # Predicted class is the index of highest score in prediction vector (i.e. argmax).\n", + " correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))\n", + " return tf.reduce_mean(tf.cast(correct_prediction, tf.float32))\n", + "\n", + "# Adam optimizer.\n", + "optimizer = tf.optimizers.Adam(learning_rate)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# Optimization process. \n", + "def run_optimization(x, y):\n", + " # Wrap computation inside a GradientTape for automatic differentiation.\n", + " with tf.GradientTape() as g:\n", + " pred = neural_net(x, is_training=True)\n", + " loss = cross_entropy(pred, y)\n", + "\n", + " # Compute gradients.\n", + " gradients = g.gradient(loss, neural_net.trainable_variables)\n", + "\n", + " # Update W and b following gradients.\n", + " optimizer.apply_gradients(zip(gradients, neural_net.trainable_variables))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step: 100, loss: 2.188605, accuracy: 0.902344\n", + "step: 200, loss: 2.182990, accuracy: 0.929688\n", + "step: 300, loss: 2.180439, accuracy: 0.945312\n", + "step: 400, loss: 2.178496, accuracy: 0.957031\n", + "step: 500, loss: 2.177517, accuracy: 0.968750\n", + "step: 600, loss: 2.177163, accuracy: 0.968750\n", + "step: 700, loss: 2.177454, accuracy: 0.960938\n", + "step: 800, loss: 2.177589, accuracy: 0.960938\n", + "step: 900, loss: 2.176507, accuracy: 0.964844\n", + "step: 1000, loss: 2.177557, accuracy: 0.960938\n" + ] + } + ], + "source": [ + "# Run training for the given number of steps.\n", + "for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):\n", + " # Run the optimization to update W and b values.\n", + " run_optimization(batch_x, batch_y)\n", + " \n", + " if step % display_step == 0:\n", + " pred = neural_net(batch_x, is_training=False)\n", + " loss = cross_entropy(pred, batch_y)\n", + " acc = accuracy(pred, batch_y)\n", + " print(\"step: %i, loss: %f, accuracy: %f\" % (step, loss, acc))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Save and Load with TF Model" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "# Save TF model.\n", + "neural_net.save_weights(filepath=\"./tfmodel.ckpt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "accuracy: 0.101562\n" + ] + } + ], + "source": [ + "# Re-build neural network model with default values.\n", + "neural_net = NeuralNet()\n", + "# Test model performance.\n", + "pred = neural_net(batch_x)\n", + "print(\"accuracy: %f\" % accuracy(pred, batch_y))" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load saved weights.\n", + "neural_net.load_weights(filepath=\"./tfmodel.ckpt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "accuracy: 0.960938\n" + ] + } + ], + "source": [ + "# Test that weights loaded correctly.\n", + "pred = neural_net(batch_x)\n", + "print(\"accuracy: %f\" % accuracy(pred, batch_y))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From f28881d256f0231f2fd00e0d8b96d4cfc1e6e46b Mon Sep 17 00:00:00 2001 From: aymericdamien Date: Wed, 3 Apr 2019 23:24:52 -0700 Subject: [PATCH 2/4] update README --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 787b54cd..1394fff9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # TensorFlow Examples -This tutorial was designed for easily diving into TensorFlow, through examples. For readability, it includes both notebooks and source codes with explanation. +This tutorial was designed for easily diving into TensorFlow, through examples. For readability, it includes both notebooks and source codes with explanation for both TF v1 & v2. It is suitable for beginners who want to find clear and concise examples about TensorFlow. Besides the traditional 'raw' TensorFlow implementations, you can also find the latest TensorFlow API practices (such as `layers`, `estimator`, `dataset`, ...). -**Update (07/25/2018):** Add new examples (GBDT, Word2Vec) + TF1.9 compatibility! (TF v1.9+ recommended). +**Update (04/03/2019):** Starting to add [TensorFlow v2 examples](tensorflow_v2)! (more coming soon). *If you are using older TensorFlow version (0.11 and under), please take a [look here](https://github.com/aymericdamien/TensorFlow-Examples/tree/0.11).* @@ -86,6 +86,46 @@ pip install tensorflow_gpu For more details about TensorFlow installation, you can check [TensorFlow Installation Guide](https://www.tensorflow.org/install/) +## TensorFlow v2 Examples + +*** More examples to be added later... *** + +#### 1 - Introduction +- **Hello World** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/1_Introduction/helloworld.ipynb)). Very simple example to learn how to print "hello world" using TensorFlow v2. +- **Basic Operations** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/1_Introduction/basic_operations.ipynb)). A simple example that cover TensorFlow v2 basic operations. + +#### 2 - Basic Models +- **Linear Regression** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/2_BasicModels/linear_regression.ipynb)). Implement a Linear Regression with TensorFlow v2. +- **Logistic Regression** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/2_BasicModels/logistic_regression.ipynb)). Implement a Logistic Regression with TensorFlow v2. + +#### 3 - Neural Networks +##### Supervised + +- **Simple Neural Network** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network.ipynb)). Use TensorFlow v2 'layers' and 'model' API to build a simple neural network to classify MNIST digits dataset. +- **Simple Neural Network (low-level)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network_raw.ipynb)). Raw implementation of a simple neural network to classify MNIST digits dataset. +- **Convolutional Neural Network** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network.ipynb)). Use TensorFlow v2 'layers' and 'model' API to build a convolutional neural network to classify MNIST digits dataset. +- **Convolutional Neural Network (low-level)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network_raw.ipynb)). Raw implementation of a convolutional neural network to classify MNIST digits dataset. + +##### Unsupervised +- **Auto-Encoder** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/autoencoder.ipynb)). Build an auto-encoder to encode an image to a lower dimension and re-construct it. +- **DCGAN (Deep Convolutional Generative Adversarial Networks)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/tensorflow_v2/dcgan.ipynb). Build a Deep Convolutional Generative Adversarial Network (DCGAN) to generate images from noise. + +#### 4 - Utilities +- **Save and Restore a model** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/4_Utils/save_restore_model.ipynb)). Save and Restore a model with TensorFlow v2. +- **Build Custom Layers & Modules** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/4_Utils/build_costum_layers.ipynb)). Learn how to build your own layers / modules and integrate them into TensorFlow v2 Models. + +## TensorFlow v2 Installation + +To install TensorFlow v2, simply run: +``` +pip install tensorflow==2.0.0a0 +``` + +or (if you want GPU support): +``` +pip install tensorflow_gpu==2.0.0a0 +``` + ## More Examples The following examples are coming from [TFLearn](https://github.com/tflearn/tflearn), a library that provides a simplified interface for TensorFlow. You can have a look, there are many [examples](https://github.com/tflearn/tflearn/tree/master/examples) and [pre-built operations and layers](http://tflearn.org/doc_index/#api). From fb50d41cfe546a47c2cf19b45779bfc582a150a4 Mon Sep 17 00:00:00 2001 From: aymericdamien Date: Wed, 3 Apr 2019 23:26:59 -0700 Subject: [PATCH 3/4] add README --- tensorflow_v2/README.md | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tensorflow_v2/README.md b/tensorflow_v2/README.md index e69de29b..7dac4eac 100644 --- a/tensorflow_v2/README.md +++ b/tensorflow_v2/README.md @@ -0,0 +1,43 @@ +## TensorFlow v2 Examples + +*** More examples to be added later... *** + +#### 0 - Prerequisite +- [Introduction to Machine Learning](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/0_Prerequisite/ml_introduction.ipynb). +- [Introduction to MNIST Dataset](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/0_Prerequisite/mnist_dataset_intro.ipynb). + +#### 1 - Introduction +- **Hello World** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/1_Introduction/helloworld.ipynb)). Very simple example to learn how to print "hello world" using TensorFlow v2. +- **Basic Operations** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/1_Introduction/basic_operations.ipynb)). A simple example that cover TensorFlow v2 basic operations. + +#### 2 - Basic Models +- **Linear Regression** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/2_BasicModels/linear_regression.ipynb)). Implement a Linear Regression with TensorFlow v2. +- **Logistic Regression** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/2_BasicModels/logistic_regression.ipynb)). Implement a Logistic Regression with TensorFlow v2. + +#### 3 - Neural Networks +##### Supervised + +- **Simple Neural Network** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network.ipynb)). Use TensorFlow v2 'layers' and 'model' API to build a simple neural network to classify MNIST digits dataset. +- **Simple Neural Network (low-level)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network_raw.ipynb)). Raw implementation of a simple neural network to classify MNIST digits dataset. +- **Convolutional Neural Network** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network.ipynb)). Use TensorFlow v2 'layers' and 'model' API to build a convolutional neural network to classify MNIST digits dataset. +- **Convolutional Neural Network (low-level)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network_raw.ipynb)). Raw implementation of a convolutional neural network to classify MNIST digits dataset. + +##### Unsupervised +- **Auto-Encoder** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/autoencoder.ipynb)). Build an auto-encoder to encode an image to a lower dimension and re-construct it. +- **DCGAN (Deep Convolutional Generative Adversarial Networks)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/tensorflow_v2/dcgan.ipynb). Build a Deep Convolutional Generative Adversarial Network (DCGAN) to generate images from noise. + +#### 4 - Utilities +- **Save and Restore a model** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/4_Utils/save_restore_model.ipynb)). Save and Restore a model with TensorFlow v2. +- **Build Custom Layers & Modules** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/4_Utils/build_costum_layers.ipynb)). Learn how to build your own layers / modules and integrate them into TensorFlow v2 Models. + +## Installation + +To install TensorFlow v2, simply run: +``` +pip install tensorflow==2.0.0a0 +``` + +or (if you want GPU support): +``` +pip install tensorflow_gpu==2.0.0a0 +``` From 22150af03040641785d098013a341f0b88844664 Mon Sep 17 00:00:00 2001 From: aymericdamien Date: Wed, 3 Apr 2019 23:38:27 -0700 Subject: [PATCH 4/4] update doc --- README.md | 46 +++++------------------------------------ tensorflow_v2/README.md | 2 +- 2 files changed, 6 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 1394fff9..72f301b4 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,15 @@ It is suitable for beginners who want to find clear and concise examples about T - **Basic Operations on multi-GPU** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/6_MultiGPU/multigpu_basics.ipynb)) ([code](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/6_MultiGPU/multigpu_basics.py)). A simple example to introduce multi-GPU in TensorFlow. - **Train a Neural Network on multi-GPU** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/6_MultiGPU/multigpu_cnn.ipynb)) ([code](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/6_MultiGPU/multigpu_cnn.py)). A clear and simple TensorFlow implementation to train a convolutional neural network on multiple GPUs. +## TensorFlow v2 + +The tutorial index for TF v2 is available here: [TensorFlow v2 Examples](tensorflow_v2). + ## Dataset Some examples require MNIST dataset for training and testing. Don't worry, this dataset will automatically be downloaded when running examples. MNIST is a database of handwritten digits, for a quick description of that dataset, you can check [this notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/0_Prerequisite/mnist_dataset_intro.ipynb). -Official Website: [http://yann.lecun.com/exdb/mnist/](http://yann.lecun.com/exdb/mnist/) +Official Website: [http://yann.lecun.com/exdb/mnist/](http://yann.lecun.com/exdb/mnist/). ## Installation @@ -86,46 +90,6 @@ pip install tensorflow_gpu For more details about TensorFlow installation, you can check [TensorFlow Installation Guide](https://www.tensorflow.org/install/) -## TensorFlow v2 Examples - -*** More examples to be added later... *** - -#### 1 - Introduction -- **Hello World** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/1_Introduction/helloworld.ipynb)). Very simple example to learn how to print "hello world" using TensorFlow v2. -- **Basic Operations** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/1_Introduction/basic_operations.ipynb)). A simple example that cover TensorFlow v2 basic operations. - -#### 2 - Basic Models -- **Linear Regression** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/2_BasicModels/linear_regression.ipynb)). Implement a Linear Regression with TensorFlow v2. -- **Logistic Regression** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/2_BasicModels/logistic_regression.ipynb)). Implement a Logistic Regression with TensorFlow v2. - -#### 3 - Neural Networks -##### Supervised - -- **Simple Neural Network** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network.ipynb)). Use TensorFlow v2 'layers' and 'model' API to build a simple neural network to classify MNIST digits dataset. -- **Simple Neural Network (low-level)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/neural_network_raw.ipynb)). Raw implementation of a simple neural network to classify MNIST digits dataset. -- **Convolutional Neural Network** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network.ipynb)). Use TensorFlow v2 'layers' and 'model' API to build a convolutional neural network to classify MNIST digits dataset. -- **Convolutional Neural Network (low-level)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/convolutional_network_raw.ipynb)). Raw implementation of a convolutional neural network to classify MNIST digits dataset. - -##### Unsupervised -- **Auto-Encoder** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/autoencoder.ipynb)). Build an auto-encoder to encode an image to a lower dimension and re-construct it. -- **DCGAN (Deep Convolutional Generative Adversarial Networks)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/tensorflow_v2/dcgan.ipynb). Build a Deep Convolutional Generative Adversarial Network (DCGAN) to generate images from noise. - -#### 4 - Utilities -- **Save and Restore a model** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/4_Utils/save_restore_model.ipynb)). Save and Restore a model with TensorFlow v2. -- **Build Custom Layers & Modules** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/4_Utils/build_costum_layers.ipynb)). Learn how to build your own layers / modules and integrate them into TensorFlow v2 Models. - -## TensorFlow v2 Installation - -To install TensorFlow v2, simply run: -``` -pip install tensorflow==2.0.0a0 -``` - -or (if you want GPU support): -``` -pip install tensorflow_gpu==2.0.0a0 -``` - ## More Examples The following examples are coming from [TFLearn](https://github.com/tflearn/tflearn), a library that provides a simplified interface for TensorFlow. You can have a look, there are many [examples](https://github.com/tflearn/tflearn/tree/master/examples) and [pre-built operations and layers](http://tflearn.org/doc_index/#api). diff --git a/tensorflow_v2/README.md b/tensorflow_v2/README.md index 7dac4eac..dcb4a2cb 100644 --- a/tensorflow_v2/README.md +++ b/tensorflow_v2/README.md @@ -24,7 +24,7 @@ ##### Unsupervised - **Auto-Encoder** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/autoencoder.ipynb)). Build an auto-encoder to encode an image to a lower dimension and re-construct it. -- **DCGAN (Deep Convolutional Generative Adversarial Networks)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/tensorflow_v2/dcgan.ipynb). Build a Deep Convolutional Generative Adversarial Network (DCGAN) to generate images from noise. +- **DCGAN (Deep Convolutional Generative Adversarial Networks)** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/3_NeuralNetworks/tensorflow_v2/dcgan.ipynb)). Build a Deep Convolutional Generative Adversarial Network (DCGAN) to generate images from noise. #### 4 - Utilities - **Save and Restore a model** ([notebook](https://github.com/aymericdamien/TensorFlow-Examples/blob/master/tensorflow_v2/notebooks/4_Utils/save_restore_model.ipynb)). Save and Restore a model with TensorFlow v2.