神经网络与深度学习(四)—手写数字识别示例上

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

这个数据集总共有60000个数据,也就是有60000张图片,每张图片的像素是28*28。每个像素点代表一个灰度值。今天先把主要程序讲一遍,明天再试着运行程序。话不多说,开讲:

1)定义类

# -*- coding: utf-8 -*-#!/usr/bin/env python3import randomimport numpy as npclass 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:/".format(j,self.evaluate(test_data),n_test)) '''这里通过索引来格式化字符串''' else: print("Epoch 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函数的导函数。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180407G17T9H00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券