guard2PFE commited on
Commit
9545ee0
·
verified ·
1 Parent(s): 6de2f3c

Update test_new.py

Browse files
Files changed (1) hide show
  1. test_new.py +319 -319
test_new.py CHANGED
@@ -1,319 +1,319 @@
1
- import argparse
2
- import numpy as np
3
- import torch
4
- import torch.backends.cudnn as cudnn
5
- import os
6
- import warnings
7
- import json
8
- from pathlib import Path
9
-
10
- from timm.models import create_model
11
-
12
- import my_models # registra TALL_SWIN
13
- import utils
14
-
15
- from video_dataset import VideoDataSet
16
- from video_dataset_aug import get_augmentor, build_dataflow
17
- from video_dataset_config import get_dataset_config, DATASET_CONFIG
18
-
19
- from sklearn.metrics import (
20
- accuracy_score, balanced_accuracy_score,
21
- precision_recall_fscore_support,
22
- confusion_matrix, classification_report,
23
- roc_auc_score, roc_curve,
24
- average_precision_score, precision_recall_curve
25
- )
26
- import matplotlib.pyplot as plt
27
-
28
- warnings.filterwarnings("ignore", category=UserWarning)
29
-
30
-
31
- def get_args_parser():
32
- parser = argparse.ArgumentParser('DeiT evaluation script', add_help=False)
33
-
34
- parser.add_argument('--model', default='TALL_SWIN', type=str)
35
- parser.add_argument('--model_name', default="TALL_SWIN")
36
- parser.add_argument('--batch-size', default=2, type=int)
37
-
38
- # Dataset parameters
39
- parser.add_argument('--data_txt_dir', type=str, default='##path_for_dataset_txt##')
40
- parser.add_argument('--data_dir', type=str, default="##path_for_dataset##")
41
- parser.add_argument('--dataset', default='ffpp', choices=list(DATASET_CONFIG.keys()))
42
- parser.add_argument('--duration', default=1, type=int)
43
- parser.add_argument('--frames_per_group', default=1, type=int)
44
- parser.add_argument('--threed_data', default=False)
45
- parser.add_argument('--input_size', default=224, type=int)
46
- parser.add_argument('--disable_scaleup', action='store_true')
47
- parser.add_argument('--random_sampling', action='store_true')
48
- parser.add_argument('--dense_sampling', default=True)
49
- parser.add_argument('--augmentor_ver', default='v1', type=str, choices=['v1', 'v2'])
50
- parser.add_argument('--scale_range', default=[256, 320], type=int, nargs="+")
51
- parser.add_argument('--modality', default='rgb', type=str)
52
- parser.add_argument('--use_lmdb', default=False)
53
- parser.add_argument('--use_pyav', default=False)
54
-
55
- # temporal module / model params
56
- parser.add_argument('--pretrained', action='store_true', default=False)
57
- parser.add_argument('--temporal_module_name', default=None, type=str,
58
- choices=['ResNet3d', 'TAM', 'TTAM', 'TSM', 'TTSM', 'MSA'])
59
- parser.add_argument('--temporal_attention_only', action='store_true', default=False)
60
- parser.add_argument('--no_token_mask', action='store_true', default=False)
61
- parser.add_argument('--temporal_heads_scale', default=1.0, type=float)
62
- parser.add_argument('--temporal_mlp_scale', default=1.0, type=float)
63
- parser.add_argument('--rel_pos', action='store_true', default=False)
64
- parser.add_argument('--temporal_pooling', type=str, default=None,
65
- choices=['avg', 'max', 'conv', 'depthconv'])
66
- parser.add_argument('--bottleneck', default=None, choices=['regular', 'dw'])
67
-
68
- parser.add_argument('--window_size', default=7, type=int)
69
- parser.add_argument('--thumbnail_rows', default=3, type=int)
70
- parser.add_argument('--hpe_to_token', default=False, action='store_true')
71
-
72
- parser.add_argument('--drop', type=float, default=0.0)
73
- parser.add_argument('--drop-path', type=float, default=0.1)
74
- parser.add_argument('--drop-block', type=float, default=None)
75
-
76
- # runtime
77
- parser.add_argument('--output_dir', default="./output")
78
- parser.add_argument('--device', default='cuda')
79
- parser.add_argument('--seed', default=42, type=int)
80
- parser.add_argument('--num_workers', default=8, type=int)
81
-
82
- parser.add_argument('--num_crops', default=1, type=int, choices=[1, 3, 5, 10])
83
- parser.add_argument('--num_clips', default=3, type=int)
84
-
85
- parser.add_argument('--world_size', default=1, type=int)
86
- parser.add_argument("--local_rank", type=int)
87
- parser.add_argument('--dist_url', default='env://')
88
-
89
- # checkpoint
90
- parser.add_argument('--initial_checkpoint', type=str, default='',
91
- help='path do .pth/.pth.tar com checkpoint (espera key "model")')
92
-
93
- parser.add_argument('--threshold', type=float, default=0.5,
94
- help='threshold para decidir classe 1 (fake) a partir de prob[:,1]')
95
- parser.add_argument('--metrics_out', default='', type=str,
96
- help='pasta pra salvar metrics.json e plots (default: output_dir)')
97
- parser.add_argument('--save_plots', action='store_true',
98
- help='salvar cm.png / roc.png / pr.png')
99
-
100
- return parser
101
-
102
-
103
- @torch.no_grad()
104
- def eval_with_outputs(data_loader, model, device, threshold: float = 0.5):
105
- model.eval()
106
- y_true, y_score, y_pred = [], [], []
107
-
108
- thr = float(threshold)
109
-
110
- for samples, targets in data_loader:
111
- samples = samples.to(device, non_blocking=True)
112
- targets = targets.to(device, non_blocking=True)
113
-
114
- logits = model(samples) # [B,2] ou [B*K,2]
115
-
116
- # se logits veio por-clip, agrega por vídeo
117
- B = targets.shape[0]
118
- if logits.shape[0] != B:
119
- if logits.shape[0] % B != 0:
120
- raise RuntimeError(
121
- f"logits batch ({logits.shape[0]}) não é múltiplo do target batch ({B})."
122
- )
123
- K = logits.shape[0] // B
124
- logits = logits.view(B, K, -1).mean(dim=1) # [B,2]
125
-
126
- probs = torch.softmax(logits, dim=1) # [B,2]
127
- p1 = probs[:, 1] # score da classe 1 (fake)
128
-
129
- # >>> AQUI é o THRESHOLD <<<
130
- hat = (p1 >= thr).long()
131
-
132
- y_true.append(targets.detach().cpu().numpy())
133
- y_score.append(p1.detach().cpu().numpy())
134
- y_pred.append(hat.detach().cpu().numpy())
135
-
136
- y_true = np.concatenate(y_true).astype(int)
137
- y_score = np.concatenate(y_score).astype(float)
138
- y_pred = np.concatenate(y_pred).astype(int)
139
- return y_true, y_score, y_pred
140
-
141
-
142
- def plot_confusion(cm, out_path):
143
- plt.figure(figsize=(6, 5))
144
- plt.imshow(cm)
145
- plt.title("Confusion Matrix")
146
- plt.xlabel("Predicted")
147
- plt.ylabel("True")
148
- for (i, j), v in np.ndenumerate(cm):
149
- plt.text(j, i, str(v), ha="center", va="center")
150
- plt.tight_layout()
151
- plt.savefig(out_path, dpi=200)
152
- plt.close()
153
-
154
-
155
- def plot_roc(y, scores, out_path):
156
- fpr, tpr, _ = roc_curve(y, scores)
157
- auc = roc_auc_score(y, scores)
158
- plt.figure(figsize=(7, 6))
159
- plt.plot(fpr, tpr, label=f"AUC={auc:.4f}")
160
- plt.plot([0, 1], [0, 1], "--", label="Chance")
161
- plt.xlabel("FPR")
162
- plt.ylabel("TPR")
163
- plt.legend(loc="best")
164
- plt.tight_layout()
165
- plt.savefig(out_path, dpi=200)
166
- plt.close()
167
-
168
-
169
- def plot_pr(y, scores, out_path):
170
- p, r, _ = precision_recall_curve(y, scores)
171
- ap = average_precision_score(y, scores)
172
- plt.figure(figsize=(7, 6))
173
- plt.plot(r, p, label=f"AP={ap:.4f}")
174
- plt.xlabel("Recall")
175
- plt.ylabel("Precision")
176
- plt.legend(loc="best")
177
- plt.tight_layout()
178
- plt.savefig(out_path, dpi=200)
179
- plt.close()
180
-
181
-
182
- def main(args):
183
- utils.init_distributed_mode(args)
184
- print(args)
185
-
186
- device = torch.device(args.device)
187
-
188
- seed = args.seed + utils.get_rank()
189
- torch.manual_seed(seed)
190
- np.random.seed(seed)
191
- cudnn.benchmark = True
192
-
193
- num_classes, train_list_name, val_list_name, test_list_name, filename_seperator, image_tmpl, filter_video, label_file = \
194
- get_dataset_config(args.dataset, args.use_lmdb)
195
-
196
- args.num_classes = num_classes
197
- args.input_channels = 3 if args.modality == 'rgb' else 2 * 5
198
-
199
- print(f"Creating model: {args.model}")
200
- model = create_model(
201
- args.model,
202
- pretrained=args.pretrained,
203
- duration=args.duration,
204
- hpe_to_token=args.hpe_to_token,
205
- rel_pos=args.rel_pos,
206
- window_size=args.window_size,
207
- thumbnail_rows=args.thumbnail_rows,
208
- token_mask=not args.no_token_mask,
209
- online_learning=False,
210
- num_classes=args.num_classes,
211
- drop_rate=args.drop,
212
- drop_path_rate=args.drop_path,
213
- drop_block_rate=args.drop_block,
214
- use_checkpoint=False
215
- )
216
- model.to(device)
217
-
218
- # mean/std
219
- if args.distributed:
220
- mean = (0.5, 0.5, 0.5) if 'mean' not in model.module.default_cfg else model.module.default_cfg['mean']
221
- std = (0.5, 0.5, 0.5) if 'std' not in model.module.default_cfg else model.module.default_cfg['std']
222
- else:
223
- mean = (0.5, 0.5, 0.5) if 'mean' not in model.default_cfg else model.default_cfg['mean']
224
- std = (0.5, 0.5, 0.5) if 'std' not in model.default_cfg else model.default_cfg['std']
225
-
226
- # dataset (val list)
227
- video_data_cls = VideoDataSet
228
- val_list = os.path.join(args.data_txt_dir, val_list_name)
229
-
230
- val_augmentor = get_augmentor(
231
- False, args.input_size, mean, std, args.disable_scaleup,
232
- threed_data=args.threed_data, version=args.augmentor_ver,
233
- scale_range=args.scale_range, num_clips=args.num_clips,
234
- num_crops=args.num_crops, dataset=args.dataset
235
- )
236
-
237
- dataset_val = video_data_cls(
238
- args.data_dir, val_list,
239
- args.duration, args.frames_per_group,
240
- num_clips=args.num_clips,
241
- modality=args.modality,
242
- dense_sampling=args.dense_sampling,
243
- image_tmpl=image_tmpl,
244
- transform=val_augmentor,
245
- is_train=False, test_mode=False,
246
- seperator=filename_seperator, filter_video=filter_video
247
- )
248
-
249
- data_loader_val = build_dataflow(
250
- dataset_val, is_train=False, batch_size=args.batch_size,
251
- workers=args.num_workers, is_distributed=args.distributed
252
- )
253
-
254
- if not args.initial_checkpoint:
255
- raise RuntimeError("Passe --initial_checkpoint apontando pro checkpoint do modelo.")
256
-
257
- checkpoint = torch.load(args.initial_checkpoint, map_location='cpu')
258
- # muitos checkpoints vêm como {"model": state_dict, ...}
259
- if isinstance(checkpoint, dict) and "model" in checkpoint:
260
- utils.load_checkpoint(model, checkpoint["model"])
261
- else:
262
- # se for state_dict direto
263
- model.load_state_dict(checkpoint, strict=False)
264
-
265
- # eval
266
- y_true, y_score, y_pred = eval_with_outputs(
267
- data_loader_val, model, device, threshold=args.threshold
268
- )
269
-
270
- acc = accuracy_score(y_true, y_pred)
271
- bacc = balanced_accuracy_score(y_true, y_pred)
272
- prec, rec, f1, _ = precision_recall_fscore_support(
273
- y_true, y_pred, average="binary", zero_division=0
274
- )
275
- cm = confusion_matrix(y_true, y_pred)
276
-
277
- roc_auc = roc_auc_score(y_true, y_score)
278
- pr_auc = average_precision_score(y_true, y_score)
279
-
280
- print(f"\nN={len(y_true)} | thr={args.threshold:.3f}")
281
- print(f"acc={acc:.4f} | bacc={bacc:.4f} | prec={prec:.4f} | rec={rec:.4f} | f1={f1:.4f} | roc_auc={roc_auc:.4f} | pr_auc={pr_auc:.4f}")
282
- print(classification_report(y_true, y_pred, digits=4, zero_division=0))
283
-
284
- outdir = args.metrics_out.strip() if args.metrics_out else args.output_dir
285
- os.makedirs(outdir, exist_ok=True)
286
-
287
- out_json = {
288
- "threshold": float(args.threshold),
289
- "acc": float(acc),
290
- "balanced_acc": float(bacc),
291
- "precision": float(prec),
292
- "recall": float(rec),
293
- "f1": float(f1),
294
- "roc_auc": float(roc_auc),
295
- "pr_auc": float(pr_auc),
296
- "confusion_matrix": cm.tolist(),
297
- "n": int(len(y_true)),
298
- }
299
- with open(os.path.join(outdir, "metrics.json"), "w", encoding="utf-8") as f:
300
- json.dump(out_json, f, indent=2)
301
-
302
- np.savez(os.path.join(outdir, "eval_outputs.npz"),
303
- y_true=y_true, y_score=y_score, y_pred=y_pred)
304
-
305
- if args.save_plots:
306
- plot_confusion(cm, os.path.join(outdir, "cm.png"))
307
- plot_roc(y_true, y_score, os.path.join(outdir, "roc.png"))
308
- plot_pr(y_true, y_score, os.path.join(outdir, "pr.png"))
309
- print(f"\n✔ Plots + metrics saved in: {os.path.abspath(outdir)}")
310
- else:
311
- print(f"\n✔ Metrics saved in: {os.path.abspath(os.path.join(outdir, 'metrics.json'))}")
312
-
313
-
314
- if __name__ == '__main__':
315
- parser = argparse.ArgumentParser('DeiT evaluation script', parents=[get_args_parser()])
316
- args = parser.parse_args()
317
- if args.output_dir:
318
- Path(args.output_dir).mkdir(parents=True, exist_ok=True)
319
- main(args)
 
1
+ import argparse
2
+ import numpy as np
3
+ import torch
4
+ import torch.backends.cudnn as cudnn
5
+ import os
6
+ import warnings
7
+ import json
8
+ from pathlib import Path
9
+
10
+ from timm.models import create_model
11
+
12
+ import my_models # registers TALL_SWIN
13
+ import utils
14
+
15
+ from video_dataset import VideoDataSet
16
+ from video_dataset_aug import get_augmentor, build_dataflow
17
+ from video_dataset_config import get_dataset_config, DATASET_CONFIG
18
+
19
+ from sklearn.metrics import (
20
+ accuracy_score, balanced_accuracy_score,
21
+ precision_recall_fscore_support,
22
+ confusion_matrix, classification_report,
23
+ roc_auc_score, roc_curve,
24
+ average_precision_score, precision_recall_curve
25
+ )
26
+ import matplotlib.pyplot as plt
27
+
28
+ warnings.filterwarnings("ignore", category=UserWarning)
29
+
30
+
31
+ def get_args_parser():
32
+ parser = argparse.ArgumentParser('DeiT evaluation script', add_help=False)
33
+
34
+ parser.add_argument('--model', default='TALL_SWIN', type=str)
35
+ parser.add_argument('--model_name', default="TALL_SWIN")
36
+ parser.add_argument('--batch-size', default=2, type=int)
37
+
38
+ # Dataset parameters
39
+ parser.add_argument('--data_txt_dir', type=str, default='##path_for_dataset_txt##')
40
+ parser.add_argument('--data_dir', type=str, default="##path_for_dataset##")
41
+ parser.add_argument('--dataset', default='ffpp', choices=list(DATASET_CONFIG.keys()))
42
+ parser.add_argument('--duration', default=1, type=int)
43
+ parser.add_argument('--frames_per_group', default=1, type=int)
44
+ parser.add_argument('--threed_data', default=False)
45
+ parser.add_argument('--input_size', default=224, type=int)
46
+ parser.add_argument('--disable_scaleup', action='store_true')
47
+ parser.add_argument('--random_sampling', action='store_true')
48
+ parser.add_argument('--dense_sampling', default=True)
49
+ parser.add_argument('--augmentor_ver', default='v1', type=str, choices=['v1', 'v2'])
50
+ parser.add_argument('--scale_range', default=[256, 320], type=int, nargs="+")
51
+ parser.add_argument('--modality', default='rgb', type=str)
52
+ parser.add_argument('--use_lmdb', default=False)
53
+ parser.add_argument('--use_pyav', default=False)
54
+
55
+ # temporal module / model params
56
+ parser.add_argument('--pretrained', action='store_true', default=False)
57
+ parser.add_argument('--temporal_module_name', default=None, type=str,
58
+ choices=['ResNet3d', 'TAM', 'TTAM', 'TSM', 'TTSM', 'MSA'])
59
+ parser.add_argument('--temporal_attention_only', action='store_true', default=False)
60
+ parser.add_argument('--no_token_mask', action='store_true', default=False)
61
+ parser.add_argument('--temporal_heads_scale', default=1.0, type=float)
62
+ parser.add_argument('--temporal_mlp_scale', default=1.0, type=float)
63
+ parser.add_argument('--rel_pos', action='store_true', default=False)
64
+ parser.add_argument('--temporal_pooling', type=str, default=None,
65
+ choices=['avg', 'max', 'conv', 'depthconv'])
66
+ parser.add_argument('--bottleneck', default=None, choices=['regular', 'dw'])
67
+
68
+ parser.add_argument('--window_size', default=7, type=int)
69
+ parser.add_argument('--thumbnail_rows', default=3, type=int)
70
+ parser.add_argument('--hpe_to_token', default=False, action='store_true')
71
+
72
+ parser.add_argument('--drop', type=float, default=0.0)
73
+ parser.add_argument('--drop-path', type=float, default=0.1)
74
+ parser.add_argument('--drop-block', type=float, default=None)
75
+
76
+ # runtime
77
+ parser.add_argument('--output_dir', default="./output")
78
+ parser.add_argument('--device', default='cuda')
79
+ parser.add_argument('--seed', default=42, type=int)
80
+ parser.add_argument('--num_workers', default=8, type=int)
81
+
82
+ parser.add_argument('--num_crops', default=1, type=int, choices=[1, 3, 5, 10])
83
+ parser.add_argument('--num_clips', default=3, type=int)
84
+
85
+ parser.add_argument('--world_size', default=1, type=int)
86
+ parser.add_argument("--local_rank", type=int)
87
+ parser.add_argument('--dist_url', default='env://')
88
+
89
+ # checkpoint
90
+ parser.add_argument('--initial_checkpoint', type=str, default='',
91
+ help='path to .pth/.pth.tar checkpoint (expects key "model")')
92
+
93
+ parser.add_argument('--threshold', type=float, default=0.5,
94
+ help='threshold to decide class 1 (fake) from prob[:,1]')
95
+ parser.add_argument('--metrics_out', default='', type=str,
96
+ help='folder to save metrics.json and plots (default: output_dir)')
97
+ parser.add_argument('--save_plots', action='store_true',
98
+ help='save cm.png / roc.png / pr.png')
99
+
100
+ return parser
101
+
102
+
103
+ @torch.no_grad()
104
+ def eval_with_outputs(data_loader, model, device, threshold: float = 0.5):
105
+ model.eval()
106
+ y_true, y_score, y_pred = [], [], []
107
+
108
+ thr = float(threshold)
109
+
110
+ for samples, targets in data_loader:
111
+ samples = samples.to(device, non_blocking=True)
112
+ targets = targets.to(device, non_blocking=True)
113
+
114
+ logits = model(samples) # [B,2] or [B*K,2]
115
+
116
+ # if logits came per-clip, aggregate per video
117
+ B = targets.shape[0]
118
+ if logits.shape[0] != B:
119
+ if logits.shape[0] % B != 0:
120
+ raise RuntimeError(
121
+ f"logits batch ({logits.shape[0]}) is not a multiple of target batch ({B})."
122
+ )
123
+ K = logits.shape[0] // B
124
+ logits = logits.view(B, K, -1).mean(dim=1) # [B,2]
125
+
126
+ probs = torch.softmax(logits, dim=1) # [B,2]
127
+ p1 = probs[:, 1] # class 1 (fake) score
128
+
129
+ # >>> THIS is the THRESHOLD <<<
130
+ hat = (p1 >= thr).long()
131
+
132
+ y_true.append(targets.detach().cpu().numpy())
133
+ y_score.append(p1.detach().cpu().numpy())
134
+ y_pred.append(hat.detach().cpu().numpy())
135
+
136
+ y_true = np.concatenate(y_true).astype(int)
137
+ y_score = np.concatenate(y_score).astype(float)
138
+ y_pred = np.concatenate(y_pred).astype(int)
139
+ return y_true, y_score, y_pred
140
+
141
+
142
+ def plot_confusion(cm, out_path):
143
+ plt.figure(figsize=(6, 5))
144
+ plt.imshow(cm)
145
+ plt.title("Confusion Matrix")
146
+ plt.xlabel("Predicted")
147
+ plt.ylabel("True")
148
+ for (i, j), v in np.ndenumerate(cm):
149
+ plt.text(j, i, str(v), ha="center", va="center")
150
+ plt.tight_layout()
151
+ plt.savefig(out_path, dpi=200)
152
+ plt.close()
153
+
154
+
155
+ def plot_roc(y, scores, out_path):
156
+ fpr, tpr, _ = roc_curve(y, scores)
157
+ auc = roc_auc_score(y, scores)
158
+ plt.figure(figsize=(7, 6))
159
+ plt.plot(fpr, tpr, label=f"AUC={auc:.4f}")
160
+ plt.plot([0, 1], [0, 1], "--", label="Chance")
161
+ plt.xlabel("FPR")
162
+ plt.ylabel("TPR")
163
+ plt.legend(loc="best")
164
+ plt.tight_layout()
165
+ plt.savefig(out_path, dpi=200)
166
+ plt.close()
167
+
168
+
169
+ def plot_pr(y, scores, out_path):
170
+ p, r, _ = precision_recall_curve(y, scores)
171
+ ap = average_precision_score(y, scores)
172
+ plt.figure(figsize=(7, 6))
173
+ plt.plot(r, p, label=f"AP={ap:.4f}")
174
+ plt.xlabel("Recall")
175
+ plt.ylabel("Precision")
176
+ plt.legend(loc="best")
177
+ plt.tight_layout()
178
+ plt.savefig(out_path, dpi=200)
179
+ plt.close()
180
+
181
+
182
+ def main(args):
183
+ utils.init_distributed_mode(args)
184
+ print(args)
185
+
186
+ device = torch.device(args.device)
187
+
188
+ seed = args.seed + utils.get_rank()
189
+ torch.manual_seed(seed)
190
+ np.random.seed(seed)
191
+ cudnn.benchmark = True
192
+
193
+ num_classes, train_list_name, val_list_name, test_list_name, filename_seperator, image_tmpl, filter_video, label_file = \
194
+ get_dataset_config(args.dataset, args.use_lmdb)
195
+
196
+ args.num_classes = num_classes
197
+ args.input_channels = 3 if args.modality == 'rgb' else 2 * 5
198
+
199
+ print(f"Creating model: {args.model}")
200
+ model = create_model(
201
+ args.model,
202
+ pretrained=args.pretrained,
203
+ duration=args.duration,
204
+ hpe_to_token=args.hpe_to_token,
205
+ rel_pos=args.rel_pos,
206
+ window_size=args.window_size,
207
+ thumbnail_rows=args.thumbnail_rows,
208
+ token_mask=not args.no_token_mask,
209
+ online_learning=False,
210
+ num_classes=args.num_classes,
211
+ drop_rate=args.drop,
212
+ drop_path_rate=args.drop_path,
213
+ drop_block_rate=args.drop_block,
214
+ use_checkpoint=False
215
+ )
216
+ model.to(device)
217
+
218
+ # mean/std
219
+ if args.distributed:
220
+ mean = (0.5, 0.5, 0.5) if 'mean' not in model.module.default_cfg else model.module.default_cfg['mean']
221
+ std = (0.5, 0.5, 0.5) if 'std' not in model.module.default_cfg else model.module.default_cfg['std']
222
+ else:
223
+ mean = (0.5, 0.5, 0.5) if 'mean' not in model.default_cfg else model.default_cfg['mean']
224
+ std = (0.5, 0.5, 0.5) if 'std' not in model.default_cfg else model.default_cfg['std']
225
+
226
+ # dataset (validation list)
227
+ video_data_cls = VideoDataSet
228
+ val_list = os.path.join(args.data_txt_dir, val_list_name)
229
+
230
+ val_augmentor = get_augmentor(
231
+ False, args.input_size, mean, std, args.disable_scaleup,
232
+ threed_data=args.threed_data, version=args.augmentor_ver,
233
+ scale_range=args.scale_range, num_clips=args.num_clips,
234
+ num_crops=args.num_crops, dataset=args.dataset
235
+ )
236
+
237
+ dataset_val = video_data_cls(
238
+ args.data_dir, val_list,
239
+ args.duration, args.frames_per_group,
240
+ num_clips=args.num_clips,
241
+ modality=args.modality,
242
+ dense_sampling=args.dense_sampling,
243
+ image_tmpl=image_tmpl,
244
+ transform=val_augmentor,
245
+ is_train=False, test_mode=False,
246
+ seperator=filename_seperator, filter_video=filter_video
247
+ )
248
+
249
+ data_loader_val = build_dataflow(
250
+ dataset_val, is_train=False, batch_size=args.batch_size,
251
+ workers=args.num_workers, is_distributed=args.distributed
252
+ )
253
+
254
+ if not args.initial_checkpoint:
255
+ raise RuntimeError("Please pass --initial_checkpoint pointing to the model checkpoint.")
256
+
257
+ checkpoint = torch.load(args.initial_checkpoint, map_location='cpu')
258
+ # many checkpoints come as {"model": state_dict, ...}
259
+ if isinstance(checkpoint, dict) and "model" in checkpoint:
260
+ utils.load_checkpoint(model, checkpoint["model"])
261
+ else:
262
+ # if it is a direct state_dict
263
+ model.load_state_dict(checkpoint, strict=False)
264
+
265
+ # eval
266
+ y_true, y_score, y_pred = eval_with_outputs(
267
+ data_loader_val, model, device, threshold=args.threshold
268
+ )
269
+
270
+ acc = accuracy_score(y_true, y_pred)
271
+ bacc = balanced_accuracy_score(y_true, y_pred)
272
+ prec, rec, f1, _ = precision_recall_fscore_support(
273
+ y_true, y_pred, average="binary", zero_division=0
274
+ )
275
+ cm = confusion_matrix(y_true, y_pred)
276
+
277
+ roc_auc = roc_auc_score(y_true, y_score)
278
+ pr_auc = average_precision_score(y_true, y_score)
279
+
280
+ print(f"\nN={len(y_true)} | thr={args.threshold:.3f}")
281
+ print(f"acc={acc:.4f} | bacc={bacc:.4f} | prec={prec:.4f} | rec={rec:.4f} | f1={f1:.4f} | roc_auc={roc_auc:.4f} | pr_auc={pr_auc:.4f}")
282
+ print(classification_report(y_true, y_pred, digits=4, zero_division=0))
283
+
284
+ outdir = args.metrics_out.strip() if args.metrics_out else args.output_dir
285
+ os.makedirs(outdir, exist_ok=True)
286
+
287
+ out_json = {
288
+ "threshold": float(args.threshold),
289
+ "acc": float(acc),
290
+ "balanced_acc": float(bacc),
291
+ "precision": float(prec),
292
+ "recall": float(rec),
293
+ "f1": float(f1),
294
+ "roc_auc": float(roc_auc),
295
+ "pr_auc": float(pr_auc),
296
+ "confusion_matrix": cm.tolist(),
297
+ "n": int(len(y_true)),
298
+ }
299
+ with open(os.path.join(outdir, "metrics.json"), "w", encoding="utf-8") as f:
300
+ json.dump(out_json, f, indent=2)
301
+
302
+ np.savez(os.path.join(outdir, "eval_outputs.npz"),
303
+ y_true=y_true, y_score=y_score, y_pred=y_pred)
304
+
305
+ if args.save_plots:
306
+ plot_confusion(cm, os.path.join(outdir, "cm.png"))
307
+ plot_roc(y_true, y_score, os.path.join(outdir, "roc.png"))
308
+ plot_pr(y_true, y_score, os.path.join(outdir, "pr.png"))
309
+ print(f"\n✔ Plots + metrics saved in: {os.path.abspath(outdir)}")
310
+ else:
311
+ print(f"\n✔ Metrics saved in: {os.path.abspath(os.path.join(outdir, 'metrics.json'))}")
312
+
313
+
314
+ if __name__ == '__main__':
315
+ parser = argparse.ArgumentParser('DeiT evaluation script', parents=[get_args_parser()])
316
+ args = parser.parse_args()
317
+ if args.output_dir:
318
+ Path(args.output_dir).mkdir(parents=True, exist_ok=True)
319
+ main(args)