下图为常见的神经网络
神经网络包含输入层,隐含层,输出层,上图就是4层神经网络(或称3层神经网络输入层不计入层数)。
下面声明一些符号和向量。
假设有m个输入样本:
令输入向量为:
令输出向量为:
第l隐含层输出为:
其sl为第l层神经元的个数。
设W(l)ij为从l-1层第j个神经元与l层第i个神经元之间的连接权重;b(l)i为第l层第i个神经元的偏置,那么:
zi(l)为l层第i个神经元的输入,f(⋅)为神经元的激活函数。通常在多层神经网络中采用非线性激活函数,而不是用线性激活函数,因为采用基于线性激活函数的多层神经网络本质上还是多个线性函数的叠加,其结果仍然为一个线性函数。如果神经网络结果仍然为一个线性函数,那不能就不能针对各种各样的需求生成各类复杂非线性的函数,其神经网络功能也大大减弱。从另一方面来说与其构建这么复杂的网络结构生成线性函数还不如直接用线性回归更快。
有m个样本,神经网络共有L层(忽略输入层),第i个输入样本x(i)对应预计输出为H(i)。
对于给定的m个训练样本,E(i)为单个样本误差,E为m个训练样本的误差。定义误差函数为:
BP算法关键在于求各层的权值参数偏导数。下面对偏导数的过程进行推导。先来看单个样本。
对于输出层:(sl 等于p)
对于L-1层:
由上可推,第l层(2≤l≤L−1)的权值和偏置的偏导可以表示为:
==如果你能看懂第2节,那么这节会相对于简单,实质上在第二节基础上加上向量化,构成矩阵进行计算。==对样本进行训练时,有两种常见的方法:一种是每次只对单个样本进行训练,另一个是多个样本同时进行训练。多样本训练有多种方法,在这里只分析全部样本同时进行训练。随着神经网络处理一个较为复杂问题时,每次只对一个样本进行训练往往效率会很低。多个样本进行训练,构建一系列矩阵来进行一系列矩阵运算,充分利用CPU和GPU的并行运算的能力从而加快训练速度,所以很有必要神经网络的向量化。为了方便分析,构建一个2层神经网络。
若是单样本的输入:
若是多个样本的输入记为A0:
记输入有n个输入,第一层有n1个神经元,第二层有n2个神经元,但是有m个样本,所以需要一个n1xm的矩阵来存储从输入层传过来的数据,记该矩阵为A1。
函数f为启动函数,Z1为偏置与输入的累加和。由此就可以获得m个样本第一层的神经元的输出。同理就可以获得第二层A2
经过一次前向传播后,则需要进行反向更新权值。
对于第2层,即输出层:
对输出矩阵记为A2,那么误差err
由第2节所推的公式,w和b参数的公式有:
注:上两式公式有1/m,因为m个样例,所以结果你必须对m个结果求以平均。h表示第2层的神经元输出,a表示第一层的神经元输出。看懂这里的表达,可以自己对这些矩阵计算一下,结果就很快会懂了。
其它层的向量化,相信在你看懂前面两节后,自己也可以推导出来,我在这里就不重复造车了。
'''
本程序能构建任意层的神经网络
执行分为两部分:
第一部分正向传播:
第二部分反向更新
create by Jiang at 2019.07.08
'''
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
keep_prob = 0.8
def sigmoid(x):
return np.tanh(x)
# 函数sigmoid的派生函数
def dsigmoid(x):
return 1.0 - np.multiply(x,x)
def mnist_extraction():
'''
该函数,获取数据。数据我已经下好了。
没获取的情况下,代码会有略微不同,自己可以百度一下。
:return: 返回加载好的数据
'''
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
train_nums = mnist.train.num_examples
validation_nums = mnist.validation.num_examples
test_nums = mnist.test.num_examples
print('MNIST数据集的个数')
print(' >>>train_nums=%d' % train_nums, '\n',
'>>>validation_nums=%d' % validation_nums, '\n',
'>>>test_nums=%d' % test_nums, '\n')
'''2)获得数据值'''
train_data = mnist.train.images # 所有训练数据
val_data = mnist.validation.images # (5000,784)
test_data = mnist.test.images # (10000,784)
print('>>>训练集数据大小:', train_data.shape, '\n',
'>>>一副图像的大小:', train_data[0].shape)
'''3)获取标签值label=[0,0,...,0,1],是一个1*10的向量'''
train_labels = mnist.train.labels # (55000,10)
val_labels = mnist.validation.labels # (5000,10)
test_labels = mnist.test.labels # (10000,10)
# test_data = mnist.test.images
# print('>>>训练集标签数组大小:', train_labels.shape, '\n',
# '>>>一副图像的标签大小:', train_labels[1].shape, '\n',
# '>>>一副图像的标签值:', train_labels[0])
#
# '''4)批量获取数据和标签【使用next_batch(batch_size)】'''
# batch_size = 100 # 每次批量训练100幅图像
# batch_xs, batch_ys = mnist.train.next_batch(batch_size)
# print('使用mnist.train.next_batch(batch_size)批量读取样本\n')
# print('>>>批量读取100个样本:数据集大小=', batch_xs.shape, '\n',
# '>>>批量读取100个样本:标签集大小=', batch_ys.shape)
return mnist
class NN():
def __init__(self,layers,mnist):
L = len(layers)
# 设置神经元权重参数和偏置参数
self.W = {}
self.b = {}
for l in range(1,L):
# print(layers[l])
self.W["W" + str(l)] = np.mat(np.random.randn(layers[l], layers[l - 1]) * 0.1)
self.b["b" + str(l)] = np.mat(np.random.randn(layers[l],1) * 0.1)
self.n = {}
for l in range(0,L):
self.n["n" + str(l)] = layers[l]
self.A = {}
self.Z = {}
self.cache = {}
# 将数据写入类中
self.mnist = mnist
def forward_activation_02(self,L,flag):
'''
:param L: 神经网络的层数
:param flag: flag为标记,是否启用dropOut正则化,flag = 1启用dropOut正则化
如果你熟悉,dropout正则化,可以令flag = 0,关闭它。
:return: 返回大致误差
'''
n,m = self.inputs.shape
if n != self.n['n0']:
raise ValueError('与输入层节点数不符')
# 初始化输入
self.A["A0"] = self.inputs
for l in range(1,L):
if flag == 0 or l == 1 or l == L-1:
self.Z["Z" + str(l)] = self.W["W" + str(l)] * self.A["A" + str(l-1)] + self.b["b" + str(l)]
self.A["A" + str(l)] = sigmoid(self.Z["Z" + str(l)])
else:
#启用dropout正则化
self.d = np.random.rand(self.A["A" + str(l-1)].shape[0],self.A["A" + str(l-1)].shape[1])
self.d = self.d < keep_prob
self.A["A" + str(l-1)] = np.multiply(self.A["A" + str(l-1)],self.d)
self.A["A" + str(l-1)] /= keep_prob
self.Z["Z" + str(l)] = self.W["W" + str(l)] * self.A["A" + str(l - 1)] + self.b["b" + str(l)]
self.A["A" + str(l)] = sigmoid(self.Z["Z" + str(l)])
# 更新cache
for l in reversed(range(1,L)):
if l == L-1:
self.cache["C" + str(l)] = np.multiply(self.A["A" + str(l)] - self.output,dsigmoid(self.A["A" + str(l)]))
else:
self.cache["C" + str(l)] = np.multiply(self.W["W" + str(l+1)].T*self.cache["C" + str(l+1)],
dsigmoid(self.A["A" + str(l)]))
err = np.sum(np.abs(self.A["A"+str(L-1)]-self.output))
return 1.0/2*err*err
def backPropagate_02(self,learning_rate,L):
alpha = learning_rate
m = self.inputs.shape[1]
for i in range(L):
l = L - i - 1
if l > 0:
self.b['b' + str(l)] = self.b['b' + str(l)] - alpha * 1.0/m*(self.cache["C" + str(l)]*np.ones((m,1)))
self.W['W' + str(l)] = self.W['W' + str(l)] - alpha * 1.0/m*(self.cache["C" + str(l)] * self.A["A" + str(l-1)].T)
def init_prameter(self,batch_size):
# 每次批量训练batch_size幅图像
batch_xs, batch_ys = self.mnist.train.next_batch(batch_size)
self.inputs = np.mat(batch_xs).transpose()
self.output = np.mat(batch_ys).transpose()
def train(self,iterations,learning_rate,L,batch_size):
# 批处理训练
m = 100 # m为批处理的次数
err = 0.0
for i in range(iterations):
for j in range(m):
self.init_prameter(batch_size)
err += self.forward_activation_02(L,1)
self.backPropagate_02(learning_rate,L)
if i % 100 == 0:
# print(self.A["A" +str(L-1)])
print('第%d次迭代误差----%lf'%(i,err))
err = 0.0
def run(self,iterations,learning_rate,L,batch_size):
self.train(iterations,learning_rate,L,batch_size)
def test(self,L):
batch_xs, batch_ys = self.mnist.test.next_batch(100)
self.inputs = np.mat(batch_xs).transpose()
self.output = np.mat(batch_ys).transpose()
self.forward_activation_02(L,0)
print("100个测试样本的实际结果:")
print(self.output.transpose())
print("100个测试样本的预测结果:")
print(self.A["A"+str(L-1)].transpose())
if __name__ == '__main__':
# 实例,构建一个三层的神经网络处识别手写字体。
minst = mnist_extraction()
#各层网络神经元的参数
layers = [784,16,10]
L = len(layers)
nn = NN(layers,minst)
nn.run(20500,0.2,L,100)
nn.test(L)
BP神经网络推导过程详解-Alex
AI遇见机器学习关注专栏