Lompat ke konten
dokumentasi.id
Commit ce728f6
v1
Star659

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-151, sementara evaluate main.py:41-71 dan train main.py:74-97 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.
Fig. 1: Siklus satu eksperimen di main.pymain.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-134.

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-151. 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-151.

⚠️ --force tidak membersihkan folder lama. Berkas dari eksperimen sebelumnya, termasuk best_model_0.th dan prediction_result.csv, tetap berada di sana dan hanya tertimpa bila langkah yang sama dijalankan lagi main.py:136-151.

Setelah direktori siap, skrip memanggil set_seed(args['seed']) yang mengunci random, numpy, torch.manual_seed, dan torch.cuda.manual_seed main.py:17-21, mengambil LABEL2INDEX serta INDEX2LABEL dari args['dataset_class'], lalu memuat model lewat load_model main.py:153-168. load_model memilih kelas model berdasarkan substring model_checkpoint dan nilai args['task'], misalnya BertForSequenceClassification untuk sequence_classification utils/functions.py:286-305. Optimizer selalu optim.Adam(model.parameters(), lr=args['lr']), dan amp.initialize hanya dipanggil ketika --fp16 diisi main.py:153-168.

FlagDefaultPemakaian di main.py
--experiment_nameexpsegmen terakhir model_dir
--model_dirsave/akar direktori eksperimen
--n_epochs10batas for epoch in range(n_epochs)
--early_stop3jumlah epoch tanpa perbaikan sebelum break
--step_size1argumen StepLR
--gamma0.5faktor decay StepLR
--forceFalsemenimpa folder eksperimen yang sudah ada

Nilai default itu dideklarasikan di parser utils/args_helper.py:35-52. 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-182. 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-182.

⚠️ shuffle=False juga dipakai untuk train_loader, sehingga urutan batch sama di setiap epoch main.py:170-182.

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-101. 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-101. 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-97. Di dalamnya StepLR dibangun dari step_size dan gamma, best_val_metric dimulai dari -100, dan count_stop dari 0 main.py:74-97. 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-97.

⚠️ train membaca args['device'], args['fp16'], dan args['max_norm'] langsung dari variabel global args, bukan dari parameter fungsi, sehingga fungsi ini tidak bisa dipakai ulang tanpa mendefinisikan args global main.py:74-97.

Setiap epoch ditutup metrics_fn(list_hyp, list_label), cetak loss dan LR, lalu scheduler.step() main.py:113-134. 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-194.

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.
Fig. 2: Keputusan early stopping dan penyimpanan checkpointSetiap 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-101. 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-134. 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-134.

⚠️ Langkah sesudah training memuat model_dir + "/best_model_0.th" secara hard-coded, jadi checkpoint hanya ditemukan selama train dipanggil dengan exp_id=0 main.py:184-194.

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-71.

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-194. Evaluasi test memanggil evaluate(..., is_test=True) yang mengembalikan lima nilai: total_loss, metrics, list_hyp, list_label, dan list_seq main.py:41-71.

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-218.

Berkas di model_dirIsiRujukan
best_model_0.thstate_dict model pada val_metric terbaikmain.py:113-134
vocab.txtsalinan vocab_path bila tidak Nonemain.py:184-194
config.jsonsalinan config_path bila tidak Nonemain.py:184-194
prediction_result.csvkolom seq, hyp, label untuk test setmain.py:196-218
evaluation_result.csvkeluaran metric_df.describe()main.py:196-218

⚠️ metrics_scores hanya menerima satu entri dari evaluasi test, sehingga evaluation_result.csv berisi keluaran describe() atas satu baris. Nilai count, mean, min, dan max pada berkas itu akan sama main.py:196-218.

Kedua CSV ditulis setelah evaluasi test selesai, sementara metrics_scores dan result_dfs sudah disiapkan sebelum training main.py:153-168. 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-9. 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-9. --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-9.

⚠️ Semua perintah di run_single_task.sh memakai --force, sehingga folder dengan --experiment_name yang sama ditimpa tanpa konfirmasi run_single_task.sh:1-9.

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-12. 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-12. Kombinasi dataset dengan jenis klasifikasinya dibahas di 12 Task Downstream dan Jenis Klasifikasi.