首页 > 解决方案 > Gensim word2vec 和大量文本

问题描述

我需要将包含在 MySQL 数据库列(约 300 万行)中的文本放入令牌列表列表中。这些文本(它们是推文,因此它们通常很短)必须在包含在列表中之前进行预处理(必须删除停用词、主题标签、标签等)。该列表应稍后作为Word2Vec参数传递。这是涉及的代码部分

import mysql.connector
import re
from gensim.models import Word2Vec
import preprocessor as p
p.set_options(
    p.OPT.URL,
    p.OPT.MENTION,
    p.OPT.HASHTAG,
    p.OPT.NUMBER
)

conn = mysql.connector.connect(...)
cursor = conn.cursor()
query = "SELECT text FROM tweet"
cursor.execute(query)
table = cursor.fetchall()

stopwords = open('stopwords.txt', encoding='utf-8').read().split('\n')
sentences = []
for row in table:
    sentences = sentences + [[w for w in re.sub(r'[^\w\s-]', ' ', p.clean(row[0])).lower().split() if w not in stopwords and len(w) > 2]]

cursor.close()
conn.close()

model = Word2Vec(sentences)
...

显然这需要很多时间,而且我知道我的方法可能效率低下。谁能推荐一个更好的?我知道这不是一个直接相关的问题gensimWord2Vec但也许使用它们的人已经面临处理大量文本的问题。

标签: pythonnlpgensimword2vec

解决方案


您没有提到您的代码需要多长时间才能运行,但您当前技术的一些潜在减速源可能包括:

  • 基于正则表达式的预处理的开销,特别是如果大量独立的正则表达式分别应用于相同的文本
  • 通过一次添加一个新项目来扩展 Python 列表的效率低下 - 随着列表变大,这有时可能是一个因素
  • 虚拟内存交换,如果您的数据大小超过物理 RAM

您可以通过使用特定于平台的工具(如top在 Linux 系统上)监控内存使用情况来检查交换问题,以查看操作期间的内存使用情况。如果这是一个贡献者,使用具有更多 RAM 的机器,或进行其他代码更改以减少 RAM 使用(见下文),将有所帮助。

您的完整prprocessing代码未显示,但一种常见的方法是许多独立的步骤,每个步骤都涉及一个或多个正则表达式,但随后返回一个简单的修改字符串(用于以后的步骤)。

尽管它非常简单且可插拔,但它经常成为预处理大量文本时可避免的缓慢的来源。例如,每个正则表达式/步骤本身可能必须重复检测标记边界,或者拆分然后重新连接字符串。或者,正则表达式可能使用复杂的匹配模式或技术(如回溯),在最坏的情况下输入可能会很昂贵。

通常,这种预处理可以通过以下一项或多项来大大改进:

  • 将多个正则表达式合并到一个步骤中,因此字符串面向一个从前到后的传递,而不是 N
  • 尽早分解成短标记,然后将文本保留为标记列表以供后续步骤使用 - 因此永远不会冗余拆分/连接,并让以后面向标记的步骤处理较小的字符串,甚至可能更简单(非正则表达式)字符串-测试

此外,即使预处理仍然有点费时,一个大的流程改进通常是确保只有在数据发生变化时才重复它。也就是说,如果您要尝试一系列不同的下游步骤,例如不同的Word2Vec参数,请确保您不会每次都进行昂贵的预处理。执行一次,将结果写入文件,然后重复使用结果文件,直到需要重新生成(因为数据或预处理规则已更改)。

最后,如果 append-one-more 模式导致您的速度变慢,您可以预先分配sentences( sentences = [Null,] * desired_length),然后替换循环中的每一行而不是 append ( sentences[row_num] = preprocessed_text)。但这可能不是主要因素,事实上,上面关于“重用结果文件”的建议是一种更好的方法,可以最大限度地减少 list-ops/RAM-usage 的使用,并在交替运行中实现重用。

也就是说,在循环之前打开一个新的工作文件。将每个预处理的文本(在标记之间有空格,并在末尾添加一个换行符)作为一个新行添加到该文件中。然后,让您的Word2Vec步骤直接从该文件工作。(在 Gensim 中,您可以通过使用LineSentence实用程序对象包装文件来实现此目的,该实用程序对象将该格式的文件作为可重复序列读回,每个项目都是令牌列表,或者使用corpus_file参数来提供文件名直接到Word2Vec.)

从可能的策略列表中,我会尝试:

  • 首先,为您现有的代码进行预处理(创建您的sentences
  • 然后,消除所有花哨的预处理,不做任何比 更复杂的事情.split(),然后重新计时。如果有很大的变化,那么是的,预处理是主要的减速,并专注于改进它。
  • 如果即使是最小的预处理仍然看起来比预期的要慢,那么 RAM/连接问题可能是一个问题,并尝试写入临时文件。

另外:不必担心在 word2vec 训练中删除停用词 - 许多已发表的工作不会打扰该步骤,并且该算法已经包含一个sample参数,该参数会导致它在训练期间跳过很多非常多的词因为没那么有趣。同样,2 个甚至 1 个字符的标记可能仍然很有趣,尤其是在推文领域,因此您可能不想总是丢弃它们。(例如,单独的表情符号可能是重要的“单词”。)


推荐阅读