word2vec

1 基于推理的方法和神经网络

用向量表示单词的方法主要有两种:一种是基于技术的方法;另一种是基于推理的方法。

1.1 基于计数的方法的问题

基于计数的方法根据一个单词周围的单词的出现频数来表示该单词。具体来说先生成所有单词的共现矩阵,再对这个矩阵进行SVD,以获得密集向量(单词的分布式表示)。

但是基于计数的方法在处理大规模语料时会出现问题。如果词汇量超过100万个,这种方法就要生成一个100万*100万的庞大矩阵,对如此庞大矩阵执行svd是不现实的,(对n*n矩阵svd复杂度O(n^3))。

而基于推理的方法使用神经网络,通常在mini-batch数据上进行学习,一次只需要看一部分学习数据,并反复更新权重。

基于推理的方法:输入上下文,模型输出各个单词的出现概率。

2 简单的word2vec

将神经网络嵌入下图所示的模型中。
在这里插入图片描述

2.1 CBOW模型的推理

CBOW=continuous bags-of-words

CBOW有两个输入层,经过中间层到达输出层。这里,从输入层到中间层的变换由相同的全连接层(权重为Win)完成,从中间层到输出层神经元的变换由另一个全连接层(权重为Wout)完成。中间层的神经元是各个输入层经全连接层变换后得到的值的平均。

权重Win的各行保存着各个单词的分布式表示。通过反复学习,不断更新各个单词的分布式表示,以正确地从上下文预测出应当出现的单词。令人惊讶的是,如此获得的响亮很好的对单词含义进行了编码。这就是word2vec的全貌。

2.2 CBOW模型的学习

这里我们处理的模型是一个进行多类别分类的神经网络。因此对其进行学习只是使用一下Sofmax函数和交叉熵误差。首先,使用Softmax函数将得分转化为概率,再求这些概率和监督标签之间的交叉熵误差,并将其作为损失进行学习。

2.3 word2vec的权重和分布式表示

输入侧和输出侧的权重都可以被视为单词的分布式表示。就word2vec(特别是skip-gram模型)而言,一般只使用输入侧的权重。

3 学习数据的准备

word2vec中使用的神经网络的输入是上下文,它的正确标签就是被这些上下文包围在中间的单词,即目标词。也就是说,我门要做的事情是,当向神经网络输入上下文时,使目标词的出现的概率高。

从语料库生成上下文contexts和目标词target。

ef create_contexts_target(corpus, window_size=1):
    '''生成上下文和目标词

    :param corpus: 语料库(单词ID列表)
    :param window_size: 窗口大小(当窗口大小为1时,左右各1个单词为上下文)
    :return:
    '''
    target = corpus[window_size:-window_size]
    contexts = []

    for idx in range(window_size, len(corpus)-window_size):
        cs = []
        for t in range(-window_size, window_size + 1):
            if t == 0:
                continue
            cs.append(corpus[idx + t])
        contexts.append(cs)

    return np.array(contexts), np.array(target)

将上下文和目标词转化为one-hot表示。

def convert_one_hot(corpus, vocab_size):
    '''转换为one-hot表示

    :param corpus: 单词ID列表(一维或二维的NumPy数组)
    :param vocab_size: 词汇个数
    :return: one-hot表示(二维或三维的NumPy数组)
    '''
    N = corpus.shape[0]

    if corpus.ndim == 1:
        one_hot = np.zeros((N, vocab_size), dtype=np.int32)
        for idx, word_id in enumerate(corpus):
            one_hot[idx, word_id] = 1

    elif corpus.ndim == 2:
        C = corpus.shape[1]
        one_hot = np.zeros((N, C, vocab_size), dtype=np.int32)
        for idx_0, word_ids in enumerate(corpus):
            for idx_1, word_id in enumerate(word_ids):
                one_hot[idx_0, idx_1, word_id] = 1

    return one_hot

4 CBOW模型的实现

import sys
sys.path.append('..')
import numpy as np
from common.layers import MatMul, SoftmaxWithLoss


class SimpleCBOW:
    def __init__(self, vocab_size, hidden_size):
        V, H = vocab_size, hidden_size

        # 初始化权重
        W_in = 0.01 * np.random.randn(V, H).astype('f')
        W_out = 0.01 * np.random.randn(H, V).astype('f')

        # 生成层
        self.in_layer0 = MatMul(W_in)
        self.in_layer1 = MatMul(W_in)
        self.out_layer = MatMul(W_out)
        self.loss_layer = SoftmaxWithLoss()

        # 将所有的权重和梯度整理到列表中
        layers = [self.in_layer0, self.in_layer1, self.out_layer]
        self.params, self.grads = [], []
        for layer in layers:
            self.params += layer.params
            self.grads += layer.grads

        # 将单词的分布式表示设置为成员变量
        self.word_vecs = W_in

    def forward(self, contexts, target):
        h0 = self.in_layer0.forward(contexts[:, 0])
        h1 = self.in_layer1.forward(contexts[:, 1])
        h = (h0 + h1) * 0.5
        score = self.out_layer.forward(h)
        loss = self.loss_layer.forward(score, target)
        return loss

    def backward(self, dout=1):
        ds = self.loss_layer.backward(dout)
        da = self.out_layer.backward(ds)
        da *= 0.5
        self.in_layer1.backward(da)
        self.in_layer0.backward(da)
        return None

5 skip-gram模型

skip-gram是反转了CBOW模型处理的上下文和目标词的模型,它从中间的单词(目标词)预测周围的多个单词(上下文)。

skip-gram模型的输入层只有一个,输出层的恶数量则与上下文的单词个数相等。因此,首先要分别求出各个输出层的损失,然后将它们加起来作为最后的损失。

# coding: utf-8
import sys
sys.path.append('..')
import numpy as np
from common.layers import MatMul, SoftmaxWithLoss


class SimpleSkipGram:
    def __init__(self, vocab_size, hidden_size):
        V, H = vocab_size, hidden_size

        # 初始化权重
        W_in = 0.01 * np.random.randn(V, H).astype('f')
        W_out = 0.01 * np.random.randn(H, V).astype('f')

        # 生成层
        self.in_layer = MatMul(W_in)
        self.out_layer = MatMul(W_out)
        self.loss_layer1 = SoftmaxWithLoss()
        self.loss_layer2 = SoftmaxWithLoss()

        # 将所有的权重和梯度整理到列表中
        layers = [self.in_layer, self.out_layer]
        self.params, self.grads = [], []
        for layer in layers:
            self.params += layer.params
            self.grads += layer.grads

        # 将单词的分布式表示设置为成员变量
        self.word_vecs = W_in

    def forward(self, contexts, target):
        h = self.in_layer.forward(target)
        s = self.out_layer.forward(h)
        l1 = self.loss_layer1.forward(s, contexts[:, 0])
        l2 = self.loss_layer2.forward(s, contexts[:, 1])
        loss = l1 + l2
        return loss

    def backward(self, dout=1):
        dl1 = self.loss_layer1.backward(dout)
        dl2 = self.loss_layer2.backward(dout)
        ds = dl1 + dl2
        dh = self.out_layer.backward(ds)
        self.in_layer.backward(dh)
        return None

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值