word2vec python实现_Word2Vec介绍:skip-gram模型的python实现

建议看此文之前先看以下两篇文章:

1. 我们的任务是什么?

假设我们有由字母组成的以下句子:a e d b d c d e e c a

Skip-gram算法就是在给出中心字母(也就是c)的情况下,预测它的上下文字母(除中心字母外窗口内的其他字母,这里的窗口大小是5,也就是左右各5个字母)。

首先创建需要的变量:

inputVectors = np.random.randn(5, 3) # 输入矩阵,语料库中字母的数量是5,我们使用3维向量表示一个字母

outputVectors = np.random.randn(5, 3) # 输出矩阵

sentence = ['a', 'e', 'd', 'b', 'd', 'c','d', 'e', 'e', 'c', 'a'] # 句子

centerword = 'c' # 中心字母

context = ['a', 'e', 'd', 'd', 'd', 'd', 'e', 'e', 'c', 'a'] # 上下文字母

tokens = dict([("a", 0), ("b", 1), ("c", 2), ("d", 3), ("e", 4)]) # 用于映射字母在输入输出矩阵中的索引

以下是测试,如何得到中心字母'c'的词向量。

>python

>inputVectors[tokens['c']]

array([ 1.08864485, -0.67509503, 0.51392943])

2. 加载必要的库和函数

python

import numpy as np

import random

softmax和sigmoid是我们自己写的函数。

python

from softmax import softmax

from sigmoid import sigmoid, sigmoid_grad

softmax用于计算向量或者矩阵每行的softmax。sigmoid用于计算sigmoid,sigmoid_grad用于计算sigmoid的导数。

>print(softmax(np.array([1,2]))) # 测试softmax

>x = np.array([[1, 2], [-1, -2]])

>print(sigmoid(x)) # 测试sigmoid

>print(sigmoid_grad(sigmoid(x))) # 测试sigmoid的梯度

[ 0.26894142 0.73105858]

[[ 0.73105858 0.88079708]

[ 0.26894142 0.11920292]]

[[ 0.19661193 0.10499359]

[ 0.19661193 0.10499359]]

3. 计算softmax代价和梯度

现在我们先考虑预测一个字母的情况,也就是说在给定中心字母‘c’的情况下,预测下一个字母是'd'。

先打造实现上述功能的单元模块softmaxCostAndGradient(predicted, target, outputVectors)如下

def softmaxCostAndGradient(predicted, target, outputVectors):

vhat = predicted # 中心词向量

z = np.dot(outputVectors, vhat) # 预测得分

yhat = softmax(z) # 预测输出yhat

cost = -np.log(y_hat[target]) # 计算代价

z = y_hat.copy()

z[target] -= 1.0

grad = np.outer(z, v_hat) # 计算中心词的梯度

gradPred = np.dot(outputVectors.T, z) # 计算输出词向量矩阵的梯度

return cost, gradPred, grad

参数解释:

predicted:输入词向量,也就是例子中'c'的词向量

target:目标词向量的索引,也就是真实值'd'的索引

outputVectors:输出向量矩阵

测试一下:

>python

>softmaxCostAndGradient(inputVectors[2], 3, outputVectors)

(2.7620705713684814,

array([ 0.46424045, -1.62684537, -0.251175 ]),

array([[ 0.32020414, -0.19856634, 0.15116255],

[ 0.15037396, -0.09325054, 0.07098881],

[ 0.04124188, -0.02557509, 0.01946954],

[-1.01988512, 0.63245545, -0.48146921],

[ 0.50806514, -0.31506349, 0.23984831]]))

4. skip-gram模型

有了单元模块,可以进一步打造skip-gram模型,相较于单元模块只能实现通过中心字母'c'来预测下一字母'd',下面要创建的skipgram模块可以实现通过中心字母'c'来预测窗口内的上下文字母context = ['a', 'e', 'd', 'd', 'd', 'd', 'e', 'e', 'c', 'a']

先给出代码如下:

def skipgram(currentWord, contextWords, tokens, inputVectors, outputVectors):

# 初始化变量

cost = 0.0

gradIn = np.zeros(inputVectors.shape)

gradOut = np.zeros(outputVectors.shape)

cword_idx = tokens[currentWord] # 得到中心单词的索引

v_hat = inputVectors[cword_idx] # 得到中心单词的词向量

# 循环预测上下文中每个字母

for j in contextWords:

u_idx = tokens[j] # 得到目标字母的索引

c_cost, c_grad_in, c_grad_out = softmaxCostAndGradient(v_hat, u_idx, outputVectors) #计算一个中心字母预测一个上下文字母的情况

cost += c_cost # 所有代价求和

gradIn[cword_idx] += c_grad_in # 中心词向量梯度求和

gradOut += c_grad_out # 输出词向量矩阵梯度求和

return cost, gradIn, gradOut

测试一下:

>python

>c, gin, gout = skipgram(centerword, context, tokens, inputVectors, outputVectors)

看一下计算后的代价是多少

>python

>c

19.055215361667734

skip-gram得到的代价是之前单元模块代价的大约10倍,因为我们的窗口大小是5,相当于计算2*5次单元模块并求和。

再看一下输出的代价gin。

>python

>gin

array([[ 0. , 0. , 0. ],

[ 0. , 0. , 0. ],

[ 2.39436138, -7.37446751, -2.73334444],

[ 0. , 0. , 0. ],

[ 0. , 0. , 0. ]])

gin只有第三行有值,其他行全是0,因为我们只更新输入矩阵中中心字母‘c’的词向量。

>python

>gout

array([[ 1.02475167, -0.63547332, 0.48376662],

[ 1.50373964, -0.93250536, 0.70988812],

[-0.67622608, 0.41934416, -0.31923403],

[-3.66698203, 2.27398434, -1.7311155 ],

[ 1.8147168 , -1.12534983, 0.85669479]])

我们需要更新输出矩阵中的所有词向量。

5. 更新权重

得到了梯度,下一步就可以更新我们的词向量矩阵。

>step = 0.01 #更新步进

>inputVectors -= step * gin # 更行输入词向量矩阵

>outputVectors -= step * gout

>print(inputVectors)

>print(outputVectors)

[[ 0.0293318 3.20359468 0.02918116]

[-0.18855591 -0.95316493 -0.64365733]

[ 1.04075763 -0.52760568 0.56859632]

[ 0.32805586 0.44831171 -1.50020838]

[-0.62146793 2.25323721 0.2220386 ]]

[[ 0.22033549 0.3710705 0.34345891]

[-0.23107472 -0.43592947 -1.24740455]

[-1.24593039 1.44290408 0.92737814]

[-0.21438081 1.50037497 -0.0857829 ]

[ 0.49294149 -0.63268862 -0.68114989]]

6. 完整测试代码

import numpy as np

import random

def softmax(x):

orig_shape = x.shape

# 根据输入类型是矩阵还是向量分别计算softmax

if len(x.shape) > 1:

# 矩阵

tmp = np.max(x,axis=1) # 得到每行的最大值,用于缩放每行的元素,避免溢出

x-=tmp.reshape((x.shape[0],1)) # 使每行减去所在行的最大值(广播运算)

x = np.exp(x) # 第一步,计算所有值以e为底的x次幂

tmp = np.sum(x, axis = 1) # 将每行求和并保存

x /= tmp.reshape((x.shape[0], 1)) # 所有元素除以所在行的元素和(广播运算)

else:

# 向量

tmp = np.max(x) # 得到最大值

x -= tmp # 利用最大值缩放数据

x = np.exp(x) # 对所有元素求以e为底的x次幂

tmp = np.sum(x) # 求元素和

x /= tmp # 求somftmax

return x

def sigmoid(x):

s = np.true_divide(1, 1 + np.exp(-x)) # 使用np.true_divide进行加法运算

return s

def sigmoid_grad(s):

ds = s * (1 - s) # 可以证明:sigmoid函数关于输入x的导数等于`sigmoid(x)(1-sigmoid(x))`

return ds

def softmaxCostAndGradient(predicted, target, outputVectors):

v_hat = predicted # 中心词向量

z = np.dot(outputVectors, v_hat) # 预测得分

y_hat = softmax(z) # 预测输出y_hat

cost = -np.log(y_hat[target]) # 计算代价

z = y_hat.copy()

z[target] -= 1.0

grad = np.outer(z, v_hat) # 计算中心词的梯度

gradPred = np.dot(outputVectors.T, z) # 计算输出词向量矩阵的梯度

return cost, gradPred, grad

def skipgram(currentWord, contextWords, tokens, inputVectors, outputVectors):

# 初始化变量

cost = 0.0

gradIn = np.zeros(inputVectors.shape)

gradOut = np.zeros(outputVectors.shape)

cword_idx = tokens[currentWord] # 得到中心单词的索引

v_hat = inputVectors[cword_idx] # 得到中心单词的词向量

# 循环预测上下文中每个字母

for j in contextWords:

u_idx = tokens[j] # 得到目标字母的索引

c_cost, c_grad_in, c_grad_out = softmaxCostAndGradient(v_hat, u_idx, outputVectors) #计算一个中心字母预测一个上下文字母的情况

cost += c_cost # 所有代价求和

gradIn[cword_idx] += c_grad_in # 中心词向量梯度求和

gradOut += c_grad_out # 输出词向量矩阵梯度求和

return cost, gradIn, gradOut

inputVectors = np.random.randn(5, 3) # 输入矩阵,语料库中字母的数量是5,我们使用3维向量表示一个字母

outputVectors = np.random.randn(5, 3) # 输出矩阵

sentence = ['a', 'e', 'd', 'b', 'd', 'c','d', 'e', 'e', 'c', 'a'] # 句子

centerword = 'c' # 中心字母

context = ['a', 'e', 'd', 'd', 'd', 'd', 'e', 'e', 'c', 'a'] # 上下文字母

tokens = dict([("a", 0), ("b", 1), ("c", 2), ("d", 3), ("e", 4)]) # 用于映射字母在输入输出矩阵中的索引

c, gin, gout = skipgram(centerword, context, tokens, inputVectors, outputVectors)

step = 0.01 #更新步进

print('原始输入矩阵:\n',inputVectors)

print('原始输出矩阵:\n',outputVectors)

inputVectors -= step * gin # 更行输入词向量矩阵

outputVectors -= step * gout

print('更新后的输入矩阵:\n',inputVectors)

print('更新后的输出矩阵:\n',outputVectors)

运行结果:

原始输入矩阵:

[[-2.00926589 -0.71395683 1.26236425]

[ 0.33655978 0.3235073 -0.82206833]

[-0.48877763 0.34254799 0.17305621]

[ 2.86310407 0.8945647 -0.74332816]

[ 1.78756342 0.32268516 -0.63464343]]

原始输出矩阵:

[[-1.65265982 -0.45342181 -1.56449817]

[ 1.26414084 -0.07519223 0.02234691]

[-1.28539302 -0.3520508 -0.80631146]

[-0.23932257 -1.61557932 1.14076112]

[-0.58790059 0.1913722 0.31944376]]

更新后的输入矩阵:

[[-2.00926589 -0.71395683 1.26236425]

[ 0.33655978 0.3235073 -0.82206833]

[-0.48012737 0.30942437 0.22498659]

[ 2.86310407 0.8945647 -0.74332816]

[ 1.78756342 0.32268516 -0.63464343]]

更新后的输出矩阵:

[[-1.64993767 -0.45532956 -1.56546197]

[ 1.26864072 -0.07834587 0.02075368]

[-1.27795164 -0.35726591 -0.80894614]

[-0.25215558 -1.60658561 1.14530477]

[-0.58973098 0.19265498 0.32009183]]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值