Lompat ke konten
dokumentasi.id
Commit ce728f6
v1
Star659

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-15. 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-71. 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-80. 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-94. 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.
Fig. 1: Siklus hidup satu pemanggilan predict.pyArgumen 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-107. 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-82 predict.py:84-90.

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-234. 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.
Fig. 2: Percabangan load_eval_modelmodel_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-234. 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-189. load_vocab sendiri mengembalikan daftar kata sekaligus vocab_map yang memetakan kata ke id utils/functions.py:133-143.

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

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-284. Pemilihan kelas per task dijelaskan di Modul Kepala Klasifikasi.

Artefak yang dibaca dan ditulis

ArtefakAsal pathPeran di load_eval_modelRujukan
vocab.txt<model_dir>/<dataset>/<experiment_name>/tokenizer BertTokenizer atau SimpleTokenizerutils/functions.py:222-234
config.json<model_dir>/<dataset>/<experiment_name>/AlbertConfig.from_json_file atau BertConfig.from_json_fileutils/functions.py:236-256
best_model_0.th<model_dir>/<dataset>/<experiment_name>/bobot base model lewat from_pretrainedutils/functions.py:258-284
prediction_result.csv<model_dir> hasil format di predict.pykeluaran result_df.to_csvpredict.py:99-105

⚠️ predict.py membentuk model_dir tanpa awalan ./ predict.py:63-71, sedangkan load_eval_model membentuk path artefak dengan awalan ./ utils/functions.py:222-234. 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-47. 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-40; 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-34 predict.py:49-61. 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-97. 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-438. 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-105.

KolomIsiSumber di predictRujukan
seqteks uji aslitest_seq dari batch_seqpredict.py:39-47
hyplabel prediksitest_hyp dari forward_fnutils/forward_fn.py:8-40
labellabel emas dari berkas ujitest_label dari forward_fnpredict.py:99-105

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

VariabelCara memperolehDipakai sebagaiRujukan
dsetnama direktori di bawah ./save--datasetpredict.sh:1-15
expnama direktori eksperimen--experiment_namepredict.sh:1-15
typecut -d_ -f1 atas exp--model_typepredict.sh:1-15
$1argumen posisi skripCUDA_VISIBLE_DEVICESpredict.sh:1-15

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:17. Nama eksperimen bebas lain akan menghasilkan --model_type yang salah dan berakhir pada raise ValueError di load_eval_model utils/functions.py:236-256.

⚠️ predict.sh memuat break di 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-15. Skrip juga menimpa variabel loop dset dengan basename-nya sendiri, jadi jangan mengandalkan $dset sebagai path penuh setelah baris tersebut predict.sh:1-15.