示例:摄氏度转化为华氏度
这是优达学城深度学习课程的第一个案例, 我们的目的是训练计算机学习摄氏度转换成华氏度有数学公式:
f = c * 1.8 + 32
首先引入相关的库:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import logging
%matplotlib inline
logger = tf.get_logger() #返回tf的日志实例
logger.setLevel(logging.ERROR)
准备训练数据
celsius_q = np.array([-40, -10, 0, 8, 15, 22, 38], dtype=float)
fahrenheit_a = np.array([-40, 14, 32, 46, 59, 72, 100], dtype=float)
相关概念:训练集与测试集
数据集通常会划分为不同的子集,用于训练和评估神经网络。在此部分,我们提到了以下术语:
- 训练集: 用于训练神经网络的数据。
- 测试集: 用于测试神经网络最终效果的数据。
通过测试数据集,我们可以使用网络从未见过的数据测试网络。这样我们便能检测模型的泛化程度,即泛化到训练期间未见过的数据的效果,而不是仅仅记住训练样本。
同样,我们通常会使用验证数据集。此类数据集不用于训练,而是在训练期间用于测试模型。我们在一定的训练步数之后使用验证集,判断训练进展如何。例如,如果在训练过程中损失降低了,但是验证集的准确率下降了,则表明模型在记住测试集。
训练完毕时,也会使用验证集衡量模型的最终准确率。
创建及编译模型
定义输入的形状,创建一个层,编译模型,使用的损失函数为均方差 mean_squared_error
,优化器使用Adam,设置优化率为0.1。
l0 = tf.keras.layers.Dense(units=1, input_shape=[1])
model = tf.keras.Sequential([l0])
model.compile(loss='mean_squared_error', optimizer=tf.keras.optimizers.Adam(0.1))
由于问题比较简单,因此我们只需要建立的密集网络将只需要一个单层神经网络(只有一个神经元)。下面单独解释每一行代码的意义。
建立一个层
我们会把这一层叫做 l0
,并且使用 tf.keras.layers.Dense
(全连接层) 来建立
l0 = tf.keras.layers.Dense(units=1, input_shape=[1])
units=1
指定本层神经元数量。神经元的数量定义了本层需要有多少内部变量来学习解决这个问题。由于这是本模型的最后一层,因此它也代表模型输出的大小。(在多层神经网络中,该层的大小与形状需要与下一层的input_shape相匹配input_shape=[1]
指定本层输入值为单值。表明这是一个包含单个成员的一维数组。由于这是本模型的第一层(也是唯一一层),因此该输入形状也是整个模型的输入形状。单值是浮点数,即摄氏度
将Layer组装到模型中
定义了层之后,使用 Sequential 将它们组装成模型
model = tf.keras.Sequential([l0])
编译模型
搭好模型架构之后,在训练模型之前,还要执行编译操作。
model.compile(loss='mean_squared_error', optimizer=tf.keras.optimizers.Adam(0.1))
在编译时,经常需要指定三个参数:
- Loss function (损失函数) 一种衡量结果与预期相差多少的方法(测得的差异称为loss(损失))
- Optimizer function (优化函数) 一种调整内部值以减少损耗的方法
- Metrics function
在训练过程中使用model.fit()
来首先计算每个点的损耗,之后对其改善。优化器功能用于计算对模型内部变量的调整,目的是调整内部变量直到模型(实际是一个数学函数)接近将摄氏度转换为华氏度的公式为止
这里使用的优化函数mean_squared_error
(均方误差)和优化器Adam是这种简单模型的标准配置,实际还有其他可用配置。
在建立自己的模型时,我们需要考虑的Optimizer优化器的部分是学习率(即上式中的0.1),这是在模型中调整值时采取的步长,它决定着目标函数能否收敛到局部最小值以及何时收敛到最小值。合适的学习率能够使目标函数在合适的时间内收敛到局部最小值。范围通常在0.001(默认)和0.1之间
训练模型
在训练过程中,模型输入摄氏温度,使用当前的内部变量(权重)执行计算,并输出相应华氏温度的值。 由于权重最初是随机设置的,因此输出并不会接近正确的值。实际输出与期望输出之间的差值通过损失函数计算,而优化器函数将指导如何调整权重。
计算,比较,调整的整个流程由fit方法进行控制:
history = model.fit(celsius_q, fahrenheit_a, epochs=2000, verbose=0)
print('Those are the layer variables: {}'.format(l0.get_weights()))
fit的各个参数含义:
- 第一个参数是输入
- 第二个参数是期望的输出结果
- epochs参数指定模型应运行此循环多少次
- verbose参数控制该方法的日志显示
- verbose = 0 为不在标准输出流输出日志信息
- verbose = 1 为输出进度条记录, 默认值
- verbose = 2 为每个epoch输出一行记录
训练结束后,通过 l0.get_weights()
可以获取通过训练得到的参数。
打印出参数分别为1.7979496和31.95248,跟实际值 权重 weights=1.8
和 偏差 biases=32
已经非常接近了:
Those are the layer variables: [array([[1.7979496]], dtype=float32), array([31.95248], dtype=float32)]
可视化训练数据
绘制训练损失值随训练次数增加而减小的曲线:
plt.xlabel('Epoch Number') #x坐标轴
plt.ylabel("Loss Magnitude") #y坐标轴
plt.plot(history.history['loss']) #标点
plt.show()
通过图可以看出,开始的时候,损失值很大,大概训练到 500 次的时候,损失值开始稳定不再有明显的变化。
使用模型
通过 model.predict
输入已知的摄氏度, 这将输出预测的华氏度
利用前面训练好的模型预测100℃时对应的℉
print(model.predict([100.0])) #向模型输入100.0
# 100*1.8 + 32 = 212
可以看到,输出值与真实值已经非常接近了:
[[211.74744]]
由于机器性能的差异,不同计算机训练出来的结果将不一样。
总结
通过此节,我们学习了使用 TensorFlow 训练了首个模型。
训练流程(发生在 model.fit(...)
当中)是指将网络的内部变量调整为最佳可能值,使它们能够将输入映射到输出。为了实现这个目标,我们将采用梯度下降法这一优化流程,它会使用数值分析找到模型内部变量的最佳可能值。
梯度下降法会以迭代方式调整参数,每次朝着正确的方向小幅更改参数,直到达到最佳值。“最佳值”是指再调整的话,会降低模型的效果。在每次迭代过程中衡量模型好坏的函数称为“损失函数”,每次调整的目标是“最小化损失函数”。
预测出一个值后,模型将计算预测值与正确值之间的差值。这个差值称为损失,用于衡量模型执行映射任务的效果。我们使用损失函数计算损失,并且在调用 model.compile() 时通过损失参数指定损失函数。
计算损失后,模型将调整所有层级的内部变量(权重和偏差),从而最小化该损失,使输出值更接近正确值。
这个优化流程称为梯度下降法。我们会通过一个具体算法计算每个内部变量的新值,并且在调用 model.compile(...)
时用优化器参数指定该算法。在此示例中,我们使用的是 Adam优化器
。
术语
- 特征: 模型的输入
- 样本: 用于训练流程的输入/输出对
- 标签: 模型的输出
- 层级: 神经网络中相互连接的节点集合。
- 模型: 神经网络的表示法
- 密集全连接层 (FC): 一个层级中的每个节点都与上个层级中的每个节点相连。
- 权重和偏差: 模型的内部变量
- 损失: 期望输出和真实输出之间的差值
- MSE: 均方误差,一种损失函数,它会将一小部分很大的差值视作比大量很小的差值更糟糕。
- 梯度下降法: 每次小幅调整内部变量,从而逐渐降低损失函数的算法。
- 优化器: 梯度下降法的一种具体实现方法。(有很多算法。在这门课程中,我们将仅使用“Adam”优化器,它是 ADAptive with Momentum 的简称,并且被视为最佳优化器。)
- 学习速率: 梯度下降过程中的损失改进“步长”。
- 批次: 在训练神经网络的过程中使用的一组样本。
- 周期: 完全经过整个训练数据集一轮
- 前向传播: 根据输入计算输出值
- 反向传播: 根据优化器算法计算内部变量的调整幅度,从输出层级开始,并往回计算每个层级,直到抵达输入层。
- 扁平化: 将二维图像转换为一维向量的过程