-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlayers.py
100 lines (81 loc) · 2.58 KB
/
layers.py
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
94
95
96
97
98
99
100
"""
Our neural nets will made up of layers.
Each layer needs to pass its input forward
and propagate gradients backward. for example,
a neural net might look like:
inputs -> Linear -> Tan -> Linear -> output
"""
from typing import Dict, Callable
import numpy as np
from vsdl.tensor import Tensor
class Layer:
def __init__(self) -> None:
self.params: Dict[str, Tensor] = {}
self.grads: Dict[str, Tensor] = {}
def forward(self, inputs: Tensor) -> Tensor:
"""
Produce the outputs corresponding to these inputs
"""
raise NotImplementedError
def backward(self, grad: Tensor) -> Tensor:
"""
Backpropagate this gradient through the layer
"""
raise NotImplementedError
class Linear(Layer):
"""
Computes outputs = inputs @ w + b
"""
def __init__(self, input_size: int, output_size: int) -> None:
# inputs will be (batch_size, input_size)
# outputs will be (batch_size, input_size)
super().__init__()
self.params['w'] = np.random.randn(input_size, output_size)
self.params['b'] = np.random.randn(output_size)
def forward(self, inputs: Tensor) -> Tensor:
"""
outputs = inputs @ w + b
"""
self.inputs = inputs
return inputs @ self.params['w'] + self.params['b']
def backward(self, grad: Tensor) -> Tensor:
"""
if y = f(x) and x = a * b + c
then dy/da = f'(x) * b
and dy/db = f'(x) * a
and dy/dc = f'(x)
if y = f(x) and x = a @ b + c
then dy/da = f'(x) @ b.T
and dy/db = a.T @ f'(x)
and dy/dc = f'(x)
"""
self.grads['b'] = np.sum(grad, axis=0)
self.grads['w'] = self.inputs.T @ grad
return grad @ self.params['w'].T
F = Callable[[Tensor], Tensor]
class Activation(Layer):
"""
An activation layer just applies a function
elementwise to its inputs.
"""
def __init__(self, f: F, f_prime: F) -> None:
super().__init__()
self.f = f
self.f_prime = f_prime
def forward(self, inputs: Tensor) -> Tensor:
self.inputs = inputs
return self.f(inputs)
def backward(self, grad: Tensor) -> Tensor:
"""
if y = f9x and x = g(z)
then dy/dz = f'(x) * g'(z)
"""
return self.f_prime(self.inputs) * grad
def tanh(x: Tensor) -> Tensor:
return np.tanh(x)
def tanh_prime(x: Tensor) -> Tensor:
y = tanh(x)
return 1 - y ** 2
class Tanh(Activation):
def __init__(self):
super().__init__(tanh, tanh_prime)