from tqdm import tqdm
import numpy
import torch
import torch.nn.functional as F
import matplotlib.pyplot as plot
import random
import mathBuilding Makemore MLP Exercise
Imports
g = torch.Generator().manual_seed(42)Setup
words = open('../data/names.txt', 'r').read().splitlines()
words[:8]['emma', 'olivia', 'ava', 'isabella', 'sophia', 'charlotte', 'mia', 'amelia']len(words)32033def generate_training_set(words, block_size, print_disabled=False):
    
    chars = sorted(list(set(''.join(words))))
    stoi = {s: i+1 for i, s in enumerate(chars)}
    stoi['.'] = 0
    itos = {i:s for s, i in stoi.items()}
    
    X, Y = [], []
    
    for w in words:
        if print_disabled: print(w)
        
        context = [0] * block_size
        for ch in w + '.':
            ix = stoi[ch]
            X.append(context)
            Y.append(ix)
            if print_disabled: print(''.join(itos[i] for i in context), '--->', itos[ix])
            context = context[1:] + [ix] # crop and append
            
    X = torch.tensor(X)
    Y = torch.tensor(Y)
    return X, YX, Y = generate_training_set(words, 3)X.shape, Y.shape(torch.Size([228146, 3]), torch.Size([228146]))def generate_train_valid_test_split(words, block_size=3):
    random.seed(42)
    random.shuffle(words)
    n1 = int(0.8*len(words))
    n2 = int(0.9*len(words))
    Xtr, Ytr = generate_training_set(words[:n1], block_size)
    Xdev, Ydev = generate_training_set(words[n1:n2], block_size)
    Xte, Yte = generate_training_set(words[n2:], block_size)
    
    return Xtr, Ytr, Xdev, Ydev, Xte, YteXtr, Ytr, Xdev, Ydev, Xte, Yte = generate_train_valid_test_split(words, block_size=3)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):
    C, W1, b1, W2, b2 = parameters
    emb = C[X]
    h = torch.tanh(emb.view(-1, block_size * embedding_size) @ W1 + b1)
    logits = h @ W2 + b2
    loss = F.cross_entropy(logits, Y)
    return lossdef _regularization_loss(parameters, lambdas):
    C = parameters[0]
    W1 = parameters[1]
    W2 = parameters[3]
    
    return lambdas[0]*(C**2).mean() + lambdas[1]*(W1**2).mean() + lambdas[2]*(W2**2).mean()def train(X, 
          Y, 
          epochs, 
          block_size=3, 
          embedding_size=10, 
          hidden_neuron=300, 
          bs=32, 
          lr=0.1, 
          parameters=[], 
          lambdas = [0, 0, 0],
          enable_print=True,
          print_at_every_nth_epoch=10000
         ):
    
    if not parameters:
        C = torch.randn((27, embedding_size), generator=g)
        W1 = torch.randn((block_size * embedding_size, hidden_neuron), generator=g)
        b1 = torch.randn(hidden_neuron, generator=g)
        W2 = torch.randn((hidden_neuron, 27), generator=g)
        b2 = torch.randn(27, generator=g)
        parameters = [C, W1, b1, W2, b2]
    
    for p in parameters: p.requires_grad = True 
        
    for epoch in tqdm(range(epochs)):
            
        ix = torch.randint(0, X.shape[0], (bs, ))
        loss = evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
        regularization_loss = _regularization_loss(parameters, lambdas)
        loss += regularization_loss
        for p in parameters:
            p.grad= None
        loss.backward()
        for p in parameters:
            p.data += - lr * p.grad
        if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
    
    return parameters, loss.item()1st try
parameters, loss = 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)  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.1188645362854004loss, evaluate_loss(parameters, Xdev, Ydev, block_size=3, embedding_size=50)(2.101083755493164, tensor(2.1680, grad_fn=<NllLossBackward0>))2nd try
parameters, loss = train(Xtr, Ytr, 300_000, block_size=3, embedding_size=50, hidden_neuron=100, bs=16384, lr=0.01, parameters=parameters, enable_print=False)100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 300000/300000 [4:07:07<00:00, 20.23it/s]loss, evaluate_loss(parameters, Xdev, Ydev, block_size=3, embedding_size=50)(2.1126763820648193, tensor(2.1603, grad_fn=<NllLossBackward0>))3rd try
parameters, loss = 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)  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.1335060596466064loss, evaluate_loss(parameters, Xdev, Ydev, block_size=3, embedding_size=50)(2.190765142440796, tensor(2.1794, grad_fn=<NllLossBackward0>))4th try
parameters, loss = 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)  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.0907206535339355loss, evaluate_loss(parameters, Xdev, Ydev, block_size=3, embedding_size=50)(2.090895414352417, tensor(2.1472, grad_fn=<NllLossBackward0>))5th try
parameters, loss = 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)  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.0829107761383057loss, evaluate_loss(parameters, Xdev, Ydev, block_size=3, embedding_size=50)(2.0964395999908447, tensor(2.1466, grad_fn=<NllLossBackward0>))Test Loss
loss, evaluate_loss(parameters, Xte, Yte, block_size=3, embedding_size=50)(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
torch.tensor(1/27).log()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
parameters, loss = 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) 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.28638744354248The initial loss is 18.98 which is high comparative to 3.2958
Lets see the probabilities of the output
parameters, loss = 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)100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 19.37it/s]0 18.204519271850586def compute_probs(parameters, X, block_size=3, embedding_size=50):
    C, W1, b1, W2, b2 = parameters
    emb = C[X]
    h = torch.tanh(emb.view(-1, block_size * embedding_size) @ W1 + b1)
    logits = h @ W2 + b2
    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
compute_probs(parameters, Xtr)[0]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, 
          block_size=3, 
          embedding_size=10, 
          hidden_neuron=300, 
          bs=32, 
          lr=0.1, 
          parameters=[], 
          lambdas = [0, 0, 0],
          enable_print=True,
          print_at_every_nth_epoch=10000
         ):
    
    if not parameters:
        C = torch.rand((27, embedding_size), generator=g)
        W1 = torch.rand((block_size * embedding_size, hidden_neuron), generator=g)
        b1 = torch.rand(hidden_neuron, generator=g)
        W2 = torch.rand((hidden_neuron, 27), generator=g)  
        b2 = torch.rand(27, generator=g)
        parameters = [C, W1, b1, W2, b2]
    
    for p in parameters: p.requires_grad = True 
        
    for epoch in tqdm(range(epochs)):
            
        ix = torch.randint(0, X.shape[0], (bs, ))
        loss = evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
        regularization_loss = _regularization_loss(parameters, lambdas)
        loss += regularization_loss
        for p in parameters:
            p.grad= None
        loss.backward()
        for p in parameters:
            p.data += - lr * p.grad
        if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
    
    return parameters, loss.item()parameters, loss = 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)100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 20.50it/s]0 7.901321887969971With 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, 
          block_size=3, 
          embedding_size=10, 
          hidden_neuron=300, 
          bs=32, 
          lr=0.1, 
          parameters=[], 
          lambdas = [0, 0, 0],
          enable_print=True,
          print_at_every_nth_epoch=10000
         ):
    
    if not parameters:
        C = torch.rand((27, embedding_size), generator=g)
        W1 = torch.rand((block_size * embedding_size, hidden_neuron), generator=g)
        b1 = torch.rand(hidden_neuron)
        W2 = torch.zeros((hidden_neuron, 27))
        b2 = torch.zeros(27)
        parameters = [C, W1, b1, W2, b2]
    
    for p in parameters: p.requires_grad = True 
        
    for epoch in tqdm(range(epochs)):
            
        ix = torch.randint(0, X.shape[0], (bs, ))
        loss = evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
        regularization_loss = _regularization_loss(parameters, lambdas)
        loss += regularization_loss
        for p in parameters:
            p.grad= None
        loss.backward()
        for p in parameters:
            p.data += - lr * p.grad
        if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
    
    return parameters, loss.item()parameters, loss = 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)100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 21.42it/s]0 3.295837163925171The initial loss is now 3.2958 (which we wanted).
Lets see how well it trains now
parameters, loss = 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)  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.8253743648529053loss2.8211419582366943Try 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, 
          block_size=3, 
          embedding_size=10, 
          hidden_neuron=300, 
          bs=32, 
          lr=0.1, 
          parameters=[], 
          lambdas = [0, 0, 0],
          enable_print=True,
          print_at_every_nth_epoch=10000
         ):
    
    if not parameters:
        C = torch.rand((27, embedding_size), generator=g)
        W1 = torch.rand((block_size * embedding_size, hidden_neuron), generator=g)
        b1 = torch.rand(hidden_neuron)
        W2 = torch.rand((hidden_neuron, 27)) * 0.01 # close to zero
        b2 = torch.zeros(27)
        parameters = [C, W1, b1, W2, b2]
    
    for p in parameters: p.requires_grad = True 
        
    for epoch in tqdm(range(epochs)):
            
        ix = torch.randint(0, X.shape[0], (bs, ))
        loss = evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
        regularization_loss = _regularization_loss(parameters, lambdas)
        loss += regularization_loss
        for p in parameters:
            p.grad= None
        loss.backward()
        for p in parameters:
            p.data += - lr * p.grad
        if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
    
    return parameters, loss.item()parameters, loss = 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)  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.826235294342041Try 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, 
          block_size=3, 
          embedding_size=10, 
          hidden_neuron=300, 
          bs=32, 
          lr=0.1, 
          parameters=[], 
          lambdas = [0, 0, 0],
          enable_print=True,
          print_at_every_nth_epoch=10000
         ):
    
    if not parameters:
        C = torch.randn((27, embedding_size), generator=g)
        W1 = torch.randn((block_size * embedding_size, hidden_neuron), generator=g)
        b1 = torch.randn(hidden_neuron)
        W2 = torch.rand((hidden_neuron, 27)) * 0.01 # close to zero
        b2 = torch.zeros(27)
        parameters = [C, W1, b1, W2, b2]
    
    for p in parameters: p.requires_grad = True 
        
    for epoch in tqdm(range(epochs)):
            
        ix = torch.randint(0, X.shape[0], (bs, ))
        loss = evaluate_loss(parameters, X[ix], Y[ix], block_size, embedding_size)
        regularization_loss = _regularization_loss(parameters, lambdas)
        loss += regularization_loss
        for p in parameters:
            p.grad= None
        loss.backward()
        for p in parameters:
            p.data += - lr * p.grad
        if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
    
    return parameters, loss.item()parameters, loss = 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)  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.1791296005249023The losses are reducing now. Lets train for 100_000 and check
parameters, loss = 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)  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.0831706523895264loss2.0791072845458984The losses are getting reduced faster!
evaluate_loss(parameters, Xdev, Ydev, block_size=3, embedding_size=50)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
C = torch.randn((27, 50), generator=g)C[X].shape; C[X].view(-1, 150).shapetorch.Size([228146, 150])def evaluate_loss_dir_conn(parameters, X, Y, block_size=3, embedding_size=10):
    C, W1, b1, W2, W3, b2 = parameters
    emb = C[X]
    h = torch.tanh(emb.view(-1, block_size * embedding_size) @ W1 + b1)
    logits = h @ W2 + b2 + C[X].view(-1, block_size * embedding_size) @ W3
    loss = F.cross_entropy(logits, Y)
    return lossdef train_dir_conn(X, 
          Y, 
          epochs, 
          block_size=3, 
          embedding_size=10, 
          hidden_neuron=300, 
          bs=32, 
          lr=0.1, 
          parameters=[], 
          lambdas = [0, 0, 0],
          enable_print=True,
          print_at_every_nth_epoch=10000
         ):
    
    if not parameters:
        C = torch.randn((27, embedding_size), generator=g)
        W1 = torch.randn((block_size * embedding_size, hidden_neuron), generator=g)
        b1 = torch.randn(hidden_neuron)
        W2 = torch.rand((hidden_neuron, 27)) * 0.01 # close to zero
        W3 = torch.rand((block_size * embedding_size, 27)) * 0.01 # close to zero
        b2 = torch.zeros(27)
        parameters = [C, W1, b1, W2, W3, b2]
    
    for p in parameters: p.requires_grad = True 
        
    for epoch in tqdm(range(epochs)):
            
        ix = torch.randint(0, X.shape[0], (bs, ))
        loss = evaluate_loss_dir_conn(parameters, X[ix], Y[ix], block_size, embedding_size)
        regularization_loss = _regularization_loss(parameters, lambdas)
        loss += regularization_loss
        for p in parameters:
            p.grad= None
        loss.backward()
        for p in parameters:
            p.data += - lr * p.grad
        if enable_print and epoch % print_at_every_nth_epoch == 0: print(epoch, loss.item())
    
    return parameters, loss.item()parameters, loss = 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)  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.0695760250091553loss2.0880658626556396evaluate_loss_dir_conn(parameters, Xdev, Ydev, block_size=3, embedding_size=50)tensor(2.1274, grad_fn=<NllLossBackward0>)evaluate_loss_dir_conn(parameters, Xte, Yte, block_size=3, embedding_size=50)tensor(2.1239, grad_fn=<NllLossBackward0>)The loss decreased by lot with this direct connection and the above method of weight initialization