{ "cells": [ { "cell_type": "markdown", "id": "e556cca0", "metadata": {}, "source": [ "# makemore_MLP\n", "\n", "----\n", "* Inspired by Andrej Karpathy's [\"Building makemore Part 2: MLP\"](https://www.youtube.com/watch?v=TCH_1BHY58I)\n", "\n", "* Useful links \n", " - [PyTorch internals reference](http://blog.ezyang.com/2019/05/pytorch-internals/)\n", " ```\n", " \"...The talk is in two parts: in the first part, I'm going to first introduce you to the conceptual universe of a tensor library ... The second part grapples with the actual nitty gritty details involved with actually coding in PyTorch...\" - Edward Z. Yang\n", " ```\n" ] }, { "cell_type": "markdown", "id": "efa0b203", "metadata": {}, "source": [ "# Table of Contents\n", "------------------\n", "- [0. Makemore: Introduction](#0)\n", " - [0.1. \"A Neural Probabilistic Language Model\" - Bengio et al. 2003, Paper Walkthrough](#001)\n", "- [1. Re-building our Training Dataset](#1)\n", "- [2. Multilayer Perceptron (MLP)](#2)\n", " - [2.1. Embedding Lookup Table](#201)\n", " - [2.2. Hidden Layer + `torch.Tensor` Internals (storage, views)](#202)\n", " - [2.3. Output Layer](#203)\n", " - [2.4. Negative Log Likelihood Loss](#204)\n", " - [2.5. Summary of the Full Network](#205)\n", " - [2.6. `F.cross_entropy` & Why?](#206)\n", " - [2.7. Training Loop + Overfitting One Batch](#207)\n", " - [2.8. Training on the Full Dataset + Minibatches](#208)\n", " - [2.9. Learning Rate](#209)\n", " - [2.10. Train-Val-Test Splits & Why?](#210)\n", " - [2.11. Experiment: Larger Hidden Layer](#211)\n", " - [2.12. Visualize Character Embeddings](#212)\n", " - [2.13. Experiment: Larger Embedding Size](#213)\n", " - [2.14. Summary of Final Code](#214)\n", " - [2.15. Sampling from the Model](#215)\n", "- [3. Conclusion](#3)\n", "------\n", "\n", "\n", "\n", "# Appendix\n", "---------------\n", "## Figures\n", "- [A1: Neural Architecture.](#a1)\n", "\n", "## Definitions/Explanations\n", "- [D0. The Neural Model: Bengio et. al. 2003 paper (Section 2)](#d0)\n", "- [D1. Curse of Dimensionality](#d1)\n", "- [D2. Model Parameters](#d2)\n", "- [D3. Hyperparameters to Optimize](#d3)\n", "- [D4. Tensor Manipulations](#d4)\n", "- [D5. Embeddings](#d5)\n", "\n", "\n", "## [Exercises](#e1)\n", "\n", "## [References](#r1)" ] }, { "cell_type": "markdown", "id": "d29a33dd", "metadata": {}, "source": [ "-----------\n", "

\n", "# 0. Makemore: Introduction\n", "---------------------------------\n", "**Makemore** takes one text file as input, where each line is assumed to be one training thing, and generates more things like it. Under the hood, it is an **autoregressive character-level language model**, with a wide choice of models from bigrams all the way to a Transformer (exactly as seen in GPT). An [autoregressive model](https://en.wikipedia.org/wiki/Autoregressive_model) specifies that the output variable depends linearly on its own previous values and on a stochastic term (an imperfectly predictable term). For example, we can feed it a database of names, and makemore will generate cool baby name ideas that all sound name-like, but are not already existing names. Or if we feed it a database of company names then we can generate new ideas for a name of a company. Or we can just feed it valid scrabble words and generate english-like babble. \n", "```\n", "\"As the name suggests, makemore makes more.\"\n", "```\n", "This is not meant to be too heavyweight of a library with a billion switches and knobs. It is one hackable file, and is mostly intended for educational purposes. [PyTorch](https://pytorch.org) is the only requirement.\n", "\n", "Current implementation follows a few key papers:\n", "\n", "- Bigram (one character predicts the next one with a lookup table of counts)\n", "- MLP, following [Bengio et al. 2003](https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf)\n", "- CNN, following [DeepMind WaveNet 2016](https://arxiv.org/abs/1609.03499) (in progress...)\n", "- RNN, following [Mikolov et al. 2010](https://www.fit.vutbr.cz/research/groups/speech/publi/2010/mikolov_interspeech2010_IS100722.pdf)\n", "- LSTM, following [Graves et al. 2014](https://arxiv.org/abs/1308.0850)\n", "- GRU, following [Kyunghyun Cho et al. 2014](https://arxiv.org/abs/1409.1259)\n", "- Transformer, following [Vaswani et al. 2017](https://arxiv.org/abs/1706.03762)" ] }, { "cell_type": "markdown", "id": "72b94459", "metadata": {}, "source": [ "In the 1st makemore tutorial notebook, we worked on a bigram model that takes into account only the local context of a word. This approach is impractical as the size of the counting matrix (also represent model’s weights) grows rapidly when we increase the context, i.e. takes more characters. For example, the first dimension of the counting matrix increases from 27 to 27x27 = 729 when we switch from bigram to trigram model.\n", "\n", "As seen above expanding the context requires a larger lookup table, but with a vast vocabulary, this approach becomes impractical. This explosion in possible combinations means the model needs a huge amount of data to learn effectively from all these combinations. Yet, in reality, encountering all these combinations in training is unlikely, making accurate predictions on unseen sequences difficult. This challenge is known as the **curse of dimensionality**: \n", "```\n", "as the vocabulary increases, the number of possible combinations to learn increase exponentially, hindering the model’s learning and generalization capabilities.\n", "```\n", "\n", "In summary essentially, curse of dimensionality refers to the exponential growth in complexity and data requirements as the number of features (words or characters) increases, hindering the model’s ability to generalize effectively. Therefore, we want to have a model that is easy to generalize.\n", "\n", "In this notebook, we want to implement a multilayer perceptron (MLP) character-level language model. We will also introduce many basics of machine learning (e.g. model training, learning rate tuning, hyperparameters, evaluation, train/dev/test splits, under/overfitting, etc.). We will go through the **Bengio et al. 2003 paper** which offers a solution with dealing with the curse of dimensionality." ] }, { "cell_type": "markdown", "id": "501a351a", "metadata": {}, "source": [ "---\n", "\n", "## 0.1. \"A Neural Probabilistic Language Model\" - Bengio et al. 2003, Paper Walkthrough\n", "-----\n", "\n", "Abstract:\n", "A goal of statistical language modeling is to learn the joint probability function of sequences of words in a language. This is intrinsically difficult because of the **curse of dimensionality**: a word sequence on which the model will be tested is likely to be different from all the word sequences seen during training. Traditional but very successful approaches based on n-grams obtain generalization by concatenating very short overlapping sequences seen in the training set. We propose to fight the curse of dimensionality by **learning a distributed representation for words** which allows each training sentence to inform the model about an exponential number of semantically neighboring sentences. The model learns simultaneously \n", "1. a distributed representation for each word along with \n", "2. the probability function for word sequences, expressed in terms of these representations. \n", "\n", "Generalization is obtained because a sequence of words that has never been seen before gets high probability if it is made of words that are similar (in the sense of having a nearby representation) to words forming an already seen sentence. Training such large models (with millions of parameters) within a reasonable time is itself a significant challenge. We report on experiments using neural networks for the probability function, showing on two text corpora that the proposed approach significantly improves on state-of-the-art n-gram models, and that the proposed approach allows to take advantage of longer contexts. \n", "\n", "**Keywords**: Statistical language modeling, artificial neural networks, distributed representation, curse of dimensionality\n", "\n", "---" ] }, { "cell_type": "markdown", "id": "077a227f", "metadata": {}, "source": [ "In the model proposed here, instead of characterizing the similarity with a discrete random or deterministic variable (which corresponds to a soft or hard partition of the set of words), we use a continuous real-vector for each word, i.e. **a learned distributed feature vector**, to represent similarity between words.\n", "\n", "In this paper, they used a vocabulary of 17000 words with each word mapped to a 30-dimensional embedding via a lookup table/embedding matrix. The number of neurons in the hidden layer is a hyper-parameter. All of them are fully connected to the input layer. The output layer has 17000 neurons, one for each word. On top of the 17000 logits, we have a softmax layer. Weights and biases of the output layer, weights and biases of the hidden layer, and the embedding lookup table are the parameters we can adjust.The architecture of their neural network is shown in the figure below.\n", "\n", "\n", "The model takes several previous characters (*context*) and tries to predict the next one. In the figure above, the context consists of three characters, but the number can be more. Since our model cannot work directly with characters, we convert characters to integers (indexes). The input layer takes the indexes of all context characters and converts them into embedding vectors using a lookup table C. The size of the embedding vector is a network parameter. The lookup table is shared across characters and has a size of *number of unique characters X embedding vector size.*\n", "\n", "The hidden layer is fully connected to the previous layer and receives one concatenated output from it. The `tanh` function is used as a non-linearity function. The output layer is also fully connected and consists of the number of neurons equal to the total number of unique characters in the dataset. It produces **logits** which are then converted to **probabilities** using the **softmax** function. The character with the highest probability is our prediction. The network parameters are optimized using back-propagation.\n", "\n", "\n", "\n", "In the model proposed here, instead of characterizing the similarity with a discrete random or deterministic variable (which corresponds to a soft or hard partition of the set of words), we use a continuous real-vector for each word, i.e. **a learned distributed feature vector**, to represent similarity between words.\n", "\n" ] }, { "cell_type": "markdown", "id": "31924cb4", "metadata": {}, "source": [ "\n", "\n", "![NN architecture](_imgs/bengio_et_al_2003_NN_MLP_architecture_.png)\n", "\n", "**Figure 1: Neural architecture: $f\\left(i, w_{t-1}, \\cdots, w_{t-n+1}\\right)=g\\left(i, C\\left(w_{t-1}\\right), \\cdots, C\\left(w_{t-n+1}\\right)\\right)$ where $g$ is the neural network and $C(i)$ is the $i$-th word feature vector.** ([Source](https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf))" ] }, { "cell_type": "markdown", "id": "4a2f48ee", "metadata": {}, "source": [ "---------\n", "

\n", "# 1. Re-building our Training Dataset\n", "-------------------------------------------------------" ] }, { "cell_type": "code", "execution_count": 1, "id": "4bc451ee", "metadata": {}, "outputs": [], "source": [ "import torch\n", "import torch.nn.functional as F\n", "import random\n", "\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 2, "id": "0065a63a", "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "['emma', 'olivia', 'ava', 'isabella', 'sophia', 'charlotte', 'mia', 'amelia']" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# read all the names into a list of words\n", "words = open('../data/names.txt', 'r').read().splitlines()\n", "words[:8]" ] }, { "cell_type": "code", "execution_count": 3, "id": "e82d3f70", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "32033" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(words)" ] }, { "cell_type": "code", "execution_count": 4, "id": "3e06abf3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i', 10: 'j', 11: 'k', 12: 'l', 13: 'm', 14: 'n', 15: 'o', 16: 'p', 17: 'q', 18: 'r', 19: 's', 20: 't', 21: 'u', 22: 'v', 23: 'w', 24: 'x', 25: 'y', 26: 'z', 0: '.'}\n" ] } ], "source": [ "# build the vocabulary of characters and mappings to/from integers\n", "chars = sorted(list(set(''.join(words))))\n", "stoi = {s:i+1 for i,s in enumerate(chars)}\n", "stoi['.'] = 0\n", "itos = {i:s for s,i in stoi.items()}\n", "print(itos)" ] }, { "cell_type": "code", "execution_count": 5, "id": "34961497", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "emma\n", "... ---> e\n", "..e ---> m\n", ".em ---> m\n", "emm ---> a\n", "mma ---> .\n", "olivia\n", "... ---> o\n", "..o ---> l\n", ".ol ---> i\n", "oli ---> v\n", "liv ---> i\n", "ivi ---> a\n", "via ---> .\n", "ava\n", "... ---> a\n", "..a ---> v\n", ".av ---> a\n", "ava ---> .\n", "isabella\n", "... ---> i\n", "..i ---> s\n", ".is ---> a\n", "isa ---> b\n", "sab ---> e\n", "abe ---> l\n", "bel ---> l\n", "ell ---> a\n", "lla ---> .\n", "sophia\n", "... ---> s\n", "..s ---> o\n", ".so ---> p\n", "sop ---> h\n", "oph ---> i\n", "phi ---> a\n", "hia ---> .\n" ] } ], "source": [ "# build the dataset\n", "\n", "block_size = 3 # context length: how many characters do we take to predict the next one?\n", "X, Y = [], []\n", "for w in words[:5]:\n", " \n", " print(w)\n", " context = [0] * block_size # start with padded context of just zero tokens\n", " #print(context)\n", " for char in w + '.':\n", " ix = stoi[char]\n", " X.append(context)\n", " Y.append(ix)\n", " #print(ix)\n", " print(''.join(itos[i] for i in context), '--->', itos[ix])\n", " context = context[1:] + [ix] # crop context and append\n", " #print(context)\n", " \n", "X = torch.tensor(X)\n", "Y = torch.tensor(Y)\n", " " ] }, { "cell_type": "code", "execution_count": 6, "id": "1420ee76", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(torch.Size([32, 3]), torch.int64, torch.Size([32]), torch.int64)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.shape, X.dtype, Y.shape, Y.dtype" ] }, { "cell_type": "code", "execution_count": 7, "id": "69cc10fa", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([ 5, 13, 13, 1, 0, 15, 12, 9, 22, 9, 1, 0, 1, 22, 1, 0, 9, 19,\n", " 1, 2, 5, 12, 12, 1, 0, 19, 15, 16, 8, 9, 1, 0])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y" ] }, { "cell_type": "markdown", "id": "15271c24", "metadata": {}, "source": [ "---------\n", "

\n", "# 2. Multilayer Perceptron (MLP)\n", "-------------------------------------------------------\n", "\n", "Let's implement a neural probabilistic language model that handles some of the limitations (poor generalization, exponential growth in complexity) of the n-gram character-level language models we've implemented in previous tutorials so far. We will build a multilayer perceptron (MLP) model. An MLP model doesn't rely on exact sequence matches, and learns syntatic & semantic similarities between different words. This enables better model generalization to unseen data." ] }, { "cell_type": "markdown", "id": "517a6ec6", "metadata": {}, "source": [ "------\n", "## 2.1. Embedding Lookup Table\n", "-----\n", "\n", "We need to build the neural network that takes `X` and predicts `Y`. However, first we need to build the embedding lookup table `C` which we initialize randomly in the beginning. We have **27 possible characters** which we are going to embed in a lower-dimensional space (**2-D**). In the paper, they embed **17000 words into a 30-dimensional space**.\n", "\n", "We can use one-hot vectors to extract embeddings. Therefore, we can assume that the lookup table itself is a layer of the neural network without a non-linearity. However, we will be using indexing due to its efficiency. Indexing can be done for 1-D, and multi-dimensional tensors of integers." ] }, { "cell_type": "code", "execution_count": 8, "id": "65a8291f", "metadata": {}, "outputs": [], "source": [ "C = torch.randn((27, 2))" ] }, { "cell_type": "code", "execution_count": 9, "id": "ce8de70c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([ 0.0795, -1.1500])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C[5]" ] }, { "cell_type": "code", "execution_count": 10, "id": "4764e0a5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([ 0.0795, -1.1500])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "F.one_hot(torch.tensor(5), num_classes=27).float() @ C" ] }, { "cell_type": "code", "execution_count": 11, "id": "338c6c7c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([32, 3, 2])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C[X].shape" ] }, { "cell_type": "code", "execution_count": 12, "id": "821d27da", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(1)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[13, 2]" ] }, { "cell_type": "code", "execution_count": 13, "id": "e7513130", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([ 0.1649, -0.0402])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C[1]" ] }, { "cell_type": "code", "execution_count": 14, "id": "10e7f5b2", "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "tensor([ 0.1649, -0.0402])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C[X][13, 2]" ] }, { "cell_type": "code", "execution_count": 15, "id": "b5ed4a32", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([32, 3, 2])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[X]\n", "emb.shape" ] }, { "cell_type": "markdown", "id": "b8f739fa", "metadata": {}, "source": [ "------\n", "## 2.2. Hidden Layer + `torch.Tensor` Internals (storage, views)\n", "-----\n", "Let's construct the hidden layer shown in Figure 1 previously. For the 1st hidden layer, the **inputs would be 6** because there would be two values in the embedding table for each of the three characters (**2 X 3**). The hidden layer, with 100 neurons, processes the embedded vectors. We use a `tanh` activation function here to introduce non-linearity and the outputs lie in this range `[-1, 1]`.\n" ] }, { "cell_type": "code", "execution_count": 16, "id": "d63d1ddc", "metadata": {}, "outputs": [], "source": [ "W1 = torch.randn((6, 100))\n", "b1 = torch.randn(100)" ] }, { "cell_type": "code", "execution_count": 17, "id": "9dbdc8ae", "metadata": {}, "outputs": [], "source": [ "#emb @ W1 + b1 #RuntimeError: mat1 and mat2 shapes cannot be multiplied (96x2 and 6x100)" ] }, { "cell_type": "markdown", "id": "0aec2ea2", "metadata": {}, "source": [ "We need to ensure that `emb` can be matrix multiplied by `W1`. So we need to transform the shape of the embeddings `emb` to ensure matrix multiplication is possible. We need to concatenate `emb` to make this possible. Torch has many ways of implementing matrix transformations due to it being a very large library." ] }, { "cell_type": "code", "execution_count": 18, "id": "b3385f70", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([32, 6])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "torch.cat([emb[:, 0, :], emb[:, 1, :], emb[:, 2, :]], 1).shape # dependent on block size" ] }, { "cell_type": "code", "execution_count": 19, "id": "b669312a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([32, 6])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "torch.cat(torch.unbind(emb, 1), 1).shape # independent of block size" ] }, { "cell_type": "code", "execution_count": 20, "id": "4c6a5774", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([[True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True],\n", " [True, True, True, True, True, True]])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb.view(emb.shape[0], 6) == torch.cat(torch.unbind(emb, 1), 1)" ] }, { "cell_type": "code", "execution_count": 21, "id": "e01c0eb8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(torch.Size([32, 100]), torch.Size([100]))" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# making sure broadcasting works as expected: element-wise addition\n", "# 32, 100 ---> 32, 100\n", "# 100 ---> 1, 100\n", "(emb.view(-1, 6) @ W1).shape, b1.shape" ] }, { "cell_type": "code", "execution_count": 22, "id": "49d44139", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([[ 0.9989, -0.5693, 0.8112, ..., -0.7093, -0.9864, 0.6689],\n", " [ 0.1384, -0.0799, -0.9994, ..., 0.9321, 0.9163, -0.1549],\n", " [ 0.6756, 0.8763, 0.0039, ..., 0.5831, 0.9628, 0.7309],\n", " ...,\n", " [-0.4285, 0.9701, -0.9946, ..., 0.9994, 1.0000, 0.2847],\n", " [-0.8484, -0.4393, 0.9855, ..., -0.9131, -0.9705, 0.9912],\n", " [ 0.7571, -0.9000, 0.6245, ..., -0.4636, -0.9902, 0.7642]])" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "h = torch.tanh(emb.view(-1, 6) @ W1 + b1)\n", "h" ] }, { "cell_type": "code", "execution_count": 23, "id": "77e05be5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([32, 100])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(emb.view(-1, 6) @ W1).shape" ] }, { "cell_type": "code", "execution_count": 24, "id": "721c1994", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([32, 100])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "h.shape" ] }, { "cell_type": "code", "execution_count": 25, "id": "dcf3cf03", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([100])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b1.shape" ] }, { "cell_type": "markdown", "id": "014efc2c", "metadata": {}, "source": [ "------\n", "## 2.3. Output Layer\n", "-----\n", "Let’s create the output layer. The output layer computes logits which are then converted to a probability distribution. This layer is crucial for generating the final character predictions.\n" ] }, { "cell_type": "code", "execution_count": 26, "id": "fad1313f", "metadata": {}, "outputs": [], "source": [ "W2 = torch.randn((100, 27))\n", "b2 = torch.randn(27)" ] }, { "cell_type": "code", "execution_count": 27, "id": "d40a4e7b", "metadata": {}, "outputs": [], "source": [ "logits = h @ W2 + b2" ] }, { "cell_type": "markdown", "id": "99e98f0c", "metadata": {}, "source": [ "------\n", "## 2.4. Negative Log Likelihood Loss\n", "-----\n", "\n", "Next, using the logits, we must calculate the probabilities and the average negative log-likelihood loss. The loss is calculated using the probability distribution, which helps in updating the network’s parameters through backpropagation\n" ] }, { "cell_type": "code", "execution_count": 28, "id": "b6266283", "metadata": {}, "outputs": [], "source": [ "counts = logits.exp()" ] }, { "cell_type": "code", "execution_count": 29, "id": "a738b50f", "metadata": {}, "outputs": [], "source": [ "prob = counts / counts.sum(1, keepdim=True)" ] }, { "cell_type": "code", "execution_count": 30, "id": "f6b07893", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(1.)" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prob[0].sum()" ] }, { "cell_type": "code", "execution_count": 31, "id": "51d4dbfe", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "tensor([ 5, 13, 13, 1, 0, 15, 12, 9, 22, 9, 1, 0, 1, 22, 1, 0, 9, 19,\n", " 1, 2, 5, 12, 12, 1, 0, 19, 15, 16, 8, 9, 1, 0])" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y" ] }, { "cell_type": "code", "execution_count": 32, "id": "2057183a", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,\n", " 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "torch.arange(32) " ] }, { "cell_type": "code", "execution_count": 33, "id": "b556f7e9", "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "tensor([2.0816e-03, 9.8024e-05, 8.0746e-09, 3.6231e-06, 3.7387e-05, 7.2478e-09,\n", " 3.4388e-11, 1.2536e-08, 3.7001e-12, 5.4971e-07, 8.6313e-13, 1.9187e-05,\n", " 1.6919e-11, 1.5080e-05, 8.3559e-19, 7.8019e-04, 1.1049e-06, 5.4742e-03,\n", " 6.0078e-17, 3.2374e-04, 8.4933e-09, 3.1721e-11, 4.0807e-12, 3.5842e-07,\n", " 8.3485e-07, 3.9156e-01, 1.3974e-06, 3.8203e-11, 1.4490e-02, 3.9155e-07,\n", " 8.9099e-09, 1.6840e-04])" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prob[torch.arange(32), Y] # 32 = shape of input X" ] }, { "cell_type": "code", "execution_count": 34, "id": "0458c695", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(-16.5189)" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prob[torch.arange(32), Y].log().mean()" ] }, { "cell_type": "markdown", "id": "11d4377f", "metadata": {}, "source": [ "------\n", "## 2.5. Summary of the Full Network\n", "-----\n", "Let's take a look at an excerpt of the neural model in the paper before we put it all together in the model.\n", "\n", "----\n", "\n", "### Relevant Excerpt from the Bengio et. al. 2003 paper (Section 2)\n", "----\n", "**Section 2: A Neural Model**\n", "\n", "The training set is a sequence $w_{1} \\cdots w_{T}$ of words $w_{t} \\in V$, where the vocabulary $V$ is a large but finite set. The objective is to learn a good model $f\\left(w_{t}, \\cdots, w_{t-n+1}\\right)=\\hat{P}\\left(w_{t} \\mid w_{1}^{t-1}\\right)$, in the sense that it gives high out-of-sample likelihood. Below, we report the geometric average of $1 / \\hat{P}\\left(w_{t} \\mid w_{1}^{t-1}\\right)$, also known as perplexity, which is also the exponential of the average negative log-likelihood. The only constraint on the model is that for any choice of $w_{1}^{t-1}, \\sum_{i=1}^{|V|} f\\left(i, w_{t-1}, \\cdots, w_{t-n+1}\\right)=1$, with $f>0$. By the product of these conditional probabilities, one obtains a model of the joint probability of sequences of words.\n", "\n", "We decompose the function $f\\left(w_{t}, \\cdots, w_{t-n+1}\\right)=\\hat{P}\\left(w_{t} \\mid w_{1}^{t-1}\\right)$ in two parts:\n", "\n", "1. A mapping $C$ from any element $i$ of $V$ to a real vector $C(i) \\in \\mathbb{R}^{m}$. It represents the distributed feature vectors associated with each word in the vocabulary. In practice, $C$ is represented by a $|V| \\times m$ matrix of free parameters.\n", "\n", "2. The probability function over words, expressed with $C$ : a function $g$ maps an input sequence of feature vectors for words in context, $\\left(C\\left(w_{t-n+1}\\right), \\cdots, C\\left(w_{t-1}\\right)\\right)$, to a conditional probability distribution over words in $V$ for the next word $w_{t}$. The output of $g$ is a vector whose $i$-th element estimates the probability $\\hat{P}\\left(w_{t}=i \\mid w_{1}^{t-1}\\right)$ as in Figure 1.\n", "\n", "$$\n", "f\\left(i, w_{t-1}, \\cdots, w_{t-n+1}\\right)=g\\left(i, C\\left(w_{t-1}\\right), \\cdots, C\\left(w_{t-n+1}\\right)\\right)\n", "$$\n", "\n", "The function $f$ is a composition of these two mappings ( $C$ and $g$ ), with $C$ being shared across all the words in the context. With each of these two parts are associated some parameters. The parameters of the mapping $C$ are simply the feature vectors themselves, represented by a $|V| \\times m$ matrix $C$ whose row $i$ is the feature vector $C(i)$ for word $i$. The function $g$ may be implemented by a feed-forward or recurrent neural network or another parametrized function, with parameters $\\omega$. The overall parameter set is $\\theta=(C, \\omega)$.\n", "\n", "Training is achieved by looking for $\\theta$ that maximizes the training corpus penalized log-likelihood:\n", "\n", "$$\n", "L=\\frac{1}{T} \\sum_{t} \\log f\\left(w_{t}, w_{t-1}, \\cdots, w_{t-n+1} ; \\theta\\right)+R(\\theta)\n", "$$\n", "\n", "where $R(\\theta)$ is a regularization term. For example, in our experiments, **$R$ is a weight decay penalty applied only to the weights of the neural network and to the $C$ matrix, not to the biases**. ${ }^{3}$\n", "\n", "In the above model, the number of free parameters **only scales linearly** with $V$, the number of words in the vocabulary. It also **only scales linearly** with the order $n$ : the scaling factor could be reduced to sub-linear if more sharing structure were introduced, e.g. using a time-delay neural network or a recurrent neural network (or a combination of both).\n", "\n", "In most experiments below, the neural network has one hidden layer beyond the word features mapping, and optionally, direct connections from the word features to the output. Therefore there are really two hidden layers: the shared word features layer $C$, which has no non-linearity (it would not add anything useful), and the ordinary hyperbolic tangent hidden layer. More precisely, the neural network computes the following function, with a softmax output layer, which guarantees positive probabilities summing to 1 :\n", "\n", "$$\n", "\\hat{P}\\left(w_{t} \\mid w_{t-1}, \\cdots w_{t-n+1}\\right)=\\frac{e^{y_{w_{t}}}}{\\sum_{i} e^{y_{i}}}\n", "$$\n", "\n", "\n", "The $y_{i}$ are the unnormalized log-probabilities for each output word $i$, computed as follows, with parameters $b, W, U, d$ and $H$ :\n", "\n", "$$\n", "\\begin{equation*}\n", "y=b+W x+U \\tanh (d+H x) \\tag{1}\n", "\\end{equation*}\n", "$$\n", "\n", "where the hyperbolic tangent tanh is applied element by element, $W$ is optionally zero (no direct connections), and $x$ is the word features layer activation vector, which is the concatenation of the input word features from the matrix $\\mathrm{C}$ :\n", "\n", "$$\n", "x=\\left(C\\left(w_{t-1}\\right), C\\left(w_{t-2}\\right), \\cdots, C\\left(w_{t-n+1}\\right)\\right)\n", "$$\n", "\n", "Let \n", "* $h$ be the number of hidden units, and \n", "* $m$ the number of features associated with each word. \n", "\n", "When no direct connections from word features to outputs are desired, the matrix $W$ is set to 0 . The free parameters of the model are \n", "* the output biases $b$ (with $|V|$ elements), \n", "* the hidden layer biases $d$ (with $h$ elements), \n", "* the hidden-to-output weights $U$ (a $|V| \\times h$ matrix), \n", "* the word features to output weights $W$ (a $|V| \\times(n-1) m$ matrix), \n", "* the hidden layer weights $H$ (a $h \\times(n-1) m$ matrix), and \n", "* the word features $C$ (a $|V| \\times m$ matrix)\n", "\n", "$$\n", "\\theta=(b, d, W, U, H, C)\n", "$$\n", "\n", "The number of free parameters is $|V|(1+n m+h)+h(1+(n-1) m)$. The dominating factor is $|V|(n m+h)$. Note that in theory, if there is a weight decay on the weights $W$ and $H$ but not on $C$, then $W$ and $H$ could converge towards zero while $C$ would blow up. In practice we did not observe such behavior when training with stochastic gradient ascent.\n", "\n", "Stochastic gradient ascent on the neural network consists in performing the following iterative update after presenting the $t$-th word of the training corpus:\n", "\n", "$$\n", "\\theta \\leftarrow \\theta+\\varepsilon \\frac{\\partial \\log \\hat{P}\\left(w_{t} \\mid w_{t-1}, \\cdots w_{t-n+1}\\right)}{\\partial \\theta}\n", "$$\n", "\n", "where $\\varepsilon$ is the \"learning rate\". Note that a large fraction of the parameters needs not be updated or visited after each example: the word features $C(j)$ of all words $j$ that do not occur in the input window.\n", "\n", "---\n", "**${ }^{3}$ The $biases$ are the additive parameters of the neural network, such as $b$ and $d$ in equation 1 below.**" ] }, { "cell_type": "markdown", "id": "17ef8fa4", "metadata": {}, "source": [ "----\n", "\n", "Now, with knowledge from the excerpt above of the model parameters, their meanings and shapes; let's combine the previous steps to get a picture of the whole MLP neural network. The general parameters from the paper are:\n", "* `V` = vocabulary size (total number of characters)\n", "* `n` = the number of characters fed in as input as **context** into the model: block_size\n", "* `m` = number of features associated with each character/ lower-dimensional space\n", "* `C` = embedding lookup table matrix \n", " - `|V| * m` = size of `C`\n", " - `C[i]` = feature vector for **word i**\n", "* Weights\n", " - `H`, hidden layer weight matrix, (size: `h * (nm)`) --> `W1` \n", " - `U`, hidden-to-output weight matrix, (size: `|V| * h`) --> `W2`\n", " - `W`, word features to output weight matrix, (size: `|V| * (nm)`) --> `W3`\n", "* `b` = output bias matrix --> (size: `|V|`) --> `b2`\n", "* `d` = hidden layer bias matrix --> (size: `h`) --> `b1`\n", "* The number of free parameters: `|V|(1+(n+1)m+h) + h(1+nm)`\n", "* The number of free parameters excluding the use of `W3`: `|V|(1+m+h) + h(1+nm)`\n", "\n", "\n", "$$\n", "\\begin{equation*}\n", "y=b+W x+U \\tanh (d+H x) \\tag{1}\n", "\\end{equation*}\n", "$$\n", "
\n", "$$\n", "parameters: \\theta=(b, d, W, U, H, C)\n", "$$\n", "\n", "In our case, these are our parameters sizes:\n", "* `n`: 3 (block_size)\n", "* `m`: 2 lower-dimensional space (arbitrary choice)\n", "* `V`: 27 characters (26 alphabets + `.` character)\n", "* `C`: 27 $\\times$ 2 (vocabulary size $\\times$ m)\n", "* input layer: 6 nodes - 3 $\\times$ 2 (number of input characters $\\times$ m)\n", "* `W1`: 6 $\\times$ 100 (input features $\\times$ number of neurons in next (hidden) layer) \n", "* `b1`: 100 hidden layer elements\n", "* hidden layer: 100 (100 neurons is a hyperparameter we chose to start with)\n", "* `W2`: 100 $\\times$ 27 (number of neurons in previous (hidden) layer $\\times$ number of neurons in next (output) layer) \n", "* `b2`: 27 output layer elements\n", "* output layer: 27 neurons\n", "* The number of free parameters (excluding \n", " `W3`): `(27 * 2) + (6 * 100) + (100 * 27) + 100 + 27`\n", " - Using the general parameters' equation above (excluding the use of `W3`): `27*(1 + (1)*2 + 100) + 100*(1 + 3*2)`\n", " - Both ways should give the same answer of `3481`\n", " \n", "$$\n", "\\begin{equation*}\n", "y=b_{2}+W_{2} \\tanh (b_{1}+W_{1} x) \\tag{2}\n", "\\end{equation*}\n", "$$\n", "
\n", "$$\n", "parameters: \\theta=(b_{1}, b_{2}, W_{1}, W_{2}, C)\n", "$$\n", "\n", "---" ] }, { "cell_type": "code", "execution_count": 35, "id": "58519ace", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(3481, 3481)" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "27*(1 + (1)*2 + 100) + 100*(1 + 3*2), (27 * 2) + (6 * 100) + (100 * 27) + 100 + 27" ] }, { "cell_type": "code", "execution_count": 36, "id": "2a533b00", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(torch.Size([32, 3]), torch.Size([32]))" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.shape, Y.shape # dataset" ] }, { "cell_type": "code", "execution_count": 37, "id": "1c0125c2", "metadata": {}, "outputs": [], "source": [ "# ------------------- MLP Implementation ---------------------------------\n", "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n", "C = torch.randn((27, 2), generator=g)\n", "W1 = torch.randn((6, 100), generator=g)\n", "b1 = torch.randn(100, generator=g)\n", "W2 = torch.randn((100, 27), generator=g)\n", "b2 = torch.randn(27, generator=g)\n", "parameters = [C, W1, b1, W2, b2]" ] }, { "cell_type": "code", "execution_count": 38, "id": "408a80d2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3481\n" ] } ], "source": [ "par_count = 0\n", "for par in parameters:\n", " if len(par.shape) == 2: # [C, W1, W2]\n", " par_count += par.shape[0]*par.shape[1]\n", " else: # [b1, b2]\n", " par_count += par.shape[0]\n", "print(par_count)" ] }, { "cell_type": "code", "execution_count": 39, "id": "c40a8e25", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3481" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "27*2 + 6*100 + 100 + 100*27 + 27" ] }, { "cell_type": "code", "execution_count": 40, "id": "7c759127", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3481" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(p.nelement() for p in parameters) # number of parameters in total" ] }, { "cell_type": "code", "execution_count": 41, "id": "896b0b31", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(17.7697)" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[X] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "counts = logits.exp()\n", "probs = counts/counts.sum(1, keepdim=True) # (32, 27)\n", "loss = -probs[torch.arange(32), Y].log().mean()\n", "loss" ] }, { "cell_type": "markdown", "id": "99e23b0c", "metadata": {}, "source": [ "------\n", "## 2.6. `F.cross_entropy` & Why?\n", "-----\n", "\n" ] }, { "cell_type": "markdown", "id": "e2a9a8bf", "metadata": {}, "source": [ "We can calculate the classification error efficiently using the `F.cross_entropy()` function. In real-world applications, we prefer this approach. In our approach to calculating the loss, we made a few intermediate tensors which is fairly inefficient. However, `F.cross_entropy()` uses in-built fused kernels to calculate the loss directly. Moreover, backpropagation is also efficient (simpler backward pass to implement) since we use clustered mathematical operations.\n", "\n", "`F.cross_entropy()` is significantly more numerically stable compared to the approach in the previous section. It does not produce errors when extremely positive inputs are given unlike `exp()`. In general, **cross-entropy loss** is a metric used in machine learning to measure the performance of a classification model." ] }, { "cell_type": "code", "execution_count": 42, "id": "f4e46bc5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(17.7697)" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "F.cross_entropy(logits, Y)" ] }, { "cell_type": "markdown", "id": "1a47511c", "metadata": {}, "source": [ "------\n", "## 2.7. Training Loop + Overfitting One Batch\n", "-----\n", "\n", "Now let's implement the training loop of forward pass, backward pass and parameter updates\n" ] }, { "cell_type": "code", "execution_count": 43, "id": "8d37bcf0", "metadata": {}, "outputs": [], "source": [ "# ------------------- MLP Implementation ---------------------------------\n", "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n", "C = torch.randn((27, 2), generator=g)\n", "W1 = torch.randn((6, 100), generator=g)\n", "b1 = torch.randn(100, generator=g)\n", "W2 = torch.randn((100, 27), generator=g)\n", "b2 = torch.randn(27, generator=g)\n", "parameters = [C, W1, b1, W2, b2]" ] }, { "cell_type": "code", "execution_count": 44, "id": "39a91fe7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.2561391294002533\n" ] } ], "source": [ "for p in parameters:\n", " p.requires_grad = True\n", "\n", "for _ in range(1000):\n", " # forward pass\n", " emb = C[X] # (32, 3, 2)\n", " h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", " logits = h @ W2 + b2 # (32, 27)\n", " loss = F.cross_entropy(logits, Y)\n", " \n", " # backward pass\n", " for p in parameters:\n", " p.grad = None\n", " loss.backward()\n", "\n", " # update parameters\n", " for p in parameters:\n", " p.data += -0.1 * p.grad\n", "\n", "print(loss.item())" ] }, { "cell_type": "code", "execution_count": 45, "id": "4afc5636", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.return_types.max(\n", "values=tensor([13.3348, 17.7904, 20.6014, 20.6121, 16.7355, 13.3348, 15.9983, 14.1722,\n", " 15.9145, 18.3614, 15.9395, 20.9265, 13.3348, 17.1090, 17.1319, 20.0602,\n", " 13.3348, 16.5893, 15.1017, 17.0581, 18.5860, 15.9670, 10.8740, 10.6871,\n", " 15.5056, 13.3348, 16.1795, 16.9743, 12.7426, 16.2009, 19.0845, 16.0196],\n", " grad_fn=),\n", "indices=tensor([19, 13, 13, 1, 0, 19, 12, 9, 22, 9, 1, 0, 19, 22, 1, 0, 19, 19,\n", " 1, 2, 5, 12, 12, 1, 0, 19, 15, 16, 8, 9, 1, 0]))" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "logits.max(1)" ] }, { "cell_type": "code", "execution_count": 46, "id": "71892b86", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([ 5, 13, 13, 1, 0, 15, 12, 9, 22, 9, 1, 0, 1, 22, 1, 0, 9, 19,\n", " 1, 2, 5, 12, 12, 1, 0, 19, 15, 16, 8, 9, 1, 0])" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y" ] }, { "cell_type": "markdown", "id": "95b18c5e", "metadata": {}, "source": [ "---\n", "Right now, we're only **overfitting** $32$ examples of the $1$st $5$ words. Therefore, it's very easy to make this neural network fit only these $32$ examples because we have over $3400$ parameters. So we're overfitting a single batch of the data and getting a very small loss and subsequently good predictions. Basically, we have so many parameters for very few examples, which makes it very easy to train and fit the model.\n", "\n", "---" ] }, { "cell_type": "markdown", "id": "008e33bc", "metadata": {}, "source": [ "------\n", "## 2.8. Training on the Full Dataset + Minibatches\n", "-----\n", "We have a large number of examples (over $220,000$), and we need to calculate parameter gradients for all of them. Each iteration takes more time compared to working with a smaller dataset. Processing over $220,000$ examples through forward and backward passes is excessively laborious.\n", "\n", "Hence, we will execute forward and backward passes and perform updates on many smaller batches of data. We will randomly select a portion of the dataset (our **mini-batch**), and perform forward pass, backward pass and parameter updates exclusively on that batch, and then iterate over the many batches in the dataset." ] }, { "cell_type": "code", "execution_count": 47, "id": "e43f0e05", "metadata": {}, "outputs": [], "source": [ "# build the dataset\n", "\n", "block_size = 3 # context length: how many characters do we take to predict the next one?\n", "X, Y = [], []\n", "for w in words:\n", " context = [0] * block_size # start with padded context of just zero tokens\n", " for char in w + '.':\n", " ix = stoi[char]\n", " X.append(context)\n", " Y.append(ix)\n", " context = context[1:] + [ix] # crop context and append\n", " \n", "X = torch.tensor(X)\n", "Y = torch.tensor(Y)" ] }, { "cell_type": "code", "execution_count": 48, "id": "0f557f2e", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "(torch.Size([228146, 3]), torch.Size([228146]))" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.shape, Y.shape # dataset" ] }, { "cell_type": "code", "execution_count": 49, "id": "60a140f0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([167880, 65556, 167199, 114265, 44013, 109392, 87178, 19177, 51653,\n", " 70472, 214704, 13043, 98079, 137395, 176582, 224226, 168103, 10475,\n", " 23089, 36106, 18860, 97111, 84437, 92120, 159954, 190704, 139401,\n", " 156509, 20076, 226111, 1477, 211735])" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "torch.randint(0, X.shape[0], (32, ))" ] }, { "cell_type": "code", "execution_count": 50, "id": "3429629b", "metadata": {}, "outputs": [], "source": [ "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n", "C = torch.randn((27, 2), generator=g)\n", "W1 = torch.randn((6, 100), generator=g)\n", "b1 = torch.randn(100, generator=g)\n", "W2 = torch.randn((100, 27), generator=g)\n", "b2 = torch.randn(27, generator=g)\n", "parameters = [C, W1, b1, W2, b2]" ] }, { "cell_type": "code", "execution_count": 51, "id": "94b05311", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3481" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(p.nelement() for p in parameters) # number of parameters in total" ] }, { "cell_type": "code", "execution_count": 52, "id": "7c0caa7f", "metadata": {}, "outputs": [], "source": [ "for p in parameters:\n", " p.requires_grad = True" ] }, { "cell_type": "code", "execution_count": 53, "id": "4ac6c4c2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.552823066711426\n" ] } ], "source": [ "for _ in range(10000):\n", " \n", " # minibatch construct\n", " ix = torch.randint(0, X.shape[0], (32, ))\n", " \n", " # forward pass\n", " emb = C[X[ix]] # (32, 3, 2)\n", " h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", " logits = h @ W2 + b2 # (32, 27)\n", " loss = F.cross_entropy(logits, Y[ix])\n", " \n", " # backward pass\n", " for p in parameters:\n", " p.grad = None\n", " loss.backward()\n", "\n", " # update parameters\n", " lr = 0.01\n", " for p in parameters:\n", " p.data += -lr * p.grad\n", "\n", "print(loss.item())" ] }, { "cell_type": "code", "execution_count": 54, "id": "9ac7c51a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.5695, grad_fn=)" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[X] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Y)\n", "loss" ] }, { "cell_type": "markdown", "id": "e1880bb1", "metadata": {}, "source": [ "---\n", "Since we're only dealing with mini-batches, the quality of our gradient is lower so the direction is not as reliable because it's not the actual gradient direction. However, the mini-batch gradient direction is good enough even when it's estimating on only a subset of the dataset (i.e. in our case, $32$ examples). In fact, approximating the gradient and taking more steps is often better than calculating the exact gradient and taking fewer steps. This is why in practice, minibatches work quite well.\n", "\n", "---" ] }, { "cell_type": "markdown", "id": "c37d0e8c", "metadata": {}, "source": [ "------\n", "## 2.9. Learning Rate\n", "-----\n", "How do we determine the learning rate? How do we gain confidence that we're stepping with the right speed? Are we stepping too slow or too fast during gradient descent?\n", "\n", "A very low learning rate will impede the pace of learning for the model (the loss barely decreases) and a very high learning rate will make the model unstable (the loss explodes)." ] }, { "cell_type": "code", "execution_count": 55, "id": "c3d55000", "metadata": {}, "outputs": [], "source": [ "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n", "C = torch.randn((27, 2), generator=g)\n", "W1 = torch.randn((6, 100), generator=g)\n", "b1 = torch.randn(100, generator=g)\n", "W2 = torch.randn((100, 27), generator=g)\n", "b2 = torch.randn(27, generator=g)\n", "parameters = [C, W1, b1, W2, b2]" ] }, { "cell_type": "code", "execution_count": 56, "id": "6c1806d0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3481" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(p.nelement() for p in parameters) # number of parameters in total" ] }, { "cell_type": "code", "execution_count": 57, "id": "2469f649", "metadata": {}, "outputs": [], "source": [ "for p in parameters:\n", " p.requires_grad = True" ] }, { "cell_type": "code", "execution_count": 58, "id": "815515f0", "metadata": {}, "outputs": [], "source": [ "lre = torch.linspace(-3, 0, 1000)\n", "lrs = 10**lre" ] }, { "cell_type": "code", "execution_count": 59, "id": "8d846c0b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5.655977725982666\n" ] } ], "source": [ "lri = []\n", "lrei = []\n", "lossi = []\n", "for i in range(1000):\n", " \n", " # minibatch construct\n", " ix = torch.randint(0, X.shape[0], (32, ))\n", " \n", " # forward pass\n", " emb = C[X[ix]] # (32, 3, 2)\n", " h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", " logits = h @ W2 + b2 # (32, 27)\n", " loss = F.cross_entropy(logits, Y[ix])\n", " #print(loss.item())\n", " \n", " # backward pass\n", " for p in parameters:\n", " p.grad = None\n", " loss.backward()\n", "\n", " # update parameters\n", " lr = lrs[i]\n", " for p in parameters:\n", " p.data += -lr * p.grad\n", " \n", " # track stats\n", " lri.append(lr)\n", " lrei.append(lre[i])\n", " lossi.append(loss.item())\n", "\n", "print(loss.item())" ] }, { "cell_type": "code", "execution_count": 60, "id": "37cdbc98", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Loss')" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.plot(lri, lossi) #\n", "plt.title('Training Loss')\n", "plt.xlabel('learning rate')\n", "plt.ylabel('Loss')" ] }, { "cell_type": "code", "execution_count": 61, "id": "fc489bfd", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Loss')" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.plot(lrei, lossi) \n", "plt.title('Training Loss')\n", "plt.xlabel('exponent of learning rate, 10^lre')\n", "plt.ylabel('Loss')" ] }, { "cell_type": "markdown", "id": "a55f1631", "metadata": {}, "source": [ "---\n", "We evenly sampled points x in some interval (say $-3$ to $0$, use `torch.linspace`) and converted them to `10**x` to create a range of learning rates. Next, we ran a training loop for these various learning rates from our sampling and plotted the loss function as a function of the learning rate. Furthermore, we plotted the loss as a function of the learning rate exponent (`log10`). Typically from the $2nd$ plot, we see a **decreasing loss function at very low learning rates, then it plateaus and finally becomes unstable (explodes/increases significantly) at high learning rates**. That plateau region is our sweet spot window.\n", "\n", "We chose the optimal learning rate `lr = 0.1` and ran $3$ training loops. Then for the remaining training loops, we reduced the learning rate by a **factor of 10 (`lr = 0.01`)** as we were confident that the loss was now in the minimal territory range. This phenomenon is called **learning rate decay**.\n", "\n", "---" ] }, { "cell_type": "markdown", "id": "fcf6cfcf", "metadata": {}, "source": [ "------\n", "## 2.10. Train-Val-Test Splits & Why?\n", "-----\n", "Smaller loss does not always mean a better model. Sometimes, when the model is too big, with many neurons and/or parameters, the model capacity grows and therefore the model becomes more likely/capable to **overfit** our training dataset. Essentially, these bigger models will try to memorize the training set. The model will generate exact replicas of the training set when we try to sample from it. There will be no new data during sampling. In addition, loss evaluation on unseen data will be very high which implies that our model has little to no predictive power.\n", "\n", "Hence, this is why the concept of train-dev-test split is very important. We divide our data set into training set, development/validation set, and test sets. The **training set** is used to optimize the model parameters. The **validation set** is used for the development over all the model hyper-parameters (hidden layer size, embedding size, regularization strength, learning rate, etc.). The **test set** is used to evaluate the model performance. We use the test set only a few times and very sparingly to prevent overfitting the test set. A typical train-dev-test percent split ratio could be **80:10:10** for the size of our data ($<100,000$)." ] }, { "cell_type": "code", "execution_count": 62, "id": "7b11813a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch.Size([182625, 3]) torch.Size([182625])\n", "torch.Size([22655, 3]) torch.Size([22655])\n", "torch.Size([22866, 3]) torch.Size([22866])\n" ] } ], "source": [ "# build the dataset\n", "\n", "def build_dataset(words): \n", " block_size = 3 # context length: how many characters do we take to predict the next one?\n", " X, Y = [], []\n", " for w in words:\n", "\n", " #print(w)\n", " context = [0] * block_size\n", " for ch in w + '.':\n", " ix = stoi[ch]\n", " X.append(context)\n", " Y.append(ix)\n", " #print(''.join(itos[i] for i in context), '--->', itos[ix])\n", " context = context[1:] + [ix] # crop and append\n", "\n", " X = torch.tensor(X)\n", " Y = torch.tensor(Y)\n", " print(X.shape, Y.shape)\n", " return X, Y\n", "\n", "\n", "random.seed(42)\n", "random.shuffle(words)\n", "n1 = int(0.8*len(words))\n", "n2 = int(0.9*len(words))\n", "\n", "Xtr, Ytr = build_dataset(words[:n1])\n", "Xdev, Ydev = build_dataset(words[n1:n2])\n", "Xte, Yte = build_dataset(words[n2:])" ] }, { "cell_type": "code", "execution_count": 63, "id": "69a51194", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "32033" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(words)" ] }, { "cell_type": "code", "execution_count": 64, "id": "8a5611ee", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(25626, 3203, 3204)" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# train-dev-test split\n", "n1, n2-n1, len(words)-n2" ] }, { "cell_type": "code", "execution_count": 65, "id": "a4218b65", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(torch.Size([182625, 3]), torch.Size([182625]))" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Xtr.shape, Ytr.shape # dataset" ] }, { "cell_type": "code", "execution_count": 66, "id": "4e29284a", "metadata": {}, "outputs": [], "source": [ "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n", "C = torch.randn((27, 2), generator=g)\n", "W1 = torch.randn((6, 100), generator=g)\n", "b1 = torch.randn(100, generator=g)\n", "W2 = torch.randn((100, 27), generator=g)\n", "b2 = torch.randn(27, generator=g)\n", "parameters = [C, W1, b1, W2, b2]" ] }, { "cell_type": "code", "execution_count": 67, "id": "84a14416", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3481" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(p.nelement() for p in parameters) # number of parameters in total" ] }, { "cell_type": "code", "execution_count": 68, "id": "7c29e576", "metadata": {}, "outputs": [], "source": [ "for p in parameters:\n", " p.requires_grad = True" ] }, { "cell_type": "code", "execution_count": 69, "id": "d7e05708", "metadata": {}, "outputs": [], "source": [ "lre = torch.linspace(-3, 0, 1000)\n", "lrs = 10**lre" ] }, { "cell_type": "code", "execution_count": 70, "id": "f0ddb44e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.2595973014831543\n" ] } ], "source": [ "lri = []\n", "lossi = []\n", "\n", "for i in range(10000):\n", " \n", " # minibatch construct\n", " ix = torch.randint(0, Xtr.shape[0], (32, ))\n", " \n", " # forward pass\n", " emb = C[Xtr[ix]] # (32, 3, 2)\n", " h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", " logits = h @ W2 + b2 # (32, 27)\n", " loss = F.cross_entropy(logits, Ytr[ix])\n", " #print(loss.item())\n", " \n", " # backward pass\n", " for p in parameters:\n", " p.grad = None\n", " loss.backward()\n", "\n", " # update parameters\n", "# lr = lrs[i]\n", " lr = 0.01\n", " for p in parameters:\n", " p.data += -lr * p.grad\n", " \n", " # track stats\n", "# lri.append(lr)\n", "# lossi.append(loss.item())\n", "\n", "print(loss.item())" ] }, { "cell_type": "code", "execution_count": 71, "id": "535d977b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.5720, grad_fn=)" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[Xtr] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Ytr)\n", "loss" ] }, { "cell_type": "code", "execution_count": 72, "id": "c1a2d2e3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.5653, grad_fn=)" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[Xdev] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Ydev)\n", "loss" ] }, { "cell_type": "markdown", "id": "8f7a38bf", "metadata": {}, "source": [ "---\n", "The training and validation set losses are about equal, so we're not overfitting. This model is not powerful enough to just be purely memorizing the data. In other words, the model is **underfitting** the data because the training and validation losses are roughly equal. This means our neural network is very small/tiny. One performance improvement we can make to the model is by scaling up the size of this neural network. We can do this by:\n", "* _**increasing the `hidden layer size`**_\n", "* _**increasing the `embedding size`**_\n", "\n", "---" ] }, { "cell_type": "markdown", "id": "500a53cf", "metadata": {}, "source": [ "------\n", "## 2.11. Experiment: Larger Hidden Layer\n", "-----\n", "Let's increase the number of neurons in the hidden layer from $\\boldsymbol{100}$ to $\\boldsymbol{300}$ neurons." ] }, { "cell_type": "code", "execution_count": 73, "id": "c651dd2d", "metadata": {}, "outputs": [], "source": [ "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n", "C = torch.randn((27, 2), generator=g)\n", "W1 = torch.randn((6, 300), generator=g)\n", "b1 = torch.randn(300, generator=g)\n", "W2 = torch.randn((300, 27), generator=g)\n", "b2 = torch.randn(27, generator=g)\n", "parameters = [C, W1, b1, W2, b2]" ] }, { "cell_type": "code", "execution_count": 74, "id": "e59780f5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10281" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(p.nelement() for p in parameters) # number of parameters in total" ] }, { "cell_type": "code", "execution_count": 75, "id": "9bb1d1d4", "metadata": {}, "outputs": [], "source": [ "for p in parameters:\n", " p.requires_grad = True" ] }, { "cell_type": "code", "execution_count": 76, "id": "99ad09c0", "metadata": {}, "outputs": [], "source": [ "lre = torch.linspace(-3, 0, 1000)\n", "lrs = 10**lre" ] }, { "cell_type": "code", "execution_count": 77, "id": "394258be", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.492058277130127\n" ] } ], "source": [ "lri = []\n", "lossi = []\n", "stepi = []\n", "\n", "for i in range(30000):\n", " \n", " # minibatch construct\n", " ix = torch.randint(0, Xtr.shape[0], (32, ))\n", " \n", " # forward pass\n", " emb = C[Xtr[ix]] # (32, 3, 2)\n", " h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", " logits = h @ W2 + b2 # (32, 27)\n", " loss = F.cross_entropy(logits, Ytr[ix])\n", " #print(loss.item())\n", " \n", " # backward pass\n", " for p in parameters:\n", " p.grad = None\n", " loss.backward()\n", "\n", " # update parameters\n", "# lr = lrs[i]\n", " lr = 0.01\n", " for p in parameters:\n", " p.data += -lr * p.grad\n", " \n", " # track stats\n", " #lri.append(lr)\n", " stepi.append(i)\n", " lossi.append(loss.item())\n", "\n", "print(loss.item())" ] }, { "cell_type": "code", "execution_count": 78, "id": "631bdf21", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Loss')" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.plot(stepi, lossi)\n", "plt.title('Training Loss')\n", "plt.xlabel('Iterations')\n", "plt.ylabel('Loss')" ] }, { "cell_type": "code", "execution_count": 79, "id": "8344e3b1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.4651, grad_fn=)" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[Xtr] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Ytr)\n", "loss" ] }, { "cell_type": "code", "execution_count": 80, "id": "c140a9c4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.4563, grad_fn=)" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[Xdev] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,6) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Ydev)\n", "loss" ] }, { "cell_type": "markdown", "id": "3dad1806", "metadata": {}, "source": [ "---\n", "The training and validation losses are still roughly equal after increasing the hidden layer size. So we're not really improving the model much more and it still underfits. Therefore, the bottleneck could be the character embedding size.\n", "\n", "---" ] }, { "cell_type": "markdown", "id": "19f5c9df", "metadata": {}, "source": [ "------\n", "## 2.12. Visualize Character Embeddings\n", "-----\n", "So far we've used $\\boldsymbol{2}$-dimensional embeddings. This can be a bottleneck in our model.\n", "\n", "Let’s try to visualize the character embeddings." ] }, { "cell_type": "code", "execution_count": 81, "id": "f2c642f5", "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.figure(figsize = (8,8))\n", "plt.scatter(C[:,0].data, C[:,1].data, s = 200)\n", "\n", "for i in range(C.shape[0]):\n", " plt.text(C[i,0].item(),C[i,1].item(), itos[i], ha = \"center\", va = \"center\", color= \"white\")\n", "plt.grid('minor')" ] }, { "cell_type": "markdown", "id": "7b1108db", "metadata": {}, "source": [ "------\n", "## 2.13. Experiment: Larger Embedding Size\n", "-----\n" ] }, { "cell_type": "code", "execution_count": 82, "id": "ce25bdb6", "metadata": {}, "outputs": [], "source": [ "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n", "C = torch.randn((27, 10), generator=g)\n", "W1 = torch.randn((30, 200), generator=g)\n", "b1 = torch.randn(200, generator=g)\n", "W2 = torch.randn((200, 27), generator=g)\n", "b2 = torch.randn(27, generator=g)\n", "parameters = [C, W1, b1, W2, b2]" ] }, { "cell_type": "code", "execution_count": 83, "id": "266a18f7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "11897" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(p.nelement() for p in parameters) # number of parameters in total" ] }, { "cell_type": "code", "execution_count": 84, "id": "16c5906d", "metadata": {}, "outputs": [], "source": [ "for p in parameters:\n", " p.requires_grad = True" ] }, { "cell_type": "code", "execution_count": 85, "id": "dc5d63ae", "metadata": {}, "outputs": [], "source": [ "lre = torch.linspace(-3, 0, 1000)\n", "lrs = 10**lre" ] }, { "cell_type": "code", "execution_count": 86, "id": "2fd1cd4f", "metadata": {}, "outputs": [], "source": [ "lri = []\n", "lossi = []\n", "stepi = []" ] }, { "cell_type": "code", "execution_count": 87, "id": "4ec9f859", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.623623847961426\n" ] } ], "source": [ "for i in range(50000):\n", " \n", " # minibatch construct\n", " ix = torch.randint(0, Xtr.shape[0], (32, ))\n", " \n", " # forward pass\n", " emb = C[Xtr[ix]] # (32, 3, 2)\n", " h = torch.tanh(emb.view(-1,30) @ W1 + b1) # (32,100)\n", " logits = h @ W2 + b2 # (32, 27)\n", " loss = F.cross_entropy(logits, Ytr[ix])\n", " #print(loss.item())\n", " \n", " # backward pass\n", " for p in parameters:\n", " p.grad = None\n", " loss.backward()\n", "\n", " # update parameters\n", "# lr = lrs[i]\n", " lr = 0.01\n", " for p in parameters:\n", " p.data += -lr * p.grad\n", " \n", " # track stats\n", " #lri.append(lr)\n", " stepi.append(i)\n", " lossi.append(loss.log10().item())\n", "\n", "print(loss.item())" ] }, { "cell_type": "code", "execution_count": 88, "id": "b7c541e7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Log loss')" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.plot(stepi, lossi)\n", "plt.title('Training Loss')\n", "plt.xlabel('Iterations')\n", "plt.ylabel('Log loss')" ] }, { "cell_type": "code", "execution_count": 89, "id": "55a3b27a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.4539, grad_fn=)" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[Xtr] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,30) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Ytr)\n", "loss" ] }, { "cell_type": "code", "execution_count": 90, "id": "c2c0e2a0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.4613, grad_fn=)" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[Xdev] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,30) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Ydev)\n", "loss" ] }, { "cell_type": "code", "execution_count": 91, "id": "5aa3d703", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAewAAAHSCAYAAAAuWvi9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nO3de3zU1aHv/e/K5EJIgOCFiIHKRcDaRkQRqNcg3mC3itXdYnfdICotR3db3X3Odj/w9Gy7H7rbfY7V9hw33dhS2e15Gq0tlqdC2YJGrcpFyiWABQJqAwaiQoCEkMxlnT9mJkySmTDJTGZmzXzer1dezPwu81srM+Q7v/Vbv7WMtVYAACCz5aW7AAAA4OwIbAAAHEBgAwDgAAIbAAAHENgAADiAwAYAwAH56S5ALOedd54dNWpUuosRt5aWFpWUlKS7GClDfbNbrtVXyr06U9/MtGXLlo+ttedHW5exgT1q1Ci988476S5G3GpqalRVVZXuYqQM9c1uuVZfKffqTH0zkzHmg1jraBIHAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcQ2AAAOIDABgDAAQQ2AAAOILAzhM8f0InTXvkDNt1FAQBkoIwdSzwXtPn8Wl3boKU1+7WvsVn5eUa+gNX4YaX6etVYzaocrqJ8T7qLCQDIAAR2mmyrb9K85Zvk9QfU0u6XJHn9wbPrPUeatXjlTj2+ardWzJ+iiSPL0llUAEAGoEk8DbbXN+meZRvU1OrtCOuuWtr9amr1as6yDdpe35TiEgIAMg2BnWJtPr/mLt+kVm/0oO6q1Rvcvs0X3/YAgOxEk3iKra5tkNcf6LRsxNBirbhvimoPHddnK4Zo75GTevT5bTrtDW7n9Qe0pvawZk+qSEeRAQAZgDPsFFtasz9qM/jYYaX6xYYPdNMPX1Nzm0/3ThvVsa6l3a+lNXUpLCUAINMQ2CnkD1jta2yOuu5QU6u2fHBMkrRy6yFdNWpop/V7G5u55QsAchiBnUIt7T7l55mo66ztHMZdozk/z6il3ddPJQMAZDoCO4VKCvPli3GWPGLoQF3xqeDtW3dcfqE2v3+003pfwKqkkC4HAJCrCOwU8uQZjRtWGnXd/sZm3fu5UVr36A0aUlygX274oNP68cNK5Ylxdg4AyH6csqXYwqqxWrxyZ7eOZ76A1SPPbYu6T0mhRwurLk5F8QAAGYoz7BSbVTlcBZ7e/doLPHmaWXlBP5UIAOACAjvFivI9WjF/iooLzowRfvBYq2596vWo2xcXBLdnTHEAyG0EdhpMHFmm6gXTVFZcoJLC6EFcUuhRWXGBqhdMYyxxAADXsNNl4sgybVw0Q2tqD2tpTZ32dpqta5AWVo3VzMoLOLMGAEgisNOqKN+j2ZMqNHtShfwBq5Z2n0oK8+kNDgDohsDOEJ48o8EDCtJdDABAhuIaNgAADiCwAQBwAIENAIADCGwAABxAYAMA4AACGwAABxDYAAA4gMAGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcQ2AAAOIDABgDAAQQ2AAAOILABAHAAgQ0AgAMIbAAAHEBgAwDgAAIbAAAHENgAADiAwAYAwAEENgAADiCwAQBwAIENAIADCGwAABxAYAMA4AACGwAABxDYAAA4ICmBbYy5zRizxxhTZ4x5LMr6TxljXjXGbDXG7DDGzErGcQEAyBUJB7YxxiPpaUkzJV0q6R5jzKVdNlss6Xlr7SRJcyT9W6LHBQAglyTjDHuKpDpr7QFrbbukakl3dNnGShocejxE0odJOC4AADnDWGsTewFj7pZ0m7X2gdDzeyVNtdY+HLHNcEn/KWmopBJJN1lrt0R5rQWSFkhSeXn5ldXV1QmVLZWam5tVWlqa7mKkDPXNbrlWXyn36pzL9Q1Yqzxj0lyi6KZPn77FWjs52rr8FJXhHknPWmufMMZ8TtIvjDGftdYGIjey1i6TtEySJk+ebKuqqlJUvMTV1NTIpfImivpmt1yrr5R7dc6l+rb5/Frz8iv63lajfY3Nys8z8gWsxg8r1derxmpW5XAV5XvSXcyzSkaT+CFJIyOejwgti3S/pOclyVr7tqQBks5LwrEBAIhpW32Tpi5Zr0PHWrX3SLOslbx+K2ulPUeatXjlTk1dsl7b65vSXdSzSkZgb5Y0zhgz2hhTqGCnslVdtvmLpBmSZIz5tIKB/VESjg0AQFTb65t0z7INamr1KhDj8m9Lu19NrV7NWbYh40M74cC21vokPSxpraR3FewNvssY811jzO2hzf5e0oPGmO2SfiVpnk304jkAADG0+fyau3yTWr3+uLZv9Qa3b/PFt306JOUatrV2taTVXZZ9J+LxbknXJONYAACczeraBnn9nbpJ6aHpF+uuKyr0SUu7GppaVXvohJ5540DHeq8/oDW1hzV7UkWqixsXRjoDAGSdpTX71dJ+5mz5/HOG6AsTh2vWj9/QfT/frMtGlHXbp6Xdr6U1daksZq+kqpc4AAAp4Q9Y7Wts7rTswvPP1dpdR3TaG5AU0Lp3j0Tdd29js/wBK09e5t32xRk2ACCrtLT7lN/HwM3PM2pp9yW5RMlBYAMAskpJYb58gc79mj9s/ES3XFquovw8lRR6NOPT5VH39QWsSgozs/E5M0sFAEAfefKMxg0r1d4jZ5rFPzp2XL/f0aA137xOn7S0a8fB6LdwjR9WmpHN4RJn2ACALLSwaqxKCjuPXvb0q3W68YnX9Nc/eVvvfdzSbZ+SQo8WVl2cqiL2GoENAMg6syqHq8DTu4gr8ORpZuUF/VSixBHYAICsU5Tv0Yr5U1RcEH2M8KfW7et0D3ZxQXD7TB5TnMAGAGSliSPLVL1gmsqKC2LOzlVS6FFZcYGqF0zTxJHd783OJHQ6AwBkrYkjy7Rx0QytefkVTSgv1N5Os3UN0sKqsZpZeUFGn1mHEdgAgKxWlB88i177yA3yB6xa2n0qKczP2N7gsRDYAICc4ckzGjygIN3F6BOuYQMA4AACGwAABxDYAAA4gMAGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcQ2AAAOIDABgDAAQQ2AAAOILABAHAAgQ0AgAMIbAAAHEBgAwDgAAIbAAAHENgAADiAwAYAwAEENgAADiCwAQBwAIENAIADCGwAABxAYAMA4AACGwAABxDYAAA4gMAGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcQ2AAAOIDABgDAAQQ2AAAOILABAHAAgQ0AgAMIbAAAHEBgAwDgAAIbAAAHENgAADiAwAYAwAEENgAADiCwAQBwQFIC2xhzmzFmjzGmzhjzWIxtvmSM2W2M2WWM+f+ScVwAAHJFfqIvYIzxSHpa0s2SDkrabIxZZa3dHbHNOEn/KOkaa+0xY8ywRI8LAEAuScYZ9hRJddbaA9badknVku7oss2Dkp621h6TJGttYxKOCwBAzjDW2sRewJi7Jd1mrX0g9PxeSVOttQ9HbPOipL2SrpHkkfRP1to/RHmtBZIWSFJ5efmV1dXVCZUtlZqbm1VaWpruYqQM9c1uuVZfKffqTH0z0/Tp07dYaydHW5dwk3ic8iWNk1QlaYSk140xldbapsiNrLXLJC2TpMmTJ9uqqqoUFS9xNTU1cqm8iaK+2S3X6ivlXp2pr3uS0SR+SNLIiOcjQssiHZS0ylrrtda+p+DZ9rgkHBsAgJyQjMDeLGmcMWa0MaZQ0hxJq7ps86KCZ9cyxpwnabykA0k4NgAAOSHhwLbW+iQ9LGmtpHclPW+t3WWM+a4x5vbQZmslfWKM2S3pVUn/l7X2k0SPDQBArkjKNWxr7WpJq7ss+07EYyvp0dAPAADoJUY6AwDAAQQ2AAAOILABAHAAgQ0AgAMIbAAAHEBgAwDgAAIbAAAHENgAADiAwAYAwAEENgAADiCwAQBwAIENAIADCGwAABxAYAMA4AACGwAABxDYAAA4gMAGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcQ2AAAOIDABgDAAQQ2AAAOILABAHAAgQ0AgAMIbAAAHEBgAwDgAAIbAAAHENgAADiAwAYAwAEENgAADiCwAQBwAIENAIADCGwAABxAYAMA4AACGwAABxDYAAA4gMAGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcQ2AAAOIDABgDAAQQ2AAAOILABAHAAgQ0AgAMIbPTZidNe+QM23cUAgJyQn+4CwB1tPr9W1zZoac1+fWHYcT34zy/LF7AaP6xUX68aq1mVw1WU70l3MQEgKxHYiMu2+ibNW75JXn9ALe1+aZjk9QfPrvccadbilTv1+KrdWjF/iiaOLEtzaQEg+9AkjrPaXt+ke5ZtUFOrNxjWUbS0+9XU6tWcZRu0vb4pxSUEgOxHYKNHbT6/5i7fpFZv9KDuqtUb3L7NF9/2veHzB7huDiBnJaVJ3Bhzm6QfSfJI+qm19vsxtrtL0guSrrLWvpOMY6N/ra5tkNcf6Lb8r66foqobBqqoIE8/f/M9/WpTfcc6rz+gNbWHNXtSRcLHj7xuvq+xWfl5huvmAHJSwmfYxhiPpKclzZR0qaR7jDGXRtlukKRvStqY6DGROktr9kdtBl+3Yau+8L/+qC/8zz9q3tWjVTawoGNdS7tfS2vqEj72tvomTV2yXotX7tTeI82yNnjd3Noz182nLllPEzyAnJCMJvEpkuqstQeste2SqiXdEWW7f5b0A0mnk3BMpIA/YLWvsTnquokTxmjNN6/Tiw9do+FlAzT63JJO6/c2NifUdM11cwDoLBmBXSGpPuL5wdCyDsaYKySNtNa+lITjIUVa2n3KzzPdlk8bc45GXnC+7vy3NzXzR29o94cnVFTQ+aOUn2fU0u7r03Ez6bo5AGSKfr+tyxiTJ+mHkubFse0CSQskqby8XDU1Nf1atmRqbm52qrzx+sal3m7LRo/Ik/xePXRJu4YOLtXki8r0pdF+XVPSOaD/tOHNPh2zqdWrB8edVsDGf4aeZ/xa8/IrKisuOPvGfZCt728suVZfKffqTH3dY2wv/ihGfQFjPifpn6y1t4ae/6MkWWv/JfR8iKT9ksJtqxdIOirp9p46nk2ePNm+8447/dJqampUVVWV7mIk3S1Pvqa9Rzo3ixd68vTyQ1fIm1+iAx81a3BxgZ5at1cbDhzt2GZCeanWPnJD0o4pSV+8okIPXjdGkvRuwwk9+vz2TusTOebZZOv7G0uu1VfKvTpT38xkjNlirZ0cbV0yzrA3SxpnjBkt6ZCkOZK+El5prT0u6byIwtRI+ja9xN2wsGqsFq/c2ek6crs/oFWvbtATtdE/PiWFHi2surhPx4t13XzcsFI9PP1i3bX0LR075dWQKGfS4evmnijN+ADguoSvYVtrfZIelrRW0ruSnrfW7jLGfNcYc3uir4/0mlU5XAWe3n1MCjx5mll5QZ+OF+u6+dUXn6fVtYd17FSwif54a/em+kSumwNApkvKNWxr7WpJq7ss+06MbauScUykRlG+RyvmT9GcZRvi6gRWXBDcvq/3RpcU5svXx97lvoBVSSGj7QLITox0hrOaOLJM1Qumqay4QCWF0YO4pNCjsuICVS+YltBY4p48o3HDSrstf6vuY82qvKDjfu9oTeLjh5XSHA4ga3E6grhMHFmmjYtmaE3t4dCgKE0q8IRHHRukhVVjNbPygqSMOhbtuvm+xmY9/WqdnlvwOQWs1a4Pj+vbv97RsT6R6+YA4AICG3Eryvdo9qQKzZ5UoZqaGm256xqVFOYn/ax2VuVwPb5qt6TOTfC/+dMh/eZPh6Luk8h1cwBwAU3i6LPBAwr6pQk6fN28uCC+s/VEr5sDgAsIbGSkVF43BwAX0CSOjNX1uvneTrN1Jfe6OQBkOgIbGS3yurk/YNXS7uuX6+YAkOkIbDjDk2c0eED/jBUOAJmOa9gAADiAwAYAwAEENgAADiCwAQBwAIENAIADCGwAABxAYAMA4AACGwAABxDYAAA4gMAGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcQ2AAAOIDABgDAAQQ2AAAOILABAHAAgQ0AgAMIbAAAHEBgAwDgAAIbAAAHENgAADiAwAYAwAEENgAADiCwAQBwAIENAIADCGwAABxAYAMA4AACGwAABxDYAAA4gMCGk3z+gE6c9sofsOkuCgCkRH66CwDEq83n1+raBi2t2a99jc3KzzPyBazGDyvV16vGalblcBXle9JdTADoFwQ2nLCtvknzlm+S1x9QS7tfkuT1B8+u9xxp1uKVO/X4qt1aMX+KJo4sS2dRAaBf0CSOjLe9vkn3LNugplZvR1h31dLuV1OrV3OWbdD2+qYUlxAA+h+BjYzW5vNr7vJNavVGD+quWr3B7dt88W0PAK4gsJHRVtc2yOsP9Gofrz+gNbWH+6lEAJAeBDYy2tKa/Z2awUcMLdbab13f8fzB68boWzeN67RPS7tfS2vqUlZGAEgFAhsZyx+w2tfY3Kd99zY2c8sXgKxCYCNjtbT7lJ9n+rRvfp5RS7svySUCgPQhsJGxSgrz5etyluzzW0VmeFFB9I+wL2BVUshdiwCyB4GNjOXJMxo3rLTTso+b23RuaZHKBhao0JOnGZcMi7rv+GGl8vTx7BwAMhGBjYy2sGqsSgrPjF7mC1j9eP0+/e6ha/SLB6Zo/0fdr3GXFHq0sOriVBYTAPodbYbIaLMqh+vxVbslnekp/uxb7+vZt96PuU+BJ08zKy/o/8I5xOcP6JTXr5LCfFoeAEcR2MhoRfkerZg/RXOWbYhr8JTiguD2jCnO2OtAtqFJHBlv4sgyVS+YprLigk7N45FKCj0qKy5Q9YJpjCWu4NjrU5es1+KVO7X3SLOsDY69bu2ZsdenLlnPMK6AQwhsOGHiyDJtXDRDS+6s1ITyUhkjFXiMjJEmlA/SkjsrtXHRDMJajL0OZCuaxOGMonyPZk+q0OxJFfIHrFrafVyT7aKvY69vXDSD5nEgwyXlDNsYc5sxZo8xps4Y81iU9Y8aY3YbY3YYY9YbYy5KxnGRHXz+gE6c9vZqZDJPntHgAQWEdReMvQ5kr4TPsI0xHklPS7pZ0kFJm40xq6y1uyM22yppsrX2lDFmoaR/lfTlRI8Nd9Ehqn90HXtdku6/drS+NHmkJOm5zX/R8jff77Q+PPb67EkVqSomgD5IRpP4FEl11toDkmSMqZZ0h6SOwLbWvhqx/QZJX03CceGobfVNmrd8k7z+QEe4eP3Bs+twh6jHV+3WivlTuCbdC9HGXv9sxWD99eQRmv30mzJGevGha7TxvaPa9eGJTtuFx16nxQLIXMloEq+QVB/x/GBoWSz3S1qThOPCQXSI6j/Rxl6/atQ5WrvriFq9fp1q9+sPOw/rqlHndNuXsdeBzGesTWxGI2PM3ZJus9Y+EHp+r6Sp1tqHo2z7VUkPS7rBWtsWZf0CSQskqby8/Mrq6uqEypZKzc3NKi0tPfuGWaIv9bWS3m040etr1Z8ePljpPu9z5f2tPXS80/OJE8ZoQFGhNu74syRp6mWXqLWtXTv2HOi2b2XFkI7HrtQ3mXKtztQ3M02fPn2LtXZytHXJaBI/JGlkxPMRoWWdGGNukrRIMcJakqy1yyQtk6TJkyfbqqqqJBQvNWpqauRSeRPVl/qu3HpQT7+ys9uZ9ezLKzTvmlEq9Bhtq2/S4hd3KpzpJYUeLblknD5/2fC0jtTlyvv7vSdf094jZ5rFP/NJk/7HX0/U038+ICOjW2ZcqMef36ZdH3b+rz+hvFR/9zc3dDx3pb7JlGt1pr7uSUZgb5Y0zhgzWsGgniPpK5EbGGMmSfp3Bc/EG5NwTDgoWoeoseeX6vMTh+vupW/JF7D65zs+q9mTKvTbPwW/87W0+/VfX9ihR57flrEd0zJp2M+FVWO1eOWZL0W7PjyhF7Yc1O8eulZSsNNZ1+vXjL0OuCHhwLbW+owxD0taK8kjabm1dpcx5ruS3rHWrpL03yWVSvq1MUaS/mKtvT3RY8Md0TpESdI1F5+ryoohWvXwNZKkogKPPmnp3ADTHrpNKZM6pmVqL/doY6//7I/v6Wd/fC/mPoy9DrghKQOnWGtXS1rdZdl3Ih7flIzjwF3hDlHh0A0zxug3Ww7qX9fuOetr/Gbh1bpr6Vuh1/NL8mvOsg0pH440k3u5M/Y6kL0YmhQpUVKYL1+UzmZv1n2smZXDdW5JoSRpSHGBKsqKo75GOKwjhUfqavPFN7JXorbXN2nOv7+d0b3cGXsdyE4ENlLCk2c0blj3Hpp1jc164j/36Bf3T9Gab16nX94/VcMGFUV9jV2P3xp1eSpG6rKSnn+nXnf+25s67YtvJLFUf5mIxNjrQPZhLHGkTNcOUWG/39Gg3+9o6PPr9vdIXdvqm/Ruwwk9tetM7/Wwv7vxYs2eVKGjLe1qaGpV7aETeuaNM7dMhb9MpGMUMcZeB7ILZ9hImVmVw1Xg6Z+PXHikrmRq8/n1o3V7defTb8ofsGr3BTR4QL6+Oi04FP5lI4Zo5mcv0KwfvaF5yzepckT3s9Xwl4l0Y+x1wH0ENlIm3CGquCD5HZySPVLXtvomTfl/1+nJdfsU+TVgcHGB7g0F9uSLhurl3UfU5gt2Plv/7pGor9UfXyYApF9fJi5KBE3iSKlwh6i5XXpZJ8oXsCopTM7H+U8fHNNXntkQ9Vr1P9x2iS46d6BWf+NaNZ3yavP7R8/6euEvE4MHFCSlfADSJ523dHKGjZTrqUNUYR+bzMcPK02oubfN59fKrQd18w9f0xeXvhWzY9kP/vBnffDJKc368R/1/T/8WTM+Xa6i/DwNLPToxkuGRd0nmV8mAKTPtvomTV2yXotX7tTeI82yNnhLp7VnbumcumR9v90dwl8RpEWsDlGrth+K2jFNkj7z39ZGfa1ER+qKdl91pOICj75QdaVuunGgCvPzOpr0dxw8rnXvHtGab16nj5vbtefISZ087e22f6JfJgCkX3jiop7GN+jv8SE4w0baRXaI6kvHtERG6opn9rAbJpyvltbTmvmjNzR3+Sa1tJ25Vr7s9QO68YnX9LfLN6qirLjb5BsM+wm4r80XvEUznsGIpP67pZPARkqdrZNGbzumJTJSV7z/CfccPqmRFwzTY7ddonHDSlUcMRjJv3yxUqu/ca1e+rvr9Iedh7uN082wn4D7Vtc2yOuPb/yFsP4YH4ImcfS73nTS8PkDGn1+if73A1M1/9nNMZupSwo9KvDkJTT8Z7z/Cd/7uEXVa2r0Z/+F+voNY9V82qe137peNXsa9c3qbTH3Y9hPIDtEm7jobPpjfAgCG/0qnnG3/+l3uzT/2tF6qbahU6BfPKxUU0edo43vfaK6j1oign6QFlaN1czKCxIKw67/CS8bMUQ/uOsyzX76TeUZo989fI02HvhER1u88n30Z724/ZBOnPZqzlUjteAXW2K+bpHHqLgwP20TkwBInlgTF8UjfEtnsvqwENjoN107aYwYWqyfzb1Ktz71esc24cB8ct2+jmXhQN93pFkfHmtVgSdPv114tcYOK03aSF3R/hOGO5H9/S0TNKAgTy9uPaTC/DyVDxqgm6+8XlU3BMu2+MWdMV83z0hL7qzUFy6/kDNrIAvEmrgoHsm+pZPARr/obSeNWMK9Lr/yzMak9rqM9Z/wx+v36c3HbtTgAQWqPXS8Y7jRX9Vt1RO1Pf93KS7wMJkGkGViTVwUj2Tf0kmnM/SLWNeHPXlG//LFSv3nI9frP+ZPUVF+fB/BZPe6LCnMj/qNedqYc1RWXKiDx1r1X375J10WZbjRaIYw8xWQlWJNXCRJP593VczJiqTk39JJYKNfxOqkMercgfrF2x/olidf14nTXs387PC4XzMZvS7DA6RM/x+vRl2/+K8u1St/btTKrYf08I0Xa12M4UbDjKRHbhqnTcx8BWSthVVjo05Ve9+zm9V4si3qPv1xSyeBjaTrqZNG/bFW7W4I3vq089BxjTjnzNzXj9w8XvOvGdXx/Nu3TNB9Ec8TnUgjPErRot/W6i9HW7ut/+IVFQpYac+Rk1paU6eJI4Zo+JABMV9v8IB8vfjQNfrmTeO5Xg1ksVSPDxELgY2kC18fjqY9YshPf0Cdtvv1O/X64hUjJEnGSF+YOFwrtx7qtH9fJ9KIHCDllLd7U/3fTP2UHrh2tIry87TwhrH61YPT9Dc/3ahLLxwS8zU3/N+cVQO5IJXjQ/SEwEbS9bWTxsFjrTp2ql2fuXCwrh93vnZ9eEJNpzoP9dmXWbni6QD3vzf+RbN+/Efd8uTrajx5WhedO1DPzp+iHQejjwk8obxUAxkfHMgZ4YmLyooLojaPS8Fm8LJ+7M/CXxwkXbiTxt4jvb938bnN9br7yhE6v7RIz79T3219X3pdRnaAW3D9GLX7Anr2rff1/3z+0/r08MH6yjMb9bmx5+rLk0fq5GmffrPlYKfbzCTp7yvPPGa4USA3hScuWlN7WEtr6rS300BQyRkfoicENvrFwqqx3SbxOHistdM92M+8caDbfmt3HdYjN49XQV6evlG9tdv6vvS6jOwAt/m9o3rgujF69q33VVlRpsL8POXnGU0ZdY58/oAqhhbrO6ti32ctMdwokMtiTVyUigl+aBJHv+hLJw0pODDJhv2f6KXaD9W1Vb0vZ7ZdO8DVHjquyoohKi3KV7svoK1/OabLRgxR1YTzdeWoc/St57bK9tCaPyA/j+FGAUjqPHFRKhDY6Be97aQRZow06VNlem5z9+bwvpzZdu0A5wtY1R87pbuvHKEtfzmmTe8d1bQx52p8+SANLPSo+sFpWv2Na/X9uyq7vVaekZ772ufoaAYgLWgSR78Jd9KY28Nc05EuHlaq5XOv0trdh/X+J6c6retrr8toHeA2v39UD14/Rv/1he3ac/ikFn/+Uv2x7mN9rYfxwfOM0Q/uuoywBpA2nGGjX4U7aSy5s1ITyktljFTgMTJGmlA+SI/eNF5DQr0u6xqbdf1/f1VLXnq3Y/9Ee11GG6Vo03tHNWxQkQyShb0AABYvSURBVP70QZM+bm5Xm9evTe8d7fF1jJFuv/zCXh8fAJKFM2z0u7N10vha1Zh+7XXZtQPcW/s/0bhFazrW3/jEaz3uX1zg0ejzBshjjE6c9qasgwkARCKwkVLhThqR+rvX5azK4Xp81W5JvRuHvKTQo3xPnuZfM0oHj+7VuMVrepzLGwD6E03iyCj90euyLx3gPnXOQC24foxkrZa9fkCnvX5ZG+zFbu2ZubynLlmv7fXRB1cBgGQisJET4hmlaGBBnoYMyNdvF16t/3nPJP3ktQM6ftoXs7NcS7tfTa1ezVm2gdAG0O8IbOSMs3WA+94XL9OmxTfpMxWDezWXd7Kn/gSAaLiGjZwSz/XylVsPdpvLe1BJsdY9erU2v39UV140VIePn9aD//GO2kKTmYSn/pw9qSKl9QGQOzjDRs6Kdb28L3N5Jzr1JwCcDYENROjrXN5S36f+BIB4ENhAhL7O5S31bepPAIgXgQ1E6Otc3lLfpv4EgHgR2ECEaEOZxqsvU38CQLwIbKCLhVVju92rfbKl+1zeT63b1/G8L1N/AkBvENhAF32Zy7svU38CQG8Q2EAXvR3KtK9TfwJAbxDYQBTxDGWa6NSfANAbdGkFYggPZbqm9rA+2rdVxijpU38CQLwI7Bzi8wd0yutnPudeCA9lWnN8n+qW3JD0qT8BIF4EdpZr8/m1urZBS2v2a19jM/M5JyDaXN4AkCoEdhbbVt+kecs3yesPdIyN7fUHBwUJz+f8+KrdWjF/CtdgASDD0eksS22vb9I9yzaoqdWb1vmcff6ATpz2MsY2ACSIM+ws1Obz92k+542LZiSleZxmeABIPs6ws9Dq2oZu8zmfTXg+50Rtq2/S1CXrtXjlTu090ixrg83w1p5php+6ZH2/ndEDQLbiDDsLRc7nPGJosVbcN0Vb64/pik8N1Y6Dx/XrLfV65KbxOre0SN+q3qrtB493zOc8e1JFn48bbobv6cw+WC6/5izbwP3LANALnGFnmWjzOV907kA98/p7mvHD1zT2/FLdcXmF7v7J2/re6nf10PQz418nMp9ztGb4EUOLtfZb10fdPtwM3+aLr9keAHIdgZ1los3nXH+sVXuOnJS10t7Gk3qz7mNJ0p8Pn9CIocUd2yUyn3M6m+EBIBcQ2Fkm2nzO7b4zQWqt7XhureTJO/MRSGQ+58hm+GhGnlOsl75xrS4bMaRjWbgZHgBwdgR2lknHfM7RmuEjjTmvRD/56pX69q+3a8fB453WJdIMDwC5hMDOQtHmcz6bROZzjtYMH3ZOSaGe+dvJ+mb1Nr3bcLLb+kSa4QEglxDYWShyPueDx1p161Ovd6z79q93aM3Ow93WJTKfc7Rm+LCTp7061NSqq0YNjbo+kWZ4AMglBHYWSvV8zj01w3v9Vl/7xRZ98YoRun3ihd3W97UZHgByDYGdpVI9n3NPzfCtXr/uf3az7r92tG769LBOx+9rMzwyD8PQAv2LtsgsFjmf89KaOu3tNExocudznlU5XI+v2i3pTE/xyCb3E6d9uuPpNzvtk0gzPDIDw9ACqUNgZ7nwfM6zJ1XIH7D9Np9zuBl+zllGOgtLtBke6cdscEBqJaVJ3BhzmzFmjzGmzhjzWJT1RcaY50LrNxpjRiXjuOid8HzO/XHNuM3n14GPm3X+oKIet0tmMzzSJ3I2uKElhVFHtEvFbHDxoKke2SLhM2xjjEfS05JulnRQ0mZjzCpr7e6Ize6XdMxae7ExZo6kH0j6cqLHRmaIdqYVzafOGahHbx6ftGZ4pEe6Z4OLB031yEbJOMOeIqnOWnvAWtsuqVrSHV22uUPSitDjFyTNMMbQNTgLxJp3e9fjt3bb9qOTbRp9Xgl/KB0XbRja/Dyjp758udY9eoP+7W+u0ICCzn9aUjkMLTPGIVsZaxNrJjLG3C3pNmvtA6Hn90qaaq19OGKbnaFtDoae7w9t83GX11ogaYEklZeXX1ldXZ1Q2VKpublZpaV9G2HMRc3NzSopLdW7DSeiNjV+7Ut/pX9//qVuyz15Rp8ePliufVvLxfc3Vn33NTbrdMTZ9aCSYs2bfYte+M831PDRUc2YdrmOHj+pre/u77TfgAJPn0fhi1er168DH7UoEMfftTxjNOb8ko7bH3mPs5sr9Z0+ffoWa+3kaOsyqtOZtXaZpGWSNHnyZFtVVZXeAvVCTU2NXCpvompqanRsyMV6+pWdUZvB598tPVHb/eNVUujRkkvGJTSNZzrk4vsbrb7+gNV9i1bL2jPv7Yih+bq5qVXffuWEpHy9dapB9109Sk/UftBpX2OkuiU39Nt9920+v6YuWa+m1u4tOPOuHqWvTrtIOw8d17ee29axvKzYr42LqlSU7+E9znLZUN9kNIkfkjQy4vmI0LKo2xhj8iUNkfRJEo6NNDrbhB/RMOGH22INQ9u1pS7a+W1/D0Pb04xx9067SF/96cZOYS0xYxzckozA3ixpnDFmtDGmUNIcSau6bLNK0tzQ47slvWITbYtH2vU04UdPmPDDXbGGoR0xdKCu+FSw1/8dl1+oze8f7bZNfw9DG+sL5JLZn9XIcwbq2flX6f5rR3daxxdIuCThwLbW+iQ9LGmtpHclPW+t3WWM+a4x5vbQZj+TdK4xpk7So5K63foFtwSsjTnhx9kw4Ye7Yg1Du7+xWfd+bpTWPXqDhhQX6JcbPui2TX8OQ9vTjHGLXtypxpOndc+yDfrZH9/rtp4vkHBFUr7uWmtXS1rdZdl3Ih6flvTXyTgWMkOeMTEn/DgbJvxw28KqsVq88kzfhYPHWjXjh6/1uE9/D0MbbqoPD9zSG3yBhCsYSxx9lup5t5EZImeDi1d/D0Pb04xxZ8MXSLiCwEaf9TThx2f+29qoy5nww32png0uHj3NGHc2fIGEKwhs9FkmnmkhNVI9G1w8evoCGUu0L5AMZYpMRTsQ+owJP3JbKmeDi0e0GePCrv3Bq1H3CX+BbPMFxz2/5cnXGMoUGYvARkLCZ1pzexhLvKTQowJPHrM2ZaFUzQYXb1n68gXy3YaTmrd8kx4cd1p7j7RLYtYxZCaaxJGw8JnWkjsrNaG8VMZIBR4jY6QJ5YO05M5KbVw0gz92Wa4/Z4OLV2+b6iV1jIUfazjTTJl1DOAMG0mRSWdayG3xNtVL0tQl6896Nm6MZG16Zh0DIhHYSLrwmRaQLvF8gVy59WDUoUxHDC3WivlTtK2+SZUVQ3TfzzfrUFOrpDNDmbo2Fj6yA03iALJarKb6nsbCH31uiX759ge65cnXO8JaYihTpBeBDSDn9DSUqSQdamrV1hjXqxnKFOlCYAPIObFmHQs71cMsdAxlinQhsAHkHIYyhYsIbAA5h6FM4SICG0BOijWU6cFjrbr1qdej7sNY+EgnAhtATmIsfLiGwAaQkzJx1jGgJwQ2gJwVOZRpnol+XTrVs44BsdDVEUBO6xjK9OVXNKG8MO2zjgGxENgA+sTnD+iU158VY8YX5QfPotc+cgNj4SNjEdgA4tbm82t1bYOW1uzP2nmjGQsfmYrABhCXbfVNmtdl3nPmjQZSh05nAM5qe31Tx7zRsSbMYN5ooH8R2AB61OYLzgN9tnmjw8LzRrf54tseQHwIbAA9Wl3b0Gne6H+4bYLunXZRx/Nv3TROD143ptM+4XmjASQPgQ2gR13njf79jgZ9/rLhHc//qnK4fr/jw077MG80kHx0OgMQU7R5o3d9eELnlhZp2KAinVtaqOOtXjUcP91t3/C80dwaBSQHgQ0gpvC80eHe4GEv1TZoVuVwnT+oSL/f0RB13/C80dwiBSQHTeIAYoo1b/Tvt3+oL0y8UDM/e4Feqo0e2MwbDSQXgQ0gpljzRu9rbFZJkUdHTrTpo5NtUfdl3mgguQhsAD2KNW/0bU+9oXue2RB1H+aNBpKPwAbQI+aNBjIDgQ2gR8wbDWQGAhvAWUXOGx2teVxi3migv9GFE0BcOuaNrj2spTV1zBsNpBiBDSBuRfkezZ5UodmTKpg3GkgxAhtAnzBvNJBaXMMGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcQ2AAAOIDABgDAAQQ2AAAOILABAHAAgQ0AgAMIbAAAHEBgAwDgAAIbAAAHENgAADiAwAYAwAEENgAADiCwAQBwAIENAIADCGwAAByQUGAbY84xxrxsjNkX+ndolG0uN8a8bYzZZYzZYYz5ciLHBAAgFyV6hv2YpPXW2nGS1oeed3VK0t9aaz8j6TZJTxljyhI8LgAAOSXRwL5D0orQ4xWSZnfdwFq711q7L/T4Q0mNks5P8LgAAOSURAO73FrbEHp8WFJ5TxsbY6ZIKpS0P8HjAgCQU4y1tucNjFkn6YIoqxZJWmGtLYvY9pi1ttt17NC64ZJqJM211m6Isc0CSQskqby8/Mrq6up46pARmpubVVpamu5ipAz1zW65Vl8p9+pMfTPT9OnTt1hrJ0dbd9bA7okxZo+kKmttQziQrbUTomw3WMGw/p619oV4Xnvy5Mn2nXfe6XPZUq2mpkZVVVXpLkbKUN/slmv1lXKvztQ3MxljYgZ2ok3iqyTNDT2eK+l3UQ5eKGmlpP+IN6wBAEBniQb29yXdbIzZJ+mm0HMZYyYbY34a2uZLkq6XNM8Ysy30c3mCxwUAIKfkJ7KztfYTSTOiLH9H0gOhx7+U9MtEjgMAQK5jpDMAABxAYAMA4AACGwAABxDYAAA4IGcC2+cP6MRpr/yBvt93DgBAuiTUSzzTtfn8Wl3boKU1+7WvsVn5eUa+gNX4YaX6etVYzaocrqJ8T7qLCQDAWWVtYG+rb9K85Zvk9QfU0u6XJHn9wbPrPUeatXjlTj2+ardWzJ+iiSOZPAwAkNmyskl8e32T7lm2QU2t3o6wlqTfLLy643FLu19NrV7NWbZB2+ub0lFMAADilnWB3ebza+7yTWr1+rutu2vpW92WtXqD27f5um8PAECmyLrAXl3bIK8/EHXdrsdvjbrc6w9oTe3h/iwWAAAJybrAXlqzv1MzeDxa2v1aWlPXTyUCACBxWRXY/oDVvsbmPu27t7GZW74AABkrqwK7pd2n/DzTp33z84xa2n1JLhEAAMmRVYFdUpgvXx/Pkn0Bq5LCrL3LDQDguKwKbE+e0bhhpTHX9xTl44eVytPHs3MAAPpbVgW2JC2sGquSwu6jl5UNLFDTqfao+5QUerSw6uL+LhoAAH2WdYE9q3K4CjydqzVsUJF+u/BqPfPGe1H3KfDkaWblBakoHgAAfZJ1gV2U79GK+VNUXHDmLLvxZJtufOI1rXjr/W7bFxcEt2dMcQBAJsu6wJakiSPLVL1gmsqKC6I2j0vBZvCy4gJVL5jGWOIAgIyXtd2iJ44s08ZFM7Sm9rCW1tRpb6fZugZpYdVYzay8gDNrAIATsjawpWDz+OxJFZo9qUL+gFVLu08lhfn0BgcAOCerAzuSJ89o8ICCdBcDAIA+ycpr2AAAZBsCGwAABxDYAAA4gMAGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAcQGADAOAAAhsAAAcYa226yxCVMeYjSR+kuxy9cJ6kj9NdiBSivtkt1+or5V6dqW9mushae360FRkb2K4xxrxjrZ2c7nKkCvXNbrlWXyn36kx93UOTOAAADiCwAQBwAIGdPMvSXYAUo77ZLdfqK+VenamvY7iGDQCAAzjDBgDAAQR2LxhjzjHGvGyM2Rf6d2iM7fzGmG2hn1URy0cbYzYaY+qMMc8ZYwpTV/rei6e+xpjLjTFvG2N2GWN2GGO+HLHuWWPMexG/i8tTW4P4GGNuM8bsCb0vj0VZXxR6v+pC79+oiHX/GFq+xxhzayrL3Vdx1PdRY8zu0Pu53hhzUcS6qJ/tTBZHfecZYz6KqNcDEevmhj7/+4wxc1Nb8r6Jo75PRtR1rzGmKWKdi+/vcmNMozFmZ4z1xhjz49DvY4cx5oqIdW69v9ZafuL8kfSvkh4LPX5M0g9ibNccY/nzkuaEHv9E0sJ01ynR+koaL2lc6PGFkhoklYWePyvp7nTX4yx19EjaL2mMpEJJ2yVd2mWb/yLpJ6HHcyQ9F3p8aWj7IkmjQ6/jSXedklDf6ZIGhh4vDNc39DzqZztTf+Ks7zxJ/yvKvudIOhD6d2jo8dB01ynR+nbZ/u8kLXf1/Q2V+XpJV0jaGWP9LElrJBlJ0yRtdPX95Qy7d+6QtCL0eIWk2fHuaIwxkm6U9EJf9k+Ts9bXWrvXWrsv9PhDSY2Sot70n6GmSKqz1h6w1rZLqlaw3pEifw8vSJoRej/vkFRtrW2z1r4nqS70epnsrPW11r5qrT0VerpB0ogUlzGZ4nl/Y7lV0svW2qPW2mOSXpZ0Wz+VM1l6W997JP0qJSXrJ9ba1yUd7WGTOyT9hw3aIKnMGDNcDr6/BHbvlFtrG0KPD0sqj7HdAGPMO8aYDcaYcMidK6nJWusLPT8oqaIfy5oM8dZXkmSMmaLgt/r9EYuXhJqhnjTGFPVTORNRIak+4nm096Vjm9D7d1zB9zOefTNNb8t8v4JnJ2HRPtuZLN763hX6nL5gjBnZy30zSdxlDl3qGC3plYjFrr2/8Yj1O3Hu/c1PdwEyjTFmnaQLoqxaFPnEWmuNMbG62F9krT1kjBkj6RVjTK2Cf+QzTpLqq9A31l9ImmutDYQW/6OCQV+o4C0V/yDpu8koN/qfMearkiZLuiFicbfPtrV2f/RXcMb/L+lX1to2Y8zXFGxNuTHNZUqFOZJesNb6I5Zl4/ubNQjsLqy1N8VaZ4w5YowZbq1tCAVUY4zXOBT694AxpkbSJEm/UbApJj90ljZC0qGkV6CXklFfY8xgSS9JWhRqcgq/dvjsvM0Y83NJ305i0ZPlkKSREc+jvS/hbQ4aY/IlDZH0SZz7Zpq4ymyMuUnBL203WGvbwstjfLYz+Q/6Wetrrf0k4ulPFey7Ed63qsu+NUkvYXL15jM5R9JDkQscfH/jEet34tz7S5N476ySFO5JOFfS77puYIwZGm76NcacJ+kaSbttsJfDq5Lu7mn/DBNPfQslrVTwGtELXdYND/1rFLz+HbUXZ5ptljTOBHvwFyr4R6xr79jI38Pdkl4JvZ+rJM0J9SIfLWmcpE0pKndfnbW+xphJkv5d0u3W2saI5VE/2ykred/EU9/hEU9vl/Ru6PFaSbeE6j1U0i2hZZksns+zjDGXKNjR6u2IZS6+v/FYJelvQ73Fp0k6HjqZcO/9TXevN5d+FLxuuV7SPknrJJ0TWj5Z0k9Dj6+WVKtg78xaSfdH7D9GwT/odZJ+Lako3XVKQn2/KskraVvEz+Whda+Efgc7Jf1SUmm66xSjnrMk7VXwTGJRaNl3FQwsSRoQer/qQu/fmIh9F4X22yNpZrrrkqT6rpN0JOL9XBVaHvOznck/cdT3XyTtCtXrVUmXROw7P/S+10m6L911SUZ9Q8//SdL3u+zn6vv7KwXvTvEqeB36fklfl/T10Hoj6enQ76NW0mRX319GOgMAwAE0iQMA4AACGwAABxDYAAA4gMAGAMABBDYAAA4gsAEAcACBDQCAAwhsAAAc8H8ANOq+2fmroiMAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.figure(figsize = (8,8))\n", "plt.scatter(C[:,0].data, C[:,1].data, s = 200)\n", "\n", "for i in range(C.shape[0]):\n", " plt.text(C[i,0].item(),C[i,1].item(), itos[i], ha = \"center\", va = \"center\", color= \"white\")\n", "plt.grid('minor')" ] }, { "cell_type": "markdown", "id": "1fc29017", "metadata": {}, "source": [ "---\n", "We can see that the validation loss is slowly diverging from the training loss. This means that our model is slowly starting to overfit. \n", "\n", "\n", "For further model improvements, we can change:\n", "* the **number of neurons in hidden layer**: `W1.shape[1]` (`b1.shape == W2.shape[0]`)\n", "* the dimensionality of the **embedding lookup table**: `C` (`m`: the embedding size)\n", "* the number of characters fed in as input as **context** into the model: `block_size`\n", "* the details of the optimization: \n", " - **number of iterations, learning rate, & learning rate decay**\n", " - **batch_size**: may be able to achieve a better convergence speed\n", " - `ix = torch.randint(0, Xtr.shape[0], (batch_size, ))`\n", " \n", "---" ] }, { "cell_type": "markdown", "id": "42008c31", "metadata": {}, "source": [ "------\n", "## 2.14. Summary of Final Code\n", "-----\n" ] }, { "cell_type": "code", "execution_count": 92, "id": "ecb26f44", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(torch.Size([182625, 3]), torch.Size([182625]))" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Xtr.shape, Ytr.shape # dataset" ] }, { "cell_type": "code", "execution_count": 93, "id": "11977440", "metadata": {}, "outputs": [], "source": [ "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n", "C = torch.randn((27, 10), generator=g)\n", "W1 = torch.randn((30, 200), generator=g)\n", "b1 = torch.randn(200, generator=g)\n", "W2 = torch.randn((200, 27), generator=g)\n", "b2 = torch.randn(27, generator=g)\n", "parameters = [C, W1, b1, W2, b2]" ] }, { "cell_type": "code", "execution_count": 94, "id": "46c68bc5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "11897" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(p.nelement() for p in parameters) # number of parameters in total" ] }, { "cell_type": "code", "execution_count": 95, "id": "0d934da2", "metadata": {}, "outputs": [], "source": [ "for p in parameters:\n", " p.requires_grad = True" ] }, { "cell_type": "code", "execution_count": 96, "id": "79329fad", "metadata": {}, "outputs": [], "source": [ "lre = torch.linspace(-3, 0, 1000)\n", "lrs = 10**lre" ] }, { "cell_type": "code", "execution_count": 97, "id": "f98c8eb3", "metadata": {}, "outputs": [], "source": [ "lri = []\n", "lossi = []\n", "stepi = []" ] }, { "cell_type": "code", "execution_count": 98, "id": "66823a59", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.3276724815368652\n" ] } ], "source": [ "for i in range(200000):\n", " \n", " # minibatch construct\n", " ix = torch.randint(0, Xtr.shape[0], (32, ))\n", " \n", " # forward pass\n", " emb = C[Xtr[ix]] # (32, 3, 2)\n", " h = torch.tanh(emb.view(-1, 30) @ W1 + b1) # (32,100)\n", " logits = h @ W2 + b2 # (32, 27)\n", " loss = F.cross_entropy(logits, Ytr[ix])\n", " #print(loss.item())\n", " \n", " # backward pass\n", " for p in parameters:\n", " p.grad = None\n", " loss.backward()\n", "\n", " # update parameters\n", " #lr = lrs[i]\n", " lr = 0.1 if i < 100000 else 0.01\n", " for p in parameters:\n", " p.data += -lr * p.grad\n", " \n", " # track stats\n", " #lri.append(lr)\n", " stepi.append(i)\n", " lossi.append(loss.log10().item())\n", "\n", "print(loss.item())" ] }, { "cell_type": "code", "execution_count": 99, "id": "98edbc63", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Log loss')" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.plot(stepi, lossi)\n", "plt.title('Training Loss')\n", "plt.xlabel('Iterations')\n", "plt.ylabel('Log loss')" ] }, { "cell_type": "code", "execution_count": 100, "id": "2dd40a75", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.1329, grad_fn=)" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[Xtr] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,30) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Ytr)\n", "loss" ] }, { "cell_type": "code", "execution_count": 101, "id": "f6eb2abe", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(2.1743, grad_fn=)" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "emb = C[Xdev] # (32, 3, 2)\n", "h = torch.tanh(emb.view(-1,30) @ W1 + b1) # (32,100)\n", "logits = h @ W2 + b2 # (32, 27)\n", "loss = F.cross_entropy(logits, Ydev)\n", "loss" ] }, { "cell_type": "code", "execution_count": 102, "id": "c4a98084", "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# visualize dimensions 0 and 1 of the embedding matrix C for all characters\n", "plt.figure(figsize = (8,8))\n", "plt.scatter(C[:,0].data, C[:,1].data, s = 200)\n", "for i in range(C.shape[0]):\n", " plt.text(C[i,0].item(),C[i,1].item(), itos[i], ha = \"center\", va = \"center\", color= \"white\")\n", "plt.grid('minor')" ] }, { "cell_type": "markdown", "id": "71350183", "metadata": {}, "source": [ "------\n", "## 2.15. Sampling from the Model\n", "-----\n", "Let’s generate some names using the model. We prompt the model with an initial context indicated by **`'...'`**. The model utilizes its learned weights and biases to transform this input into a set of output probabilities. From these probabilities, we randomly sample the next character to progressively build the name." ] }, { "cell_type": "code", "execution_count": 103, "id": "92ff70ce", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([1, 3, 10])" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "context = [0] * block_size\n", "C[torch.tensor([context])].shape" ] }, { "cell_type": "code", "execution_count": 104, "id": "a493ce5a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "carmah.\n", "amelle.\n", "khismilialaty.\n", "salaysleer.\n", "hutlaymerric.\n", "kaeli.\n", "nellara.\n", "chaiiv.\n", "kaleigh.\n", "ham.\n", "jois.\n", "quinn.\n", "shorden.\n", "jadii.\n", "waje.\n", "madiaryxi.\n", "jace.\n", "pirran.\n", "edde.\n", "oib.\n" ] } ], "source": [ "# sample from the model\n", "g = torch.Generator().manual_seed(2147483647 + 10)\n", "\n", "for _ in range(20):\n", " \n", " out = []\n", " context = [0] * block_size # initialize with all ...\n", " while True:\n", " emb = C[torch.tensor([context])] # (1,block_size,d)\n", " h = torch.tanh(emb.view(1, -1) @ W1 + b1)\n", " logits = h @ W2 + b2\n", " probs = F.softmax(logits, dim=1)\n", " ix = torch.multinomial(probs, num_samples=1, generator=g).item()\n", " context = context[1:] + [ix]\n", " out.append(ix)\n", " if ix == 0:\n", " break\n", "\n", " print(''.join(itos[i] for i in out))" ] }, { "cell_type": "markdown", "id": "f1a767ea", "metadata": {}, "source": [ "---------\n", "

\n", "# 3. Conclusion\n", "-------------------------------------------------------\n", "In this notebook we implemented a multilayer perceptron (MLP) model as the character-level language model to handle the issue of **curse of dimensionality** from the n-gram character-level models. We aim to address the limitations of the bigram and trigram language models, particularly their struggle to produce name-like names because of their restrictions to only a single preceding character analysis. During the building and training of the model, some of the concepts we learned about were **embeddings, minibatches, hyperparameter optimization, under/overfitting, dataset train-dev-test splits, & model evaluation.**\n", "\n", "This notebook explains how to build a language model using neural networks. It covers the fundamental concepts of training a neural network on a training set and evaluating its performance on a test set, as well as splitting data into train, development, and test sets. The series also delves into the specifics of how a neural network can predict the next character in a sequence, and how to use the Torch library for language modeling operations. Additionally, it demonstrates how to build a language model using the \"logistic\" neural net shape and create a language model that can predict the next character in a sequence.\n", "\n", "This notebook also covers more advanced topics, such as how to perform forward and backward passes in language modeling, determine a learning rate for a language model, and optimize the model for better performance. It covers how to improve model performance by tuning optimization parameters and increasing the number of input characters. Overall, this notebook offers a comprehensive guide to building and optimizing language models using neural networks, making it a valuable resource for anyone looking to learn about this topic." ] }, { "cell_type": "markdown", "id": "2b2c5040", "metadata": {}, "source": [ "---------\n", "

\n", "# Appendix: Tensor Manipulations\n", "-------------------------------------------------------\n", "\n", "We go over tensor manipulations in `PyTorch` that can be helpful for mathematical operations of matrices and tensors. We go over two `torch` methods." ] }, { "cell_type": "markdown", "id": "7727299f", "metadata": {}, "source": [ "### Concatenation\n", "----" ] }, { "cell_type": "code", "execution_count": 105, "id": "19570f1c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([[[ 2, 3],\n", " [ 4, 5],\n", " [ 6, 7]],\n", "\n", " [[ 8, 9],\n", " [10, 11],\n", " [12, 13]]])" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "test = torch.tensor([[[2, 3],\n", " [4, 5],\n", " [6, 7]],\n", " \n", " [[8, 9],\n", " [10, 11],\n", " [12, 13]]])\n", "test" ] }, { "cell_type": "code", "execution_count": 106, "id": "b51c798d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([[ 2, 3, 4, 5, 6, 7],\n", " [ 8, 9, 10, 11, 12, 13]])" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "test_cat = torch.cat([test[:, 0, :], test[:, 1, :], test[:, 2, :]], 1)\n", "test_cat" ] }, { "cell_type": "code", "execution_count": 107, "id": "c9182acf", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(torch.Size([2, 3, 2]), torch.Size([2, 6]))" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "test.shape, test_cat.shape" ] }, { "cell_type": "markdown", "id": "00de1d6d", "metadata": {}, "source": [ "### View\n", "----\n", "`.view` is much more efficient and the underlying `PyTorch` storage does not change. Furthermore, no new additional memory is allocated." ] }, { "cell_type": "code", "execution_count": 108, "id": "bd0f802a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17])" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = torch.arange(18)\n", "a" ] }, { "cell_type": "code", "execution_count": 109, "id": "0b5f18e7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([18])" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.shape" ] }, { "cell_type": "code", "execution_count": 110, "id": "0aad7f71", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([[ 0, 1, 2, 3, 4, 5, 6, 7, 8],\n", " [ 9, 10, 11, 12, 13, 14, 15, 16, 17]])" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.view(2, 9) # extremely efficient" ] }, { "cell_type": "code", "execution_count": 111, "id": "4a017b81", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "tensor([[ 0, 1],\n", " [ 2, 3],\n", " [ 4, 5],\n", " [ 6, 7],\n", " [ 8, 9],\n", " [10, 11],\n", " [12, 13],\n", " [14, 15],\n", " [16, 17]])" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.view(9, 2)" ] }, { "cell_type": "code", "execution_count": 112, "id": "7320fa64", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "tensor([[[ 0, 1],\n", " [ 2, 3],\n", " [ 4, 5]],\n", "\n", " [[ 6, 7],\n", " [ 8, 9],\n", " [10, 11]],\n", "\n", " [[12, 13],\n", " [14, 15],\n", " [16, 17]]])" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.view(3, 3, 2)" ] }, { "cell_type": "code", "execution_count": 113, "id": "c9ced986", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ " 0\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5\n", " 6\n", " 7\n", " 8\n", " 9\n", " 10\n", " 11\n", " 12\n", " 13\n", " 14\n", " 15\n", " 16\n", " 17\n", "[torch.LongStorage of size 18]" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.storage()" ] }, { "cell_type": "markdown", "id": "5acec05d", "metadata": {}, "source": [ "---------\n", "

\n", "# Appendix: Embeddings\n", "-------------------------------------------------------\n", "\n", "We go over embeddings and embedding lookup tables in `PyTorch` that are necessary for the MLP model we build in this notebook. *Embedding an integer* means mapping the integer to a feature vector. Let's go through various types of embeddings." ] }, { "cell_type": "markdown", "id": "62b9e393", "metadata": {}, "source": [ "### Single Integer Embedding\n", "----" ] }, { "cell_type": "code", "execution_count": 114, "id": "092b44e5", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([ 0.5915, 0.2553, -1.2696, -0.4579, 0.4209, 3.0067, 1.9783, -1.1180,\n", " 0.9884, -1.3976], grad_fn=)\n", "tensor([ 0.5915, 0.2553, -1.2696, -0.4579, 0.4209, 3.0067, 1.9783, -1.1180,\n", " 0.9884, -1.3976], grad_fn=)\n" ] } ], "source": [ "# Direct indexing access\n", "print(C[1])\n", "\n", "\n", "# Using one-hot encoding \n", "print(F.one_hot(torch.tensor(1), num_classes=27).float() @ C)" ] }, { "cell_type": "markdown", "id": "c7b3399d", "metadata": {}, "source": [ "### Multiple Integers Embedding\n", "----" ] }, { "cell_type": "code", "execution_count": 115, "id": "bebf5ce1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[ 2.3033e+00, 3.0027e-01, 3.8556e-01, -1.1099e+00, 1.3141e-01,\n", " 4.6951e-01, -2.0946e+00, 1.4270e+00, 5.9253e-01, 2.0175e+00],\n", " [ 5.9151e-01, 2.5531e-01, -1.2696e+00, -4.5790e-01, 4.2094e-01,\n", " 3.0067e+00, 1.9783e+00, -1.1180e+00, 9.8836e-01, -1.3976e+00],\n", " [ 1.9471e-01, 1.6241e-03, -6.8770e-01, -3.3083e-01, 3.3575e-01,\n", " 8.5847e-01, 8.4312e-01, 1.4996e-02, -1.4439e-01, 3.6371e-01]],\n", " grad_fn=)\n", "tensor([[ 2.3033e+00, 3.0027e-01, 3.8556e-01, -1.1099e+00, 1.3141e-01,\n", " 4.6951e-01, -2.0946e+00, 1.4270e+00, 5.9253e-01, 2.0175e+00],\n", " [ 5.9151e-01, 2.5531e-01, -1.2696e+00, -4.5790e-01, 4.2094e-01,\n", " 3.0067e+00, 1.9783e+00, -1.1180e+00, 9.8836e-01, -1.3976e+00],\n", " [ 1.9471e-01, 1.6241e-03, -6.8770e-01, -3.3083e-01, 3.3575e-01,\n", " 8.5847e-01, 8.4312e-01, 1.4996e-02, -1.4439e-01, 3.6371e-01]],\n", " grad_fn=)\n" ] } ], "source": [ "# You can use lists to embed multiple integers at once.\n", "print(C[[0, 1, 2]])\n", "\n", "\n", "# Tensors will work as well\n", "x = torch.tensor([0, 1, 2])\n", "print(C[x])" ] }, { "cell_type": "markdown", "id": "08df4ef4", "metadata": {}, "source": [ "### Embedding using multi-dimensional tensor\n", "----" ] }, { "cell_type": "code", "execution_count": 116, "id": "02b49703", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[[ 2.3033e+00, 3.0027e-01, 3.8556e-01, -1.1099e+00, 1.3141e-01,\n", " 4.6951e-01, -2.0946e+00, 1.4270e+00, 5.9253e-01, 2.0175e+00],\n", " [ 5.9151e-01, 2.5531e-01, -1.2696e+00, -4.5790e-01, 4.2094e-01,\n", " 3.0067e+00, 1.9783e+00, -1.1180e+00, 9.8836e-01, -1.3976e+00],\n", " [ 1.9471e-01, 1.6241e-03, -6.8770e-01, -3.3083e-01, 3.3575e-01,\n", " 8.5847e-01, 8.4312e-01, 1.4996e-02, -1.4439e-01, 3.6371e-01]],\n", "\n", " [[ 2.3033e+00, 3.0027e-01, 3.8556e-01, -1.1099e+00, 1.3141e-01,\n", " 4.6951e-01, -2.0946e+00, 1.4270e+00, 5.9253e-01, 2.0175e+00],\n", " [ 5.9151e-01, 2.5531e-01, -1.2696e+00, -4.5790e-01, 4.2094e-01,\n", " 3.0067e+00, 1.9783e+00, -1.1180e+00, 9.8836e-01, -1.3976e+00],\n", " [ 1.9471e-01, 1.6241e-03, -6.8770e-01, -3.3083e-01, 3.3575e-01,\n", " 8.5847e-01, 8.4312e-01, 1.4996e-02, -1.4439e-01, 3.6371e-01]]],\n", " grad_fn=)\n" ] } ], "source": [ "x = torch.tensor([\n", " [0, 1, 2], \n", " [0, 1, 2]\n", "])\n", "print(C[x])" ] }, { "cell_type": "markdown", "id": "633040e3", "metadata": {}, "source": [ "-----\n", "

\n", "# Exercises\n", "----\n", "1. Tune the hyperparameters of the training to beat **karpathy's best validation loss of 2.2**.\n", "2. I was not careful with the intialization of the network in this video.\n", " 1. What is the loss you'd get if the predicted probabilities at initialization were perfectly uniform? What loss do we achieve? \n", " 2. Can you tune the initialization to get a starting loss that is much more similar to (1)?\n", "3. Read the [Bengio et al 2003 paper](https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf), implement and try any idea from the paper. Did it work?" ] }, { "cell_type": "markdown", "id": "a9154496", "metadata": {}, "source": [ "-----\n", "

\n", "# References\n", "----\n", "1. \"Building makemore Part 2: MLP\" [youtube video](https://www.youtube.com/watch?v=TCH_1BHY58I), Sept 2022.\n", "2. Andrej Karpathy **Makemore** [github repo](https://github.com/karpathy/makemore).\n", "3. Andrej Karpathy **Neural Networks: Zero to Hero** [github repo](https://github.com/karpathy/nn-zero-to-hero/tree/master) ([notebook](https://github.com/karpathy/nn-zero-to-hero/blob/master/lectures/makemore/makemore_part2_mlp.ipynb) to follow video tutorial with).\n", "4. Article: \"Multilayer Perceptron with Andrej Karpathy\" - Kavishka Abeywardana, Pt [1](https://medium.com/@kdwa2404/multilayer-perceptron-with-andrej-karpathy-part-1-7f530278c2b7), [2](https://medium.com/@kdwa2404/multilayer-perceptron-with-andrej-karpathy-part-2-38a75dc2e5a4), March 2024.\n", "5. Article: \"Implementing a Character-Level Language Model Using MLP\" - Tahir Rauf, Pt [2A](https://blog.gopenai.com/implementing-a-character-level-bigram-language-model-using-mlp-part-2a-73e79dd0ae8d), [2B](https://ai.plainenglish.io/implementing-a-character-level-bigram-language-model-using-mlp-part-2-b-c72a126b32a3), Dec 2023.\n", "6. \"Deep Dive into AI: Analyzing and implementing Multilayer Perceptrons\" - Ada Choudhry, [article](https://medium.com/@adachoudhry26/deep-dive-into-ai-analyzing-and-implementing-multilayer-perceptrons-f8acaecfd846), Jan 2024.\n", "7. \"Notes on Andrej Karpathy’s makemore videos. Part 2.\" - Maxime Markov, [article](https://medium.com/@maxmarkovvision/notes-on-andrej-karpathys-makemore-videos-part-2-58ee51912c5c), Nov 2022.\n", "8. \"What Is the Curse of Dimensionality?\" - Badreesh Shetty, [article](https://builtin.com/data-science/curse-dimensionality), Aug 2022.\n", "9. \"The Curse of Dimensionality in Machine Learning: Challenges, Impacts, and Solutions\" - Abid Ali Awan, [article](https://www.datacamp.com/blog/curse-of-dimensionality-machine-learning), Sep 2023.\n", "10. \"A Neural Probabilistic Language Model\" - Bengio et. al. [Academic Paper](https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf), 2003\n", "11. PyTorch Resources: [`torch.cat`](https://pytorch.org/docs/stable/generated/torch.cat.html), [`torch.Tensor.view`](https://pytorch.org/docs/stable/generated/torch.Tensor.view.html)\n", "12. Wikipedia: [Autoregressive Model](https://en.wikipedia.org/wiki/Autoregressive_model)\n", "13. Stack Overflow: [`np.random.rand` vs `np.random.randn`](https://stackoverflow.com/questions/47240308/differences-between-numpy-random-rand-vs-numpy-random-randn-in-python)\n", "\n", "------" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.10" } }, "nbformat": 4, "nbformat_minor": 5 }