人工神经网络是一种经典的机器学习模型,随着深度学习的发展神经网络模型日益完善.

联想大家熟悉的回归问题, 神经网络模型实际上是根据训练样本创造出一个多维输入多维输出的函数, 并使用该函数进行预测, 网络的训练过程即为调节该函数参数提高预测精度的过程.神经网络要解决的问题与最小二乘法回归解决的问题并无根本性区别.

回归和分类是常用神经网络处理的两类问题, 如果你已经了解了神经网络的工作原理可以在http://playground.tensorflow.org/上体验一个浅层神经网络的工作过程.

感知机(Perceptron)是一个简单的线性二分类器, 它保存着输入权重, 根据输入和内置的函数计算输出.人工神经网络中的单个神经元即是感知机.

在前馈神经网络的预测过程中, 数据流从输入到输出单向流动, 不存在循环和返回的通道.

目前大多数神经网络模型都属于前馈神经网络, 在下文中我们将详细讨论前馈过程.

多层感知机(Multi Layer Perceptron, MLP)是由多个感知机层全连接组成的前馈神经网络, 这种模型在非线性问题中表现出色.

所谓全连接是指层A上任一神经元与临近层B上的任意神经元之间都存在连接.

反向传播(Back Propagation,BP)是误差反向传播的简称,这是一种用来训练人工神经网络的常见算法, 通常与最优化方法(如梯度下降法)结合使用.

本文介绍的神经网络模型在结构上属于MLP, 因为采用BP算法进行训练, 人们也称其为BP神经网络.

BP神经网络原理

经典的BP神经网络通常由三层组成: 输入层, 隐含层与输出层.通常输入层神经元的个数与特征数相关,输出层的个数与类别数相同, 隐含层的层数与神经元数均可以自定义.

每个神经元代表对数据的一次处理:

每个隐含层和输出层神经元输出与输入的函数关系为:

\[I_j=\sum_iW_{ij}O_i
\]

\[O_j= sigmod(I_l) =\frac{1}{1+e^{-I_l}}
\]

其中\(W_{ij}\)表示神经元i与神经元j之间连接的权重,\(O_j\)代表神经元j的输出, sigmod是一个特殊的函数用于将任意实数映射到(0,1)区间.

上文中的sigmod函数称为神经元的激励函数(activation function), 除了sigmod函数\(\frac{1}{1+e^{-I_l}}\)外, 常用还有tanh和ReLU函数.

我们用一个完成训练的神经网络处理回归问题, 每个样本拥有n个输入.相应地,神经网络拥有n个输入神经元和1个输出神经元.

实际应用中我们通常在输入层额外增加一个偏置神经元, 提供一个可控的输入修正;或者为每个隐含层神经元设置一个偏置参数.

我们将n个特征依次送入输入神经元, 隐含层神经元获得输入层的输出并计算自己输出值, 输出层的神经元根据隐含层输出计算出回归值.

上述过程一般称为前馈(Feed-Forward)过程, 该过程中神经网络的输入输出与多维函数无异.

现在我们的问题是如何训练这个神经网络.

作为监督学习算法,BP神经网络的训练过程即是根据前馈得到的预测值和参考值比较, 根据误差调整连接权重\(W_{ij}\)的过程.

训练过程称为反向传播过程(BackPropagation), 数据流正好与前馈过程相反.

首先我们随机初始化连接权重\(W_{ij}\), 对某一训练样本进行一次前馈过程得到各神经元的输出.

首先计算输出层的误差:

\[E_j= sigmod'(O_j)*(T_j-O_j) =O_j(1-O_j)(T_j-O_j)
\]

其中\(E_j\)代表神经元j的误差,\(O_j\)表示神经元j的输出, \(T_j\)表示当前训练样本的参考输出, \(sigmod'(x)\)是上文sigmod函数的一阶导数.

计算隐含层误差:

\[E_j= sigmod'(O_j)*\sum_kE_kW_{jk} =O_j(1-O_j)\sum_kE_kW_{jk}
\]

隐含层输出不存在参考值, 使用下一层误差的加权和代替\((T_j-O_j)\).

计算完误差后就可以更新\(W_{ij}\)和\(\theta_j\):

\[W_{ij}=W_{ij}+\lambda E_jO_i
\]

其中\(\lambda\)是一个称为学习率的参数,一般在(0,0.1)区间上取值.

实际上为了加快学习的效率我们引入称为矫正矩阵的机制, 矫正矩阵记录上一次反向传播过程中的\(E_jO_i\)值, 这样\(W_j\)更新公式变为:

\[W_{ij}=W_{ij}+\lambda E_jO_i + \mu C_{ij}
\]

\(\mu\)是一个称为矫正率的参数.随后更新矫正矩阵:

\[C_{ij} = E_jO_i
\]

每一个训练样本都会更新一次整个网络的参数.我们需要额外设置训练终止的条件.

最简单的训练终止条件为设置最大迭代次数, 如将数据集迭代1000次后终止训练.

单纯的设置最大迭代次数不能保证训练结果的精确度, 更好的办法是使用损失函数(loss function)作为终止训练的依据.

损失函数可以选用输出层各节点的方差:

\[L = \sum_j(T_j-O_j)^2
\]

为了避免神经网络进行无意义的迭代, 我们通常在训练数据集中抽出一部分用作校验.当预测误差高于阈值时提前终止训练.

Python实现BP神经网络

首先实现几个工具函数:

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

定义sigmod函数和它的导数:

def sigmoid(x):
return 1.0 / (1.0 + math.exp(-x)) def sigmod_derivate(x):
return x * (1 - x)

定义BPNeuralNetwork类, 使用三个列表维护输入层,隐含层和输出层神经元, 列表中的元素代表对应神经元当前的输出值.使用两个二维列表以邻接矩阵的形式维护输入层与隐含层, 隐含层与输出层之间的连接权值, 通过同样的形式保存矫正矩阵.

定义setup方法初始化神经网络:

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)

定义predict方法进行一次前馈, 并返回输出:

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[:]

定义back_propagate方法定义一次反向传播和更新权值的过程, 并返回最终预测误差:

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] = sigmod_derivate(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] = sigmod_derivate(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

定义train方法控制迭代, 该方法可以修改最大迭代次数, 学习率\(\lambda\), 矫正率\(\mu\)三个参数.

def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
for i 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)

编写test方法,演示如何使用神经网络学习异或逻辑:

def test(self):
cases = [
[0, 0],
[0, 1],
[1, 0],
[1, 1],
]
labels = [[0], [1], [1], [0]]
self.setup(2, 5, 1)
self.train(cases, labels, 10000, 0.05, 0.1)
for case in cases:
print(self.predict(case))

完整源代码参见bpnn.py

使用tensorflow实现一个神经网络可能是更简单高效的方法, 可以参见tensorflow入门指南中的第二节:实现一个简单神经网络.

BP神经网络与Python实现的更多相关文章

  1. BP神经网络在python下的自主搭建梳理

    本实验使用mnist数据集完成手写数字识别的测试.识别正确率认为是95% 完整代码如下: #!/usr/bin/env python # coding: utf-8 # In[1]: import n ...

  2. 三层BP神经网络的python实现

    这是一个非常漂亮的三层反向传播神经网络的python实现,下一步我准备试着将其修改为多层BP神经网络. 下面是运行演示函数的截图,你会发现预测的结果很惊人! 提示:运行演示函数的时候,可以尝试改变隐藏 ...

  3. BP神经网络原理及python实现

    [废话外传]:终于要讲神经网络了,这个让我踏进机器学习大门,让我读研,改变我人生命运的四个字!话说那么一天,我在乱点百度,看到了这样的内容: 看到这么高大上,这么牛逼的定义,怎么能不让我这个技术宅男心 ...

  4. Python实现bp神经网络识别MNIST数据集

    title: "Python实现bp神经网络识别MNIST数据集" date: 2018-06-18T14:01:49+08:00 tags: [""] cat ...

  5. python构建bp神经网络_曲线拟合(一个隐藏层)__2.代码实现

    IDE:jupyter 抽象程度可能不是那么高,以后再优化. 理论和代码实现的差距还是挺大的 数据集请查看 python构建bp神经网络(一个隐藏层)__1.数据可视化 部分代码预览 git上传.ip ...

  6. 用Python实现BP神经网络(附代码)

    用Python实现出来的机器学习算法都是什么样子呢? 前两期线性回归及逻辑回归项目已发布(见文末链接),今天来讲讲BP神经网络. BP神经网络 全部代码 https://github.com/lawl ...

  7. python手写bp神经网络实现人脸性别识别1.0

    写在前面:本实验用到的图片均来自google图片,侵删! 实验介绍 用python手写一个简单bp神经网络,实现人脸的性别识别.由于本人的机器配置比较差,所以无法使用网上很红的人脸大数据数据集(如lf ...

  8. Python语言编写BP神经网络

    Python语言编写BP神经网络 2016年10月31日 16:42:44 ldy944758217 阅读数 3135   人工神经网络是一种经典的机器学习模型,随着深度学习的发展神经网络模型日益完善 ...

  9. Python使用numpy实现BP神经网络

    Python使用numpy实现BP神经网络 本文完全利用numpy实现一个简单的BP神经网络,由于是做regression而不是classification,因此在这里输出层选取的激励函数就是f(x) ...

随机推荐

  1. datePecker时间控件区间写法

    成交时间: <input type="text" onclick="WdatePicker({dateFmt:'yyyy-MM-dd',maxDate:'#F{$d ...

  2. google Kickstart Round G 2017 三道题题解

    A题:给定A,N,P,计算A的N!次幂对P取模的结果. 数据范围: T次测试,1 ≤ T ≤ 100 1<=A,N,P<=105 快速幂一下就好了.O(nlogn). AC代码: #inc ...

  3. leetcode 3.Longest Substring Without Repeating Charcters

    在一个字符串中寻找出最长的无重复字符的子串的,在不断的后续检索中需要去掉前面一个重复的字符,那么就是需要记录之前所出现过的字符的,在这里需要利用hashmap来记录字符和其出现的位置之间的映射关系的, ...

  4. centOS连接没问题,使用SecureCRT就不能连接

    原因: 将NAT模式改为自定义的模式即可:

  5. DP常用模板

    递推模板: 从结果往回推,需要设定边界为无穷大,并建立状态转移方程 ;j<n;j++) d[n][j]=a[n][j];///边界处理 ;i>=;i--){ ;j<i;j++)/// ...

  6. python魔法方法

    1.__call__ 实现__call__后,该类的对象可以被调用 举例如: class test_call_: def __init__(self, n): self.n = n def __cal ...

  7. 运行gunicorn失败:[ERROR] Connection in use: ('0.0.0.0', 8000)

    参考:https://pdf-lib.org/Home/Details/5262 执行命令:gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app,遇到如下错误: [2019-0 ...

  8. java_basic_基础

    变量 类型 运算符 条件 循环 调试 字符串 数组 从键盘输入数据 switch的用法 从变量接收值 从键盘接收值 输出到一个文件 基本类型的赋值与引用类型的赋值是不一样的 是将引用类型的地址 每一个 ...

  9. Spring IOC 相关的面试题

    Spring最基础的部分就是IOC,对IOC的理解程度从某个方面代表着你对Spring  的理解程度,看了网上的一些面试题,针对Spring IOC相关的重点是下面几个: 1.Spring中Bean ...

  10. Ubuntu下三种方法设置环境变量

    一种用于当前终端,一种用于当前用户,一种用于所有用户: 用于当前终端: 在当前终端中输入:export PATH=$PATH:<你的要加入的路径> 不过上面的方法只适用于当前终端,一旦当前 ...