神经网络是由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。
神经网络中最基本的成分是神经元模型。M-P神经元模型,在这个模型中,神经元接收到来自n个其他神经元传递过来的输入信号,这些输入信号通过带权重的连接进行传递,神经元接收到的总输入值将于神经元的阈值进行比较,然后通过激活函数处理以产生神经元的输出。
理想的激活函数是阶跃函数,但是阶跃函数具有不连续、不光滑等不太好的性质,因此实际常用Sigmoid函数作为激活函数。
感知机由两层神经元组成,输入层接收外界输入信号后传递给输出层,输出层是M-P神经元,亦称为“阈值逻辑单元”。
感知机能很容易地实现逻辑与、或、非运算。
感知机只有输出层神经元进行激活函数处理,即只拥有一层功能神经元,其学习能力非常有限,与、或、非问题都是线性可分的。
要解决非线性可分问题,需考虑使用多层功能神经元,如异或问题。
输入层与输出层之间的神经元被称为隐层或者隐含层,隐含层和输出层神经元都是拥有激活函数的功能神经元。
神经网络的学习过程,就是根据训练数据来调整神经元之间的连接权以及每个功能神经元的阈值。
多层网络的学习能力比单层感知机强的多。欲训练多层网络,需要更强大的算法,如误差逆传播(error BackPropagation,BP)算法。
训练集为
,输入示例由d个属性描述,输出l维实值向量,其中隐层有q个神经元。输出层第j个神经元的阈值为
,隐层第h个神经元的阈值为
,输入层第i个神经元与隐层第h个神经元之间的连接权为
,隐层第h个神经元和输出层第j个神经元之间的连接权为
,记隐层第h个神经元接收到的输入为
,输出层第j个神经元接收到的输入为
,其中
为隐层第h个神经元的输出,隐层和输出层神经元使用Sigmoid函数。
对于训练例
,假定神经网络的输出为
,即
。
其中这个神经网络一共有(d+l+1)q+l个参数需要确定,输入层到隐层的d x q个权值,隐层到输出层的 q x l个圈子,q个隐层神经元的阈值,l个输出神经元的阈值。
BP算法基于梯度下降策略,以目标的负梯度方向对参数进行调整,
其中
,
BP算法的目标是要最小化训练集D上的累积误差,
只需一个包含足够多神经元的隐层,多层前馈网络就能以任意精度逼近任意复杂度的连续函数。如何设置隐层神经元个数仍然是个未决问题,实际应用中同城靠“试错法”调整。
BP神经网络经常遭遇过拟合,其训练误差持续降低,但测试误差却可能上升。有两种策略常用来缓解BP神经网络的过拟合。第一种策略是早停,将数据分为训练集和验证集,训练集用来计算梯度、更新权值和阈值,验证集用来估计误差,若训练集误差降低,但验证集误差上升,则停止训练,同时返回具有最小验证集误差的连接权和阈值。第二种策略是正则化,其基本思想是在误差目标函数中增加一个用于描述网络复杂度的部分,例如链接权与阈值的平方和,如:
确定隐层节点数目的经验公式:
,其中a为1-10之间的调节常数
代码如下:
# 参考自https://gitee.com/hdt3213/ClassifiersDemo/blob/master/BPNN/bpnn.py#
import math
import random
random.seed(0)
def rand(a, b):
return (b - a) * random.random() + a
def make_matrix(m, n, fill=0.0):
mat = []
for i in range(m):
mat.append([fill] * n)
return mat
def sigmoid(x):
return 1.0 / (1.0 + math.exp(-x))
def sigmoid_derivative(x):
return x * (1 - x)
class BPNeuralNetwork:
def __init__(self):
self.input_n = 0
self.hidden_n = 0
self.output_n = 0
self.input_cells = []
self.hidden_cells = []
self.output_cells = []
self.input_weights = []
self.output_weights = []
self.input_correction = []
self.output_correction = []
def setup(self, ni, nh, no):
self.input_n = ni + 1
self.hidden_n = nh
self.output_n = no
# init cells
self.input_cells = [1.0] * self.input_n
self.hidden_cells = [1.0] * self.hidden_n
self.output_cells = [1.0] * self.output_n
# init weights
self.input_weights = make_matrix(self.input_n, self.hidden_n)
self.output_weights = make_matrix(self.hidden_n, self.output_n)
# random activate
for i in range(self.input_n):
for h in range(self.hidden_n):
self.input_weights[i][h] = rand(-0.2, 0.2)
for h in range(self.hidden_n):
for o in range(self.output_n):
self.output_weights[h][o] = rand(-2.0, 2.0)
# init correction matrix
self.input_correction = make_matrix(self.input_n, self.hidden_n)
self.output_correction = make_matrix(self.hidden_n, self.output_n)
def predict(self, inputs):
# activate input layer
for i in range(self.input_n - 1):
self.input_cells[i] = inputs[i]
# activate hidden layer
for j in range(self.hidden_n):
total = 0.0
for i in range(self.input_n):
total += self.input_cells[i] * self.input_weights[i][j]
self.hidden_cells[j] = sigmoid(total)
# activate output layer
for k in range(self.output_n):
total = 0.0
for j in range(self.hidden_n):
total += self.hidden_cells[j] * self.output_weights[j][k]
self.output_cells[k] = sigmoid(total)
return self.output_cells[:]
def back_propagate(self, case, label, learn, correct):
# feed forward
self.predict(case)
# get output layer error
output_deltas = [0.0] * self.output_n
for o in range(self.output_n):
error = label[o] - self.output_cells[o]
output_deltas[o] = sigmoid_derivative(self.output_cells[o]) * error
# get hidden layer error
hidden_deltas = [0.0] * self.hidden_n
for h in range(self.hidden_n):
error = 0.0
for o in range(self.output_n):
error += output_deltas[o] * self.output_weights[h][o]
hidden_deltas[h] = sigmoid_derivative(self.hidden_cells[h]) * error
# update output weights
for h in range(self.hidden_n):
for o in range(self.output_n):
change = output_deltas[o] * self.hidden_cells[h]
self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o]
self.output_correction[h][o] = change
# update input weights
for i in range(self.input_n):
for h in range(self.hidden_n):
change = hidden_deltas[h] * self.input_cells[i]
self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
self.input_correction[i][h] = change
# get global error
error = 0.0
for o in range(len(label)):
error += 0.5 * (label[o] - self.output_cells[o]) ** 2
return error
def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
for j in range(limit):
error = 0.0
for i in range(len(cases)):
label = labels[i]
case = cases[i]
error += self.back_propagate(case, label, learn, correct)
def test(self):
cases = [
[0, 0],
[0, 1],
[1, 0],
[1, 1],
]
labels = [[1,0], [0,1], [0,1], [1,0]]
self.setup(2, 5, 2)
self.train(cases, labels, 10000, 0.05, 0.1)
for case in cases:
print(self.predict(case))
if __name__ == '__main__':
nn = BPNeuralNetwork()
nn.test()