131250208 / TPlinker-joint-extraction

438 stars 94 forks source link

发现BuildData预处理长样本内存溢出的问题 #40

Closed jarork closed 3 years ago

jarork commented 3 years ago

我观察BuildData在预处理NYT_star数据集的时候,内存占用很少。但是昨天我使用一个中文样本集做预处理的时候,发现在add_token_span的时候发生了内存溢出(20GB内存被占满)的问题。我的样本集总览是这样的:

样本集中最小文本长度 : 600 CHAR 样本集前 10% 文本最大长度 : 5092 CHAR 样本集前 20% 文本最大长度 : 6216 CHAR 样本集前 30% 文本最大长度 : 6852 CHAR 样本集前 40% 文本最大长度 : 7476 CHAR 样本集前 50% 文本最大长度 : 8074 CHAR 样本集前 60% 文本最大长度 : 9075 CHAR 样本集前 70% 文本最大长度 : 10171 CHAR 样本集前 80% 文本最大长度 : 11265 CHAR 样本集前 90% 文本最大长度 : 12293 CHAR 样本集中最大文本长度 : 18453 CHAR

每个样本都有几百个关系和几百个实体,样本集中一共有不到400个这样的样本。 不知内存溢出是否和滑动窗口有关呢?求教发生这种问题时,除了使用BuildData处理分割后的样本集再拼接train,valid,test,rel2id,ent2id还有data_stattistics,还有没有更好的方法呢?谢谢作者大大~

jarork commented 3 years ago

补充一下,在使用BuildData预处理之前,我把原数据转换成了TPLinker的标准格式,也就是:

{ ("id": , # 如test_8, train_12, valid_0。不在这个模块添加这一项,split的时候再加) "text":str, "relation_list": [ { "subject": , "subj_char_span": , # e.g [3, 10] "object": , "obj_char_span": , "predicate": , }, ], "entity_list": [ { "text":str, 实体名 "type":str, 实体类型 "char_span": [char_start:int, char_end:int] } ] }

BuildData的配置文件我是这么设置的: exp_name: ruijin_v2 # nyt_star, nyt, webnlg_star, webnlg, ace05_lu data_in_dir: ../datasets/ori_data ori_data_format: tplinker # casrel (webnlg_star, nyt_star), etl_span (webnlg), raw_nyt (nyt), tplinker (see readme)

encoder: BERT bert_path: ../../pretrained_models/chinese-bert-wwm-ext-hit-pytorch-huggingface data_out_dir: ../datasets/train_data

add_char_span: true # 如果原数据集已标char_span,此项设为false ignore_subword: false # 中文数据集设为false separate_char_by_white: false check_tok_span: true # 检查原数据集tok_span是否正确

jarork commented 3 years ago

还有想求教一个问题是,明明我的原数据已经有char_span了,但是在preprocess/build_data_config.yaml中我修改add_char_span为false会报错:

File "/nlp_data/hyy/tplinker/common/utils.py", line 337, in add_tok_span rel["obj_tok_span"] = char_span2tok_span(obj_char_span, char2tok_span) File "/nlp_data/hyy/tplinker/common/utils.py", line 327, in char_span2tok_span tok_span = [tok_span_list[0][0], tok_span_list[-1][1]] IndexError: list index out of range # 我使用BuildData前的数据格式如下: [ { "id": "test_0", "text": "1.血压控制目标......此处省略5000-10000字......第三医院\n内分泌乖卜1\n", "relation_list": [ { "subject": "血压", "subj_char_span": [ 2, 4 ], "object": "糖尿病", "obj_char_span": [ 9, 12 ], "predicate": "Test_Disease" }, 此处省略几百个关系 ], "entity_list": [ { "text": "2型糖尿病", "type": "Disease", "char_span": [ 411, 416 ] }, 此处省略几百个实体 ],

jarork commented 3 years ago

在缩小样本之后,数据集的大小是这样的:

7.3M Apr 15 13:44 test_data_0.json 5.5M Apr 15 13:43 train_data_0.json 6.8M Apr 15 13:44 valid_data_0.json

但是,运行BuildData之后,数据集的大小却是这样的。。。不明所以

total 4.0G 129 Apr 15 14:07 data_statistics.txt 214 Apr 15 14:07 ent2id.json 202 Apr 15 14:07 rel2id.json 1.4G Apr 15 13:56 test_data_0.json 979M Apr 15 14:07 train_data_0.json 1.7G Apr 15 14:03 valid_data_0.json

然后我随便打开了生成之后的test_data_0.json查看了一下,发现样本数是一样的; 但是,每个样本的关系数量都变成了好几万个,实体出现了好几千个,因此占用了超多的空间。 这么多个实体里面有很多都是同样的实体,只不过char_span不同

而且check_tok_span的时候发生了一些Error:

{'test_data_0': {'miss_samples': 0, 'tok_span_error': 304}, 'train_data_0': {'miss_samples': 0, 'tok_span_error': 282}, 'valid_data_0': {'miss_samples': 0, 'tok_span_error': 269}}

131250208 commented 3 years ago

还有想求教一个问题是,明明我的原数据已经有char_span了,但是在preprocess/build_data_config.yaml中我修改add_char_span为false会报错:

File "/nlp_data/hyy/tplinker/common/utils.py", line 337, in add_tok_span rel["obj_tok_span"] = char_span2tok_span(obj_char_span, char2tok_span) File "/nlp_data/hyy/tplinker/common/utils.py", line 327, in char_span2tok_span tok_span = [tok_span_list[0][0], tok_span_list[-1][1]] IndexError: list index out of range

我使用BuildData前的数据格式如下:

[ { "id": "test_0", "text": "1.血压控制目标......此处省略5000-10000字......第三医院\n内分泌乖卜1\n", "relation_list": [ { "subject": "血压", "subj_char_span": [ 2, 4 ], "object": "糖尿病", "obj_char_span": [ 9, 12 ], "predicate": "Test_Disease" }, 此处省略几百个关系 ], "entity_list": [ { "text": "2型糖尿病", "type": "Disease", "char_span": [ 411, 416 ] }, 此处省略几百个实体 ],

这个你自己设个断点看看什么问题越界。已经有char span就不要自动加char span了,因为默认有关系的两个实体,所有对应的char_span都会加上这个关系,如果你的实体和关系数量很大的话,自然会引入很多冗余的(或者错误的)关系。build data只是用来加char span和token span的,你可以根据自己数据的情况和使用的encoder自行添加token span。

jarork commented 3 years ago

还有想求教一个问题是,明明我的原数据已经有char_span了,但是在preprocess/build_data_config.yaml中我修改add_char_span为false会报错:

File "/nlp_data/hyy/tplinker/common/utils.py", line 337, in add_tok_span rel["obj_tok_span"] = char_span2tok_span(obj_char_span, char2tok_span) File "/nlp_data/hyy/tplinker/common/utils.py", line 327, in char_span2tok_span tok_span = [tok_span_list[0][0], tok_span_list[-1][1]] IndexError: list index out of range

我使用BuildData前的数据格式如下:

[ { "id": "test_0", "text": "1.血压控制目标......此处省略5000-10000字......第三医院\n内分泌乖卜1\n", "relation_list": [ { "subject": "血压", "subj_char_span": [ 2, 4 ], "object": "糖尿病", "obj_char_span": [ 9, 12 ], "predicate": "Test_Disease" }, 此处省略几百个关系 ], "entity_list": [ { "text": "2型糖尿病", "type": "Disease", "char_span": [ 411, 416 ] }, 此处省略几百个实体 ],

这个你自己设个断点看看什么问题越界。已经有char span就不要自动加char span了,因为默认有关系的两个实体,所有对应的char_span都会加上这个关系,如果你的实体和关系数量很大的话,自然会引入很多冗余的(或者错误的)关系。build data只是用来加char span和token span的,你可以根据自己数据的情况和使用的encoder自行添加token span。

运行BuildData之前,我自己已经检查过char_span了,用char_span对文本切片都能和每个实体名对应上。但是我注意到我的文本长度到了BuildData里面utils.py的clean_data_wo_span函数之后,文本长度和我之前的就对不上了。但我明明separate_char_by_white设为了false,还是被去掉了连续的空格。所以去掉空格之后,char_span就对不上了。

def clean_data_wo_span(self, ori_data, separate = False, data_type = "train"):
        '''
        rm duplicate whitespaces
        and add whitespaces around tokens to keep special characters from them
        '''
        def clean_text(text):
            text = re.sub("\s+", " ", text).strip()
            if separate:
                text = re.sub("([^A-Za-z0-9])", r" \1 ", text)
                text = re.sub("\s+", " ", text).strip()
            return text

        for sample in tqdm(ori_data, desc = "clean data"):
            sample["text"] = clean_text(sample["text"])
            if data_type == "test":
                continue
            for rel in sample["relation_list"]:
                rel["subject"] = clean_text(rel["subject"])
                rel["object"] = clean_text(rel["object"])
        return ori_data

您确定clearn_text函数里的 text = re.sub("\s+", " ", text).strip() 应该写在if separate:之外嘛? 谢谢

Apr 17: 注释掉“text = re.sub("\s+", " ", text).strip()”之后就没有问题了。