学习就是从训练数据中自动获取最优权重参数的过程。引入损失函数这一指标,学习的目的是找出使损失函数达到最小的权重参数。使用函数斜率的梯度法来找这个最小值。
人工智能有两派,一派认为实现人工智能必须用逻辑和符号系统,自顶向下看问题;另一派认为通过仿造人脑可以达到人工智能,自底向上看问题。前一派是“想啥来啥”,后一派是“吃啥补啥”。前者偏唯心,后者偏唯物。两派一直是人工智能领域“两个阶级、两条路线”的斗争,这斗争有时还是你死我活。今天学习的是神经网络派。
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_xfor i in range(step_num):grad = numerical_gradient(f,x)x -= lr * gradreturn 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_gradientclass 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_gradientclass 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) + b1z1 = sigmoid(a1)a2 = np.dot(z1,W2) + b2y = softmax(a2)return ydef 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 accuracydef 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-batchbatch_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))