深入浅出word2vec(上)

时间:2020-9-4 作者:admin


引言

本文是对近日学习word2vec的一个总结,期间看了不少博客和论文。

word2vec是一种高效的训练词向量的模型,基于上下文相似的两个词,它们的词向量也应该相似, 比如,“A dog is running in the room”和”A cat is running in the room”。这两个句子,只是”cat”和”dog”不同,word2vec认为它们是相似的,而n-gram模型做不到这一点。

word2vec有两个模型:CBOW(COntinuous Bag of Words)和Skip-Gram。

深入浅出word2vec(上)

CBOW模型中,通过一个上下文(比如说一个句子)来预测目标词;而Skip-Gram模型则相反,根据给定的输入词来预测上下文。

Skip-Gram:能够很好地处理少量的训练数据,而且能够很好地表示不常见的单词或短语
CBOW:比skip-gram训练快几倍,对出现频率高的单词的准确度稍微更好一些

Simple CBOW模型

要想理解CBOW和SkipGram模型,我们先从最简单版本的CBOW模型开始介绍,又被称为One Word模型,上下文只有一个单词,目标词也是一个单词。
意味着给定一个上下文词来预测一个目标词。有点类似bigram模型。

深入浅出word2vec(上)

在上图中

VV

是词典大小,

NN

是一个超参数,是隐藏层中单元数量,也是我们要学的词向量的维度,一般最多设置到300。

输入向量

xx

V×1V \times 1

的one-hot向量,只有

xk=1\color{red}{ x_k=1}

,其他都是

00

输入层和输出层之间的权重是一个

V×NV \times N

的矩阵

WW

,给定一个上下文单词,隐藏层

hh

计算如下:

h=WTx=W(k,)T:=vwIT(1)
h = W^T x = W_{(k,\cdot)}^T := v_{w_I}^T \tag{1}

WW

V×NV \times N

hh

的维度是

N×1N \times 1

这个公式详细描述一下,展开上面的

WW

矩阵:

WV×N=[w11w12w1Nw21w22w2NwV1wV2wVN]
W_{V \times N} = \left[
\begin{matrix}
w_{11} & w_{12} & \cdots & w_{1N} \\
w_{21} & w_{22} & \cdots & w_{2N} \\
\vdots & \vdots & \ddots & \vdots \\
w_{V1} & w_{V2} & \cdots & w_{VN}
\end{matrix} \right]

xx

x=[x1x2xV]
x = \left[
\begin{matrix}
x_1 \\ x_2 \\ \vdots \\ x_V
\end{matrix} \right]

h=WTx=[w11w21wk1wV1w12w22wk2wV2w1Nw2NwkNwVN]N×V[x1x2xkxV]=[wk1wk2wkN]
h = W^T x = \left[
\begin{matrix}
w_{11} & w_{21} & \cdots & w_{k1} \cdots & w_{V1} \\
w_{12} & w_{22} & \cdots & w_{k2} \cdots & w_{V2} \\
\vdots & \vdots & \ddots & \vdots & \vdots \\
w_{1N} & w_{2N} & \cdots & w_{kN} \cdots & w_{VN}
\end{matrix} \right]_{N \times V} \left[
\begin{matrix}
x_1 \\ x_2 \\ \vdots \\ x_k \\ \vdots \\ x_V
\end{matrix} \right]
= \left[
\begin{matrix}
w_{k1} \\ w_{k2} \\ \vdots \\ w_{kN}
\end{matrix} \right] \\

WW

的第

ii

行用

vwv_w

表示,相当于是

ww

的词向量,是

1×N1 \times N

的。

WTxW^T x

得到

N×1N \times 1

的列向量,相当于是

WW

xk=1x_k=1

对应的那一行。

基本上就是拷贝了

WW

的第

kk

行到

hh

去了。

输入单词

wIw_I

的向量表示是

vwIv_{w_I}

,维度是

N×1N \times 1

从隐藏层到输出层,有一个不同的权重矩阵

WW^′

,它是

N×VN \times V

的。使用这个权重矩阵,可以计算第

jj

个单词的得分

uju_j

:

uj=vwjTh(2)
u_j = {v^{\prime} _{w_j}}^T \cdot h \tag{2}

vwjv^′_{w_j}

是矩阵

WW^′

的第

jj

列,维度是

N×1N \times 1

的,

vwjT{v^′_{w_j}}^T

维度就是

1×N1 \times N

。因此

uju_j

是这两个向量的内积,结果是一个标量,代表某个单词的分数。

这个得分可以理解为衡量中心词与输出词的相似度,

hh

其实就是输入词的向量

vwIv_{w_I}

我们可以一次性求出所有单词的得分:

u=WThu = {W^′}^T \cdot h

,得到的是

V×1V \times 1

的向量,

VV

是词典大小。

接着对

uu

进行softmax就可以得到每个单词得分的概率分布:

p(wjwI)=yj=exp(uj)j=1Vexp(uj)(3)
p(w_j|w_I) = y_j = \frac{exp(u_j)}{\sum_{j^{\prime} = 1}^V exp(u_{j^{\prime}})} \tag{3}

yjy_j

是输出层第

jj

个单元的输出。把

(1)(1)

,

(2)(2)

代入到

(3)(3)

得:

p(wjwI)=exp(vwjTvwI)j=1Vexp(vwjTvwI)(4)
p(w_j|w_I) = \frac{ exp ({v^{\prime} _{w_j}}^T \cdot v_{w_I} )}{ \sum^V_{j^′=1} exp({v^{\prime} _{w_{j^′}}}^T v_{w_I} ) } \tag{4}

这里要注意的是:

  • 输入单词
    xx

    和输出单词

    yy

    都是one-hot向量


  • vwv_w

    vwv^′_w

    是输入单词

    ww

    的两种表示,分别称为输入向量和输出向量


  • vwv_w

    来自

    WW

    的行


  • vwv^′_w

    来自

    WW^′

    的列

更新权重:隐藏层到输出层

下面我们就可以根据上面的式子来求梯度了。

训练目标是最大化公式

(4)(4)

,即给定输入单词

wIw_I

,最大化观察到输出单词

wOw_O

的条件概率(用

jj^*

表示它输出层的索引)。

maxp(wOwI)=maxyj=maxlogyj=maxlogexp(uj)logj=1Vexp(uj)=ujlogj=1Vexp(uj):=E
\begin{aligned}
\max p(w_O|w_I) &= \max \, y_{j^*} \\
&= \max \, \log \, y_{j^*} \\
&= \max \, \log \exp (u_{j^*}) – \log \sum_{j^{\prime} = 1}^V exp(u_{j^{\prime}}) \\
&= u_j^* – log \sum_{j^{\prime} = 1}^V exp(u_{j^{\prime}}) := -E
\end{aligned}

:=:=

是记作的意思,即整个式子记作

E-E

,也就是

E=logp(wOwI)E = -\log \, p(w_O|w_I)

,因为我们习惯最小化损失函数。

现在我们更新隐藏层和输出层之间的权重。

下面求

EE

uju_j

的偏导,得到了

Euj=yjtj:=ej(5)
\frac{\partial E}{\partial u_j} = y_j – t_j := e_j \tag{5}

j=jj=j^*

时,

tj=1t_j=1

,否则

tj=0t_j=0

下面给出公式推导:

Euj=(ujlogj=1Vexp(uj))uj=ujuj+(logj=1Vexp(uj))uj=tj+exp(uj)j=1Vexp(uj)=yjtj
\begin{aligned}
\frac{\partial E}{\partial u_j} &=- \frac{ \partial \left( u_j^* – log \sum_{j^{\prime} = 1}^V exp(u_{j^{\prime}}) \right) }{\partial u_j} \\
&= -\frac{\partial u_{j^*}}{\partial u_j} + \frac{\partial \left(\log \sum_{j^{\prime} = 1}^V \exp (u_{j^{\prime}}) \right)}{\partial u_j} \\
&= – t_j + \frac{exp(u_j)}{\sum_{j^{\prime} = 1}^V exp(u_j)} \\
&= y_j – t_j
\end{aligned}

其中

(logj=1Vexp(uj))uj
\frac{\partial \left(\log \sum_{j^{\prime} = 1}^V \exp (u_{j^{\prime}}) \right)}{\partial u_j}


是通过复合函数的求导法则来求的,

logf(x)x=f(x)f(x)\frac{\partial \log f(x)}{\partial x} = \frac{f(x)^{\prime}}{f(x)}

,这里把

f(x)=j=1Vexp(uj)f(x)=\sum_{j^{\prime} = 1}^V \exp (u_{j^{\prime}})

要求

j=1Vexp(uj)\sum_{j^{\prime} = 1}^V \exp (u_{j^{\prime}})

uju_j

的偏导,其实很简单,把求和符号展开即可。

(exp(u1)+exp(u2)++exp(uj)++exp(uV))uj=exp(uj)
\frac{ \partial \left(exp(u_1) + exp(u_2) + \cdots + exp(u_j) + \cdots +exp(u_V) \right)}{\partial u_j} = exp(u_j)

uju_j

看成一个变量,其他

u1,u2,u_1,u_2, \cdots

都是与

uju_j

无关的,因此求导结果为0。

根据公式

(3)(3)

就可以化简为

yjtjy_j – t_j

结果简单地就是预测值与真实值之差。

下一步就是对

wijw^′_{ij}

求导来获取它的梯度。

来看下

ujwij\frac{\partial u_j}{\partial w^′_{ij}}

由公式

(2)(2)

知道

uju_j

wijw^′_{ij}

的关系。

h=vwI=[h1,h2,,hN]h=v_{w_I}=[h_1,h_2,\cdots,h_N]

vwjT=[w1j,w2j,,w1N]{v^′_{w_j}}^T = [w^′_{1j},w^′_{2j},\cdots,w^′_{1N}]

uj=h1w1j+h2w2j++hiwij++hNwNju_j = h_1 \cdot w^′_{1j} + h_2 \cdot w^′_{2j} + \cdots + h_i \cdot w^′_{ij} + \cdots + h_N \cdot w^′_{Nj}

所以

ujwij=hi\frac{\partial u_j}{\partial w^′_{ij}} = h_i

Ewij=Eujujwij=ejhi(6)
\frac{\partial E}{\partial w^′_{ij}} = \frac{\partial E}{\partial u_j} \cdot \frac{\partial u_j}{\partial w^′_{ij}} = e_j \cdot h_i \tag{6}

现在就可以使用梯度下降来更新隐藏层到输出层的权重:

wij=wijηejhi
w^′_{ij} = w^′_{ij} – \eta \cdot e_j \cdot h_i


或者向量的形式为:

vwj=vwjηejh
v^′_{w_j} = v^′_{w_j} – \eta \cdot e_j \cdot h

hih_i

是隐藏层的第

ii

个单元,

vwjv′_{w_j}

是单词

wjw_j

的输出向量。对每个训练样本都需要做一次复杂度为

VV

的操作去更新

WW^′

更新权重:输入层到隐藏层

接着我们关注输入层到隐藏层的权重。首先求

Ehi\frac{\partial E}{\partial h_i}

Ehi=j=1VEujujhi=j=1Vejwij:=EHi
\frac{\partial E}{\partial h_i} = \sum_{j=1}^V \frac{\partial E}{\partial u_j} \cdot \frac{\partial u_j}{\partial h_i} \\
= \sum_{j=1}^V e_j \cdot w^′_{ij}\\ := EH_i

EHEH

是一个

NN

维的向量(

N×1N \times 1

),就是所有输出单词的权重之和,权重是它们的预测错误。

下一步就是要求

EE

WW

的导数,首先回顾下隐藏层就是输入层的线性变换:

hi=k=1Vxkwki
h_i = \sum_{k=1}^V x_k \cdot w_{ki}

然后我们用链式法则来求

EE

WW

的导数:

Ewki=Ehihiwki=EHixk
\frac{\partial E}{\partial w_{ki}} = \frac{\partial E}{\partial h_i} \cdot \frac{\partial h_i}{\partial w_{ki}} \\
= EH_i \cdot x_k

向量化形式等价于

xx

EHEH

的张量积:

EW=xEH=xEHT
\frac{\partial E}{\partial W} = x \otimes EH = x \cdot EH^T

这样就得到了一个

V×NV \times N

的矩阵,因为

xx

向量中只有一个元素为

11

,其他都为

00

,所以在

EW\frac{\partial E}{\partial W}

的矩阵中,只有一行是非零的。并且这一行的值是

EHTEH^T

现在我们就可以写出

WW

的更新式子了:

vwI=vwIηEHT
v_{w_I} = v_{w_I} – \eta \cdot EH^T

因为只有一行是非零的,所以一次也只会更新一行。

CBOW模型

CBOW模型的图示如下:

深入浅出word2vec(上)

CBOW模型由多个单词作为输入,每个输入都是one-hot模型,同样输出一个单词。由多个上下文单词来预测中心词。计算隐藏层的时候,取输入单词的平均向量,然后乘以权重

WW

作为输出:

h=1C(x1T+x2T++xCT)W=1C(vw1+vw2++vwC)
h = \frac{1}{C} (x_1^T + x_2^T + \cdots + x_C^T) W \\
= \frac{1}{C}(v_{w_1} + v_{w_2} + \dots + v_{w_C})

CC

是上下文单词数量,因为是把

CC

个输入单词的平均向量作为输入向量,损失函数的定义和上面一个单词的模型一样。

更新隐藏层到输出层的式子也是一样的:

vwj=vwjηejh    forj=1,2,,V
v^′_{w_j} = v^′_{w_j} – \eta \cdot e_j \cdot h \,\,\,\, for\, j = 1,2, \cdots,V

更新输入层到隐藏层的权重和之前一样,除了我们需要将梯度均摊到每个输入单词上:

vwI,c=vwI,c1CηEHT    forc=1,2,,C
v_{w_{I,c}} = v_{w_{I,c}} – \frac{1}{C} \cdot \eta \cdot EH^T \,\,\,\, for\, c = 1,2,\cdots,C

这里每次会更新

WW

中的

CC

行。

Skipgram模型

深入浅出word2vec(上)

Skip-Gram模型和CBOW模型相反,把中心词放到输入层中,输出层输出的是上下文词。即用中心词来预测上下文词。

我们仍然使用

vwIv_{w_I}

来表示Skip-gram模型的唯一输入向量。然后隐藏层输出

hh

的定义也和

(1)(1)

一样。

h=WTx=W(k,)T:=vwIT
h = W^T x = W_{(k,\cdot)}^T := v_{w_I}^T

在输出层,不是输出一个多项式分布,而是输出

CC

个多项式分布。但每个分布使用同样的权重矩阵来计算:

p(wc,jwI)=yc,j=exp(uc,j)j=1Vexp(uj)
p(w_{c,j}|w_I) = y_{c,j} = \frac{exp(u_{c,j})}{\sum_{j^′=1}^V exp(u_{j^′})}

需要注意的是,这

CC

个输出是相互独立的。

wc,jw_{c,j}

是第

cc

个panel(输出)中的第

jj

个单词。

wIw_I

是输入单词。

yc,jy_{c,j}

是第

cc

个输出层中的第

jj

个单元。

uc,ju_{c,j}

是第

cc

个输出的第

jj

个单元的得分。因为这些输出都共享同样的权重,因此

uc,j=uj=vwjTh   forc=1,2,,C
u_{c,j} = u_j = {v^′_{w_j}}^T \cdot h \,\, \, for \, c = 1,2,\cdots,C

vwjv^′_{w_j}

是词典中第

jj

个单词的输出向量,它是矩阵

WW^′

中的第

jj

列。

参数更新的式子和简单CBOW模型有点不同,

E=logp(wO,1,wO,2,,wO,CwI)=logc=1CP(wO,cwi)=logc=1Cexp(uc,jc)j=1Vexp(uj)=logc=1Cexp(uc,jc)+logc=1Cj=1Vexp(uj)=c=1Cujc+log(j=1Vexp(uj))C=c=1Cujc+Clogj=1Vexp(uj)
\begin{aligned}
E &= -\log p(w_{O,1},w_{O,2},\cdots,w_{O,C}|w_I) \\
&= – \log \prod_{c=1}^C P(w_{O,c}|w_i) \\
&= – \log \prod_{c=1}^C \frac{exp(u_{c,j^*_c})}{\sum_{j^′=1}^V exp(u_{j^′})} \\
&= – \log \prod_{c=1}^C exp(u_{c,j^*_c}) + \log \prod_{c=1}^C \sum_{j^′=1}^V exp(u_{j^′})\\
&= – \sum_{c=1}^C u_{j^*_c} + \log (\sum_{j^′=1}^V exp(u_{j^′}))^C\\
&= – \sum_{c=1} ^ C u_{j^*_c} + C \cdot \log \sum_{j^′=1}^V exp(u_{j^′})
\end{aligned}

wO,cw_{O,c}

代表第

cc

个输出单词,

jcj^*_c

表示第

cc

个输出单词的索引。
因为这

CC

个输出是相互独立的,因此

p(wO,1,wO,2,,wO,CwI)=P(wO,cwI)p(w_{O,1},w_{O,2},\cdots,w_{O,C}|w_I) = \prod P(w_{O,c}|w_I)

下面我们求梯度,对第

cc

个多项分布的第

jj

项的梯度为:

Euc,j=yc,jtc,j:=ec,j
\frac{\partial E}{\partial u_{c,j}} = y_{c,j} – t_{c,j} := e_{c,j}

就是某个输出的预测错误,考虑到

CC

个多项分布产生的影响,所以需要求和。

为了简化,我们定义一个

VV

维的向量

EI=EI1,,EIVEI = {EI_1,\cdots,EI_V}

作为所有上下文单词的预测错误之和。

对第

jj

个单词的预测错误之和为:

EIj=c=1Cec,j
EI_j = \sum_{c=1}^C e_{c,j}

接下来,对隐藏层到输出层矩阵

WW^\prime

求导:

Ewij=c=1CEuc,juc,jwij=EIjhi
\frac{\partial E}{\partial w^\prime_{ij}} = \sum_{c=1}^C \frac{\partial E}{\partial u_{c,j}} \cdot \frac{\partial u_{c,j}}{\partial w^\prime_{ij}} = EI_j \cdot h_i

所以更新隐藏层到输出层权重的式子为:

wij=wijηEIjhi
w^\prime_{ij} = w^\prime_{ij} -\eta \cdot EI_j \cdot h_i


或者

vwj=vwjηEIjh   forj=1,2,,V
v^\prime_{w_j} = v^\prime_{w_j} – \eta \cdot EI_j \cdot h \,\,\, for\, j=1,2,\cdots,V

下面考虑对隐藏层的梯度:

Ehi=c=1Cj=1VEuc,juc,jhi=c=1Cj=1Vec,jwij=j=1VEIjwij:=EHi
\begin{aligned}
\frac{\partial E}{\partial h_i} &= \sum_{c=1}^C \sum_{j=1}^V \frac{\partial E}{\partial u_{c,j}} \frac{\partial u_{c,j}}{\partial h_i } \\
&= \sum_{c=1}^C \sum_{j=1}^V e_{c,j} \cdot w^\prime_{ij} \\
&= \sum_{j=1}^V EI_j \cdot w^\prime_{ij} := EH_i
\end{aligned}

和简单CBOW模型一样,整成向量化的形式为:

Eh=EHT
\frac{\partial E}{\partial h} = EH^T

由于输入只有一个词,

h=vwITh=v_{w_I}^T

,每次也是更新

WW

的一行:

vwI=vwIηEHT
v_{w_I} = v_{w_I} – \eta \cdot EH^T

简单代码实现

# -*- coding: utf-8 -*-
# @Author  : Jue

from collections import defaultdict

import numpy as np


class word2vec:
	def __init__(self, settings):
		self.n = settings['n']
		self.eta = settings['learning_rate']
		self.epochs = settings['epochs']
		self.window = settings['window_size']
		# true:cbow ; false:skipgram
		self.cbow = settings['model'] == 'cbow'

	def generate_training_data(self, corpus):
		# 单词计数
		word_counts = defaultdict(int)
		for row in corpus:
			for word in row:
				word_counts[word] += 1

		# 词典大小V
		self.v_count = len(word_counts.keys())

		# 生成LOOKUP 词典
		self.words_list = sorted(list(word_counts.keys()), reverse=False)

		# 单词对应的索引
		self.word_index = dict((word, i) for i, word in enumerate(self.words_list))
		# 索引对应的单词
		self.index_word = dict((i, word) for word, i in self.word_index.items())

		training_data = []

		for sentence in corpus:
			sent_len = len(sentence)

			for i, word in enumerate(sentence):
				# 目标词
				w_target = self.word2onehot(sentence[i])

				# 上下文词
				w_context = []
				for j in range(i - self.window, i + self.window + 1):
					if j != i and sent_len - 1 >= j >= 0:
						w_context.append(self.word2onehot(sentence[j]))

				training_data.append([w_target, w_context])  # 中心词,上下文词
		return np.array(training_data, dtype=object)

	def train(self, training_data, debug=False):
		# 初始化权重矩阵
		self.w1 = np.random.uniform(-0.8, 0.8, (self.v_count, self.n))  # 目标词矩阵 W v x n
		self.w2 = np.random.uniform(-0.8, 0.8, (self.n, self.v_count))  # 上下文词矩阵  W′ n x v

		# 迭代epochs次
		for i in range(self.epochs):
			self.loss = 0
			# 中心词,上下文词
			for w_t, w_c in training_data:
				if self.cbow:
					x = np.mean(w_c, axis=0)
				else:
					x = w_t
				# 前向传播
				y_pred, h, u = self.forward_pass(x)

				# 计算损失 e_j
				if self.cbow:
					e = y_pred - w_t  # dE/du
				else:
					e = np.sum([np.subtract(y_pred, word) for word in w_c], axis=0)

				# 反向传播
				self.backprop(e, h, x)
				if self.cbow:
					self.loss += -float(u[w_t == 1]) + np.log(np.sum(np.exp(u)))
				else:
					self.loss += -np.sum([u[word == 1] for word in w_c]) + len(w_c) * np.log(np.sum(np.exp(u)))

			if i % 100 == 0 and debug:
				print('EPOCH:', i, 'LOSS:', self.loss)

	def forward_pass(self, x):
		'''
		:param x:  vx1 one-hot向量
		:return:
		'''
		h = np.dot(self.w1.T, x)  # (nxv)  (vx1) -> nx1
		u = np.dot(self.w2.T, h)  # (v x n) (n x 1)   -> vx1 计算每个单词的得分
		y_c = self.softmax(u)  # 通过softmax进行归一化,得到每个单词对应的概率
		return y_c, h, u

	def backprop(self, e, h, x):
		'''

		:param e: v x 1
		:param h: n x 1
		:param x: v x 1
		:return:
		'''
		dw2 = np.outer(h, e)  # n x v    W′的梯度

		dw1 = np.outer(x, np.dot(self.w2, e))  # (vx1)  (nxv vx1)->nx1

		self.w1 -= self.eta * dw1
		self.w2 -= self.eta * dw2

	def word2onehot(self, word):
		word_vec = np.zeros((self.v_count, 1))
		word_vec[self.word_index[word]] = 1
		return word_vec

	def softmax(self, x):
		e_x = np.exp(x - np.max(x))
		return e_x / e_x.sum(axis=0)

	def word_2_vec(self, word):
		w_index = self.word_index[word]
		return self.w1[w_index]


def cos_similarity(v1, v2):
	return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))


if __name__ == '__main__':
	settings = {}
	settings['n'] = 2  # dimension of word embeddings
	settings['window_size'] = 2  # context window +/- center word
	settings['min_count'] = 0  # minimum word count
	settings['epochs'] = 5000  # number of training epochs
	settings['neg_samp'] = 5  # number of negative words to use during training
	settings['learning_rate'] = 0.1  # learning rate
	settings['model'] = 'skipgram'  # cbow or skipgram
	np.random.seed(0)  # set the seed for reproducibility

	corpus = [['A', 'dog', 'is', 'running', 'in', 'the', 'room'],
	          ['A', 'cat', 'is', 'running', 'in', 'the', 'room']]
	# corpus = []
	# corpus = [['natural', 'language', 'processing', 'and', 'machine', 'learning', 'is', 'fun', 'and', 'exciting']]
	# I like playing football with my friends
	w2v = word2vec(settings)

	# 生成训练数据
	training_data = w2v.generate_training_data(corpus)
	# print(training_data)
	# 训练
	w2v.train(training_data, debug=True)
	for w1 in w2v.word_index.keys():
		for w2 in w2v.word_index.keys():
			print("%s & %s similarity is %s" % (w1, w2, cos_similarity(w2v.word_2_vec(w1), w2v.word_2_vec(w2))))

	vecs = np.array([w2v.word_2_vec(vec) for vec in w2v.word_index.keys()])

	import matplotlib.pyplot as plt

	plt.scatter(vecs[:, 0], vecs[:, 1])

	words = list(w2v.word_index.keys())
	for i, word in enumerate(words):
		plt.annotate(word, xy=(vecs[i, 0], vecs[i, 1]))
	plt.show()

深入浅出word2vec(上)

至此我们知道了word2vec的原理和代码实现,但训练效率低是它的一个缺点,在下篇文章将会介绍两种优化的方法。

参考

  1. Word2vec from Scratch with Python and NumPy
  2. word2vec Parameter Learning Explained
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。