前面两节,讲述了梯度下降和方向传播的原理,这里我通过mnist训练来讲述下python的实现方法
头文件
numpy用于矩阵运算,random用于数据集的shuffle,mnist_loader 用于加载数据集
import numpy as np
import random
import mnist_loader
初始化网络
其中参数是sizes 格式是[784,30,10] 代表每层神经元的个数,self.weights 格式[34*784],[10*34],self.biases格式是[34*1]和[10*1] 前者代表每两层神经元之间连线的权重,后者代表每个神经元上的偏向值。
def __init__(self,sizes):
self.layers = len(sizes)
self.weights = [np.random.randn(x,y) for (x,y) in zip(sizes[1:],sizes[:-1])]
self.biases = [np.random.randn(x,1) for x in sizes[1:]]
SGD
原理部分第二节已经说过了,我们来解读一下参数
train_data:我们输入的训练集可以抽象成[(x,y),(x1,y1)....(xn,yn)]
epoches:训练的轮数由我们自己指定
mini_size:每次随机抽样训练的训练样本数量
eta():表示learning rate 学习率,可以理解为步长
test_data:格式与train_data相同,用于测试(注意本实例中没有介绍验证集)
程序中先对数据全集进行shuffle打乱操作,之后按mini_size 进行分堆,下面分别对每个mini_batch 进行训练,也就是随机抽样的过程,这样做极大的加快了学习效率。
def SGD(self,train_data,epoches,mini_size,eta,test_data):
if test_data: n_test = len(test_data)
n = len(train_data) #表示 训练样本数量
for epoch in range(epoches):
random.shuffle(train_data)
mini_batches = [train_data[k:k+mini_size] for k in xrange(0,n,mini_size)] #xrange 返回的是生成器
for mini_batch in mini_batches:
self.update_minibatch(mini_batch,eta)
if test_data:
print "epoches {0} acc {1}/{2} ".format(epoch,self.cal_acc(test_data),n_test)
else:
print "no test data"
update_minibatch 主要完成的操作是,求出样本集中每个实例的偏导数累加求和,在根据下面的公式进行公式对weights和biases进行更新
def update_minibatch(self,mini_batch,eta):
nabla_b = [np.zeros(b.shape) for b in self.biases] #随机梯度下降算法,用于存储我们指定的minibatch中的数据的bias的总和
nabla_w = [np.zeros(w.shape) for w in self.weights]
for x,y in mini_batch:
new_nabla_b ,new_nabla_w = self.back(x,y)
nabla_w = [nw+w for nw,w in zip(new_nabla_w,nabla_w)]
nabla_b = [nb+b for nb,b in zip(new_nabla_b,nabla_b)]
self.weights = [w-((eta/len(mini_batch))*nabla_w) for w,nabla_w in zip(self.weights,nabla_w)]
self.biases = [b-((eta/len(mini_batch))*nabla_b) for b,nabla_b in zip(self.biases,nabla_b)]
反向传播
梯度下降和反向传播的核心就是在于学习参数的偏导数计算,下图是反向传播求偏导数的过程。
1,首先初始化各层的参数为zero 和Pytorch 中optimizer.zero_grad() 操作大体相似,避免之前梯度的累加,用于存储各个层偏导数结果
nabla_b = [np.zeros(b.shape) for b in self.biases] #随机梯度下降算法,用于存储我们指定的minibatch中的数据的bias的总和
nabla_w = [np.zeros(w.shape) for w in self.weights]
2,z 在原始的公式中相当于 多个 的累加和,待激活的值, 第一层默认等于原始输入的图片数据的tensor值
z = np.dot(w,activation)+b
activation=,都是和神经元的个数一致。
3,进行前向传播,计算所有的z和activation的值
for w,b in zip(self.weights,self.biases):
# print w.shape,b.shape
z = np.dot(w,activation)+b
zs.append(z)
activation = sigmoid(z)
activations.append(activation)
4,根据公式计算最后一层的error
delta = cost(activations[-1],y)*sigmoid_prime(zs[-1])
5,更新最后一层的bias和weight ,其中,bias等于最后一层的error值,weight是最后一层的error值与前一层激活值的转置乘积
nabla_b[-1] = delta
#delta 目前可能是10*1 的向量,而activation[-2]可能是784*1 无法直接点乘 需要转置后者 最终是10个784维向量 内部相加输出 10*1
nabla_w[-1] = np.dot(delta,activations[-2].transpose())
6,根据链式求导法则 ,反向更新各层的weight值与bias值 ,并最终以(nabla_w,nable_w)形式返回给随机梯度下降算法(update_mini_batch)的进行,全局weights和biases的更新
for l in range(2,self.layers):
delta = np.dot(self.weights[-l+1].transpose(),delta)*sigmoid_prime(zs[-l])
nabla_b[-l] =delta
nabla_w[-l] = np.dot(delta,activations[-l-1].transpose())
return (nabla_b,nabla_w)
准确率计算
测试集是10000个(x,y),首先进行前向传播和softmax,获取最终预测的label值,并计算预测值与真实值的数量来计算准确率。
def cal_acc(self,test_data):
test_set = [(self.feedforward(x),y) for x,y in test_data]
return np.sum(int(np.argmax(x))==y for (x,y) in test_set)
def feedforward(self,x):
for w,b in zip(self.weights,self.biases):
x = np.dot(w,x)+b
x = sigmoid(x) #一定不能忘记激活
下面是工具方法,包含了的导数:
def sigmoid_prime(z):
return sigmoid(z)*(1-sigmoid(z))
激活函数 :
def sigmoid(z):
return 1/(1+np.exp(-z))
源代码请移步 坚持一件事或许很难,但坚持下来一定很酷!^_^