传统推荐算法(五) FFM模型(3) 反思tf.estimator的小"bug"

写在前面

有关如何使用tf.estimator,请官网中文版:
https://www.tensorflow.org/guide/custom_estimators?hl=zh-cn
有关tf.estimator检查点checkpoints的详细分析,请参考
https://juejin.im/entry/5b0226986fb9a07ac85ab475

按照代码使用tf.estimator.train_and_evaluate进行训练和验证时,出现了一个问题:

我将batch_size设置为891(所有数据),训练epoches设为10,验证时tf.estimator.EvalSpec的steps参数设置为1。

显然,训练会将完整数据集过10遍,验证会将数据集过1遍。
由于训练时间过短,checkpoints只保存train开始和结束时的两个检查点,训练结束后最后的模型会保存起来。
而验证会从最近的检查点中恢复出数据,也就是会加载出训练结束时保存的模型。
然而问题出现了:

训练时显示的accuracy等指标和验证时的结果相差太大:
第八步:INFO:tensorflow:accuracy = 0.68237936, auc = 0.7075512, loss = 1.1856021
第九步:INFO:tensorflow:accuracy = 0.6830808, auc = 0.71318686, loss = 1.138809
第十步:INFO:tensorflow:accuracy = 0.6837511, auc = 0.7182056, loss = 1.095356

验证时结果:
INFO:tensorflow:Saving dict for global step 10: accuracy = 0.72053874, auc = 0.76414853, global_step = 10, loss = 1.054848

按理说,训练和验证用相同的数据集,第十步的结果应该和验证时的结果一样,但是相差这么大,真是不能理解。因此做了一系列测试实验来探究原因。

训练过程中的指标显示

tf.summary

tf.summary.scalar 会在 TRAIN 和 EVAL 模式下向 TensorBoard 提供准确率,代码分别如下:

tf.summary.scalar('accuracy', accuracy[1])
tf.summary.scalar('train_auc', auc[1])

summary_hook = tf.train.SummarySaverHook(1,output_dir="./checkpoint/",
summary_op=tf.summary.merge_all())

return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op, training_hooks=[summary_hook])
metrics = {'accuracy': accuracy}
return tf.estimator.EstimatorSpec(mode, loss=loss, eval_metric_ops=metrics)

训练过程中的accuracy需要使用tf.summary手动记录,并且只使用tf.summary.scalar不行,此处加了个summary_hook才能正常使用。

使用tensorboard观察训练过程中产生的accuracy指标:
20190806191653.png

没有第0步的accuracy,第1步是0.6779,第九步0.6838,第十步0.6869。

log_hook参数

可以在自定义的model_fn中这样使用:

show_dict = {
    "loss":"loss",
    "accuracy":'accuracy/value',
    "auc":'auc/value'
}
log_hook = tf.train.LoggingTensorHook(show_dict, every_n_iter=10)
return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op, training_hooks=[log_hook])

every_n_iter是指迭代几次显示一次。也可以在tf.estimator.train_and_evaluate中这样使用:

    show_dict = {
        "loss":"loss",
        "accuracy":"accuracy/value",
        "auc":"auc/value"
    }
    log_hook = tf.train.LoggingTensorHook(show_dict, every_n_iter=100)

    train_spec = tf.estimator.TrainSpec(input_fn=train_input_fn, hooks=[log_hook])
    eval_spec = tf.estimator.EvalSpec(input_fn=train_input_fn, steps=1)
    tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)

效果是一样的。我们看看这样记录训练指标时,在控制台的结果:

第0步accuracy是0,第1步0.67789,第9步0.6837511,没有第10步的结果。

这和使用tf.summary是一致的。它们这和验证时的结果都不一致:

通过这两个小实验,可以看出不是显示工具的问题。应该是训练时显示的指标本身有问题,训练时显示的accuracy是我们定义的accuracy吗?

阶段训练和直接训练

我将epoches设为10,训练两次。然后直接将epoches设为20,看看两次指标有何变化。

将epoches设为10,第一次训练结果:

第0步是0,只显示到第9步,第10步结果通过tensorboard可以看到。第二次训练结果:
在这里插入图片描述
第10步accuracy为0,这并不影响,每次训练的第一步向来是0。tensorboard中可以看到第一次训练已经获得第10步的值。

但是,第11步的结果是0.7205,这不是第一步验证时的值吗?而且0.7205和0.6838差距太大,它们并不是正常训练过程中的平滑上升,accuracy的显示肯定有问题

看看epoches设为20训练结果:
在这里插入图片描述
第11步显示的accuracy是0.6899296,并不是分阶段训练中显示的0.7205,显然这个显示的指标accuracy并不是我们在程序中定义的accuracy,但肯定和它有关,并且是动态变化的!

tf.estimator的另一种训练方式

实战代码中我们使用tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)这个高阶API,配置好train_spec和eval_spec即可。但是这样使用不够灵活。比如我将数据分为训练集,验证集和测试集三部分,将验证集上表现较好的模型拿出来在测试集上测试,这样就不能用。这时可以使用相对底层些的estimator.trainestimator.evaluate

我们看看如何使用这两个方法来训练和测试数据:

    estimator = tf.estimator.Estimator(
        model_fn = model_fn,
        model_dir=FLAGS.model_path,
        params=hparams,
        config=tf.estimator.RunConfig(
            tf_random_seed=hparams.seed,
            log_step_count_steps=1))

    train_metrics = estimator.evaluate(input_fn=train_input_fn)
    eval_metrics = estimator.evaluate(input_fn=train_input_fn)
    print("train metrics: %r"% train_metrics)
    print("eval metrics: %r"% eval_metrics)

    # 训练数据在模型上的预测准确率
    for i in range(10):
        estimator.train(input_fn=train_input_fn, steps=1, hooks=[log_hook])
        train_metrics = estimator.evaluate(input_fn=train_input_fn)
        # 评估数据在模型上的预测准确率
        eval_metrics = estimator.evaluate(input_fn=train_input_fn)
        print("train metrics: %r"% train_metrics)
        print("eval metrics: %r"% eval_metrics)

此时显示的accuracy肯定是真实的accuracy,得到结果后,我整理了个表格,将其记录为real_acc:
在这里插入图片描述

最开始我觉得,可能是每次计算后都会重新取均值,比如第7步训练结束后,结果应该是:

avg_acc(7) = (avg_acc(6)*6+acc(7))/7

根据这个公式可以计算出真正的acc(7),记为cal_acc,如上图所示。可以发现这个猜想不对,因为第10步计算出的0.7152和真实的0.7205差太多。但已经很接近了。

我又发现cal_acc(7)和real_acc(6)一致,cal_acc(6)和real_acc(5)一致。会不会是这样的呢:

avg_acc(7) = (avg_acc(6)*6+real_acc(6))/7

通过计算,所有数据都满足这个规律!也就是说,第7步显示的accuracy是前6步的accuracy的均值,不是第7步的训练结束后在数据集上获得的实时结果。

但是还有两个问题:
第1步的值哪来的?
我猜想是按一定规律生成的一个比第一个real_acc小的值,这样在tensorboard中显示的就是accuracy是平滑上升。通过改变随机种子的值,重新得到一组数据,我验证了这个猜想。

第10步训练结束后的real_acc会加入计算吗?
我们可以发现,控制台只显示到0.68375,此时第9步和第10步的real_acc都没有加入计算;
而在tensorboard中显示step=10时accuracy=0.6869,此时第9步的real_acc加入了计算;
第10步训练结束后,模型的效果是0.7205,这个结果没有在任何地方显示。不过可以通过estimator.evaluate(input_fn=train_input_fn)自己算啊。

结论

我们发现,使用tf.estimator.train_and_evaluate时,训练过程中实时显示的准确率并非我们代码中定义的accuracy,而是一个取均值的结果,并且这个均值不包括当前step模型的accuracy值。

实际上,结合之前分析的验证集和测试集问题,还是建议使用estimator.trainestimator.evaluate这种相对更底层的方式。tf.estimator.train_and_evaluate这种高阶API,快速搭配模型挺好用,但是不方便自己魔改。

参考

[1] https://www.tensorflow.org/guide/custom_estimators?hl=zh-cn
[2] https://juejin.im/entry/5b0226986fb9a07ac85ab475

公众号

更多精彩内容请移步公众号:推荐算法工程师

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值