原文教程:tensorflow官方教程
记录关键内容与学习感受。未完待续。。
——tf.contrib.learn框架,通过其高级别的EstimatorAPI,使得构建和训练机器学习模型变得容易。Estimator提供了类,你可以快速的实例化并安装普通的模型类别,如回归器和分类器:
LinearClassifier:构建了一个线性分类模型。
LinearRegressor:构建了一个线性回归模型。
DNNClassifier:构建了一个神经网络分类模型。
DNNRegressor:构建了一个神经网络回归模型。
——但是如果tf.contrib.learn预先定义的模型类型中没有可以满足你的需求的怎么办?或许你需要更多对于模型配置的精细控制,例如有能力自定义最优化使用的损失函数,或者是对每一个神经网络层明确不同的激励函数。或许你需要实现一个排名或者推荐系统,这样无论是分类器和回归器都产生预测都不合适。
——这个教程覆盖了如何使用tf.contrib.learn提供的构建模块,来创建你自己的Estimator,这可以基于鲍鱼的身体尺寸,预测他们的年龄。你可以从如下几个方面学习:
实例化一个Estimator。
构建一个自定义模型函数。
使用tf.contrib.layers配置神经网络。
从tf.contrib.losses中选择一个合适的损失函数。
为你的模型定义一个训练操作。
生成并返回预测。
——这个教程假设你已经知道了tf.contrib.learn API的基础知识,例如特征列和fit()操作。如果你之前没有使用过tf.contrib.learn,或者需要补习,你应该首先回顾一下一下教程:
tf.contrib.learn Quickstart:对于使用tf.contrib.learn训练一个神经网络的快速介绍。
TensorFlow Linear Model Tutorial:对特征列的介绍,和使用tf.contrib.learn建立一个线性分类器的综述。
——通过贝壳上的圈数,预测鲍鱼(海螺)的年龄是可能的。然而,因为这个任务要求切割、染色,并且在显微镜下观察贝壳,寻找其他可以预测年龄的测量值是比较合适的。
——Abalone Data Set包含鲍鱼的以下特征数据。
Feature | Description |
---|---|
Length | Length of abalone (in longest direction; in mm) |
鲍鱼的长度(在最长的方向上,单位毫米) | |
Diameter | Diameter of abalone (measurement perpendicular to length; in mm) |
鲍鱼的直径(测量长度的垂线,单位毫米) | |
Height | Height of abalone (with its meat inside shell; in mm) |
鲍鱼的高度(壳里带肉测量,单位毫米) | |
Whole Weight | Weight of entire abalone (in grams) |
整个鲍鱼的重量(单位克) | |
Shucked Weight | Weight of abalone meat only (in grams) |
只有鲍鱼肉的重量(单位克) | |
Viscera Weight | Gut weight of abalone (in grams), after bleeding |
在出血之后,鲍鱼内脏的重量(单位克) | |
Shell Weight | Weight of dried abalone shell (in grams) |
干燥的鲍鱼壳的重量(单位克) |
.
——预测的标签是圈数,作为鲍鱼年龄的代理。
——这篇教程使用了三个数据集。abalone_train.csv包含了有标签的训练数据,共3320个例子。abalone_test.csv包含了有标签的测试数据,共850个例子。abalone_predict包含了7个例子来做出预测。
——为了将鲍鱼数据集喂给模型,你需要下载并加载CSVs到tensorflow的Datasets中。首先,添加一些Python和tensorflow的标准导入模块。
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import tempfile
import urllib
import numpy as np
import tensorflow as tf
tf.logging.set_verbosity(tf.logging.INFO)
——接着定义flags,允许用户通过命令行,随意的指定CSV文件进行训练、测试和预测数据集,并且使用日志:
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_string(
"train_data",
"",
"Path to the training data"
)
flags.DEFINE_string(
"test_data",
"",
"Path to the test data."
)
flags.DEFINE_string(
"predict_data",
"",
"Path to the prediction data"
)
tf.logging.set_verbosity(tf.logging.INFO)
——然后定义函数来加载CSVs(要么从命令行选项中指定文件,要么从tensorflow.org中下载)。
def maybe_download():
'''maybe downloads training data and returns train and test file names'''
if FLAGS.train_data:
train_file_name=FLAGS.train_data
else:
train_file=tempfile.NamedTemporaryFile(delete=False)
urllib.urlretrieve("http://download.tensorflow.org/data/abalone_train.csv",train_file.name)
train_file_name=train_file.name
train_file.close()
print("Training data is download to %s"%train_file.name)
if FLAGS.test_data:
test_file=tempfile.NamedTemporaryFile(delete=False)
urllib.urlretrieve("http://download.tensorflow.org/data/abalone_test.csv",test_file.name)
test_file_name=test_file.name
test_file.close()
print("Test data is downloaded to %s"%test_file_name)
if FLAGS.predict_data:
predict_file_name=FLAGS.predict_data
else:
predict_file=tempfile.NamedTemporaryFile(delete=False)
urllib.urlretrieve("http://download.tensorflow.org/data/abalone_predict.csv",predict_file.name)
predict_file_name=predict_file.name
predict_file.close()
print("Prediction data is downloaded to %s"%predict_file_name)
return train_file_name,test_file_name,predict_file_name
——最终,创建一个main()函数,并且加载鲍鱼csv文件到Datasets:
def main(unused_argv):
# load datasets
abalone_train,abalone_test,abalone_predict=maybe_download()
# training examples
training_set=tf.contrib.learn.datasets.base.load_csv_without_header(
filename=abalone_train,
target_dtype=op.int,
features_dtype=np.float64
)
# test examples
test_set=tf.contrib.learn.datasets.base.load_csv_without_header(
filename=abalone_test,
target_dtype=np.int,
features_dtype=np.float64
)
# set of 7 examples for which to predict abalone ages
prediction_set=tf.contrib.learn.datasets.base.load_csv_without_header(
filename=abalone_predict,
target_dtype=np.int,
features_dtype=np.float64
)
if __name__ == "__main__":
tf.app.run()
——当使用tf.contrib.learn提供的类中的一个类来定义模型的时候,如DNNClassifier,你需要在构建器中提供所有正确的配置参数,如:
my_nn = tf.contrib.learn.DNNClassifier(
feature_columns=[age,height,weight],
hidden_units=[10,10,10],
activation_fn=tf.nn.relu,
dropout=0.2,
n_classes=3,
optimizer="Adam"
)
——你不需要写进一步的代码来知道tensorflow如何训练模型、计算损失或者返回预测值,逻辑已经在DNNClassifier中写好了。
——相比之下,当你从零开始创建你自己的estimator时,constructor对于模型配置只接收两个高级别的参数,model_fn和params。
nn = tf.contrib.learn.Estimator(
model_fn=model_fn,
params=model_params
)
model_fn:一个函数对象,包含了上面所有提到的支持训练、验证和预测的逻辑。你要负责实现这个功能,在下一节中,Constructing the model_fn涵盖了创建一个模型函数的细节。
params:一个可选字典,包含了超参数(例如,learning rate, dropout),传递给model_fn。
——注意,就像tf.contrib.learn预定义的回归器和分类器一样,Estimator初始化程序也需要接收一般的配置参数model_dir和config。
——对于鲍鱼年龄预测器,模型将会接收一个超参数:learning rate。在你的代码最开始的地方,日志配置的后面,定义LEARNING_RATE作为一个常数(下面用粗体标出)。
tf.logging.set_verbosity(tf.logging.INFO)
# Learning rate for the model
LEARNING_RATE = 0.001
——注意:这里LEARNING_RATE设置为0.001,但是你可以在模型训练中,根据需要调整这个值以实现最好的结果。
——接着添加以下的代码到main()函数中,创建包含学习速率的字典model_params,并实例化Estimator:
# set model params
model_params = {"learning_rate":LEARNING_RATE}
# build 2 layer fully connected DNN with 10,10 units respectively
m = tf.contrib.learn.Estimator(
model_fn=model_fn,
params=model_params
)
——对于一个EstimatorAPI模型函数基本的骨架差不多是这样的:
def model_fn(features, targets, mode, params):
# logic to do the following:
# 1. configure the model via tensorflow operations
# 2. define the loss function for training/evaluation
# 3. define the training operation/optimizer
# 4. generate predictions
return predictions,loss,train_op
——model_fn必须接收三个参数:
features:一个字典,包含了通过fit(),,evaluate()或者predict()操作传递给模型的特征。
targets:一个Tensor,包含了通过fit(),,evaluate()或者predict()操作传递给模型的标签。对于predict()调用,可能是空的。因为这些是模型推算出来的值。
mode:以下ModeKeys字符串值中的一个,表明 model_fn是在哪个文本被调用的。
——model_fn也接收params参数,是一个包含训练中使用的超参数的字典(如上述骨架中所展示的那样)。
——函数体执行了以下的任务(细节部分将在后面章节详细描述):
配置模型,这里对于鲍鱼预测器来说,是一个神经网络。
定义损失函数,用来评估模型的预测值与目标值有多匹配。
定义训练的操作,指定optimizer算法来最小化损失函数得到的损失值。
——最终,依赖于model_fn运行的哪个mode,返回以下三个值中的一个或多个:
predictions(在INFER和EVAL模式下需要):一个字典,将你选择的key name映射到包含模型预测值的tensor中。例如(下面的代码):
predictions = {"results":tensor_of_predictions}
loss(在EVAL和TRAIN模式下需要):一个Tensor,包含了标量损失值:模型的损失函数计算的所有输入样本的损失输出(将会在Defining loss for the model部分进行更深一步的讨论)。用在TRAIN模式下,为了错误处理和日志记录。在EVAL模式下自动的包括在一个度量中。
train_op(只在TRAIN模式下需要):一个运行训练每一步的操作。
——构建neural network需要创建和连接输入层,隐藏层和输出层。
——输入层是一些列节点(模型中的每一个特征),接收在features参数下传递给model_fn的特征数据。如果features包含了所有特征数据的(这种情况,x和y数据集直接传递给fit(),evaluate()和predict()操作)n维tensor,然后其可以作为输入层。如果features通过输入函数,包含了传递给模型的feature columns字典,你可以使用tf.contrib.layers的input_from_feature_columns()函数,将其转换为一个输入层的tensor。
input_layer = tf.contrib.layers.imput_from_feature_columns(
columns_to_tensors=features,
feature_columns=[age,height,weight]
)
——如上所示,input_from_feature_columns()需要两个参数:
columns_to_tensors:一个模型的FeatureColumns到包含了与之相关的特征数据的Tensors的映射。这就是实际在features参数上传递给model_fn的值 。
feature_columns:模型中所有FeatureColumns的列表,在以上例子中是年龄、高度和宽度。
——神经网络的输入层必须通过一个激励函数(将前一层的数据做一个非线性转换),连接一个或多个隐藏层。隐藏层连接到输出层,即模型的最后一层。tf.contrib.layers对于构建全连接层,提供以下方便的函数:
hidden_layer = tf.contrib.layers.relu(inputs=input_layer,num_outputs=10)
second_hidden_layer = tf.contrib.layers.rulu6(inputs=hidden_layer,num_outputs=20)
output_layer = tf.contrib.layers.linear(inputs=second_hidden_layer,num_outputs=3)
——所有这些函数都是更一般化函数fully_connected()的一部分,也可以使用其他激励函数添加到全连接层中,如:
output_layer = tf.contrib.layers.fully_connected(
inputs=second_hidden_layer,
num_outputs=10,
activation_fn=tf.sigmoid
)
——以上代码创建了一个神经网络层output_layer,使用sigmoid激励函数(tf.sigmoid)全连接于second_hidden_layer层。对于tensorflow中预先定义的可用的激励函数,可以参见API docs。
将它们所有放在一起,以下代码构建了一个完整的鲍鱼预测器的神经网络。
def model_fn(features,targets,mode,params):
# model function for estimator
# connect the first hidden layer to input layer(features)
# with relu activation
first_hidden_layer = tf.contrib.layers.relu(features,10)
# connect the second hidden layer to first hidden layer with relu
second_hidden_layer = tf.contrib.layers.relu(first_hidden_layer,10)
# connect the output layer to second hidden layer(no activation fn)
output_layer = tf.contrib.layers.linear(second_hidden_layer,1)
# reshape output layer to 1-dim tensor to return predictions
predictions = tf.reshape(output_layer,[-1])
predictions_dict = {"age":predictions}
——这里,因为你直接将鲍鱼数据集,通过x,y参数,传递给fit(),,evaluate()和predict()操作,输入层是features Tensor传递给model_fn。网络包含了两层隐藏层,每层10个节点,使用了RelU激励函数。输出层没有使用激励函数,并且reshaped为一个一维tensor来捕捉模型的预测,并存储在predictions_dict中。
——model_fn 必须返回一个tensor,包含loss值,这量化了在训练和验证期间,模型对于目标值预测的好坏程度。tf.contrib.losses模块提供了方便的函数,可以使用不同的度量来计算损失值。
absolute_difference(predictions, targets):使用absolute-difference
formula(也称之为L1 loss)计算损失。
log_loss(predictions, targets):使用logistic loss forumula(通常用于逻辑回归)计算损失。
mean_squared_error(predictions, targets):使用mean
squared error(均方误差,也称之为 L2 loss)计算损失。
——-以下对loss的定义,使用mean_squared_error(),添加到鲍鱼model_fn中:
# calculate loss using mean squared error
loss = tf.contrib.losses.mean_squared_error(predictions,targets)
——对于tf.contrib.loss更多损失函数和细节参见API docs。
——训练操作定义了,在模型拟合训练数据时,tensorflow将使用的优化算法。特别是在训练时,目标是最小化损失。tf.contrib.layers API提供了optimize_loss函数,只需返回一个训练操作就可以。optimize_loss需要四个参数:
loss:model_fn计算的损失值(参见Defining Loss for the Model)。
global_step:一个整型Variable,表示每一次模型训练增加的步数,TensorFlow使用get_global_step()函数可以很容易的创建或者增加。
learning_rate:在训练时优化算法使用的learning rate超参数,也称之为步的大小。
optimizer:优化算法在训练期间使用。optimizer可以接受一下string值的任何一个,这些值代表了tf.contrib.layers.optimizers预先定义的优化算法。
SGD:gradient descent(tf.train.GradientDescentOptimizer)的实现。
Adagrad:AdaGrad optimization algorithm(tf.train.AdagradOptimizer)的实现。
Adam:Adam optimization algorithm(tf.train.AdamOptimizer)的实现。
Ftrl:FTRL-Proximal algorithm(tf.train.FtrlOptimizer)的实现。
Momentum:使用momentum(tf.train.MomentumOptimizer)的随机梯度下降的实现。
RMSProp:RMSprop algorithm(tf.train.RMSPropOptimizer)的实现。
——注意:optimize_loss函数提供额外的可选参数来进一步地设置优化器,例如实现decay。查看API docs获取更多信息。
——以下代码对鲍鱼model_fn,使用Defining Loss for the Model的损失值,定义了一个训练操作,学习速率使用params参数传递给函数,使用SGD优化器。对于global_step,在tf.contrib.framework中比较方便的函数get_global_step()负责生成一个整型变量。
train_op = tf.contrib.layers.optimize_loss(
loss=loss,
global_step=tf.contrib.framework.get_global_step(),
learning_rate=params['learning_rate'],
optimizer="SGD"
)
——最后,来完整鲍鱼年龄预测器的model_fn。以下代码配置了神经网络,定义了损失和训练操作,并且返回predictions_dict,loss和 train_op。
def model_fn(features,targets,mode,params):
# model function for estimator
# connect the first hidden layer to input layer(features)
# with relu activation
first_hidden_layer = tf.contrib.layers.relu(features,10)
# connect the second hidden layer to first hidden layer with relu
second_hidden_layer = tf.contrib.layers.relu(first_hidden_layer,10)
# connect the output layer to second hidden layer(no activation fn)
output_layer = tf.contrib.layers.linear(second_hidden_layer,1)
# reshape output layer to 1-dim tensor to return predictions
predictions = tf.reshape(output_layer,[-1])
predictions_dict = {"age":predictions}
# calculate loss using mean squared error
loss = tf.contrib.losses.mean_squared_error(predictions,targets)
train_op = tf.contrib.layers.optimize_loss(
loss=loss,
global_step=tf.contrib.framework.get_global_step(),
learning_rate=params['learning_rate'],
optimizer="SGD"
)
return predictions_dict, loss, train_op
——你已经对鲍鱼预测器实例化了Estimator,并在model_fn定义了它的行为,剩下的就是训练,验证和预测了。
——以下代码添加在main()函数的最后,神经网络拟合数据并验证准确度。
# fit
nn.fit(x=training_set.data,y=trainging_set.target,steps=5000)
# score accuracy
ev = nn.evaluate(x=test_set.data,y=test_set.target,steps=1)
loss_score = ev["loss"]
print("loss: %s" % loss_score)
——运行代码,你将看到以下结果。
——上面的loss分数是,当在ABALONE_TEST数据集上运行时,model_fn 返回的均方误差。
——为了在ABALONE_PREDICT数据集上预测年龄,添加以下代码到main()函数中:
# print out predictions
predictions = m.predict(
x=prediction_set.data,
as_iterable=True
)
for i,p in enumerate(predictions):
print("Prediction %s: %s" % (i+1,p["ages"]))
——这里,predict()函数返回的结果在predictions 作为一个迭代,for循环enumerates,并且打印结果。重新运行代码,你将看到如下额外的输出:
——最后,完整代码如下:
# step 1 import standard module
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import tempfile
import urllib
import numpy as np
import tensorflow as tf
tf.logging.set_verbosity(tf.logging.INFO)
# learning rate for the model
LEARNING_RATE = 0.001
# step 2 define flag, specfied csv files
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_string(
"train_data",
"",
"Path to the training data"
)
flags.DEFINE_string(
"test_data",
"",
"Path to the test data."
)
flags.DEFINE_string(
"predict_data",
"",
"Path to the prediction data"
)
# step 3 define function to download csv files
def maybe_download():
'''maybe downloads training data and returns train and test file names'''
if FLAGS.train_data:
train_file_name=FLAGS.train_data
else:
train_file=tempfile.NamedTemporaryFile(delete=False)
urllib.urlretrieve("http://download.tensorflow.org/data/abalone_train.csv",train_file.name)
train_file_name=train_file.name
train_file.close()
print("Training data is download to %s"%train_file.name)
if FLAGS.test_data:
test_file_name=FLAGS.test_data
else:
test_file=tempfile.NamedTemporaryFile(delete=False)
urllib.urlretrieve("http://download.tensorflow.org/data/abalone_test.csv",test_file.name)
test_file_name=test_file.name
test_file.close()
print("Test data is downloaded to %s"%test_file_name)
if FLAGS.predict_data:
predict_file_name=FLAGS.predict_data
else:
predict_file=tempfile.NamedTemporaryFile(delete=False)
urllib.urlretrieve("http://download.tensorflow.org/data/abalone_predict.csv",predict_file.name)
predict_file_name=predict_file.name
predict_file.close()
print("Prediction data is downloaded to %s"%predict_file_name)
return train_file_name,test_file_name,predict_file_name
def model_fn(features,targets,mode,params):
# model function for estimator
# connect the first hidden layer to input layer(features)
# with relu activation
first_hidden_layer = tf.contrib.layers.relu(features,10)
# connect the second hidden layer to first hidden layer with relu
second_hidden_layer = tf.contrib.layers.relu(first_hidden_layer,10)
# connect the output layer to second hidden layer(no activation fn)
output_layer = tf.contrib.layers.linear(second_hidden_layer,1)
# reshape output layer to 1-dim tensor to return predictions
predictions = tf.reshape(output_layer,[-1])
predictions_dict = {"ages":predictions}
# calculate loss using mean squared error
loss = tf.contrib.losses.mean_squared_error(predictions,targets)
train_op = tf.contrib.layers.optimize_loss(
loss=loss,
global_step=tf.contrib.framework.get_global_step(),
learning_rate=params['learning_rate'],
optimizer="SGD"
)
return predictions_dict, loss, train_op
# step 4 define main
def main(unused_argv):
# load datasets
abalone_train,abalone_test,abalone_predict=maybe_download()
# training examples
training_set=tf.contrib.learn.datasets.base.load_csv_without_header(
filename=abalone_train,
target_dtype=np.int,
features_dtype=np.float64
)
# test examples
test_set=tf.contrib.learn.datasets.base.load_csv_without_header(
filename=abalone_test,
target_dtype=np.int,
features_dtype=np.float64
)
# set of 7 examples for which to predict abalone ages
prediction_set=tf.contrib.learn.datasets.base.load_csv_without_header(
filename=abalone_predict,
target_dtype=np.int,
features_dtype=np.float64
)
# set model params
model_params = {"learning_rate":LEARNING_RATE}
# build 2 layer fully connected DNN with 10,10 units respectively
m = tf.contrib.learn.Estimator(model_fn=model_fn,params=model_params)
# fit
m.fit(x=training_set.data,y=training_set.target,steps=5000)
# score accuracy
ev = m.evaluate(x=test_set.data,y=test_set.target,steps=1)
loss_score = ev["loss"]
print("loss: %s" % loss_score)
# print out predictions
predictions = m.predict(x=prediction_set.data,as_iterable=True)
for i,p in enumerate(predictions):
print("Prediction %s: %s" % (i+1,p["ages"]))
if __name__ == "__main__":
tf.app.run()
——对于建立Estimators一些额外的信息,参见以下API docs:
——以上,结束。