人工神经网络已经有无数的开源框架,比如tensorflow,caffe等,可以直接用。但最近需要做一个小样例,把基本思想讲一讲,因此自己写了一个demo,以供参考。
下面直接上代码,代码中有注释,比较容易理解
ANN.py
# -*- coding:utf-8 -*-
import numpy
import random
class ANN:
# layers为列表,其长度给出层数,包括输入和输出层,每个元素给出每层神经元数量
# 例如[3, 5, 2]代表输入参数为3,中间隐藏层有5个神经元,输出2个结果的神经网络
def __init__(self, layers):
self.num_layers = len(layers)
self.sizes = layers
self._biases = [numpy.random.randn(y, 1) for y in layers[1:]]
self._weights = [numpy.random.randn(y, x) for x, y in zip(layers[:-1], layers[1:])]
def _sigmoid(self, z):
return 1.0/(1.0 + numpy.exp(-z))
# sigmoid函数的导数
def _Dsigmoid(self, z):
return self._sigmoid(z)*(1-self._sigmoid(z))
# 计算输出向量
def calc(self, input):
for b, w in zip(self._biases, self._weights):
input = self._sigmoid(numpy.dot(w, input) + b)
return input
# 训练神经网络
# trainData: 训练集
# epochs: 训练轮数,对trainData训练多少轮
# size: 训练子集的大小
# rate: learning rate,学习速率,步长
# testData: 测试集
def train(self, trainData, epochs, size, rate, testData=None):
if testData:
n_test = len(testData)
n = len(trainData)
for j in range(epochs):
random.shuffle(trainData)
mini_batches = [trainData[k:k+size] for k in range(0, n, size)]
for mini_bat in mini_batches:
self._update(mini_bat, rate)
if testData:
error = self.test(testData)
if (j % (epochs/10) == 0):
print("Epoch {0}: error = {1}".format(j, error))
else:
if (j % (epochs/10) == 0):
print("Epoch {0} complete.".format(j))
def _update(self, mini_batch, rate):
nabla_b = [numpy.zeros(b.shape) for b in self._biases]
nabla_w = [numpy.zeros(w.shape) for w in self._weights]
for x, y in mini_batch:
delta_b, delta_w = self._backpropagation(x, y)
nabla_b = [nb + db for nb, db in zip(nabla_b, delta_b)]
nabla_w = [nw + dw for nw, dw in zip(nabla_w, delta_w)]
self._weights = [w - (rate/len(mini_batch))*nw for w, nw in zip(self._weights, nabla_w)]
self._biases = [b - (rate/len(mini_batch))*nb for b, nb in zip(self._biases, nabla_b)]
# 后向传播算法
def _backpropagation(self, x, y):
nabla_b = [numpy.zeros(b.shape) for b in self._biases]
nabla_w = [numpy.zeros(w.shape) for w in self._weights]
# forward
activation = x
activations = [x]
zs = []
for b, w in zip(self._biases, self._weights):
z = numpy.dot(w, activation) + b
zs.append(z)
activation = self._sigmoid(z)
activations.append(activation)
# backward
delta = self._derivative(activations[-1], y) * self._Dsigmoid(zs[-1])
nabla_b[-1] = delta
nabla_w[-1] = numpy.dot(delta, activations[-2].transpose())
for layer in range(2, self.num_layers):
z = zs[-layer]
sp = self._Dsigmoid(z)
delta = numpy.dot(self._weights[-layer+1].transpose(), delta) * sp
nabla_b[-layer] = delta
nabla_w[-layer] = numpy.dot(delta, activations[-layer-1].transpose())
return (nabla_b, nabla_w)
# 计算测试集误差,注意此时输出一定为1维矢量
def test(self, testData):
test_results = [(self.calc(x), self._translate(y)) for (x, y) in testData]
err = [output - result for (output, result) in test_results]
return sum(numpy.dot(ei.transpose(), ei) for ei in err)
# 如有必要,将结果进行编码,便于和神经网络输出结果相比较
# 比如经典的手写识别,就需要把数字y转化为向量
def _translate(self, y):
return y
# 整个神经网络的导数
def _derivative(self, output, y):
return output - y
可以看到,得益于numpy的矩阵运算,代码非常简单。
下面是测试,使用上面的代码拟合and函数
test.py
# -*- coding:utf-8 -*-
import ANN
layers = [2, 2, 2]
input = numpy.array([[0,0],[0,1],[1,0],[1,1]])
out = numpy.array([[1,0],[1,0],[1,0],[0,1]])
inputs = [numpy.reshape(i, (2,1)) for i in input]
outputs = [numpy.reshape(o, (2,1)) for o in out]
data = [(inputs[i], outputs[i]) for i in range(len(inputs))]
net = ANN.ANN(layers)
net.train(data, 50000, len(inputs), 0.1, data)
for i in range(len(inputs)):
print(inputs[i].T, ": ", net.calc(inputs[i]).T)
可以看到,构造了一个2x2x2的神经网络,输入为2位,输出为2位,中间层也为2个神经元,即可通过训练获得相应的and函数。需要注意的是numpy构造的矢量必须和ANN.py中风格一致(如果第一个指标代表行,第二个指标代表列,则输入输出均必须为列矢量)。计算结果如下: