python - 如何使用更少的内存完成文本分类任务

标签 python memory numpy scikit-learn text-classification

(1)我的目标: 我正在尝试使用 SVM 将 10000 个文档(每个文档有 400 个单词)分类为 10 个类(均匀分布)。我工作中探索的特征包括单词 n-gram (n=1~4)、字符 n-gram(n=1~6)。

(2)我的方法:我使用文档中每个特征的频率值向量来表示每个文档。并使用 TF-IDF 对向量进行形式化。我的部分代码如下:

def commonVec(dicts,count1,count2):
    ''' put features with frequency between count1 and count2 into a common vector used for SVM training''' 
    global_vector = []
    master = {}
    for i, d in enumerate(dicts):
        for k in d:
            master.setdefault(k, []).append(i)
    for key in master:
        if (len(master[key])>=count1 and len(master[key])<=count2):  
            global_vector.append(key)
    global_vector1 = sorted(global_vector)
    return global_vector1 
def featureComb(mix,count1,count2,res1):
    '''combine word n-gram and character n-gram into a vector'''
    if mix[0]:
        common_vector1 = []
        for i in mix[0]:
            dicts1 = []
            for res in res1: #I stored all documents into database. res1 is the document result set and res is each document. 
                dicts1.append(ngram.characterNgrams(res[1], i)) # characterNgrams()will return a dictionary with feature name as the key, frequency as the value.
            common_vector1.extend(commonVec(dicts1, count1, count2))
    else:
        common_vector1 = []
    if mix[1]:
        common_vector2 = []
        for j in mix[1]:
            dicts2 = []
            for res in res1:
                dicts2.append(ngram.wordNgrams(res[1], j))        
            common_vector2.extend(commonVec(dicts2, count1, count2))
    else:
        common_vector2 = []
    return common_vector1+common_vector2

def svmCombineVector(mix,global_combine,label,X,y,res1):
    '''Construct X vector that can be used to train SVM'''
    lstm = []
    for res in res1:            
        y.append(label[res[0]]) # insert class label into y

        dici1 = {}
        dici2 = {}
        freq_term_vector = []
        for i in mix[0]:             
            dici1.update(ngram.characterNgrams(res[1], i))
        freq_term_vector.extend(dici1[gram] if gram in dici1 else 0 for gram in global_combine)    
        for j in mix[1]:
            dici2.update(ngram.wordNgrams(res[1], j))
        freq_term_vector.extend(dici2[gram] if gram in dici2 else 0 for gram in global_combine)
        lstm.append(freq_term_vector)
    freq_term_matrix = np.matrix(lstm)
    transformer = TfidfTransformer(norm="l2")
    tfidf = transformer.fit_transform(freq_term_matrix)
    X.extend(tfidf.toarray())

X = []
y = []
character = [1,2,3,4,5,6]
word = [1,2,3,4]
mix = [character,word]
global_vector_combine = featureComb(mix, 2, 5000, res1)
print len(global_vector_combine) # 542401
svmCombineVector(mix,global_vector_combine,label,X,y,res1)
clf1 = svm.LinearSVC()
clf1.fit(X, y)

(3)我的问题: 但是,当我运行源代码时,出现了内存错误。

Traceback (most recent call last):
      File "svm.py", line 110, in <module>
        functions.svmCombineVector(mix,global_vector_combine,label,X,y,res1)
      File "/home/work/functions.py", line 201, in svmCombineVector
        X.extend(tfidf.toarray())
      File "/home/anaconda/lib/python2.7/site-packages/scipy/sparse/compressed.py", line 901, in toarray
        return self.tocoo(copy=False).toarray(order=order, out=out)
      File "/home/anaconda/lib/python2.7/site-packages/scipy/sparse/coo.py", line 269, in toarray
        B = self._process_toarray_args(order, out)
      File "/home/anaconda/lib/python2.7/site-packages/scipy/sparse/base.py", line 789, in _process_toarray
    _args
        return np.zeros(self.shape, dtype=self.dtype, order=order)
    MemoryError

我真的很难用它,需要 stackoverflow 的帮助。

  1. 谁能解释一些细节并给我一些解决方法?
  2. 谁能检查我的源代码并告诉我其他一些更有效地利用内存的方法?

最佳答案

您面临的主要问题是您使用的功能太多。您已经设法从仅包含 400 个单词的文档中生成 542401 个特征,这实际上是非常了不起的!我见过 SVM 分类器仅使用 150 个特征就可以高精度地将垃圾邮件与非垃圾邮件分开——所选单词的字数可以充分说明文档是否是垃圾邮件。这些使用词干提取和其他规范化技巧来使功能更有效。

您需要花一些时间细化您的特征。考虑哪些特征最有可能包含对这项任务有用的信息。尝试不同的功能。只要你一直把除了厨房水槽之外的所有东西都扔进去,你就会出现内存错误。现在你正试图将 10000 个数据点传递给你的支持向量机,每个数据点有 542401 个维度。那是 542401 * 10000 * 4 = 21 GB(保守估计)的数据。我的电脑只有 4 GB 的 RAM。你必须这样削减。1

这样做的第一步是考虑您的总词汇量有多大。每个文档只有 400 个单词,但假设这 400 个单词是从 5000 个单词的词汇表中提取的。这意味着将有 5000 ** 4 = 6.25 * 10 ** 14 个可能的 4-gram。那是 五千万亿个可能的 4 克。当然,并非所有这些 4 克都会出现在您的文档中,但这对解释内存不足的原因大有帮助。你真的需要这 4 克吗?你能只用 2 克吗?可能有 5000 ** 2 = 2500 万个 2-gram。即使所有可能的 2-gram 出现(不太可能),这也更容易适应内存。

还要记住,即使 SVM 可以处理千万亿个数据点,它也可能会给出不好的结果,因为当你给任何学习算法太多的特征时,它会倾向于过度拟合,从不相关的模式中挑选出来并过度概括他们。有很多方法可以解决这个问题,但如果可以的话,最好不要处理它。

我还会提到这些不是“新手”问题。这些都是拥有博士学位的机器学习专家必须处理的问题。他们想出了很多聪明的解决方案,但我们没有那么聪明,所以我们必须以不同的方式聪明。

虽然我不能在不了解更多信息的情况下为您提供关于聪明的具体建议,但我要说的是,首先,至少在某些情况下,词干提取是一个好主意。词干提取只是消除了语法变形,因此同一词的不同形式(“swim”和“swimming”)被视为相同。这可能会显着减少你的词汇量,至少如果你正在处理英文文本。一个常见的选择是 porter stemmer ,包含在 nltk 中,以及一些other packages .此外,如果您还没有这样做,您可能应该去除标点符号并将所有单词减少为小写。从那里开始,它真的取决于。文体学(识别作者)有时只需要助词(“a”、“an”、“the”)、连词(“and”、“but”)和其他非常常见的词;另一方面,垃圾邮件有其自己感兴趣的古怪词汇表。在这个层面上,很难提前说出什么会起作用;您几乎肯定需要尝试不同的方法,看看哪种方法最有效。一如既往,测试至关重要!

<子>1。好吧,您可能有大量 RAM 可供使用。例如,我可以在我当前的工作场所访问一台具有 48G RAM 的机器。但我怀疑它也能处理这个问题,因为 SVM 将有自己的数据内部表示,这意味着在某个时候至少会有一个副本;如果在任何时候需要第二份副本 -- kaboom。

关于python - 如何使用更少的内存完成文本分类任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27689953/

相关文章:

c - madvise(DODUMP) 在与成功的 madvise(DONTDUMP) 相同的 ptr/size 上失败并返回 EINVAL

python - 如何使用从另一个变量确定的颜色绘制 3d 散点图?

python numpy`函数数组(lambda函数)

python - 删除 Pandas 中的评论行

python - 如何在Python中执行线性回归时减少rmse

java - 为 Java 应用程序设置 MaxDirectMemory 和 MaxHeapMemory

python - 使用 Sci-kit Learn SVM 时预测始终相同

python - 将自定义 pandas 函数应用于列时出现奇怪的行为

python - 设置项目级 Jupyter Notebook 配置文件

C++ 静态类成员,谁来收拾烂摊子?