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

终于要开始实战阶段了,这里我们要实现的是一个手写数字的识别,这个可以应用于邮局的邮编号码识别。要实现神经网络学习,我们先需要一个手写数字图片的数据集,这个数据集我们用的是美国国家标准与技术研究所的子数据集—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函数的导函数。

希望有志同道合的小伙伴关注我的公众平台,欢迎您的批评指正,共同交流进步。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值