欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用estimator 构建神经网络

程序员文章站 2022-07-13 11:42:18
...

这种方法很适用于单步预测的模型,像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