这里所说的“学习”是指从训练数据中自动获取最优权重参数的过程。
神经网络的学习可以结合机器学习,如导入损失函数这一指标,学习的目的就是找到使损失函数的值最小使的权重参数
一,数据
通常要解决某个问题,特别是需要发现某种模式时,人们一般会综合考 虑各种因素后再给出回答。而机器学习的方法则极力避免人为介入,尝试从收集到的数据中发现答案,神经网络或深度学习更加避免人为介入。
譬如,要识别一个手写数是不是数字5
人,可以通过自己来识别处5
机器学习,可以考虑通过提取特征量,再将特征量转换为向量,然后通过机器学习算法来进行学习。需要使用合适的特征量;
而,神经网络,特征量也是由机器来学习的
二,损失函数
正如机器学习中的定义一样,神经网络中的损失函数也是用来描述当前模型预测和实际结果的差别
2.1 均方误差
可以做损失函数的函数有很多,最有名的是均方误差
是神经网络的输出,表示监督数据,表示数据的维数
用python来实现为:
def mean_squared_error(y, t):
return 0.5 * np.sum((y-t)**2)
2.2 交叉熵误差
还有交叉熵误差
可以看出,此式只计算对应正确解标签的输出的自然对数。正确解标签对应的输出越大,值越接近0,也就是误差越小
用代码来实现
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta)) #防止log0
上面两式子都是针对单个样本的误差,但是在训练中,应该是针对所有的数据,这时可以把所有误差求和再除以样本数,如:
2.3 mini-batch学习
数据集中数据一般都会有非常多,假如要全部处理,会耗费相当长时间,这时可以从全部数据中选出一部分,作为全部数据的“近似”,称为mini-batch,使用mini-batch来学习的方式,叫作mini-batch学习。
在实操中怎么进行设定呢,代码如下
##导入数据
import sys, os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=False, one_hot_label=True)
##设置随机抽取
train_size = x_train.shape[0] ##训练集的样本个数
batch_size = 10 ##抽取数目
batch_mask = np.random.choice(train_size, batch_size) #随机生成0到样本个数之间的batch_size个数
#随机生成数为切片索引号生出mini-batch
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
三,数值微分
因为在这本书里面,所有的都是用python自己写的,所以这里需要再过一下相关概念,并给出相关python实现,方便后续调用
3.1 导数
导数就是某个瞬间的变化量,数学上的定义如下:
这个导数的含义就是x的“微小变化”将导致函数f(x)的值在多大程度上发生变化
用代码来实现:
# 定义导数
def numerical_diff(f, x):
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2*h)
使用 是赋予的微小变化量,且可以避免舍入误差;
使用,原因主要是第二种的误差相对于第一种的误差较大
3.2 偏导数
对于有多个未知数的函数中,需要区分对哪一个未知数求导,这种有多个变量的函数中,对某一个变量求导得到的导数称为偏导数
注意求某个变量的偏导,其他变量视为常量即可
四,梯度
有多个变量,当多个变量确定一个具体的点时,在这个点的各个变量的偏导数汇总成一个向量,这个向量称为梯度。如
代码实现如下:
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)
# f(x-h)的计算
x[idx] = tmp_val - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val # 还原值
return grad
因为梯度指向的是此点函数值减小最多的方向,所以我们可以采取不断沿着梯度的方向来寻找最小值的方法来找到损失函数最小值,这种方法称为梯度下降法
具体的流程就是,函数的取值从当前位置沿着梯 度方向前进一定距离,然后在新的地方重新求梯度,再沿着新梯度方向前进, 如此反复,不断地沿梯度方向前进。
用数学式来表示,如下:
η为学习率,决定更新参数的强度
在神经网络中,学习率这种参数称为超参数,是由人工决定的,过大或者过小都无法得到好的结果
神经网络里面,是要找损失函数的最小值,求的梯度是指权重参数的梯度
五,学习算法的实现
神经网络的学习分为下面4个步骤:
1,从训练数据中随机选出一部分数据,这部分数据称为mini-batch。我们的目标是减小mini-batch的损失函数的值
2,计算梯度:为了减小mini-batch的损失函数的值,需要求出各个权重参数的梯度。梯度表示损失函数的值减小最多的方向。
3,更新参数:将权重参数沿梯度方向进行微小更新。
4,重复1、2、3
因为以上步骤是随机选择的mini batch数据,所以也称为随机梯度下降法,在很多框架里面,也是用SGD来实现。
接下来来用一个2层神经网络为对象,使用MNIST数据集进行学习
为了方便调用,将这个2层神经网络封装成一个类
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):
#params保存神经网络的参数
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
将其封装好后,在进行使用,并且以mini-batch为对象,使用梯度法更新参数
import numpy as np
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_ laobel = True)
train_loss_list = []
# 超参数
iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
for i in range(iters_num):
# 获取mini-batch
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)