4
Alur Training dan Evaluasi
main.py menjalankan satu eksperimen penuh dari satu berkas: membuat direktori eksperimen, menyusun dataset dan dataloader, menjalankan loop train dengan early stopping, memuat ulang checkpoint terbaik, lalu mengevaluasi pada test set dan menulis dua berkas CSV. Blok if __name__ == "__main__" memuat seluruh orkestrasi itu main.py:136-1511, sementara evaluate main.py:41-712 dan train main.py:74-973 adalah dua fungsi yang dipanggilnya.
Urutan kerja satu eksperimen
sequenceDiagram
autonumber
participant SH as run_single_task.sh
participant MP as main.py
participant AH as utils.args_helper
participant LM as utils.functions.load_model
participant DL as Dataset dan DataLoader
participant TR as train
participant EV as evaluate
participant DS as model_dir
SH->>MP: python3 main.py --dataset ... --early_stop N --force
MP->>AH: get_parser() lalu append_dataset_args(args)
AH-->>MP: dataset_class, dataloader_class, forward_fn, metrics_fn, valid_criterion
MP->>DS: os.makedirs(model_dir, exist_ok=True)
alt folder ada dan --force tidak diberikan
MP-->>SH: raise Exception model directory already exists
end
MP->>LM: load_model(args)
LM-->>MP: model, tokenizer, vocab_path, config_path
MP->>DL: train_loader, valid_loader, test_loader
MP->>TR: train(... exp_id=0)
loop setiap epoch sampai n_epochs atau early stop
TR->>TR: forward_fn, zero_grad, clip_grad_norm_, optimizer.step
TR->>EV: evaluate(valid_loader, is_test=False)
EV-->>TR: val_loss, val_metrics
TR->>DS: torch.save best_model_0.th bila val_metric membaik
end
TR-->>MP: loop selesai
MP->>DS: salin vocab.txt dan config.json
MP->>EV: evaluate(test_loader, is_test=True)
EV-->>MP: test_loss, test_metrics, test_hyp, test_label, test_seq
MP->>DS: prediction_result.csv dan evaluation_result.csv
%% fig: Siklus satu eksperimen di main.py
%% desc: main.py memanggil args helper, memuat model dan dataloader, menjalankan train dengan validasi per epoch, lalu mengevaluasi test set dan menulis berkas keluaran.Diagram di atas memperlihatkan bahwa main.py memegang kendali: ia yang memanggil append_dataset_args, load_model, konstruktor Dataset dan DataLoader, lalu train, dan akhirnya evaluate untuk test set. train mengembalikan kendali hanya setelah n_epochs habis atau count_stop mencapai early_stop main.py:113-1344.
Menyiapkan direktori dan model
Nama direktori eksperimen dibentuk dengan format '{}/{}/{}' dari model_dir, dataset, dan experiment_name, sehingga nilai default --model_dir save/ dan --experiment_name exp pada dataset emotion-twitter menghasilkan save/emotion-twitter/exp main.py:136-1511. Bila folder belum ada, os.makedirs(model_dir, exist_ok=True) dijalankan. Bila folder sudah ada dan --force diberikan, skrip hanya mencetak overwriting model directory lalu lanjut tanpa menghapus isi folder. Bila folder sudah ada tanpa --force, skrip berhenti dengan pesan model directory ... already exists, use --force if you want to overwrite the folder main.py:136-1511.
⚠️
--forcetidak membersihkan folder lama. Berkas dari eksperimen sebelumnya, termasukbest_model_0.thdanprediction_result.csv, tetap berada di sana dan hanya tertimpa bila langkah yang sama dijalankan lagi main.py:136-1511.
Setelah direktori siap, skrip memanggil set_seed(args['seed']) yang mengunci random, numpy, torch.manual_seed, dan torch.cuda.manual_seed main.py:17-215, mengambil LABEL2INDEX serta INDEX2LABEL dari args['dataset_class'], lalu memuat model lewat load_model main.py:153-1686. load_model memilih kelas model berdasarkan substring model_checkpoint dan nilai args['task'], misalnya BertForSequenceClassification untuk sequence_classification utils/functions.py:286-3057. Optimizer selalu optim.Adam(model.parameters(), lr=args['lr']), dan amp.initialize hanya dipanggil ketika --fp16 diisi main.py:153-1686.
| Flag | Default | Pemakaian di main.py |
|---|---|---|
--experiment_name | exp | segmen terakhir model_dir |
--model_dir | save/ | akar direktori eksperimen |
--n_epochs | 10 | batas for epoch in range(n_epochs) |
--early_stop | 3 | jumlah epoch tanpa perbaikan sebelum break |
--step_size | 1 | argumen StepLR |
--gamma | 0.5 | faktor decay StepLR |
--force | False | menimpa folder eksperimen yang sudah ada |
Nilai default itu dideklarasikan di parser utils/args_helper.py:35-528. Rincian seluruh flag dan pemilihan checkpoint dibahas di Konfigurasi Argumen dan Pemilihan Model.
Penyusunan dataset dan dataloader
Tiga dataset dibangun dengan pola yang sama, yaitu args['dataset_class'](path, tokenizer, lowercase=args['lower'], no_special_token=args['no_special_token']), lalu dibungkus args['dataloader_class'] main.py:170-1829. train_loader memakai batch_size=args['train_batch_size'], sedangkan valid_loader dan test_loader memakai batch_size=args['valid_batch_size']; ketiganya memakai max_seq_len=args['max_seq_len'], num_workers=16, dan shuffle=False main.py:170-1829.
⚠️
shuffle=Falsejuga dipakai untuktrain_loader, sehingga urutan batch sama di setiap epoch main.py:170-1829.
Pemetaan dataset ke komponen task dilakukan append_dataset_args; untuk emotion-twitter ia mengisi task, num_labels, dataset_class, dataloader_class, forward_fn, metrics_fn, valid_criterion = 'F1', dan tiga path dataset utils/args_helper.py:83-10110. Berkas test yang dirujuk bernama test_preprocess_masked_label.csv, jadi label test disamarkan dan prediksi test dipakai langsung untuk submission utils/args_helper.py:83-10110. Bentuk kelas dataset dan dataloader dibahas di Data Utils dan Dataloader, sedangkan isi forward_fn dan metrics_fn dibahas di Forward Pass dan Metrik Evaluasi.
Loop train dan early stopping
train menerima loader, optimizer, forward_fn, metrics_fn, valid_criterion, i2w, n_epochs, evaluate_every, early_stop, step_size, gamma, model_dir, dan exp_id main.py:74-973. Di dalamnya StepLR dibangun dari step_size dan gamma, best_val_metric dimulai dari -100, dan count_stop dari 0 main.py:74-973. Setiap batch menjalankan forward_fn, optimizer.zero_grad(), lalu loss.backward() dengan torch.nn.utils.clip_grad_norm_ sebesar args['max_norm'] — atau jalur amp.scale_loss bila args['fp16'] aktif — dan ditutup optimizer.step() main.py:74-973.
⚠️
trainmembacaargs['device'],args['fp16'], danargs['max_norm']langsung dari variabel globalargs, bukan dari parameter fungsi, sehingga fungsi ini tidak bisa dipakai ulang tanpa mendefinisikanargsglobal main.py:74-973.
Setiap epoch ditutup metrics_fn(list_hyp, list_label), cetak loss dan LR, lalu scheduler.step() main.py:113-1344. Validasi hanya berjalan ketika (epoch+1) % evaluate_every == 0, dan main.py memanggil train dengan evaluate_every=1 sehingga validasi terjadi setiap epoch main.py:184-19411.
graph TD
A["train: for epoch in range(n_epochs)"] --> B["metrics_fn(list_hyp, list_label)"]
B --> C["scheduler.step()"]
C --> D["evaluate(valid_loader, is_test=False)"]
D --> E["val_metric = val_metrics[valid_criterion]"]
E --> F{"best_val_metric < val_metric"}
F -->|ya| G["best_val_metric = val_metric, count_stop = 0"]
G --> H["torch.save best_model_0.th"]
H --> A
F -->|tidak| I["count_stop += 1"]
I --> J{"count_stop == early_stop"}
J -->|belum| A
J -->|sudah| K["break"]
A -->|epoch habis| L["load_state_dict best_model_0.th"]
L --> M["evaluate(test_loader, is_test=True)"]
M --> N["prediction_result.csv dan evaluation_result.csv"]
style G fill:#2d7d46,color:#fff
style H fill:#2d7d46,color:#fff
style E fill:#f59e0b,color:#000
style F fill:#f59e0b,color:#000
style I fill:#dc2626,color:#fff
style K fill:#dc2626,color:#fff
style L fill:#8b5cf6,color:#fff
style N fill:#336791,color:#fff
%% fig: Keputusan early stopping dan penyimpanan checkpoint
%% desc: Setiap epoch menyimpan checkpoint bila val_metric membaik, menaikkan count_stop bila tidak, lalu berhenti saat count_stop mencapai early_stop.val_metric diambil dari val_metrics[valid_criterion], dan seluruh task mengisi valid_criterion dengan 'F1' utils/args_helper.py:83-10110. Bila val_metric lebih besar dari best_val_metric, checkpoint disimpan dan count_stop direset ke 0; bila tidak, count_stop naik satu dan loop break begitu count_stop == early_stop main.py:113-1344. Karena main.py mengirim exp_id=0, nama berkasnya best_model_0.th; hanya bila exp_id bernilai None berkasnya bernama best_model.th main.py:113-1344.
⚠️ Langkah sesudah training memuat
model_dir + "/best_model_0.th"secara hard-coded, jadi checkpoint hanya ditemukan selamatraindipanggil denganexp_id=0main.py:184-19411.
Nilai val_loss yang dikembalikan evaluate adalah jumlah loss seluruh batch, bukan rata-ratanya, karena total_loss hanya diakumulasi dan pembagian total_loss/(i+1) hanya dipakai untuk teks pbar main.py:41-712.
Fase evaluasi dan berkas keluaran
Setelah training, main.py menyalin vocab_path ke vocab.txt dan config_path ke config.json bila keduanya tidak None, memuat checkpoint terbaik dengan model.load_state_dict(torch.load(model_dir + "/best_model_0.th")), lalu mencetak =========== EVALUATION PHASE =========== main.py:184-19411. Evaluasi test memanggil evaluate(..., is_test=True) yang mengembalikan lima nilai: total_loss, metrics, list_hyp, list_label, dan list_seq main.py:41-712.
Hasil test diubah menjadi DataFrame dengan kolom seq, hyp, dan label, digabung lewat pd.concat(result_dfs), lalu disimpan ke model_dir + "/prediction_result.csv"; ringkasan metrik disimpan terpisah ke evaluation_result.csv lewat metric_df.describe().to_csv(...) main.py:196-21812.
Berkas di model_dir | Isi | Rujukan |
|---|---|---|
best_model_0.th | state_dict model pada val_metric terbaik | main.py:113-1344 |
vocab.txt | salinan vocab_path bila tidak None | main.py:184-19411 |
config.json | salinan config_path bila tidak None | main.py:184-19411 |
prediction_result.csv | kolom seq, hyp, label untuk test set | main.py:196-21812 |
evaluation_result.csv | keluaran metric_df.describe() | main.py:196-21812 |
⚠️
metrics_scoreshanya menerima satu entri dari evaluasi test, sehinggaevaluation_result.csvberisi keluarandescribe()atas satu baris. Nilaicount,mean,min, danmaxpada berkas itu akan sama main.py:196-21812.
Kedua CSV ditulis setelah evaluasi test selesai, sementara metrics_scores dan result_dfs sudah disiapkan sebelum training main.py:153-1686. Format berkas submission dan label yang disamarkan dibahas di Struktur Berkas Dataset dan Submission.
Menjalankan banyak task sekaligus
run_single_task.sh menerima empat argumen posisional: $1 untuk CUDA_VISIBLE_DEVICES, $2 untuk --dataset, $3 untuk --early_stop, dan $4 untuk --train_batch_size run_single_task.sh:1-913. Setiap barisnya menjalankan python3 main.py dengan --n_epochs 1 --step_size 1 --gamma 0.9 --force; keluarga scratch dan fasttext memakai --lr 1e-4, sedangkan bert-base-multilingual-uncased, xlm-roberta, xlm-mlm, dan indobenchmark/* memakai --lr 1e-5 run_single_task.sh:1-913. --experiment_name dibentuk dari nama checkpoint, batch size, step_size, gamma, learning rate, nilai early_stop, dan num_layers, misalnya scratch_b$4_step1_gamma0.9_lr1e-4_early$3_layer2_lowerTrue run_single_task.sh:1-913.
⚠️ Semua perintah di
run_single_task.shmemakai--force, sehingga folder dengan--experiment_nameyang sama ditimpa tanpa konfirmasi run_single_task.sh:1-913.
run_all_tasks.sh memanggil bash run_single_task.sh $1 <dataset> $2 $3 untuk 12 dataset: absa-airy, absa-prosa, doc-sentiment-prosa, emotion-twitter, entailment-ui, keyword-extraction-prosa, qa-factoid-itb, ner-grit, ner-prosa, pos-idn, term-extraction-airy, dan pos-prosa run_all_tasks.sh:1-1214. Artinya $1 script induk menjadi device, $2 menjadi early stopping, dan $3 menjadi batch size untuk setiap pemanggilan di run_single_task.sh run_all_tasks.sh:1-1214. Kombinasi dataset dengan jenis klasifikasinya dibahas di 12 Task Downstream dan Jenis Klasifikasi.