tensorflow学习笔记(六):TF.contrib.learn大杂烩

  这一节介绍一个常用的高级API:tf.contrib_learn。这个API使配置、训练和计算变得更简单。现在依然是依照官方教程进行一些学习和补充。而且程序依然会放在github里。而且从这里开始一直是按照最新的tensorflow版本(目前是r1.2)来进行。这个API的内容因为太高级了,也不准备多介绍了,因为比它好用的高级API还是有一些的,只不过这个API在自己创建Estimator上的灵活度还是很高的

一、API简略浏览

  在写程序之前依然先看一下API中的类、函数和方法。
  这里 有???的是因为实在基础太差有的没有接触过,希望有人能够指教

1、Estimator

  Estimator中包含了各种机器学习和深度学习的类,我们可以直接使用这些高阶类(当然也可以我们自己在此基础上创建自己的子类)。

大致介绍
tf.contrib.learn.BaseEstimator 基类但是一般不用,而是用Estimator
tf.contrib.learn.Estimator 这个给了我们自定义模型的机会,非常重要后面总结
tf.contrib.learn.Trainable 是Estimator的一部分内容
tf.contrib.learn.Evaluable 和上面同理
tf.contrib.learn.KMeansClustering 一个用于K-means的Estimator
tf.contrib.learn.ModeKeys ??
tf.contrib.learn.MetricSpec ??
tf.contrib.learn.PredictionKey ???
tf.contrib.learn.DNNClassifier 创建一个DNN模型分类器,API中有例子
tf.contrib.learn.DNNRegressor 创建一个DNN回归模型
tf.contrib.learn.DNNLinearCombinedRegressor 创建一个线性和DNN的联合回归模型
tf.contrib.learn.DNNLinearCombinedClassifier 创建一个线性和DNN的联合分类模型
tf.contrib.learn.LinearClassifier 创建一个线性和DNN的联合分类模型
tf.contrib.learn.LinearRegressor 线性回归模型
tf.contrib.learn.LogisticRegressor 逻辑回归模型
tf.contrib.learn.SVM 用于二分类的SVM

2、Distributed training utilities

用于分布式训练的实用程序。因为对分布式不了解所以这个先跳过吧。

大致介绍
tf.contrib.learn.Experiment 简单的建立模型试验的类,包括建模所需的所有信息
tf.contrib.learn.ExportStrategy 可以输出一系列的模型??
tf.contrib.learn.TaskType ???

3、Graph actions

这部分用于在图中执行多样的训练、评估和推断活动。

大致介绍
tf.contrib.learn.RunConfig 用于管理Estimator运行的控制信息
tf.contrib.learn.evaluate 评估从检查点加载的模型(弃用了)
tf.contrib.learn.infer 从检查点中恢复图(弃用)
tf.contrib.learn.run_feeds 弃用
tf.contrib.learn.run_n 弃用
tf.contrib.learn.train 弃用

上面几个弃用的方程可以用tf.train.* utilities来代替。

3、Input processing

队列和读取批量的输入数据。这个在上一节中有提及一点。

大致介绍
tf.contrib.learn.extract_dask_data 从dask*(dask.Series or dask.DataFrame)中读取数据
tf.contrib.learn.extract_dask_labels 同理只不过是读取标签
tf.contrib.learn.extract_pandas_data 从 pandas.DataFrame读取数据用于预测
tf.contrib.learn.extract_pandas_labels 从 pandas.DataFrame读取标签
tf.contrib.learn.extract_pandas_matrix 从 pandas.DataFrame读取数据
tf.contrib.learn.infer_real_valued_columns_from_input 把实值矩阵转为FeatureColumn对象
tf.contrib.learn.infer_real_valued_columns_from_input_fn 把返回元组的输入函数作为参数转为FeatureColumn对象
tf.contrib.learn.read_batch_examples Adds operations to read, queue, batch Example protos
tf.contrib.learn.read_batch_features Adds operations to read, queue, batch and parse Example protos.
tf.contrib.learn.read_batch_record_features Reads TFRecord, queues, batches and parses Example proto

上面三个英文的都涉及到了proto这种文件格式,这是google常用的,暂时还不是很懂是什么,不过caffe也用到了。
*dask是分布式的dataframe用到的。

4、Export utilities

输出实例

大致介绍
tf.contrib.learn.build_parsing_serving_input_fn ???
tf.contrib.learn.ProblemType ???

二、tf.contrib.learn Quickstart


  这部分来自官方教程。用到了上面提到的的Estimator中的内容,有助于帮助我们简单理解现有的Estimator,为后面自定义Estimator做准备。可以学到的东西有:     
* 如何读取CSV数据转为TensorFlow所用数据
* 如何利用tf.contrib.learn构建简单的神经网络分类器
* 如何利用模型测试新数据
完整的程序如下,数据集是Iris:

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import urllib

import numpy as np
import tensorflow as tf

# Data sets
IRIS_TRAINING = "iris_training.csv"
IRIS_TRAINING_URL = "http://download.tensorflow.org/data/iris_training.csv"

IRIS_TEST = "iris_test.csv"
IRIS_TEST_URL = "http://download.tensorflow.org/data/iris_test.csv"

def main():
  # If the training and test sets aren't stored locally, download them.
  # 下面这部分是看数据存不存在,不存在就下载
  if not os.path.exists(IRIS_TRAINING):
    raw = urllib.urlopen(IRIS_TRAINING_URL).read()
    with open(IRIS_TRAINING, "w") as f:
      f.write(raw)

  if not os.path.exists(IRIS_TEST):
    raw = urllib.urlopen(IRIS_TEST_URL).read()
    with open(IRIS_TEST, "w") as f:
      f.write(raw)
'''
Load datasets.用learn封装好的方法读取数据,很奇怪我没有在API上找到这个方法,后来在https://www.tensorflow.org/versions/master/api_docs/python/tf/contrib/learn/datasets找到了,进去看了下源码https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/learn/python/learn/datasets/base.py 发现这个是单纯用python的函数在读取整个数据,怪不得没有写在API里,因为还是针对一些常用的样例数据集写的。
返回值是一个元组,分别是(data,target)

'''
  training_set = tf.contrib.learn.datasets.base.load_csv_with_header(
      filename=IRIS_TRAINING,
      target_dtype=np.int,
      features_dtype=np.float32)
  test_set = tf.contrib.learn.datasets.base.load_csv_with_header(
      filename=IRIS_TEST,
      target_dtype=np.int,
      features_dtype=np.float32)

  # Specify that all features have real-value data
  # 指定数据的形式,下面的意思是形成一个4维度,列名为"",数据格式为float32(默认)的形式。
  feature_columns = [tf.contrib.layers.real_valued_column("", dimension=4)]

  # Build 3 layer DNN with 10, 20, 10 units respectively.
  classifier = tf.contrib.learn.DNNClassifier(feature_columns=feature_columns,
                                              hidden_units=[10, 20, 10],
                                              n_classes=3,
                                              model_dir="/tmp/iris_model")
  # Define the training inputs
  def get_train_inputs():
    x = tf.constant(training_set.data)
    y = tf.constant(training_set.target)

    return x, y

  # Fit model.
  classifier.fit(input_fn=get_train_inputs, steps=2000)

  # Define the test inputs
  def get_test_inputs():
    x = tf.constant(test_set.data)
    y = tf.constant(test_set.target)

    return x, y

  # Evaluate accuracy.
  #返回的是一个字典,取准确度
  accuracy_score = classifier.evaluate(input_fn=get_test_inputs,
                                       steps=1)["accuracy"]

  print("\nTest Accuracy: {0:f}\n".format(accuracy_score))

  # Classify two new flower samples.
  # predict参数函数只要返回一个数据矩阵即可
  def new_samples():
    return np.array(
      [[6.4, 3.2, 4.5, 1.5],
       [5.8, 3.1, 5.0, 1.7]], dtype=np.float32)

  predictions = list(classifier.predict(input_fn=new_samples))

  print(
      "New Samples, Class Predictions:    {}\n"
      .format(predictions))

if __name__ == "__main__":
    main()

三、Building Input Functions with tf.contrib.learn

前面我们用的输入函数使用的Python一次性读入所有数据,现在我们要换一个更好的方式,使用input_fn来预处理数据,并将数据feed给我们的模型。现在介绍使用input_fn封装数据输入的流程。

(1)基本形式

其基本的形式为:

def my_input_fn():
    # Preprocess your data here...预处理数据如归一化等,进行数据清洗

    # ...then return 1) a mapping of feature columns to Tensors with
    # the corresponding feature data, and 2) a Tensor containing labels
    # 返回两个tensor,feature_cols为一个键值对对应特征名和特征的数值,labels为标签值
    return feature_cols, labels

(2)转换特征数据为Tensors

如果输入数据为panda或arrays在input_fn返回之前需要转换为Tensor。

  • 对于连续数据可以用constant来转换:
feature_column_data = [1, 2.4, 0, 9.9, 3, 120]
feature_tensor = tf.constant(feature_column_data)
  • 对于sparse, categorical data(大部分数据都是0)需要转为一个SparseTensor,SparseTensor由三个部分组成:
    • dense_shape尺寸:和我们平时说的shape一样
    • indices索引:用于指示非零值的位置
    • values值:可以由索引得到所需要的值
      下面这个对应了一个3行5列的二维SparseTensor,坐标[0,1]处值为6、[2,4]处为0.5
sparse_tensor = tf.SparseTensor(indices=[[0,1], [2,4]],
                                values=[6, 0.5],
                                dense_shape=[3, 5])

[[0, 6, 0, 0, 0]
 [0, 0, 0, 0, 0]
 [0, 0, 0, 0, 0.5]]

(3)将转换后的Tensor输入到模型中

上面我们用过了模型fit的函数,这里更细致的讲一下。注意fit的输入为:

classifier.fit(input_fn=my_input_fn, steps=2000)

可以看到input_fn的参数为input_fn 函数 而不是返回值,也就是说下面这种情况是不允许的:

classifier.fit(input_fn=my_input_fn(training_set), steps=2000)

那么如何用参数化的函数来初始化呢?

  • 可以用下面这种形式:
def my_input_function_training_set():
  return my_input_function(training_set)

classifier.fit(input_fn=my_input_fn_training_set, steps=2000)
  • 或者使用Python的functools.partial:
classifier.fit(input_fn=functools.partial(my_input_function,
                                          data_set=training_set), steps=2000)
  • 或者使用lambda来进行函数封装:
classifier.fit(input_fn=lambda: my_input_fn(training_set), steps=2000)

这样进行参数化的函数封装有一个很大的好处,可以用同样的函数来处理evaluate 和 predict操作:

classifier.evaluate(input_fn=lambda: my_input_fn(test_set), steps=2000)

(4)例子讲解

下面用一个神经网络来预测房价,数据来自于UCI Housing Data Set,特征内容如下:

Feature Description
CRIM Crime rate per capita
ZN Fraction of residential land zoned to permit 25,000+ sq ft lots
INDUS Fraction of land that is non-retail business
NOX Concentration of nitric oxides in parts per 10 million
RM Average Rooms per dwelling
AGE Fraction of owner-occupied residences built before 1940
DIS Distance to Boston-area employment centers
TAX Property tax rate per $10,000
PTRATIO Student-teacher ratio

输入数据可以在这里下载:boston_train.csv, boston_test.csv, and boston_predict.csv。

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import itertools

import pandas as pd
import tensorflow as tf

tf.logging.set_verbosity(tf.logging.INFO)
#数据内容
COLUMNS = ["crim", "zn", "indus", "nox", "rm", "age",
           "dis", "tax", "ptratio", "medv"]
FEATURES = ["crim", "zn", "indus", "nox", "rm",
            "age", "dis", "tax", "ptratio"]
LABEL = "medv"

#定义输入函数处理程序
def input_fn(data_set):
  feature_cols = {k: tf.constant(data_set[k].values) for k in FEATURES}
  labels = tf.constant(data_set[LABEL].values)
  return feature_cols, labels


def main(unused_argv):
  # Load datasets
  # 用pandas读取输入数据
  training_set = pd.read_csv("boston_train.csv", skipinitialspace=True,
                             skiprows=1, names=COLUMNS)
  test_set = pd.read_csv("boston_test.csv", skipinitialspace=True,
                         skiprows=1, names=COLUMNS)

  # Set of 6 examples for which to predict median house values
  prediction_set = pd.read_csv("boston_predict.csv", skipinitialspace=True,
                               skiprows=1, names=COLUMNS)

  # Feature cols
  # 产生特征列的形式
  feature_cols = [tf.contrib.layers.real_valued_column(k)
                  for k in FEATURES]

  # Build 2 layer fully connected DNN with 10, 10 units respectively.
  regressor = tf.contrib.learn.DNNRegressor(feature_columns=feature_cols,
                                            hidden_units=[10, 10],
                                            model_dir="/tmp/boston_model")

  # Fit
  # 和前面说的一样用lambda来fit参数化的函数
  regressor.fit(input_fn=lambda: input_fn(training_set), steps=5000)

  # Score accuracy
  ev = regressor.evaluate(input_fn=lambda: input_fn(test_set), steps=1)
  loss_score = ev["loss"]
  print("Loss: {0:f}".format(loss_score))

  # Print out predictions
  y = regressor.predict(input_fn=lambda: input_fn(prediction_set))
  # .predict() returns an iterator; convert to a list and print predictions
  # islice(y,6)是只看前6个结果
  predictions = list(itertools.islice(y, 6))
  print("Predictions: {}".format(str(predictions)))

if __name__ == "__main__":
  tf.app.run()

四、Logging and Monitoring Basics with tf.contrib.learn

  这部分主要是模型训练的监控,主要利用TensorFlow的 logging capabilities(记录功能)和Monitor API 。例子程序建立在tf.contrib.learn Quickstart的基础之上。如果没有过程记录,其实整个算法就和黑盒子一样什么都看不到,比如有的时候可能模型在很早就已经收敛了或者看看模型是不是early stopping了是很必要的。
  一种解决方法是多次使用fit来一步一步评估模型,但是这明显很慢所以并不建议使用,所以 tf.contrib.learn提供了Monitor API帮助我们在训练过程中评估模型,下面内容主要有三个过程:

  • 如何进行记录
  • 如何设置一个ValidationMonitor进行流式的监控
  • 在TensorBoard上进行可视化

(1)Logging with TensorFlow记录

Tensorflow记录有5个等级DEBUG, INFO, WARN, ERROR, and FATAL(严重程度升序),比如我设计记录等级是INFO,那么我就屏蔽了Debug的内容但是保留高等级的记录信息。默认配置的记录等级是WARN也就是我们平时看到的,因为我们平时并没有看到INFO和DEBUG的信息。但是现在,因为我们要进行模型评估所以调整记录等级为INFO
方法是在import后面加上:

tf.logging.set_verbosity(tf.logging.INFO)

这时候运行代码的时候就会看到:

INFO:tensorflow:loss = 1.18812, step = 1
INFO:tensorflow:loss = 0.210323, step = 101
INFO:tensorflow:loss = 0.109025, step = 201

而且tf.contrib.learn会自动的每100个step输出训练损失评估指标到stderr。

(2)ValidationMonitor进行流式监控

tf.contrib.learn提供了一些高级的Monitor帮助我们在fit的时候进一步进行更细微的监控

Monitor 描述
CaptureVariable 每n个step保存一个特殊的变量值
PrintTensor 每n个step记录一个特殊的tensor值
SummarySaver 每n个step用tf.summary.FileWriter保存给定的tensor到tf.Summary protocol buffers
ValidationMonitor 每n个step记录一个特定的评估指标集合,而且可以满足条件情况下设置早停止

  我们如果想要在训练的同时评估测试集的结果,就可以使用ValidationMonitor作用在测试数据上。默认的every_n_steps为100,这里我们设置every_n_steps为50,并把下面的程序放到classifier的后面 :

validation_monitor = tf.contrib.learn.monitors.ValidationMonitor(
    test_set.data,
    test_set.target,
    every_n_steps=50)

  因为ValidationMonitor依赖于保存当前的checkpoint进行评估操作,所以我们需要在classifier中加入tf.contrib.learn.RunConfig(包含save_checkpoints_secs这个记录着保存两次checkpoint的时间差),因为iris训练数据少,所以可以设置save_checkpoints_secs为1

#model_dir保存着checkpoint是可以断点再训练的关键
classifier = tf.contrib.learn.DNNClassifier(
    feature_columns=feature_columns,
    hidden_units=[10, 20, 10],
    n_classes=3,
    model_dir="/tmp/iris_model",
    config=tf.contrib.learn.RunConfig(save_checkpoints_secs=1))

最终在fit的时候要附上validation_monitor,注意需要一个列表封装,因为可以同时有几种monitor存在:

classifier.fit(x=training_set.data,
               y=training_set.target,
               steps=2000,
               monitors=[validation_monitor])

最终得到的结果应该类似于:

INFO:tensorflow:Validation (step 50): loss = 1.71139, global_step = 0, accuracy = 0.266667
...
INFO:tensorflow:Validation (step 300): loss = 0.0714158, global_step = 268, accuracy = 0.966667
...
INFO:tensorflow:Validation (step 1750): loss = 0.0574449, global_step = 1729, accuracy = 0.966667

流式监控进阶内容
  1.自定义度量:
  可以看到ValidationMonitor会记录loss和accuracy,但是我们同样可以自定义度量方法。可以在ValidationMonitor的构造函数上加入metrics参数,其参数是一个键值对,键为想要记录的度量的名称,值为相应的MetricSpec对象。
MetricSpec对象可以接收下面几个参数(这里不是很明白):

  • metric_fn:计算并返回度量值的函数,可以使用现有的tf.contrib.metrics.streaming_precision或tf.contrib.metrics.streaming_recall也可以定制自己的函数
  • prediction_key:可以看成是预测结果的类别,包括:CLASSES、LOGISTIC、PROBABILITIES、SCORES、TOP_K等
  • label_key:这个在input_fn使用的时候才用
  • weights_key:
    下面的代码就定义了三种度量方法:
validation_metrics = {
    "accuracy":
        tf.contrib.learn.MetricSpec(
            metric_fn=tf.contrib.metrics.streaming_accuracy,
            prediction_key=tf.contrib.learn.PredictionKey.CLASSES),
    "precision":
        tf.contrib.learn.MetricSpec(
            metric_fn=tf.contrib.metrics.streaming_precision,
            prediction_key=tf.contrib.learn.PredictionKey.CLASSES),
    "recall":
        tf.contrib.learn.MetricSpec(
            metric_fn=tf.contrib.metrics.streaming_recall,
            prediction_key=tf.contrib.learn.PredictionKey.CLASSES)
}

把这个字典放入validation_monitor的metrics参数中,即metrics=validation_metrics,得到的结果如下:

INFO:tensorflow:Validation (step 50): recall = 0.0, loss = 1.20626, global_step = 1, precision = 0.0, accuracy = 0.266667
...
INFO:tensorflow:Validation (step 600): recall = 1.0, loss = 0.0530696, global_step = 571, precision = 1.0, accuracy = 0.966667
...
INFO:tensorflow:Validation (step 1500): recall = 1.0, loss = 0.0617403, global_step = 1452, precision = 1.0, accuracy = 0.966667

  2.早停止:
  我们可以设置早停止选项在需要停止的时候停止训练:

参数 描述
early_stopping_metric 早停止指标如loss或者accuracy
early_stopping_metric_minimize True代表希望最小化上面的指标,False希望最大化上面的指标
early_stopping_rounds 默认是None也就是不会早停止,如果是n就代表指标在n轮都不变那就停止
validation_monitor = tf.contrib.learn.monitors.ValidationMonitor(
    test_set.data,
    test_set.target,
    every_n_steps=50,
    metrics=validation_metrics,
    early_stopping_metric="loss",
    early_stopping_metric_minimize=True,
    early_stopping_rounds=200)

结果是:

...
INFO:tensorflow:Validation (step 1150): recall = 1.0, loss = 0.056436, global_step = 1119, precision = 1.0, accuracy = 0.966667
INFO:tensorflow:Stopping. Best step: 800 with loss = 0.048313818872.

接着就可以直接在TensorBoard上看结果了,注意logdir的地址:

$ tensorboard --logdir=/tmp/iris_model/
Starting TensorBoard 39 on port 6006

(3)全部程序

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os

import numpy as np
import tensorflow as tf

tf.logging.set_verbosity(tf.logging.INFO)

# Data sets
IRIS_TRAINING = os.path.join(os.path.dirname(__file__), "iris_training.csv")
IRIS_TEST = os.path.join(os.path.dirname(__file__), "iris_test.csv")


def main(unused_argv):
  # Load datasets.
  training_set = tf.contrib.learn.datasets.base.load_csv_with_header(
      filename=IRIS_TRAINING, target_dtype=np.int, features_dtype=np.float)
  test_set = tf.contrib.learn.datasets.base.load_csv_with_header(
      filename=IRIS_TEST, target_dtype=np.int, features_dtype=np.float)

  validation_metrics = {
      "accuracy":
          tf.contrib.learn.MetricSpec(
              metric_fn=tf.contrib.metrics.streaming_accuracy,
              prediction_key="classes"),
      "precision":
          tf.contrib.learn.MetricSpec(
              metric_fn=tf.contrib.metrics.streaming_precision,
              prediction_key="classes"),
      "recall":
          tf.contrib.learn.MetricSpec(
              metric_fn=tf.contrib.metrics.streaming_recall,
              prediction_key="classes")
  }
  validation_monitor = tf.contrib.learn.monitors.ValidationMonitor(
      test_set.data,
      test_set.target,
      every_n_steps=50,
      metrics=validation_metrics,
      early_stopping_metric="loss",
      early_stopping_metric_minimize=True,
      early_stopping_rounds=200)

  # Specify that all features have real-value data
  feature_columns = [tf.contrib.layers.real_valued_column("", dimension=4)]

  # Build 3 layer DNN with 10, 20, 10 units respectively.
  classifier = tf.contrib.learn.DNNClassifier(
      feature_columns=feature_columns,
      hidden_units=[10, 20, 10],
      n_classes=3,
      model_dir="/tmp/iris_model",
      config=tf.contrib.learn.RunConfig(save_checkpoints_secs=1))

  # Fit model.
  classifier.fit(x=training_set.data,
                 y=training_set.target,
                 steps=2000,
                 monitors=[validation_monitor])

  # Evaluate accuracy.
  accuracy_score = classifier.evaluate(
      x=test_set.data, y=test_set.target)["accuracy"]
  print("Accuracy: {0:f}".format(accuracy_score))

  # Classify two new flower samples.
  new_samples = np.array(
      [[6.4, 3.2, 4.5, 1.5], [5.8, 3.1, 5.0, 1.7]], dtype=float)
  y = list(classifier.predict(new_samples))
  print("Predictions: {}".format(str(y)))


if __name__ == "__main__":
  tf.app.run()

你可能感兴趣的:(深度学习)