使用 SKLearn 在 Django 项目中大大超出 Heroku 内存配额

Posted

技术标签:

【中文标题】使用 SKLearn 在 Django 项目中大大超出 Heroku 内存配额【英文标题】:Heroku Memory quota vastly exceeded in Django Project using SKLearn 【发布时间】:2018-09-23 01:37:03 【问题描述】:

我在 Heroku 上部署了一个 Django 应用程序,目的是允许可信的、已知的内部用户上传 CSV 文件,单击“运行”,然后在幕后使用 Django 应用程序:

    加载保存的 sklearn 管道 .pkl 模型(假设大小为 120 MB) 使用 Pandas 读取用户的 CSV 数据 使用 CSV 数据作为输入在模型上调用 predict 使用 Django 存储将文件输出到 S3

这适用于小型 CSV 文件,但如果用户上传大型 CSV 文件,则会导致 Memory quota vastly exceeded...而且较大的 CSV 文件会增加内存消耗是有道理的。

我不确定在哪里进行调整。 我想知道是否有人在部署 sklearn 模型时遇到过类似的情况以及他们是如何“解决”的?

我的想法是:

    识别内存泄漏?甚至不知道从哪里开始。 Django DEBUG 设置为 False。 将我的 celery 任务改为分块处理输入的文件? 以某种方式使用 joblib 制作更小的 SKLearn 管道文件(我已经使用 compress=1)? 增加 Heroku 测功机?工人?

我的 django models.py 看起来像这样:

from django.db import models
from django.urls import reverse


class MLModel(models.Model):
    name = models.CharField(max_length=80)
    file = models.FileField(upload_to="models/")
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name


class Upload(models.Model):
    name = models.CharField(max_length=100)
    mlmodel = models.ForeignKey(MLModel, on_delete=models.CASCADE)
    file = models.FileField(upload_to='data/')

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('edit', kwargs='pk': self.pk)

我的 celery 任务如下所示:

@shared_task
def piparoo(id):
    instance = Upload.objects.get(id=id)
    model = joblib.load(instance.mlmodel.file.storage.open(instance.mlmodel.file.name))
    data = pd.read_csv(instance.file)
    data['Predicted'] = model.predict(data)

    buffer = StringIO()
    data.to_csv(buffer, index=False)
    content = buffer.getvalue().encode('utf-8')
    default_storage.save('output/results_.csv'.format(id), ContentFile(content))

Heroku 日志:

2018-04-12T06:12:53.592922+00:00 app[worker.1]: [2018-04-12 06:12:53,592: INFO/MainProcess] Received task: predictions.tasks.piparoo[f1ca09e1-6bba-4115-8989-04bb32d4f08e]
2018-04-12T06:12:53.737378+00:00 heroku[router]: at=info method=GET path="/predict/" host=tdmpredict.herokuapp.com request_id=ffad9785-5cb6-4e3c-a87c-94cbca47d109 fwd="24.16.35.31" dyno=web.1 connect=0
ms service=33ms status=200 bytes=6347 protocol=https
2018-04-12T06:13:08.054486+00:00 heroku[worker.1]: Error R14 (Memory quota exceeded)
2018-04-12T06:13:08.054399+00:00 heroku[worker.1]: Process running mem=572M(111.9%)
2018-04-12T06:13:28.026973+00:00 heroku[worker.1]: Error R15 (Memory quota vastly exceeded)
2018-04-12T06:13:28.026765+00:00 heroku[worker.1]: Process running mem=1075M(210.1%)
2018-04-12T06:13:28.026973+00:00 heroku[worker.1]: Stopping process with SIGKILL
2018-04-12T06:13:28.187650+00:00 heroku[worker.1]: Process exited with status 137
2018-04-12T06:13:28.306221+00:00 heroku[worker.1]: State changed from up to crashed

【问题讨论】:

【参考方案1】:

解决了我的问题的解决方案(以常识的方式)。

不是一次将用户的 CSV 文件读入内存,而是使用 Pandas chunksize 参数分块处理它,然后在最后将数据帧列表连接成一个。我还删除了模型 (120 MB),试图为将来的进程释放内存。

我的 celery 任务现在看起来像这样:

@shared_task
def piparoo(id):
    instance = Upload.objects.get(id=id)
    model = joblib.load(instance.mlmodel.file.storage.open(instance.mlmodel.file.name))

    final = []
    for chunk in pd.read_csv(instance.file, chunksize=5000):
        chunk['Predicted'] = model.predict(chunk)
        final.append(chunk)

    del model
    final = pd.concat(final)

    buffer = StringIO()
    final.to_csv(buffer, index=False)
    content = buffer.getvalue().encode('utf-8')
    default_storage.save('output/results_.csv'.format(id), ContentFile(content))

【讨论】:

以上是关于使用 SKLearn 在 Django 项目中大大超出 Heroku 内存配额的主要内容,如果未能解决你的问题,请参考以下文章

使用PyCharm创建Django项目及基本配置

使用PyCharm创建Django项目及基本配置

Django简述及第一个项目搭建

sklearn实现一元线性回归 Python机器学习系列

sklearn学习笔记之开始

模板继承