首页 > 编程知识 正文

采样信号表达式,自然语言处理应用案例

时间:2023-05-04 17:52:45 阅读:9528 作者:3399

总结了目录word2vec中出现的背景跳字模型(skip-gram )连续词袋模型(CBOW )负采样的具体训练过程

word2vec出现的背景是,自然语言处理需要对文本进行编码,将语言中的词转换为矢量的形式,我们知道计算机世界里只有数字,所以这是必须要做的工作。 一些人可能认为最常见的编码,如one-hot编码,可能会被用来通过自然语言处理编码文本。 答案是肯定的。 当然可以,但没有效果。 也许没有人会这么做。 因为文本几乎是相关的,构成文本的单词更是相关的,它们之间应该可以通过各自的语言向量来表示它们的关系。 我们知道余弦相似度可以用来表示两个向量的相似性,也可以用余弦相似度来表示单词向量的相似性,但是使用one-hot代码,无论取哪个单词,结果都是0。 也就是说,单词单身男子类似于人类,因此单独热代码不适用于本领域。 所以人们需要构建单身男子的转换。 word2vec应运而生。 跳跃模型(skip-gram )不手写这些基础知识。 这里引用Dive-into-DL-PyTorch上的介绍。

连续词袋模型(CBOW )下

从总结可以看出,这些训练语向量的方法都是使用来测量两个语向量之间的相似度,这是为什么呢? 因为测量两个向量的相似度,不仅需要考虑其角度,还需要考虑其长度。 内积运算就是这样的。

因此,word2vec使用内积相似度是合理的。 在负抽样中,可以看到实际上跳跃模型和连续词袋模型几乎相同。 它们有共同之处,每次计算条件概率时,都必须用分母统计所有词与中心词(或背景词)的向量内积。 虽然我们知道字典非常大,但每次迭代计算条件概率时都要进行如此巨大的内积计算,成本很高。 当然,计算后根据loss修正词向量时,要修正所有词向量。 负采样是为了解决这个问题。 断字模型与连续词袋模型相似,这里以从单词到向量为例介绍这两种方法。 负样本采用背景窗口。 原来不是更新了所有单词和中心词的向量内积吗? 现在不是这样了。 选择窗口大小的背景词来填充窗口,每次用这些背景词和中心词进行向量内积。 而且,根据loss变更参数也只是修改这些单词和中心词的向量,并不是全部都修改。 负采样修正了原目标函数。 给出中心词W c W_{c} Wc的背景窗口,将背景词W o W_{o} Wo出现在其背景窗口中视为事件,计算该事件的概率和

函数与sigmoid激活函数的定义相同。

首先,考虑到使文本序列中所有该事件的联合概率最大化来训练单词向量。 具体而言,考虑给定长度为t的文本序列,将时间步t的词设为W t W^{t} Wt,并且将背景窗口大小设为m,使搭配概率最大化

用sigmoid函数根据内积结果生成相应的概率值是合理的。 内积结果越大概率值越高,即内积相似度越高概率越高,这是合理的。 但是,由于上面的背景窗口只包含真正的背景词,所以所有词的向量相等,值无限大的情况下,上面的联合概率最大化为1。 很明显,这样的语言向量是没有意义的。内积相似度(即向量w*向量u)其实我总是不太清楚这里为什么必须放负样本。 确实联合概率很难达到1,但这也很正常。 一定有损失。 如果有人知道的话,请在评论中告诉我。 那个现在好像不能只考虑背景语。 也可以输入噪音语。 很明显,背景词在中心词附近以正八经出现,而噪声词是我们人为添加的,所以背景词是正类,噪声词是负类。 每次更新单词向量时,都会更新正类和负类以及中心词的单词向量。 这里好像跳转模型的分母考虑了所有词,但是这里只考虑背景词和少量噪声词,噪声词的数量为跳字模型。 负采样其实是指对k个负类,即噪声词进行采样。 条件概率是这样的:

在具体训练过程中,具体来说,训练单词向量,使一个中心词与多个背景词相对应,设定背景窗口的大小s,然后中心词以一句中的第一个s词和最后一个s词为背景词,随机选择k个噪声词。 然后,需要统一大小,但由于中心词可能位于句子的开头或结尾,背景词的数量不一致,因此在所有中心

词对应的最多的背景词+噪声词作为尺寸max_len,然后不满的补0,最终得到了[batch_size,max_len]的训练样本。然后我们要设置label,背景词的标签是1,噪声词是负类,标签是0。对于长度补充的也设置为0,意思是只让背景词和噪声词向量参与运算。还得设置是否是填充的,用mask来表示,对于填充的元素设置为0,背景词和噪声词都是1,之所以设置mask是因为在调用nn.functional.binary_cross_entropy_with_logits时需要设置填充的不参与运算。举个栗子: class SigmoidBinaryCrossEntropyLoss(nn.Module): def __init__(self): # none mean sum super(SigmoidBinaryCrossEntropyLoss, self).__init__() def forward(self, inputs, targets, mask=None): """ input – Tensor shape: (batch_size, len) target – Tensor of the same shape as input """ inputs, targets, mask = inputs.float(), targets.float(), mask.float() res = nn.functional.binary_cross_entropy_with_logits(inputs, targets, reduction="none", weight=mask) return res.mean(dim=1)loss = SigmoidBinaryCrossEntropyLoss()pred = torch.tensor([[1.5, 0.3, -1, 2], [1.1, -0.6, 2.2, 0.4]])# 标签变量label中的1和0分别代表背景词和噪声词label = torch.tensor([[1, 0, 0, 0], [1, 1, 0, 0]])mask = torch.tensor([[1, 1, 1, 1], [1, 1, 1, 0]]) # 掩码变量loss(pred, label, mask) * mask.shape[1] / mask.float().sum(dim=1) 输出tensor([0.8740, 1.2100])

上面的运算过程就相当于:

def sigmd(x): return - math.log(1 / (1 + math.exp(-x)))print('%.4f' % ((sigmd(1.5) + sigmd(-0.3) + sigmd(1) + sigmd(-2)) / 4)) # 注意1-sigmoid(x) = sigmoid(-x)print('%.4f' % ((sigmd(1.1) + sigmd(-0.6) + sigmd(-2.2)) / 3)) 输出0.87401.2100

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。