天行九歌 2018-09-07
众所周知,word嵌入在许多NLP任务中非常强大。目前的大多数实现都使用了所谓的“分布假设”,即在相似语境下的单词具有相似的含义。人类不仅可以通过上下文来判断两个词是否相似,还可以通过观察或想象这个词可能代表的物体来判断。很多时候,相似的物体与具有相似含义的词有关,比如“car”和“truck”、“house”和“building”、“dog”和“cat”。
在这篇文章中,我想探索这个想法,并尝试创建一个词嵌入,以保持词的语义意义,我将使用一个神经网络训练图像。
我想使用相关ResNet模型创建单词嵌入,这是一个非常流行的图像分类器,它是在'ImageNet'数据集上训练的。如ImageNet网站所述“ImageNet是根据WordNet层次结构组织的图像数据库(目前只是名词)”。它在ImageNet数据集上进行了训练,该数据集包含超过1400万个图像和1000个类。我将使用模型最后一个inner层的预测值来创建单词向量,并假设如果对应的图像相似,单词就有相似的含义。
我们来看一个小例子。我们有4个词:truck, wine, car和a bottle.
作为人类,我们可以说“truck”和“car”比“truck”和“bottle”更相近,或者“wine”和“bottle”比“wine”和“car”更相近。事实上,这些词的形象看起来很相似。这些物品在现实生活中看起来很相近。
在这里,我将只为名词单词创建嵌入,因为它们的图像可能比“to”、“and”、“the”等词的图像更好地描述实际单词。
首先,导入Python依赖库
%matplotlib inline
import matplotlib.pyplot as plt
import nltk
from nltk.corpus import wordnet as wn
from gensim.models import KeyedVectors
from gensim.models import KeyedVectors
import scipy
import cv2
from keras.applications.resnet50 import ResNet50
from google_images_download import google_images_download
from sklearn.manifold import TSNE
我们需要找到一长串名词。我们将使用NLTK:
nouns = set()
for synset in list(wordnet.all_synsets('n')):
n = synset.name().split('.')[0]
if len(n) > 2 and n.isalpha():
nouns.add(n)
经过一些清洁,我们有大约39,000个名词。实际上,并非所有这些都是名词。
接下来,我们想要获得我们拥有的每个单词的图像。我们将使用Google图片搜索。有一个python库google_images_download ,它将帮助我们。我们想要重新缩放图像,以便所有图像都具有相同的大小。然后,我们将使用ResNet创建单词vector。我使用网络的最后一层softmax,Python代码如下:
resnet = ResNet50(weights='imagenet',
include_top=False,
pooling='avg')
embeddings = KeyedVectors(2048)
for word in nouns:
response = google_images_download.googleimagesdownload()
path = response.download({'keywords': word, 'limit': 1})[word][0]
img = cv2.imread(path)
img = scipy.misc.imresize(img, 224.0 / img.shape[0])
img = img.reshape((1,) + img.shape)
embeddings[word] = resnet.predict(img)[0]
我们来看看我们的嵌入:
labels, vectors = zip(*map(lambda v: (v, embeddings[v]), embeddings.vocab))
tsne = TSNE(n_components=2, n_iter=1000, perplexity=3, metric='cosine', verbose=1)
transformed = tsne.fit_transform(vectors)
x = transformed[:,0]
y = transformed[:,1]
plt.figure(figsize=(10, 10))
for i in range(len(x)):
plt.scatter(x[i],y[i])
plt.annotate(labels[i],
xy=(x[i], y[i]),
xytext=(5, 2),
textcoords='offset points',
ha='right',
va='bottom')
plt.show()
看起来不错。我们可以看到“car”和“truck”这个词比“car”和“dog”更相似
embeddings.similarity('car', 'truck') # = 0.419
embeddings.similarity('car', 'dog') # = 0.270
这并不奇怪,这只是“car”、“truck”和“dog”形象的相似之处!我们使用ResNet模型的inner层来获得这些图像的一般表示,这样比较起来就更容易了。
我们的嵌入可以保留单词之间的语义相似性,但是它忽略了其他嵌入所具有的非常有趣的部分。在我们的嵌入中,我们不能像皇后-国王=女人-男人那样做,因为我们的嵌入只捕获两个词/对象之间的相似性,但它不能捕获单词之间更复杂的关系。我们这里不使用英语,我们只看图像之间的相似性。
现在我想用我的新嵌入构建一个简单的翻译系统。这很简单,给定任何语言中的任何单词,我们将使用谷歌图像搜索来找到对应的图像,然后我们将使用ResNet来预测最终的层值,最后,找到与这些预测值最相似的英语单词,Python代码如下:
def translate(word):
response = google_images_download.googleimagesdownload()
path = response.download({'keywords': word, 'limit': 1})[word][0]
img = cv2.imread(path)
img = scipy.misc.imresize(img, 224.0 / img.shape[0])
img = img.reshape((1,) + img.shape)
vector = resnet.predict(img)[0]
return embedding.most_similar([vector])[0]
让我们看一些例子:
>>> translate("מכונית") # 'car' in Hebrew
Output: clio # Brand of car (Renault Clio)
>>> translate("ristorante") # 'restaurant' in Italian
Output: 'grubstake' # A restaurant in San Fransisco
>> Translate("еда") # 'meal' in Russian
Output: thanksgiving
正如你所看到的那样,它并不完美,但确实输出了一些有趣的翻译。
这种“嵌入”实际上是在ResNet图像表示之上的简单“K最近邻”模型。它并不是真正翻译单词,但有一点好处是我们能够比较(或分类)数千种类型的单词/图像/类,而原始ResNet模型仅在1000个类上进行训练。