TensorFlow知识图谱实战
上QQ阅读APP看书,第一时间看更新

2.1.5 使用TensorFlow标准化编译对iris模型进行拟合

在2.1.3节中,笔者使用了符合传统TensorFlow习惯的梯度更新方式对参数进行更新。然而实际这种看起来符合编程习惯的梯度计算和更新方法,可能并不符合大多数有机器学习使用经验的读者使用。本节以修改后的iris分类为例讲解标准化TensorFlow的编译方法。

对于大多数机器学习的程序设计人员来说,往往习惯了使用fit函数和compile函数对数据进行数据载入和参数分析。代码如下(请读者先运行,后面会有更为详细的运行分析):

【程序2-4】

下面我们详细分析一下代码。

1.数据的获取

本例还是使用sklearn中的iris数据集作为数据来源,之后将target转化成one-hot的形式进行存储。顺便提一句,TensorFlow本身也带有one-hot函数,即tf.one_hot,有兴趣的读者可以自行学习。

数据读取之后的处理在后文讲解(这个问题先放一下,请继续往下阅读)。

2.模型的建立和参数更新

这里不准备采用新模型的建立方法,对于读者来说,熟悉函数化编程已经能够应付绝大多数的深度学习模型的建立。在后面的章节中,我们会教会读者自定义某些层的方法。

对于梯度的更新,到目前为止的程序设计中都是采用类似回传调用等方式对参数进行更新,这是由程序设计者手动完成的。而TensorFlow推荐使用自带的梯度更新方法,代码如下:

    model.compile(optimizer=tf.optimizers.Adam(1e-3),
loss=tf.losses.categorical_crossentropy,metrics = ['accuracy'])
    model.fit(train_data, epochs=500)

compile函数是模型适配损失函数和选择优化器的专用函数,fit函数的作用是把训练参数加载进模型中。下面分别对它们进行讲解。

(1)compile

compile函数的作用是用于配置训练模型专用编译函数,源码如下:

    compile(optimizer, loss=None, metrics=None, loss_weights=None,
sample_weight_mode=None, weighted_metrics=None, target_tensors=None)

这里我们主要介绍其中3个重要的参数:

  • optimizer:字符串(优化器名)或者优化器实例。
  • loss:字符串(目标函数名)或目标函数。如果模型具有多个输出,可以通过传递损失函数的字典或列表在每个输出上使用不同的损失。模型最小化的损失值将是所有单个损失的总和。
  • metrics:在训练和测试期间的模型评估标准。通常会使用metrics = ['accuracy']。要为多输出模型的不同输出指定不同的评估标准,还可以传递一个字典,如metrics ={'output_a':'accuracy'}。

可以看到,优化器(optimizer)被传入了选定的优化器函数,loss是损失函数,这里也被传入选定的多分类crossentry函数。metrics用来评估模型的标准,一般用准确率表示。

实际上,compile编译函数是一个多重回调函数的集合,对于所有的参数来说,实际上就是根据对应函数的“地址”回调对应的函数,并将参数传入。

举个例子,在上面的编译器中我们传递的是一个TensorFlow自带的损失函数,实际上往往是针对不同的计算和误差传递不同的损失函数,这里自定义一个均方差(MSE)损失函数,代码如下:

这个损失函数接收两个参数,分别是y_true和y_pred,即预测值和真实值的形式参数。之后根据需要计算出真实值和预测值之间的误差。

损失函数名作为地址传递给compile后,即可作为自定义的损失函数在模型中进行编译,代码如下:

至于优化器的自定义,实际上也是可以的。一般情况下,优化器的编写需要比较高的编程技巧以及对模型的理解,这里建议读者直接使用TensorFlow自带的优化器即可。

(2)fit

fit函数的作用是以给定数量的轮次(数据集上的迭代)训练模型,主要参数有如下4个:

  • x:训练数据的Numpy数组(如果模型只有一个输入),或者是Numpy数组的列表(如果模型有多个输入)。如果模型中的输入层被命名,也可以传递一个字典,将输入层名称映射到Numpy数组。如果从本地框架张量馈送(例如TensorFlow数据张量)数据,x可以是None(默认)。
  • y:目标(标签)数据的Numpy数组(如果模型只有一个输出),或者是Numpy数组的列表(如果模型有多个输出)。如果模型中的输出层被命名,也可以传递一个字典,将输出层名称映射到Numpy数组。如果从本地框架张量馈送(例如TensorFlow数据张量)数据,y可以是None(默认)。
  • batch_size:整数或None,每次梯度更新的样本数。如果未指定,则默认为32。
  • epochs:整数,训练模型迭代轮次。一个轮次是在整个x和y上的一轮迭代。注意,与initial_epoch一起,epochs被理解为“最终轮次”。模型并不是训练了epochs轮,而是到第epochs轮停止训练。

fit函数的主要作用就是对输入的数据进行修改。如果读者已经成功运行了程序2-4,那么现在换一种略微修改后的代码重新运行iris数据集,代码如下:

【程序2-5】

程序2-4和程序2-5的最大不同是数据读取方式的变化。在程序2-4中,数据的读取方式和fit函数的载入方式如下:

iris的数据读取被分成两部分:数据特征部分和label部分。label部分使用Keras自带的工具进行离散化处理。离散化后处理的部分又被tf.data.Dataset API整合成一个新的数据集,并且依batch被切分成多个部分。

此时fit的处理对象是一个被tf.data.Dataset API处理后的Tensor类型数据,并且在切分的时候依照整合的内容被依次读取。在读取的过程中,由于它是一个Tensor类型的数据,fit内部的batch_size划分不起作用,而使用生成数据的tf中数据生成器的batch_size划分。如果读者对其还是不理解,可以使用如下代码段打印重新整合后的train_data数据:

     for iris_data,iris_target in train_data

在程序2-5中,对应于数据读取和载入的部分代码如下:

数据在读取和载入的过程中没有变化,将处理后的数据直接输入到fit函数中供模式使用。此时,由于是直接对数据进行操作,对数据的划分由fit函数负责,因此fit函数中的batch_size被设定为128。