学习就是从训练数据中自动获取最优权重参数的过程。引入损失函数这一指标,学习的目的是找出使损失函数达到最小的权重参数。使用函数斜率的梯度法来找这个最小值。
人工智能有两派,一派认为实现人工智能必须用逻辑和符号系统,自顶向下看问题;另一派认为通过仿造人脑可以达到人工智能,自底向上看问题。前一派是“想啥来啥”,后一派是“吃啥补啥”。前者偏唯心,后者偏唯物。两派一直是人工智能领域“两个阶级、两条路线”的斗争,这斗争有时还是你死我活。今天学习的是神经网络派。
4.1 从数据中学习
4.1.1 数据驱动
数据是机器学习的命根子。机器学习避免人为介入,通过数据发现模式。比如识别手写数字5,可以从图像中提取特征量,再用机器学习学习这些特征量的模式。其中图像转换为向量时使用的特征量仍由人设计,不同问题需要人工考虑不同的特征量。
神经网络(深度学习)称为端到端学习,图像中的特征量也由机器来学习。不管识别5还是识别狗,神经网络都是通过不断学习数据,尝试发现模式。
4.1.2 训练数据和测试数据
追求的模型泛化能力。训练数据也叫监督数据。一套数据集,无法获得正确的评价。要避免对某数据集的过拟合。
4.2 损失函数
损失函数表示神经网络恶劣程度指标。一般乘上一个负值。
4.2.1 均方误差
4.2.2 交叉熵误差
4.2.3 mini-batch学习
从训练数据中选出小批量学习,称为mini-batch学习。随机选择小批量做为全体训练数据的近似值。
4.2.4 mini-batch版交叉熵误差实现
4.2.5 为何要设定损失函数
为了找到使损失函数值尽可能小的地方,需要计算参数导数,以导数为指引,逐步更新参数值。
对权重参数的损失函数求导,表示的是:如果稍微改变这个权重的值,损失函数的值如何变化。
1)导数为负,权重参数正向变化,可以减小损失函数的值。
2)导数为正,权重参数负向变化,可以减小损失函数的值。
3)导数为0,权重参数哪个方向变化,损失函数都不变化。
不能直接使用识别精度,是因为大部分地方的参数导数为0,导致参数无法更新。
为啥是0?比如识别精度为32%, 微调权重参数,识别精度仍旧是32%,即使改变,也不会联系变化,而是33%,34%等离散值。而损失函数会连续变化。作为激活函数的阶跃函数也有类似特征,大部分地方导数为0,所以不能使用阶跃函数,要使用斜率连续变化的sigmoid函数。
4.3 数值微分
什么是梯度。
4.3.1 导数
采用中心差分
(f(x+h)-f(x-h))/(2*h)
利用微小的差分求导的过程称为数值微分numerical differentiation
数学公式推导求导称为解析性求导。如y= 公式求导为=2x,这样算出的是没有误差的真导数
4.3.2 数值微分的例子
数值微分的计算结果和真导数误差很小。
4.3.3 偏导数
有两个变量的情况。或者多个变量。有多个变量的函数的导数称为偏导数。
偏导数将多个变量中的某个变量定为目标变量,其他变量固定为某个值。
4.4 梯度
由全部变量的偏导数汇总而成的向量称为梯度(gradient)
4.4.1 梯度法
使用梯度寻找损失函数最小值的方法就是梯度法。梯度是各点处函数值减小最多的方向。方向往往不是函数的最小值。是极小值。
不断沿梯度方向前进,逐渐减小函数值的过程,叫梯度法 gradient method
学习率:一次学习,在多大程度上更新参数。
梯度下降法实现:
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
4.4.2 神经网络的梯度
神经网络梯度:损失函数关于权重参数的梯度。形状与W相同。
求梯度代码
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
4.5 学习算法的实现
4.5.1 二层神经网络类
#two_layer_net.py
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
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
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
4.5.2 mini-batch学习
# train_neuralnet.py
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_label=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)
4.5.3 基于测试数据评价
epoch是一个单位,所有训练数据被使用一次时的更新次数。10000训练数据,mini-batch为100,共执行梯度下降法10000/100=100次,100次就是一个epoch
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_label=True)
train_loss_list = []
train_acc_list = []
test_acc_list = []
#平均每个epoch的重复次数
iter_per_epoch = max(train_size/batch_size,1)
#超参数
iters_num = 10000
batch_size=100
learning_rate=0.1
network=TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
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)
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))