页面树结构

2017-11-09 ApacheCN 开源组织,第二期邀请成员活动,一起走的更远 : http://www.apachecn.org/member/209.html


MachineLearning 优酷地址 : http://i.youku.com/apachecn

转至元数据结尾
转至元数据起始

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

建立

要尝试本教程的代码:

  1.  如果还没有安装TensorFlow

  2. 下载教程代码

  3. 安装熊猫数据分析库。tf.learn不需要大熊猫,但它支持它,本教程使用大熊猫。安装大熊猫:

    1. 获取pip

    ```shell#Ubuntu / Linux 64位$ sudo apt-get install python-pip python-dev

    #Mac OS X $ sudo easy_install pip $ sudo easy_install - 升级六个```

    1. 使用pip安装熊猫:

    shell $ sudo pip install pandas

    如果您在安装熊猫麻烦,请查阅 说明书 上的熊猫网站。

  4. 使用以下命令执行教程代码来训练本教程中描述的线性模型:

shell $ python wide_n_deep_tutorial.py --model_type=wide

请继续阅读,了解此代码如何构建其线性模型。

 

阅读人口普查数据

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

import tempfile
import urllib
train_file = tempfile.NamedTemporaryFile()
test_file = tempfile.NamedTemporaryFile()
urllib.urlretrieve("http://mlr.cs.umass.edu/ml/machine-learning-databases/adult/adult.data", train_file.name)
urllib.urlretrieve("http://mlr.cs.umass.edu/ml/machine-learning-databases/adult/adult.test", test_file.name)
CSV文件下载后,让我们将它们读入 熊猫数据帧。
import pandas as pd
COLUMNS = ["age", "workclass", "fnlwgt", "education", "education_num",
           "marital_status", "occupation", "relationship", "race", "gender",
           "capital_gain", "capital_loss", "hours_per_week", "native_country",
           "income_bracket"]
df_train = pd.read_csv(train_file, names=COLUMNS, skipinitialspace=True)
df_test = pd.read_csv(test_file, names=COLUMNS, skipinitialspace=True, skiprows=1)
由于任务是二进制分类问题,我们将构建一个名为“label”的标签列,如果收入超过50K,其值为1,否则为0。
LABEL_COLUMN = "label"
df_train[LABEL_COLUMN] = (df_train["income_bracket"].apply(lambda x: ">50K" in x)).astype(int)
df_test[LABEL_COLUMN] = (df_test["income_bracket"].apply(lambda x: ">50K" in x)).astype(int)
接下来,我们来看看数据框,看看可以用来预测目标标签的列。列可以分为两类 - 分类和连续列:
  • 如果一个列的值只能是有限集合中的一个类别,那么这个列称为分类。例如,一个人的本国(美国,印度,日本等)或教育程度(高中,大学等)是分类栏。
  • 如果其值可以是连续范围内的任何数值,则称为连续列。例如,一个人的资本收益(如$ 14,084)是一个连续的列。
CATEGORICAL_COLUMNS = ["workclass", "education", "marital_status", "occupation",
                       "relationship", "race", "gender", "native_country"]
CONTINUOUS_COLUMNS = ["age", "education_num", "capital_gain", "capital_loss", "hours_per_week"]
以下是普查收入数据集中列的列表:
列名称类型描述
年龄连续个人的年龄
workclass明确的个人拥有的雇主类型(政府,军事,私人等)。
fnlwgt连续人口普查员认为观察值的人数(样本重量)。该变量不会被使用。
教育明确的该个人获得的最高教育水平。
education_num连续数字形式最高的教育水平。
婚姻状况明确的个人的婚姻状况
占用明确的占领个人。
关系明确的妻子,自己的孩子,丈夫,不在家,其他相对,未婚。
种族明确的白人,亚太岛民族,印裔爱斯基摩人,其他黑人。
性别明确的女人男人。
资本收益连续资本收益记录。
capital_loss连续资本损失记录。
hours_per_week连续每周工作时间。
祖国明确的个人原籍国。
收入明确的“> 50K”或“<= 50K”,这意味着该人每年的收入超过$ 50,000。

将数据转换成传感器

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

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

 feature_cols将在下一节中使用键的构造列。因为我们希望调用fitevaluate方法用不同的数据,我们定义两个不同的输入助洗剂的功能, train_input_fn并且test_input_fn其是除了它们通过不同的数据相同input_fn。注意,input_fn在构造TensorFlow图时将不会在运行图时调用它。返回的是将输入数据表示为TensorFlow计算的基本单位a Tensor(或SparseTensor)。

我们的模型表示输入数据为恒定张量,这意味着该张量表示为恒定值,在这种情况下的特定列的值df_traindf_test。这是将数据传递到TensorFlow中的最简单的方法。用于表示输入数据的另一种更先进的方法 是构造一个表示文件或其他数据源的输入和读取器,并以TensorFlow运行图形的方式遍历文件。火车或测试数据帧中的每个连续列将被转换成一个Tensor通常是表示密集数据的良好格式。对于分类数据,我们必须将数据表示为a SparseTensor。该数据格式适用于表示稀疏数据。 

import tensorflow as tf

def input_fn(df):
  # Creates a dictionary mapping from each continuous feature column name (k) to
  # the values of that column stored in a constant Tensor.
  continuous_cols = {k: tf.constant(df[k].values)
                     for k in CONTINUOUS_COLUMNS}
  # Creates a dictionary mapping from each categorical feature column name (k)
  # to the values of that column stored in a tf.SparseTensor.
  categorical_cols = {k: tf.SparseTensor(
      indices=[[i, 0] for i in range(df[k].size)],
      values=df[k].values,
      dense_shape=[df[k].size, 1])
                      for k in CATEGORICAL_COLUMNS}
  # Merges the two dictionaries into one.
  feature_cols = dict(continuous_cols.items() + categorical_cols.items())
  # Converts the label column into a constant Tensor.
  label = tf.constant(df[LABEL_COLUMN].values)
  # Returns the feature columns and the label.
  return feature_cols, label

def train_input_fn():
  return input_fn(df_train)

def eval_input_fn():
  return input_fn(df_test) 

模型的选择和工程特征

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

基本分类特征列

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

gender = tf.contrib.layers.sparse_column_with_keys(
  column_name="gender", keys=["Female", "Male"]) 
如果我们不提前知道一组可能的价值怎么办?不是问题 我们可以用sparse_column_with_hash_bucket
education = tf.contrib.layers.sparse_column_with_hash_bucket("education", hash_bucket_size=1000) 
将会发生的是,education 在训练中遇到他们时,功能列中的每个可能的值都将被排列成一个整数ID。参见下面的示例图:
 
ID特征
... 
9"Bachelors"
... 
103"Doctorate"
... 
375"Masters"
... 

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

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

race = tf.contrib.layers.sparse_column_with_hash_bucket("race", hash_bucket_size=100)
marital_status = tf.contrib.layers.sparse_column_with_hash_bucket("marital_status", hash_bucket_size=100)
relationship = tf.contrib.layers.sparse_column_with_hash_bucket("relationship", hash_bucket_size=100)
workclass = tf.contrib.layers.sparse_column_with_hash_bucket("workclass", hash_bucket_size=100)
occupation = tf.contrib.layers.sparse_column_with_hash_bucket("occupation", hash_bucket_size=1000)
native_country = tf.contrib.layers.sparse_column_with_hash_bucket("native_country", hash_bucket_size=1000) 

基本连续特征列

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

age = tf.contrib.layers.real_valued_column("age")
education_num = tf.contrib.layers.real_valued_column("education_num")
capital_gain = tf.contrib.layers.real_valued_column("capital_gain")
capital_loss = tf.contrib.layers.real_valued_column("capital_loss")
hours_per_week = tf.contrib.layers.real_valued_column("hours_per_week") 

通过桶装连续分类

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

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

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

age_buckets = tf.contrib.layers.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.contrib.layers.crossed_column([education, occupation], hash_bucket_size=int(1e4))
我们也可以创建CrossedColumn超过两列。每个组成列可以是一个基本特征列,分别是(SparseColumn),一个分段实值特征列(BucketizedColumn)或另一个CrossColumn。以下是一个例子:
age_buckets_x_education_x_occupation = tf.contrib.layers.crossed_column(
  [age_buckets, education, occupation], hash_bucket_size=int(1e6)) 

定义逻辑回归模型

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

  • SparseColumn
  • RealValuedColumn
  • BucketizedColumn
  • CrossedColumn

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

model_dir = tempfile.mkdtemp()
m = tf.contrib.learn.LinearClassifier(feature_columns=[
  gender, native_country, education, occupation, workclass, marital_status, race,
  age_buckets, education_x_occupation, age_buckets_x_education_x_occupation],
  model_dir=model_dir) 
该模型还自动学习一个偏离项,它控制预测,而不需要观察任何特征(参见“逻辑回归如何运作”以获得更多的解释)。学习的模型文件将被存储在model_dir

训练和评估我们的模型

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

m.fit(input_fn=train_input_fn, steps=200) 
在模型训练后,我们可以评估我们的模型在预测保持数据的标签方面有多好:
results = m.evaluate(input_fn=eval_input_fn, steps=1)
for key in sorted(results):
    print("%s: %s" % (key, results[key]))
输出的第一行应该是这样accuracy: 0.83557522,这意味着精度是83.6%。随意尝试更多的功能和转换,看看你能做得更好!

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

 

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

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

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

m = tf.contrib.learn.LinearClassifier(feature_columns=[
  gender, native_country, education, occupation, workclass, marital_status, race,
  age_buckets, education_x_occupation, age_buckets_x_education_x_occupation],
  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正则化强度的各种组合,并找到最佳控制过拟合的最佳参数,并为您提供所需的模型大小。

 

逻辑回归如何运作

最后,让我们花一点时间来讨论Logistic回归模型实际上是什么样的,以防你不熟悉它。我们将标签标示为 ÿ,并将观察到的特征集合作为特征向量  X=[X1,X2,。。。,Xð]。我们定义 ÿ=1 如果一个人获得> 50,000美元和  ÿ=0除此以外。在逻辑回归中,标签为正的概率( ÿ=1)给出了功能  X 作为:

哪里   是功能的模型权重   。  b是常数,通常被称为模型的偏见。该方程由两部分组成:线性模型和逻辑函数:
  • 线性模型:首先,我们可以看到  是线性模型,其中输出是输入特征的线性函数  X。偏见 b是没有观察任何功能的预测。型号重量   反映了功能的特点  与正标记相关。如果  与阳性标本,重量正相关   增加和概率  将更接近1.另一方面,如果   与正标记负相关,然后与体重负相关   降低和概率   将接近0。

  • 逻辑函数:其次,我们可以看到有一个逻辑函数(也称为Sigmoid函数) 应用于线性模型。逻辑函数用于转换线性模型的输出  从任何实数到范围  [0,1],这可以被解释为概率。

模型训练是一个优化问题:目标是找到一组模型权重(即模型参数),以最小化训练数据中定义的损失函数,如Logistic回归模型的物流损失。损失函数测量地面实际标签与模型预测之间的差异。如果预测非常接近实地标签,损失值将会很低; 如果预测距离标签非常远,则损失值会很高。

 

深入学习

如果您有兴趣了解更多信息,请查看我们的“深度和深度学习教程”,我们将向您介绍如何通过使用TF.Learn API联合培训线性模型和深层神经网络的优势。

  • 无标签