Sepanjang section ini, setiap angka yang kita cetak — akurasi 0,974, tabel MSE overfitting, kecocokan klaster 30/30 — sengaja dirancang untuk muncul identik setiap kali kodenya dijalankan ulang, di mesin mana pun. Kemampuan mengulang hasil dengan persis sama itulah reproduksibilitas, dan ia bukan kemewahan akademik: tanpanya kita tidak bisa membedakan apakah perubahan performa berasal dari model baru atau sekadar dari kebetulan pengacakan. Chapter penutup section ini membahas dari mana keacakan masuk ke alur kerja machine learning, bagaimana parameter random_state menjinakkannya, dan kebiasaan apa yang perlu dibiasakan agar setiap eksperimen kita bisa direproduksi.
Dari Mana Kekacakan Masuk ke Alur Kerja #
Keacakan dalam machine learning bukan satu titik, melainkan tersebar di banyak tahap. Sebagian besar algoritme dan utilitas scikit-learn sengaja memakai langkah acak — misalnya mengacak urutan data agar data latih tidak terurut menurut kelas — dan setiap langkah acak itu adalah lubang tempat hasil bisa melenceng antar eksekusi.
| Tahap kerja | Sumber keacakan | Parameter kendali |
|---|---|---|
| Data sintetis | Pembangkit angka acak NumPy | np.random.default_rng(seed) |
train_test_split | Pengacakan indeks sebelum membagi | random_state=... |
| Cross-validation | Pengacakan lipatan KFold | KFold(..., shuffle=True, random_state=...) |
| KMeans | Pemilihan posisi pusat awal | random_state=... (bersama n_init) |
| Random Forest | Pemilihan subset fitur tiap pohon | random_state=... |
| Jaringan saraf | Inisialisasi bobot awal | Seed framework (di luar scikit-learn) |
Mekanisme di balik semua kendali itu sama: pseudo-random number generator (PRNG). PRNG menghasilkan deret angka yang tampak acak tetapi sepenuhnya ditentukan oleh satu angka awal — seed. Seed yang sama menghasilkan deret yang sama; deret yang sama membuat pengacakan data, pemilihan pusat klaster, dan pemilihan subset fitur berlangsung identik. Seed yang berbeda menghasilkan deret berbeda — tetapi deret itu juga tetap, karena seednya tetap. Itulah sebabnya dua eksperimen dengan seed berbeda menghasilkan dua hasil yang berbeda namun masing-masing stabil.
Membuktikannya dengan Kode #
File reproduksibilitas.py berikut menyelidiki tiga klaim sekaligus: (a) pemanggilan pembagian data dengan seed yang sama dua kali menghasilkan hasil identik; (b) seed yang berbeda menghasilkan hasil berbeda yang tetap deterministik; dan (c) prinsip yang sama berlaku untuk Generator NumPy maupun estimator KMeans. Angka-angka pada output dijamin tetap karena setiap sumber acak telah berseed.
# random_state mengubah proses acak menjadi dapat direproduksi.
# Bandingkan: seed sama dipanggil dua kali (hasil identik) vs seed berbeda
# (hasil berbeda, tetap deterministik karena angkanya tetap tercetak sama).
import numpy as np
from sklearn.cluster import KMeans
from sklearn.model_selection import train_test_split
# Data sintetis dibuat dengan Generator berseed -> data selalu identik.
rng = np.random.default_rng(42)
X = rng.normal(loc=0.0, scale=1.0, size=(60, 2))
def rata2_split(seed):
"""Pisahkan data dengan random_state tertentu, kembalikan rata-rata kolom X."""
X_latih, X_uji = train_test_split(X, test_size=0.25, random_state=seed)
return X_latih.mean(), X_uji.mean()
# Bagian A: seed sama, dua pemanggilan -> hasil identik.
a_pertama = rata2_split(42)
a_kedua = rata2_split(42)
print("=== random_state=42 dipanggil dua kali ===")
print(f"pemanggilan pertama: mean latih {a_pertama[0]:.4f}, mean uji {a_pertama[1]:.4f}")
print(f"pemanggilan kedua : mean latih {a_kedua[0]:.4f}, mean uji {a_kedua[1]:.4f}")
print(f"identik? {a_pertama == a_kedua}")
print()
# Bagian B: seed berbeda -> hasil berbeda, tapi angkanya tetap deterministik.
b = rata2_split(7)
print("=== random_state=7 sebagai pembanding ===")
print(f"mean latih {b[0]:.4f}, mean uji {b[1]:.4f}")
print(f"berbeda dari seed 42? {a_pertama != b}")
print()
# Bagian C: Generator NumPy berseed juga menghasilkan deret yang sama.
rng_a = np.random.default_rng(42)
rng_b = np.random.default_rng(42)
print(f"default_rng(42) dua kali -> tiga angka pertama sama? "
f"{np.array_equal(rng_a.random(3), rng_b.random(3))}")
print()
# Bagian D: estimator acak juga tunduk pada random_state.
km_pertama = KMeans(n_clusters=2, n_init=10, random_state=42).fit(X)
km_kedua = KMeans(n_clusters=2, n_init=10, random_state=42).fit(X)
print(f"KMeans random_state=42 dua kali -> label klaster sama? "
f"{np.array_equal(km_pertama.labels_, km_kedua.labels_)}")
print(f"pusat klaster 0: [{km_pertama.cluster_centers_[0][0]:.3f}, "
f"{km_pertama.cluster_centers_[0][1]:.3f}]")Output yang Dihasilkan:
=== random_state=42 dipanggil dua kali ===
pemanggilan pertama: mean latih -0.0422, mean uji -0.1151
pemanggilan kedua : mean latih -0.0422, mean uji -0.1151
identik? True
=== random_state=7 sebagai pembanding ===
mean latih -0.1423, mean uji 0.1854
berbeda dari seed 42? True
default_rng(42) dua kali -> tiga angka pertama sama? True
KMeans random_state=42 dua kali -> label klaster sama? True
pusat klaster 0: [0.509, 0.439]Bedah kode baris demi baris:
- Baris 10–11 (
default_rng(42)): data 60×2 dibuat dari Generator berseed. Karena deret angkanya tetap, matriksXyang sama terbentuk di setiap eksekusi — fondasi bagi seluruh perbandingan sesudahnya. - Baris 14–17 (
def rata2_split): fungsi bantu yang membagiXdengantest_size=0.25danrandom_state=seed, lalu mengembalikan rata-rata kolom data latih dan data uji sebagai sidik jari pembagian tersebut. Pembagian yang berbeda hampir pasti menghasilkan rata-rata yang berbeda. - Baris 20–27 (Bagian A):
rata2_split(42)dipanggil dua kali dan keduanya mencetak mean latih −0,0422 serta mean uji −0,1151 — angka yang persis sama, dibuktikanidentik? True. Seed yang sama memutar ulang deret PRNG dari awal, sehingga urutan pengacakan yang sama terulang. - Baris 29–34 (Bagian B):
rata2_split(7)menghasilkan mean latih −0,1423 dan mean uji 0,1854 — jelas berbeda dari seed 42, tetapi jika Anda menjalankan ulang file ini seratus kali, pasangan angka inilah yang selalu muncul. Berbeda bukan berarti tak terkendali. - Baris 36–40 (Bagian C): dua Generator terpisah
default_rng(42)menghasilkan tiga angka pertama yang sama (True) — perilaku yang sama pada lapisan NumPy murni. - Baris 43–49 (Bagian D): dua objek KMeans ber-
random_state=42menghasilkan label klaster dan pusat klaster yang identik. Perhatikan sekaligus betapa pentingnya ini untuk debugging: tanpa seed, dua baris ini bisa saja menghasilkan klaster berbeda dan kita akan bingung mana yang “benar”.
Sebagai pembanding: tanpa random_state, train_test_split menarik keacakan dari keadaan global yang berubah di setiap eksekusi — pembagian data, dan tentu rata-ratanya, akan berbeda setiap kali kode dijalankan. Kita sengaja tidak mendemonstrasikannya di chapter ini, karena contoh kode Course ini harus mencetak hasil yang sama persis di setiap eksekusi; silakan coba sendiri dengan menghapus argumen random_state dan mengamati angkanya bergeser.
NumPy: Keadaan Global vs Generator Eksplisit #
NumPy menyediakan dua sistem yang perlu dibedakan. Sistem lawas memakai keadaan global: np.random.seed(42) menyetel satu pembangkit bersama untuk seluruh program, dan fungsi seperti np.random.normal() diam-diam membacanya. Masalahnya, keadaan global adalah efek samping tersembunyi — pustaka lain yang Anda impor bisa ikut menggesernya, dan urutan pemanggilan mana pun mengubah hasil pemanggilan berikutnya. Sistem modern memakai Generator eksplisit: rng = np.random.default_rng(42) menciptakan objek pembangkit mandiri yang kita pegang dan operasikan secara eksplisit lewat rng.normal() — tidak ada keadaan tersembunyi, dan dua Generator berbeda tidak saling mengganggu.
| Aspek | np.random.seed (lawas) | np.random.default_rng (modern) |
|---|---|---|
| Cakupan | Satu keadaan global untuk seluruh program | Objek mandiri yang kita pegang sendiri |
| Efek samping | Pemanggilan mana pun menggeser hasil pemanggilan lain | Tidak ada — keadaan terkunci dalam objek |
| Pengaruh pustaka lain | Bisa ikut tergeser tanpa sadar | Tidak mungkin |
| Status rekomendasi | Warisan, hanya untuk kode lama | Standar untuk kode baru |
scikit-learn menjembatani keduanya dengan santun: parameter random_state menerima bilangan bulat (seed), objek Generator, atau None (acak penuh). Menuliskan bilangan bulat adalah kebiasaan yang kita pakai konsisten di seluruh Course ini karena paling eksplisit saat dibaca.
Mengapa Ini Penting dalam Praktik #
Tiga situasi kerja membuat reproduksibilitas bukan pilihan. Pertama, debugging: ketika akurasi turun dari 0,97 menjadi 0,91, kita perlu memastikan satu-satunya hal yang berubah adalah kode yang kita edit — bukan pembagian data yang kebetulan lain. Kedua, perbandingan yang adil: klaim “model A lebih baik dari model B” hanya sah jika keduanya diuji pada pembagian data yang sama; tanpa seed, selisih akurasi bisa murni artefak pengacakan. Ketiga, audit dan kolaborasi: tim lain, auditor, atau kita sendiri enam bulan kemudian harus bisa menjalankan ulang pelatihan dan mendapatkan hasil yang tercatat.
Dengan ini section Dasar Machine Learning tuntas: istilah, mekanisme belajar, paradigma, alur kerja pustaka, overfitting, dan reproduksibilitas. Fondasi konsep sudah berdiri; pekerjaan nyata berikutnya ada pada bahan bakunya — data. Section Persiapan Data akan kita mulai dari Chapter Memuat dan Menyimpan Data.
Referensi Resmi #
- Glossary of Common Terms and Terminology — scikit-learn
— definisi resmi parameter
random_statebeserta nilai yang diterimanya. - Common pitfalls: controlling randomness — scikit-learn — panduan resmi mengendalikan keacakan di seluruh alur kerja.
- Random sampling (numpy.random) — NumPy Documentation — dokumentasi sistem Generator modern beserta rekomendasinya.
- Legacy random generation — NumPy Documentation
— penjelasan resmi keadaan global
np.random.seedwarisan dan alasan digantikan.