终于要开始实战阶段了,这里我们要实现的是一个手写数字的识别,这个可以应用于邮局的邮编号码识别。要实现神经网络学习,我们先需要一个手写数字图片的数据集,这个数据集我们用的是美国国家标准与技术研究所的子数据集—mnist。以下是取自该数据集的一些图像:

这个数据集总共有60000个数据,也就是有60000张图片,每张图片的像素是28*28。每个像素点代表一个灰度值。今天先把主要程序讲一遍,明天再试着运行程序。话不多说,开讲:
1)定义类
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
import random
import numpy as np
class Network(object):
def __init__(self,sizes):
self.num_layers=len(sizes)
self.sizes=sizes
self.biases=[np.random.randn(y,1) for y in sizes[1:]]
'''创建一个偏差向量'''
self.weights=[np.random.randn(y,x) for x,y in zip(sizes[:-1],sizes[1:])]
'''创建一个权重偏差矩阵''这里我们首先引入两个函数模块random和numpy,前者是用于生成随机数,后者是用来矩阵运算的模块。
然后,定义一个Network class。初始化属性,该神经网络的层数;神经网络的大小;初始化偏差向量;初始化权重矩阵。
这里的难点是初始化两个参数,这里你会看到zip函数两个for循环,我们先讲zip函数。这个函数是将若干列表中的元素按位置组成元组的列表,然后输出。示例如下:
>>> a = [1,2,3] >>> b = [4,5,6] >>> c = [4,5,6,7,8] >>> zipped = zip(a,b) # 打包为元组的列表 [(1, 4), (2, 5), (3, 6)] >>> zip(a,c) # 元素个数与最短的列表一致 [(1, 4), (2, 5), (3, 6)] >>> zip(*zipped) # 与 zip 相反,可理解为解压,返回二维矩阵式 [(1, 2, 3), (4, 5, 6)]
这里的sizes是[784,30,10],于是变成了zip([784,30],[30,10])。接下来是randn(x1,x2,....)函数,这个函数的意思是生成x1*x2*....数组。这样我们就生成了30*784和10*30两个矩阵。
2)前馈算法(feedforward)
def feedforward(self,a):
for b,w in zip(self.biases,self.weights):
a=sigmoid(np.dot(w,a)+b)
return a
这里通过for循环将weight和biases从矩阵中取出来,然后再赋给dot点乘求和,再用sigmoid函数激励。
3)随机梯度下降算法(SGD)
def SGD(self,training_data,epochs,mini_batch_size,eta,test_data=None):
if test_data:n_test=len(test_data)
'''测试集大小'''
n=len(training_data)
'''训练集大小'''
for j in range(epochs):
'''循环次数'''
random.shuffle(training_data)
'''打乱数据'''
mini_batches=[training_data[k:k+mini_batch_size] for k in range(0, n, mini_batch_size)]
for mini_batch in mini_batches:
self.update_minibatch(mini_batch,eta)
'''将训练数据全都分成小的小的数据集当作列表更新,还有学习率'''
if test_data:
print("Epoch{0}:{1}/{2}".format(j,self.evaluate(test_data),n_test))
'''这里通过索引来格式化字符串'''
else:
print("Epoch{0} complete".format(j))
'''数据已经检索完毕'''这里唯一要注意的就是,这里我们用切片将数据分成了小的数据集。这里第二个for循环里面,取小数据集大小为步长,用于切分数据集。
4)更新小的数据集
def update_minibatch(self,mini_batch,eta):
nabla_b=[np.zeros(b.shape) for b in self.biases]
nabla_w=[np.zeros(w.shape) for w in self.weights]
'''初始化生成相对应的偏导数矩阵'''
for x,y in mini_batch:
delta_nabla_b,delta_nabla_w=self.backprop(x,y)
'''更新偏导数矩阵'''
nabla_b=[nb+dnb for nb,dnb in zip(nabla_b,delta_nabla_b)]
nabla_w=[nw+dnw for nw,dnw in zip(nabla_w,delta_nabla_w)]
'''求偏导数和'''
self.weights=[w-(eta/len(mini_batch))*nw for w,nw in zip(self.weights,nabla_w)]
self.biases=[b-(eta/len(mini_batch))*nb for b,nb in zip(self.biases,nabla_b)]
'''公式20和21'''
这里,我们先用zeros初始化代价函数关于weight和baises的偏导数矩阵,接下来用BP算法更新,接下来再计算偏导数和,最后根据下图公式来更新得到weights和biases:
5)BP算法更新梯度
def backprop(self,x,y):
nabla_b=[np.zeros(b.shape) for b in self.biases]
nabla_w=[np.zeros(w.shape) for w in self.weights]
'''初始化偏置和权重矩阵'''
activation=x
activations=[x]
zs=[]
'''第一步,初始化每层的激励值'''
for b,w in zip(self.biases,self.weights):
z=np.dot(w,activation)+b
'''点乘'''
zs.append(z)
activation=sigmoid(z)
activations.append(activation)
'''第二步,正向更新(feedforward)激励值'''
delta=self.cost_derivative(activations[-1],y)
'''第三步,求误差error'''
sigmoid_prime(zs[-1])
nabla_b[-1]=delta
nabla_w[-1]=np.dot(delta,activations[-2].transpose())
'''第五步,先求出最后一层的w和b,transpose矩阵转置'''
for l in range(2,self.num_layers):
'''第四步,从倒数第二层开始更新'''
z=zs[-l]
sp=sigmoid_prime(z)
delta = np.dot(self.weights[-l+1].transpose(),delta)*sp
'''从后一层开始更新,计算delta(error)'''
nabla_b[-l]=delta
nabla_w[-l]=np.dot(delta,activations[-l-1].transpose())
'''第五步,更新偏导数'''
return (nabla_b,nabla_w)这里是BP算法最重要的地方。前面简单的注释都可以看懂,最后面的一段结合以下公式:
6)计算准确率和代价函数求偏导
def evaluate(self,test_data):
test_results=[(np.argmax(self.feedforward(x)),y)
for (x,y) in test_data]
'''输出测试集的神经网络输出值和期望值'''
return sum(int(x==y)for (x,y) in test_results)
'''返回结果中的正确数总个数(布尔值和)'''
def cost_derivative(self,output_activations,y):
return (output_activations-y)
'''代价函数求偏导'''
这里需要讲的是函数numpy.argmax(),这个函数是用来输出列表中最大值的索引,如下:

最大值是8,所以其索引是4,a[4]=8。
7)其他
def sigmoid(z):
return 1.0/(1.0+np.exp(-z))
def sigmoid_prime(z):
return sigmoid(z)*(1-sigmoid(z))这两个就很简单了,第一个是sigmoid 函数,第二个是sigmoid函数的导函数。
希望有志同道合的小伙伴关注我的公众平台,欢迎您的批评指正,共同交流进步。


本文介绍了一个基于神经网络的手写数字识别系统实现过程,重点讲解了使用MNIST数据集进行训练的方法,包括网络架构设计、前馈算法、随机梯度下降算法及BP算法等关键步骤。
4204

被折叠的 条评论
为什么被折叠?



