【问题标题】:Using word2vec to classify words in categories使用 word2vec 对类别中的单词进行分类
【发布时间】:2018-05-19 21:16:13
【问题描述】:

背景

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

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

我的目标是训练一个模型,该模型接受一个新的输入字符串并预测它属于哪个类别。例如,如果新输入是“紫色”,那么我应该能够将“颜色”预测为正确的类别。如果新输入是“Calgary”,它应该将“Places”预测为正确的类别。

方法

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

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

例如,对于输入“pink”,我可以计算它与向量“names”中单词的相似度,取一个平均值,然后对其他 2 个向量也这样做。给我最高相似度平均值的向量将是输入所属的正确向量。

问题

鉴于我在 NLP 和机器学习方面的知识有限,我不确定这是否是最佳方法,因此我正在寻求帮助和建议以更好地解决我的问题。我愿意接受所有建议,也请指出我可能犯的任何错误,因为我是机器学习和 NLP 领域的新手。

【问题讨论】:

  • 使用 spacy 的 ner,你也可以用你的数据训练 spacy 模型。
  • @AyodhyankitPaul 我现在就用谷歌搜索!感谢您的反馈,如果可能的话,如果您能提供小演示,会很高兴,很想看到这个

标签: python machine-learning nlp word2vec gensim


【解决方案1】:

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

为了运行它,您必须从 here 下载并解压缩预训练的 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 中的单词。请记住,模型大小仅取决于您拥有的数据和您可能希望能够查询的单词。

【讨论】:

  • 这很有趣。所以很明显,词嵌入已经被创建了。当我尝试 print(process('kobe')) 时,它将 'kobe' 归类为一个地方,即使 'kobe' 是一个名称,但是当我将 'kobe' 作为类型名称添加到数据字典中时,它会将 kobe 归类为一个名称。我试图了解幕后发生的事情。它给名字的分数最高(9.38),但地点类别的分数非常接近(9.08)。
  • 有些术语自然是在边界上。请记住,嵌入是从文本中学习的。例如,paris 经常用作城市和名称,Paris Hilton。神户也一样:我只知道一个名字的用法,虽然很受欢迎,但它在日本也是一个地方 - en.wikipedia.org/wiki/Kobe。这是分类中的常见问题。至于一般理解,请参阅此答案 - stackoverflow.com/a/46727571/712995 以及它所指的进一步链接
  • 1) 当然可以,但是您必须将 python dict 更改为元组列表。如果你想这样做的话,每个不同的词都有一个单独的系数索引会更简单。 2)负分是绝对有可能的,这里没问题。 3) 该解决方案使用已经训练过的模型。如果你想自己训练,这是完全可能的,但请记住训练数据的大小必须非常大才能产生影响。类似于维基百科的大小。
  • 它知道很多单词,因为它是在一个巨大的文本语料库上训练的。显然,其中有一些关于 a1a2、... 详细描述 GloVe 需要大量篇幅,您可以从这里开始:nlp.stanford.edu/projects/glove
  • 我认为这是一个不错且优雅的解决方案 (+1);并且边界上的术语,例如“神户”(我也知道它是一个地方,而不是一个名字),可以通过额外的后处理规则来解决(例如,当两个最高分之间的差异低于阈值时,返回两者等)
【解决方案2】:

另外,PyTorch 有一个更好更快的 Glove 的 implementation

【讨论】:

    猜你喜欢
    • 2010-10-31
    • 2017-07-21
    • 2023-03-21
    • 2020-10-08
    • 2018-01-07
    • 2016-10-02
    • 2014-10-31
    • 2018-09-13
    • 2019-02-20
    相关资源
    最近更新 更多