Machine learning sering digunakan bukan hanya untuk mengenali gambar atau memprediksi angka, tetapi juga untuk membaca pola dari kebiasaan manusia. Dalam studi kasus ini, kita akan melihat bagaimana data kebiasaan makan, aktivitas fisik, dan kondisi tubuh dapat digunakan untuk memprediksi kategori risiko obesitas.
Materi ini hanya untuk edukasi machine learning dan bukan saran medis, diagnosis, atau rekomendasi gizi personal.
Dari paper ke workflow praktik.
Paper sumber membahas pemanfaatan artificial intelligence untuk membaca risiko obesitas dan menghubungkannya dengan ide meal planning. Fokus pembelajaran kita bukan menyalin hasil paper, tetapi membangun ulang workflow yang mudah diikuti: dari dataset, preprocessing, training, evaluasi, sampai interpretasi.
Masalah machine learning-nya adalah multiclass classification. Input berupa kebiasaan makan, aktivitas fisik, kondisi tubuh, dan lifestyle-related features. Output berupa kategori level obesitas. Karena konteksnya sensitif, hasil model harus dibaca sebagai eksperimen data, bukan keputusan medis.
Alur eksperimen dari data sampai insight.
- Load dataset dari URL publik
- Exploratory Data Analysis sederhana
- Encoding fitur kategorikal
- Train-test split dengan stratifikasi target
- Training Random Forest, Gradient Boosting, SVM, dan KNN
- Evaluasi accuracy, precision, recall, F1, confusion matrix, dan classification report
- Feature importance untuk membaca faktor yang paling berpengaruh
- Contoh meal planning hint yang aman sebagai output edukatif
Ubah masalah nyata menjadi task machine learning.
Kita ingin menjawab pertanyaan praktis: dari data kebiasaan dan kondisi tubuh, apakah model dapat mengenali kategori risiko obesitas dengan cukup stabil? Dalam tutorial ini target utama diambil dari kolom NObeyesdad jika tersedia, lalu setiap fitur kategorikal diubah menjadi angka agar dapat dipakai model scikit-learn.
Setup Environment
Kita memakai pandas, numpy, matplotlib, dan scikit-learn saja supaya notebook ringan dan mudah dijalankan di Colab atau Jupyter.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.metrics import (
accuracy_score,
precision_score,
recall_score,
f1_score,
classification_report,
confusion_matrix
)
import warnings
warnings.filterwarnings("ignore")
Environment siap. Tidak ada dependency tambahan seperti seaborn agar tutorial tetap sederhana.
Load Dataset
Dataset dibaca langsung dari tautan publik. Jika URL berubah, kamu bisa mengganti DATA_URL dengan file CSV lokal.
import pandas as pd
DATA_URL = "https://autotrain.app/opendata92bd.html?file=ObesityDataSet_raw_and_data_sinthetic.csv"
df = pd.read_csv(DATA_URL)
df.head()
Output yang diharapkan: beberapa baris awal berisi fitur seperti gender, age, height, weight, family history, kebiasaan konsumsi, aktivitas fisik, dan target kategori obesitas.
Data Understanding
Sebelum training, kita cek ukuran data, tipe kolom, missing value, dan distribusi target. Distribusi target penting karena data tidak selalu seimbang.
print("Shape:", df.shape)
df.info()
df.isnull().sum().sort_values(ascending=False)
target_col = "NObeyesdad" if "NObeyesdad" in df.columns else df.columns[-1]
print("Target column:", target_col)
df[target_col].value_counts()
Jika kolom NObeyesdad tidak ditemukan, kode otomatis memakai kolom terakhir sebagai target. Catatan ini penting saat dataset dari sumber lain punya nama kolom berbeda.
Visualisasi Distribusi Target
Plot batang membantu melihat apakah kelas target relatif seimbang atau ada kelas yang jauh lebih dominan.
df[target_col].value_counts().plot(kind="bar", figsize=(10, 4))
plt.title("Distribusi Kategori Obesitas")
plt.xlabel("Kategori")
plt.ylabel("Jumlah Data")
plt.xticks(rotation=45, ha="right")
plt.tight_layout()
plt.show()
Jika ada kelas yang kecil, metrik macro average menjadi penting karena setiap kelas diberi bobot perhatian yang lebih setara.
Bagian praktik lengkap: preprocessing, training, evaluasi, interpretasi, dan file download.
Ringkasan, problem framing, dataset overview, dan preview kode awal tetap bisa dibaca gratis. Untuk mengikuti workflow penuh seperti notebook praktik, gunakan Project Access.
Feature, Target, dan Encoding
Fitur kategorikal seperti kebiasaan makan atau transportasi perlu diubah menjadi angka. Label target juga diencode agar model membaca kelas sebagai integer.
target_col = "NObeyesdad" if "NObeyesdad" in df.columns else df.columns[-1]
X = df.drop(columns=[target_col])
y = df[target_col]
print("Features:", X.shape)
print("Target:", y.shape)
X_encoded = X.copy()
for col in X_encoded.columns:
if X_encoded[col].dtype == "object":
le = LabelEncoder()
X_encoded[col] = le.fit_transform(X_encoded[col].astype(str))
target_encoder = LabelEncoder()
y_encoded = target_encoder.fit_transform(y.astype(str))
print("Target classes:")
for idx, label in enumerate(target_encoder.classes_):
print(idx, "->", label)
Output target classes membantu membaca kembali arti angka prediksi, misalnya 0 -> Insufficient Weight atau 4 -> Obesity Type I.
Train Test Split
Stratifikasi menjaga proporsi target di train dan test set agar evaluasi lebih adil.
X_train, X_test, y_train, y_test = train_test_split(
X_encoded,
y_encoded,
test_size=0.2,
random_state=42,
stratify=y_encoded
)
print(X_train.shape, X_test.shape)
Sekitar 80 persen data masuk training dan 20 persen data masuk testing.
Training Beberapa Model
Kita membandingkan model ensemble dan model berbasis jarak/margin. SVM dan KNN dibungkus Pipeline dengan StandardScaler karena sensitif terhadap skala fitur.
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
models = {
"Random Forest": RandomForestClassifier(
n_estimators=200,
random_state=42,
class_weight="balanced"
),
"Gradient Boosting": GradientBoostingClassifier(
random_state=42
),
"SVM": Pipeline([
("scaler", StandardScaler()),
("model", SVC(kernel="rbf", probability=True, random_state=42))
]),
"KNN": Pipeline([
("scaler", StandardScaler()),
("model", KNeighborsClassifier(n_neighbors=5))
])
}
Model belum dilatih pada cell ini. Kita hanya menyiapkan daftar eksperimen yang akan dijalankan pada loop evaluasi.
Evaluation Loop
Akurasi saja belum cukup. Macro precision, recall, dan F1 membantu membaca performa lintas kelas.
results = []
for name, model in models.items():
model.fit(X_train, y_train)
preds = model.predict(X_test)
results.append({
"model": name,
"accuracy": accuracy_score(y_test, preds),
"precision_macro": precision_score(y_test, preds, average="macro", zero_division=0),
"recall_macro": recall_score(y_test, preds, average="macro", zero_division=0),
"f1_macro": f1_score(y_test, preds, average="macro", zero_division=0)
})
results_df = pd.DataFrame(results).sort_values(by="f1_macro", ascending=False)
results_df
results_df mengurutkan model berdasarkan F1 macro. Angka final dapat berbeda tergantung versi library, split data, dan preprocessing.
Confusion Matrix Model Terbaik
Confusion matrix memperlihatkan kelas mana yang sering tertukar. Ini sangat berguna untuk task multiclass.
best_model_name = results_df.iloc[0]["model"]
best_model = models[best_model_name]
best_preds = best_model.predict(X_test)
cm = confusion_matrix(y_test, best_preds)
fig, ax = plt.subplots(figsize=(8, 6))
im = ax.imshow(cm)
ax.set_title(f"Confusion Matrix - {best_model_name}")
ax.set_xlabel("Predicted Label")
ax.set_ylabel("True Label")
ax.set_xticks(range(len(target_encoder.classes_)))
ax.set_yticks(range(len(target_encoder.classes_)))
ax.set_xticklabels(target_encoder.classes_, rotation=45, ha="right")
ax.set_yticklabels(target_encoder.classes_)
for i in range(cm.shape[0]):
for j in range(cm.shape[1]):
ax.text(j, i, cm[i, j], ha="center", va="center")
plt.tight_layout()
plt.show()
Diagonal utama menunjukkan prediksi benar. Nilai di luar diagonal menunjukkan kelas yang tertukar.
Classification Report
Report ini memberi precision, recall, dan F1 untuk setiap kelas target.
print(classification_report(
y_test,
best_preds,
target_names=target_encoder.classes_,
zero_division=0
))
Perhatikan kelas dengan recall rendah karena berarti banyak data kelas tersebut gagal ditemukan.
Feature Importance
Untuk interpretasi tambahan, kita memakai Random Forest agar bisa melihat fitur mana yang paling sering membantu pemisahan kelas.
rf_model = models["Random Forest"]
rf_model.fit(X_train, y_train)
importance_df = pd.DataFrame({
"feature": X_encoded.columns,
"importance": rf_model.feature_importances_
}).sort_values(by="importance", ascending=False)
importance_df.head(15)
top_features = importance_df.head(15).sort_values("importance")
plt.figure(figsize=(8, 6))
plt.barh(top_features["feature"], top_features["importance"])
plt.title("Top 15 Feature Importance - Random Forest")
plt.xlabel("Importance")
plt.tight_layout()
plt.show()
Feature importance bukan bukti sebab-akibat. Ia hanya memberi sinyal fitur yang sering dipakai model untuk membuat keputusan.
Meal Planning Hint Edukatif
Bagian ini sengaja dibuat hati-hati: outputnya hanya contoh edukatif, bukan saran diet personal.
def educational_meal_planning_hint(predicted_label):
label = predicted_label.lower()
if "obesity" in label or "overweight" in label:
return {
"focus": "Porsi seimbang dan pilihan makanan padat nutrisi",
"plate_idea": "Perbanyak sayur, sumber protein tanpa lemak, karbohidrat kompleks, dan batasi makanan tinggi gula.",
"note": "Ini hanya contoh edukatif, bukan saran medis atau diet personal."
}
if "insufficient" in label:
return {
"focus": "Kecukupan energi dan nutrisi",
"plate_idea": "Pastikan asupan karbohidrat, protein, lemak sehat, dan mikronutrien cukup.",
"note": "Ini hanya contoh edukatif, bukan saran medis atau diet personal."
}
return {
"focus": "Menjaga pola makan seimbang",
"plate_idea": "Pertahankan variasi makanan, aktivitas fisik, dan kebiasaan makan yang konsisten.",
"note": "Ini hanya contoh edukatif, bukan saran medis atau diet personal."
}
sample_index = 0
sample = X_test.iloc[[sample_index]]
pred_class_id = best_model.predict(sample)[0]
pred_label = target_encoder.inverse_transform([pred_class_id])[0]
pred_label, educational_meal_planning_hint(pred_label)
Output berupa label prediksi dan dictionary berisi fokus edukatif, ide piring makan, serta catatan batasan.
Insight akhir dan batasan eksperimen.
- Model dapat membaca pola dari fitur gaya hidup, tetapi pola tersebut sangat bergantung pada kualitas dataset.
- Evaluasi lintas kelas penting karena akurasi tinggi bisa menyembunyikan kegagalan pada kelas tertentu.
- Feature importance membantu menjelaskan fitur yang berpengaruh, tetapi tidak boleh dibaca sebagai sebab-akibat medis.
- Meal planning dalam tutorial ini hanya contoh format output edukatif, bukan rekomendasi gizi personal.
File pendukung studi kasus.
Gunakan notebook untuk belajar bertahap, atau pakai source code jika ingin langsung menjalankan eksperimen di environment sendiri.
