"""AI01 lab tests for nn.py (step 7)."""
import math
import random
import unittest
from _impl import load
engine = load("engine")
nn = load("nn")
Value = engine.Value
def set_params(module, values):
params = module.parameters()
assert len(params) == len(values)
for p, v in zip(params, values):
p.data = v
class Step7NeuralNet(unittest.TestCase):
def setUp(self):
random.seed(7)
def test_neuron_parameters_are_weights_then_bias(self):
n = nn.Neuron(3)
ps = n.parameters()
self.assertEqual(len(ps), 4)
self.assertTrue(all(isinstance(p, Value) for p in ps))
self.assertTrue(all(-1.0 <= p.data <= 1.0 for p in ps[:3]))
self.assertEqual(ps[3].data, 0.0, "the bias starts at 0.0")
def test_weights_start_on_both_sides_of_zero(self):
ws = [p.data for p in nn.Neuron(200).parameters()[:-1]]
self.assertTrue(all(-1.0 <= w <= 1.0 for w in ws))
self.assertLess(min(ws), -0.5)
self.assertGreater(max(ws), 0.5)
def test_neuron_forward_with_tanh(self):
n = nn.Neuron(2)
set_params(n, [0.5, -1.0, 0.25]) # w0, w1, b
out = n([1.0, 2.0]) # 0.5 - 2 + 0.25 = -1.25
self.assertIsInstance(out, Value)
self.assertAlmostEqual(out.data, math.tanh(-1.25))
def test_linear_neuron(self):
n = nn.Neuron(2, nonlin=False)
set_params(n, [0.5, -1.0, 0.25])
self.assertAlmostEqual(n([1.0, 2.0]).data, -1.25)
def test_neuron_accepts_values_as_inputs(self):
n = nn.Neuron(2, nonlin=False)
set_params(n, [0.5, -1.0, 0.25])
x0 = Value(1.0)
out = n([x0, Value(2.0)])
out.backward()
self.assertAlmostEqual(out.data, -1.25)
self.assertAlmostEqual(x0.grad, 0.5)
def test_layer_returns_a_list_or_a_single_value(self):
self.assertEqual(len(nn.Layer(2, 3)([1.0, 2.0])), 3)
self.assertIsInstance(nn.Layer(2, 1)([1.0, 2.0]), Value)
self.assertEqual(len(nn.Layer(2, 3).parameters()), 9)
def test_mlp_parameter_counts(self):
self.assertEqual(len(nn.MLP(3, [4, 4, 1]).parameters()), 41)
self.assertEqual(len(nn.MLP(2, [8, 1]).parameters()), 33)
def test_mlp_single_output_is_a_value(self):
self.assertIsInstance(nn.MLP(2, [3, 1])([0.5, -1.0]), Value)
def test_last_layer_is_linear(self):
model = nn.MLP(1, [1])
set_params(model, [3.0, 2.0])
self.assertAlmostEqual(model([5.0]).data, 17.0)
def test_hidden_layers_use_tanh(self):
model = nn.MLP(1, [1, 1])
set_params(model, [1.0, 0.0, 1.0, 0.0]) # hidden w, b, then output w, b
self.assertAlmostEqual(model([2.0]).data, math.tanh(2.0))
def test_parameters_are_the_live_objects(self):
model = nn.MLP(2, [3, 1])
before = model([0.5, -1.0]).data
for p in model.parameters():
p.data += 0.1
self.assertNotAlmostEqual(model([0.5, -1.0]).data, before)
def test_every_parameter_gets_the_right_gradient(self):
model = nn.MLP(2, [3, 2, 1])
x = [0.7, -0.4]
out = model(x)
out.backward()
h = 1e-6
for i, p in enumerate(model.parameters()):
analytic = p.grad
p.data += h
up = model(x).data
p.data -= 2 * h
down = model(x).data
p.data += h
with self.subTest(parameter=i):
self.assertAlmostEqual(analytic, (up - down) / (2 * h), places=6)
def test_zero_grad_clears_every_parameter(self):
model = nn.MLP(2, [3, 1])
model([0.5, -1.0]).backward()
model.zero_grad()
self.assertTrue(all(p.grad == 0.0 for p in model.parameters()))
if __name__ == "__main__":
unittest.main()