【问题标题】:How to use word embeddings (i.e., Word2vec, GloVe or BERT) to calculate the most word similarity in a set of N words?如何使用词嵌入(即 Word2vec、GloVe 或 BERT)来计算一组 N 个词中最大的词相似度?
【发布时间】:2020-10-14 05:26:04
【问题描述】:

我正在尝试通过输入单词列表并输出一个单词来计算语义相似度,这是列表中单词相似度最高的。

例如

如果我传入一个单词列表

words = ['portugal', 'spain', 'belgium', 'country', 'netherlands', 'italy']

它应该输出我这样的东西-

['country']

【问题讨论】:

  • 我认为这可能不是嵌入的情况。这就是您定义the most word similarity 的方式。我想知道你真正想要输出什么。

标签: python nlp word2vec bert-language-model cosine-similarity


【解决方案1】:

首先,在 Google 新闻上训练的预训练 word2vec 需要从 https://drive.google.com/file/d/0B7XkCwpI5KDYNlNUTTlSS21pQmM/edit 下载。

那么,词嵌入之间的余弦相似度可以计算如下:

import gensim
import warnings
warnings.filterwarnings(action='ignore', category=UserWarning, module='gensim')
from gensim.models.keyedvectors import KeyedVectors
from numpy import dot
from numpy.linalg import norm

def cosine_sim(a,b):
    return dot(a, b)/(norm(a)*norm(b))

# load the w2v model
path_pretraind_model='./GoogleNews-vectors-negative300.bin/GoogleNews-vectors-negative300.bin'  #set as the path of pretraind model 
model = KeyedVectors.load_word2vec_format(path_pretraind_model, binary=True)


wlist = ['portugal', 'spain', 'belgium', 'country', 'netherlands', 'italy']
lenwlist=len(wlist)
avrsim=[]
#compute cosine similarity between each word in wlist with the other words in wlist  
for i in range(lenwlist):
    word=wlist[i]
    totalsim=0
    wordembed=model[word] 
    for j in range(lenwlist):
        if i!=j:
            word2embed=model[wlist[j]] 
            totalsim+=cosine_sim(wordembed, word2embed)
    avrsim.append(totalsim/ (lenwlist-1)) #add the average similarity between word and any other words in wlist   

index_min=avrsim.index(min(avrsim)) #get min similarity        
print(wlist[index_min])

如果你的意思是词嵌入之间的余弦相似度,“国家”与其他词的相似度最低。

【讨论】:

  • 请注意,还有一个现有的实用方法.doesnt_match(),用于计算哪个单词与所有单词的平均值最不相似radimrehurek.com/gensim/models/… – & 想要“最核心”计算的人可以根据该方法的源代码对其进行建模。
  • 感谢您的努力,但是当我申请到这个列表时 wlist=[ 'screen', 'memory', 'laptop', 'processor'] 似乎屏幕是最相似的。
  • 请注意,上面的代码会找到与其他单词最不相似的单词。因为你想得到国家,而国家与['portugal','spain','belgium','country','netherlands','italy']中的其他词的相似性最小。如果你想得到最相似的,你需要使用 index_min=avrsim.index(max(avrsim)) 而不是 min(avrsim)。在 wlist=[ 'screen', 'memory', 'laptop', 'processor'] 的情况下,与其他词最不相似的是 screen,最相似的是笔记本电脑。
【解决方案2】:

手套嵌入

要加载预训练的 GloVe 嵌入,我们将使用一个名为 torchtext 的包。它包含其他有用的处理文本的工具,我们将在课程后面看到。 torchtext GloVe 向量的文档位于:https://torchtext.readthedocs.io/en/latest/vocab.html#glove

首先加载一组 GloVe 嵌入。第一次运行以下代码时,Python 将下载一个包含预训练嵌入的大文件 (862MB)。

import torch
import torchtext

glove = torchtext.vocab.GloVe(name="6B", # trained on Wikipedia 2014 corpus of 6 billion words
                              dim=50)   # embedding size = 100

我们来看看“汽车”这个词的嵌入是什么样子的:

glove['cat']

张量([ 0.4528, -0.5011, -0.5371, -0.0157, 0.2219, 0.5460, -0.6730, -0.6891, 0.6349, -0.1973, 0.3368, 0.7735, 0.9009, 0.3849, 0.3837, 0.2657, -0.0806, 0.6109, -1.2894, -0.2231, -0.6158, 0.2170, 0.3561, 0.4450, 0.6089, -1.1633, -1.1579, 0.3612, 0.1047, -0.7832, 1.4352, 0.1863, -0.2611, 0.8328, -0.2312, 0.3248, 0.1449, -0.4455, 0.3350, -0.9595, -0.0975、0.4814、-0.4335、0.6945、0.9104、-0.2817、0.4164、-1.2609、 0.7128, 0.2378])

它是一个尺寸为 (50,) 的火炬张量。如果有的话,很难确定这个嵌入中的每个数字意味着什么。然而,我们知道这个嵌入空间是有结构的。也就是说,这个嵌入空间中的距离是有意义的。

测量距离

为了探索嵌入空间的结构,有必要引入距离的概念。您可能已经熟悉欧几里得距离的概念。两个向量的欧几里得距离 x=[x1,x2,...xn] 而 y=[y1,y2,...yn] 只是它们的差 x−y 的 2 范数。

PyTorch 函数 torch.norm 为我们计算向量的 2 范数,因此我们可以像这样计算两个向量之间的欧几里得距离:

x = glove['cat']
y = glove['dog']
torch.norm(y - x)

张量(1.8846)

余弦相似度是另一种距离度量。余弦相似度测量两个向量之间的角度,并且具有只考虑向量的方向而不考虑它们的大小的特性。 (下节课我们会用到这个属性。)

x = torch.tensor([1., 1., 1.]).unsqueeze(0)
y = torch.tensor([2., 2., 2.]).unsqueeze(0)
torch.cosine_similarity(x, y) # should be one

张量([1.])

余弦相似度是一种相似度度量而不是距离度量:相似度越大,词嵌入彼此之间就越“接近”。

x = glove['cat']
y = glove['dog']
torch.cosine_similarity(x.unsqueeze(0), y.unsqueeze(0))

张量([0.9218])

单词相似度

现在我们在嵌入空间中有了距离的概念,我们可以讨论在嵌入空间中彼此“接近”的单词。现在,让我们使用欧几里得距离来看看各种单词与“猫”这个词的接近程度。

word = 'cat'
other = ['dog', 'bike', 'kitten', 'puppy', 'kite', 'computer', 'neuron']
for w in other:
    dist = torch.norm(glove[word] - glove[w]) # euclidean distance
    print(w, float(dist))

狗 1.8846031427383423

自行车 5.048375129699707

小猫 3.5068609714508057

小狗 3.0644655227661133

风筝 4.210376262664795

计算机 6.030652046203613

神经元 6.228669166564941

事实上,我们可以在整个词汇表中查找最接近嵌入空间中某个点的单词——例如,我们可以查找最接近另一个单词(如“cat”)的单词。

def print_closest_words(vec, n=5):
    dists = torch.norm(glove.vectors - vec, dim=1)     # compute distances to all words
    lst = sorted(enumerate(dists.numpy()), key=lambda x: x[1]) # sort by distance
    for idx, difference in lst[1:n+1]:                         # take the top n
        print(glove.itos[idx], difference)

print_closest_words(glove["cat"], n=10)

狗 1.8846031

兔子 2.4572797

猴子 2.8102052

猫 2.8972247

老鼠 2.9455352

野兽2.9878407

怪物 3.0022194

宠物 3.0396757

蛇 3.0617998

小狗 3.0644655

print_closest_words(glove['nurse'])

医生 3.1274529

牙医 3.1306612

护士 3.26872

儿科医生 3.3212206

辅导员 3.3987114

print_closest_words(glove['computer'])

计算机 2.4362664

软件 2.926823

技术 3.190351

电子 3.5067408

计算 3.5999784

我们还可以查看哪些词最接近两个词的中点:

print_closest_words((glove['happy'] + glove['sad']) / 2)

快乐 1.9199749

感觉 2.3604643

对不起 2.4984782

几乎没有 2.52593

想象一下 2.5652788

print_closest_words((glove['lake'] + glove['building']) / 2)

环绕 3.0698414

3.1112068 附近

桥 3.1585503

沿着 3.1610188

岸上 3.1618817

类比

GloVe 向量的一个令人惊讶的方面是嵌入空间中的方向可能是有意义的。 GloVe 向量的结构类似于这样的某些类比关系:

国王-男人+女人≈王后

print_closest_words(glove['king'] - glove['man'] + glove['woman'])

王后 2.8391209

王子 3.6610038

伊丽莎白 3.7152522

女儿 3.8317878

寡妇 3.8493774

我们得到合理的答案,例如“女王”、“王位”和我们现任女王的名字。

我们也可以反过来打个比方:

print_closest_words(glove['queen'] - glove['woman'] + glove['man'])

国王 2.8391209

王子 3.2508988

皇冠 3.4485192

骑士 3.5587437

加冕 3.6198905

或者,沿着性别轴尝试不同但相关的类比:

print_closest_words(glove['king'] - glove['prince'] + glove['princess'])

王后 3.1845968

国王 3.9103293

新娘 4.285721

女士 4.299571

姐姐4.421178

print_closest_words(glove['uncle'] - glove['man'] + glove['woman'])

祖母 2.323353

阿姨2.3527892

孙女 2.3615322

女儿 2.4039288

大叔2.6026237

print_closest_words(glove['grandmother'] - glove['mother'] + glove['father'])

叔叔2.0784423

父亲 2.0912483

孙子 2.2965577

侄子 2.353551

长老2.4274695

print_closest_words(glove['old'] - glove['young'] + glove['father'])

父亲 4.0326614

儿子 4.4065413

祖父 4.51851

孙子 4.722089

女儿 4.786716

我们可以将嵌入移向“好”或“坏”的方向:

print_closest_words(glove['programmer'] - glove['bad'] + glove['good'])

多功能 4.381561

创意 4.5690007

企业家 4.6343737

启用 4.7177725

智能4.7349973

print_closest_words(glove['programmer'] - glove['good'] + glove['bad'])

黑客 3.8383653

故障 4.003873

发起人 4.041952

破解 4.047719

序列号 4.2250676

词向量中的偏差

机器学习模型给人一种“公平”的感觉,因为模型在没有人工干预的情况下做出决策。但是,模型可以并且确实可以学习训练数据中存在的任何偏差!

GloVe 向量似乎足够无害:它们只是某些嵌入空间中单词的表示。即便如此,我们将展示 GloVe 向量的结构编码了它们所训练的文本中存在的日常偏见。

我们将从一个类比的例子开始:

医生-男人+女人≈??

让我们使用 GloVe 向量来找到上述类比的答案:

print_closest_words(glove['doctor'] - glove['man'] + glove['woman'])

护士 3.1355345

怀孕的 3.7805371

孩子 3.78347

女人 3.8643107

母亲 3.922231

医生-男人+女人≈护士的类比非常令人担忧。只是为了验证一下,如果我们翻转性别术语,不会出现相同的结果:

print_closest_words(glove['doctor'] - glove['woman'] + glove['man'])

男人 3.9335632

同事 3.975502

他自己 3.9847782

兄弟3.9997008

另一个 4.029071

我们发现其他职业也存在类似的性别偏见。

print_closest_words(glove['programmer'] - glove['man'] + glove['woman'])

神童 3.6688528

心理治疗师 3.8069527

治疗师 3.8087194

介绍 3.9064546

瑞典出生的 4.1178856

除了第一个结果,其他的词都与编程无关!相反,如果我们翻转性别术语,我们会得到截然不同的结果:

print_closest_words(glove['programmer'] - glove['woman'] + glove['man'])

设置 4.002241

创新者 4.0661883

程序员 4.1729574

黑客 4.2256656

天才4.3644104

以下是“工程师”的结果:

print_closest_words(glove['engineer'] - glove['man'] + glove['woman'])

技术员 3.6926973

机械师 3.9212747

先锋4.1543956

开创性的4.1880875

教育者 4.2264576

print_closest_words(glove['engineer'] - glove['woman'] + glove['man'])

建造者 4.3523865

机械师 4.402976

工程师 4.477985

工作 4.5281315

替换 4.600204

【讨论】:

    猜你喜欢
    • 2020-06-18
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 2019-11-17
    • 2014-03-25
    • 1970-01-01
    • 2013-03-20
    相关资源
    最近更新 更多