深度学习视频基础(三):神经网络的学习

神经网络的学习:这里所说的“学习”是指从训练数据中自动获取最优权重参数的过程

4.1从数据中学习

神经网络的特征就是可以从数据中学习。
所谓“从数据中学习”,是指可以由数据自动决定权重参数的值。

4.1.1 数据驱动

数据是机器学习的命根子。
例如:识别数字5
方案一:先从图像中提取特征量,再用机器学习技术学习这些特征量的模式。
“特征量”是指可以从输入数据(输入图像)中准确地提取本质数(重要的数据)的转换器。
在计算机视觉领域,常用的特征量包括SIFT、SURF和HOG等。使用这些特征量将图像数据转换为向量,然后对转换后的向量使用机器学习中的SVM、KNN等分类器进行学习。
在这里插入图片描述

4.1.2 训练数据和测试数据

首先,使用训练数据进行学习,寻找最优的参数;
然后,使用测试数据评价训练得到的模型的实际能力。
分为训练集和测试集的原因:
因为我们追求的是模型的泛化能力。为了正确评价模型的泛化能力,就必须划分训练数据和测试数据。另外,训练数据也可以称为监督数据
泛化能力是指处理未被观察过的数据(不包含在训练数据中的数据)的能力。获得泛化能力是机器学习的最终目标。
只对某个数据集过度拟合的状态称为过拟合(over fitting)。

4.2 损失函数

神经网络以某个指标为线索寻找最优权重参数。神经网络的学习中所用的指标称为损失函数(loss function)。这个损失函数可以使用任意函数,但一般用均方误差和交叉熵误差等。
损失函数是表示神经网络性能的“恶劣程度”的指标,即当前的神经网络对监督数据在多大程度上不拟合,在多大程度上不一致。

4.2.1 均方误差(mean squared error)

公式如下:
在这里插入图片描述
在这里插入图片描述
监督数据就是训练数据

在这里插入图片描述
神经网络的输出 y 是softmax函数的输出。由于softmax函数的输出可以理解为概率,因此上例表示“0”的概率是0.1,“1”的概率是0.05,“2”的概率是0.6等。 t 是监督数据,将正确解标签设为1,其他均设为0。这里,标签“2”为1,表示正确解是“2”。将正确解标签表示为1,其他标签表示为0的表示方法称为one-hot表示。

均方误差会计算神经网络的输出和正确解监督数据的各个元素之差的平方,再求总和。

python实现
在这里插入图片描述
重新计算一下例子
在这里插入图片描述
第一个例子中,正确解是“2”,神经网络的输出的最大值是“2”;
第二个例子中,正确解是“2”,神经网络的输出的最大值是“7”。
如实验结果所示,我们发现第一个例子的损失函数的值更小,和监督数据之间的误差较小。也就是说,均方误差显示第一个例子的输出结果与监督数据更加吻合。

4.2.2交叉熵误差 (cross entropy error)

公式如下:
在这里插入图片描述
在这里插入图片描述
这个实际上只计算对应正确解标签的输出的自然对数。
假设正确解标签的索引是“2”,与之对应的神经网络的输出是0.6,则交叉熵误差是−log0.6 = 0.51;
若“2”对应的输出是0.1,则交叉熵误差为−log0.1 = 2.30。
也就是说,交叉熵误差的值是由正确解标签所对应的输出结果决定的。

在这里插入图片描述
如上图所示
x等于1时,y为0;随着x向0靠近,y逐渐变小。
正确解标签对应的输出越大,公式的值越接近0;当输出为1时,交叉熵误差为0。此外,如果正确解标签对应的输出较小,公式的值较大。
因此,这个输出的y越大,误差越小。
python实现:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第一个例子中,正确解标签对应的输出为0.6,此时的交叉熵误差大约为0.51。第二个例子中,正确解标签对应的输出为0.1的低值,此时的交叉熵误差大约为2.3。

4.2.3 mini-batch学习

机器学习使用训练数据进行学习。使用训练数据进行学习,严格来说,就是针对训练数据计算损失函数的值,找出使该值尽可能小的参数。因此,计算损失函数时必须将所有的训练数据作为对象。
如果训练数据有100个的话,我们就要把这100个损失函数的总和作为学习的指标。

如果要求所有训练数据的损失函数的总和,以交叉熵误差为例,可以写成下面式子。
在这里插入图片描述
在这里插入图片描述
我们从全部数据中选出一部分,作为全部数据的“近似”。神经网络的学习也是从训练数据中选出一批数据(称为mini-batch,小批量),然后对每个mini-batch进行学习。比如,从60000个训练数据中随机选择100笔,再用这100笔数据进行学习。这种学习方式称为mini-batch学习。

编程实现mini-batch:
在这里插入图片描述
通过设定参数 one_hot_label=True ,可以得到one-hot表示(即仅正确解标签为1,其余为0的数据结构)。
在这里插入图片描述
使用 np.random.choice() 可以从指定的数字中随机选择想要的数字。
比如,np.random.choice(60000, 10) 会从0到59999之间随机选择10个数字。
我们只需指定这些随机选出的索引,取出mini-batch,然后使用这个mini-batch计算损失函数即可。

4.2.4 mini-batch版交叉熵误差的实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2.5 为何要设定损失函数

在神经网络的学习中,寻找最优参数(权重和偏置)时,要寻找使损失函数的值尽可能小的参数。为了找到使损失函数的值尽可能小的地方,需要计算参数的导数(确切地讲是梯度),然后以这个导数为指引,逐步更新参数的值。
假设有一个神经网络,现在我们来关注这个神经网络中的某一个权重参数。此时,对该权重参数的损失函数求导,表示的是“如果稍微改变这个权重参数的值,损失函数的值会如何变化”。
如果导数的值为负,通过使该权重参数向正方向改变,可以减小损失函数的值;反过来,如果导数的值为正,则通过使该权重参数向负方向改变,可以减小损失函数的值。
不过,当导数的值为0时,无论权重参数向哪个方向变化,损失函数的值都不会改变,此时该权重参数的更新会停在此处。

在进行神经网络的学习时,不能将识别精度作为指标。因为如果以识别精度为指标,则参数的导数在绝大多数地方都会变为0。

4.3数值微分

4.3.1 导数

在这里插入图片描述
在这里插入图片描述
代码实现:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数值微分:数值微分就是用数值方法近似求解函数的导数的过程。
数值微分含有误差。为了减小这个误差,我们可以计算函数f在(x + h)和(x − h)之间的差分。因为这种计算方法以x为中心,计算它左右两边的差分,所以也称为中心差分(而(x + h)和x之间的差分称为前向差分)。
下面,我们基于上述两个要改进的点来实现数值微分(数值梯度)。
在这里插入图片描述
举例:
先来看一个由下式表示的2次函数。
在这里插入图片描述
Python实现
在这里插入图片描述
绘制图像:
在这里插入图片描述
在这里插入图片描述
x = 5和x = 10处的导数:
在这里插入图片描述
偏导数:
在这里插入图片描述
代码实现:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
偏导数:
在这里插入图片描述

4.4梯度

梯度就是该点偏导的值
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实际上,梯度会指向各点处的函数值降低的方向。
更严格地讲,梯度指示的方向是各点处的函数值减小最多的方向。

4.4.1 梯度法

机器学习的主要任务是在学习时寻找最优参数。
神经网络也必须在学习时找到最优参数(权重和偏置)。
这里所说的最优参数是指损失函数取最小值时的参数。

**梯度表示的是各点处的函数值减小最多的方向。**无法保证梯度所指的方向就是函数的最小值或者真正应该前进的方向。
函数的极小值、最小值以及被称为鞍点(saddle point)的地方,梯度为0。

虽然梯度的方向并不一定指向最小值,但沿着它的方向能够最大限度地减小函数的值。因此,在寻找函数的最小值(或者尽可能小的值)的位置的任务中,要以梯度的信息为线索,决定前进的方向。

在梯度法中,函数的取值从当前位置沿着梯度方向前进一定距离,然后在新的地方重新求梯度,再沿着新梯度方向前进,如此反复,不断地沿梯度方向前进。像这样,通过不断地沿梯度方向前进,逐渐减小函数值的过程就是梯度法(gradient method)。梯度法是解决机器学习中最优化问题的常用方法,特别是在神经网络的学习中经常被使用。

根据目的是寻找最小值还是最大值,梯度法的叫法有所不同。严格地讲,寻找最小值的梯度法称为梯度下降法(gradient descent method),寻找最大值的梯度法称为梯度上升法(gradient ascent method)。但是通过反转损失函数的符号,求最小值的问题和求最大值的问题会变成相同的问题,因此“下降”还是“上升”的差异本质上并不重要。一般来说,神经网络(深度学习)中,梯度法主要是指梯度下降法。

数学公式表示:
在这里插入图片描述
学习率需要事先确定为某个值,比如0.01或0.001。一般而言,这个值过大或过小,都无法抵达一个“好的位置”。在神经网络的学习中,一般会一边改变学习率的值,一边确认学习是否正确进行了。

Python实现:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设初始值为 (-3.0, 4.0) ,开始使用梯度法寻找最小值。最终的结果是 (-6.1e-10, 8.1e-10) ,非常接近 (0,0) 。实际上,真的最小值就是 (0,0) 。

关于学习率
在这里插入图片描述

4.4.2 神经网络的梯度

神经网络的学习也要求梯度。这里所说的梯度是指损失函数关于权重参数的梯度。
在这里插入图片描述
在这里插入图片描述
代码实现一个simpleNet的类
在这里插入图片描述
这里使用了 common/functions.py 中的 softmax 和cross_entropy_error 方法,以及 common/gradient.py 中的numerical_gradient 方法。 simpleNet 类只有一个实例变量,即形状为2×3的权重参数。它有两个方法,一个是用于预测的 predict(x) ,另一个是用于求损失函数值的 loss(x,t) 。这里参数 x 接收输入数据, t 接收正确解标签。
在这里插入图片描述
在这里插入图片描述
求梯度
使用 numerical_gradient(f, x) 求梯度(这里定义的函数 f(W) 的参数 W 是一个伪参数。因为 numerical_gradient(f,x) 会在内部执行 f(x) ,为了与之兼容而定义了 f(W) )。
在这里插入图片描述
numerical_gradient(f, x) 的参数 f 是函数, x 是传给函数 f 的参数。因此,这里参数 x 取 net.W ,并定义一个计算损失函数的新函数 f ,然后把这个新定义的函数传递给 numerical_gradient(f, x) 。
在这里插入图片描述

4.5学习算法的实现

神经网络的学习步骤
前提
神经网络存在合适的权重和偏置,调整权重和偏置以便拟合训练数据的过程称为“学习”。神经网络的学习分成下面4个步骤。

步骤1(mini-batch)
从训练数据中随机选出一部分数据,这部分数据称为mini-batch。我们的目标是减小mini-batch的损失函数的值。

步骤2(计算梯度)
为了减小mini-batch的损失函数的值,需要求出各个权重参数的梯度。梯度表示损失函数的值减小最多的方向。

步骤3(更新参数)
将权重参数沿梯度方向进行微小更新

步骤4(重复)
重复步骤1、步骤2、步骤3

4.5.1 两层神经网络的类

通过梯度下降法更新参数,不过因为这里使用的数据是随机选择的mini batch数据,所以又称为随机梯度下降法(stochastic gradient descent)。“随机”指的是“随机选择的”的意思,因此,随机梯度下降法是“对随机选择的数据进行的梯度下降法”。
深度学习的很多框架中,随机梯度下降法一般由一个名为SGD的函数来实现。SGD来源于随机梯度下降法的英文名称的首字母。

# params 保存神经网络的参数的字典型变量(实例变量)                 
# params [ 'w1']是第1层的权重,params [ ' b1 ']是第1层的偏置
# params [ 'W2']是第2层的权重,params [ 'b2 ']是第2层的偏置 
                                               
# 保存梯度的字典型变量(numerical_gradient()方法的返回值)。      
# grads [ "w1']是第1层权重的梯度,grads [ 'b1']是第1层偏置的梯度
# grads [ "W2']是第2层权重的梯度,grads [ 'b2']是第2层偏置的梯度
# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
from common.functions import *
from common.gradient import numerical_gradient

# params 保存神经网络的参数的字典型变量(实例变量)
# params [ 'w1']是第1层的权重,params [ ' b1 ']是第1层的偏置。
# params [ 'W2']是第2层的权重,params [ 'b2 ']是第2层的偏置

# 保存梯度的字典型变量(numerical_gradient()方法的返回值)。
# grads [ "w1']是第1层权重的梯度,grads [ 'b1']是第1层偏置的梯度。
# grads [ "W2']是第2层权重的梯度,grads [ 'b2']是第2层偏置的梯度
class TwoLayerNet:
    # 进行初始化,参数从头开始依次表示输入层的神经元数、隐藏层数的神经元数、输出层的神经元数、初始化时权重的标准差
    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)
    # 进行识别(推理),参数x是图像数据
    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 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        
        return y
        
    # x:输入图像数据, t:监督(正确的标签)数据,计算损失函数的值
    def loss(self, x, t):
        y = self.predict(x)
        
        return cross_entropy_error(y, t)
    # 计算识别精度,参数同上
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        t = np.argmax(t, axis=1)
        
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
        
    # x:输入图像数据, t:监督(即正确标签)数据,计算权重参数的梯度
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)
        
        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
    # 计算权重参数的梯度,是上面函数的高速版。里面参数同上
    def gradient(self, x, t):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        grads = {}
        
        batch_num = x.shape[0]
        
        # forward
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        
        # backward
        dy = (y - t) / batch_num
        grads['W2'] = np.dot(z1.T, dy)
        grads['b2'] = np.sum(dy, axis=0)
        
        da1 = np.dot(dy, W2.T)
        dz1 = sigmoid_grad(a1) * da1
        grads['W1'] = np.dot(x.T, dz1)
        grads['b1'] = np.sum(dz1, axis=0)

        return grads

TwoLayerNet 类有 params 和 grads 两个字典型实例变量。 params 变量中保存了权重参数,比如 params[‘W1’] 以NumPy数组的形式保存了第1层的权重参数。此外,第1层的偏置可以通过 param[‘b1’] 进行访问。
例:
在这里插入图片描述
如上所示, params 变量中保存了该神经网络所需的全部参数。并且,params 变量中保存的权重参数会用在推理处理(前向处理)中。推理处理实现:
在这里插入图片描述
与 params 变量对应, grads 变量中保存了各个参数的梯度。
如下所示,
使用 numerical_gradient() 方法计算梯度后,梯度的信息将保存在 grads 变量中。
在这里插入图片描述
进行手写数字识别时,输入图像的大小是784(28 × 28),输出为10个类别,所以指定参数 input_size=784 、output_size=10 ,将隐藏层的个数hidden_size设置为一个合适的值即可。

4.5.2 mini-batch的实现

以TwoLayerNet 类为对象,使用MNIST数据集进行学习。

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

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

iters_num = 10000  # 适当设定循环的次数
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    # 计算梯度
    #grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch)
    
    # 更新参数
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
    
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

# 绘制图形
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

mini-batch的大小为100,需要每次从60000个训练数据中随机取出100个数据(图像数据和正确解标签数据)。然后,对这个包含100笔数据的mini-batch求梯度,使用随机梯度下降法(SGD)更新参数。这里,梯度法的更新次数(循环的次数)为10000。每更新一次,都对训练数据计算损失函数的值,并把该值添加到数组中。
在这里插入图片描述
随着学习的进行,损失函数的值在不断减小。这是学习正常进行的信号,表示神经网络的权重参数在逐渐拟合数据。也就是
说,神经网络的确在学习!通过反复地向它浇灌(输入)数据,神经网络正在逐渐向最优参数靠近。

4.5.3 基于测试数据的评价

训练数据的损失函数值减小,虽说是神经
网络的学习正常进行的一个信号,但光看这个结果还不能说明该神经网络在其他数据集上也一定能有同等程度的表现。
神经网络的学习中,必须确认是否能够正确识别训练数据以外的其他数据,即确认是否会发生过拟合。
过拟合是指,虽然训练数据中的数字图像能被正确辨别,但是不在训练数据中的数字图像却无法被识别的现象。

**神经网络学习的最初目标是掌握泛化能力,**因此,要评价神经网络的泛化能力,就必须使用不包含在训练数据中的数据。

epoch是一个单位。一个epoch表示学习中所有训练数据均被使用过一次时的更新次数。比如,对于10000笔训练数据,用大小为100笔数据的mini-batch进行学习时,重复随机梯度下降法100次,所有的训练数据就都被“看过”了。此时,100次就是一个epoch。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值