深入浅出XDL(四):模型训练

上一篇我们分析了XDL的framework的架构设计,了解了XDL的模型构建和运行机制,以及XDL如何将tensorflow作为自己的backend。

本篇继续分析XDL的架构设计,重点关注XDL的参数训练过程。

样例

还是以XDL自带的deepctr(代码位置:xdl/xdl/examples/deepctr/deepctr.py)为例:

def train():
    ...
    emb2 = xdl.embedding('emb2', batch['sparse1'], xdl.TruncatedNormal(stddev=0.001), 8, 1024, vtype='hash')
    
    ## 可以看到,loss op并没有传入train_op,那么train_op的优化目标是什么呢?
    loss = model(batch['deep0'], [emb1, emb2], batch['label'])
    train_op = xdl.SGD(0.5).optimize()
    log_hook = xdl.LoggerHook(loss, "loss:{0}", 10)
    sess = xdl.TrainSession(hooks=[log_hook])
    while not sess.should_stop():
        sess.run(train_op)

@xdl.tf_wrapper()
def model(deep, embeddings, labels):
    input = tf.concat([deep] + embeddings, 1)
    fc1 = tf.layers.dense(
        input, 128, kernel_initializer=tf.truncated_normal_initializer(
            stddev=0.001, dtype=tf.float32))
    fc2 = tf.layers.dense(
        fc1, 64, kernel_initializer=tf.truncated_normal_initializer(
            stddev=0.001, dtype=tf.float32))
    fc3 = tf.layers.dense(
        fc2, 32, kernel_initializer=tf.truncated_normal_initializer(
            stddev=0.001, dtype=tf.float32))
    y = tf.layers.dense(
        fc3, 1, kernel_initializer=tf.truncated_normal_initializer(
            stddev=0.001, dtype=tf.float32))
    loss = tf.losses.sigmoid_cross_entropy(labels, y)
    
    ## model函数返回的是模型的目标loss op, 而不是train op,为什么呢?
    return loss

细心的读者发现,XDL采用的优化器是自己实现的优化器xdl.SGD,没有采用tensorflow的优化器,而且并没有将loss op传入优化器xdl.SGD,再接下来就直接sess.run(train_op)了,那么优化器的优化目标是什么呢?xdl是如何实现参数更新的呢?

关键在于三个地方:

  • 装饰器xdl.tf_wrapper
  • backend op: TFBackendOp
  • 优化器xdl.SGD,继承自xdl.Optimizer

参数更新的步骤

  • 装饰器xdl.tf_wrapper与被装饰函数约定,被装饰函数的第一个返回值必须是模型的loss
  • 由前篇分析,我们知道xdl的sparse参数,会赋值给对应的tensorflow placeholder中然后参与tensorflow graph的运行,xdl的dense类型参数,会赋值给对应的tensorflow variable然后参与tensroflow graph的运行
  • 赋值的方式是通过TFBackendOp的Input
  • 装饰器xdl.tf_wrapper调用函数add_backprop_ops,为之前添加的tensorflow placeholder或则tensorflow variable添加梯度计算op。注意这些梯度计算op都是添加到了tensorflow graph中。
  • TFBackendOp的输出包括了以上的梯度计算Op的输出
  • TFBackendOp传输的梯度是loss关于指定的tensorflow placeholder和tensorflow variable的梯度,实际上,也就是loss关于xdl sparse和dense参数的梯度
  • 有了loss关于xdl参数的梯度后,接下来就是通过几类学习算法,更新XDL参数

总结

我们知道,参数学习的过程,可以大致分为前向计算、梯度后向传递、参数更新三个阶段,从这个角度出发,我们分析一下XDL的参数学习过程:

  • xdl参数由参数服务器存储与维护
  • 参数值经由worker中的PsDensePullOp或PsSparsePullOp,传至worker
  • 参数作为TFBackendOp的Input,feed于相应的tensorflow placeholder或variable
  • 运行TFBackendOp,实际上也就是调用tensorflow runtime驱动tensorflow graph运行
  • tensorflow graph的执行结果,通过TFBackendOp的output输出
  • TFBackendOp的output输出主要是指定的Op以及用户定义的模型loss关于模型参数的梯度
  • 梯度传入特定的xdl update op,具体的update op取决于用户采用的优化器,比如xdl.SGD,xdl.Momentum,xdl.Adagrad,xdl.Ftrl等等
  • xdl update op通过DensePush或SparsePush API,更新xdl参数
  • 至此一次完整的参数学习过程结束
发布了52 篇原创文章 · 获赞 105 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/gaofeipaopaotang/article/details/100984571