🔄 Bài 33: Luyện tập — Ba bộ dữ liệu, ba bài học
Chặng 8 · Học máy thực chiến (Bài 31 → 42) · đây là bài 3/12 của chặng.
🔄 Đây là bài luyện tập — không có khái niệm nào mới. Toàn bộ công cụ bạn cần đã nằm trong Bài 31 và 32. Hôm nay chỉ đem chúng chạy trên ba bộ dữ liệu khác nhau.
🎯 Mục tiêu: tập nhìn ra khi nào kết quả của máy đáng tin và khi nào không — kỹ năng mà chỉ luyện mới có, không đọc lý thuyết mà thành.
📖 Bài này có nhịp khác: mỗi bộ dữ liệu đi theo bốn bước — nhìn dữ liệu, đoán trước khi chạy, máy trả lời, điều rút ra. Hãy thật sự dừng lại ở bước “đoán trước”, đừng đọc lướt qua. Cái sai của bạn ở bước đó mới là chỗ bài học đọng lại.
Sau bài này bạn sẽ:
- Chạy được trọn vẹn quy trình hồi quy trên một bộ dữ liệu lạ, không cần xem lại bài cũ.
- Biết rằng scikit-learn luôn trả về một đường thẳng — kể cả khi dữ liệu chẳng có quan hệ nào.
- Có một cách đơn giản để tự hỏi: “mô hình này có thật sự khá hơn đoán bừa không?”
- Nhìn thấy tận mắt một ô dữ liệu nhập sai phá hỏng cả mô hình như thế nào.
Hai bài vừa rồi bạn học ba động tác fit – predict, và cách xếp dữ liệu thành bảng. Đủ đồ nghề rồi. Hôm nay không thêm gì cả — chỉ mang đúng bộ đồ nghề ấy đi qua ba bộ dữ liệu.
Nhưng đừng coi đây là bài dễ. Ba bộ này được chọn có chủ ý: bộ đầu chạy ngon lành, bộ thứ hai lừa bạn, bộ thứ ba bị hỏng vì đúng một ô nhập sai. Cả ba đều chạy trót lọt, không bộ nào báo lỗi. Việc của bạn là phân biệt được chúng.
🧰 Bộ đồ nghề của bạn — chỉ có bấy nhiêu, và hôm nay không thêm dòng nào:
X = x.reshape(-1, 1) # Bai 32 - xep thanh bang mo_hinh = LinearRegression().fit(X, y) # Bai 31 - cho may hoc mo_hinh.coef_[0] # Bai 31 - do doc w mo_hinh.intercept_ # Bai 31 - diem cat b mo_hinh.predict([[40]]) # Bai 31 - hoi may ((mo_hinh.predict(X) - y) ** 2).mean() # Bai 28 - MSE
Hình 1 — Ba bộ dữ liệu và đường thẳng mà scikit-learn tìm ra cho mỗi bộ. Hình này vẽ từ đúng những con số bạn sẽ chạy trong phòng thực hành. Hãy để ý đường màu đỏ ở khung thứ ba: nó dốc xuống.
1. Bộ dữ liệu thứ nhất — khi mọi thứ chạy đúng như mong đợi
🌱 Dữ liệu: một cây con được đo chiều cao sau mỗi 5 ngày.
| Số ngày | 5 | 10 | 15 | 20 | 25 | 30 |
|---|---|---|---|---|---|---|
| Chiều cao (cm) | 7,1 | 10,8 | 15,2 | 19,1 | 22,8 | 27,3 |
🤔 Đoán trước khi chạy. Nhìn hai dòng số trên, hãy trả lời trong đầu: mỗi ngày cây cao thêm khoảng bao nhiêu? Và lúc mới trồng (ngày 0) cây cao chừng nào? Có con số trong đầu rồi hãy đọc tiếp.
💻 Máy trả lời:
w = 0.8051 # moi ngay cao them khoang 0,8 cm b = 2.9600 # luc trong da cao san gan 3 cm MSE = 0.0339
Bộ số liệu này ta dựng từ một quy luật giấu sẵn: cao = 0,8 × ngày + 3, rồi thêm chút nhiễu đo đạc. Máy tìm lại được 0,8051 và 2,96 — gần như đúng y, dù nó chưa từng được cho biết quy luật.
💡 Điều rút ra: khi quan hệ trong dữ liệu là có thật và gần thẳng, mô hình đường thẳng làm rất tốt. Hệ số w đọc được thành lời (“mỗi ngày cao thêm 0,8 cm”), và dự đoán cho ngày 40 — 35,17 cm — là con số đáng tin.
2. Bộ dữ liệu thứ hai — khi máy trả lời rất tự tin về một điều vô nghĩa
📝 Dữ liệu: tám học sinh, ta ghi số ký tự trong tên và điểm thi.
| Số ký tự trong tên | 7 | 12 | 9 | 15 | 8 | 11 | 10 | 13 |
|---|---|---|---|---|---|---|---|---|
| Điểm thi | 7,5 | 6,0 | 8,5 | 5,5 | 9,0 | 6,5 | 7,0 | 8,0 |
🤔 Đoán trước khi chạy. Tên dài ngắn thì liên quan gì tới điểm thi? Rõ ràng là không. Vậy bạn nghĩ scikit-learn sẽ làm gì — báo lỗi? trả về w = 0? hay từ chối chạy?
💻 Máy trả lời:
w = -0.3058 b = 10.4987 MSE = 0.7296 predict([[20]]) -> 4.38 diem # ten 20 ky tu predict([[ 3]]) -> 9.58 diem # ten 3 ky tu
Máy không báo lỗi, không do dự, không cảnh báo gì cả. Nó điềm nhiên kết luận: tên càng dài thì điểm càng thấp, cứ mỗi ký tự mất 0,31 điểm. Và nó sẵn sàng dự đoán một bạn tên 3 ký tự sẽ được 9,58 điểm.
Đây là điều quan trọng nhất của cả bài: scikit-learn không bao giờ nói “dữ liệu này chẳng có quan hệ nào”. Bạn bảo nó tìm đường thẳng khớp nhất, nó tìm đường thẳng khớp nhất. Việc phán xét con số ấy có nghĩa hay không là việc của bạn, không phải của thư viện.
📏 Một cách tự kiểm tra rất đơn giản — không cần công cụ mới, chỉ dùng lại đúng MSE của Bài 28.
Hãy so mô hình với cách đoán lười nhất có thể: cứ nhắm mắt trả lời bằng giá trị trung bình, bất kể đầu vào là gì. Nếu mô hình không khá hơn cách đoán lười đó bao nhiêu, nghĩa là nó chẳng học được gì.
MSE_mo_hinh = ((mo_hinh.predict(X) - y) ** 2).mean() MSE_doan_bua = ((y.mean() - y) ** 2).mean() ti_le = MSE_mo_hinh / MSE_doan_bua # cang gan 0 cang tot
Tỉ lệ gần 0% nghĩa là mô hình giỏi hơn hẳn. Gần 100% nghĩa là nó chẳng hơn gì việc đoán bừa. Cách so sánh này có tên gọi riêng trong ngành, ta sẽ gặp ở chặng sau; hôm nay cứ dùng nó như một phép chia thường.
Hình 2 — Cùng một phép chia áp cho cả ba bộ. Bộ 1 gần như chạm vạch 0. Bộ 3 gần chạm vạch 100 — mô hình của nó gần như không hơn gì việc nhắm mắt đọc giá trung bình.
💡 Điều rút ra: chạy được không có nghĩa là đúng. Trước khi tin một con số, hãy hỏi hai điều: quan hệ này có hợp lẽ thường không? và mô hình có thật sự khá hơn đoán bừa không?
3. Bộ dữ liệu thứ ba — một ô nhập sai phá hỏng tất cả
🏠 Dữ liệu: tám căn nhà, diện tích và giá bán.
| Diện tích (m²) | 40 | 50 | 60 | 70 | 80 | 90 | 100 | 55 |
|---|---|---|---|---|---|---|---|---|
| Giá (tỉ đồng) | 2,1 | 2,6 | 3,1 | 3,6 | 4,1 | 4,6 | 5,1 | 28,5 |
Bảy căn đầu theo đúng quy luật giá = 0,05 × diện tích + 0,1. Căn cuối cùng — 55 m² mà giá 28,5 tỉ — là một ô nhập sai: người nhập liệu gõ nhầm, đáng ra phải là 2,85.
🤔 Đoán trước khi chạy. Bảy trên tám dòng hoàn toàn sạch, chỉ một dòng sai. Bạn nghĩ mô hình sẽ lệch đi một chút, hay hỏng hẳn?
💻 Máy trả lời:
w = -0.0623 <- SO AM! b = 10.9591 MSE = 67.2330 predict([[75]]) -> 6.28 ty
Độ dốc mang dấu âm. Nghĩa là mô hình đã kết luận: nhà càng rộng thì càng rẻ. Một ô nhập sai duy nhất không chỉ làm sai lệch con số — nó làm sai cả chiều của quy luật.
Sửa đúng một ô, từ 28.5 thành 2.85, rồi chạy lại:
w = 0.0500 # dung y quy luat that b = 0.1000 # dung y quy luat that MSE = 0.0000 predict([[75]]) -> 3.85 ty
Từ vô dụng thành hoàn hảo, chỉ nhờ sửa một chữ số. Và dự đoán cho căn 75 m² nhảy từ 6,28 tỉ xuống 3,85 tỉ — chênh nhau 2,4 tỉ đồng.
💡 Điều rút ra: vì sao chỉ một điểm mà sức phá hoại lớn đến vậy? Bài 28 đã trả lời rồi: MSE bình phương sai số. Căn nhà nhập sai lệch khoảng 25 tỉ so với đường thẳng đúng, bình phương lên thành khoảng 625 — át hết mọi sai số nhỏ của bảy căn còn lại cộng lại. Mô hình buộc phải bẻ cong về phía nó để giảm con số khổng lồ ấy.
Đây là lý do trong công việc thật, dọn dữ liệu chiếm phần lớn thời gian, còn gọi fit thì chỉ mất một dòng.
⚠️ Ba câu hỏi cho mọi kết quả, từ nay về sau. Đây là thứ đáng mang theo nhất từ bài này:
- Vẽ dữ liệu ra xem chưa? Cả ba vấn đề hôm nay đều lộ ra ngay khi nhìn Hình 1 — trước khi cần tính toán gì.
- Mô hình có khá hơn đoán bừa bằng trung bình không? Một phép chia, hai dòng lệnh.
- Hệ số có hợp lẽ thường không? Nhà rộng mà rẻ hơn, tên dài mà điểm thấp hơn — những dấu hiệu này mắt người bắt được, máy thì không.
📝 Ba bộ, đặt cạnh nhau
| Bộ 1 · Cây | Bộ 2 · Tên | Bộ 3 · Nhà | |
|---|---|---|---|
| Chạy có lỗi không? | Không | Không | Không |
| w máy tìm ra | 0,8051 | −0,3058 | −0,0623 |
| MSE | 0,0339 | 0,7296 | 67,2330 |
| So với đoán bừa | 0,07% | 55,59% | 97,88% |
| Hệ số có hợp lẽ? | Có | Không | Không — sai cả chiều |
| Vấn đề nằm ở đâu | Không có | Dữ liệu vốn không có quan hệ | Một ô nhập sai |
| Cách xử lý | Dùng được | Bỏ đi, tìm đặc trưng khác | Sửa dữ liệu rồi chạy lại |
✎ Kiểm tra nhanh
Câu 1. Với bộ “độ dài tên”, scikit-learn trả về w = −0,3058. Kết luận đúng là:
- Tên càng dài thì điểm thi càng thấp
- Thư viện đã tính sai, cần cài lại
- Máy vẫn tìm đường thẳng khớp nhất như được yêu cầu; con số này không có ý nghĩa thực tế nào
- Cần thêm dữ liệu thì con số sẽ đúng
Câu 2. MSE của mô hình bằng 97,88% MSE của việc đoán bừa bằng giá trị trung bình. Điều đó nghĩa là:
- Mô hình chính xác tới 97,88%
- Mô hình gần như không khá hơn việc nhắm mắt đọc giá trị trung bình
- Mô hình sai 97,88% số lần dự đoán
- Cần chạy thêm 97,88% số vòng lặp nữa
Câu 3. Vì sao chỉ một dòng nhập sai lại kéo lệch được cả đường thẳng?
- Vì scikit-learn ưu tiên dòng cuối cùng của bảng
- Vì dòng đó có diện tích lớn nhất
- Vì mô hình chỉ nhìn vào một vài dòng chứ không nhìn hết
- Vì MSE bình phương sai số, nên một điểm lệch rất xa bị phạt nặng gấp bội và át hết các điểm còn lại
Câu 4. Khi dữ liệu hoàn toàn không có quan hệ, scikit-learn sẽ:
- Vẫn trả về một đường thẳng và không cảnh báo gì
- Báo lỗi
NoRelationError - Trả về
w = 0chính xác - Từ chối chạy
fit
Câu 5. Sau khi sửa ô nhập sai, mô hình cho w = 0,0500 và b = 0,1000 với MSE bằng 0. Điều này cho thấy:
- Mô hình đã học thuộc lòng dữ liệu nên không dùng được
- Có lỗi trong cách tính MSE
- Bảy căn nhà nằm chính xác trên một đường thẳng, và máy đã tìm lại đúng đường ấy
- Cần thêm dữ liệu vì kết quả quá đẹp
giá = 0,05 × diện tích + 0,1 mà không thêm nhiễu, nên đường thẳng đi qua trọn vẹn mọi điểm. Trong đời thực dữ liệu luôn có nhiễu và MSE bằng 0 là chuyện đáng nghi — Bài 35 sẽ nói kỹ.Câu 6. Việc đầu tiên nên làm khi nhận một bộ dữ liệu lạ là:
- Gọi
fitngay để xem mô hình cho ra gì - Vẽ dữ liệu ra và nhìn bằng mắt
- Thêm càng nhiều đặc trưng càng tốt
- Đổi sang mô hình phức tạp hơn
💻 Thực hành: ba bộ, một chương trình
▶ Cách dùng: bấm Khởi động Python một lần (khoảng 15 giây), đợi báo sẵn sàng rồi bấm Chạy code. Hàm khao_sat ở đầu chương trình làm hết mọi việc — ba bộ dữ liệu chỉ việc gọi lại nó.
💡 Thử thách (làm được cả ba là bạn đã nắm chắc bài):
- Tự dựng một bộ không có quan hệ. Thay
ky_tubằng tám con số bạn nghĩ ra trong đầu, giữ nguyêndiem. Tỉ lệ phần trăm ra bao nhiêu? Chạy vài lần với các con số khác nhau — nó có bao giờ xuống dưới 10% không, và vì sao? - Đổi mức độ sai của ô nhập nhầm. Trong Bộ 3, thử lần lượt
28.5,10.0,5.0,3.5. Ghi lạiwmỗi lần. Sai bao nhiêu thìwbắt đầu đổi sang dấu âm? Con số ấy nói gì về việc dữ liệu “bẩn tới mức nào thì còn cứu được”? - Thêm một cây thứ bảy vào Bộ 1 với
ngay = 35nhưngcao = 5— như thể có người đo nhầm. Bộ 1 vốn rất sạch giờ ra sao? Tỉ lệ phần trăm nhảy lên bao nhiêu?
Bài tiếp theo: hôm nay ta đã đo mô hình trên chính dữ liệu nó vừa học — và Bộ 3 sau khi sửa cho MSE bằng 0 tròn trĩnh. Nghe thì tuyệt, nhưng đó là một cái bẫy lớn. Bài 34 sẽ chỉ ra vì sao chấm điểm bằng chính đề đã ôn là cách chấm vô nghĩa, và cách làm đúng đơn giản đến bất ngờ. Đây là bài quan trọng nhất của cả chặng.