深度学习
一、神经网络基础
1.感知机
w1 和 w2是控制输入信号的重要性的参数,而偏置是调整神经元被激活的容易程度(输出信号为 1 的程度)的参数。
单层感知机的局限性:只能表示由一条直线分割的空间。
# 与门
def AND(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.7
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
# 与非门
def NAND(x1, x2):
x = np.array([x1, x2])
w = np.array([-0.5, -0.5]) # 仅权重和偏置与AND不同!
b = 0.7
a = np.sum(w * x) + b
if a <= 0:
return 0
else:
return 1
# 或门
def OR(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5]) # 仅权重和偏置与AND不同!
b = -0.2
a = np.sum(w * x) + b
if a <= 0:
return 0
else:
return 1
# 异或门(多层感知机)
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
2.神经网络
激活函数
①sigmoid 函数
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
x = np.linspace(-5, 5, 100)
y = 1 / (1 + np.exp(-x))
plt.plot(x, y)
plt.axhline(y=0, color='k')
plt.axvline(x=0, color='k')
plt.show()
②tanh函数(双曲正切函数)
③ReLU 函数(修正线性单元)
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
x = np.linspace(-5, 5, 100)
y = np.maximum(0, x)
plt.plot(x, y)
# plt.axhline(y=0, color='k')
# plt.axvline(x=0, color='k')
plt.show()
④Leaky ReLU(带泄露的修正线性单元)
⑤PReLU(Parameteric Rectified Linear Unit,参数化修正线性单元)
⑥RReLU(Randomized Leaky Rectified Linear Unit,随机带泄露的修正线性单元)
⑦ELU(Exponential Linear Unit,指数修正线性单元)
更多激活函数见:https://www.cnblogs.com/CJT-blog/p/10421822.html
3.矩阵乘法
np.dot(X, W)
4.神经网络前向传播算法
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def identity_function(x):
return x
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)
- 模块化
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def identity_function(x):
return x
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
network['b1'] = np.array([0.1, 0.2, 0.3])
network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
network['b2'] = np.array([0.1, 0.2])
network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
network['b3'] = np.array([0.1, 0.2])
return network
def forward(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
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 = identity_function(a3)
return y
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
5.恒等函数与softmax函数
(1)恒等函数
恒等函数会将输入按原样输出,对于输入的信息,不加以任何改动地直接输出。
def identity_function(x):
return x
恒等函数一般用于回归问题。
(2)softmax函数
softmax函数返回的是每个类别的概率。
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
缺陷:exp()为指数函数,exp(a)可能为一个很大的值,导致溢出(inf)。
-
溢出缺陷改进
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
softmax函数一般作为输出层的激活函数。
二、神经网络学习算法
1.损失函数(loss function)
(1)均方误差(mean squared error,MSE)
yk 是表示神经网络的输出,tk 表示监督数据,k 表示数据的维数。
def mean_squared_error(y, t):
return 0.5 * np.sum((y-t)**2)
(2)交叉熵(cross entropy error)
log 表示以 e为底数的自然对数ln(loge)。yk 是神经网络的输出,tk 是正确解标签。并且,tk 中只有正确解标签的索引为 1,其他均为 0(one-hot 表示)。
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta))
2. mini-batch学习
mini-batch给训练过程引入了随机性,使得训练过程不致陷入局部最优。
- 独热编码
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(t * np.log(y + 1e-7)) / batch_size
- 非独热编码
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
3.梯度下降法
-
求导数
def numerical_diff(f, x):
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2*h)
-
求梯度
函数z=f(x,y)在点P(x,y)的梯度,记作gradf(x,y)或▽f(x,y)。gradf(x,y)=
def numerical_gradient(f, x):
h = 1e-4 # 0.0001
# 生成和x形状相同的数组
grad = np.zeros_like(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
- 梯度下降算法
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
- 梯度下降法可视化
def function_2(x):
return x[0]**2 + x[1]**2
def draw_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)
init_x = np.array([-3.0, 4.0])
lr = 0.1![062.png](https://upload-images.jianshu.io/upload_images/17476284-2603f60b731a6d85.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
step_num = 20
x, x_history = draw_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.xlabel('X0')
plt.ylabel('X1')
plt.show()
三、神经网络反向传播算法
1.计算图
计算图将计算过程用图形表示出来。这里说的图形是数据结构图,通过多个节点和边表示(连接节点的直线称为“边”)。
2.链式法则
反向传播将局部导数向正方向的反方向(从右到左)传递,传递这个局部导数的原理,是基于链式法则(chain rule)的。
3.激活函数层实现
4.Affine/Softmax层实现
(1)Affine层(仿射层)
class Affine:
def __init__(self, W, b):
self.W = W
self.b = b
self.x = None
self.dW = None
self.db = None
def forward(self, x):
self.x = x
out = np.dot(x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis=0)
return dx
(2)Softmax-with-Loss 层
class SoftmaxWithLoss:
def __init__(self):
self.loss = None # 损失
self.y = None # softmax的输出
self.t = None # 监督数据(one-hot vector)
def forward(self, x, t):
self.t = t
self.y = softmax(x)
self.loss = cross_entropy_error(self.y, self.t)
return self.loss
def backward(self, dout=1):
batch_size = self.t.shape[0]
dx = (self.y - self.t) / batch_size
return dx
四、神经网络最优化
1.参数更新
(1)随机梯度下降法(stochastic gradient descent,SGD)
class SGD:
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缺点:在某些情况下效率低
(2)Momentum算法
W 表示要更新的权重参数,∂L/∂W表示损失函数关于 W 的梯度,η 表示学习率。
这里新出现了一个变量 v,对应物理上的速度。
class Momentum:
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = None
def update(self, params, grads):
if self.v is None:
self.v = {}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
params[key] += self.v[key]
(3)AdaGrad算法
在神经网络的学习中,学习率(数学式中记为 η)的值很重要。学习率过小,会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散而不能正确进行。
在关于学习率的有效技巧中,有一种被称为学习率衰减(learning rate decay)的方法,即随着学习的进行,使学习率逐渐减小。实际上,一开始“多”学,然后逐渐“少”学的方法,在神经网络的学习中经常被使用。
逐渐减小学习率的想法,相当于将“全体”参数的学习率值一起降低。而 AdaGrad [6] 进一步发展了这个想法,针对“一个一个”的参数,赋予其“定制”的值。
AdaGrad 会为参数的每个元素适当地调整学习率,与此同时进行学习(AdaGrad 的 Ada 来自英文单词 Adaptive,即“适当的”的意思)。
这里新出现了变量 h,如式 (6.5) 所示,它保存了以前的所有梯度值的平方和(式(6.5)中的⊙表示对应矩阵元素的乘法)。
class AdaGrad:
def __init__(self, lr=0.01):
self.lr = lr
self.h = None
def update(self, params, grads):
if self.h is None:
self.h = {}
for key, val in params.items():
self.h[key] = np.zeros_like(val)
for key in params.keys():
self.h[key] += grads[key] * grads[key]
params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
2.权重初始值
权重初始化不能为全0初始化,否则会导致梯度下降法失效。
也不能将权重初始值设成一样的值,否则在误差反向传播法中,所有的权重值都会进行相同的更新,导致多结点无意义。
为了防止“权重均一化”(严格地讲,是为了瓦解权重的对称结构),必须随机生成初始值。
(1)Xavier初始值
node_num = 100 # 前一层的节点数
w = np.random.randn(node_num, node_num) / np.sqrt(node_num)
sigmoid函数、tanh函数等S形函数作为激活函数时适用Xavier初始值。
(2)He初始值
当激活函数使用 ReLU 时,一般推荐使用 ReLU 专用的初始值,也就是 Kaiming He 等人推荐的初始值,也称为“He 初始值”。
当前一层的节点数为 n 时,He 初始值使用标准差为√(2/n)的高斯分布。
node_num = 100 # 前一层的节点数
w = np.random.randn(node_num, node_num) * np.sqrt(2 / node_num)
ReLu函数作为激活函数时适用He初始值。
3.批标准化(Batch Normalization)
这里对 mini-batch 的 m 个输入数据的集合 B={x1, x2···, xm}求均值μB和方差σB^2。然后,对输入数据进行均值为 0、方差为 1(合适的分布)的正规化。ε 为一个极小值,防止分母为0。
Batch Norm 层会对正规化后的数据进行缩放和平移的变换。
4.正则化
(1)过拟合
过拟合原因
- 模型拥有大量参数、表现力强。
- 训练数据少。
(2)权值衰减
权值衰减是一直以来经常被使用的一种抑制过拟合的方法。该方法通过在学习的过程中对大的权重进行惩罚,来抑制过拟合。很多过拟合原本就是因为权重参数取值过大才发生的。
在求权重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数λW。
(3)Dropout(随机失活)
Dropout 是一种在学习的过程中随机删除神经元的方法。训练时,随机选出隐藏层的神经元,然后将其删除。被删除的神经元不再进行信号的传递,如下图所示。训练时,每传递一次数据,就会随机选择要删除的神经元。然后,测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例后再输出。
class Dropout:
def __init__(self, dropout_ratio=0.5):
self.dropout_ratio = dropout_ratio
self.mask = None
def forward(self, x, train_flg=True):
if train_flg:
self.mask = np.random.rand(*x.shape) > self.dropout_ratio
return x * self.mask
else:
return x * (1.0 - self.dropout_ratio)
def backward(self, dout):
return dout * self.mask
5.超参数验证
- 步骤 0:设定超参数的范围。
- 步骤 1:从设定的超参数范围中随机采样。
- 步骤 2:使用步骤 1 中采样到的超参数的值进行学习,通过验证数据评估识别精度(但是要将 epoch 设置得很小)。
- 步骤3:重复步骤 1 和步骤 2(100 次等),根据它们的识别精度的结果,缩小超参数的范围。