{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Table of Contents

\n", "
" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:31.563865Z", "start_time": "2020-09-27T04:24:31.246587Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "" ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# code for loading the format for the notebook\n", "import os\n", "\n", "# path : store the current path to convert back to it later\n", "path = os.getcwd()\n", "os.chdir(os.path.join('..', '..', 'notebook_format'))\n", "\n", "from formats import load_style\n", "load_style(css_style='custom2.css', plot_style=False)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:32.787515Z", "start_time": "2020-09-27T04:24:31.567387Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ethen 2020-09-26 21:24:32 \n", "\n", "CPython 3.6.4\n", "IPython 7.15.0\n", "\n", "torch 1.6.0\n", "numpy 1.18.5\n", "matplotlib 3.1.0\n" ] } ], "source": [ "os.chdir(path)\n", "\n", "# 1. magic for inline plot\n", "# 2. magic to print version\n", "# 3. magic so that the notebook will reload external python modules\n", "# 4. magic to enable retina (high resolution) plots\n", "# https://gist.github.com/minrk/3301035\n", "%matplotlib inline\n", "%load_ext watermark\n", "%load_ext autoreload\n", "%autoreload 2\n", "%config InlineBackend.figure_format='retina'\n", "\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import torch\n", "import torch.nn as nn\n", "import torch.optim as optim\n", "import torch.nn.functional as F\n", "\n", "%watermark -a 'Ethen' -d -t -v -p torch,numpy,matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Pytorch Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```bash\n", "# installation on a mac\n", "# for more information on installation refer to\n", "# the following link:\n", "# http://pytorch.org/\n", "conda install pytorch torchvision -c pytorch \n", "```\n", "\n", "At its core, PyTorch provides two main features:\n", "\n", "- An n-dimensional Tensor, similar to numpy array but can run on GPUs. PyTorch provides many functions for operating on these Tensors, thus it can be used as a general purpose scientific computing tool.\n", "- Automatic differentiation for building and training neural networks.\n", "\n", "Let's dive in by looking at some examples:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Linear Regression" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:32.827980Z", "start_time": "2020-09-27T04:24:32.792161Z" } }, "outputs": [], "source": [ "# make up some trainig data and specify the type to be float, i.e. np.float32\n", "# We DO not recommend double, i.e. np.float64, especially on the GPU. GPUs have bad\n", "# double precision performance since they are optimized for float32\n", "X_train = np.asarray([3.3, 4.4, 5.5, 6.71, 6.93, 4.168, 9.779, 6.182, 7.59, \n", " 2.167, 7.042, 10.791, 5.313, 7.997, 5.654, 9.27, 3.1], dtype = np.float32)\n", "X_train = X_train.reshape(-1, 1)\n", "y_train = np.asarray([1.7, 2.76, 2.09, 3.19, 1.694, 1.573, 3.366, 2.596, 2.53, \n", " 1.221, 2.827, 3.465, 1.65, 2.904, 2.42, 2.94, 1.3], dtype = np.float32)\n", "y_train = y_train.reshape(-1, 1)\n", "\n", "# Convert numpy array to Pytorch Tensors\n", "X = torch.FloatTensor(X_train)\n", "y = torch.FloatTensor(y_train)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we start defining the linear regression model, recall that in linear regression, we are optimizing for the squared loss.\n", "\n", "\\begin{align}\n", "L = \\frac{1}{2}(y-(Xw + b))^2\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:32.869936Z", "start_time": "2020-09-27T04:24:32.832417Z" } }, "outputs": [], "source": [ "# with linear regression, we apply a linear transformation\n", "# to the incoming data, i.e. y = Xw + b, here we only have a 1\n", "# dimensional data, thus the feature size will be 1\n", "model = nn.Linear(in_features=1, out_features=1)\n", "\n", "# although we can write our own loss function, the nn module\n", "# also contains definitions of popular loss functions; here\n", "# we use the MSELoss, a.k.a the L2 loss, and size_average parameter\n", "# simply divides it with the number of examples\n", "criterion = nn.MSELoss(size_average=True)\n", "\n", "# Then we use the optim module to define an Optimizer that will update the weights of\n", "# the model for us. Here we will use SGD; but it contains many other\n", "# optimization algorithms. The first argument to the SGD constructor tells the\n", "# optimizer the parameters that it should update\n", "learning_rate = 0.01\n", "optimizer = optim.SGD(model.parameters(), lr=learning_rate)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:32.938585Z", "start_time": "2020-09-27T04:24:32.874108Z" } }, "outputs": [], "source": [ "# start the optimization process\n", "n_epochs = 100\n", "for _ in range(n_epochs):\n", " # torch accumulates the gradients, thus before running new things\n", " # use the optimizer object to zero all of the gradients for the\n", " # variables it will update (which are the learnable weights of the model),\n", " # think in terms of refreshing the gradients before doing the another round of update\n", " optimizer.zero_grad()\n", "\n", " # forward pass: compute predicted y by passing X to the model\n", " output = model(X)\n", "\n", " # compute the loss function\n", " loss = criterion(output, y)\n", "\n", " # backward pass: compute gradient of the loss with respect to model parameters\n", " loss.backward()\n", "\n", " # call the step function on an Optimizer makes an update to its parameters\n", " optimizer.step()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:33.670201Z", "start_time": "2020-09-27T04:24:32.942266Z" } }, "outputs": [ { "data": { "image/png": "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Wrdu7baGXr16meXLlxd8/cUXXzhdY+zYsYV+L5UqVTIff/yxV+faf5z+z7333lto/5o1a5p169Z59ZmUqp8xAAAgrD2/KNU0/vtnTq+vfzoS7NKKJO/f95uMF3kxRH9NgALJydLw4VJhI3g2mzRsmNS4sX0aeBmTkZGhLVu2SJJatmypyy67rMjnxsbG6rrrrtPKlSt15MgR7dy5U61bt3bZd8iQIdq6dav+9Kc/6c4771T9+vV14MAB5ebmFvl+jz/+uBYuXChJatWqlZ566ildfvnlysjI0IIFC/T2229r4MCBRb6eJ4cOHdLNN98sSZowYYLi4uJUsWJFfffdd0pKStKhQ4f0//7f/1OfPn2U4OLP/dy5c2rXrp1uuukmde7cWfXr15cxRvv27dOCBQs0f/587d27V7fccou2bNmi6Ohon9R9YQ39+/fXjh07JEkJCQl65JFH1KhRI+3fv1+TJ0/W8uXLdfTo0UKvU69ePd1666265ppr1KxZM0VHR2v//v1av369Jk+erJMnT2rQoEFKSUk57+/Aww8/rAEDBui5557TwoULVb9+fS1fvrzQ+zVr1ky33nqrrrrqKjVq1EjlypXTvn37tGrVKk2fPl3p6em69dZbtW3bNtWpU6fkHxYAAEAAZWWf02VjXP+baOf43oouHxngioLAmxQeTi+V9RHquDjPI9MXvtyMqpZ2X331VcGo35133lns8//+978XnP/BBx+c1+Y4GizJvPPOOx6v5WmEOiUlxViWZSSZTp06mZMnTzr1+eijj867X0lGqCWZiy++2Pzyyy9OfXbt2mUqVKhgJJlbbrnF5ffy448/evxeV65caSIiIowk895777nsU9IR6jfffLPge3F3jaFDh573mbkaod67d6/Jzs52e5/9+/ebBg0aGEnm7rvvdtknf9S5cePGhda9Z88ep9kOjn744QdTpUoVI8k899xzhV7vQqXmZwwAAAhLa3Yddjkq/coy9zMXS6uSjFDzDHUoS02V1q4t3jlr1tjPK2Mcn0W96KKLin2+4zmenmvt3r27HnzwwWJfP9+UKVPyf1Gjd999V5UrV3bqM2DAAN16661e3+NCkyZN0sUXX+x0/JJLLtEtt9wiSVrr5u9Jy5YtPV67Z8+euummmyRJn3zySQkrdW3y5MmSpBo1amjSpEku+7z22muqXbu2x+s0adJE5cuXd9vesGFDPfXUU5KkRYsWFfw5eat58+ZOz1U7ateunR544AFJ/vvsAAAA/GHozO80ePoGp+Mr/hqnp3q1CkJFwcOU71Dm7UJjycllbuXvzMzMgvdVqlQp9vmO5+QvCubK3XffXexrO1q1apUk+2JonTp1cttv8ODBWrBgQYnuJUnVqlXTjTfe6La9c+fOmjdvno4eParjx4+revXqHq/3xx9/6Pjx4zp79mzBsfwg+/3335e43gsdOnRI27fbt1y4/fbbVbVqVZf9qlSpojvuuENvvfVWka+dkZGh9PR0ZWVlFYTnSpUqFbTt3btXzZo1K+F38D/Hjh3T0aNHdebMmYL75X/e27dvV05OjsfADwAAEGx/ZJ7VlRNWOR2vVy1a6/7eQ5ER7gcTQhWBOpR5CIZ+OS+IHIOWN6tOO54TExPjtl/79u2Lfe18Z86c0Z49eyTJY5iW7EHXFy655BJFRLifiFKjRo2C95mZmS4D9VdffaU33nhDq1at8vicsj9WrN66dWvB+yuvvNJj36uuuqrQQL1v3z69+uqr+vTTT7Vv3z6PfY8cOVLiQL1161b961//0tKlSwtWoHfFZrPp2LFjPEcNAABKrY827tdT//nB6fiLt7bToKsbBaGi0oFAHco8BEO/nBdEtWrVKnjvKbi443iO47UuFBsbW+xr5zt+/HjB+8KmJxfWXlT5I67uOIZtVwurjRs3Ts8//3yR7nX69OniFVcEjgG+sLBZt25dj+1Lly7VgAEDlJWVVaR7l/T7mTZtmkaMGKFz584F5H4AAAD+YLMZ9fh/q5WW7vxvqG+fTVDdGN8vSluW8Ax1KPN2te4yuMp327ZtC8Lhpk2bin1+SkpKwfsOHTq47RcZGQYrFeZJTk4uCNPNmjXT5MmT9cMPP+j48ePKyckpWIhh9OjRAanH0/PIhTly5IgGDRqkrKwsValSRePGjdPXX3+tw4cP6+zZswXfS7LDYxIleYZ6586dBWG6Tp06+sc//qFNmzYpPT1d2dnZBfebNm2aT+4HAADgDz/9cVLNnl3iFKa7tailtIn9wj5MS4xQh7Y2baS4uOItTBYfX+aen5bs07Q7dOiglJQU7d69Wzt27HC79dWFjh07pi+//FKSfWS4VSv/LKTgOJ36jz/+8Ni3sPZAmDp1qiT7qPw333zjdtS8sO2qSsJxRsDvv//usa+n9v/85z8FMwQWLFignj17uuznq+9l5syZOnfunCIjI7VmzRq3f6f8+dkBAACUxGurftRrq3Y7HZ9+X2f1aOV5ZmA4YYQ61I0ZI3l4hvY8ERFSgEYb/eH+++8veP/aa68V+bx3331XZ86ckSTdd999JRoJ9SQ6OlrNmzeXVPgo+saNG/1SQ3Gk5q32fv3113ucgu7PWtu1a1fw/rvvvvPY11N7/vdSo0YNt2FaKvx7Kerfjfz7tW/f3uMvaErDnzMAAICjs+dy1WTkYpdheuu4RML0BQjUoS4hQZoypfBQHREhTZ1aJqd757v//vsLnqN977339MUXXxR6zq5duzR+/HhJ9ueNH3/8cb/WmJD3+e7YscNjqH7//ff9WkdR5D/7e+rUKbd9Nm/erG+//dZvNdSvX79gpsHHH3/sdsG5U6dOaf78+W6vk/+9nDlzRjabzWWfrKwszZ4922M90dH2aU2Oq5x7up+nz+7QoUNatGiRx+sAAAAE0sa0o7r0uWVOx+/u0khpE/upajQ7klyIQB0Ohg6VVqywT+d2JT7e3j5kSGDr8rHKlStr5syZsixLNptNN998s5Ytc/6BkG/z5s1KSEgoWKTqjTfeUIMGDfxa4/DhwwtGOUeMGOEycP33v//1yZZZJZW/B/W6desKVid39Mcff+iee+7xex0PPfSQJCk9Pd3tLzz+9re/6fDhw26vkf+9ZGVluQzeubm5euCBB3Tw4EGPtdSrV0+SdPjw4fO2anN3v927d2v9+vVO7VlZWRo0aBALkQEAgFLjb/O2aMA7XzsdX/RoV71wSzsXZ0AiUIePhARp9Wpp2zbp9del8ePt/922zX68DI9MO+rdu7cmTZqkiIgIZWZmqk+fPurfv7/ef/99ff3119qwYYM++ugj3X333bryyit14MABSdKzzz6roUOH+r2+Tp06adiwYZLs0307d+6sGTNmaNOmTVq9erUee+wxDRw4UFdddVXBOf6agl6YwYMHS7KPssbHx2vSpElav3691q9fr1dffVXt27fX9u3bdc011/i1joceekhXXHGFJGn69OlKTEzUJ598opSUFC1cuFC9e/fWlClTPG41dscdd6hChQqS7DMZRo4cqeTkZG3cuFGzZs3S1VdfrQ8//FBdu3b1WMu1114ryb7N1YgRI/TNN99oz549Ba98+b9osNls6tevn1588UWtXbtWGzZs0Ntvv60OHTpo9erVhd4PAADA305k5ajJyMX6ePOB845XLB+p3RP66PKGztuq4n9YlCzctGlTJhcdK45HHnlEjRs31p///GelpaVp8eLFWrx4scu+derU0SuvvKJ77703YPVNmjRJBw8e1GeffaadO3dqyAUzA5o2baoPPvhALVq0kPS/acaBNmDAAN1///2aMWOGDh48qMcee+y89sjISP3rX//SsWPH9PXXzr/N9JVy5crps88+U48ePbRr1y6tXLlSK1euPK9PYmKinnjiCfXq1cvlNRo2bKi3335bDzzwgM6cOaOXX35ZL7/88nl9Bg4cqGHDhnl8xrpHjx7q0qWLvvnmG33wwQf64IMPzmvPX6n7yiuv1PPPP6+xY8fq+PHjGjVqlNO1nnjiCbVt21ZfffVVkT4HAAAAX1u69ZAe+neK0/Fn+7bS8LjmQaio7GGEGiGpf//+2rlzp2bNmqUBAwaoWbNmqly5sipWrKhGjRqpb9++mjx5svbs2RPQMC1JUVFRWrRokWbMmKFu3bqpWrVqqlSpklq3bq1nn31WmzZtUs2aNQv6V6tWLaD1OZo+fbpmz56t6667TlWrVlWFChXUuHFj3XPPPVq/fr3fnznPV79+fW3evFkvvPCC2rZtq4oVK6p69erq0qWLJk+erKVLlyoqKsrjNe6//359+eWXuuWWW1S7dm2VL19e9erVU+/evTVv3jzNnTu30G3RIiIitGLFCj333HNq3769qlSp4nYGwZgxY7R48WIlJiYqNjZWUVFRatiwoW677TatWLFCr776qtefBwAAQEkYY3TLW1+5DNNfPn09YboYLPY+9cyyrE0dO3bsWNy9jXfs2CFJRd66CXC0bt06XXfddZKklStXehw1RXjiZwwAAPDGr8ey1O1l58V72zaI0aePdgva44bB1KlTJ6WkpKQYYzoV91ymfAOl0IcffijJPt25U6di/+8aAAAAcDJt3V6N/2y70/E37rpCN7WvH4SKyj4CNRBgR48elWVZio2Nddm+fPlyvfvuu5KkG2+80W0/AAAAoCjO5drUcfxKZZw559S2efQNiq3s+dE5uEegBgJs+/bt6tu3r/7v//5PPXv2VPPmzRUZGalffvlFCxcu1Jw5c5Sbm6vo6Gi9+OKLwS4XAAAAZdi2AyfUf9I6p+P9L6+nNwd1DEJFoYVADQRBZmampk+frunTp7tsr1q1qubNm6dWrVoFuDIAAACEiuc/TdWMr9Kcjs8d3kVdmtV0PgHFRqAGAuyKK67QrFmztGzZMm3ZskV//PGHjh8/rqpVq6pFixbq3bu3Hn30UdWpUyfYpQIAAKAMOnX2nNqMXe6ybef43oou73lnExQdgRoIsMqVK2vw4MEaPHhwsEsBAABAiFnz4x+6d/oGp+OPXt9CT/a6NAgVhTYCNQAAAACEgPtnbNAXu/5wOr7yr3FqWbdqECoKfQRqAAAAACjDDmee0VUTkp2ON6heUV8+fb0iIsJvb+lAIVADAAAAQBk1/7v9evq/Pzgdf+m2drrrqkZBqCi8EKgBAAAAoIyx2Yy6v7pavxzNcmrb8GyC6sREB6Gq8EOgBgAAAIAyZM/hk+r5zzVOx69rWUuzh14dhIrCF4EaAAAAAMqIf638Ua8n73Y6PuO+K3V9K7ZdDTQCNQAAAACUcmfP5erS55a5bNv2fC9VqUC0C4aIYBcAAAAAAHDvu7SjLsP04GsaK21iP8J0EPHJAwAAAEAp9dd5W7Rg8wGn458+2k3tGlYLQkVwRKAGAAAAgFLmRFaO2ietcDpeOSpSW8Ymqnwkk41LAwI1AAAAAJQiS7Ye0sP/TnE6/mzfVhoe1zwIFcEdAjUAAAAAlALGGN0yeb2+33/cqe3Lp6/XxTUqBaEqeEKgBgAAAIAg+/VYlrq9/IXT8XYNqmnRo11lWVYQqkJhmHgP5Fm2bJksy5JlWfrmm2+CXY5f3HnnnbIsS61atfLq/J07dxZ8RnPnznVqf+eddwraf/vtt5KWCwAAEBbe+/Jnl2F60l1X6NM/dyNMl2IEaoSM1atXF4S5orzGjRsX7JIBAAAQxs7l2tRu7HK9sHiHU9vm0Tfoxvb1g1AVioNADRTRyJEjZVmWoqOjC+1b0pFgAAAAhLZtB06oxailyjx77rzjN7Wvr7SJ/RRbOSpIlaE4eIYaIemhhx7Sww8/7LFPnTp1zvu6d+/eMsb4s6yQN2LECI0YMSLYZQAAAJRq4xalaub6NKfj84Z30dXNaga+IHiNQI2QVKdOHbVt2zbYZQAAAAAFTp09pzZjl7ts2zm+t6LLRwa4IpQUgRoAAAAA/Gz1rsO6b8Z3Tsf/3KOFnki8NAgVwRd4hhrI426V7/yVq19++WVJ0tmzZ10ucvbbb78VPGc9b948SdKuXbuc+rl7BvvMmTN666231LNnT1100UWKiopS7dq1df3112vy5MnKzs4u9HvYunWr7rnnHjVo0EDR0dFq1KiR7rnnHm3evNkHn1DhClvlu0uXLrIsS71795Yk7d+/X3/961/VsmVLVaxYUTVq1FDPnj21YMGCIt3vxIkTmjhxorp166batWsrKipKdevWVd++fTVnzhzZbDaffn8AAADeuHf6BpdhetXf4gjTZRwj1EApsGXLFt1yyy3at2/fecePHDmi1atXa/Xq1Zo8ebI+++wzNWnSxOU15syZoyFDhignJ6fg2P79+zVnzhzNnz9f7733nj+/hWJbs2aNbrvtNh09erTg2JkzZ5ScnKzk5GSNGjVKL7zwgtvzV61apTvvvFPp6ennHT98+LCWLl2qpUuXaurUqVqwYIFq1Kjht+8DAADAncOZZ3TVhGSn4w1jK2rtU9crIoLtsMo6RqiBQgwcOFBbt27V0KFDJUlRUVHaunWr06tWrVr661//qq1btxaMwDZt2tSpX0pKynnX37Vrl+Li4rRv3z5VrVpVTz/9tD7++GNt3LhRq1at0hNPPKEKFSooNTVVffv21cmTJ51qXLdune677z7l5OQoOjpazzzzjNauXatvv/1Wr732mqpXr65hw4Zp+/bt/v/AiuCXX37RrbfeqnLlymnixIlat26dvvvuO7355psFi8VNmDBB69atc3n+mjVr1LdvX6Wnp6t27dpKSkrSokWLtGnTJi1ZskQPPPCAIiIitHbtWt1+++3Kzc0N5LcHAACged/94jJMT7ytndb9vQdhOkQwQo2QdPjwYW3bts1te2xsrBo0aFCka8XGxio2Nla1atWSJFmW5XbBs7p166pu3bqqVq2aJHv4LmxxtLvvvluZmZnq0KGDVqxYodq1a5/XnpCQoNtvv109evTQjh079Prrr2vUqFHn9Xn44YeVm5ur8uXLa/ny5YqLiytou+qqq3Trrbfq6quv1tatW4v0Pfvbjh071KxZM3355ZeqX/9/+yt27txZ8fHx6tixo3JycvTWW2+pW7du55175swZ3X333crJyVFiYqI+/vhjVa5c+bw+ffr0UZ8+fTRgwACtXr1ac+fO1Z/+9KeAfG8AACC82WxG8a9+of1HTzu1bXg2QXViCt+CFWUHI9QISW+//bbatWvn9nVhIA2WVatWaePGjZKkWbNmOYXpfNdcc42GDRsmSZo+ffp5bV9++WVBUH7wwQfPC9P5GjVqVPAMeGkxefLk88J0vrZt26pv376S7CPRF5ozZ45+/fVXRUdHa/bs2U5hOt9tt92m/v37S3L+zAAAAPxhz+FMNXt2iVOYjrukttIm9iNMhyBGqEuBJiMXB7uEgEmb2C/YJZQqCxculCRdeumluvzyyz32jYuL06RJk/Tzzz/r8OHDBVOjV61aVdDn/vvvd3v+HXfcoYcfflinTp3yQeUlU7t2bfXq1ctte+fOnbVw4UIdOnRIZ5KrEYUAACAASURBVM6cOW8ht/zPLD4+3mkv8QvFxcXp008/PW+ROQAAAH/454pdeuPzPU7HZ9x/pa6/1PO/WVB2EagRksaOHatx48YFu4xC5Y9O568GXlS//fZbQZjMH52OiopS+/bt3Z4THR2tyy+/XF9//XUJKvaNVq1aeWx3XEQsMzPzvECd/5ktX768yJ9ZVlaWMjIyFBMT40W1AAAA7p3JyVWr0ctctm17vpeqVCByhTKmfANBdPjwYa/Oy8rKKnifv0p2jRo1FBkZ6fG8unXrenU/X6tUqZLH9oiI//1ounBBsSNHjnh1T8fPDAAAwBc27D3qMkwPvqax0ib2I0yHAf6ESwGmQYev/LDYvn17zZkzp8jnNW/e3OlYcUa4yyqbzVawt3SfPn30yiuvFPnc/EXlAAAAfOEvczfrky0HnY5/+mg3tWtYLQgVIRgI1EAQ1apVS/v27dPJkycLXQ3cndjYWElSenq6cnNzPY5S//77717do7SIiIhQbGys0tPTlZ2d7fVnBgAA4K3jWdnqkLTS6XiVCuW0ecwNKh/JJOBwwp82UETFGQEuat8rrrhCkvTTTz/p0KFDXtXVrl07SVJ2dra+//57t/3Onj2rH374wat7lCb5n9l3332n7OzsIFcDAADCyeIfDrkM06P6tta253sRpsMQf+JAEeUvjJWTkyNjTJH6nj171mO/m2++ueD9a6+95lVdPXv2LHg/a9Yst/3mz59fKlb4Lqn8zywjI0PTpk0LcjUAACAcGGN005vr9MgHKU5tXz59vYbFNQtCVSgNCNRAEdWrV0+S/TnevXv3FqnvwYMHdebMGbf9+vXrVzDi+uqrr+rDDz/0eN3du3dr3rx55x2Li4tTmzZtJNn33/7qq6+czvv11181cuRIj9cuK4YOHVqwf/VTTz2lL774wmP/lJQULVmyJBClAQCAELT/aJaaPrNEP/x64rzjlzespr0v9dXFNTwvtorQRqAGiujaa68teP/YY49p3bp12r17t/bs2aM9e/actxp1ft/s7Gw99NBD+vbbbwv6/fzzzwX9LMvSvHnzFBsbK5vNpkGDBqlfv36aPXu2vv32W6WkpGj58uV65ZVXFB8fr0svvVSffvqpU22TJ09WRESEcnJydMMNN+i5557TunXr9N133+mNN95Q586dlZ6eXjA9vCyrWLGi5s+fr6ioKJ06dUo33HCD7rrrLs2fP18bN27Uxo0btXjxYiUlJalz587q1KmT1q9fH+yyAQBAGfTelz/rulecf3n/5qArtOjRbmGxKCw8Y1EyoIjatm2rm266SYsWLdLixYu1ePHi89oPHTqkiy66SJLUu3dvdezYUSkpKZo5c6ZmzpxZ0K9ChQrnjVq3bNlS69ev14ABA5SamqolS5Z4HFF1tZdyXFycpk+frmHDhun06dOaMGGCJkyYUNBevnx5vffee1qyZEnBvtVlWdeuXbV69WoNHDhQ+/fv19y5czV37ly3/dl/GgAAFEdOrk0dnl+hU9m5Tm1bxtyg6pWiglAVSiNGqIFimD9/vl566SV17txZMTExbn8rWa5cOX3++ed65plndPnll6tKlSoef4PZqlUrff/99/rwww81YMAANW7cWBUrVlRUVJTq1q2r6667Tk8//bTWrVunyZMnu7zGvffeq40bN2rQoEGqV6+eoqKi1LBhQ911111av369Bg8e7JPPoLS45pprtHv3bk2ZMkX9+/dXgwYNVKFCBVWoUEENGjRQjx49NHbsWG3ZskVPP/10sMsFAABlxNZfT6jlqKVOYfrmDvWVNrEfYRrnsQpbXCncWZa1qWPHjh03bdpUrPN27NghSWrdurU/ygIQ5vgZAwCA741duE2zvt7ndHz+g9foqqY1glARAqFTp05KSUlJMcZ0Ku65TPkGAAAAENZOnj2ntmOXu2zbOb63ostHBrgilBUEagAAAABh64tdh3X/jO+cjj+W0FJ/u+GSIFSEsoRADQAAACAsDZ6+QWt//MPp+Kq/xatFnSpBqAhlDYEaAAAAQFg5nHFGV72Y7HS8UY1KWv1kd0VEsB0WioZADQAAACBs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\n", "text/plain": [ "
" ] }, "metadata": { "image/png": { "height": 361, "width": 490 }, "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# plot the data and the fitted line to confirm the result\n", "\n", "# change default style figure and font size\n", "plt.rcParams['figure.figsize'] = 8, 6\n", "plt.rcParams['font.size'] = 14\n", "\n", "# convert a torch FloatTensor back to a numpy ndarray\n", "# here, we also call .detach to detach the result from the computation history,\n", "# to prevent future computations on it from being tracked\n", "y_pred = model(X).detach().numpy()\n", "plt.plot(X_train, y_train, 'ro', label='Original data')\n", "plt.plot(X_train, y_pred, label='Fitted line')\n", "plt.legend()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:33.708121Z", "start_time": "2020-09-27T04:24:33.672934Z" } }, "outputs": [ { "data": { "text/plain": [ "OrderedDict([('weight', tensor([[0.3563]])), ('bias', tensor([0.0564]))])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# to get the parameters, i.e. weight and bias from the model,\n", "# we can use the state_dict() attribute from the model that\n", "# we've defined\n", "model.state_dict()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:33.742727Z", "start_time": "2020-09-27T04:24:33.711117Z" } }, "outputs": [ { "data": { "text/plain": [ "[Parameter containing:\n", " tensor([[0.3563]], requires_grad=True),\n", " Parameter containing:\n", " tensor([0.0564], requires_grad=True)]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# or we could get it from the model's parameter\n", "# which by itself is a generator\n", "list(model.parameters())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Linear Regression Version 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A better way of defining our model is to inherit the `nn.Module` class, to use it all we need to do is define our model's forward pass and the `nn.Module` will automatically define the backward method for us, where the gradients will be computed using autograd." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:33.812197Z", "start_time": "2020-09-27T04:24:33.745912Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch [20/100], Loss: 0.1608\n", "Epoch [40/100], Loss: 0.1602\n", "Epoch [60/100], Loss: 0.1596\n", "Epoch [80/100], Loss: 0.1591\n", "Epoch [100/100], Loss: 0.1586\n" ] } ], "source": [ "class LinearRegression(nn.Module):\n", "\n", " def __init__(self, in_features, out_features):\n", " super().__init__() # boilerplate call\n", " self.in_features = in_features\n", " self.out_features = out_features\n", " self.linear = nn.Linear(in_features, out_features) \n", "\n", " def forward(self, x):\n", " out = self.linear(x)\n", " return out\n", "\n", "\n", "# same optimization process\n", "n_epochs = 100\n", "learning_rate = 0.01\n", "criterion = nn.MSELoss(size_average=True)\n", "model = LinearRegression(in_features=1, out_features=1)\n", "\n", "# when we defined our LinearRegression class, we've assigned\n", "# a neural network's component/layer to a class variable in the\n", "# __init__ function, and now notice that we can directly call\n", "# .parameters() on the class we've defined due to some Python magic\n", "# from the Pytorch devs\n", "optimizer = optim.SGD(model.parameters(), lr=learning_rate)\n", "\n", "for epoch in range(n_epochs):\n", " # forward + backward + optimize\n", " optimizer.zero_grad()\n", " output = model(X)\n", " loss = criterion(output, y)\n", " loss.backward()\n", " optimizer.step()\n", "\n", " # print the loss per 20 epoch this time\n", " if (epoch + 1) % 20 == 0:\n", " # starting from pytorch 0.4.0, we use .item to get a python number from a\n", " # torch scalar, before loss.item() looks something like loss.data[0]\n", " print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch + 1, n_epochs, loss.item()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After training our model, we can also [save the model's parameter and load it back into the model in the future](https://stackoverflow.com/questions/42703500/best-way-to-save-a-trained-model-in-pytorch)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.419957Z", "start_time": "2020-09-27T04:24:33.815188Z" } }, "outputs": [ { "data": { "image/png": "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t956S5deemme7zdu3Fi33HKLJGmZm/9OGjVq5PHaXbp0Ua9evSRJ//vf/4rYqWvjxo2TJFWpUkVvvfWWy2Nef/11VatWzeN16tWrpzJlyritX3LJJXrmmWckSV999VXOn5O3GjZsmOe56tyaN2+uBx54QJLvfnYAAAC2GzBAmj/fuU6TK3Fxznr//vb25SNM+Q5lJWgvuPT09Jz3kZGRhT4/9zlpHpbu79evX6GvndvChQslORdDa926tdvj7rnnHs2YUfRt1CtVqqSbbrrJbb1NmzaaNm2ajhw5omPHjqly5coer3fo0CEdO3ZMp0+fzvledpD94YcfitzvhQ4cOKBNmzZJkm677TZVrFjR5XGRkZG644479M477xT42mlpaUpNTVVGRkZOeC5fvnxObefOnWrQoEERP8Gfjh49qiNHjujUqVM598v+eW/atElnzpzxGPgBAACCRny88xXCOwxlI1CHshK0F1zuoOXNqtO5z4nysHR/ixYtCn3tbKdOndL27dslyWOYlpxBtzg0btxYYWHuJ6JUqVIl5316errLQL1ixQq9+eabWrhwocfnlH2xYvXGjRtz3rdt29bjse3atcs3UO/evVuvvvqqvv76a+3evdvjsYcPHy5yoN64caNee+01zZkzJ2cFelccDoeOHj3Kc9QAACC0xMSEXIC+EIE6lJWgveCqVq2a895TcHEn9zm5r3Wh6OjoQl8727Fjf+4al9/05PzqBZU94upO7rDtamG1kSNH6vnnny/QvU6ePFm45gogd4DPL2zWqFHDY33OnDnq06ePMjIyCnTvon6eiRMn6qGHHtLZs2dtuR8AAADsxzPUoawE7QXXrFmznHC4fv36Qp+fnJyc875ly5ZujytVqlThmwtSSUlJOWG6QYMGGjdunH788UcdO3ZMZ86cyVmIYbhNqzN6eh45P4cPH9Zdd92ljIwMRUZGauTIkVq1apUOHjyo06dP53yWpFyPSRTlGeotW7bkhOnq1avrlVde0fr165WamqrMzMyc+02cOLFY7gcAAAD/YIQ6lMXESLGxhVuYLC4uKKdlREVFqWXLlkpOTta2bdu0efNmt1tfXejo0aP69ttvJTlHhps0aeKTHnNPpz506JDHY/Or22HChAmSnKPy3333ndtR8/y2qyqK3DMCfv/9d4/Heqr/97//zZkhMGPGDHXp0sXlccX1WaZMmaKzZ8+qVKlSWrp0qdv/pnz5swMAAIDvMUId6kaMcO7xVhBBvhfc/fffn/P+9ddfL/B577//vk6dOiVJuu+++4o0EupJRESEGjZsKCn/UfR169b5pIfCSDm32vv111/vcQq6L3tt3rx5zvu1a9d6PNZTPfuzVKlSxW2YlvL/LAX9byP7fi1atPD4C5pA+HMGAADwlYzMs/rnlxs1ZvZmHTmR6e92fIJAHeri46Xx4/MP1WFh0oQJQTndO9v999+f8xztBx98oMWLF+d7ztatWzVq1ChJzueNH3/8cZ/2GH/u57t582aPofqjjz7yaR8Fkf3s74kTJ9wes2HDBq1evdpnPdSqVStnpsGXX37pdsG5EydOaPr06W6vk/1ZTp06JYfD4fKYjIwMTZ061WM/ERERknTeKuee7ufpZ3fgwAF99dVXHq8DAAAQrCYs26ErRszTp2v2aPyyHXp1/lZ/t+QTBOqSoITsBVehQgVNmTJFlmXJ4XDo5ptv1ty5c90ev2HDBsXHx+csUvXmm2+qdu3aPu1x0KBBOaOcDz30kMvA9cUXXxTLlllFlb0H9fLly3NWJ8/t0KFDuvvuu33ex+DBgyVJqampbn/h8eSTT+rgwYNur5H9WTIyMlwG76ysLD3wwAPav3+/x15q1qwpSTp48OB5W7W5u9+2bdu0cuXKPPWMjAzdddddLEQGAABCzq9HM1RvyCyNnr35vO/XreJ5sdxgRaAuKeLjpSVLpJ9+kt54Qxo1yvl/f/rJ+f0gHpnOrVu3bnrrrbcUFham9PR0de/eXTfeeKM++ugjrVq1SmvWrNHnn3+ufv36qW3bttq3b58k6dlnn9WAAQN83l/r1q01cOBASc7pvm3atNHkyZO1fv16LVmyRI899pj69u2rdu3a5Zzjqyno+bnnnnskOUdZ4+Li9NZbb2nlypVauXKlXn31VbVo0UKbNm1Shw4dfNrH4MGDddVVV0mSJk2apISEBP3vf/9TcnKyZs6cqW7dumn8+PEetxq74447VLZsWUnOmQxDhgxRUlKS1q1bpw8//FDt27fXp59+qo4dO3rs5ZprrpHk3ObqoYce0nfffaft27fnvLJl/6LB4XCoZ8+eGjNmjJYtW6Y1a9bo3XffVcuWLbVkyZJ87wcAABAsjDF65JNkXftS3lmitSuX0wOdirYdaaBiUbKSpgTsBffII4+obt26evTRR7Vr1y7NmjVLs2bNcnls9erV9fLLL+vee++1rb+33npL+/fv1zfffKMtW7ao/wUzA+rXr69PPvlEl112maQ/pxnbrU+fPrr//vs1efJk7d+/X4899th59VKlSum1117T0aNHtWrVKp/1Ubp0aX3zzTfq3Lmztm7dqgULFmjBggXnHZOQkKCnnnpKXbt2dXmNSy65RO+++64eeOABnTp1Si+99JJeeuml847p27evBg4c6PEZ686dO+vqq6/Wd999p08++USffPLJefXslbrbtm2r559/Xs8995yOHTumoUOH5rnWU089pWbNmmnFihUF+jkAAAAEqjU7j+iO913/e3D6gx3Urn4VmzuyDyPUCEk33nijtmzZog8//FB9+vRRgwYNVKFCBZUrV0516tRRjx49NG7cOG3fvt3WMC1J4eHh+uqrrzR58mRde+21qlSpksqXL6+mTZvq2Wef1fr163XRRRflHF+pUiVb+8tt0qRJmjp1qjp16qSKFSuqbNmyqlu3ru6++26tXLnS58+cZ6tVq5Y2bNigF154Qc2aNVO5cuVUuXJlXX311Ro3bpzmzJmj8PBwj9e4//779e233+qWW25RtWrVVKZMGdWsWVPdunXTtGnT9Nlnn+W7LVpYWJjmz5+vYcOGqUWLFoqMjHQ7g2DEiBGaNWuWEhISFB0drfDwcF1yySW69dZbNX/+fL366qte/zwAAAACwemzWeo4dpHLMH3jlTW188UeIR2mJcli71PPLMta36pVq1aF3dt482bnMwMF3boJyG358uXq1KmTJGnBggUeR01RMvF3DAAA8KfP1uzRkC83uqwt/8f1uiQ6eJ6Zbt26tZKTk5ONMa0Ley5TvoEA9Omnn0pyTndu3brQ/7sGAABAtpQUKSlJSkuToqKcaweF+COQvnQo/bTajl7osvZsjyYaFNvQ5o78i0AN2OzIkSOyLEvR0dEu6/PmzdP7778vSbrpppvcHgcAAAAPkpKkxERp2bK8tdhYacSIkFmY1y5DZ2zUf1bvyfP9CuGltHZYF5UPL3nxsuR9YsDPNm3apB49euj2229Xly5d1LBhQ5UqVUp79uzRzJkz9fHHHysrK0sREREaM2aMv9sFAAAIPhMnSoMGSQ6H6/qyZVJCgjRhQtBvHWuHn/b9oRvfWu6yNuX+trru8uo2dxQ4CNSAH6Snp2vSpEmaNGmSy3rFihU1bdo0NWnSxObOAAAAglxSkucwnc3hkAYOlOrWZaTajbNZDvV6e4U2HUjLU+vUqKo+6t/Ob1u8BgoCNWCzq666Sh9++KHmzp2r77//XocOHdKxY8dUsWJFXXbZZerWrZv++te/qnr1kvubPgAAAK8lJuYfprM5HNKoUQRqF77+Yb8e/XSDy1rSU3FqWC3S5o4CE4EasFmFChV0zz336J577vF3KwAAAKElJcX1M9OeLF3qPI+FyiRJf2ScUYvE+S5rj3a+TE8lXG5zR4GNQA0AAAAgNCQleX8egVovz92icUt+cVn74bkEVSpXxuaOAh+BGgAAAEBoSMv7rK9PzwsR2w8eV5d/L3VZG/eXVurRvKbNHQUPAjUAAACA0BAVZe95Qc7hMOo3cbVW/pKap9asdpT+93BHlS4V5ofOggeBGgAAAEBo8HZxsRK4KNniLQd1/5S1LmvfPHqtmtWuZHNHwYlADQAAACA0xMRIsbGFW5gsLq5EPT994vRZtRq1QKfP5l0J/Z4OdZV4czM/dBW8CNQAAAAAQseIEVJCQsG2zgoLk4YP931PAeK9pb9o7JwtLmvrhnVR1ciyNncU/AjUAAAAAEJHfLw0frw0aJDnUB0WJk2YUCKme+89kqFOLy92WXv5tit1R9tLbe4odBCoAQAAAISWAQOkevWkUaOc+0xfKC7OOTId4mHaGKPBHydrbspveWp1qpTXwifjFF6aRceKgkANAAAAIPTExztfKSnOfabT0pyrecfHl4hnplfvSFXf8d+5rH0xuINa161ic0ehiUANAAAAIHTFxJSIAJ3t1Jksxb2yWL+nnc5Tu6VlLb3Wt6Usy/JDZ6GJQA0AAAAAIeA/q3dr6IyfXNZWDumsWpXL2dxR6CNQAwAAAEAQO5h2Su3GJLmsDevZVA90amBzRyUHgRoAAAAAgtSQL37UZ2v35vl+VERprX62i8qFl/JDVyUHS7oB58ydO1eWZcmyLH33nesFHILdnXfeKcuy1KRJE6/O37JlS87P6LPPPstTf++993Lqv/2WdzVJAAAAFI8ffz2mekNmuQzTUwe0048juxKmbUCgRshYsmRJTpgryGvkyJH+bhkAAAAolLNZDnV9bZl6vb0iTy2ucTXtfLGHOjWq5ofOSiYCNVBAQ4YMkWVZioiIyPfYoo4EAwAAABea+f0+XTZ0jrb+np6ntvjp6/Rh/3as4G0znqFGSBo8eLAefvhhj8dUr179vK+7desmY4wv2wp5Dz30kB566CF/twEAABBSjmVkqmXiApe1x+Mb6YkbGtvcEbIRqBGSqlevrmbNmvm7DQAAAKBIXpyzWe8v3eGy9uPIBEVFlLG5I+RGoAYAAACAALPt93Td8Noyl7X3+rVSt2Y1be4IrvAMNXCOu1W+s1eufumllyRJp0+fdrnI2W+//ZbznPW0adMkSVu3bs1znLtnsE+dOqV33nlHXbp00cUXX6zw8HBVq1ZN119/vcaNG6fMzMx8P8PGjRt19913q3bt2oqIiFCdOnV09913a8OGDcXwE8pffqt8X3311bIsS926dZMk7d27V0888YQaNWqkcuXKqUqVKurSpYtmzJhRoPv98ccfGjt2rK699lpVq1ZN4eHhqlGjhnr06KGPP/5YDoejWD8fAACArzkcRne8v8plmG5xaWX9MqYHYTqAMEINBIDvv/9et9xyi3bv3n3e9w8fPqwlS5ZoyZIlGjdunL755hvVq1fP5TU+/vhj9e/fX2fOnMn53t69e/Xxxx9r+vTp+uCDD3z5EQpt6dKluvXWW3XkyJGc7506dUpJSUlKSkrS0KFD9cILL7g9f+HChbrzzjuVmpp63vcPHjyoOXPmaM6cOZowYYJmzJihKlWq+OxzAAAAFJeFm37XAx+tc1mb/VgnXVEryuaOkB8CNZCPvn376tprr9Xrr7+uiRMnKjw8XOvXr89zXNWqVfXEE0+oX79+euaZZzR37lzVr19fX3311XnHhYWdPzFk69atio2NVXp6uipWrKjBgwfr6quvVp06dXTs2DHNmTNHb7/9tlJSUtSjRw+tWbNGkZGR511j+fLluu+++5SVlaWIiAg98cQT6t69u8qWLatVq1ZpzJgxGjhwoBo3DowFK/bs2aPevXurTJkyOSPMZcuW1erVq5WYmKiDBw9q9OjR6tatm6699to85y9dulQ9evTQmTNnVK1aNT366KNq2bKlateurd9//11ffvmlJk2apGXLlum2227TwoULVaoU+zACAIDAdPz0WbV8fr7OOvIukHt/x3p67qYYP3SFgiBQIyQdPHhQP/30k9t6dHS0ateuXaBrRUdHKzo6WlWrVpUkWZbldsGzGjVqqEaNGqpUqZIk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\n", "text/plain": [ "
" ] }, "metadata": { "image/png": { "height": 361, "width": 490 }, "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "checkpoint_path = 'model.pkl'\n", "torch.save(model.state_dict(), checkpoint_path)\n", "model.load_state_dict(torch.load(checkpoint_path))\n", "\n", "y_pred = model(X).detach().numpy()\n", "plt.plot(X_train, y_train, 'ro', label='Original data')\n", "plt.plot(X_train, y_pred, label='Fitted line')\n", "plt.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Logistic Regression" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's now look at a classification example, here we'll define a logistic regression that takes in a bag of words representation of some text and predicts over two labels \"English\" and \"Spanish\"." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.457814Z", "start_time": "2020-09-27T04:24:34.426086Z" } }, "outputs": [], "source": [ "# define some toy dataset\n", "train_data = [\n", " ('me gusta comer en la cafeteria'.split(), 'SPANISH'),\n", " ('Give it to me'.split(), 'ENGLISH'),\n", " ('No creo que sea una buena idea'.split(), 'SPANISH'),\n", " ('No it is not a good idea to get lost at sea'.split(), 'ENGLISH')\n", "]\n", "\n", "test_data = [\n", " ('Yo creo que si'.split(), 'SPANISH'),\n", " ('it is lost on me'.split(), 'ENGLISH')\n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The next code chunk create words to index mappings. To build our bag of words (BoW) representation, we need to assign each word in our vocabulary an unique index. Let's say our entire corpus only consists of two words \"hello\" and \"world\", with \"hello\" corresponding to index 0 and \"world\" to index 1. Then the BoW vector for the sentence \"hello world hello world\" will be [2, 2], i.e. the count for the word \"hello\" will be at position 0 of the array and so on." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.496039Z", "start_time": "2020-09-27T04:24:34.460582Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'me': 0, 'gusta': 1, 'comer': 2, 'en': 3, 'la': 4, 'cafeteria': 5, 'Give': 6, 'it': 7, 'to': 8, 'No': 9, 'creo': 10, 'que': 11, 'sea': 12, 'una': 13, 'buena': 14, 'idea': 15, 'is': 16, 'not': 17, 'a': 18, 'good': 19, 'get': 20, 'lost': 21, 'at': 22, 'Yo': 23, 'si': 24, 'on': 25}\n" ] } ], "source": [ "idx_to_label = ['SPANISH', 'ENGLISH']\n", "label_to_idx = {\"SPANISH\": 0, \"ENGLISH\": 1}\n", "\n", "word_to_idx = {}\n", "for sent, _ in train_data + test_data:\n", " for word in sent:\n", " if word not in word_to_idx:\n", " word_to_idx[word] = len(word_to_idx)\n", "print(word_to_idx)\n", "\n", "VOCAB_SIZE = len(word_to_idx)\n", "NUM_LABELS = len(label_to_idx)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we define our model using the inherenting from `nn.Module` approach and also two helper functions to convert our data to torch Tensors so we can use to during training." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.527915Z", "start_time": "2020-09-27T04:24:34.498743Z" } }, "outputs": [], "source": [ "class BoWClassifier(nn.Module):\n", "\n", " def __init__(self, vocab_size, num_labels):\n", " super().__init__()\n", " self.linear = nn.Linear(vocab_size, num_labels)\n", "\n", " def forward(self, bow_vector):\n", " \"\"\"\n", " When we're performing a classification, after passing\n", " through the linear layer or also known as the affine layer\n", " we also need pass it through the softmax layer to convert a vector\n", " of real numbers into probability distribution, here we use\n", " log softmax for numerical stability reasons.\n", " \"\"\"\n", " return F.log_softmax(self.linear(bow_vector), dim = 1)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.560137Z", "start_time": "2020-09-27T04:24:34.530479Z" } }, "outputs": [], "source": [ "def make_bow_vector(sentence, word_to_idx):\n", " vector = torch.zeros(len(word_to_idx))\n", " for word in sentence:\n", " vector[word_to_idx[word]] += 1\n", "\n", " return vector.view(1, -1)\n", "\n", "\n", "def make_target(label, label_to_idx):\n", " return torch.LongTensor([label_to_idx[label]])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are now ready to train this!" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.762028Z", "start_time": "2020-09-27T04:24:34.562782Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "true label: SPANISH predicted label: SPANISH\n", "true label: ENGLISH predicted label: ENGLISH\n" ] } ], "source": [ "model = BoWClassifier(VOCAB_SIZE, NUM_LABELS)\n", "\n", "# note that instead of using NLLLoss (negative log likelihood),\n", "# we could have used CrossEntropyLoss and remove the log_softmax\n", "# function call in our forward method. The CrossEntropyLoss docstring\n", "# explicitly states that this criterion combines `LogSoftMax` and\n", "# `NLLLoss` in one single class.\n", "criterion = nn.NLLLoss()\n", "optimizer = optim.SGD(model.parameters(), lr=0.1)\n", "\n", "n_epochs = 100\n", "for epoch in range(n_epochs):\n", " for instance, label in train_data:\n", " bow_vector = make_bow_vector(instance, word_to_idx)\n", " target = make_target(label, label_to_idx)\n", "\n", " # standard step to perform the forward and backward step\n", " model.zero_grad()\n", " log_probs = model(bow_vector)\n", " loss = criterion(log_probs, target)\n", " loss.backward()\n", " optimizer.step()\n", "\n", "# we can also wrap the code block in with torch.no_grad(): to\n", "# prevent history tracking, this is often used in model inferencing,\n", "# or when evaluating the model as we won't be needing the gradient during\n", "# this stage\n", "\n", "with torch.no_grad():\n", " # predict on the test data to check if the model actually learned anything\n", " for instance, label in test_data:\n", " bow_vec = make_bow_vector(instance, word_to_idx)\n", " log_probs = model(bow_vec)\n", "\n", " y_pred = np.argmax(log_probs[0].numpy())\n", " label_pred = idx_to_label[y_pred]\n", " print('true label: ', label, ' predicted label: ', label_pred)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Recurrent Neural Network (RNN)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The idea behind RNN is to make use of sequential information that exists in our dataset. In feedforward neural network, we assume that all inputs and outputs are independent of each other. But for some tasks, this might not be the best way to tackle the problem. For example, in Natural Language Processing (NLP) applications, if we wish to predict the next word in a sentence (one business application of this is [Swiftkey](https://en.wikipedia.org/wiki/SwiftKey)), then we could imagine that knowing the word that comes before it can come in handy." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Vanilla RNN" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The input $x$ will be a sequence of words, and each $x_t$ is a single word. And because of how matrix multiplication works, we can't simply use a word index like (36) as an input, instead we represent each word as a one-hot vector with a size of the total number of vocabulary. For example, the word with index 36 have the value 1 at position 36 and the rest of the value in the vector would all be 0's." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.815218Z", "start_time": "2020-09-27T04:24:34.765000Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sequence input size torch.Size([1, 5, 4])\n", "out size torch.Size([1, 5, 2])\n", "sequence size torch.Size([1, 1, 2])\n", "\n", "comparing rnn cell output:\n", "tensor([[-0.7762, 0.8319]], grad_fn=)\n" ] }, { "data": { "text/plain": [ "tensor([[-0.7762, 0.8319]], grad_fn=)" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "torch.manual_seed(777)\n", "\n", "# suppose we have a\n", "# one hot encoding for each char in 'hello'\n", "# and the sequence length for the word 'hello' is 5\n", "seq_len = 5\n", "h = [1, 0, 0, 0]\n", "e = [0, 1, 0, 0]\n", "l = [0, 0, 1, 0]\n", "o = [0, 0, 0, 1]\n", "\n", "# here we specify a single RNN cell with the property of\n", "# input_dim (4) -> output_dim (2)\n", "# batch_first explained in the following\n", "rnn_cell = nn.RNN(input_size=4, hidden_size=2, batch_first=True)\n", "\n", "# our input shape should be of shape\n", "# (batch, seq_len, input_size) when batch_first=True;\n", "# the input size basically refers to the number of features\n", "# (seq_len, batch_size, input_size) when batch_first=False (default)\n", "# thus we reshape our input to the appropriate size, torch.view is\n", "# equivalent to numpy.reshape\n", "inputs = torch.Tensor([h, e, l, l, o])\n", "inputs = inputs.view(1, 5, -1)\n", "\n", "# our hidden is the weights that gets passed along the cells,\n", "# here we initialize some random values for it:\n", "# (batch, num_layers * num_directions, hidden_size) for batch_first=True\n", "# disregard the second argument as of now\n", "hidden = torch.zeros(1, 1, 2)\n", "out, hidden = rnn_cell(inputs, hidden)\n", "print('sequence input size', inputs.size())\n", "print('out size', out.size())\n", "print('sequence size', hidden.size())\n", "\n", "# the first value returned by the rnn cell is all\n", "# of the hidden state throughout the sequence, while\n", "# the second value is the most recent hidden state;\n", "# hence we can compare the last slice of the the first\n", "# value with the second value to confirm that they are\n", "# the same\n", "print('\\ncomparing rnn cell output:')\n", "print(out[:, -1, :])\n", "hidden[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the next section, we'll teach our RNN to produce \"ihello\" from \"hihell\"." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.848599Z", "start_time": "2020-09-27T04:24:34.819024Z" } }, "outputs": [], "source": [ "# create an index to character mapping\n", "idx2char = ['h', 'i', 'e', 'l', 'o']\n", "\n", "# Teach hihell -> ihello\n", "x_data = [[0, 1, 0, 2, 3, 3]] # hihell\n", "x_one_hot = [[[1, 0, 0, 0, 0], # h 0\n", " [0, 1, 0, 0, 0], # i 1\n", " [1, 0, 0, 0, 0], # h 0\n", " [0, 0, 1, 0, 0], # e 2\n", " [0, 0, 0, 1, 0], # l 3\n", " [0, 0, 0, 1, 0]]] # l 3\n", "\n", "x_one_hot = np.array(x_one_hot)\n", "y_data = np.array([1, 0, 2, 3, 3, 4]) # ihello\n", "\n", "# As we have one batch of samples, we will change them to variables only once\n", "inputs = torch.Tensor(x_one_hot)\n", "labels = torch.LongTensor(y_data)\n", "\n", "# hyperparameters\n", "seq_len = 6 # |hihell| == 6, equivalent to time step\n", "input_size = 5 # one-hot size\n", "batch_size = 1 # one sentence per batch\n", "num_layers = 1 # one-layer rnn\n", "num_classes = 5 # predicting 5 distinct character\n", "hidden_size = 4 # output from the RNN" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.884466Z", "start_time": "2020-09-27T04:24:34.851702Z" } }, "outputs": [], "source": [ "class RNN(nn.Module):\n", " \"\"\"\n", " The RNN model will be a RNN followed by a linear layer,\n", " i.e. a fully-connected layer\n", " \"\"\"\n", " def __init__(self, seq_len, num_classes, input_size, hidden_size, num_layers):\n", " super().__init__()\n", " self.seq_len = seq_len\n", " self.num_layers = num_layers\n", " self.input_size = input_size\n", " self.num_classes = num_classes\n", " self.hidden_size = hidden_size\n", " self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)\n", " self.linear = nn.Linear(hidden_size, num_classes)\n", "\n", " def forward(self, x):\n", " # assuming batch_first = True for RNN cells\n", " batch_size = x.size(0)\n", " hidden = self._init_hidden(batch_size)\n", " x = x.view(batch_size, self.seq_len, self.input_size)\n", "\n", " # apart from the output, rnn also gives us the hidden\n", " # cell, this gives us the opportunity to pass it to\n", " # the next cell if needed; we won't be needing it here\n", " # because the nn.RNN already computed all the time steps\n", " # for us. rnn_out will of size [batch_size, seq_len, hidden_size]\n", " rnn_out, _ = self.rnn(x, hidden)\n", " linear_out = self.linear(rnn_out.view(-1, hidden_size))\n", " return linear_out\n", "\n", " def _init_hidden(self, batch_size):\n", " \"\"\"\n", " Initialize hidden cell states, assuming\n", " batch_first = True for RNN cells\n", " \"\"\"\n", " return torch.zeros(batch_size, self.num_layers, self.hidden_size)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:34.963676Z", "start_time": "2020-09-27T04:24:34.887230Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "network architecture:\n", " RNN(\n", " (rnn): RNN(5, 4, batch_first=True)\n", " (linear): Linear(in_features=4, out_features=5, bias=True)\n", ")\n", "epoch: 1, loss: 1.756\n", "Predicted string: eeeeee\n", "epoch: 2, loss: 1.626\n", "Predicted string: ehhhhh\n", "epoch: 3, loss: 1.485\n", "Predicted string: elllll\n", "epoch: 4, loss: 1.405\n", "Predicted string: llllll\n", "epoch: 5, loss: 1.293\n", "Predicted string: illlll\n", "epoch: 6, loss: 1.217\n", "Predicted string: iiilll\n", "epoch: 7, loss: 1.057\n", "Predicted string: iollll\n", "epoch: 8, loss: 0.967\n", "Predicted string: ielllo\n", "epoch: 9, loss: 0.837\n", "Predicted string: ihlllo\n", "epoch: 10, loss: 0.696\n", "Predicted string: ihello\n", "epoch: 11, loss: 0.615\n", "Predicted string: ihello\n", "epoch: 12, loss: 0.535\n", "Predicted string: ihhllo\n", "epoch: 13, loss: 0.452\n", "Predicted string: ihhllo\n", "epoch: 14, loss: 0.387\n", "Predicted string: ihello\n", "epoch: 15, loss: 0.322\n", "Predicted string: ihello\n" ] } ], "source": [ "# Set loss, optimizer and the RNN model\n", "torch.manual_seed(777)\n", "rnn = RNN(seq_len, num_classes, input_size, hidden_size, num_layers)\n", "print('network architecture:\\n', rnn)\n", "\n", "# train the model\n", "num_epochs = 15\n", "criterion = nn.CrossEntropyLoss()\n", "optimizer = optim.Adam(rnn.parameters(), lr=0.1)\n", "for epoch in range(1, num_epochs + 1):\n", " optimizer.zero_grad()\n", " outputs = rnn(inputs)\n", " loss = criterion(outputs, labels)\n", " loss.backward()\n", " optimizer.step()\n", "\n", " # check the current predicted string\n", " # max gives the maximum value and its\n", " # corresponding index, we will only\n", " # be needing the index\n", " _, idx = outputs.max(dim = 1)\n", " idx = idx.detach().numpy()\n", " result_str = [idx2char[c] for c in idx]\n", " print('epoch: {}, loss: {:1.3f}'.format(epoch, loss.item()))\n", " print('Predicted string: ', ''.join(result_str))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### LSTM" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The example below uses an LSTM to generate part of speech tags. The usage of LSTM API is essentially the same as the RNN we were using in the last section. Expect in this example, we will prepare the word to index mapping ourselves and as for the modeling part, we will add an embedding layer before the LSTM layer, this is a common technique in NLP applications. So for each word, instead of using the one hot encoding way of representation the data (which can be inefficient and it treats all words as independent entities with no relationships amongst each other), word embeddings will compress them into a lower dimension that encode the semantics of the words, i.e. how similar each word is used within our given corpus." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:35.001461Z", "start_time": "2020-09-27T04:24:34.969372Z" } }, "outputs": [ { "data": { "text/plain": [ "{'The': 0,\n", " 'dog': 1,\n", " 'ate': 2,\n", " 'the': 3,\n", " 'apple': 4,\n", " 'Everybody': 5,\n", " 'read': 6,\n", " 'that': 7,\n", " 'book': 8}" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# These will usually be more like 32 or 64 dimensional.\n", "# We will keep them small for this toy example\n", "EMBEDDING_SIZE = 6\n", "HIDDEN_SIZE = 6\n", "\n", "training_data = [\n", " (\"The dog ate the apple\".split(), [\"DET\", \"NN\", \"V\", \"DET\", \"NN\"]),\n", " (\"Everybody read that book\".split(), [\"NN\", \"V\", \"DET\", \"NN\"])\n", "]\n", "\n", "idx_to_tag = ['DET', 'NN', 'V']\n", "tag_to_idx = {'DET': 0, 'NN': 1, 'V': 2}\n", "\n", "word_to_idx = {}\n", "for sent, tags in training_data:\n", " for word in sent:\n", " if word not in word_to_idx:\n", " word_to_idx[word] = len(word_to_idx)\n", "\n", "word_to_idx" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:35.033797Z", "start_time": "2020-09-27T04:24:35.004608Z" } }, "outputs": [ { "data": { "text/plain": [ "tensor([0, 1, 2, 3, 4])" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def prepare_sequence(seq, to_idx):\n", " \"\"\"Convert sentence/sequence to torch Tensors\"\"\"\n", " idxs = [to_idx[w] for w in seq]\n", " return torch.LongTensor(idxs)\n", "\n", "seq = training_data[0][0]\n", "inputs = prepare_sequence(seq, word_to_idx)\n", "inputs" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:35.066190Z", "start_time": "2020-09-27T04:24:35.036299Z" } }, "outputs": [], "source": [ "class LSTMTagger(nn.Module):\n", "\n", " def __init__(self, embedding_size, hidden_size, vocab_size, tagset_size):\n", " super().__init__()\n", " self.embedding_size = embedding_size\n", " self.hidden_size = hidden_size\n", " self.vocab_size = vocab_size\n", " self.tagset_size = tagset_size\n", "\n", " self.embedding = nn.Embedding(vocab_size, embedding_size)\n", " self.lstm = nn.LSTM(embedding_size, hidden_size)\n", " self.hidden2tag = nn.Linear(hidden_size, tagset_size)\n", "\n", " def forward(self, x):\n", " embed = self.embedding(x)\n", " hidden = self._init_hidden()\n", "\n", " # the second dimension refers to the batch size, which we've hard-coded\n", " # it as 1 throughout the example\n", " lstm_out, lstm_hidden = self.lstm(embed.view(len(x), 1, -1), hidden)\n", " output = self.hidden2tag(lstm_out.view(len(x), -1))\n", " return output\n", "\n", " def _init_hidden(self):\n", " # the dimension semantics are [num_layers, batch_size, hidden_size]\n", " return (torch.rand(1, 1, self.hidden_size),\n", " torch.rand(1, 1, self.hidden_size))" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:36.315396Z", "start_time": "2020-09-27T04:24:35.068859Z" } }, "outputs": [], "source": [ "model = LSTMTagger(EMBEDDING_SIZE, HIDDEN_SIZE, len(word_to_idx), len(tag_to_idx))\n", "criterion = nn.CrossEntropyLoss()\n", "optimizer = optim.SGD(model.parameters(), lr=0.1)\n", "\n", "epochs = 300\n", "for epoch in range(epochs):\n", " for sentence, tags in training_data:\n", " model.zero_grad()\n", "\n", " sentence = prepare_sequence(sentence, word_to_idx)\n", " target = prepare_sequence(tags, tag_to_idx)\n", "\n", " output = model(sentence)\n", " loss = criterion(output, target)\n", " loss.backward()\n", " optimizer.step()" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "ExecuteTime": { "end_time": "2020-09-27T04:24:36.349279Z", "start_time": "2020-09-27T04:24:36.318418Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "expected target: ['DET', 'NN', 'V', 'DET', 'NN']\n", "generated target: ['DET', 'NN', 'V', 'DET', 'NN']\n" ] } ], "source": [ "inputs = prepare_sequence(training_data[0][0], word_to_idx)\n", "tag_scores = model(inputs)\n", "\n", "# validating that the sentence \"the dog ate the apple\".\n", "# the correct tag should be DET NOUN VERB DET NOUN\n", "print('expected target: ', training_data[0][1])\n", "\n", "tag_scores = tag_scores.detach().numpy()\n", "tag = [idx_to_tag[idx] for idx in np.argmax(tag_scores, axis = 1)]\n", "print('generated target: ', tag)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Reference" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- [Github: Simple PyTorch Tutorials Zero to ALL!](https://github.com/hunkim/PyTorchZeroToAll)\n", "- [Github: PyTorch Tutorial for Deep Learning Researchers](https://github.com/yunjey/pytorch-tutorial)\n", "- [PyTorch Documentation: Deep Learning for NLP with Pytorch](http://pytorch.org/tutorials/beginner/deep_learning_nlp_tutorial.html)\n", "- [PyTorch Documentation: Deep Learning with PyTorch: A 60 Minute Blitz](http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.11" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": true, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "256px" }, "toc_section_display": true, "toc_window_display": true }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }