政安晨的个人主页:政安晨
欢迎 👍点赞✍评论⭐收藏
收录专栏: TensorFlow与Keras实战演绎
希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!
这篇文章中,咱们将使用Keras和TensorFlow训练一个神经网络,如果您是第一次看到这篇文章,那就是训练您的第一个神经网络。
前言
随机梯度下降(Stochastic Gradient Descent,简称SGD)是深度学习中最常用的优化算法之一。它是一种迭代的优化算法,用于求解目标函数的最小值。
在深度学习中,目标是通过不断调整模型的参数来最小化损失函数。
SGD的基本思想是每次迭代时,随机选择一小批训练样本(称为一个mini-batch),计算这个mini-batch的梯度,并用梯度的负方向更新模型的参数。这样反复进行迭代,直到达到停止条件为止。
与传统的梯度下降算法不同,SGD每次迭代只利用一个mini-batch来估计梯度,因此计算速度更快。此外,在数据比较多的情况下,SGD还具有一定的随机性,能够逃离局部最小值,从而更有可能达到全局最小值。
然而,SGD也存在一些问题。由于每次迭代只使用一个mini-batch,因此梯度估计可能不够准确,导致更新方向不够理想,收敛速度较慢。为了解决这个问题,可以使用一些改进的SGD算法,如批量梯度下降(Batch Gradient Descent)、动量法(Momentum)、Adam等。
总的来说,SGD是深度学习中非常重要的优化算法,能够高效地训练深度神经网络模型。它的简单性和可扩展性使其成为了深度学习中的核心算法之一。
在这个系列的前两篇文章中,我们学习了如何通过堆叠稠密层来构建全连接网络。当网络首次创建时,所有的权重都是随机设置的 - 网络还不知道任何东西。
在本文中,我们将看到如何训练神经网络;我们将看到神经网络如何学习。
与所有机器学习任务一样,我们首先需要一组训练数据。训练数据中的每个示例由一些特征(输入)和一个期望的目标结果(输出)组成。训练网络意味着调整其权重,使其能够将特征转化为目标结果。
例如,在80种麦片数据集中,我们希望有一个网络可以获取每种麦片的“糖分”、“纤维”和“蛋白质”含量,并预测该麦片的“卡路里”含量。如果我们成功地训练了一个网络来做到这一点,那么它的权重必须以某种方式表示这些特征与目标结果之间的关系,如训练数据所示。
除了训练数据,我们还需要两个东西:
一个“损失函数”,用于衡量网络的预测效果。 一个“优化器”,用于告诉网络如何调整权重。
损失函数
我们已经了解了如何为网络设计架构,但我们还没有看到如何告诉网络解决什么问题。
这就是损失函数的工作。
损失函数衡量的是目标的真实值与模型预测值之间的差异。
不同的问题需要不同的损失函数。
我们一直在研究回归问题,其中的任务是预测一些数值--比如80种谷物中的卡路里,红酒质量评分。其他回归任务可能包括预测房屋价格或汽车燃油效率。
回归问题常用的损失函数是平均绝对误差(MAE)。对于每个预测值y_pred,MAE通过计算真实目标值y_true与预测值之间的绝对差abs(y_true - y_pred)来衡量它们之间的差异。
在数据集上的总MAE损失是所有这些绝对差异的均值。
平均绝对误差是拟合曲线与数据点之间的平均距离
除了MAE之外,你可能在回归问题中看到其他的损失函数,如均方误差(MSE)或Huber损失(在Keras中都可用)。
在训练过程中,模型将使用损失函数作为指导,找到其权重的正确值(较低的损失更好)。换句话说,损失函数告诉网络它的目标。
优化器 - 随机梯度下降
我们已经描述了我们希望网络解决的问题,但现在我们需要说明如何解决这个问题。
这是优化器的工作:优化器是一种调整权重以最小化损失的算法。
几乎所有用于深度学习的优化算法都属于一种称为随机梯度下降(SGD)的算法家族。它们是迭代算法,在训练网络时分步进行。
训练的一个步骤如下所示:
1. 抽样一些训练数据,并通过网络进行预测。
2. 衡量预测结果与真实值之间的损失。
3. 最后,调整权重以使损失减小。
这种调整的步骤可以一直小到你接受为止,当然,这种调整也是有限度的,是有边界的。
(使用随机梯度下降训练神经网络。)
每个迭代的训练数据样本被称为一个小批量(通常简称为“批量”),而完整的一轮训练数据被称为一个周期。
你训练的周期数决定了网络将会看到每个训练样本的次数。
上面左边第一幅图淡红色的点表示整个训练集,而实心红点表示小批量。每当看到一个新的小批量时,它会将权重(斜率w和y截距b)向着该批量上的正确值进行调整。一批一批地训练,最终线条会收敛到最佳拟合。你可以看到,随着权重接近其真实值,损失越来越小。
学习率和批大小
请注意,该行只在每个批次的方向上进行了小幅度的偏移(而不是完全移动)。这些偏移的大小由学习率决定。较小的学习率意味着网络需要看到更多的小批量数据,才能使其权重收敛到最佳值。
学习率和小批量大小是对训练过程影响最大的两个参数。它们之间的相互作用常常是微妙的,选择这些参数的正确方式并不总是明显的。(我们将在练习中探讨这些影响。)
幸运的是,对于大多数工作来说,不必进行广泛的超参数搜索就能获得令人满意的结果。Adam是一种具有自适应学习率的SGD算法,它适用于大多数问题,无需任何参数调整(在某种意义上它是“自我调节的”)。Adam是一个非常好的通用优化器。
添加损失函数和优化器
定义模型后,可以使用模型的compile方法添加损失函数和优化器:
model.compile(
optimizer="adam",
loss="mae",
)
请注意,我们可以仅通过一个字符串来指定损失函数和优化器。您也可以通过Keras API直接访问这些(如果您想要调整参数的话),但对于我们来说,使用默认值就可以了。
名字有什么重要性?
梯度是一个向量,告诉我们权重需要朝着哪个方向前进。
更准确地说,它告诉我们如何改变权重,以使损失函数最快地变化。
我们将这个过程称为梯度下降,因为它使用梯度来沿着损失曲线向最小值降低。
随机意味着“由机会决定”。
我们的训练是随机的,因为小批量是从数据集中随机抽取的样本。这就是为什么它被称为随机梯度下降(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']
这个网络应该有多少个输入?
我们可以通过查看数据矩阵中的列数来发现这一点。确保在这里不包括目标变量('quality'),只包括输入特征。
print(X_train.shape)
十一个列意味着十一个输入。
我们选择了一个三层网络,有超过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行训练数据一次性输入优化器(即batch_size),并在整个数据集上重复这个过程10次(即epochs)。
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=10,
)
你可以看到,在模型训练过程中,Keras会实时更新并显示损失值。
通常,更好的观察损失的方法是将其绘制出来。实际上,fit方法会在训练过程中记录损失值,并保存在一个名为History的对象中。我们将把这些数据转换为Pandas的dataframe格式,以便更容易进行绘图。
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();
注意随着每个周期的进行,损失逐渐趋于平缓。当损失曲线变得水平时,意味着模型已经学习到了它所能学习的所有知识,没有继续训练更多周期的必要了。
练习:随机梯度下降
介绍
在这个练习中,您将在燃油经济数据集上训练神经网络,然后探索学习率和批处理大小对随机梯度下降(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))
如果你喜欢的话,可以看一下数据。
在这个案例中,我们的目标是“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.添加损失函数和优化器。
在训练网络之前,我们需要定义要使用的损失函数和优化器。使用模型的compile方法,添加Adam优化器和MAE损失。
# YOUR CODE HERE
____
# Check your answer
q_1.check()
# Lines below will give you a hint or solution code
#q_1.hint()
#q_1.solution()
2.训练模型
一旦您已经定义了模型并使用损失函数和优化器进行了编译,您就可以开始训练了。
使用批量大小为128,在200个周期内训练网络。输入数据为X,目标为y。
# YOUR CODE HERE
history = ____
# Check your answer
q_2.check()
# Lines below will give you a hint or solution code
#q_2.hint()
#q_2.solution()
最后一步是查看损失曲线并评估训练情况。运行下面的单元格以获得训练损失的图表。
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();
评估培训成功程度
如果您进一步训练模型,您是否预期损失会进一步减少?
# View the solution (Run this cell to receive credit!)
q_3.check()
你通过学习率和批量大小可以对以下方面进行一定程度的控制:
* 模型训练所花费的时间
* 学习曲线的噪声程度
* 损失的减小程度
为了更好地理解这两个参数,我们将看看线性模型,即最简单的神经网络。
只有一个权重和一个偏置,更容易看出参数的变化会产生什么影响。
下一个单元格将生成类似教程中的动画。
更改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
)
学习率和批量大小
这些参数的变化产生了什么影响?在你考虑完后,观察下面代码后进行讨论。
# View the solution (Run this cell to receive credit!)
q_4.check()
小伙伴们可以参考我这个系列中前面的文章搭建环境,自己演练一下,会有更深的理解。