前言
该代码为本人在闲暇时按照对神经网络算法的理解手撸的,没有参考任何的代码和完成后也没有double check,所以不保证步骤完全正确,只能从测试用例上看出代码还是能用的。
本着"Talk is cheap. Show me the code."的原则,后文直接放出代码。代码实现了一个简单的神经元Perceptron(不带threshold),一个简单的神经网络层,以及两个测试用例。
希望研究神经网络算法的朋友请参考其他材料,本文不含任何公示推导。
代码
import numpy as np
import matplotlib.pyplot as plt
# Activation Function
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Neural Unit
class Perceptron(object):
def __init__(self, input_size: int, initializer: list = None, activation_func: str = None):
# W[0] is the bias
self.input_size = input_size
# W is parameters of Perceptron
self.n_W = self.input_size + 1
self.W = np.random.uniform(low=0.0, high=1.0, size=self.n_W)
# X is the input vector of Perceptron
self.X = None
self.output = 0.0
self.delta_W = np.array([0] * self.n_W, dtype=np.float)
self.delta_X = np.array([0] * input_size, dtype=np.float)
self.activation_func = activation_func
if initializer:
assert len(initializer) == self.n_W
self.W = initializer
def forward(self, X):
assert len(X) == self.input_size
self.X = np.array(X, dtype=float)
y = np.sum(self.W[1:] * self.X) + self.W[0]
if self.activation_func == 'sigmoid':
self.output = sigmoid(y)
else:
self.output = y
return self.output
def update(self, lr):
self.W = self.W + lr * self.delta_W
self.delta_W = np.array([0] * self.n_W, dtype=np.float)
def __call__(self, X):
return self.forward(X)
# Neural Layer
class Layer(object):
def __init__(self, input_size: int, output_size: int, activation_func='sigmoid', lr=0.1):
self.input_size = input_size
self.output_size = output_size
self.net = np.array(
[Perceptron(input_size=input_size, activation_func=activation_func) for _ in range(output_size)])
self.activation_func = activation_func
self.inputs = np.array([0] * input_size, dtype=np.float)
self.lr = lr
self.outputs = np.array([0] * output_size, dtype=np.float)
def forward(self, X):
self.inputs = np.array(X, dtype=np.float)
self.outputs = np.array([p(X) for p in self.net])
return self.outputs
def __call__(self, X):
return self.forward(X)
def backward(self, delta_outputs):
assert len(delta_outputs) == len(self.net)
for idx in range(self.output_size):
delta_output = delta_outputs[idx]
p = self.net[idx]
o = self.outputs[idx]
if self.activation_func == 'sigmoid':
# W0 is the bias
p.delta_W = delta_output * o * (1 - o) * np.array([1] + list(p.X)) # expand X for W_0
p.delta_X = delta_output * o * (1 - o) * p.W[1:]
else:
# linear
p.delta_W = delta_output * np.array([1] + list(p.X))
# W0 is the bias
p.delta_X = delta_output * p.W[1:]
def update(self):
for p in self.net:
p.update(self.lr)
if __name__ == '__main__':
# standard version ============================
samples = [[[-2, -1], 1],
[[25, 6], 0],
[[17, 4], 0],
[[-15, -6], 1]]
# training
layer1 = Layer(2, 10)
layer2 = Layer(10, 1, activation_func='')
for i in range(1000):
# iteration
print(f'iteration {i}')
text_X = samples[3][0]
text_y_d = samples[3][1]
test_y = layer2(layer1((samples[3][0])))[0]
print(f'X:{text_X}, y_d:{text_y_d}, y:{test_y}')
for X, y_d in samples:
# forward
y = layer2(layer1(X))[0]
# backward layer2 -> layer1
err = y_d - y # delta_outputs of layer 2
layer2.backward([err])
delta_outputs = np.array([0.0] * layer2.input_size, dtype=np.float) # delta_outputs of layer 1
for p in layer2.net:
delta_outputs += p.delta_X
layer1.backward(delta_outputs)
# update gradient
layer2.update()
layer1.update()
# result
for X, y_d in samples:
y = layer2(layer1(X))[0]
print(f'The final result: X:{X}, y_d:{y_d}, y:{y}')
# # simple version ============================
# samples = [[[0, 1], 1],
# [[1, 0], -1]]
# # training
# layer1 = Layer(2, 1, activation_func='')
# for i in range(100):
# # iteration
# print(f'iteration {i}')
# for X, y_d in samples:
# # forward
# y = layer1(X)[0]
# # backward
# err = y_d - y
# layer1.backward([err])
# print(f'W:{layer1.net[0].W}')
# print(f'delta_W:{layer1.net[0].delta_W}')
# layer1.update()
# print(f'X:{X}, y_d:{y_d}, y:{y}')
测试情况
用例1
X: [0, 1] -> y: 1
X: [1, 0] -> y: -1
非常简单,使用1层仅1个神经元的神经网络即可
结果:
迭代100次后:
W:[-0.00408549 -0.99589394 1.00406116]
delta_W:[2.43257503e-05 0.00000000e+00 2.43257503e-05]
X:[0, 1], y_d:1, y:0.9999756742496532
W:[-0.00408305 -0.99589394 1.00406359]
delta_W:[-2.30078698e-05 -2.30078698e-05 -0.00000000e+00]
X:[1, 0], y_d:-1, y:-0.9999769921301684
W是神经网络单元的参数,delta_W是待更新的梯度。
y_d 是期望值, y是预测值。
用例2
测试用例参考了一下下文:
# Define dataset
data = np.array([
[-2, -1], # Alice
[25, 6], # Bob
[17, 4], # Charlie
[-15, -6] # diana
])
all_y_trues = np.array([
1, # Alice
0, # Bob
0, # Charlie
1 # diana
])
这里用了两层神经网络。
结果:
y_d 是期望值, y是预测值。
The final result: X:[-2, -1], y_d:1, y:0.9999900485975061
The final result: X:[25, 6], y_d:0, y:0.0012005012989202424
The final result: X:[17, 4], y_d:0, y:0.0001167411498785853
The final result: X:[-15, -6], y_d:1, y:1.0000069102572744