Skip to main content

Machine Learning Regression hingga Menjadi REST API dengan FastAPI

· 36 min read
Ucup TopekoX
Ucup TopekoX
TimposuLabs creator

Materi ini membahas proses membangun aplikasi Machine Learning Regression secara end-to-end, mulai dari pengolahan dataset menggunakan Jupyter Notebook sampai model dapat digunakan sebagai REST API dan dijalankan pada VPS.

Studi kasus yang digunakan adalah prediksi harga rumah berdasarkan:

  • Luas rumah
  • Jumlah kamar
  • Jarak ke kota
  • Kondisi rumah

Teknologi yang digunakan:

Python
Pandas
NumPy
Matplotlib
Scikit-learn
Joblib
Jupyter Notebook
FastAPI
Uvicorn
PM2
Nginx
VPS

Alur keseluruhan project:

Dataset
↓
Jupyter Notebook
↓
Data Exploration
↓
Data Cleaning
↓
Visualisasi
↓
Preprocessing
↓
Train-Test Split
↓
Membandingkan Algoritma
↓
Evaluasi
↓
Memilih Model
↓
Menyimpan Model
↓
FastAPI
↓
Testing API
↓
Upload ke VPS
↓
Uvicorn
↓
PM2
↓
Nginx
↓
HTTPS
↓
Production API

1. Apa Itu Machine Learning Regression?​

Regression adalah salah satu jenis supervised learning yang digunakan untuk memprediksi nilai numerik.

Contohnya:

Luas rumah → Harga rumah
Luas tanah → Harga tanah
Umur kendaraan → Harga kendaraan
Jam belajar → Nilai ujian
Jumlah penduduk → Konsumsi listrik

Misalnya kita mempunyai data:

LuasKamarJarak KotaKondisiHarga
80210sedang425000000
12035baik750000000
15043baik1100000000

Model belajar dari hubungan antara:

Feature
↓
Harga

Setelah model selesai dilatih, kita dapat memberikan rumah baru:

Luas = 120
Kamar = 3
Jarak kota = 5
Kondisi = baik

dan model menghasilkan:

Prediksi harga = Rp xxx.xxx.xxx

2. Regression vs Classification​

Regression dan classification sama-sama dapat menggunakan supervised learning, tetapi target yang diprediksi berbeda.

Regression​

Output berupa angka.

Prediksi harga
Prediksi suhu
Prediksi pendapatan
Prediksi penjualan

Contoh:

Rp 750.000.000

Classification​

Output berupa kategori.

Spam
Tidak Spam

atau:

Baik
Sedang
Buruk

Secara sederhana:

Target angka → Regression
Target kategori → Classification

3. Studi Kasus Prediksi Harga Rumah​

Kita akan membuat model:

Input
├── luas
├── jumlah_kamar
├── jarak_ke_kota
└── kondisi

↓

Machine Learning

↓

Output
└── harga

Contoh data:

FeatureContoh
luas120
jumlah_kamar3
jarak_ke_kota5
kondisibaik

Target:

harga

4. Struktur Project​

Struktur project yang akan digunakan:

prediksi-harga-rumah/
│
├── regression_harga_rumah.ipynb
├── rumah.csv
├── model_forest.joblib
├── main.py
├── requirements.txt
│
└── venv/

Keterangan:

regression_harga_rumah.ipynb
→ Notebook untuk training Machine Learning

rumah.csv
→ Dataset

model_forest.joblib
→ Model Machine Learning yang sudah dilatih

main.py
→ FastAPI

requirements.txt
→ Daftar dependency

venv/
→ Python virtual environment

5. Menyiapkan Environment Python​

Sebaiknya setiap project mempunyai environment sendiri agar dependency tidak saling bertabrakan.

Jika menggunakan Conda:

conda create -n house_regression python=3.12

Aktifkan:

conda activate house_regression

Install library:

pip install pandas numpy matplotlib scikit-learn joblib jupyterlab

Jalankan Jupyter:

jupyter lab

Bagian Jupyter Notebook

6. Membuat Notebook​

Buat file:

regression_harga_rumah.ipynb

Notebook inilah yang akan digunakan untuk seluruh proses Machine Learning.

Tahapan notebook:

Import Library
↓
Dataset
↓
Data Inspection
↓
Data Cleaning
↓
EDA
↓
Visualisasi
↓
Feature & Target
↓
Train-Test Split
↓
Preprocessing
↓
Model Training
↓
Model Comparison
↓
Evaluation
↓
Model Selection
↓
Prediction
↓
Save Model

7. Import Library​

Pada cell pertama:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import joblib

from sklearn.model_selection import train_test_split

from sklearn.compose import ColumnTransformer

from sklearn.preprocessing import (
OneHotEncoder,
StandardScaler
)

from sklearn.pipeline import Pipeline

from sklearn.linear_model import LinearRegression

from sklearn.neighbors import KNeighborsRegressor

from sklearn.tree import DecisionTreeRegressor

from sklearn.ensemble import (
RandomForestRegressor,
GradientBoostingRegressor
)

from sklearn.metrics import (
mean_absolute_error,
mean_squared_error,
r2_score
)

from sklearn.inspection import permutation_importance

Library tersebut mempunyai fungsi berbeda.

NumPy
→ operasi numerik

Pandas
→ pengolahan dataset

Matplotlib
→ visualisasi

Scikit-learn
→ Machine Learning

Joblib
→ menyimpan model

8. Membuat Dataset Contoh​

Jika belum mempunyai dataset sendiri, kita dapat membuat dataset simulasi.

8.1 Membuat Jumlah Data​

np.random.seed(42)

jumlah_data = 500

Kita akan membuat:

500 rumah

8.2 Membuat Feature Luas​

luas = np.random.randint(
40,
250,
jumlah_data
)

Nilai luas rumah akan berada pada kisaran:

40 – 249

8.3 Membuat Jumlah Kamar​

jumlah_kamar = np.random.randint(
1,
6,
jumlah_data
)

8.4 Membuat Jarak ke Kota​

jarak_ke_kota = np.random.randint(
1,
31,
jumlah_data
)

8.5 Membuat Kondisi Rumah​

kondisi = np.random.choice(
[
"buruk",
"sedang",
"baik"
],
jumlah_data,
p=[
0.2,
0.4,
0.4
]
)

8.6 Memberikan Nilai untuk Kondisi​

Kita membuat pengaruh kondisi rumah:

nilai_kondisi = {
"buruk": -100_000_000,
"sedang": 0,
"baik": 150_000_000
}

8.7 Membuat Harga​

harga = (
luas * 5_000_000
+ jumlah_kamar * 30_000_000
- jarak_ke_kota * 8_000_000
+ np.array([
nilai_kondisi[k]
for k in kondisi
])
+ np.random.normal(
0,
60_000_000,
jumlah_data
)
)

Dalam dataset simulasi ini:

Luas lebih besar
→ harga cenderung lebih tinggi

Jumlah kamar lebih banyak
→ harga cenderung lebih tinggi

Jarak ke kota semakin jauh
→ harga cenderung lebih rendah

Kondisi baik
→ harga cenderung lebih tinggi

8.8 Membuat DataFrame​

df = pd.DataFrame({
"luas": luas,
"jumlah_kamar": jumlah_kamar,
"jarak_ke_kota": jarak_ke_kota,
"kondisi": kondisi,
"harga": harga.astype(int)
})

8.9 Melihat Data​

df.head()

8.10 Menyimpan Dataset​

df.to_csv(
"rumah.csv",
index=False
)

Jika dataset sudah tersedia, tahap pembuatan dataset dapat dilewati.


9. Membaca Dataset​

Untuk project sebenarnya:

df = pd.read_csv(
"rumah.csv"
)

Kemudian:

df.head()

10. Melihat Dimensi Dataset​

Gunakan:

df.shape

Contoh:

(500, 5)

Artinya:

500 baris
5 kolom

11. Melihat Nama Kolom​

df.columns

Hasil:

luas
jumlah_kamar
jarak_ke_kota
kondisi
harga

12. Melihat Informasi Dataset​

df.info()

Informasi ini berguna untuk mengetahui:

Jumlah baris
Nama kolom
Tipe data
Non-null value
Memory usage

13. Statistik Deskriptif​

df.describe()

Kita dapat mengetahui:

count
mean
std
min
25%
50%
75%
max

Ini membantu memahami karakteristik data numerik.


14. Memeriksa Missing Value​

df.isnull().sum()

Jika hasil:

luas 0
jumlah_kamar 0
jarak_ke_kota 0
kondisi 0
harga 0

berarti tidak terdapat missing value.


15. Memeriksa Data Duplikat​

df.duplicated().sum()

Jika ditemukan duplicate:

df = df.drop_duplicates()

16. Memeriksa Data Kategorikal​

df["kondisi"].unique()

Kemudian:

df["kondisi"].value_counts()

Kita dapat mengetahui jumlah rumah berdasarkan:

baik
sedang
buruk
tip

Misalnya kita memiliki dataset yang Missing Value (NaN) rincian:

luas 6
jumlah_kamar 6
jarak_ke_kota 6
kondisi 6
harga 0
dtype: int64

Kita dapat melakukan preprocessing dengan memasukan nilai Median:

# mengisi data Nan dengan nilai Median pada data numerik
df["luas"] = df["luas"].fillna(df["luas"].median())
df["jumlah_kamar"] = df["jumlah_kamar"].fillna(df["jumlah_kamar"].median())
df["jarak_ke_kota"] = df["jarak_ke_kota"].fillna(df["jarak_ke_kota"].median())
df["luas"] = df["luas"].fillna(df["luas"].median())

# .str.strip() -> menghilangkan spasi di awal/akhir.
# .str.lower() -> mengubah semuanya menjadi huruf kecil
df["kondisi"] = df["kondisi"].str.strip().str.lower()

# mengisi data Nan dengan nilai Median pada data string
# mode() -> mengambil nilai yang paling sering muncul (modus)
# [0] -> Hasil dari .mode() adalah sebuah Series, bukan satu nilai langsung.
# Contoh hasil: 0 baru, 1 bekas
df["kondisi"] = df["kondisi"].fillna(df["kondisi"].mode()[0])

# df["kondisi"].value_counts()

df.isna().sum() # cek data kosong

Sehingga hasilnya:

luas 0
jumlah_kamar 0
jarak_ke_kota 0
kondisi 0
harga 0
dtype: int64

Tidak terdapat lagi data yang Missing Value (NaN).


Exploratory Data Analysis

17. Mengapa EDA Penting?​

EDA atau Exploratory Data Analysis digunakan untuk memahami data sebelum melakukan training.

EDA dapat membantu menemukan:

Distribusi data
Outlier
Missing value
Hubungan antar fitur
Hubungan feature dengan target
Data yang tidak masuk akal

Jangan langsung melakukan training sebelum memahami dataset.


18. Visualisasi Distribusi Harga​

plt.figure(figsize=(8, 5))

plt.hist(
df["harga"],
bins=30
)

plt.title(
"Distribusi Harga Rumah"
)

plt.xlabel(
"Harga Rumah"
)

plt.ylabel(
"Jumlah Rumah"
)

plt.show()

Histogram menunjukkan distribusi nilai harga rumah.


19. Visualisasi Distribusi Luas​

plt.figure(figsize=(8, 5))

plt.hist(
df["luas"],
bins=25
)

plt.title(
"Distribusi Luas Rumah"
)

plt.xlabel(
"Luas Rumah"
)

plt.ylabel(
"Jumlah Rumah"
)

plt.show()

20. Scatter Plot Luas vs Harga​

plt.figure(figsize=(8, 5))

plt.scatter(
df["luas"],
df["harga"],
alpha=0.6
)

plt.title(
"Hubungan Luas Rumah dengan Harga"
)

plt.xlabel(
"Luas Rumah"
)

plt.ylabel(
"Harga Rumah"
)

plt.show()

Scatter plot membantu melihat hubungan antara:

luas
↓
harga

Jika titik cenderung bergerak naik dari kiri ke kanan, terdapat hubungan positif.

Visualisasi


21. Scatter Plot Jarak vs Harga​

plt.figure(figsize=(8, 5))

plt.scatter(
df["jarak_ke_kota"],
df["harga"],
alpha=0.6
)

plt.title(
"Hubungan Jarak ke Kota dengan Harga"
)

plt.xlabel(
"Jarak ke Kota"
)

plt.ylabel(
"Harga Rumah"
)

plt.show()

Pada dataset simulasi, semakin jauh jarak rumah dari kota, harga cenderung menurun.


22. Jumlah Kamar vs Harga​

Hitung rata-rata harga:

harga_per_kamar = (
df.groupby(
"jumlah_kamar"
)["harga"]
.mean()
)

Visualisasikan:

plt.figure(figsize=(8, 5))

harga_per_kamar.plot(
kind="bar"
)

plt.title(
"Rata-rata Harga berdasarkan Jumlah Kamar"
)

plt.xlabel(
"Jumlah Kamar"
)

plt.ylabel(
"Rata-rata Harga"
)

plt.show()

23. Kondisi Rumah vs Harga​

Hitung:

harga_kondisi = (
df.groupby(
"kondisi"
)["harga"]
.mean()
.sort_values()
)

Visualisasikan:

plt.figure(figsize=(8, 5))

harga_kondisi.plot(
kind="bar"
)

plt.title(
"Rata-rata Harga berdasarkan Kondisi Rumah"
)

plt.xlabel(
"Kondisi"
)

plt.ylabel(
"Rata-rata Harga"
)

plt.show()

24. Boxplot Harga​

Boxplot dapat membantu melihat outlier.

plt.figure(figsize=(8, 4))

plt.boxplot(
df["harga"],
vert=False
)

plt.title(
"Boxplot Harga Rumah"
)

plt.xlabel(
"Harga"
)

plt.show()

25. Correlation Matrix​

Ambil feature numerik:

numeric_df = df[
[
"luas",
"jumlah_kamar",
"jarak_ke_kota",
"harga"
]
]

Hitung korelasi:

correlation = numeric_df.corr()

correlation

Visualisasikan:

plt.figure(figsize=(8, 6))

plt.imshow(
correlation,
interpolation="nearest"
)

plt.title(
"Correlation Matrix"
)

plt.xticks(
range(len(correlation.columns)),
correlation.columns,
rotation=45
)

plt.yticks(
range(len(correlation.columns)),
correlation.columns
)

plt.colorbar()

for i in range(
len(correlation.columns)
):

for j in range(
len(correlation.columns)
):

plt.text(
j,
i,
f"{correlation.iloc[i, j]:.2f}",
ha="center",
va="center"
)

plt.tight_layout()

plt.show()

Korelasi berada pada rentang:

-1 sampai 1

Secara sederhana:

+1 → hubungan positif kuat
0 → hubungan linear lemah
-1 → hubungan negatif kuat

Namun korelasi tidak otomatis berarti hubungan sebab-akibat.


Persiapan Machine Learning

26. Menentukan Feature​

Feature adalah input yang digunakan model.

X = df[
[
"luas",
"jumlah_kamar",
"jarak_ke_kota",
"kondisi"
]
]

27. Menentukan Target​

Target adalah nilai yang ingin diprediksi.

y = df[
"harga"
]

Sehingga:

X
↓
Input

y
↓
Target

28. Train-Test Split​

Bagi dataset:

X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42
)

Artinya:

80% → training
20% → testing

Periksa:

print(
"X Train:",
X_train.shape
)

print(
"X Test:",
X_test.shape
)

print(
"y Train:",
y_train.shape
)

print(
"y Test:",
y_test.shape
)

29. Menentukan Numerical Feature​

numerical_columns = [
"luas",
"jumlah_kamar",
"jarak_ke_kota"
]

30. Menentukan Categorical Feature​

categorical_columns = [
"kondisi"
]

Preprocessing

31. One Hot Encoding​

Machine Learning tidak dapat langsung memahami:

baik
sedang
buruk

sebagai kategori teks.

Kita dapat menggunakan:

OneHotEncoder()

Contohnya:

baik
sedang
buruk

menjadi konsep:

kondisi_baik
kondisi_sedang
kondisi_buruk

Setelah One Hot Encoding:

kondisi_baik kondisi_sedang kondisi_buruk
1 0 0
0 1 0
0 0 1

32. Preprocessing untuk Model yang Membutuhkan Scaling​

Untuk model seperti Linear Regression dan KNN, kita dapat melakukan scaling terhadap feature numerik.

preprocessor_scaled = ColumnTransformer(
transformers=[
(
"numeric",
StandardScaler(),
numerical_columns
),
(
"categorical",
OneHotEncoder(
handle_unknown="ignore"
),
categorical_columns
)
]
)

33. Preprocessing untuk Tree-Based Model​

Decision Tree, Random Forest, dan Gradient Boosting tidak membutuhkan scaling numerik seperti KNN.

Kita tetap harus mengubah categorical feature.

preprocessor_tree = ColumnTransformer(
transformers=[
(
"categorical",
OneHotEncoder(
handle_unknown="ignore"
),
categorical_columns
)
],
remainder="passthrough"
)

Membandingkan Algoritma Regression

34. Mengapa Membandingkan Algoritma?​

Jangan langsung menganggap:

Random Forest pasti terbaik.

Setiap dataset dapat menghasilkan performa yang berbeda.

Karena itu kita akan mencoba:

Linear Regression
KNN Regression
Decision Tree Regression
Random Forest Regression
Gradient Boosting Regression

Kemudian membandingkan hasilnya.


35. Linear Regression​

Linear Regression merupakan algoritma regression yang mencoba memodelkan hubungan linear.

Secara sederhana:

y = b0 + b1x1 + b2x2 + ...

Pipeline:

linear_model = Pipeline(
steps=[
(
"preprocessor",
preprocessor_scaled
),
(
"regressor",
LinearRegression()
)
]
)

Kelebihan:

Sederhana
Cepat
Mudah dipahami
Bagus sebagai baseline

Kekurangan:

Kurang fleksibel terhadap pola non-linear

36. KNN Regression​

KNN menggunakan data yang paling dekat untuk melakukan prediksi.

knn_model = Pipeline(
steps=[
(
"preprocessor",
preprocessor_scaled
),
(
"regressor",
KNeighborsRegressor(
n_neighbors=5
)
)
]
)

KNN sangat dipengaruhi oleh jarak.

Karena itu scaling penting.

Kelebihan:

Sederhana
Dapat menangkap pola non-linear

Kekurangan:

Sensitif terhadap skala
Prediction dapat lambat pada dataset besar
Pemilihan K berpengaruh

37. Decision Tree Regression​

Decision Tree membagi data berdasarkan kondisi tertentu.

Konsep sederhananya:

Apakah luas > 100?
│
┌───┴───┐
Ya Tidak
│ │
↓ ↓
...

Pipeline:

decision_tree_model = Pipeline(
steps=[
(
"preprocessor",
preprocessor_tree
),
(
"regressor",
DecisionTreeRegressor(
random_state=42
)
)
]
)

Kelebihan:

Menangkap hubungan non-linear
Tidak membutuhkan scaling
Mudah dipahami

Kekurangan:

Mudah overfitting

38. Random Forest Regression​

Random Forest terdiri dari banyak Decision Tree.

Konsepnya:

Dataset
↓
Tree 1
Tree 2
Tree 3
Tree 4
...
Tree N
↓
Digabungkan
↓
Prediction

Pipeline:

random_forest_model = Pipeline(
steps=[
(
"preprocessor",
preprocessor_tree
),
(
"regressor",
RandomForestRegressor(
n_estimators=200,
random_state=42,
n_jobs=-1
)
)
]
)

Kelebihan:

Bagus untuk data tabular
Dapat menangkap pola non-linear
Lebih stabil dibanding single Decision Tree
Tidak membutuhkan scaling

Kekurangan:

Model lebih besar
Training lebih berat
Interpretasi lebih sulit

39. Gradient Boosting Regression​

Gradient Boosting membangun model secara bertahap.

Model berikutnya berusaha memperbaiki kesalahan model sebelumnya.

Pipeline:

gradient_boosting_model = Pipeline(
steps=[
(
"preprocessor",
preprocessor_tree
),
(
"regressor",
GradientBoostingRegressor(
random_state=42
)
)
]
)

Kelebihan:

Bagus untuk data tabular
Dapat menangkap pola kompleks
Sering menghasilkan performa tinggi

Kekurangan:

Lebih kompleks
Parameter lebih banyak
Dapat overfitting

Training Model

40. Membuat Dictionary Model​

models = {

"Linear Regression":
linear_model,

"KNN":
knn_model,

"Decision Tree":
decision_tree_model,

"Random Forest":
random_forest_model,

"Gradient Boosting":
gradient_boosting_model
}

41. Training Semua Model​

results = []

trained_models = {}

for name, model in models.items():

model.fit(
X_train,
y_train
)

y_pred = model.predict(
X_test
)

mae = mean_absolute_error(
y_test,
y_pred
)

mse = mean_squared_error(
y_test,
y_pred
)

rmse = np.sqrt(mse)

r2 = r2_score(
y_test,
y_pred
)

results.append({
"Model": name,
"MAE": mae,
"MSE": mse,
"RMSE": rmse,
"R2": r2
})

trained_models[name] = model

Evaluasi Regression

42. Mean Absolute Error​

MAE atau Mean Absolute Error menghitung rata-rata error absolut.

Secara sederhana:

Actual = 500 juta
Prediksi = 450 juta

Error = 50 juta

Semakin kecil:

MAE
↓
semakin baik

43. Mean Squared Error​

MSE menghitung error yang dikuadratkan.

Error
↓
Error²

Kesalahan besar akan mendapatkan penalti lebih besar.

Semakin kecil MSE:

semakin baik

44. Root Mean Squared Error​

RMSE:

RMSE = √MSE

Keuntungannya adalah satuannya kembali sama dengan target.

Jika target:

Rupiah

maka RMSE juga dapat dipahami dalam skala Rupiah.

Semakin kecil:

RMSE
↓
semakin baik

45. R² Score​

R² menunjukkan seberapa baik model menjelaskan variasi target.

Secara umum:

R² mendekati 1 → semakin baik
R² mendekati 0 → kemampuan penjelasan rendah

R² dapat bernilai negatif pada kondisi tertentu.

Semakin tinggi:

R²
↓
umumnya semakin baik

Perbandingan Model

46. Membuat DataFrame Hasil Evaluasi​

results_df = pd.DataFrame(
results
)

Urutkan berdasarkan RMSE:

results_df = results_df.sort_values(
by="RMSE"
)

Tampilkan:

results_df

Contoh struktur:

ModelMAEMSERMSER²
Linear Regression............
KNN............
Decision Tree............
Random Forest............
Gradient Boosting............

Jangan memasukkan angka hasil evaluasi secara manual dalam dokumentasi. Nilai tersebut harus berasal dari notebook ketika dataset dijalankan.


47. Visualisasi Perbandingan RMSE​

plt.figure(figsize=(10, 5))

plt.bar(
results_df["Model"],
results_df["RMSE"]
)

plt.title(
"Perbandingan RMSE Model Regression"
)

plt.xlabel(
"Model"
)

plt.ylabel(
"RMSE"
)

plt.xticks(
rotation=30,
ha="right"
)

plt.tight_layout()

plt.show()

Interpretasi:

RMSE lebih kecil
↓
error prediksi lebih kecil

48. Visualisasi Perbandingan MAE​

plt.figure(figsize=(10, 5))

plt.bar(
results_df["Model"],
results_df["MAE"]
)

plt.title(
"Perbandingan MAE Model Regression"
)

plt.xlabel(
"Model"
)

plt.ylabel(
"MAE"
)

plt.xticks(
rotation=30,
ha="right"
)

plt.tight_layout()

plt.show()

49. Visualisasi Perbandingan R²​

plt.figure(figsize=(10, 5))

plt.bar(
results_df["Model"],
results_df["R2"]
)

plt.title(
"Perbandingan R² Model Regression"
)

plt.xlabel(
"Model"
)

plt.ylabel(
"R²"
)

plt.xticks(
rotation=30,
ha="right"
)

plt.tight_layout()

plt.show()

Interpretasi:

MAE → semakin kecil semakin baik
RMSE → semakin kecil semakin baik
R² → semakin besar semakin baik

Model Selection

50. Menentukan Model Terbaik Berdasarkan RMSE​

best_row = results_df.iloc[0]

best_model_name = best_row[
"Model"
]

print(
"Model terbaik berdasarkan RMSE:",
best_model_name
)

Ambil model:

best_model = trained_models[
best_model_name
]

Namun model dengan metric terbaik tidak selalu otomatis menjadi pilihan production.

Pertimbangkan juga:

Akurasi
Ukuran model
Kecepatan inference
Kompleksitas
Interpretability
Kebutuhan aplikasi

Untuk tutorial deployment ini kita menggunakan:

Random Forest

sehingga file akhirnya:

model_forest.joblib

Evaluasi Random Forest

51. Mengambil Random Forest​

model_forest = trained_models[
"Random Forest"
]

Prediksi:

y_pred_forest = model_forest.predict(
X_test
)

52. Actual vs Prediction​

Buat DataFrame:

comparison_df = pd.DataFrame({
"Actual": y_test.values,
"Prediction": y_pred_forest
})

Lihat:

comparison_df.head(
10
)

53. Visualisasi Actual vs Prediction​

plt.figure(figsize=(7, 7))

plt.scatter(
y_test,
y_pred_forest,
alpha=0.6
)

minimum = min(
y_test.min(),
y_pred_forest.min()
)

maximum = max(
y_test.max(),
y_pred_forest.max()
)

plt.plot(
[minimum, maximum],
[minimum, maximum]
)

plt.title(
"Actual vs Predicted Price"
)

plt.xlabel(
"Harga Aktual"
)

plt.ylabel(
"Harga Prediksi"
)

plt.show()

Jika prediksi sempurna, titik akan berada dekat garis diagonal.


54. Menghitung Residual​

Residual:

Actual - Prediction

Kode:

residual = (
y_test.values
- y_pred_forest
)

55. Residual Plot​

plt.figure(figsize=(8, 5))

plt.scatter(
y_pred_forest,
residual,
alpha=0.6
)

plt.axhline(
y=0
)

plt.title(
"Residual Plot"
)

plt.xlabel(
"Predicted Price"
)

plt.ylabel(
"Residual"
)

plt.show()

Residual yang tersebar relatif acak di sekitar nol biasanya merupakan pola yang lebih baik daripada residual yang menunjukkan pola sistematis.


56. Distribusi Residual​

plt.figure(figsize=(8, 5))

plt.hist(
residual,
bins=30
)

plt.title(
"Distribusi Residual"
)

plt.xlabel(
"Residual"
)

plt.ylabel(
"Jumlah"
)

plt.show()

Feature Importance

57. Mengapa Feature Importance?​

Feature importance membantu memahami fitur mana yang berkontribusi terhadap prediksi model.

Untuk Pipeline yang kita gunakan, kita dapat menggunakan permutation importance.


58. Menghitung Permutation Importance​

importance = permutation_importance(
model_forest,
X_test,
y_test,
n_repeats=10,
random_state=42,
scoring="r2"
)

Buat DataFrame:

importance_df = pd.DataFrame({
"Feature": X_test.columns,
"Importance": importance.importances_mean
})

Urutkan:

importance_df = importance_df.sort_values(
by="Importance",
ascending=False
)

Tampilkan:

importance_df

59. Visualisasi Feature Importance​

plt.figure(figsize=(8, 5))

plt.bar(
importance_df["Feature"],
importance_df["Importance"]
)

plt.title(
"Permutation Feature Importance"
)

plt.xlabel(
"Feature"
)

plt.ylabel(
"Importance"
)

plt.xticks(
rotation=30,
ha="right"
)

plt.tight_layout()

plt.show()

Hasil ini membantu memahami feature mana yang paling memberikan kontribusi terhadap performa model pada data pengujian.


Prediction

60. Membuat Data Rumah Baru​

Misalnya kita ingin memprediksi rumah:

Luas = 120
Jumlah kamar = 3
Jarak kota = 5
Kondisi = baik

Buat DataFrame:

new_house = pd.DataFrame(
[
[
120,
3,
5,
"baik"
]
],
columns=[
"luas",
"jumlah_kamar",
"jarak_ke_kota",
"kondisi"
]
)

61. Melakukan Prediction​

prediction = model_forest.predict(
new_house
)

Tampilkan:

print(
f"Prediksi Harga Rumah: "
f"Rp {prediction[0]:,.0f}"
)

Menyimpan Model

62. Mengapa Model Harus Disimpan?​

Training tidak perlu dilakukan setiap kali API menerima request.

Prosesnya:

Training
↓
Model
↓
Simpan
↓
Production
↓
Load Model
↓
Prediction

63. Menyimpan Model dengan Joblib​

joblib.dump(
model_forest,
"model_forest.joblib"
)

File:

model_forest.joblib

sekarang berisi Pipeline:

Preprocessing
+
Random Forest

64. Menguji Model yang Disimpan​

Load:

loaded_model = joblib.load(
"model_forest.joblib"
)

Prediction:

loaded_prediction = loaded_model.predict(
new_house
)

Tampilkan:

print(
f"Prediksi: "
f"Rp {loaded_prediction[0]:,.0f}"
)

Jika berhasil, model siap digunakan oleh API.


FastAPI

65. Apa Itu FastAPI?​

FastAPI adalah framework Python untuk membangun API.

Dalam project ini FastAPI berfungsi sebagai penghubung:

Client
↓
FastAPI
↓
Machine Learning Model
↓
Prediction
↓
JSON

66. Install FastAPI​

pip install fastapi uvicorn

67. Membuat requirements.txt​

Buat:

requirements.txt

Isi:

fastapi
uvicorn[standard]
pandas
joblib
scikit-learn

Untuk deployment, sebaiknya dependency dan versinya dikunci berdasarkan environment training.

Misalnya:

pip freeze > requirements.txt

Dengan demikian environment server lebih mudah disamakan dengan environment training.


Membuat API

68. Membuat main.py​

Buat:

main.py

Gunakan kode berikut:

from typing import Literal

from fastapi import FastAPI
from pydantic import BaseModel, Field

import joblib
import pandas as pd


app = FastAPI(
title="Prediksi Harga Rumah",
description="REST API Machine Learning untuk memprediksi harga rumah"
)


model = joblib.load(
"model_forest.joblib"
)


class HouseInput(BaseModel):

luas: int = Field(gt=0)

jumlah_kamar: int = Field(gt=0)

jarak_ke_kota: int = Field(ge=0)

kondisi: Literal[
"baik",
"sedang",
"buruk"
]


@app.get("/")
def home():

return {
"message": "API Prediksi Harga Rumah aktif"
}


@app.post("/predict")
def predict(data: HouseInput):

input_df = pd.DataFrame(
[[
data.luas,
data.jumlah_kamar,
data.jarak_ke_kota,
data.kondisi
]],
columns=[
"luas",
"jumlah_kamar",
"jarak_ke_kota",
"kondisi"
]
)

prediction = model.predict(
input_df
)[0]

return {
"input": {
"luas": data.luas,
"jumlah_kamar": data.jumlah_kamar,
"jarak_ke_kota": data.jarak_ke_kota,
"kondisi": data.kondisi
},
"prediksi_harga": float(
prediction
)
}

69. Mengapa Menggunakan Pydantic?​

Bagian:

class HouseInput(BaseModel):

digunakan untuk menentukan bentuk data yang boleh diterima API.

Contohnya:

{
"luas": 120,
"jumlah_kamar": 3,
"jarak_ke_kota": 5,
"kondisi": "baik"
}

FastAPI akan melakukan validasi sebelum data diberikan kepada model.


70. Mengapa Menggunakan Literal?​

Kita hanya mengizinkan:

baik
sedang
buruk

Dengan:

Literal[
"baik",
"sedang",
"buruk"
]

Jika client mengirim:

sangat_bagus

FastAPI akan menolak request karena kategori tersebut tidak termasuk kategori yang diperbolehkan.


Menjalankan API

71. Menjalankan FastAPI Secara Lokal​

Di terminal:

uvicorn main:app --reload

Penjelasan:

main
↓
main.py

app
↓
object FastAPI

--reload cocok digunakan untuk development karena server akan melakukan reload ketika source code berubah.

Jangan menggunakan --reload untuk production.


72. Menguji Endpoint Home​

Buka:

http://127.0.0.1:8000/

Response:

{
"message": "API Prediksi Harga Rumah aktif"
}

73. Swagger Documentation​

FastAPI secara otomatis menyediakan dokumentasi.

Buka:

http://127.0.0.1:8000/docs

Di sana tersedia endpoint:

GET /
POST /predict

Klik:

Try it out

untuk melakukan testing.


74. Testing Prediction API​

Request:

{
"luas": 120,
"jumlah_kamar": 3,
"jarak_ke_kota": 5,
"kondisi": "baik"
}

Response:

{
"input": {
"luas": 120,
"jumlah_kamar": 3,
"jarak_ke_kota": 5,
"kondisi": "baik"
},
"prediksi_harga": 725631245.12
}

Angka pada prediksi_harga hanya contoh format. Nilai sebenarnya berasal dari model yang Anda latih.


75. Testing Menggunakan cURL​

curl -X POST \
"http://127.0.0.1:8000/predict" \
-H "Content-Type: application/json" \
-d '{
"luas": 120,
"jumlah_kamar": 3,
"jarak_ke_kota": 5,
"kondisi": "baik"
}'

Deployment ke VPS

76. Apa yang Akan Dipindahkan ke VPS?​

Tidak semua file dari project perlu dipindahkan.

File utama:

main.py
model_forest.joblib
requirements.txt

Notebook:

regression_harga_rumah.ipynb

tidak wajib berada di server production.

Dataset:

rumah.csv

juga tidak diperlukan jika API hanya melakukan inference menggunakan model yang sudah disimpan.


77. Login ke VPS​

Dari terminal:

ssh username@IP_VPS

Contoh:

ssh deploy@192.168.1.100

Sebaiknya aplikasi production dijalankan menggunakan user non-root.


78. Update VPS​

sudo apt update
sudo apt upgrade -y

79. Install Python​

sudo apt install python3 python3-pip python3-venv -y

Periksa:

python3 --version

Menyiapkan PM2

80. Apa Itu PM2?​

PM2 merupakan process manager yang umum digunakan pada ekosistem Node.js, tetapi dapat digunakan untuk menjalankan command seperti Uvicorn.

Dalam project ini:

PM2
↓
Uvicorn
↓
FastAPI
↓
Machine Learning

PM2 membantu:

Menjalankan process
Restart process
Melihat log
Monitoring status
Menjalankan kembali setelah reboot

81. Install Node.js dan NPM​

sudo apt install nodejs npm -y

Periksa:

node --version

dan:

npm --version

82. Install PM2​

sudo npm install -g pm2

Cek:

pm2 --version

Menyiapkan Directory Project

83. Membuat Folder Project​

sudo mkdir -p /var/www/prediksi-rumah

Berikan ownership:

sudo chown -R $USER:$USER \
/var/www/prediksi-rumah

Masuk:

cd /var/www/prediksi-rumah

Upload File ke VPS

84. Upload Menggunakan SFTP​

Untuk VPS, lebih disarankan menggunakan SFTP daripada FTP biasa karena menggunakan SSH.

Dari komputer lokal:

sftp username@IP_VPS

Masuk directory:

cd /var/www/prediksi-rumah

Upload:

put main.py

Kemudian:

put model_forest.joblib

Dan:

put requirements.txt

Keluar:

exit

85. Upload Menggunakan SCP​

Alternatif lain:

scp main.py \
username@IP_VPS:/var/www/prediksi-rumah/

Model:

scp model_forest.joblib \
username@IP_VPS:/var/www/prediksi-rumah/

Requirements:

scp requirements.txt \
username@IP_VPS:/var/www/prediksi-rumah/

Atau sekaligus:

scp \
main.py \
model_forest.joblib \
requirements.txt \
username@IP_VPS:/var/www/prediksi-rumah/

86. Upload Menggunakan FTP dari Terminal​

Jika server memang menyediakan FTP, salah satu client terminal yang dapat digunakan adalah lftp.

Install:

sudo apt install lftp

Connect:

lftp -u USERNAME,PASSWORD ftp://IP_VPS

Masuk:

cd /var/www/prediksi-rumah

Upload:

put main.py
put model_forest.joblib
put requirements.txt

Keluar:

bye

Namun plain FTP sebaiknya dihindari pada server internet karena kredensial dan transfer data tidak terenkripsi seperti SFTP.


Python Environment di VPS

87. Membuat Virtual Environment​

Masuk:

cd /var/www/prediksi-rumah

Buat:

python3 -m venv venv

Aktifkan:

source venv/bin/activate

88. Install Dependency​

Upgrade pip:

pip install --upgrade pip

Install requirements:

pip install -r requirements.txt

89. Memeriksa File​

ls

Seharusnya:

main.py
model_forest.joblib
requirements.txt
venv

Testing di VPS

90. Menjalankan Uvicorn Manual​

Sebelum menggunakan PM2, test terlebih dahulu:

venv/bin/uvicorn \
main:app \
--host 0.0.0.0 \
--port 8000

Jika tidak ada error, berarti aplikasi berhasil dijalankan.


91. Testing dari VPS​

Buka terminal lain:

curl http://127.0.0.1:8000/

Testing prediction:

curl -X POST \
"http://127.0.0.1:8000/predict" \
-H "Content-Type: application/json" \
-d '{
"luas": 120,
"jumlah_kamar": 3,
"jarak_ke_kota": 5,
"kondisi": "baik"
}'

Jika berhasil:

FastAPI
↓
Model
↓
Prediction

berarti deployment dapat dilanjutkan.

Hentikan Uvicorn:

Ctrl + C

Menjalankan dengan PM2

92. Menjalankan Uvicorn dengan PM2​

Masuk:

cd /var/www/prediksi-rumah

Kemudian:

pm2 start \
"venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000" \
--name prediksi-rumah

93. Melihat Status PM2​

pm2 status

Jika status:

online

berarti API sedang berjalan.


94. Melihat Log​

pm2 logs prediksi-rumah

Log sangat berguna ketika terjadi:

ModuleNotFoundError
FileNotFoundError
Port conflict
Model loading error

95. Restart API​

pm2 restart prediksi-rumah

96. Stop API​

pm2 stop prediksi-rumah

97. Delete Process​

pm2 delete prediksi-rumah

98. Menjalankan PM2 Setelah Reboot​

Jalankan:

pm2 startup

PM2 akan memberikan command yang perlu dijalankan.

Setelah itu:

pm2 save

Dengan demikian process yang telah disimpan dapat dipulihkan ketika server reboot.


PM2 Ecosystem File

99. Membuat ecosystem.config.js​

Agar konfigurasi lebih mudah dikelola, buat:

ecosystem.config.js

Isi:

module.exports = {
apps: [
{
name: "prediksi-rumah",

script:
"/var/www/prediksi-rumah/venv/bin/uvicorn",

args:
"main:app --host 127.0.0.1 --port 8000",

cwd:
"/var/www/prediksi-rumah",

interpreter:
"none"
}
]
};

Jalankan:

pm2 start ecosystem.config.js

Kemudian:

pm2 save

Pendekatan ini lebih mudah ketika konfigurasi aplikasi mulai bertambah.


Nginx

100. Mengapa Menggunakan Nginx?​

Kita sebenarnya dapat membuat API langsung:

IP_VPS:8000

Tetapi production biasanya lebih baik menggunakan:

Domain
↓
Nginx
↓
Uvicorn
↓
FastAPI

Keuntungannya antara lain:

Reverse proxy
HTTPS
Domain
Security
Routing

101. Install Nginx​

sudo apt install nginx -y

Periksa:

sudo systemctl status nginx

102. Membuat Konfigurasi Nginx​

Buat:

sudo nano \
/etc/nginx/sites-available/prediksi-rumah

Isi:

server {

listen 80;

server_name api.example.com;

location / {

proxy_pass http://127.0.0.1:8000;

proxy_set_header Host $host;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header X-Forwarded-Proto $scheme;
}
}

Ganti:

api.example.com

dengan domain yang digunakan.


103. Mengaktifkan Konfigurasi​

sudo ln -s \
/etc/nginx/sites-available/prediksi-rumah \
/etc/nginx/sites-enabled/

Test:

sudo nginx -t

Jika berhasil:

sudo systemctl reload nginx

HTTPS

104. Mengapa HTTPS?​

API production sebaiknya menggunakan HTTPS.

Tanpa HTTPS:

HTTP

Dengan HTTPS:

HTTPS

Data antara client dan server dienkripsi selama koneksi HTTPS.


105. Install Certbot​

sudo apt install \
certbot \
python3-certbot-nginx \
-y

106. Mengaktifkan SSL​

sudo certbot \
--nginx \
-d api.example.com

Setelah berhasil, API dapat diakses melalui:

https://api.example.com

Swagger:

https://api.example.com/docs

Firewall

107. Mengatur UFW​

Jika menggunakan Nginx:

sudo ufw allow OpenSSH

Kemudian:

sudo ufw allow 'Nginx Full'

Aktifkan:

sudo ufw enable

Periksa:

sudo ufw status

Karena Uvicorn hanya mendengarkan:

127.0.0.1:8000

port 8000 tidak perlu dibuka ke publik ketika Nginx menjadi reverse proxy.


Update Model

108. Training Model Baru​

Ketika dataset bertambah:

Dataset baru
↓
Jupyter Notebook
↓
Training
↓
Evaluasi
↓
model_forest.joblib baru

109. Upload Model Baru​

Dari komputer:

scp model_forest.joblib \
username@IP_VPS:/var/www/prediksi-rumah/

Kemudian login:

ssh username@IP_VPS

110. Restart API​

cd /var/www/prediksi-rumah

Kemudian:

pm2 restart prediksi-rumah

API sekarang menggunakan model baru.


Troubleshooting

111. Model Tidak Ditemukan​

Jika muncul:

FileNotFoundError:
model_forest.joblib

periksa:

pwd

Kemudian:

ls

Pastikan:

main.py
model_forest.joblib

berada pada directory yang benar.


112. Module Tidak Ditemukan​

Contoh:

ModuleNotFoundError:
No module named 'sklearn'

Aktifkan environment:

source venv/bin/activate

Kemudian:

pip install -r requirements.txt

113. Port 8000 Sudah Digunakan​

Periksa:

sudo ss -tulpn | grep 8000

Kemudian periksa:

pm2 status

Kemungkinan terdapat process lama yang masih berjalan.


114. API Tidak Bisa Diakses​

Periksa beberapa hal:

Uvicorn berjalan
PM2 online
Nginx aktif
Domain mengarah ke VPS
Firewall
Port

Periksa PM2:

pm2 status

Periksa log:

pm2 logs prediksi-rumah

Periksa Nginx:

sudo nginx -t

115. Error Ketika Load Joblib​

Model Machine Learning bergantung pada library dan versi tertentu.

Karena itu environment training dan production sebaiknya menggunakan dependency yang kompatibel.

Simpan dependency:

pip freeze > requirements.txt

Kemudian install dependency tersebut di VPS:

pip install -r requirements.txt

Production Best Practice

116. Jangan Menggunakan --reload​

Development:

uvicorn main:app --reload

Production:

uvicorn main:app --host 127.0.0.1 --port 8000

117. Jangan Menjalankan Aplikasi sebagai Root​

Sebaiknya gunakan user khusus deployment.

Contoh:

deploy

daripada menjalankan aplikasi sebagai:

root

118. Validasi Input​

API harus memvalidasi:

Luas > 0
Jumlah kamar > 0
Jarak >= 0
Kondisi harus kategori yang valid

Pydantic membantu melakukan validasi tersebut.


119. Gunakan HTTPS​

Production API sebaiknya menggunakan:

HTTPS

bukan hanya:

HTTP

120. Jangan Membuka Port Internal Jika Tidak Diperlukan​

Jika menggunakan:

Nginx
↓
127.0.0.1:8000

maka port 8000 tidak perlu dibuka ke internet.

Client cukup mengakses:

HTTPS
↓
Nginx

Alur Deployment Lengkap

121. Arsitektur Training​

LAPTOP

rumah.csv
↓
Jupyter Notebook
↓
Data Cleaning
↓
EDA
↓
Visualisasi
↓
Preprocessing
↓
Train-Test Split
↓
┌──────────────────────┐
│ Linear Regression │
│ KNN │
│ Decision Tree │
│ Random Forest │
│ Gradient Boosting │
└──────────────────────┘
↓
Evaluation
↓
Comparison
↓
Model Selection
↓
model_forest.joblib

122. Arsitektur Production​

INTERNET
↓
HTTPS
↓
Nginx
↓
127.0.0.1:8000
↓
Uvicorn
↓
FastAPI
↓
Pydantic Validation
↓
Pandas
↓
Machine Learning
Pipeline
↓
Random Forest
↓
Prediction
↓
JSON

Checklist Project

123. Checklist Jupyter Notebook​

[ ] Dataset tersedia
[ ] Data berhasil dibaca
[ ] Data berhasil diperiksa
[ ] Missing value diperiksa
[ ] Duplicate diperiksa
[ ] EDA dilakukan
[ ] Visualisasi dibuat
[ ] Feature ditentukan
[ ] Target ditentukan
[ ] Train-test split
[ ] Preprocessing
[ ] Linear Regression
[ ] KNN
[ ] Decision Tree
[ ] Random Forest
[ ] Gradient Boosting
[ ] MAE
[ ] MSE
[ ] RMSE
[ ] R²
[ ] Perbandingan model
[ ] Actual vs Prediction
[ ] Residual analysis
[ ] Feature importance
[ ] Prediction data baru
[ ] Model disimpan

124. Checklist FastAPI​

[ ] FastAPI terinstall
[ ] requirements.txt tersedia
[ ] model_forest.joblib tersedia
[ ] main.py dibuat
[ ] Pydantic validation
[ ] Endpoint /
[ ] Endpoint /predict
[ ] Swagger berhasil
[ ] Prediction berhasil
[ ] cURL berhasil

125. Checklist VPS​

[ ] SSH dapat digunakan
[ ] Python terinstall
[ ] Virtual environment dibuat
[ ] Dependency terinstall
[ ] File model diupload
[ ] main.py diupload
[ ] requirements.txt diupload
[ ] Uvicorn berhasil
[ ] PM2 terinstall
[ ] PM2 online
[ ] PM2 startup
[ ] pm2 save

126. Checklist Production​

[ ] Nginx terinstall
[ ] Domain mengarah ke VPS
[ ] Reverse proxy aktif
[ ] HTTPS aktif
[ ] Firewall aktif
[ ] Port internal tidak terbuka publik
[ ] API tidak menggunakan --reload
[ ] Aplikasi tidak berjalan sebagai root
[ ] Input tervalidasi
[ ] Dependency kompatibel
[ ] Log dapat diperiksa

Kesimpulan

127. Dari Notebook hingga Production API​

Dalam project ini kita belajar bahwa Machine Learning bukan hanya tentang:

model.fit()

Tetapi merupakan sebuah proses yang lebih panjang:

Dataset
↓
Data Understanding
↓
Data Cleaning
↓
EDA
↓
Visualisasi
↓
Feature Engineering / Preprocessing
↓
Train-Test Split
↓
Model Training
↓
Model Comparison
↓
Evaluation
↓
Model Selection
↓
Model Saving
↓
API
↓
Deployment
↓
Monitoring

Model yang awalnya hanya berada di:

Jupyter Notebook

akhirnya dapat digunakan oleh aplikasi lain melalui:

REST API

dengan arsitektur:

Client
↓
Nginx
↓
FastAPI
↓
Machine Learning Pipeline
↓
Random Forest
↓
Prediction

Dengan workflow ini, kita telah membangun contoh sederhana Machine Learning to Production (ML deployment).

Hal yang paling penting adalah memahami bahwa model Machine Learning yang baik bukan hanya model dengan metric bagus, tetapi model yang:

dilatih dengan benar
↓
dievaluasi dengan benar
↓
preprocessing konsisten
↓
dapat disimpan
↓
dapat digunakan kembali
↓
dapat diakses aplikasi
↓
dapat dijalankan secara stabil di server

Itulah transisi dari Machine Learning experiment menjadi Machine Learning application.

Referensi​