第四章 神经网络的学习
机器学习的方法是避免人为介入,尝试从收集到的数据中发现答案(模式)。神经网络或深度学习则比以往的机器学习方法更能避免人为介入。
1. 从数据中学习:
- 方案一:机器学习
从图像中提取特征量,再用机器学习技术学习这些特征量的模式。 使用这些特征量将图像数据转换为向量,然后对转换后的向量使用机器学习中的SVM、KNN等分类器进行学习。(对不同问题要考虑特征量的选取)
这里所说的“特征量”是指可以从输入数据(输入图像)中准确地提取本质数据(重要的数据)的转换器。图像的特征量通常表示为向量的形式。在计算机视觉领域,常用的特征量包括SIFT、SURF和HOG等。
机器学习中,一般将数据分为训练数据和测试数据两部分来进行学习和实验。首先,使用训练数据进行学习,寻找最优的参数;然后,使用测试数据评价训练得到的模型的实际能力。
泛化能力是指处理未被观察过的数据(不包含在训练数据中的数据)的能力。为了正确评价模型的泛化能力,就必须划分训练数据和测试数据。另外,训练数据也可以称为监督数据。
获得泛化能力是机器学习的最终目标。
- 方案二:神经网络
神经网络直接学习图像本身。(对所有问题可以使用同样流程),神经网络可以将数据直接作为原始数据,进行“端对端”的学习。
神经网络的学习通过损失函数表示现在的状态。然后,以这个损失函数为基准,寻找最优权重参数。这个损失函数可以使用任意函数,但一般用均方误差和交叉熵误差等。
深度学有时也称为端到端机器学习(end-to-end machine learning)。这里所说的端到端是指从一端到另一端的意思,也就是从原始数据(输入)中获得目标结果(输出)的意思。
2. 损失函数:用来评价网络性能
- 均方误差
均方误差会计算神经网络输出的和正确解监督数据的各个元素之差的平方,再求总和。
def mean_squared_error(y, t):
return 0.5 * np.sum((y-t)**2)
损失函数的值越小,和监督数据之间的误差越小。
监督数据用于训练和评估机器学习模型,而神经网络输出数据是模型生成的预测或估计,用于评估模型的性能。
模型的目标是通过学习从监督数据中产生与实际目标标签相匹配的输出数据,以提高其性能和泛化能力。
- 交叉熵误差
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta))
加上了一个微小值delta,作为保护性对策。这是因为,当出现np.log(0)时,np.log(0)会变为负无限大的-inf,这样一来就会导致后续计算无法进行。
- mini-batch学习
前面都是针对单个数据的损失函数,如果要求所有训练数据的损失函数的总和,以交叉熵误差为例:
这里,假设数据有N个,tnk表示第n个数据的第k个元素的值(ynk是神经网络的输出,tnk是监督数据)。式子只是把求单个数据的损失函数的式(4.2)扩大到了N份数据,不过最后还要除以N进行正规化。通过除以N,可以求单个数据的“平均损失函数”。通过这样的平均化,可以获得和训练数据的数量无关的统一指标。
mini-batch的损失函数是利用一部分样本数据来近似地计算整体。
当监督数据是标签形式(非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
-
损失函数的重要性
在神经网络的学习中,寻找最优参数(权重和偏置)时,要寻找使损失函数的值尽可能小的参数。为了找到使损失函数的值尽可能小的地方,需要计算参数的导数(确切地讲是梯度),然后以这个导数为指引,逐步更新参数的值。假设有一个神经网络,现在我们来关注这个神经网络中的某一个权重参数。此时,对该权重参数的损失函数求导,表示的是“如果稍微改变这个权重参数的值,损失函数的值会如何变化”。如果导数的值为负,通过使该权重参数向正方向改变,可以减小损失函数的值;反过来,如果导数的值为正,则通过使该权重参数向负方向改变,可以减小损失函数的值。不过,当导数的值为0时,无论权重参数向哪个方向变化,损失函数的值都不会改变,此时该权重参数的更新会停在此处。
3.梯度
由全部变量的偏导数汇总而成的向量称为梯度(gradient)。
梯度指示的方向是各点处的函数值减小最多的方向
梯度法:
η表示更新量,在神经网络的学习中,称为学习率(事先确定,不能过大或过小)。学习率决定在一次学习中,应该学习多少,以及在多大程度上更新参数。
像学习率这样的参数称为超参数,是人工设定的。一般来说,超参数需要尝试多个值,以便找到一种可以使学习顺利进行的设定。
梯度下降法示例:
def gradient_descent(f, init_x, lr=0.01, step_num=100):
x = init_x
for i in range(step_num):
grad = numerical_gradient(f, x)
x -= lr * grad
return x
参数f是要进行最优化的函数,init_x是初始值,lr是学习率learning rate,step_num是梯度法的重复次数,numerical_gradient(f,x)会求函数的梯度,用该梯度乘以学习率得到的值进行更新操作,由step_num指定重复的
次数。
求神经网络的梯度
import sys, os
sys.path.append(os.pardir)
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient
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
这里使用了 common/functions.py中的 softmax和 cross_entropy_error方法,以及common/gradient.py中的numerical_gradient方法。simpleNet类只有一个实例变量,即形状为2×3的权重参数。它有两个方法,一个是用于预测的predict(x),另一个是用于求损失函数值的loss(x,t)。这里参数x接收输入数据,t接收正确解标签。
>>> net = simpleNet()
>>> print(net.W) # 权重参数
>>> x = np.array([0.6, 0.9])
>>> p = net.predict(x)
>>> print(p)
>>> np.argmax(p) # 最大值的索引
>>> t = np.array([0, 0, 1]) # 正确解标签
>>> net.loss(x, t)
>>> def f(W):
... return net.loss(x, t)
...
>>> dW = numerical_gradient(f, net.W)
>>> print(dW)# 神经网络的梯度矩阵
4.算法的实现
-
前提
神经网络存在合适的权重和偏置,调整权重和偏置以便拟合训练数据的过程称为“学习”。神经网络的学习分成下面4个步骤。 -
步骤1(mini-batch)
从训练数据中随机选出一部分数据,这部分数据称为mini-batch。我们的目标是减小mini-batch的损失函数的值。 -
步骤2(计算梯度)
为了减小mini-batch的损失函数的值,需要求出各个权重参数的梯度。梯度表示损失函数的值减小最多的方向。 -
步骤3(更新参数)
将权重参数沿梯度方向进行微小更新。 -
步骤4(重复)
重复步骤1、步骤2、步骤3。
两层神经网络
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)
#W是权重矩阵,B是初始化为0的偏置矩阵
#定义前向传播函数进行预测
def predict(self, x):#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
#其中输入数据经过权重和偏置的线性变换,然后通过激活函数(Sigmoid)引入非线性,最终通过 Softmax 函数将结果转化为概率分布,用于多类别分类问题的决策。
#定义损失函数:
# x:输入数据, t:监督数据
def loss(self, x, t):
y = self.predict(x)
return cross_entropy_error(y, t)#返回损失值,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
# 计算权重参数的梯度
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