我正在尝试通过输入单词列表来计算语义相似度,并输出一个单词,该单词是列表中最相似的单词。
例如:
如果我传入一个单词列表:
words = ['portugal', 'spain', 'belgium', 'country', 'netherlands', 'italy']
它应该给我输出类似这样的东西-
['country']
我正在尝试通过输入单词列表来计算语义相似度,并输出一个单词,该单词是列表中最相似的单词。
例如:
如果我传入一个单词列表:
words = ['portugal', 'spain', 'belgium', 'country', 'netherlands', 'italy']
['country']
GloVe嵌入
为了加载预训练的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
让我们来看看单词“car”的嵌入是什么样子:
glove['cat']
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))
张量(tensor)[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
实际上,我们可以查找整个词汇表中距离嵌入向量空间另一个单词(例如“猫”)最接近的单词。
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向量的结构具有某些类比关系,例如这种关系通常成立:
king−man+woman≈queen
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向量结构编码了它们所训练的文本中存在的日常偏见。
我们将从一个例子类比开始:
doctor−man+woman≈??
让我们使用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'])
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
首先,需要从https://drive.google.com/file/d/0B7XkCwpI5KDYNlNUTTlSS21pQmM/edit下载在 Google News 上训练的预训练的 word2vec。
然后,可以按如下方式计算单词嵌入之间的 余弦相似度:
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])
如果你所说的相似性是指单词嵌入之间的余弦相似度,那么"country"与其他单词的相似度最低。
.doesnt_match()
,用于计算哪个单词与所有单词的平均值最不相似:https://radimrehurek.com/gensim/models/keyedvectors.html#gensim.models.keyedvectors.Word2VecKeyedVectors.doesnt_match - 想要进行“最中心化”计算的人可以根据该方法的源代码来构建模型。 - gojomo