SS3M commited on
Commit
b934157
·
verified ·
1 Parent(s): 71a6746

Upload 20_weight_5_21's state dict

Browse files
.gitattributes CHANGED
@@ -82,3 +82,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
82
  11_12_13_lr_17/logs/11_12_13_lr_17_log_plot.jpg filter=lfs diff=lfs merge=lfs -text
83
  12_13_lr_16/logs/12_13_lr_16_log_plot.jpg filter=lfs diff=lfs merge=lfs -text
84
  8_entities_top_span_self_ensemble_no_weight_20/logs/8_entities_top_span_self_ensemble_no_weight_20_log_plot.jpg filter=lfs diff=lfs merge=lfs -text
 
 
82
  11_12_13_lr_17/logs/11_12_13_lr_17_log_plot.jpg filter=lfs diff=lfs merge=lfs -text
83
  12_13_lr_16/logs/12_13_lr_16_log_plot.jpg filter=lfs diff=lfs merge=lfs -text
84
  8_entities_top_span_self_ensemble_no_weight_20/logs/8_entities_top_span_self_ensemble_no_weight_20_log_plot.jpg filter=lfs diff=lfs merge=lfs -text
85
+ 20_weight_5_21/logs/20_weight_5_21_log_plot.jpg filter=lfs diff=lfs merge=lfs -text
20_weight_5_21/20_weight_5_21.py ADDED
@@ -0,0 +1,2231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # %% [code]
2
+ get_ipython().system('pip install evaluate seqeval underthesea positional-encodings[pytorch] pytorch-crf')
3
+
4
+ # %% [code]
5
+ import warnings
6
+ warnings.filterwarnings('ignore')
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.optim as optim
11
+ from torch.utils.data import Dataset, TensorDataset, DataLoader
12
+ import torch.nn.functional as F
13
+ import albumentations as albu
14
+ from transformers import AutoTokenizer, AutoModel
15
+ import torch.distributed as dist
16
+ from torch.nn.parallel import DistributedDataParallel as DDP
17
+ from positional_encodings.torch_encodings import PositionalEncoding1D
18
+ from torchcrf import CRF
19
+
20
+ from sklearn.metrics import f1_score
21
+ from sklearn.preprocessing import MinMaxScaler, StandardScaler
22
+ from scipy.spatial.transform import Rotation as R
23
+ from sklearn.model_selection import KFold, StratifiedGroupKFold, GroupKFold, StratifiedKFold
24
+ from sklearn.metrics import precision_recall_fscore_support
25
+ from timm.utils import ModelEmaV3
26
+ import timm
27
+
28
+ import os
29
+ import gc
30
+ import json
31
+ from pathlib import Path
32
+ import pickle
33
+ from tqdm.auto import tqdm
34
+ import copy
35
+ import numpy as np
36
+ import pandas as pd
37
+ import polars as pl
38
+ from PIL import Image
39
+ import time
40
+ from tqdm import tqdm
41
+ from matplotlib import pyplot as plt
42
+ import seaborn as sns
43
+ from multiprocessing import Manager as MemoryManager
44
+ from functools import lru_cache
45
+ import shutil
46
+ import glob
47
+ import cv2
48
+ import random
49
+ import re
50
+ import joblib
51
+ import math
52
+ from huggingface_hub import HfApi, snapshot_download
53
+ import evaluate
54
+ from underthesea import word_tokenize as vi_tokenize_tool
55
+ import spacy
56
+ en_tokenize_tool = spacy.load("en_core_web_sm")
57
+ from collections import defaultdict, Counter
58
+
59
+ # %% [code]
60
+ # Global config
61
+ SEEDS = [26092004]
62
+ topk = 1
63
+ nfolds = 5
64
+ only_fold_idx = 0
65
+ test_only = 0
66
+ debug_only = 0
67
+
68
+ # Config thư mục
69
+ dataset = 'kltn/only_entities' # conll003, ontonotes, phoner, vietbio, vietmed, vimed, kltn/only_entities, kltn/raw
70
+ root_dir = f'/kaggle/input/notebooks/sambui22022517/kltn-data/{dataset}' ## Thư mục chứa file train, val, test
71
+ train_dir = f'{root_dir}'
72
+ # val_dir = f'{root_dir}/val'
73
+ test_dir = f'{root_dir}'
74
+
75
+ # Config checkpoints
76
+
77
+ # Config training
78
+ epochs = 18 if not debug_only else 2
79
+ batch_size = 32
80
+ device = "cuda" if torch.cuda.is_available() else "cpu"
81
+ # # Thêm biến toàn cục nào đó vào đây
82
+ repo_name = 'SS3M/kltn-experiments'
83
+ state_dict_save_name = "20_weight_5_21"
84
+ checkpoints_dir = state_dict_save_name
85
+ pretrained_dir = "/kaggle/working"
86
+ os.makedirs(f'{checkpoints_dir}', exist_ok=True)
87
+
88
+ backbone_model_name = "bert-base-uncased" if dataset in ["conll003", "ontonotes"] else "vinai/phobert-base"
89
+ word_tokenize = lambda text: [token.text for token in en_tokenize_tool(text)] if dataset == dataset in ["conll003", "ontonotes"] else vi_tokenize_tool(text)
90
+ max_len_dict = {
91
+ 'kltn/raw': 256,
92
+ 'kltn/only_entities': 68,
93
+ 'conll003': 46,
94
+ 'ontonotes': 61,
95
+ 'phoner': 68,
96
+ 'vietbio': 125,
97
+ 'vietmed': 36,
98
+ 'vimed': 100,
99
+ }
100
+ zero_entities_rate_dict = {
101
+ 'kltn/raw': 1000,
102
+ 'kltn/only_entities': 0.2,
103
+ 'conll003': 1000, # mean keep all zero-entities samples
104
+ 'ontonotes': 1000,
105
+ 'phoner': 1000,
106
+ 'vietbio': 1000,
107
+ 'vietmed': 1000,
108
+ 'vimed': 1000,
109
+ }
110
+
111
+ max_len = max_len_dict[dataset]
112
+ max_n_parts = 3 if dataset in ['kltn/raw'] else 1
113
+ max_span_len = 10
114
+ zero_entities_rate = zero_entities_rate_dict[dataset]
115
+
116
+ # Trainer
117
+ trainer_params = {
118
+ "training_time": "00:11:30:00",
119
+ "eval_mode": "max",
120
+ "topk": topk,
121
+ "save_name": state_dict_save_name,
122
+ "save_best": True,
123
+ "save_last": True,
124
+ "device": device,
125
+ "logging": True,
126
+ "logging_file": True,
127
+ "checkpoints_dir": checkpoints_dir,
128
+ "early_stopping": 30,
129
+ "eval_from_ratio": 0.4,
130
+ "eval_every": 1,
131
+ "schedule_in_step": False,
132
+ "use_ema": True,
133
+ "ema_from_ratio": 0.3,
134
+ "ema_decay": 0.9995,
135
+ "max_grad_norm": 200.0,
136
+ "return_best": True,
137
+ "return_last": True,
138
+ }
139
+
140
+ # Memory
141
+ train_memory_params = {
142
+ 'max_len': max_len,
143
+ 'max_n_parts': max_n_parts,
144
+ 'max_span_len': max_span_len,
145
+ 'weight_rate': 5.0,
146
+ }
147
+ val_memory_params = {
148
+ 'max_len': max_len,
149
+ 'max_n_parts': max_n_parts,
150
+ 'max_span_len': max_span_len,
151
+ 'weight_rate': 5.0,
152
+ }
153
+
154
+ # Data Loader
155
+ def seed_worker(worker_id):
156
+ worker_seed = torch.initial_seed() % 2**32
157
+ np.random.seed(worker_seed)
158
+ random.seed(worker_seed)
159
+
160
+ train_loader_params = {
161
+ 'batch_size': batch_size,
162
+ 'shuffle': True,
163
+ 'pin_memory':True,
164
+ 'num_workers': 2,
165
+ 'drop_last': False,
166
+ 'worker_init_fn': seed_worker,
167
+ 'persistent_workers': False,
168
+ }
169
+ val_loader_params = {
170
+ 'batch_size': batch_size,
171
+ 'shuffle': False,
172
+ 'pin_memory':True,
173
+ 'num_workers': 1,
174
+ 'drop_last': False,
175
+ 'worker_init_fn': seed_worker,
176
+ 'persistent_workers': False,
177
+ }
178
+
179
+ # Model
180
+ model_params = {
181
+ 'backbone_model_name': backbone_model_name,
182
+ 'max_span_len': max_span_len,
183
+ 'topk_spans': 200,
184
+ 'keep_neighbor': 2,
185
+ }
186
+
187
+ # Loss Func
188
+ loss_func_params = {
189
+ 'lambda_ce': 1.0,
190
+ }
191
+ eval_func_params = {}
192
+
193
+ # Optim
194
+ optim_params = {
195
+ 'name': 'AdamW',
196
+ 'lr': 1e-4,
197
+ 'weight_decay': 1e-4,
198
+ }
199
+ scheduler_params = {
200
+ 'name': 'CosineAnnealingLR',
201
+ 'T_max': 20, # Số epoch để hoàn thành một chu kỳ giảm LR
202
+ 'eta_min': 1e-6 # Learning rate nhỏ nhất trong chu kỳ
203
+ }
204
+
205
+ # %% [code]
206
+ def set_seed(seed=42):
207
+ random.seed(seed)
208
+ np.random.seed(seed)
209
+ torch.manual_seed(seed)
210
+ torch.cuda.manual_seed(seed)
211
+ torch.cuda.manual_seed_all(seed) # if using multi-GPU
212
+ torch.use_deterministic_algorithms(False)
213
+ torch.backends.cudnn.deterministic = True
214
+ torch.backends.cudnn.benchmark = False
215
+ os.environ['PYTHONHASHSEED'] = str(seed)
216
+
217
+ # %% [code]
218
+ class CustomLoss(nn.Module):
219
+ def __init__(self, lambda_ce=1.0):
220
+ super().__init__()
221
+ self.lambda_ce = lambda_ce
222
+
223
+ self.ce = nn.CrossEntropyLoss(
224
+ ignore_index=-100,
225
+ reduction='none'
226
+ )
227
+
228
+ def forward(
229
+ self,
230
+ logits, labels, weights, # weights: (B, N)
231
+ start_logits, start_labels,
232
+ end_logits, end_labels,
233
+ ):
234
+ # =====================================
235
+ # SPAN LOSS
236
+ # =====================================
237
+
238
+ B, N, C = logits.shape
239
+
240
+ flat_logits = logits.reshape(-1, C)
241
+ flat_labels = labels.reshape(-1)
242
+ flat_weights = weights.reshape(-1)
243
+
244
+ valid_mask = flat_labels != -100
245
+
246
+ if valid_mask.any():
247
+ ce_loss = self.ce(flat_logits, flat_labels) # (B*N,)
248
+
249
+ ce_loss = ce_loss[valid_mask]
250
+ valid_weights = flat_weights[valid_mask]
251
+
252
+ loss = (ce_loss * valid_weights).sum() / valid_weights.sum().clamp(min=1e-8)
253
+
254
+ else:
255
+ loss = logits.sum() * 0.0
256
+
257
+ # =====================================
258
+ # START LOSS
259
+ # =====================================
260
+
261
+ B, L, C = start_logits.shape
262
+
263
+ start_logits_flat = start_logits.reshape(B * L, C)
264
+ start_labels_flat = start_labels.reshape(-1)
265
+
266
+ start_loss = self.ce(start_logits_flat, start_labels_flat)
267
+ start_valid = start_labels_flat != -100
268
+
269
+ if start_valid.any():
270
+ start_loss = start_loss[start_valid].mean()
271
+ else:
272
+ start_loss = logits.sum() * 0.0
273
+
274
+ # =====================================
275
+ # END LOSS
276
+ # =====================================
277
+
278
+ end_logits_flat = end_logits.reshape(B * L, C)
279
+ end_labels_flat = end_labels.reshape(-1)
280
+
281
+ end_loss = self.ce(end_logits_flat, end_labels_flat)
282
+ end_valid = end_labels_flat != -100
283
+
284
+ if end_valid.any():
285
+ end_loss = end_loss[end_valid].mean()
286
+ else:
287
+ end_loss = logits.sum() * 0.0
288
+
289
+ return {
290
+ "total": loss + start_loss + end_loss,
291
+ "span_loss": loss,
292
+ "start_loss": start_loss,
293
+ "end_loss": end_loss,
294
+ }
295
+
296
+ # %% [code]
297
+ ## Viết eval_fn vào đây
298
+
299
+ # Bỏ hết eval_fn và trọng số vào đây
300
+ class CustomEvalFn(nn.Module):
301
+ def __init__(self):
302
+ super().__init__()
303
+
304
+ def compute_f1(self, tp, fp, fn):
305
+ precision = tp / (tp + fp + 1e-8)
306
+ recall = tp / (tp + fn + 1e-8)
307
+ f1 = 2 * precision * recall / (precision + recall + 1e-8)
308
+ return precision, recall, f1
309
+
310
+ def forward(self, pred, gold):
311
+ pred_set = set(pred)
312
+ gold_set = set(gold)
313
+
314
+ tp = len(pred_set & gold_set)
315
+ fp = len(pred_set - gold_set)
316
+ fn = len(gold_set - pred_set)
317
+
318
+ precision, recall, f1 = self.compute_f1(tp, fp, fn)
319
+
320
+ return {
321
+ f"precision": precision,
322
+ f"recall": recall,
323
+ f"f1": f1,
324
+ }
325
+
326
+ class SpanErrorAnalyzer:
327
+ def __init__(self, pad_token_id=0):
328
+ self.pad_token_id = pad_token_id
329
+
330
+ # ===== helper =====
331
+ def _to_set(self, data):
332
+ """
333
+ data: list of (b, tuple(ids))
334
+ -> dict[b] = set(tuple(ids))
335
+ """
336
+ res = defaultdict(set)
337
+ for b, ids in data:
338
+ ids = tuple([i for i in ids if i != self.pad_token_id])
339
+ if len(ids) > 0:
340
+ res[b].add(ids)
341
+ return res
342
+
343
+ def _iou(self, a, b):
344
+ """
345
+ a, b: tuple(ids)
346
+ """
347
+ set_a, set_b = set(a), set(b)
348
+ inter = len(set_a & set_b)
349
+ union = len(set_a | set_b)
350
+ if union == 0:
351
+ return 0.0
352
+ return inter / union
353
+
354
+ def _boundary_error(self, pred, gold):
355
+ """
356
+ đo lệch boundary dựa trên overlap prefix/suffix
357
+ """
358
+ # left match
359
+ left = 0
360
+ for i in range(min(len(pred), len(gold))):
361
+ if pred[i] == gold[i]:
362
+ left += 1
363
+ else:
364
+ break
365
+
366
+ # right match
367
+ right = 0
368
+ for i in range(1, min(len(pred), len(gold)) + 1):
369
+ if pred[-i] == gold[-i]:
370
+ right += 1
371
+ else:
372
+ break
373
+
374
+ return {
375
+ "left_match": left,
376
+ "right_match": right,
377
+ "pred_len": len(pred),
378
+ "gold_len": len(gold),
379
+ }
380
+
381
+ # ===== main =====
382
+ def analyze(self, preds, golds):
383
+ pred_map = self._to_set(preds)
384
+ gold_map = self._to_set(golds)
385
+
386
+ all_batches = set(pred_map.keys()) | set(gold_map.keys())
387
+
388
+ stats = Counter()
389
+
390
+ detailed_errors = []
391
+
392
+ for b in all_batches:
393
+ pset = pred_map.get(b, set())
394
+ gset = gold_map.get(b, set())
395
+
396
+ matched_gold = set()
397
+
398
+ # ===== check predictions =====
399
+ for p in pset:
400
+ if p in gset:
401
+ stats["exact_match"] += 1
402
+ matched_gold.add(p)
403
+ else:
404
+ # tìm gold gần nhất
405
+ best_iou = 0
406
+ best_g = None
407
+
408
+ for g in gset:
409
+ iou = self._iou(p, g)
410
+ if iou > best_iou:
411
+ best_iou = iou
412
+ best_g = g
413
+
414
+ if best_iou > 0:
415
+ stats["partial_match"] += 1
416
+
417
+ boundary = self._boundary_error(p, best_g)
418
+
419
+ detailed_errors.append({
420
+ "type": "boundary_error",
421
+ "batch": b,
422
+ "pred": p,
423
+ "gold": best_g,
424
+ "iou": best_iou,
425
+ **boundary
426
+ })
427
+ else:
428
+ if b not in gold_map:
429
+ stats["no_event_sample"] += 1
430
+ err_type = "no_event_sample"
431
+ else:
432
+ stats["completely_wrong"] += 1
433
+ err_type = "completely_wrong"
434
+
435
+ detailed_errors.append({
436
+ "type": err_type,
437
+ "batch": b,
438
+ "pred": p
439
+ })
440
+
441
+ # ===== check missing =====
442
+ for g in gset:
443
+ if g not in matched_gold:
444
+ # check if any pred overlaps
445
+ overlap = any(self._iou(p, g) > 0 for p in pset)
446
+
447
+ if overlap:
448
+ stats["miss_with_overlap"] += 1
449
+ else:
450
+ stats["miss"] += 1
451
+
452
+ detailed_errors.append({
453
+ "type": "miss",
454
+ "batch": b,
455
+ "gold": g
456
+ })
457
+
458
+ return {
459
+ "summary": {
460
+ "exact_match": (stats["exact_match"], stats["exact_match"] / len(preds)),
461
+ "partial_match": (stats["partial_match"], stats["partial_match"] / len(preds)),
462
+ "no_event_sample": (stats["no_event_sample"], stats["no_event_sample"] / len(preds)),
463
+ "completely_wrong": (stats["completely_wrong"], stats["completely_wrong"] / len(preds)),
464
+ "miss": (stats["miss"], stats["miss"] / len(golds)),
465
+ "miss_with_overlap": (stats["miss_with_overlap"], stats["miss_with_overlap"] / len(golds)),
466
+ },
467
+ "details": detailed_errors
468
+ }
469
+
470
+ # %% [code]
471
+ ## Viết cấu trúc model vào đây
472
+ def get_span_reprs(hidden, spans):
473
+ """
474
+ Args:
475
+ hidden: (B, L, H)
476
+ spans: (B, N, 2)
477
+
478
+ Return:
479
+ span_repr: (B, N, 4*H)
480
+ """
481
+
482
+ B, N, _ = spans.shape
483
+ H = hidden.size(-1)
484
+
485
+ batch_idx = torch.arange(B, device=hidden.device).unsqueeze(1)
486
+
487
+ start_idx = spans[..., 0] # (B, N)
488
+ end_idx = spans[..., 1] # (B, N)
489
+ start_h = hidden[batch_idx, start_idx]
490
+ end_h = hidden[batch_idx, end_idx]
491
+
492
+ span_repr = torch.cat(
493
+ [start_h, end_h, end_h - start_h, end_h * start_h],
494
+ dim=-1
495
+ )
496
+
497
+ return span_repr
498
+
499
+ def filter_spans(
500
+ start_logits, # (B, L, C)
501
+ end_logits, # (B, L, C)
502
+ attn_mask, # (B, L)
503
+ max_span_length=10,
504
+ topk_spans=20,
505
+ keep_neighbor=1
506
+ ):
507
+ """
508
+ Return:
509
+ spans: (B, N, 2)
510
+ N là số span lớn nhất trong batch sau expand + remove duplicate.
511
+ Padding bằng (0, 0)
512
+ """
513
+
514
+ device = start_logits.device
515
+ B, L, C = start_logits.shape
516
+
517
+ # Clone
518
+ start_logits = start_logits.detach()
519
+ end_logits = end_logits.detach()
520
+
521
+ # Prob
522
+ prob_s = F.softmax(start_logits, dim=-1) # (B, L, C)
523
+ prob_e = F.softmax(end_logits, dim=-1) # (B, L, C)
524
+
525
+ all_batch_spans = []
526
+
527
+ for b in range(B):
528
+
529
+ valid_len = int(attn_mask[b].sum().item())
530
+
531
+ candidate_spans = []
532
+
533
+ # Enumerate all valid spans
534
+ for s in range(1, valid_len):
535
+
536
+ max_e = min(valid_len - 1, s + max_span_length - 1)
537
+
538
+ for e in range(s, max_e + 1):
539
+
540
+ length = e - s + 1
541
+
542
+ score = (
543
+ (1.0 - prob_s[b, s, 0].item())
544
+ + (1.0 - prob_e[b, e, 0].item())
545
+ - (length / max_span_length)
546
+ )
547
+ candidate_spans.append((score, s, e))
548
+
549
+ # Top-k
550
+ candidate_spans = sorted(
551
+ candidate_spans,
552
+ key=lambda x: x[0],
553
+ reverse=True
554
+ )[:topk_spans]
555
+
556
+ # Expand neighbors
557
+ final_spans = set()
558
+
559
+ for _, s, e in candidate_spans:
560
+
561
+ for ds in range(-keep_neighbor, keep_neighbor + 1):
562
+ for de in range(-keep_neighbor, keep_neighbor + 1):
563
+
564
+ ns = s + ds
565
+ ne = e + de
566
+
567
+ if ns <= 0:
568
+ continue
569
+
570
+ if ne >= valid_len:
571
+ continue
572
+
573
+ if ns > ne:
574
+ continue
575
+
576
+ if (ne - ns + 1) > max_span_length:
577
+ continue
578
+
579
+ final_spans.add((ns, ne))
580
+
581
+ final_spans = sorted(list(final_spans))
582
+
583
+ if len(final_spans) == 0:
584
+ final_spans = [(0, 0)]
585
+
586
+ all_batch_spans.append(final_spans)
587
+
588
+ # Padding
589
+ max_num_spans = max(len(x) for x in all_batch_spans)
590
+
591
+ padded_spans = []
592
+
593
+ for spans in all_batch_spans:
594
+
595
+ pad_size = max_num_spans - len(spans)
596
+
597
+ spans = spans + [(0, 0)] * pad_size
598
+
599
+ padded_spans.append(spans)
600
+
601
+ spans = torch.tensor(
602
+ padded_spans,
603
+ dtype=torch.long,
604
+ device=device
605
+ ) # (B, N, 2)
606
+
607
+ return spans
608
+
609
+ class MLP(nn.Module):
610
+ def __init__(self, in_size, hid_size, out_size):
611
+ super().__init__()
612
+ self.mlp = nn.Sequential(
613
+ nn.Linear(in_size, hid_size),
614
+ nn.ReLU(),
615
+ nn.Linear(hid_size, out_size)
616
+ )
617
+
618
+ def forward(self, x):
619
+ return self.mlp(x)
620
+
621
+ class IEModel(nn.Module):
622
+ def __init__(self, backbone_model_name, num_labels, max_span_len, topk_spans, keep_neighbor):
623
+ super().__init__()
624
+ self.max_span_len = max_span_len
625
+ self.topk_spans = topk_spans
626
+ self.keep_neighbor = keep_neighbor
627
+
628
+ self.encoder = AutoModel.from_pretrained(backbone_model_name)
629
+ hidden_size = self.encoder.config.hidden_size
630
+
631
+ self.start_classifier = MLP(hidden_size, hidden_size, num_labels)
632
+ self.end_classifier = MLP(hidden_size, hidden_size, num_labels)
633
+
634
+ self.span_classifier = MLP(4*hidden_size, hidden_size, num_labels)
635
+
636
+ def encode(self, input_ids, attention_mask):
637
+ B, n_parts, L = input_ids.shape
638
+ input_ids = input_ids.view(-1, L)
639
+ attention_mask = attention_mask.view(-1, L)
640
+
641
+ outputs = self.encoder(input_ids=input_ids, attention_mask=attention_mask)
642
+ hidden_states = outputs.last_hidden_state # B * n_parts, L, H
643
+
644
+ hidden_states = hidden_states.view(B, n_parts, L, -1).reshape(B, n_parts*L, -1) # B, L, H
645
+ attention_mask = attention_mask.view(B, n_parts, L).reshape(B, n_parts*L) # B, L, H
646
+ return hidden_states, attention_mask
647
+
648
+ def get_token_logits(self, hidden_states):
649
+ start_logits = self.start_classifier(hidden_states) # B, N, classes
650
+ end_logits = self.end_classifier(hidden_states) # B, N, classes
651
+ return start_logits, end_logits
652
+
653
+ def get_logits(self, span_reprs):
654
+ logits = self.span_classifier(span_reprs) # N, classes
655
+ return logits
656
+
657
+ def forward(self, input_ids, attention_mask, spans=None):
658
+ hidden_states, attention_mask = self.encode(input_ids, attention_mask)
659
+ start_logits, end_logits = self.get_token_logits(hidden_states)
660
+ if spans is None:
661
+ spans = filter_spans(start_logits, end_logits, attention_mask, self.max_span_len, self.topk_spans, self.keep_neighbor)
662
+ span_reprs = get_span_reprs(hidden_states, spans)
663
+ logits = self.get_logits(span_reprs)
664
+ return start_logits, end_logits, logits, spans
665
+
666
+ def test_model():
667
+ model = nn.DataParallel(IEModel(backbone_model_name, 7, 10, 100, 0)).to(device)
668
+ model.eval()
669
+ total_params = sum(p.numel() for p in model.parameters())
670
+ print(f"Total params: {total_params:,}")
671
+
672
+ vocab_size = model.module.encoder.config.vocab_size
673
+ max_len = model.module.encoder.config.max_position_embeddings
674
+
675
+ bz = 32
676
+ i = torch.randint(0, vocab_size, (bz, 5, 10)).to(device)
677
+ a = torch.ones(bz, 5, 10).to(device)
678
+ s = torch.ones(bz, 3, 2, dtype=torch.long).to(device)
679
+
680
+ with torch.no_grad():
681
+ r = model(i, a)
682
+
683
+ if type(r) == tuple:
684
+ print([r[i].shape if type(r[i]) == type(torch.Tensor()) else len(r[i]) for i in range(len(r))])
685
+ else:
686
+ print(r.shape)
687
+
688
+ test_model()
689
+
690
+ # %% [code]
691
+ def configure_optimizers(network, optim_params, scheduler_params):
692
+ try:
693
+ optim_params = copy.copy(optim_params)
694
+ scheduler_params = copy.copy(scheduler_params)
695
+
696
+ optim_name = optim_params.pop('name')
697
+ scheduler_name = scheduler_params.pop('name')
698
+
699
+ optimizer_cls = globals().get(optim_name) or getattr(optim, optim_name, None)
700
+ scheduler_cls = globals().get(scheduler_name) or getattr(optim.lr_scheduler, scheduler_name, None)
701
+
702
+ if optimizer_cls is None:
703
+ raise ValueError(f"Optimizer '{optim_name}' is not available!")
704
+
705
+ optimizer = optimizer_cls(network.parameters(), **optim_params)
706
+
707
+ scheduler = None
708
+ if scheduler_params and scheduler_cls: # Chỉ tạo scheduler nếu có tham số
709
+ scheduler = scheduler_cls(optimizer, **scheduler_params)
710
+
711
+ return optimizer, scheduler
712
+
713
+ except KeyError as e:
714
+ raise ValueError(f"Missing {e} in config!!")
715
+
716
+ def freeze(self, model):
717
+ model.eval()
718
+ for param in model.parameters():
719
+ param.requires_grad = False
720
+
721
+ def unfreeze(self, model):
722
+ model.train()
723
+ for param in model.parameters():
724
+ param.requires_grad = True
725
+
726
+ def reduce_batch_size(loader, ratio=0.5):
727
+ new_bs = max(1, int(loader.batch_size * ratio))
728
+
729
+ shuffle = isinstance(loader.sampler, RandomSampler)
730
+
731
+ new_loader = DataLoader(
732
+ dataset=loader.dataset,
733
+ batch_size=new_bs,
734
+ shuffle=shuffle,
735
+ sampler=None if shuffle else loader.sampler,
736
+ num_workers=loader.num_workers,
737
+ collate_fn=loader.collate_fn,
738
+ pin_memory=loader.pin_memory,
739
+ drop_last=loader.drop_last,
740
+ timeout=loader.timeout,
741
+ worker_init_fn=loader.worker_init_fn,
742
+ multiprocessing_context=loader.multiprocessing_context,
743
+ generator=loader.generator,
744
+ prefetch_factor=loader.prefetch_factor if loader.num_workers > 0 else None,
745
+ persistent_workers=loader.persistent_workers,
746
+ pin_memory_device=loader.pin_memory_device
747
+ )
748
+
749
+ return new_loader
750
+
751
+ def list_to_tuple(x):
752
+ if isinstance(x, (list, tuple)):
753
+ return tuple(list_to_tuple(i) for i in x)
754
+ return x
755
+
756
+ def fmt(x):
757
+ if isinstance(x, float):
758
+ return round(x, 5)
759
+ if isinstance(x, dict):
760
+ return {k: fmt(v) for k, v in x.items()}
761
+ if isinstance(x, list):
762
+ return [fmt(v) for v in x]
763
+ return x
764
+
765
+ class ModelEmaV3Proxy(ModelEmaV3):
766
+ def __getattr__(self, name):
767
+ try:
768
+ return super().__getattr__(name)
769
+ except AttributeError:
770
+ return getattr(self.module, name)
771
+
772
+ class DataParallelProxy(nn.DataParallel):
773
+ def __getattr__(self, name):
774
+ try:
775
+ return super().__getattr__(name)
776
+ except AttributeError:
777
+ attr = getattr(self.module, name)
778
+
779
+ if callable(attr):
780
+ def wrapper(*args, **kwargs):
781
+ return self._parallel_apply_method(name, *args, **kwargs)
782
+ return wrapper
783
+
784
+ return attr
785
+
786
+ def _parallel_apply_method(self, method_name, *inputs, **kwargs):
787
+ if not self.device_ids:
788
+ return getattr(self.module, method_name)(*inputs, **kwargs)
789
+
790
+ inputs_scattered, kwargs_scattered = self.scatter(inputs, kwargs, self.device_ids)
791
+
792
+ replicas = self.replicate(self.module, self.device_ids)
793
+
794
+ outputs = self.parallel_apply(
795
+ [getattr(replica, method_name) for replica in replicas],
796
+ inputs_scattered,
797
+ kwargs_scattered
798
+ )
799
+
800
+ return self.gather(outputs, self.output_device)
801
+
802
+ def align(
803
+ all_spans, # (B, N, 2)
804
+ pred_spans, # (B, M, 2)
805
+ obj, # (B, N)
806
+ pad_value=0
807
+ ):
808
+ """
809
+ Return:
810
+ align_obj: (B, M)
811
+ """
812
+
813
+ B, N, _ = all_spans.shape
814
+ M = pred_spans.shape[1]
815
+
816
+ device = all_spans.device
817
+
818
+ # =========================================================
819
+ # Compare spans
820
+ # =========================================================
821
+
822
+ # (B, M, 1, 2)
823
+ pred_expand = pred_spans.unsqueeze(2)
824
+
825
+ # (B, 1, N, 2)
826
+ all_expand = all_spans.unsqueeze(1)
827
+
828
+ # (B, M, N)
829
+ match = (pred_expand == all_expand).all(dim=-1)
830
+
831
+ # =========================================================
832
+ # Gather obj
833
+ # =========================================================
834
+
835
+ # default = pad_value
836
+ align_obj = torch.full(
837
+ (B, M),
838
+ pad_value,
839
+ dtype=obj.dtype,
840
+ device=device
841
+ )
842
+
843
+ # matched index
844
+ has_match = match.any(dim=-1) # (B, M)
845
+
846
+ matched_idx = match.float().argmax(dim=-1) # (B, M)
847
+
848
+ gathered = torch.gather(
849
+ obj,
850
+ dim=1,
851
+ index=matched_idx
852
+ ) # (B, M)
853
+
854
+ align_obj[has_match] = gathered[has_match]
855
+
856
+ # =========================================================
857
+ # Force padding spans -> pad_value
858
+ # =========================================================
859
+
860
+ pad_mask = (pred_spans == 0).all(dim=-1)
861
+
862
+ align_obj[pad_mask] = pad_value
863
+
864
+ return align_obj
865
+
866
+ def extract_spans(
867
+ all_spans, # (B, N, 2)
868
+ all_label, # (B, N)
869
+ pred_spans # (B, M, 2)
870
+ ):
871
+ """
872
+ Return:
873
+ pred_list:
874
+ [(bidx, (s, e)), ...]
875
+
876
+ gold_list:
877
+ [(bidx, (s, e)), ...]
878
+ """
879
+
880
+ # =========================================================
881
+ # Gold spans
882
+ # =========================================================
883
+
884
+ gold_mask = all_label > 0 # (B, N)
885
+
886
+ gold_indices = gold_mask.nonzero(as_tuple=False)
887
+
888
+ gold_list = [
889
+ (
890
+ int(b),
891
+ (
892
+ int(all_spans[b, n, 0]),
893
+ int(all_spans[b, n, 1])
894
+ )
895
+ )
896
+ for b, n in gold_indices
897
+ ]
898
+
899
+ # =========================================================
900
+ # Pred spans
901
+ # =========================================================
902
+
903
+ pred_mask = (pred_spans > 0).all(dim=-1) # (B, M)
904
+
905
+ pred_indices = pred_mask.nonzero(as_tuple=False)
906
+
907
+ pred_list = [
908
+ (
909
+ int(b),
910
+ (
911
+ int(pred_spans[b, m, 0]),
912
+ int(pred_spans[b, m, 1])
913
+ )
914
+ )
915
+ for b, m in pred_indices
916
+ ]
917
+
918
+ return gold_list, pred_list
919
+
920
+ def extract_entities(
921
+ input_ids, # (B, L)
922
+ start_logits, # (B, L, C)
923
+ end_logits, # (B, L, C)
924
+ logits, # (B, N, C)
925
+ pred_spans, # (B, N, 2)
926
+ id2label,
927
+ alpha=1.0
928
+ ):
929
+ """
930
+ Return:
931
+ [
932
+ (batch_idx, ([token_ids], label_name)),
933
+ ...
934
+ ]
935
+ """
936
+
937
+ # =========================================================
938
+ # Log-softmax
939
+ # =========================================================
940
+
941
+ start_logprob = F.log_softmax(start_logits, dim=-1) # (B, L, C)
942
+ end_logprob = F.log_softmax(end_logits, dim=-1) # (B, L, C)
943
+ span_logprob = F.log_softmax(logits, dim=-1) # (B, N, C)
944
+
945
+ # =========================================================
946
+ # Gather start/end score for pred spans
947
+ # =========================================================
948
+
949
+ start_idx = pred_spans[..., 0] # (B, N)
950
+ end_idx = pred_spans[..., 1] # (B, N)
951
+
952
+ B, N = start_idx.shape
953
+ C = logits.shape[-1]
954
+
955
+ # (B, N, C)
956
+ start_score = torch.gather(
957
+ start_logprob,
958
+ dim=1,
959
+ index=start_idx.unsqueeze(-1).expand(-1, -1, C)
960
+ )
961
+
962
+ # (B, N, C)
963
+ end_score = torch.gather(
964
+ end_logprob,
965
+ dim=1,
966
+ index=end_idx.unsqueeze(-1).expand(-1, -1, C)
967
+ )
968
+
969
+ # =========================================================
970
+ # Ensemble score
971
+ # =========================================================
972
+
973
+ score = (
974
+ span_logprob
975
+ + alpha * (start_score + end_score)
976
+ ) # (B, N, C)
977
+
978
+ # =========================================================
979
+ # Predict label
980
+ # =========================================================
981
+
982
+ pred_labels = score.argmax(dim=-1) # (B, N)
983
+
984
+ keep = (
985
+ (pred_labels > 0) &
986
+ (start_idx > 0) &
987
+ (end_idx > 0)
988
+ )
989
+
990
+ # =========================================================
991
+ # Extract entities
992
+ # =========================================================
993
+
994
+ results = []
995
+
996
+ for bidx in range(B):
997
+
998
+ valid_idxes = keep[bidx].nonzero(as_tuple=False).squeeze(-1)
999
+
1000
+ for idx in valid_idxes:
1001
+
1002
+ lb = pred_labels[bidx, idx]
1003
+
1004
+ s, e = pred_spans[bidx, idx].tolist()
1005
+
1006
+ token_ids = input_ids[bidx, s:e+1].tolist()
1007
+
1008
+ results.append(
1009
+ (
1010
+ bidx,
1011
+ (
1012
+ token_ids,
1013
+ id2label[lb.item()]
1014
+ )
1015
+ )
1016
+ )
1017
+
1018
+ return results
1019
+
1020
+ class Trainer:
1021
+ def __init__(
1022
+ self, training_time="00:11:30:00", eval_mode="max", topk=1, save_name="network", save_best=True, save_last=False, max_grad_norm=200.0,
1023
+ logging=0, logging_file=False, checkpoints_dir="", early_stopping=False, eval_from_ratio=-1, eval_every=1, device='cpu',
1024
+ schedule_in_step=True, use_ema=True, ema_from_ratio=-1, ema_decay=0.999, return_best=True, return_last=True
1025
+ ):
1026
+ self.ema_net = None
1027
+
1028
+ self.training_time = self._time_str_to_seconds(training_time)
1029
+ self.mode = eval_mode
1030
+ self.topk = topk
1031
+ self.device = device
1032
+ self.logging = logging if logging < epochs else 1
1033
+ self.logging_file = logging_file
1034
+ self.checkpoints_dir = checkpoints_dir
1035
+ self.early_stopping = early_stopping
1036
+ self.eval_from_ratio = eval_from_ratio
1037
+ self.eval_every = eval_every
1038
+ self.save_name = save_name
1039
+ self.save_best = save_best
1040
+ self.save_last = save_last
1041
+ self.return_best = return_best
1042
+ self.return_last = return_last
1043
+ self.max_grad_norm = max_grad_norm
1044
+ self.schedule_in_step = schedule_in_step
1045
+ self.use_ema = use_ema
1046
+ self.ema_from_ratio = ema_from_ratio
1047
+ self.ema_decay = ema_decay
1048
+
1049
+ self.best_stage = [[float('-inf') if self.mode == 'max' else float('inf'), None, None]]
1050
+ self.grad_scaler = torch.amp.GradScaler(self.device, init_scale=1024.0)
1051
+
1052
+ def fit(self, network, optimizer, scheduler, loss_fn, epochs, train_loader, val_loader=None, eval_fn=None, start_epoch=1, start_training_time=None, id2label=None):
1053
+ if eval_fn is None:
1054
+ if self.mode == "max":
1055
+ eval_fn = lambda *x: -loss_fn(*x)
1056
+ else:
1057
+ eval_fn = lambda *x: loss_fn(*x)
1058
+
1059
+ if torch.cuda.device_count() > 1:
1060
+ network = DataParallelProxy(network)
1061
+ network = network.to(self.device)
1062
+
1063
+ if not start_training_time:
1064
+ start_training_time = time.time()
1065
+
1066
+ start_ema = int(epochs * self.ema_from_ratio)
1067
+ start_eval = int(epochs * self.eval_from_ratio)
1068
+
1069
+ if val_loader is None:
1070
+ print(f'[Trainer CallBack] 📢 Không có Val Set, không thể đánh giá và Early Stopping!')
1071
+ else:
1072
+ model_to_use_str = 'mô hình EMA' if self.use_ema else 'mô hình gốc'
1073
+ start_model_update_str = f'Bắt đầu cập nhật EMA từ epoch {start_epoch + start_ema}!' if self.use_ema else ''
1074
+ print(f'[Trainer CallBack] 📢 Đánh giá bằng {model_to_use_str} từ epoch {start_epoch + start_eval}!', start_model_update_str)
1075
+
1076
+ training_log = {}
1077
+ for epoch in range(start_epoch, epochs+start_epoch):
1078
+ if self.use_ema and self.ema_net is None and epoch - start_epoch >= start_ema:
1079
+ self.ema_net = ModelEmaV3Proxy(network, self.ema_decay, device=self.device)
1080
+
1081
+ try:
1082
+ teaching_rate = math.cos(math.pi / 2 * (epoch - 2) / (epochs - 2)) if epoch - 2 > 0 else 1.0
1083
+ train_loss_epoch, train_loss_epoch_dict = self._train_epoch(network, train_loader, optimizer, scheduler, loss_fn, teaching_rate)
1084
+ logging_dict = {'lr': [group['lr'] for group in optimizer.param_groups], 'train_loss': train_loss_epoch}
1085
+ logging_dict.update(train_loss_epoch_dict)
1086
+
1087
+ if val_loader is not None and epoch - start_epoch >= start_eval and (epoch - start_epoch - start_eval) % self.eval_every == 0:
1088
+ eval_net = self.ema_net.module if (self.use_ema and self.ema_net is not None) else network
1089
+
1090
+ val_score, val_score_dict, _ = self._eval_epoch(eval_net, val_loader, eval_fn, id2label)
1091
+ update = self._update_best_network(eval_net, val_score, epoch)
1092
+ logging_dict.update({'val_score': val_score, 'best_score': self.best_stage[0][0], 'new_best_model': update})
1093
+ logging_dict.update(val_score_dict)
1094
+ if not self.schedule_in_step and scheduler:
1095
+ scheduler.step()
1096
+
1097
+ except RuntimeError as e:
1098
+ if "out of memory" in str(e).lower():
1099
+ print(f"[Trainer CallBack] ⚠️ Epoch {epoch}/{epochs}: CUDA Out of Memory! Clearing GPU cache...")
1100
+ torch.cuda.empty_cache()
1101
+ gc.collect()
1102
+ if torch.cuda.is_available():
1103
+ torch.cuda.synchronize()
1104
+ print(f"[Trainer CallBack] ✅ Epoch {epoch}/{epochs}: GPU memory cleared")
1105
+
1106
+ train_loader = reduce_batch_size(train_loader, ratio=0.5)
1107
+ if val_loader is not None:
1108
+ val_loader = reduce_batch_size(val_loader, ratio=0.5)
1109
+
1110
+ logging_dict = {'lr': [group['lr'] for group in optimizer.param_groups], 'train_loss': float('inf')}
1111
+ else:
1112
+ raise
1113
+
1114
+ training_log[epoch] = logging_dict
1115
+ if self.is_early_stopping(epoch):
1116
+ print(f'[Trainer CallBack] 📢 Epoch {epoch}/{epochs}: Detect Overfitting! Breaking Training Process...')
1117
+ break
1118
+ if self.logging:
1119
+ if epoch % self.logging == 0:
1120
+ print(f'[Trainer CallBack] 📢 Epoch {epoch}/{epochs}:', fmt(logging_dict))
1121
+ else:
1122
+ print(f'{epoch}...', end=' ')
1123
+
1124
+ if self._at_time_limit(start_training_time):
1125
+ print(f'[Trainer CallBack] ⚠️ Epoch {epoch}/{epochs}: Thời gian training giới hạn là {self.training_time}, hết giờ tại epoch {epoch}/{epochs}')
1126
+ break
1127
+
1128
+ if self.logging_file:
1129
+ os.makedirs(f'{self.checkpoints_dir}/logs', exist_ok=True)
1130
+ with open(f"{self.checkpoints_dir}/logs/{self.save_name}_logging.json", "a", encoding="utf-8") as f:
1131
+ f.write(json.dumps(training_log))
1132
+
1133
+ if self.use_ema and self.ema_net is not None:
1134
+ self._save_state_dict(self.ema_net.module)
1135
+ else:
1136
+ self._save_state_dict(network)
1137
+ print(f'[Trainer CallBack] 📢 Kết thúc training.\n')
1138
+
1139
+ best_model, last_model = None, None
1140
+ eval_net = self.ema_net.module if (self.use_ema and self.ema_net is not None) else network
1141
+ if self.return_best :
1142
+ best_model = self.best_stage[0][2] if self.best_stage[0][2] is not None else eval_net.state_dict()
1143
+ best_model = {k.replace("module.", ""): v.detach().cpu().clone() for k, v in best_model.items()}
1144
+ if self.return_last:
1145
+ last_model = eval_net.state_dict()
1146
+ last_model = {k.replace("module.", ""): v.detach().cpu().clone() for k, v in last_model.items()}
1147
+
1148
+ del network
1149
+ torch.cuda.empty_cache()
1150
+ gc.collect()
1151
+ return training_log, best_model, last_model
1152
+
1153
+ def _time_str_to_seconds(self, time_str):
1154
+ days, hours, minutes, seconds = map(int, time_str.split(":"))
1155
+ return days * 86400 + hours * 3600 + minutes * 60 + seconds
1156
+
1157
+ def _update_best_network(self, network, val_score, epoch):
1158
+ topk = max(1, self.topk)
1159
+ self.best_stage.append([val_score, epoch, {k: v.detach().cpu().clone() for k, v in network.state_dict().items()}])
1160
+ self.best_stage = sorted(self.best_stage, reverse=(self.mode == 'max'), key=lambda x: x[0])[:topk]
1161
+ if val_score in [x[0] for x in self.best_stage]:
1162
+ return True
1163
+ return False
1164
+
1165
+ def is_early_stopping(self, epoch):
1166
+ if self.best_stage[0][1] is None:
1167
+ return False
1168
+ if not self.early_stopping:
1169
+ return False
1170
+ return epoch - self.best_stage[0][1] >= self.early_stopping
1171
+
1172
+ def _at_time_limit(self, start_training_time):
1173
+ return time.time() - start_training_time >= self.training_time
1174
+
1175
+ def _save_state_dict(self, network):
1176
+ if self.topk <= 0:
1177
+ return
1178
+
1179
+ if self.save_best:
1180
+ for r in range(self.topk):
1181
+ os.makedirs(f'{self.checkpoints_dir}/r{r+1}s', exist_ok=True)
1182
+
1183
+ for rank, (score, epoch, state_dict) in enumerate(self.best_stage):
1184
+ if state_dict is None:
1185
+ continue
1186
+ state_dict = {k.replace("module.", ""): v.detach().cpu().clone() for k, v in state_dict.items()}
1187
+ torch.save(state_dict, f'{self.checkpoints_dir}/r{rank+1}s/{self.save_name}_r{rank+1}_vs{score:.5f}_{"ema" if self.ema_net is not None else ""}.pth')
1188
+ if self.save_last:
1189
+ os.makedirs(f'{self.checkpoints_dir}/lasts', exist_ok=True)
1190
+ state_dict = {k.replace("module.", ""): v.detach().cpu().clone() for k, v in network.state_dict().items()}
1191
+ torch.save(state_dict, f'{self.checkpoints_dir}/lasts/{self.save_name}_last_{"ema" if self.ema_net is not None else ""}.pth')
1192
+
1193
+ def _train_epoch(self, network, train_loader, optimizer, scheduler, loss_fn, teaching_rate):
1194
+ network.train()
1195
+ total_loss = 0
1196
+ total_loss_dict = {}
1197
+ for batch_idx, batch in enumerate(train_loader):
1198
+ optimizer.zero_grad()
1199
+ with torch.autocast(device_type=self.device, dtype=torch.float16):
1200
+ loss, loss_dict = self._cal_loss(network, batch, batch_idx, loss_fn, teaching_rate)
1201
+
1202
+ for k, v in loss_dict.items():
1203
+ t = total_loss_dict.get(k, 0)
1204
+ total_loss_dict[k] = t + v
1205
+ self.grad_scaler.scale(loss).backward()
1206
+ self.grad_scaler.unscale_(optimizer)
1207
+ grad_norm = nn.utils.clip_grad_norm_(network.parameters(), self.max_grad_norm)
1208
+ # print(grad_norm) # Bỏ cmt dòng này để biết nên chọn max_grad_norm bằng bao nhiêu...
1209
+ self.grad_scaler.step(optimizer)
1210
+ self.grad_scaler.update()
1211
+ if self.schedule_in_step and scheduler:
1212
+ scheduler.step()
1213
+ if self.use_ema and self.ema_net is not None:
1214
+ self.ema_net.update(network)
1215
+ total_loss += loss
1216
+ return (total_loss / len(train_loader)).item(), {k: v.item() / len(train_loader) for k, v in total_loss_dict.items()}
1217
+
1218
+ def _eval_epoch(self, network, val_loader, eval_fn, id2label):
1219
+ network.eval()
1220
+ total_score = 0.0
1221
+ total_score_dict = {}
1222
+ object_lists = None # sẽ init sau
1223
+
1224
+ with torch.no_grad():
1225
+ for batch_idx, batch in enumerate(val_loader):
1226
+ score, score_dict, objects = self._cal_val_score(network, batch, batch_idx, eval_fn, id2label)
1227
+ total_score += score
1228
+
1229
+ for k, v in score_dict.items():
1230
+ t = total_score_dict.get(k, 0)
1231
+ total_score_dict[k] = t + v
1232
+
1233
+ if objects:
1234
+ if object_lists is None:
1235
+ object_lists = [[] for _ in range(len(objects))]
1236
+
1237
+ for i, obj in enumerate(objects):
1238
+ object_lists[i].append(obj.detach())
1239
+
1240
+ if object_lists is not None:
1241
+ object_arrays = [
1242
+ torch.concat(obj_list, dim=0).cpu().numpy()
1243
+ for obj_list in object_lists
1244
+ ]
1245
+ else:
1246
+ object_arrays = []
1247
+
1248
+ return total_score / len(val_loader), {k: v / len(val_loader) for k, v in total_score_dict.items()}, object_arrays
1249
+
1250
+ def _cal_loss(self, network, batch, batch_idx, loss_fn, teaching_rate):
1251
+ # Bạn cần override _cal_loss để tính loss
1252
+ input_ids = batch['input_ids'].to(self.device)
1253
+ attention_mask = batch['attention_mask'].to(self.device)
1254
+ all_spans = batch['all_spans'].to(self.device)
1255
+ all_labels = batch['all_labels'].to(self.device)
1256
+ all_weights = batch['all_weights'].to(self.device)
1257
+ start_labels = batch['start_labels'].to(self.device)
1258
+ end_labels = batch['end_labels'].to(self.device)
1259
+
1260
+ hidden_states, attention_mask = network.encode(input_ids, attention_mask)
1261
+ start_logits, end_logits = network.get_token_logits(hidden_states)
1262
+
1263
+ choice = random.random()
1264
+ if choice < teaching_rate:
1265
+ pred_spans = all_spans
1266
+ else:
1267
+ pred_spans = filter_spans(start_logits, end_logits, attention_mask, network.max_span_len, network.topk_spans, network.keep_neighbor)
1268
+
1269
+ span_reprs = get_span_reprs(hidden_states, pred_spans)
1270
+ logits = network.get_logits(span_reprs)
1271
+
1272
+ align_labels = align(all_spans, pred_spans, all_labels, -100)
1273
+ align_weights = align(all_spans, pred_spans, all_weights, 0)
1274
+
1275
+ loss_dict = loss_fn(
1276
+ logits, align_labels, align_weights,
1277
+ start_logits, start_labels,
1278
+ end_logits, end_labels,
1279
+ )
1280
+ return loss_dict['total'], loss_dict
1281
+
1282
+ def _cal_val_score(self, network, batch, batch_idx, eval_fn, id2label):
1283
+ # Bạn cần override _cal_val_score để tính val score, list bên cạnh là để trả về y hay pred gì đó (nếu cần)
1284
+ input_ids = batch['input_ids'].to(self.device)
1285
+ attention_mask = batch['attention_mask'].to(self.device)
1286
+ all_spans = batch['all_spans'].to(self.device)
1287
+ all_labels = batch['all_labels'].to(self.device)
1288
+ gold_entities = batch['gold_entities']
1289
+
1290
+ B, _, _ = input_ids.shape
1291
+
1292
+ hidden_states, attention_mask = network.encode(input_ids, attention_mask)
1293
+ start_logits, end_logits = network.get_token_logits(hidden_states)
1294
+ pred_spans = filter_spans(start_logits, end_logits, attention_mask, network.max_span_len, network.topk_spans, network.keep_neighbor)
1295
+ span_reprs = get_span_reprs(hidden_states, pred_spans)
1296
+ logits = network.get_logits(span_reprs)
1297
+
1298
+ gold_list, pred_list = extract_spans(all_spans, all_labels, pred_spans)
1299
+ gold_list = list_to_tuple(gold_list)
1300
+ pred_list = list_to_tuple(pred_list)
1301
+ span_score = eval_fn(pred_list, gold_list)['recall']
1302
+
1303
+ pred_ids = extract_entities(input_ids.reshape(B, -1), start_logits, end_logits, logits, pred_spans, id2label, alpha=0.5)
1304
+ pred_ids = list_to_tuple(pred_ids)
1305
+ gold_ids = list_to_tuple(gold_entities)
1306
+ score_dict = eval_fn(pred_ids, gold_ids)
1307
+
1308
+ score_dict.update({'span_recall': span_score})
1309
+ return score_dict['f1'] + score_dict['span_recall'], score_dict, []
1310
+
1311
+ # %% [code]
1312
+ class PhoBERTSpanAligner:
1313
+ def __init__(self, tokenizer, max_len):
1314
+ self.tokenizer = tokenizer
1315
+ self.max_len = max_len
1316
+
1317
+ # ===== 1. Extract discontinuous spans =====
1318
+ def extract_spans(self, sample):
1319
+ entity_spans = []
1320
+
1321
+ for event in sample["entities"]:
1322
+ entity_type = event["label"]
1323
+ spans = [tuple(event["offset"])]
1324
+ entity_spans.append({
1325
+ "spans": spans,
1326
+ "label": entity_type
1327
+ })
1328
+
1329
+ return entity_spans
1330
+
1331
+ # ===== 2. Word offsets =====
1332
+ def build_word_offsets(self, text, words):
1333
+ offsets = []
1334
+ pointer = 0
1335
+
1336
+ for word in words:
1337
+ start = text.find(word, pointer)
1338
+ end = start + len(word)
1339
+ offsets.append((start, end))
1340
+ pointer = end
1341
+
1342
+ return offsets
1343
+
1344
+ # ===== 3. Char → word =====
1345
+ def char_span_to_word_span(self, word_offsets, start, end):
1346
+ start_word = None
1347
+ end_word = None
1348
+
1349
+ for i, (w_start, w_end) in enumerate(word_offsets):
1350
+ if w_start <= start < w_end:
1351
+ start_word = i
1352
+ if w_start < end <= w_end:
1353
+ end_word = i
1354
+
1355
+ return start_word, end_word
1356
+
1357
+ # ===== 4. Word → subword =====
1358
+ def word_to_subword_map(self, words):
1359
+ mapping = []
1360
+ subword_index = 1 # <s>
1361
+
1362
+ for word in words:
1363
+ sub_tokens = self.tokenizer.tokenize(word)
1364
+ start = subword_index
1365
+ end = subword_index + len(sub_tokens) - 1
1366
+ mapping.append((start, end))
1367
+ subword_index += len(sub_tokens)
1368
+
1369
+ return mapping
1370
+
1371
+ # ===== 5. Span → subword =====
1372
+ def span_to_subword(self, word_offsets, word_subword_map, spans):
1373
+ sub_spans = []
1374
+
1375
+ for span_start, span_end in spans:
1376
+ w_start, w_end = self.char_span_to_word_span(
1377
+ word_offsets, span_start, span_end
1378
+ )
1379
+ if w_start is None or w_end is None:
1380
+ continue
1381
+
1382
+ sub_start = word_subword_map[w_start][0]
1383
+ sub_end = word_subword_map[w_end][1]
1384
+ sub_spans.append((sub_start, sub_end))
1385
+
1386
+ return sub_spans
1387
+
1388
+ def extract_valid_spans(self, sub_spans):
1389
+ valid_spans = []
1390
+ for s, e in sub_spans:
1391
+ if s < 0 or e < 0 or s >= self.max_len or e >= self.max_len or s > e:
1392
+ continue
1393
+ valid_spans.append((s, e))
1394
+ return valid_spans
1395
+
1396
+ def encode(self, sample):
1397
+ text = sample["text"]
1398
+ entities = self.extract_spans(sample)
1399
+
1400
+ # ===== 1. Word tokenize =====
1401
+ words = word_tokenize(text)
1402
+ sentence = " ".join(words)
1403
+
1404
+ # ===== 2. Mapping =====
1405
+ word_offsets = self.build_word_offsets(text, words)
1406
+ word_subword_map = self.word_to_subword_map(words)
1407
+
1408
+ # ===== 3. Tokenize FULL =====
1409
+ encoding = self.tokenizer(
1410
+ sentence,
1411
+ max_length=self.max_len,
1412
+ truncation=True,
1413
+ padding="max_length",
1414
+ return_tensors="pt"
1415
+ )
1416
+ input_ids = encoding["input_ids"][0]
1417
+ attention_mask = encoding["attention_mask"][0]
1418
+
1419
+ # ===== 5. Convert spans =====
1420
+ entities_gold_spans = []
1421
+
1422
+ for ent in entities:
1423
+ label = ent["label"]
1424
+
1425
+ sub_spans = self.span_to_subword(
1426
+ word_offsets,
1427
+ word_subword_map,
1428
+ ent["spans"]
1429
+ )
1430
+ valid_spans = self.extract_valid_spans(sub_spans)
1431
+ if len(valid_spans) == 0:
1432
+ continue
1433
+ entities_gold_spans.append((tuple(valid_spans), label))
1434
+
1435
+ return {
1436
+ "input_ids": input_ids,
1437
+ "attention_mask": attention_mask,
1438
+ "entities_gold_spans": entities_gold_spans,
1439
+ }
1440
+
1441
+ def generate_spans(attention_mask, max_span_len):
1442
+ seq_len = attention_mask.sum().item() - 2
1443
+ spans = []
1444
+ for i in range(1, seq_len+1):
1445
+ for j in range(i, min(i+max_span_len, seq_len+1)):
1446
+ spans.append((i, j))
1447
+ return spans
1448
+
1449
+ def match_gold_labels(
1450
+ gold_spans, # (N, 2)
1451
+ gold_labels, # (N,)
1452
+ pred_spans, # (M, 2)
1453
+ default_label=-100
1454
+ ):
1455
+ """
1456
+ Return:
1457
+ pred_labels: (M,)
1458
+ """
1459
+
1460
+ pred_labels = torch.full(
1461
+ (pred_spans.size(0),),
1462
+ default_label,
1463
+ dtype=gold_labels.dtype,
1464
+ device=gold_labels.device
1465
+ )
1466
+ if gold_spans.size(0) == 0:
1467
+ return pred_labels
1468
+
1469
+ # (M, N)
1470
+ matched = (pred_spans[:, None, :] == gold_spans[None, :, :]).all(dim=-1)
1471
+ has_match = matched.any(dim=1)
1472
+
1473
+ # lấy index gold đầu tiên match
1474
+ gold_idx = matched.float().argmax(dim=1)
1475
+
1476
+ pred_labels[has_match] = gold_labels[gold_idx[has_match]]
1477
+
1478
+ return pred_labels
1479
+
1480
+ class KLTNDataset(Dataset):
1481
+ def __init__(self, all_data, using_idxes, label2id, tokenizer, max_len, max_n_parts, max_span_len, weight_rate):
1482
+ super().__init__()
1483
+ self.tokenizer = tokenizer
1484
+ self.aligner = PhoBERTSpanAligner(tokenizer, max_len*max_n_parts)
1485
+ self.all_data = all_data
1486
+ self.using_idxes = using_idxes
1487
+ self.label2id = label2id
1488
+ self.max_len = max_len
1489
+ self.max_n_parts = max_n_parts
1490
+ self.max_span_len = max_span_len
1491
+ self.weight_rate = weight_rate
1492
+
1493
+ def __len__(self):
1494
+ return len(self.using_idxes)
1495
+
1496
+ def span_iou(self, span1, span2):
1497
+ s1, e1 = span1
1498
+ s2, e2 = span2
1499
+
1500
+ # intersection
1501
+ inter_left = max(s1, s2)
1502
+ inter_right = min(e1, e2)
1503
+ intersection = max(0, inter_right - inter_left + 1)
1504
+
1505
+ # lengths
1506
+ len1 = e1 - s1 + 1
1507
+ len2 = e2 - s2 + 1
1508
+
1509
+ # union
1510
+ union = len1 + len2 - intersection
1511
+ if union == 0:
1512
+ return 0.0
1513
+
1514
+ return intersection / union
1515
+
1516
+ def get_weights(self, spans, pos_spans):
1517
+ # spans: (N, 2), pos_spans: (K, 2)
1518
+ N, K = spans.size(0), pos_spans.size(0)
1519
+ device = spans.device
1520
+
1521
+ # ===== edge case =====
1522
+ if K == 0:
1523
+ weights = torch.ones(N, device=device, dtype=torch.float)
1524
+ return weights
1525
+
1526
+ # ===== IoU =====
1527
+ s1, e1 = spans[:, None, 0], spans[:, None, 1]
1528
+ s2, e2 = pos_spans[None, :, 0], pos_spans[None, :, 1]
1529
+
1530
+ inter_s = torch.maximum(s1, s2)
1531
+ inter_e = torch.minimum(e1, e2)
1532
+ inter = (inter_e - inter_s + 1).clamp(min=0)
1533
+
1534
+ len1 = (e1 - s1 + 1)
1535
+ len2 = (e2 - s2 + 1)
1536
+ union = len1 + len2 - inter
1537
+
1538
+ iou = inter / (union + 1e-8) # (N, K)
1539
+
1540
+ # ===== weights: IoU=0 -> 1, else 10*IoU =====
1541
+ max_iou, _ = iou.max(dim=1)
1542
+ if self.weight_rate is not None:
1543
+ weights = torch.where(max_iou > 0, 1 + self.weight_rate * max_iou, torch.ones_like(max_iou))
1544
+ else:
1545
+ weights = torch.ones_like(max_iou)
1546
+
1547
+ return weights
1548
+
1549
+ def to_span_label_tensors(self, data, label_map):
1550
+ if len(data) == 0:
1551
+ return (
1552
+ torch.zeros((0, 2), dtype=torch.long),
1553
+ torch.zeros((0,), dtype=torch.long)
1554
+ )
1555
+
1556
+ spans = torch.tensor([list(spans[0]) for spans, _ in data], dtype=torch.long)
1557
+ labels = torch.tensor([label_map[label] for _, label in data], dtype=torch.long)
1558
+ return spans, labels
1559
+
1560
+ def __getitem__(self, idx):
1561
+ ridx = self.using_idxes[idx]
1562
+ sample = self.all_data[ridx]
1563
+ result = self.aligner.encode(sample)
1564
+
1565
+ input_ids = result["input_ids"].squeeze(0)
1566
+ attention_mask = result["attention_mask"].squeeze(0)
1567
+ entities_gold_spans = result["entities_gold_spans"]
1568
+
1569
+ # Get all spans
1570
+ all_spans = torch.tensor(generate_spans(attention_mask, self.max_span_len))
1571
+ gold_spans = torch.tensor([spans[0] for spans, _ in entities_gold_spans], dtype=torch.long) if entities_gold_spans else torch.empty(0, 2, dtype=torch.long)
1572
+ gold_labels = torch.tensor([self.label2id[label] for _, label in entities_gold_spans], dtype=torch.long) if entities_gold_spans else torch.empty(0, dtype=torch.long)
1573
+ all_labels = match_gold_labels(
1574
+ gold_spans, # (N, 2)
1575
+ gold_labels, # (N,)
1576
+ all_spans, # (M, 2)
1577
+ default_label=0
1578
+ )
1579
+ all_weights = self.get_weights(all_spans, gold_spans)
1580
+
1581
+ # Get label
1582
+ gold_entities = []
1583
+ start_labels = torch.ones_like(input_ids) * (1-attention_mask) * (-100)
1584
+ end_labels = torch.ones_like(input_ids) * (1-attention_mask) * (-100)
1585
+ for spans, label in entities_gold_spans:
1586
+ s, e = spans[0]
1587
+
1588
+ start_labels[s] = self.label2id[f'{label}']
1589
+ end_labels[e] = self.label2id[f'{label}']
1590
+
1591
+ gold_entities.append((tuple(input_ids[s:e+1].tolist()), label))
1592
+
1593
+ input_ids = input_ids.reshape(self.max_n_parts, self.max_len)
1594
+ attention_mask = attention_mask.reshape(self.max_n_parts, self.max_len)
1595
+
1596
+ n_valid_parts = math.ceil(attention_mask.sum().item() / self.max_len)
1597
+ input_ids = input_ids[:n_valid_parts]
1598
+ attention_mask = attention_mask[:n_valid_parts]
1599
+ start_labels = start_labels[:n_valid_parts*self.max_len]
1600
+ end_labels = end_labels[:n_valid_parts*self.max_len]
1601
+
1602
+ return {
1603
+ "input_ids": input_ids,
1604
+ "attention_mask": attention_mask,
1605
+ "all_spans": all_spans,
1606
+ "all_labels": all_labels,
1607
+ "all_weights": all_weights,
1608
+ "start_labels": start_labels,
1609
+ "end_labels": end_labels,
1610
+ "gold_entities": gold_entities,
1611
+ }
1612
+
1613
+ def _pad_batch(tensor_list, pad_value=0):
1614
+ """
1615
+ tensor_list: list of tensors
1616
+ mỗi tensor shape: (Nk, n_parts_i, max_len_i)
1617
+
1618
+ return:
1619
+ padded tensor shape: (B, max_Nk, max_n_parts, max_len)
1620
+ """
1621
+
1622
+ # lấy max toàn batch
1623
+ max_Nk = max(t.size(0) for t in tensor_list)
1624
+ max_n_parts = max(t.size(1) for t in tensor_list)
1625
+ max_len = max(t.size(2) for t in tensor_list)
1626
+
1627
+ padded = []
1628
+
1629
+ for t in tensor_list:
1630
+ Nk, n_parts_i, max_len_i = t.shape
1631
+
1632
+ # pad chiều n_parts và max_len trước
1633
+ if n_parts_i < max_n_parts or max_len_i < max_len:
1634
+ new_t = t.new_full(
1635
+ (Nk, max_n_parts, max_len),
1636
+ pad_value
1637
+ )
1638
+ new_t[:, :n_parts_i, :max_len_i] = t
1639
+ t = new_t
1640
+
1641
+ # pad chiều Nk
1642
+ if Nk < max_Nk:
1643
+ pad_tensor = t.new_full(
1644
+ (max_Nk - Nk, max_n_parts, max_len),
1645
+ pad_value
1646
+ )
1647
+ t = torch.cat([t, pad_tensor], dim=0)
1648
+
1649
+ padded.append(t)
1650
+
1651
+ return torch.stack(padded) # (B, max_Nk, max_n_parts, max_len)
1652
+
1653
+ def collate_fn(batch):
1654
+ gold_entities = []
1655
+ for bidx, b in enumerate(batch):
1656
+ for entity in b['gold_entities']:
1657
+ gold_entities.append([bidx, entity])
1658
+
1659
+ input_ids = [b["input_ids"].unsqueeze(-1) for b in batch]
1660
+ attention_mask = [b["attention_mask"].unsqueeze(-1) for b in batch]
1661
+ all_spans = [b["all_spans"].unsqueeze(-1) for b in batch]
1662
+ all_labels = [b["all_labels"].unsqueeze(-1).unsqueeze(-1) for b in batch]
1663
+ all_weights = [b["all_weights"].unsqueeze(-1).unsqueeze(-1) for b in batch]
1664
+ start_labels = [b["start_labels"].unsqueeze(-1).unsqueeze(-1) for b in batch]
1665
+ end_labels = [b["end_labels"].unsqueeze(-1).unsqueeze(-1) for b in batch]
1666
+
1667
+ # pad theo Nk
1668
+ input_ids = _pad_batch(input_ids, pad_value=0).squeeze(-1)
1669
+ attention_mask = _pad_batch(attention_mask, pad_value=0).squeeze(-1)
1670
+ all_spans = _pad_batch(all_spans, pad_value=0).squeeze(-1)
1671
+ all_labels = _pad_batch(all_labels, pad_value=-100).squeeze(-1).squeeze(-1)
1672
+ all_weights = _pad_batch(all_weights, pad_value=0).squeeze(-1).squeeze(-1)
1673
+ start_labels = _pad_batch(start_labels, pad_value=-100).squeeze(-1).squeeze(-1)
1674
+ end_labels = _pad_batch(end_labels, pad_value=-100).squeeze(-1).squeeze(-1)
1675
+
1676
+ return {
1677
+ "input_ids": input_ids,
1678
+ "attention_mask": attention_mask,
1679
+ "all_spans": all_spans,
1680
+ "all_labels": all_labels,
1681
+ "all_weights": all_weights,
1682
+ "start_labels": start_labels,
1683
+ "end_labels": end_labels,
1684
+ "gold_entities": gold_entities,
1685
+ }
1686
+
1687
+ # %% [code]
1688
+ def shift_bidx(spans, batch_idx):
1689
+ shifted = []
1690
+ for bidx, ent in spans:
1691
+ new_bidx = bidx + batch_idx * batch_size
1692
+ shifted.append((new_bidx, ent))
1693
+ return shifted
1694
+
1695
+ def refactor_entities(entities, save_dict):
1696
+ i, c = [], []
1697
+ for bidx, (ids, lb) in entities:
1698
+ if (bidx, ids) not in i:
1699
+ i.append((bidx, ids))
1700
+
1701
+ if (bidx, (ids, lb)) not in c:
1702
+ c.append((bidx, (ids, lb)))
1703
+
1704
+ save_dict['Ent-I'].extend(i)
1705
+ save_dict['Ent-C'].extend(c)
1706
+
1707
+ def test(
1708
+ network, state_dicts, test_loader, eval_fn, analyzer, device, id2label, tokenizer,
1709
+ alphas=[0.0, 0.2, 0.4, 0.5, 0.6, 0.8, 1.0], keep_neighbors=[0, 1, 2, 3]
1710
+ ):
1711
+ if torch.cuda.device_count() > 1:
1712
+ network = DataParallelProxy(network)
1713
+ network = network.to(device)
1714
+ network.eval()
1715
+
1716
+ eval_types = ['Ent-I', 'Ent-C']
1717
+
1718
+ all_pred = {(keep_neighbor, alpha): {eval_type: [] for eval_type in eval_types} for alpha in alphas for keep_neighbor in keep_neighbors}
1719
+ all_gold = {eval_type: [] for eval_type in eval_types}
1720
+
1721
+ list_input_ids = []
1722
+
1723
+ with torch.no_grad():
1724
+ for batch_idx, batch in enumerate(test_loader):
1725
+ input_ids = batch['input_ids'].to(device)
1726
+ attention_mask = batch['attention_mask'].to(device)
1727
+ all_spans = batch['all_spans'].to(device)
1728
+ gold_entities = batch['gold_entities']
1729
+
1730
+ B, _, _ = input_ids.shape
1731
+ list_input_ids.extend(input_ids.reshape(B, -1).tolist())
1732
+
1733
+ list_hidden_states = []
1734
+ list_start_logits = []
1735
+ list_end_logits = []
1736
+ for sd in state_dicts:
1737
+ if torch.cuda.device_count() > 1:
1738
+ network.module.load_state_dict(sd)
1739
+ else:
1740
+ network.load_state_dict(sd)
1741
+
1742
+ hidden_states, attention_mask = network.encode(input_ids, attention_mask)
1743
+ start_logits, end_logits = network.get_token_logits(hidden_states)
1744
+ list_hidden_states.append(hidden_states)
1745
+ list_start_logits.append(start_logits)
1746
+ list_end_logits.append(end_logits)
1747
+
1748
+ ensemble_start_logits = torch.stack(list_start_logits, dim=0).mean(dim=0)
1749
+ ensemble_end_logits = torch.stack(list_end_logits, dim=0).mean(dim=0)
1750
+
1751
+ for keep_neighbor in keep_neighbors:
1752
+ list_logits = []
1753
+ spans = filter_spans(ensemble_start_logits, ensemble_end_logits, attention_mask, network.max_span_len, network.topk_spans, keep_neighbor)
1754
+
1755
+ for sd, hidden_states in zip(state_dicts, list_hidden_states):
1756
+ if torch.cuda.device_count() > 1:
1757
+ network.module.load_state_dict(sd)
1758
+ else:
1759
+ network.load_state_dict(sd)
1760
+ span_reprs = get_span_reprs(hidden_states, spans)
1761
+ logits = network.get_logits(span_reprs)
1762
+ list_logits.append(logits)
1763
+
1764
+ ensemble_logits = torch.stack(list_logits, dim=0).mean(dim=0)
1765
+ for alpha in alphas:
1766
+ pred_entities = extract_entities(input_ids.reshape(B, -1), ensemble_start_logits, ensemble_end_logits, ensemble_logits, spans, id2label, alpha=alpha)
1767
+ pred_entities = shift_bidx(pred_entities, batch_idx)
1768
+ refactor_entities(pred_entities, all_pred[keep_neighbor, alpha])
1769
+
1770
+ gold_entities = shift_bidx(gold_entities, batch_idx)
1771
+ refactor_entities(gold_entities, all_gold)
1772
+
1773
+ # ===== GLOBAL EVAL =====
1774
+ final_score = {}
1775
+ max_key = -1
1776
+ max_scores = -1
1777
+ for key in all_pred.keys():
1778
+ final_score[key] = {}
1779
+ for eval_type in eval_types:
1780
+ score = eval_fn(list_to_tuple(all_pred[key][eval_type]), list_to_tuple(all_gold[eval_type]))
1781
+ final_score[key][eval_type] = score
1782
+ if max_scores < final_score[key]['Ent-C']['f1']:
1783
+ max_scores = final_score[key]['Ent-C']['f1']
1784
+ max_key = key
1785
+
1786
+ analyze_result = analyzer.analyze(list_to_tuple(all_pred[max_key]['Ent-I']), list_to_tuple(all_gold['Ent-I']))
1787
+
1788
+ # ===== PREDICT =====
1789
+ predictions = []
1790
+ for input_ids in list_input_ids:
1791
+ predictions.append([tokenizer.decode(input_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True)])
1792
+ for bidx, (ids, lb) in all_pred[max_key]['Ent-C']:
1793
+ predictions[bidx].append((tokenizer.decode(ids, skip_special_tokens=True, clean_up_tokenization_spaces=True), lb))
1794
+
1795
+ return final_score, analyze_result, predictions
1796
+
1797
+ # %% [code]
1798
+ with open(f'{train_dir}/train.json', "r", encoding="utf-8") as f:
1799
+ data_train = json.load(f)
1800
+
1801
+ with open(f'{test_dir}/test.json', "r", encoding="utf-8") as f:
1802
+ data_test = json.load(f)
1803
+
1804
+ print('Train:', len(data_train))
1805
+ print('Test:', len(data_test))
1806
+
1807
+ # %% [code]
1808
+ entity_types = ['O'] + sorted(list(set([e['label'] for d in data_train + data_test for e in d['entities']])))
1809
+ # bio_entity_type = ['O'] + [f'{prefix}-{ent}' for ent in entity_types for prefix in ['B', 'I']]
1810
+ label2id = {l: i for i, l in enumerate(entity_types)}
1811
+ id2label = {i: l for l, i in label2id.items()}
1812
+
1813
+ # %% [code]
1814
+ zero_entities_idxes = []
1815
+ for idx, d in enumerate(data_train):
1816
+ if len(d['entities']) == 0:
1817
+ zero_entities_idxes.append(idx)
1818
+
1819
+ n_zero_entities_samples = len(zero_entities_idxes)
1820
+ n_has_entities_samples = len(data_train) - n_zero_entities_samples
1821
+
1822
+ random.seed(42)
1823
+ k = min(int(n_has_entities_samples * zero_entities_rate), len(zero_entities_idxes))
1824
+ sampled_zero_entities_idxes = random.sample(zero_entities_idxes, k)
1825
+
1826
+ new_data_train = []
1827
+ for idx, d in enumerate(data_train):
1828
+ if len(d['entities']) == 0:
1829
+ if idx in sampled_zero_entities_idxes:
1830
+ new_data_train.append(d)
1831
+ else:
1832
+ new_data_train.append(d)
1833
+ data_train = new_data_train
1834
+
1835
+ print('Train:', len(data_train))
1836
+
1837
+ # %% [code]
1838
+ if debug_only:
1839
+ data_train = data_train[:10]
1840
+ data_test = data_test[:10]
1841
+
1842
+ print('Train:', len(data_train))
1843
+ print('Test:', len(data_test))
1844
+
1845
+ # %% [code]
1846
+ tokenizer = AutoTokenizer.from_pretrained(backbone_model_name)
1847
+
1848
+ # %% [code]
1849
+ print('Experiment name:', state_dict_save_name)
1850
+
1851
+ # %% [code]
1852
+ # trainset = KLTNDataset(data_train, np.array(range(len(data_train))), label2id, tokenizer, **train_memory_params)
1853
+ # train_loader = DataLoader(trainset, collate_fn=collate_fn, **train_loader_params)
1854
+ # for b in train_loader:
1855
+ # break
1856
+
1857
+ # %% [code]
1858
+ if not test_only:
1859
+ full_idxes = np.array(range(len(data_train)))
1860
+ training_logs, best_models, last_models = [], [], []
1861
+ start_training_time = time.time()
1862
+ for seed in SEEDS:
1863
+ kf = KFold(n_splits=nfolds, shuffle=True, random_state=seed)
1864
+ for fold_idx, (tr_idx, va_idx) in enumerate(kf.split(full_idxes)):
1865
+ if only_fold_idx is not None and only_fold_idx >= 0 and only_fold_idx != fold_idx:
1866
+ continue
1867
+ set_seed(seed)
1868
+
1869
+ train_idxes, val_idxes = full_idxes[tr_idx], full_idxes[va_idx]
1870
+
1871
+ trainset = KLTNDataset(data_train, train_idxes, label2id, tokenizer, **train_memory_params)
1872
+ valset = KLTNDataset(data_train, val_idxes, label2id, tokenizer, **val_memory_params)
1873
+
1874
+ generator = torch.Generator()
1875
+ generator.manual_seed(seed)
1876
+ train_loader = DataLoader(trainset, generator=generator, collate_fn=collate_fn, **train_loader_params)
1877
+ val_loader = DataLoader(valset, generator=generator, collate_fn=collate_fn, **val_loader_params)
1878
+
1879
+ my_model = IEModel(
1880
+ num_labels=len(label2id),
1881
+ **model_params
1882
+ )
1883
+ total_params = sum(p.numel() for p in my_model.parameters())
1884
+ print(f"Total params: {total_params:,}")
1885
+
1886
+ # optimizer, scheduler = configure_optimizers(my_model, optim_params, scheduler_params)
1887
+ encoder_params = set(map(id, my_model.encoder.parameters()))
1888
+ other_params = [
1889
+ p for p in my_model.parameters()
1890
+ if id(p) not in encoder_params
1891
+ ]
1892
+ optimizer = optim.AdamW([
1893
+ {"params": my_model.encoder.parameters(), "lr": 2e-5},
1894
+ {"params": other_params}
1895
+ ], lr=5e-4)
1896
+ scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20, eta_min=1e-6)
1897
+
1898
+ loss_fn = CustomLoss(
1899
+ **loss_func_params
1900
+ )
1901
+ eval_fn = CustomEvalFn(**eval_func_params)
1902
+ trainer_params['save_name'] = f'{state_dict_save_name}_s{seed}_f{fold_idx}'
1903
+ trainer = Trainer(**trainer_params)
1904
+
1905
+ print(f'Start Training Fold {fold_idx}...')
1906
+ training_log, best_model, last_model = trainer.fit(
1907
+ my_model, optimizer, scheduler, loss_fn, epochs, train_loader, val_loader, eval_fn,
1908
+ start_epoch=1, start_training_time=start_training_time, id2label=id2label
1909
+ )
1910
+
1911
+ training_logs.append(training_log)
1912
+ best_models.append(best_model)
1913
+ last_models.append(last_model)
1914
+
1915
+ # %% [code]
1916
+ def load_all_state_dicts(folder):
1917
+ files = []
1918
+
1919
+ for file in os.listdir(folder):
1920
+ if file.endswith(".pt") or file.endswith(".pth"):
1921
+ m = re.search(r"f(\d+)", file) # tìm f<số>
1922
+ if m:
1923
+ fold = int(m.group(1))
1924
+ files.append((fold, file))
1925
+
1926
+ # sort theo fold
1927
+ files.sort(key=lambda x: x[0])
1928
+
1929
+ state_dicts = []
1930
+ for fold, file in files:
1931
+ path = os.path.join(folder, file)
1932
+ print(f"Loading fold {fold}: {file}")
1933
+ state_dict = torch.load(path, map_location="cpu")
1934
+ state_dicts.append(state_dict)
1935
+
1936
+ return state_dicts
1937
+
1938
+ if test_only:
1939
+ snapshot_download(repo_id=repo_name, local_dir="", repo_type="model", allow_patterns=[f"{state_dict_save_name}/**"])
1940
+ get_ipython().system('rm -rf .cache .gitattributes')
1941
+
1942
+ best_models = load_all_state_dicts(f"{state_dict_save_name}/r1s")
1943
+ last_models = load_all_state_dicts(f"{state_dict_save_name}/lasts")
1944
+
1945
+ # %% [code]
1946
+ def dict_to_df(data, row_names):
1947
+ """
1948
+ Input:
1949
+ {
1950
+ model_name: {
1951
+ (level1, ..., level(n-1)): {
1952
+ leveln: {
1953
+ metric: value
1954
+ }
1955
+ }
1956
+ }
1957
+ }
1958
+
1959
+ Output:
1960
+ - level1 -> level(n-1): columns thường
1961
+ - MultiIndex columns:
1962
+ (model_name, leveln, metric)
1963
+ """
1964
+
1965
+ rows = {}
1966
+
1967
+ for model_name, model_data in data.items():
1968
+
1969
+ for upper_levels, last_level_dict in model_data.items():
1970
+
1971
+ # đảm bảo tuple
1972
+ if not isinstance(upper_levels, tuple):
1973
+ upper_levels = (upper_levels,)
1974
+
1975
+ # ===== tạo key row =====
1976
+ row_key = upper_levels
1977
+
1978
+ if row_key not in rows:
1979
+
1980
+ row = {}
1981
+
1982
+ for i, lv in enumerate(upper_levels):
1983
+ row[row_names[i]] = lv
1984
+
1985
+ rows[row_key] = row
1986
+
1987
+ # ===== add metrics =====
1988
+ for last_level, metrics in last_level_dict.items():
1989
+
1990
+ for metric, value in metrics.items():
1991
+
1992
+ rows[row_key][
1993
+ (model_name, last_level, metric)
1994
+ ] = value
1995
+
1996
+ # ===== dataframe =====
1997
+ df = pd.DataFrame(rows.values())
1998
+
1999
+ # ===== split columns =====
2000
+ normal_cols = [
2001
+ c for c in df.columns
2002
+ if not isinstance(c, tuple)
2003
+ ]
2004
+
2005
+ metric_cols = [
2006
+ c for c in df.columns
2007
+ if isinstance(c, tuple)
2008
+ ]
2009
+
2010
+ df = df[normal_cols + metric_cols]
2011
+
2012
+ # ===== build multi columns =====
2013
+ df.columns = pd.MultiIndex.from_tuples([
2014
+ ("", "", c) if not isinstance(c, tuple) else c
2015
+ for c in df.columns
2016
+ ])
2017
+
2018
+ return df
2019
+
2020
+ def dict_to_records(data):
2021
+ """
2022
+ Input:
2023
+ {
2024
+ model_name: {
2025
+ (level1, level2, ..., leveln): {
2026
+ metric: value
2027
+ }
2028
+ }
2029
+ }
2030
+
2031
+ Output:
2032
+ [
2033
+ {
2034
+ "model": model_name,
2035
+ "levels": [...],
2036
+ "metrics": {...}
2037
+ },
2038
+ ...
2039
+ ]
2040
+ """
2041
+
2042
+ records = []
2043
+
2044
+ for model_name, model_data in data.items():
2045
+
2046
+ for levels, metrics in model_data.items():
2047
+
2048
+ # ensure tuple/list compatible
2049
+ if not isinstance(levels, (tuple, list)):
2050
+ levels = [levels]
2051
+
2052
+ records.append(
2053
+ {
2054
+ "model": model_name,
2055
+ "levels": list(levels),
2056
+ "metrics": metrics
2057
+ }
2058
+ )
2059
+
2060
+ return records
2061
+
2062
+ # %% [code]
2063
+ os.makedirs(f'{checkpoints_dir}/results', exist_ok=True)
2064
+ testset = KLTNDataset(data_test, range(len(data_test)), label2id, tokenizer, **val_memory_params)
2065
+ generator = torch.Generator()
2066
+ test_loader = DataLoader(testset, generator=generator, collate_fn=collate_fn, **val_loader_params)
2067
+ eval_fn = CustomEvalFn(**eval_func_params)
2068
+ analyzer = SpanErrorAnalyzer()
2069
+ my_model = IEModel(
2070
+ num_labels=len(label2id),
2071
+ **model_params
2072
+ )
2073
+ total_params = sum(p.numel() for p in my_model.parameters())
2074
+ print(f"Total params: {total_params:,}")
2075
+
2076
+ # %% [code]
2077
+ start_time = time.time()
2078
+
2079
+ best_score, best_analyze_result, best_pred_test = test(my_model, best_models, test_loader, eval_fn, analyzer, device, id2label, tokenizer)
2080
+ last_score, last_analyze_result, last_pred_test = test(my_model, last_models, test_loader, eval_fn, analyzer, device, id2label, tokenizer)
2081
+
2082
+ result_test = {"Best model": best_score, "Last model": last_score}
2083
+ analyze_result = {"Best model": best_analyze_result, "Last model": last_analyze_result}
2084
+ analyze_result_sumary = {"Best model": best_analyze_result['summary'], "Last model": last_analyze_result['summary']}
2085
+ pred_test = {"Best model": best_pred_test, "Last model": last_pred_test}
2086
+
2087
+ with open(f"{checkpoints_dir}/results/{state_dict_save_name}_test_{my_model.keep_neighbor}.json", "w", encoding="utf-8") as f:
2088
+ json.dump(dict_to_records(result_test), f, ensure_ascii=False, indent=2)
2089
+
2090
+ with open(f"{checkpoints_dir}/results/{state_dict_save_name}_error_analyze_result_{my_model.keep_neighbor}.json", "w", encoding="utf-8") as f:
2091
+ json.dump(analyze_result, f, ensure_ascii=False, indent=2)
2092
+
2093
+ with open(f"{checkpoints_dir}/results/{state_dict_save_name}_pred_test_{my_model.keep_neighbor}.json", "w", encoding="utf-8") as f:
2094
+ json.dump(pred_test, f, ensure_ascii=False, indent=2)
2095
+
2096
+ print('Test:', time.time() - start_time, 's --> Done!')
2097
+ print(json.dumps(analyze_result_sumary, ensure_ascii=False, indent=4))
2098
+
2099
+ # %% [code]
2100
+ result_test_df = dict_to_df(result_test, row_names=['keep_neightbor', 'alpha'])
2101
+
2102
+ col = ("Best model", "Ent-C", "f1")
2103
+ sorted_df = result_test_df.sort_values(
2104
+ by=col,
2105
+ ascending=False
2106
+ ).reset_index(drop=True)
2107
+ sorted_df.to_excel(f"{checkpoints_dir}/results/{state_dict_save_name}_test_df_.xlsx")
2108
+
2109
+ sorted_df
2110
+
2111
+ # %% [code]
2112
+ def get_avg_best_score(logs):
2113
+ return float(np.mean([list(log.values())[-1]['best_score'] for log in logs]))
2114
+
2115
+ def get_avg_log(logs, epochs):
2116
+ avg_log = {}
2117
+
2118
+ for epoch in range(1, epochs + 1):
2119
+ val_score = 0.0
2120
+ train_loss = 0.0
2121
+ n_eval = 0
2122
+
2123
+ for idx in range(len(logs)):
2124
+ log = logs[idx].get(epoch, logs[idx].get(str(epoch)))
2125
+ if log is None:
2126
+ continue
2127
+
2128
+ val_score += log.get('val_score', 0.0)
2129
+ train_loss += log.get('train_loss', 0.0)
2130
+ n_eval += 1
2131
+
2132
+ if n_eval == 0:
2133
+ continue
2134
+
2135
+ avg_log[epoch] = {
2136
+ 'train_loss': train_loss / n_eval,
2137
+ 'val_score': val_score / n_eval if val_score != 0 else float('inf')
2138
+ }
2139
+
2140
+ return avg_log
2141
+
2142
+ def parse_label_key(label: str):
2143
+ try:
2144
+ first = float(label.split('_', 1)[0]) # số đầu: trước dấu _
2145
+ last = float(re.findall(r'_(\d+(?:\.\d+)?)$', label)[0])
2146
+ return first, last
2147
+ except:
2148
+ return (0, 0)
2149
+
2150
+ def plot_training_logs(logs_dict, save_path=None, figsize=(24, 10)):
2151
+ fig, axes = plt.subplots(1, 2, figsize=figsize)
2152
+
2153
+ # ===== Plot Train Loss =====
2154
+ for name, log in logs_dict.items():
2155
+ epochs = sorted(log.keys())
2156
+ train_loss = [log[e]['train_loss'] for e in epochs]
2157
+ axes[0].plot(epochs, train_loss, label=name)
2158
+
2159
+ axes[0].set_xlabel('Epoch')
2160
+ axes[0].set_ylabel('Train Loss')
2161
+ axes[0].set_title('Training Loss')
2162
+ axes[0].grid(True)
2163
+
2164
+ # ===== Plot Validation Score =====
2165
+ for name, log in logs_dict.items():
2166
+ epochs = sorted(log.keys())
2167
+ val_score = [log[e]['val_score'] for e in epochs]
2168
+ axes[1].plot(epochs, val_score, label=name)
2169
+
2170
+ axes[1].set_xlabel('Epoch')
2171
+ axes[1].set_ylabel('Validation Score')
2172
+ axes[1].set_title('Validation Score')
2173
+ axes[1].grid(True)
2174
+
2175
+ # ===== Shared Legend =====
2176
+ handles, labels = axes[0].get_legend_handles_labels()
2177
+ pairs = list(zip(handles, labels))
2178
+ pairs_sorted = sorted(
2179
+ pairs,
2180
+ key=lambda x: parse_label_key(x[1])
2181
+ )
2182
+ handles_sorted, labels_sorted = zip(*pairs_sorted)
2183
+
2184
+ axes[0].legend(
2185
+ handles_sorted,
2186
+ labels_sorted,
2187
+ loc='center left',
2188
+ bbox_to_anchor=(1.01, 0.5),
2189
+ borderaxespad=0.
2190
+ )
2191
+
2192
+ plt.tight_layout(rect=[0, 0, 1, 1])
2193
+
2194
+ if save_path is not None:
2195
+ os.makedirs(os.path.dirname(save_path), exist_ok=True) if os.path.dirname(save_path) else None
2196
+ plt.savefig(save_path, dpi=300, bbox_inches='tight')
2197
+
2198
+ plt.show()
2199
+
2200
+ # %% [code]
2201
+ if not test_only:
2202
+ snapshot_download(repo_id=repo_name, local_dir="", repo_type="model", allow_patterns=["**/*ent*.json"], ignore_patterns=["**/*crf*.json"])
2203
+ get_ipython().system('rm -rf .cache .gitattributes')
2204
+
2205
+ # %% [code]
2206
+ if not test_only:
2207
+ experiments = {}
2208
+ for experiment in os.listdir(pretrained_dir):
2209
+ if '.virtual_documents' in experiment:
2210
+ continue
2211
+ experiment_logs = []
2212
+ try:
2213
+ for seed in SEEDS:
2214
+ for fold_idx in range(nfolds):
2215
+ with open(f"{pretrained_dir}/{experiment}/logs/{experiment}_s{seed}_f{fold_idx}_logging.json", "r", encoding="utf-8") as f:
2216
+ experiment_log = json.load(f)
2217
+ experiment_logs.append(experiment_log)
2218
+ except:
2219
+ pass
2220
+ experiments[experiment] = get_avg_log(experiment_logs, 1000)
2221
+ experiments[state_dict_save_name] = get_avg_log(training_logs, 1000)
2222
+
2223
+ # %% [code]
2224
+ if not test_only:
2225
+ score = get_avg_best_score(training_logs)
2226
+ state_dict_save_name, score
2227
+
2228
+ # %% [code]
2229
+ if not test_only:
2230
+ plot_training_logs(experiments, save_path=f'{checkpoints_dir}/logs/{state_dict_save_name}_log_plot.jpg', figsize=(18, 7.5))
2231
+
20_weight_5_21/lasts/20_weight_5_21_s26092004_f0_last_ema.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:88851e70742e3d665b6e509250202e2254da24e695e8f4e0f99a0f7087ca403a
3
+ size 554297838
20_weight_5_21/logs/20_weight_5_21_log_plot.jpg ADDED

Git LFS Details

  • SHA256: e5ed308fcbea3ef9b9e62a3b9984ea19037030aeb974c1b1d95ccdee01818fb8
  • Pointer size: 131 Bytes
  • Size of remote file: 558 kB
20_weight_5_21/logs/20_weight_5_21_s26092004_f0_logging.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"1": {"lr": [2e-05, 0.0005], "train_loss": 0.16994395852088928, "total": 0.16994396570076684, "span_loss": 0.030130048381875787, "start_loss": 0.06546142049312859, "end_loss": 0.07435230065351628}, "2": {"lr": [1.988303923565381e-05, 0.0004969282409784868], "train_loss": 0.12120853364467621, "total": 0.12120853635170661, "span_loss": 0.019469167881001302, "start_loss": 0.04836207944764446, "end_loss": 0.05337718454001668}, "3": {"lr": [1.9535036904803962e-05, 0.0004877886008156408], "train_loss": 0.11089951545000076, "total": 0.11089951422975475, "span_loss": 0.017557751872540852, "start_loss": 0.04475033942916128, "end_loss": 0.04859154980032054}, "4": {"lr": [1.8964561979789496e-05, 0.00047280612778499774], "train_loss": 0.10087183117866516, "total": 0.10087182869652651, "span_loss": 0.01585112902756974, "start_loss": 0.04102819281759843, "end_loss": 0.04399250471905132}, "5": {"lr": [1.8185661446562005e-05, 0.00045234974009654937], "train_loss": 0.09143310785293579, "total": 0.09143311195096335, "span_loss": 0.0142580899131438, "start_loss": 0.03750183890157735, "end_loss": 0.039673120233185005}, "6": {"lr": [1.7217514421272206e-05, 0.00042692314190604356], "train_loss": 0.08365950733423233, "total": 0.08365950523107794, "span_loss": 0.01312214066157333, "start_loss": 0.03447093718407739, "end_loss": 0.036066399665435914}, "7": {"lr": [1.60839598967785e-05, 0.00039715242044697206], "train_loss": 0.07358024269342422, "total": 0.0735802434555574, "span_loss": 0.01142503750131009, "start_loss": 0.03050842199863969, "end_loss": 0.03164683513097619}, "8": {"lr": [1.4812909747525698e-05, 0.00036377062968501693], "train_loss": 0.06751851737499237, "total": 0.06751851678361034, "span_loss": 0.010514583982123283, "start_loss": 0.02803976912948924, "end_loss": 0.028964243633511213, "val_score": 1.6855599227339935, "best_score": 1.6855599227339935, "new_best_model": true, "precision": 0.6494018987928765, "recall": 0.7320837073479181, "f1": 0.686889411937129, "span_recall": 0.9986705107968652}, "9": {"lr": [1.3435661446562005e-05, 0.0003275997400965494], "train_loss": 0.05839870870113373, "total": 0.05839870787653062, "span_loss": 0.009097942098550386, "start_loss": 0.024364919220021878, "end_loss": 0.024935884939484787, "val_score": 1.681836349033959, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6446903707723148, "recall": 0.7297324654184155, "f1": 0.6832218683296147, "span_recall": 0.9986144807043441}, "10": {"lr": [1.1986127417882198e-05, 0.00028953039902753766], "train_loss": 0.0521458275616169, "total": 0.05214582840912565, "span_loss": 0.008145578617887034, "start_loss": 0.02177901078763123, "end_loss": 0.022221242735144676, "val_score": 1.6818857032393872, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.646446701412737, "recall": 0.7276510214964472, "f1": 0.6832686698780648, "span_recall": 0.9986170333613227}, "11": {"lr": [1.0500000000000003e-05, 0.0002505], "train_loss": 0.04576708376407623, "total": 0.04576708312271826, "span_loss": 0.007178331634197374, "start_loss": 0.019163503801442845, "end_loss": 0.019425210371705127, "val_score": 1.6783491349692201, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6439603503287585, "recall": 0.7234559667360737, "f1": 0.680071349405465, "span_recall": 0.9982777855637556}, "12": {"lr": [9.013872582117811e-06, 0.00021146960097246246], "train_loss": 0.04068997874855995, "total": 0.04068997907132127, "span_loss": 0.0064686668059091585, "start_loss": 0.01701318731249345, "end_loss": 0.01720815373906348, "val_score": 1.6755674992797602, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6426225417090546, "recall": 0.7196518985625462, "f1": 0.6776225146528508, "span_recall": 0.9979449846269095}, "13": {"lr": [7.564338553438001e-06, 0.00017340025990345064], "train_loss": 0.036281902343034744, "total": 0.03628190340710592, "span_loss": 0.0057504380995365015, "start_loss": 0.015184639151899958, "end_loss": 0.015346837350281342, "val_score": 1.6740218929742807, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6427414825198506, "recall": 0.7159367609273003, "f1": 0.6760734610224972, "span_recall": 0.9979484319517836}, "14": {"lr": [6.1870902524743065e-06, 0.00013722937031498307], "train_loss": 0.032750070095062256, "total": 0.032750069462033606, "span_loss": 0.00524781975018505, "start_loss": 0.013743052474608828, "end_loss": 0.013759246280301276, "val_score": 1.6757514247607257, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6469344256540179, "recall": 0.714557476784171, "f1": 0.6777460081490175, "span_recall": 0.998005416611707}, "15": {"lr": [4.916040103221507e-06, 0.00010384757955302797], "train_loss": 0.029431307688355446, "total": 0.029431308428624143, "span_loss": 0.0047199133056845324, "start_loss": 0.012393829874515266, "end_loss": 0.01231763827993991, "val_score": 1.6741625728102016, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6461921315863427, "recall": 0.7122410952757223, "f1": 0.676240418670118, "span_recall": 0.9979221541400819}, "16": {"lr": [3.7824855787278e-06, 7.40768580939564e-05], "train_loss": 0.026982108131051064, "total": 0.02698210859645206, "span_loss": 0.004310576192089271, "start_loss": 0.011418497715668014, "end_loss": 0.01125303388907964, "val_score": 1.673512691253174, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6463151892317377, "recall": 0.710721742081854, "f1": 0.6756294152769629, "span_recall": 0.9978832759762094}, "17": {"lr": [2.814338553438001e-06, 4.865025990345063e-05], "train_loss": 0.025057148188352585, "total": 0.025057149239929778, "span_loss": 0.004040878268178446, "start_loss": 0.010565734829164338, "end_loss": 0.01045051988374594, "val_score": 1.6738688426193746, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6483287570181101, "recall": 0.7090788969403761, "f1": 0.6759871542269876, "span_recall": 0.9978816883923859}, "18": {"lr": [2.0354380202105066e-06, 2.8193872215002235e-05], "train_loss": 0.023574385792016983, "total": 0.023574385581701544, "span_loss": 0.0038398398308196902, "start_loss": 0.009994559077459584, "end_loss": 0.009740018124950872, "val_score": 1.673816159832896, "best_score": 1.6855599227339935, "new_best_model": false, "precision": 0.6487965133084223, "recall": 0.7082805836719371, "f1": 0.6758685442346553, "span_recall": 0.99794761559824}}
20_weight_5_21/r1s/20_weight_5_21_s26092004_f0_r1_vs1.68556_ema.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:271bd584f241a00d4f4fcb5da0a7c303b5cbb486ca9c75916fb0a13e4745e261
3
+ size 554299766
20_weight_5_21/results/20_weight_5_21_error_analyze_result_2.json ADDED
The diff for this file is too large to render. See raw diff
 
20_weight_5_21/results/20_weight_5_21_pred_test_2.json ADDED
The diff for this file is too large to render. See raw diff
 
20_weight_5_21/results/20_weight_5_21_test_2.json ADDED
@@ -0,0 +1,1066 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "model": "Best model",
4
+ "levels": [
5
+ 0,
6
+ 0.0
7
+ ],
8
+ "metrics": {
9
+ "Ent-I": {
10
+ "precision": 0.6074528171920323,
11
+ "recall": 0.8196858444085698,
12
+ "f1": 0.6977884984925462
13
+ },
14
+ "Ent-C": {
15
+ "precision": 0.5363686458186139,
16
+ "recall": 0.7230940663007492,
17
+ "f1": 0.6158895826445122
18
+ }
19
+ }
20
+ },
21
+ {
22
+ "model": "Best model",
23
+ "levels": [
24
+ 1,
25
+ 0.0
26
+ ],
27
+ "metrics": {
28
+ "Ent-I": {
29
+ "precision": 0.6074109787171241,
30
+ "recall": 0.8196858444085698,
31
+ "f1": 0.69776089391536
32
+ },
33
+ "Ent-C": {
34
+ "precision": 0.5363317032849808,
35
+ "recall": 0.7230940663007492,
36
+ "f1": 0.6158652276308191
37
+ }
38
+ }
39
+ },
40
+ {
41
+ "model": "Best model",
42
+ "levels": [
43
+ 2,
44
+ 0.0
45
+ ],
46
+ "metrics": {
47
+ "Ent-I": {
48
+ "precision": 0.6072854978648896,
49
+ "recall": 0.8196858444085698,
50
+ "f1": 0.6976780932867215
51
+ },
52
+ "Ent-C": {
53
+ "precision": 0.5362209062108964,
54
+ "recall": 0.7230940663007492,
55
+ "f1": 0.6157921741456357
56
+ }
57
+ }
58
+ },
59
+ {
60
+ "model": "Best model",
61
+ "levels": [
62
+ 3,
63
+ 0.0
64
+ ],
65
+ "metrics": {
66
+ "Ent-I": {
67
+ "precision": 0.6071182706866259,
68
+ "recall": 0.8196858444085698,
69
+ "f1": 0.6975677230123439
70
+ },
71
+ "Ent-C": {
72
+ "precision": 0.5360732479687896,
73
+ "recall": 0.7230940663007492,
74
+ "f1": 0.6156947964539542
75
+ }
76
+ }
77
+ },
78
+ {
79
+ "model": "Best model",
80
+ "levels": [
81
+ 0,
82
+ 0.2
83
+ ],
84
+ "metrics": {
85
+ "Ent-I": {
86
+ "precision": 0.6242522344988216,
87
+ "recall": 0.8244260619008974,
88
+ "f1": 0.7105094471935813
89
+ },
90
+ "Ent-C": {
91
+ "precision": 0.553452037440669,
92
+ "recall": 0.7302442195183115,
93
+ "f1": 0.6296741083139648
94
+ }
95
+ }
96
+ },
97
+ {
98
+ "model": "Best model",
99
+ "levels": [
100
+ 1,
101
+ 0.2
102
+ ],
103
+ "metrics": {
104
+ "Ent-I": {
105
+ "precision": 0.6242522344988216,
106
+ "recall": 0.8244260619008974,
107
+ "f1": 0.7105094471935813
108
+ },
109
+ "Ent-C": {
110
+ "precision": 0.553452037440669,
111
+ "recall": 0.7302442195183115,
112
+ "f1": 0.6296741083139648
113
+ }
114
+ }
115
+ },
116
+ {
117
+ "model": "Best model",
118
+ "levels": [
119
+ 2,
120
+ 0.2
121
+ ],
122
+ "metrics": {
123
+ "Ent-I": {
124
+ "precision": 0.6242522344988216,
125
+ "recall": 0.8244260619008974,
126
+ "f1": 0.7105094471935813
127
+ },
128
+ "Ent-C": {
129
+ "precision": 0.553452037440669,
130
+ "recall": 0.7302442195183115,
131
+ "f1": 0.6296741083139648
132
+ }
133
+ }
134
+ },
135
+ {
136
+ "model": "Best model",
137
+ "levels": [
138
+ 3,
139
+ 0.2
140
+ ],
141
+ "metrics": {
142
+ "Ent-I": {
143
+ "precision": 0.6242083040108204,
144
+ "recall": 0.8244260619008974,
145
+ "f1": 0.7104809915305229
146
+ },
147
+ "Ent-C": {
148
+ "precision": 0.553413089373291,
149
+ "recall": 0.7302442195183115,
150
+ "f1": 0.6296489001746466
151
+ }
152
+ }
153
+ },
154
+ {
155
+ "model": "Best model",
156
+ "levels": [
157
+ 0,
158
+ 0.4
159
+ ],
160
+ "metrics": {
161
+ "Ent-I": {
162
+ "precision": 0.6263836987938896,
163
+ "recall": 0.8257272980752618,
164
+ "f1": 0.7123726997685013
165
+ },
166
+ "Ent-C": {
167
+ "precision": 0.5558062469149292,
168
+ "recall": 0.7320085430395282,
169
+ "f1": 0.6318531531565776
170
+ }
171
+ }
172
+ },
173
+ {
174
+ "model": "Best model",
175
+ "levels": [
176
+ 1,
177
+ 0.4
178
+ ],
179
+ "metrics": {
180
+ "Ent-I": {
181
+ "precision": 0.6263836987938896,
182
+ "recall": 0.8257272980752618,
183
+ "f1": 0.7123726997685013
184
+ },
185
+ "Ent-C": {
186
+ "precision": 0.5558062469149292,
187
+ "recall": 0.7320085430395282,
188
+ "f1": 0.6318531531565776
189
+ }
190
+ }
191
+ },
192
+ {
193
+ "model": "Best model",
194
+ "levels": [
195
+ 2,
196
+ 0.4
197
+ ],
198
+ "metrics": {
199
+ "Ent-I": {
200
+ "precision": 0.6263836987938896,
201
+ "recall": 0.8257272980752618,
202
+ "f1": 0.7123726997685013
203
+ },
204
+ "Ent-C": {
205
+ "precision": 0.5558062469149292,
206
+ "recall": 0.7320085430395282,
207
+ "f1": 0.6318531531565776
208
+ }
209
+ }
210
+ },
211
+ {
212
+ "model": "Best model",
213
+ "levels": [
214
+ 3,
215
+ 0.4
216
+ ],
217
+ "metrics": {
218
+ "Ent-I": {
219
+ "precision": 0.6263836987938896,
220
+ "recall": 0.8257272980752618,
221
+ "f1": 0.7123726997685013
222
+ },
223
+ "Ent-C": {
224
+ "precision": 0.5558062469149292,
225
+ "recall": 0.7320085430395282,
226
+ "f1": 0.6318531531565776
227
+ }
228
+ }
229
+ },
230
+ {
231
+ "model": "Best model",
232
+ "levels": [
233
+ 0,
234
+ 0.5
235
+ ],
236
+ "metrics": {
237
+ "Ent-I": {
238
+ "precision": 0.6247184684680287,
239
+ "recall": 0.8249837345470535,
240
+ "f1": 0.7110185395735589
241
+ },
242
+ "Ent-C": {
243
+ "precision": 0.5550394144140237,
244
+ "recall": 0.732287120437615,
245
+ "f1": 0.6314609391628255
246
+ }
247
+ }
248
+ },
249
+ {
250
+ "model": "Best model",
251
+ "levels": [
252
+ 1,
253
+ 0.5
254
+ ],
255
+ "metrics": {
256
+ "Ent-I": {
257
+ "precision": 0.6247184684680287,
258
+ "recall": 0.8249837345470535,
259
+ "f1": 0.7110185395735589
260
+ },
261
+ "Ent-C": {
262
+ "precision": 0.5550394144140237,
263
+ "recall": 0.732287120437615,
264
+ "f1": 0.6314609391628255
265
+ }
266
+ }
267
+ },
268
+ {
269
+ "model": "Best model",
270
+ "levels": [
271
+ 2,
272
+ 0.5
273
+ ],
274
+ "metrics": {
275
+ "Ent-I": {
276
+ "precision": 0.6247184684680287,
277
+ "recall": 0.8249837345470535,
278
+ "f1": 0.7110185395735589
279
+ },
280
+ "Ent-C": {
281
+ "precision": 0.5550394144140237,
282
+ "recall": 0.732287120437615,
283
+ "f1": 0.6314609391628255
284
+ }
285
+ }
286
+ },
287
+ {
288
+ "model": "Best model",
289
+ "levels": [
290
+ 3,
291
+ 0.5
292
+ ],
293
+ "metrics": {
294
+ "Ent-I": {
295
+ "precision": 0.6247184684680287,
296
+ "recall": 0.8249837345470535,
297
+ "f1": 0.7110185395735589
298
+ },
299
+ "Ent-C": {
300
+ "precision": 0.5550394144140237,
301
+ "recall": 0.732287120437615,
302
+ "f1": 0.6314609391628255
303
+ }
304
+ }
305
+ },
306
+ {
307
+ "model": "Best model",
308
+ "levels": [
309
+ 0,
310
+ 0.6
311
+ ],
312
+ "metrics": {
313
+ "Ent-I": {
314
+ "precision": 0.6203115177756371,
315
+ "recall": 0.8254484617521838,
316
+ "f1": 0.7083266819720063
317
+ },
318
+ "Ent-C": {
319
+ "precision": 0.5507438709222946,
320
+ "recall": 0.7321942613049195,
321
+ "f1": 0.6286374821440827
322
+ }
323
+ }
324
+ },
325
+ {
326
+ "model": "Best model",
327
+ "levels": [
328
+ 1,
329
+ 0.6
330
+ ],
331
+ "metrics": {
332
+ "Ent-I": {
333
+ "precision": 0.6203115177756371,
334
+ "recall": 0.8254484617521838,
335
+ "f1": 0.7083266819720063
336
+ },
337
+ "Ent-C": {
338
+ "precision": 0.5507438709222946,
339
+ "recall": 0.7321942613049195,
340
+ "f1": 0.6286374821440827
341
+ }
342
+ }
343
+ },
344
+ {
345
+ "model": "Best model",
346
+ "levels": [
347
+ 2,
348
+ 0.6
349
+ ],
350
+ "metrics": {
351
+ "Ent-I": {
352
+ "precision": 0.6203115177756371,
353
+ "recall": 0.8254484617521838,
354
+ "f1": 0.7083266819720063
355
+ },
356
+ "Ent-C": {
357
+ "precision": 0.5507438709222946,
358
+ "recall": 0.7321942613049195,
359
+ "f1": 0.6286374821440827
360
+ }
361
+ }
362
+ },
363
+ {
364
+ "model": "Best model",
365
+ "levels": [
366
+ 3,
367
+ 0.6
368
+ ],
369
+ "metrics": {
370
+ "Ent-I": {
371
+ "precision": 0.6203115177756371,
372
+ "recall": 0.8254484617521838,
373
+ "f1": 0.7083266819720063
374
+ },
375
+ "Ent-C": {
376
+ "precision": 0.5507438709222946,
377
+ "recall": 0.7321942613049195,
378
+ "f1": 0.6286374821440827
379
+ }
380
+ }
381
+ },
382
+ {
383
+ "model": "Best model",
384
+ "levels": [
385
+ 0,
386
+ 0.8
387
+ ],
388
+ "metrics": {
389
+ "Ent-I": {
390
+ "precision": 0.6144745035628488,
391
+ "recall": 0.8254484617521838,
392
+ "f1": 0.7045057860000036
393
+ },
394
+ "Ent-C": {
395
+ "precision": 0.5452155261879573,
396
+ "recall": 0.7317299656414413,
397
+ "f1": 0.624851315382126
398
+ }
399
+ }
400
+ },
401
+ {
402
+ "model": "Best model",
403
+ "levels": [
404
+ 1,
405
+ 0.8
406
+ ],
407
+ "metrics": {
408
+ "Ent-I": {
409
+ "precision": 0.6144745035628488,
410
+ "recall": 0.8254484617521838,
411
+ "f1": 0.7045057860000036
412
+ },
413
+ "Ent-C": {
414
+ "precision": 0.5452155261879573,
415
+ "recall": 0.7317299656414413,
416
+ "f1": 0.624851315382126
417
+ }
418
+ }
419
+ },
420
+ {
421
+ "model": "Best model",
422
+ "levels": [
423
+ 2,
424
+ 0.8
425
+ ],
426
+ "metrics": {
427
+ "Ent-I": {
428
+ "precision": 0.6144745035628488,
429
+ "recall": 0.8254484617521838,
430
+ "f1": 0.7045057860000036
431
+ },
432
+ "Ent-C": {
433
+ "precision": 0.5452155261879573,
434
+ "recall": 0.7317299656414413,
435
+ "f1": 0.624851315382126
436
+ }
437
+ }
438
+ },
439
+ {
440
+ "model": "Best model",
441
+ "levels": [
442
+ 3,
443
+ 0.8
444
+ ],
445
+ "metrics": {
446
+ "Ent-I": {
447
+ "precision": 0.6144745035628488,
448
+ "recall": 0.8254484617521838,
449
+ "f1": 0.7045057860000036
450
+ },
451
+ "Ent-C": {
452
+ "precision": 0.5452155261879573,
453
+ "recall": 0.7317299656414413,
454
+ "f1": 0.624851315382126
455
+ }
456
+ }
457
+ },
458
+ {
459
+ "model": "Best model",
460
+ "levels": [
461
+ 0,
462
+ 1.0
463
+ ],
464
+ "metrics": {
465
+ "Ent-I": {
466
+ "precision": 0.6039529987090919,
467
+ "recall": 0.82647086160347,
468
+ "f1": 0.6979043982285245
469
+ },
470
+ "Ent-C": {
471
+ "precision": 0.5363037424434312,
472
+ "recall": 0.7332157117645712,
473
+ "f1": 0.6194884620898355
474
+ }
475
+ }
476
+ },
477
+ {
478
+ "model": "Best model",
479
+ "levels": [
480
+ 1,
481
+ 1.0
482
+ ],
483
+ "metrics": {
484
+ "Ent-I": {
485
+ "precision": 0.6039529987090919,
486
+ "recall": 0.82647086160347,
487
+ "f1": 0.6979043982285245
488
+ },
489
+ "Ent-C": {
490
+ "precision": 0.5363037424434312,
491
+ "recall": 0.7332157117645712,
492
+ "f1": 0.6194884620898355
493
+ }
494
+ }
495
+ },
496
+ {
497
+ "model": "Best model",
498
+ "levels": [
499
+ 2,
500
+ 1.0
501
+ ],
502
+ "metrics": {
503
+ "Ent-I": {
504
+ "precision": 0.6039529987090919,
505
+ "recall": 0.82647086160347,
506
+ "f1": 0.6979043982285245
507
+ },
508
+ "Ent-C": {
509
+ "precision": 0.5363037424434312,
510
+ "recall": 0.7332157117645712,
511
+ "f1": 0.6194884620898355
512
+ }
513
+ }
514
+ },
515
+ {
516
+ "model": "Best model",
517
+ "levels": [
518
+ 3,
519
+ 1.0
520
+ ],
521
+ "metrics": {
522
+ "Ent-I": {
523
+ "precision": 0.6039529987090919,
524
+ "recall": 0.82647086160347,
525
+ "f1": 0.6979043982285245
526
+ },
527
+ "Ent-C": {
528
+ "precision": 0.5363037424434312,
529
+ "recall": 0.7332157117645712,
530
+ "f1": 0.6194884620898355
531
+ }
532
+ }
533
+ },
534
+ {
535
+ "model": "Last model",
536
+ "levels": [
537
+ 0,
538
+ 0.0
539
+ ],
540
+ "metrics": {
541
+ "Ent-I": {
542
+ "precision": 0.6673011341100267,
543
+ "recall": 0.7820429407930272,
544
+ "f1": 0.7201300874648195
545
+ },
546
+ "Ent-C": {
547
+ "precision": 0.5886271710678177,
548
+ "recall": 0.68920048286685,
549
+ "f1": 0.6349559365138765
550
+ }
551
+ }
552
+ },
553
+ {
554
+ "model": "Last model",
555
+ "levels": [
556
+ 1,
557
+ 0.0
558
+ ],
559
+ "metrics": {
560
+ "Ent-I": {
561
+ "precision": 0.667168794100795,
562
+ "recall": 0.7821358862340532,
563
+ "f1": 0.7200924133091284
564
+ },
565
+ "Ent-C": {
566
+ "precision": 0.5885197811776829,
567
+ "recall": 0.6892933419995456,
568
+ "f1": 0.6349328493629471
569
+ }
570
+ }
571
+ },
572
+ {
573
+ "model": "Last model",
574
+ "levels": [
575
+ 2,
576
+ 0.0
577
+ ],
578
+ "metrics": {
579
+ "Ent-I": {
580
+ "precision": 0.6672215616324476,
581
+ "recall": 0.7823217771161052,
582
+ "f1": 0.7202019288034768
583
+ },
584
+ "Ent-C": {
585
+ "precision": 0.588585017835443,
586
+ "recall": 0.6894790602649368,
587
+ "f1": 0.6350496015992106
588
+ }
589
+ }
590
+ },
591
+ {
592
+ "model": "Last model",
593
+ "levels": [
594
+ 3,
595
+ 0.0
596
+ ],
597
+ "metrics": {
598
+ "Ent-I": {
599
+ "precision": 0.6670893097704516,
600
+ "recall": 0.7824147225571313,
601
+ "f1": 0.7201642520247893
602
+ },
603
+ "Ent-C": {
604
+ "precision": 0.5883984467861253,
605
+ "recall": 0.6894790602649368,
606
+ "f1": 0.6349409904129887
607
+ }
608
+ }
609
+ },
610
+ {
611
+ "model": "Last model",
612
+ "levels": [
613
+ 0,
614
+ 0.2
615
+ ],
616
+ "metrics": {
617
+ "Ent-I": {
618
+ "precision": 0.6612503868766162,
619
+ "recall": 0.7943117390084633,
620
+ "f1": 0.7216991041067492
621
+ },
622
+ "Ent-C": {
623
+ "precision": 0.5841844630140945,
624
+ "recall": 0.7010864518518887,
625
+ "f1": 0.6373190344195457
626
+ }
627
+ }
628
+ },
629
+ {
630
+ "model": "Last model",
631
+ "levels": [
632
+ 1,
633
+ 0.2
634
+ ],
635
+ "metrics": {
636
+ "Ent-I": {
637
+ "precision": 0.6611992263050976,
638
+ "recall": 0.7943117390084633,
639
+ "f1": 0.7216686320957615
640
+ },
641
+ "Ent-C": {
642
+ "precision": 0.5841392649898768,
643
+ "recall": 0.7010864518518887,
644
+ "f1": 0.6372921365112755
645
+ }
646
+ }
647
+ },
648
+ {
649
+ "model": "Last model",
650
+ "levels": [
651
+ 2,
652
+ 0.2
653
+ ],
654
+ "metrics": {
655
+ "Ent-I": {
656
+ "precision": 0.6612254371030007,
657
+ "recall": 0.7944046844494893,
658
+ "f1": 0.7217226042876035
659
+ },
660
+ "Ent-C": {
661
+ "precision": 0.5841714374125141,
662
+ "recall": 0.7011793109845842,
663
+ "f1": 0.6373496468661756
664
+ }
665
+ }
666
+ },
667
+ {
668
+ "model": "Last model",
669
+ "levels": [
670
+ 3,
671
+ 0.2
672
+ ],
673
+ "metrics": {
674
+ "Ent-I": {
675
+ "precision": 0.6612254371030007,
676
+ "recall": 0.7944046844494893,
677
+ "f1": 0.7217226042876035
678
+ },
679
+ "Ent-C": {
680
+ "precision": 0.5841714374125141,
681
+ "recall": 0.7011793109845842,
682
+ "f1": 0.6373496468661756
683
+ }
684
+ }
685
+ },
686
+ {
687
+ "model": "Last model",
688
+ "levels": [
689
+ 0,
690
+ 0.4
691
+ ],
692
+ "metrics": {
693
+ "Ent-I": {
694
+ "precision": 0.6498451310715042,
695
+ "recall": 0.799516683705921,
696
+ "f1": 0.7169528205236348
697
+ },
698
+ "Ent-C": {
699
+ "precision": 0.5741482208955396,
700
+ "recall": 0.7057294084866693,
701
+ "f1": 0.6331750346257595
702
+ }
703
+ }
704
+ },
705
+ {
706
+ "model": "Last model",
707
+ "levels": [
708
+ 1,
709
+ 0.4
710
+ ],
711
+ "metrics": {
712
+ "Ent-I": {
713
+ "precision": 0.6498451310715042,
714
+ "recall": 0.799516683705921,
715
+ "f1": 0.7169528205236348
716
+ },
717
+ "Ent-C": {
718
+ "precision": 0.5741482208955396,
719
+ "recall": 0.7057294084866693,
720
+ "f1": 0.6331750346257595
721
+ }
722
+ }
723
+ },
724
+ {
725
+ "model": "Last model",
726
+ "levels": [
727
+ 2,
728
+ 0.4
729
+ ],
730
+ "metrics": {
731
+ "Ent-I": {
732
+ "precision": 0.6498451310715042,
733
+ "recall": 0.799516683705921,
734
+ "f1": 0.7169528205236348
735
+ },
736
+ "Ent-C": {
737
+ "precision": 0.5741482208955396,
738
+ "recall": 0.7057294084866693,
739
+ "f1": 0.6331750346257595
740
+ }
741
+ }
742
+ },
743
+ {
744
+ "model": "Last model",
745
+ "levels": [
746
+ 3,
747
+ 0.4
748
+ ],
749
+ "metrics": {
750
+ "Ent-I": {
751
+ "precision": 0.6498451310715042,
752
+ "recall": 0.799516683705921,
753
+ "f1": 0.7169528205236348
754
+ },
755
+ "Ent-C": {
756
+ "precision": 0.5741482208955396,
757
+ "recall": 0.7057294084866693,
758
+ "f1": 0.6331750346257595
759
+ }
760
+ }
761
+ },
762
+ {
763
+ "model": "Last model",
764
+ "levels": [
765
+ 0,
766
+ 0.5
767
+ ],
768
+ "metrics": {
769
+ "Ent-I": {
770
+ "precision": 0.6403743315503265,
771
+ "recall": 0.8013755925264416,
772
+ "f1": 0.711885393237052
773
+ },
774
+ "Ent-C": {
775
+ "precision": 0.5655080213899543,
776
+ "recall": 0.707029436344408,
777
+ "f1": 0.6283992852853965
778
+ }
779
+ }
780
+ },
781
+ {
782
+ "model": "Last model",
783
+ "levels": [
784
+ 1,
785
+ 0.5
786
+ ],
787
+ "metrics": {
788
+ "Ent-I": {
789
+ "precision": 0.6403743315503265,
790
+ "recall": 0.8013755925264416,
791
+ "f1": 0.711885393237052
792
+ },
793
+ "Ent-C": {
794
+ "precision": 0.5655080213899543,
795
+ "recall": 0.707029436344408,
796
+ "f1": 0.6283992852853965
797
+ }
798
+ }
799
+ },
800
+ {
801
+ "model": "Last model",
802
+ "levels": [
803
+ 2,
804
+ 0.5
805
+ ],
806
+ "metrics": {
807
+ "Ent-I": {
808
+ "precision": 0.6403743315503265,
809
+ "recall": 0.8013755925264416,
810
+ "f1": 0.711885393237052
811
+ },
812
+ "Ent-C": {
813
+ "precision": 0.5655080213899543,
814
+ "recall": 0.707029436344408,
815
+ "f1": 0.6283992852853965
816
+ }
817
+ }
818
+ },
819
+ {
820
+ "model": "Last model",
821
+ "levels": [
822
+ 3,
823
+ 0.5
824
+ ],
825
+ "metrics": {
826
+ "Ent-I": {
827
+ "precision": 0.6403743315503265,
828
+ "recall": 0.8013755925264416,
829
+ "f1": 0.711885393237052
830
+ },
831
+ "Ent-C": {
832
+ "precision": 0.5655080213899543,
833
+ "recall": 0.707029436344408,
834
+ "f1": 0.6283992852853965
835
+ }
836
+ }
837
+ },
838
+ {
839
+ "model": "Last model",
840
+ "levels": [
841
+ 0,
842
+ 0.6
843
+ ],
844
+ "metrics": {
845
+ "Ent-I": {
846
+ "precision": 0.6332525519566473,
847
+ "recall": 0.8014685379674676,
848
+ "f1": 0.7074991745889933
849
+ },
850
+ "Ent-C": {
851
+ "precision": 0.5595946243661896,
852
+ "recall": 0.7075865911405816,
853
+ "f1": 0.6249487361486331
854
+ }
855
+ }
856
+ },
857
+ {
858
+ "model": "Last model",
859
+ "levels": [
860
+ 1,
861
+ 0.6
862
+ ],
863
+ "metrics": {
864
+ "Ent-I": {
865
+ "precision": 0.6332525519566473,
866
+ "recall": 0.8014685379674676,
867
+ "f1": 0.7074991745889933
868
+ },
869
+ "Ent-C": {
870
+ "precision": 0.5595946243661896,
871
+ "recall": 0.7075865911405816,
872
+ "f1": 0.6249487361486331
873
+ }
874
+ }
875
+ },
876
+ {
877
+ "model": "Last model",
878
+ "levels": [
879
+ 2,
880
+ 0.6
881
+ ],
882
+ "metrics": {
883
+ "Ent-I": {
884
+ "precision": 0.6332525519566473,
885
+ "recall": 0.8014685379674676,
886
+ "f1": 0.7074991745889933
887
+ },
888
+ "Ent-C": {
889
+ "precision": 0.5595946243661896,
890
+ "recall": 0.7075865911405816,
891
+ "f1": 0.6249487361486331
892
+ }
893
+ }
894
+ },
895
+ {
896
+ "model": "Last model",
897
+ "levels": [
898
+ 3,
899
+ 0.6
900
+ ],
901
+ "metrics": {
902
+ "Ent-I": {
903
+ "precision": 0.6332525519566473,
904
+ "recall": 0.8014685379674676,
905
+ "f1": 0.7074991745889933
906
+ },
907
+ "Ent-C": {
908
+ "precision": 0.5595946243661896,
909
+ "recall": 0.7075865911405816,
910
+ "f1": 0.6249487361486331
911
+ }
912
+ }
913
+ },
914
+ {
915
+ "model": "Last model",
916
+ "levels": [
917
+ 0,
918
+ 0.8
919
+ ],
920
+ "metrics": {
921
+ "Ent-I": {
922
+ "precision": 0.6127454455230645,
923
+ "recall": 0.8034203922290143,
924
+ "f1": 0.6952465164445427
925
+ },
926
+ "Ent-C": {
927
+ "precision": 0.5410080102073148,
928
+ "recall": 0.708700900732929,
929
+ "f1": 0.6136034683167426
930
+ }
931
+ }
932
+ },
933
+ {
934
+ "model": "Last model",
935
+ "levels": [
936
+ 1,
937
+ 0.8
938
+ ],
939
+ "metrics": {
940
+ "Ent-I": {
941
+ "precision": 0.6127454455230645,
942
+ "recall": 0.8034203922290143,
943
+ "f1": 0.6952465164445427
944
+ },
945
+ "Ent-C": {
946
+ "precision": 0.5410080102073148,
947
+ "recall": 0.708700900732929,
948
+ "f1": 0.6136034683167426
949
+ }
950
+ }
951
+ },
952
+ {
953
+ "model": "Last model",
954
+ "levels": [
955
+ 2,
956
+ 0.8
957
+ ],
958
+ "metrics": {
959
+ "Ent-I": {
960
+ "precision": 0.6127454455230645,
961
+ "recall": 0.8034203922290143,
962
+ "f1": 0.6952465164445427
963
+ },
964
+ "Ent-C": {
965
+ "precision": 0.5410080102073148,
966
+ "recall": 0.708700900732929,
967
+ "f1": 0.6136034683167426
968
+ }
969
+ }
970
+ },
971
+ {
972
+ "model": "Last model",
973
+ "levels": [
974
+ 3,
975
+ 0.8
976
+ ],
977
+ "metrics": {
978
+ "Ent-I": {
979
+ "precision": 0.6127454455230645,
980
+ "recall": 0.8034203922290143,
981
+ "f1": 0.6952465164445427
982
+ },
983
+ "Ent-C": {
984
+ "precision": 0.5410080102073148,
985
+ "recall": 0.708700900732929,
986
+ "f1": 0.6136034683167426
987
+ }
988
+ }
989
+ },
990
+ {
991
+ "model": "Last model",
992
+ "levels": [
993
+ 0,
994
+ 1.0
995
+ ],
996
+ "metrics": {
997
+ "Ent-I": {
998
+ "precision": 0.5925951272922306,
999
+ "recall": 0.8048145738444048,
1000
+ "f1": 0.6825903541861594
1001
+ },
1002
+ "Ent-C": {
1003
+ "precision": 0.5226526142892672,
1004
+ "recall": 0.7091651963964071,
1005
+ "f1": 0.6017887347225229
1006
+ }
1007
+ }
1008
+ },
1009
+ {
1010
+ "model": "Last model",
1011
+ "levels": [
1012
+ 1,
1013
+ 1.0
1014
+ ],
1015
+ "metrics": {
1016
+ "Ent-I": {
1017
+ "precision": 0.5925951272922306,
1018
+ "recall": 0.8048145738444048,
1019
+ "f1": 0.6825903541861594
1020
+ },
1021
+ "Ent-C": {
1022
+ "precision": 0.5226526142892672,
1023
+ "recall": 0.7091651963964071,
1024
+ "f1": 0.6017887347225229
1025
+ }
1026
+ }
1027
+ },
1028
+ {
1029
+ "model": "Last model",
1030
+ "levels": [
1031
+ 2,
1032
+ 1.0
1033
+ ],
1034
+ "metrics": {
1035
+ "Ent-I": {
1036
+ "precision": 0.5925951272922306,
1037
+ "recall": 0.8048145738444048,
1038
+ "f1": 0.6825903541861594
1039
+ },
1040
+ "Ent-C": {
1041
+ "precision": 0.5226526142892672,
1042
+ "recall": 0.7091651963964071,
1043
+ "f1": 0.6017887347225229
1044
+ }
1045
+ }
1046
+ },
1047
+ {
1048
+ "model": "Last model",
1049
+ "levels": [
1050
+ 3,
1051
+ 1.0
1052
+ ],
1053
+ "metrics": {
1054
+ "Ent-I": {
1055
+ "precision": 0.5925951272922306,
1056
+ "recall": 0.8048145738444048,
1057
+ "f1": 0.6825903541861594
1058
+ },
1059
+ "Ent-C": {
1060
+ "precision": 0.5226526142892672,
1061
+ "recall": 0.7091651963964071,
1062
+ "f1": 0.6017887347225229
1063
+ }
1064
+ }
1065
+ }
1066
+ ]
20_weight_5_21/results/20_weight_5_21_test_df_.xlsx ADDED
Binary file (8.2 kB). View file