深度学习——神经网络的学习

前言

神经网络的学习,这里所说的**“学习”是指从训练数据中自动获取最优权重参数的过程。为了使神经网络能进行学习,将引入损失函数这一指标。而学习的目的就是以该损失函数为基准**,找出能使它的值达到最小的权重参数。为了找出尽可能小的损失函数的值,本章我们将介绍利用了函数斜率的梯度法

从数据中学习

神经网络的特征就是可以从数据中学习。所谓“从数据中学习”,是指可以由数据自动决定权重参数的值。从前文“感知机的学习”中,我们可以手动设置只有3个参数的神经网络,而在真是的神经网络中,参数成千上万,因此通过对神经网络的学习,利用数据决定参数值的方法。
(对于线性可分问题,可以通过有限次数的学习,实现自动学习,而非线性的问题无法通过自动学习来完成)

数据驱动

①数据是机器学习的核心
②神经网络或深度学习更能比机器学习方法更能避免人为介入
以“识别手写数字”为例子,如何识别数字“5”
深度学习——神经网络的学习_第1张图片
对比机器学习和深度学习的两种方案:
深度学习——神经网络的学习_第2张图片
机器学习:
先从图像中提取特征量,再用机器学习技术学习这些特征量的模式。这里所说的“特征量”是指可以从输入数据(输入图像)中准确地提取本质数据(重要的数据)的转换器。**图像的特征量通常表示为向量的形式。**在计算机视觉领域,常用的特征量包括SIFT、SURF和HOG等。使用这些特征量将图像数据转换为向量,然后对转换后的向量使用机器学习中的SVM、KNN等分类器进行学习。将图像转换为向量时使用的特征量仍是由人设计的。对于不同的问题,必须使用合适的特征量(必须设计专门的特征量)
————————————————————————
深度学习:
可以看到,使用此方法没有人为的介入。
深度学习有时也称为端到端机器学习,这里所说的端到端是指从一端到另一端的意思,也就是从原始数据(输入)中获得目标结果(输出)的意思。
**神经网络的优点是对所有的问题都可以用同样的流程来解决。**不管要求解的问题是识别5,还是识别狗,神经网络都是通过不断地学习所提供的数据。也就是说,与待处理的问题无关,神经网络可以将数据直接作为原始数据,进行“端对端”的学习

训练数据和测试数据

机器学习中,一般将数据分为训练数据和测试数据两部分来进行学习和实验等。首先,使用训练数据进行学习,寻找最优的参数;然后,使用测试数据评价训练得到的模型的实际能力。为什么需要将数据分为训练数据和测试数据呢?因为我们追求的是模型的泛化能力。为了正确评价模型的泛化能力,就必须划分训练数据和测试数据。泛化能力是指处理未被观察过的数据(不包含在训练数据中的数据)的能力。获得泛化能力是机器学习的最终目标。比如,在识别手写数字的问题中,泛化能力可能会被用在自动读取明信片的邮政编码的系统上。此时,手写数字识别就必须具备较高的识别“某个人”写的字的能力。注意这里不是“特定的某个人写的特定的文字”,而是“任意一个人写的任意文字”。如果系统只能正确识别已有的训练数据,那有可能是只学习到了训练数据中的个人的习惯写法。
因此,仅仅用一个数据集去学习和评价参数,是无法进行正确评价的。

这样会导致可以顺利地处理某个数据集,但无法处理其他数据集的情况。只对某个数据集过度拟合的状态称为过拟合(over fitting)。避免过拟合也是机器学习的一个重要课题。(监督数据又称为训练数据)

损失函数

神经网络的学习通过某个指标表示现在的状态。然后,以这个指标为基准,寻找最优权重参数。神经网络以某个指标为线索寻找最优权重参数。神经网络的学习中所用的指标称为损失函数(loss function)。这个损失函数可以使用任意函数,但一般用均方误差和交叉熵误差等。
注:损失函数是表示神经网络性能“恶劣程度”的指标。

均方误差

在这里插入图片描述
这里,yk是表示神经网络的输出,tk表示监督数据,k表示数据的维数。
比如,在手写数字识别的例子中,yk、tk是由如下10个元素构成的数据。

y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

神经网络的输出y是softmax函数的输出。由于softmax函数的输出可以理解为概率,因此上例表示“0”的概率是0.1,“1”的概率是0.05,“2”的概率是0.6等。t是监督数据,将正确解标签设为1,其他均设为0。标签“2”为1,表示正确解是“2”(对应正确解标签为2)。
将正确解标签表示为1,其他标签表示为0的表示方法称为one-hot表示
根据公式,均方误差会计算神经网络的输出和正确解监督数据的各个元素之差的平方,再求总和。用python实现均方误差公式如下:

def mean_squared_error(y, t):
 return 0.5 * np.sum((y-t)**2)
import numpy as np
def mean_squared_error(y, t):
    return 0.5 * np.sum((y - t)**2)
#设“2”为正确解
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
#设“2”的概率最高
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
print(mean_squared_error(np.array(y),np.array(t) )) 
#设“7”的概率最高
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
print(mean_squared_error(np.array(y),np.array(t) ))

在这里插入图片描述
通过均方误差公式的实验结果可以得出,第一个例子的损失函数的值更小,和监督数据(训练数据)的误差较小,显示出第一个例子的输出结果与监督数据更吻合。
———————————————————————————————

交叉熵误差

在这里插入图片描述
log表示以e为底数的自然对数(log e)。
tk中只有正确解标签的索引为1,其他均为0(one-hot表示)。
因此,式(4.2)实际上只计算对应正确解标签的输出的自然对数。
假设正确解标签的索引是“2”,与之对应的神经网络的输出是0.6,则交叉熵误差是−log 0.6 = 0.51。
交叉熵误差的值是由正确解标签所对应的输出结果决定的。
深度学习——神经网络的学习_第3张图片
 自然对数y = log x的图像;
 当输出结果越接近“1”时(也就是softmax函数对应的概率越高),则该log x图像越接近“0”,换句话说误差接近0。
 使用python来实现交叉熵误差。

def cross_entropy_error(y, t):
 	delta = 1e-7
 return -np.sum(t * np.log(y + delta))

函数内部在计算np.log时,加上了一个微小值delta。这是因为,当出现np.log(0)时,np.log(0)会变为负无大的,这样一来就会导致后续计算无法进行。

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))
#设“2”为正确解
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
#设“2”的概率最高
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
print(cross_entropy_error(np.array(y),np.array(t)))
#设“7”的概率最高
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
print(cross_entropy_error(np.array(y),np.array(t)))

通过该实验得到的结果与均方误差相同,当正确解和对应索引的输出值“配对”时,误差最小。

mini—batch学习

计算损失函数时必须将所有的训练数据作为对象。也就是说,如果训练数据有100个的话,我们就要把这100个损失函数的总和作为学习的指标。前面损失函数的例子考虑的是针对单个数据的值,如果要考虑所有训练数据的损失函数总和,以交叉熵误差为例,可以写成下面的(4.3)
在这里插入图片描述假设数据有N个,tnk表示第n个数据的第k个元素的值(ynk是神经网络的输出,tnk是监督数据)。通过除以N,可以求单个数据的“平均损失函数”。通过这样的平均化,可以获得和训练数据的数量无关的统一指标。
解决了大数据量的情况下也可以求得单个数据的平均损失函数。
当碰到海量数据时,我们会从数据中选出一部分,作为全部数据的“近似”,神经网络的学习也是从训练数据中选出一批数据(称为mini-batch),这种方式成为 mini-batch学习
———————————————————————————————
现在做的是如何从训练数据中随机抽取10笔数据,可以使用NumPy的np.random.choice()。
在这里插入图片描述

train_size = x_train.shape[0]
batch_size = 10
#np.random.choice()可以从指定的数字中随机选择想要的数字。
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]

我们只需指定这些随机选出的索引,取出mini-batch,然后使用这个mini-batch计算损失函数即可。

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

我们来实现一个可以同时处理单个数据和批量数据(数据作为batch集中输入)两种情况的函数。

def cross_entropy_error(y, t):
    #y的维度为1时,即求单个数据的交叉熵误差时,需要改变数据的形状。
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    return -np.sum(t * np.log(y + 1e-7)) / batch_size
#设“2”为正确解
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
#设“2”的概率最高
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
cross_entropy_error(np.array(y),np.array(t))
#设“2”为正确解
t = [[0, 0, 1],[0,0,0],[0,0,0]]
#设“2”的概率最高
y = [[0.1, 0.05, 0.9], [0.0, 0.05, 0.1], [0.0, 0.1, 0.05]]
cross_entropy_error(np.array(y),np.array(t))

上述情况是当监督数据是one-hot形式,下面介绍标签形式(像“2”,“7”这类数字的情况)

def cross_entropy_error(y, t):
 	if y.ndim == 1:
		 t = t.reshape(1, t.size)
 		y = y.reshape(1, y.size)
 	batch_size = y.shape[0]
	return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

比如当batch_size为5
时,np.arange(batch_size)会生成一个NumPy 数组[0, 1, 2, 3, 4]。因为
t中标签是以[2, 7, 0, 9, 4]的形式存储的,所以y[np.arange(batch_size),
t]能抽出各个数据的正确解标签对应的神经网络的输出(在这个例子中,
y[np.arange(batch_size), t] 会生成 NumPy 数 组 [y[0,2], y[1,7], y[2,0],
y[3,9], y[4,4]])

设定损失函数的原因

“导数”在神经网络中的作用。在神经网络的学习中,寻找最优参数(权重和偏置)时,要寻找使损失函数的值尽可能小的参数。为了找到使损失函数的值尽可能小的地方,需要计算参数的导数(确切地讲是梯度),然后以这个导数为指引,逐步更新参数的值。
什么是“调参”的过程:
假设有一个神经网络,现在我们来关注这个神经网络中的某一个权重参
数。此时,对该权重参数的损失函数求导,表示的是“如果稍微改变这个权重参数的值,损失函数的值会如何变化”。如果导数的值为负,通过使该权重参数向正方向改变,可以减小损失函数的值;反过来,如果导数的值为正,则通过使该权重参数向负方向改变,可以减小损失函数的值。不过,当导数的值为0时,无论权重参数向哪个方向变化,损失函数的值都不会改变,此时该权重参数的更新会停在此处
———————————————————————————————
为什么不用识别精度作为调参的指标呢?
因为在此情况下,参数的导数绝大多数地方都会变成0。如果以识别精度为指标,即使稍微改变权重参数的值,识别精度可能也不会改变,即使发生改变改变,也会是变化成不连续、离散的值,而不是像,损失函数的值从0.9532变化到0.9532…这样发生连续的变化。这种不连续性和离散性导致不能选择识别精度作为指标。
前文中提到,作为激活函数的阶跃函数也有同样的情况。出于相同的原因,如果使用阶跃函数作为激活函数,神经网络的学习将无法进行。
(阶跃函数的导数在绝大多数地方(除了0以外的地方)均为0。也就是说,如果使用了阶跃函数,那么即便将损失函数作为指标,参数的微小变化也会被阶跃函数抹杀,导致损失函数的值不会产生任何变化。)
对于sigmoid函数作为激活函数时:
不仅函数的输出(竖轴的值)是连续变化的,曲线的斜率(导数)也是连续变化的。也就是说,sigmoid函数的导数在任何地方都不为0。
深度学习——神经网络的学习_第4张图片

数值微分

梯度法使用梯度的信息决定前进的方向。将介绍绍梯度是什么、有什么性质等。

导数

导数就是表示某个瞬间的变化量。
用公式表示:
在这里插入图片描述
两个注意的地方:
①如何表示h这一个极小量(无限接近于0),在计算机中,当h很小时,会产生舍入误差。
②“真的导数”对应函数在x出的斜率(切线),而计算式求的结果,因为h不可能无限接近0的缘故,因此结果与“切线”在严格意义上不一致。
深度学习——神经网络的学习_第5张图片
几个名词
中心差分:函数f在(x + h)和(x − h)之间的差分。
前向差分:(x + h)和x之间的差分。
优化后的导数式用python代码表示:

def numerical_diff(f, x):
	h = 1e-4 # 0.0001
	return (f(x+h) - f(x-h)) / (2*h)

利用微小的差分求导数的过程称为数值微分。而利用数学式的推导求导数的过程,称为“解析性求导”。比如,y=x^2的导数,得到y’=2x,当x=2时,y的导数为4,。

下面来实现一个数值微分:

import numpy as np
import matplotlib.pylab as plt
def function_1(x):
    return 0.01*x**2 + 0.1*x
x = np.arange(0.0, 20.0, 0.1) # 以0.1为单位,从0到20的数组x
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x, y)
plt.show()

函数图像如下:
深度学习——神经网络的学习_第6张图片
下面来求x=5和x=10处的导数:

numerical_diff(function_1, 5)
0.1999999999990898
numerical_diff(function_1, 10)
0.2999999999986347

通过数学解析式求导,再带入x=5和x=10,分别得到0.2和0.3,无限近似,误差小到可以认为它们是相等的。
画出函数切线的代码:

import numpy as np
import matplotlib.pylab as plt
def function_1(x):
    return 0.01*x**2 + 0.1*x
def numerical_diff(f, x):
    h = 1e-4 # 0.0001
    return (f(x+h) - f(x-h)) / (2*h)

def tangent_line(f,x):
    k=numerical_diff(f,x)
    b=f(x)-k*x
    return lambda t:k*t+b
x = np.arange(0.0, 20.0, 0.1) # 以0.1为单位,从0到20的数组x
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x, y)
tf=tangent_line(function_1,5)
y2=tf(x)
plt.plot(x,y2)
plt.show()
print("函数在x=5处的导数为:",numerical_diff(function_1,5))

深度学习——神经网络的学习_第7张图片

偏导数

在这里插入图片描述

def function_2(x):
return x[0]**2 + x[1]2
或者return np.sum(x
2)

我们假定向参数输入了一个NumPy数组,先计算NumPy数组中各个元素的平方,再求它们的和。
下面是函数的图像

from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import pyplot as plt

fig = plt.figure()
ax = Axes3D(fig)
x = np.arange(-10, 10, 0.1)
y = np.arange(-10, 10, 0.1)
X, Y = np.meshgrid(x, y) # 网格的创建,生成二维数组
Z=X**2+Y**2
plt.xlabel("x")
plt.ylabel("y")
ax.plot_surface(X, Y, Z, rstride=1, cstride=1)
plt.show()

深度学习——神经网络的学习_第8张图片
怎么求偏导数:
拿上述的(4.6)式举例,要求x0=3,x1=4时,关于x0的偏导数。

def function_tmp1(x0):
    return x0*x0+4.0**2
numerical_diff(function_tmp1,3.0)

Out:6.00000000000378
偏导数和单变量的导数一样,都是求某个地方的斜率。不过,偏导数需要将多个变量中的某一个变量定为目标变量,并将其他变量固定为某个值。

梯度

现在,我们希望一起计算x0和x1的偏导数。比如,我们来考虑求x0 = 3, x1 = 4时(x0, x1)的偏导数 。另外,像 这样的由全部变量的偏导数汇总
而成的向量称为梯度(gradient)。
用python代码实现梯度计算公式

def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x) # 生成和x形状相同的数组
    for idx in range(x.size):
        tmp_val = x[idx]
 # f(x+h)的计算
        x[idx] = tmp_val + h
        fxh1 = f(x)
        print("---",fxh1)
 # f(x-h)的计算
        x[idx] = tmp_val - h
        fxh2 = f(x)
        print("---",fxh2)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmp_val # 还原值
    return grad
def function_2(x):
    return x[0]**2+x[1]**2
numerical_gradient(function_2,np.array([3.0,4.0]))

深度学习——神经网络的学习_第9张图片
通过结果可以看到,f(x+h)、f(x-h)的值,以及对应在(3,4)的梯度。
这个梯度意味着什么呢?
这里我们画的是元素值为负梯度的向量(负梯度方向是梯度法中变量的更新方向。)
深度学习——神经网络的学习_第10张图片
梯度会指向各点处的函数值降低的方向。更严格地讲,梯度指示的方向
是各点处的函数值减小最多的方向。

梯度法

神经网络也必须在学习时找到最优参数(权重和偏置)。最优参数是指损失函数取最小值时的参数
注意的是:梯度表示的是各点处的函数值减小最多的方向。无法保证梯度所指的方向就是函数的最小值或者真正应该前进的方向。

知识补充:函数的极小值、最小值以及被称为鞍点(saddle point)的地方,梯度为 0。
极小值是局部最小值,也就是限定在某个范围内的最小值。
鞍点是从某个方向上看是极大值,从另一个方向上看则是极小值的点。
梯度法是要寻找梯度为 0的地方,但是那个地方不一定就是最小值,也有可能是极小值或者鞍点。

虽然梯度的方向并不一定指向最小值,但沿着它的方向能够最大限度地
减小函数的值。
因此要以梯度的信息为线索,决定前进的方向!
在梯度法中,函数的取值从当前位置沿着梯度方向前进一定距离,然后在新的地方重新求梯度,再沿着新梯度方向前进,如此反复,不断地沿梯度方向前进。像这样,通过不断地沿梯度方向前进逐渐减小函数值的过程就是梯度法(gradient method)。

寻找最小值的梯度法称为梯度下降法。
寻找最大值的梯度法称为梯度上升法。
但是通过反转损失函数的符号,求最小值的问题和求最大值的问题会
变成相同的问题。
一般来说,神经网络(深度学习)中,梯度法主要是指梯度下降法。

以下是用数学式来表示的梯度法:
深度学习——神经网络的学习_第11张图片
η表示更新量,在神经网络的学习中,称为学习率
学习率决定在一次学习中,应该学习多少,以及在多大程度上更新数。
式(4.7)是表示更新一次的式子,这个步骤会反复执行,逐渐减小函数值。
学习率需要事先确定为某个值,比如0.01或0.001。
以下是用python实现的梯度下降法(目的是求得函数的极小值,也可能是最小值):

def gradient_descent(f, init_x, lr, step_num):
    #参数f是要进行最优化的函数,init_x是初始值,lr是学习率learning rate
    #step_num是梯度法的重复次数。
    #使用这个函数可以求函数的极小值
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
    return x
init_x=np.array([-3.0,4.0])
#优化的函数是f(x1+x2)=x1^2+x2^2,初始值是[-3.0,4.0],学习率为0.1,下降次数是100
gradient_descent(function_2,init_x,0.1,100)

在这里插入图片描述

对于学习率而言,过大或者过小的学习率可能都不一定可以得到一个更好的结果!
学习率这样的参数也称为超参数,需要人工设定而不是像权重参数是通过训练数据获得的,往往超参数需要尝试多个值。

———————————————————————————————

神经网络的梯度

这里所说的梯度是指损失函数关于权重参数的梯度。
有一个只有一个形状为2 × 3的权重W的神经网络,损失函数用L表示。
深度学习——神经网络的学习_第12张图片
下面,以一个简单的神经网络为例子,来实现求梯度的代码。
实现一个名为simpleNet的类。

import sys, os
sys.path.append(os.pardir)
import numpy as np
class simpleNet:
    def __init__(self):
        self.W = np.random.randn(2,3) # 用高斯分布进行初始化,随机的权重参数
    def predict(self, x):
        return np.dot(x, self.W)
    def loss(self, x, t):
        z = self.predict(x)
        y = softmax(z)
        loss = cross_entropy_error(y, t)
        return loss
net =simpleNet()
print(net.W)
x=np.array([0.6,0.9])
p=net.predict(x)
print(p)
t=np.array([0,1,0])
#神经网络的损失函数
net.loss(x,t)
def f(W):
    return net.loss(x,t)
dW = numerical_gradient(f, net.W)
print(dW)

深度学习——神经网络的学习_第13张图片

学习算法的实现

第一步:mini-batch
从训练数据中随机选出一部分数据,称为mini-batch,我们的目标是减小mini-batch的损失函数的值。
第二步:计算梯度
为了减小mini-batch的损失函数的值,需要求出各个权重参数的梯度,梯度表示损失函数的值减小最多的方向。
第三步: 更新参数
将权重参数沿梯度方向进行微小的更新。
第四步:重复第一二三步
因为这里的数据选择的是mini batch,又称随机梯度下降法(stochastic gradient descent)。
随机梯度下降法一般由一个名为SGD的函数来实现

以下举例一个2层神经网络的类

TwoLayerNet:

import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
from common.functions import *
from common.gradient import numerical_gradient

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)

    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

深度学习——神经网络的学习_第14张图片
深度学习——神经网络的学习_第15张图片
对于__init__初始化方法。从第1个参数开始,依次表示输入层的神经元数、隐藏层的神经元数、输出层的神经元数。另外,因为进行手写数字识别时,输入图像的大小是784(28 × 28),输出为10个类别,所以指定参数input_size=784、output_size=10,将隐藏层的个数hidden_size设置为一个合适的值即可。
如何设置权重参数的初始值这个问题是关系到神经网络能否成功学习的重要问题。但在此次实验中权重使用符合高斯分布的随机数进行初始化,偏置使用0进行初始化。

mini-batch的实现

所谓mini-batch学习,就是从训练数据中随机选择一部分数据(称为mini-batch),再以这些mini-batch为对象,使用梯度法更新参数的过程。
下面以TwoLayerNet类为对象,使用MNIST数据集进行学习。

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

#读入数据
(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 = []
# 平均每个epoch的重复次数
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)
    # 计算每个epoch的识别精度
    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()

深度学习——神经网络的学习_第16张图片

基于测试数据的评价

我们确认了通过反复学习可以使损失函数的值
逐渐减小这一事实。不过这个损失函数的值,严格地讲是“对训练数据的某个mini-batch的损失函数”的值。训练数据的损失函数值减小,虽说是神经网络的学习正常进行的一个信号,但光看这个结果还不能说明该神经网络在其他数据集上也一定能有同等程度的表现。
必须确认是否能够正确识别训练数据以外的其他数据,即确认是否会发生过拟合。
过拟合是指,虽然训练数据中的数字图像能被正确辨别,但是不在训练数据中的数字图像却无法被识别的现象。
这里,每经过一个epoch,我们都会记录下训练数据和测试数据的识别精度。

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

随着epoch的前进(学习的进行),我们发现使用训练数据和测试数据评价的识别精度都提高了,并且,这两个识别精度基本上没有差异(两条线基本重叠在一起)。因此,可以说这次的学习中没有发生过拟合的现象。

总结

①机器学习中使用的数据集分为训练数据和测试数据。
②神经网络用训练数据进行学习,并用测试数据评价学习到的模型的
泛化能力。
③神经网络的学习以损失函数为指标,更新权重参数,以使损失函数
的值减小。
④利用某个给定的微小值的差分求导数的过程,称为数值微分。
⑤利用数值微分,可以计算权重参数的梯度。
⑥数值微分虽然费时间,但是实现起来很简单。下一章中要实现的稍
微复杂一些的误差反向传播法可以高速地计算梯度

参考:

《深度学习入门:基于Python的理论与实现 》斋藤康毅

你可能感兴趣的:(深度学习,深度学习,神经网络,学习)