Gensim word2vec和大量文本
Gensim word2vec and large amount of texts
我需要将 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)
...
显然这需要很多时间,而且我知道我的方法可能效率低下。谁能推荐一个更好的?我知道这不是与 gensim
和 Word2Vec
直接相关的问题,但也许使用它们的人已经面临处理大量文本的问题。
您没有提到您的代码需要多长时间才能完成 运行,但您当前技术的一些潜在减速原因可能包括:
- 基于正则表达式的预处理的开销,尤其是当大量独立的正则表达式分别应用于相同的文本时
- 通过一次添加一个新项目来扩展 Python 列表的效率低下 - 随着列表变大有时可能是一个因素
- 虚拟内存交换,如果您的数据大小超过物理 RAM
您可以通过使用特定于平台的工具(如 Linux 系统上的 top
)监控内存使用情况来检查交换问题,以查看操作期间的内存使用情况。如果这是一个贡献者,使用具有更多 RAM 的机器,或进行其他代码更改以减少 RAM 使用(见下文),将会有所帮助。
您的完整 prprocessing
代码未显示,但一种常见的方法是许多独立的步骤,每个步骤都涉及一个或多个正则表达式,然后 returns 一个简单的修改字符串(用于后续步骤)。
尽管如此吸引人的简单性和可插入性,但在预处理大量文本时,它常常成为本可避免的缓慢的根源。例如,每个 regex/step 本身可能必须重复检测标记边界,或者拆分然后重新连接字符串。或者,正则表达式可能使用复杂的匹配模式或技术(如回溯),这些技术在最坏情况下的输入可能代价高昂。
通常可以通过以下一种或多种方式大大改进这种预处理:
- 将多个正则表达式合并到一个步骤中,因此一个字符串面临一个从前到后的传递,而不是 N
- 尽早分解成短标记,然后将文本作为标记列表留给后面的步骤——因此永远不会冗余 splitting/joining,让后面的面向标记的步骤处理更小的字符串,甚至可能更简单的(非正则表达式)字符串测试
此外,即使预处理仍然有点耗时,但大的过程改进通常是确保仅在数据发生变化时重复。也就是说,如果您要尝试一系列不同的下游步骤,例如不同的 Word2Vec
参数,请确保您不会每次都进行昂贵的预处理。做一次,将结果写到文件中,然后重复使用结果文件,直到需要重新生成(因为数据或预处理规则发生了变化)。
最后,如果 append-one-more 模式导致你的速度变慢,你可以预先分配你的 sentences
(sentences = [Null,] * desired_length
),然后替换循环中的每一行而不是追加(sentences[row_num] = preprocessed_text
)。但这可能不是主要因素,事实上上面关于“重用结果文件”的建议是最小化 list-ops/RAM-usage 以及跨备用 运行 启用重用的更好方法。
也就是说,在循环之前打开一个新的工作文件。将每个预处理文本(标记之间有空格,末尾有一个换行符)作为一个新行附加到此文件。然后,让您的 Word2Vec
步骤直接从该文件开始工作。 (在 Gensim 中,您可以通过使用 LineSentence
实用程序对象包装文件来完成此操作,该实用程序对象将那种格式的文件作为可重复序列读回,每个项目都是一个标记列表, 或 通过使用 corpus_file
参数将文件名直接提供给 Word2Vec
。)
从可能的策略列表中,我会尝试:
- 首先,对现有代码进行预处理(创建
sentences
- 然后,消除所有花哨的预处理,不做比
.split()
更复杂的事情,然后重新计时。如果有很大的变化,那么是的,预处理是主要的减速,并集中精力改进它。
- 如果即使是最小的预处理看起来仍然比预期的要慢,那么可能 RAM/concatenation 问题是一个问题,并尝试写入一个临时文件。
另外:在 word2vec 训练中完全没有必要担心删除停用词 - 许多已发表的作品都不会理会这一步,并且该算法已经包含一个 sample
参数,这会导致它跳过训练期间很多被过度表达的词都变得不那么有趣了。同样,2 个甚至 1 个字符的标记可能仍然很有趣,尤其是在推文领域,因此您可能不想总是丢弃它们。 (例如,单独的表情符号可能很重要 'words'。)
我需要将 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)
...
显然这需要很多时间,而且我知道我的方法可能效率低下。谁能推荐一个更好的?我知道这不是与 gensim
和 Word2Vec
直接相关的问题,但也许使用它们的人已经面临处理大量文本的问题。
您没有提到您的代码需要多长时间才能完成 运行,但您当前技术的一些潜在减速原因可能包括:
- 基于正则表达式的预处理的开销,尤其是当大量独立的正则表达式分别应用于相同的文本时
- 通过一次添加一个新项目来扩展 Python 列表的效率低下 - 随着列表变大有时可能是一个因素
- 虚拟内存交换,如果您的数据大小超过物理 RAM
您可以通过使用特定于平台的工具(如 Linux 系统上的 top
)监控内存使用情况来检查交换问题,以查看操作期间的内存使用情况。如果这是一个贡献者,使用具有更多 RAM 的机器,或进行其他代码更改以减少 RAM 使用(见下文),将会有所帮助。
您的完整 prprocessing
代码未显示,但一种常见的方法是许多独立的步骤,每个步骤都涉及一个或多个正则表达式,然后 returns 一个简单的修改字符串(用于后续步骤)。
尽管如此吸引人的简单性和可插入性,但在预处理大量文本时,它常常成为本可避免的缓慢的根源。例如,每个 regex/step 本身可能必须重复检测标记边界,或者拆分然后重新连接字符串。或者,正则表达式可能使用复杂的匹配模式或技术(如回溯),这些技术在最坏情况下的输入可能代价高昂。
通常可以通过以下一种或多种方式大大改进这种预处理:
- 将多个正则表达式合并到一个步骤中,因此一个字符串面临一个从前到后的传递,而不是 N
- 尽早分解成短标记,然后将文本作为标记列表留给后面的步骤——因此永远不会冗余 splitting/joining,让后面的面向标记的步骤处理更小的字符串,甚至可能更简单的(非正则表达式)字符串测试
此外,即使预处理仍然有点耗时,但大的过程改进通常是确保仅在数据发生变化时重复。也就是说,如果您要尝试一系列不同的下游步骤,例如不同的 Word2Vec
参数,请确保您不会每次都进行昂贵的预处理。做一次,将结果写到文件中,然后重复使用结果文件,直到需要重新生成(因为数据或预处理规则发生了变化)。
最后,如果 append-one-more 模式导致你的速度变慢,你可以预先分配你的 sentences
(sentences = [Null,] * desired_length
),然后替换循环中的每一行而不是追加(sentences[row_num] = preprocessed_text
)。但这可能不是主要因素,事实上上面关于“重用结果文件”的建议是最小化 list-ops/RAM-usage 以及跨备用 运行 启用重用的更好方法。
也就是说,在循环之前打开一个新的工作文件。将每个预处理文本(标记之间有空格,末尾有一个换行符)作为一个新行附加到此文件。然后,让您的 Word2Vec
步骤直接从该文件开始工作。 (在 Gensim 中,您可以通过使用 LineSentence
实用程序对象包装文件来完成此操作,该实用程序对象将那种格式的文件作为可重复序列读回,每个项目都是一个标记列表, 或 通过使用 corpus_file
参数将文件名直接提供给 Word2Vec
。)
从可能的策略列表中,我会尝试:
- 首先,对现有代码进行预处理(创建
sentences
- 然后,消除所有花哨的预处理,不做比
.split()
更复杂的事情,然后重新计时。如果有很大的变化,那么是的,预处理是主要的减速,并集中精力改进它。 - 如果即使是最小的预处理看起来仍然比预期的要慢,那么可能 RAM/concatenation 问题是一个问题,并尝试写入一个临时文件。
另外:在 word2vec 训练中完全没有必要担心删除停用词 - 许多已发表的作品都不会理会这一步,并且该算法已经包含一个 sample
参数,这会导致它跳过训练期间很多被过度表达的词都变得不那么有趣了。同样,2 个甚至 1 个字符的标记可能仍然很有趣,尤其是在推文领域,因此您可能不想总是丢弃它们。 (例如,单独的表情符号可能很重要 'words'。)