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 直接相关的问题,但也许使用它们的人已经面临处理大量文本的问题。

解决方法

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

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

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

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

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

通常可以通过以下一个或多个来极大地改进这种预处理:

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

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

最后,如果 append-one-more 模式导致您的缓慢,您可以预先分配您的 sentences (sentences = [Null,] * desired_length),然后替换循环中的每一行而不是追加 ({ {1}})。但这可能不是主要因素,事实上,上面关于“重用结果文件”的建议是最小化列表操作/RAM 使用量以及允许跨交替运行重用的更好方法。

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

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

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

另外:在 word2vec 训练中不必担心移除停用词——很多已发表的工作都没有涉及到这一步,并且算法已经包含一个 .split() 参数,这会导致它跳过很多训练期间被过度表达的单词不那么有趣。类似地,2 个甚至 1 个字符的标记可能仍然很有趣,尤其是在推文领域,因此您可能不想总是丢弃它们。 (例如,单独的表情符号可以是重要的“单词”。)