44sys .path .insert (0 , str (Path (__file__ ).parent .parent / "python_lib" ))
55
66import math
7+ import random
78import numpy as np
89from sklearn .datasets import fetch_openml
910from sklearn .model_selection import train_test_split
2021def load_mnist ():
2122 print ("Loading MNIST..." )
2223 mnist = fetch_openml ("mnist_784" , version = 1 , as_frame = False )
23- x = mnist .data .astype (np .float32 ) / 255.0 # normalize to [0,1]
24+ x = mnist .data .astype (np .float32 ) / 255.0
2425 y = mnist .target .astype (np .int32 )
2526 return x , y
2627
@@ -32,16 +33,6 @@ def to_one_hot(y, n_classes=10):
3233 return one_hot
3334
3435
35- def make_batches (x , y , batch_size , shuffle = True ):
36- n = x .shape [0 ]
37- indices = np .arange (n )
38- if shuffle :
39- np .random .shuffle (indices )
40- for start in range (0 , n , batch_size ):
41- batch_idx = indices [start : start + batch_size ]
42- yield x [batch_idx ], y [batch_idx ]
43-
44-
4536# ─── network ─────────────────────────────────────────────────────────────────
4637
4738def make_net ():
@@ -67,18 +58,24 @@ def print_weight_stats(net, batch_num):
6758
6859# ─── training ────────────────────────────────────────────────────────────────
6960
70- def train_epoch (net , loss_fn , optim , x , y , batch_size = 64 ):
71- #print_weight_stats(net, 0)
61+ def train_epoch (net , loss_fn , optim , x_t , y_t , batch_size = 64 ):
62+ n = x_t .dims [0 ]
63+
64+ indices = list (range (n ))
65+ random .shuffle (indices )
66+ x_shuf = x_t .slice (indices )
67+ y_shuf = y_t .slice (indices )
7268
7369 total_loss = 0.0
7470 n_batches = 0
75- max_batches = math .ceil (x .shape [0 ] / batch_size )
76- for xb , yb in make_batches (x , y , batch_size ):
77- xTensor = fromNumpy (xb )
78- yTensor = fromNumpy (yb )
71+ max_batches = math .ceil (n / batch_size )
72+ for start in range (0 , n , batch_size ):
73+ end = min (start + batch_size , n )
74+ xb = x_shuf .slice (start , end )
75+ yb = y_shuf .slice (start , end )
7976
80- pred = net .forward (xTensor )
81- loss = loss_fn (yTensor , pred )
77+ pred = net .forward (xb )
78+ loss = loss_fn (yb , pred )
8279 loss .backward ()
8380
8481 optim .clipGradients (1.0 )
@@ -89,30 +86,25 @@ def train_epoch(net, loss_fn, optim, x, y, batch_size=64):
8986 n_batches += 1
9087 if n_batches == 1 or n_batches % 10 == 0 :
9188 print (f"Batch { n_batches } / { max_batches } , loss { loss .getitem (0 )} " )
92- #print_weight_stats(net, n_batches)
9389
9490 return total_loss / n_batches
9591
9692
97- def evaluate (net , x , y_int , batch_size = 256 ):
93+ def evaluate (net , x_t , y_np , batch_size = 256 ):
94+ n = x_t .dims [0 ]
9895 correct = 0
99- total = 0
100- for xb , yb in make_batches (x , y_int , batch_size , shuffle = False ):
101- xTensor = fromNumpy (xb )
102- pred = net .forward (xTensor )
96+ for start in range (0 , n , batch_size ):
97+ end = min (start + batch_size , n )
98+ pred = net .forward (x_t .slice (start , end ))
10399 pred_np = toNumpy (pred )
104-
105100 predicted = np .argmax (pred_np , axis = 1 )
106-
107- correct += np .sum (predicted == np .argmax (yb , axis = 1 ))
108- total += len (yb )
109- return correct / total
101+ correct += np .sum (predicted == np .argmax (y_np [start :end ], axis = 1 ))
102+ return correct / n
110103
111104
112105# ─── main ────────────────────────────────────────────────────────────────────
113106
114107if __name__ == "__main__" :
115- # load and split data
116108 x , y_int = load_mnist ()
117109 y = to_one_hot (y_int )
118110
@@ -122,16 +114,18 @@ def evaluate(net, x, y_int, batch_size=256):
122114
123115 print (f"Train: { x_train .shape } , Val: { x_val .shape } " )
124116
125- # setup
117+ x_train_t = fromNumpy (x_train )
118+ y_train_t = fromNumpy (y_train )
119+ x_val_t = fromNumpy (x_val )
120+
126121 net = make_net ()
127122 loss_fn = CrossEntropyWithSoftmax ()
128- optim = RmsProp (net .parameters (), 0.0001 , 0.999 ) # lr and decay
123+ optim = RmsProp (net .parameters (), 0.0001 , 0.999 )
129124
130- # training loop
131125 n_epochs = 5
132126 for epoch in range (n_epochs ):
133- train_loss = train_epoch (net , loss_fn , optim , x_train , y_train )
134- val_acc = evaluate (net , x_val , y_val )
127+ train_loss = train_epoch (net , loss_fn , optim , x_train_t , y_train_t )
128+ val_acc = evaluate (net , x_val_t , y_val )
135129 print (
136130 f"Epoch { epoch + 1 } /{ n_epochs } "
137131 f"loss={ train_loss :.4f} "
0 commit comments