深度学习(鱼书)day05--神经网络的学习(前三节)
通过除以N,可以求单个数据的“
深度学习(鱼书)day05–神经网络的学习(前三节)
本章的主题是神经网络的学习。这里所说的“学习”是指从训练数据中自动获取最优权重参数的过程。本章中,为了使神经网络能进行学习,将导入损失函数这一指标。而学习的目的就是以该损失函数为基准,找出能使它的值达到最小的权重参数。为了找出尽可能小的损失函数的值,本章我们将介绍利用了函数斜率的梯度法。

一、从数据中学习
根据“感知机收敛定理”,通过有限次数的学习,线性可分问题是可解的。但是,非线性可分问题则无法通过(自动)学习来解决。
-
数据驱动
从数据中寻找答案、从数据中发现模式、根据数据讲故事。
机器学习的方法则极力避免人为介入,尝试从收集到的数据中发现答案(模式)。神经网络或深度学习则比以往的机器学习方法更能避免人为介入。
我们的目标是实现能区别是否是5的程序:

从零开始想出一个可以识别5的算法,不如考虑通过有效利用数据来解决这个问题。
-
一种方案是,先从图像中提取特征量,再用机器学习技术学习这些特征量的模式。这里所说的“特征量”是指可以从输入数据(输入图像)中准确地提取本质数据(重要的数据)的转换器。图像的特征量通常表示为向量的形式。在计算机视觉领域,常用的特征量包括SIFT、SURF和HOG等。使用这些特征量将图像数据转换为向量,然后对转换后的向量使用机器学习中的SVM、KNN等分类器进行学习。
-
机器学习的方法中,由机器从收集到的数据中找出规律性。但是需要注意的是,将图像转换为向量时使用的特征量仍是由人设计的。对于不同的问题,必须使用合适的特征量(必须设计专门的特征量),才能得到好的结果。比如,为了区分狗的脸部,人们需要考虑与用于识别5的特征量不同的其他特征量。即使使用特征量和机器学习的方法,也需要针对不同的问题人工考虑合适的特征量。
-
神经网络直接学习图像本身,连图像中包含的重要特征量也都是由机器来学习的:

-
深 度 学 习 有 时 也 称 为 端 到 端 机 器 学 习(end-to-end machinelearning)。这里所说的端到端是指从一端到另一端的意思,也就是从原始数据(输入)中获得目标结果(输出) 的意思。
-
神经网络的优点是对所有的问题都可以用同样的流程来解决。比如,不管要求解的问题是识别5,还是识别狗,抑或是识别人脸,神经网络都是通过不断地学习所提供的数据,尝试发现待求解的问题的模式。与待处理的问题无关,神经网络可以将数据直接作为原始数据,进行“端对端”的学习。
-
训练数据和测试数据
机器学习中,一般将数据分为训练数据和测试数据两部分来进行学习和实验等。
- 首先,使用训练数据进行学习,寻找最优的参数;然后,使用测试数据评价训练得到的模型的实际能力。我们追求的是模型的泛化能力,为了正确评价模型的泛化能力,就必须划分训练数据和测试数据。另外,训练数据也可以称为监督数据。
- 泛化能力是指处理未被观察过的数据(不包含在训练数据中的数据)的能力。获得泛化能力是机器学习的最终目标。比如,在识别手写数字的问题中,泛化能力可能会被用在自动读取明信片的邮政编码的系统上。此时,手写数字识别就必须具备较高的识别“某个人”写的字的能力,注意这是“任意一个人写的任意文字”。如果系统只能正确识别已有的训练数据,那有可能是只学习到了训练数据中的个人的习惯写法。
- 仅仅用一个数据集去学习和评价参数,是无法进行正确评价的。这样会导致可以顺利地处理某个数据集,但无法处理其他数据集的情况。**只对某个数据集过度拟合的状态称为过拟合(over fitting)。**避免过拟合也是机器学习的一个重要课题。
二、损失函数
神经网络的学习通过某个指标表示现在的状态。然后,以这个指标为基准,寻找最优权重参数。神经网络的学习中所用的指标称为损失函数(loss function)。这个损失函数可以使用任意函数,但一般用均方误差和交叉熵误差等。
损失函数是表示神经网络性能的“恶劣程度”的指标,即当前的神经网络对监督数据在多大程度上不拟合,在多大程度上不一致。以“性能的恶劣程度”为指标可能会使人感到不太自然,但是如果给损失函数乘上一个负值,就可以解释为**“在多大程度上不坏”,即“性能有多好”。并且,“使性能的恶劣程度达到最小”和“使性能的优良程度达到最大”是等价的,不管是用“恶劣程度”还是“优良程度”**,做的事情本质上都是一样的。
-
均方误差:均方误差会计算神经网络的输出和正确解监督数据的各个元素之差的平方,再求总和。
可以用作损失函数的函数有很多,其中最有名的是均方误差(mean squared error)。均方误差如下式所示:
E=12∑k(yk−tk)2 E=\frac{1}{2} \sum_{k}\left(y_{k}-t_{k}\right)^{2} E=21k∑(yk−tk)2
yk是表示神经网络的输出,tk表示监督数据,k表示数据的维数。手写数字识别的例子中,yk、tk是由如下10个元素构成的数据。

上例表示“0”的概率是0*.1,“1”的概率是0.05,“2”的概率是0.*6等。t是监督数据,将正确解标签设为1,其他均设为0。这里,标签“2”为1,表示正确解是“2”。将正确解标签表示为1,其他标签表示为0的表示方法称为one-hot表示。
def mean_square_error(y, t):
return 0.5 * sum((y-t)**2)
# 例1:“2”的概率最高的情况(0.6)
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
mean_squared_error(np.array(y), np.array(t))
# 例2:“7”的概率最高的情况(0.6)
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
mean_squared_error(np.array(y), np.array(t))

第一个例子的损失函数的值更小,和监督数据之间的误差较小。也就是说,均方误差显示第一个例子的输出结果与监督数据更加吻合。
-
交叉熵误差
这里,log表示以e为底数的自然对数(log e )。交叉熵误差(cross entropy error) 也经常被用作损失函数。交叉熵误差如下式所示。
E=−∑k(tklogk) E=-\sum_{k}(t_{k}\log{k}) E=−k∑(tklogk)
yk是神经网络的输出,tk是正确解标签。并且,tk中只有正确解标签的索引为1,其他均为0(one-hot表示)。实际上只计算对应正确解标签的输出的自然对数。交叉熵误差的值是由正确解标签所对应的输出结果决定的。

正确解标签对应的输出越大,式(4.2)的值越接近0;当输出为1时,交叉熵误差为0。此外,如果正确解标签对应的输出较小,则式(4.2)的值较大。
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta))
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
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))
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
cross_entropy_error(np.array(y), np.array(t))
加上了一个微小值delta。这是因为,当出现np.log(0)时,np.log(0)会变为负无限大的-inf,这样一来就会导致后续计算无法进行。

-
mini-batch学习
机器学习使用训练数据进行学习,就是针对训练数据计算损失函数的值,找出使该值尽可能小的参数。计算损失函数时必须将所有的训练数据作为对象。
前面介绍的损失函数的例子中考虑的都是针对单个数据的损失函数。如果要求所有训练数据的损失函数的总和,以交叉熵误差为例,可以写成下面的式字:
E=−∑n∑k(tnklogynk) E=-\sum_n\sum_k(t_{nk}\log{y_{nk}}) E=−n∑k∑(tnklogynk)
这里,假设数据有N个,tnk表示第n个数据的第k个元素的值(ynk是神经网络的输出,tnk是监督数据)。式子虽然看起来有一些复杂,其实只是把求单个数据的损失函数的式子扩大到了N份数据,不过最后还要除以N进行正规化。通过除以N,可以求单个数据的“平均损失函数”。通过这样的平均化,可以获得和训练数据的数量无关的统一指标。比如,即便训练数据有1000个或10000个,也可以求得单个数据的平均损失函数。如果遇到大数据,这种情况下以全部数据为对象计算损失函数是不现实的。我们从全部数据中选出一部分,作为全部数据的“近似”。神经网络的学习也是从训练数据中选出一批数据(称为mini-batch,小批量),然后对每个mini-batch进行学习。比如,从60000个训练数据中随机选择100笔,再用这100笔数据进行学习。这种学习方式称为mini-batch学习。
-
从训练数据中随机选择指定个数的数据的代码,以进行mini-batch学习:
先来看一下用于读入MNIST数据集的代码:
import sys, os sys.path.append(os.pardir) import numpy as np from dataset.mnist import load_mnist (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True) print(x_train.shape) # (60000, 784) print(t_train.shape) # (60000, 10)训练数据有60000个,输入数据是784维(28 × 28)的图像数据,监督数据是10维的数据。因此,上面的x_train、t_train的形状分别是**(60000, 784)和(60000, 10)。从这个训练数据中随机抽取10笔数据,我们可以使用NumPy**的
np.random.choice(),写成如下形式:train_size = x_train.shape[0] batch_size = 10 batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask]np.random.choice(60000, 10)会从0到59999之间随机选择10个数字。我们可以得到一个包含被选数据的索引的数组。array([ 8013, 14666, 58210, 23832, 52091, 10153, 8107, 19410, 27260,21411])
之后,我们只需指定这些随机选出的索引,取出mini-batch,然后使用这个mini-batch计算损失函数即可。
-
-
mini-batch版交叉熵误差的实现
这里,我们来实现一个可以同时处理单个数据和批量数据(数据作为batch集中输入)两种情况的函数。
y的维度为1时,即求单个数据的交叉熵误差时,需要改变数据的形状。并且,当输入为mini-batch时,要用batch的个数进行正规化,计算单个数据的平均交叉熵误差:
def cross_entropy_error(y, t): if y.ndim == 1: y = y.reshape(1, y.size) t = t.reshape(1, t.size) batch_size = y.shape[0] return -np.sum(t * np.log(y + 1e-7)) / batch_size

当监督数据是标签形式(非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
np.log(y[np.arange(batch_size), t])的作用:高效提取每个样本的真实类别对应的预测概率
(1) np.arange(batch_size)
生成一个从 0到 batch_size-1的数组,例如:
# batch_size = 2 时
np.arange(2) = [0, 1]
(2) y[np.arange(batch_size), t]
这是一个**高级索引(Advanced Indexing)**操作,目的是从 y中提取:
- 第
i个样本(i来自np.arange(batch_size)) - 第
t[i]类的概率(t[i]是真实标签)
示例:
y = np.array([[0.1, 0.3, 0.6], # 样本 0 的预测概率(3 类)
[0.2, 0.7, 0.1]]) # 样本 1 的预测概率
t = np.array([2, 1]) # 真实标签:样本 0 是第 2 类,样本 1 是第 1 类
# 提取每个样本真实类别的概率
y[np.arange(2), t] = y[[0, 1], [2, 1]] = [y[0, 2], y[1, 1]] = [0.6, 0.7]
-
为何要设定损失函数
在神经网络的学习中,寻找最优参数(权重和偏置)时,要寻找使损失函数的值尽可能小的参数。为了找到使损失函数的值尽可能小的地方,需要计算参数的导数(确切地讲是梯度),然后以这个导数为指引,逐步更新参数的值。
对该权重参数的损失函数求导,表示的是“如果稍微改变这个权重参数的值,损失函数的值会如何变化”。
- 如果导数的值为负,通过该权重参数向正方向改变,可以减小损失函数的值;
- 反过来,如果导数的值为正,则通过使该权重参数向负方向改变,可以减小损失函数的值。
- 当导数的值为0时,无论权重参数向哪个方向变化,损失函数的值都不会改变,此时该权重参数的更新会停在此处。
在进行神经网络的学习时,不能将识别精度作为指标。 因为如果以识别精度为指标,则参数的导数在绝大多数地方都会变为0。
识别精度对微小的参数变化基本上没有什么反应,即便有反应,它的值也是不连续地、突然地变化。作为激活函数的阶跃函数也有同样的情况。出于相同的原因,如果使用阶跃函数作为激活函数,神经网络的学习将无法进行:

三、数值微分
梯度法使用梯度的信息决定前进的方向,我们先介绍一下导数。
-
导数
导数就是表示某个瞬间的变化量。它可以定义成下面的式子:
df(x)dx=limh→0f(x+h)−f(x)h \frac{\mathrm{d}f(x)}{\mathrm{d}x} = \lim_{h\to0}\frac{f(x+h)-f(x)}{h} dxdf(x)=h→0limhf(x+h)−f(x)
求函数的导数的程序:-
不好的示例
def numerical_diff(f, x): h = 10e-50 return (f(x+h) - f(x)) / h(1)这样产生了舍入误差(rounding error)。所谓舍入误差,是指因省略小数的精细部分的数值(比如,小数点第8位以后的数值)而造成最终的计算结果上的误差。比如,在Python中,舍入误差可如下表示。
np.float32(1e-50) # 0.0我们需要将微小值h改为10^4
(2)虽然上述实现中计算了函数f在x+h和x之间的差分,但是这个计算从一开始就有误差。如图4-5所示,“真的导数”对应函数在x处的斜率(称为切线),但上述实现中计算的导数对应的是(x + h)和x之间的斜率。因此,真的导数(真的切线)和上述实现中得到的导数的值在严格意义上并不一致。这个差异的出现是因为h不可能无限接近0。
-

为了减小这个误差,我们可以计算函数f在**(x + h)和(x − h)之间的差分。因为这种计算方法以x为中心,计算它左右两边的差分,所以也称为中心差分**(而(x + h)和x之间的差分称为前向差分)。我们基于上述两个要改进的点来实现数值微分(数值梯度)。
def numerical_diff(f, x):
h = 1e-4
return (f(x+h) - f(x-h)) / (2*h)
-
数值微分的例子
y=0.01x2+0.1x y = 0.01x^2 + 0.1x y=0.01x2+0.1xdef function_1(x): return 0.01*x**2 + 0.1*x
我们来计算一下这个函数在x = 5和x = 10处的导数。
numerical_diff(function_1, 5) numerical_diff(function_1, 10)

我们用上面的数值微分的值作为斜率,画一条直线:
- 偏导数
f(x0,x1)=x02+x12 f(x_0,x_1) = x_0^2 + x_1^2 f(x0,x1)=x02+x12
def function_2(x):
return np.sum(x**2)

我们把这里讨论的有多个变量的函数的导数称为偏导数。用数学式表示的话,可以写成
偏导数需要将多个变量中的某一个变量定为目标变量,并将其他变量固定为某个值。
-
问题1
求x0 = 3, x1 = 4时,关于x0的偏导数

def function_tmp1(x0): return x0*x0 + 4.0**2.0 -
问题2
求x0 = 3, x1 = 4时,关于x1的偏导数

def function_tmp2(x1): return x1*x1 + 3.0**2.0

更多推荐
所有评论(0)