YouTube2Text

Building makemore Part 2: MLP — Transcript

by Andrej Karpathy · 12,731 words · 2,147 segments · language en · Watch on YouTube

Full transcript

  1. 0:00Hi everyone.
  2. 0:02Today we are continuing our
  3. 0:03implementation of makemore.
  4. 0:05Now in the last lecture we implemented
  5. 0:06the bigram language model and we
  6. 0:08implemented both using counts and also
  7. 0:11using a super simple neural network that
  8. 0:13had a single linear layer.
  9. 0:15Now this is the
  10. 0:17Jupyter notebook that we built out last
  11. 0:19lecture.
  12. 0:20And we saw that the way we approached
  13. 0:21this is that we looked at only the
  14. 0:23single previous character and we
  15. 0:25predicted the distribution for the
  16. 0:26character that would go next in the
  17. 0:28sequence. And we did that by taking
  18. 0:30counts and normalizing them into
  19. 0:32probabilities
  20. 0:33so that each row here sums to one.
  21. 0:36Now this is all well and good if you
  22. 0:38only have one character of previous
  23. 0:40context.
  24. 0:41And this works and it's approachable.
  25. 0:43The problem with this model of course is
  26. 0:45that the predictions from this model are
  27. 0:48not very good because you only take one
  28. 0:50character of context. So the model
  29. 0:52didn't produce very name-like sounding
  30. 0:54things.
  31. 0:56Now the problem with this approach
  32. 0:57though is that if we are to take more
  33. 1:00context into account when predicting the
  34. 1:01next character in the sequence, things
  35. 1:03quickly blow up and this table the size
  36. 1:06of this table grows and in fact it grows
  37. 1:08exponentially with the length of the
  38. 1:10context.
  39. 1:11Because if we only take a single
  40. 1:12character at a time, that's 27
  41. 1:13possibilities of context.
  42. 1:16But if we take two characters in the
  43. 1:17past and try to predict the third one,
  44. 1:19suddenly the number of rows in this
  45. 1:21matrix, you can look at it that way,
  46. 1:23is 27 * 27. So there's 729 possibilities
  47. 1:27for what could have come in the context.
  48. 1:30If we take three characters as the
  49. 1:31context, suddenly we have
  50. 1:3420,000 possibilities of context.
  51. 1:37And so that's just way too many rows of
  52. 1:40this matrix. It's way too few counts
  53. 1:43for each possibility and the whole thing
  54. 1:45just kind of explodes and doesn't work
  55. 1:47very well.
  56. 1:49So that's why today we're going to move
  57. 1:50on to this bullet point here and we're
  58. 1:52going to implement a multi-layer
  59. 1:53perceptron model to predict the next uh
  60. 1:57in a sequence. And this modeling
  61. 1:59approach that we're going to adopt
  62. 2:00follows this paper Bengio et al. 2003.
  63. 2:04So, I have the paper pulled up here.
  64. 2:06Now, this isn't the very first paper
  65. 2:07that proposed the use of uh multi-layer
  66. 2:10perceptrons or neural networks to
  67. 2:11predict the next character or token in a
  68. 2:13sequence, but it's definitely one that
  69. 2:15is uh was very influential around that
  70. 2:17time. It is very often cited to stand in
  71. 2:19for this idea, and I think it's a very
  72. 2:21nice write-up. And so, this is the paper
  73. 2:23that we're going to first look at and
  74. 2:25then implement. Now, this paper has 19
  75. 2:27pages. So, we don't have time to go into
  76. 2:30the full detail of this paper, but I
  77. 2:31invite you to read it. Uh it's very
  78. 2:33readable, interesting, and has a lot of
  79. 2:34interesting ideas in it as well.
  80. 2:37In the introduction, they describe the
  81. 2:38exact same problem I just described. And
  82. 2:40then to address it, they propose the
  83. 2:42following model.
  84. 2:43Now, keep in mind that we are building a
  85. 2:46character-level language model. So,
  86. 2:48we're working on the level of
  87. 2:49characters. In this paper, they have a
  88. 2:51vocabulary of 17,000 possible words, and
  89. 2:54they instead built a word-level language
  90. 2:56model. But, we're going to still stick
  91. 2:57with the characters, but we'll take the
  92. 2:59same modeling approach.
  93. 3:01Now, what they do is basically they
  94. 3:03propose to take every one of these
  95. 3:04words, 17,000 words, and they're going
  96. 3:07to associate to each word a say
  97. 3:1030-dimensional feature vector.
  98. 3:12So, every word is now embedded into a
  99. 3:1630-dimensional space. You can think of
  100. 3:18it that way. So, we have 17,000 points
  101. 3:21or vectors in a 30-dimensional space,
  102. 3:23and that's um you might imagine that's
  103. 3:25very crowded. That's a lot of points for
  104. 3:27a very small space.
  105. 3:28Now,
  106. 3:30in the beginning, these words are
  107. 3:31initialized completely randomly. So,
  108. 3:32they're spread out at random.
  109. 3:34But, then we're going to tune these
  110. 3:36embeddings of these words using back
  111. 3:38propagation. So, during the course of
  112. 3:40training of this neural network, these
  113. 3:42points or vectors are going to basically
  114. 3:43move around in this space. And you might
  115. 3:46imagine that, for example, words that
  116. 3:48have very similar meanings or that are
  117. 3:50indeed synonyms of each other might end
  118. 3:52up in a very similar part of the space.
  119. 3:54And conversely, words that mean very
  120. 3:55different things would go somewhere else
  121. 3:57in the space.
  122. 3:59Now, their modeling approach otherwise
  123. 4:01is identical to ours. They are using a
  124. 4:03multi-layer neural network to predict
  125. 4:04the next word given the previous words.
  126. 4:07And to train the neural network, they
  127. 4:08are maximizing the log likelihood of the
  128. 4:10training data, just like we did.
  129. 4:12So, the modeling approach itself is
  130. 4:14identical. Now, here they have a
  131. 4:16concrete example of this intuition.
  132. 4:18Why does it work?
  133. 4:20Basically, suppose that for example, you
  134. 4:21are trying to predict "A dog was running
  135. 4:23in a blank."
  136. 4:25Now, suppose that the exact phrase "A
  137. 4:27dog was running in a" has never occurred
  138. 4:30in the training data.
  139. 4:31And here you are at uh sort of test time
  140. 4:33later when the model is deployed
  141. 4:35somewhere.
  142. 4:36And it's trying to make a sentence, and
  143. 4:38it's saying "A dog was running in a
  144. 4:40blank."
  145. 4:41And because it's never encountered this
  146. 4:43exact phrase in the training set, you're
  147. 4:45out of distribution, as we say. Like,
  148. 4:47you don't have fundamentally any
  149. 4:49reason to suspect um
  150. 4:52what might come next.
  151. 4:54But, this approach actually allows you
  152. 4:55to get around that. Because maybe you
  153. 4:57didn't see the exact phrase "A dog was
  154. 4:59running in a something." But, maybe
  155. 5:01you've seen similar phrases. Maybe
  156. 5:02you've seen the phrase "The dog was
  157. 5:04running in a blank."
  158. 5:06And maybe your network has learned that
  159. 5:07A and the are like frequently are
  160. 5:10interchangeable with each other. And so,
  161. 5:12maybe it took the embedding for A and
  162. 5:14the embedding for the, and it actually
  163. 5:16put them like nearby each other in the
  164. 5:17space. And so, you can transfer
  165. 5:19knowledge through that embedding, and
  166. 5:21you can generalize in that way.
  167. 5:23Similarly, the network could know that
  168. 5:25cats and dogs are animals, and they
  169. 5:26co-occur in lots of very similar
  170. 5:28contexts. And so, even though you
  171. 5:30haven't seen this exact phrase,
  172. 5:32or if you haven't seen exactly walking
  173. 5:34or running, you can through the
  174. 5:36embedding space transfer knowledge, and
  175. 5:38you can generalize to novel scenarios.
  176. 5:42So, let's now scroll down to the diagram
  177. 5:43of the neural network. Uh they have a
  178. 5:45nice uh diagram here.
  179. 5:47And in this example, we are taking three
  180. 5:49previous words
  181. 5:51and we are trying to predict the fourth
  182. 5:53word
  183. 5:54in the sequence.
  184. 5:56Now, these three previous words, as I
  185. 5:57mentioned, we have a vocabulary of
  186. 5:5917,000
  187. 6:01possible words.
  188. 6:03So, every one of these
  189. 6:04basically are the index of the incoming
  190. 6:08word.
  191. 6:09And because there are 17,000 words, this
  192. 6:11is an integer between 0 and 16,999.
  193. 6:17Now, there's also a lookup table that
  194. 6:19they call C.
  195. 6:21This lookup table is a matrix that is
  196. 6:2217,000 by, say, 30.
  197. 6:26And basically what we're doing here is
  198. 6:27we're treating this as a lookup table.
  199. 6:29And so, every index is plucking out a
  200. 6:32row of this embedding matrix
  201. 6:35so that each index is converted to the
  202. 6:3730-dimensional vector that corresponds
  203. 6:39to the embedding vector for that word.
  204. 6:42So, here we have the input layer of 30
  205. 6:45neurons for three words, making up 90
  206. 6:48neurons in total.
  207. 6:50And here they're saying that this matrix
  208. 6:52C is shared across all the words. So,
  209. 6:54we're always indexing into the same
  210. 6:56matrix C over and over.
  211. 6:59for each one of these words.
  212. 7:02Next up is the hidden layer of this
  213. 7:03neural network.
  214. 7:04The size of this hidden neural layer of
  215. 7:06this neural net is a hyper parameter.
  216. 7:09So, we use the word hyper parameter when
  217. 7:10it's kind of like a design choice up to
  218. 7:12the designer of the neural net. And this
  219. 7:13can be as large as you'd like or as
  220. 7:15small as you'd like. So, for example,
  221. 7:17the size could be 100.
  222. 7:19And we are going to go over multiple
  223. 7:20choices of the size of this hidden
  224. 7:22layer, and we're going to evaluate how
  225. 7:24well they work.
  226. 7:26So, say there were 100 neurons here,
  227. 7:28all of them would be fully connected to
  228. 7:29the 90 words or 90 um
  229. 7:33numbers that make up these three words.
  230. 7:35So, this is a fully connected layer.
  231. 7:38Then there's a tanh non-linearity.
  232. 7:40And then there's this output layer. And
  233. 7:42because there are 17,000 possible words
  234. 7:44that could come next, this layer has
  235. 7:4617,000 neurons, and all of them are
  236. 7:49fully connected to all of these neurons
  237. 7:52in the hidden layer.
  238. 7:54So there's a lot of parameters here
  239. 7:56because there's a lot of words. So most
  240. 7:58computation is here. This is the
  241. 7:59expensive layer.
  242. 8:01Now there are 17,000 logits here. So on
  243. 8:04top of there, we have the softmax layer,
  244. 8:06which we've seen in our previous video
  245. 8:08as well. So every one of these logits is
  246. 8:10exponentiated, and then everything is
  247. 8:12normalized to sum to one so that we have
  248. 8:15a nice probability distribution for the
  249. 8:17next word in the sequence.
  250. 8:19Now of course during training, we
  251. 8:21actually have the label. We have the
  252. 8:23identity of the next word in the
  253. 8:24sequence.
  254. 8:25That word or its index is used to pluck
  255. 8:29out the probability of that word,
  256. 8:32and then we are maximizing the
  257. 8:34probability of that word with respect to
  258. 8:37the parameters of this neural net.
  259. 8:39So the parameters are the weights and
  260. 8:41biases of this output layer, the weights
  261. 8:44and biases of this hidden layer, and the
  262. 8:47embedding lookup table C. And all of
  263. 8:49that is optimized using backpropagation.
  264. 8:52And these dashed arrows, ignore those.
  265. 8:55That represents a variation of a neural
  266. 8:57net that we are not going to explore in
  267. 8:58this video.
  268. 8:59So that's the setup, and now let's
  269. 9:01implement it.
  270. 9:02Okay, so I started a brand new notebook
  271. 9:04for this lecture.
  272. 9:05We are importing PyTorch, and we are
  273. 9:07importing Matplotlib so we can create
  274. 9:09figures.
  275. 9:10Then I am reading all the names into a
  276. 9:13list of words like I did before, and I'm
  277. 9:15showing the first eight right here.
  278. 9:18Keep in mind that we have a 32,000 in
  279. 9:20total. These are just the first eight.
  280. 9:22And then here I'm building out the
  281. 9:24vocabulary of characters and all the
  282. 9:25mappings from the characters as strings
  283. 9:28to integers and vice versa.
  284. 9:31Now the first thing we want to do is we
  285. 9:32want to compile the data set for the
  286. 9:34neural network. And I had to rewrite
  287. 9:36this code.
  288. 9:37I'll show you in a second what it looks
  289. 9:39like.
  290. 9:41So this is the code that I created for
  291. 9:43the data set creation. So let me first
  292. 9:44run it and then I'll briefly explain how
  293. 9:46this works.
  294. 9:48So first we're going to define something
  295. 9:50called block size. And this is basically
  296. 9:52the context length of how many
  297. 9:54characters do we take to predict the
  298. 9:56next one. So here in this example we're
  299. 9:58taking three characters to predict the
  300. 9:59fourth one. So we have a block size of
  301. 10:02three. That's the size of the block that
  302. 10:04supports the prediction.
  303. 10:06Then here I'm building out the X and Y.
  304. 10:10The X are the input to the neural net
  305. 10:12and the Y are the labels for each
  306. 10:15example inside X.
  307. 10:17Then I'm iterating over the first five
  308. 10:19words. I'm doing first five just for
  309. 10:21efficiency while we are developing all
  310. 10:23the code. But then later we're going to
  311. 10:24come here and erase this so that we use
  312. 10:26the entire training set.
  313. 10:29So here I'm printing the word Emma.
  314. 10:32And here I'm basically showing the
  315. 10:33examples that we can generate. The five
  316. 10:36examples that we can generate out of the
  317. 10:37single
  318. 10:38serve word Emma.
  319. 10:41So
  320. 10:42when we are given the context of just
  321. 10:44dot dot dot, the first character in a
  322. 10:45sequence is E.
  323. 10:47In this context, the label is M.
  324. 10:50When the context is this, the label is
  325. 10:52M.
  326. 10:53And so forth.
  327. 10:54And so the way I build this out is first
  328. 10:56I start with a padded context of just
  329. 10:57zero tokens.
  330. 10:59Then I iterate over all the characters.
  331. 11:01I get the character in the sequence and
  332. 11:04I basically build out the array Y of
  333. 11:07this current character and the array X
  334. 11:09which stores the current running
  335. 11:10context.
  336. 11:11And then here see I print everything and
  337. 11:14here I
  338. 11:15crop the context and enter the new
  339. 11:17character in the sequence. So this is
  340. 11:19kind of like a rolling window of
  341. 11:20context.
  342. 11:22Now we can change the block size here to
  343. 11:24for example four.
  344. 11:25And in that case we would be predicting
  345. 11:27the fifth character given the previous
  346. 11:29four.
  347. 11:30Or it can be five and then it would look
  348. 11:32like this.
  349. 11:34Or it can be say 10.
  350. 11:36And then it would look something like
  351. 11:37this. We're taking 10 characters to
  352. 11:39predict the 11th one.
  353. 11:41And we're always padding with dots.
  354. 11:43So let me bring this back to three, just
  355. 11:45so that uh we have what we have here in
  356. 11:48the paper.
  357. 11:50And finally, the data set right now
  358. 11:51looks as follows.
  359. 11:53From these five words, we have created a
  360. 11:55data set of 32 examples.
  361. 11:57And each input to the neural net is
  362. 11:59three integers, and we have a label that
  363. 12:02is also an integer, uh Y. So X looks
  364. 12:05like this.
  365. 12:06These are the individual examples.
  366. 12:08And then Y are the labels.
  367. 12:12So
  368. 12:13given this,
  369. 12:15let's now write the neural network that
  370. 12:17takes these X's and predicts the Y's.
  371. 12:19First, let's build the embedding uh look
  372. 12:21up table C.
  373. 12:23So we have 27 possible characters, and
  374. 12:25we're going to embed them in a lower
  375. 12:26dimensional space.
  376. 12:28In the paper, they have 17,000 words,
  377. 12:31and they embed them in uh spaces as
  378. 12:33small dimensional as 30. So they cram
  379. 12:3617,000 um
  380. 12:38words into 30-dimensional space. In our
  381. 12:40case, we have only 27 possible
  382. 12:42characters, so let's cram them in
  383. 12:44something as small as to start with, for
  384. 12:46example, a two-dimensional space.
  385. 12:48So this look up table will be random
  386. 12:50numbers,
  387. 12:51and we'll have 27 rows, and we'll have
  388. 12:54two columns.
  389. 12:56Right? So each 20 each one of 27
  390. 12:58characters will have a two-dimensional
  391. 13:00embedding.
  392. 13:02So that's our matrix C of embeddings, in
  393. 13:05the beginning initialized randomly.
  394. 13:07Now, before we embed all of the integers
  395. 13:10inside the input X using this look up
  396. 13:12table C,
  397. 13:14let me actually just try to embed a
  398. 13:15single individual integer, like say
  399. 13:17five.
  400. 13:19Um so we get a sense of how this works.
  401. 13:21Now, one way this works, of course, is
  402. 13:24we can just take the C, and we can index
  403. 13:26into row five.
  404. 13:28And that gives us a vector, the fifth
  405. 13:30row of C.
  406. 13:31And um
  407. 13:33this is one way to do it.
  408. 13:34The other way that I presented in the
  409. 13:36previous lecture is actually seemingly
  410. 13:38different, but actually identical.
  411. 13:40So, in the previous lecture, what we did
  412. 13:42is we took these integers and we used
  413. 13:43the one-hot encoding to first encode
  414. 13:46them.
  415. 13:46So, F.one_hot
  416. 13:48we want to encode integer five
  417. 13:50and we want to tell it that the number
  418. 13:52of classes is 27. So, that's the
  419. 13:5426-dimensional vector of all zeros
  420. 13:56except the fifth bit is turned on.
  421. 14:00Now, this actually doesn't work.
  422. 14:03The reason is that
  423. 14:04this input actually must be a
  424. 14:05torch.tensor.
  425. 14:08And I'm making some of these errors
  426. 14:09intentionally just so you get to see
  427. 14:10some errors and how to fix them.
  428. 14:12Uh so, this must be a tensor not an int,
  429. 14:14fairly straightforward to fix.
  430. 14:16We get a one-hot vector. The fifth
  431. 14:18dimension is one and the shape of this
  432. 14:20is 27.
  433. 14:22And now notice that, just as I briefly
  434. 14:24alluded to in the previous video, if we
  435. 14:26take this one-hot vector and we multiply
  436. 14:29it by C
  437. 14:33then
  438. 14:35um
  439. 14:35what would you expect?
  440. 14:37Well, number one
  441. 14:39first you'd expect an error
  442. 14:41because um
  443. 14:43expected scalar type long but found
  444. 14:45float. So, a little bit confusing, but
  445. 14:48the problem here is that one-hot, the
  446. 14:50data type of it
  447. 14:52is long. It's a 64-bit integer. But,
  448. 14:56this is a float tensor and so PyTorch
  449. 14:58doesn't know how to multiply an int with
  450. 15:00a float and that's why we had to
  451. 15:02explicitly cast this to a float so that
  452. 15:04we can multiply.
  453. 15:06Now, the output actually here
  454. 15:09is identical.
  455. 15:11And that it's identical because of the
  456. 15:12way the matrix uh multiplication here
  457. 15:14works. We have the one-hot um vector
  458. 15:17multiplying columns of C and because of
  459. 15:20all the zeros, they actually end up
  460. 15:22masking out everything in C except for
  461. 15:24the fifth row, which is plucked out.
  462. 15:27And so we actually arrive at the same
  463. 15:28result.
  464. 15:30And that tells you that here we can
  465. 15:31interpret this first piece here, this
  466. 15:34embedding of the integer. We can either
  467. 15:36think of it as the integer indexing into
  468. 15:38a lookup table C.
  469. 15:40But equivalently, we can also think of
  470. 15:41this little piece here as a first layer
  471. 15:44of this bigger neural net.
  472. 15:46This layer here has neurons that have no
  473. 15:48non-linearity, they're no tan h, they're
  474. 15:50just linear neurons, and their weight
  475. 15:52matrix is C.
  476. 15:55And then we are encoding integers into
  477. 15:57one-hot and feeding those into a neural
  478. 15:59net. And this first layer basically
  479. 16:01embeds them.
  480. 16:02So those are two equivalent ways of
  481. 16:04doing the same thing. We're just going
  482. 16:06to index because it's much much faster,
  483. 16:08and we're going to discard this
  484. 16:09interpretation of one-hot inputs into
  485. 16:12neural nets. And we're just going to
  486. 16:14index integers and create and use
  487. 16:16embedding tables. Now, embedding a
  488. 16:18single integer like five is easy enough.
  489. 16:20We can simply ask PyTorch to retrieve
  490. 16:22the fifth row of C. Or the row index
  491. 16:25five of C.
  492. 16:27But how do we simultaneously embed all
  493. 16:30of these 32 by three integers stored in
  494. 16:32array X?
  495. 16:34Luckily, PyTorch indexing is fairly
  496. 16:36flexible and quite powerful. So it
  497. 16:39doesn't just work to um
  498. 16:41ask for a single element five like this.
  499. 16:44You can actually index using lists. So
  500. 16:46for example, we can get the rows five,
  501. 16:48six, and seven, and this will just work
  502. 16:51like this. We can index with a list.
  503. 16:53It doesn't just have to be a list, it
  504. 16:55can also be a actually a tensor of
  505. 16:57integers.
  506. 16:58And we can index with that.
  507. 17:00So this is a integer tensor 567, and
  508. 17:03this will just work as well.
  509. 17:06In fact, we can also, for example,
  510. 17:07repeat row seven and retrieve it
  511. 17:09multiple times. And uh
  512. 17:12that same index will just get embedded
  513. 17:14multiple times here.
  514. 17:16So here we are indexing with a
  515. 17:17one-dimensional
  516. 17:19tensor of integers. But, it turns out
  517. 17:21that you can also index with
  518. 17:22multi-dimensional tensors of integers.
  519. 17:25Here, we have a two-dimensional in
  520. 17:27tensor of integers. So, we can simply
  521. 17:29just do C at X.
  522. 17:32And this just works.
  523. 17:34And the shape of this
  524. 17:36is
  525. 17:3732 by 3, which is the original shape.
  526. 17:40And now, for every one of those 32 by 3
  527. 17:41integers, we've retrieved the embedding
  528. 17:43vector here.
  529. 17:46So, basically, we have that as an
  530. 17:48example.
  531. 17:49The 13th or example, index 13,
  532. 17:53um the second dimension is the integer
  533. 17:56one as an example.
  534. 17:58And so, here, if we do C of X, which
  535. 18:02gives us that array, and then we index
  536. 18:04into 13 by 2 of that array,
  537. 18:07then we we get the embedding
  538. 18:09here.
  539. 18:10And you can verify that C at one,
  540. 18:14which is the integer at that location,
  541. 18:16is indeed equal to this.
  542. 18:20You see they're equal.
  543. 18:21So, basically, long story short, PyTorch
  544. 18:23indexing is awesome. And to embed
  545. 18:26simultaneously
  546. 18:28all of the integers in X, we can simply
  547. 18:30do C of X. And that is our embedding.
  548. 18:33And that just works.
  549. 18:35Now, let's construct this layer here,
  550. 18:37the hidden layer.
  551. 18:38So, we have that W1, as I'll call it,
  552. 18:42are these weights, which we will
  553. 18:44initialize randomly.
  554. 18:46Now, the number of inputs to this layer
  555. 18:48is going to be 3 * 2, right? Because we
  556. 18:51have two-dimensional embeddings, and we
  557. 18:52have three of them.
  558. 18:53So, the number of inputs is six.
  559. 18:56And the number of neurons in this layer
  560. 18:58is a variable up to us. Let's use 100
  561. 19:01neurons as an example.
  562. 19:03And then, biases will be also
  563. 19:05initialized randomly as an example.
  564. 19:07And let's And we just need 100 of them.
  565. 19:11Now, the problem with this is we can't
  566. 19:13simply Normally, we would take the
  567. 19:15input, in this case that's embedding,
  568. 19:17and we'd like to multiply it with these
  569. 19:19weights.
  570. 19:20And then we would like to add the bias.
  571. 19:22This is roughly what we want to do.
  572. 19:24But the problem here is that these
  573. 19:25embeddings are stacked up in the
  574. 19:27dimensions of this input tensor.
  575. 19:29So, this will not work, this matrix
  576. 19:31multiplication, because this is a shape
  577. 19:3232 by 3 by 2, and I can't multiply that
  578. 19:35by 6 by 100.
  579. 19:37So, somehow we need to concatenate these
  580. 19:40inputs here together so that we can do
  581. 19:41something along these lines, which
  582. 19:43currently does not work.
  583. 19:45So, how do we transform this 32 by 3 by
  584. 19:472 into a 32 by 6 so that we can actually
  585. 19:50perform this uh multiplication over
  586. 19:53here? I'd like to show you that there
  587. 19:55are usually many ways of uh implementing
  588. 19:58what you'd like to do in torch.
  589. 20:00And some of them will be faster, better,
  590. 20:02shorter, etc.
  591. 20:03And that's because torch is a very large
  592. 20:06library, and it's got lots and lots of
  593. 20:07functions. So, if we just go to the
  594. 20:09documentation and click on torch, you'll
  595. 20:11see that my slider here is very tiny,
  596. 20:14and that's because there are so many
  597. 20:15functions that you can call on these
  598. 20:16tensors
  599. 20:17to transform them, create them, multiply
  600. 20:20them, add them, perform all kinds of
  601. 20:22different operations on them.
  602. 20:24And so, this is kind of like
  603. 20:28the space of possibility, if you will.
  604. 20:31Now, one of the things that you can do
  605. 20:32is if we can control here, control F for
  606. 20:34concatenate. And we see that there's a
  607. 20:36function, torch.cat, short for
  608. 20:38concatenate.
  609. 20:40And this concatenates a given sequence
  610. 20:42of tensors in a given dimension.
  611. 20:45And uh these tensors must have the same
  612. 20:46shape, etc. So, we can use the
  613. 20:48concatenate operation to, in a naive
  614. 20:51way, concatenate these three embeddings
  615. 20:53for each input.
  616. 20:56So, in this case, we have emb of
  617. 20:58emb of the shape. And really what we
  618. 21:00want to do is we want to retrieve these
  619. 21:02three parts and concatenate them.
  620. 21:05So, we want to grab all the examples.
  621. 21:08We want to grab
  622. 21:10first the zeroth um
  623. 21:13index and then all of
  624. 21:16this.
  625. 21:17So, this plucks out
  626. 21:20the 32 by 2 embeddings of just the first
  627. 21:24word here.
  628. 21:26And so, basically we want this guy.
  629. 21:28We want the first dimension. And we want
  630. 21:31the second dimension.
  631. 21:32And these are the three pieces
  632. 21:34individually.
  633. 21:36And then we want to treat this as a
  634. 21:38sequence and we want to torch.cat
  635. 21:40on that sequence. So, this is the list.
  636. 21:43torch.cat takes a sequence of tensors.
  637. 21:47And then we have to tell it along which
  638. 21:49dimension to concatenate.
  639. 21:51So, in this case all these are 32 by 2
  640. 21:53and we want to concatenate not across
  641. 21:55dimension zero, but across dimension
  642. 21:57one.
  643. 21:58So, passing in one
  644. 22:00gives us a result.
  645. 22:01The shape of this is 32 by 6 exactly as
  646. 22:04we'd like.
  647. 22:05So, that basically took 32 and squashed
  648. 22:08these by concatenating them into 32 by
  649. 22:106.
  650. 22:11Now, this is kind of ugly because this
  651. 22:13code would not generalize if we want to
  652. 22:15later change the block size. Right now,
  653. 22:17we have three inputs.
  654. 22:19Three words. But what if we had five?
  655. 22:22Then here we would have to change the
  656. 22:23code because I'm indexing directly.
  657. 22:25Well, torch comes to rescue again
  658. 22:27because there turns out to be a function
  659. 22:29called unbind.
  660. 22:31And it removes a tensor dimension.
  661. 22:35So, removes a tensor dimension, returns
  662. 22:37a tuple of all slices along a given
  663. 22:39dimension without it.
  664. 22:41So, this is exactly what we need.
  665. 22:43And basically when we call torch.unbind
  666. 22:48torch.unbind
  667. 22:50of emb and passing dimension um one,
  668. 22:55index one.
  669. 22:56This gives us a list of
  670. 22:58um a list of tensors exactly equivalent
  671. 23:01to this.
  672. 23:02So, running this
  673. 23:04gives us a length
  674. 23:06three
  675. 23:07and it's exactly this list. And so, we
  676. 23:09can call torch.cat on it.
  677. 23:12And along the first dimension.
  678. 23:15And this works.
  679. 23:16And the shape is the same.
  680. 23:19But now this is uh it doesn't matter if
  681. 23:21we have block size three or five or 10,
  682. 23:23this will just work.
  683. 23:24So, this is one way to do it. But it
  684. 23:26turns out that in this case, there's
  685. 23:28actually a significantly better and more
  686. 23:30efficient way. And this gives me an
  687. 23:32opportunity to hint at some of the
  688. 23:34internals of torch.tensor.
  689. 23:36So, let's create
  690. 23:38an array here
  691. 23:40of elements from zero to 17. And the
  692. 23:42shape of this
  693. 23:44is just 18. It's a single vector of 18
  694. 23:46numbers.
  695. 23:48It turns out that we can very quickly
  696. 23:49re-represent this as different sized and
  697. 23:53dimensional tensors.
  698. 23:54We do this by calling a view.
  699. 23:57And we can say that actually this is not
  700. 23:59a single vector of 18, this is a 2x9
  701. 24:02tensor.
  702. 24:04Or alternatively, this is a 9x2 tensor.
  703. 24:08Or this is actually a 3x3x2 tensor.
  704. 24:11As long as the total number of elements
  705. 24:13here multiply to be the same, uh this
  706. 24:16will just work.
  707. 24:18And in PyTorch, this operation calling
  708. 24:21dot view is extremely efficient.
  709. 24:24And the reason for that is that in each
  710. 24:26tensor, there's something called the
  711. 24:28underlying storage.
  712. 24:30And the storage is just the numbers
  713. 24:32always as a one-dimensional vector. And
  714. 24:34this is how this tensor is represented
  715. 24:37in the computer memory. It's always a
  716. 24:38one-dimensional vector.
  717. 24:41But when we call that view, we are
  718. 24:44manipulating some of attributes of that
  719. 24:46tensor that dictate how this
  720. 24:48one-dimensional sequence is interpreted
  721. 24:51to be an N-dimensional tensor.
  722. 24:53And so, what's happening here is that no
  723. 24:55memory is being changed, copied, moved,
  724. 24:57or created when we call dot view. The
  725. 24:59The storage
  726. 25:00is identical, but when you call dot
  727. 25:02view, some of the internal um
  728. 25:05attributes of the view of this tensor
  729. 25:07are being manipulated and changed. In
  730. 25:09particular, there's something There's
  731. 25:10something called a storage offset,
  732. 25:12strides, and shapes, and those are
  733. 25:14manipulated so that this one-dimensional
  734. 25:16sequence of bytes is seen as different
  735. 25:18n-dimensional arrays.
  736. 25:20There's a blog post here from Eric
  737. 25:22called PyTorch Internals, where he goes
  738. 25:25into some of this with respect to tensor
  739. 25:27and how the view of a tensor is
  740. 25:29represented.
  741. 25:30And this is really just like a logical
  742. 25:32construct of representing the physical
  743. 25:34memory.
  744. 25:35And uh so this is a pretty good um
  745. 25:38blog post that you can go into. I might
  746. 25:39also create an entire video on the
  747. 25:41internals of torch tensor and how this
  748. 25:42works.
  749. 25:44For here, we just note that this is an
  750. 25:46extremely efficient operation.
  751. 25:48And if I delete this and come back to
  752. 25:50our emb,
  753. 25:53we see that the shape of our emb is 32
  754. 25:55by 3 by 2, but we can simply ask for
  755. 25:58PyTorch to view this instead as a 32 by
  756. 26:016.
  757. 26:03And the way this gets flattened into a
  758. 26:0532 by 6 array
  759. 26:07just happens that
  760. 26:09these two
  761. 26:10get stacked up in a single row. And so
  762. 26:13that's basically the concatenation
  763. 26:15operation that we're after.
  764. 26:17And you can verify that this actually
  765. 26:18gives the exact same result as what we
  766. 26:20had before.
  767. 26:22So this is an element-wise equals, and
  768. 26:23you can see that all the elements of
  769. 26:25these two tensors are the same.
  770. 26:27And so we get the exact same result.
  771. 26:30So long story short, we can actually
  772. 26:32just come here,
  773. 26:34and if we just view this as a 32 by 6
  774. 26:38um instead, then this multiplication
  775. 26:40will work and give us the hidden states
  776. 26:43that we're after.
  777. 26:44So if this is H,
  778. 26:46then H.shape is now the 100-dimensional
  779. 26:49activations for every one of our 32
  780. 26:52examples.
  781. 26:53And this gives the desired result. Let
  782. 26:55me do two things here. Number one, let's
  783. 26:57not use 32. We can, for example, do
  784. 27:00something like
  785. 27:01um
  786. 27:02emb.shape
  787. 27:04at zero.
  788. 27:05So that we don't hardcode these numbers.
  789. 27:07And this would work for any size of this
  790. 27:09emb.
  791. 27:10Or alternatively, we can also do -1.
  792. 27:12When we do -1, PyTorch will infer what
  793. 27:15this should be.
  794. 27:16Because the number of elements must be
  795. 27:17the same, and we're saying that this is
  796. 27:19six, PyTorch will derive that this must
  797. 27:21be 32 or whatever else it is if emb is
  798. 27:24of different size.
  799. 27:26The other thing is here,
  800. 27:28um
  801. 27:29one more thing I'd like to point out is
  802. 27:33here when we do the concatenation,
  803. 27:35this actually is much less efficient
  804. 27:37because um this concatenation would
  805. 27:39create a whole new tensor with a whole
  806. 27:41new storage. So new memory is being
  807. 27:43created because there's no way to
  808. 27:44concatenate tensors just by manipulating
  809. 27:47the view attributes. So this is
  810. 27:49inefficient and creates all kinds of new
  811. 27:50memory.
  812. 27:52Uh so let me delete this now.
  813. 27:55We don't need this.
  814. 27:57And here to calculate H, we want to also
  815. 27:59dot tanh
  816. 28:01of this to get our
  817. 28:04oops, to get our H.
  818. 28:07So these are now numbers between -1 and
  819. 28:081 because of the tanh.
  820. 28:10And we have that the shape is 32 by 100.
  821. 28:14And that is basically this hidden layer
  822. 28:16of activations here
  823. 28:17for every one of our 32 examples.
  824. 28:20Now there's one more thing I glossed
  825. 28:21over that we have to be very careful
  826. 28:23with, and that's this and that's this
  827. 28:25plus here.
  828. 28:26In particular, we want to make sure that
  829. 28:27the broadcasting will do what we like.
  830. 28:30The shape of this is 32 by 100, and B1's
  831. 28:33shape is 100.
  832. 28:35So we see that the addition here will
  833. 28:37broadcast these two. And in particular,
  834. 28:39we have 32 by 100 broadcasting to 100.
  835. 28:44So broadcasting will align on the right,
  836. 28:47create a fake dimension here.
  837. 28:49So, this will become a 1 by 100 row
  838. 28:50vector.
  839. 28:52And then it will copy vertically
  840. 28:54for every one of these rows of 32 and do
  841. 28:57an element-wise addition.
  842. 28:58So, in this case, the correct thing will
  843. 29:00be happening because the same bias
  844. 29:02vector
  845. 29:03will be added to all the rows
  846. 29:05of
  847. 29:06this matrix.
  848. 29:08So, that is correct. That's what we'd
  849. 29:09like. And uh it's always good practice
  850. 29:11to just make sure uh so that you don't
  851. 29:13shoot yourself in the foot. And finally,
  852. 29:15let's create the final layer here.
  853. 29:17So, let's create
  854. 29:19W2 and B2.
  855. 29:22The input now is 100.
  856. 29:25And the output number of neurons will be
  857. 29:27for us 27 because we have 27 possible
  858. 29:29characters that come next.
  859. 29:31So, the biases will be 27 as well.
  860. 29:35So, therefore, the logits, which are the
  861. 29:36outputs of this neural net,
  862. 29:38are going to be um
  863. 29:41H multiplied by W2 plus B2.
  864. 29:47Logits.shape is 32 by 27.
  865. 29:50And the logits look
  866. 29:52good. Now, exactly as we saw in the
  867. 29:54previous video, we want to take these
  868. 29:55logits and we want to first exponentiate
  869. 29:58them to get our fake counts.
  870. 30:00And then we want to normalize them into
  871. 30:01a probability.
  872. 30:03So, prob is counts.divide.
  873. 30:05And now uh counts.sum
  874. 30:09along the first dimension and keep dims
  875. 30:11as true, exactly as in the previous
  876. 30:12video.
  877. 30:14And so,
  878. 30:16prob.shape now is 32 by 27.
  879. 30:20And you'll see that every row of prob
  880. 30:23sums to one, so it's normalized.
  881. 30:26So, that gives us the probabilities.
  882. 30:28Now, of course, we have the actual
  883. 30:29letter that comes next. And that comes
  884. 30:31from this array Y,
  885. 30:34which we which we created during the
  886. 30:36data set creation. So, Y is this last
  887. 30:39piece here, which is the identity of the
  888. 30:40next character in the sequence that we'd
  889. 30:42like to now predict.
  890. 30:44So, what we'd like to do now is just as
  891. 30:46in the previous video, we'd like to
  892. 30:48index into the rows of prob, and each
  893. 30:50row we'd like to pluck out the
  894. 30:52probability assigned to the correct
  895. 30:54character
  896. 30:55as given here.
  897. 30:57So, first we have torch.arange of 32,
  898. 31:00which is kind of like a iterator over um
  899. 31:03numbers from 0 to 31,
  900. 31:05and then we can index into prob in the
  901. 31:07following way.
  902. 31:09prob in torch.arange of 32, which
  903. 31:12iterates the rows. And then in each row,
  904. 31:14we'd like to grab this column as given
  905. 31:17by Y.
  906. 31:19So, this gives the current probabilities
  907. 31:21as assigned by this neural network with
  908. 31:23this setting of its weights
  909. 31:25to the correct character in the
  910. 31:26sequence.
  911. 31:27And you can see here that this looks
  912. 31:29okay for some of these characters. Like
  913. 31:31this is basically 0.2,
  914. 31:33but it doesn't look very good at all for
  915. 31:34many other characters. Like this is
  916. 31:360.0701
  917. 31:38probability. And so, the network thinks
  918. 31:40that some of these are extremely
  919. 31:42unlikely. But of course, we haven't
  920. 31:43trained the neural network yet. So,
  921. 31:46um this will improve, and ideally all of
  922. 31:49these numbers here of course are one
  923. 31:50because then we are correctly predicting
  924. 31:52the next character.
  925. 31:53Now, just as in the previous video, we
  926. 31:55want to take these probabilities, we
  927. 31:57want to look at the log probability, and
  928. 31:59then we want to look at the average log
  929. 32:01probability,
  930. 32:02and then negative of it to create the
  931. 32:04negative log likelihood loss.
  932. 32:07So, the loss here is 17.
  933. 32:10And this is the loss that we'd like to
  934. 32:11minimize to get the network to predict
  935. 32:14the correct character in the sequence.
  936. 32:16Okay, so I rewrote everything here and
  937. 32:18made it a bit more respectable.
  938. 32:20So, here's our data set.
  939. 32:22Here's all the parameters that we
  940. 32:23defined.
  941. 32:24I'm now using a generator to make it
  942. 32:26reproducible.
  943. 32:27I clustered all the parameters into a
  944. 32:29single list of parameters, so that for
  945. 32:31example, it's easy to count them and see
  946. 32:33that in total we currently have about
  947. 32:343,400 parameters.
  948. 32:37And this is the forward pass as we
  949. 32:38developed it.
  950. 32:39And we arrive at a single number here,
  951. 32:42the loss, that is currently expressing
  952. 32:44how well this neural network works with
  953. 32:46the current setting of parameters.
  954. 32:48Now, I would like to make it even more
  955. 32:50respectable.
  956. 32:51So, in particular, see these lines here,
  957. 32:53where we take the logits and we
  958. 32:54calculate the loss.
  959. 32:57Um we're not actually reinventing the
  960. 32:59wheel here. This is just um
  961. 33:01classification, and many people use
  962. 33:03classification, and that's why there is
  963. 33:05a functional.cross_entropy function in
  964. 33:07PyTorch to calculate this much more
  965. 33:09efficiently.
  966. 33:10So, we could just simply call
  967. 33:11f.cross_entropy,
  968. 33:13and we can pass in the logits, and we
  969. 33:14can pass in the
  970. 33:16uh array of targets, Y.
  971. 33:18And this calculates the exact same loss.
  972. 33:21Um so, in fact, we can simply put this
  973. 33:24here
  974. 33:25and erase these three lines, and we're
  975. 33:27going to get the exact same result. Now,
  976. 33:30there are actually many good reasons to
  977. 33:31prefer f.cross_entropy over rolling your
  978. 33:34own implementation like this. I did this
  979. 33:36for educational reasons, but you'd never
  980. 33:38use this in practice. Why is that?
  981. 33:40Number one, when you use
  982. 33:41f.cross_entropy, PyTorch will not
  983. 33:43actually create all these intermediate
  984. 33:45tensors, because these are all new
  985. 33:47tensors in memory, and all this is
  986. 33:49fairly inefficient to run like this.
  987. 33:52Instead, PyTorch will cluster up all
  988. 33:54these operations and very often create
  989. 33:57have a fused kernels that very
  990. 33:59efficiently evaluate these expressions
  991. 34:00that are sort of like clustered uh
  992. 34:02mathematical operations.
  993. 34:04Number two, the backward pass can be
  994. 34:06made much more efficient. And not just
  995. 34:08because it's a fused kernel, but also
  996. 34:10analytically and mathematically, it's
  997. 34:12much it's often a very much uh simpler
  998. 34:15backward pass to implement.
  999. 34:17We actually saw this with micrograd.
  1000. 34:19You see here when we implemented tanh,
  1001. 34:21the forward pass of this operation to
  1002. 34:23calculate the tanh was actually a fairly
  1003. 34:25complicated mathematical expression.
  1004. 34:28But because it's a clustered
  1005. 34:29mathematical expression, when we did the
  1006. 34:31backward pass, we didn't individually
  1007. 34:33backward through the exp and the two
  1008. 34:35times and the minus one and the
  1009. 34:36division, etc. We just said it's 1 - t
  1010. 34:39squared. And that's a much simpler
  1011. 34:41mathematical expression.
  1012. 34:43And we were able to do this because
  1013. 34:44we're able to reuse calculations and
  1014. 34:46because we are able to mathematically
  1015. 34:48and analytically derive the derivative
  1016. 34:50and often that expression simplifies
  1017. 34:52mathematically. And so there's much less
  1018. 34:54to implement.
  1019. 34:56So not only can can it be made more
  1020. 34:57efficient because it runs in a fused
  1021. 34:59kernel, but also because the expressions
  1022. 35:01can take a much simpler form
  1023. 35:03mathematically.
  1024. 35:06So that's number one. Number two,
  1025. 35:08under the hood, after cross entropy can
  1026. 35:10also be significantly more
  1027. 35:12um
  1028. 35:13numerically well-behaved. Let me show
  1029. 35:15you an example of how this works.
  1030. 35:19Suppose we have a logits of -2, 3, -3,
  1031. 35:220, and 5.
  1032. 35:24And then we are taking the exponent of
  1033. 35:25it and normalizing it to sum to one. So
  1034. 35:28when logits take on these values,
  1035. 35:30everything is well and good and we get a
  1036. 35:31nice probability distribution.
  1037. 35:33Now consider what happens when some of
  1038. 35:35these logits take on more extreme values
  1039. 35:37and that can happen during optimization
  1040. 35:39of a neural network.
  1041. 35:40Suppose that some of these numbers grow
  1042. 35:42very negative, like say -100.
  1043. 35:45Then actually everything will come out
  1044. 35:47fine. We still get the probabilities
  1045. 35:48that um you know, are well-behaved and
  1046. 35:52they sum to one and everything is great.
  1047. 35:54But because of the way the exp works, if
  1048. 35:56you have very positive logits, like say
  1049. 35:58positive 100 in here,
  1050. 36:00you actually start to run into trouble
  1051. 36:02and we get not a number here.
  1052. 36:04And the reason for that is that these
  1053. 36:06counts
  1054. 36:08have an inf here.
  1055. 36:10So if you pass in a very negative number
  1056. 36:12to exp, you just get a very negative
  1057. 36:15Sorry, not negative, but very small
  1058. 36:16number, very very near zero and that's
  1059. 36:18fine.
  1060. 36:20But if you pass in a very positive
  1061. 36:21number, suddenly we run out of range in
  1062. 36:23our floating point number that
  1063. 36:25represents these counts.
  1064. 36:28So basically we're taking E and we're
  1065. 36:29raising it to the power of 100 and that
  1066. 36:32gives us inf because we run out of
  1067. 36:34dynamic range on this floating point
  1068. 36:35number that is count.
  1069. 36:38And so, we cannot pass very large logits
  1070. 36:41through this expression.
  1071. 36:43Now, let me reset these numbers to
  1072. 36:45something reasonable.
  1073. 36:47The way PyTorch solves this is that you
  1074. 36:50see how we have a well-behaved result
  1075. 36:52here.
  1076. 36:53It turns out that because of the
  1077. 36:54normalization here, you can actually
  1078. 36:56offset logits by any arbitrary constant
  1079. 36:59value that you want. So, if I add one
  1080. 37:01here,
  1081. 37:02you actually get the exact same result.
  1082. 37:04Or if I add two,
  1083. 37:06or if I subtract three.
  1084. 37:08Any offset will produce the exact same
  1085. 37:10probabilities.
  1086. 37:12So, because negative numbers are okay,
  1087. 37:15but positive numbers can actually
  1088. 37:16overflow this exp, what PyTorch does is
  1089. 37:19it internally calculates the maximum
  1090. 37:21value that occurs in the logits and it
  1091. 37:23subtracts it. So, in this case it would
  1092. 37:25subtract five.
  1093. 37:27And so, therefore the greatest number in
  1094. 37:28logits will become zero and all the
  1095. 37:30other numbers will become some negative
  1096. 37:32numbers.
  1097. 37:33And then the result of this is always
  1098. 37:35well-behaved. So, even if we have a 100
  1099. 37:37here previously,
  1100. 37:39not good, but because PyTorch will
  1101. 37:41subtract 100, this will work.
  1102. 37:44And so, there's many good reasons to
  1103. 37:46call cross entropy. Number one, the
  1104. 37:49forward pass can be much more efficient.
  1105. 37:50The backward pass can be much more
  1106. 37:52efficient. And also things can be much
  1107. 37:54more numerically well-behaved. Okay, so
  1108. 37:56let's now set up the training of this
  1109. 37:58neural net.
  1110. 37:59We have the forward pass.
  1111. 38:02Uh we don't need these. Except we have
  1112. 38:05the loss is equal to the after cross
  1113. 38:07entropy. That's the forward pass.
  1114. 38:09Then we need the backward pass. First we
  1115. 38:12want to set the gradients to be zero.
  1116. 38:14So, for P and parameters, we want to
  1117. 38:16make sure that P.grad is none, which is
  1118. 38:18the same as setting it to zero in
  1119. 38:19PyTorch.
  1120. 38:21And then loss.backward to populate those
  1121. 38:23gradients.
  1122. 38:24Once we have the gradients, we can do
  1123. 38:25the parameter update. So, for P in
  1124. 38:27parameters, we want to take all the data
  1125. 38:30and we want to nudge it
  1126. 38:32learning rate times p.grad.
  1127. 38:36And then we want to repeat this
  1128. 38:39a few times.
  1129. 38:41Um
  1130. 38:44And let's print the loss here as well.
  1131. 38:48Now, this won't suffice and will create
  1132. 38:50an error because we also have to go for
  1133. 38:52P in parameters
  1134. 38:54and we have to make sure that
  1135. 38:55p.requires_grad
  1136. 38:57is set to true in PyTorch.
  1137. 38:59And this should just work.
  1138. 39:03Okay. So, we started off with loss of 17
  1139. 39:05and we're decreasing it.
  1140. 39:08Let's run longer.
  1141. 39:10And you see how the loss decreases
  1142. 39:12a lot here. So,
  1143. 39:17if we just run for 1,000 times, we get a
  1144. 39:20very, very low loss. And that means that
  1145. 39:21we're making very good predictions. Now,
  1146. 39:23the reason that this is so
  1147. 39:25straightforward right now is because
  1148. 39:27we're only um
  1149. 39:29overfitting 32 examples.
  1150. 39:32So, we only have 32 examples uh of the
  1151. 39:34first five words
  1152. 39:36and therefore it's very easy to make
  1153. 39:38this neural net fit only these two 32
  1154. 39:40examples because we have 3,400
  1155. 39:43parameters and only 32 examples. So,
  1156. 39:46we're doing what's called overfitting a
  1157. 39:47single batch of the data
  1158. 39:50and getting a very low loss and good
  1159. 39:52predictions. Um but that's just because
  1160. 39:54we have so many parameters for so few
  1161. 39:56examples. So, it's easy to uh make this
  1162. 39:58be very low.
  1163. 40:00Now, we're not able to achieve exactly
  1164. 40:01zero. And the reason for that is we can,
  1165. 40:04for example, look at the logits which
  1166. 40:06are being predicted.
  1167. 40:08And uh we can look at the max along the
  1168. 40:11first dimension.
  1169. 40:13And in PyTorch, uh max reports both the
  1170. 40:16actual values that take on the maximum
  1171. 40:19uh, number, but also the indices of
  1172. 40:20these.
  1173. 40:22And you'll see that the indices are very
  1174. 40:23close to the labels.
  1175. 40:26But in some cases they differ. For
  1176. 40:28example, in this very first example, uh,
  1177. 40:31the predicted index is 19, but the label
  1178. 40:33is five.
  1179. 40:35And we're not able to make loss be zero,
  1180. 40:37and fundamentally that's because here
  1181. 40:40the very first or the zeroth index is
  1182. 40:43the example where dot dot dot is
  1183. 40:44supposed to predict E. But you see how
  1184. 40:46dot dot dot is also supposed to predict
  1185. 40:48an O. And dot dot dot is also supposed
  1186. 40:50to predict an I, and then S as well. And
  1187. 40:53so basically E, O, A, or S are all
  1188. 40:56possible outcomes in the training set
  1189. 40:58for the exact same input. So we're not
  1190. 41:00able to completely overfit and um,
  1191. 41:03and make the loss be exactly zero. Uh,
  1192. 41:06so but we're getting very close in the
  1193. 41:08cases where uh, there's a unique input
  1194. 41:11for a unique output. In those cases we
  1195. 41:13do what's called overfit, and we
  1196. 41:15basically get the exact same and the
  1197. 41:16exact correct result.
  1198. 41:18So now all we have to do
  1199. 41:21is we just need to make sure that we
  1200. 41:22read in the full data set and optimize
  1201. 41:24the neural net.
  1202. 41:25Okay, so let's swing back up where we
  1203. 41:27created the data set.
  1204. 41:29And we see that here we only use the
  1205. 41:30first five words. So let me now erase
  1206. 41:32this,
  1207. 41:33and let me erase the print statements,
  1208. 41:35otherwise we'll be printing way too
  1209. 41:36much.
  1210. 41:38And so when we process the full data set
  1211. 41:40of all the words, we now have 228,000
  1212. 41:43examples instead of just 32.
  1213. 41:45So let's now scroll back down. The
  1214. 41:47dataset is much larger. We initialize
  1215. 41:49the weights, the same number of
  1216. 41:51parameters. They all require gradients.
  1217. 41:54And then let's push this print out loss
  1218. 41:56dot item to be here.
  1219. 41:58And let's just see how the optimization
  1220. 41:59goes if we run this.
  1221. 42:04Okay, so we started with a fairly high
  1222. 42:05loss, and then as we're optimizing, the
  1223. 42:07loss is coming down.
  1224. 42:11But you'll notice that it takes quite a
  1225. 42:13bit of time for every single iteration.
  1226. 42:15So, let's actually address that. Because
  1227. 42:17we're doing way too much work forwarding
  1228. 42:19and backwarding 220,000 examples.
  1229. 42:22In practice, what people usually do is
  1230. 42:24they perform forward and backward pass
  1231. 42:26and update on mini batches of the data.
  1232. 42:29So, what we will want to do is we want
  1233. 42:31to randomly select some portion of the
  1234. 42:33data set, and that's a mini batch, and
  1235. 42:35then only forward backward and update on
  1236. 42:37that little mini batch. And then, um,
  1237. 42:40we iterate on those mini batches.
  1238. 42:42So, in PyTorch, we can, for example, use
  1239. 42:43torch.randint.
  1240. 42:45We can generate numbers between 0 and 5
  1241. 42:47and make 32 of them.
  1242. 42:51Um,
  1243. 42:52I believe the size has to be a tuple
  1244. 42:56in PyTorch.
  1245. 42:57So, we can have a tuple 32 of numbers
  1246. 43:00between 0 and 5. But, actually, we want
  1247. 43:02x.shape of 0 here.
  1248. 43:05And so, this creates uh integers that
  1249. 43:08index into our data set, and there's 32
  1250. 43:10of them.
  1251. 43:11So, if our mini batch size is 32, then
  1252. 43:13we can come here and we can first do uh
  1253. 43:16mini batch
  1254. 43:18construct.
  1255. 43:20So, in the integers that we want to
  1256. 43:22optimize in this
  1257. 43:23um single iteration are in the ix.
  1258. 43:27And then, we want to index into x with
  1259. 43:31ix to only grab those rows.
  1260. 43:34So, we're only getting 32 rows of x.
  1261. 43:37And therefore, embeddings will again be
  1262. 43:3832 by 3 by 2, not 200,000 by 3 by 2.
  1263. 43:43And then, this ix has to be used not
  1264. 43:44just to index into x, but also to index
  1265. 43:48into y.
  1266. 43:50And now, this should be mini batches,
  1267. 43:52and this should be much much faster. So,
  1268. 43:55Okay, so it's instant almost.
  1269. 43:58So, this way, we can run many many
  1270. 44:00examples
  1271. 44:01nearly instantly and decrease the loss
  1272. 44:03much much faster.
  1273. 44:05Now, because we're only dealing with
  1274. 44:06mini batches, the quality of our
  1275. 44:08gradient is lower. So, the direction is
  1276. 44:11not as reliable. It's not the actual
  1277. 44:13gradient direction.
  1278. 44:14But, the gradient direction is good
  1279. 44:16enough even when it's estimating on only
  1280. 44:1832 examples that it is useful.
  1281. 44:21And so, it's much better to have an
  1282. 44:24approximate gradient and just make more
  1283. 44:25steps than it is to evaluate the exact
  1284. 44:28gradient and take fewer steps. So,
  1285. 44:30that's why in practice,
  1286. 44:32uh this works quite well.
  1287. 44:34So, let's continue the optimization.
  1288. 44:38Let me take out this loss.item from here
  1289. 44:41and uh place it over here at the end.
  1290. 44:46Okay, so we're hovering around 2.5 or
  1291. 44:48so.
  1292. 44:49Um
  1293. 44:50however, this is only the loss for that
  1294. 44:51mini batch. So, let's actually evaluate
  1295. 44:53the loss
  1296. 44:55here
  1297. 44:56for all of X
  1298. 44:58and for all of Y, just so we have a full
  1299. 45:01sense of exactly how well the model is
  1300. 45:03doing right now.
  1301. 45:05So, right now we're at about 2.7 on the
  1302. 45:07entire training set.
  1303. 45:09So, let's run the optimization for a
  1304. 45:11while.
  1305. 45:12Okay, we're at 2.6.
  1306. 45:152.57
  1307. 45:172.53
  1308. 45:22Okay.
  1309. 45:22So, one issue, of course, is we don't
  1310. 45:24know if we're stepping too slow or too
  1311. 45:27fast.
  1312. 45:28Um so, this point one, I just guessed
  1313. 45:30it. So, one question is, how do you
  1314. 45:32determine this learning rate?
  1315. 45:34And um how do we gain confidence that
  1316. 45:37we're stepping in the right um
  1317. 45:39sort of speed? So, I'll show you one way
  1318. 45:41to determine a reasonable learning rate.
  1319. 45:43It works as follows. Let's reset our
  1320. 45:46parameters
  1321. 45:47to the initial
  1322. 45:49um
  1323. 45:49settings.
  1324. 45:51And now, let's
  1325. 45:52print in every step.
  1326. 45:55But, let's only do 10 steps or so.
  1327. 45:58Or maybe maybe 100 steps.
  1328. 46:01We want to find like a very reasonable
  1329. 46:02set
  1330. 46:03search range, if you will. So, for
  1331. 46:05example, if this is like very low,
  1332. 46:07then
  1333. 46:10we see that the loss is barely
  1334. 46:11decreasing. So, that's not
  1335. 46:13That's like too low, basically. So,
  1336. 46:15let's try
  1337. 46:16this one.
  1338. 46:18Okay, so we're decreasing the loss, but
  1339. 46:20like not very quickly. So, that's a
  1340. 46:21pretty good low range.
  1341. 46:23Now, let's reset it again.
  1342. 46:26And now, let's try to find the place at
  1343. 46:27which the loss kind of explodes.
  1344. 46:29Uh so, maybe at -1.
  1345. 46:33Okay, we see that we're minimizing the
  1346. 46:34loss, but you see how it's kind of
  1347. 46:36unstable. It goes up and down quite a
  1348. 46:38bit.
  1349. 46:39Um so, -1 is probably like a fast
  1350. 46:42learning rate. Let's try -10.
  1351. 46:45Okay, so this isn't optimizing. This is
  1352. 46:48not working very well. So, -10 is way
  1353. 46:50too big. -1 was already kind of big. Um
  1354. 46:54so, therefore, -1 was like somewhat
  1355. 46:57reasonable if I reset.
  1356. 47:00So, I'm thinking that the right learning
  1357. 47:01rate is somewhere between
  1358. 47:03um -0.001
  1359. 47:05and um
  1360. 47:07-1.
  1361. 47:08So, the way we can do this here is we
  1362. 47:09can use uh torch.linspace.
  1363. 47:13And we want to basically do something
  1364. 47:14like this, between 0 and 1, but
  1365. 47:17um
  1366. 47:19uh number of steps is one more parameter
  1367. 47:21that's required. Let's do 1,000 steps.
  1368. 47:23This creates 1,000
  1369. 47:26um numbers between 0.001 and 1.
  1370. 47:29But, it doesn't really make sense to
  1371. 47:31step between these linearly. So,
  1372. 47:33instead, let me create learning rate
  1373. 47:35exponent.
  1374. 47:36And instead of 0.001, this will be a -3,
  1375. 47:40and this will be a 0. And then, the
  1376. 47:42actual uh LRs that we want to search
  1377. 47:44over are going to be 10 to the power of
  1378. 47:46LRE.
  1379. 47:48So, now what we're doing is we're
  1380. 47:49stepping linearly between the exponents
  1381. 47:51of these learning rates. This is 0.001,
  1382. 47:54and this is 1, because uh 10 to the
  1383. 47:56power of zero is one.
  1384. 47:58And therefore, we are spaced
  1385. 48:00exponentially in this interval.
  1386. 48:02So, these are the candidate learning
  1387. 48:03rates
  1388. 48:04that we want to sort of like search
  1389. 48:06over, roughly.
  1390. 48:07So, now what we're going to do is
  1391. 48:10here, we are going to run the
  1392. 48:12optimization for 1,000 steps.
  1393. 48:14And instead of using a fixed number, we
  1394. 48:17are going to use learning rate
  1395. 48:19indexing into here, lrs of I, and make
  1396. 48:22this I.
  1397. 48:25So, basically, let me reset this to be,
  1398. 48:28again, starting from random,
  1399. 48:30creating these learning rates between
  1400. 48:32negative um 0.0 between 0.001 and um
  1401. 48:36one, but exponentially stepped.
  1402. 48:39And here what we're doing is we're
  1403. 48:41iterating a thousand times. We're going
  1404. 48:43to use the learning rate
  1405. 48:45um that's in the beginning very, very
  1406. 48:47low. In the beginning, it's going to be
  1407. 48:490.001, but by the end, it's going to be
  1408. 48:52one.
  1409. 48:53And then we're going to step with that
  1410. 48:55learning rate.
  1411. 48:57And now what we want to do is we want to
  1412. 48:58keep track of the uh um
  1413. 49:04learning rates that we used, and we want
  1414. 49:06to look at the losses that resulted.
  1415. 49:09And so here, let me
  1416. 49:12track stats.
  1417. 49:14So, lri.append lr,
  1418. 49:16and um
  1419. 49:18loss i.append
  1420. 49:20loss.item.
  1421. 49:22Okay.
  1422. 49:23So, again, reset everything,
  1423. 49:27and then run.
  1424. 49:30And so, basically, we started with a
  1425. 49:31very low learning rate, and we went all
  1426. 49:33the way up to uh learning rate of
  1427. 49:34negative one. And now what we can do is
  1428. 49:37we can plt.plot,
  1429. 49:39and we can plot the two. So, we can plot
  1430. 49:41the learning rates on the x-axis, and
  1431. 49:43the losses we saw on the y-axis.
  1432. 49:46And often, you're going to find that
  1433. 49:47your plot looks something like this,
  1434. 49:50where in the beginning, um
  1435. 49:52you had very low learning rates. We
  1436. 49:53basically anything
  1437. 49:55barely anything happened.
  1438. 49:56Then we got to like a nice spot here.
  1439. 50:00And then as we increase the learning
  1440. 50:01rate enough, uh, we basically started to
  1441. 50:03be kind of unstable here.
  1442. 50:05So a good learning rate turns out to be
  1443. 50:07somewhere around here.
  1444. 50:10Um, and because we have LRI here,
  1445. 50:13um,
  1446. 50:14we actually may want to, um,
  1447. 50:19do not LR, uh,
  1448. 50:21not the learning rate, but the exponent.
  1449. 50:22So that would be the LRE at I is maybe
  1450. 50:25what we want to log. So let me reset
  1451. 50:27this and redo that calculation.
  1452. 50:30But now on the x-axis we have the, um,
  1453. 50:34exponent of the learning rate. And so we
  1454. 50:36can see the exponent of the learning
  1455. 50:37rate that is good to use. It would be
  1456. 50:38sort of like roughly in the valley here,
  1457. 50:41because here the learning rates are just
  1458. 50:42way too low. And then here where we
  1459. 50:44expect relatively good learning rate
  1460. 50:46somewhere here. And then here things are
  1461. 50:47starting to explode. So somewhere around
  1462. 50:50-1 as the exponent of the learning rate
  1463. 50:52is a pretty good setting. And 10 to the
  1464. 50:55-1 is .1. So .1 is actually .1 was
  1465. 50:59actually a fairly good learning rate
  1466. 51:01around here.
  1467. 51:02And that's what we had in the initial
  1468. 51:03setting.
  1469. 51:04Um, but that's roughly how you would
  1470. 51:06determine it.
  1471. 51:07And so here now we can take out the
  1472. 51:10tracking of these.
  1473. 51:12And we can just simply set LR to be 10
  1474. 51:15to the -1 or
  1475. 51:18basically otherwise .1 as it was before.
  1476. 51:20And now we have some confidence that
  1477. 51:21this is actually a fairly good learning
  1478. 51:23rate.
  1479. 51:24And so now we can do is we can crank up
  1480. 51:26the iterations.
  1481. 51:27We can reset our optimization.
  1482. 51:30And, um,
  1483. 51:32we can run for a pretty long time using
  1484. 51:34this learning rate.
  1485. 51:36Oops, and we don't want to print. That's
  1486. 51:38way too much printing.
  1487. 51:40So let me again reset.
  1488. 51:42And run 10,000 steps.
  1489. 51:48Okay, so we're at 0.2 2.48 roughly.
  1490. 51:51Let's run another 10,000 steps.
  1491. 51:582.46
  1492. 52:00And now let's do one learning rate
  1493. 52:01decay. What this means is we're going to
  1494. 52:03take our learning rate and we're going
  1495. 52:04to 10x lower it. And so we're at the
  1496. 52:07late stages of training potentially and
  1497. 52:10we may want to go uh a bit slower.
  1498. 52:12Let's do one more actually at 0.1 just
  1499. 52:14to see if
  1500. 52:17we're making a dent here.
  1501. 52:19Okay, we're still making dent. And by
  1502. 52:20the way, the bigram loss that we
  1503. 52:23achieved last video was 2.45. So we've
  1504. 52:26already surpassed the bigram model.
  1505. 52:29And once I get a sense that this is
  1506. 52:30actually kind of starting to plateau
  1507. 52:31off, uh people like to do as I mentioned
  1508. 52:33this learning rate decay. So let's try
  1509. 52:36to decay the loss,
  1510. 52:37uh the learning rate I mean.
  1511. 52:42And we achieve at about 2.3 now.
  1512. 52:46Obviously, this is janky and not exactly
  1513. 52:48how you would train it in production,
  1514. 52:50but this is roughly what you're going
  1515. 52:51through. You first find a decent
  1516. 52:53learning rate using the approach that I
  1517. 52:54showed you.
  1518. 52:55Then you start with that learning rate
  1519. 52:57and you train for a while.
  1520. 52:58And then at the end people like to do a
  1521. 53:00learning rate decay where you decay the
  1522. 53:02learning rate by say a factor of 10 and
  1523. 53:04you do a few more steps and then you get
  1524. 53:06a trained network, roughly speaking.
  1525. 53:08So we've achieved 2.3 and uh
  1526. 53:10dramatically improved on the bigram
  1527. 53:12language model using this simple neural
  1528. 53:14net as described here
  1529. 53:16um using these 3,400 parameters.
  1530. 53:20Now there's something we have to be
  1531. 53:20careful with.
  1532. 53:22I said that we have a better model
  1533. 53:24because we are achieving a lower loss,
  1534. 53:262.3, much lower than 2.45 with the
  1535. 53:28bigram model previously.
  1536. 53:30Now that's not exactly true. And the
  1537. 53:32reason that's not true is that
  1538. 53:37this is actually fairly small model, but
  1539. 53:39these models can get larger and larger
  1540. 53:41if you keep adding neurons and
  1541. 53:42parameters. So, you can imagine that we
  1542. 53:44don't potentially have a thousand
  1543. 53:46parameters. We could have 10,000 or
  1544. 53:47100,000 or millions of parameters.
  1545. 53:50And as the capacity of the neural
  1546. 53:51network grows,
  1547. 53:52it becomes more and more capable of
  1548. 53:55overfitting your training set.
  1549. 53:57What that means is that the loss on the
  1550. 53:59training set, on the data that you're
  1551. 54:00training on, will become very, very low,
  1552. 54:03as low as zero.
  1553. 54:04But all that the model is doing is
  1554. 54:06memorizing your training set verbatim.
  1555. 54:09So, if you take that model, and it looks
  1556. 54:10like it's working really well, but you
  1557. 54:12try to sample from it, you will
  1558. 54:13basically only get examples exactly as
  1559. 54:15they are in the training set. You won't
  1560. 54:17get any new data.
  1561. 54:19In addition to that, if you try to
  1562. 54:20evaluate the loss on some withheld names
  1563. 54:23or other words, you will actually see
  1564. 54:25that the loss on those can be very high.
  1565. 54:28As a basically, it's not a good model.
  1566. 54:30So, the standard in the field it is is
  1567. 54:32to split up your data set into three
  1568. 54:34splits, as we call them. We have the
  1569. 54:36training split, the dev split or the
  1570. 54:38validation split,
  1571. 54:40and the test split.
  1572. 54:42So, training split,
  1573. 54:45test or um sorry, dev or validation
  1574. 54:48split,
  1575. 54:49and test split.
  1576. 54:52And typically, this would be say 80% of
  1577. 54:54your data set, this could be 10% and
  1578. 54:56this 10% roughly.
  1579. 54:58So, you have these three splits of the
  1580. 55:00data.
  1581. 55:01Now, these 80% of your training of of
  1582. 55:03the data set, the training set, is used
  1583. 55:05to optimize the parameters of the model,
  1584. 55:07just like we're doing here, using
  1585. 55:08gradient descent.
  1586. 55:10These 10% of the um examples, the dev or
  1587. 55:13validation split, they're used for
  1588. 55:15development over all the hyper
  1589. 55:17parameters of your model. So, hyper
  1590. 55:19parameters are for example, the size of
  1591. 55:21this hidden layer,
  1592. 55:22the size of the embedding. So, this is a
  1593. 55:24hundred or a two for us, but we could
  1594. 55:26try different things.
  1595. 55:28The strength of the regularization,
  1596. 55:29which we aren't using yet so far.
  1597. 55:32So, there's lots of different hyper
  1598. 55:33parameters and settings that go into
  1599. 55:34defining a neural net, and you can try
  1600. 55:36many different variations of them and
  1601. 55:38see whichever one works best on your
  1602. 55:41validation split.
  1603. 55:43So, this is used to train the
  1604. 55:44parameters.
  1605. 55:45This is used to train the hyper
  1606. 55:47parameters.
  1607. 55:48And test split is used to evaluate uh
  1608. 55:51basically the performance of the model
  1609. 55:53at the end.
  1610. 55:54So, we're only evaluating the loss on
  1611. 55:55the test split very, very sparingly and
  1612. 55:57very few times because every single time
  1613. 56:00you evaluate your test loss and you
  1614. 56:02learn something from it,
  1615. 56:04you are basically starting to also train
  1616. 56:06on the test split. So, you are only
  1617. 56:09allowed to test the loss on the test set
  1618. 56:12um very, very few times. Otherwise, you
  1619. 56:15risk overfitting to it as well as you
  1620. 56:17experiment on your model.
  1621. 56:19So, let's also split up our training
  1622. 56:21data into train, dev, and test. And
  1623. 56:24then, we are going to train on train and
  1624. 56:26only evaluate on test very, very
  1625. 56:28sparingly.
  1626. 56:29Okay, so here we go.
  1627. 56:31Here is where we took all the words and
  1628. 56:33put them into X and Y tensors.
  1629. 56:36So, instead, let me create a new cell
  1630. 56:37here and let me just copy-paste some
  1631. 56:39code here
  1632. 56:41because I don't think it's that
  1633. 56:43complex, but um
  1634. 56:45we're going to try to save a little bit
  1635. 56:46of time.
  1636. 56:47I'm converting this to be a function
  1637. 56:49now. And this function takes some list
  1638. 56:51of words and builds the arrays X and Y
  1639. 56:54for those words only.
  1640. 56:56And then here, I am shuffling up all the
  1641. 56:59words. So, these are the input words
  1642. 57:01that we get.
  1643. 57:02We are randomly shuffling them all up.
  1644. 57:05And then, um
  1645. 57:06we're going to
  1646. 57:08set N1 to be
  1647. 57:10the number of examples, that's 80% of
  1648. 57:11the words, and N2 to be 90% of the
  1649. 57:14weight of the words.
  1650. 57:16So, basically, if length of words is
  1651. 57:1830,000, N1 is
  1652. 57:21Oh, sorry. I should probably run this.
  1653. 57:24N1 is 25,000 and N2 is 28,000.
  1654. 57:28And so, here we see that I'm calling
  1655. 57:30build data set to build the training set
  1656. 57:32X and Y
  1657. 57:33by indexing into up to N1. So, we're
  1658. 57:36going to have only 25,000 training
  1659. 57:38words.
  1660. 57:39And then we're going to have um
  1661. 57:42roughly
  1662. 57:44N2 minus N1
  1663. 57:463,000 validation examples or dev
  1664. 57:49examples. And we're going to have
  1665. 57:52um
  1666. 57:53len of words basically minus N2
  1667. 57:57or 3,204
  1668. 57:59examples
  1669. 58:01here for the test set.
  1670. 58:03So,
  1671. 58:04now we have X's and Y's for all those
  1672. 58:07three splits.
  1673. 58:11Um
  1674. 58:13Oh, yeah. I'm printing their size here
  1675. 58:14inside the function as well.
  1676. 58:19But here we don't have words, but these
  1677. 58:20are already the individual examples made
  1678. 58:22from those words.
  1679. 58:25So, let's now scroll down here.
  1680. 58:27And the data set now for training is
  1681. 58:31more like this.
  1682. 58:33And then when we reset the network,
  1683. 58:38when we're training, we're only going to
  1684. 58:40be training using X train,
  1685. 58:43X train, and Y train.
  1686. 58:47So, that's the only thing we're training
  1687. 58:49on.
  1688. 58:57Let's see where we are on the
  1689. 59:00single batch.
  1690. 59:02Let's now train maybe a few more steps.
  1691. 59:08Training a neural network can take a
  1692. 59:09while. Usually, you don't do it in line.
  1693. 59:11You launch a bunch of jobs and you wait
  1694. 59:12for them to finish.
  1695. 59:13Um
  1696. 59:14can take in multiple days and so on.
  1697. 59:16Luckily, this is a very small network.
  1698. 59:21Okay, so the loss is pretty good. Oh, we
  1699. 59:24accidentally used a learning rate that
  1700. 59:25is way too low.
  1701. 59:27So, let me actually come back.
  1702. 59:29We use the decay learning rate of 0.01.
  1703. 59:35So, this will train much faster.
  1704. 59:37And then here, when we evaluate, uh
  1705. 59:39let's use the dev set here.
  1706. 59:42X dev
  1707. 59:43and Y dev to evaluate the loss.
  1708. 59:47Okay.
  1709. 59:48Uh
  1710. 59:48and let's not decay the learning rate
  1711. 59:50and only do say 10,000 examples.
  1712. 59:55And let's evaluate the dev loss once
  1713. 59:58here.
  1714. 59:59Okay, so we're getting about 2.3 on dev.
  1715. 1:00:01And so, the neural network when it was
  1716. 1:00:02training did not see these dev examples.
  1717. 1:00:05It hasn't optimized on them. And yet,
  1718. 1:00:08when we evaluate the loss on these dev,
  1719. 1:00:10we actually get a pretty decent loss.
  1720. 1:00:12And so, we can also look at what the
  1721. 1:00:16loss is on all of training set.
  1722. 1:00:19Oops.
  1723. 1:00:20And so, we see that the training and the
  1724. 1:00:22dev loss are about equal. So, we're not
  1725. 1:00:24over- fitting. Um this model is not
  1726. 1:00:27powerful enough to just be purely
  1727. 1:00:29memorizing the data. And so far, we are
  1728. 1:00:32what's called under-fitting because the
  1729. 1:00:34training loss and the dev or test losses
  1730. 1:00:36are roughly equal. So, what that
  1731. 1:00:38typically means is that our network is
  1732. 1:00:40very tiny, very small. And we expect to
  1733. 1:00:43make uh performance improvements by
  1734. 1:00:45scaling up the size of this neural net.
  1735. 1:00:47So, let's do that now. So, let's come
  1736. 1:00:49over here
  1737. 1:00:50and let's increase the size of the
  1738. 1:00:51neural net.
  1739. 1:00:52The easiest way to do this is we can
  1740. 1:00:54come here to the hidden layer, which
  1741. 1:00:55currently has 100 neurons, and let's
  1742. 1:00:57just pump this up. So, let's do 300
  1743. 1:00:59neurons.
  1744. 1:01:00And then, this is also 300 biases. And
  1745. 1:01:03here we have 300 inputs into the final
  1746. 1:01:05layer.
  1747. 1:01:07So,
  1748. 1:01:08let's initialize our neural net. We now
  1749. 1:01:10have 10,000 10,000 parameters instead of
  1750. 1:01:133,000 parameters.
  1751. 1:01:15And then, we're not using this.
  1752. 1:01:18And then here, what I'd like to do is
  1753. 1:01:19I'd like to actually uh keep track of uh
  1754. 1:01:23that
  1755. 1:01:24um
  1756. 1:01:27Okay, let's just do this. Let's keep
  1757. 1:01:29stats again.
  1758. 1:01:30And here when we're keeping track of the
  1759. 1:01:34loss, let's just also keep track of the
  1760. 1:01:37steps. And let's just have a eye here.
  1761. 1:01:41And let's train on 30,000.
  1762. 1:01:44Or rather say
  1763. 1:01:46Okay, let's try 30,000.
  1764. 1:01:48And we are at 0.1.
  1765. 1:01:51And
  1766. 1:01:52we should be able to run this.
  1767. 1:01:55And optimize neural net.
  1768. 1:01:57And then here basically I want to
  1769. 1:01:59PLT.plot
  1770. 1:02:01the steps
  1771. 1:02:02against the loss.
  1772. 1:02:09So these are the x's and y's.
  1773. 1:02:11And this is
  1774. 1:02:13the loss function and how it's being
  1775. 1:02:15optimized.
  1776. 1:02:16Now you see that there's quite a bit of
  1777. 1:02:18thickness to this, and that's because we
  1778. 1:02:19are optimizing over these mini batches.
  1779. 1:02:21And the mini batches create a little bit
  1780. 1:02:23of noise in this.
  1781. 1:02:25Uh where are we in the dev set? We are
  1782. 1:02:27at 2.5. So we still haven't optimized
  1783. 1:02:30this neural net very well.
  1784. 1:02:32And that's probably because we made it
  1785. 1:02:33bigger. It might take longer for this
  1786. 1:02:34neural net to converge.
  1787. 1:02:36Um
  1788. 1:02:37and so let's continue training.
  1789. 1:02:40Um
  1790. 1:02:42yeah, let's just continue training.
  1791. 1:02:46One possibility is that the batch size
  1792. 1:02:48is so low
  1793. 1:02:49that we just have way too much noise in
  1794. 1:02:51the training. And we may want to
  1795. 1:02:53increase the batch size so that we have
  1796. 1:02:54a bit more
  1797. 1:02:56correct gradient and we're not thrashing
  1798. 1:02:58too much. And we can actually like
  1799. 1:03:00optimize more properly.
  1800. 1:03:08Okay.
  1801. 1:03:08Uh this will now become meaningless
  1802. 1:03:10because we've reinitialized these. So
  1803. 1:03:13yeah, this looks not pleasing like now.
  1804. 1:03:16But there probably is like a tiny
  1805. 1:03:17improvement, but it's so hard to tell.
  1806. 1:03:20Uh let's go again.
  1807. 1:03:222.52
  1808. 1:03:25Let's try to decrease the learning rate
  1809. 1:03:27by a factor of two.
  1810. 1:03:50Okay, we're at 2.32.
  1811. 1:03:52Let's continue training.
  1812. 1:04:05We basically expect to see a lower loss
  1813. 1:04:07than what we had before, because now we
  1814. 1:04:09have a much, much bigger model, and we
  1815. 1:04:10were underfitting. So, we'd expect that
  1816. 1:04:12increasing the size of the model should
  1817. 1:04:14help the neural net.
  1818. 1:04:162.32 Okay, so that's not happening too
  1819. 1:04:18well.
  1820. 1:04:19Now, one other concern is that even
  1821. 1:04:21though we've made the tanh layer here,
  1822. 1:04:23uh the hidden layer, much, much bigger,
  1823. 1:04:25it could be that the bottleneck of the
  1824. 1:04:26network right now are these embeddings
  1825. 1:04:28that are two dimensional. It can be that
  1826. 1:04:30we're just cramming way too many
  1827. 1:04:32characters into just two dimensions, and
  1828. 1:04:34the neural net is not able to really use
  1829. 1:04:36that space effectively, and that that is
  1830. 1:04:38sort of like the bottleneck to our
  1831. 1:04:39network's performance.
  1832. 1:04:42Okay, 2.23. So, just by decreasing the
  1833. 1:04:45learning rate, I was able to make quite
  1834. 1:04:46a bit of progress. Let's run this one
  1835. 1:04:47more time.
  1836. 1:04:51And then evaluate the training and the
  1837. 1:04:53dev loss.
  1838. 1:04:56Now, one more thing after training that
  1839. 1:04:58I'd like to do is I'd like to visualize
  1840. 1:05:00the um
  1841. 1:05:02embedding vectors for these um
  1842. 1:05:05characters before we scale up uh the
  1843. 1:05:08embedding size from two.
  1844. 1:05:09Because we'd like to make uh this
  1845. 1:05:11bottleneck potentially go away.
  1846. 1:05:13But once I make this greater than two,
  1847. 1:05:15we won't be able to visualize them.
  1848. 1:05:17So here, okay, we're at 2.23 and 2.24.
  1849. 1:05:21So um
  1850. 1:05:22we're not improving much more, and maybe
  1851. 1:05:24the bottleneck now is the character
  1852. 1:05:25embedding size, which is two.
  1853. 1:05:28So here I have a bunch of code that will
  1854. 1:05:29create a figure,
  1855. 1:05:31and then we're going to visualize
  1856. 1:05:34the embeddings that were trained by the
  1857. 1:05:35neural net
  1858. 1:05:36on these characters. Because right now
  1859. 1:05:38the embedding size is just two, so we
  1860. 1:05:40can visualize all the characters with
  1861. 1:05:41the X and the Y coordinates as the two
  1862. 1:05:44embedding locations for each of these
  1863. 1:05:46characters.
  1864. 1:05:47And so here are the X coordinates and
  1865. 1:05:50the Y coordinates, which are the columns
  1866. 1:05:51of C.
  1867. 1:05:52And then for each one, I also include
  1868. 1:05:55the text of the little character.
  1869. 1:05:58So here what we see is actually kind of
  1870. 1:05:59interesting.
  1871. 1:06:01Um
  1872. 1:06:02the network has basically learned to
  1873. 1:06:04separate out the characters and cluster
  1874. 1:06:06them a little bit. Uh so for example,
  1875. 1:06:08you see how the vowels
  1876. 1:06:09a e i o u are clustered up here.
  1877. 1:06:12So what that's telling us that is that
  1878. 1:06:14the neural net treats these as very
  1879. 1:06:15similar, right? Because when they feed
  1880. 1:06:17into the neural net, the embedding
  1881. 1:06:19uh for all of these characters is very
  1882. 1:06:21similar. And so the neural net thinks
  1883. 1:06:23that they're very similar and kind of
  1884. 1:06:24like interchangeable, if that makes
  1885. 1:06:26sense.
  1886. 1:06:27Um
  1887. 1:06:29then the the points that are like really
  1888. 1:06:31far away are for example Q. Q is kind of
  1889. 1:06:33treated as an exception, and Q has a
  1890. 1:06:35very special embedding vector, so to
  1891. 1:06:37speak.
  1892. 1:06:38Similarly, dot, which is a special
  1893. 1:06:40character, is all the way out here.
  1894. 1:06:42And a lot of the other letters are sort
  1895. 1:06:44of like clustered up here.
  1896. 1:06:46And so it's kind of interesting that
  1897. 1:06:47there's a little bit of structure here
  1898. 1:06:49um after the training,
  1899. 1:06:51and it's not definitely not random, and
  1900. 1:06:53these embeddings make sense.
  1901. 1:06:56So we're now going to scale up the
  1902. 1:06:57embedding size and won't be able to
  1903. 1:06:59visualize it directly, but we expect
  1904. 1:07:01that because we're underfitting,
  1905. 1:07:03and we made this layer much bigger and
  1906. 1:07:06did not sufficiently improve the loss.
  1907. 1:07:08We're thinking that the
  1908. 1:07:09um
  1909. 1:07:10constraint to better performance right
  1910. 1:07:12now could be these embedding vectors.
  1911. 1:07:15So, let's make them bigger. Okay, so
  1912. 1:07:16let's scroll up here.
  1913. 1:07:18And now we don't have two-dimensional
  1914. 1:07:19embeddings, we are going to have
  1915. 1:07:21say 10-dimensional embeddings for each
  1916. 1:07:23word.
  1917. 1:07:25Then
  1918. 1:07:26this layer will receive 3 * 10, so 30
  1919. 1:07:30inputs
  1920. 1:07:31will go into
  1921. 1:07:33um
  1922. 1:07:33the hidden layer.
  1923. 1:07:35Let's also make the hidden layer a bit
  1924. 1:07:37smaller. So, instead of 300, let's just
  1925. 1:07:38do 200 neurons in that hidden layer.
  1926. 1:07:41So, now the total number of elements
  1927. 1:07:43will be slightly bigger at 11,000.
  1928. 1:07:47And then we here we have to be a bit
  1929. 1:07:48careful because um
  1930. 1:07:50okay, the learning rate we set to 0.1.
  1931. 1:07:53Here we are hardcoding six and
  1932. 1:07:56obviously, if you're working in
  1933. 1:07:56production, you don't want to be
  1934. 1:07:57hardcoding magic numbers. But instead of
  1935. 1:08:00six, this should now be 30.
  1936. 1:08:02Um
  1937. 1:08:04and let's run it for 50,000 iterations
  1938. 1:08:06and let me split out the initialization
  1939. 1:08:08here outside
  1940. 1:08:10so that when we run this cell multiple
  1941. 1:08:12times, it's not going to wipe out our
  1942. 1:08:14loss.
  1943. 1:08:17In addition to that,
  1944. 1:08:19here
  1945. 1:08:20let's instead of logging loss.item,
  1946. 1:08:22let's actually uh log the
  1947. 1:08:25let's um do log 10,
  1948. 1:08:28I believe that's a function of the loss.
  1949. 1:08:32And I'll show you why in a second. Let's
  1950. 1:08:34optimize this.
  1951. 1:08:37Basically, I'd like to plot the log loss
  1952. 1:08:39instead of the loss because when you
  1953. 1:08:40plot the loss, many times it can have
  1954. 1:08:42this hockey stick appearance and log
  1955. 1:08:44squashes it in.
  1956. 1:08:46Uh so, it just kind of like looks nicer.
  1957. 1:08:49So, the x-axis is step I
  1958. 1:08:51and the y-axis will be the loss I.
  1959. 1:09:00And then here, this is 30.
  1960. 1:09:03Ideally, we wouldn't be hard coding
  1961. 1:09:04these.
  1962. 1:09:08Okay, so let's look at the loss.
  1963. 1:09:11Okay? It's again very thick because the
  1964. 1:09:13mini batch size is very small, but the
  1965. 1:09:15total loss over the training set is 2.3
  1966. 1:09:18and the the test or the dev set is 2.38
  1967. 1:09:20as well.
  1968. 1:09:22So so far so good. Uh let's try to now
  1969. 1:09:24decrease the learning rate
  1970. 1:09:25by a factor of 10
  1971. 1:09:29and train for another 50,000 iterations.
  1972. 1:09:35We'd hope that we would be able to beat
  1973. 1:09:37uh 2.32.
  1974. 1:09:43But again, we're just kind of like doing
  1975. 1:09:44this very haphazardly, so I don't
  1976. 1:09:46actually have confidence that our
  1977. 1:09:48learning rate is set very well, that our
  1978. 1:09:50learning rate decay, which we just do at
  1979. 1:09:53random, is set very well.
  1980. 1:09:55And um so the optimization here is kind
  1981. 1:09:57of suspect, to be honest, and this is
  1982. 1:09:59not how you would do it typically in
  1983. 1:10:00production. In production, you would
  1984. 1:10:02create parameters or hyper parameters
  1985. 1:10:04out of all these settings, and then you
  1986. 1:10:05would run lots of experiments and see
  1987. 1:10:07whichever ones are working well for you.
  1988. 1:10:11Okay.
  1989. 1:10:12So we have 2.17 now and 2.2. Okay. So
  1990. 1:10:16you see how the training and the
  1991. 1:10:18validation performance are starting to
  1992. 1:10:20slightly slowly depart.
  1993. 1:10:23So maybe we're getting the sense that
  1994. 1:10:24the neural net is getting good enough or
  1995. 1:10:28that number of parameters is large
  1996. 1:10:30enough that we are slowly starting to
  1997. 1:10:32overfit.
  1998. 1:10:34Uh let's maybe run one more iteration of
  1999. 1:10:36this
  2000. 1:10:38and see where we get.
  2001. 1:10:41But yeah, basically, you would be
  2002. 1:10:43running lots of experiments, and then
  2003. 1:10:44you are slowly scrutinizing whichever
  2004. 1:10:46ones give you the best dev performance.
  2005. 1:10:48And then once you find all the uh hyper
  2006. 1:10:50parameters that make your dev
  2007. 1:10:51performance good, you take that model
  2008. 1:10:53and you evaluate the test set
  2009. 1:10:55performance a single time. And that's
  2010. 1:10:57the number that you report in your paper
  2011. 1:10:59or wherever else you want to talk about
  2012. 1:11:00and brag about your model.
  2013. 1:11:05So, let's then rerun the plot and rerun
  2014. 1:11:08the train and dev.
  2015. 1:11:11And because we're getting lower loss
  2016. 1:11:12now, it is the case that the embedding
  2017. 1:11:14size of these was holding us back very
  2018. 1:11:17likely.
  2019. 1:11:20Okay, so 2.16 2.19 is what we're roughly
  2020. 1:11:22getting.
  2021. 1:11:24So, there's many ways to go from many
  2022. 1:11:26ways to go from here. We can continue
  2023. 1:11:28tuning the optimization.
  2024. 1:11:30We can continue, for example, playing
  2025. 1:11:32with the sizes of the neural net. Or we
  2026. 1:11:33can increase the number of um
  2027. 1:11:36words or characters, in our case, that
  2028. 1:11:38we are taking as an input. So, instead
  2029. 1:11:39of just three characters, we could be
  2030. 1:11:40taking more characters than as an input.
  2031. 1:11:43And that could further improve the loss.
  2032. 1:11:46Okay, so I changed the code slightly, so
  2033. 1:11:48we have here 200,000 steps of the
  2034. 1:11:50optimization. And in the first 100,000,
  2035. 1:11:52we're using a learning rate of 0.1. And
  2036. 1:11:54then in the next 100,000, we're using a
  2037. 1:11:56learning rate of 0.01.
  2038. 1:11:58This is the loss that I achieve.
  2039. 1:12:00And these are the performance on the
  2040. 1:12:01training and validation loss.
  2041. 1:12:04And in particular, the best validation
  2042. 1:12:05loss I've been able to obtain in the
  2043. 1:12:07last 30 minutes or so is 2.17.
  2044. 1:12:10So, now I invite you to beat this
  2045. 1:12:12number. And you have quite a few knobs
  2046. 1:12:14available to you to, I think, surpass
  2047. 1:12:15this number.
  2048. 1:12:17So, number one, you can of course change
  2049. 1:12:18the number of neurons in the hidden
  2050. 1:12:20layer of this model.
  2051. 1:12:21You can change the dimensionality of the
  2052. 1:12:23embedding uh lookup table.
  2053. 1:12:25You can change the number of characters
  2054. 1:12:26that are feeding in as an input um as
  2055. 1:12:29the context into this model.
  2056. 1:12:32And then, of course, you can change the
  2057. 1:12:33details of the optimization. How long
  2058. 1:12:35are we running? What is the learning
  2059. 1:12:37rate? How does it change over time?
  2060. 1:12:39Uh how does it decay?
  2061. 1:12:41Uh you can change the batch size, and
  2062. 1:12:42you may be able to actually achieve a
  2063. 1:12:44much better convergence speed in terms
  2064. 1:12:46of uh how many seconds or minutes it
  2065. 1:12:48takes to train the model and uh get uh
  2066. 1:12:51your result in terms of really good um
  2067. 1:12:54loss.
  2068. 1:12:55And then of course, I actually invite
  2069. 1:12:57you to read this paper. It is 19 pages,
  2070. 1:12:59but at this point you should actually be
  2071. 1:13:00able to read a good chunk of this paper
  2072. 1:13:03and understand
  2073. 1:13:04uh pretty good chunks of it.
  2074. 1:13:06And this paper also has quite a few
  2075. 1:13:08ideas for improvements that you can play
  2076. 1:13:09with.
  2077. 1:13:11So, all of those are knobs available to
  2078. 1:13:12you, and you should be able to beat this
  2079. 1:13:14number. I'm leaving that as an exercise
  2080. 1:13:16to the reader, and uh that's it for now,
  2081. 1:13:18and I'll see you next time.
  2082. 1:13:24Before we wrap up, I also wanted to show
  2083. 1:13:25how you would sample from the model.
  2084. 1:13:28So, we're going to generate 20 samples.
  2085. 1:13:31At first, we begin with all dots, so
  2086. 1:13:33that's the context. And then until we
  2087. 1:13:36generate the zeroth character again,
  2088. 1:13:40we're going to embed the current context
  2089. 1:13:43using the embedding table C.
  2090. 1:13:46Now, usually uh here, the first
  2091. 1:13:48dimension was the size of the training
  2092. 1:13:50set, but here we're only working with a
  2093. 1:13:51single example that we're generating, so
  2094. 1:13:53this is just um dimension one, just for
  2095. 1:13:55simplicity.
  2096. 1:13:57Um and so this embedding then gets
  2097. 1:14:00projected into the hidden state. You get
  2098. 1:14:02the logits. Now, we calculate the
  2099. 1:14:04probabilities. For that, you can use
  2100. 1:14:06f.softmax
  2101. 1:14:08um of logits, and that just basically
  2102. 1:14:10exponentiates the logits and makes them
  2103. 1:14:12sum to one.
  2104. 1:14:13And similar to cross entropy, it is
  2105. 1:14:15careful that there's no overflows.
  2106. 1:14:18Once we have the probabilities, we
  2107. 1:14:19sample from them using torch.multinomial
  2108. 1:14:22to get our next index, and then we shift
  2109. 1:14:24the context window to append the index
  2110. 1:14:26and record it.
  2111. 1:14:28And then we can just um decode all the
  2112. 1:14:30integers to strings and print them out.
  2113. 1:14:33And so these are some example samples,
  2114. 1:14:35and you can see that the model now works
  2115. 1:14:36much better. So, the words here are much
  2116. 1:14:39more word-like or name-like. So, we have
  2117. 1:14:41things like ham, um
  2118. 1:14:44joes,
  2119. 1:14:46uh lela,
  2120. 1:14:48you know, it's starting to sound a
  2121. 1:14:49little bit more name-like. So, we're
  2122. 1:14:51definitely making progress, uh, but we
  2123. 1:14:52can still improve on this model quite a
  2124. 1:14:54lot.
  2125. 1:14:55Okay, sorry. There's some bonus content.
  2126. 1:14:57I wanted to mention that I want to make
  2127. 1:14:59these notebooks more accessible. And so,
  2128. 1:15:01I don't want you to have to, like,
  2129. 1:15:03install Jupyter notebooks and torch and
  2130. 1:15:04everything else. So, I will be sharing a
  2131. 1:15:06link to a Google Colab.
  2132. 1:15:09And the Google Colab will look like a
  2133. 1:15:10notebook in your browser. And you can
  2134. 1:15:13just go to a URL, and you'll be able to
  2135. 1:15:15execute all the code that you saw in the
  2136. 1:15:17Google Colab. And so, this is me
  2137. 1:15:20executing the code in this lecture, and
  2138. 1:15:22I shortened it a little bit.
  2139. 1:15:23Uh, but basically, you you're able to
  2140. 1:15:25train the exact same network, and then
  2141. 1:15:27plot and sample from the model, and
  2142. 1:15:29everything is ready for you to, like,
  2143. 1:15:30tinker with the numbers right there in
  2144. 1:15:32your browser. No installation necessary.
  2145. 1:15:34Um, so, I just wanted to point that out,
  2146. 1:15:36and the link to this will be in the
  2147. 1:15:37video description.

About this transcript

This page contains the full transcript of Building makemore Part 2: MLP by Andrej Karpathy, generated from the public captions YouTube serves with the video. The transcript has 12,731 words across 2,147 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.

What you can do with it

Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to translate the transcript into 100+ languages.

Free YouTube transcript tool

YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, translation to 100+ languages, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.