Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pipeline添加自定义function后, 循环使用内存溢出 #1437

Closed
1 task done
luoy2 opened this issue Mar 16, 2020 · 18 comments
Closed
1 task done

pipeline添加自定义function后, 循环使用内存溢出 #1437

luoy2 opened this issue Mar 16, 2020 · 18 comments
Labels
bug wontfix The problem described is a bug which will never be fixed.

Comments

@luoy2
Copy link

luoy2 commented Mar 16, 2020

Describe the bug
我需要在tokenizer和tagger后, 直接取到一个"token/tag"拼接的词。故此添加了一个hanlp_get_tokens的function append在pipline里。 详情见下方代码。

实际任务需要预测一万篇文章的分词和tag; 然而在gpu环境下跑大概1/10的loop时oop;
disable了GPU, 用tracemalloc查看发现transform\txt.py内存溢出。

Code to reproduce the issue
Provide a reproducible test case that is the bare minimum necessary to generate the problem.

import pandas as pd
from tqdm import tqdm
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
import hanlp
import tensorflow as tf
# Set CPU as available physical device
# tf.config.set_visible_devices([], 'GPU')
tf.config.list_physical_devices()
tokenizer = hanlp.load('PKU_NAME_MERGED_SIX_MONTHS_CONVSEG')
tagger = hanlp.load(hanlp.pretrained.pos.CTB5_POS_RNN_FASTTEXT_ZH)
def hanlp_get_tokens(tokens, tags, target_tag_list=[]):
    res = []
    for tokens, post in zip(tokens, tags):
        for i, j in zip(tokens, post):
            if i.strip():
                if not target_tag_list:
                    res.append(i + '/' + j)
                else:
                    if j in target_tag_list:
                        res.append(i + '/' + j)
    return res

pipeline = hanlp.pipeline() \
    .append(hanlp.utils.rules.split_sentence, output_key='sentences') \
    .append(tokenizer, output_key='tokens') \
    .append(tagger, output_key='tags') \
    .append(hanlp_get_tokens, input_key=('tokens', 'tags'), output_key='target_token')

import tracemalloc
import linecache

def display_top(snapshot, key_type='lineno', limit=3):
    snapshot = snapshot.filter_traces((
        tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
        tracemalloc.Filter(False, "<unknown>"),
    ))
    top_stats = snapshot.statistics(key_type)

    print("Top %s lines" % limit)
    for index, stat in enumerate(top_stats[:limit], 1):
        frame = stat.traceback[0]
        # replace "/path/to/module/file.py" with "module/file.py"
        filename = os.sep.join(frame.filename.split(os.sep)[-2:])
        print("#%s: %s:%s: %.1f KiB"
              % (index, filename, frame.lineno, stat.size / 1024))
        line = linecache.getline(frame.filename, frame.lineno).strip()
        if line:
            print('    %s' % line)

    other = top_stats[limit:]
    if other:
        size = sum(stat.size for stat in other)
        print("%s other: %.1f KiB" % (len(other), size / 1024))
    total = sum(stat.size for stat in top_stats)
    print("Total allocated size: %.1f KiB" % (total / 1024))

# 获取数据
text = "今天我回家买了个大西瓜;"*100
# data_df = data_df.loc[data_df['doc_id'] >= 5150]
tracemalloc.start()
for index, i in enumerate(tqdm([text]*20000)):
    tokenized = {}
    # title_counters = Counter(title_token)
    res = pipeline(i)['target_token']
    # content_counters = Counter(content_token)
    snapshot = tracemalloc.take_snapshot()
    display_top(snapshot)

Describe the current behavior
可以看到的是, 随着iteration数目增加, word += c这里的内存也不断增加。。。但是我翻来覆去看那段代码完全没看出问题, 故此只能提出issue, 期待何老师看看。具体log已经附上。

Expected behavior
loop prediction不会增加内存使用

System information

  • OS Platform and Distribution (e.g., Linux Ubuntu 16.04): Windows 10
  • Python version: 3.7
  • HanLP version: 2.0.0-alpha.39

Other info / logs
log gist

  • I've completed this form and searched the web for solutions.
@luoy2 luoy2 added the bug label Mar 16, 2020
@hankcs
Copy link
Owner

hankcs commented Mar 16, 2020

感谢汇报,我认为OOV的问题是你那一万篇文章中有一篇含有特别长的句子,你可以试试写个函数限制句子长度看看会不会OOV。

tracemalloc的汇报未必是真实的内存占用,而是包含未被垃圾回收的内存占用,我发现仅使用tokenizer的时候内存没有显著增长

pipeline = hanlp.pipeline() \
    .append(hanlp.utils.rules.split_sentence, output_key='sentences') \
    .append(tokenizer, output_key='tokens') \
    # .append(tagger, output_key='tags') \
    # .append(hanlp_get_tokens, input_key=('tokens', 'tags'), output_key='target_token')

而一旦加入了tagger,word += c的内存就开始增长了。有趣的是,这句话只有tokenizer会调用,而且如你所言没有什么问题,它根本不产生全局变量,内存是怎么累积起来得不到释放的呢?
是否跟Python generator的内存回收机制有关呢?

@luoy2
Copy link
Author

luoy2 commented Mar 16, 2020

何老师可以看到, 我上方提供的代码里面其实就是同一个句子反复预测, 但是内存还是上涨。

  • 长句子问题:
    我考虑过这个问题, 所以之前debug的时候每次都记录了doc_id, 然后OOV的时候把doc_id前100-后100都跑了一遍, 没有问题。

  • 真实内存占用
    汇报issue后我就单独在跑这个任务, 现在任务管理器中Python的内存已经涨到5个G了。

  • gc.collect() 实测没有用

  • 目前有效的办法是定期

del tokenizer
del tagger
del pipline
gc.collect()
tokenizer = ..
tagger =..
pipline = ..

但是还是没有办法解决根本问题。。。用了这一套过后跑4000个iter也OOV了。

我不知道以下两个issue是否related, 供何老师参考:

Memory leak in model.fit #37505

Repeatedly calling model.predict results in memory leak

如果是的话是不是就是tf2的bug, 因为据汇报tf1.15 - 没有上述问题。

@hankcs
Copy link
Owner

hankcs commented Mar 16, 2020

现在任务管理器中Python的内存已经涨到5个G了

fasttext可能本身就要占用几个G。是增长量达到5个G吗?

如果是的话是不是就是tf2的bug, 因为据汇报tf1.15 - 没有上述问题。
可能有关系,有人说 I'm also still facing the same issue with TensorFlow 2.1.0.,也有人说 Still facing this problem in tf-nightly-gpu 2.2.0.dev20200307

不过我没有在hanlp.com的服务器上观察到内存泄露的情况。跟python代码不同,我用的是tensorflow_serving提供服务,从上线到现在没有重启过。

我也单独测过transform\txt.py,也没有出现问题,暂时觉得是tensorflow python接口里面有内存泄露。

@luoy2
Copy link
Author

luoy2 commented Mar 16, 2020

经过测试, 每一轮都

del tokenizer
del tagger
del pipline
gc.collect()

显著降低了内存溢出。 然而, 3个小时后python内存实际使用量达到了8个G。

@hankcs
Copy link
Owner

hankcs commented Mar 17, 2020

仅运行HanLP的代码不运行tensorflow的predict不会导致内存泄露:

transform:TxtBMESFormat = tokenizer.transform
for index, i in enumerate(tqdm([text] * 20000)):
    # title_counters = Counter(title_token)
    # res = pipeline(i)
    # content_counters = Counter(content_token)
    transform.Y_to_outputs(tf.zeros((5, 4)), inputs=list('商品和服务'))
    snapshot = tracemalloc.take_snapshot()
    display_top(snapshot)

@heibaicai
Copy link

我也是和他一样的问题,并且在GPU上经过测试时,同一个句子循环,句子长度不长,一个句20个字。我自己用一块GPU,发现显存会增长(并不是持续增长),最后都会显存占满,导致报错OOM

@hankcs
Copy link
Owner

hankcs commented Mar 17, 2020

已确定是TensorFlow的问题,正在等TF社区回复。

@heibaicai
Copy link

好的,谢谢何老师回复

@hankcs
Copy link
Owner

hankcs commented Mar 17, 2020

官方说能够复现这个bug。想想觉得tf也不过如此,这么大的项目竟然有这种致命的错误。你们介意在生产环境中用PyTorch吗?

@luoy2
Copy link
Author

luoy2 commented Mar 17, 2020

官方说能够复现这个bug。想想觉得tf也不过如此,这么大的项目竟然有这种致命的错误。你们介意在生产环境中用PyTorch吗?

我也比较震惊, 一开始自己debug的时候就觉得不可能是tf内存溢出, 结果查着查着发现还真有可能是。。。 其实我们生产环境是用的tf1.15, 在这里用hanlp的tf2.1 也是出于一个技术验证的方向。 因此, 只要能够dockerize我觉得问题都不大啊。

tensorflow还有一个好处是对java的支持, 这个是pytorch现阶段做不了的吧

@hankcs
Copy link
Owner

hankcs commented Mar 17, 2020

pytorch的部署现在还赶不上TensorFlow。且看tf官方修复这个bug的速度怎么样,再决定要不要主力转PyTorch吧。反正做research比较倾向于PyTorch,只要在HanLP现有框架下实现一个TorchComponent就行了。

@heibaicai
Copy link

如能推出pytorch版,对很多喜好用pytorch的学习者来说也是一个福音

@luoy2
Copy link
Author

luoy2 commented Mar 18, 2020

pytorch的部署现在还赶不上TensorFlow。且看tf官方修复这个bug的速度怎么样,再决定要不要主力转PyTorch吧。反正做research比较倾向于PyTorch,只要在HanLP现有框架下实现一个TorchComponent就行了。

虽然research倾向于pytorch, 但是research完了过后就是工程化落地的事情,转起来也比较麻烦。。不过就像@heibaicai说的,

如能推出pytorch版,对很多喜好用pytorch的学习者来说也是一个福音

话说回来, 超大型语料有没有estimate releasing time呀? 戳手手。。。。

@hankcs
Copy link
Owner

hankcs commented Mar 18, 2020

语料库本身版权受限,但这上面的大型语言模型应该会在暑假发布。

@luoy2
Copy link
Author

luoy2 commented Mar 18, 2020

语料库本身版权受限,但这上面的大型语言模型应该会在暑假发布。

感谢回复~ 期待~

@luoy2
Copy link
Author

luoy2 commented May 12, 2020

可怕, 两个月过去了这个issue tf 社区一动都没动, bug不修复hanlp 2.0就没法投产, 血崩

@hankcs
Copy link
Owner

hankcs commented May 12, 2020

的确,对tf失望了,已经开始用PyTorch重写了部分模块,beta版还是得靠PyTorch挑大梁。

@hankcs hankcs added the wontfix The problem described is a bug which will never be fixed. label Jan 1, 2021
@hankcs
Copy link
Owner

hankcs commented Jan 1, 2021

Hi @luoy2 ,TF对这个bug一直无所作为。部署的时候用tensorflow_serving就可以跳过这个问题。

HanLP2.1发布了PyTorch后端,欢迎使用 https://github.com/hankcs/HanLP/tree/doc-zh

@hankcs hankcs closed this as completed Jan 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug wontfix The problem described is a bug which will never be fixed.
Projects
None yet
Development

No branches or pull requests

3 participants