MuzzammilShah commited on
Commit
9470d95
·
verified ·
1 Parent(s): 571a460

Initial commits for files

Browse files
Files changed (5) hide show
  1. A-main-notebook.ipynb +480 -0
  2. README.md +59 -0
  3. StarterCode.ipynb +446 -0
  4. VisualizationTools.ipynb +0 -0
  5. names.txt +0 -0
A-main-notebook.ipynb ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "import torch\n",
10
+ "import torch.nn.functional as F\n",
11
+ "import matplotlib.pyplot as plt # for making figures\n",
12
+ "%matplotlib inline"
13
+ ]
14
+ },
15
+ {
16
+ "cell_type": "code",
17
+ "execution_count": 2,
18
+ "metadata": {},
19
+ "outputs": [
20
+ {
21
+ "data": {
22
+ "text/plain": [
23
+ "['emma', 'olivia', 'ava', 'isabella', 'sophia', 'charlotte', 'mia', 'amelia']"
24
+ ]
25
+ },
26
+ "execution_count": 2,
27
+ "metadata": {},
28
+ "output_type": "execute_result"
29
+ }
30
+ ],
31
+ "source": [
32
+ "# read in all the words\n",
33
+ "words = open('names.txt', 'r').read().splitlines()\n",
34
+ "words[:8]"
35
+ ]
36
+ },
37
+ {
38
+ "cell_type": "code",
39
+ "execution_count": 3,
40
+ "metadata": {},
41
+ "outputs": [
42
+ {
43
+ "data": {
44
+ "text/plain": [
45
+ "32033"
46
+ ]
47
+ },
48
+ "execution_count": 3,
49
+ "metadata": {},
50
+ "output_type": "execute_result"
51
+ }
52
+ ],
53
+ "source": [
54
+ "len(words)"
55
+ ]
56
+ },
57
+ {
58
+ "cell_type": "code",
59
+ "execution_count": 4,
60
+ "metadata": {},
61
+ "outputs": [
62
+ {
63
+ "name": "stdout",
64
+ "output_type": "stream",
65
+ "text": [
66
+ "{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",
67
+ "27\n"
68
+ ]
69
+ }
70
+ ],
71
+ "source": [
72
+ "# build the vocabulary of characters and mappings to/from integers\n",
73
+ "chars = sorted(list(set(''.join(words))))\n",
74
+ "stoi = {s:i+1 for i,s in enumerate(chars)}\n",
75
+ "stoi['.'] = 0\n",
76
+ "itos = {i:s for s,i in stoi.items()}\n",
77
+ "vocab_size = len(itos)\n",
78
+ "print(itos)\n",
79
+ "print(vocab_size)"
80
+ ]
81
+ },
82
+ {
83
+ "cell_type": "code",
84
+ "execution_count": 5,
85
+ "metadata": {},
86
+ "outputs": [
87
+ {
88
+ "name": "stdout",
89
+ "output_type": "stream",
90
+ "text": [
91
+ "torch.Size([182625, 3]) torch.Size([182625])\n",
92
+ "torch.Size([22655, 3]) torch.Size([22655])\n",
93
+ "torch.Size([22866, 3]) torch.Size([22866])\n"
94
+ ]
95
+ }
96
+ ],
97
+ "source": [
98
+ "# build the dataset\n",
99
+ "block_size = 3 # context length: how many characters do we take to predict the next one?\n",
100
+ "\n",
101
+ "def build_dataset(words): \n",
102
+ " X, Y = [], []\n",
103
+ " \n",
104
+ " for w in words:\n",
105
+ " context = [0] * block_size\n",
106
+ " for ch in w + '.':\n",
107
+ " ix = stoi[ch]\n",
108
+ " X.append(context)\n",
109
+ " Y.append(ix)\n",
110
+ " context = context[1:] + [ix] # crop and append\n",
111
+ "\n",
112
+ " X = torch.tensor(X)\n",
113
+ " Y = torch.tensor(Y)\n",
114
+ " print(X.shape, Y.shape)\n",
115
+ " return X, Y\n",
116
+ "\n",
117
+ "import random\n",
118
+ "random.seed(42)\n",
119
+ "random.shuffle(words)\n",
120
+ "n1 = int(0.8*len(words))\n",
121
+ "n2 = int(0.9*len(words))\n",
122
+ "\n",
123
+ "Xtr, Ytr = build_dataset(words[:n1]) # 80%\n",
124
+ "Xdev, Ydev = build_dataset(words[n1:n2]) # 10%\n",
125
+ "Xte, Yte = build_dataset(words[n2:]) # 10%"
126
+ ]
127
+ },
128
+ {
129
+ "cell_type": "code",
130
+ "execution_count": 7,
131
+ "metadata": {},
132
+ "outputs": [
133
+ {
134
+ "name": "stdout",
135
+ "output_type": "stream",
136
+ "text": [
137
+ "11897\n"
138
+ ]
139
+ }
140
+ ],
141
+ "source": [
142
+ "# MLP revisited\n",
143
+ "n_embd = 10 # the dimensionality of the character embedding vectors\n",
144
+ "n_hidden = 200 # the number of neurons in the hidden layer of the MLP\n",
145
+ "\n",
146
+ "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n",
147
+ "C = torch.randn((vocab_size, n_embd), generator=g)\n",
148
+ "W1 = torch.randn((n_embd * block_size, n_hidden), generator=g) * (5/3)/((n_embd * block_size)**0.5) #* 0.2\n",
149
+ "#b1 = torch.randn(n_hidden, generator=g) * 0.01\n",
150
+ "W2 = torch.randn((n_hidden, vocab_size), generator=g) * 0.01\n",
151
+ "b2 = torch.randn(vocab_size, generator=g) * 0\n",
152
+ "\n",
153
+ "# BatchNorm parameters\n",
154
+ "bngain = torch.ones((1, n_hidden))\n",
155
+ "bnbias = torch.zeros((1, n_hidden))\n",
156
+ "bnmean_running = torch.zeros((1, n_hidden))\n",
157
+ "bnstd_running = torch.ones((1, n_hidden))\n",
158
+ "\n",
159
+ "parameters = [C, W1, W2, b2, bngain, bnbias]\n",
160
+ "print(sum(p.nelement() for p in parameters)) # number of parameters in total\n",
161
+ "for p in parameters:\n",
162
+ " p.requires_grad = True"
163
+ ]
164
+ },
165
+ {
166
+ "cell_type": "code",
167
+ "execution_count": 10,
168
+ "metadata": {},
169
+ "outputs": [
170
+ {
171
+ "name": "stdout",
172
+ "output_type": "stream",
173
+ "text": [
174
+ " 0/ 200000: 3.2342\n",
175
+ " 10000/ 200000: 1.8947\n",
176
+ " 20000/ 200000: 1.8914\n",
177
+ " 30000/ 200000: 1.9489\n",
178
+ " 40000/ 200000: 2.1701\n",
179
+ " 50000/ 200000: 2.0639\n",
180
+ " 60000/ 200000: 2.0728\n",
181
+ " 70000/ 200000: 2.3965\n",
182
+ " 80000/ 200000: 2.4142\n",
183
+ " 90000/ 200000: 2.2257\n",
184
+ " 100000/ 200000: 2.2824\n",
185
+ " 110000/ 200000: 1.8584\n",
186
+ " 120000/ 200000: 2.1613\n",
187
+ " 130000/ 200000: 1.9009\n",
188
+ " 140000/ 200000: 1.8430\n",
189
+ " 150000/ 200000: 2.3324\n",
190
+ " 160000/ 200000: 2.2026\n",
191
+ " 170000/ 200000: 1.6905\n",
192
+ " 180000/ 200000: 1.9502\n",
193
+ " 190000/ 200000: 2.0909\n"
194
+ ]
195
+ }
196
+ ],
197
+ "source": [
198
+ "# same optimization as last time\n",
199
+ "max_steps = 200000\n",
200
+ "batch_size = 32\n",
201
+ "lossi = []\n",
202
+ "\n",
203
+ "for i in range(max_steps):\n",
204
+ " \n",
205
+ " # minibatch construct\n",
206
+ " ix = torch.randint(0, Xtr.shape[0], (batch_size,), generator=g)\n",
207
+ " Xb, Yb = Xtr[ix], Ytr[ix] # batch X,Y\n",
208
+ " \n",
209
+ " # forward pass\n",
210
+ " emb = C[Xb] # embed the characters into vectors\n",
211
+ " embcat = emb.view(emb.shape[0], -1) # concatenate the vectors\n",
212
+ " hpreact = embcat @ W1 #+ b1 # hidden layer pre-activation\n",
213
+ " \n",
214
+ " #hpreact = bngain * (hpreact - hpreact.mean(0, keepdim=True)) / (hpreact.std(0, keepdim=True)) + bnbias #batch normalisation layer\n",
215
+ " #----------------\n",
216
+ " # BatchNorm layer\n",
217
+ " #----------------\n",
218
+ " bnmeani = hpreact.mean(0, keepdim=True)\n",
219
+ " bnstdi = hpreact.std(0, keepdim=True)\n",
220
+ " \n",
221
+ " hpreact = bngain * (hpreact - bnmeani) / bnstdi + bnbias\n",
222
+ " \n",
223
+ " with torch.no_grad():\n",
224
+ " bnmean_running = 0.999 * bnmean_running + 0.001 * bnmeani\n",
225
+ " bnstd_running = 0.999 * bnstd_running + 0.001 * bnstdi\n",
226
+ " #----------------\n",
227
+ "\n",
228
+ " h = torch.tanh(hpreact) # hidden layer\n",
229
+ " logits = h @ W2 + b2 # output layer\n",
230
+ " loss = F.cross_entropy(logits, Yb) # loss function\n",
231
+ " \n",
232
+ " # backward pass\n",
233
+ " for p in parameters:\n",
234
+ " p.grad = None\n",
235
+ " loss.backward()\n",
236
+ " \n",
237
+ " # update\n",
238
+ " lr = 0.1 if i < 100000 else 0.01 # step learning rate decay\n",
239
+ " for p in parameters:\n",
240
+ " p.data += -lr * p.grad\n",
241
+ "\n",
242
+ " # track stats\n",
243
+ " if i % 10000 == 0: # print every once in a while\n",
244
+ " print(f'{i:7d}/{max_steps:7d}: {loss.item():.4f}')\n",
245
+ " lossi.append(loss.log10().item())\n",
246
+ "\n",
247
+ " #break #Add this while experienting so you dont have to print all the steps"
248
+ ]
249
+ },
250
+ {
251
+ "cell_type": "markdown",
252
+ "metadata": {},
253
+ "source": [
254
+ "-----------------\n",
255
+ "\n",
256
+ "Used for lecture : [00:12:59](https://www.youtube.com/watch?v=P6sfmUTpUmc&t=779s) fixing the saturated tanh "
257
+ ]
258
+ },
259
+ {
260
+ "cell_type": "code",
261
+ "execution_count": null,
262
+ "metadata": {},
263
+ "outputs": [],
264
+ "source": [
265
+ "#plt.hist(h.view(-1).tolist(), 50)"
266
+ ]
267
+ },
268
+ {
269
+ "cell_type": "code",
270
+ "execution_count": null,
271
+ "metadata": {},
272
+ "outputs": [],
273
+ "source": [
274
+ "#plt.hist(hpreact.view(-1).tolist(), 50)"
275
+ ]
276
+ },
277
+ {
278
+ "cell_type": "code",
279
+ "execution_count": null,
280
+ "metadata": {},
281
+ "outputs": [],
282
+ "source": [
283
+ "#plt.figure(figsize=(20,10))\n",
284
+ "#plt.imshow(h.abs() > 0.99, cmap='gray', interpolation='nearest')"
285
+ ]
286
+ },
287
+ {
288
+ "cell_type": "markdown",
289
+ "metadata": {},
290
+ "source": [
291
+ "End of Used for lecture : [00:12:59](https://www.youtube.com/watch?v=P6sfmUTpUmc&t=779s) fixing the saturated tanh \n",
292
+ "\n",
293
+ "----------------"
294
+ ]
295
+ },
296
+ {
297
+ "cell_type": "code",
298
+ "execution_count": 24,
299
+ "metadata": {},
300
+ "outputs": [
301
+ {
302
+ "data": {
303
+ "text/plain": [
304
+ "[<matplotlib.lines.Line2D at 0x21a782d9720>]"
305
+ ]
306
+ },
307
+ "execution_count": 24,
308
+ "metadata": {},
309
+ "output_type": "execute_result"
310
+ },
311
+ {
312
+ "data": {
313
+ "image/png": "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yJ1lPNcXrRrV13Sf9l6/2cVBREREUmFwQkRERFJhcEJERERSYXBCREREUmFwQkRERBLlh2VwQkRERJJhcEJEREQwSJTohMEJERERIa/wst5NMGJwQkRERPi/LSf0boIRgxMiIiLC9uMFejfBiMEJERERSYXBCREREUmFwQkRERFJVZaYwQkRERExCRsRERHJRaKOEwYnRERExJ4TIiIikgwzxBIRERFZwOCEiIiIpMLghIiIiDjnhIiIiMgSBidERETEpcREREREljA4ISIiIi4lJiIiIrl4yRObMDghIiIiwCDReh0GJ0RERMQJsURERESWMDghIiIiqTA4ISIiIg7rEBERkVw4IZaIiIikwp4TIiIiIgsYnBAREZFUGJwQERER09cTERERWcLghIiIiCRaq8PghIiIiCTD4ISIiIi4lJiIiIjkIlFswuCEiIiIuFqHiIiIJOMlT2zC4ISIiIjkwuCEiIiIINOsEwYnRERExNU6RERERJYwOCEiIiKJBnUYnBARERE4rENERESSMUjUd8LghIiIiKTC4ISIiIikwuCEiIiIOOeEiIiIyBIGJ0RERMSeEyIiIpILV+sQERGRXOSJTRicEBEREeAl0bgOgxMiIiKSqeOEwQkRERFxQqw0Zg3vpncTiIiIqAaPDk4GdY7WuwlERERUg0cHJ0RERFRJolEdBidEREQkFwYnRERExKXEREREJBl5YhP7gpOZM2ciLi4OAQEBSE5Oxrp16yxuO3/+fHTv3h1hYWGoV68eEhMT8dVXX9ndYCIiItKeW6evnzdvHtLT0zFx4kRs2rQJCQkJSEtLQ15entntw8PD8fLLLyMzMxNbt27FqFGjMGrUKPz5558ON56IiIjqHtXByfTp0zF69GiMGjUK8fHxmD17NoKCgjB37lyz2/fr1w+33XYbOnTogFatWuHpp59Gly5dsGrVKocbr4X46BC9m0BERETVqApOSkpKsHHjRqSmpl7dgZcXUlNTkZmZafPxQghkZGRgz5496NOnj8XtiouLUVhYaPLjLEMSY5y2byIiInch0XxYdcHJ6dOnUV5ejsjISJPbIyMjkZOTY/FxBQUFqF+/Pvz8/DB48GB88MEHuPHGGy1uP3nyZISGhhp/YmNj1TSTiIiI3JhLVusEBwcjKysL69evx1tvvYX09HQsW7bM4vbjx49HQUGB8efo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0eR2dzx/58+fF5s3bxabN28WAMT06dPF5s2bjatVpkyZIsLCwsQvv/witm7dKoYMGWJ2KXHXrl3F2rVrxapVq0SbNm1MlqHm5+eLyMhI8cADD4jt27eL7777TgQFBdVapunj4yOmTZsmdu3aJSZOnGh2maattqg5vpKSEnHrrbeKpk2biqysLJP/yaoVDmvWrBHvvvuuyMrKEgcOHBBff/21aNSokRgxYoQUx2frGM+fPy+ef/55kZmZKQ4dOiQWL14sunXrJtq0aSMuX77s9uewSkFBgQgKChKzZs2q9XjZz6Gt64IQcn122mqLEh4bnAghxAcffCCaNWsm/Pz8RI8ePcTff/+td5MEALM/n332mRBCiOzsbNGnTx8RHh4u/P39RevWrcW4ceNM8mQIIcThw4fFoEGDRGBgoIiIiBDPPfecKC0tNdlm6dKlIjExUfj5+YmWLVsan6M6rV+jYcOGiejoaOHn5yeaNGkihg0bJvbv32+8/9KlS+KJJ54QDRo0EEFBQeK2224TJ0+edItjq+7PP/8UAMSePXtMbnfH87d06VKz78mRI0cKISqXR7766qsiMjJS+Pv7ixtuuKHWcZ85c0bce++9on79+iIkJESMGjVKnD9/3mSbLVu2iOuuu074+/uLJk2aiClTptRqy/fffy/atm0r/Pz8RMeOHcXvv/9ucr+Stqg5vkOHDln8n6zKW7Nx40aRnJwsQkNDRUBAgOjQoYN4++23TS7seh6frWO8ePGiGDBggGjUqJHw9fUVzZs3F6NHj64VxLrrOawyZ84cERgYKPLz82s9XvZzaOu6IIRcn51K2mKL4cqBExEREUnBI+ecEBERkbwYnBAREZFUGJwQERGRVBicEBERkVQYnBAREZFUGJwQERGRVBicEBERkVQYnBAREZFUGJwQERGRVBicEBERkVQYnBAREZFUGJwQERGRVP4fr/NzQnir1AEAAAAASUVORK5CYII=",
314
+ "text/plain": [
315
+ "<Figure size 640x480 with 1 Axes>"
316
+ ]
317
+ },
318
+ "metadata": {},
319
+ "output_type": "display_data"
320
+ }
321
+ ],
322
+ "source": [
323
+ "plt.plot(lossi)"
324
+ ]
325
+ },
326
+ {
327
+ "cell_type": "code",
328
+ "execution_count": null,
329
+ "metadata": {},
330
+ "outputs": [],
331
+ "source": [
332
+ "# # calibrate the batch norm at the end of training\n",
333
+ "\n",
334
+ "# with torch.no_grad():\n",
335
+ "# # pass the training set through\n",
336
+ "# emb = C[Xtr]\n",
337
+ "# embcat = emb.view(emb.shape[0], -1)\n",
338
+ "# hpreact = embcat @ W1 # + b1\n",
339
+ "# # measure the mean/std over the entire training set\n",
340
+ "# bnmean = hpreact.mean(0, keepdim=True)\n",
341
+ "# bnstd = hpreact.std(0, keepdim=True)"
342
+ ]
343
+ },
344
+ {
345
+ "cell_type": "code",
346
+ "execution_count": 11,
347
+ "metadata": {},
348
+ "outputs": [
349
+ {
350
+ "name": "stdout",
351
+ "output_type": "stream",
352
+ "text": [
353
+ "train 2.037672996520996\n",
354
+ "val 2.107128620147705\n"
355
+ ]
356
+ }
357
+ ],
358
+ "source": [
359
+ "@torch.no_grad() # this decorator disables gradient tracking\n",
360
+ "def split_loss(split):\n",
361
+ " x,y = {\n",
362
+ " 'train': (Xtr, Ytr),\n",
363
+ " 'val': (Xdev, Ydev),\n",
364
+ " 'test': (Xte, Yte),\n",
365
+ " }[split]\n",
366
+ " emb = C[x] # (N, block_size, n_embd)\n",
367
+ " embcat = emb.view(emb.shape[0], -1) # concat into (N, block_size * n_embd)\n",
368
+ " hpreact = embcat @ W1 #+ b1\n",
369
+ " #hpreact = bngain * (hpreact - hpreact.mean(0, keepdim=True)) / (hpreact.std(0, keepdim=True)) + bnbias #batch normalisation layer\n",
370
+ " hpreact = bngain * (hpreact - bnmean_running) / bnstd_running + bnbias\n",
371
+ " h = torch.tanh(hpreact) # (N, n_hidden)\n",
372
+ " logits = h @ W2 + b2 # (N, vocab_size)\n",
373
+ " loss = F.cross_entropy(logits, y)\n",
374
+ " print(split, loss.item())\n",
375
+ "\n",
376
+ "split_loss('train')\n",
377
+ "split_loss('val')"
378
+ ]
379
+ },
380
+ {
381
+ "cell_type": "code",
382
+ "execution_count": 11,
383
+ "metadata": {},
384
+ "outputs": [
385
+ {
386
+ "data": {
387
+ "text/plain": [
388
+ "tensor(3.2958)"
389
+ ]
390
+ },
391
+ "execution_count": 11,
392
+ "metadata": {},
393
+ "output_type": "execute_result"
394
+ }
395
+ ],
396
+ "source": [
397
+ "#The initial loss value that we expect\n",
398
+ "-torch.tensor(1/27.0).log()"
399
+ ]
400
+ },
401
+ {
402
+ "cell_type": "code",
403
+ "execution_count": 10,
404
+ "metadata": {},
405
+ "outputs": [
406
+ {
407
+ "name": "stdout",
408
+ "output_type": "stream",
409
+ "text": [
410
+ "mora.\n",
411
+ "mayah.\n",
412
+ "see.\n",
413
+ "mel.\n",
414
+ "rylee.\n",
415
+ "emmadiejd.\n",
416
+ "leg.\n",
417
+ "adelyn.\n",
418
+ "elin.\n",
419
+ "shi.\n",
420
+ "jen.\n",
421
+ "eden.\n",
422
+ "estanar.\n",
423
+ "kayziquetta.\n",
424
+ "noshir.\n",
425
+ "roshiriel.\n",
426
+ "kendreth.\n",
427
+ "konnie.\n",
428
+ "casube.\n",
429
+ "ged.\n"
430
+ ]
431
+ }
432
+ ],
433
+ "source": [
434
+ "# sample from the model\n",
435
+ "g = torch.Generator().manual_seed(2147483647 + 10)\n",
436
+ "\n",
437
+ "for _ in range(20):\n",
438
+ " \n",
439
+ " out = []\n",
440
+ " context = [0] * block_size # initialize with all ...\n",
441
+ " while True:\n",
442
+ " # forward pass the neural net\n",
443
+ " emb = C[torch.tensor([context])] # (1,block_size,d)\n",
444
+ " h = torch.tanh(emb.view(1, -1) @ W1 + b1)\n",
445
+ " logits = h @ W2 + b2\n",
446
+ " probs = F.softmax(logits, dim=1)\n",
447
+ " # sample from the distribution\n",
448
+ " ix = torch.multinomial(probs, num_samples=1, generator=g).item()\n",
449
+ " context = context[1:] + [ix]\n",
450
+ " out.append(ix)\n",
451
+ " # if we sample the special '.' token, break\n",
452
+ " if ix == 0:\n",
453
+ " break\n",
454
+ " \n",
455
+ " print(''.join(itos[i] for i in out)) # decode and print the generated word"
456
+ ]
457
+ }
458
+ ],
459
+ "metadata": {
460
+ "kernelspec": {
461
+ "display_name": "venv",
462
+ "language": "python",
463
+ "name": "python3"
464
+ },
465
+ "language_info": {
466
+ "codemirror_mode": {
467
+ "name": "ipython",
468
+ "version": 3
469
+ },
470
+ "file_extension": ".py",
471
+ "mimetype": "text/x-python",
472
+ "name": "python",
473
+ "nbconvert_exporter": "python",
474
+ "pygments_lexer": "ipython3",
475
+ "version": "3.10.0"
476
+ }
477
+ },
478
+ "nbformat": 4,
479
+ "nbformat_minor": 2
480
+ }
README.md ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## SET 1 - MAKEMORE (PART 3) 🔗
2
+
3
+ [![Documentation](https://img.shields.io/badge/Documentation-Available-blue)](https://muzzammilshah.github.io/Road-to-GPT/Makemore-part3/)
4
+ ![Number of Commits](https://img.shields.io/github/commit-activity/m/MuzzammilShah/NeuralNetworks-LanguageModels-3?label=Commits)
5
+ [![Last Commit](https://img.shields.io/github/last-commit/MuzzammilShah/NeuralNetworks-LanguageModels-3.svg?style=flat)](https://github.com/MuzzammilShah/NeuralNetworks-LanguageModels-3/commits/main)
6
+ ![Project Status](https://img.shields.io/badge/Status-Done-success)
7
+
8
+ &nbsp;
9
+
10
+ ### **Overview**
11
+ In this repository, I implemented **Batch Normalization** within a neural network framework to enhance training stability and performance, following Andrej Karpathy's approach in the **Makemore - Part 3** video.
12
+
13
+ This implementation focuses on **normalizing activations and gradients, addressing initialization issues, and utilizing Kaiming initialization to prevent saturation of activation functions**. Additionally, **visualization graphs** were created at the end to analyze the effects of these techniques on the training process and model performance.
14
+
15
+ &nbsp;
16
+
17
+ ### **🗂️Repository Structure**
18
+
19
+ ```plaintext
20
+ ├── .gitignore
21
+ ├── A-Main-Notebook.ipynb
22
+ ├── StarterCode.ipynb
23
+ ├── VisualizationTools.ipynb
24
+ ├── README.md
25
+ ├── notes/
26
+ │ ├── A-main-makemore-part3.md
27
+ │ └── README.md
28
+ └── names.txt
29
+ ```
30
+
31
+ - **Notes Directory**: Contains detailed notes corresponding to each notebook section.
32
+ - **Jupyter Notebooks**: Step-by-step implementation and exploration of the concepts.
33
+ - **README.md**: Overview and guide for this repository.
34
+ - **names.txt**: Supplementary data file used in training the model.
35
+
36
+ &nbsp;
37
+
38
+ ### **📄Instructions**
39
+
40
+ To get the best understanding:
41
+
42
+ 1. Start by reading the notes in the `notes/` directory. Each section corresponds to a notebook for step-by-step explanations.
43
+ 2. Open the corresponding Jupyter Notebook (e.g., `A-Main-Notebook.ipynb` for `A-main-makemore-part3.md`).
44
+ 3. Follow the code and comments for a deeper dive into the implementation details.
45
+
46
+ &nbsp;
47
+
48
+ ### **⭐Documentation**
49
+
50
+ For a better reading experience and detailed notes, visit my **[Road to GPT Documentation Site](https://muzzammilshah.github.io/Road-to-GPT/)**.
51
+
52
+ > **💡Pro Tip**: This site provides an interactive and visually rich explanation of the notes and code. It is highly recommended you view this project from there.
53
+
54
+ &nbsp;
55
+
56
+ ### **✍🏻Acknowledgments**
57
+ Notes and implementations inspired by the **Makemore - Part 3** video by [Andrej Karpathy](https://karpathy.ai/).
58
+
59
+ For more of my projects, visit my [Portfolio Site](https://muhammedshah.com).
StarterCode.ipynb ADDED
@@ -0,0 +1,446 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "Importing the PyTorch and Matplotlib utilities as before"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "code",
12
+ "execution_count": 1,
13
+ "metadata": {},
14
+ "outputs": [],
15
+ "source": [
16
+ "import torch\n",
17
+ "import torch.nn.functional as F\n",
18
+ "import matplotlib.pyplot as plt # for making figures\n",
19
+ "%matplotlib inline"
20
+ ]
21
+ },
22
+ {
23
+ "cell_type": "markdown",
24
+ "metadata": {},
25
+ "source": [
26
+ "Reading all the words"
27
+ ]
28
+ },
29
+ {
30
+ "cell_type": "code",
31
+ "execution_count": 2,
32
+ "metadata": {},
33
+ "outputs": [
34
+ {
35
+ "data": {
36
+ "text/plain": [
37
+ "['emma', 'olivia', 'ava', 'isabella', 'sophia', 'charlotte', 'mia', 'amelia']"
38
+ ]
39
+ },
40
+ "execution_count": 2,
41
+ "metadata": {},
42
+ "output_type": "execute_result"
43
+ }
44
+ ],
45
+ "source": [
46
+ "# read in all the words\n",
47
+ "words = open('names.txt', 'r').read().splitlines()\n",
48
+ "words[:8]"
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "code",
53
+ "execution_count": 3,
54
+ "metadata": {},
55
+ "outputs": [
56
+ {
57
+ "data": {
58
+ "text/plain": [
59
+ "32033"
60
+ ]
61
+ },
62
+ "execution_count": 3,
63
+ "metadata": {},
64
+ "output_type": "execute_result"
65
+ }
66
+ ],
67
+ "source": [
68
+ "len(words)"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "markdown",
73
+ "metadata": {},
74
+ "source": [
75
+ "Printing the vocabulary of all the lower case letters and the special dot token"
76
+ ]
77
+ },
78
+ {
79
+ "cell_type": "code",
80
+ "execution_count": 4,
81
+ "metadata": {},
82
+ "outputs": [
83
+ {
84
+ "name": "stdout",
85
+ "output_type": "stream",
86
+ "text": [
87
+ "{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",
88
+ "27\n"
89
+ ]
90
+ }
91
+ ],
92
+ "source": [
93
+ "# build the vocabulary of characters and mappings to/from integers\n",
94
+ "chars = sorted(list(set(''.join(words))))\n",
95
+ "stoi = {s:i+1 for i,s in enumerate(chars)}\n",
96
+ "stoi['.'] = 0\n",
97
+ "itos = {i:s for s,i in stoi.items()}\n",
98
+ "vocab_size = len(itos)\n",
99
+ "print(itos)\n",
100
+ "print(vocab_size)"
101
+ ]
102
+ },
103
+ {
104
+ "cell_type": "markdown",
105
+ "metadata": {},
106
+ "source": [
107
+ "Here we are reading the dataset and processing it. In the end of this cell, we are also splitting the dataset into three- Train, Dev and Loss split"
108
+ ]
109
+ },
110
+ {
111
+ "cell_type": "code",
112
+ "execution_count": 5,
113
+ "metadata": {},
114
+ "outputs": [
115
+ {
116
+ "name": "stdout",
117
+ "output_type": "stream",
118
+ "text": [
119
+ "torch.Size([182625, 3]) torch.Size([182625])\n",
120
+ "torch.Size([22655, 3]) torch.Size([22655])\n",
121
+ "torch.Size([22866, 3]) torch.Size([22866])\n"
122
+ ]
123
+ }
124
+ ],
125
+ "source": [
126
+ "# build the dataset\n",
127
+ "block_size = 3 # context length: how many characters do we take to predict the next one?\n",
128
+ "\n",
129
+ "def build_dataset(words): \n",
130
+ " X, Y = [], []\n",
131
+ " \n",
132
+ " for w in words:\n",
133
+ " context = [0] * block_size\n",
134
+ " for ch in w + '.':\n",
135
+ " ix = stoi[ch]\n",
136
+ " X.append(context)\n",
137
+ " Y.append(ix)\n",
138
+ " context = context[1:] + [ix] # crop and append\n",
139
+ "\n",
140
+ " X = torch.tensor(X)\n",
141
+ " Y = torch.tensor(Y)\n",
142
+ " print(X.shape, Y.shape)\n",
143
+ " return X, Y\n",
144
+ "\n",
145
+ "import random\n",
146
+ "random.seed(42)\n",
147
+ "random.shuffle(words)\n",
148
+ "n1 = int(0.8*len(words))\n",
149
+ "n2 = int(0.9*len(words))\n",
150
+ "\n",
151
+ "Xtr, Ytr = build_dataset(words[:n1]) # 80%\n",
152
+ "Xdev, Ydev = build_dataset(words[n1:n2]) # 10%\n",
153
+ "Xte, Yte = build_dataset(words[n2:]) # 10%"
154
+ ]
155
+ },
156
+ {
157
+ "cell_type": "markdown",
158
+ "metadata": {},
159
+ "source": [
160
+ "Almost the same MLP, but we have cleaned it up to add those hard coded values into variables so we just have to modify them there"
161
+ ]
162
+ },
163
+ {
164
+ "cell_type": "code",
165
+ "execution_count": 6,
166
+ "metadata": {},
167
+ "outputs": [
168
+ {
169
+ "name": "stdout",
170
+ "output_type": "stream",
171
+ "text": [
172
+ "11897\n"
173
+ ]
174
+ }
175
+ ],
176
+ "source": [
177
+ "# MLP revisited\n",
178
+ "n_embd = 10 # the dimensionality of the character embedding vectors\n",
179
+ "n_hidden = 200 # the number of neurons in the hidden layer of the MLP\n",
180
+ "\n",
181
+ "g = torch.Generator().manual_seed(2147483647) # for reproducibility\n",
182
+ "C = torch.randn((vocab_size, n_embd), generator=g)\n",
183
+ "W1 = torch.randn((n_embd * block_size, n_hidden), generator=g)\n",
184
+ "b1 = torch.randn(n_hidden, generator=g)\n",
185
+ "W2 = torch.randn((n_hidden, vocab_size), generator=g)\n",
186
+ "b2 = torch.randn(vocab_size, generator=g)\n",
187
+ "\n",
188
+ "parameters = [C, W1, b1, W2, b2]\n",
189
+ "print(sum(p.nelement() for p in parameters)) # number of parameters in total\n",
190
+ "for p in parameters:\n",
191
+ " p.requires_grad = True"
192
+ ]
193
+ },
194
+ {
195
+ "cell_type": "markdown",
196
+ "metadata": {},
197
+ "source": [
198
+ "Here we are optimizing the NN. Same as before, just those hard coded numbers (or magic numbers as Andrej sensei calls it) have been replaced with variable names for more readability"
199
+ ]
200
+ },
201
+ {
202
+ "cell_type": "code",
203
+ "execution_count": 7,
204
+ "metadata": {},
205
+ "outputs": [
206
+ {
207
+ "name": "stdout",
208
+ "output_type": "stream",
209
+ "text": [
210
+ " 0/ 200000: 27.8817\n",
211
+ " 10000/ 200000: 2.8244\n",
212
+ " 20000/ 200000: 2.5473\n",
213
+ " 30000/ 200000: 2.8961\n",
214
+ " 40000/ 200000: 2.0967\n",
215
+ " 50000/ 200000: 2.5020\n",
216
+ " 60000/ 200000: 2.4999\n",
217
+ " 70000/ 200000: 2.0510\n",
218
+ " 80000/ 200000: 2.4076\n",
219
+ " 90000/ 200000: 2.3172\n",
220
+ " 100000/ 200000: 2.0199\n",
221
+ " 110000/ 200000: 2.3338\n",
222
+ " 120000/ 200000: 1.8767\n",
223
+ " 130000/ 200000: 2.3989\n",
224
+ " 140000/ 200000: 2.2102\n",
225
+ " 150000/ 200000: 2.1937\n",
226
+ " 160000/ 200000: 2.0843\n",
227
+ " 170000/ 200000: 1.8780\n",
228
+ " 180000/ 200000: 1.9727\n",
229
+ " 190000/ 200000: 1.8222\n"
230
+ ]
231
+ }
232
+ ],
233
+ "source": [
234
+ "# same optimization as last time\n",
235
+ "max_steps = 200000\n",
236
+ "batch_size = 32\n",
237
+ "lossi = []\n",
238
+ "\n",
239
+ "for i in range(max_steps):\n",
240
+ " \n",
241
+ " # minibatch construct\n",
242
+ " ix = torch.randint(0, Xtr.shape[0], (batch_size,), generator=g)\n",
243
+ " Xb, Yb = Xtr[ix], Ytr[ix] # batch X,Y\n",
244
+ " \n",
245
+ " # forward pass\n",
246
+ " emb = C[Xb] # embed the characters into vectors\n",
247
+ " embcat = emb.view(emb.shape[0], -1) # concatenate the vectors\n",
248
+ " hpreact = embcat @ W1 + b1 # hidden layer pre-activation\n",
249
+ " h = torch.tanh(hpreact) # hidden layer\n",
250
+ " logits = h @ W2 + b2 # output layer\n",
251
+ " loss = F.cross_entropy(logits, Yb) # loss function\n",
252
+ " \n",
253
+ " # backward pass\n",
254
+ " for p in parameters:\n",
255
+ " p.grad = None\n",
256
+ " loss.backward()\n",
257
+ " \n",
258
+ " # update\n",
259
+ " lr = 0.1 if i < 100000 else 0.01 # step learning rate decay\n",
260
+ " for p in parameters:\n",
261
+ " p.data += -lr * p.grad\n",
262
+ "\n",
263
+ " # track stats\n",
264
+ " if i % 10000 == 0: # print every once in a while\n",
265
+ " print(f'{i:7d}/{max_steps:7d}: {loss.item():.4f}')\n",
266
+ " lossi.append(loss.log10().item())"
267
+ ]
268
+ },
269
+ {
270
+ "cell_type": "markdown",
271
+ "metadata": {},
272
+ "source": [
273
+ "Here we plot the loss"
274
+ ]
275
+ },
276
+ {
277
+ "cell_type": "code",
278
+ "execution_count": 8,
279
+ "metadata": {},
280
+ "outputs": [
281
+ {
282
+ "data": {
283
+ "text/plain": [
284
+ "[<matplotlib.lines.Line2D at 0x28412485fc0>]"
285
+ ]
286
+ },
287
+ "execution_count": 8,
288
+ "metadata": {},
289
+ "output_type": "execute_result"
290
+ },
291
+ {
292
+ "data": {
293
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAicAAAGdCAYAAADJ6dNTAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAATylJREFUeJzt3XdYVfXjB/D3ZYMKqMgSFPcWEZNwDxzkT9uaWpqVpek3v2FllCPtm1iZ2TAtc7QdZTbcouTCheJIRXGBynAxlf35/QH3ei/33Mm4B+779Tw8j/fcc879HC7e876fqRBCCBARERHJhI2lC0BERESkjuGEiIiIZIXhhIiIiGSF4YSIiIhkheGEiIiIZIXhhIiIiGSF4YSIiIhkheGEiIiIZMXO0gUwRklJCW7cuIF69epBoVBYujhERERkBCEEsrOz4evrCxsb4+tDakQ4uXHjBvz9/S1dDCIiIjJDcnIy/Pz8jN6/RoSTevXqASi9OFdXVwuXhoiIiIyRlZUFf39/1X3cWDUinCibclxdXRlOiIiIahhTu2SwQywRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREclKjVj4r6qs2HcZyXfu4Znu/mjrzQUFiYiI5MCqa042nbyB1QeuIOn2PUsXhYiIiMpYdTghIiIi+WE4ISIiIllhOCEiIiJZYTgBICxdACIiIlKx6nCiUCgsXQQiIiIqx6rDCREREckPwwkRERHJisnhZM+ePRg+fDh8fX2hUCiwceNGo4/dv38/7Ozs0KVLF1NfloiIiKyEyeEkNzcXgYGBWLJkiUnHZWRkYNy4cRg4cKCpL1nlBHvEEhERyYbJ09eHh4cjPDzc5BeaNGkSxowZA1tbW5NqW6oSu8MSERHJT7X0OVm1ahUuXbqEOXPmGLV/fn4+srKyNH6IiIjIOlR5OLlw4QLefvtt/Pjjj7CzM66iJioqCm5ubqoff3//Ki4lERERyUWVhpPi4mKMGTMGc+fORevWrY0+LjIyEpmZmaqf5OTkKiwlERERyYnJfU5MkZ2djaNHj+L48eOYOnUqAKCkpARCCNjZ2WH79u0YMGCA1nGOjo5wdHSsyqKVwx6xREREclGl4cTV1RWnTp3S2PbVV19h165d+PXXX9GsWbOqfHmDOEEsERGR/JgcTnJycpCYmKh6fPnyZcTHx6NBgwZo0qQJIiMjcf36dXz//fewsbFBx44dNY739PSEk5OT1nYiIiIiwIxwcvToUfTv31/1OCIiAgAwfvx4rF69GikpKUhKSqq8EhIREZFVUQgh/ynIsrKy4ObmhszMTLi6ulbaeZ9edgBHrtzFsme7YmhHn0o7LxEREZl//+baOuAMsURERHJi1eFEwTliiYiIZMeqwwkRERHJD8MJERERyQrDCREREckKwwk4PywREZGcWHc4YX9YIiIi2bHucEJERESyw3BCREREssJwQkRERLLCcALOEEtERCQnDCdEREQkK1YdTjhYh4iISH6sOpwQERGR/DCcABCcho2IiEg2GE6IiIhIVhhOiIiISFasOpwo2COWiIhIdqw6nBAREZH8MJyAk7ARERHJCcMJERERyQrDCREREcmKVYcTBeeIJSIikh2rDidEREQkPwwnAOeHJSIikhGGEyIiIpIVhhMiIiKSFasOJ5whloiISH6sOpwQERGR/DCcABCcIpaIiEg2GE6IiIhIVhhOiIiISFasOpywQywREZH8WHU4ISIiIvlhOCEiIiJZYTghIiIiWWE4ISIiIlmx6nCiAHvEEhERyY1VhxMiIiKSH5PDyZ49ezB8+HD4+vpCoVBg48aNevffsGEDBg0ahEaNGsHV1RWhoaHYtm2bueWtEpwgloiISD5MDie5ubkIDAzEkiVLjNp/z549GDRoEDZv3oy4uDj0798fw4cPx/Hjx00uLBEREdV+dqYeEB4ejvDwcKP3X7x4scbj+fPn448//sBff/2FoKAgU1+eiIiIajmTw0lFlZSUIDs7Gw0aNNC5T35+PvLz81WPs7KyqqQsaVl5AIDs/KIqOT8RERGZrto7xC5cuBA5OTkYOXKkzn2ioqLg5uam+vH396+SslxIzwEAzNp4ukrOT0RERKar1nDy888/Y+7cuVi3bh08PT117hcZGYnMzEzVT3JycjWWkoiIiCyp2pp11qxZg5deegnr169HWFiY3n0dHR3h6OhYTSUjIiIiOamWmpNffvkFEyZMwC+//IJhw4ZVx0sSERFRDWVyzUlOTg4SExNVjy9fvoz4+Hg0aNAATZo0QWRkJK5fv47vv/8eQGlTzvjx4/HZZ58hJCQEqampAABnZ2e4ublV0mUQERFRbWFyzcnRo0cRFBSkGgYcERGBoKAgzJ49GwCQkpKCpKQk1f7ffPMNioqKMGXKFPj4+Kh+pk2bVkmXQERERLWJyTUn/fr1g9Azperq1as1HsfExJj6EkRERGTFuLYOERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREckKwwkRERHJCsMJERERyQrDCREREcmKyeFkz549GD58OHx9faFQKLBx40aDx8TExKBr165wdHREy5YtsXr1ajOKSkRERNbA5HCSm5uLwMBALFmyxKj9L1++jGHDhqF///6Ij4/Hf//7X7z00kvYtm2byYUlIiKi2s/O1APCw8MRHh5u9P7Lli1Ds2bN8MknnwAA2rVrh3379uHTTz/FkCFDTH15IiIiquWqvM9JbGwswsLCNLYNGTIEsbGxVf3SREREVAOZXHNiqtTUVHh5eWls8/LyQlZWFu7fvw9nZ2etY/Lz85Gfn696nJWVVdXFJCIiIpmQ5WidqKgouLm5qX78/f0tXSQiIiKqJlUeTry9vZGWlqaxLS0tDa6urpK1JgAQGRmJzMxM1U9ycnJVF5OIiIhkosqbdUJDQ7F582aNbTt27EBoaKjOYxwdHeHo6FjVRSMiIiIZMrnmJCcnB/Hx8YiPjwdQOlQ4Pj4eSUlJAEprPcaNG6faf9KkSbh06RLeeustnDt3Dl999RXWrVuH119/vXKugIiIiGoVk8PJ0aNHERQUhKCgIABAREQEgoKCMHv2bABASkqKKqgAQLNmzbBp0ybs2LEDgYGB+OSTT/Dtt99yGDERERFJUgghhKULYUhWVhbc3NyQmZkJV1fXSjtvwNubVP++smBYpZ2XiIiIzL9/y3K0jiWUlMg+oxEREVkFhpMy8dcyLF0EIiIiAsOJSg1o3SIi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",
294
+ "text/plain": [
295
+ "<Figure size 640x480 with 1 Axes>"
296
+ ]
297
+ },
298
+ "metadata": {},
299
+ "output_type": "display_data"
300
+ }
301
+ ],
302
+ "source": [
303
+ "plt.plot(lossi)"
304
+ ]
305
+ },
306
+ {
307
+ "cell_type": "markdown",
308
+ "metadata": {},
309
+ "source": [
310
+ "Seeing the loss in train and val loss. There is a slight modification to this as to how the splitting is done."
311
+ ]
312
+ },
313
+ {
314
+ "cell_type": "markdown",
315
+ "metadata": {},
316
+ "source": [
317
+ "Here the decorator `@torch.no_grad()` basically tells PyTorch to not maintain the grad value, as it assumes/anticipated that the backpropagation will be calculated after this and we are saying No."
318
+ ]
319
+ },
320
+ {
321
+ "cell_type": "code",
322
+ "execution_count": 9,
323
+ "metadata": {},
324
+ "outputs": [
325
+ {
326
+ "name": "stdout",
327
+ "output_type": "stream",
328
+ "text": [
329
+ "train 2.12243390083313\n",
330
+ "val 2.1646578311920166\n"
331
+ ]
332
+ }
333
+ ],
334
+ "source": [
335
+ "@torch.no_grad() # this decorator disables gradient tracking\n",
336
+ "def split_loss(split):\n",
337
+ " x,y = {\n",
338
+ " 'train': (Xtr, Ytr),\n",
339
+ " 'val': (Xdev, Ydev),\n",
340
+ " 'test': (Xte, Yte),\n",
341
+ " }[split]\n",
342
+ " emb = C[x] # (N, block_size, n_embd)\n",
343
+ " embcat = emb.view(emb.shape[0], -1) # concat into (N, block_size * n_embd)\n",
344
+ " h = torch.tanh(embcat @ W1 + b1) # (N, n_hidden)\n",
345
+ " logits = h @ W2 + b2 # (N, vocab_size)\n",
346
+ " loss = F.cross_entropy(logits, y)\n",
347
+ " print(split, loss.item())\n",
348
+ "\n",
349
+ "split_loss('train')\n",
350
+ "split_loss('val')"
351
+ ]
352
+ },
353
+ {
354
+ "cell_type": "markdown",
355
+ "metadata": {},
356
+ "source": [
357
+ "Sampling of the model: Forward pass -> Sampling from the distribution -> Continuing till we get the special token '.'"
358
+ ]
359
+ },
360
+ {
361
+ "cell_type": "code",
362
+ "execution_count": 10,
363
+ "metadata": {},
364
+ "outputs": [
365
+ {
366
+ "name": "stdout",
367
+ "output_type": "stream",
368
+ "text": [
369
+ "mora.\n",
370
+ "mayah.\n",
371
+ "see.\n",
372
+ "mel.\n",
373
+ "rylee.\n",
374
+ "emmadiejd.\n",
375
+ "leg.\n",
376
+ "adelyn.\n",
377
+ "elin.\n",
378
+ "shi.\n",
379
+ "jen.\n",
380
+ "eden.\n",
381
+ "estanar.\n",
382
+ "kayziquetta.\n",
383
+ "noshir.\n",
384
+ "roshiriel.\n",
385
+ "kendreth.\n",
386
+ "konnie.\n",
387
+ "casube.\n",
388
+ "ged.\n"
389
+ ]
390
+ }
391
+ ],
392
+ "source": [
393
+ "# sample from the model\n",
394
+ "g = torch.Generator().manual_seed(2147483647 + 10)\n",
395
+ "\n",
396
+ "for _ in range(20):\n",
397
+ " \n",
398
+ " out = []\n",
399
+ " context = [0] * block_size # initialize with all ...\n",
400
+ " while True:\n",
401
+ " # forward pass the neural net\n",
402
+ " emb = C[torch.tensor([context])] # (1,block_size,d)\n",
403
+ " h = torch.tanh(emb.view(1, -1) @ W1 + b1)\n",
404
+ " logits = h @ W2 + b2\n",
405
+ " probs = F.softmax(logits, dim=1)\n",
406
+ " # sample from the distribution\n",
407
+ " ix = torch.multinomial(probs, num_samples=1, generator=g).item()\n",
408
+ " context = context[1:] + [ix]\n",
409
+ " out.append(ix)\n",
410
+ " # if we sample the special '.' token, break\n",
411
+ " if ix == 0:\n",
412
+ " break\n",
413
+ " \n",
414
+ " print(''.join(itos[i] for i in out)) # decode and print the generated word"
415
+ ]
416
+ },
417
+ {
418
+ "cell_type": "markdown",
419
+ "metadata": {},
420
+ "source": [
421
+ "So yeah, this will be our starting point. Also use this as a revision for the previous lecture."
422
+ ]
423
+ }
424
+ ],
425
+ "metadata": {
426
+ "kernelspec": {
427
+ "display_name": "venv",
428
+ "language": "python",
429
+ "name": "python3"
430
+ },
431
+ "language_info": {
432
+ "codemirror_mode": {
433
+ "name": "ipython",
434
+ "version": 3
435
+ },
436
+ "file_extension": ".py",
437
+ "mimetype": "text/x-python",
438
+ "name": "python",
439
+ "nbconvert_exporter": "python",
440
+ "pygments_lexer": "ipython3",
441
+ "version": "3.10.0"
442
+ }
443
+ },
444
+ "nbformat": 4,
445
+ "nbformat_minor": 2
446
+ }
VisualizationTools.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
names.txt ADDED
The diff for this file is too large to render. See raw diff