pytorch之wod2vec实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytorch之wod2vec实现相关的知识,希望对你有一定的参考价值。

参考技术A 介绍在语料库上训练的词嵌入模型的实现。我们将会介绍一些实现中的技巧,如二次采样(subsampling)。

首先引入实验所需的包或模块。

PTB(Penn Tree Bank)是一个常用的小型语言料库[1]。它采样自《华尔街日报》的文章,包括训练集,验证集和测试集。我们将在PTB训练集上训练词嵌入模型。该数据集的每一行作为一个句子。句子中的每个词由空格隔开。

确保 ptb.train.txt 已经放在了文件夹 ../../data/ptb 下。

对于数据集的前3个句子,打印每个句子的词数和前5个词。这个数据集中句尾符为 “<EOS>”,生僻词全用 “<UNK>” 表示,数字则被替换变成“ N”。

输出:

为了计算简单,我们只保留在数据集中至少出现5次的词。

然后将词映射到整体索引。

文本数据中一般会出现一些生成词,如英文中的“ the”“ a”和“ in”。通常来说,在一个背景窗口中,一个词(如“ chip”)和可能频词(如“微处理器”)同时出现比和较高频词(如“ the”)同时出现对训练词嵌入模型更有益。因此,训练词嵌入模型时可以对词进行二次采样[2]。我,和越高频的词被最大化的概率值。

可以看到,二次采样后我们去掉了一半左右的词。下面比较一个词在二次采样前后出现在数据集中的次数。可见穿透词“ the”的采样率不足1/20。

但补充词“ join”则完整地保留了下来。

我们将与中心词距离不超过背景窗口大小的词作为它的背景词。下面定义函数提取所有中心词和它们的背景词。它每次在多个1和 max_window_size (最大背景窗口)之间随机均匀采样一个整体作为背景窗口大小。

下面我们创建一个人工数据集,其中包含词数分别为7和3的两个句子。设最大背景窗口为2,打印所有中心词和它们的背景词。

输出:

实验中,我们设最大背景窗口大小为5。下面提取数据集中所有的中心词及其背景词。

词频与总词频之比的0.75次方[2]。

我们从数据集中提取所有中心词 all_centers ,以及每个中心词对应的背景词 all_contexts 和噪声词 all_negatives 。我们先定义一个 Dataset 类。

为了避免填充项对损失函数计算的影响,我们构造了变量变量 masks ,其每一个元素分别与连结后的背景词和噪声词 contexts_negatives 中的元素一一对应。当 contexts_negatives 变量中的某个元素为填充项时,相同位置的变量变量 masks 中的元素取0,否则取1。为了区分正类和负类,我们还需要将 contexts_negatives 变量中的背景词和噪声词区分开来。取决于变量的构造思路,我们只需创建与 contexts_negatives 变量形状相同的标签变量 labels ,变为与背景词(正类)对应的元素设置1,其余清0。

下面的我们实现这个小批量重新编码函数 batchify 。它的小批量输入 data 是一个长度为批量大小的列表,其中每个元素分别包含中心词 center ,背景词 context 和噪声词 negative 。该函数返回的小批量数据符合我们需要的格式,例如,包含了预设变量。

我们用刚刚定义的 batchify 函数指定 DataLoader 实例中小批量的读取方式,然后打印读取的第一个批量中各个变量的形状。

输出:

我们将通过使用嵌入层和小批量乘法来实现跳字模型。它们也常常用于实现其他自然语言处理的应用。

获取词嵌入的层称为嵌入层,在PyTorch中可以通过创建 nn.Embedding 实例得到。嵌入层的权重是一个矩阵,其行数为字典大小( num_embeddings ),列数为每个词向量的尺寸( embedding_dim )。字典大小为20,词向量的尺寸为4。

输出:

下面我们将形状为(2,3)的索引输入进嵌入层,由于词向量的尺寸为4,我们得到形状为(2,3,4)的词向量。

输出:

输出:

在前向计算中,跳字模型的输入包含中心词索引 center 以及连结的背景词与噪声词索引 contexts_and_negatives 。其中 center 变量的形状为(批量,1),而 contexts_and_negatives 变量的形状为(批量,, max_len )。一个变量先通过词嵌入层分别由词索引变换为词向量,再通过小批量复制得到形状为(批量,1, max_len )的输出。输出中的每个元素是中心词向量与背景词向量或噪声词向量的内积。

在训练词嵌入模型之前,我们需要定义模型的损失函数。

根据负采样中损失函数的定义,我们可以使用二元交叉熵损失函数,如下定义 SigmoidBinaryCrossEntropyLoss 。

值得一提的是,我们可以通过分配变量指定小批量中参与损失函数计算的部分预测值和标签:当中断为1时,相应位置的预测值和标签将参与损失函数的计算;当掩盖编码为0时,相应位置的预测值和标签则不参与损失函数的计算。我们之前提到,变量变量可用于避免填充项对损失函数计算的影响。

输出:

作为比较,下面纠正零开始实现二元交叉熵损失函数的计算,并根据变量变量 mask 计算变量为1的预测值和标签的损失。

输出:

我们分别构造中心词和背景词的嵌入层,将超参数词向量维度 embed_size 设置成100。

下面定义的训练函数。由于填充项的存在,与之前的训练函数划分,损失函数的计算稍有不同。

现在我们就可以使用负采样训练跳字模型了。

输出:

训练好词嵌入模型之后,我们可以根据两个词向量的余弦相似度表示词与词之间在语义上的相似度。可以看到,使用训练得到的词嵌入模型时,与词“ chip”语义最接近的词大多与芯片有关。

输出:

focal loss 之 pytorch 实现

# coding=utf-8
import torch
import torch.nn.functional as F

from torch import nn
from torch.nn import CrossEntropyLoss
import numpy as np

class MultiFocalLoss(nn.Module):
    """
    Focal_Loss= -1*alpha*((1-pt)**gamma)*log(pt)
    Args:
        num_class: number of classes
        alpha: class balance factor shape=[num_class, ]
        gamma: hyper-parameter
        reduction: reduction type
    """

    def __init__(self, num_class, alpha=None, gamma=2, reduction='mean'):
        super(MultiFocalLoss, self).__init__()
        self.num_class = num_class
        self.gamma = gamma
        self.reduction = reduction
        self.smooth = 1e-4
        self.alpha = alpha
        if alpha is None:
            self.alpha = torch.ones(num_class, ) - 0.5
        elif isinstance(alpha, (int, float)):
            self.alpha = torch.as_tensor([alpha] * num_class)
        elif isinstance(alpha, (list, np.ndarray)):
            self.alpha = torch.as_tensor(alpha)
        if self.alpha.shape[0] != num_class:
            raise RuntimeError('the length not equal to number of class')

    def forward(self, logit, target):
        """
        N: batch size C: class num
        :param logit: [N, C] 或者 [N, C, d1, d2, d3 ......]
        :param target: [N] 或 [N, d1, d2, d3 ........]
        :return: 
        """
        # assert isinstance(self.alpha,torch.Tensor)\\
        alpha = self.alpha.to(logit.device)
        prob = F.softmax(logit, dim=1)

        if prob.dim() > 2:
            # used for 3d-conv:  N,C,d1,d2 -> N,C,m (m=d1*d2*...)
            N, C = logit.shape[:2]
            prob = prob.view(N, C, -1)
            prob = prob.transpose(1, 2).contiguous()  # [N,C,d1*d2..] -> [N,d1*d2..,C]
            prob = prob.view(-1, prob.size(-1))  # [N,d1*d2..,C]-> [N*d1*d2..,C]

        ori_shp = target.shape
        target = target.view(-1, 1)

        prob = prob.gather(1, target).view(-1) + self.smooth  # avoid nan
        logpt = torch.log(prob)
        # alpha_class = alpha.gather(0, target.squeeze(-1))
        alpha_weight = alpha[target.squeeze().long()]
        loss = -alpha_weight * torch.pow(torch.sub(1.0, prob), self.gamma) * logpt

        if self.reduction == 'mean':
            loss = loss.mean()
        elif self.reduction == 'none':
            loss = loss.view(ori_shp)

        return loss

if __name__ == "__main__":
    batch_size, seq_len, num_class = 1, 2, 3
    
    # 二维
    Loss_Func = MultiFocalLoss(num_class=num_class, alpha=1, gamma=2, reduction='mean')
    logits = torch.rand(batch_size, num_class, requires_grad=True)  # (batch_size, num_classes)
    targets = torch.randint(0, num_class, size=(batch_size,))  # (batch_size, )
    loss = Loss_Func(logits, targets)
    print(loss)
    loss.backward()


    # 多维
    Loss_Func = MultiFocalLoss(num_class=num_class, gamma=2.0, reduction='mean')
    logits = torch.rand(batch_size, seq_len, num_class, requires_grad=True)  # (batch_size, num_classes)
    targets = torch.randint(0, num_class, size=(batch_size, seq_len))  # (batch_size, )

    loss = Loss_Func(logits.permute(0,2,1), targets)  # 类别必须放在第二个维度
    print(loss)
    loss.backward()

以上是关于pytorch之wod2vec实现的主要内容,如果未能解决你的问题,请参考以下文章

教程 | PyTorch内部机制解析:如何通过PyTorch实现Tensor

PT之Transformer:基于PyTorch框架利用Transformer算法针对IMDB数据集实现情感分类的应用案例代码解析

机器学习之神经网络的公式推导与python代码(手写+pytorch)实现

PyTorch 之 神经网络 Mnist 分类任务

从基础概念到实现,小白如何快速入门PyTorch

(机器学习深度学习常用库框架|Pytorch篇)第三节:Pytorch之torchvision详解