SS3M commited on
Commit
c0ae05d
·
verified ·
1 Parent(s): e245d7f

Upload 1_use_crf_actions_2's state dict

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

Git LFS Details

  • SHA256: 0678c3a9c5b68cc73a96eb6c5449ab65353b0faaf0c33f148f88095a755e18b6
  • Pointer size: 131 Bytes
  • Size of remote file: 426 kB
1_use_crf_actions_2/logs/1_use_crf_actions_2_s26092004_f0_logging.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"1": {"lr": [2e-05, 0.0005], "train_loss": 527.4216918945312, "total": 527.4217086156825, "trg_loss": 110.86127329380446, "arg_loss": 416.5605033881897}, "2": {"lr": [1.988303923565381e-05, 0.0004969282409784868], "train_loss": 275.3879699707031, "total": 275.3879779767667, "trg_loss": 62.998468507986445, "arg_loss": 212.38968719748306}, "3": {"lr": [1.9535036904803962e-05, 0.0004877886008156408], "train_loss": 194.59805297851562, "total": 194.59804574056147, "trg_loss": 48.82070577202323, "arg_loss": 145.77740803484994}, "4": {"lr": [1.8964561979789496e-05, 0.00047280612778499774], "train_loss": 151.9293975830078, "total": 151.92939254598258, "trg_loss": 40.136522113988384, "arg_loss": 111.79283639883833}, "5": {"lr": [1.8185661446562005e-05, 0.00045234974009654937], "train_loss": 125.91262817382812, "total": 125.9126255445305, "trg_loss": 34.42792912028074, "arg_loss": 91.48458676185867}, "6": {"lr": [1.7217514421272206e-05, 0.00042692314190604356], "train_loss": 109.31681060791016, "total": 109.31681086640852, "trg_loss": 30.00713750907551, "arg_loss": 79.30961096321394}, "7": {"lr": [1.60839598967785e-05, 0.00039715242044697206], "train_loss": 97.21910095214844, "total": 97.21909789448209, "trg_loss": 26.489858119554697, "arg_loss": 70.72928515246853}, "8": {"lr": [1.4812909747525698e-05, 0.00036377062968501693], "train_loss": 84.4925308227539, "total": 84.49252783155856, "trg_loss": 23.411455182115198, "arg_loss": 61.08100269240077, "val_score": 0.0026602519117402126, "best_score": 0.0026602519117402126, "new_best_model": true, "precision": 0.0027532228463130384, "recall": 0.00258732009591674, "f1": 0.0026602519117402126}, "9": {"lr": [1.3435661446562005e-05, 0.0003275997400965494], "train_loss": 76.3427734375, "total": 76.34277438286544, "trg_loss": 20.849826809051308, "arg_loss": 55.49306858059051, "val_score": 0.0026317834921011723, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.0027275793974111647, "recall": 0.0025567863663258543, "f1": 0.0026317834921011723}, "10": {"lr": [1.1986127417882198e-05, 0.00028953039902753766], "train_loss": 68.9592514038086, "total": 68.9592509680542, "trg_loss": 18.596103959946756, "arg_loss": 50.36311108422072, "val_score": 0.0026415939396468944, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.0027301699546704067, "recall": 0.0025711045624633045, "f1": 0.0026415939396468944}, "11": {"lr": [1.0500000000000003e-05, 0.0002505], "train_loss": 61.94004440307617, "total": 61.94004492376573, "trg_loss": 16.504184187439495, "arg_loss": 45.435798342207164, "val_score": 0.0026407516652165045, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.0027238285462542855, "recall": 0.0025739405027987807, "f1": 0.0026407516652165045}, "12": {"lr": [9.013872582117811e-06, 0.00021146960097246246], "train_loss": 55.12532043457031, "total": 55.125317642788, "trg_loss": 14.781628146176185, "arg_loss": 40.34369327807357, "val_score": 0.0026589911430898875, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.0027489717984193443, "recall": 0.00258448145028591, "f1": 0.0026589911430898875}, "13": {"lr": [7.564338553438001e-06, 0.00017340025990345064], "train_loss": 49.890846252441406, "total": 49.89084432478219, "trg_loss": 13.394519905614715, "arg_loss": 36.49633576355276, "val_score": 0.002649241705891035, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.0027659072575636034, "recall": 0.0025523064181135597, "f1": 0.002649241705891035}, "14": {"lr": [6.1870902524743065e-06, 0.00013722937031498307], "train_loss": 45.58797073364258, "total": 45.58796814496612, "trg_loss": 12.089481675036302, "arg_loss": 33.49848741529526, "val_score": 0.0025571891251009128, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.002685038698841698, "recall": 0.002450563748812271, "f1": 0.0025571891251009128}, "15": {"lr": [4.916040103221507e-06, 0.00010384757955302797], "train_loss": 40.99066162109375, "total": 40.99065978944821, "trg_loss": 11.161405077746855, "arg_loss": 29.82927078291384, "val_score": 0.0023336041387431164, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.0024742019457229245, "recall": 0.0022165861963067755, "f1": 0.0023336041387431164}, "16": {"lr": [3.7824855787278e-06, 7.40768580939564e-05], "train_loss": 38.14157485961914, "total": 38.14157414690223, "trg_loss": 10.282149987899322, "arg_loss": 27.859427940464666, "val_score": 0.0023597667567146364, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.002534259888980454, "recall": 0.0022165861963067755, "f1": 0.0023597667567146364}, "17": {"lr": [2.814338553438001e-06, 4.865025990345063e-05], "train_loss": 35.24580764770508, "total": 35.2458063589061, "trg_loss": 9.637188596623911, "arg_loss": 25.608643287149082, "val_score": 0.0025279380417228613, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.0027443405541551094, "recall": 0.0023515348765022303, "f1": 0.0025279380417228613}, "18": {"lr": [2.0354380202105066e-06, 2.8193872215002235e-05], "train_loss": 32.44328689575195, "total": 32.443285636495645, "trg_loss": 9.203154306328654, "arg_loss": 23.240117149685382, "val_score": 0.002553353910381408, "best_score": 0.0026602519117402126, "new_best_model": false, "precision": 0.0028091884935854345, "recall": 0.0023515348765022303, "f1": 0.002553353910381408}}
1_use_crf_actions_2/r1s/1_use_crf_actions_2_s26092004_f0_r1_vs0.00266_ema.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:facc51016c783de3c97f24e63f31e0bea0b3cb3555b69b119bf7ec0b23eadbcd
3
+ size 559093027
1_use_crf_actions_2/results/1_use_crf_actions_2_error_analyze_result.json ADDED
The diff for this file is too large to render. See raw diff
 
1_use_crf_actions_2/results/1_use_crf_actions_2_pred_test.json ADDED
The diff for this file is too large to render. See raw diff
 
1_use_crf_actions_2/results/1_use_crf_actions_2_test.json ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Best model": {
3
+ "Trg-I": {
4
+ "precision": 0.2727602905565705,
5
+ "recall": 0.6462994836470273,
6
+ "f1": 0.3836199515549089
7
+ },
8
+ "Trg-C": {
9
+ "precision": 0.0002421307506050337,
10
+ "recall": 0.0005735589331787394,
11
+ "f1": 0.0003405082967853744
12
+ },
13
+ "Arg-I": {
14
+ "precision": 0.20763898613632276,
15
+ "recall": 0.5096236466742484,
16
+ "f1": 0.29505994315246376
17
+ },
18
+ "Arg-C": {
19
+ "precision": 0.19844575909258988,
20
+ "recall": 0.4866094420596681,
21
+ "f1": 0.28192057673345733
22
+ },
23
+ "Soft-Event": {
24
+ "precision": 0.0002100325550459586,
25
+ "recall": 0.0005150214592270257,
26
+ "f1": 0.0002983771663069298
27
+ },
28
+ "Strict-Event": {
29
+ "precision": 0.0,
30
+ "recall": 0.0,
31
+ "f1": 0.0
32
+ }
33
+ },
34
+ "Last model": {
35
+ "Trg-I": {
36
+ "precision": 0.26995772195937584,
37
+ "recall": 0.6227768215702159,
38
+ "f1": 0.37664815677961394
39
+ },
40
+ "Trg-C": {
41
+ "precision": 0.0003730415319567607,
42
+ "recall": 0.0008603383997681092,
43
+ "f1": 0.0005204225304513276
44
+ },
45
+ "Arg-I": {
46
+ "precision": 0.21234567901226375,
47
+ "recall": 0.4729334937270382,
48
+ "f1": 0.2930933446764704
49
+ },
50
+ "Arg-C": {
51
+ "precision": 0.20289351851844023,
52
+ "recall": 0.451416309012488,
53
+ "f1": 0.2799574085505688
54
+ },
55
+ "Soft-Event": {
56
+ "precision": 0.00030864197530852287,
57
+ "recall": 0.0006866952789693675,
58
+ "f1": 0.0004258674275238831
59
+ },
60
+ "Strict-Event": {
61
+ "precision": 0.0,
62
+ "recall": 0.0,
63
+ "f1": 0.0
64
+ }
65
+ }
66
+ }
1_use_crf_actions_2/results/1_use_crf_actions_2_test_df.xlsx ADDED
Binary file (5.61 kB). View file
 
1_use_crf_actions_2/results/1_use_crf_actions_2_test_df_best.xlsx ADDED
Binary file (5.61 kB). View file