目录

前言:

4.1从数据中学习

4.1.1数据驱动

4.1.2训练数据和测试数据

4.2损失函数

4.2.1均方误差

4.2.2交叉熵误差

4.2.3mini-batch学习

4.2.5为什么要设定损失函数

4.3数值微分

4.3.1导数

4.3.2一个微分的例子

4.3.3偏导数

4.4梯度

4.4.1梯度法

4.4.2神经网络的梯度

4.5 学习算法的实现

4.5.1 2层神经网络的类

4.5.2 mini-batch的实现

4.5.3 基于测试数据的评价

小结:


前言:

上一章讲了神经网络前向传播内容,这一章讲如何根据数据训练出相关权重参数的过程。我们在实战中直接得出了参数权重,接下爱我们要学习

4.1从数据中学习

介绍神经网络的学习,即利用数据决定参数值的方法。我们将针对上一个实验的训练集进行学习

4.1.1数据驱动

图像的特征量通常表示为向量的形式。

神经网络基础学习笔记(三)神经网络的学习_机器学习基础

前面学习过分类算法SVM以及KNN,我们手动提取特征向量。

深 度 学 习 有 时 也 称 为 端 到 端 机 器 学 习(end-to-end machine learning)。这里所说的端到端是指从一端到另一端的意思,也就是 从原始数据(输入)中获得目标结果(输出)的意思。

神经网络的优点是对所有的问题都可以用同样的流程来解决。

4.1.2训练数据和测试数据

1.训练数据和测试数据:训练数据用来训练模型的,而测试数据就是不包含在训练模型内的数据用来评判训练后模型好坏的数据

2.泛化能力:如果测试的成绩好那么他的泛化能力就好。

3.过拟合:适应训练数据强,但泛化能力弱。

4.2损失函数

神经网络的学习的目的就是以该损失函数为基准,找出能使它 的值达到最小的权重参数

种评判方法均方误差和交叉熵误差

4.2.1均方误差

神经网络基础学习笔记(三)神经网络的学习_损失函数_02

实现代码:

神经网络基础学习笔记(三)神经网络的学习_损失函数_03

4.2.2交叉熵误差

交叉熵误差的值是由正确解标签所对应的输出结果决定的

神经网络基础学习笔记(三)神经网络的学习_神经网络_04

实现代码:

神经网络基础学习笔记(三)神经网络的学习_数据_05

4.2.3mini-batch学习

单个单个计算的话,数据处理时间过长,我们希望时间大部分花费咋计算上面。计算所有数据的(损失函数值的和/总数)的平均损失函数值

神经网络基础学习笔记(三)神经网络的学习_机器学习基础_06

分析上一个实验的数据包

神经网络基础学习笔记(三)神经网络的学习_损失函数_07

随机抽取10笔

神经网络基础学习笔记(三)神经网络的学习_损失函数_08

为了找到使损失函数的值尽可能小的地方,需要计算参数的导数(确切地讲是梯度),然后以这个导数为指引,

逐步更新参数的值。

4.2.5为什么要设定损失函数

因为损失函数可导且连续,便于调试。

以一个例子,比如有100个训练数据进行测试,发现精度为32%,如果以识别精度为指标,那么稍微修改权重参数精度也只会是32%,稍微改动大一点点权重参数精度可能直接变为33%,这种是离散不连续的变化;而损失函数不一样,稍微改变权重关系时,损失函数值就会立刻改变(如0.9524变为0.9612),这种值是连续性的,因为离散型的变化其导数(斜率)一般都为0,而连续型的变化导数一般不为0,所以能很容易判别出权重参数变化时的模型好坏。

神经网络基础学习笔记(三)神经网络的学习_机器学习基础_09

4.3数值微分

4.3.1导数


神经网络基础学习笔记(三)神经网络的学习_神经网络_10

神经网络基础学习笔记(三)神经网络的学习_机器学习基础_11

1.10e-50精度会有误差,比如python的float精度为小数点后4位,这里已经是50位了,所以要改成10e-4 舍入误差

2.f(x+h)-f(x)/h(向前差分)这个误差也很大,因为根据1的改变,h不是一个趋近于0的数,所以误差变大,应该用中心法改成f(x+h)-f(x-h)/2h(中心差分)

神经网络基础学习笔记(三)神经网络的学习_神经网络的学习_12

利用微小的差分求导数的过程称为数值微分(numerical  differentiation)

数值微分数值梯度

改进后代码

神经网络基础学习笔记(三)神经网络的学习_数据_13

中值定理的感觉

注意:

这种利用微小差分的导数过程为数值微分,而用数学公式推导的如y=x²导数为y=2x这种交解析性求导,这种叫做真导数

4.3.2一个微分的例子

如y=0.01x²+0.1x的导数实现

神经网络基础学习笔记(三)神经网络的学习_神经网络_14

神经网络基础学习笔记(三)神经网络的学习_神经网络的学习_15

神经网络基础学习笔记(三)神经网络的学习_神经网络_16

可以发现改进后的微分代码误差非常小

数值微分代码:

神经网络基础学习笔记(三)神经网络的学习_损失函数_17

4.3.3偏导数

比如实现神经网络基础学习笔记(三)神经网络的学习_数据_18的偏导数

先看这个函数的代码实现与图像:

神经网络基础学习笔记(三)神经网络的学习_损失函数_19

神经网络基础学习笔记(三)神经网络的学习_神经网络_20

偏导数实现:原理其实跟一元导数一样,就是带入一个真值消除一个变量而已

神经网络基础学习笔记(三)神经网络的学习_数据_21

公式难在数值微分,肉眼偏导验算一下,第一条公式为2*X0

4.4梯度

在刚才的例子中,我们按变量分别计算了x0和x1的偏导数。现在,我 们希望一起计算x0和x1的偏导数。比如

神经网络基础学习笔记(三)神经网络的学习_损失函数_22

比如我们求一个函数y=x0²+x1²变量有x0,x1,当我们对他全部变量(这里最多只有2个)进行偏导汇总而成的变量叫梯度。

神经网络基础学习笔记(三)神经网络的学习_损失函数_23

神经网络基础学习笔记(三)神经网络的学习_损失函数_24

梯度指向的图

神经网络基础学习笔记(三)神经网络的学习_机器学习基础_25

从这个图可以看出,梯度指向的点的函数值越来越小,反之越来越大,这是梯度重要性质!

神经网络基础学习笔记(三)神经网络的学习_神经网络的学习_26

4.4.1梯度法

寻找最小值的梯度法称为梯度下降法(gradient descent method), 寻找最大值的梯度法称为梯度上升法(gradient ascent method)。

神经网络(深度学习)中,梯度法主要是指梯度下降法

从上面的梯度我们可以知道梯度其实就是寻找梯度为0的地方,但是梯度为0不一定是最小值(高数里的靶点),他可能是极小值或者是鞍点(某方向看是极小值,另一方向看是极大值,导数为0的点),所以我们可以计算一次梯度后再次计算一次梯度,这样最后就能找到真正的最小值点了,这就是梯度法。

神经网络基础学习笔记(三)神经网络的学习_数据_27

学习率决定在一次学习中,应该学习多少,以及在多大程度上更新参数

注意:找最小其实跟找最大是一样的,就是取负的问题而已,不用太在意这个。

(梯度下降法)代码实现:

神经网络基础学习笔记(三)神经网络的学习_机器学习基础_28

numerical_gradient(f,x)会求函数的梯度,用该梯度乘以学习率得到的值进行更新操作,由step_num指定重复的 次数。

神经网络基础学习笔记(三)神经网络的学习_损失函数_29

这个例子中得到的最后的x值都是非常小的数,都几乎趋向于0,根据解析式方式(自己动笔试试)我们知道最小值为(0,0),跟上面的例子几乎一致(实际的例子中最小值不一定是0)。

每次求导后的过程:

实现代码:

# coding: utf-8
import numpy as np
import matplotlib.pylab as plt
from gradient_2d import numerical_gradient #求梯度,原理与数值微分相同

#梯度下降法
def gradient_descent(f, init_x, lr=0.01, step_num=100):
x = init_x
x_history = []

for i in range(step_num):
# 记录前一个x,用于绘图痕迹
x_history.append( x.copy() )
# 梯度下降法,为梯度乘以学习率
grad = numerical_gradient(f, x)
x -= lr * grad

return x, np.array(x_history)

# 求偏导,np.sum(x**2)
def function_2(x):
return x[0]**2 + x[1]**2

init_x = np.array([-3.0, 4.0])

# 学习率为0.1
lr = 0.1
# 梯度法的重复次数
step_num = 20
x, x_history = gradient_descent(function_2, init_x, lr=lr, step_num=step_num)

plt.plot( [-5, 5], [0,0], '--b')
plt.plot( [0,0], [-5, 5], '--b')
plt.plot(x_history[:,0], x_history[:,1], 'o')

plt.xlim(-3.5, 3.5)
plt.ylim(-4.5, 4.5)
plt.xlabel("X0")
plt.ylabel("X1")
plt.show()


神经网络基础学习笔记(三)神经网络的学习_机器学习基础_30

这里有个学习率太大  太小的例子:

神经网络基础学习笔记(三)神经网络的学习_损失函数_31

太大时结果会发散成很大的数,太小的话结果几乎没更新就结束了

所以学习率n太大太小都不好学习率被称为超参数,一般认为多次设定后取一个合理值。

学习率这样的参数称为超参数。

相对于神经网络的权重参数是通过训练 数据和学习算法自动获得的,学习率这样的超参数则是人工设定的。

4.4.2神经网络的梯度

所说的梯度是指损失函数关于权重参数的梯度

如图,我们有2*3的W权重参数,L为损失函数,梯度用神经网络基础学习笔记(三)神经网络的学习_神经网络的学习_32表示,如图:

神经网络基础学习笔记(三)神经网络的学习_损失函数_33

为simpleNet的类(源代码在ch04/gradient_simplenet.py 中

代码实现:

# coding: utf-8
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):
# 初始化2*3权重参数
self.W = np.random.randn(2,3)

def predict(self, x):
# 一层 权重乘以变量 == 一层感知机
return np.dot(x, self.W)

def loss(self, x, t):
# 计算交叉熵softmax()函数的损失值
z = self.predict(x)
y = softmax(z)
loss = cross_entropy_error(y, t)

return loss

x = np.array([0.6, 0.9])
t = np.array([0, 0, 1])

net = simpleNet()

f = lambda w: net.loss(x, t)
dW = numerical_gradient(f, net.W)

print(dW)


神经网络基础学习笔记(三)神经网络的学习_神经网络_34

来自-优秀的方同学: ​​javascript:void(0)​

神经网络基础学习笔记(三)神经网络的学习_数据_35

来自-优秀的方同学: ​​javascript:void(0)​


4.5 学习算法的实现

神经网络基础学习笔记(三)神经网络的学习_数据_36

神经网络基础学习笔记(三)神经网络的学习_神经网络_37

随机梯度下降法(stochastic gradient descent)。“随机”指的是“随机选择的” 的意思,因此,随机梯度下降法是“对随机选择的数据进行的梯度下降法”。简称SGD

实现代码:

4.5.1 2层神经网络的类

two_layer_net:

# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
from common.functions import *
from common.gradient import numerical_gradient


class TwoLayerNet:
"""
从第1个参数开始,依次表示:
输入层的神经元数、隐藏层的神经元数、输出层的神经元数
输入图像大小784 输出10个数字(0-9)
"""
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
# 初始化权重,是有要求的但是后面在补上
self.params = {}
# 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:监督数据
# numerical_gradient(self, 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

# 使用误差反向传播法计算梯度
def gradient(self, x, t):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
grads = {}

batch_num = x.shape[0]

# forward
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)

# backward
dy = (y - t) / batch_num
grads['W2'] = np.dot(z1.T, dy)
grads['b2'] = np.sum(dy, axis=0)

da1 = np.dot(dy, W2.T)
dz1 = sigmoid_grad(a1) * da1
grads['W1'] = np.dot(x.T, dz1)
grads['b1'] = np.sum(dz1, axis=0)

return grads



神经网络基础学习笔记(三)神经网络的学习_神经网络_38

神经网络基础学习笔记(三)神经网络的学习_数据_39

4.5.2 mini-batch的实现

类:train_neuralnet:

# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
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)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

# 超参数
iters_num = 10000 # 适当设定循环的次数
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

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)

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))

# 绘制图形
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

可以发现随着学习的进行,损失函数的值在不断减小。这 是学习正常进行的信号,表示神经网络的权重参数在逐渐拟合数据。也就是 说,神经网络的确在学习!通过反复地向它浇灌(输入)数据,神经网络正 在逐渐向最优参数靠近。

神经网络基础学习笔记(三)神经网络的学习_数据_40

实线表示训练数据的识别精度,虚线表示测试数据的识别精 度

4.5.3 基于测试数据的评价

神经网络基础学习笔记(三)神经网络的学习_神经网络_41

光看这个结果还不能说明该神经网络在 其他数据集上也一定能有同等程度的表现。

神经网络的学习中,必须确认是否能够正确识别训练数据以外的其他数 据,即确认是否会发生过拟合。

要评价神经网络的泛 化能力,就必须使用不包含在训练数据中的数据

 epoch是一个单位。一个 epoch表示学习中所有训练数据均被使用过 一次时的更新次数。比如,对于 10000笔训练数据,用大小为 100 笔数据的mini-batch进行学习时,重复随机梯度下降法 100次,所 有的训练数据就都被“看过”了A。此时,100次就是一个 epoch。

代码在上面:

神经网络基础学习笔记(三)神经网络的学习_损失函数_42

神经网络基础学习笔记(三)神经网络的学习_损失函数_43

之所以要计算每一个epoch的识别精度,是因 为如果在for语句的循环中一直计算识别精度,会花费太多时间

没有必要那么频繁地记录识别精度(只要从大方向上大致把握识别精度的推 移就可以了

小结:

本章所学的内容


  • 机器学习中使用的数据集分为训练数据和测试数据
  • 神经网络用训练数据进行学习,并用测试数据评价学习到的模型的 泛化能力。 
  • 神经网络的学习以损失函数为指标,更新权重参数,以使损失函数的值减小。
  • 利用某个给定的微小值的差分求导数的过程,称为数值微分
  • 利用数值微分,可以计算权重参数的梯度
  • 数值微分虽然费时间,但是实现起来很简单。

需要注意的时候,你会觉得比较多内容这一章,建议书写一下


名称

函数

数值微分

numerical_diff(x)

神经网络基础学习笔记(三)神经网络的学习_神经网络的学习_44偏导数


function_2(x)


偏导数

神经网络基础学习笔记(三)神经网络的学习_神经网络的学习_45

神经网络基础学习笔记(三)神经网络的学习_神经网络的学习_46梯度

numerical_gradient(f,x)

梯度下降法

gradient_descent(f, init_x, lr=0.01, step_num=100)


下一章中要实现的稍 微复杂一些的误差反向传播法可以高速地计算梯度。