8
Data Utils dan Dataloader
Modul utils/data_utils.py menyimpan seluruh pasangan kelas Dataset dan DataLoader untuk 12 task downstream dalam satu berkas. Setiap task mendapat satu subkelas torch.utils.data.Dataset yang membaca berkasnya sendiri, lalu satu subkelas DataLoader yang menyetel collate_fn ke metode _collate_fn miliknya. Keduanya dipilih pada saat runtime lewat kunci dataset_class dan dataloader_class di utils/args_helper.py:83-881.
Satu pasang kelas untuk setiap task
Konstanta statis pada setiap kelas Dataset menentukan jumlah label yang akhirnya dipakai untuk menyusun kepala klasifikasi. Tabel berikut memetakan nilai --dataset ke kelasnya.
--dataset | Kelas Dataset | Kelas DataLoader | NUM_LABELS | Rujukan |
|---|---|---|---|---|
term-extraction-airy | AspectExtractionDataset | AspectExtractionDataLoader | 5 | utils/data_utils.py:12-162 |
ner-grit | NerGritDataset | NerDataLoader | 7 | utils/data_utils.py:101-1053 |
ner-prosa | NerProsaDataset | NerDataLoader | 11 | utils/data_utils.py:156-1604 |
pos-idn | PosTagIdnDataset | PosTagDataLoader | 41 | utils/data_utils.py:245-2495 |
pos-prosa | PosTagProsaDataset | PosTagDataLoader | 26 | utils/data_utils.py:300-3046 |
emotion-twitter | EmotionDetectionDataset | EmotionDetectionDataLoader | 5 | utils/data_utils.py:389-3937 |
entailment-ui | EntailmentDataset | EntailmentDataLoader | 2 | utils/data_utils.py:443-4478 |
doc-sentiment-prosa | DocumentSentimentDataset | DocumentSentimentDataLoader | 3 | utils/data_utils.py:504-5089 |
keyword-extraction-prosa | KeywordExtractionDataset | KeywordExtractionDataLoader | 3 | utils/data_utils.py:559-56310 |
qa-factoid-itb | QAFactoidDataset | QAFactoidDataLoader | 3 | utils/data_utils.py:649-65311 |
absa-airy | AspectBasedSentimentAnalysisAiryDataset | AspectBasedSentimentAnalysisDataLoader | [4, 4, 4, 4, 4, 4, 4, 4, 4, 4] | utils/data_utils.py:744-75012 |
absa-prosa | AspectBasedSentimentAnalysisProsaDataset | AspectBasedSentimentAnalysisDataLoader | [3, 3, 3, 3, 3, 3] | utils/data_utils.py:772-77813 |
Dua task ABSA memakai daftar NUM_LABELS, bukan satu angka, karena setiap aspek punya klasifikasi sendiri: 10 aspek pada data Airy dan 6 aspek pada data Prosa. NUM_ASPECTS menyimpan panjang daftar itu, dan AspectBasedSentimentAnalysisDataLoader membacanya dari dataset.NUM_ASPECTS saat __init__ untuk menentukan lebar label_batch (utils/data_utils.py:801-82714).
Nama task di kolom pertama bukan bagian dari modul data; pemetaannya ada di append_dataset_args pada utils/args_helper.py:83-881, dan setiap cabang mengisi dataset_class serta dataloader_class sekaligus. Rincian arti tiap label dibahas di 12 Task Downstream dan Jenis Klasifikasi.
Tokenisasi subword dan pemetaan subword_to_word_indices
Kelas Dataset untuk task token-level memakai pola yang sama. Berkas teks dibaca baris demi baris; baris yang memuat \t dipecah menjadi token dan label, dan baris kosong menutup satu kalimat (utils/data_utils.py:18-3815). AspectExtractionDataset memakai penanda if '\t' in line, sedangkan NerGritDataset dan PosTagIdnDataset memakai if len(line.strip()) > 0; keduanya menghasilkan daftar {'sentence': ..., 'seq_label': ...}.
Pada __getitem__, setiap kata di-encode sendiri dengan add_special_tokens=False. Panjang subword_list menentukan berapa kali word_idx diulang ke dalam subword_to_word_indices, sehingga satu kata yang pecah menjadi tiga subword menyumbang tiga entri dengan indeks yang sama (utils/data_utils.py:44-6216). cls_token_id disisipkan di depan dan sep_token_id di belakang, dan keduanya diberi penanda -1 karena bukan bagian dari kata mana pun.
graph TD
raw["berkas .txt: token TAB label"] --> loader["load_dataset"]
loader --> buf["dataset: list of sentence + seq_label"]
buf --> enc["tokenizer.encode(word, add_special_tokens=False)"]
enc --> map["subword_to_word_indices: word_idx per subword"]
map --> cls["cls_token_id di depan, sep_token_id di belakang"]
cls --> idx["posisi khusus ditandai -1"]
idx --> item["np.array(subwords), np.array(subword_to_word_indices), np.array(seq_label), sentence"]
style raw fill:#336791,color:#fff
style buf fill:#8b5cf6,color:#fff
style idx fill:#f59e0b,color:#000
style item fill:#2d7d46,color:#fff
%% fig: Dari baris berlabel ke satu contoh Dataset
%% desc: load_dataset mengubah berkas teks menjadi daftar kalimat, lalu __getitem__ memecah setiap kata menjadi subword dan mencatat pemetaan subword ke indeks kata.Pemetaan yang sama dipakai tiga kali: oleh NerDataLoader, PosTagDataLoader, dan KeywordExtractionDataLoader. Ketiganya meneruskan subword_to_word_indices_batch sebagai argumen tersendiri ke model (utils/data_utils.py:211-24017, utils/data_utils.py:355-38418, utils/data_utils.py:614-64419).
Untuk model berbasis kata, SimpleTokenizer di utils/functions.py mengambil peran tokenizer Hugging Face. Kelas ini menambah <cls>, <sep>, <pad>, dan beberapa token khusus lain ke vocab pada __init__, lalu menyimpan cls_token_id dan sep_token_id sebagai atribut sehingga __getitem__ di data_utils.py dapat memakainya tanpa perubahan (utils/functions.py:16-3720). Metode encode mengembalikan daftar id tanpa token khusus bila add_special_tokens=False, dan menyisipkan <cls> serta <sep> bila tidak (utils/functions.py:48-6121). Metode encode_plus menambahkan token_type_ids agar pasangan kalimat dapat dibedakan (utils/functions.py:63-8522).
collate_fn: padding, masking, dan nilai penanda
Setiap DataLoader menyetel self.collate_fn = self._collate_fn dan menyimpan max_seq_len dengan nilai default 512. _collate_fn menghitung panjang terpanjang di dalam batch, memotongnya dengan min(self.max_seq_len, max_seq_len), lalu menyiapkan array numpy dengan lebar tersebut (utils/data_utils.py:67-9623).
| Array | dtype | Nilai awal | Peran |
|---|---|---|---|
subword_batch | np.int64 | np.zeros | id subword, sisa posisi tetap 0 |
mask_batch | np.float32 | np.zeros, diisi 1 sepanjang token asli | attention_mask |
subword_to_word_indices_batch | np.int64 | np.full(..., -1) | indeks kata asal per subword |
seq_label_batch | np.int64 | np.full(..., -100) | label token-level, posisi padding diabaikan |
token_type_batch | np.int64 | np.zeros | penanda segmen A/B pada task pasangan kalimat |
Nilai -100 adalah penanda abaikan yang dikenali lintas modul. forward_word_classification berhenti mengiterasi label begitu menemukan -100, sehingga posisi padding tidak masuk perhitungan metrik (utils/forward_fn.py:43-5624). Task sekuens dengan satu label per kalimat memakai label_batch berbentuk (batch_size, 1) yang diisi -100 sebelum ditimpa, seperti pada EmotionDetectionDataLoader (utils/data_utils.py:414-43825) dan DocumentSentimentDataLoader (utils/data_utils.py:530-55426).
graph TD
batch["batch: list of tuple dari __getitem__"] --> maxlen["max_seq_len = min(self.max_seq_len, max panjang batch)"]
maxlen --> sub["subword_batch: np.zeros int64"]
maxlen --> mask["mask_batch: np.zeros float32, diisi 1 untuk token asli"]
maxlen --> ind["subword_to_word_indices_batch: np.full -1"]
maxlen --> lab["seq_label_batch: np.full -100"]
sub --> ret["return 4 sampai 6 numpy array"]
mask --> ret
ind --> ret
lab --> ret
style maxlen fill:#f59e0b,color:#000
style sub fill:#8b5cf6,color:#fff
style ind fill:#8b5cf6,color:#fff
style lab fill:#8b5cf6,color:#fff
style ret fill:#2d7d46,color:#fff
%% fig: Penyusunan satu batch di _collate_fn
%% desc: Panjang terpanjang di dalam batch menentukan lebar setiap array, lalu nilai penanda -1 dan -100 diisi pada posisi yang tidak terpakai.Bentuk tuple yang dikembalikan berbeda antar task, dan main.py tidak pernah membongkarnya sendiri. Loop evaluasi memakai batch_data[-1] sebagai list_seq dan mengirim batch_data[:-1] ke forward_fn, sehingga jumlah elemen di depan bebas berubah asal forward_fn yang dipilih mengetahuinya (main.py:47-5027).
Kelas DataLoader | Tuple yang dikembalikan | Rujukan |
|---|---|---|
AspectExtractionDataLoader, NerDataLoader, PosTagDataLoader, KeywordExtractionDataLoader | subword, mask, subword_to_word_indices, seq_label, seq | utils/data_utils.py:67-9623 |
EmotionDetectionDataLoader, DocumentSentimentDataLoader | subword, mask, label, seq | utils/data_utils.py:414-43825 |
EntailmentDataLoader | subword, mask, token_type, label, seq | utils/data_utils.py:472-49928 |
QAFactoidDataLoader | subword, mask, token_type, subword_to_word_indices, seq_label, seq | utils/data_utils.py:708-73929 |
AspectBasedSentimentAnalysisDataLoader | subword, mask, label, seq dengan label selebar num_aspects | utils/data_utils.py:801-82714 |
Task yang membandingkan dua kalimat mengisi token_type_batch dari encode_plus. EntailmentDataset memanggil encode_plus(sent_A, sent_B, add_special_tokens=not self.no_special_token, return_token_type_ids=True) lalu mengembalikan subwords, token_type_ids, label, dan gabungan sent_A + "|" + sent_B (utils/data_utils.py:459-46630). QAFactoidDataset membangun token_type_ids secara manual: 0 untuk question beserta cls_token_id dan sep_token_id perantara, 1 untuk passage dan sep_token_id penutup. Semua subword dari question diberi subword_to_word_indices -1 karena label hanya dipasang pada passage (utils/data_utils.py:670-70331).
Alur dari konfigurasi ke forward_fn
main.py membuat tiga pasang dataset dan loader, satu untuk train, valid, dan test, dengan num_workers=16 dan shuffle=False pada ketiganya. Argumen max_seq_len, batch_size, dan no_special_token diteruskan langsung dari args (main.py:172-18232). Baris yang sama juga dipakai predict.py saat menyiapkan test_loader untuk inferensi (predict.py:94-9533).
graph LR
args["args dataset_class dan dataloader_class"] --> ds["Dataset(dataset_path, tokenizer, lowercase, no_special_token)"]
ds --> dl["DataLoader(dataset, max_seq_len, batch_size, num_workers=16, shuffle=False)"]
dl --> loop["evaluate: for batch_data in data_loader"]
loop --> fw["forward_fn(model, batch_data[:-1], i2w=i2w)"]
loop --> seq["batch_data[-1] masuk list_seq"]
style args fill:#f59e0b,color:#000
style ds fill:#8b5cf6,color:#fff
style dl fill:#336791,color:#fff
style fw fill:#2d7d46,color:#fff
%% fig: Dataset, DataLoader, dan pemakaiannya di main.py
%% desc: Kelas yang dipilih lewat args menghasilkan dataset dan loader, lalu setiap batch dibongkar menjadi input model dan deret teks asli.Versi awal di notebook
Empat notebook di data_utils_notebook/ memuat versi kelas-kelas ini sebelum dipindahkan ke modul. Bedanya terlihat pada penanganan token khusus. Di data_utils_notebook/postag_data_utils.ipynb:22-7134, setiap kata di-encode dengan add_special_tokens=True dan subword_to_word_indices dibangun mulai dari 0 tanpa entri -1, sehingga tidak ada posisi khusus di awal maupun akhir. Notebook tersebut juga belum menyiapkan mask_batch; _collate_fn hanya mengembalikan subword_batch, subword_to_word_indices_batch, dan seq_label_batch (data_utils_notebook/postag_data_utils.ipynb:80-8935). Ketiga notebook lain mengikuti pola yang sama untuk task masing-masing (data_utils_notebook/ner_data_utils.ipynb:22-8936, data_utils_notebook/aspect_extraction_data_util.ipynb:22-8937, data_utils_notebook/aspect_based_sentiment_analysis.ipynb:22-7138).
⚠️
NewsCategorizationDataLoadertidak dapat dijalankan apa adanya._collate_fnmemakaiNUM_LABELSsebagai nama lepas padahal konstanta itu hanya ada sebagai atribut kelasNewsCategorizationDataset, loop-nya membongkar dua nilai(subwords, labels)sementaraseq_list.append(raw_seq)menyebut variabel yang tidak pernah didefinisikan, dan tidak ada cabangnews-category-prosadiappend_dataset_args. Rujuk utils/data_utils.py:858-88739 dan bandingkan dengan pemetaan task di utils/args_helper.py:83-881;append_dataset_argshanya melemparValueError(f'Unknown dataset name ...')untuk nama yang tidak dikenal.
Muara seluruh tensor ini, termasuk cara forward_fn mengubahnya menjadi list_hyp dan list_label, dibahas di Forward Pass dan Metrik Evaluasi. Urutan pemanggilan loader di dalam train dan evaluate dibahas di Alur Training dan Evaluasi, sedangkan asal nilai max_seq_len dan batch_size ada di Konfigurasi Argumen dan Pemilihan Model.