这一节介绍一个常用的高级API:tf.contrib_learn。这个API使配置、训练和计算变得更简单。现在依然是依照官方教程进行一些学习和补充。而且程序依然会放在github里。而且从这里开始一直是按照最新的tensorflow版本(目前是r1.2)来进行。这个API的内容因为太高级了,也不准备多介绍了,因为比它好用的高级API还是有一些的,只不过这个API在自己创建Estimator上的灵活度还是很高的
在写程序之前依然先看一下API中的类、函数和方法。
这里 有???的是因为实在基础太差有的没有接触过,希望有人能够指教
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 |
用于分布式训练的实用程序。因为对分布式不了解所以这个先跳过吧。
类 | 大致介绍 |
---|---|
tf.contrib.learn.Experiment | 简单的建立模型试验的类,包括建模所需的所有信息 |
tf.contrib.learn.ExportStrategy | 可以输出一系列的模型?? |
tf.contrib.learn.TaskType | ??? |
这部分用于在图中执行多样的训练、评估和推断活动。
类 | 大致介绍 |
---|---|
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来代替。
队列和读取批量的输入数据。这个在上一节中有提及一点。
类 | 大致介绍 |
---|---|
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用到的。
输出实例
类 | 大致介绍 |
---|---|
tf.contrib.learn.build_parsing_serving_input_fn | ??? |
tf.contrib.learn.ProblemType | ??? |
这部分来自官方教程。用到了上面提到的的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()
前面我们用的输入函数使用的Python一次性读入所有数据,现在我们要换一个更好的方式,使用input_fn来预处理数据,并将数据feed给我们的模型。现在介绍使用input_fn封装数据输入的流程。
其基本的形式为:
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
如果输入数据为panda或arrays在input_fn返回之前需要转换为Tensor。
feature_column_data = [1, 2.4, 0, 9.9, 3, 120]
feature_tensor = tf.constant(feature_column_data)
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]]
上面我们用过了模型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)
classifier.fit(input_fn=functools.partial(my_input_function,
data_set=training_set), steps=2000)
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)
下面用一个神经网络来预测房价,数据来自于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()
这部分主要是模型训练的监控,主要利用TensorFlow的 logging capabilities(记录功能)和Monitor API 。例子程序建立在tf.contrib.learn Quickstart的基础之上。如果没有过程记录,其实整个算法就和黑盒子一样什么都看不到,比如有的时候可能模型在很早就已经收敛了或者看看模型是不是early stopping了是很必要的。
一种解决方法是多次使用fit来一步一步评估模型,但是这明显很慢所以并不建议使用,所以 tf.contrib.learn提供了Monitor API帮助我们在训练过程中评估模型,下面内容主要有三个过程:
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。
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对象可以接收下面几个参数(这里不是很明白):
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
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()