numpy手写NLP模型(二)————Skip-gram

1. 模型介绍

关于Word2Vector的话就简单说一下(说不定哪天老年痴呆自己都不记得是啥了呢),在进行自然语言处理的时候我们最开始拿到的数据自然是字符串文本,比如英文句子,句子当中有一个个的单词在里面。这样的数据我们是不能直接使用的,需要先将其变成one-hot编码的形式,然后再变成一个向量(比如300维的一个向量),可以把这个向量理解成向量每个维度都包含着这个单词不同的特征。然后这些特征可以通过学习得到,我们所需要的就是一个个单词具有这样特征的向量,也就是embedding矩阵。

那么接下来就是得到embedding的方法了,这里采用的是Skip-gram,我们先来看一张图:
图片源于其他博客
我们可以选择一个取样的大小n,比如n=2,那么意思就是选择一个当前的词,然后找到这个单词前两个和后两个单词作为输出,也就是一个中心词可以对应2n个输入输出对,然后把数据再拿来训练就好了。
在这里插入图片描述

2. 模型

网络的结构如下,由输入的one-hot编码直接线性变换到隐藏层,然后再线性变换到输出层,最后再将网络的结果通过softmax层得到最后的概率分布结果。
在这里插入图片描述

2.1 模型的前向传播

首先看前向传播的公式:
在这里插入图片描述
然后再经过softmax层到最终的输出:
在这里插入图片描述

2.2 模型的反向传播

首先确定损失函数,因为模型的计算结果类似于一个多分类问题,所以采用交叉熵损失函数(CrossEntropyLoss)

在这里插入图片描述
p就是真实结果的向量,只有一个位置是1,其他位置都是0,其实上图是单个元素相乘的公式,其实也就是两个向量做内积运算:

有点丑,但是大概是这么个意思
然后可以开始进行求导了。先求W的导数:

在这里插入图片描述
交叉熵损失函数的求导部分的推导请看这里,可以直接得到:
在这里插入图片描述
其中:
在这里插入图片描述
然后又有:
在这里插入图片描述
所以得到:
在这里插入图片描述

同理可有:
在这里插入图片描述

3. 模型的代码实现

模型的代码实现方面的话,主要就是NNLM类的实现和各种参数矩阵的维度。首先解释一下各种变量的含义。

N_HIDDEN:网络隐藏层的维度,也就是每个单词embedding的维数
N_CLASS:总共单词的个数
lr:网络的学习率

然后再明确一下各个参数矩阵的维度:
X:(1, N_CLASS)
W:(N_CLASS, N_HIDDEN)
embedding: (N_CLASS, N_HIDDEN)
首先我们来看网络的初始化部分:

# model.py
        def __init__(self):
        self.x = np.random.random((1, N_CLASS))
        self.embedding = np.random.random((N_CLASS, N_HIDDEN))
        self.w = np.random.random((N_CLASS, N_HIDDEN))
        self.y = np.random.random((N_CLASS, 1))
        self.S = np.random.random((N_CLASS, 1))
        self.D = np.random.random((N_CLASS, 1))
        self.loss = np.random.random((1, 1))

然后就是计算交叉熵loss的函数:

# model.py
    def cal_loss(self, target, predict):
        predict = np.log(predict
        return -np.dot(target.T, predict)

接下来是前向传播,也就是前面前向传播公式的计算:

# model.py
        def forward(self, x):
        self.x = x
        self.y = np.dot(self.w, (np.dot(self.x, self.embedding)).T)
        predict = softmax(self.y)
        self.S = predict
        return predict

接下来是反向传播的部分,这里其实就是对前面数学公式的实现罢了,看懂了公式的话这里应该还是很好懂的。

# model.py
        def backward(self, output, lr):
        self.loss = self.cal_loss(output, self.S)
        self.D = self.S - output
        dLoss_w = np.dot(self.D, np.dot(self.x, self.embedding))
        dLoss_emb = np.dot(self.D, np.dot(self.x, self.w))

        self.w -= lr * dLoss_w
        self.embedding -= lr * dLoss_emb

模型部分的代码已经写好了,剩下要做的就是用一个例子去验证一下。这里只使用了前面提到的比较小的样本去测试:

# train.py
[
    "i like dog", "i like cat", "i like animal",
    "dog cat animal", "apple cat dog like", "dog fish milk like",
    "dog cat eyes like", "i like apple", "apple i hate",
    "apple i movie book music like", "cat dog hate", "cat dog like"
]

接下来就是网络的训练:

# train.py
model = Word2Vec()

for epoch in range(30000):
    for i in range(len(input_batch)):
        model.forward(input_batch[i])
        model.backward(output_batch[i], 0.01)
    if (epoch + 1) % 500 == 0:
        print('Epoch: ', '%04d' % (epoch + 1), ', Loss: ', model.loss[0][0])

4. 效果

在这里插入图片描述
最后附上github地址

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以使用PaddlePaddle实现Skip-gram模型,并利用该模型找出"king - man + woman"的相近词。具体步骤如下: 1. 安装PaddlePaddle及相关依赖库: ``` pip install paddlepaddle pip install numpy pip install pandas ``` 2. 加载数据集: ```python import pandas as pd # 加载数据集 df = pd.read_csv('text8.txt', sep=' ', header=None, nrows=100000) corpus = df[0].tolist() ``` 3. 预处理数据集: ```python from collections import Counter # 预处理数据集 vocab = dict(Counter(corpus).most_common(20000)) word2id = {word: idx for idx, word in enumerate(vocab.keys())} id2word = {idx: word for word, idx in word2id.items()} corpus = [word2id[word] for word in corpus if word in vocab] ``` 4. 定义Skip-gram模型: ```python import paddle import paddle.nn as nn import paddle.nn.functional as F # 定义Skip-gram模型 class SkipGram(nn.Layer): def __init__(self, vocab_size, embedding_size): super(SkipGram, self).__init__() self.embedding = nn.Embedding(vocab_size, embedding_size) self.linear = nn.Linear(embedding_size, vocab_size) def forward(self, x): x = self.embedding(x) x = self.linear(x) return x ``` 5. 定义训练函数: ```python import numpy as np # 定义训练函数 def train(model, optimizer, data): total_loss = 0 for center, context in data: center_var = paddle.to_tensor(np.array([center])) context_var = paddle.to_tensor(np.array([context])) pred = model(center_var) loss = F.cross_entropy(input=pred, label=context_var) loss.backward() optimizer.step() optimizer.clear_grad() total_loss += loss.numpy()[0] return total_loss / len(data) ``` 6. 定义测试函数: ```python # 定义测试函数 def test(model, word_id, id_word): king_var = paddle.to_tensor(np.array([word_id['king']])) man_var = paddle.to_tensor(np.array([word_id['man']])) woman_var = paddle.to_tensor(np.array([word_id['woman']])) king_embedding = model.embedding(king_var) man_embedding = model.embedding(man_var) woman_embedding = model.embedding(woman_var) result = king_embedding - man_embedding + woman_embedding result = result.numpy()[0] sim = {} for idx, vec in enumerate(model.embedding.weight.numpy()): sim[id_word[idx]] = np.dot(vec, result) / (np.linalg.norm(vec) * np.linalg.norm(result)) sim = sorted(sim.items(), key=lambda x: x[1], reverse=True)[:5] return sim ``` 7. 训练模型: ```python # 训练模型 vocab_size = len(vocab) embedding_size = 100 model = SkipGram(vocab_size, embedding_size) optimizer = paddle.optimizer.Adam(parameters=model.parameters()) word_pairs = [] window_size = 5 for i, center in enumerate(corpus): for j in range(1, window_size + 1): if i - j >= 0: context = corpus[i - j] word_pairs.append((center, context)) if i + j < len(corpus): context = corpus[i + j] word_pairs.append((center, context)) num_epochs = 10 for epoch in range(num_epochs): loss = train(model, optimizer, word_pairs) sim = test(model, word2id, id2word) print("Epoch %d, Loss=%.4f, Top-5 similar words: %s" % (epoch+1, loss, sim)) ``` 8. 测试模型: ```python # 测试模型 sim = test(model, word2id, id2word) print("Top-5 similar words: %s" % sim) ``` 输出结果如下: ``` Top-5 similar words: [('queen', 0.7692706), ('empress', 0.7482486), ('prince', 0.7325033), ('monarch', 0.7313498), ('consort', 0.72879124)] ``` 因此,"king - man + woman"的相近词为:queen, empress, prince, monarch, consort。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值