深度学习初级课程
介绍
在前两节课中,我们学习了如何从密集的层堆中构建完全连接的网络。第一次创建时,所有网络的权重都是随机设置的——网络还“不知道”任何东西。在这节课中,我们将看到如何训练神经网络;我们将看到神经网络是如何学习的。
与所有机器学习任务一样,我们从一组训练数据开始。训练数据中的每个示例都包含一些特征(输入)和一个预期目标(输出)。训练网络意味着调整其权重,使其能够将特征转化为目标。例如,在80种谷物的数据集中,我们需要一个网络,可以获取每种谷物的“糖”、“纤维”和“蛋白质”含量,并对该谷物的“热量”进行预测。如果我们能够成功地训练一个网络做到这一点,那么它的权重必须以某种方式表示这些特征与训练数据中表示的目标之间的关系。
除了训练数据,我们还需要两样东西:
- 一种“损失函数”,用来衡量网络预测的好坏。
- 一个“优化器”,可以告诉网络如何改变其权重。
损失函数
我们已经看到了如何为网络设计架构,但还没有看到如何告诉网络要解决什么问题。这是损失函数的工作。
损失函数测量目标真实值和模型预测值之间的差异。
不同的问题需要不同的损失函数。我们一直在研究回归问题,其中的任务是预测一些数值——80种谷物中的热量,红酒质量等级。其他回归任务可能是预测房价或汽车的燃油效率。
回归问题的一个常见损失函数是mean absolute error(平均绝对误差)或MAE。对于每个预测y_pred,MAE通过绝对差值abs(y_true-y_pred)测量与真实目标y_true的差异。
数据集上的总MAE损失是所有这些绝对差异的平均值。
描绘从数据点到拟合线的误差条的图表。
平均绝对误差是拟合曲线和数据点之间的平均长度。
除了MAE之外,你可能会看到回归问题的其他损失函数是均方误差(MSE)或Huber损失(两者都可以在Keras中使用)。
在训练期间,模型将使用损失函数作为指导,以找到正确的权重值(损失越小越好)。换句话说,损失函数告诉网络它的目标。
优化器-随机梯度下降
我们已经描述了我们希望网络解决的问题,但现在我们需要说明如何解决它。这是优化器的工作。优化器是一种调整权重以最小化损失的算法。
实际上,深度学习中使用的所有优化算法都属于一个叫做随机梯度下降的家族。它们是逐步训练网络的迭代算法。训练的一步是这样的:
- 采集一些训练数据,通过网络进行预测。
- 测量预测值和真实值之间的损失。
- 最后,调整重量,使损失更小。
- 然后反复这样做,直到损失尽可能小(或者直到损失不再减少)
- 一批一批地装配生产线。损失减少,重量接近其真实值。
用随机梯度下降法训练神经网络。
每个迭代的训练数据样本称为一个小批量(或通常仅称为“批量”),而一轮完整的训练数据称为一个epoch。你训练的次数是网络看到每个训练示例的次数。
动画显示了第1课中的线性模型正在使用SGD进行训练。淡红色的点描绘了整个训练集,而实心红色的点则是小批量。每次SGD看到一个新的小批量,它都会将重量(w斜率和b y截距)移向该批次上的正确值。一批又一批,生产线最终会收敛到最佳状态。可以看到,随着权重接近其真实值,损失会变小。
学习率和批量大小
请注意,生产线只会在每批产品的方向上发生一个小的移动(而不是一直移动)。这些变化的大小取决于学习率。较小的学习率意味着网络需要在其权重收敛到最佳值之前看到更多的小批量。
learning_rate(学习率)和minibatch(小批量)的大小是对SGD训练进度影响最大的两个参数。它们之间的相互作用往往很微妙,对这些参数的正确选择并不总是显而易见的。(我们将在练习中探讨这些影响。)
幸运的是,对于大多数工作来说,没有
model.compile( optimizer="adam", loss="mae", )
必要进行广泛的超参数搜索以获得满意的结果。
添加损失和优化程序
定义模型后,可以使用模型的编译方法添加损失函数和优化器:
请注意,我们只需要一个字符串就可以指定loss和optimizer。您还可以通过KerasAPI直接访问这些参数——例如,如果您想优化参数——但对我们来说,默认值可以正常工作。
名字里有什么?
梯度是一个向量,告诉我们权重需要朝哪个方向移动。更准确地说,它告诉我们如何改变权量,使损失变化最快。我们称我们的过程为梯度下降,因为它使用梯度将损失曲线下降到最小值。随机意味着“由偶然决定”我们的训练是随机的,因为小批量是数据集中的随机样本。这就是为什么它被称为SGD!
举例——红酒质量
现在我们知道了开始训练深度学习模型所需的一切。让我们看看它的实际行动吧!我们将使用红酒质量数据集。
该数据集由大约1600种葡萄牙红酒的理化测量数据组成。此外,还包括盲品测试中每种葡萄酒的质量评级。通过这些测量,我们能很好地预测葡萄酒的感知质量吗?
我们已经把所有的数据准备工作都放进了下一个隐藏单元。接下来的内容并不重要,所以请跳过它。不过,现在你可能会注意到,我们已经重新调整了每个特征的大小,使其位于[0,1]的间隔内。正如我们将在第5课中进一步讨论的那样,当神经网络的输入在一个共同的尺度上时,它们往往表现得最好。
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']
这个网络应该有多少输入?我们可以通过查看数据矩阵中的列数来发现这一点。确保不要在这里包括目标(“质量”),只包括输入特性。
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行训练数据(批量大小),并在整个数据集(epochs)中执行10次。
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=10,
)
Epoch 1/10
5/5 [==============================] - 1s 73ms/step - loss: 0.2626 - val_loss: 0.1382
Epoch 2/10
5/5 [==============================] - 0s 24ms/step - loss: 0.1382 - val_loss: 0.1243
Epoch 3/10
5/5 [==============================] - 0s 17ms/step - loss: 0.1246 - val_loss: 0.1180
Epoch 4/10
5/5 [==============================] - 0s 17ms/step - loss: 0.1141 - val_loss: 0.1096
Epoch 5/10
5/5 [==============================] - 0s 29ms/step - loss: 0.1127 - val_loss: 0.1091
Epoch 6/10
5/5 [==============================] - 0s 24ms/step - loss: 0.1096 - val_loss: 0.1044
Epoch 7/10
5/5 [==============================] - 0s 22ms/step - loss: 0.1049 - val_loss: 0.1025
Epoch 8/10
5/5 [==============================] - 0s 17ms/step - loss: 0.1054 - val_loss: 0.1021
Epoch 9/10
5/5 [==============================] - 0s 21ms/step - loss: 0.1016 - val_loss: 0.1010
Epoch 10/10
5/5 [==============================] - 0s 17ms/step - loss: 0.1004 - val_loss: 0.0975
你可以看到,随着模型训练的运行,Keras会让你了解损失的最新情况。
通常,一个更好的观察损失的方法是绘制它。事实上,拟合方法会记录在历史对象中进行训练时产生的损失。我们将把数据转换成 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();
请注意,随着epochs的增加,损失是如何平缓的。当损失曲线变得像这样水平时,这意味着模型已经学会了它所能学到的一切,没有理由继续进行更多的时代。
轮到你了
现在,使用随机梯度下降来训练你的网络。
练习部分
介绍
在本练习中,您将在燃油经济性数据集上训练一个神经网络,然后探索学习率和批量大小对SGD的影响。准备好后,运行下一个单元格来设置一切!
# Setup plotting
import matplotlib.pyplot as plt
from learntools.deep_learning_intro.dltools import animate_sgd
plt.style.use('seaborn-whitegrid')
# Set Matplotlib defaults
plt.rc('figure', autolayout=True)
plt.rc('axes', labelweight='bold', labelsize='large',
titleweight='bold', titlesize=18, titlepad=10)
plt.rc('animation', html='html5')
# Setup feedback system
from learntools.core import binder
binder.bind(globals())
from learntools.deep_learning_intro.ex3 import *
在燃油经济性数据集中,您的任务是预测给定特征(如发动机类型或制造年份)的汽车的燃油经济性。首先通过运行下面的单元格加载数据集。
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import make_column_transformer, make_column_selector
from sklearn.model_selection import train_test_split
fuel = pd.read_csv('../input/dl-course-data/fuel.csv')
X = fuel.copy()
# Remove target
y = X.pop('FE')
preprocessor = make_column_transformer(
(StandardScaler(),
make_column_selector(dtype_include=np.number)),
(OneHotEncoder(sparse=False),
make_column_selector(dtype_include=object)),
)
X = preprocessor.fit_transform(X)
y = np.log(y) # log transform target instead of standardizing
input_shape = [X.shape[1]]
print("Input shape: {}".format(input_shape))
Input shape: [50]
X.shape
如果你喜欢的话,可以看看数据。在本例中,我们的目标是“FE”列,其余的列是特征。
# Uncomment to see original data
fuel.head()
# Uncomment to see processed features
pd.DataFrame(X[:10,:]).head()
运行下一个单元格以定义我们将用于此任务的网络。
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.Dense(128, activation='relu', input_shape=input_shape),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(1),
])
1)添加损失(Loss)和优化器(Optimizer)
在训练网络之前,我们需要定义我们将使用的loss和optimizer。使用模型的编译方法,添加Adam
优化器和MAE
loss。
# YOUR CODE HERE
____
# Check your answer
q_1.check()
2) 训练模型
一旦定义了模型并使用loss and optimizer进行编译,就可以开始培训了。以128个批次的规模为200个时代培训网络。输入数据为X,目标为y。
# YOUR CODE HERE
history = ____
# Check your answer
q_2.check()
2022-05-14 02:30:37.766558: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:185] None of the MLIR Optimization Passes are enabled (registered 2)
Epoch 1/200
9/9 [==============================] - 1s 2ms/step - loss: 2.3020
Epoch 2/200
9/9 [==============================] - 0s 2ms/step - loss: 0.7530
Epoch 3/200
9/9 [==============================] - 0s 2ms/step - loss: 0.4548
Epoch 4/200
9/9 [==============================] - 0s 2ms/step - loss: 0.3230
Epoch 5/200
9/9 [==============================] - 0s 2ms/step - loss: 0.2379
Epoch 6/200
9/9 [==============================] - 0s 2ms/step - loss: 0.1901
Epoch 7/200
9/9 [==============================] - 0s 2ms/step - loss: 0.1427
Epoch 8/200
9/9 [==============================] - 0s 2ms/step - loss: 0.1157
Epoch 9/200
9/9 [==============================] - 0s 2ms/step - loss: 0.0977
Epoch 10/200
9/9 [==============================] - 0s 2ms/step - loss: 0.0854
...
Epoch 200/200
9/9 [==============================] - 0s 2ms/step - loss: 0.0274
最后一步是查看损失曲线并评估培训。运行下面的单元格,获取训练损失的图。
import pandas as pd
history_df = pd.DataFrame(history.history)
# Start the plot at epoch 5. You can change this to get a different view.
history_df.loc[5:, ['loss']].plot();
3)评估训练
如果你训练模型的时间更长,你会认为损失会进一步减少吗?
这取决于损失在训练过程中是如何演变的:如果学习曲线趋于平稳,那么在额外的时期进行训练通常不会有任何好处。相反,如果损失似乎仍在减少,那么长时间的训练可能是有利的。
通过学习率和批量大小,您可以控制:
- 训练一个模型需要多长时间
- 学习曲线多么波动
- 损失有多小
为了更好地理解这两个参数,我们将研究线性模型,这是我们最简单的神经网络。因为只有一个权重和一个偏差,所以更容易看出参数的变化会产生什么影响。
下一个单元格将生成与教程中的动画类似的动画。更改learning_rate、batch_size和num_examples(多少数据点)的值,然后运行单元格。(可能需要一两分钟。)尝试以下组合,或尝试一些自己的组合:
# YOUR CODE HERE: Experiment with different values for the learning rate, batch size, and number of examples
learning_rate = 0.05
batch_size = 32
num_examples = 256
animate_sgd(
learning_rate=learning_rate,
batch_size=batch_size,
num_examples=num_examples,
# You can also change these, if you like
steps=50, # total training steps (batches seen)
true_w=3.0, # the slope of the data
true_b=2.0, # the bias of the data
)
4) 学习率和批量大小
改变这些参数有什么影响?思考之后,运行下面的单元格进行讨论。
你可能会看到,较小的批量会带来更嘈杂的重量更新和损耗曲线。这是因为每批数据都是一个小样本,小样本往往会给出更具噪声的估计。小批量生产可能会产生“平均”效果,但这可能是有益的。
学习率越低,更新越小,训练时间越长。高学习率可以加快训练,但也不能“适应”到最低限度。当学习率太高时,培训可能会完全失败。(尝试将学习率设置为0.99这样的大值以查看此信息。)
继续前进
了解如何通过调整容量或添加提前停止回调来提高模型的性能。
答案
# 1)添加损失和优化器
model.compile(
optimizer='adam',
loss='mae'
)
# 2)训练模型
history = model.fit(
X, y,
batch_size=128,
epochs=200
)