目录
1. 数学和Python的复习
- 向量和矩阵(或张量)
- 矩阵中对应元素(element-wise)的运算
- 广播
- 向量内积(一维数组)和矩阵乘积(二维数组)
- 矩阵的现状检查(3X2 * 2X4 = 3X4)
2. 神经网络的推理
- 全连接:
- 所有相邻的神经元之间都存在由箭头表示的连接
- 输出 = 输入 X 权重 + 偏置
- 全连接层的变换时线性变换,使用非线性的激活函数可以增强神经网络的表现力
- 层的类化及正向传播的实现
- 全连接层为Affine层,sigmoid函数为Sigmoid层
- 正向传播:神经网络的推理;反向传播:相反方向传播梯度
- 实现层的代码规范:
- 所有的层都有forward()方法和backward()方法
- 所有层都有params和grads实例变量,分别保存参数(权重和梯度)和各个参数的梯度
3. 神经网络的学习
-
损失函数
- 损失(loss):指示神经网络的性能,基于监督数据和预测结果计算;计算损失的函数称为损失函数(loss function)
- 进行多类别分类的神经网络使用交叉熵误差(cross entrop error)作为损失函数
- Softmax函数:可解释为概率
- one-hot向量形式:只有1个1,其余为0
-
导数和梯度
- 将关于向量各个元素的导数罗列到一起,就得到了梯度
-
链式法则
- 链式法则是复合函数的求导法则
- 可以认为神经网络是由多个函数复合而成的,误差反向传播法会利用链式法则求梯度
-
计算图——计算过程的图像表示,各节点:
节点 反向传播 加法节点 原样的将上游传播的梯度传播出去 乘法节点 梯度 * 将正向传播时的输入替换后的值 分支节点 梯度之和 repeat节点 N个分支的梯度之和(广播可以通过该节点表示) sum节点 通用的加法节点 MatMul节点(矩阵乘积) 见下方代码
class MatMul:
def __init__(self, W):
self.params = [W]
self.grads = [np.zeros_like(W)]
self.x = None
def forward(self, x):
W, = self.params # 这里的逗号为了取出权重W而不是[W]
out = np.dot(x, W)
self.x = x
return out
def backward(self, dout):
W, = self.params
dx = np.dot(dout, W.T)
dW = np.dot(self.x.T, dout)
self.grads[0][...] = dW # deep copy ,固定grads的内存地址,方便处理
return dx
一些层的实现
- Sigmoid层
class Sigmoid:
def __init__(self):
self.params, self.grads = [], []
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
- Affine层(MatMul节点进行矩阵乘积运算,Repeat节点实现广播)
class Affine:
def __init__(self, W, b):
self.params = [W, b]
self.grads = [np.zeros_like(W), np.zeros_like(b)]
self.x = None
def forward(self, x):
W, b = self.params
out = np.dot(x, W) + b
self.x = x
return out
def backward(self, dout):
W, b = self.params
dx = np.dot(dout, W.T)
dW = np.dot(self.x.T, dout)
db = np.sum(dout, axis=0)
self.grads[0][...] = dW
self.grads[1][...] = db
return dx
- Softmax with Loss层
class SoftmaxWithLoss:
def __init__(self):
self.params, self.grads = [], []
self.y = None # softmax的输出
self.t = None # 监督标签
def forward(self, x, t):
self.t = t
self.y = softmax(x)
# 在监督标签为one-hot向量的情况下,转换为正确解标签的索引
if self.t.size == self.y.size:
self.t = self.t.argmax(axis=1)
loss = cross_entropy_error(self.y, self.t)
return loss
def backward(self, dout=1):
batch_size = self.t.shape[0]
dx = self.y.copy()
dx[np.arange(batch_size), self.t] -= 1
dx *= dout
dx = dx / batch_size
return dx
- 权重的更新
- 步骤:mini-batch→计算梯度→更新参数→重复前3步
- 随机梯度下降法(SGD):将权重朝梯度发反方向更新
4. 使用神经网络解决问题
- 这里先对一个螺旋状数据集进行了神经网络的学习,不展开
Trainer类
进行学习的类,提高代码复用率
- 初始化程序接收神经网络和优化器
def __init__(self, model, optimizer):
self.model = model
self.optimizer = optimizer
self.loss_list = []
self.eval_interval = None
self.current_epoch = 0
- 然后调用fit( )方法开始学习
def fit(self, x, t, max_epoch=10, batch_size=32, max_grad=None, eval_interval=20):
"""
@param x: 输入数据
@param t: 监督标签
@param max_epoch: 进行学习的epoch数
@param batch_size: mini-batch的大小
@param max_grad: 梯度的最大范数
@param eval_interval: 输出结果的间隔——迭代次数
"""
data_size = len(x)
max_iters = data_size // batch_size
self.eval_interval = eval_interval
model, optimizer = self.model, self.optimizer
total_loss = 0
loss_count = 0
start_time = time.time()
for epoch in range(max_epoch):
# 打乱
idx = numpy.random.permutation(numpy.arange(data_size))
x = x[idx]
t = t[idx]
for iters in range(max_iters):
batch_x = x[iters*batch_size:(iters+1)*batch_size]
batch_t = t[iters*batch_size:(iters+1)*batch_size]
# 计算梯度,更新参数
loss = model.forward(batch_x, batch_t)
model.backward()
params, grads = remove_duplicate(model.params, model.grads) # 将共享的权重整合为1个
if max_grad is not None:
clip_grads(grads, max_grad)
optimizer.update(params, grads)
total_loss += loss
loss_count += 1
# 评价
if (eval_interval is not None) and (iters % eval_interval) == 0:
avg_loss = total_loss / loss_count
elapsed_time = time.time() - start_time
print('| epoch %d | iter %d / %d | time %d[s] | loss %.2f'
% (self.current_epoch + 1, iters + 1, max_iters, elapsed_time, avg_loss))
self.loss_list.append(float(avg_loss))
total_loss, loss_count = 0, 0
self.current_epoch += 1
- 画出损失值
def plot(self, ylim=None):
x = numpy.arange(len(self.loss_list))
if ylim is not None:
plt.ylim(*ylim)
plt.plot(x, self.loss_list, label='train')
plt.xlabel('iterations (x' + str(self.eval_interval) + ')')
plt.ylabel('loss')
plt.show()
5. 计算的高速化
位精度
- numpy默认使用64位的数据类型,但对于神经网络,32位浮点数也可以无损的推理和学习
- 对于神经网络的推理,16位浮点数也足够了,但普通CPU或GPU运算是使用32位执行的所有可以将权重数据用16位精度保存
GPU(CuPy)
- 深度学习有大量的乘法累加运算组成,且绝大部分可以并行运算,GPU比CUP擅长
- CuPy是基于GPU进行并行计算的库