深度学习与图像识别32 神经网络基础13  基于数值微分的反向传播

基于数值微分的反向传播

我们尝试使用基于数值微分的方式实现手写数字的识别,并且是使用mini_batch来提升计算性能,使用的优化方法是随机梯度下降法,这里需要补充一点的是:

随机指的是“随机选择数据源中的小批次”的意思,随机梯度下降法的英文名字就叫作SGD。

下面我们就来实现手写数字识别的神经网络,整体过程  相比之前的案例要复杂很多,不过不用担心,我们一步一步进行剖析,从之前学过的,易于理解的,容易实现的代码开始编写。

第一步,激活函数的定义。在前向传播上,我们主要是使用两类激活函数,ReLU和Softmax。这两个函数在之前已经详细讲解过了,在这里我们给出与其对应的Python实现代码。 激活函数ReLU的实现流程:

def _relu(in_data):

   return np.maximum(0,in_data)  

激活函数Softmax的实现流程:

def _softmax(x):

   if x.ndim == 2:

       c = np.max(x,axis=1)

       x = x.T - c #溢出对策

       y = np.exp(x) / np.sum(np.exp(x),axis=0)

       return y.T

   c = np.max(x)

exp_x = np.exp(x-c)

   return exp_x / np.sum(exp_x)  

第二步,损失函数以及数值微分的计算逻辑。这两个函数之前也已经详细讲解过了,在这里我们只给出相应的Python实现代码。

计算基于小批次的损失函数的损失值,其中,y代表真实值,p代表预测值。计算代码具体如下:

def cross_entropy_error(p, y):

   delta = 1e-7

batch_size = p.shape[0]

   return -np.sum(y * np.log(p + delta)) / batch_size  

数值微分的实现逻辑如下(对于不理解nditer使用方法的读者,可以参阅之前写的“补充概念:Np.nditer”的相关内容):

def numerical_gradient(f, x):

   h = 1e-4 # 0.0001

   grad = np.zeros_like(x)

   it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])

   while not it.finished:

       idx = it.multi_index

       tmp_val = x[idx]

       x[idx] = float(tmp_val) + h

       fxh1 = f(x) # f(x+h)

       x[idx] = tmp_val - h

       fxh2 = f(x) # f(x-h)

       grad[idx] = (fxh1 - fxh2) / (2*h)

       x[idx] = tmp_val #还原值

       it.iternext()return grad  

第三步,定义神经网络。 我们先来看下初始化类的代码,首先在类TwoLayerNet中定义第一个初始化函数,函数接受4个参数:

input_size代表输入的神经元个数;hidden_size代表隐藏层神经元的个数;output_size代表输出层神经元的个数;

最后的weight_init_std则是为了防止权重太大,其默认值为0.01。

那么对于手写数字识别MNIST 来说:input_size的值就可以设置为784,原因是28*28=784;而output_size则可以设置为10,因为其是一个10分类的问题(对应于数字0~9);对于hidden_size的设置则可以根据经验自行设置。

params是一个字典,里面存储的是权重以及偏移量的值,W1的形状是(input_size,hidden_size),W2的形状是(hidden_size,output_size),具体代码如下:

def __init__(self, input_size,

hidden_size, output_size, weight_init_std=0.01):

       #初始化权重

       self.params = {}

       self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)

       self.params['b1'] = np.zeros(hidden_size)

       self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)

self.params['b2'] = np.zeros(output_size)

接着我们来实现对应于这个TwoLayerNet类中的前向传播算法:前向传播的实现方式已在本章的前向传播中重点阐述过,其核心思想就是通过矩阵运算计算出结果,再通过激活函数丰富其“表达能力”,最后通过_softmax函数计算概率输出结果。

实现代码具体如下:

def predict(self, x):

   W1, W2 = self.params['W1'], self.params['W2']

   b1, b2 = self.params['b1'], self.params['b2']

   a1 = np.dot(x, W1) + b1

   z1 = _relu(a1)

   a2 = np.dot(z1, W2) + b2

p = _softmax(a2)

   return p  

接着,我们再来看下如何计算损失值。下面这段代码还是比较容易理解的,对于预测值来说,其结果就是通过前向传播计算得到的,然后调用函数cross_entropy_error,得到损失函数的损失值Loss,我们的目标就是使Loss不断减少,具体代码如下:

# x:输入数据, y:监督数据

   def loss(self, x, y):

       p = self.predict(x)

       return cross_entropy_error(p, y)  

接着,我们来看下如何实现梯度下降。值得注意的一点 是,参数W是一个伪参数,另外因为名字重复问题,numerical_gradient函数与类中的numerical_gradient重名了,类中该函数调用的是我们之前实现的数值微分的函数,而非类函数自调用。

实现代码具体如下:

# x:输入数据, y:监督数据

   def numerical_gradient(self, x, y):

       loss_W = lambda W: self.loss(x, y)grads = {}

       grads['W1'] = numerical_gradient(loss_W, self.params['W1'])

       grads['b1'] = numerical_gradient(loss_W, self.params['b1'])

       grads['W2'] = numerical_gradient(loss_W, self.params['W2'])

       grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

       return grads  

最后,我们来实现准确度 函数。这个实现方式比较简单,对于p来说就是预测值的矩阵,我们取每一行的最大值的索引与真实值中每一行最大值的索引,如果两者的值相同,则说明预测准确。

实现代码具体如下:

def accuracy(self, x, t):

   p= self.predict(x)

   p = np.argmax(y, axis=1)

   y = np.argmax(t, axis=1)

   accuracy = np.sum(p == y) / float(x.shape[0])

return accuracy  

第四步,查看损失值。 首先,我们通过PyTorch导入MNIST数据源,这部分代码在此就不再赘述了。

接着,我们需要定义训练集和测试集。值得注意的是,通过PyTorch导入的标签并不是one-hot encoding格式,而是需要我们通过代码自行转换,转换逻辑之前已经给出,实现代码具体如下:

x_train = train_dataset.train_data.numpy().reshape(-1,28*28)

y_train_tmp = train_dataset.train_labels.reshape(train_dataset.train_labels.shape[0],1)

y_train = torch.zeros(y_train_tmp.shape[0], 10).scatter_(1, y_train_tmp, 1).numpy()

x_test = test_dataset.test_data.numpy().reshape(-1,28*28)

y_test_tmp = test_dataset.test_labels.reshape(test_dataset.test_labels.shape[0],1)

y_test = torch.zeros(y_test_tmp.shape[0], 10).scatter_(1, y_test_tmp, 1).numpy()  

下面我们来初始化手写的神经网络以及一些超参数然后输出Loss。如果我们观察到Loss值在不断下降,则说明我们的代码是有效的!值得注意的一点是,下述程序中使用的np.random.choice函数是随机选择一个批次的数据的,因此可能会选择到与之前批次重复的数据!实现代码具体如下:

#超参数

iters_num = 1000                #适当设定循环的次数

train_size = x_train.shape[0]

batch_size = 100

learning_rate = 0.001

network = TwoLayerNet(input_size = 784,hidden_size=50,output_size=10)

for i in range(iters_num):

   batch_mask =np.random.choice(train_size,batch_size)

   x_batch = x_train[batch_mask]

   y_batch = y_train[batch_mask]

   grad = network.numerical_gradient(x_batch,y_batch)

   for key in ('W1','b1','W2','b2'):

       network.params[key] -= learning_rate*grad[key]#记录学习过程

   loss = network.loss(x_batch,y_batch)

   if i % 100 == 0:

       print(loss)  

通过Loss的观察,经过1000次的迭代,从原来的约等于13降低到约等于2。我们可以更进一步地通过调用print(network.accuracy(x_test,y_test))

来观察一下此时的手写数字识别的准确率,我随机运行了几次,准确率大概是在75%。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容