这是我的后续行动上一个问题。正如 Tim Peters 所建议的,使用Manager
可能不一定是最好的方法。不幸的是我有太多的脚手架代码来发布SSCCE。相反,我将尝试提供我的问题的详细解释。请随意浏览整个代码库Github,但现在有点混乱。
背景
我正在研究自然语言处理,我想做(类似的)基于字典的文档分类平滑。训练分类器将单词和短语与正确答案联系起来的想法。例如,包含单词的文档socialist
很可能与政治有关,并且包含短语的内容lava temperature
可能与地质有关。该系统通过查看少数预先标记的示例。由于语言多种多样,分类器永远不会“了解”它在生产中可能遇到的所有可能的短语。
这就是字典的用武之地。假设我们有一种便宜又简单的方法获得几乎所有短语的同义词(我会引用自己,因为它的品味很差)。当较差的分类器遇到它不知道的短语时,我们可以在字典中查找它并告诉分类器“看,你不知道communism
,但这有点像socialist
,你知道这一点!”。如果字典合理,分类器通常会表现得更好。
伪代码
data = Load training and testing documents (300MB on disk)
dictionary = Load dictionary (200MB - 2GB on disk) and place into a `dict` for fast look-ups
Repeat 25 times:
do_work(data, dictionary)
def do_work(data, dictionary)
X = Select a random sample of data
Train a classifier on X
Y = Select a random sample of data
Using dictionary, classify all documents in Y
Write results to disk
问题
上面的循环是并行化的完美候选者。我一直在使用Python 2.7multiprocessing.Pool
(通过joblib.Parallel
,因为它很简单,并且在出现问题时提供非常有用的回溯)。所有工作进程都需要对字典和文档集合进行只读访问。工作进程不需要相互通信,也不需要与父进程通信——它们所做的只是生成、执行一些魔法、写入文件然后死亡。
字典需要支持快速随机访问。我不知道样本是什么文件Y
将包含,所以我不能轻易地修剪字典并只传递每个工人需要的部分。该字典将被经常查询 - 每次运行的典型命中次数为数百万。
目前,我的代码受内存限制,因为(我相信)正在为每个工作进程创建文档集合和字典的副本。解析时data
and dictionary
通常会消耗几 GB 的 RAM。我尝试过使用multiprocessing.managers.BaseManager
以避免复制大型对象,但这会减慢工作人员的速度。
问题
还有哪些其他替代方案可以加快速度?我考虑过的事情包括:
- MongoDB/CouchDB/memcached 应该可以很好地处理并发访问,但我担心吞吐量。在我之前的问题的评论中也建议了 Zeromq,但还没有机会研究它。
- 在记忆中
sqlite
数据库和数据库连接不能跨进程共享,因此每个工作线程都需要自己的磁盘数据库连接。这意味着一开始会产生大量 I/O,并且随着每个工作进程的缓存增长,内存使用量也会很高。
- 内存映射
- 使用线程而不是进程
这个问题还表明许多现实世界的问题看起来需要只读访问dict
可能会触发fork()
是写时复制,因此可能无法完全避免复制大对象。