一、深度学习结构三要素
(一)线性模型
在机器学习(machine learning)领域,我们通常使用的是高维数据集,建模时采用线性代数表示法会比较方便。当我们的输入包含
d
d
d个特征时,我们将预测结果
y
^
\hat{y}
y^线性代数表示为
y
^
=
w
1
x
1
+
.
.
.
+
w
d
x
d
+
b
.
\hat{y} = w_1 x_1 + ... + w_d x_d + b.
y^=w1x1+...+wdxd+b.
点积形式:
y
^
=
w
⋅
x
+
b
.
\hat{y} = \mathbf{w}· \mathbf{x} + b.
y^=w⋅x+b.
(二)损失函数
回归问题中最常用的损失函数是平方误差函数(Squared error function)。当样本的预测值为 y ^ \hat{y} y^,其相应的真实标签为 y y y时,平方误差可以定义为以下公式:
L
o
s
s
=
1
2
(
y
^
−
y
)
2
.
Loss = \frac{1}{2} \left(\hat{y} - y\right)^2.
Loss=21(y^−y)2.
由于平方误差函数中的二次方项,估计值
y
^
(
i
)
\hat{y}^{(i)}
y^(i)和观测值
y
(
i
)
y^{(i)}
y(i)之间较大的差异将导致更大的损失。为了度量模型在整个数据集上的质量,我们需计算在训练集
n
n
n个样本上的损失均值(也等价于求和)。
L
(
w
,
b
)
=
1
n
∑
i
=
1
n
l
(
i
)
(
w
,
b
)
=
1
n
∑
i
=
1
n
1
2
(
w
⋅
x
(
i
)
+
b
−
y
(
i
)
)
2
.
L(\mathbf{w}, b) =\frac{1}{n}\sum_{i=1}^n l^{(i)}(\mathbf{w}, b) =\frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}· \mathbf{x}^{(i)} + b - y^{(i)}\right)^2.
L(w,b)=n1i=1∑nl(i)(w,b)=n1i=1∑n21(w⋅x(i)+b−y(i))2.
在训练模型时,我们希望寻找一组参数(
w
∗
,
b
∗
\mathbf{w}^*, b^*
w∗,b∗),这组参数能最小化在所有训练样本上的总损失。如下式:
w ∗ , b ∗ = argmin w , b L ( w , b ) . \mathbf{w}^*, b^* = \operatorname*{argmin}_{\mathbf{w}, b}\ L(\mathbf{w}, b). w∗,b∗=w,bargmin L(w,b).
(三)基础优化算法
计算损失函数最优解有两种方法,一种为最小二乘法,另一种为梯度下降(gradient descent)。我们用到一种名为梯度下降(gradient descent)的方法,这种方法几乎可以优化所有深度学习模型。它通过不断地在损失函数递减的方向上更新参数来降低误差。在每次迭代中,我们首先随机抽样一个小批量 b b b,它是由固定数量的训练样本组成的。然后,我们计算小批量的平均损失关于模型参数的导数(也可以称为梯度)。最后,我们将梯度乘以一个预先确定的正数 η \eta η,并从当前参数的值中减掉。我们用下面的数学公式来表示这一更新过程( ∂ \partial ∂表示偏导数):
( w , b ) ← ( w , b ) − η ∣ B ∣ ∑ i ∈ B ∂ ( w , b ) l ( i ) ( w , b ) . (\mathbf{w},b) \leftarrow (\mathbf{w},b) - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_{(\mathbf{w},b)} l^{(i)}(\mathbf{w},b). (w,b)←(w,b)−∣B∣ηi∈B∑∂(w,b)l(i)(w,b).
二、深度学习框架
在过去的几年里,出于对深度学习强烈的兴趣,许多公司、学者和业余爱好者开发了各种成熟的开源框架。深度学习框架有助于建模者聚焦业务场景和模型设计本身,省去大量而繁琐的代码编写工作,其优势主要表现在如下两个方面:
- 节省编写大量底层代码的精力:深度学习框架屏蔽了底层实现,用户只需关注模型的逻辑结构,同时简化了计算逻辑,降低了深度学习入门门槛;
- 省去了部署和适配环境的烦恼:深度学习框架具备灵活的移植性,可将代码部署到CPU、GPU或移动端上,选择具有分布式性能的深度学习框架会使模型训练更高效。
在构建模型的过程中,每一步所需要完成的任务均可以拆分成个性化和通用化两个部分。深度学习框架的本质是自动实现建模过程中相对通用的模块,建模者只实现模型中个性化的部分,这样可以在“节省投入”和“产出强大”之间达到一个平衡。
- 个性化部分:往往是指定模型由哪些逻辑元素组合,由建模者完成;
- 通用部分:聚焦这些元素的算法实现,由深度学习框架完成。
三、实现
(一)定义模型
全连接层在Linear
类中定义。值得注意的是,我们将两个参数传递到nn.Linear
中。第一个指定输入特征形状,即2,第二个指定输出特征形状,输出特征形状为单个标量,因此为1。
# nn是神经网络的缩写
import paddle
from paddle import nn
net = paddle.nn.Sequential(paddle.nn.Linear(2, 1))# 线性回归是单层神经网络,Sequential是一个有序的容器,神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行
(二)定义损失函数
计算均方误差使用的是MSELoss
类,也称为
L
2
L_2
L2范数。
默认情况下,它返回所有样本损失的平均值。
loss = paddle.nn.MSELoss() # 均方误差
(三)定义优化算法
小批量随机梯度下降算法是一种优化神经网络的标准工具,
当我们(实例化一个SGD
实例时,我们要指定优化的参数(可通过net.parameters()
从我们的模型中获得)以及优化算法所需的超参数字典。小批量随机梯度下降只需要设置lr
值,这里设置为0.03。
trainer = paddle.optimizer.SGD(learning_rate=0.03, parameters = net.parameters())
(四)、训练
通过深度学习框架的高级API来实现我们的模型只需要相对较少的代码。我们不必单独分配参数、不必定义我们的损失函数,也不必手动实现小批量随机梯度下降。当我们需要更复杂的模型时,高级API的优势将大大增加。当我们有了所有的基本组件,训练过程代码与我们从零开始实现时所做的非常相似。
回顾一下:在每个迭代周期里,我们将完整遍历一次数据集(train_data
),不停地从中获取一个小批量的输入和相应的标签。对于每一个小批量,我们会进行以下步骤:
- 通过调用
net(X)
生成预测并计算损失l
(前向传播)。 - 通过进行反向传播来计算梯度。
- 通过调用优化器来更新模型参数。
为了更好的衡量训练效果,我们计算每个迭代周期后的损失,并打印它来监控训练过程。
import numpy as np
from paddle.io import TensorDataset, DataLoader
# 生成数据集
def synthetic_data(w, b, num_examples):
X = paddle.normal(0, 1, (num_examples, len(w)))
y = paddle.matmul(X, w) + b
y += paddle.normal(0, 0.01, y.shape)
return X, y.reshape((-1, 1))
true_w = paddle.to_tensor([2, -3.4])
true_b = -4.2
true_n = 1000
features, labels = synthetic_data(true_w, true_b, true_n)
#读取数据集
def load_array(data_array, batch_size, is_train=True):
# 构造数据迭代器
dataset = TensorDataset(data_array)# 由张量列表定义的数据集
return DataLoader(dataset, batch_size=batch_size, shuffle=is_train) # 之后从DataLoader中随机挑选b个样本
batch_size = 10
data_iter = load_array((features, labels), batch_size)
#训练模型
num_epochs = 10
for epoch in range(num_epochs):
for X, y in data_iter():
l = loss(net(X), y)
# 清除梯度
trainer.clear_grad()
# 反向传播
l.backward()
# 最小化loss,更新参数
trainer.step()
l = loss(net(features), labels)
train_cost = l.numpy()[0]
print(f'epoch {epoch + 1}, loss', train_cost)
w = net[0].weight
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias
print('b的估计误差:', true_b - b)