随机梯度下降 —— 深度学习 3/6
使用 Keras和Tensor flow训练你的第一个神经网络
简介
- 在前两节课中,我们学会了如何利用一堆密度层构建一个全连接的神经网络。在构建的一开始,网络中所有的全重都是随机生成的 —— 神经网络什么都不知道。这节课,我们将学到如何训练神经网络,也就是了解神经网络是如何学习的。
- 和所有的机器学习任务一样,神经网络也是从一大堆训练数据入手的。训练集中的每个例子都包含一些特征(输入),还有一个预期的目标(输出)。训练网络就是调整其中的权重值,从而使得输入的特征能转变成目标值。
- 在80麦片的数据集中,我们想要一个能够输入每个麦片的含糖量、纤维量、蛋白质含量,然后转换预测出麦片的卡路里值地网络。如果我们成功地训练了这样的网络,那么这个权重就在一定程度上代表了输入特征和目标,在训练集中的关系。
- 要训练一个网络,我们还额外需要两个东西:
- 损失函数 衡量网络的预测精准度;
- 优化器 告诉网络如何让修改他的权值。
损失函数
- 我们已经见过如何设计神经网络的框架,但是我们还没有学过如何告诉神经网络要处理的问题,这个工作就要交给损失函数。
- 损失函数计量了目标真实值和模型测量值的差距。
- 不同的问题使用的损失函数不一样。我们已经见过回归分析问题,这种问题是要预测一个数字型的值——80麦片中的卡路里值、红酒质量中的评分。其他的任务可能是预测房价或者汽车的燃料使用效率。
- 一个回归问题的常见的损失函数是平均绝对误差(MAE)。对于每个预测值
y_pred
,都要计算他与目标实际值y_true
的绝对误差abs(y_true - y_pred)
,最后取平均。 - 除了MAE,回归分析中常用的损失函数还有 平均误差平方 (MSE)或者Huber损失函数(这两个都可以在Keras中使用)。
- 在训练过程中,模型会将损失函数作为修正权重的方法(损失越小越好)。也就是说,损失函数告诉模型他的客观性。
优化器——随机梯度下降
- 我们已经告诉模型要解决的问题了,但是我们还需要说怎么解决它,这就是优化器的工作。优化器是一个可以调整权重来达到最小损失的算法。
- 实际上,深度学习中的优化算法都属于一个叫随机梯度下降的范畴。它是一种通过一系列迭代步骤来训练网络的算法。
训练的步骤如下:
- 选中一些训练数据做样本,来用神经网络做出预测。
- 算出预测值和实际值间的损失。
- 最后,按照一个方向调整参数使得损失变小。
然后就一遍一遍重复这个步骤,直到损失值在你可接受的范围内(或者损失值不再减少)。
- 每次训练一遍训练集样本的叫 一批,当走完一整个数据集叫 一个阶段。阶段数是你的网络看过整个数据集每个项目的次数。
学习指数&批大小
- 注意到直线每一批仅仅向某一方向变化很小一个幅度(而不是一大步),这个变化幅度大小是由参数 学习指数 决定的。学习指数越小,说明神经网络需要经过更多批次的处理来获得最适应的权重值。
- 学习指数和批次总数是两个最主要的SDG训练过程的影响因素。它们两者的互动是十分微妙的,而且这些参数的正确选择往往不那么好确定。
- 幸好,对于大多数的工作我们不必做过多的产参数搜索来获得令人满意的结果。亚当(Adam) 是一个具有学习指数自适应的SGD算法。它可以用来解决大多数问题,而且不需要微调任何参数(也就是说他是自调节的)。亚当是一个很好的广泛应用的优化器。
添加损失函数和优化器
在定义完一个模型之后,你可以使用 compile
方法来添加优化器和损失函数:
model.compile(
optimizer="adam",
loss="mae",
)
- 注意到这里我们仅仅使用损失函数和优化器的字符串就可以直接使用对应的算法。你也可以通过Keras API来使用并调整参数。这里我们用默认的就可以。
名称解析:随机梯度下降
梯度 指的是指明权值变化的方向的向量。准确的来说,他告诉我们汝蘅变化权重使得损失值变化得最快。
我们将上面讲的过程叫做梯度 下降,是因为我们在不断调节使得损失曲线不断下降到最小值。
我们将训练过程称为 随机 是因为我们训练时使用的数据集中的样本是随机取的。
案例:红酒质量
- 我们已经了解了一切所需要的内容来训练一个深度学习模型。下面我们将使用红酒质量预测的数据集来实际应用下。
- 该数据集包含了1600个葡萄牙红酒的理化性质。它还包含了一个通过盲样品尝获得的各个酒的质量评分。我们怎样才能通过这些理化性质来预测盲尝结果呢?
- 下面是数据预处理的过程,要注意的是,我们将每个特征的取值都转化到[0,1]的区间内。我们将在第五课中谈到,神经网络在输入都是相同范围的时候表现往往会更好。
import pandas as pd
from IPython.display import display
red_wine = pd.read_csv('../input/dl-course-data/red-wine.csv')
# Create training and validation splits
df_train = red_wine.sample(frac=0.7, random_state=0)
df_valid = red_wine.drop(df_train.index)
display(df_train.head(4))
# Scale to [0, 1]
max_ = df_train.max(axis=0)
min_ = df_train.min(axis=0)
df_train = (df_train - min_) / (max_ - min_)
df_valid = (df_valid - min_) / (max_ - min_)
# Split features and target
X_train = df_train.drop('quality', axis=1)
X_valid = df_valid.drop('quality', axis=1)
y_train = df_train['quality']
y_valid = df_valid['quality']
- 该神经网络的输入应该有多少?我们可以通过数据矩阵的列数来保证我们的目标
Quality
没有包含在输入特征中。
print(X_train.shape)
(1119, 11)
- 十一列就是十一个输入。
- 我们选择使用三层的神经网络,包含1500多个神经元,来作为模型。这个学习数据中很复杂的关系已经足够了。
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.Dense(512, activation='relu', input_shape=[11]),
layers.Dense(512, activation='relu'),
layers.Dense(512, activation='relu'),
layers.Dense(1),
])
- 决定了模型的架构只是一部分的成功。我们可以使用简单的验证损失来作为向导。
- 下面加入优化器和损失函数:
model.compile(
optimizer='adam',
loss='mae',
)
- 现在我们就可以开始训练了,我们告诉Keras,每次向优化器输入256行数据(批大小),然后做十次循环遍历整个数据集(阶段数)。
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=10,
)
2021-09-13 20:12:30.283860: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)
2021-09-13 20:12:30.296955: I tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2199995000 Hz
Epoch 1/10
5/5 [==============================] - 1s 151ms/step - loss: 0.2938 - val_loss: 0.1411
Epoch 2/10
5/5 [==============================] - 0s 25ms/step - loss: 0.1480 - val_loss: 0.1267
Epoch 3/10
5/5 [==============================] - 0s 25ms/step - loss: 0.1300 - val_loss: 0.1195
Epoch 4/10
5/5 [==============================] - 0s 25ms/step - loss: 0.1222 - val_loss: 0.1165
Epoch 5/10
5/5 [==============================] - 0s 26ms/step - loss: 0.1150 - val_loss: 0.1087
Epoch 6/10
5/5 [==============================] - 0s 26ms/step - loss: 0.1090 - val_loss: 0.1052
Epoch 7/10
5/5 [==============================] - 0s 26ms/step - loss: 0.1115 - val_loss: 0.1115
Epoch 8/10
5/5 [==============================] - 0s 26ms/step - loss: 0.1112 - val_loss: 0.1053
Epoch 9/10
5/5 [==============================] - 0s 27ms/step - loss: 0.1090 - val_loss: 0.1167
Epoch 10/10
5/5 [==============================] - 0s 26ms/step - loss: 0.1090 - val_loss: 0.1030
- 你可以看到Keras在每次训练结束后都会跟进损失的变化。
- 通常,更直观的分析损失变化是通过图表。
fit
函数实际上记录着训练中所有的损失值。我们将数据转换为 Pandas 的数据网格能够更好的画图查看:
import pandas as pd
# convert the training history to a dataframe
history_df = pd.DataFrame(history.history)
# use Pandas native plot method
history_df['loss'].plot();
- 注意到损失值随着阶段的增加在不断变小。当损失曲线变成近乎水平,那么就表明模型已经尽全力学习了数据集中的所有信息,没有增加阶段的必要了。