Repository navigation
Expand file tree
/
Copy pathv1.py
More file actions
93 lines (72 loc) · 2.92 KB
/
Copy pathv1.py
File metadata and controls
93 lines (72 loc) · 2.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
# ---- config ----
N, D, H, C, L = 4096, 512, 1024, 10, 12
batch_size = 64
epochs = 5
lr = 1e-2
device = "cuda" if torch.cuda.is_available() else "cpu"
# ---- data ----
class DemoDataset(Dataset):
def __init__(self, n, d, c):
"""
this method is mostly for setting the path to data folder, split IDs, augemntations, ...
reading data (images, text files, ...) can happen here if data is small enough to comfortably fit in RAM (an when using few workers)
each worker will create an instance of this class, meaning it will run this method and create / allocate memory for whatever its implementation do
"""
self.x = torch.randn(n, d)
self.y = torch.randint(0, c, (n,))
# this is used to determin how many batches / samples in an epoch
def __len__(self):
return self.x.size(0)
# this is what's called by a single worker to callect samples for the batch
def __getitem__(self, idx):
"""
it's most common and recommended to read / preprocess a data sample in here on the fly
"""
return self.x[idx], self.y[idx]
dataset = DemoDataset(N, D, C)
loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
# ---- model ----
class MLPBlock(nn.Module): #& pre-norm residual block
def __init__(self, dim):
super().__init__()
self.norm = nn.LayerNorm(dim)
self.fc = nn.Linear(dim, dim)
def forward(self, x):
return x + torch.relu(self.fc(self.norm(x))) #& residual add around norm+linear+relu
class MLP(nn.Module):
def __init__(self, in_dim, hidden_dim, out_dim, n_layers):
super().__init__()
self.in_proj = nn.Linear(in_dim, hidden_dim)
self.blocks = nn.Sequential(
*[MLPBlock(hidden_dim) for _ in range(n_layers - 1)] #& stack of residual blocks
)
# we can equtally use nn.ModuleList for self.blocks
self.out = nn.Linear(hidden_dim, out_dim)
def forward(self, x):
x = self.in_proj(x)
x = self.blocks(x)
# if we used nn.ModuleList for self.blocks, it's forward would be:
# for block in self.blocks:
# x = block(x)
return self.out(x)
model = MLP(D, H, C, L).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=lr) #&
loss_fn = nn.CrossEntropyLoss()
# ---- train ----
model.train()
for epoch in range(epochs):
total_loss, seen = 0.0, 0
for xb, yb in loader:
xb, yb = xb.to(device), yb.to(device)
# by default .backward() add the computed grad to the existing one, so we zero out the old grad each step
optimizer.zero_grad()
logits = model(xb)
loss = loss_fn(logits, yb)
loss.backward()
optimizer.step()
total_loss += loss.item() * xb.size(0)
seen += xb.size(0)
print(f"epoch {epoch}: loss {total_loss / seen:.4f}")