使用word2vec对单词进行分类

16

背景

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

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

我的目标是训练一个模型,它可以接受新的输入字符串并预测它属于哪个类别。例如,如果新输入是“紫色”,那么我应该能够预测“颜色”是正确的类别。如果新输入是“卡尔加里”,它应该预测“地点”是正确的类别。
方法:
我进行了一些研究,并发现了Word2vec。这个库有一个“相似性”和“最相似性”函数,我可以使用它们。因此,我想到了以下一种蛮力方法:
1.获取新输入。 2.计算它与每个向量中每个单词的相似度并取平均值。
例如,对于输入“粉红色”,我可以计算它与向量“名称”中的单词的相似度,取平均值,然后对其他2个向量也做同样的操作。给我提供最高相似度平均值的向量将是输入所属的正确向量。
问题:
鉴于我在自然语言处理和机器学习方面的知识有限,我不确定这是否是最好的方法,因此我正在寻求帮助和建议,以找到更好的方法来解决我的问题。我接受所有建议,并请指出我可能犯的任何错误,因为我是机器学习和自然语言处理领域的新手。

2
使用Spacy的NER功能,您还可以使用自己的数据训练Spacy模型。 - Aaditya Ura
1
@AyodhyankitPaul 我现在就去谷歌一下!感谢您的反馈,如果可能的话,我希望您能提供一个小演示,我很想看看这个。 - Dinero
2个回答

30

如果您正在寻找最简单/最快的解决方案,那么我建议您使用预训练的词嵌入(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}

...看起来相当合理。就是这样!如果您不需要如此庞大的模型,可以根据它们的tf-idf得分过滤glove中的单词。请记住,模型大小仅取决于您拥有的数据和可能要查询的单词。


1
这非常有趣。很明显,单词嵌入已经被创建了。当我尝试打印(process('kobe'))时,它将“kobe”分类为地点,即使“kobe”是一个名字,但当我将“kobe”添加到数据字典中作为类型名称时,它将kobe分类为名字。我正在尝试理解发生了什么。它给了名字最高的分数(9.38),但地点类别的分数也相当接近(9.08)。 - Dinero
3
有些术语自然处于边缘地带。请记住,嵌入是从文本中学习的。例如,“巴黎”经常被用作城市和名字(帕里斯·希尔顿)。同样,科比:我只知道它作为一个非常流行的名字,但它也可以是日本的一个地方——https://en.wikipedia.org/wiki/Kobe。这是分类中的一个常见问题。至于一般的理解,请参阅此答案 - https://dev59.com/xaXja4cB1Zd3GeqPYezT#46727571 以及它所引用的链接。 - Maxim
2
  1. 当然可以,但你需要将Python字典更改为元组列表。如果您想这样做,最简单的方法是针对不同单词使用单独的系数索引。
  2. 负分数完全可行,没有问题。
  3. 此解决方案使用已经训练好的模型。如果您想自己训练它,完全可以,但请记住,训练数据的大小必须非常大才能产生差异。与维基百科大小相当的东西。
- Maxim
2
它知道很多单词,因为它是在一个巨大的文本语料库上进行训练的。显然,其中有关于a1a2等的内容。详细描述GloVe需要很多空间,您可以从这里开始:https://nlp.stanford.edu/projects/glove/ - Maxim
2
我认为这是一个不错且优雅的解决方案(+1);而边缘上的术语,比如“kobe”(我也知道它是一个地名,而不是一个名字),可以通过额外的后处理规则来解决(例如,当两个最高分之间的差异低于一个阈值时,返回两个等等)。 - desertnaut
显示剩余7条评论

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接