用 16k 行标记数据训练 textblob 不起作用(只有少数有效)

数据挖掘 Python 分类 多类分类 朴素贝叶斯分类器
2021-09-30 16:33:07

我在 csv 中有标记数据,如下所示:

title,type
Women Jacket A,Clothes
Mens Running Shoes B,Shoes
Children backpack,Bags

和一个脚本:

from textblob.classifiers import NaiveBayesClassifier

with open('train_small.csv', 'r') as fp:
    cl = NaiveBayesClassifier(fp, format="csv")

print ( cl.classify("Womens running shoe") )

如果我使用train_small.csv3 个类别和 32 行,我会得到正确且即时的打印结果。

如果我使用完整的训练集(train.csv16k 行,10 个类别),我不会得到结果。今天,我的笔记本电脑运行了 6 个小时,然后我不得不按下电源按钮将其关闭,因为它的负载为 60 并且没有响应。

系统是:

Operating System: Kubuntu 19.04
Kernel Version: 5.0.0-15-generic
OS Type: 64-bit
Processors: 4 × Intel® Core™ i5-7200U CPU @ 2.50GHz
Memory: 7.7 GiB of RAM

不是很好,但完全不够..?我需要使用另一个库/算法还是只是不同的硬件?

1个回答

Oh Wo​​w,textblob即使是合理数量的数据,它的默认设置也无法处理。

TL;DR:您确实需要更好地控制库的功能。

NaiveBayesClassifier,或者更确切地说它的超类NLTKClassifier使用默认的 of feature_extractor在这种情况下,它使用basic_extractor,除了以某种方式(与我们在这里无关)词干之外,它还执行以下操作:

features = dict(((u'contains({0})'.format(word), (word in tokens))
    for word in word_features))

每个文件(样本)一次。

哎哟! 这将为每个文档创建一个 python 字典。并将每个文档的术语频率存储在这些字典中。在 16k 行(文档/样本)上,这很可能是太多的内存。

在 Ubuntu 上,您可能有交换空间,这是用作内存的额外磁盘空间,您的操作系统发现它内存不足,开始将内存页面交换到此交换空间。然而,如果与实际内存相比,交换空间非常慢,因此计算需要很长时间。最后,正是这种交换空间和这种缓慢性使算法永远运行,而不是使您的 CPU 过热或使机器完全冻结和内存不足。

解决问题的更好方法

ML 技术一般不理解单词或字符串,它们只理解数字。 textblob对您隐藏这个事实,因为它在幕后执行特征提取。这适用于玩具问题,但可能会损害任何更复杂问题的解决方案(可能是因为问题复杂性或数据量,或两者兼而有之)。

您需要自己控制特征提取。朴素贝叶斯分类器是一种相当简单的算法,可以很好地与多种提取技术配合使用。只要您不使用昂贵的 python 字典将频率保存在内存中,术语频率(简单地计算每个文档中每个不同单词的数量)就可以很好地工作。但是,一旦您控制了特征提取,就可以使用更多技术:TF-IDF、n-gram。我觉得sklearn对文本特征的解释很有启发性但我离题了。

不幸的是, nltk(下面的库texblob)并没有让事情变得更容易。它要求其训练集具有字典接口。换句话说,如果没有大量的 hack,nltk就无法在大量数据上进行训练。

nltk选项1:直接挂钩

首先让我们假设您的文件已命名data.csv,我们将尝试提取逐行读取文件的特征。首先让我生成一个数据文件:

import nltk
nltk.download('gutenberg')  # just in case
from nltk.corpus import gutenberg
corpus = gutenberg.words('melville-moby_dick.txt')
with open('data.csv', 'w') as f:
    for i in range(10**4):  # 10k
        f.write(' '.join([w for w in corpus[i*3:i*3+3] if w != ','])
                + ',' + str(i%2) + '\n')
f.close()

这只是一些数据的生成,与手头的问题没有太大关系。我没有你的文件,所以我只是随机使用 Moby Dick 文本中的单词生成一个随机的 10k 行 CSV。

进入实际代码。我们仍将使用texblob's朴素贝叶斯,但不允许它构建庞大的字典列表。相反,我们将直接挂钩:

from textblob.classifiers import NaiveBayesClassifier
import csv

cls = NaiveBayesClassifier('')

class FeatureDict(object):
    def __iter__(self):
        with open('data.csv') as f:
            for row in csv.reader(f):
                yield {w: w for w in row[0].split()}, row[1]

cls.train_features = FeatureDict()  # this is passed to nltk
cls.classify('bang Boom!')

这是一个 hack,但在不断的内存中运行。

选项2:切换到sklearn

sklearn的矢量化器将在(或在' 数据帧内NumPy的 NumPy 数组内)执行操作。pandas哪些是内存效率高的——尤其是与数千个 python 字典相比。

sklearn需要分两步执行此操作:一个用于数据矢量化,另一个用于预测 make_pipeline加入步骤。假设data.csv我们可以做相同的数据准备:

from sklearn.feature_extraction.text import tfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
import pandas as pd

data = pd.read_csv('data.csv', names=['doc', 'label'])
# some words are badly formatted, sklearn have issues with them
data = data.dropna()
model = make_pipeline(TfidfVectorizer(), MultinomialNB())
model.fit(data['doc'], data['label'])
model.predict(['buggy ho!'])

sklearn解决方案稍微优雅一些​​,但可能会在某些时候耗尽内存(可能是 500k 样本)。在那种情况下,一个人需要破解sklearn自己才能在不断的记忆中做事。