Chương 1·Bài 1.1

Bài toán học máy

Một model chỉ có ý nghĩa khi input của nó có sẵn lúc cần dự đoán, và output trả lời đúng câu hỏi đề đặt ra. Ba ví dụ dưới đây đi từ một bảng nhỏ, tới một baseline, rồi tới bước ghép dự đoán với đúng mã buổi.

Ví dụ 1: Chọn thông tin để chuẩn bị ghế

Mỗi hàng là một buổi sinh hoạt của câu lạc bộ. Chiều thứ Sáu, ta đã biết số người đăng ký và dự báo mưa. Số người thật sự đến thì phải sau buổi mới biết. Việc cần làm là dự đoán số người đến, còn mã buổi chỉ dùng để ghép kết quả.

Python
import pandas as pd

sessions = pd.DataFrame({
    "session_id": ["S01", "S02", "S03"],
    "registered": [20, 30, 40],
    "rain_forecast": [0.0, 0.5, 1.0],
    "attendance": [18, 23, 28],
})
features = ["registered", "rain_forecast"]
X = sessions[features]             # Bảng hai chiều: mỗi hàng là một buổi, mỗi cột là một feature.
y = sessions["attendance"]         # Target là vector một chiều, và không được nằm trong X.

print("X/y shape:", X.shape, y.shape)
print("Feature:", X.columns.tolist())
print("Nhãn nếu hỏi có ít nhất 25 người:", (y >= 25).astype(int).tolist())
assert "attendance" not in X.columns
assert "session_id" not in X.columns

Shape (3, 2) nghĩa là ba buổi, mỗi buổi có hai thông tin biết trước. Hai dòng assert ở cuối kiểm hai lỗi hay gặp nhất: để lọt chính đáp án vào input, và để lọt mã buổi vào làm feature. Nếu đổi câu hỏi thành “buổi này có ít nhất 25 người không”, nhãn trở thành 0, 0, 1. Vẫn là bảng dữ liệu ấy, nhưng bài toán đã chuyển từ regression sang classification.

Ví dụ 2: So baseline với một quy luật tuyến tính

Sáu buổi train dưới đây được dựng theo một quy luật giả định: attendance = 2 + 0.8 × registered − 6 × rain_forecast. Hai buổi validation lệch khỏi quy luật một người, để thay cho những yếu tố ta chưa ghi lại được. Câu hỏi là: dùng feature có giúp MAE giảm so với cách đoán bằng trung bình train không?

Python
import numpy as np
import pandas as pd
from sklearn.dummy import DummyRegressor
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error

train = pd.DataFrame({
    "registered": [10, 20, 30, 10, 20, 30],
    "rain_forecast": [0, 0, 0, 1, 1, 1],
    "attendance": [10, 18, 26, 4, 12, 20],
})
valid = pd.DataFrame({
    "registered": [25, 15],
    "rain_forecast": [0.5, 0.5],
    "attendance": [20, 10],
})
columns = ["registered", "rain_forecast"]
models = {"mean": DummyRegressor(), "linear": LinearRegression()}
valid_errors = {}
for name, model in models.items():
    model.fit(train[columns], train["attendance"])
    prediction = model.predict(valid[columns])
    valid_errors[name] = mean_absolute_error(valid["attendance"], prediction)
    print(f"{name}: prediction={np.round(prediction, 2).tolist()}, MAE={valid_errors[name]:.2f} người")

chosen = min(valid_errors, key=valid_errors.get)
print("Chọn bằng validation:", chosen)
print("Hệ số linear:", np.round(models["linear"].coef_, 2).tolist())

Trung bình của sáu target trong train là 15, nên baseline đoán cả hai buổi là 15, và sai trung bình 5 người. Linear đoán 19 và 11, mỗi buổi chỉ lệch 1 người. Hai hệ số 0.8 và −6 khớp đúng với quy luật đã dựng: thêm 10 người đăng ký thì có thêm khoảng 8 người đến, còn trời chắc chắn mưa thì bớt 6 người. Baseline luôn là việc nên làm đầu tiên, vì nó cho ta một mốc để biết model có thật sự giúp gì không. Ở đây kết quả chỉ kiểm quy trình trên dữ liệu giả lập, chưa nói gì về một câu lạc bộ thật.

Ví dụ 3: Dự đoán đúng, nhưng ghép nhầm mã buổi

Dữ liệu mới có các mã theo thứ tự S09, S07, S08, chứ không tăng dần. Một model học từ bốn buổi nhỏ sẽ dự đoán cho cả ba buổi, lần này không có target. Sau đó ta kiểm file CSV, và thử xem một lỗi sắp xếp làm hỏng kết quả ra sao.

Python
from io import StringIO
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression

train = pd.DataFrame({"registered": [10, 20, 30, 40]})
y_train = np.array([9, 17, 25, 33])
model = LinearRegression().fit(train, y_train)
new = pd.DataFrame({
    "session_id": ["S09", "S07", "S08"],
    "registered": [35, 15, 25],
})
prediction = np.round(model.predict(new[["registered"]]), 2)
result = pd.DataFrame({"session_id": new["session_id"], "attendance": prediction})
read_back = pd.read_csv(StringIO(result.to_csv(index=False)))
assert read_back.columns.tolist() == ["session_id", "attendance"]
assert read_back["session_id"].tolist() == new["session_id"].tolist()
assert read_back["session_id"].is_unique
assert np.isfinite(read_back["attendance"]).all()
print(read_back.to_string(index=False))

wrong = result.copy()
wrong["attendance"] = np.sort(prediction)  # Số hàng vẫn đúng, nhưng mỗi dự đoán đã sang tay buổi khác.
print("Ghép đúng sau khi sort riêng prediction:", wrong.equals(result))

Buổi S09 có 35 người đăng ký, nên phải nhận dự đoán 29 người. Nếu sắp xếp riêng cột dự đoán, nó lại nhận 13 người, vốn là của S07. File vẫn đủ hàng, đủ cột, không có lỗi nào được báo, nhưng điểm sẽ tệ đi rất nhiều. Kiểm shape và tên cột là cần, nhưng chưa đủ, ta còn phải kiểm mỗi ID có đi cùng đúng dự đoán của nó không. Khi cần sắp theo ID, hãy sắp cả hàng của bảng kết quả, đừng sắp riêng một cột.

Mục lục