- สร้าง ML API ด้วย Scikit-learn และ FastAPI ตั้งแต่ Train Model จนถึง Docker
- 1. เตรียม Environment
- 2. โครงสร้างโปรเจกต์
- 3. Dataset ที่ใช้ในตัวอย่าง
- 4. Train Machine Learning Model
- 5. ทำไมต้อง Save Model
- 6. สร้าง FastAPI Application
- 7. อธิบาย Input Schema
- 8. Response Schema
- 9. เปิด API Server
- 10. Swagger UI
- 11. ทดสอบ API ด้วย cURL
- 12. Prediction Flow
- 13. ใช้ Scikit-learn Pipeline
- 14. เพิ่ม Validation ให้ Input
- 15. เพิ่ม Health Check
- 16. เพิ่ม Model Information Endpoint
- 17. Batch Prediction
- 18. Dockerize ML API
- 19. เพิ่ม .dockerignore
- 20. ตัวอย่าง requirements.txt
- 21. การเชื่อมต่อจาก JavaScript
- 22. Architecture เมื่อใช้ร่วมกับ Web Application
- 23. แยก Training Service และ Prediction Service
- 24. Model Versioning
- 25. อย่า Load Model ทุก Request
- 26. Security Considerations
- 27. Production Considerations
- 28. ML API กับแนวคิด MLOps
- 29. ตัวอย่าง CI/CD
- 30. แนวทางทดสอบ ML API
- 31. Workflow สำหรับ Workshop
- 32. สรุป
#สร้าง ML API ด้วย Scikit-learn และ FastAPI ตั้งแต่ Train Model จนถึง Docker
Machine Learning Model ที่ผ่านการ Train แล้วจะมีประโยชน์มากขึ้นเมื่อสามารถนำไปใช้งานร่วมกับระบบอื่นได้ เช่น Web Application, Mobile Application, Backend Service หรือระบบภายในองค์กร
แนวทางที่นิยมคือการนำ Machine Learning Model มาเปิดให้บริการผ่าน REST API เพื่อให้ระบบอื่นส่งข้อมูลเข้ามาและรับผลการทำนายกลับไป
บทความนี้จะแสดงขั้นตอนการสร้าง ML API ด้วย Scikit-learn และ FastAPI แบบครบวงจร ตั้งแต่
- เตรียมข้อมูล
- Train Machine Learning Model
- ประเมินผลโมเดล
- บันทึกโมเดลด้วย
joblib - โหลดโมเดลเข้าสู่ FastAPI
- สร้าง Endpoint สำหรับ Prediction
- ทดสอบผ่าน Swagger UI
- ทดสอบด้วย cURL
- สร้าง Docker Image
- แนวทางปรับปรุงสำหรับ Production
ตัวอย่างใช้ Dataset ยอดนิยมคือ Iris Dataset และสร้างโมเดลด้วย RandomForestClassifier
#ภาพรวม Architecture
แนวคิดพื้นฐานของ ML API สามารถสรุปได้ดังนี้
Dataset
↓
Data Preparation
↓
Scikit-learn
↓
Train Model
↓
Evaluate Model
↓
Save Model
↓
model.joblib
↓
FastAPI
↓
REST API
↓
Client Application
เมื่อระบบพร้อมใช้งาน Client สามารถส่งข้อมูลผ่าน HTTP Request
Web / Mobile / Backend
↓
POST /predict
↓
FastAPI
↓
Scikit-learn Model
↓
Prediction
↓
JSON Response
#1. เตรียม Environment
ควรแยก Python Environment ของโปรเจกต์ออกจากระบบหลักด้วย Virtual Environment
สร้างโฟลเดอร์โปรเจกต์
mkdir ml-api
cd ml-api
สร้าง Virtual Environment
python -m venv .venv
บน macOS หรือ Linux
source .venv/bin/activate
บน Windows
.venv\Scripts\activate
ติดตั้ง Library ที่ต้องใช้
pip install scikit-learn fastapi uvicorn joblib numpy
สามารถสร้างไฟล์ requirements.txt
scikit-learn
fastapi
uvicorn
joblib
numpy
จากนั้นติดตั้งด้วย
pip install -r requirements.txt
#2. โครงสร้างโปรเจกต์
โครงสร้างตัวอย่าง
ml-api/
├── app/
│ ├── __init__.py
│ └── main.py
├── model/
│ └── iris_model.joblib
├── train.py
├── requirements.txt
└── Dockerfile
แบ่งหน้าที่ออกเป็น
train.pyสำหรับ Train และ Save Modelmodel/สำหรับเก็บ Model Artifactapp/main.pyสำหรับสร้าง APIrequirements.txtสำหรับ DependencyDockerfileสำหรับ Containerization
#3. Dataset ที่ใช้ในตัวอย่าง
Scikit-learn มี Iris Dataset มาให้ใช้งานโดยตรง
Dataset มีข้อมูลดอก Iris จำนวน 3 Class ได้แก่
0 = setosa
1 = versicolor
2 = virginica
Input Feature มี 4 ค่า
sepal length
sepal width
petal length
petal width
ตัวอย่างข้อมูล
5.1, 3.5, 1.4, 0.2
เป้าหมายคือให้ Model ทำนายว่าข้อมูลดังกล่าวเป็น Iris ชนิดใด
#4. Train Machine Learning Model
สร้างไฟล์
train.py
และเพิ่มโค้ดดังนี้
from pathlib import Path
import joblib
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
# Load dataset
iris = load_iris()
X = iris.data
y = iris.target
# Split dataset
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y
)
# Create model
model = RandomForestClassifier(
n_estimators=100,
random_state=42
)
# Train model
model.fit(X_train, y_train)
# Evaluate model
y_pred = model.predict(X_test)
accuracy = accuracy_score(
y_test,
y_pred
)
print(f"Accuracy: {accuracy:.4f}")
# Create model directory
Path("model").mkdir(
exist_ok=True
)
# Save model
joblib.dump(
model,
"model/iris_model.joblib"
)
print("Model saved successfully")
รัน Training
python train.py
ตัวอย่างผลลัพธ์
Accuracy: 0.9667
Model saved successfully
หลังจากรันจะได้ไฟล์
model/iris_model.joblib
ไฟล์นี้คือ Machine Learning Model ที่พร้อมนำไปใช้ใน API
#5. ทำไมต้อง Save Model
ในการใช้งานจริง เราไม่ควร Train Model ใหม่ทุกครั้งที่ Client เรียก API
กระบวนการควรแยกเป็นสอง Phase
Training Phase
Dataset
↓
Train Model
↓
model.joblib
และ
Serving Phase
model.joblib
↓
Load Model
↓
API
↓
Prediction
ข้อดีคือ
- ลดเวลาในการตอบสนอง
- ไม่ต้อง Train ซ้ำ
- Version Model ได้
- Deploy Model ได้อิสระจาก Training Pipeline
#6. สร้าง FastAPI Application
สร้างไฟล์
app/main.py
เพิ่มโค้ด
from pathlib import Path
import joblib
import numpy as np
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(
title="Iris ML API",
description="Machine Learning API using Scikit-learn",
version="1.0.0"
)
MODEL_PATH = Path(
"model/iris_model.joblib"
)
model = joblib.load(
MODEL_PATH
)
CLASS_NAMES = [
"setosa",
"versicolor",
"virginica"
]
class IrisInput(BaseModel):
sepal_length: float
sepal_width: float
petal_length: float
petal_width: float
class PredictionResponse(BaseModel):
prediction: int
class_name: str
probability: float
@app.get("/")
def root():
return {
"message": "Iris Machine Learning API"
}
@app.get("/health")
def health():
return {
"status": "ok"
}
@app.post(
"/predict",
response_model=PredictionResponse
)
def predict(data: IrisInput):
features = np.array([
[
data.sepal_length,
data.sepal_width,
data.petal_length,
data.petal_width
]
])
prediction = model.predict(
features
)[0]
probabilities = model.predict_proba(
features
)[0]
probability = probabilities[
prediction
]
return {
"prediction": int(prediction),
"class_name": CLASS_NAMES[prediction],
"probability": float(probability)
}
#7. อธิบาย Input Schema
FastAPI ใช้ Pydantic ในการ Validate Request
class IrisInput(BaseModel):
sepal_length: float
sepal_width: float
petal_length: float
petal_width: float
Client ต้องส่ง JSON ที่มีโครงสร้างตรงกับ Schema
{
"sepal_length": 5.1,
"sepal_width": 3.5,
"petal_length": 1.4,
"petal_width": 0.2
}
หาก Client ส่งข้อมูลไม่ครบ FastAPI จะตอบ Validation Error โดยอัตโนมัติ
#8. Response Schema
กำหนด Response ด้วย
class PredictionResponse(BaseModel):
prediction: int
class_name: str
probability: float
ตัวอย่าง Response
{
"prediction": 0,
"class_name": "setosa",
"probability": 1.0
}
ทำให้ API มี Contract ที่ชัดเจน
#9. เปิด API Server
รันด้วย Uvicorn
uvicorn app.main:app --reload
Server จะทำงานที่
http://127.0.0.1:8000
ทดสอบ Root Endpoint
http://127.0.0.1:8000
ผลลัพธ์
{
"message": "Iris Machine Learning API"
}
#10. Swagger UI
FastAPI สร้าง Interactive API Documentation ให้อัตโนมัติ
เปิด
http://127.0.0.1:8000/docs
จะพบ Swagger UI
สามารถเลือก
POST /predict
จากนั้นกด
Try it out
ใส่ข้อมูล
{
"sepal_length": 5.1,
"sepal_width": 3.5,
"petal_length": 1.4,
"petal_width": 0.2
}
กด Execute
ตัวอย่าง Response
{
"prediction": 0,
"class_name": "setosa",
"probability": 1.0
}
#11. ทดสอบ API ด้วย cURL
สามารถทดสอบจาก Command Line
curl -X POST \
http://127.0.0.1:8000/predict \
-H "Content-Type: application/json" \
-d '{
"sepal_length": 5.1,
"sepal_width": 3.5,
"petal_length": 1.4,
"petal_width": 0.2
}'
ตัวอย่างผลลัพธ์
{
"prediction": 0,
"class_name": "setosa",
"probability": 1.0
}
#12. Prediction Flow
เมื่อ Client เรียก
POST /predict
กระบวนการทำงานคือ
JSON Request
↓
Pydantic Validation
↓
Convert to NumPy Array
↓
model.predict()
↓
model.predict_proba()
↓
Build JSON Response
↓
Return Prediction
#13. ใช้ Scikit-learn Pipeline
สำหรับงานจริงควรใช้ Pipeline แทนการแยก Preprocessing ออกจาก Model
ตัวอย่าง Logistic Regression พร้อม StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
pipeline = Pipeline([
(
"scaler",
StandardScaler()
),
(
"classifier",
LogisticRegression()
)
])
pipeline.fit(
X_train,
y_train
)
บันทึก Pipeline
joblib.dump(
pipeline,
"model/model.joblib"
)
ข้อดีคือ Preprocessing จะติดไปกับ Model
Input
↓
StandardScaler
↓
LogisticRegression
↓
Prediction
ช่วยลดปัญหา
Training-Serving Skew
ซึ่งเกิดจากขั้นตอน Preprocessing ตอน Train ไม่ตรงกับตอนใช้งานจริง
#14. เพิ่ม Validation ให้ Input
สามารถกำหนด Constraint เพิ่มเติมด้วย Pydantic
from pydantic import BaseModel, Field
class IrisInput(BaseModel):
sepal_length: float = Field(
gt=0
)
sepal_width: float = Field(
gt=0
)
petal_length: float = Field(
gt=0
)
petal_width: float = Field(
gt=0
)
หากส่งค่าติดลบ เช่น
{
"sepal_length": -5,
"sepal_width": 3.5,
"petal_length": 1.4,
"petal_width": 0.2
}
FastAPI จะ Reject Request ให้อัตโนมัติ
#15. เพิ่ม Health Check
Health Check มีประโยชน์มากในระบบ Production
@app.get("/health")
def health():
return {
"status": "ok"
}
ระบบภายนอกสามารถตรวจสอบ
GET /health
เช่น
- Docker
- Kubernetes
- Load Balancer
- Monitoring System
#16. เพิ่ม Model Information Endpoint
สามารถสร้าง Endpoint สำหรับดูข้อมูล Model
@app.get("/model-info")
def model_info():
return {
"model": "RandomForestClassifier",
"version": "1.0.0",
"dataset": "Iris"
}
ช่วยให้ตรวจสอบได้ว่า API กำลังใช้ Model Version ใด
#17. Batch Prediction
บางระบบต้องการส่งหลาย Record พร้อมกัน
สามารถสร้าง Schema
from typing import List
class BatchRequest(BaseModel):
items: List[IrisInput]
ตัวอย่าง Endpoint
@app.post("/predict/batch")
def predict_batch(
request: BatchRequest
):
features = [
[
item.sepal_length,
item.sepal_width,
item.petal_length,
item.petal_width
]
for item in request.items
]
predictions = model.predict(
features
)
return {
"predictions": [
{
"prediction": int(pred),
"class_name": CLASS_NAMES[pred]
}
for pred in predictions
]
}
Request ตัวอย่าง
{
"items": [
{
"sepal_length": 5.1,
"sepal_width": 3.5,
"petal_length": 1.4,
"petal_width": 0.2
},
{
"sepal_length": 6.7,
"sepal_width": 3.1,
"petal_length": 4.7,
"petal_width": 1.5
}
]
}
#18. Dockerize ML API
สร้างไฟล์
Dockerfile
ตัวอย่าง
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install \
--no-cache-dir \
-r requirements.txt
COPY . .
EXPOSE 8000
CMD [
"uvicorn",
"app.main:app",
"--host",
"0.0.0.0",
"--port",
"8000"
]
Build Docker Image
docker build \
-t sklearn-api .
รัน Container
docker run \
-p 8000:8000 \
sklearn-api
จากนั้นเปิด
http://localhost:8000/docs
#19. เพิ่ม .dockerignore
สร้าง
.dockerignore
เนื้อหา
.venv
__pycache__
*.pyc
.git
.gitignore
README.md
ช่วยลดขนาด Docker Build Context
#20. ตัวอย่าง requirements.txt
fastapi
uvicorn
scikit-learn
joblib
numpy
สำหรับ Production ควร Pin Version เช่น
fastapi==0.116.1
uvicorn==0.35.0
scikit-learn==1.7.1
joblib==1.5.1
numpy==2.3.2
อย่างไรก็ตาม Version ควรตรวจสอบและกำหนดตาม Environment ที่ใช้จริง
#21. การเชื่อมต่อจาก JavaScript
Frontend สามารถเรียก ML API ด้วย fetch
const response = await fetch(
"http://localhost:8000/predict",
{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
sepal_length: 5.1,
sepal_width: 3.5,
petal_length: 1.4,
petal_width: 0.2
})
}
);
const result = await response.json();
console.log(result);
ตัวอย่างผลลัพธ์
{
prediction: 0,
class_name: "setosa",
probability: 1.0
}
#22. Architecture เมื่อใช้ร่วมกับ Web Application
ตัวอย่าง
React / Next.js
↓
REST API
↓
FastAPI
↓
Scikit-learn Pipeline
↓
Prediction
ถ้ามี Database
Frontend
↓
FastAPI
├── PostgreSQL
└── ML Model
#23. แยก Training Service และ Prediction Service
สำหรับ Production Architecture ควรแยกหน้าที่
Training Pipeline
Dataset
↓
Data Cleaning
↓
Feature Engineering
↓
Model Training
↓
Evaluation
↓
Model Registry
และ
Prediction Service
Client
↓
API Gateway
↓
FastAPI
↓
Model Artifact
↓
Prediction
ข้อดีคือ Training และ Serving Scale แยกกันได้
#24. Model Versioning
ควร Version Model เช่น
model/
├── iris-v1.joblib
├── iris-v2.joblib
└── iris-v3.joblib
หรือกำหนดผ่าน Environment Variable
MODEL_PATH=model/iris-v2.joblib
ใน Python
import os
MODEL_PATH = os.getenv(
"MODEL_PATH",
"model/iris-v1.joblib"
)
ช่วยให้เปลี่ยน Model ได้โดยไม่แก้ Source Code
#25. อย่า Load Model ทุก Request
ตัวอย่างที่ไม่ควรทำ
@app.post("/predict")
def predict(data: IrisInput):
model = joblib.load(
"model/model.joblib"
)
prediction = model.predict(...)
return prediction
เพราะทุก Request ต้องอ่านไฟล์ Model ใหม่
แนวทางที่เหมาะสมคือ Load ครั้งเดียวตอน Application Startup
model = joblib.load(
"model/model.joblib"
)
จากนั้น Reuse Model ทุก Request
#26. Security Considerations
ไฟล์ joblib หรือ pickle ไม่ควรถูกโหลดจากแหล่งที่ไม่น่าเชื่อถือ
เนื่องจาก Serialized Python Object อาจมีโค้ดอันตรายฝังอยู่ได้
แนวทางคือ
โหลดเฉพาะ Model Artifact
ที่สร้างโดย Pipeline ขององค์กร
และผ่านกระบวนการตรวจสอบแล้ว
ไม่ควรรับไฟล์ Model จาก User แล้วโหลดด้วย joblib.load() โดยตรง
#27. Production Considerations
เมื่อ Deploy จริง ควรพิจารณาเรื่องต่อไปนี้
#Input Validation
ตรวจสอบ
Data Type
Range
Missing Values
Unexpected Values
#Error Handling
ควรมี Standard Error Response
{
"error": "prediction_failed",
"message": "Unable to generate prediction"
}
#Logging
ควร Log ข้อมูล เช่น
timestamp
request_id
model_version
latency
prediction
แต่ควรระวังไม่ให้ Log ข้อมูลส่วนบุคคลหรือ Sensitive Data โดยไม่จำเป็น
#Monitoring
ติดตาม
Request Count
Latency
Error Rate
Prediction Distribution
Model Drift
Data Drift
#28. ML API กับแนวคิด MLOps
เมื่อระบบเริ่มใหญ่ขึ้น Workflow จะเปลี่ยนจาก
Train
↓
Save Model
↓
API
ไปเป็น
Data
↓
Training Pipeline
↓
Experiment Tracking
↓
Model Evaluation
↓
Model Registry
↓
Deployment
↓
Monitoring
↓
Retraining
เครื่องมือที่สามารถนำมาต่อยอดได้ เช่น
MLflow
DVC
Docker
GitHub Actions
Kubernetes
Prometheus
Grafana
#29. ตัวอย่าง CI/CD
สามารถใช้ GitHub Actions
Git Push
↓
Run Unit Test
↓
Train / Validate Model
↓
Build Docker Image
↓
Push Container Registry
↓
Deploy
สำหรับงานจริงควรหลีกเลี่ยงการ Train Model ในทุก Commit หาก Dataset หรือ Training Process ใช้เวลานาน และควรแยก Training Pipeline ออกจาก Application Deployment Pipeline ตามความเหมาะสม
#30. แนวทางทดสอบ ML API
ควรทดสอบอย่างน้อย 3 ระดับ
Unit Test
API Test
Model Validation Test
ตัวอย่างสิ่งที่ควรตรวจสอบ
API ตอบ HTTP 200 หรือไม่
Input Validation ทำงานหรือไม่
Prediction Format ถูกต้องหรือไม่
Model สามารถ Load ได้หรือไม่
Probability อยู่ระหว่าง 0-1 หรือไม่
Health Check ทำงานหรือไม่
#31. Workflow สำหรับ Workshop
สามารถใช้บทความนี้ทำ Workshop ตามลำดับ
Create Project
↓
Install Dependencies
↓
Load Dataset
↓
Train Model
↓
Evaluate Model
↓
Save Model
↓
Create FastAPI
↓
Create /predict
↓
Test Swagger
↓
Test cURL
↓
Docker Build
↓
Docker Run
#32. สรุป
การสร้าง ML API ด้วย Scikit-learn ไม่ได้ซับซ้อน หากแยกขั้นตอนออกเป็น
Training
Serving
Deployment
Scikit-learn ทำหน้าที่ด้าน
Data Processing
Model Training
Prediction
FastAPI ทำหน้าที่
REST API
Input Validation
API Documentation
HTTP Communication
Docker ทำหน้าที่
Packaging
Environment Isolation
Deployment
ดังนั้น Architecture ที่เหมาะสมจะเป็น
Client Application
↓
FastAPI
↓
Input Validation
↓
Scikit-learn Pipeline
↓
ML Prediction
↓
JSON Response
เมื่อพัฒนาต่อไปสามารถเพิ่ม
Model Versioning
Experiment Tracking
CI/CD
Monitoring
Model Registry
Data Drift Detection
Automatic Retraining
ซึ่งเป็นพื้นฐานสำคัญของการพัฒนา Machine Learning System และ MLOps ในระดับ Production