我目前正在尝试使用 Python 和 Scikit-learn 构建一些文本分类工具。
我的文本不是英文的,因此不受词干分解或其他基于英文的降维的通常处理。
结果,TfIdf矩阵变得非常大(150,000x150,000)它可以使用普通PC进行处理,但是对它们运行网格搜索太多了,所以我求助于亚马逊网络服务来运行网格搜索. (我的参数集也很大)
这是我的代码:
# coding: utf-8
import os, json, codecs, nltk
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer,TfidfTransformer
from sklearn.grid_search import GridSearchCV
from time import time
from sklearn.pipeline import Pipeline
from sklearn.naive_bayes import MultinomialNB
print("Importing dataset...")
with open('y_data.json','r') as fp:
y = json.load(fp)
with open('dataset.json','r') as fp:
dataset = json.load(fp)
print("Importing stop words...")
with codecs.open('stopword.txt','r','utf-8') as fp:
stopword = []
for w in fp:
stopword.append(w.strip())
light_st = set(stopword)
with codecs.open('st_data.txt','r','cp874') as fp:
for w in fp:
stopword.append(w.strip())
heavy_st = set(stopword)
def pre_process_1(text):
return text.replace("|"," ")
def tokenize_1(text):
return text.split()
pipeline = Pipeline([('vec', CountVectorizer(encoding='cp874', preprocessor=pre_process_1, tokenizer=tokenize_1, stop_words=heavy_st, token_pattern=None)),('tfidf', TfidfTransformer()), ('clf', MultinomialNB())])
parameters = {
'vec__max_df': (0.5, 0.625, 0.75, 0.875, 1.0),
'vec__max_features': (None, 5000, 10000, 20000),
'vec__min_df': (1, 5, 10, 20, 50),
'tfidf__use_idf': (True, False),
'tfidf__sublinear_tf': (True, False),
'vec__binary': (True, False),
'tfidf__norm': ('l1', 'l2'),
'clf__alpha': (1, 0.1, 0.01, 0.001, 0.0001, 0.00001)
}
if __name__ == "__main__":
grid_search = GridSearchCV(pipeline, parameters, n_jobs=-1, verbose=2)
t0 = time()
grid_search.fit(dataset, y)
print("done in {0}s".format(time() - t0))
print("Best score: {0}".format(grid_search.best_score_))
print("Best parameters set:")
best_parameters = grid_search.best_estimator_.get_params()
for param_name in sorted(list(parameters.keys())):
print("\t{0}: {1}".format(param_name, best_parameters[param_name]))
这是我的软件环境的详细信息:
起初,我使用一个小得多的实例(r3.2xlarge;8 核)运行我的模型,但从计算中发现它需要很长时间(2 天)。所以,我决定扩大我的机器并使用最大的实例(我使用 r3,因为我的脚本非常占用内存);然而,它并没有我想象的那么快。
当我尝试监控 CPU 负载时(watch -n 5 uptime)...我发现即使我让它运行一段时间,平均 CPU 负载也不会超过 9。 (据我了解,一台 32 核的机器,当充分利用其所有内核时,应该在 32 左右)。
我试着改变
n_job
参数到不同的数字 (8, 32, 128) 具有相同的结果。 (但是,我认为该脚本会尝试按照指示运行尽可能多的作业,因为当我终止该进程时,我会看到类似...“Process ForkPoolWorker-30:”的内容,并且它们的回溯会掠过屏幕)
使用 ps x -C python3.4 命令进一步检查发现只有 8 个 python 进程正在运行。我推断这可能是 python 或操作系统的某种限制(我使用没有很多内核的 t2.micro 实例构建我的 AMI)所以,我决定重做我从头开始重建环境的工作,包括编译 Python使用 c3.4xlarge,并将操作系统更改为 Amazon Linux(我认为是 Fedora 的一个分支)以更好地与硬件兼容。
但是,我的脚本仍然没有超过 8 个内核。
最后,使用来自 Scikit-learn 网站的演示文本分类代码:http://scikit-learn.org/stable/auto_examples/grid_search_text_feature_extraction.html
(使用 SGDClassifier 而不是 MultinomialNB)它可以与所有 32 个内核完美运行!
所以......也许,与网格搜索算法和朴素贝叶斯分类器有关?
我正在考虑提交错误,但首先想知道这是朴素贝叶斯的预期行为还是我的代码有问题?
更新
我找不到直接测试内存带宽是否是罪魁祸首的方法。但是我尝试以各种方式对我的并行代码和 CPU 使用进行计时,以找出瓶颈发生的确切位置。
实验一:只进行向量化和变换。
使用我的真实数据作为输入(150,000 个文本文档;每个包含大约 130 个单词)
参数空间约为400。
多线程由 Joblib(与 Scikit-learn 使用的模块相同)完成。我有:
使用 8 个线程:在 841.017783164978 秒内完成并使用 24.636999999999993 % 的 CPU。
使用 16 个线程:在 842.9525656700134 秒内完成并使用 24.700749999999985 % 的 CPU。
使用所有 32 个线程:在 857.024197101593 秒内完成并使用 24.242250000000013 % 的 CPU。
结果清楚地表明矢量化过程无法随着处理能力的增加而扩展。
实验 2:这次我只对预先矢量化的数据执行 MultinomialNB。
像以前一样使用大约400的参数空间,我得到:
使用 8 个线程:在 2102.0565922260284 秒内完成并使用 25.486000000000054 % 的 CPU。
使用 16 个线程:在 1385.6887295246124 秒内完成并使用 49.83674999999993 % 的 CPU。
使用所有 32 个线程:在 1319.416403055191 秒内完成并使用 89.90074999999997 % 的 CPU。
从 8 个线程到 16 个线程的过渡显示出巨大的改进。但是,随着线程数增加到 32,完成的总时间只会稍微缩短一点,而 CPU 使用率却大幅增加。这点我不太明白。
实验 3:我将这两个过程结合在一起。
使用 8 个线程:在 3385.3253166675568 秒内完成并使用 25.68999999999995 % 的 CPU。
使用 16 个线程:在 2066.499200105667 秒内完成并使用 49.359249999999996 % 的 CPU。
使用所有 32 个线程:在 2018.8800330162048 秒内完成并使用 54.55375000000004 % 的 CPU。
我从自己的并行代码中获得的时间与从 GridsearchCV 获得的时间之间存在一些差异,但这可能是因为我在代码中进行了简化(我没有进行交叉验证或完整参数迭代,例如网格搜索)
结论
根据我的测试,我得出结论。 (如果我错了,请纠正我)
我认为我现在最好的选择是进行集群计算。 :)
最佳答案
看起来您的工作都是受内存限制的。
朴素贝叶斯是一个极其简单的模型,其训练算法由单个(稀疏)矩阵乘法和一些求和组成。同样,tf-idf 计算起来也很简单:它对输入求和,计算一些日志,然后存储结果。
其实NB就是这么简单,这个程序的瓶颈几乎肯定在CountVectorizer
,它会多次转换内存中的数据结构,直到将所有术语计数塞入正确的矩阵格式。如果并行执行大量操作,则可能会遇到内存带宽瓶颈。
(这都是有根据的猜测,但这是基于我参与 scikit-learn 开发。我是 MultinomialNB
的作者之一,也是许多入侵 CountVectorizer
以加快速度的人之一。)
关于python - 在多核机器上对 sklearn.naive_bayes.MultinomialNB 执行网格搜索不会使用所有可用的 CPU 资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26569478/