问题 使用word2vec对类别中的单词进行分类


背景

我有一些带有一些样本数据的向量,每个向量都有一个类别名称(地点,颜色,名称)。

['john','jay','dan','nathan','bob']  -> 'Names'
['yellow', 'red','green'] -> 'Colors'
['tokyo','bejing','washington','mumbai'] -> 'Places'

我的目标是训练一个模型,该模型采用新的输入字符串并预测它属于哪个类别。例如,如果新输入为“紫色”,那么我应该能够将“颜色”预测为正确的类别。如果新输入是“卡尔加里”,它应该将“地点”预测为正确的类别。

APPROACH

我做了一些研究并遇到过 Word2vec。该库具有“相似性”和“最相似”功能,我可以使用它。所以我想到的一种蛮力方法如下:

  1. 接受新的投入。
  2. 计算它与每个向量中每个单词的相似度并取平均值。

因此,例如对于输入“粉红色”,我可以计算其与向量“名称”中的单词的相似性取平均值,然后对其他2个向量也执行此操作。给出最高相似度平均值的向量将是输入所属的正确向量。

问题

鉴于我在NLP和机器学习方面的知识有限,我不确定这是否是最好的方法,因此我正在寻找有关解决问题的更好方法的帮助和建议。我对所有建议持开放态度,并且请指出我可能因为我是机器学习和NLP世界的新手而犯的任何错误。


2796
2017-12-06 04:16


起源

使用spacy's ner,您还可以使用您的数据训练spacy模型。 - Ayodhyankit Paul
@AyodhyankitPaul我现在就谷歌吧!感谢您的反馈,如果可能的话会喜欢它,如果您能提供小型演示,我很乐意看到这一点 - Dinero


答案:


如果您正在寻找最简单/最快速的解决方案,那么我建议您使用预先训练好的单词嵌入(Word2Vec或GloVe),并在其上构建一个简单的查询系统。这些向量已经在一个庞大的语料库中进行了训练,并且可能包含对您的域数据足够好的近似值。

以下是我的解决方案:

import numpy as np

# Category -> words
data = {
  'Names': ['john','jay','dan','nathan','bob'],
  'Colors': ['yellow', 'red','green'],
  'Places': ['tokyo','bejing','washington','mumbai'],
}
# Words -> category
categories = {word: key for key, words in data.items() for word in words}

# Load the whole embedding matrix
embeddings_index = {}
with open('glove.6B.100d.txt') as f:
  for line in f:
    values = line.split()
    word = values[0]
    embed = np.array(values[1:], dtype=np.float32)
    embeddings_index[word] = embed
print('Loaded %s word vectors.' % len(embeddings_index))
# Embeddings for available words
data_embeddings = {key: value for key, value in embeddings_index.items() if key in categories.keys()}

# Processing the query
def process(query):
  query_embed = embeddings_index[query]
  scores = {}
  for word, embed in data_embeddings.items():
    category = categories[word]
    dist = query_embed.dot(embed)
    dist /= len(data[category])
    scores[category] = scores.get(category, 0) + dist
  return scores

# Testing
print(process('pink'))
print(process('frank'))
print(process('moscow'))

为了运行它,您必须从中下载并解压缩预先训练好的GloVe数据 这里 (小心,800Mb!)。在运行时,它应该产生这样的东西:

{'Colors': 24.655489603678387, 'Names': 5.058711671829224, 'Places': 0.90213905274868011}
{'Colors': 6.8597321510314941, 'Names': 15.570847320556641, 'Places': 3.5302454829216003}
{'Colors': 8.2919375101725254, 'Names': 4.58830726146698, 'Places': 14.7840416431427}

......看起来很合理。就是这样!如果您不需要这么大的模型,可以过滤单词 glove 根据他们的 TF-IDF 得分了。请记住,模型大小仅取决于您拥有的数据和您可能希望查询的单词。


12
2017-12-11 17:54



这很有趣。显然,嵌入式这个词已经被创建了。当我尝试打印(进程('kobe'))时,它将'kobe'分类为一个地方,即使'kobe'是一个名字然而当我将'kobe'添加到数据字典作为类型名称时,它将kobe分类为名称。我试图了解幕后发生的事情。它的得分最高(9.38),但地方类别得分非常接近(9.08)。 - Dinero
有些术语自然是在边界上。请记住,嵌入是从文本中学习的。例如。, paris 帕丽斯·希尔顿经常被用作城市和名称。科比也是如此:我知道只有一种用法作为名称,虽然非常受欢迎,但它也是日本的一个地方 - en.wikipedia.org/wiki/Kobe 。这是分类中的常见问题。至于一般的理解,请看这个答案 - stackoverflow.com/a/46727571/712995 它指的是进一步的链接 - Maxim
1)当然会,但你必须将python dict更改为元组列表。如果你想这样做,每个不同的单词有一个单独的系数索引会更简单。 2)负分是绝对可能的,这里没问题。 3)该解决方案使用了 已经训练过 模型。如果你想自己训练,这是完全可能的,但是记住训练数据的大小必须非常大才能有所作为。与维基百科的大小相当的东西。 - Maxim
它知道很多单词,因为它是在一个巨大的文本语料库上训练的。显然,这有一些关于 a1, a2,...详细描述GloVe需要很大的空间,你可以从这里开始: nlp.stanford.edu/projects/glove - Maxim
我认为这是一个很好的和优雅的解决方案(+1);边界上的术语,例如'kobe'(我也知道它是一个地方,而不是名称),可以通过额外的后处理规则来解决(例如,当两个最高分之间的差异低于阈值时,返回两个等) - desertnaut


还有,它的价值, PyTorch 有一个好的,更快的 履行 这些天手套


0
2018-05-14 16:43