深度学习分享3:bp神经网络代码分析(底层代码)

分析

写出网络结构

class Network:#定义一个神经网络对象
    def __init__(self,sizes):#初始参数,sizes是每层的神经元个数
        self.sizes=sizes
        self.num_layers=len(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:])]

sizes在这里是[784,30,10]的列表,有一个输入层,一个隐藏层,一个输出层,三层就可以解决很多复杂的问题,图像是28*28的大小,将其变成向量,将会有784个输入,隐藏层设置为30个,因为标记有10个数字,所以输出层设置为10个。
np.random.randn(y,1)生成一组y行1列具有标准正态分布的数组。
biases是偏置,python里面都是以0作为开始的索引,在第一层是没有偏置的,网上有很多神经网络的图可以参考,所以对于biases来说,第二层和第三层才有,而weights是中间的连接权重,第一层到第二层,倒数第二层到最后一层,下面是具体例子

import numpy as np
sizes=[3,2,5]
weights=[np.random.randn(y,x) for x,y in zip(sizes[:-1],sizes[1:])]
biases=[np.random.randn(y,1) for y in sizes[1:]]
biases


weights

[array([[ 1.93644392],
        [-0.58031346]]),
 array([[ 0.78745218],
        [ 1.36421958],
        [-2.63017445],
        [-0.64817633],
        [-0.2543122 ]])]


[array([[-1.50742591, -1.04597376,  0.38083617],
        [-2.3646064 , -1.47218182,  1.65298773]]),
 array([[ 0.61369543,  0.92788957],
        [ 0.34476232,  0.25176812],
        [-1.41325467, -1.24151791],
        [ 0.08862834, -0.17967047],
        [ 1.57842794,  0.54186952]])]

可以看出,当sizes是[3,2,5]时weights,biases是三维阵列,至于规律应该很明显,weights里面有32和25的两个阵列。random起到一个初始化的作用,我们的最终目的就是求出weights和biases的网络,然后用这个网络来预测。

前向传播

def feedforward(self,a):
        for w,b in zip(self.weights,self.biases):
            a=sigmoid(np.dot(w,a)+b)
            return a

输入一个a,a是一个向量,a通过网络层层运算会得到最终的结果。比如在我们做的项目中,train_data是一个有着60000个图像的训练集,每个样本都是28*28的灰度图像,每个点的范围是0到255,每幅图像对应一个0到9的标签。
下载手写数字的代码如下:

import tensorflow as tf
mnist=tf.keras.datasets.mnist
train_data,test_data=mnist.load_data()
print(type(train_data))
print(type(train_data[0]))
print(train_data[0].shape)
print(train_data[1].shape))

<class 'tuple'>
<class 'numpy.ndarray'>
(60000, 28, 28)
(60000,)

其中train_data是一个元祖(tuple),第一个索引值是一个阵列,形状是(60000, 28, 28)可以叫做train_x,第二个索引值也是阵列(60000,)可以叫做train_y,接下来的工作大概就是把train_x喂进去,通过网络前向传播得到一个数字,这个数字与train_y比对,得到一个损失函数,常用的有均方误差MSE和交叉熵函数,这个损失函数反向传播对权重和偏置求偏微分,运用梯度下降法更新权重和偏置,使得误差越来越小,不断迭代,最终达到损失函数的极小值,在这个过程中涉及到很多细节,反向传播应该是最难的一个环节,可能出现过拟合或者欠拟合等情况。
举个例子,我们人的大脑有一千亿个神经元,这比一般的bp神经网络,卷积神经网络等多多了,给人狮子老虎大象的图片各一张,并告诉他对应的名字,下次再给他一张狮子的图片,他一般不会认成大象,但有可能认成老虎,然后纠正他,这就是一个减小损失函数的过程,他大脑的轴突树突胞体神经元连接粗细等等发生变化,这就是更新偏置和权重的过程,训练的越多,他的印象就越深刻,大脑的变化就越小,最终他能一口说出对应关系。
顺便提一下,权重越多模型越复杂,需要的训练样本就越多,人脑有千亿神经元,但是给极少的样本就可以训练出很好的效果,一般认为权重值很小,大脑每次变化很小,而且规范化的能力极强,规范化是优化神经网络的一种重要的方法。

 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_mini_batch(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))

这段代码是network对象的一种反向传播方法,实质上就是一个不断更新权重和偏置的过程,并且会输出更新了多少次(epoch)和对于测试集的正确率,SDG是随机梯度下降法的意思,如果把测试集一下子全输入网络运算量很大,噪声也是一个问题,但是如果把测试集分批输入的话会好很多,SDG里面的self.update_mini_batch(mini_batch, eta)函数才是随机梯度下降法的灵魂,里面有一些数学公式推导和符号规定,这里暂时把它看成一个黑匣子,功能就是更新偏置和权重,使损失函数越来越来小,而self.evaluate(test_data)这个函数的功能是计算有多少个测试集是正确的,用它除以测试集的总个数就是测试集的正确率了。test_data默认为None,可以修改,eta叫做学习率,是self.update_mini_batch(mini_batch, eta)里面的一个参数,一般取较小值,它是一个超参数,顺便说一下超参数,就是人工智能里面机器无法学习的数据,是人为设定的数据,举个例子,给不同的人同样的训练数据,他们所擅长的领域和学习的速率是不一样的,这大概就可以理解为天赋,超参数没设好就好比生下一个傻子,给再多的训练样本都学不好,设好了就是天才,而这个东西很多时候靠经验和实验或者猜想得出。
说完了这几个硬骨头,剩下的代码应该可以理解了,mini_batch_size是随机梯度下降法分组的大小,60000个数据,如果mini_batch_size是10就分为6000份,把10个样本输入进去取平均就行了,取平均其实需要对偏微分的过程有足够的了解,应该自己动手推一下,不然理解不了网络是怎么工作的。
!!!!!!!!!!!刚写的一堆东西忘记保存了😭

测试集判断

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)

其中self.feedforward(x)的输出是一个列向量,分别是十个数字的概率值,argmax是这些概率中最大概率的索引值,然后这个索引与标记值进行比较,如果相同就返回True,然后求和,再除以测试集总个数就得到了正确率。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值