码迷,mamicode.com
首页 > 其他好文 > 详细

[教程10]TensorFlow线性模型教程

时间:2017-10-29 16:47:28      阅读:257      评论:0      收藏:0      [点我收藏+]

标签:美国   回归   github   关系   com   builder   step   通过   temporary   

[教程10]TensorFlow线性模型教程

在本教程中,我们将使用TensorFlow中的tf.estimator API来解决二进制分类问题:根据年龄,性别,教育和职业(特征)等个人的普查数据,我们将尝试预测人每年赚取5万多美元(目标标签)。我们将训练逻辑回归模型,并给出个人信息,我们的模型将输出0到1之间的数字,这可以解释为个人年收入超过5万美元的可能性。

阅读人口普查数据

我们将使用的数据集是 普查收入数据集您可以 手动下载 培训数据 和测试数据,或使用如下代码:

import tempfile
import urllib
train_file = tempfile.NamedTemporaryFile()
test_file = tempfile.NamedTemporaryFile()
urllib.urlretrieve("https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data", train_file.name)
urllib.urlretrieve("https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test", test_file.name)

CSV文件下载后,让我们将它们读入 熊猫数据帧。

由于任务是二进制分类问题,我们将构建一个名为“label”的标签列,如果收入超过50K,则其值为1,否则为0。

train_labels = (df_train["income_bracket"].apply(lambda x: ">50K" in x)).astype(int)
test_labels = (df_test["income_bracket"].apply(lambda x: ">50K" in x)).astype(int)

接下来,我们来看看数据框架,看看我们可以使用哪些列来预测目标标签。列可以分为两种类型 - 分类和连续列:

  • 如果一个列的值只能是有限集合中的一个类别,那么该列称为分类例如,一个人的本国(美国,印度,日本等)或教育程度(高中,大学等)是分类栏目。
  • 如果其值可以是连续范围内的任何数值,则该列称为连续例如,一个人的资本收益(例如$ 14,084)是一个连续的列。

以下是“普查收入”数据集中列的列表:

列名称类型描述
年龄 连续 个人的年龄
workclass 明确的 个人拥有的雇主类型(政府,军事,私人等)。
fnlwgt 连续 人口普查员认为观察值(样本重量)的人数。该变量不会被使用。
教育 明确的 为个人所取得的最高水平的教育。
education_num 连续 数字形式最高的教育水平。
婚姻状况 明确的 个人的婚姻状况
占用 明确的 个人的职业。
关系 明确的 妻子,自己的孩子,丈夫,不在家,其他相对,未婚。
种族 明确的 白人,亚太岛民族,美印爱斯基摩人,其他黑人。
性别 明确的 女人男人。
资本收益 连续 资本收益记录。
capital_loss 连续 资本损失记录。
hours_per_week 连续 每周工作时数。
祖国 明确的 个人原籍国。
收入 明确的 “> 50K”或“<= 50K”,这意味着该人是否每年赚取超过$ 50,000。

将数据转换成传感器

构建tf.estimator模型时,通过Input Builder函数指定输入数据。此构建器函数将不会被调用,直到它后来传递给tf.estimator.Estimator方法,如trainevaluate此函数的目的是构造以tf.Tensors或tf.SparseTensor形式表示的输入数据更详细地说,Input Builder函数返回以下对象

  1. feature_cols:从特征列名称到Tensors或 SparseTensors
  2. label:A Tensor包含标签列。

feature_cols将在下一节中使用键的构造列。因为我们想用不同的数据调用trainevaluate方法,我们定义一个方法,返回基于给定数据的输入函数。请注意,在构建TensorFlow图时,将不会在运行图时调用返回的输入函数。返回的是将输入数据表示为TensorFlow计算的基本单位a Tensor(或SparseTensor)。

我们使用该tf.estimator.inputs.pandas_input_fn方法从熊猫数据帧创建输入函数。火车或测试数据帧中的每个连续列将被转换成一个Tensor,通常是表示密集数据的良好格式。对于分类数据,我们必须将数据表示为a SparseTensor该数据格式适用于表示稀疏数据。代表输入数据的另一种更先进的方法是构造一个 表示文件或其他数据源输入和读取器,并以TensorFlow运行图形方式遍历文件。

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)
  # remove NaN elements
  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)

模型的选择和工程特征

选择和制作右侧的特征列是学习有效模型的关键。特征柱可以是在原来的数据帧(让我们称它们为原料的列中的任一个基本特征的列)的基础上在一个或多个碱基列(我们称它们限定一些转化或创建任何新列得出的特征列)。基本上,“特征列”是可用于预测目标标签的任何原始或派生变量的抽象概念。

基本分类特征列

要定义分类功能的功能列,我们可以CategoricalColumn使用tf.feature_column API 创建一个 如果您知道列的所有可能的特征值的集合,并且只有其中的几个可以使用categorical_column_with_vocabulary_list列表中的每个键将从0开始分配一个自动增量ID。例如,对于gender 列,我们可以通过执行以下操作将特征字符串“Female”分配给整数ID为0,将“Male”分配给1。

gender = tf.feature_column.categorical_column_with_vocabulary_list(
    "gender", ["Female", "Male"])

如果我们不提前知道一组可能的价值呢?不是问题 我们可以用

categorical_column_with_hash_bucket
occupation = tf.feature_column.categorical_column_with_hash_bucket(
    "occupation", hash_bucket_size=1000)

将会发生的是,occupation 在训练中遇到这些特征列,每个可能的值将被哈希到一个整数ID。见下面的示例图:

ID特征
...  
9 "Machine-op-inspct"
...  
103 "Farming-fishing"
...  
375 "Protective-serv"
...

无论我们选择哪种方式定义一个SparseColumn,每个特征字符串将通过查找固定映射或散列来映射到整数ID。请注意,散列碰撞是可能的,但可能不会显着影响模型质量。在引擎盖下,LinearModel该类负责管理映射和创建tf.Variable以存储每个功能ID的模型参数(也称为模型权重)。模型参数将通过后面的模型训练过程学习。

我们将采取类似的技巧来定义其他分类功能:

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"
    ])
native_country = tf.feature_column.categorical_column_with_hash_bucket(
    "native_country", hash_bucket_size=1000)

基本连续特征列

类似地,我们可以NumericColumn为模型中要使用的每个连续特征列定义一个

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")

通过Bucketization进行连续分类

有时,连续特征与标签之间的关系不是线性的。作为一个假设的例子,一个人的收入可能随着职业生涯的早期阶段的年龄而增长,那么增长可能会在某个时候慢一些,最后退休后的收入就会下降。在这种情况下,将raw age作为实值特征列可能不是一个好的选择,因为模型只能学习三种情况之一:

  1. 随着年龄的增长,收入总是以一定的速度增长(正相关),
  2. 收入总是随着年龄的增长而下降(负相关),或
  3. 无论什么年龄(无相关性),收入保持不变

如果我们想要分别了解收入和每个年龄组之间的细粒度关联,我们可以利用桶化Bucketization是将连续特征的整个范围划分成一组连续的仓/桶的过程,然后根据该值所在的桶将原始数值特征转换为桶ID(作为分类特征)。因此,我们可以定义一个bucketized_column以上age为:

age_buckets = tf.feature_column.bucketized_column(
    age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])

哪里boundaries是桶边界的列表。在这种情况下,有10个边界,导致11个年龄组的桶(从17岁及以下,18-24,25-29,...,到65岁以上)。

用交叉列相交多个列

单独使用每个基本特征列可能不足以解释数据。例如,不同职业的教育与标签(赚取> 50,000美元)的相关性可能会有所不同。因此,如果我们只学了一个模型的权重education="Bachelors"education="Masters",我们就无法捕捉到每一个教育,职业组合(例如区分education="Bachelors" AND occupation="Exec-managerial" 和education="Bachelors" AND occupation="Craft-repair")。要了解不同功能组合之间的差异,我们可以向模型添加交叉的特征列

education_x_occupation = tf.feature_column.crossed_column(
    ["education", "occupation"], hash_bucket_size=1000)

我们也可以创建CrossedColumn超过两列。每个构成列可以是基本特征列,分类(SparseColumn),分段实值特征列(BucketizedColumn)或甚至另一个CrossColumn这里有一个例子:

age_buckets_x_education_x_occupation = tf.feature_column.crossed_column(
    [age_buckets, "education", "occupation"], hash_bucket_size=1000)

定义逻辑回归模型

在处理输入数据并定义所有特征列之后,我们现在可以将它们全部放在一起,构建一个Logistic回归模型。在上一节中,我们看到了几种类型的基础和派生的功能列,其中包括:

  • CategoricalColumn
  • NumericColumn
  • BucketizedColumn
  • CrossedColumn

所有这些都是抽象FeatureColumn类的类,并且可以添加到feature_columns模型字段中:

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)
]

model_dir = tempfile.mkdtemp()
m = tf.estimator.LinearClassifier(
    model_dir=model_dir, feature_columns=base_columns + crossed_columns)

该模型还自动学习一个偏离项,它控制预测,而不需要观察任何特征(参见“逻辑回归如何运作”以获得更多的解释)。学习的模型文件将被存储model_dir

培训和评估我们的模型

在将所有功能添加到模型之后,现在来看看如何实际训练模型。训练一个模型只是一个使用tf.estimator API的单行:

# set num_epochs to None to get infinite stream of data.
m.train(
    input_fn=input_fn(train_file_name, num_epochs=None, shuffle=True),
    steps=train_steps)

在模型训练后,我们可以评估我们的模型在预测保持数据的标签方面有多好:

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.83557522,这意味着精度是83.6%。随意尝试更多的功能和变革,看看你能做得更好!

如果您希望看到一个有效的端对端示例,您可以下载我们的 示例代码并设置model_type标志wide

添加正则化以防止过度配合

正则化是一种用于避免过度拟合的技术当您的模型对其进行培训的数据执行情况良好时,会发生过度拟合,但对于模型以前未见过的测试数据(如实时流量)更糟。通常,当模型过于复杂时,通常发生过拟合,例如相对于观察到的训练数据的数量具有太多的参数。正则化允许您控制您的模型的复杂性,并使模型更可概括为看不见的数据。

在线性模型库中,您可以将L1和L2正则化添加到模型中:

m = tf.estimator.LinearClassifier(
    model_dir=model_dir, feature_columns=base_columns + crossed_columns,
    optimizer=tf.train.FtrlOptimizer(
      learning_rate=0.1,
      l1_regularization_strength=1.0,
      l2_regularization_strength=1.0),
    model_dir=model_dir)

L1和L2正则化之间的一个重要区别在于L1正则化趋向于使模型权重保持为零,创建较为稀疏的模型,而L2正则化也试图使模型权重接近零但不一定为零。因此,如果增加L1正则化的强度,则您将具有较小的模型大小,因为许多模型权重将为零。当特征空间非常大但稀疏时,并且当存在阻止您提供太大模型的资源约束时,这通常是希望的。

在实践中,您应该尝试L1,L2正则化强度的各种组合,并找到最佳控制过拟合的最佳参数,并为您提供所需的模型大小。

技术分享

 

 

[教程10]TensorFlow线性模型教程

标签:美国   回归   github   关系   com   builder   step   通过   temporary   

原文地址:http://www.cnblogs.com/yinghuali/p/7750273.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!