【问题标题】:Gensim word2vec and large amount of textsGensim word2vec 和大量文本
【发布时间】:2021-07-02 00:05:20
【问题描述】:

我需要将 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 nlp gensim word2vec


    【解决方案1】:

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

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

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

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

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

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

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

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

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

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

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

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

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

    【讨论】:

    • 感谢您的详细解答。但是,如果我只上传一个月的推文(约 250,000 条),则需要 10 分钟。我试图通过上传所有推文来进行测试,但 7 小时后它上传了大约 1,300,000 条。正如您所建议的,我将尝试通过提前定义列表的长度并将预处理操作限制在必要的范围内来进行一些测试。
    • 更新:在开始时设置列表大小就足以大幅减少时间(大约 10 分钟内已将大约 300 万条文本加载到列表中 - 包括预处理和标记化)
    猜你喜欢
    • 1970-01-01
    • 2021-01-10
    • 2020-05-10
    • 2020-07-11
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    • 2017-03-20
    • 2019-10-15
    相关资源
    最近更新 更多