从0构建词语to vector

本文介绍了如何利用jieba分词构建词库,通过skim-gram技术和nce损失函数训练词向量模型,以解决搜索时名词组合的问题。重点讲解了如何处理1代表未知词汇的处理和负采样策略,以提升搜索引擎的精确度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

词语embedding

word embedding 已经很常见也很容易实现,但是在各种应用场景下,特别是搜索情况下,一些名词不应该分开embedding,比如。小米 ,安踏。等等。如果用户搜索小米,分词器都是以单个词作为分隔,所以如果对名词embedding需要重新构建分词器。

第一步构建词库

如何构建由词语构成的词库呢,答案是使用jieba分词器提取你的语料库。


tags = jieba.analyse.extract_tags(content, topK=topK)

content是一个字符串,topk 是要提取多少词语,
topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20。
然后再将结果写入文件中,得到词库如下
在这里插入图片描述

第二步,构建词语与数字的映射

再输入到模型前,需要将其转化为数字。如 新款2021 映射后就是[0,1],当然还需要加一些特殊字符,比如cls 这种。
这里采用的是tensorflow 的

vectorize_layer = tf.keras.layers.TextVectorization(
        max_tokens=50005,
        split='whitespace',
        output_mode='int',
        #  output_sequence_length=2,
        #  pad_to_max_tokens=True,
        vocabulary=vocab)

注意分词时vectorize_layer 会以空格分,我们用jieba分词后句子会形成[‘aaaaa’, ‘啊啊啊’, ‘吧吧吧’]这种形式,我们只需用’ '.join(). 连接起来就可以了。

skim-gram

选取一个单词作为context word,然后再在一个滑动窗口下面随机选一个作为target word,这样就构成一个正样本对的训练样本,tensorflow 提供了一个很好用的接口如下

window_size = 2
positive_skip_grams, _ = tf.keras.preprocessing.sequence.skipgrams(
      example_sequence,
      vocabulary_size=vocab_size,
      window_size=window_size,
      negative_samples=0)
print(len(positive_skip_grams))

如果你example_sequence = a b c d e f. window_size =2 那positive_skip_grams可能如下
(2, 3): (a, b)
(55, 2): (c, a)
(43, 22): (d, e)
(11, 22): (f, e)
(3, 43): (b, d)

最终输入模型时如下:
在这里插入图片描述

负样本随机取词库的其他的单词,tensorflow也提供一个方便的接口:

negative_sampling_candidates, _, _ = tf.random.log_uniform_candidate_sampler(
    true_classes=context_class,  # class that should be sampled as 'positive'
    num_true=1,  # each positive skip-gram has 1 positive context class
    num_sampled=num_ns,  # number of negative context words to sample
    unique=True,  # all the negative samples should be unique
    range_max=vocab_size,  # pick index of the samples from [0, vocab_size]
    seed=SEED,  # seed for reproducibility
    name="negative_sampling"  # name of this operation
)

nce损失

模型采用最简单的,直接输出context word,target word,还有负样本的embedding,然后计算nce损失。

class Word2v(tf.keras.Model):
     def __init__(self):
         super(Word2v, self).__init__()
         # self.encode =encoder
         self.embed = tf.keras.layers.Embedding(50005, 128, mask_zero=True)

     def call(self, inputs,laebl,neg):
         input = self.embed(inputs)
         label = self.embed(laebl)
         neg = self.embed(neg)
        
         return input,label,neg

由于我只想构建词语embedding,而词语是很多的,我只提取了5万个词语。单个字的单词词库里没有,所以vectorize_layer过后会有很多1的存在,1代表未知词语,词库里没有,这时计算nce loss 时要把这些带1 的不管是context word中带1,还是target word中带1,这些样本其损失都置0,只计算词库中都有的样本对。损失如下

@tf.function
def loss_nce(cont, target, neg, input, label):
    cont_loss = tf.not_equal(input, 1)
    target_loss = tf.not_equal(label, 1)
    cont_loss = tf.cast(cont_loss, dtype=tf.float32)
    target_loss = tf.cast(target_loss, dtype=tf.float32)
    cont_target = cos_sim(cont, target)
    cont_neg = cos_sim(cont, neg)
    pos = tf.exp(cont_target)
    pos = tf.reshape(pos, [pos.shape[0], 1])
    #     print(pos.shape)
    total = tf.exp(cont_neg)
    n_sum = tf.reduce_sum(total, axis=-1)
    # print(n_sum.shape)
    loss = pos / (pos + n_sum)
    loss = tf.math.log(loss)
    #     print(loss.shape)
    #     print(cont_loss.shape)
    loss = loss * cont_loss * target_loss
    loss = -loss
    loss = tf.reduce_sum(loss)
    return loss

cont为context word embedding后的结果 , target为context word embedding后的结果 , neg为负采样就是随机词库里的词语 embedding后的结果, input 是context word embedding前,也就是模型的输入 ,主要是用他来判断哪里含有1,将对应位置的损失置0, label 就是输入模型的target。

    @tf.function
    def train_step(input, label, neg):
        with tf.GradientTape() as tape:
            cont, target, neg = model(input, label, neg)
            loss_value = loss_nce(cont, target, neg, input, label)
        grads = tape.gradient(loss_value, model.trainable_weights)
        optimizer.apply_gradients(zip(grads, model.trainable_weights))
        return loss_value
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值