为啥训练我的朴素贝叶斯分类器会占用这么多内存?

Posted

技术标签:

【中文标题】为啥训练我的朴素贝叶斯分类器会占用这么多内存?【英文标题】:Why does training my Naive Bayes Classifier take so much memory?为什么训练我的朴素贝叶斯分类器会占用这么多内存? 【发布时间】:2020-05-31 16:10:33 【问题描述】:

最近,我一直在做一个需要对 Twitter 数据进行情绪分析的项目。我正在使用 Textblob 库中的朴素贝叶斯分类器,并尝试使用 160 万条推文对其进行训练(如果有人想知道,可以在这里找到:https://www.kaggle.com/kazanova/sentiment140)。直接传入 160 万条推文会导致内存错误,因此我决定将其分块,以便一次只训练 1000 条推文。这取得了小小的成功,因为在我的计算机冻结之前,我只能在我的本地计算机上获得大约 10,000 条推文,因为我使用了太多的内存。然后我在 Google colab 上试用了它,这样我就可以在云中运行我的代码。使用 TPU 和 GPU,在会话崩溃并且我不得不重新启动运行时之前,我获得的最大推文也是 28,000 条。这是我的代码:

with open("shuffledlist.pickle", 'rb') as f: #Loading in my list of 1.6 million tweets
    full_data = pickle.load(f)

training_data = (tweet for tweet in full_data[:1500000]) 

try:
    with open("sentimentclassifier.pickle", "rb") as file: #makes a new classifier if one doesnt exist
        classifier = pickle.load(file)
        print("Got existing classifier")
except EOFError:
    classifier = NaiveBayesClassifier(full_data[:1000])
    print("Made new classifier")
del full_data

feeding_size = 1000
left_splice = 0
right_splice = feeding_size + left_splice

count = 0
new_start_time = time.time()
past_times = 0

while right_splice < 1500000:
    loop_time = time.time()
    data = itertools.islice(training_data,left_splice,right_splice)
    try:
        classifier.update(data)
    except Exception:
        print("Houston we got a problem")
        with open("sentimentclassifier.pickle", "wb") as sentiment:
             pickle.dump(classifier, sentiment, protocol = -1)
        sys.exit("Yo it ended at  and ".format(left_splice, right_splice))
    past_times += time.time() - loop_time
    count += 1
    string = "Left:  Right: . Took  seconds. Total Time Elapsed: . Average Time for each: . Count: ."\
        .format(left_splice, right_splice, time.time()-loop_time, time.time() - new_start_time, past_times/count, count)
    sys.stdout.write('\r' + string)
    left_splice += feeding_size
    right_splice += feeding_size
    with open("sentimentclassifier.pickle", "wb") as sentiment:
        pickle.dump(classifier, sentiment, protocol = -1)
        print("Done dumping cycle !".format(count))

print("Done! Right: , Left: !".format(left_splice, right_splice))

with open("sentimentclassifier.pickle", "wb") as sentiment:
    pickle.dump(classifier, sentiment, protocol = -1)


print("Training took  seconds!".format(time.time()-new_start_time))

一些注意事项:

由于我的主要问题是我的情绪分类器.pickle 文件有多大,我尝试使用 gzip,但是打开和关闭文件的时间太长了,这尤其糟糕,因为我需要打开文件每循环一次,因为我不想在程序崩溃时丢失任何进度。

我从使用列表切换到使用生成器,这确实显着提高了速度。

在 google colab 中,我尝试一次通过 10,000 个,这是最后的努力,不出所料,它没有达到最佳效果。

我不确定 nltk 的朴素贝叶斯分类器是否更有效,但我真的希望这是最后的手段,因为重新格式化我的推文列表可能需要几个小时。但如果它真的更有效,我会很乐意重做我的代码,如果这意味着我可以让它工作。

谢谢您,任何建议将不胜感激!

【问题讨论】:

【参考方案1】:

我会尝试在单独的训练文件中将训练数据拆分为可管理的块。与其打开一个包含 150 万条推文的文件,不如将该文件拆分为每个文件几千条推文。循环训练一个文件,关闭该文件,然后训练下一个文件。这样一来,您就不会一次将太多内容加载到 ram 中。它必须以您的方式将全部 150 万条推文保存在内存中。这会让你的 RAM 陷入困境。

要明确:

这个:

with open("shuffledlist.pickle", 'rb') as f: #Loading in my list of 160 万推文 full_data = pickle.load(f)

    >training_data = (tweet for tweet in full_data[:1500000]) 

正在将整个文件加载到 RAM 中。因此,在这条线之后进行分块并不会减少您正在使用的 ram 数量,尽管它会减少您正在使用的 GPU 核心数量,因为您每次迭代只提供 GPU x 数量。如果您首先加载较小的文件,您将从一开始就减少使用的 RAM 量。

【讨论】:

似乎阻碍我的 RAM 的主要因素是分类器本身,因为在大约 28,000 条推文之后,它变成了 3 GB。但我会尝试这个想法,因为它不会受到伤害。我会在我在家的时候尝试一下并更新你。 分类器在您的硬盘上。无需将整个文件加载到 ram 中,只是为了在每次运行时更新它。试着简单地把它放在这里......想象你有一个文本文件作为脚本运行的算法列表。比如,老式的机器学习。您创建规则以派生新算法,然后您想要更新算法文本文件。 Open("algorithms","a") as algorithms: 不会加载整个文本文件。它只是要打开它,然后当您写入它时,您只需添加其中的内容。 5 gigs 或 5kb,没关系。 这里是你想要做的事情的链接:medium.com/@mrgarg.rajat/… 哦,所以我以为我的分类器占用了内存,但它实际上是我的数据集?对数据集本身进行分块应该可以解决问题吗?我对此只有一个问题。那么为什么我的程序在前 1000 条推文中并不慢,但在 10,000 条之后它会冻结我的计算机? 如果左切片保持为零,则它加载到 gpu 中的数据集随着每次迭代而增加。也许我只是需要更多的咖啡......我不知道......但我没有看到左切片发生任何变化,而右切片随着每次运行而变得越来越高。您可以通过在每次运行时按提要大小增加左切片来解决此问题,但最终,将训练数据分块并以较小的批次运行对您的 RAM 和处理器更友好。

以上是关于为啥训练我的朴素贝叶斯分类器会占用这么多内存?的主要内容,如果未能解决你的问题,请参考以下文章

训练朴素贝叶斯分类器

通俗易懂-朴素贝叶斯

朴素贝叶斯文本分类在一个类别中失败。为啥? [关闭]

朴素贝叶斯-商品评论情感分析

机器学习之朴素贝叶斯

使用朴素贝叶斯分类器进行意见挖掘