from tqdm import tqdm
import numpy
import torch
import torch.nn.functional as F
import matplotlib.pyplot as plot
import random
import math
Building Makemore MLP Exercise
Imports
= torch.Generator().manual_seed(42) g
Setup
= open('../data/names.txt', 'r').read().splitlines()
words 8] words[:
['emma', 'olivia', 'ava', 'isabella', 'sophia', 'charlotte', 'mia', 'amelia']
len(words)
32033
def generate_training_set(words, block_size, print_disabled=False):
= sorted(list(set(''.join(words))))
chars = {s: i+1 for i, s in enumerate(chars)}
stoi '.'] = 0
stoi[= {i:s for s, i in stoi.items()}
itos
= [], []
X, Y
for w in words:
if print_disabled: print(w)
= [0] * block_size
context for ch in w + '.':
= stoi[ch]
ix
X.append(context)
Y.append(ix)if print_disabled: print(''.join(itos[i] for i in context), '--->', itos[ix])
= context[1:] + [ix] # crop and append
context
= torch.tensor(X)
X = torch.tensor(Y)
Y return X, Y
= generate_training_set(words, 3) X, Y
X.shape, Y.shape
(torch.Size([228146, 3]), torch.Size([228146]))
def generate_train_valid_test_split(words, block_size=3):
42)
random.seed(
random.shuffle(words)= int(0.8*len(words))
n1 = int(0.9*len(words))
n2
= generate_training_set(words[:n1], block_size)
Xtr, Ytr = generate_training_set(words[n1:n2], block_size)
Xdev, Ydev = generate_training_set(words[n2:], block_size)
Xte, Yte
return Xtr, Ytr, Xdev, Ydev, Xte, Yte
= generate_train_valid_test_split(words, block_size=3) Xtr, Ytr, Xdev, Ydev, Xte, Yte
Xtr.shape, Ytr.shape
(torch.Size([182625, 3]), torch.Size([182625]))
Xdev.shape, Ydev.shape
(torch.Size([22655, 3]), torch.Size([22655]))
Xte.shape, Yte.shape
(torch.Size([22866, 3]), torch.Size([22866]))
E01
Tune the hyperparameters of the training to beat the validation loss of 2.2
no of neurons in the hidden layer
embedding size
no of characters
epochs
learning rate; change/decay it over the epochs
batch size
def evaluate_loss(parameters, X, Y, block_size=3, embedding_size=10):
= parameters
C, W1, b1, W2, b2 = C[X]
emb = torch.tanh(emb.view(-1, block_size * embedding_size) @ W1 + b1)
h = h @ W2 + b2
logits = F.cross_entropy(logits, Y)
loss return loss
def _regularization_loss(parameters, lambdas):
= parameters[0]
C = parameters[1]
W1 = parameters[3]
W2
return lambdas[0]*(C**2).mean() + lambdas[1]*(W1**2).mean() + lambdas[2]*(W2**2).mean()
def train(X,
Y,
epochs, =3,
block_size=10,
embedding_size=300,
hidden_neuron=32,
bs=0.1,
lr=[],
parameters= [0, 0, 0],
lambdas =True,
enable_print=10000
print_at_every_nth_epoch
):
if not parameters:
= torch.randn((27, embedding_size), generator=g)
C = torch.randn((block_size * embedding_size, hidden_neuron), generator=g)
W1 = torch.randn(hidden_neuron, generator=g)
b1 = torch.randn((hidden_neuron, 27), generator=g)
W2 = torch.randn(27, generator=g)
b2 = [C, W1, b1, W2, b2]
parameters
for p in parameters: p.requires_grad = True
for epoch in tqdm(range(epochs)):
= torch.randint(0, X.shape[0], (bs, ))
ix
= evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
loss = _regularization_loss(parameters, lambdas)
regularization_loss += regularization_loss
loss
for p in parameters:
= None
p.grad
loss.backward()
for p in parameters:
+= - lr * p.grad
p.data
if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
return parameters, loss.item()
1st try
= train(Xtr, Ytr, 100_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=10_000) parameters, loss
0%| | 4/100000 [00:00<1:36:08, 17.34it/s] 10%|████████████████████▊ | 10003/100000 [08:59<1:19:38, 18.83it/s] 20%|█████████████████████████████████████████▌ | 20003/100000 [17:47<1:10:41, 18.86it/s] 30%|██████████████████████████████████████████████████████████████▍ | 30004/100000 [26:35<1:02:27, 18.68it/s] 40%|████████████████████████████████████████████████████████████████████████████████████ | 40005/100000 [51:47<49:39, 20.13it/s] 50%|█████████████████████████████████████████████████████████████████████████████████████████████████████████ | 50005/100000 [59:59<40:59, 20.32it/s] 60%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▊ | 60005/100000 [1:08:05<32:22, 20.58it/s] 70%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▌ | 70003/100000 [1:16:09<24:24, 20.49it/s] 80%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍ | 80005/100000 [1:24:15<16:17, 20.45it/s] 90%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ | 90003/100000 [1:32:20<08:05, 20.60it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100000/100000 [1:40:25<00:00, 16.60it/s]
0 17.771562576293945
10000 2.3100812435150146
20000 2.236790418624878
30000 2.1661746501922607
40000 2.145174980163574
50000 2.1430141925811768
60000 2.1360814571380615
70000 2.1251132488250732
80000 2.1180062294006348
90000 2.1188645362854004
=3, embedding_size=50) loss, evaluate_loss(parameters, Xdev, Ydev, block_size
(2.101083755493164, tensor(2.1680, grad_fn=<NllLossBackward0>))
2nd try
= train(Xtr, Ytr, 300_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.01, parameters=parameters, enable_print=False) parameters, loss
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 300000/300000 [4:07:07<00:00, 20.23it/s]
=3, embedding_size=50) loss, evaluate_loss(parameters, Xdev, Ydev, block_size
(2.1126763820648193, tensor(2.1603, grad_fn=<NllLossBackward0>))
3rd try
= train(Xtr, Ytr, 10_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=1, parameters=parameters, enable_print=True, print_at_every_nth_epoch=1000) parameters, loss
0%| | 4/10000 [00:00<08:24, 19.80it/s] 10%|█████████████████████▎ | 1003/10000 [00:49<07:26, 20.17it/s] 20%|██████████████████████████████████████████▍ | 2003/10000 [01:39<06:38, 20.07it/s] 30%|███████████████████████████████████████████████████████████████▋ | 3005/10000 [02:28<05:46, 20.21it/s] 40%|████████████████████████████████████████████████████████████████████████████████████▉ | 4004/10000 [03:18<04:57, 20.19it/s] 50%|██████████████████████████████████████████████████████████████████████████████████████████████████████████ | 5003/10000 [04:07<04:09, 19.99it/s] 60%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎ | 6003/10000 [04:56<03:16, 20.39it/s] 70%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍ | 7004/10000 [05:46<02:29, 20.07it/s] 80%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 8003/10000 [06:35<01:37, 20.42it/s] 90%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▉ | 9004/10000 [07:24<00:48, 20.40it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 10000/10000 [08:13<00:00, 20.24it/s]
0 2.1231608390808105
1000 2.1130003929138184
2000 2.1557743549346924
3000 2.136502265930176
4000 2.142028331756592
5000 2.1329710483551025
6000 2.1422650814056396
7000 2.148254632949829
8000 2.13120698928833
9000 2.1335060596466064
=3, embedding_size=50) loss, evaluate_loss(parameters, Xdev, Ydev, block_size
(2.190765142440796, tensor(2.1794, grad_fn=<NllLossBackward0>))
4th try
= train(Xtr, Ytr, 10_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, parameters=parameters, enable_print=True, print_at_every_nth_epoch=1000) parameters, loss
0%| | 5/10000 [00:00<08:24, 19.82it/s] 10%|█████████████████████▎ | 1005/10000 [00:49<07:20, 20.43it/s] 20%|██████████████████████████████████████████▌ | 2005/10000 [01:38<06:34, 20.29it/s] 30%|███████████████████████████████████████████████████████████████▋ | 3002/10000 [02:27<05:44, 20.32it/s] 40%|████████████████████████████████████████████████████████████████████████████████████▉ | 4004/10000 [03:17<04:58, 20.06it/s] 50%|██████████████████████████████████████████████████████████████████████████████████████████████████████████ | 5003/10000 [04:07<04:03, 20.56it/s] 60%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎ | 6003/10000 [04:56<03:15, 20.48it/s] 70%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍ | 7003/10000 [05:45<02:27, 20.34it/s] 80%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 8003/10000 [06:35<01:39, 20.12it/s] 90%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▉ | 9004/10000 [07:24<00:47, 21.10it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 10000/10000 [08:13<00:00, 20.25it/s]
0 2.141832113265991
1000 2.091341495513916
2000 2.089855909347534
3000 2.079847574234009
4000 2.081550121307373
5000 2.096187114715576
6000 2.0649683475494385
7000 2.0917818546295166
8000 2.0842249393463135
9000 2.0907206535339355
=3, embedding_size=50) loss, evaluate_loss(parameters, Xdev, Ydev, block_size
(2.090895414352417, tensor(2.1472, grad_fn=<NllLossBackward0>))
5th try
= train(Xtr, Ytr, 100_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.01, parameters=parameters, enable_print=True, print_at_every_nth_epoch=10_000) parameters, loss
0%| | 5/100000 [00:00<1:23:39, 19.92it/s] 10%|████████████████████▊ | 10004/100000 [08:12<1:14:28, 20.14it/s] 20%|█████████████████████████████████████████▌ | 20005/100000 [16:24<1:05:10, 20.45it/s] 30%|███████████████████████████████████████████████████████████████ | 30005/100000 [24:34<57:04, 20.44it/s] 40%|████████████████████████████████████████████████████████████████████████████████████ | 40002/100000 [32:45<49:26, 20.22it/s] 50%|█████████████████████████████████████████████████████████████████████████████████████████████████████████ | 50005/100000 [40:56<41:04, 20.28it/s] 60%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | 60004/100000 [49:07<32:55, 20.25it/s] 70%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | 70005/100000 [57:18<24:55, 20.05it/s] 80%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍ | 80004/100000 [1:05:29<16:18, 20.43it/s] 90%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ | 90003/100000 [1:13:40<08:24, 19.82it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100000/100000 [1:21:51<00:00, 20.36it/s]
0 2.0824689865112305
10000 2.0864546298980713
20000 2.0779430866241455
30000 2.0869970321655273
40000 2.0827417373657227
50000 2.1026248931884766
60000 2.0927939414978027
70000 2.0810811519622803
80000 2.095008611679077
90000 2.0829107761383057
=3, embedding_size=50) loss, evaluate_loss(parameters, Xdev, Ydev, block_size
(2.0964395999908447, tensor(2.1466, grad_fn=<NllLossBackward0>))
Test Loss
=3, embedding_size=50) loss, evaluate_loss(parameters, Xte, Yte, block_size
(2.0964395999908447, tensor(2.1446, grad_fn=<NllLossBackward0>))
E02
- Weight Initialization
What is the loss you’d get if the predicted probabilities at initialization were perfectly uniform? What loss do we achieve?
Can you tune the initialization to get a starting loss that is much more similar to (1)?
Answer to (1)
If the predicted probabilities were uniform then the probabilities would have been 1/27
of each character prediction
And we would have take the log of the probability which would have been
1/27).log() torch.tensor(
tensor(-3.2958)
to the get the loss it would have been
- torch.tensor(1/27).log()
tensor(3.2958)
No we sum up the losses and divide by the count, (n * (3.2958))/n
which is equal to 3.2958
Lets see the initial loss when we train the model with current initialization
= train(Xtr, Ytr, 10, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=1) parameters, loss
40%|███████████████████████████████████████████████████████████████████████████████████████▏ | 4/10 [00:00<00:00, 19.51it/s] 90%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ | 9/10 [00:00<00:00, 19.94it/s]100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 10/10 [00:00<00:00, 19.88it/s]
0 18.27653694152832
1 17.431493759155273
2 16.35456085205078
3 16.05698585510254
4 15.747321128845215
5 15.394339561462402
6 15.205368995666504
7 14.835010528564453
8 14.528204917907715
9 14.28638744354248
The initial loss is 18.98
which is high comparative to 3.2958
Lets see the probabilities of the output
= train(Xtr, Ytr, 1, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=1) parameters, loss
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 19.37it/s]
0 18.204519271850586
def compute_probs(parameters, X, block_size=3, embedding_size=50):
= parameters
C, W1, b1, W2, b2 = C[X]
emb = torch.tanh(emb.view(-1, block_size * embedding_size) @ W1 + b1)
h = h @ W2 + b2
logits return F.softmax(logits, dim=1)
compute_probs(parameters, Xtr)
tensor([[2.9970e-06, 2.3740e-08, 2.1316e-10, ..., 1.2648e-13, 8.5370e-04,
8.7376e-08],
[1.7422e-05, 1.1364e-09, 1.3196e-09, ..., 3.6301e-13, 3.8613e-06,
2.4013e-07],
[5.8833e-05, 5.7244e-06, 1.0801e-02, ..., 9.2642e-07, 2.9683e-06,
2.4511e-06],
...,
[5.7658e-11, 1.4429e-09, 9.7899e-11, ..., 1.0416e-11, 8.2188e-09,
2.7279e-10],
[7.0990e-01, 8.7623e-12, 1.6534e-07, ..., 3.1374e-09, 3.6852e-06,
1.1986e-04],
[9.9999e-01, 1.0279e-07, 6.2436e-11, ..., 1.4053e-10, 6.7408e-14,
1.2024e-09]], grad_fn=<SoftmaxBackward0>)
Lets view a single row of probabilities
0] compute_probs(parameters, Xtr)[
tensor([2.9970e-06, 2.3740e-08, 2.1316e-10, 1.9171e-08, 3.7981e-04, 2.2313e-02,
1.3911e-17, 1.0186e-09, 9.7561e-10, 5.6293e-12, 8.8295e-09, 3.4877e-09,
1.2439e-08, 7.9825e-14, 7.3846e-04, 1.0648e-11, 5.4885e-08, 3.0407e-13,
2.0024e-02, 9.5325e-01, 1.7357e-03, 2.2441e-08, 6.8103e-04, 2.4685e-05,
1.2648e-13, 8.5370e-04, 8.7376e-08], grad_fn=<SelectBackward0>)
to get a uniform probability, I think we need to have all logits as equal so that we can get probability of each as 1/27
Try 1
lets try uniform wieght initialization
def train_v2(X,
Y,
epochs, =3,
block_size=10,
embedding_size=300,
hidden_neuron=32,
bs=0.1,
lr=[],
parameters= [0, 0, 0],
lambdas =True,
enable_print=10000
print_at_every_nth_epoch
):
if not parameters:
= torch.rand((27, embedding_size), generator=g)
C = torch.rand((block_size * embedding_size, hidden_neuron), generator=g)
W1 = torch.rand(hidden_neuron, generator=g)
b1 = torch.rand((hidden_neuron, 27), generator=g)
W2 = torch.rand(27, generator=g)
b2 = [C, W1, b1, W2, b2]
parameters
for p in parameters: p.requires_grad = True
for epoch in tqdm(range(epochs)):
= torch.randint(0, X.shape[0], (bs, ))
ix
= evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
loss = _regularization_loss(parameters, lambdas)
regularization_loss += regularization_loss
loss
for p in parameters:
= None
p.grad
loss.backward()
for p in parameters:
+= - lr * p.grad
p.data
if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
return parameters, loss.item()
= train_v2(Xtr, Ytr, 1, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=1) parameters, loss
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 20.50it/s]
0 7.901321887969971
With uniform weight initialization the intial loss (6.422
) obtained is less than of normal weight initialization (17.7
)
Try 2
Lets initialize the last layers of weights and biases as zero.
def train_v3(X,
Y,
epochs, =3,
block_size=10,
embedding_size=300,
hidden_neuron=32,
bs=0.1,
lr=[],
parameters= [0, 0, 0],
lambdas =True,
enable_print=10000
print_at_every_nth_epoch
):
if not parameters:
= torch.rand((27, embedding_size), generator=g)
C = torch.rand((block_size * embedding_size, hidden_neuron), generator=g)
W1 = torch.rand(hidden_neuron)
b1 = torch.zeros((hidden_neuron, 27))
W2 = torch.zeros(27)
b2 = [C, W1, b1, W2, b2]
parameters
for p in parameters: p.requires_grad = True
for epoch in tqdm(range(epochs)):
= torch.randint(0, X.shape[0], (bs, ))
ix
= evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
loss = _regularization_loss(parameters, lambdas)
regularization_loss += regularization_loss
loss
for p in parameters:
= None
p.grad
loss.backward()
for p in parameters:
+= - lr * p.grad
p.data
if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
return parameters, loss.item()
= train_v3(Xtr, Ytr, 1, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=1) parameters, loss
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 21.42it/s]
0 3.295837163925171
The initial loss is now 3.2958
(which we wanted).
Lets see how well it trains now
= train_v3(Xtr, Ytr, 30_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=10_000) parameters, loss
0%| | 0/30000 [00:00<?, ?it/s] 33%|██████████████████████████████████████████████████████████████████████▎ | 10005/30000 [07:59<15:49, 21.07it/s] 67%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 20004/30000 [15:58<08:02, 20.72it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30000/30000 [23:57<00:00, 20.86it/s]
0 3.295837163925171
10000 2.8236281871795654
20000 2.8253743648529053
loss
2.8211419582366943
Try 3
As we can see the losses are not decreasing faster, lets not initialize weight to zero but close to zero and see …
def train_v4(X,
Y,
epochs, =3,
block_size=10,
embedding_size=300,
hidden_neuron=32,
bs=0.1,
lr=[],
parameters= [0, 0, 0],
lambdas =True,
enable_print=10000
print_at_every_nth_epoch
):
if not parameters:
= torch.rand((27, embedding_size), generator=g)
C = torch.rand((block_size * embedding_size, hidden_neuron), generator=g)
W1 = torch.rand(hidden_neuron)
b1 = torch.rand((hidden_neuron, 27)) * 0.01 # close to zero
W2 = torch.zeros(27)
b2 = [C, W1, b1, W2, b2]
parameters
for p in parameters: p.requires_grad = True
for epoch in tqdm(range(epochs)):
= torch.randint(0, X.shape[0], (bs, ))
ix
= evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
loss = _regularization_loss(parameters, lambdas)
regularization_loss += regularization_loss
loss
for p in parameters:
= None
p.grad
loss.backward()
for p in parameters:
+= - lr * p.grad
p.data
if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
return parameters, loss.item()
= train_v4(Xtr, Ytr, 30_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=10_000) parameters, loss
0%| | 3/30000 [00:00<23:51, 20.95it/s] 33%|██████████████████████████████████████████████████████████████████████▎ | 10005/30000 [08:00<15:48, 21.08it/s] 67%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 20003/30000 [16:00<08:02, 20.73it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30000/30000 [24:00<00:00, 20.82it/s]
0 3.29825496673584
10000 2.8132688999176025
20000 2.826235294342041
Try 4
Lets not try to uniformly initiate all the weights but only the last layers and the rest we can keep as normal initialized
def train_v5(X,
Y,
epochs, =3,
block_size=10,
embedding_size=300,
hidden_neuron=32,
bs=0.1,
lr=[],
parameters= [0, 0, 0],
lambdas =True,
enable_print=10000
print_at_every_nth_epoch
):
if not parameters:
= torch.randn((27, embedding_size), generator=g)
C = torch.randn((block_size * embedding_size, hidden_neuron), generator=g)
W1 = torch.randn(hidden_neuron)
b1 = torch.rand((hidden_neuron, 27)) * 0.01 # close to zero
W2 = torch.zeros(27)
b2 = [C, W1, b1, W2, b2]
parameters
for p in parameters: p.requires_grad = True
for epoch in tqdm(range(epochs)):
= torch.randint(0, X.shape[0], (bs, ))
ix
= evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
loss = _regularization_loss(parameters, lambdas)
regularization_loss += regularization_loss
loss
for p in parameters:
= None
p.grad
loss.backward()
for p in parameters:
+= - lr * p.grad
p.data
if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
return parameters, loss.item()
= train_v5(Xtr, Ytr, 30_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=10_000) parameters, loss
0%| | 3/30000 [00:00<24:39, 20.28it/s] 33%|██████████████████████████████████████████████████████████████████████▎ | 10003/30000 [08:13<16:37, 20.04it/s] 67%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 20005/30000 [16:28<08:11, 20.33it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30000/30000 [24:42<00:00, 20.24it/s]
0 3.2991583347320557
10000 2.175701379776001
20000 2.1791296005249023
The losses are reducing now. Lets train for 100_000 and check
= train_v5(Xtr, Ytr, 200_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=10_000) parameters, loss
0%| | 4/200000 [00:00<3:01:12, 18.39it/s] 5%|██████████▍ | 10004/200000 [08:33<2:43:37, 19.35it/s] 10%|████████████████████▊ | 20002/200000 [16:56<2:28:24, 20.21it/s] 15%|███████████████████████████████▏ | 30004/200000 [25:11<2:19:51, 20.26it/s] 20%|█████████████████████████████████████████▌ | 40005/200000 [33:26<2:12:10, 20.17it/s] 25%|████████████████████████████████████████████████████ | 50003/200000 [41:41<2:04:02, 20.15it/s] 30%|██████████████████████████████████████████████████████████████▍ | 60003/200000 [49:56<1:55:31, 20.20it/s] 35%|████████████████████████████████████████████████████████████████████████▊ | 70003/200000 [58:23<1:49:37, 19.76it/s] 40%|██████████████████████████████████████████████████████████████████████████████████▍ | 80004/200000 [1:06:54<1:40:25, 19.92it/s] 45%|████████████████████████████████████████████████████████████████████████████████████████████▋ | 90003/200000 [1:15:17<1:30:28, 20.26it/s] 50%|██████████████████████████████████████████████████████████████████████████████████████████████████████▌ | 100005/200000 [1:23:47<1:22:01, 20.32it/s] 55%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████▊ | 110003/200000 [1:32:17<1:16:43, 19.55it/s] 60%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | 120004/200000 [1:40:49<1:06:07, 20.16it/s] 65%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎ | 130003/200000 [1:49:13<1:00:42, 19.22it/s] 70%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▉ | 140004/200000 [1:57:51<49:56, 20.03it/s] 75%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎ | 150004/200000 [2:06:14<40:59, 20.32it/s] 80%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▌ | 160004/200000 [2:14:38<32:47, 20.33it/s] 85%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▉ | 170003/200000 [2:22:59<25:09, 19.87it/s] 90%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎ | 180003/200000 [2:31:25<16:36, 20.06it/s] 95%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 190005/200000 [2:39:51<08:15, 20.16it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 200000/200000 [2:48:07<00:00, 19.83it/s]
0 3.292088270187378
10000 2.184924364089966
20000 2.168978452682495
30000 2.129955768585205
40000 2.1225433349609375
50000 2.1296749114990234
60000 2.1202642917633057
70000 2.131760358810425
80000 2.1080808639526367
90000 2.1024396419525146
100000 2.0863888263702393
110000 2.0778346061706543
120000 2.084108591079712
130000 2.085371255874634
140000 2.084995985031128
150000 2.0778989791870117
160000 2.0879881381988525
170000 2.0799689292907715
180000 2.0731143951416016
190000 2.0831706523895264
loss
2.0791072845458984
The losses are getting reduced faster!
=3, embedding_size=50) evaluate_loss(parameters, Xdev, Ydev, block_size
tensor(2.1343, grad_fn=<NllLossBackward0>)
E03
Read the Bengio et al 2003 paper, implement and try any idea from the paper. Did it work?
In the paper there is a mention of direct connection from the word features to output.
Lets implement the direct connection from embedding to output and see the results
Direct connection from embedding to output
= torch.randn((27, 50), generator=g) C
; C[X].view(-1, 150).shape C[X].shape
torch.Size([228146, 150])
def evaluate_loss_dir_conn(parameters, X, Y, block_size=3, embedding_size=10):
= parameters
C, W1, b1, W2, W3, b2 = C[X]
emb = torch.tanh(emb.view(-1, block_size * embedding_size) @ W1 + b1)
h = h @ W2 + b2 + C[X].view(-1, block_size * embedding_size) @ W3
logits = F.cross_entropy(logits, Y)
loss return loss
def train_dir_conn(X,
Y,
epochs, =3,
block_size=10,
embedding_size=300,
hidden_neuron=32,
bs=0.1,
lr=[],
parameters= [0, 0, 0],
lambdas =True,
enable_print=10000
print_at_every_nth_epoch
):
if not parameters:
= torch.randn((27, embedding_size), generator=g)
C = torch.randn((block_size * embedding_size, hidden_neuron), generator=g)
W1 = torch.randn(hidden_neuron)
b1 = torch.rand((hidden_neuron, 27)) * 0.01 # close to zero
W2 = torch.rand((block_size * embedding_size, 27)) * 0.01 # close to zero
W3 = torch.zeros(27)
b2 = [C, W1, b1, W2, W3, b2]
parameters
for p in parameters: p.requires_grad = True
for epoch in tqdm(range(epochs)):
= torch.randint(0, X.shape[0], (bs, ))
ix
= evaluate_loss_dir_conn(parameters, X[ix], Y[ix], block_size, embedding_size)
loss = _regularization_loss(parameters, lambdas)
regularization_loss += regularization_loss
loss
for p in parameters:
= None
p.grad
loss.backward()
for p in parameters:
+= - lr * p.grad
p.data
if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
return parameters, loss.item()
= train_dir_conn(Xtr, Ytr, 100_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.1, enable_print=True, print_at_every_nth_epoch=10_000) parameters, loss
0%| | 2/100000 [00:00<2:09:58, 12.82it/s] 10%|████████████████████▊ | 10002/100000 [11:49<1:43:19, 14.52it/s] 20%|█████████████████████████████████████████▌ | 20002/100000 [23:20<1:35:02, 14.03it/s] 30%|██████████████████████████████████████████████████████████████▍ | 30002/100000 [35:10<1:21:53, 14.24it/s] 40%|███████████████████████████████████████████████████████████████████████████████████▏ | 40002/100000 [46:47<1:09:35, 14.37it/s] 50%|█████████████████████████████████████████████████████████████████████████████████████████████████████████ | 50002/100000 [58:30<57:41, 14.44it/s] 60%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▊ | 60002/100000 [1:10:02<46:07, 14.45it/s] 70%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▌ | 70002/100000 [1:21:52<35:18, 14.16it/s] 80%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍ | 80002/100000 [1:33:29<22:48, 14.61it/s] 90%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ | 90002/100000 [1:44:59<11:26, 14.57it/s]100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100000/100000 [1:56:30<00:00, 14.31it/s]
0 3.29349684715271
10000 2.151575803756714
20000 2.122009515762329
30000 2.1049506664276123
40000 2.107222318649292
50000 2.098936080932617
60000 2.0728397369384766
70000 2.1058623790740967
80000 2.0761640071868896
90000 2.0695760250091553
loss
2.0880658626556396
=3, embedding_size=50) evaluate_loss_dir_conn(parameters, Xdev, Ydev, block_size
tensor(2.1274, grad_fn=<NllLossBackward0>)
=3, embedding_size=50) evaluate_loss_dir_conn(parameters, Xte, Yte, block_size
tensor(2.1239, grad_fn=<NllLossBackward0>)
The loss decreased by lot with this direct connection and the above method of weight initialization