Lompat ke konten
dokumentasi.id
Commit ce728f6
v1
Star659

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

10

Modul Kepala Klasifikasi

Direktori modules/ berisi tepat dua berkas, word_classification.py dan multi_label_classification.py, tanpa __init__.py. Keduanya diimpor lewat jalur modul penuh di utils/functions.py, yaitu from modules.word_classification import ... dan from modules.multi_label_classification import ... (utils/functions.py:6-7). Setiap berkas mendefinisikan empat kepala klasifikasi di atas encoder transformer: satu untuk BERT, ALBERT, XLM, dan XLM-Roberta. Jumlah label dibaca dari config.num_labels pada varian word classification (modules/word_classification.py:35-44) dan dari config.num_labels_list pada varian multi-label (modules/multi_label_classification.py:34-43).

Peta base model ke kelas prediksi

Pemilihan kelas tidak terjadi di dalam modules/, melainkan di get_model_class. Fungsi itu mencocokkan substring model_type untuk menentukan base_cls, lalu mencocokkan nilai task untuk menentukan pred_cls (utils/functions.py:145-178). Jenis task itu sendiri dijelaskan di 12 Task Downstream dan Jenis Klasifikasi.

graph TD
  GM["get_model_class(model_type, task)"]
  GM -->|"'babert-lite'"| AB["base_cls = AlbertModel"]
  GM -->|"'xlm-mlm'"| XB["base_cls = XLMModel"]
  GM -->|"'xlm-roberta'"| RB["base_cls = XLMRobertaModel"]
  GM -->|"lainnya"| BB["base_cls = BertModel"]
  AB --> AW["AlbertForWordClassification"]
  AB --> AM["AlbertForMultiLabelClassification"]
  XB --> XW["XLMForWordClassification"]
  XB --> XM["XLMForMultiLabelClassification"]
  RB --> RW["XLMRobertaForWordClassification"]
  RB --> RM["XLMRobertaForMultiLabelClassification"]
  BB --> BW["BertForWordClassification"]
  BB --> BM["BertForMultiLabelClassification"]
  style GM fill:#f59e0b,color:#000
  style AB fill:#336791,color:#fff
  style XB fill:#336791,color:#fff
  style RB fill:#336791,color:#fff
  style BB fill:#336791,color:#fff
  style AW fill:#2d7d46,color:#fff
  style XW fill:#2d7d46,color:#fff
  style RW fill:#2d7d46,color:#fff
  style BW fill:#2d7d46,color:#fff
  style AM fill:#8b5cf6,color:#fff
  style XM fill:#8b5cf6,color:#fff
  style RM fill:#8b5cf6,color:#fff
  style BM fill:#8b5cf6,color:#fff
%% fig: Pemetaan model_type dan task ke kelas kepala
%% desc: get_model_class memilih base_cls dari substring model_type, lalu memilih kepala word classification atau multi-label dari nilai task.
Fig. 1: Pemetaan model_type dan task ke kelas kepalaget_model_class memilih base_cls dari substring model_type, lalu memilih kepala word classification atau multi-label dari nilai task.
Varian model_typebase_clstask = token_classificationtask = multi_label_classification
'babert-lite'AlbertModelAlbertForWordClassificationAlbertForMultiLabelClassification
'xlm-mlm'XLMModelXLMForWordClassificationXLMForMultiLabelClassification
'xlm-roberta'XLMRobertaModelXLMRobertaForWordClassificationXLMRobertaForMultiLabelClassification
'babert', 'bert-base-multilingual', 'word2vec', 'fasttext', 'scratch'BertModelBertForWordClassificationBertForMultiLabelClassification

Cabang terakhir diambil untuk semua model_type yang tidak cocok dengan tiga pola sebelumnya, sesuai komentar pada blok else (utils/functions.py:145-178). Argumen yang mengisi model_type dan task dibahas di Konfigurasi Argumen dan Pemilihan Model.

Kepala word classification: rata-rata per kata

Keempat kelas word classification menerima subword_to_word_ids sebagai argumen forward. Setelah encoder dijalankan, tiap posisi kata i dihitung dari rata-rata representasi subword yang memakai id tersebut. Hasilnya ditumpuk menjadi word_batch, di-dropout, lalu dilewatkan ke self.classifier (modules/word_classification.py:90-107).

graph LR
  ENC["outputs[0]: sequence_output"] --> MASK["mask = (subword_to_word_ids == i).unsqueeze(-1)"]
  MASK --> LAT["word_latents: (sequence_output * mask).sum(dim=1) / mask.sum()"]
  LAT --> STK["torch.stack(word_latents, dim=1)"]
  STK --> DR["self.dropout"]
  DR --> LIN["self.classifier: Linear(hidden_size, num_labels)"]
  LIN --> LOG["logits"]
  LOG --> LOSS["CrossEntropyLoss(logits.view(-1, num_labels), labels.view(-1))"]
  style ENC fill:#336791,color:#fff
  style LAT fill:#8b5cf6,color:#fff
  style LOG fill:#8b5cf6,color:#fff
  style LOSS fill:#2d7d46,color:#fff
%% fig: Alur kepala word classification
%% desc: Representasi subword dirata-ratakan per kata, di-dropout, diklasifikasikan per kata, lalu loss dihitung bila labels tersedia.
Fig. 2: Alur kepala word classificationRepresentasi subword dirata-ratakan per kata, di-dropout, diklasifikasikan per kata, lalu loss dihitung bila labels tersedia.

Bentuk __init__ keempat kelas hampir identik: self.num_labels = config.num_labels, satu nn.Dropout, dan satu nn.Linear(config.hidden_size, config.num_labels) (modules/word_classification.py:38-42). Perbedaan yang tersisa ada pada nama atribut encoder dan sumber probabilitas dropout. BertForWordClassification memakai config.hidden_dropout_prob (modules/word_classification.py:35-44), AlbertForWordClassification juga memakai config.hidden_dropout_prob (modules/word_classification.py:111-120), sedangkan XLMForWordClassification memakai config.dropout (modules/word_classification.py:165-169). XLMRobertaForWordClassification memakai config.hidden_dropout_prob dengan atribut encoder bernama self.roberta (modules/word_classification.py:250-254).

Signature forward juga tidak seragam. Varian BERT dan XLM-Roberta menerima inputs_embeds, sedangkan varian ALBERT dan XLM tidak (modules/word_classification.py:46-56, modules/word_classification.py:122-132). Varian XLM meneruskan langs ke self.transformer (modules/word_classification.py:213-220).

Kepala multi-label: satu linear per kolom label

Pada varian multi-label, self.num_labels bukan bilangan bulat melainkan list config.num_labels_list, dan self.classifiers berupa nn.ModuleList dengan satu nn.Linear(config.hidden_size, num_label) untuk setiap entri list itu (modules/multi_label_classification.py:34-43). Panjang list menentukan berapa kolom label yang diprediksi sekaligus, misalnya config.num_labels_list = [2,3,4,5,6,7] pada blok uji XLMRobertaForMultiLabelClassification (modules/multi_label_classification.py:380-386).

graph LR
  B1["BertModel / AlbertModel: self.dropout(outputs[1])"] --> H0["classifiers[0]: Linear(hidden_size, num_labels_list[0])"]
  B2["XLMModel / XLMRobertaModel: self.dropout(self.pooler(outputs[0][:,0,:]))"] --> H0
  B1 --> HN["classifiers[n]: Linear(hidden_size, num_labels_list[n])"]
  B2 --> HN
  H0 --> LG["logits (list)"]
  HN --> LG
  LG --> TL["total_loss: penjumlahan CrossEntropyLoss per kolom label"]
  style B1 fill:#336791,color:#fff
  style B2 fill:#336791,color:#fff
  style H0 fill:#8b5cf6,color:#fff
  style HN fill:#8b5cf6,color:#fff
  style TL fill:#2d7d46,color:#fff
%% fig: Percabangan kepala multi-label
%% desc: Vektor ringkas encoder masuk ke satu linear per kolom label, lalu loss tiap kolom dijumlahkan menjadi total_loss.
Fig. 3: Percabangan kepala multi-labelVektor ringkas encoder masuk ke satu linear per kolom label, lalu loss tiap kolom dijumlahkan menjadi total_loss.

Representasi masukan kepala berbeda antar base model. BertForMultiLabelClassification dan AlbertForMultiLabelClassification memakai outputs[1] setelah dropout (modules/multi_label_classification.py:89-93, modules/multi_label_classification.py:162-166). XLMForMultiLabelClassification dan XLMRobertaForMultiLabelClassification menambahkan self.pooler = nn.Sequential(nn.Linear(config.hidden_size, config.hidden_size), nn.Tanh()) dan memakai token pertama outputs[0][:,0,:] (modules/multi_label_classification.py:242-246, modules/multi_label_classification.py:334-338).

Loss dihitung dengan CrossEntropyLoss. total_loss diinisialisasi 0, lalu ditambah loss setiap kolom label di dalam loop zip(logits, self.num_labels), sehingga outputs menjadi (total_loss, logits, ...) (modules/multi_label_classification.py:96-103). Varian XLM dan XLM-Roberta menjalankan pola yang sama (modules/multi_label_classification.py:249-256).

Kunci config yang dibaca kepala

Kunci configTipeDibaca oleh
num_labelsintself.num_labels dan nn.Linear(config.hidden_size, config.num_labels) di kelas word classification
num_labels_listlist intself.num_labels dan panjang nn.ModuleList di kelas multi-label
hidden_sizeintdimensi masukan classifier maupun classifiers
hidden_dropout_probfloatnn.Dropout di kelas BERT, ALBERT, dan XLM-Roberta
dropoutfloatnn.Dropout di kelas XLM

Nilai num_labels dan num_labels_list diisi pemanggil sebelum model dibuat. Ketika num_labels berbentuk list, pemanggil menulis config.num_labels = max(num_labels) sekaligus config.num_labels_list = num_labels (utils/functions.py:262-266). Pola yang sama dipakai saat model diinstansiasi dari embedding word2vec atau fasttext (utils/functions.py:192-198).

Memasang base model terlatih ke kepala

load_eval_model membuat kepala lebih dulu dengan pred_cls(config=config), lalu memuat bobot encoder memakai base_cls.from_pretrained(model_path, from_tf=False, config=config). Bobot itu dipasang dengan mengganti atribut yang namanya cocok: bert, albert, roberta, atau transformer. Bila tidak ada yang cocok, fungsi tidak melempar error, hanya membuat objek ValueError tanpa me-raise (utils/functions.py:260-282).

Nama atribut itu harus konsisten dengan base_model_prefix. XLMRobertaForWordClassification menyetel config_class = XLMRobertaConfig, pretrained_model_archive_map = XLM_ROBERTA_PRETRAINED_MODEL_ARCHIVE_MAP, dan base_model_prefix = "roberta" (modules/word_classification.py:243-246).

Jebakan yang perlu diketahui

⚠️ XLMRobertaForWordClassification mewarisi BertPreTrainedModel, bukan kelas XLM-Roberta, lalu menimpa config_class dan pretrained_model_archive_map secara manual (modules/word_classification.py:243-246). base_model_prefix yang benar tetap wajib agar bobot roberta.* termuat.

⚠️ Argumen token_type_ids dikomentari pada pemanggilan encoder di XLMRobertaForWordClassification (modules/word_classification.py:306-313), tetapi tetap dikirim pada XLMRobertaForMultiLabelClassification (modules/multi_label_classification.py:325-332). Dua kepala dengan base model sama meneruskan argumen berbeda.

⚠️ Normalisasi rata-rata kata memakai mask.sum() yang bernilai skalar untuk seluruh batch, bukan jumlah subword per contoh (modules/word_classification.py:92-98). Nilai word_latents karena itu bukan rata-rata per kata yang ternormalisasi penuh.

⚠️ AlbertForMultiLabelClassification menghitung loss dengan loss_fct(logit, label.view(-1)) tanpa meratakan logit lebih dulu (modules/multi_label_classification.py:169-176), sedangkan BertForMultiLabelClassification memakai logit.view(-1, num_label) (modules/multi_label_classification.py:99-102).

⚠️ MSELoss diimpor di kedua berkas tetapi tidak dipakai di mana pun (modules/word_classification.py:7, modules/multi_label_classification.py:7).

Uji cepat di akhir berkas

Kedua berkas ditutup dengan blok if __name__ == '__main__' yang menginstansiasi keempat kelas memakai checkpoint contoh: bert-base-uncased, albert-base-v2, xlm-mlm-100-1280, dan xlm-roberta-base (modules/word_classification.py:337-356). Blok pada berkas multi-label juga menyetel config.num_labels_list dan menjalankan forward dengan labels untuk mencetak output[0] dan output[1] (modules/multi_label_classification.py:352-386). Hasil forward itu yang kemudian dipakai metrik evaluasi, seperti dijelaskan di Forward Pass dan Metrik Evaluasi.