Initial commits for files
Browse files- A-main-notebook.ipynb +480 -0
- README.md +59 -0
- StarterCode.ipynb +446 -0
- VisualizationTools.ipynb +0 -0
- 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/YmNj1TSTiIiI3JhLVusEBwcjKysL69evx1tvvYX09HQsW7bM4vbjx49HQUGB8efo0aOuaCYREZHHkqjjBD5qNo6IiIC3tzdyc3NNbs/NzUVUVJTFx3l5eaF169YAgMTEROzatQuTJ09Gv379zG7v7+8Pf39/NU0jIiKiOkJVz4mfnx+SkpKQkZFhvK2iogIZGRlISUlRvJ+KigoUFxereWqnkWmMjYiISC8yJWFT1XMCAOnp6Rg5ciS6d++OHj16YMaMGSgqKsKoUaMAACNGjECTJk0wefJkAJXzR7p3745WrVqhuLgYCxYswFdffYVZs2ZpeyRERERkNy+J0rKqDk6GDRuGU6dOYcKECcjJyUFiYiIWLlxonCSbnZ0Nr2pHWFRUhCeeeALHjh1DYGAg2rdvj6+//hrDhg3T7iiIiIjIQfL0nBiEEELvRthSWFiI0NBQFBQUICRE27wkH684iLcW7NJ0n0RERO7mvuRmePu2zpru097rt0SdOEREREQMToiIiEgyHh+cSDQ5mYiISDcyXQ49PjghIiIiuTA4ISIiIqnynDA4ISIiIqkwOCEiIiKpMDghIiIiqTA4ISIiIqlWr3p8cGKQ6WwQERHpRKaroccHJ0RERCQXBidEREQkFY8PTmTqxiIiItKLTNMcPD44ISIiIk6Ilda1rRvq3QQiIiJdGCQaS/D44OSOpKbG3zs3CdOvIURERASAwQlCA331bgIRERFV4/HBCREREXHOibRkOjFERESuJNMlkMEJERERSYXBCREREUmFwQkRERFJNbWBwQkRERFJhcEJERERMX29rITQuwVERETE4KSGHi3C9W4CERGRR2NwUsNtXZvo3QQiIiKPxuCkGoNBriQ0REREriLT9Y/BCREREUkVnTA4ISIiInhxtQ4RERHJRJ7QhMFJLQG+3no3gYiIyKMxOKnhps7R6Nu2kd7NICIicimJRnUYnNTk5+OFLx7qoXcziIiIPBaDEyIiIpIKgxMiIiKSCoMTAD3iKlPW39Gtqc4tISIi0odBovU6Pno3QAbfPtoTFy6XITTIV++mEBER6YITYiXj7WVgYEJERB5NotiEwQkRERHJhcEJERERSYXBCREREUk16YTBCREREUmFwQkRERFJhcEJERERSYXBCREREcFLniknDE4smfdoT9yaEKN3M4iIiFzCixNi5ZfcsiHev7er3s0gIiLyOAxOiIiIiBliiYiIiCxhcKJSVQVjIiKiukSiKScMTtTq2ZLBCRERkTMxOCEiIiKpMDghIiIiGCQa12FwQkRERFJhcGLDj2NSnJ7v5JnUNk7dPxERkTthcGJDUvNw3JoQg4SmoQCAWxObaP4c9fx8NN8nERGRu+JVUaEfx/RC4eUyhNfzU/W4h69rgYXbc3A8/5LFbQSEo80jIiKqM9hzopCPt5fqwAQAXr05HgmxoU5oERERkXYkmg/L4ISIiIgAg0QJ7BmcOFG7yOBat41La2d225AAjrAREREBDE6c5qkb2uCPp3sDAIZd0wwA0CKiHp7s3xpP9GuFYH/TYCSivr/L20hERFSFwzoewNfLAC+vyjPdt20jLB/XD38+0wcA8MLA9tj62gDjtoLzYYmISGdeDE48T/OG9eDnc/XldkUmvkBfbyx4qrfTn4eIiEhLdgUnM2fORFxcHAICApCcnIx169ZZ3Pbjjz9G79690aBBAzRo0ACpqalWtyftdI9rgPiYEL2bQUREbsCtJ8TOmzcP6enpmDhxIjZt2oSEhASkpaUhLy/P7PbLli3Dvffei6VLlyIzMxOxsbEYMGAAjh8/7nDjSZkx/Vrp3QS7JbdgFWgiIk+jOjiZPn06Ro8ejVGjRiE+Ph6zZ89GUFAQ5s6da3b7b775Bk888QQSExPRvn17fPLJJ6ioqEBGRobDjafaMp7rW+u26nNaOjVxr56Uh69roXcTiIjIxVQFJyUlJdi4cSNSU1Ov7sDLC6mpqcjMzFS0j4sXL6K0tBTh4Za/ERcXF6OwsNDkpy7Tcj5sq0b1rd7v7+Ot4bM5n0xVMomIyDVUBSenT59GeXk5IiMjTW6PjIxETk6Oon28+OKLiImJMQlwapo8eTJCQ0ONP7GxsWqa6XSDOkUBAK6Ja2Bxm4hg5UuDuVqHiIj0JtN3QZdm/poyZQq+++47LFu2DAEBARa3Gz9+PNLT041/FxYWShWgvH9vV+zLvYDssxex/vBGk/tuaN8YkaEBuDOpqU6tIyIicm+qgpOIiAh4e3sjNzfX5Pbc3FxERUVZfey0adMwZcoULF68GF26dLG6rb+/P/z95U1K5uvthfiYEBw9d7HWfTd1jsYddgQm7DwhIiI9yTSMrmpYx8/PD0lJSSaTWasmt6akpFh83DvvvIM33ngDCxcuRPfu3e1vrRuQIci4rWsTAO69Soe0wbIIROSOVH9ypaenY+TIkejevTt69OiBGTNmoKioCKNGjQIAjBgxAk2aNMHkyZMBAP/6178wYcIE/Pe//0VcXJxxbkr9+vVRv771yZueQmgc0ky/OwETb4lHWJD6KspUt8SEBaIw57zezSAiUkX1UuJhw4Zh2rRpmDBhAhITE5GVlYWFCxcaJ8lmZ2fj5MmTxu1nzZqFkpIS3HnnnYiOjjb+TJs2TbujIBMGg8HhwMTZS457t4lweB8P9GyuQUvIEXMeSLK5TZOwwFq3pXWMNLMlEelJnkEdOzPEjh07FkeOHEFxcTHWrl2L5ORk433Lli3D559/bvz78OHDEELU+nnttdccbTtpLLVDY7SNrI8n+zt/OGjiLR0d3sfzA8xXeCbXGRBvO8h49eb4Wre1bsxeUyKyjLV1HBDXsJ7LnqtHnPMzpdb398Ffz/bFuLT2Tn8ufx/H33qhQb4atMR9jUzRv+fI1gS6Rc/2wcBO1ifLExHVxODEAe2igjH7/iT8/OS1xtv06BYb3CUa/ds1wuz7bXexu5s+bR0f/qmrokJrD5fUNLp3Sxe0xLLGwZZTBhARWcLgxEEDO0UhMTZM8/2+MaQjpt+doGjbhvX88NmoHg5/Q332xrbG3+v7O3eVR80v3N4WanW7W0ZbV3qwV5zNbexZ1k5EnslXgx5trcjTEg9mLkPsAylxaBHh3GGjj4Z3w8cjuqN9VDB++8d1aF5tmOpfd1jPRaM1P2/nvhVbOuG19LEQULmKFkNjjri/ZzNdn5+ItDW4c7TeTTBicOLBAny9cGN8JBY+0wedmoSa3Ne8YT3FK3Z+eNw0x829PVx/0ercJBTto4It3v/HM71d2Bq53HONc7IrN1CwIkzrZfJE5Dy+3vKs12FwIjGTyYZW3jN61+a5Ji4ct3erTPzWq1VDTL69s8vb8NqtHbHwmT4W7/fk4SE9zkdNqR24dJiIlGNwojFnxQnyxLPmvTW0Mz68rytmK8h7Yc7oPlcnbt7fsxlCAnzw77uUzbmx5a9nLQctdVFMqOkkVBlSUn94X1fj71EhnCRLRNYxOHFDw5P1H+uvOQk40M8bN3eJQUiAfct7n76hjfH37s3DkTVhgGaTOdtGWh7uqW5oYgzeuydRk+e0xtq47h3d6uYE1gDfqz1XfhJNuiOiq2T4IlOFnxJkl6YNbC9jVaPmah0vOyebvjjQ/hwtM+7piiGJTex+vFK9Wje0eN8bQx1PTmeLVj1S9moWHoTBnWN0bYOn6N+ukd5NILILgxMPI1RMUHnq+ja2N7KTsyJ0NcUOJ5jJXKqGM4bwgvycu4S7WXiQ1R6piPrWJ7k+fF0Lq/d3axZm8b4fx6Tg5i7RmHZXAuJjnFsegSq9PNix9ziRXhicSEJN0KDVdd3Hy/rpH9AxClPvdN6SYkv1dexZ4WHromrOQzYutHWRreDCFnOp6JVKah6OD+/rhqhQzjlxFZlWXxCpweDEAzxwJc35kETTrvRrW9vOvtpE4+Gb6topnAtizWcPXoNpdyWY5GixpPq8lpqU5FkJr+e+VZ4furYFWjeuj7u6az+nJal5A833SUSejcFJHWCrp6FJWCD2vDkQM4YlmtxuKSurErckVAY69vRYVHn2xrZ4rG9L/FIt/b81T13futZt/ds3xp0KJ85Wz4Bb0709rOcCSW4RjuYNgxQ9jy0GHdZeTbglHovT+zpl2OjHMb003ycRuZ5M/WwMTiSgZEjHNOWJ+reQv4+3ZvM8Vr7QHwPiI/HjmF7ISO+n+HHBNVLi1/P3wfhBHZCgMP1/aw16Wuxla8XP4C6uz6xoadKwlvN5rM0hIfl5WXgvvDG0k4tbIr9HPHCYtyaJFuswOCH1fLwNMBgMSGreQFVl4J/HKushkZGt1Ukf3tvV6v3O4sjqJPNMP53eu0fdcTEfrFwsvW9DApw78dodveLgBHln+nXsdR43fMrgREI7J6UBkKuLTQvNwq8Oi4QG1g5qqpJz9Wrl/ErEAzs6ViSxJmu9FXe5cfE9f19+RLgzg8GguIAoyatz09A6dz2whZ88EnL2clK9+Hp74dex1+GnJ3qZrXq8/IV+2PTqjYjUIYNoSivLuUeq+NpZnPAZK3NdZNOykWMFEg1wLI+KHl3rehdQdDa9y1u42pP9racT0Ltgp7087DQyONFa5xoF9Ooie7PAApXfALo2M9896e/j7fIVMZnjr8fcB7sjTUFPytu32VejpklYIGYN72Zzu3QVQUzV6qKq5djVJzc3DvFX2cKrRlxZ2eWI6ywsEVdCSZCotXFp7RBsxzBHdw/rZncX49La49vRPfGnhVpbji6nt6RvW+sJ75TOrdOTHpP1LambX9F1sPKF/sg7fxntrFTGtUTqbzY12vafB5JQz0yvh0ua4oQXKjo0ENGhypZLt25cHwG+XrhcWqH6eWxddBNiw/CUlaXO1Y26Ng73Xan83KlJKBY81RvRoQFYf/gslu45hWviGmBzdr7qNgK2c994El9vA0rLZf7nJEv0CHIHd4nG8r2nzN7XMqIefnnyWsT983e79++Mzz+Z8ZNII7HhQUhqHu6S59JzRrU7RP/OVDVXpb3KIDQsyA8bXknF9tfTHD5/E2/piDbVVg/Fx4SgQT0/DOgYhcm3d9a9dk31Ojr2DoU5m5Ke/Yj69vdAVfecGw3ryer+ns6tJ3adgpxPjnBkMYC9PbbuTs5PDqrFWnebK4eSwhSuzmkRUTl3oWaFXEe5sqJtl6a1X9c3b+uMybd3xtePJKveX0R9f7NzbWRm7X3XzcLwXGigL6be2QX/vitBt142ax65rgV2ThrosueLdKOMuEr/v13tzaGd0bOl87783Rgf6bR9A44Nhd8nQaFXPTA4cROmeU5M3ZlkPYGYVja9eiP8fbxtbwjgr2f7YMuEAWiscTDh6l6jqh6SQZ0q85jU9/fBvT2aIaK+Px7sFQfA+R9saikZN65ZVVqN5eP6Yc4DSehnpajcXd1jFVeVrr6KyxW8vAwmvTvOpubC5OiEZEep7REEgFsTXFPE8f17utqcE+XI+9oR2i/p1wfznJBm7u7e1KFMr2qomazq6+2lKgeKq3krrDny2z+uQ9aEG9HMTHbYfw5qj/+OTsYHOuU4cUT/do3tfmzzhvWQ1jFKs2RvSnZzTVztXpqbu0Tjpyfkzk47rHssBigMXu/v2Qw/K8yWLJP37kk0Zox2hqrVVI1DAjBpiPXkcS0j7AvuBnVyLLWAs+a4uHPJDEcxOJGArF2pddErgzsgNjwQ4wcp+6bj4+2FsCDzHxC+3l7o1SrCqd/C37sn0eTvVhp9s7anuKKezI27D+wUZXHllyz+dWcXi5l86wqDweDWBQZ3TkrTvIdXJj1aXB0OaxcZjKXP93OLJHwMTnQ0/e4EDEmMwbBrmuGFK92CD/RUt5Szdxvry9fI1CO9W2LlC9ejaQPthxJ+eDzFYqVlew1JbGL8PTo0AAstLI90pmtbu37lQ01O7262I1ZzVRf4h/e5X8+cO9Eir5TSlTS6DDtVa5rBUDkfcOtraa5vh0oMTnR0e7emeO+ervDz8cJNnaOx8ZVUTBrS0ebjqnen3+zkmi7u9f1aG4FXekLUZqq9Ji4cXz2sfqKsUiEBvrqsfukR59zgxJHJgvZw9soMR5i7xg3urOx/PLmF+QmjgX7On19jT36MmvV9nDnh1YSOnTyunl9VU4iZzNyyYnAikYb1/U0CDyXfzLQs8ia7RsHaLO20ZVF6H7w5tBOeSVWWd0StaBurN4Ymmh+/d7ehGKUSYkMxpl8rTLnd+UsmR6Y0Rx8bybJ05cApbmBh+FHpnBdXaRIWiL1vDqrVS/zdoykIckEg5SyuXEmoRqNqSRkfujbO6rYyXU7kH3jyYNW/jcj0pnG12fcnYV/ueaS0dM3wQtMGQbhf5fCaGn3bNsK4tHaIjw7BqM/X17q/jYurL//3kWTc98lalz5ndQYYjKsdMnblOvW5kjV6D3V1o2rNPhLmmlGTi8ddQvLebSLw3I1t0T46BKO/3KB3c4xu7BCJIF9v+Pt6KcqELQsGJyS9gZ2iMNDB2fR66NkyHJ+OvKbW7QaDAU/2b+2051UbyPbScJjDXS4kSlh7GRvW80PBpVKH9j9jWCKemZfl0D5IHgaDAf9QmOXZlQwGYKoD9a70Il9ITVRHRIYESJmETA1P7rFzNi0LXNbTaDikbY1eu+EemgBMS9byAdli70IvrSfm64HBiRvSo24EKffm0E5oHxWMfypcrky2hQa6Pt+DtflcnZqEal4kzZHSKdFhyupDmXND+8ZI6xiJ0b1bYFxaO/sbIaGnbfRk/OLkvDIN6/lhrpneU6XmPNDd+Luat0dPFw2BOxODEzc0ID4Sn47sjjX/vF7vppAZ9/dsjoXP9FFcUFApV9T96hBde77LXd0rM71e397+xG3WWIoBqmcrddVk6OpGXRuH2PBAPNGvVa37Hu9b+zYtmJv0bGvSe9+2jTDngSS7n/PTB6/BnAe64+XB8Qh28coprT1XI7h69sa22PbaAIvbO7tWWMP6fg7luUmys/K1vXPmZKpKzODEDRkMBtzQIRIxDnxbUsrDCmF6rNX/vB6/jr0OzRvWTvIWExaI3W8MxKcju5t5pP7WvXyD4m0tvZ97mFmG2yDIDyvG9TfmIKpOSeK9uQ9af720GjL74qEeaNWoPppHaL9MVWkbZSjh0CQsEE3MfCY6Wmn77u7KyjDIJNSNlgxbwuCE6rTf/nGd3k1wC03CAtHZTKHDKgG+3rovW//yoR546abagULj4AD4OJiFdepdCcaK09XZe8ypHSJxffurF+wXB7ZHbHigSc6S7s0bKC7a2bxG+QRzmYKfur6NzdozzpLWMRLfP5aiy3NXsXSqavZGqe0daBFR394m6fLlzpH5JjLNMWNw4iZG9WoBoG5MdHKV27s2QScXVmyWRQs764vIrk/bRni0j3OGU8Lr+eHZG9s6Zd8AMKZfK6x84XqTSbA+3l74v7HK5jz88FiKsdAkUFmlt6Z6/j42a884YuIt8RbvMxgMZnuf6gIt803Z2qx5wyCHErVNvCUeXz7Uw+7Hy4TBiZtIjY/Eyhf647MH7Z9cpQetvzhIFNhLq0N0CGbfn6T4wqe36udUpm9urqA06WLjkADF8wiq6jFZCybsMeraFpruTwuP9mnptH2P7t0CzRsG4T4HViyp/fxbPq6/Q3O7fLwMDvVwyvTv597rHD1MrM6pj8k1lNbpsMYd88LozV9FYjCZDUlsggHxUS5JWz/1zi4W72sbWR97cy/Uuj3AV5vXedKQjhiREof/rDioyf5qenlwPF66qYOii70W/7Nq3N61CeZvPu7S53S1uvHfWEd52rdIoupsfd7bs7IgKtTyqp+4iHomQyfV/flMH80qQltj7ZiqT6uxVcqgZmASHx3iULvM2f3GQNzVPdbi/TXrTM28rxtaRtTDh/d1s/gYJWd03qM98dT1rXFfD+fnYNF7npUl04cl6t0Ep2NwQuSB+l6pL2Ort0DOj2b7dG0WhqTm1udFvHbr1cKb1a9L7aKCjSn2nUUIIDTI8iqLFhH1cGN8JO5Maqp6Bcq3j/Z0tHm12FqxVDPJ3OAu0VjyfD90UBso1YjDkls2RPqAdlKm5ZeRu6645Nklq+pqsTl3pOWZ6NqsAX5/6jqsfUn5MlxnaRel/bd6c2ScM6GGwWDAxyO6Y5odqchrLi21Vp12+t2V+3/rNudNrtVS1UTce64x34uj58XZ1cM9denTmnNOiDxUxxh9VzL99o/r8PfBMxhm4aKiRrdmDbDu8FkNWmWZrF389ujbphGGJzdDfEztwPD2bk1xU+doBPh645Wft+nQOnU+e/AabDmWj+QW7p8VVW8yvcfZc0IkGUtftlz9LUyN4Cs1hPq0UV5HpFOTUDzSuyW8LeQoUdNr9+F9XTG6dwu7EsXVhYRVanl5GfDWbZ0xPNn8CiAlSeacwZ53eD1/H/RqFWHxfVSTRNdfswZcSWjXsJ7rSzbIhD0nRDpqEOSLcxdLcX0756SGd5VF6X2xav9p3JoQo8vzNw4JwMuD43HgVO3VIVVqfthPvbMLMg+ewW1dmzi7eXYbkqjP6+kqz6S2wYzF+/DGUPcYQnJUr1YN8UvWCevbtI7Ab/+4Ds0aevbqTPacEOlo4TN9MGNYIsaYqd/iTqJCA3BnUlP4Sbocd2z/1uhVo2DmXd1jMf3uRLsnVr4wsLKOi6UVPo56/96ueO+erk7Zt1JVPWGOZuC15JnUttj4SioesLMWjFpv31Y7eZ0r3ZUUiw/v62ozIO7UJBQhLqpzNKha2gGZOpXk/CQh0oC1SX+yiAwJwNCuTaS9qNcVz6e103w8vXtcOHZOSjNZ4VNlaFfzPR739KicX3Nta9fPj+jURP3E4xvjI/HNI8lYM955RUYb1nddUce+7ZQPOzqDl5cBN3eJQdMG6uuiOWtUt/qKPZmGvPiJSHXOu8MScF3rCJvl0mUl78wSz9b9SoXY8GrDQ0F+piPjDev54dex15nU0KmubWQwtkwcgK8eSjZ7vzPZUwDPYDDg2tYRaBxcuSy4Zu+TUgN0LAyo5v+paugvOjTAxpaOPbdMK8dk/bzhnBOqc27r2hS3dXW/SqJkqmNMqNkMo3ppUM8PWyYMQIBf7Yu8t5cB5RUCvVpHWC2gCFiegNu0gfxzDHq0CMeaA2dUP06Wb+S2mjHvsRR8tGw/xvZvrc0TWrjyh7twsqu7poNgcEJWSbxAxOO46lT84/rW+GDJfgD6Li187ZaOaBTsL9WEVUtJ0v58pg9+3nwco3urr/Xy9cPJWLwrFw9dF6f4MWFWkrU5U134PGhg5bVr3bg+pt+d6LrGVOOseT3uisGJxDpEh6B9VDAaBbtuTFZ2U+9KwPBP1uKlm5ybrdOTPZDS3Bic6Ck0yBcv3dRB1WMCNVwCqyYua924Pp5Pa2fX81zXJgLXmak2bu3p20YGY/yg9rWysNZFWn/+jUiJw+bsfKR20G6oyVeDbLUDOuoz9CVrwMngRGLeXgYseKq3NF2iMri2dQT2vjmIE0jJrJiwQDyT2gb1/Hzw1oJdejfHqR7r6z4rvJRmAa7+UffD4yl4d9FesxOOHRHg641Z9ydptr/buzZBRzPJ7FwtMsQfQx3sZWQSNlLMy8ES2HURAxOy5pnUthjdR/3wCmnvlyevxVPXt8YTdiyVvyYuHP8d3RNtI4Od0DLtTB+WaPEz2lWdEo/3bYXMf96gavnxPdfEIjjAB/e6oICiPdhzQkR1UuNgf+SdL9a7GR4tITYMCbFhejdDSvf2iMXfB8+irKICR89ewpBEx3o9vFTOWZlyRxe8dVtnHLSSuFBPDE6ISBoBPtrNGfn6kWRMXrALz97YVrN9kvsJqjYPKcKFOVVsmXx7FwghcKm0HIdPX0SHaOf0EFmbU6I05b8eGJyQU8VHB2PL0Xy9m0FuomfLhhjcJRptGzv+Qd02MhifjeqhQavqBlcMMdyV1BQ/bDyG/jonO6vOy8uAXZMGokII6YaEDQYDgvx8zBZgdJUWEfXQtEGgdDWmGJyQVXEN6zn0+PE3dUBwgK9uNVfckayz513By8uAmfd107sZZKc3hnbCjfGRuLZ17dVHegr007aQ4Q3tGyNjdx5ul2iZu718vL2wfFx/qVLXAwxOyIZmDYPw30eSEV7fvqRBIQHql4OSeX4aLFdUwr/a0Ips3zRJXzFh1pcuB/h6Y0DHKKvb1AX392yOV2+OR7Nw/RLnPdanJeasOKjJvmQc3mFwQjb1kuxbUF1XM6Pju8MSMH3RXrw7LNElzx8a6Is3hnaClwGo78+PCLrqjm5NceBUEVJaur42kGziImz3KjszE+z4mzoYgxNrWWDdtSeWnzxEktMjHb+rqsQ6Ii5C/nTvjpAxg4CPtxd7QmE7Jfzno67BnOUHMeUOfasguzMGJx7G3vLwRLL4v7HX4kT+JXSMsV7DxlEG6UbhyV30a9cY/do11nSfN8ZHIutoPiJD5Flx5EwMTjzM27d1xsi56zD2eo0KWxG5WJemYejSNEzvZpATXNMiHMv2nJJu5YgMHu3TEnEN66FHi3C9m+ISDE48TOvG9bH6n9fr3Qwiolr+fVcCPl9zGHcmsap4Tb7eXhjcJVrvZrgM+/iJiEgKDev747kB7dDcwRQGWmOhUddjcEJERGTFo31aYcuEAXo3wzI3XZFjDYMTIsm469I/orosNOjqPBj+jzqfXcHJzJkzERcXh4CAACQnJ2PdunUWt92xYwfuuOMOxMXFwWAwYMaMGfa2lYiIiDyA6uBk3rx5SE9Px8SJE7Fp0yYkJCQgLS0NeXl5Zre/ePEiWrZsiSlTpiAqqu5nDiQikhK/7pMbUR2cTJ8+HaNHj8aoUaMQHx+P2bNnIygoCHPnzjW7/TXXXIOpU6finnvugb+/Z6zPJiJyVOcmzs3jQiQzVcFJSUkJNm7ciNTU1Ks78PJCamoqMjMzNW8cEZFe9KoUu+6lG7Dwmd7SrVgh95LWMRJhQb4Y0DFS76bYRVWek9OnT6O8vByRkaYHGxkZid27d2vWqOLiYhQXFxv/Liws1GzfRETWLHu+H3ILL6NtZLAuz984JACNQ6wX2COyZfb9SSivEG6bFVzKVk+ePBmhoaHGn9jYWL2bREQeIi6iHpJZ2I6saFhfrikKvmYCEIPB4LaBCaAyOImIiIC3tzdyc3NNbs/NzdV0suv48eNRUFBg/Dl69Khm+yYiIrLH7Pu7Yfyg9kiMDdO7KQCA8YPao2NMCEb3bql3UzSnaljHz88PSUlJyMjIwNChQwEAFRUVyMjIwNixYzVrlL+/PyfPEhGRVAZ2kit9/GN9W+Gxvq30boZTqO7zSU9Px8cff4wvvvgCu3btwpgxY1BUVIRRo0YBAEaMGIHx48cbty8pKUFWVhaysrJQUlKC48ePIysrC/v379fuKIjqkLSOlb2QTcICdW4JEZE+VBf+GzZsGE6dOoUJEyYgJycHiYmJWLhwoXGSbHZ2Nry8rsY8J06cQNeuXY1/T5s2DdOmTUPfvn2xbNkyx4+AqI6JjwnByhf6o1Ewew+JyDMZhJA/M09hYSFCQ0NRUFCAkBB9lvcREbmzIR+uwpZjBQCAw1MG69wa8hT2Xr/ddyovERER1UkMToiIPID0XeRE1TA4ISIiIqkwOCEiIiKpMDghIiIiqTA4ISIiIqkwOCEiIiKpMDghIiIiqTA4ISIiIqkwOCEiIiKpMDghIiIiqTA4ISIiIqkwOCEi8gDXto4AAAT48mOf5OejdwOIiMj5nr6hDZqEBaJv20Z6N4XIJgYnREQeIMDXG/f3bK53M4gUYf8eERERSYXBCREREUmFwQkRERFJhcEJERERSYXBCREREUmFwQkRERFJhcEJERERSYXBCREREUmFwQkRERFJhcEJERERSYXBCREREUmFwQkRERFJhcEJERERScUtqhILIQAAhYWFOreEiIiIlKq6blddx5Vyi+Dk/PnzAIDY2FidW0JERERqnT9/HqGhoYq3Nwi14YwOKioqcOLECQQHB8NgMGi238LCQsTGxuLo0aMICQnRbL8yqevHyONzf3X9GHl87q+uH6Mzj08IgfPnzyMmJgZeXspnkrhFz4mXlxeaNm3qtP2HhITUyTdcdXX9GHl87q+uHyOPz/3V9WN01vGp6TGpwgmxREREJBUGJ0RERCQVjw5O/P39MXHiRPj7++vdFKep68fI43N/df0YeXzur64fo4zH5xYTYomIiMhzeHTPCREREcmHwQkRERFJhcEJERERSYXBCREREUnFo4OTmTNnIi4uDgEBAUhOTsa6dev0bhImT56Ma665BsHBwWjcuDGGDh2KPXv2mGzTr18/GAwGk5/HH3/cZJvs7GwMHjwYQUFBaNy4McaNG4eysjKTbZYtW4Zu3brB398frVu3xueff16rPVq/Rq+99lqttrdv3954/+XLl/Hkk0+iYcOGqF+/Pu644w7k5ua6xbFViYuLq3WMBoMBTz75JAD3O38rVqzALbfcgpiYGBgMBvz8888m9wshMGHCBERHRyMwMBCpqanYt2+fyTZnz57F8OHDERISgrCwMDz88MO4cOGCyTZbt25F7969ERAQgNjYWLzzzju12vLDDz+gffv2CAgIQOfOnbFgwQLVbVFzfKWlpXjxxRfRuXNn1KtXDzExMRgxYgROnDhhsg9z53zKlClSHJ+tYwSABx98sFb7Bw4caLKNu55DAGb/Hw0GA6ZOnWrcRuZzqOS6INNnp5K22CQ81HfffSf8/PzE3LlzxY4dO8To0aNFWFiYyM3N1bVdaWlp4rPPPhPbt28XWVlZ4qabbhLNmjUTFy5cMG7Tt29fMXr0aHHy5EnjT0FBgfH+srIy0alTJ5Gamio2b94sFixYICIiIsT48eON2xw8eFAEBQWJ9PR0sXPnTvHBBx8Ib29vsXDhQuM2zniNJk6cKDp27GjS9lOnThnvf/zxx0VsbKzIyMgQGzZsED179hS9evVyi2OrkpeXZ3J8ixYtEgDE0qVLhRDud/4WLFggXn75ZTF//nwBQPz0008m90+ZMkWEhoaKn3/+WWzZskXceuutokWLFuLSpUvGbQYOHCgSEhLE33//LVauXClat24t7r33XuP9BQUFIjIyUgwfPlxs375dfPvttyIwMFDMmTPHuM3q1auFt7e3eOedd8TOnTvFK6+8Inx9fcW2bdtUtUXN8eXn54vU1FQxb948sXv3bpGZmSl69OghkpKSTPbRvHlzMWnSJJNzWv1/Vs/js3WMQggxcuRIMXDgQJP2nz171mQbdz2HQgiT4zp58qSYO3euMBgM4sCBA8ZtZD6HSq4LMn122mqLEh4bnPTo0UM8+eSTxr/Ly8tFTEyMmDx5so6tqi0vL08AEMuXLzfe1rdvX/H0009bfMyCBQuEl5eXyMnJMd42a9YsERISIoqLi4UQQrzwwguiY8eOJo8bNmyYSEtLM/7tjNdo4sSJIiEhwex9+fn5wtfXV/zwww/G23bt2iUAiMzMTOmPzZKnn35atGrVSlRUVAgh3Pv81fzgr6ioEFFRUWLq1KnG2/Lz84W/v7/49ttvhRBC7Ny5UwAQ69evN27zxx9/CIPBII4fPy6EEOKjjz4SDRo0MB6fEEK8+OKLol27dsa/7777bjF48GCT9iQnJ4vHHntMcVvUHp8569atEwDEkSNHjLc1b95cvPvuuxYfI8vxCWH+GEeOHCmGDBli8TF17RwOGTJEXH/99Sa3udM5rHldkOmzU0lblPDIYZ2SkhJs3LgRqampxtu8vLyQmpqKzMxMHVtWW0FBAQAgPDzc5PZvvvkGERER6NSpE8aPH4+LFy8a78vMzETnzp0RGRlpvC0tLQ2FhYXYsWOHcZvqx1+1TdXxO/M12rdvH2JiYtCyZUsMHz4c2dnZAICNGzeitLTU5Dnbt2+PZs2aGZ9T9mOrqaSkBF9//TUeeughk6KV7nz+qjt06BBycnJMnic0NBTJyckm5ywsLAzdu3c3bpOamgovLy+sXbvWuE2fPn3g5+dncjx79uzBuXPnFB2zkrZooaCgAAaDAWFhYSa3T5kyBQ0bNkTXrl0xdepUk+5ydzi+ZcuWoXHjxmjXrh3GjBmDM2fOmLS/rpzD3Nxc/P7773j44Ydr3ecu57DmdUGmz04lbVHCLQr/ae306dMoLy83OUkAEBkZid27d+vUqtoqKirwzDPP4Nprr0WnTp2Mt993331o3rw5YmJisHXrVrz44ovYs2cP5s+fDwDIyckxe2xV91nbprCwEJcuXcK5c+ec8holJyfj888/R7t27XDy5Em8/vrr6N27N7Zv346cnBz4+fnV+tCPjIy02W4Zjs2cn3/+Gfn5+XjwwQeNt7nz+aupqj3mnqd6Wxs3bmxyv4+PD8LDw022adGiRa19VN3XoEEDi8dcfR+22uKoy5cv48UXX8S9995rUiDtqaeeQrdu3RAeHo41a9Zg/PjxOHnyJKZPn+4Wxzdw4EDcfvvtaNGiBQ4cOICXXnoJgwYNQmZmJry9vevUOfziiy8QHByM22+/3eR2dzmH5q4LMn12KmmLEh4ZnLiLJ598Etu3b8eqVatMbn/00UeNv3fu3BnR0dG44YYbcODAAbRq1crVzVRl0KBBxt+7dOmC5ORkNG/eHN9//z0CAwN1bJlzfPrppxg0aBBiYmKMt7nz+fNkpaWluPvuuyGEwKxZs0zuS09PN/7epUsX+Pn54bHHHsPkyZOlSgluyT333GP8vXPnzujSpQtatWqFZcuW4YYbbtCxZdqbO3cuhg8fjoCAAJPb3eUcWrou1DUeOawTEREBb2/vWrOHc3NzERUVpVOrTI0dOxa//fYbli5diqZNm1rdNjk5GQCwf/9+AEBUVJTZY6u6z9o2ISEhCAwMdNlrFBYWhrZt22L//v2IiopCSUkJ8vPzLT6nOx3bkSNHsHjxYjzyyCNWt3Pn81e1L2vPExUVhby8PJP7y8rKcPbsWU3Oa/X7bbXFXlWByZEjR7Bo0SKbZeWTk5NRVlaGw4cPW2179XbreXw1tWzZEhERESbvSXc/hwCwcuVK7Nmzx+b/JCDnObR0XZDps1NJW5TwyODEz88PSUlJyMjIMN5WUVGBjIwMpKSk6NiyymVmY8eOxU8//YQlS5bU6kY0JysrCwAQHR0NAEhJScG2bdtMPkyqPlDj4+ON21Q//qptqo7fVa/RhQsXcODAAURHRyMpKQm+vr4mz7lnzx5kZ2cbn9Odju2zzz5D48aNMXjwYKvbufP5a9GiBaKiokyep7CwEGvXrjU5Z/n5+di4caNxmyVLlqCiosIYmKWkpGDFihUoLS01OZ527dqhQYMGio5ZSVvsURWY7Nu3D4sXL0bDhg1tPiYrKwteXl7GoRCZj8+cY8eO4cyZMybvSXc+h1U+/fRTJCUlISEhwea2Mp1DW9cFmT47lbRFEcVTZ+uY7777Tvj7+4vPP/9c7Ny5Uzz66KMiLCzMZCazHsaMGSNCQ0PFsmXLTJa0Xbx4UQghxP79+8WkSZPEhg0bxKFDh8Qvv/wiWrZsKfr06WPcR9WSsQEDBoisrCyxcOFC0ahRI7NLxsaNGyd27dolZs6caXbJmNav0XPPPSeWLVsmDh06JFavXi1SU1NFRESEyMvLE0JULkFr1qyZWLJkidiwYYNISUkRKSkpbnFs1ZWXl4tmzZqJF1980eR2dzx/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 |
+
[](https://muzzammilshah.github.io/Road-to-GPT/Makemore-part3/)
|
4 |
+

|
5 |
+
[](https://github.com/MuzzammilShah/NeuralNetworks-LanguageModels-3/commits/main)
|
6 |
+

|
7 |
+
|
8 |
+
|
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 |
+
|
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 |
+
|
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 |
+
|
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 |
+
|
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
|
|