scikit-learn là điểm khởi đầu tự nhiên cho machine learning trong Python. Nó có giao diện nhất quán, tài liệu tốt, và bao phủ hầu hết các thuật toán cổ điển bạn cần cho những bài toán thực tế.
Quy trình chung
Gần như mọi mô hình trong scikit-learn đều tuân theo cùng một khuôn mẫu:
mo_hinh = ThuatToan() # 1. Khởi tạo
mo_hinh.fit(X_train, y_train) # 2. Huấn luyện
du_doan = mo_hinh.predict(X_test) # 3. Dự đoán
Sự nhất quán này có nghĩa là khi bạn học xong một thuật toán, việc thử thuật toán khác chỉ là đổi tên lớp.
Chuẩn bị dữ liệu
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_diabetes
du_lieu = load_diabetes()
X, y = du_lieu.data, du_lieu.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
Việc tách dữ liệu là bắt buộc. Đánh giá mô hình trên chính dữ liệu đã dùng để huấn luyện sẽ cho kết quả đẹp một cách giả tạo — mô hình chỉ đang nhớ lại chứ không thực sự học được quy luật.
Bài toán hồi quy
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score
mo_hinh = LinearRegression()
mo_hinh.fit(X_train, y_train)
du_doan = mo_hinh.predict(X_test)
print(f"Sai số tuyệt đối trung bình: {mean_absolute_error(y_test, du_doan):.2f}")
print(f"Hệ số R²: {r2_score(y_test, du_doan):.3f}")
Bài toán phân loại
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
mo_hinh = RandomForestClassifier(n_estimators=100, random_state=42)
mo_hinh.fit(X_train, y_train)
du_doan = mo_hinh.predict(X_test)
print(classification_report(y_test, du_doan))
print(confusion_matrix(y_test, du_doan))
Đừng chỉ nhìn vào độ chính xác
Với dữ liệu mất cân bằng, độ chính xác là con số đánh lừa. Giả sử bạn phát hiện gian lận và chỉ 1% giao dịch là gian lận: một mô hình luôn dự đoán “không gian lận” đạt độ chính xác 99% mà hoàn toàn vô dụng.
Trong trường hợp đó, hãy nhìn vào precision (trong số các trường hợp mô hình báo dương, bao nhiêu thực sự đúng) và recall (trong số các trường hợp thực sự dương, mô hình bắt được bao nhiêu). Chọn ưu tiên cái nào phụ thuộc vào chi phí của từng loại sai sót.
Pipeline — tránh rò rỉ dữ liệu
Đây là lỗi tinh vi mà rất nhiều người mắc phải: chuẩn hóa dữ liệu trước khi tách tập huấn luyện và tập kiểm tra.
# SAI — thông tin từ tập test rò rỉ vào quá trình huấn luyện
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # dùng cả tập test để tính trung bình
X_train, X_test = train_test_split(X_scaled, ...)
Pipeline giải quyết triệt để vấn đề này:
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
quy_trinh = Pipeline([
("chuan_hoa", StandardScaler()),
("mo_hinh", LogisticRegression(max_iter=1000)),
])
quy_trinh.fit(X_train, y_train)
quy_trinh.score(X_test, y_test)
Khi gọi fit, pipeline chỉ tính tham số chuẩn hóa từ tập huấn luyện. Khi predict, nó áp dụng đúng tham số đó cho dữ liệu mới. Không có đường nào để thông tin rò rỉ.
Xử lý dữ liệu hỗn hợp
Dữ liệu thực tế thường có cả cột số lẫn cột phân loại:
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
from sklearn.impute import SimpleImputer
cot_so = ["tuoi", "thu_nhap", "so_nam_lam_viec"]
cot_phan_loai = ["gioi_tinh", "thanh_pho", "trinh_do"]
tien_xu_ly = ColumnTransformer([
("so", Pipeline([
("dien_thieu", SimpleImputer(strategy="median")),
("chuan_hoa", StandardScaler()),
]), cot_so),
("phan_loai", OneHotEncoder(handle_unknown="ignore"), cot_phan_loai),
])
quy_trinh = Pipeline([
("tien_xu_ly", tien_xu_ly),
("mo_hinh", RandomForestClassifier(random_state=42)),
])
Tham số handle_unknown="ignore" rất quan trọng cho môi trường thật: khi gặp một giá trị chưa từng xuất hiện lúc huấn luyện, mô hình sẽ bỏ qua thay vì báo lỗi và làm sập dịch vụ.
Kiểm định chéo
Một lần tách dữ liệu có thể cho kết quả may rủi. Kiểm định chéo đánh giá đáng tin cậy hơn:
from sklearn.model_selection import cross_val_score
diem = cross_val_score(quy_trinh, X, y, cv=5, scoring="f1_macro")
print(f"F1 trung bình: {diem.mean():.3f} (±{diem.std():.3f})")
Độ lệch chuẩn cũng đáng chú ý không kém giá trị trung bình. Nếu nó lớn, mô hình của bạn nhạy cảm với cách chia dữ liệu — dấu hiệu cho thấy kết quả chưa ổn định.
Tìm siêu tham số
from sklearn.model_selection import GridSearchCV
luoi = {
"mo_hinh__n_estimators": [100, 200, 300],
"mo_hinh__max_depth": [None, 10, 20],
"mo_hinh__min_samples_split": [2, 5, 10],
}
tim_kiem = GridSearchCV(quy_trinh, luoi, cv=5, scoring="f1_macro", n_jobs=-1)
tim_kiem.fit(X_train, y_train)
print(f"Tham số tốt nhất: {tim_kiem.best_params_}")
print(f"Điểm tốt nhất: {tim_kiem.best_score_:.3f}")
Chú ý cú pháp hai dấu gạch dưới: mo_hinh__n_estimators nghĩa là tham số n_estimators của bước tên mo_hinh trong pipeline.
Lưu và dùng lại mô hình
import joblib
joblib.dump(tim_kiem.best_estimator_, "mo_hinh.joblib")
# Ở nơi khác
mo_hinh = joblib.load("mo_hinh.joblib")
ket_qua = mo_hinh.predict(du_lieu_moi)
Vì bạn lưu cả pipeline, mọi bước tiền xử lý đi kèm luôn. Dữ liệu mới chỉ cần đúng định dạng cột ban đầu, không phải chuẩn hóa thủ công.
Lời khuyên thực tế
Hãy bắt đầu bằng mô hình đơn giản nhất và coi nó là mốc so sánh. Rất nhiều bài toán được giải tốt bằng hồi quy tuyến tính hoặc cây quyết định, và một mô hình đơn giản dễ giải thích cho người dùng hơn nhiều.
Chất lượng dữ liệu quan trọng hơn việc chọn thuật toán. Thời gian bỏ ra để làm sạch dữ liệu và tạo đặc trưng tốt gần như luôn mang lại kết quả cao hơn so với thời gian dành cho việc tinh chỉnh siêu tham số.