深度学习入门:基于Python的理论与实现 - 学习笔记
第1章 Python入门
核心思想: 掌握Python基础语法和NumPy、Matplotlib库,为深度学习实现做好准备。
1.1 Python是什么
是什么? 简单、易读、开源的编程语言,适合数据科学和深度学习。
为什么需要? 深度学习框架(TensorFlow、PyTorch等)都提供Python接口。
作用? 实现深度学习算法,进行数值计算和数据可视化。
1.2 NumPy
是什么? 用于数值计算的Python库,提供高效的数组操作。
为什么需要? 深度学习需要大量矩阵运算,NumPy提供优化实现。
作用? 实现向量、矩阵运算,支持广播机制。
对应代码:
python
import numpy as np
# 生成NumPy数组
x = np.array([1.0, 2.0, 3.0])
# 算术运算(element-wise)
x = np.array([1.0, 2.0, 3.0])
y = np.array([2.0, 4.0, 6.0])
x + y # array([3., 6., 9.])
x * y # array([2., 8., 18.])
# 广播
A = np.array([[1, 2], [3, 4]])
A * 10 # array([[10, 20], [30, 40]])
# 矩阵乘法
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
np.dot(A, B) # array([[19, 22], [43, 50]])
1.3 Matplotlib
是什么? Python的绘图库。
为什么需要? 可视化训练过程和结果。
对应代码:
python
import numpy as np
import matplotlib.pyplot as plt
# 绘制sin函数
x = np.arange(0, 6, 0.1)
y = np.sin(x)
plt.plot(x, y)
plt.show()
# 绘制多条曲线
y1 = np.sin(x)
y2 = np.cos(x)
plt.plot(x, y1, label="sin")
plt.plot(x, y2, linestyle="--", label="cos")
plt.legend()
plt.show()
第2章 感知机
核心思想: 感知机是神经网络的起源,通过权重和偏置实现逻辑电路,但单层感知机无法解决非线性问题。
2.1 感知机是什么
是什么? 接收多个输入信号,输出一个信号的算法。
数学表达式:
y={0(w1x1+w2x2≤θ)1(w1x1+w2x2>θ)y = \begin{cases} 0 & (w_1x_1 + w_2x_2 \leq \theta) \\ 1 & (w_1x_1 + w_2x_2 > \theta) \end{cases}y={01(w1x1+w2x2≤θ)(w1x1+w2x2>θ)
或等价形式:
y={0(b+w1x1+w2x2≤0)1(b+w1x1+w2x2>0)y = \begin{cases} 0 & (b + w_1x_1 + w_2x_2 \leq 0) \\ 1 & (b + w_1x_1 + w_2x_2 > 0) \end{cases}y={01(b+w1x1+w2x2≤0)(b+w1x1+w2x2>0)
其中 bbb 为偏置,w1,w2w_1, w_2w1,w2 为权重。
对应代码:
python
import numpy as np
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])
b = 0.7
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
def OR(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.2
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
2.2 感知机的局限性
是什么? 单层感知机无法表示异或门(XOR)。
为什么? 异或门是非线性问题,单层感知机只能表示线性空间。
局限? 无法用一条直线将异或门的输出分开。
[由此引出:多层感知机]
→ 通过叠加层(多层感知机)可以解决非线性问题
→ 理论上2层感知机可以表示任意函数
2.3 多层感知机
是什么? 叠加了多层的感知机。
为什么需要? 单层感知机无法解决非线性问题。
作用? 通过组合与门、与非门、或门实现异或门。
对应代码:
python
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
真值表验证:
| x1 | x2 | s1(NAND) | s2(OR) | y(AND) |
|---|---|---|---|---|
| 0 | 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 | 1 |
| 1 | 0 | 1 | 1 | 1 |
| 1 | 1 | 0 | 1 | 0 |
第3章 神经网络
核心思想: 神经网络通过平滑的激活函数(如sigmoid)替代阶跃函数,实现可学习的非线性映射。
3.1 激活函数
是什么? 将输入信号的总和转换为输出信号的函数。
为什么需要? 决定如何激活输入信号的总和,引入非线性。
作用? 使神经网络能够学习复杂的非线性模式。
对比表格:
| 特性 | 阶跃函数 | Sigmoid函数 | ReLU函数 |
|---|---|---|---|
| 表达式 | h(x)={0x≤01x>0h(x)=\begin{cases}0&x\leq0\\1&x>0\end{cases}h(x)={01x≤0x>0 | h(x)=11+e−xh(x)=\frac{1}{1+e^{-x}}h(x)=1+e−x1 | h(x)={xx>00x≤0h(x)=\begin{cases}x&x>0\\0&x\leq0\end{cases}h(x)={x0x>0x≤0 |
| 平滑性 | 不连续 | 平滑连续 | 连续(原点不可导) |
| 输出范围 | {0, 1} | (0, 1) | [0, +∞) |
| 导数 | 几乎处处为0 | h(x)(1−h(x))h(x)(1-h(x))h(x)(1−h(x)) | {1x>00x≤0\begin{cases}1&x>0\\0&x\leq0\end{cases}{10x>0x≤0 |
| 梯度消失 | 严重 | 可能 | 缓解(负半轴) |
[由此引出:激活函数的选择]
→ 使用ReLU缓解梯度消失
→ 使用Batch Normalization稳定梯度分布
→ 使用残差连接构建捷径
对应代码:
python
import numpy as np
# 阶跃函数
def step_function(x):
return np.array(x > 0, dtype=np.int)
# Sigmoid函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# ReLU函数
def relu(x):
return np.maximum(0, x)
3.2 为什么激活函数必须是非线性的
是什么? 激活函数不能使用线性函数。
为什么? 使用线性函数时,多层网络等价于单层网络。
证明: 假设 h(x)=cxh(x) = cxh(x)=cx,则3层网络:
y(x)=h(h(h(x)))=c⋅c⋅c⋅x=c3xy(x) = h(h(h(x))) = c \cdot c \cdot c \cdot x = c^3xy(x)=h(h(h(x)))=c⋅c⋅c⋅x=c3x
这等价于单层网络 y(x)=axy(x) = axy(x)=ax(其中 a=c3a = c^3a=c3)。
3.3 3层神经网络的实现
对应代码:
python
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
3.4 输出层的设计
是什么? 根据问题类型选择不同的输出层激活函数。
对比表格:
| 问题类型 | 输出层激活函数 | 损失函数 |
|---|---|---|
| 回归问题 | 恒等函数 | 均方误差 |
| 二分类问题 | Sigmoid函数 | 交叉熵误差 |
| 多分类问题 | Softmax函数 | 交叉熵误差 |
Softmax函数:
yk=eak∑i=1neaiy_k = \frac{e^{a_k}}{\sum_{i=1}^{n}e^{a_i}}yk=∑i=1neaieak
Softmax实现(含溢出对策):
python
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特征: 输出值在0.0~1.0之间,总和为1,可解释为概率。
3.5 手写数字识别(MNIST)
对应代码:
python
import sys, os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
# 加载数据
(x_train, t_train), (x_test, t_test) = load_mnist(
normalize=True, flatten=True, one_hot_label=False)
# 神经网络推理
def predict(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 = softmax(a3)
return y
# 批处理
batch_size = 100
for i in range(0, len(x), batch_size):
x_batch = x[i:i+batch_size]
y_batch = predict(network, x_batch)
p = np.argmax(y_batch, axis=1)
第4章 神经网络的学习
核心思想: 神经网络通过损失函数评估性能,利用梯度下降法自动更新权重参数,从数据中学习最优解。
4.1 从数据中学习
是什么? 由数据自动决定权重参数的值。
为什么需要? 人工决定成千上万个参数是不可能的。
作用? 避免人为介入,实现端到端学习。
训练数据与测试数据:
- 训练数据:用于学习,调整权重参数
- 测试数据:用于评估泛化能力
- 泛化能力:处理未被观察过的数据的能力
4.2 损失函数
是什么? 表示神经网络性能的"恶劣程度"的指标。
为什么需要? 为神经网络学习提供优化目标。
作用? 以损失函数为基准,找出使它的值达到最小的权重参数。
[由此引出:为什么不能用识别精度作为指标]
→ 识别精度对微小参数变化不敏感(离散值)
→ 绝大多数地方导数为0,无法更新参数
→ 损失函数是连续值,可以计算梯度
4.3 均方误差
是什么? 计算神经网络输出与监督数据各元素之差的平方和。
公式:
E=12∑k(yk−tk)2E = \frac{1}{2}\sum_{k}(y_k - t_k)^2E=21k∑(yk−tk)2
对应代码:
python
def mean_squared_error(y, t):
return 0.5 * np.sum((y - t)**2)
4.4 交叉熵误差
是什么? 计算正确解标签对应的输出的自然对数。
公式:
E=−∑ktklog(yk)E = -\sum_{k}t_k\log(y_k)E=−k∑tklog(yk)
对应代码:
python
def cross_entropy_error(y, t):
delta = 1e-7 # 防止log(0)
return -np.sum(t * np.log(y + delta))
# 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
4.5 数值微分与梯度
是什么? 利用微小差分求导数的方法。
为什么需要? 计算损失函数对权重参数的梯度。
作用? 梯度指示损失函数值减小最多的方向。
数值微分(中心差分):
python
def numerical_diff(f, x):
h = 1e-4 # 0.0001
return (f(x + h) - f(x - h)) / (2 * h)
梯度:
python
def numerical_gradient(f, x):
h = 1e-4
grad = np.zeros_like(x)
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = tmp_val + h
fxh1 = f(x)
x[idx] = tmp_val - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2 * h)
x[idx] = tmp_val
return grad
4.6 梯度下降法
是什么? 沿着梯度方向更新参数,逐渐减小函数值的方法。
公式:
x0=x0−η∂f∂x0x_0 = x_0 - \eta\frac{\partial f}{\partial x_0}x0=x0−η∂x0∂f
x1=x1−η∂f∂x1x_1 = x_1 - \eta\frac{\partial f}{\partial x_1}x1=x1−η∂x1∂f
其中 η\etaη 为学习率。
对应代码:
python
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.7 神经网络的学习算法实现
对应代码:
python
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
学习过程:
python
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]
[由此引出:数值微分的缺点]
→ 计算量大,耗时长
→ 需要更高效的方法:误差反向传播法
第5章 误差反向传播法
核心思想: 通过计算图和链式法则,高效地计算神经网络中各层参数的梯度。
5.1 计算图
是什么? 将计算过程用图形表示,通过节点和边表示。
为什么需要? 直观理解计算过程,高效计算导数。
作用? 通过正向传播计算结果,通过反向传播计算导数。
局部计算: 无论全局多么复杂,各个节点只需进行与自己相关的简单计算。
5.2 链式法则
是什么? 复合函数的导数等于各函数导数的乘积。
公式:
如果 z=f(g(x))z = f(g(x))z=f(g(x)),则
∂z∂x=∂z∂g⋅∂g∂x\frac{\partial z}{\partial x} = \frac{\partial z}{\partial g} \cdot \frac{\partial g}{\partial x}∂x∂z=∂g∂z⋅∂x∂g
计算图中的反向传播: 将上游传来的导数乘以局部导数,传递给下游。
5.3 加法节点和乘法节点的反向传播
加法节点: 将上游的值原封不动地传给下游
∂z∂x=1,∂z∂y=1\frac{\partial z}{\partial x} = 1, \quad \frac{\partial z}{\partial y} = 1∂x∂z=1,∂y∂z=1
乘法节点: 将上游的值乘以正向传播时的"翻转值"
∂z∂x=y,∂z∂y=x\frac{\partial z}{\partial x} = y, \quad \frac{\partial z}{\partial y} = x∂x∂z=y,∂y∂z=x
对应代码:
python
class MulLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self, x, y):
self.x = x
self.y = y
out = x * y
return out
def backward(self, dout):
dx = dout * self.y # 翻转x和y
dy = dout * self.x
return dx, dy
class AddLayer:
def __init__(self):
pass
def forward(self, x, y):
out = x + y
return out
def backward(self, dout):
dx = dout * 1
dy = dout * 1
return dx, dy
5.4 激活函数层的实现
ReLU层:
python
class Relu:
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x <= 0)
out = x.copy()
out[self.mask] = 0
return out
def backward(self, dout):
dout[self.mask] = 0
dx = dout
return dx
Sigmoid层:
python
class Sigmoid:
def __init__(self):
self.out = None
def forward(self, x):
out = 1 / (1 + np.exp(-x))
self.out = out
return out
def backward(self, dout):
dx = dout * (1.0 - self.out) * self.out
return dx
5.5 Affine层(全连接层)
是什么? 实现矩阵乘积和偏置加法的层。
正向传播: Y=X⋅W+BY = X \cdot W + BY=X⋅W+B
反向传播:
∂L∂X=∂L∂Y⋅WT\frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} \cdot W^T∂X∂L=∂Y∂L⋅WT
∂L∂W=XT⋅∂L∂Y\frac{\partial L}{\partial W} = X^T \cdot \frac{\partial L}{\partial Y}∂W∂L=XT⋅∂Y∂L
∂L∂B=∑i∂L∂Yi\frac{\partial L}{\partial B} = \sum_{i}\frac{\partial L}{\partial Y_i}∂B∂L=i∑∂Yi∂L
对应代码:
python
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
5.6 Softmax-with-Loss层
是什么? Softmax函数和交叉熵误差的组合层。
反向传播的"漂亮"结果:
∂L∂ak=yk−tk\frac{\partial L}{\partial a_k} = y_k - t_k∂ak∂L=yk−tk
这是Softmax输出与教师标签的差分,直接表示当前神经网络的误差。
对应代码:
python
class SoftmaxWithLoss:
def __init__(self):
self.loss = None
self.y = None
self.t = None
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
5.7 误差反向传播法的实现
对应代码:
python
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)
self.layers = OrderedDict()
self.layers['Affine1'] = \
Affine(self.params['W1'], self.params['b1'])
self.layers['Relu1'] = Relu()
self.layers['Affine2'] = \
Affine(self.params['W2'], self.params['b2'])
self.lastLayer = SoftmaxWithLoss()
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
def gradient(self, x, t):
# forward
self.loss(x, t)
# backward
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
grads = {}
grads['W1'] = self.layers['Affine1'].dW
grads['b1'] = self.layers['Affine1'].db
grads['W2'] = self.layers['Affine2'].dW
grads['b2'] = self.layers['Affine2'].db
return grads
5.8 梯度确认
是什么? 比较数值微分和误差反向传播法的结果,确认实现是否正确。
为什么需要? 误差反向传播法实现复杂,容易出错。
对应代码:
python
grad_numerical = network.numerical_gradient(x_batch, t_batch)
grad_backprop = network.gradient(x_batch, t_batch)
for key in grad_numerical.keys():
diff = np.average(np.abs(grad_backprop[key] - grad_numerical[key]))
print(key + ":" + str(diff))
第6章 与学习相关的技巧
核心思想: 通过优化参数更新方法、合理设置权重初始值、使用正则化技术,提高神经网络的学习效率和泛化能力。
6.1 参数的更新方法
对比表格:
| 方法 | 特点 | 优点 | 缺点 |
|---|---|---|---|
| SGD | 沿梯度方向更新 | 简单、易实现 | 非均向函数效率低 |
| Momentum | 引入速度概念 | 加速收敛,减少振荡 | 需要额外参数 |
| AdaGrad | 自适应学习率 | 适合稀疏数据 | 学习率可能过小 |
| Adam | Momentum + AdaGrad | 综合优点,效果好 | 需要更多超参数 |
SGD:
python
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]
Momentum:
python
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]
AdaGrad:
python
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)
6.2 权重的初始值
是什么? 神经网络学习开始前权重的初始赋值。
为什么重要? 关系到学习能否成功进行。
[由此引出:权重初始化的关键问题]
→ 权重不能设为0(会导致对称性问题)
→ 权重不能太大(会导致梯度消失)
→ 权重不能太小(会导致表现力受限)
对比表格:
| 初始值 | 适用激活函数 | 公式 |
|---|---|---|
| Xavier | Sigmoid、Tanh | 1n\sqrt{\frac{1}{n}}n1 |
| He | ReLU | 2n\sqrt{\frac{2}{n}}n2 |
Xavier初始值:
python
node_num = 100 # 前一层的节点数
w = np.random.randn(node_num, node_num) / np.sqrt(node_num)
He初始值:
python
node_num = 100 # 前一层的节点数
w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)
6.3 Batch Normalization
是什么? 对每层的激活值进行正规化的技术。
为什么需要? 使各层拥有适当的广度,加速学习。
作用?
- 使学习快速进行(可增大学习率)
- 不那么依赖初始值
- 抑制过拟合
算法:
μB←1m∑i=1mxi\mu_B \leftarrow \frac{1}{m}\sum_{i=1}^{m}x_iμB←m1i=1∑mxi
σB2←1m∑i=1m(xi−μB)2\sigma^2_B \leftarrow \frac{1}{m}\sum_{i=1}^{m}(x_i - \mu_B)^2σB2←m1i=1∑m(xi−μB)2
x^i←xi−μBσB2+ϵ\hat{x}_i \leftarrow \frac{x_i - \mu_B}{\sqrt{\sigma^2_B + \epsilon}}x^i←σB2+ϵ xi−μB
yi←γx^i+βy_i \leftarrow \gamma\hat{x}_i + \betayi←γx^i+β
6.4 正则化
是什么? 抑制过拟合的技术。
为什么需要? 防止模型过度拟合训练数据,提高泛化能力。
[由此引出:过拟合的原因]
→ 模型拥有大量参数、表现力强
→ 训练数据少
6.4.1 权值衰减
是什么? 对大的权重进行惩罚。
公式:
L=L0+12λW2L = L_0 + \frac{1}{2}\lambda W^2L=L0+21λW2
其中 λ\lambdaλ 是控制正则化强度的超参数。
6.4.2 Dropout
是什么? 随机删除神经元的方法。
为什么有效? 可以理解为集成学习的近似实现。
对应代码:
python
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
6.5 超参数的验证
是什么? 寻找最优超参数值的过程。
超参数包括:
- 各层神经元数量
- batch大小
- 学习率
- 权值衰减系数
验证数据: 专门用于评估超参数性能的数据,不能用测试数据。
最优化步骤:
- 设定超参数的范围(对数尺度)
- 从范围内随机采样
- 使用采样值进行学习,通过验证数据评估
- 重复步骤2-3,根据结果缩小范围
对应代码:
python
# 对数尺度随机采样
weight_decay = 10 ** np.random.uniform(-8, -4)
lr = 10 ** np.random.uniform(-6, -2)
第7章 卷积神经网络(CNN)
核心思想: CNN通过卷积层和池化层保持数据的空间结构,高效提取图像特征。
7.1 整体结构
是什么? 新增了卷积层(Convolution层)和池化层(Pooling层)的神经网络。
为什么需要? 全连接层忽视了数据的形状,无法利用空间信息。
典型结构:
Conv → ReLU → Pooling → Conv → ReLU → Pooling → Affine → ReLU → Affine → Softmax
7.2 卷积层
是什么? 对输入数据应用滤波器(卷积核)的层。
卷积运算: 滤波器在输入数据上滑动,计算乘积累加。
输出大小计算公式:
OH=H+2P−FHS+1OH = \frac{H + 2P - FH}{S} + 1OH=SH+2P−FH+1
OW=W+2P−FWS+1OW = \frac{W + 2P - FW}{S} + 1OW=SW+2P−FW+1
其中:H=输入高,W=输入宽,P=填充,S=步幅,FH=滤波器高,FW=滤波器宽
对比表格:
| 概念 | 说明 |
|---|---|
| 填充(Padding) | 在输入数据周围填充0,调整输出大小 |
| 步幅(Stride) | 滤波器应用的位置间隔 |
| 滤波器 | 卷积层的权重参数 |
| 偏置 | 加到滤波器应用结果上的固定值 |
7.3 池化层
是什么? 缩小高、长方向空间的层。
为什么需要? 减少参数数量,提高模型鲁棒性。
Max池化: 取目标区域的最大值
特点:
- 没有要学习的参数
- 通道数不发生变化
- 对微小位置变化具有鲁棒性
7.4 im2col技巧
是什么? 将输入数据展开为适合矩阵乘积的形式。
为什么需要? 高效实现卷积运算。
对应代码(概念):
python
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
# 将输入数据展开为2维数组
# 便于使用矩阵乘法高效计算卷积
pass
总结:深度学习核心概念图谱
感知机(单层)
↓ [无法解决非线性问题]
多层感知机
↓ [激活函数不连续,无法学习]
神经网络(Sigmoid/ReLU)
↓ [需要高效计算梯度]
误差反向传播法
↓ [需要优化学习效率]
优化方法(SGD/Momentum/AdaGrad/Adam)
↓ [需要稳定训练]
权重初始化(Xavier/He)+ Batch Normalization
↓ [防止过拟合]
正则化(权值衰减/Dropout)
↓ [处理图像数据]
卷积神经网络(CNN)
重要公式汇总
激活函数:
Sigmoid: σ(x)=11+e−x\sigma(x) = \frac{1}{1 + e^{-x}}σ(x)=1+e−x1
ReLU: f(x)=max(0,x)f(x) = \max(0, x)f(x)=max(0,x)
Softmax: yk=eak∑i=1neaiy_k = \frac{e^{a_k}}{\sum_{i=1}^{n}e^{a_i}}yk=∑i=1neaieak
损失函数:
均方误差: E=12∑k(yk−tk)2E = \frac{1}{2}\sum_{k}(y_k - t_k)^2E=21∑k(yk−tk)2
交叉熵误差: E=−∑ktklog(yk)E = -\sum_{k}t_k\log(y_k)E=−∑ktklog(yk)
梯度下降:
W←W−η∂L∂WW \leftarrow W - \eta\frac{\partial L}{\partial W}W←W−η∂W∂L
权重初始化:
Xavier: 1n\sqrt{\frac{1}{n}}n1
He: 2n\sqrt{\frac{2}{n}}n2
卷积输出大小:
OH=H+2P−FHS+1OH = \frac{H + 2P - FH}{S} + 1OH=SH+2P−FH+1
笔记整理完成