全连接神经网络
概念
使用非线性函数变换的多层感知机又称深度前馈网络、 深度神经网络 。
这种网络的目的是使得整个模型近似于某个函数 。在前馈神经网络中定义了一个,并且学习参数的值,使得它能够到最佳的函数近似。
前馈神经网络结构:
网络结构: 许多函数复合在一起表示,
链式结构是神经网络中最经常用的结构
深度: 链的全部长度
输出层: 前馈神经网络的最后一层
隐藏层: 中间层
宽度: 隐藏层的维数决定模型的宽度
神经元(单元) : 表示一个从向量到标量的函数,持有一定的模型参数值。接受
训练数据中的标签只是指导了最后一层的输出,由学习算法来制定中间层产生的输出
为什么要使用非线性函数进行变换?
**函数引入了非线性的拟合能力。
不加非线性**函数的多层感知器只能解决多条直线相交划分的类的问题,而且层数越多,相对复杂。相反,加入了非线性**函数的多层感知器可以解决分界线为曲线、圆等分类问题,从而能够真正解决非线性的分类问题。
我们通常定义: 每一个输入数据为n维向量,
为 第 l 层 第 i 个神经元 与 第 j 个 输入相连的权重,第 l 层中每个神经元的权重为 k 维向量, k 为 (l -1) 层神经元的数量
第 l 层中所有 p 个神经元的权重组成矩阵
反向传播算法 (Back propagation)
反向传播概念讲解
反向传播算法的核心通过比较输出 y 和期望输出t , 向输入层方向逐层计算梯度并更新权值,与前馈运算正好相反。
如果训练得到的结果和期望值不一致,说明权重 w 不恰当,这里使用的调整的手法就是反向传播。
向前解释: 从输入到输出的方向
向后解释 : 从输出到输入的方向
外国文献: bottom 输入,top 输出 。 top to bottom 从输入到输出
推导及公式
写**函数或者代价函数的话 ,要理解反向传播的概念。
– 损失函数定义
– **函数定义
– 经过**函数的隐层输出
– 未经过**函数的隐层计算
– 神经元固有的参数
推导出第一个重要的公式:
(1)
计算最后一层神经元的固有参数的更新
固有参数层与层之间关系重要公式
(2)
当前神经元固有函数的更新是以下两部分的乘积
1.下一层所有神经元与当前神经元和下一层神经元的权重相乘之和
2.当前神经元输出的**函数的导数
损失函数对于权重、偏置的偏导数,求取变化量的重要公式:
(3)
(4)
权重、偏置的更新公式同单个神经元公式:
感知器的梯度下降和神经网络的反向传播方法的对比:
反向传播算法也是利用了梯度下降的方法,从输出层逐层向前进行权值、偏置的更新。每轮迭代每一层更新一次。感知器一轮迭代这个神经元更新一次。
神经网络的优化
梯度消失 (vanishing gradient)
使用 sigmoid 函数 时,它的导数为
sigmoid 导数最大值为0.25,它是关于y轴对称的二次曲线 每一次传播都会是原来的 ,越靠近输入端的层上的越小。因此,越靠近输入层的神经元对应的权重更新就会越小,很可能就几乎为0。
zig zag 现象
由于sigmoid **函数输出为正数,由之前的公式可以得到 经过**函数的隐层输出 。同一层某个神经元的 是固定的,对于前一层连接这个神经元的权重的更新值。所以 总是同正,或者同负。 不可能出现某个权重增加,某个权重减小这种情况。 如果是有 他们的更新的梯度 总会出现在1,3象限。
所以梯度更新有可能出现拉链状更新的情况,由下图可知。这样会极大增长优化时长。
神经元死亡
ReLu 函数如果落入x轴的负半轴上。神经元输出为 0。 会造成:
- 下一层神经元权重不会更新
- 由于输出是0,自身的**函数的导数也为0,这样对应层的 也为0。对应权重更新公式可知。自身的所有权重不会得到更新。
工业经验,如果学习率设置过大。神经元死亡过多,基本就没办法挽救了。所以可以根据神经元死亡数量来调整学习率。
反向传播代码实现
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def sigmoidDerivationx(y):
return y * (1 - y)
if __name__ == '__main__':
# 初始化一些参数
alpha = 0.5
numIter = 1000000 #迭代次数
w1 = [[0.15, 0.20], [0.25, 0.30]] # Weight of input layer
w2 = [[0.40, 0.45], [0.50, 0.55]]
# print(np.array(w2).T)
b1 = 0.35
b2 = 0.60
x = [0.05, 0.10]
y = [0.01, 0.99]
# 前向传播
z1 = np.dot(w1, x) + b1 # dot函数是常规的矩阵相乘
a1 = sigmoid(z1)
z2 = np.dot(w2, a1) + b2
a2 = sigmoid(z2)
for n in range(numIter):
# 反向传播 使用代价函数为C=1 / (2n) * sum[(y-a2)^2]
# 分为两次
# 一次是最后一层对前面一层的错误
delta2 = np.multiply(-(y-a2), np.multiply(a2, 1-a2))
# for i in range(len(w2)):
# print(w2[i] - alpha * delta2[i] * a1)
#计算非最后一层的错误
# print(delta2)
delta1 = np.multiply(np.dot(np.array(w2).T, delta2), np.multiply(a1, 1-a1))
# print(delta1)
# for i in range(len(w1)):
# print(w1[i] - alpha * delta1[i] * np.array(x))
#更新权重
for i in range(len(w2)):
w2[i] = w2[i] - alpha * delta2[i] * a1
for i in range(len(w1)):
w1[i] = w1[i] - alpha * delta1[i] * np.array(x)
#继续前向传播,算出误差值
z1 = np.dot(w1, x) + b1
a1 = sigmoid(z1)
z2 = np.dot(w2, a1) + b2
a2 = sigmoid(z2)
print(str(n) + " result:" + str(a2[0]) + ", result:" +str(a2[1]))
# print(str(n) + " error1:" + str(y[0] - a2[0]) + ", error2:" +str(y[1] - a2[1]))
参考博客: