神经网络入门
用到的函数
# 计算导数
def numerical_diff(f, x):
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2*h)
# 计算所有偏导数 =梯度
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
# 梯度下降法 x0 = x0 - lr*grad
def gradient_descent(f, init_x, lr=0.01, step_num=100):
x = init_x
x_history = []
for i in range(step_num):
x_history.append( x.copy() )
grad = numerical_gradient(f, x)
x -= lr * grad
return x, np.array(x_history)
def softmax(x):
if x.ndim == 2:
x = x.T
x = x - np.max(x, axis=0)
y = np.exp(x) / np.sum(np.exp(x), axis=0)
return y.T
x = x - np.max(x) # 溢出对策
return np.exp(x) / np.sum(np.exp(x))
# 激活函数 softmax 元素和为1
# softmax函数的输出是0.0到1.0之间的实数。并且,softmax
# 函数的输出值的总和是1
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
def softmax(a):
c = np.max(a)
exp_a = np.exp(a - c) # 溢出对策
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
# 激活函数 平滑 阶跃函数和sigmoid函数 ReLU(Rectified Linear Unit)函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
// 3层神经网络的实现 代码实现
def init_network():
with open("sample_weight.pkl", 'rb') as f:
network = pickle.load(f)
return network
def predict(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
print(W1.shape)
print(W2.shape)
print(W3.shape)
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = softmax(a3)
return y
softmax函数
回归问题用恒等函数,分类问题用softmax函数
softmax函数的输出是0.0到1.0之间的实数。并且,softmax函数的输出值的总和是1。输出总和为1是softmax函数的一个重要性质。正因为有了这个性质,我们才可以把softmax函数的输出解释为“概率”。
3层神经网络的推理处理代码
import sys, os
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
import numpy as np
import pickle
from dataset.mnist import load_mnist
from common.functions import sigmoid, softmax
def get_data():
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
return x_test, t_test
def init_network():
with open("sample_weight.pkl", 'rb') as f:
network = pickle.load(f)
return network
def predict(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
print(W1.shape)
print(W2.shape)
print(W3.shape)
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = softmax(a3)
return y
x, t = get_data()
print(x.shape)
network = init_network()
accuracy_cnt = 0
//评价它的识别精度(accuracy),即能在多大程度上正确分类
for i in range(len(x)):
y = predict(network, x[i])
p= np.argmax(y) # 获取概率最高的元素的索引
if p == t[i]:
accuracy_cnt += 1
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
损失函数
均方误差
4.2.5 为何要设定损失函数
交叉熵误差
梯度
神经网络的梯度
代码实现梯度
2层神经网络
# coding: utf-8
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变量中保存了该神经网络所需的全部参数
# params['W1'] 是第1层的权重,params['b1']是第1层的偏置。
# params['W2']是第2层的权重,params['b2']是第2层的偏置
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['W1']是第1层权重的梯度,grads['b1']是第1层偏置的梯度。
# grads['W2']是第2层权重的梯度,grads['b2']是第2层偏置的梯度
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
# 计算权重参数的梯度。
# numerical_gradient()的高速版,将在下一章实现
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
加法节点的反向传播
乘法节点的反向传播
ReLu层
Sigmoid层
Affine/Softmax层的实现
仿射变换
Softmax-with-Loss 层
由于(y1,y2,y3)是 Softmax层的输 出 ,( t1,t2,t3)是监督数据,所以(y1−t1,y2−t2,y3−t3)是 Softmax层的输出和教师标签的差分。
神经网络的反向传播会把这个差分表示的误差传递给前面的层,这是神经网络学习中的重要性质。
神经网络学习的目的就是通过调整权重参数,使神经网络的输出(Softmax
的输出)接近教师标签。因此,必须将神经网络的输出与教师标签的误差高
效地传递给前面的层。刚刚的(y1−t1,y2−t2,y3−t3)正是 Softmax层的输出
与教师标签的差,直截了当地表示了当前神经网络的输出与教师标签的误差。
使用交叉熵误差作为softmax函数的损失函数后,反向传播得到
(y1 − t1,y2 − t2,y3 − t3)这样 “漂亮”的结果。实际上,这样“漂亮”
的结果并不是偶然的,而是为了得到这样的结果,特意设计了交叉
熵误差函数。回归问题中输出层使用“恒等函数”,损失函数使用
“平方和误差”,也是出于同样的理由(3.5节)。也就是说,使用“平
方和误差”作为“恒等函数”的损失函数,反向传播才能得到(y1−
t1, y2 − t2, y3 − t3)这样“漂亮”的结果。
反向传播:
这里有错误 -1/s 应该为 1/s
SDG
class SGD:
"""随机梯度下降法(Stochastic Gradient Descent)"""
//参数lr表示learning rate(学习率)
def __init__(self, lr=0.01):
self.lr = lr
def update(self, params, grads):
for key in params.keys():
params[key] -= self.lr * grads[key]
SGD低效的根本原因是,梯度的方向并没有指向最小值的方向
Momentum
AdaGrad
AdaGrad的Ada来自英文单词Adaptive,即“适当的”的意思
学习率衰减(learning rate decay)的方法,即随着学习的进行,使学习率逐渐减小。实际上,一开始“多”学,然后逐渐“少”学的方法,在神经网络的学习中经常被使用。
Adam
Momentum参照小球在碗中滚动的物理规则进行移动,AdaGrad为参
数的每个元素适当地调整更新步伐。如果将这两个方法融合在一起会怎么样呢?这就是Adam方法的基本思路。
Adam会设置3个超参数。一个是学习率(论文中以α出现),另外两个是一次momentum系数β1和二次momentum系数β2。根据论文,标准的设定值是β1为0.9,β2为0.999。设置了这些值后,大多数情况下都能顺利运行
对比4种方法:
观察权重初始值是如何影响隐藏层的激活值的分布的
观察了各层的激活值分布,并从中了解到如果设定了合适的权重初始值,则各层的激活值分布会有适当的广度,从而可以顺利地进行学习。
那么,为了使各层拥有适当的广度,“强制性”地调整激活值的分布会怎样呢?实际上,Batch Normalization[11]方法就是基于这个想法而产生的。
Batch Normalization
在上一节,我们观察了各层的激活值分布,并从中了解到如果设定了合适的权重初始值,则各层的激活值分布会有适当的广度,从而可以顺利地进行学习。那么,为了使各层拥有适当的广度,“强制性”地调整激活值的分布会怎样呢?实际上,Batch Normalization[11]方法就是基于这个想法而产生的。
为什么Batch Norm这么惹人注目呢?因为Batch Norm有以下优点。
• 可以使学习快速进行(可以增大学习率)。
• 不那么依赖初始值(对于初始值不用那么神经质)。
• 抑制过拟合(降低Dropout等的必要性)
式(6.7)所做的是将mini-batch的输入数据{x1,x2,…,xm}变换为均值为0、方差为1的数据,非常简单。通过将这个处理插入到激活函数的前面(或者后面)A,可以减小数据分布的偏向。
权值衰减
权值衰减是一直以来经常被使用的一种抑制过拟合的方法。该方法通过在学习的过程中对大的权重进行惩罚,来抑制过拟合。很多过拟合原本就是因为权重参数取值过大才发生的。
Dropout
机器学习中经常使用集成学习。所谓集成学习,就是让多个模型单独进行学习,推理时再取多个模型的输出的平均值。用神经网络的语境来说,比如,准备5个结构相同(或者类似)的网络,分别进行学习,测试时,以这5个网络的输出的平均值作为答案。实验告诉们,通过进行集成学习,神经网络的识别精度可以提高好几个百分点。这个集成学习与Dropout有密切的关系。这是因为可以将Dropout理解为,通过在学习过程中随机删除神经元,从而每一次都让不同的模型进行学习。并且,推理时,通过对神经元的输出乘以删除比例(比如,0.5等),可以取得模型的平均值。也就是说,可以理解成,Dropout将集成学习的效果(模拟地)通过一个网络实现了。
超参数的验证
比如各层的神经元数量、batch大小、参数更新时的学习率或权值衰减等。如果这些超参数没有设置合适的值,模型的性能就会很差。虽然超参数的取值非常重要,但是在决定超参数的过程中一般会伴随很多的试错。本节将介绍尽可能高效地寻找超参数的值的方法。
训练数据用于参数(权重和偏置)的学习,验证数据用于超参数的性能评估。为了确认泛化能力,要在最后使用(比较理想的是只用一次)测试数据。
超参数的最优
在超参数的最优化中,如果需要更精炼的方法,可以使用贝叶斯最优化(Bayesian optimization)。贝叶斯最优化运用以贝叶斯定理为中心的数学理论,能够更加严密、高效地进行最优化。
卷积神经网络
CNN中新出现了卷积层(Convolution层)和池化层(Pooling层)
卷积层
全连接层存在的问题
全连接层存在什么问题呢?那就是数据的形状被“忽视”了。比如,输
入数据是图像时,图像通常是高、长、通道方向上的3维形状。但是,向全连接层输入时,需要将3维数据拉平为1维数据。实际上,前面提到的使用了MNIST数据集的例子中,输入图像就是1通道、高28像素、长28像素的(1,28,28)形状,但却被排成1列,以784个数据的形式输入到最开始的Affine层。
而卷积层可以保持形状不变。当输入数据是图像时,卷积层会以3维数据的形式接收输入数据,并同样以3维数据的形式输出至下一层。因此,在CNN中,可以(有可能)正确理解图像等具有形状的数据。另外,CNN中,有时将卷积层的输入输出数据称为特征图(feature map)。其中,卷积层的输入数据称为输入特征图(input featuremap), 输 出数据称为输出特征图(output feature map)。
乘积累加运算
在全连接的神经网络中,除了权重参数,还存在偏置。CNN中,滤波器的参数就对应之前的权重。并且,CNN中也存在偏置。
填充
步幅
下对于填充和步幅,如何计算输出大小。
通道数只能设定为和输入数据的通道数相同的值
通道方向上有多个特征图时,会按通道进行输入数据和滤波器的卷积运算,并将结果相加,从而得到输出
在这个例子中,数据输出是1张特征图。所谓1张特征图,换句话说,就是通道数为1的特征图。
如果要在通道方向上也拥有多个卷积运算的输出,该怎么做呢?为此,就需要用到多个滤波器(权重)。用图表示的话,如图7-11所示。
批处理
池化层
除了Max池化之外,还有Average池化等。相对于Max池化是从目标区域中取出最大值,Average池化则是计算目标区域的平均值。在图像识别领域,主要使用Max池化。
池化层有以下特征。
- 没有要学习的参数
池化层和卷积层不同,没有要学习的参数。池化只是从目标区域中取最大值(或者平均值),所以不存在要学习的参数。 - 通道数不发生变化
经过池化运算,输入数据和输出数据的通道数不会发生变化。如图7-15所示,计算是按通道独立进行的。 - 对微小的位置变化具有鲁棒性(健壮)
基于im2col的展开
im2col这个名称是“image to column”的缩写,翻译过来就是“从图像到矩阵”的意思
对于输入数据,将应用滤波器的区域(3维方块)横向展开为1列。im2col会在所有应用滤波器的地方进行这个展开处理
接口
im2col (input_data, filter_h, filter_w, stride=1, pad=0)
• input_data―由(数据量,通道,高,长)的 4维数组构成的输入数据
• filter_h―滤波器的高
• filter_w―滤波器的长
• stride―步幅
• pad―填充
end