import numpy
import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt # for making figures
Building a WaveNet
Starter Code
Imports
Read words
= open('../data/names.txt', 'r').read().splitlines()
words print(len(words))
print(max(len(w) for w in words))
print(words[:8])
32033
15
['emma', 'olivia', 'ava', 'isabella', 'sophia', 'charlotte', 'mia', 'amelia']
Build vocabulary of characters and mapping to/from integers
= 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 = len(itos)
vocab_size print(itos)
print(vocab_size)
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i', 10: 'j', 11: 'k', 12: 'l', 13: 'm', 14: 'n', 15: 'o', 16: 'p', 17: 'q', 18: 'r', 19: 's', 20: 't', 21: 'u', 22: 'v', 23: 'w', 24: 'x', 25: 'y', 26: 'z', 0: '.'}
27
Shuffle words
import random
42)
random.seed( random.shuffle(words)
Build the dataset
= 3 # context length: how many characters do we take to predict the next one?
block_size
def build_dataset(words):
= [], []
X, Y
for w in words:
= [0] * block_size
context for ch in w + '.':
= stoi[ch]
ix
X.append(context)
Y.append(ix)= context[1:] + [ix]
context
= torch.tensor(X)
X = torch.tensor(Y)
Y print(X.shape, Y.shape)
return X, Y
= int(0.8 * len(words))
n1 = int(0.9 * len(words))
n2
= build_dataset(words[:n1])
Xtr, Ytr = build_dataset(words[n1: n2])
Xdev, Ydev = build_dataset(words[n2:]) Xte, Yte
torch.Size([182625, 3]) torch.Size([182625])
torch.Size([22655, 3]) torch.Size([22655])
torch.Size([22866, 3]) torch.Size([22866])
for x, y in zip(Xtr[:20], Ytr[:20]):
print(''.join(itos[ix.item()] for ix in x), '-->', itos[y.item()])
... --> y
..y --> u
.yu --> h
yuh --> e
uhe --> n
hen --> g
eng --> .
... --> d
..d --> i
.di --> o
dio --> n
ion --> d
ond --> r
ndr --> e
dre --> .
... --> x
..x --> a
.xa --> v
xav --> i
avi --> e
Neural Network
class Linear:
def __init__(self, fan_in, fan_out, bias=True):
self.weight = torch.randn((fan_in, fan_out)) / fan_in ** 0.5 # note: kaiming init
self.bias = torch.zeros(fan_out) if bias else None
def __call__(self, x):
self.out = x @ self.weight
if self.bias is not None:
self.out += self.bias
return self.out
def parameters(self):
return [self.weight] + ([] if self.bias is None else [self.bias])
class BatchNorm1d:
def __init__(self, dim, eps=1e-5, momentum=0.1):
self.eps = eps
self.momentum = momentum
self.training = True
# parameters (trained with backprop)
self.gamma = torch.ones(dim)
self.beta = torch.zeros(dim)
# buffers (trained with a running `momentum update`)
self.running_mean = torch.zeros(dim)
self.running_var = torch.ones(dim)
def __call__(self, x):
# calculate the forward pass
if self.training:
= x.mean(0, keepdim=True)
xmean = x.var(0, keepdim=True)
xvar else:
= self.running_mean
xmean = self.running_var
xvar
= (x - xmean) / torch.sqrt(xvar + self.eps)
xhat self.out = self.gamma * xhat + self.beta
# update the buffers
if self.training:
with torch.no_grad():
self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * xmean
self.running_var = (1 - self.momentum) * self.running_var + self.momentum * xvar
return self.out
def parameters(self):
return [self.gamma, self.beta]
class Tanh:
def __call__(self, x):
self.out = torch.tanh(x)
return self.out
def parameters(self):
return []
42); torch.manual_seed(
= 10 # the dimensionality of the character embedding vectors
n_embd = 200 # the number of neurons in the hidden layer of the MLP
n_hidden
= torch.randn((vocab_size, n_embd))
C = [
layers * block_size, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
Linear(n_embd
Linear(n_hidden, vocab_size)
]
# parameter init
with torch.no_grad():
-1].weight * 0.1 # last layer make less confident
layers[
= [C] + [p for layer in layers for p in layer.parameters()]
parameters print(sum(p.nelement() for p in parameters))
for p in parameters:
= True p.requires_grad
12097
Training
# same optimization as last time
= 200_000
max_steps = 32
batch_size = []
lossi
for i in range(max_steps):
# minibatch construct
= torch.randint(0, Xtr.shape[0], (batch_size,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb
# forward pass
= C[Xb] # embed the characters into vectors
emb = emb.view(emb.shape[0], -1) # concatenate the vectors
x for layer in layers:
= layer(x)
x
= F.cross_entropy(x, Yb)
loss
# backward pass
for p in parameters:
= None
p.grad
loss.backward()
# update: simple SGD
= 0.1 if i < 150_000 else 0.01 # step learning rate decay
lr for p in parameters:
+= -lr * p.grad
p.data
# track stats
if i % 10_000 == 0:
print(f'{i:7d}/{max_steps:7d}: {loss.item(): .4f}')
lossi.append(loss.log10().item())
0/ 200000: 3.4915
10000/ 200000: 2.2179
20000/ 200000: 2.3681
30000/ 200000: 2.1342
40000/ 200000: 2.4067
50000/ 200000: 2.2406
60000/ 200000: 1.9608
70000/ 200000: 1.9236
80000/ 200000: 2.6588
90000/ 200000: 2.0502
100000/ 200000: 2.2596
110000/ 200000: 1.6270
120000/ 200000: 2.1705
130000/ 200000: 2.2806
140000/ 200000: 2.1980
150000/ 200000: 1.8434
160000/ 200000: 1.8251
170000/ 200000: 2.3077
180000/ 200000: 2.0817
190000/ 200000: 2.1585
plt.plot(lossi)
Eval Mode
# put layers into eval mode (needed for batchnorm especially)
for layer in layers:
= False layer.training
# evaluate the loss
@torch.no_grad() # this decorator disables gradient tracking inside pytorch
def split_loss(split):
= {
x, y 'train': (Xtr, Ytr),
'val': (Xdev, Ydev),
'test': (Xte, Yte)
}[split]
= C[x]
emb = emb.view(emb.shape[0], -1)
x for layer in layers:
= layer(x)
x = F.cross_entropy(x, y)
loss print(split, loss.item())
'train')
split_loss('val') split_loss(
NameError: name 'C' is not defined
Sample from model
for _ in range(20):
= []
out = [0] * block_size # initialize with all ...
context while True:
# forward pass the neural net
= C[torch.tensor([context])] # (1, block_size, n_embd)
emb = emb.view(emb.shape[0], -1)
x for layer in layers:
= layer(x)
x = x
logits = F.softmax(logits, dim = 1)
probs # sample from the distribution
= torch.multinomial(probs, num_samples = 1).item()
ix = context[1:] + [ix]
context
out.append(ix)# if we sample the special '.' token, break
if ix == 0: break
print(''.join(itos[i] for i in out))
ivon.
fanili.
thoommestenell.
mattevyn.
alana.
joleshaun.
siah.
prus.
carleen.
jah.
jorrena.
joriah.
jas.
vishylaharia.
juna.
vio.
orven.
mina.
laylee.
esteffead.
Fix the lr plot
10] lossi[:
[0.5430157780647278,
0.5576249957084656,
0.523175835609436,
0.5327444672584534,
0.5206513404846191,
0.5284044742584229,
0.5306796431541443,
0.5056970119476318,
0.5213009119033813,
0.5147265195846558]
10).view(2, 5) torch.arange(
tensor([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
-1, 1000).shape torch.tensor(lossi).view(
torch.Size([200, 1000])
-1, 1000).mean(1).shape torch.tensor(lossi).view(
torch.Size([200])
-1, 1000).mean(1)) plt.plot(torch.tensor(lossi).view(
Pytorchifying the code
layers, containers, torch.nn, fun bugs
Layers
Introducing new layers: embedding, flatten
class Embedding:
def __init__(self, num_embeddings, embedding_dim):
self.weight = torch.randn((num_embeddings, embedding_dim))
def __call__(self, IX):
self.out = self.weight[IX]
return self.out
def parameters(self):
return [self.weight]
class Flatten:
def __call__(self, x):
self.out = x.view(x.shape[0], -1)
return self.out
def parameters(self):
return []
= 10 # the dimensionality of the character embedding vectors
n_embd = 200 # the number of neurons in the hidden layer of the MLP
n_hidden
= [
layers
Embedding(vocab_size, n_embd),
Flatten(),* block_size, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
Linear(n_embd
Linear(n_hidden, vocab_size)
]
# parameter init
with torch.no_grad():
-1].weight *= 0.1 # last layer make less confident
layers[
= [p for layer in layers for p in layer.parameters()]
parameters print(sum(p.nelement() for p in parameters))
for p in parameters:
= True p.requires_grad
12097
# same optimization as last time
= 200_000
max_steps = 32
batch_size = []
lossi
for i in range(max_steps):
# minibatch construct
= torch.randint(0, Xtr.shape[0], (batch_size,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb
#forward pass
= Xb
x for layer in layers:
= layer(x)
x
= F.cross_entropy(x, Yb)
loss
# backward pass
for p in parameters:
= None
p.grad
loss.backward()
# update: simple SGD
= 0.1 if i < 150_000 else 0.01 # step learning rate decay
lr for p in parameters:
+= -lr * p.grad
p.data
# track stats
if i % 10_000 == 0:
print(f'{i:7d}/{max_steps:7d}: {loss.item(): .4f}')
lossi.append(loss.log10().item())
0/ 200000: 3.6104
10000/ 200000: 2.1986
20000/ 200000: 2.3563
30000/ 200000: 2.2846
40000/ 200000: 2.3796
50000/ 200000: 2.2467
60000/ 200000: 2.2208
70000/ 200000: 2.3856
80000/ 200000: 1.7651
90000/ 200000: 2.1291
100000/ 200000: 2.0727
110000/ 200000: 2.3707
120000/ 200000: 1.8593
130000/ 200000: 2.2348
140000/ 200000: 1.7190
150000/ 200000: 2.3271
160000/ 200000: 1.9129
170000/ 200000: 2.0262
180000/ 200000: 1.7435
190000/ 200000: 2.2444
Containers
class Sequential:
def __init__(self, layers):
self.layers = layers
def __call__(self, x):
for layer in self.layers:
= layer(x)
x self.out = x
return self.out
def parameters(self):
# get parameters of all layers and stretch them out into one list
return [p for layer in self.layers for p in layer.parameters()]
= 10 # the dimensionality of the character embedding vectors
n_embd = 200 # the number of neurons in the hidden layer of the MLP
n_hidden
= Sequential([
model
Embedding(vocab_size, n_embd),
Flatten(),* block_size, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
Linear(n_embd
Linear(n_hidden, vocab_size)
])
# parameter init
with torch.no_grad():
-1].weight * 0.1 # last layer make less confident
layers[
= model.parameters()
parameters print(sum(p.nelement() for p in parameters))
for p in parameters:
= True p.requires_grad
12097
# same optimization as last time
= 200_000
max_steps = 32
batch_size = []
lossi
for i in range(max_steps):
# minibatch construct
= torch.randint(0, Xtr.shape[0], (batch_size,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb
#forward pass
= model(Xb)
logits
= F.cross_entropy(logits, Yb)
loss
# backward pass
for p in parameters:
= None
p.grad
loss.backward()
# update: simple SGD
= 0.1 if i < 150_000 else 0.01 # step learning rate decay
lr for p in parameters:
+= -lr * p.grad
p.data
# track stats
if i % 10_000 == 0:
print(f'{i:7d}/{max_steps:7d}: {loss.item(): .4f}')
lossi.append(loss.log10().item())
0/ 200000: 3.6506
10000/ 200000: 2.3977
20000/ 200000: 1.8598
30000/ 200000: 2.1111
40000/ 200000: 2.3361
50000/ 200000: 2.1747
60000/ 200000: 2.1322
70000/ 200000: 2.3737
80000/ 200000: 2.1701
90000/ 200000: 2.1263
100000/ 200000: 1.7871
110000/ 200000: 2.3637
120000/ 200000: 2.1640
130000/ 200000: 2.1788
140000/ 200000: 2.4404
150000/ 200000: 2.1481
160000/ 200000: 2.0980
170000/ 200000: 2.0363
180000/ 200000: 2.1364
190000/ 200000: 1.8888
eval mode
# put layers into eval mode (needed for batchnorm especially)
for layer in model.layers:
= False layer.training
# evaluate the loss
@torch.no_grad() # this decorator disables gradient tracking inside pytorch
def split_loss(split):
= {
x, y 'train': (Xtr, Ytr),
'val': (Xdev, Ydev),
'test': (Xte, Yte)
}[split]
= model(x)
logits = F.cross_entropy(logits, y)
loss print(split, loss.item())
'train')
split_loss('val') split_loss(
train 1.765791416168213
val 1.9921294450759888
Sample from model
for _ in range(20):
= []
out = [0] * block_size # initialize with all ...
context while True:
# forward pass the neural net
= model(torch.tensor([context]))
logits = F.softmax(logits, dim = 1)
probs # sample from the distribution
= torch.multinomial(probs, num_samples = 1).item()
ix = context[1:] + [ix]
context
out.append(ix)# if we sample the special '.' token, break
if ix == 0: break
print(''.join(itos[i] for i in out))
nayah.
kent.
rohyn.
aaissalyn.
axminaiyah.
tayyah.
dum.
brena.
hoselia.
kehanikki.
ormonadit.
luchelyn.
lin.
jannal.
noes.
sude.
raylen.
anvika.
alea.
wylin.
Increase the context size
= 8
block_size
= build_dataset(words[:n1])
Xtr, Ytr = build_dataset(words[n1: n2])
Xdev, Ydev = build_dataset(words[n2:]) Xte, Yte
torch.Size([182625, 8]) torch.Size([182625])
torch.Size([22655, 8]) torch.Size([22655])
torch.Size([22866, 8]) torch.Size([22866])
for x, y in zip(Xtr[:20], Ytr[:20]):
print(''.join(itos[ix.item()] for ix in x), '-->', itos[y.item()])
........ --> y
.......y --> u
......yu --> h
.....yuh --> e
....yuhe --> n
...yuhen --> g
..yuheng --> .
........ --> d
.......d --> i
......di --> o
.....dio --> n
....dion --> d
...diond --> r
..diondr --> e
.diondre --> .
........ --> x
.......x --> a
......xa --> v
.....xav --> i
....xavi --> e
= 10 # the dimensionality of the character embedding vectors
n_embd = 200 # the number of neurons in the hidden layer of the MLP
n_hidden
= Sequential([
model
Embedding(vocab_size, n_embd),
Flatten(),* block_size, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
Linear(n_embd
Linear(n_hidden, vocab_size)
])
# parameter init
with torch.no_grad():
-1].weight * 0.1 # last layer make less confident
layers[
= model.parameters()
parameters print(sum(p.nelement() for p in parameters))
for p in parameters:
= True p.requires_grad
22097
training mode
# same optimization as last time
= 200_000
max_steps = 32
batch_size = []
lossi
for i in range(max_steps):
# minibatch construct
= torch.randint(0, Xtr.shape[0], (batch_size,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb
#forward pass
= model(Xb)
logits
= F.cross_entropy(logits, Yb)
loss
# backward pass
for p in parameters:
= None
p.grad
loss.backward()
# update: simple SGD
= 0.1 if i < 150_000 else 0.01 # step learning rate decay
lr for p in parameters:
+= -lr * p.grad
p.data
# track stats
if i % 10_000 == 0:
print(f'{i:7d}/{max_steps:7d}: {loss.item(): .4f}')
lossi.append(loss.log10().item())
0/ 200000: 3.7162
10000/ 200000: 1.8141
20000/ 200000: 2.0207
30000/ 200000: 2.1630
40000/ 200000: 2.4232
50000/ 200000: 2.3131
60000/ 200000: 1.9144
70000/ 200000: 1.9182
80000/ 200000: 2.4822
90000/ 200000: 2.1356
100000/ 200000: 2.0794
110000/ 200000: 2.1322
120000/ 200000: 1.8838
130000/ 200000: 2.3476
140000/ 200000: 2.0343
150000/ 200000: 2.0312
160000/ 200000: 2.3358
170000/ 200000: 2.0393
180000/ 200000: 2.1933
190000/ 200000: 1.9221
eval mode
# put layers into eval mode (needed for batchnorm especially)
for layer in model.layers:
= False layer.training
# evaluate the loss
@torch.no_grad() # this decorator disables gradient tracking inside pytorch
def split_loss(split):
= {
x, y 'train': (Xtr, Ytr),
'val': (Xdev, Ydev),
'test': (Xte, Yte)
}[split]
= model(x)
logits = F.cross_entropy(logits, y)
loss print(split, loss.item())
'train')
split_loss('val') split_loss(
train 1.9201935529708862
val 2.0279388427734375
Performance log
- original (3 character context + 200 hidden neurons, 12K params): train 2.058, val 2.105
- context: 3 -> 8 (22K params): train 1.918, val 2.027
# sample from model
for _ in range(20):
= []
out = [0] * block_size
context
while True:
# forward pass the neural net
= model(torch.tensor([context]))
logits = F.softmax(logits, dim=1)
probs # sample from the distribution
= torch.multinomial(probs, num_samples=1).item()
ix #shift the context window and track the samples
= context[1:] + [ix]
context
out.append(ix)# if we sample the special '.' token, break
if ix == 0: break
print(''.join(itos[i] for i in out))
henyx.
terna.
nyaad.
maganta.
jalori.
delci.
dysenni.
kalven.
allington.
teb.
kingstonaa.
leonnsta.
paisle.
anny.
shanidi.
azrislaca.
brandan.
jiriana.
nathawa.
ella.
Implementing Wavenet
= torch.randint(0, Xtr.shape[0], (4,)) # lets look at batch of just 4 examples
ix = Xtr[ix], Ytr[ix]
Xb, Yb = model(Xb)
logits print(Xb.shape)
Xb
torch.Size([4, 8])
tensor([[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 1, 19, 8, 22]])
ix
tensor([ 71354, 75603, 1768, 142786])
0].out.shape model.layers[
torch.Size([4, 8, 10])
1].out.shape model.layers[
torch.Size([4, 80])
2].out.shape model.layers[
torch.Size([4, 200])
4, 5, 6, 80) @ torch.randn(80, 200) + torch.randn(200)).shape (torch.randn(
torch.Size([4, 5, 6, 200])
4, 4, 20) @ torch.randn(20, 200) + torch.randn(200)).shape (torch.randn(
torch.Size([4, 4, 200])
= torch.randn(4, 8, 10) # goal: want this to be (4, 4, 20) where consecutive 10d vectors are concatenated
e = torch.cat([e[:, ::2, :], e[:, 1::2, :]], dim=2) explicit
4, 4, 20) == explicit).all() (e.view(
tensor(True)
class FlattenConsecutive:
def __init__(self, n):
self.n = n
def __call__(self, x):
= x.shape
B, T, C = x.view(B, T//self.n, C*self.n)
x if x.shape[1] == 1:
= x.squeeze(1)
x self.out = x
return self.out
def parameters(self):
return []
block_size
8
= 10 # the dimentionality of the character embedding vectors
n_embd = 200 # the number of neurons in the hidden layer of the MLP
n_hidden
= Sequential([
model
Embedding(vocab_size, n_embd),
FlattenConsecutive(block_size),* block_size, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
Linear(n_embd
Linear(n_hidden, vocab_size),
])
# parameter init
with torch.no_grad():
-1].weight *= 0.1 # last layer make less confident
layers[
= model.parameters()
parameters print(sum(p.nelement() for p in parameters))
for p in parameters: p.requires_grad = True
22097
= torch.randint(0, Xtr.shape[0], (4,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb = model(Xb)
logits print(Xb.shape)
Xb
torch.Size([4, 8])
tensor([[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 12, 1]])
for layer in model.layers:
print(layer.__class__.__name__, ':', tuple(layer.out.shape))
Embedding : (4, 8, 10)
FlattenConsecutive : (4, 80)
Linear : (4, 200)
BatchNorm1d : (4, 200)
Tanh : (4, 200)
Linear : (4, 27)
= 2 block_size
= 10 # the dimentionality of the character embedding vectors
n_embd = 200 # the number of neurons in the hidden layer of the MLP
n_hidden
= Sequential([
model
Embedding(vocab_size, n_embd),* block_size, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(block_size), Linear(n_embd * block_size, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(block_size), Linear(n_hidden * block_size, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(block_size), Linear(n_hidden
Linear(n_hidden, vocab_size),
])
# parameter init
with torch.no_grad():
-1].weight *= 0.1 # last layer make less confident
layers[
= model.parameters()
parameters print(sum(p.nelement() for p in parameters))
for p in parameters: p.requires_grad = True
170897
= torch.randint(0, Xtr.shape[0], (4,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb = model(Xb)
logits print(Xb.shape)
Xb
torch.Size([4, 8])
tensor([[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 1, 2, 8, 9],
[ 0, 0, 0, 0, 0, 18, 1, 9],
[ 0, 0, 0, 0, 0, 0, 7, 1]])
for layer in model.layers:
print(layer.__class__.__name__, ':', tuple(layer.out.shape))
Embedding : (4, 8, 10)
FlattenConsecutive : (4, 4, 20)
Linear : (4, 4, 200)
BatchNorm1d : (4, 4, 200)
Tanh : (4, 4, 200)
FlattenConsecutive : (4, 2, 400)
Linear : (4, 2, 200)
BatchNorm1d : (4, 2, 200)
Tanh : (4, 2, 200)
FlattenConsecutive : (4, 400)
Linear : (4, 200)
BatchNorm1d : (4, 200)
Tanh : (4, 200)
Linear : (4, 27)
Training Wavenet: First Pass
= 10 # the dimensionality of the character embedding vectors
n_embd = 68 # the number of neurons in the hidden layer of the MLP
n_hidden # --> want to have the same parameters when the block size was 8
= 2
block_size
= Sequential([
model
Embedding(vocab_size, n_embd),2), Linear(n_embd*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(2), Linear(n_hidden*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(2), Linear(n_hidden*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(
Linear(n_hidden, vocab_size),
])
# parameter init
with torch.no_grad():
-1].weight *= 0.1
layers[
= model.parameters()
parameters print(sum(p.nelement() for p in parameters))
for p in parameters: p.requires_grad = True
22397
# same optimization as last time
= 200_000
max_steps = 32
batch_size = []
lossi
for i in range(max_steps):
# minibatch construct
= torch.randint(0, Xtr.shape[0], (batch_size,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb
# forward pass
= model(Xb)
logits = F.cross_entropy(logits, Yb)
loss
# backward pass
for p in parameters:
= None
p.grad
loss.backward()
# update: simple SGD
= 0.1 if i < 150_000 else 0.01 # step learning rate decay
lr for p in parameters:
+= -lr * p.grad
p.data
# track stats
if i % 10_000 == 0: # print every once in a while
print(f'{i:7d}/{max_steps:7d}: {loss.item():.4f}')
lossi.append(loss.log10().item())
0/ 200000: 3.6852
10000/ 200000: 2.3648
20000/ 200000: 2.1416
30000/ 200000: 1.9978
40000/ 200000: 2.2832
50000/ 200000: 1.9900
60000/ 200000: 2.0611
70000/ 200000: 1.8055
80000/ 200000: 2.6343
90000/ 200000: 2.1440
100000/ 200000: 2.0204
110000/ 200000: 1.8402
120000/ 200000: 1.5136
130000/ 200000: 1.8751
140000/ 200000: 2.1031
150000/ 200000: 2.0538
160000/ 200000: 2.0164
170000/ 200000: 2.2596
180000/ 200000: 1.9372
190000/ 200000: 1.6855
for layer in model.layers:
print(layer.__class__.__name__, ':', tuple(layer.out.shape))
Embedding : (32, 8, 10)
FlattenConsecutive : (32, 4, 20)
Linear : (32, 4, 68)
BatchNorm1d : (32, 4, 68)
Tanh : (32, 4, 68)
FlattenConsecutive : (32, 2, 136)
Linear : (32, 2, 68)
BatchNorm1d : (32, 2, 68)
Tanh : (32, 2, 68)
FlattenConsecutive : (32, 136)
Linear : (32, 68)
BatchNorm1d : (32, 68)
Tanh : (32, 68)
Linear : (32, 27)
-1, 1000).mean(1)) plt.plot(torch.tensor(lossi).view(
# put layers into eval mode (needed for batchnorm especially)
for layer in model.layers: layer.training = False
# evaluate the loss
@torch.no_grad() # this decorator disables gradient tracking inside pytorch
def split_loss(split):
= {
x, y 'train': (Xtr, Ytr),
'val': (Xdev, Ydev),
'test': (Xte, Yte)
}[split]= model(x)
logits = F.cross_entropy(logits, y)
loss print(split, loss.item())
'train')
split_loss('val') split_loss(
train 1.9430139064788818
val 2.027573585510254
Fixing BatchNorm1D bug
class BatchNorm1d:
def __init__(self, dim, eps=1e-5, momentum=0.1):
self.eps = eps
self.momentum = momentum
self.training = True
# parameters (trained with backprop)
self.gamma = torch.ones(dim)
self.beta = torch.zeros(dim)
# buffers (trained with a running `momentum update`)
self.running_mean = torch.zeros(dim)
self.running_var = torch.ones(dim)
def __call__(self, x):
# calculate the forward pass
if self.training:
if x.ndim == 2: dim = 0
elif x.ndim == 3: dim = (0, 1)
= x.mean(dim, keepdim=True)
xmean = x.var(dim, keepdim=True)
xvar else:
= self.running_mean
xmean = self.running_var
xvar
= (x - xmean) / torch.sqrt(xvar + self.eps)
xhat self.out = self.gamma * xhat + self.beta
# update the buffers
if self.training:
with torch.no_grad():
self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * xmean
self.running_var = (1 - self.momentum) * self.running_var + self.momentum * xvar
return self.out
def parameters(self):
return [self.gamma, self.beta]
= 10 # the dimensionality of the character embedding vectors
n_embd = 68 # the number of neurons in the hidden layer of the MLP
n_hidden # --> want to have the same parameters when the block size was 8
# block_size = 2
= Sequential([
model
Embedding(vocab_size, n_embd),2), Linear(n_embd*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(2), Linear(n_hidden*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(2), Linear(n_hidden*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(
Linear(n_hidden, vocab_size),
])
# parameter init
# with torch.no_grad():
# layers[-1].weight *= 0.1
= model.parameters()
parameters print(sum(p.nelement() for p in parameters))
for p in parameters: p.requires_grad = True
22397
= torch.randint(0, Xtr.shape[0], (4,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb = model(Xb)
logits print(Xb.shape)
Xb
torch.Size([4, 8])
tensor([[ 0, 0, 0, 0, 0, 0, 8, 5],
[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 11, 1, 13, 2, 18],
[ 0, 25, 1, 26, 13, 9, 14, 5]])
for layer in model.layers:
print(layer.__class__.__name__, ':', tuple(layer.out.shape))
Embedding : (4, 8, 10)
FlattenConsecutive : (4, 4, 20)
Linear : (4, 4, 68)
BatchNorm1d : (4, 4, 68)
Tanh : (4, 4, 68)
FlattenConsecutive : (4, 2, 136)
Linear : (4, 2, 68)
BatchNorm1d : (4, 2, 68)
Tanh : (4, 2, 68)
FlattenConsecutive : (4, 136)
Linear : (4, 68)
BatchNorm1d : (4, 68)
Tanh : (4, 68)
Linear : (4, 27)
3].running_mean.shape model.layers[
torch.Size([1, 1, 68])
# same optimization as last time
= 200_000
max_steps = 32
batch_size = []
lossi
for i in range(max_steps):
# minibatch construct
= torch.randint(0, Xtr.shape[0], (batch_size,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb
# forward pass
= model(Xb)
logits = F.cross_entropy(logits, Yb)
loss
# backward pass
for p in parameters:
= None
p.grad
loss.backward()
# update: simple SGD
= 0.1 if i < 150_000 else 0.01 # step learning rate decay
lr for p in parameters:
+= -lr * p.grad
p.data
# track stats
if i % 10_000 == 0: # print every once in a while
print(f'{i:7d}/{max_steps:7d}: {loss.item():.4f}')
lossi.append(loss.log10().item())
0/ 200000: 3.4804
10000/ 200000: 1.9962
20000/ 200000: 2.0834
30000/ 200000: 2.0940
40000/ 200000: 2.6074
50000/ 200000: 1.9911
60000/ 200000: 1.6505
70000/ 200000: 2.0625
80000/ 200000: 2.0239
90000/ 200000: 1.9839
100000/ 200000: 1.8198
110000/ 200000: 1.9920
120000/ 200000: 1.8590
130000/ 200000: 2.0162
140000/ 200000: 1.9531
150000/ 200000: 1.9374
160000/ 200000: 1.7478
170000/ 200000: 1.7850
180000/ 200000: 1.9224
190000/ 200000: 1.7220
-1, 1000).mean(1)) plt.plot(torch.tensor(lossi).view(
for layer in model.layers: layer.training = False
'train') split_loss(
train 1.9116132259368896
'val') split_loss(
val 2.023597240447998
'test') split_loss(
test 2.013340711593628
Scaling up Wavenet
= 24 # the dimensionality of the character embedding vectors
n_embd = 128 # the number of neurons in the hidden layer of the MLP
n_hidden # --> want to have the same parameters when the block size was 8
= Sequential([
model
Embedding(vocab_size, n_embd),2), Linear(n_embd*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(2), Linear(n_hidden*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(2), Linear(n_hidden*2, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
FlattenConsecutive(
Linear(n_hidden, vocab_size),
])
# parameter init
with torch.no_grad():
-1].weight *= 0.1
model.layers[
= model.parameters()
parameters print(sum(p.nelement() for p in parameters))
for p in parameters: p.requires_grad = True
76579
# same optimization as last time
= 200_000
max_steps = 32
batch_size = []
lossi
for i in range(max_steps):
# minibatch construct
= torch.randint(0, Xtr.shape[0], (batch_size,))
ix = Xtr[ix], Ytr[ix]
Xb, Yb
# forward pass
= model(Xb)
logits = F.cross_entropy(logits, Yb)
loss
# backward pass
for p in parameters:
= None
p.grad
loss.backward()
# update: simple SGD
= 0.1 if i < 150_000 else 0.01 # step learning rate decay
lr for p in parameters:
+= -lr * p.grad
p.data
# track stats
if i % 10_000 == 0: # print every once in a while
print(f'{i:7d}/{max_steps:7d}: {loss.item():.4f}')
lossi.append(loss.log10().item())
0/ 200000: 3.2997
10000/ 200000: 1.8203
20000/ 200000: 1.8036
30000/ 200000: 2.1780
40000/ 200000: 2.1453
50000/ 200000: 1.8008
60000/ 200000: 1.6666
70000/ 200000: 2.3526
80000/ 200000: 1.4201
90000/ 200000: 1.7949
100000/ 200000: 1.5605
110000/ 200000: 1.7919
120000/ 200000: 1.6035
130000/ 200000: 1.9319
140000/ 200000: 1.8383
150000/ 200000: 1.6744
160000/ 200000: 1.5064
170000/ 200000: 1.3609
180000/ 200000: 1.8823
190000/ 200000: 2.1009
for layer in model.layers: layer.training = False
'train')
split_loss('val') split_loss(
train 1.765791416168213
val 1.9921294450759888
len(Xdev)
22655