Interaksi dan Rasio

Fitur multiplikatif yang memperjelas pola.

Daftar isi

Hubungan antara fitur dan target tidak selalu berupa penjumlahan efek yang terpisah. Dua fitur dapat saling memperkuat, dan dua besaran baru bermakna setelah saling dibagi. Topik ini membahas dua bentuk fitur multiplikatif, yaitu interaksi yang merupakan hasil kali dua fitur dan rasio yang membagi satu fitur dengan fitur lain, lengkap dengan pengaman pembagian nol, pengukuran efeknya pada skor model linear, serta pembuatan interaksi otomatis dengan PolynomialFeatures.

Interaksi menangkap efek gabungan#

Fitur interaksi adalah hasil kali dua fitur, ditulis x1⋅x2x_1 \cdot x_2, yang menyatakan bahwa pengaruh satu fitur bergantung pada nilai fitur pasangannya. Model linear tanpa interaksi hanya membentuk y^=w1x1+w2x2\hat{y} = w_1 x_1 + w_2 x_2, yaitu penjumlahan efek masing-masing fitur. Bentuk itu tidak dapat menyatakan situasi ketika efektivitas dosis pupuk bergantung pada ketersediaan air, karena efek pupuk pada tanaman kering berbeda dari efeknya pada tanaman yang terairi.

Menambahkan suku interaksi mengubah model menjadi y^=w1x1+w2x2+w12x1x2\hat{y} = w_1 x_1 + w_2 x_2 + w_{12} x_1 x_2, sehingga permukaan prediksinya dapat melengkung mengikuti pola gabungan tersebut. Suku w12x1x2w_{12} x_1 x_2 inilah yang tidak dapat ditiru oleh model linear apa pun tanpa kolom hasil kalinya. Karena itu interaksi dibuat sebagai kolom terlebih dahulu, bukan diharapkan muncul sendiri dari model.

Rasio menormalkan skala antar objek#

Fitur rasio a/ba / b mengubah jumlah total menjadi intensitas per satuan ukuran, sehingga objek yang ukurannya berbeda dapat diperbandingkan. Total pupuk pada petak seluas empat hektar dan petak seluas setengah hektar tidak dapat dibandingkan langsung, sedangkan dosis per hektar bermakna sama untuk keduanya. Bentuk per-unit semacam ini lazim pada data yang objeknya berbeda ukuran, seperti petak, gedung, atau dokumen.

Operasi pembagian memiliki bahaya yang tidak dimiliki penjumlahan, yaitu penyebut bernilai nol. Hasil bagi dengan nol berupa inf atau nan, dan nilai semacam itu membuat fit gagal dengan pesan Input X contains NaN atau menghasilkan prediksi yang tidak terdefinisi. Bentuk amannya adalah np.divide dengan argumen where, yang hanya menghitung baris dengan penyebut positif dan mengisi baris lain dari out.

python Contoh: pengaman pembagian nol dengan np.divide
import numpy as np

# petak kedua belum diukur sehingga luasnya nol
luas = np.array([2.0, 0.0, 1.5])
pupuk = np.array([80.0, 60.0, 45.0])

# np.divide dengan where hanya menghitung baris yang penyebutnya positif
rasio = np.divide(pupuk, luas, out=np.zeros_like(pupuk), where=luas > 0)
print(rasio.tolist())
text Keluaran
[40.0, 0.0, 30.0]

Baris kedua yang luasnya nol diisi nilai 0.0 alih-alih inf. Pemilihan nilai isian merupakan keputusan domain, bukan aturan bawaan, karena 0.0 berarti tanpa dosis pada petak yang belum diukur. Apabila baris dengan penyebut nol tidak seharusnya ikut dimodelkan, langkah yang lebih tepat adalah membuang baris tersebut sebelum pembagian.

Skor yang naik bertahap#

Program berikut menyusun 250 petak tanaman dengan tiga kolom asli, yaitu pupuk_kg, luas_ha, dan hujan_mm, kemudian mengukur tiga model linear yang fiturnya bertambah satu tahap demi satu tahap. Targetnya adalah hasil panen per hektar. Tahap pertama hanya menggunakan kolom asli, tahap kedua menambahkan rasio pupuk_per_ha, dan tahap ketiga menambahkan interaksi pupuk_hujan yang merupakan hasil kali rasio dengan curah hujan.

python skor-bertahap.py
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split

# 250 petak tanaman dengan dosis pupuk, curah hujan, dan luas yang berbeda
n = 250
rng = np.random.default_rng(42)
luas_ha = rng.uniform(0.5, 4.0, n)
pupuk_ha = rng.uniform(20, 160, n)
hujan_mm = rng.uniform(700, 1600, n)

df = pd.DataFrame({
    "pupuk_kg": pupuk_ha * luas_ha,
    "luas_ha": luas_ha,
    "hujan_mm": hujan_mm,
})

# hasil panen per hektar (ton) mengikuti dosis per hektar beserta interaksinya dengan hujan
df["hasil_per_ha"] = (
    2.0 + 0.020 * pupuk_ha + 0.0012 * hujan_mm + 0.00002 * pupuk_ha * hujan_mm + rng.normal(0, 0.3, n)
)

# fitur turunan: rasio menormalkan pupuk antar petak, interaksi menangkap sinerginya
df["pupuk_per_ha"] = df["pupuk_kg"] / df["luas_ha"]
df["pupuk_hujan"] = df["pupuk_per_ha"] * df["hujan_mm"]

X_train, X_test, y_train, y_test = train_test_split(
    df[["pupuk_kg", "luas_ha", "hujan_mm", "pupuk_per_ha", "pupuk_hujan"]],
    df["hasil_per_ha"],
    test_size=0.25,
    random_state=42,
)

dasar = ["pupuk_kg", "luas_ha", "hujan_mm"]
m1 = LinearRegression().fit(X_train[dasar], y_train)
m2 = LinearRegression().fit(X_train[dasar + ["pupuk_per_ha"]], y_train)
m3 = LinearRegression().fit(X_train[dasar + ["pupuk_per_ha", "pupuk_hujan"]], y_train)

print("R2 dasar (tiga kolom asli)       :", round(r2_score(y_test, m1.predict(X_test[dasar])), 3))
print("R2 ditambah rasio pupuk_per_ha   :", round(r2_score(y_test, m2.predict(X_test[dasar + ["pupuk_per_ha"]])), 3))
print("R2 ditambah interaksi pupuk_hujan:", round(r2_score(y_test, m3.predict(X_test[dasar + ["pupuk_per_ha", "pupuk_hujan"]])), 3))
text Keluaran
R2 dasar (tiga kolom asli)       : 0.827
R2 ditambah rasio pupuk_per_ha   : 0.944
R2 ditambah interaksi pupuk_hujan: 0.961

Tangga keluaran menunjukkan kontribusi masing-masing bentuk. Kolom asli saja mencapai 0.827 karena efek utamanya memang linear, tetapi model itu tidak dapat mengekspresikan hasil bagi pupuk_kg dengan luas_ha, dan rasio menutup celah itu hingga 0.944. Interaksi menaikkan skor lagi menjadi 0.961 dengan menangkap sinergi dosis dan air. Angka 0.961 mendekati batas yang ditetapkan derau 0.3 karena data ini sintetis, sedangkan pada data nyata langkah yang sama tetap bergerak ke arah yang sama dengan jarak yang lebih pendek.

Interaksi otomatis dengan PolynomialFeatures#

Menuliskan hasil kali satu per satu menjadi melelahkan apabila kandidat pasangannya banyak. PolynomialFeatures dengan interaction_only=True membuat seluruh pasangan hasil kali sekaligus tanpa suku pangkat, sehingga derajat 2 pada dua fitur menghasilkan tiga kolom, yaitu kedua fitur asli beserta hasil kalinya. Argumen include_bias=False membuang kolom konstanta yang tidak diperlukan ketika modelnya mempunyai intersep sendiri.

python Contoh: nama fitur hasil PolynomialFeatures
import pandas as pd
from sklearn.preprocessing import PolynomialFeatures

df = pd.DataFrame({"pupuk_per_ha": [40.0, 90.0], "hujan_mm": [800.0, 1400.0]})
pf = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)
X = pf.fit_transform(df)

print("Bentuk keluaran:", X.shape)
print("Nama fitur:", list(pf.get_feature_names_out(df.columns)))
text Keluaran
Bentuk keluaran: (2, 3)
Nama fitur: ['pupuk_per_ha', 'hujan_mm', 'pupuk_per_ha hujan_mm']

Keluaran menegaskan bahwa dua fitur asli bertambah satu kolom interaksi dan setiap kolomnya mempunyai nama yang dapat dibaca. Pada fitur yang jumlahnya banyak, kolom interaksi tumbuh mendekati separuh kuadrat jumlah fitur yang tersedia, sehingga pembuatannya sebaiknya dibatasi pada pasangan yang diperkirakan berinteraksi dan diletakkan di dalam Pipeline bersama scaling serta model.

Kesalahan umum#

  • Membagi tanpa pengaman pada kolom yang bisa nol. Hasil inf atau nan membuat fit gagal di tengah proses dengan pesan yang tidak selalu menunjuk ke pembagian itu. Perbaikannya dengan np.divide bersama argumen where atau dengan membuang baris yang penyebutnya nol.
  • Membuat seluruh pasangan interaksi sekaligus. Dengan dua puluh fitur muncul 190 kolom hasil kali, dan data yang kecil menjadi jenuh oleh kolom yang kebanyakan derau. Perbaikannya dengan memilih pasangan berdasarkan penalaran domain terlebih dahulu.
  • Membagi fitur yang bisa bernilai negatif. Rasio terhadap penyebut negatif membalik urutannya, sehingga nilai besar dan kecil tertukar makna. Perbaikannya dengan memastikan penyebut memang positif pada seluruh domainnya atau mengganti rasio dengan selisih.

Ringkasan#

  • Fitur interaksi berupa hasil kali dua fitur dan menangkap efek gabungan yang tidak dapat dinyatakan oleh penjumlahan linear.
  • Fitur rasio menormalkan jumlah total menjadi intensitas per satuan ukuran, dengan penyebut nol diamankan melalui np.divide bersama argumen where.
  • Pada data panen sintetis, R2R^2 naik dari 0.827 menjadi 0.944 setelah rasio ditambahkan dan 0.961 setelah interaksi ditambahkan.
  • PolynomialFeatures(degree=2, interaction_only=True) membuat seluruh pasangan hasil kali sekaligus lengkap dengan nama kolomnya.

Referensi#