TensorFlow实战 ——tf.nn.nce_loss()函数解析

本文转载于简书,如有冒犯,请联系,望见谅!

转自地址:https://www.jianshu.com/p/fab82fa53e16

先看看tensorflow的nce-loss的API:

  1. def nce_loss(weights, biases, inputs, labels, num_sampled, num_classes,

  2. num_true=1,

  3. sampled_values=None,

  4. remove_accidental_hits=False,

  5. partition_strategy="mod",

  6. name="nce_loss")

假设nce_loss之前的输入数据是K维的,一共有N个类,那么

weight.shape = (N, K)

bias.shape = (N)

inputs.shape = (batch_size, K)

labels.shape = (batch_size, num_true)

num_true : 实际的正样本个数

num_sampled: 采样出多少个负样本

num_classes = N

sampled_values: 采样出的负样本,如果是None,就会用不同的sampler去采样。待会儿说sampler是什么。

remove_accidental_hits: 如果采样时不小心采样到的负样本刚好是正样本,要不要干掉

partition_strategy:对weights进行embedding_lookup时并行查表时的策略。TF的embeding_lookup是在CPU里实现的,这里需要考虑多线程查表时的锁的问题

nce_loss的实现逻辑如下:

_compute_sampled_logits: 通过这个函数计算出正样本和采样出的负样本对应的output和label

sigmoid_cross_entropy_with_logits: 通过 sigmoid cross entropy来计算output和label的loss,从而进行反向传播。这个函数把最后的问题转化为了num_sampled+num_real个两类分类问题,然后每个分类问题用了交叉熵的损伤函数,也就是logistic regression常用的损失函数。TF里还提供了一个softmax_cross_entropy_with_logits的函数,和这个有所区别。

再来看看TF里word2vec的实现,他用到nce_loss的代码如下

  1. loss=tf.reduce_mean(tf.nn.nce_loss(weights=nce_weights,

  2. biases=nce_biases,

  3. labels=train_labels,

  4. inputs=embed,

  5. num_sampled=num_sampled,

  6. num_classes=vocabulary_size))

可以看到,它这里并没有传sampled_values,那么它的负样本是怎么得到的呢?继续看nce_loss的实现,可以看到里面处理sampled_values=None的代码如下

  1. if sampled_values is None:

  2. sampled_values = candidate_sampling_ops.log_uniform_candidate_sampler(

  3. true_classes=labels,

  4. num_true=num_true,

  5. num_sampled=num_sampled,

  6. unique=True,

  7. range_max=num_classes)

所以,默认情况下,他会用log_uniform_candidate_sampler去采样。那么log_uniform_candidate_sampler是怎么采样的呢?他的实现在这里:

1、会在[0, range_max)中采样出一个整数k

2、P(k) = (log(k + 2) - log(k + 1)) / log(range_max + 1)

可以看到,k越大,被采样到的概率越小。那么在TF的word2vec里,类别的编号有什么含义吗?看下面的代码:

  1. def build_dataset(words):

  2. count = [['UNK', -1]]

  3. count.extend(collections.Counter(words).most_common(vocabulary_size - 1))

  4. dictionary = dict()

  5. for word, _ in count:

  6. dictionary[word] = len(dictionary)

  7. data = list()

  8. unk_count = 0

  9. for word in words:

  10. if word in dictionary:

  11. index = dictionary[word]

  12. else:

  13. index = 0 # dictionary['UNK']

  14. unk_count += 1

  15. data.append(index)

  16. count[0][1] = unk_count

  17. reverse_dictionary = dict(zip(dictionary.values(), dictionary.keys()))

  18. return data, count, dictionary, reverse_dictionary

可以看到,TF的word2vec实现里,词频越大,词的类别编号也就越小。因此,在TF的word2vec里,负采样的过程其实就是优先采词频高的词作为负样本。

在提出负采样的原始论文中, 包括word2vec的原始C++实现中。是按照热门度的0.75次方采样的,这个和TF的实现有所区别。但大概的意思差不多,就是越热门,越有可能成为负样本。

 

作者:xlvector

链接:https://www.jianshu.com/p/fab82fa53e16

來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值