Skip to content

Commit f1634e5

Browse files
authored
add analysis module in text classification application (#3011)
* add_analysis_module * add_analysis_module * add_analysis_module * change_argument_format * add_analysis_module * add_evaluation_result_for_analysis_module
1 parent 813e5c9 commit f1634e5

File tree

39 files changed

+3966
-704
lines changed

39 files changed

+3966
-704
lines changed

applications/text_classification/doccano.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,10 @@ doccano支持`TextFile`、`TextLine`、`JSONL`和`CoNLL`四种数据上传格式
163163

164164

165165
### 7.1 多分类任务
166-
通过 [doccano.py](./doccano.py) 脚本进行数据形式转换,然后便可以按照[多分类文本任务指南](multi_class/README.md)进行相应模型训练。
167-
运行
166+
通过 [doccano.py](./doccano.py) 脚本进行数据形式转换,然后便可以按照[多分类文本任务指南](multi_class/README.md)中固定格式进行相应模型训练。
167+
168+
数据标注转化运行:
169+
168170
```shell
169171
python doccano.py \
170172
--doccano_file doccano.jsonl \
@@ -173,9 +175,13 @@ python doccano.py \
173175
--task_type "multi_class"
174176
```
175177

178+
稀疏数据识别出的有效标注请增加配置参数`--valid`,脏数据清洗的标注数据(文本中有脏数据标签)请增加配置参数`--dirty`,更多稀疏数据识别和脏数据清洗详见[多分类训练评估与模型优化指南](multi_class/analysis/README.md)
179+
176180
### 7.2 多标签任务
177-
通过 [doccano.py](./doccano.py) 脚本进行数据形式转换,然后便可以按照[多标签文本分类任务指南](multi_label/README.md)进行相应模型训练。
178-
运行
181+
通过 [doccano.py](./doccano.py) 脚本进行数据形式转换,然后便可以按照[多标签文本分类任务指南](multi_label/README.md)中固定格式进行相应模型训练。
182+
183+
数据标注转化运行:
184+
179185
```shell
180186
python doccano.py \
181187
--doccano_file doccano.jsonl \
@@ -184,9 +190,14 @@ python doccano.py \
184190
--task_type "multi_label"
185191
```
186192

193+
稀疏数据识别出的有效标注请增加配置参数`--valid`,脏数据清洗的标注数据(文本中有脏数据标签)请增加配置参数`--dirty`,更多稀疏数据识别和脏数据清洗详见[多标签训练评估与模型优化指南](multi_label/analysis/README.md)
194+
187195
### 7.3 层次分类任务
188-
通过 [doccano.py](./doccano.py) 脚本进行数据形式转换,然后便可以按照[层次文本分类任务指南](hierarchical/README.md)进行相应模型训练。
189-
运行
196+
197+
通过 [doccano.py](./doccano.py) 脚本进行数据形式转换,然后便可以按照[层次文本分类任务指南](hierarchical/README.md)中固定格式进行相应模型训练。
198+
199+
数据标注转化运行:
200+
190201
```shell
191202
python doccano.py \
192203
--doccano_file doccano.jsonl \
@@ -195,6 +206,7 @@ python doccano.py \
195206
--task_type "hierarchical"
196207
```
197208

209+
稀疏数据识别出的有效标注请增加配置参数`--valid`,脏数据清洗的标注数据(文本中有脏数据标签)请增加配置参数`--dirty`,更多稀疏数据识别和脏数据清洗详见[层次分类训练评估与模型优化指南](hierarchical/analysis/README.md)
198210
可配置参数说明:
199211

200212
- ``doccano_file``: 从doccano导出的数据标注文件。
@@ -204,8 +216,11 @@ python doccano.py \
204216
- ``is_shuffle``: 是否对数据集进行随机打散,默认为True。
205217
- ``seed``: 随机种子,默认为1000.
206218
- ``separator``: 不同层标签之间的分隔符,该参数只对层次文本分类任务有效。默认为"##"。
219+
- ``valid``: 是否为稀疏数据筛选的有效标注数据,默认为False.
220+
- ``dirty``: 是否为脏数据清洗策略标注数据,默认为False.
207221

208222
转化后的doccano标注数据目录结构如下:
223+
209224
```text
210225
data/
211226
├── train.txt # 训练数据集文件
@@ -220,5 +235,6 @@ data/
220235
- 脚本会自动生成data.txt,如果数据划分为 train/dev/test 数据集,data.txt则为test数据集无标签数据;如果数据划分为 train/dev 数据集,data.txt为无标签数据。**如果有未标注数据,则用未标注数据文件替换data.txt**
221236
- 每次执行 [doccano.py](./doccano.py) 脚本,将会覆盖已有的同名数据文件
222237
- 对于从doccano导出的文件,默认文件中的每条数据都是经过人工正确标注的。
238+
223239
## References
224240
- **[doccano](https://github.com/doccano/doccano)**

applications/text_classification/doccano.py

Lines changed: 48 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -25,56 +25,34 @@
2525
import paddle
2626
from paddlenlp.utils.log import logger
2727

28+
# yapf: disable
2829
parser = argparse.ArgumentParser()
29-
30-
parser.add_argument("--doccano_file",
31-
default="doccano.jsonl",
32-
type=str,
33-
help="The doccano file exported from doccano platform.")
34-
parser.add_argument("--save_dir",
35-
default="./data",
36-
type=str,
37-
help="The path of data that you wanna save.")
38-
parser.add_argument("--splits",
39-
default=[0.8, 0.2],
40-
type=float,
41-
nargs="*",
42-
help="The ratio of samples in datasets. "
43-
"[0.8, 0.2] means 80% samples "
44-
"used for training, 20% for evaluation.")
45-
parser.add_argument("--task_type",
46-
choices=['multi_class', 'multi_label', 'hierarchical'],
47-
default="multi_label",
48-
type=str,
49-
help="Select task type, multi_class for"
50-
"multi classification task, multi_label"
51-
"for multi label classification task and"
52-
"hierarchical for hierarchical classification,"
53-
"defaults to multi_label.")
54-
parser.add_argument("--is_shuffle",
55-
default=True,
56-
type=bool,
57-
help="Whether to shuffle the labeled"
58-
"dataset, defaults to True.")
59-
parser.add_argument("--seed",
60-
type=int,
61-
default=3,
62-
help="Random seed for initialization")
63-
parser.add_argument("--separator",
64-
type=str,
65-
default="##",
66-
help="Separator for hierarchical classification")
67-
30+
parser.add_argument("--doccano_file", default="doccano.jsonl", type=str, help="The doccano file exported from doccano platform.")
31+
parser.add_argument("--save_dir", default="./data", type=str, help="The path of data that you wanna save.")
32+
parser.add_argument("--splits", default=[0.8, 0.2], type=float, nargs="*", help="The ratio of samples in datasets. [0.8, 0.2] means 80% samples used for training, 20% for evaluation.")
33+
parser.add_argument("--task_type", choices=['multi_class', 'multi_label', 'hierarchical'], default="multi_label", type=str, help="Select task type, multi_class for multi classification task, multi_label for multi label classification task and hierarchical for hierarchical classification, defaults to multi_label.")
34+
parser.add_argument("--is_shuffle", default=True, type=bool, help="Whether to shuffle the labeled dataset, defaults to True.")
35+
parser.add_argument("--seed", type=int, default=3, help="Random seed for initialization")
36+
parser.add_argument("--separator", type=str, default="##", help="Separator for hierarchical classification")
37+
parser.add_argument("--valid", action='store_true', help="Whether annotate valid data(extracted from sparse strategy)")
38+
parser.add_argument("--dirty", action='store_true', help="Whether annotate dirty data(extracted from dirty data cleaning strategy)")
6839
args = parser.parse_args()
40+
# yapf: enable
6941

7042

7143
def set_seed(seed):
44+
"""
45+
Set random seed
46+
"""
7247
paddle.seed(seed)
7348
random.seed(seed)
7449
np.random.seed(seed)
7550

7651

7752
def do_convert():
53+
"""
54+
Convert doccano jsonl to fixed format
55+
"""
7856
set_seed(args.seed)
7957

8058
tic_time = time.time()
@@ -84,9 +62,11 @@ def do_convert():
8462
if not os.path.exists(args.save_dir):
8563
os.makedirs(args.save_dir)
8664

87-
if len(args.splits) != 2 and len(args.splits) != 3:
65+
if len(args.splits) != 1 and len(args.splits) != 2 and len(
66+
args.splits) != 3:
8867
raise ValueError(
89-
"Only len(splits)==2 / len(splits)==3 accepted for splits.")
68+
"Only len(splits)==1 /len(splits)==2 / len(splits)==3 accepted for splits."
69+
)
9070

9171
def _check_sum(splits):
9272
if len(splits) == 2:
@@ -121,8 +101,11 @@ def _check_sum(splits):
121101
raise ValueError("There exists comma \',\' in {}".format(l))
122102

123103
if args.task_type == 'multi_label' or args.task_type == 'multi_class':
124-
example = ' '.join(
125-
text.strip().split('\t')) + '\t' + ','.join(labels) + '\n'
104+
if args.dirty:
105+
text = ' '.join(text.strip().split('\t')[:-1])
106+
else:
107+
text = ' '.join(text.strip().split('\t'))
108+
example = text + '\t' + ','.join(labels) + '\n'
126109
for l in labels:
127110
if l not in label_list:
128111
label_list.append(l)
@@ -135,15 +118,19 @@ def _check_sum(splits):
135118
label_dict.append(l)
136119
if l not in label_list:
137120
label_list.append(l)
138-
example = ' '.join(text.strip().split('\t')) + '\t' + ','.join(
139-
label_list) + '\n'
121+
if args.dirty:
122+
text = ' '.join(text.strip().split('\t')[:-1])
123+
else:
124+
text = ' '.join(text.strip().split('\t'))
125+
example = text + '\t' + ','.join(label_list) + '\n'
140126
examples.append(example)
141127

142-
save_path = os.path.join(args.save_dir, 'label.txt')
143-
with open(save_path, "w", encoding="utf-8") as f:
144-
label_list = sorted(label_list)
145-
for l in label_list:
146-
f.write(l + '\n')
128+
if not args.dirty and not args.valid:
129+
save_path = os.path.join(args.save_dir, 'label.txt')
130+
with open(save_path, "w", encoding="utf-8") as f:
131+
label_list = sorted(label_list)
132+
for l in label_list:
133+
f.write(l + '\n')
147134

148135
def _save_examples(save_dir, file_name, examples, is_data=False):
149136
count = 0
@@ -160,13 +147,22 @@ def _save_examples(save_dir, file_name, examples, is_data=False):
160147
if args.is_shuffle:
161148
indexes = np.random.permutation(len(raw_examples))
162149
raw_examples = [raw_examples[i] for i in indexes]
163-
if len(args.splits) == 2:
150+
151+
if len(args.splits) == 1:
152+
if args.valid:
153+
_save_examples(args.save_dir, "valid.txt", examples)
154+
elif args.dirty:
155+
_save_examples(args.save_dir, "train_dirty.txt", examples)
156+
else:
157+
_save_examples(args.save_dir, "train.txt", examples)
158+
_save_examples(args.save_dir, "data.txt", examples, True)
159+
elif len(args.splits) == 2:
164160
i1, _ = args.splits
165161
p1 = int(len(raw_examples) * i1)
166162
_save_examples(args.save_dir, "train.txt", examples[:p1])
167163
_save_examples(args.save_dir, "dev.txt", examples[p1:])
168164
_save_examples(args.save_dir, "data.txt", examples[p1:], True)
169-
if len(args.splits) == 3:
165+
elif len(args.splits) == 3:
170166
i1, i2, _ = args.splits
171167
p1 = int(len(raw_examples) * i1)
172168
p2 = int(len(raw_examples) * (i1 + i2))

0 commit comments

Comments
 (0)