Tensorflow的负采样函数Sampled softmax loss踩坑之旅

版权声明:本文章为原创,未经许可不得转载 https://blog.csdn.net/weixin_41864878/article/details/89032038

谷歌16年出的论文《Deep Neural Networks for Youtube Recommendation》中提到文章采用了负采样的思想来进行extreme multiclass分类任务
我的tensorflow实现已上传CSDN资源https://download.csdn.net/download/weixin_41864878/11107472
Tensorflow提供了两种负采样,分别是NCE loss 和Sampled softmax loss,两者最大的区别就是针对的任务不同,代码实现上两者也只有最后的loss函数不同,两者用的采样函数及算logits方法都相同
NCE loss

  sampled_losses = sigmoid_cross_entropy_with_logits(
      labels=labels, logits=logits, name="sampled_losses")

Sampled softmax loss

  labels = array_ops.stop_gradient(labels, name="labels_stop_gradient")
  sampled_losses = nn_ops.softmax_cross_entropy_with_logits_v2(
      labels=labels, logits=logits)

显然,NCE能针对多标签,在NLP任务上应用比较多, 而后者针对单个标签分类任务
实验中我采用了后者
sampled softmax 原文:On Using Very Large Target Vocabulary for Neural Machine Translation
在这里强调TF中的采样方式,有4种:

function 采样方式
1 log_uniform_candidate_sampler 只能用于标签顺序和出现频率成反比的情况,因此需要清洗数据,重新进行标签映射
2 learned_unigram_candidate_sampler 适用于不知道标签分布的任何情况
3 uniform_candidate_sampler 均匀采样
4 fixed_unigram_candidate_sampler 允许用户指定概率

代码中默认的是第一种,如果要更改需要自己重写
根据官方的指导代码,我构造了如下代码:
(具体参数请移步官方代码~)

def net_factory():
    input = input
    net = net() #网络结构,注意这里是softmax输出前的embedding
    output = tf.nn.softmax()
    ##
    weights = tf.Variable(tf.truncated_normal([num_class, embedding_size],
                      stddev=1.0 / math.sqrt(embedding_size)))
    biases = tf.Variable(tf.zeros([num_class]))
    loss = tf.reduce_mean(tf.nn.sampled_softmax_loss(weights, biases, train_labels, net,
                 num_sampled, num_class))

首先出现的第一个错误是device的错误,原因是这个计算不能在GPU上算,必须在CPU中计算(反正在GPU跑不起来,如果理解有误欢迎指正)
因此在session的config中写入:

config = tf.ConfigProto(allow_soft_placement = True)

或者网络定义之中,在loss计算之前加上with tf.device(’/cpu:0’)
但是出现了不能在tf.train.GradientDescentOptimizer(learning_rate).compute_gradients(loss)中进行计算,报错是梯度名字为None,但是能用tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)计算,但是由于我还有正则损失需要计算,必须解决这个问题,于是在查阅资料之后,修改代码如下:
(里面self我复制过来懒得修改了。。)

    self.weights = tf.get_variable('soft_weight',[self.item_classes, self.embedding_size], initializer=tf.variance_scaling_initializer())
    biases = tf.get_variable('soft_biases', initializer=tf.zeros([self.item_classes]), trainable=False)
    loss = tf.reduce_mean(tf.nn.sampled_softmax_loss(weights, biases, train_labels, net,
                 num_sampled, num_class))

于是就可以愉快的和别的loss相加计算梯度了,我看到github上开源项目基本上都是第一种方式写的,如果程序中是单一loss基本就用通用形式就可以了

负样本个数的影响:目前来看对loss计算来说基本没有影响,设置为100和10的时候loss值有显著变化,个数为10时loss下降较快

参考:
github代码段:https://github.com/tensorflow/tensorflow/blob/r1.10/tensorflow/python/ops/nn_impl.py
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/ops/candidate_sampling_ops.py
官方指导手册demo:https://docs.pythontab.com/tensorflow/tutorials/word2vec/#_3
http://www.algorithmdog.com/tf-candidate-sampling
https://blog.csdn.net/u010223750/article/details/69948463
https://blog.csdn.net/wuzqChom/article/details/77073246

猜你喜欢

转载自blog.csdn.net/weixin_41864878/article/details/89032038
今日推荐