V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
drymonfidelia
V2EX  ›  Python

有没有了解 paddleocr 的 V 友,我用 Flask 写了个简易 API,内存占用会越来越大, 4~6 个小时就把机器 64GB 内存占满了,只能用 cron 每小时重启,是内存泄露的问题吗

  •  
  •   drymonfidelia · 259 天前 · 3774 次点击
    这是一个创建于 259 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这是我的代码,因为我平时 python 写得少,不熟练,如果代码质量差请别喷

    import json, re
    import uuid
    from urllib.parse import quote, unquote
    import yaml
    import os, urllib.request
    import time
    from flask import Flask, config, render_template, request, redirect, make_response
    
    from paddleocr import PaddleOCR, draw_ocr
    
    ocr = PaddleOCR(use_angle_cls=True, lang='en')
    
    app = Flask(__name__)
    
    
    @app.route('/ocr', methods=['GET'])
    def do_ocr():
        if request.method == 'GET':
            data = request.args
        if 'url' in data:
            url = unquote(data['url'])
        else:
            return ""
        tmp_path = f"/dev/shm/{uuid.uuid4()}"
        urllib.request.urlretrieve(url, tmp_path)
        result = ocr.ocr(tmp_path, cls=True)
        data = ""
        for recr in result:
            rec = recr[1][0].upper()
            data += rec + ","
        os.remove(tmp_path)
        return data.strip(",")
    
    
    if __name__ == '__main__':
        app.run()
    

    ocrapi.py 是从别处复制来的猴子补丁(不然好像并发会有问题)

    from gevent import monkey, pywsgi
    
    monkey.patch_all()
    
    from app import app
    

    用了 supervisor 启动:

    [program:ocrapi]
    command=gunicorn --workers 6 --worker-class=gevent --worker-connections=50 -b 0.0.0.0:8080 ocrapi:app
    directory=/opt/ocrapi/
    user=root
    autorestart=true
    redirect_stderr=true
    stdout_logfile=/var/log/ocrapi.log
    loglevel=info
    

    服务启动时内存占用在 2GB 左右,随后越来越大

    另外想问下如果只是用于识别包装盒上的产品序列号(大写英文数字组成,位数固定,手机拍照的,白底黑字/黑底白字都有可能)有效果更好的方案吗?

    28 条回复    2024-07-19 11:01:33 +08:00
    ggvm
        1
    ggvm  
       259 天前
    查一下具体的内存是被什么东西占用了?

    估计是模型被多次加载,但从来不释放?
    Puteulanus
        2
    Puteulanus  
       259 天前
    ocr = PaddleOCR(use_angle_cls=True, lang='en')
    这个要是放 do_ocr 里面呢

    我之前看别人另一个识别的用的 https://cnocr.readthedocs.io/zh/latest/usage/
    drymonfidelia
        3
    drymonfidelia  
    OP
       259 天前
    @Puteulanus 如果是这个要放进去的原因,为什么放外面反而会被多次加载?放外面不是更应该不会被多次加载么?
    Puteulanus
        4
    Puteulanus  
       259 天前
    @drymonfidelia 我的意思是既然感觉 PaddleOCR 有问题,就别维持它的全局对象了,没有引用看垃圾回收能不能收了它
    est
        5
    est  
       259 天前
    @drymonfidelia 可能是 ocr 对象维持了一些历史记录。你扫一次内存就占着不释放了。
    ClericPy
        6
    ClericPy  
       259 天前
    如果对这个库不了解, 可以在子进程里初始化它, 子进程销毁的时候就清理了. 不过估计会慢... 优化的地方很多
    drymonfidelia
        7
    drymonfidelia  
    OP
       259 天前
    @Puteulanus 但是每次 ocr 都加载一次模型感觉会很慢
    kaneg
        8
    kaneg  
       259 天前 via iPhone
    /dev/shm/这个是在内存的,会不会文件没有删掉,滞留在内存了
    drymonfidelia
        9
    drymonfidelia  
    OP
       259 天前
    @kaneg 我看 top 是 gunicorn 这个进程占的内存
    drymonfidelia
        10
    drymonfidelia  
    OP
       259 天前
    @kaneg 不过你提醒我了 /dev/shm/ 确实堆了 10GB 的图没删掉,不知道为什么,不过影响不大
    infun
        11
    infun  
       259 天前
    @drymonfidelia 怎么会影响不大。。。这里应该用 with 自动管理
    drymonfidelia
        12
    drymonfidelia  
    OP
       259 天前
    @infun with 能实现代码块结束自动删除文件么
    kuanat
        13
    kuanat  
       259 天前
    paddleocr 内存分配管理有问题是个很长时间的问题了。我印象官方 github 有几个 issues 就是讨论这个的,而且跨了多个版本,你可以去查一下。

    之前有说是因为框架缓存的原因,有人说不是。我之前遇到这个问题也是通过重启来解决,尝试读了一下 c++ 代码部分还是太复杂了,没有解决的精力和能力。
    infun
        14
    infun  
       259 天前
    bringyou
        15
    bringyou  
       259 天前
    如#13 所言,paddle OCR 的内存占用是个老问题,可以看 issue
    https://github.com/PaddlePaddle/PaddleOCR/discussions/6977
    bringyou
        16
    bringyou  
       259 天前
    也可以手动调用 try_shrink_memory 来释放内存
    ON9
        17
    ON9  
       259 天前 via Android
    一定要用猴子吗,用 fastapi 呢
    NoOneNoBody
        18
    NoOneNoBody  
       259 天前
    python 多进程会将全局环境复制到每个子进程
    flask 我不清楚要怎么做,最好使用 cache 而不是全局变量,然后每个子进程调用时从 cache 懒加载读取
    Eished
        19
    Eished  
       259 天前
    我也是 Flask+paddleocr ,用的 CPU 版本,1G 内存,都是小图片,大概半年才会内存满了重启一次
    founddev
        20
    founddev  
       259 天前
    可以尝试 paddleocr 转 onnx 用 PyTorch ,TF 或者 tensorrt 这种比较成熟的框架,尽量不要使用受众很小的这种框架,有 bug 都不一样会改
    clemente
        21
    clemente  
       258 天前
    不要用 paddle 百度都快放弃了
    CHchenkeyi
        22
    CHchenkeyi  
       256 天前
    至少代码没看出来由啥问题
    CHchenkeyi
        23
    CHchenkeyi  
       256 天前
    做了很多飞桨的,基本上有问题就是 版本问题,要么更新 paddle 版本,要么更新 ocr 版本。很玄学
    drymonfidelia
        24
    drymonfidelia  
    OP
       156 天前 via iPhone
    @CHchenkeyi 更新了最新版更离谱了,运行半分钟 100%用完内存
    so1n
        25
    so1n  
       156 天前
    遇到内存溢出问题,都是用 gunicorn 的 max_requests
    yinmin
        26
    yinmin  
       156 天前
    @drymonfidelia 25 楼是正解。gunicorn 加参数 --max-requests 1000 试试
    (每个 worker 接收到 1000 次请求后就重启一个新的)

    你自己测试一下 max-requests 用 500 、1000 、5000 、10000 哪个更优化。
    mMartin
        27
    mMartin  
       156 天前
    把 OCR 单独封装个单例试试 paddleOCR 我至少在十个生产环境用 还真没遇到内存泄露
    mMartin
        28
    mMartin  
       156 天前
    要么就把 OCR 单独封装一个服务 RPC 调用 我检测分割分类和 OCR 经常一起用
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2542 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 03:18 · PVG 11:18 · LAX 19:18 · JFK 22:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.