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:



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:

ModelMAEMSERMSE
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