本文主要介绍神经网络算法的过程:
目录
1.神经网络算法介绍
1.1 神经网络
神经网络就是模拟人的思维方式,通过学习或训练过程中改变参数值,以适应满足周围环境的要求,这是一个具有学习能力的系统,是一个非线性的数学关系,其特色在于信息的分布式存储和并行协同处理。虽然单个神经元的结构极其简单,功能有限,但大量神经元构成的网络系统所能实现的行为却是极其丰富多彩的。
1.2 神经网络的结构
多层向前神经网络是众多神经网络算法版本中比较著名、效果表现较好、比较典型的。该网络由以下几部分组成:输入层(input layer), 隐藏层 (hidden layers), 输入层 (output layers)。
上图是一个神经网络的的拓扑结构,以下是关于神经网络的说明:
- 每层由神经单元组成;
- 输入层(input layer)是由训练集的实例特征向量传入;
- 经过连接结点的权重(weight)传入下一层,一层的输出是下一层的输入;
- 隐藏层的个数可以是任意的,输入层是一层,输出层也是一层;
- 每个神经单元也可以被称作神经结点,根据生物学来源定义;
- 以上成为2层的神经网络(输入层不算);
- 一层中加权的求和,然后根据非线性方程转化输出;
- 作为多层向前神经网络,理论上,如果有足够多的隐藏层(hidden layers) 和足够大的训练集, 可以模拟出任何方程。
1.3 神经网络的结构设计
往往来说,我们设计神经网络结构的一般思路归纳如下:
- 使用神经网络训练数据之前,必须确定神经网络的层数,以及每层单元的个数;
- 特征向量在被传入输入层时通常被先标准化(normalize)到0和1之间 ,是为了加速学习过程;
- 离散型变量可以被编码成每一个输入单元对应一个特征值可能赋的值。比如:特征值A可能取三个值(a0, a1, a2), 可以使用3个输入单元来代表A。如果A=a0, 那么代表a0的单元值就取1, 其他取0;如果A=a1, 那么代表a1de单元值就取1,其他取0,以此类推;
- 神经网络不仅可以用来做分类(classification)问题,也可以解决回归(regression)问题。
- 对于分类问题,如果是2类,可以用一个输出单元表示(0和1分别代表2类),如果是多分类,每一个类别用一个输出单元表示,所以输入层的单元数量通常等于类别的数量;
- 对于回归问题,如果没有明确的规则来设计最好有多少个隐藏层,通常根据实验测试和误差,以及准确度来实验并改进。
1.4交叉验证算法
训练或者学习过程中,神经网络算法是如何判断输出层的结果是否正确的呢?答案是用的交叉验证算法,交叉验证算法有好几种,感兴趣的可以去百科里自行了解,这里介绍的是K折交叉验证。K折交叉验证的具体过程为,初始采样分割成K个子样本,一个单独的子样本被保留作为验证模型的数据,其他K-1个样本用来训练。交叉验证重复K次,每个子样本验证一次,平均K次的结果或者使用其它结合方式,最终得到一个单一估测。这个方法的优势在于,同时重复运用随机产生的子样本进行训练和验证,每次的结果验证一次,一般设k=10,10折交叉验证是最常用的。
1.5前向传播与反向传播算法
交叉验证过程中发现模型验证不正确该怎样处理呢?那就更新神经元的权重W和偏置b,引入正向传播和反向传播算法来解决训练参数的迭代和更新问题。过程如下:
- 通过迭代性的来处理训练集中的实例;
- 对比经过神经网络后输入层预测值(predicted value)与真实值(true value)之间,如果二者存在差异则进行反向传播算法调整;
- 反方向(从输出层=>隐藏层=>输入层)来以最小化误差(error)来更新每个连接的权重W和偏置b。
算法详细介绍如下:
输入:数据集,学习率l, 一个多层前向神经网络。
- 初始化权重W和偏向b: 随机初始化在-1到1之间,或者-0.5到0.5之间,每个单元有一个偏向。
- 对于每一个训练实例X,执行以下步骤:
由输入层向前传入:
对于一个完整的正向传播,其逻辑运算过程:
对于数据集中的任意输入,
对于神经网络算法的output,为
其中S型函数O满足:
3.根据误差Err反向传送:
- 对输出层:
- 对隐藏层:
- 权重更新:
- 偏向更新:
4.终止条件:
- 权重的更新低于某个阈值;
- 预测的错误率低于某个阈值;
- 达到预设一定的循环次数。
1.6反向传播算法的举例
参考一个哥们的博文的例子,有如下这么一个神经网络:
如上是进行一次神经算法正向传送后得到输出为0.474,跟实际值不一致,进行反向传播算法后,更新后的参数为:
2.神经网络算法的实现
搭建一个简单的神经网络,代码如下:
import numpy as np
def tanh(x):
''''双曲函数'''
return np.tanh(x)
def tanh_deriv(x):
''''双曲函数的导数'''
return 1.0 - np.tanh(x)*np.tanh(x)
def logistic(x):
'''逻辑函数'''
return 1/(1 + np.exp(-x))
def logistic_derivative(x):
''''逻辑函数的导数'''
return logistic(x)*(1-logistic(x))
class NeuralNetwork:
def __init__(self, layers, activation='tanh'):
"""
func:搭建一个神经网络算法;
input:训练集X,y,学习率l;
seld define:非线性函数的转化方法;
output:a。
"""
if activation == 'logistic':
self.activation = logistic
self.activation_deriv = logistic_derivative
elif activation == 'tanh':
self.activation = tanh
self.activation_deriv = tanh_deriv
self.weights = []
## 定义神经网络的层数
for i in range(1, len(layers) - 1):
self.weights.append((2*np.random.random((layers[i - 1] + 1, layers[i] + 1))-1)*0.25)
self.weights.append((2*np.random.random((layers[i] + 1, layers[i + 1]))-1)*0.25)
def fit(self, X, y, learning_rate=0.2, epochs=10000):
X = np.atleast_2d(X)
temp = np.ones([X.shape[0], X.shape[1]+1])
## 添加偏置
temp[:, 0:-1] = X
X = temp
y = np.array(y)
for k in range(epochs):
i = np.random.randint(X.shape[0])
a = [X[i]]
## 进行前向传播
for l in range(len(self.weights)):
## 计算每个隐藏层和输出层的输出
a.append(self.activation(np.dot(a[l], self.weights[l])))
## 计算误差函数Err,a的最后一个为输出层
error = y[i] - a[-1]
## 根据误差Err函数计算delta
deltas = [error * self.activation_deriv(a[-1])]
## 开始反向传播,从倒数第二层开始,根据deltas更新w和b
for l in range(len(a) - 2, 0, -1):
deltas.append(deltas[-1].dot(self.weights[l].T)*self.activation_deriv(a[l]))
deltas.reverse()
for i in range(len(self.weights)):
layer = np.atleast_2d(a[i])
delta = np.atleast_2d(deltas[i])
## 更新w
self.weights[i] += learning_rate * layer.T.dot(delta)
def predict(self, x):
x = np.array(x)
temp = np.ones(x.shape[0]+1)
temp[0:-1] = x
a = temp
for l in range(0, len(self.weights)):
a = self.activation(np.dot(a, self.weights[l]))
return a
if __name__ == '__main__':
nn = NeuralNetwork([2, 2, 1], 'tanh')
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([0, 1, 1, 0])
nn.fit(X, y)
for i in [[0, 0], [0, 1], [1, 0], [1, 1]]:
print(i, nn.predict(i))