在前文 @{$wide$TensorFlow Liner Model Tutorial} 中,咱们使用 人口收入普查数据集 训练了一个 logistic 线性回归模型去预测我的年收入超过 5 万美圆的几率。TensorFlow 在训练深度神经网络方面效果也很好,那么你可能会考虑该如何取舍它的功能了 -- 但是,为何不选择二者兼得呢?那么,是否能够将二者的优点结合在一个模型中呢?html
在这篇文章中,咱们将会介绍如何使用 TF.Learn API 同时训练一个广度线性模型和一个深度前馈神经网络。这种方法结合了记忆和泛化的优点。它在通常的大规模回归和具备稀疏输入特性的分类问题(例如,分类特征存在一个很大的可能值域)上颇有效。若是你有兴趣学习更多关于广度和深度学习如何工做的问题,请参考 研究论文python
如今,咱们来看一个简单的例子。git
上图展现了广度模型(具备稀疏特征和转换性质的 logistic 回归模型),深度模型(具备一个嵌入层和多个隐藏层的前馈神经网络),广度和深度模型(二者的联合训练)的区别比较。在高层级里,只须要经过如下三个步骤就能使用 TF.Learn API 配置广度,深度或广度和深度模型。github
选择广度部分的特征:选择要使用的稀疏基本列和交叉列。api
选择深度部分的特征:选择连续列,每一个分类列的嵌入维度和隐藏层大小。bash
将它们一块儿放入广度和深度模型(DNNLinearCombinedClassifier
)。网络
若是想要尝试本教程中的代码:app
安装 TensorFlow ,请前往此处。机器学习
下载 教程代码。ide
安装 pandas 数据分析库。由于本教程中须要使用 pandas 数据。虽然 tf.learn 不要求 pandas,可是它支持 pandas。安装 pandas:
a. 获取 pip:
# Ubuntu/Linux 64-bit
$ sudo apt-get install python-pip python-dev
# Mac OS X
$ sudo easy_install pip
$ sudo easy_install --upgrade six复制代码
b. 使用 pip 安装 pandas
$ sudo pip install pandas复制代码
若是你在安装过程当中遇到问题,请前往 pandas 网站上的 说明 。
$ python wide_n_deep_tutorial.py --model_type=wide_n_deep复制代码
请继续阅读,了解此代码如何构建其线性模型。
首先,定义咱们使用的基本分类和连续特征的列。这些列将被做为模型的广度部分和深度部分的构件块。
import tensorflow as tf
gender = tf.feature_column.categorical_column_with_vocabulary_list(
"gender", ["Female", "Male"])
education = tf.feature_column.categorical_column_with_vocabulary_list(
"education", [
"Bachelors", "HS-grad", "11th", "Masters", "9th",
"Some-college", "Assoc-acdm", "Assoc-voc", "7th-8th",
"Doctorate", "Prof-school", "5th-6th", "10th", "1st-4th",
"Preschool", "12th"
])
marital_status = tf.feature_column.categorical_column_with_vocabulary_list(
"marital_status", [
"Married-civ-spouse", "Divorced", "Married-spouse-absent",
"Never-married", "Separated", "Married-AF-spouse", "Widowed"
])
relationship = tf.feature_column.categorical_column_with_vocabulary_list(
"relationship", [
"Husband", "Not-in-family", "Wife", "Own-child", "Unmarried",
"Other-relative"
])
workclass = tf.feature_column.categorical_column_with_vocabulary_list(
"workclass", [
"Self-emp-not-inc", "Private", "State-gov", "Federal-gov",
"Local-gov", "?", "Self-emp-inc", "Without-pay", "Never-worked"
])
# 展现一个哈希的例子:
occupation = tf.feature_column.categorical_column_with_hash_bucket(
"occupation", hash_bucket_size=1000)
native_country = tf.feature_column.categorical_column_with_hash_bucket(
"native_country", hash_bucket_size=1000)
# 连续基列
age = tf.feature_column.numeric_column("age")
education_num = tf.feature_column.numeric_column("education_num")
capital_gain = tf.feature_column.numeric_column("capital_gain")
capital_loss = tf.feature_column.numeric_column("capital_loss")
hours_per_week = tf.feature_column.numeric_column("hours_per_week")
# 转换
age_buckets = tf.feature_column.bucketized_column(
age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])复制代码
广度模型是一个具备稀疏和交叉特征列的线性模型:
base_columns = [
gender, native_country, education, occupation, workclass, relationship,
age_buckets,
]
crossed_columns = [
tf.feature_column.crossed_column(
["education", "occupation"], hash_bucket_size=1000),
tf.feature_column.crossed_column(
[age_buckets, "education", "occupation"], hash_bucket_size=1000),
tf.feature_column.crossed_column(
["native_country", "occupation"], hash_bucket_size=1000)
]复制代码
具备交叉特征列的广度模型能够有效地记忆特征之间的稀疏交互。也就是说,交叉特征列不能归纳没有在训练数据中出现的特征组合。让咱们采用嵌入方式来添加一个深度模型来修复这个问题。
深度模型是一个前馈神经网络,如前图所示。每个稀疏,高维度分类特征首先都会被转换成一个低维度密集的实值矢量,一般被称为嵌入式矢量。这些低维度密集的嵌入式矢量与连续特征相连,而后在正向传递中馈入神经网络的隐藏层。嵌入值随机初始化,并与其余模型参数一块儿训练,以最大化减小训练损失。若是你有兴趣了解更多关于嵌入的知识,请在查阅教程 Vector Representations of Words 或在 Wikipedia 上查阅 Word Embedding。
咱们将使用 embedding_column
配置分类嵌入列,并将它们与连续列链接:
deep_columns = [
tf.feature_column.indicator_column(workclass),
tf.feature_column.indicator_column(education),
tf.feature_column.indicator_column(gender),
tf.feature_column.indicator_column(relationship),
# 展现一个嵌入例子
tf.feature_column.embedding_column(native_country, dimension=8),
tf.feature_column.embedding_column(occupation, dimension=8),
age,
education_num,
capital_gain,
capital_loss,
hours_per_week,
]复制代码
嵌入的 dimension
越高,自由度就越高,模型将不得不学习这些特性的表示。为了简单起见,咱们设置全部特征列的维度为 8。从经验上看,关于维度的设定最好是从 \log_{2}(n) 或 k\sqrt[4]{n} 值开始,这里的 n 表明特征列中惟一特征的数量,k 是一个很小的常量(一般小于10)。
经过密集嵌入,深度模型能够更好的归纳,并更好对以前没有在训练数据中碰见的特征进行预测。然而,当两个特征列之间的底层交互矩阵是稀疏和高等级时,很难学习特征列的有效低维度表示。在这种状况下,大多数特征对之间的交互应该为零,除了少数几个,但密集的嵌入将致使全部特征对的非零预测,从而可能过分泛化。另外一方面,具备交叉特征的线性模型能够用更少的模型参数有效地记住这些“异常规则”。
如今,咱们来看看如何联合训练广度和深度模型,让它们优点和劣势互补。
经过将其最终输出的对数概率做为预测结合起来,而后将预测提供给 logistic 损失函数,将广度模型和深度模型相结合。全部的图形定义和变量分配都已经被处理,因此你只须要建立一个 DNNLinearCombinedClassifier
:
import tempfile
model_dir = tempfile.mkdtemp()
m = tf.contrib.learn.DNNLinearCombinedClassifier(
model_dir=model_dir,
linear_feature_columns=wide_columns,
dnn_feature_columns=deep_columns,
dnn_hidden_units=[100, 50])复制代码
在训练模型以前,请先阅读人口普查数据集,就像在 《TensorFlow 线性模型教程》 中所作的同样。 输入数据处理的代码再次为你提供方便:
import pandas as pd
import urllib
# 为数据集定义列名
CSV_COLUMNS = [
"age", "workclass", "fnlwgt", "education", "education_num",
"marital_status", "occupation", "relationship", "race", "gender",
"capital_gain", "capital_loss", "hours_per_week", "native_country",
"income_bracket"
]
def maybe_download(train_data, test_data):
"""Maybe downloads training data and returns train and test file names."""
if train_data:
train_file_name = train_data
else:
train_file = tempfile.NamedTemporaryFile(delete=False)
urllib.request.urlretrieve(
"https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data",
train_file.name) # pylint: disable=line-too-long
train_file_name = train_file.name
train_file.close()
print("Training data is downloaded to %s" % train_file_name)
if test_data:
test_file_name = test_data
else:
test_file = tempfile.NamedTemporaryFile(delete=False)
urllib.request.urlretrieve(
"https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test",
test_file.name) # pylint: disable=line-too-long
test_file_name = test_file.name
test_file.close()
print("Test data is downloaded to %s"% test_file_name)
return train_file_name, test_file_name
def input_fn(data_file, num_epochs, shuffle):
"""Input builder function."""
df_data = pd.read_csv(
tf.gfile.Open(data_file),
names=CSV_COLUMNS,
skipinitialspace=True,
engine="python",
skiprows=1)
# 移除 NaN 元素
df_data = df_data.dropna(how="any", axis=0)
labels = df_data["income_bracket"].apply(lambda x: ">50K" in x).astype(int)
return tf.estimator.inputs.pandas_input_fn(
x=df_data,
y=labels,
batch_size=100,
num_epochs=num_epochs,
shuffle=shuffle,
num_threads=5)复制代码
阅读数据以后,你能够训练并评估模型:
# 将 num_epochs 设置为 None,以得到无限的数据流
m.train(
input_fn=input_fn(train_file_name, num_epochs=None, shuffle=True),
steps=train_steps)
# 在全部数据被消耗以前,为了运行评估,设置 steps 为 None
results = m.evaluate(
input_fn=input_fn(test_file_name, num_epochs=1, shuffle=False),
steps=None)
print("model directory = %s" % model_dir)
for key in sorted(results):
print("%s: %s" % (key, results[key]))复制代码
输出的第一行应该相似 accuracy: 0.84429705
。咱们能够看到使用广度和深度模型将广度线性模型精度约 83.6% 提升到了约 84.4%。若是你想看端对端的工做示例,你能够下载咱们的 示例代码。
请注意,本教程只是一个小型数据基的简单示例,为了让你快速熟悉 API。若是你有大量具备稀疏特征列和大量可能特征值的数据集,广度和深度学习将会更增强大。此外,请随时关注咱们的 研究论文,以了解更多关于在实际中广度和深度学习在大型机器学习方面如何应用的思考。