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
import random
import math
from tinygrad.nn import Linear
from tinygrad.tensor import Tensor
from tinygrad.nn.optim import Adam
HIDDEN_LAYERS = 10
HIDDEN_WIDTH = 10 # Measure accuracy as a function of this.
BIAS = True
class TwoDimensionalClassifier:
def __init__(self):
self.layers = [Linear(HIDDEN_WIDTH, HIDDEN_WIDTH, bias=BIAS) for _ in range(0, HIDDEN_LAYERS)]
self.layers = [Linear(2, HIDDEN_WIDTH, bias=BIAS)] + self.layers + [Linear(HIDDEN_WIDTH, 2, bias=BIAS)]
def transform(self, x):
for layer in self.layers[:-1]:
x = layer(x)
x = x.relu()
return self.layers[-1](x)
def transformLayerByLayer(self, x):
for layer in self.layers[:-1]:
x = layer(x).relu()
yield x
yield self.layers[-1](x).softmax()
def __call__(self, x):
return self.transform(x).softmax()
def trained(distribution, training_steps=3000):
Tensor.training = True
net = TwoDimensionalClassifier()
opt = Adam([layer.weight for layer in net.layers])
for _ in range(training_steps):
point, classification = distribution.sample()
guess = net(point)
error = guess.sparse_categorical_crossentropy(classification)
opt.zero_grad()
error.backward()
opt.step()
Tensor.training = False
return net
# A circle at the origin. Points inside the circle are class 1, points
# outside the circle are class 0.
class CircleAtOrigin:
def __init__(self, radius=1):
self.radius = 1
# Gets a random point in a 3x3 square centered on the origin and its
# classification ([0] or [1] for inside the circle).
def sample(self):
x = random.random() * self.radius*3 - self.radius*1.5
y = random.random() * self.radius*3 - self.radius*1.5
point = Tensor([x, y])
classification = Tensor([1]) if x*x + y*y <= self.radius*self.radius else Tensor([0])
return point, classification