使用estimator 构建神经网络
这种方法很适用于单步预测的模型,像RL之类的需要根据网络的输出结果再次组织数据训练的网络,用起来不是很方便。
本文是根据 这个 知乎文章整理而成,为了以后查找方便。数据处理请阅读 这篇博文。
简介
我们在使用神经网络解决一个问题时主要分为为三个阶段:训练,评估,预测。这三个阶段网络模型,数据操作,记录操作相同。
不同的是 使用的数据集和模型的操作。因此我们把相同的部分提取出来,方便代码编写。下面开始!
共用部分:
- input_fn:导入数据 + 数据集的处理
- model_fn:构建模型
三个阶段:
通过调用对应的train(), evaluate(), predict()方法来执行不同的阶段。
优势
- 学习流程:Estimator 封装了对机器学习不同阶段的控制,用户无需不断的为新机器学习任务重复编写训练、评估、预测的代码。可以专注于对网络结构的控制。
- 网络结构:Estimator 的网络结构是在 model_fn 中独立定义的,用户创建的任何网络结构都可以在 Estimator 的控制下进行机器学习。这可以允许用户很方便的使用别人定义好的 model_fn。
- 数据导入:Estimator 的数据导入也是由 input_fn 独立定义的。例如,用户可以非常方便的只通过改变 input_fn 的定义,来使用相同的网络结构学习不同的数据。
一、生成input_fn
送入到Estimator中的input_fn需要是一个函数,而不是具体的数据。所以这里用input_fn_maker来控制如何生成不同的input_fn函数。input_fn在执行时会返回一个字典,里面的key对应着不同的feature(包括label在内)
tfr = TFrecorder()
def input_fn_maker(path, data_info_path, shuffle=False, batch_size = 1, epoch = 1, padding = None):
def input_fn():
# tfr.get_filenames会返回包含path下的所有tfrecord文件的list
# shuffle会让这些文件的顺序打乱
filenames = tfr.get_filenames(path=path, shuffle=shuffle)
dataset=tfr.get_dataset(paths=filenames, data_info=data_info_path, shuffle = shuffle,
batch_size = batch_size, epoch = epoch, padding =padding)
iterator = dataset.make_one_shot_iterator()
return iterator.get_next()
return input_fn
padding_info = ({'image':[784,],'label':[]})
# 生成3个input_fn
test_input_fn = input_fn_maker('mnist_tfrecord/test/', 'mnist_tfrecord/data_info.csv',
padding = padding_info)
train_input_fn = input_fn_maker('mnist_tfrecord/train/', 'mnist_tfrecord/data_info.csv', shuffle=True, batch_size = 512,
padding = padding_info)
# 用来评估训练集用,不想要shuffle
train_eval_fn = input_fn_maker('mnist_tfrecord/train/', 'mnist_tfrecord/data_info.csv', batch_size = 512,
padding = padding_info)
二、model_fn
模型函数必须要有features, mode两个参数,可自己选择加入labels。最后要返回特定的tf.estimator.EstimatorSpec()。
模型有三个阶段都共用的正向传播部分,和由mode值来控制返回不同tf.estimator.EstimatorSpec的三个分支。
def model_fn(features, mode):
# reshape 784维的图片到28x28的平面表达,1为channel数
features['image'] = tf.reshape(features['image'],[-1,28,28,1])
#就在这里构建网络
create_network()
#预测分支:
predictions = {
"image":features['image'],
"conv1_out":conv1,
"pool1_out":pool1,
"conv2_out":conv2,
"pool2_out":pool2,
"pool2_flat_out":pool2_flat,
"dense1_out":dense1,
"logits":logits,
"classes": tf.argmax(input=logits, axis=1),
"labels": features['label'],
"probabilities": tf.nn.softmax(logits, name="softmax_tensor")
}
# 当mode为tf.estimator.ModeKeys.PREDICT时,我们就让模型返回预测的操作
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
# 训练和评估时都会用到loss
loss = tf.losses.sparse_softmax_cross_entropy(labels=features['label'], logits=logits)
# 训练分支
if mode == tf.estimator.ModeKeys.TRAIN:
optimizer = tf.train.AdamOptimizer(learning_rate=1e-3)
train_op = optimizer.minimize(
loss=loss,
# global_step用于记录训练了多少步
global_step=tf.train.get_global_step())
# 返回的tf.estimator.EstimatorSpec根据
return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)
# 注意评估的时候,模型和训练时一样,是一个循环的loop,不断累积计算评估指标。
# 其中有两个局部变量total和count来控制
# 把网络中的某个tensor结果直接作为字典的value是不好用的
# loss的值是始终做记录的,eval_metric_ops中是额外想要知道的评估指标
eval_metric_ops = {"accuracy": tf.metrics.accuracy(labels=features['label'], predictions=predictions["classes"])}
# 不好用:eval_metric_ops = {"probabilities": predictions["probabilities"]}
return tf.estimator.EstimatorSpec(mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
三、创建estimator
注意:送入的model_fn和input_fn一样是一个函数,不可以是model_fn()返回结果,
# model_dir 表示模型要存到哪里
mnist_classifier = tf.estimator.Estimator(
model_fn=model_fn, model_dir="mnist_model_cnn")
训练后的模型参数会保存在model_dir中,随着训练在目录下生成拥有类似下面内容的checkpoint文件。
model_checkpoint_path: "model.ckpt-860"
all_model_checkpoint_paths: "model.ckpt-1"
all_model_checkpoint_paths: "model.ckpt-430"
all_model_checkpoint_paths: "model.ckpt-431"
all_model_checkpoint_paths: "model.ckpt-860"
当你再次运行相同model_dir的Estimator时,它默认会读取model_checkpoint_path: "model.ckpt-860"的模型权重。
封装了保存模型的代码。
四、训练
1. 日志
# 在训练或评估的循环中,每50次print出一次字典中的数值
tensors_to_log = {"probabilities": "softmax_tensor"}
logging_hook = tf.train.LoggingTensorHook(tensors=tensors_to_log, every_n_iter=50)
2. 训练
- hooks:如果不送值,则训练过程中不会显示字典中的数值
- steps:指定了训练多少次,如果不送值,则训练到dataset API遍历完数据集为止
- max_steps:指定了最大训练次数
mnist_classifier.train(input_fn=train_input_fn, hooks=[logging_hook])
如果再次运行,同时没有限制最大训练次数,那么网络会从最后一个checkpoint读取模型权重接着训练。
五、评估
# 训练集
eval_results = mnist_classifier.evaluate(input_fn=train_eval_fn, checkpoint_path=None)
print('train set')
print(eval_results)
# 测试集
# checkpoint_path是可以指定选择那个时刻保存的权重进行评估
eval_results = mnist_classifier.evaluate(input_fn=test_input_fn, checkpoint_path=None)
print('test set')
print(eval_results)
六、预测
predicts =list(mnist_classifier.predict(input_fn=test_input_fn))
predicts是一个list,list中的元素是dictionary
predicts[0].keys()
# 输出为:
dict_keys(['image', 'conv1_out', 'pool1_out', 'conv2_out', 'pool2_out', 'pool2_flat_out', 'dense1_out', 'logits', 'classes', 'labels', 'probabilities'])
七、可视化
tensorboard --logdir=mnist_model_cnn
打开http://localhost:6006
上一篇: Flink window 的类型
下一篇: machine-learning-ex4
推荐阅读
-
Spring Boot2 系列教程(一) | 如何使用 IDEA 构建 Spring Boot 工程
-
使用ASP.NET Core 3.x 构建 RESTful API - 5.1 输入验证
-
详解使用 Docker 构建 LNMP 环境
-
Facebook如何使用"我们"的数据去构建人工智能
-
使用python3构建文件传输的方法
-
实战SpringCloud响应式微服务系列教程(第九章)使用Spring WebFlux构建响应式RESTful服务
-
使用dockerfile构建nginx镜像的方法示例
-
使用create-react-app+react-router-dom+axios+antd+react-redux构建react项目
-
使用Python构建Hopfield网络的教程
-
vue+cordova构建跨平台应用集成并使用Cordova plugin