使用 Estimators 构建线性模型

在 TensorFlow.org 上查看 在 Google Colab 中运行 在 GitHub 上查看源代码 下载笔记本

概述

本端到端演练使用 tf.estimator API 训练逻辑回归模型。该模型通常用作其他更复杂算法的基线。

设置

pip install sklearn
import os
import sys

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import clear_output
from six.moves import urllib

加载泰坦尼克号数据集

您将使用泰坦尼克号数据集,其目标(相当令人毛骨悚然)是根据性别、年龄、等级等特征预测乘客的生存情况。

import tensorflow.compat.v2.feature_column as fc

import tensorflow as tf
# Load dataset.
dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')
dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')
y_train = dftrain.pop('survived')
y_eval = dfeval.pop('survived')

探索数据

数据集包含以下特征

dftrain.head()
dftrain.describe()

训练集和评估集中分别有 627 个和 264 个示例。

dftrain.shape[0], dfeval.shape[0]

大多数乘客年龄在 20 多岁和 30 多岁。

dftrain.age.hist(bins=20)

船上男性乘客数量大约是女性乘客数量的两倍。

dftrain.sex.value_counts().plot(kind='barh')

大多数乘客属于“三等舱”。

dftrain['class'].value_counts().plot(kind='barh')

女性的生存几率远高于男性。这显然是模型的一个预测特征。

pd.concat([dftrain, y_train], axis=1).groupby('sex').survived.mean().plot(kind='barh').set_xlabel('% survive')

模型的特征工程

评估器使用一个名为 特征列 的系统来描述模型应该如何解释每个原始输入特征。评估器需要一个数值输入向量,而特征列则描述了模型应该如何转换每个特征。

选择和构建正确的特征列集是学习有效模型的关键。特征列可以是原始特征 dict 中的原始输入之一(基本特征列),也可以是使用对一个或多个基本列定义的转换创建的任何新列(派生特征列)。

线性评估器使用数值特征和分类特征。特征列适用于所有 TensorFlow 评估器,其目的是定义用于建模的特征。此外,它们还提供了一些特征工程功能,例如独热编码、归一化和分箱。

基本特征列

CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck',
                       'embark_town', 'alone']
NUMERIC_COLUMNS = ['age', 'fare']

feature_columns = []
for feature_name in CATEGORICAL_COLUMNS:
  vocabulary = dftrain[feature_name].unique()
  feature_columns.append(tf.feature_column.categorical_column_with_vocabulary_list(feature_name, vocabulary))

for feature_name in NUMERIC_COLUMNS:
  feature_columns.append(tf.feature_column.numeric_column(feature_name, dtype=tf.float32))

input_function 指定如何将数据转换为 tf.data.Dataset,以流式方式为输入管道提供数据。 tf.data.Dataset 可以接受多个来源,例如数据框、csv 格式文件等等。

def make_input_fn(data_df, label_df, num_epochs=10, shuffle=True, batch_size=32):
  def input_function():
    ds = tf.data.Dataset.from_tensor_slices((dict(data_df), label_df))
    if shuffle:
      ds = ds.shuffle(1000)
    ds = ds.batch(batch_size).repeat(num_epochs)
    return ds
  return input_function

train_input_fn = make_input_fn(dftrain, y_train)
eval_input_fn = make_input_fn(dfeval, y_eval, num_epochs=1, shuffle=False)

您可以检查数据集

ds = make_input_fn(dftrain, y_train, batch_size=10)()
for feature_batch, label_batch in ds.take(1):
  print('Some feature keys:', list(feature_batch.keys()))
  print()
  print('A batch of class:', feature_batch['class'].numpy())
  print()
  print('A batch of Labels:', label_batch.numpy())

您还可以使用 tf.keras.layers.DenseFeatures 层检查特定特征列的结果

age_column = feature_columns[7]
tf.keras.layers.DenseFeatures([age_column])(feature_batch).numpy()

DenseFeatures 仅接受密集张量,要检查分类列,您需要先将其转换为指示器列

gender_column = feature_columns[0]
tf.keras.layers.DenseFeatures([tf.feature_column.indicator_column(gender_column)])(feature_batch).numpy()

将所有基本特征添加到模型后,让我们训练模型。使用 tf.estimator API 训练模型只需一条命令

linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns)
linear_est.train(train_input_fn)
result = linear_est.evaluate(eval_input_fn)

clear_output()
print(result)

派生特征列

现在您已经达到了 75% 的准确率。单独使用每个基本特征列可能不足以解释数据。例如,年龄和标签之间的相关性对于不同的性别可能不同。因此,如果您只为 gender="Male"gender="Female" 学习一个模型权重,您将无法捕获每个年龄-性别组合(例如,区分 gender="Male"age="30" 以及 gender="Male"age="40")。

要了解不同特征组合之间的差异,您可以将交叉特征列添加到模型中(您也可以在交叉列之前对年龄列进行分箱)

age_x_gender = tf.feature_column.crossed_column(['age', 'sex'], hash_bucket_size=100)

将组合特征添加到模型后,让我们再次训练模型

derived_feature_columns = [age_x_gender]
linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns+derived_feature_columns)
linear_est.train(train_input_fn)
result = linear_est.evaluate(eval_input_fn)

clear_output()
print(result)

现在它达到了 77.6% 的准确率,略高于仅在基本特征上训练的模型。您可以尝试使用更多特征和转换,看看是否可以做得更好!

现在您可以使用训练模型对评估集中的乘客进行预测。TensorFlow 模型经过优化,可以一次对一批或多个示例进行预测。之前,eval_input_fn 是使用整个评估集定义的。

pred_dicts = list(linear_est.predict(eval_input_fn))
probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])

probs.plot(kind='hist', bins=20, title='predicted probabilities')

最后,查看结果的接收者操作特征 (ROC),这将让我们更好地了解真阳性率和假阳性率之间的权衡。

from sklearn.metrics import roc_curve
from matplotlib import pyplot as plt

fpr, tpr, _ = roc_curve(y_eval, probs)
plt.plot(fpr, tpr)
plt.title('ROC curve')
plt.xlabel('false positive rate')
plt.ylabel('true positive rate')
plt.xlim(0,)
plt.ylim(0,)