32  Notebook Bab 14 - Data Multimodal

Open In ColabNotebook Bab 14 ini punya dua bagian. Bagian Demo tinggal Anda jalankan lalu amati keluarannya; bagian Mini Project berisi soal dan data yang Anda kerjakan sendiri.Karena memakai pretrained model, notebook ini paling praktis dijalankan di Google Colab. Kita menggabungkan modalitas teks (embedding) dengan tabular, lalu menangani kasus satu modalitas hilang.

32.1 Persiapan

%pip install -q sentence-transformers scikit-learn

import numpy as np
from sklearn.datasets import fetch_20newsgroups
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sentence_transformers import SentenceTransformer

rng = np.random.default_rng(42)
print('Setup selesai.')

33 Section 1 - Demo: Fusi Teks + Tabular

33.1 Data: dokumen (teks) + fitur tabular sintetis

Fitur tabular dibuat membawa sinyal kelas yang bising, jadi tiap modalitas hanya memuat sebagian informasi.

cats = ['rec.sport.hockey', 'sci.med', 'comp.graphics']
news = fetch_20newsgroups(subset='all', categories=cats,
                          remove=('headers', 'footers', 'quotes'), random_state=42)
texts, y = news.data, news.target

tab = np.zeros((len(y), 3))
for k in range(3):
    tab[y == k, k] = 1.0
tab = tab + rng.normal(0, 0.9, tab.shape)   # sinyal kelas yang bising
print('Dokumen:', len(texts), '| tabular:', tab.shape)
enc = SentenceTransformer('all-MiniLM-L6-v2')
emb = enc.encode(texts, batch_size=64, show_progress_bar=True, normalize_embeddings=True)
print('Embedding teks:', emb.shape)

33.2 Teks saja vs tabular saja vs fusi

idx = np.arange(len(y))
itr, ite = train_test_split(idx, test_size=0.25, random_state=42, stratify=y)
sc = StandardScaler().fit(tab[itr])
tab_tr, tab_te = sc.transform(tab[itr]), sc.transform(tab[ite])
emb_tr, emb_te = emb[itr], emb[ite]

def acc(Xtr, Xte):
    clf = LogisticRegression(max_iter=2000).fit(Xtr, y[itr])
    return accuracy_score(y[ite], clf.predict(Xte))

fus_tr = np.hstack([emb_tr, tab_tr])
fus_te = np.hstack([emb_te, tab_te])
print('Teks saja    :', round(acc(emb_tr, emb_te), 3))
print('Tabular saja :', round(acc(tab_tr, tab_te), 3))
print('Fusi         :', round(acc(fus_tr, fus_te), 3))

33.3 Saat satu modalitas hilang

clf = LogisticRegression(max_iter=2000).fit(fus_tr, y[itr])
emb_te_miss = emb_te.copy()
hilang = rng.random(len(ite)) < 0.5
emb_te_miss[hilang] = 0.0   # 50% teks hilang -> vektor nol
fus_te_miss = np.hstack([emb_te_miss, tab_te])
print('Fusi, 50% teks hilang:', round(accuracy_score(y[ite], clf.predict(fus_te_miss)), 3))

🔎 Amati. Fusi biasanya menyamai atau melampaui modalitas tunggal terbaik karena menggabungkan sinyal yang saling melengkapi. Ketika satu modalitas hilang saat inferensi (di sini teks dinolkan), model fusi tidak kolaps: ia masih bertumpu pada modalitas tabular yang tersisa, walau akurasinya turun. Kunci fusi yang benar: baris tiap modalitas berasal dari entitas yang sama dan split dilakukan per entitas.

34 Section 2 - Mini Project

34.1 Soal

Bangun pipeline multimodal untuk memprediksi kelas dari teks + tabular.

Tugas:

  1. Ekstrak embedding teks (SBERT) dan bakukan fitur tabular (fit pada data latih saja).
  2. Bandingkan tiga konfigurasi: teks saja, tabular saja, dan fusi (concatenation).
  3. Uji ketahanan: nolkan salah satu modalitas pada 30 persen data uji, ukur penurunan.

Luaran: kode fusi + tabel akurasi + 3-4 kalimat analisis peran tiap modalitas.

Kriteria penilaian: (a) scaler di-fit pada data latih saja; (b) perbandingan adil (model sama); (c) analisis penanganan modalitas hilang.

# DATA AWAL (jangan diubah) - dua kategori teks + tabular bising.
cats2 = ['sci.space', 'talk.politics.guns']
news2 = fetch_20newsgroups(subset='all', categories=cats2,
                           remove=('headers', 'footers', 'quotes'), random_state=7)
texts2, y2 = news2.data, news2.target
tab2 = np.zeros((len(y2), 2))
for k in range(2):
    tab2[y2 == k, k] = 1.0
tab2 = tab2 + rng.normal(0, 1.0, tab2.shape)
print('Dokumen:', len(texts2), '| tabular:', tab2.shape)
# Kerjakan di sini.
# Petunjuk: enc.encode(texts2) untuk embedding; np.hstack untuk fusi; StandardScaler fit di train.