11
Inferensi dan Prediksi
Halaman dokumentasi ini menjelaskan apa yang dikerjakan predict.py setelah model selesai dilatih. Skrip itu tidak melatih ulang apa pun: ia memeriksa keberadaan checkpoint, memuat ulang bobot, menjalankan forward pass pada test_set_path, lalu menuliskan tabel prediksi. Tiga ketergantungan utamanya terlihat pada import di kepala berkas, yaitu load_eval_model, get_eval_parser, dan append_dataset_args predict.py:13-151. Perilaku parser dan pemetaan dataset per task dibahas di Konfigurasi Argumen dan Pemilihan Model; halaman ini mulai dari titik ketika args sudah lengkap.
Saat dijalankan, blok __main__ menetapkan torch.backends.cudnn.deterministic = True, memanggil get_eval_parser(), meneruskan hasilnya ke append_dataset_args(args), lalu membentuk model_dir dari args['model_dir'], args['dataset'], dan args['experiment_name'] dengan pola '{}/{}/{}' predict.py:63-712. get_eval_parser hanya menerima argumen inferensi: --experiment_name, --model_dir dengan default ./save, --dataset, --model_type dengan default bert-base-multilingual-uncased, --max_seq_len 512, --batch_size 4, --no_special_token, --lower, --fp16, --local_rank -1, --seed 42, dan --device dengan default cuda utils/args_helper.py:62-803. append_dataset_args melengkapi args dengan task, num_labels, dataset_class, dataloader_class, forward_fn, metrics_fn, serta test_set_path per dataset; untuk emotion-twitter nilainya ./dataset/emot_emotion-twitter/test_preprocess_masked_label.csv utils/args_helper.py:83-944. Berkas test itu memang ada di repository, dan sufiks _masked_label menandakan bahwa label pada berkas uji dipakai untuk menghitung metrik lokal, bukan untuk submission.
Siklus hidup satu pemanggilan prediksi
sequenceDiagram
autonumber
participant CLI as predict.py
box rgb(245,158,11) Konfigurasi
participant P as get_eval_parser
participant A as append_dataset_args
end
box rgb(51,103,145) Storage
participant FS as save direktori
end
box rgb(139,92,246) Pemuatan dan inferensi
participant L as load_eval_model
participant F as forward_fn
participant M as metrics_fn
end
CLI->>P: parse argumen CLI
P-->>CLI: dict args
CLI->>A: lengkapi task dan dataset_class
A-->>CLI: args siap pakai
CLI->>FS: os.path.exists best_model_0.th
alt checkpoint tidak ada
FS-->>CLI: False
CLI->>CLI: cetak Model doesn't exist in model_dir
else checkpoint ada
FS-->>CLI: True
CLI->>L: load_eval_model args
L->>FS: baca vocab.txt dan config.json
L->>FS: baca best_model_0.th
L-->>CLI: model dan tokenizer
CLI->>CLI: bangun dataset_class dari test_set_path
CLI->>F: predict model dan test_loader
loop setiap batch
F-->>CLI: loss dan batch_hyp
CLI->>M: metrics_fn list_hyp list_label
M-->>CLI: metrics
end
CLI->>FS: to_csv prediction_result.csv
end
%% fig: Siklus hidup satu pemanggilan predict.py
%% desc: Argumen di-parse, checkpoint diperiksa di direktori save, model dimuat lewat load_eval_model, lalu prediksi per batch dihitung dan ditulis ke CSV.Gerbang tunggal alur itu adalah pemeriksaan os.path.exists(model_dir + "/best_model_0.th"). Bila berkas tidak ditemukan, skrip mencetak Model doesn't exist in {model_dir} dan berhenti tanpa menulis apa pun predict.py:106-1075. Bila ditemukan, load_eval_model(args) dipanggil, optim.Adam disiapkan atas seluruh parameter model, amp.initialize dijalankan hanya ketika --fp16 diisi, dan model dipindah ke GPU ketika args['device'] bernilai cuda predict.py:79-826 predict.py:84-907.
Dua jalur pemuatan di load_eval_model
load_eval_model menyusun tiga path artefak dari args['model_dir'], args['dataset'], dan args['experiment_name'], yaitu vocab.txt, config.json, dan best_model_0.th utils/functions.py:222-2348. Dua jalur sesudahnya sangat berbeda, dan perbedaan itulah yang menentukan tokenizer mana yang akhirnya dipakai.
graph TD
MT["args model_type"] --> Q1{"word2vec atau fasttext?"}
Q1 -->|ya| W["load_word_embedding_model"]
W --> WB["vocab.txt lewat load_vocab"]
W --> WE["embedding_path per model_type"]
W --> WR["return model dan tokenizer"]
Q1 -->|tidak| Q2{"cabang model_type"}
Q2 -->|albert atau babert| A1["Config.from_json_file lalu BertTokenizer"]
Q2 -->|scratch| A2["BertConfig.from_pretrained bert-base-uncased"]
Q2 -->|bert-base-multilingual| A3["BertConfig.from_pretrained model_type"]
Q2 -->|xlm-mlm-100-1280| A4["XLMConfig.from_pretrained model_type"]
Q2 -->|xlm-roberta| A5["XLMRobertaConfig.from_pretrained model_type"]
Q2 -->|selain itu| ERR["raise ValueError Invalid model_type argument values"]
A1 --> GC["get_model_class"]
A2 --> GC
A3 --> GC
A4 --> GC
A5 --> GC
GC --> CF["config.num_labels dari args num_labels"]
CF --> INS["instansiasi pred_cls dengan config"]
INS --> BP["base_cls.from_pretrained best_model_0.th"]
BP --> PLUG["pasang base_model ke bert, albert, roberta, atau transformer"]
PLUG --> RET["return model dan tokenizer"]
ERR --> STOP["inferensi dibatalkan"]
style WB fill:#336791,color:#fff
style BP fill:#336791,color:#fff
style WE fill:#8b5cf6,color:#fff
style GC fill:#8b5cf6,color:#fff
style PLUG fill:#8b5cf6,color:#fff
style Q1 fill:#f59e0b,color:#000
style Q2 fill:#f59e0b,color:#000
style CF fill:#f59e0b,color:#000
style RET fill:#2d7d46,color:#fff
style WR fill:#2d7d46,color:#fff
style ERR fill:#dc2626,color:#fff
style STOP fill:#dc2626,color:#fff
%% fig: Percabangan load_eval_model
%% desc: model_type menentukan apakah bobot dibangun dari embedding berkas atau dari config.json, dan setiap cabang berakhir pada return model dan tokenizer.Cabang pertama berlaku ketika model_type memuat word2vec atau fasttext. Pada kasus itu fungsi tidak membaca config.json sama sekali, melainkan langsung memanggil load_word_embedding_model dengan vocab_path, kelas word tokenizer, emb_path dari args['embedding_path'], dan args['num_labels'], lalu kembali lebih awal utils/functions.py:227-2349. Di dalamnya, SimpleTokenizer dibangun dari vocab.txt lewat load_vocab, dan matriks embedding diisi dari berkas embedding; jalur word2vec memakai hidden_size 400 dengan 8 attention head, sedangkan fasttext memakai hidden_size 300 dengan 10 head utils/functions.py:180-18910. load_vocab sendiri mengembalikan daftar kata sekaligus vocab_map yang memetakan kata ke id utils/functions.py:133-14311.
Cabang kedua khusus model transformer. albert dan babert membaca config.json melalui from_json_file dan memakai BertTokenizer(vocab_path), sementara scratch, bert-base-multilingual, xlm-mlm-100-1280, dan xlm-roberta memakai from_pretrained pada model_type. Nilai model_type di luar daftar itu memicu ValueError('Invalid model_type argument values') utils/functions.py:236-25612.
Sesudah config tersedia, get_model_class memilih pasangan base class dan kepala klasifikasi, config.num_labels diisi dari args['num_labels'], pred_cls(config=config) diinstansiasi, dan bobot best_model_0.th dimuat lewat base_cls.from_pretrained(model_path, from_tf=False, config=config). Base model hasil muatan itu kemudian dipasang ke atribut yang sesuai pada model klasifikasi, yaitu model.bert, model.albert, model.roberta, atau model.transformer utils/functions.py:258-28413. Pemilihan kelas per task dijelaskan di Modul Kepala Klasifikasi.
Artefak yang dibaca dan ditulis
| Artefak | Asal path | Peran di load_eval_model | Rujukan |
|---|---|---|---|
vocab.txt | <model_dir>/<dataset>/<experiment_name>/ | tokenizer BertTokenizer atau SimpleTokenizer | utils/functions.py:222-2348 |
config.json | <model_dir>/<dataset>/<experiment_name>/ | AlbertConfig.from_json_file atau BertConfig.from_json_file | utils/functions.py:236-25612 |
best_model_0.th | <model_dir>/<dataset>/<experiment_name>/ | bobot base model lewat from_pretrained | utils/functions.py:258-28413 |
prediction_result.csv | <model_dir> hasil format di predict.py | keluaran result_df.to_csv | predict.py:99-10514 |
⚠️
predict.pymembentukmodel_dirtanpa awalan./predict.py:63-712, sedangkanload_eval_modelmembentuk path artefak dengan awalan./utils/functions.py:222-2348. Kedua skrip harus dijalankan dari akar repository agar pemeriksaan checkpoint dan pemuatan bobot menunjuk direktori yang sama.
Forward pass, metrik, dan tabel keluaran
Fungsi predict menerima model, data_loader, forward_fn, metrics_fn, dan i2w, lalu memanggil model.eval(). Setiap batch diambil lewat tqdm, elemen terakhir batch disimpan sebagai batch_seq, dan sisanya diteruskan ke forward_fn bersama i2w dan args['device'] predict.py:39-4715. Untuk klasifikasi sekuens, forward_sequence_classification mengubah batch menjadi tensor, menjalankan model(...), mengambil torch.topk(logits, 1)[1] sebagai hipotesis, dan memetakan id ke label lewat i2w utils/forward_fn.py:8-4016; rincian metriknya ada di Forward Pass dan Metrik Evaluasi.
Metrik tidak dihitung sekali di akhir. Setiap batch menambahkan list_hyp dan list_label, lalu metrics_fn dipanggil ulang atas akumulasi itu, dan hasilnya ditempel ke progress bar lewat metrics_to_string yang memformat setiap nilai dengan pola {}:{:.2f} predict.py:30-3417 predict.py:49-6118. Karena itu angka pada progress bar adalah metrik kumulatif sampai batch berjalan, bukan metrik batch tunggal.
Data uji dibangun dengan args['dataset_class'](test_dataset_path, tokenizer, lowercase=args['lower'], no_special_token=args['no_special_token']) dan dibungkus args['dataloader_class'] dengan max_seq_len, batch_size, num_workers=16, dan shuffle=False predict.py:91-9719. Perilaku collate_fn per dataset dijelaskan di Data Utils dan Dataloader; sebagai contoh, EmotionDetectionDataLoader memotong sekuens pada max_seq_len, menyusun subword_batch dan mask_batch, lalu mengembalikan seq_list sebagai elemen terakhir batch utils/data_utils.py:414-43820. Elemen terakhir itulah yang dibaca predict sebagai batch_seq, sehingga teks asli ikut terbawa sampai ke CSV.
Seluruh hasil dirakit menjadi DataFrame dengan tiga kolom, dicetak lewat result_df.head(), lalu disimpan ke model_dir + "/prediction_result.csv" predict.py:99-10514.
| Kolom | Isi | Sumber di predict | Rujukan |
|---|---|---|---|
seq | teks uji asli | test_seq dari batch_seq | predict.py:39-4715 |
hyp | label prediksi | test_hyp dari forward_fn | utils/forward_fn.py:8-4016 |
label | label emas dari berkas uji | test_label dari forward_fn | predict.py:99-10514 |
Menyapu seluruh direktori save/ dengan predict.sh
predict.sh menjalankan predict.py berulang kali tanpa perlu menulis perintah per eksperimen. Skrip itu melakukan glob ./save/* untuk dataset, lalu glob $dset/* untuk eksperimen, dan hanya memproses entri yang benar-benar direktori predict.sh:1-1521. Untuk setiap pasangan, nama dataset dan nama eksperimen diambil ulang dengan rev | cut -d/ -f1 | rev, sedangkan model_type ditebak dari prefiks sebelum garis bawah pertama pada nama eksperimen melalui cut -d_ -f1 predict.sh:1-1521. Perintah akhirnya adalah python3 predict.py --experiment_name $exp --dataset $dset --model_type $type --batch_size 12 --lower, dengan CUDA_VISIBLE_DEVICES=$1 diteruskan dari argumen skrip predict.sh:1-1521.
| Variabel | Cara memperoleh | Dipakai sebagai | Rujukan |
|---|---|---|---|
dset | nama direktori di bawah ./save | --dataset | predict.sh:1-1521 |
exp | nama direktori eksperimen | --experiment_name | predict.sh:1-1521 |
type | cut -d_ -f1 atas exp | --model_type | predict.sh:1-1521 |
$1 | argumen posisi skrip | CUDA_VISIBLE_DEVICES | predict.sh:1-1521 |
Penebakan model_type dari prefiks nama eksperimen hanya bekerja bila nama eksperimen mengikuti pola main.py --experiment_name, seperti bert-base-multilingual-uncased_b$4_step1_gamma0.9_lr1e-5_early$3_layer12_lowerFalse run_single_task.sh:1722. Nama eksperimen bebas lain akan menghasilkan --model_type yang salah dan berakhir pada raise ValueError di load_eval_model utils/functions.py:236-25612.
⚠️
predict.shmemuatbreakdi akhir badan loop dataset, sehingga hanya direktori dataset pertama hasil glob./save/*yang disapu. Eksperimen pada dataset lain tidak pernah diprediksi selama baris itu masih ada predict.sh:1-1521. Skrip juga menimpa variabel loopdsetdengan basename-nya sendiri, jadi jangan mengandalkan$dsetsebagai path penuh setelah baris tersebut predict.sh:1-1521.