Skip to content

SET B - LECTURE NOTES


Introducing F.cross_entropy and why

Timestamp: 00:32:49

Now, after finding/calculating the logits value, instead of finding its exponent, then its probability distribution and then the negative log likelihood, we directly use PyTorch's F.cross_entropy() - it basically does all of those three steps, but is much more efficient.

Also it turns out, we've only been doing those steps for academic purposes and this won't happen during the actual training of NN. So using F.cross_entropy() is makes forward, backward pass much more efficient (and it saves memory of doing all those 3 steps cluttered into one)

So we have this

emb = C[X]
h = torch.tanh(emb.view(-1,6) @ W1 + b1)
logits = h @ W2 + b2
counts = logits.exp()
prob = counts / counts.sum(1, keepdims=True)
loss = - prob[torch.arange(32), Y].log().mean()
loss

to this

emb = C[X]
h = torch.tanh(emb.view(-1,6) @ W1 + b1)
logits = h @ W2 + b2
loss = F.cross_entropy(logits, Y)
loss

 

Implementing the training loop, overfitting one batch

Timestamp: 00:37:56

Now we implement the training of the neural net for that set of data (we just took the first five words for now)

Now, we already have the forward pass where we find the value of the loss

#forward pass
emb = C[X]
h = torch.tanh(emb.view(-1,6) @ W1 + b1)
logits = h @ W2 + b2
loss = F.cross_entropy(logits, Y)
loss

Then we calculate the backward pass

#backward pass
for p in parameters:
    p.grad = None
loss.backward()

Then we update the values

#update
for p in parameters:
    p.data += -0.1 * p.grad

Now, we want all of this in a loop, so putting them all together:

for _ in range(100):
    #forward pass
    emb = C[X]
    h = torch.tanh(emb.view(-1,6) @ W1 + b1)
    logits = h @ W2 + b2
    loss = F.cross_entropy(logits, Y)

    #backward pass
    for p in parameters:
        p.grad = None
    loss.backward()

    #update
    for p in parameters:
        p.data += -0.1 * p.grad

print(loss.item())

Finally, just before this entire process, we need to declare requires_grad parameter to True

for p in parameters:
    p.requires_grad = True

 

Training on the full dataset, minibatches

Timestamp: 00:41:25

First we try to train on the entire dataset, we noticed that the loss was decreasing but there was a fair bit of lag during each step. So the gradient is moving in the right direction but with much larger time and steps.

So we split them into mini batches, and only send those sets of batches for training and then we noticed that the training is almost instant.

This is a much better practice to follow, as "it is much better to take the approximate gradient and make more steps; then taking the exact gradient with fewer steps."

 

Finding a good initial learning rate

Timestamp: 00:45:40

So, in the update section we had just randomly guessed a value for the learning rate, we put it as 0.1. But there is also a way to determine the best possible learning rate.

So we set like a range of values like the exp of -3 to exp of 0 (that is basically 0.001 to 1) and then plot a graph to see where the value gets the lowest.

lre = torch.linspace(-3, 0, 1000)
lrs = 10**lre
#Remember to reset the parameters and only then run this

lri = []
lossi = []

for i in range(1000):

    #Minibatch
    xi = torch.randint(0, X.shape[0], (32,))

    #forward pass
    emb = C[X[xi]]
    h = torch.tanh(emb.view(-1,6) @ W1 + b1)
    logits = h @ W2 + b2
    loss = F.cross_entropy(logits, Y[xi])
    #print(loss.item())

    #backward pass
    for p in parameters:
        p.grad = None
    loss.backward()

    #update
    lr = lrs[i]
    for p in parameters:
        p.data += -0.1 * p.grad

    #keeping track
    lri.append(lre[i]) #We are taking the exponent of the learning rate for the x-axis
    lossi.append(loss.item())

print(loss.item())
plt.plot(lri, lossi)

So seeing the graph, we had closely to 0.1 (although it can be lower for me as I had a lower loss value to begin with) and so we continue with that value.

We kept on training till the loss reduces and once we feel like we were close, we made the learning rate more smaller (this is called learning rate decay).

So this is not exactly how we do in production, but the process is the same. We decide on a learning rate, train for a while and towards the end we do like a learning rate decay and then finally we get a trained neural net!

(Also, we have surpassed the loss value we got in the bigram model! So we've already made improvements)