使用预设的 Estimator 模型
在之前的学习中,我们学习到了如何使用 Keras 来快速、简洁地构建网络模型,我们也认识到了使用 tf.keras 构建模型的优点。但是在 TensorFlow 之中构建网络模型的方式远远不止 Keras 一种方式,那么这节课开始我们便开始学习如何使用 Estimator 来构建模型。
1. 什么是 Estimator
Estimator 是 TensorFlow 之中模型的一种高级表示,也就是说,我们可以像使用 Keras 一样来创建自己的模型,并进行训练、评估等操作。Estimator 主要可以提供的功能包括:
- 模型的训练;
- 模型的评估;
- 使用模型进行预测。
在 TensorFlow 之中,Estimator 的大部分 API 都集中在 tf.estimator 之中。
在 API 层面上,Keras 与 Estimator 处于相同的层面,与 Keras 相似,Estimator 也是一种模型层面的高阶 API,因此也可以和 Keras 一样快捷、方便地使用。
但是与 Keras 不同的是,Estimator 在 TensorFlow1.1 版本的时候就已经被引入了 TensorFlow,也正因如此,TensorFlow 对于 Estimator 的支持也较好一些。
2. 使用 Estimator 构建模型的一般步骤
在 TensorFlow 之中,使用 Estimator 进行模型训练大致需要经过四个步骤,它们分别是:
- 定义特征列;
- 定义输入函数;
- 创建(实例化)一个 Estimator 模型;
- 训练并进行相应的评估等操作。
对于这四个步骤,相信特征列和输入函数这两个名词对于大家都很陌生,那么我们便来进行相应解释:
- 特征列:
“特征列(feature columns)是一个对象,用于描述模型应该如何使用特征字典中的原始输入数据”——官方定义
简单来说,特征列就是来告诉模型你要使用数据中的哪些数据列,其中哪些数据列为离散值,哪些数据列为连续值。
- 输入函数:简单来说,输入函数是一个函数,它返回的是一个数据集,其中的每条数据都是(特征,标签)的形式。我们可以将输入函数理解为“模型用来取数据的地方,通过输入函数,模型可以不断取得新的数据”。
因为 TensorFlow 内部提供了很多内置的 Estimator 模型,因此这节课我们会采用其预设的模型来进行分类学习。
对于这四步的具体细节,我们会在接下来的示例过程中具体演示。
3. 实例-使用 Estimator 进行泰坦尼克生存预测
首先我们需要先获取数据集,也就是泰塔尼克数据集,我们采用之前相同的方式来获取数据集:
import pandas as pd
import tensorflow as tf
train_file = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
columns = ['survived', 'sex', 'age', 'n\_siblings\_spouses', 'parch', 'fare', 'class', 'deck', 'embark\_town', 'alone']
categorical = ['sex', 'n\_siblings\_spouses', 'parch', 'class', 'deck', 'embark\_town', 'alone']
numeric = ['age', 'fare']
train_df = pd.read_csv(train_file, names=columns)
其中我们 columns 变量定义了全部的列,同时我们定义了 categorical 和 numeric 两个数据列,分别代表离散值以及连续值,这便于我们后面的编码处理。
然后我们进行测试集与训练集的划分:
test_df = train_df.sample(frac=0.2)
train_df = train_df[~train_df.index.isin(test_df.index)]
train_df_y = train_df.pop('survived')
test_df_y = test_df.pop('survived')
print(len(train_df), len(test_df))
在这里,我们使用 train_df.sample() 方法来按照 0.2 的采样率进行随机采样,从来获取我们的测试集,然后我们将原来集合中的测试集去掉,得到剩下的训练集。然后我们又采用 pop 的方式来获取到标签值。这里我们按照传统的8:2的比例进行分割。
我们可以得到输出:
502, 125
然后我们就进行特征列的构造,在这里我们将特征列分为连续值与离散值,对于离散值,我们进行了独热编码的处理,因为独热编码在训练的过程中较大的优势:
def transfer2one\_hot(feature, feature_vocab):
return tf.feature_column.indicator_column(tf.feature_column.categorical_column_with_vocabulary_list(feature, feature_vocab))
features = []
for feature in categorical:
features.append(
transfer2one_hot(feature, train_df[feature].unique())
)
for feature in numeric:
features.append(
tf.feature_column.numeric_column(feature, dtype=tf.float32)
)
在这里我们使用到了三个非常重要的函数:
- tf.feature_column.numeric_column,将某一列作为连续值视作数据特征;
- tf.feature_column.indicator_column,将某一离散列作为独热编码视作数据列;
- tf.feature_column.categorical_column_with_vocabulary_list,接收第一个参数为某一离散特征列,第二个参数为该离散特征列的所有取值情况,将一个普通列视作离散数据列(未经过独热编码)。
然后我们便可以定义我们的输入函数,在这里我们将输入函数分为训练时的输入函数以及测试时的输入函数,具体来说:
def train\_input\_fn(features, labels, batch_size=8):
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
dataset = dataset.shuffle(len(train_df))
dataset = dataset.repeat(30000)
return dataset.batch(batch_size)
def test\_input\_fn(features, labels, batch_size=8):
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
return dataset.batch(batch_size)
train_fn = lambda: train_input_fn(train_df, train_df_y)
test_fn = lambda: test_input_fn(test_df, test_df_y)
在输入函数之中,我们使用参数中的 features 和 labels 创建了一个数据集,并且将其返回。并且对于训练集,我们采用了数据增强,也就是随机化以及重复操作,在这里,30000 就是我们要训练的 Steps 数量(我们所使用的 batch_size 的数量,这里是8)。
最后因为输入函数是一个函数,因此我们需要定义一个 lambda 函数来返回一个输入函数,否则输入函数将直接返回一个数据集,这是我们不希望看到的。
再者我们就可以载入一个内置的模型:
classifier = tf.estimator.DNNClassifier(
feature_columns=feature_columns,
hidden_units=[50, 30, 20],
n_classes=2)
在这里我们定义了一个 Estimator 内置的分类器,它的三个参数如下:
- feature_columns,特征列,就是我们刚刚处理过的特征列,包含连序列与离散列;
- hidden_units,隐藏单元的数量,在这里我们定义了三层;
- n_classes,输出的种类,这里我们只有生存和不能生存两类。
最后我们便可以进行模型的训练与测试,具体的实现细节非常简单:
classifier.train(input_fn=train_fn, steps=30000)
test_result = classifier.evaluate(input_fn=test_fn)
print(test_result)
我们首先进行了训练,输入函数为我们之前定义的训练的输入函数,然后训练 Steps 为 30000 ,然后我们便进行了测试,此时的输入函数为我们为测试专门定制的输入函数。我们可以得到结果:
......
INFO:tensorflow:global_step/sec: 861.581
INFO:tensorflow:loss = 0.4950667, step = 47500 (0.114 sec)
INFO:tensorflow:global_step/sec: 885.82
INFO:tensorflow:loss = 0.30664578, step = 47600 (0.113 sec)
INFO:tensorflow:global_step/sec: 879.637
INFO:tensorflow:loss = 0.82413065, step = 47700 (0.116 sec)
INFO:tensorflow:global_step/sec: 814.178
INFO:tensorflow:loss = 0.5236651, step = 47800 (0.123 sec)
INFO:tensorflow:global_step/sec: 829.556
INFO:tensorflow:loss = 0.4500552, step = 47900 (0.118 sec)
......
{'accuracy': 0.724, 'accuracy_baseline': 0.7624, 'auc': 0.5004091, 'auc_precision_recall': 0.43117255, 'average_loss': 1.7417283, 'label/mean': 0.376, 'loss': 2.7342584, 'precision': 0.0, 'prediction/mean': 0.025458882, 'recall': 0.0, 'global_step': 0}
我们可以看到,最后我们在测试集上的准确率为 0.72 ,也是一个不错的结果。
4. 小结
在这节课之中,我们学习了什么是 Estimator,同时也了解了使用 Estimator 进行训练的一般步骤,最后我们也学习了如何在实战中使用 Estimator 模型。