Tổng quan học máy
Trong các chủ đề trước, ta xây tác tử dựa trên tri thức được lập trình sẵn: luật cờ Vua, bản đồ đường đi, hàm hữu dụng viết tay. Cách này tốt khi luật rõ ràng và ổn định. Nhưng có vô số bài toán mà tri thức không thể mô tả thành luật: làm sao viết quy tắc để nhận ra một con mèo trong ảnh, hay để dịch một câu tiếng Việt sang tiếng Anh cho tự nhiên? Thế giới phức tạp hơn bất kỳ tập luật nào con người viết ra.
Học máy (machine learning) là một cách lập trình khác: thay vì dạy máy từng quy tắc, ta cho máy tự học quy luật từ ví dụ.
Ý tưởng cốt lõi
Định nghĩa kinh điển của Tom Mitchell (1997): một chương trình được nói là học từ
kinh nghiệm E đối với tác vụ T và độ đo hiệu năng P, nếu hiệu năng của nó trên T,
đo bằng P, được cải thiện theo E. Ba thành phần này luôn cần chỉ rõ:
- Tác vụ
T: cái máy cần làm, ví dụ phân loại email là rác hay không. - Kinh nghiệm
E: dữ liệu máy được thấy, ví dụ 10000 email đã gắn nhãn. - Hiệu năng
P: cách đo lường làm tốt nghĩa là gì, ví dụ tỷ lệ phân loại đúng.
Khác biệt cốt lõi so với lập trình truyền thống: lập trình truyền thống là người viết luật, máy chạy luật; còn học máy là người đưa dữ liệu, máy tự tìm ra luật. Hai cách này bổ sung cho nhau. Dùng học máy khi luật khó viết hoặc thay đổi nhanh (lọc thư rác, nhận diện khuôn mặt, gợi ý sản phẩm); dùng lập trình truyền thống khi luật ổn định và rõ ràng (tính tổng một cột bảng, kiểm tra số nguyên tố).
Ba dạng học máy
Người ta chia học máy thành ba dạng chính, dựa trên cái mà máy được cho thấy.
- Học có giám sát (supervised): máy thấy các ví dụ có nhãn, mỗi ví dụ gồm đầu vào và đầu ra mong muốn. Mục tiêu là học một hàm ánh xạ đầu vào sang đầu ra, tổng quát hóa cho dữ liệu mới chưa thấy. Đây là dạng phổ biến nhất trong ứng dụng thực tế.
- Học không giám sát (unsupervised): máy chỉ thấy đầu vào, không có nhãn. Mục tiêu là tìm cấu trúc ẩn trong dữ liệu.
- Học tăng cường (reinforcement learning): tác tử tương tác với môi trường, thực hiện hành động và nhận phần thưởng hoặc phạt. Máy không được cho biết câu trả lời đúng, mà phải tự khám phá qua thử nghiệm để cực đại hóa tổng thưởng theo thời gian.
Bảng dưới so sánh ba dạng để bạn nắm nhanh sự khác nhau:
| Tiêu chí | Có giám sát | Không giám sát | Tăng cường |
|---|---|---|---|
| Máy được cho thấy | Đầu vào kèm nhãn đúng | Chỉ đầu vào, không nhãn | Trạng thái và tín hiệu thưởng/phạt |
| Mục tiêu | Học hàm ánh xạ đầu vào sang đầu ra | Tìm cấu trúc ẩn | Học chính sách hành động tối ưu |
| Tín hiệu học | Nhãn đúng cho từng mẫu | Không có nhãn | Phần thưởng, thường trễ |
| Bài toán con điển hình | Phân loại, hồi quy | Phân cụm, giảm chiều | Điều khiển, ra quyết định tuần tự |
| Ví dụ | Lọc thư rác, dự đoán giá nhà | Phân khúc khách hàng | AlphaGo, robot tập đi, xe tự lái |
Phân loại, hồi quy và phân cụm
Trong ba dạng trên, ba bài toán con xuất hiện thường xuyên nhất:
- Phân loại (classification): bài toán có giám sát mà đầu ra là nhãn rời rạc. Ví dụ: email thuộc nhóm rác hay không rác; ảnh là mèo, chó hay chim; ảnh chụp phổi là viêm phổi hay bình thường.
- Hồi quy (regression): bài toán có giám sát mà đầu ra là số thực liên tục. Ví dụ: từ diện tích nhà dự đoán giá; từ tuổi, giới tính, huyết áp dự đoán rủi ro tim mạch.
- Phân cụm (clustering): bài toán không giám sát nhóm các mẫu tương tự nhau vào cùng một cụm, dù không có nhãn cho trước. Ví dụ: nhóm khách hàng theo hành vi mua, nhóm bài báo theo chủ đề.
Mẹo phân biệt nhanh: nếu có nhãn và nhãn là loại, đó là phân loại; nếu có nhãn và nhãn là số, đó là hồi quy; nếu không có nhãn mà muốn gom nhóm, đó là phân cụm.
Quy trình học máy
Mọi dự án học máy thực tế đều đi theo một quy trình chung. Có thể tóm vào bốn nhóm việc lớn:
- Dữ liệu: thu thập (từ log, cảm biến, web, hoặc gắn nhãn tay), làm sạch (xử lý giá trị thiếu, loại bản trùng, sửa lỗi nhập liệu), chuẩn hóa (đưa các đặc trưng về cùng phạm vi). Bước này thường chiếm phần lớn thời gian của cả dự án.
- Đặc trưng (feature): biến dữ liệu thô thành các con số mô hình học được. Có thể tạo đặc trưng mới từ đặc trưng gốc để mô hình học dễ hơn, ví dụ trích ngày trong tuần từ một mốc thời gian.
- Huấn luyện (training): chọn một họ mô hình rồi cho nó học trên tập huấn luyện, tức ước lượng các tham số sao cho khớp dữ liệu. Mỗi mô hình còn có các siêu tham số cần chọn, ví dụ tốc độ học hay số cụm.
- Đánh giá (evaluation): đo hiệu năng trên dữ liệu mô hình chưa thấy để biết nó tổng quát hóa tốt tới đâu, rồi dựa vào đó tinh chỉnh hoặc chọn mô hình.
Đây là một vòng lặp: đánh giá xong thường quay lại cải tiến dữ liệu, đặc trưng hoặc mô hình, rồi đo lại. Một dự án không kết thúc khi triển khai, mà bắt đầu một vòng giám sát lâu dài.
Quá khớp và chưa khớp
Khi mô hình cho kết quả kém trên dữ liệu mới, thường do một trong hai nguyên nhân.
- Chưa khớp (underfitting): mô hình quá đơn giản để mô tả dữ liệu, nên sai nhiều trên cả tập huấn luyện lẫn dữ liệu mới. Ví dụ dùng một đường thẳng để khớp dữ liệu có dạng đường cong.
- Quá khớp (overfitting): mô hình quá phức tạp, học thuộc lòng tập huấn luyện kể cả nhiễu, thay vì học quy luật chung. Hệ quả: sai số trên tập huấn luyện rất thấp nhưng sai số trên dữ liệu mới lại cao.
Dấu hiệu kinh điển của quá khớp là khoảng cách lớn giữa lỗi huấn luyện và lỗi trên dữ liệu kiểm chứng. Ví dụ minh họa:
- Lỗi huấn luyện
0.02, lỗi kiểm chứng0.03: khớp tốt. - Lỗi huấn luyện
0.02, lỗi kiểm chứng0.25: quá khớp rõ. - Lỗi huấn luyện
0.30, lỗi kiểm chứng0.32: chưa khớp rõ.
Đằng sau hai hiện tượng này là đánh đổi thiên lệch và phương sai. Mô hình quá đơn giản có thiên lệch cao (chưa khớp); mô hình quá phức tạp có phương sai cao (quá khớp). Luôn tồn tại một mức độ phức tạp tối ưu cân bằng hai bên. Các cách chống quá khớp thường dùng: thêm dữ liệu, giảm độ phức tạp mô hình, thêm phạt vào hàm mất mát (regularization), dừng sớm khi lỗi kiểm chứng bắt đầu tăng, và kiểm chứng chéo.
Không có bữa trưa miễn phí
Một câu hỏi tự nhiên là: có tồn tại một thuật toán học máy tốt nhất cho mọi bài toán không? Định lý không có bữa trưa miễn phí trả lời là không. Lấy trung bình trên tất cả các bài toán có thể có, mọi thuật toán học đều ngang nhau; một thuật toán chạy tốt trên nhóm bài toán này thì phải trả giá bằng việc chạy dở trên nhóm khác. Sở dĩ trong thực tế vẫn có thuật toán tỏ ra vượt trội là vì các bài toán ta thực sự quan tâm không trải đều mà mang những cấu trúc chung, và mỗi thuật toán mang sẵn một giả định phù hợp với một số cấu trúc nhất định. Bài học rút ra rất thực tế: đừng đi tìm một mô hình vạn năng, mà hãy thử vài họ mô hình rồi để dữ liệu và bước đánh giá chọn ra cái hợp với bài toán trước mặt.
Chia train/test và các độ đo
Để biết mô hình tổng quát hóa tốt hay không, ta không được đánh giá trên chính dữ liệu đã dùng để học. Vì vậy dữ liệu được chia thành các phần riêng biệt, không trùng nhau:
- Tập huấn luyện (training set): để máy học, ước lượng tham số.
- Tập kiểm chứng (validation set): để chọn mô hình và tinh chỉnh siêu tham số.
- Tập kiểm tra (test set): để báo cáo hiệu năng cuối cùng, chỉ chạm một lần.
Tỷ lệ phổ biến là 70 phần trăm cho huấn luyện, 15 phần trăm kiểm chứng, 15 phần trăm kiểm tra; hoặc 60/20/20. Một quy tắc vàng: đừng bao giờ tinh chỉnh trên tập kiểm tra. Nếu bạn xem kết quả test rồi sửa mô hình cho đẹp hơn, thông tin từ test đã rò rỉ vào quyết định, khiến ước lượng quá lạc quan và mô hình kém khi triển khai thật.
Nhiều người nghĩ chỉ cần không huấn luyện trên tập kiểm tra là an toàn, nhưng rò rỉ thường tinh vi hơn thế. Một lỗi kinh điển là chuẩn hóa dữ liệu (trừ trung bình, chia độ lệch chuẩn) trên toàn bộ dữ liệu rồi mới chia train/test. Khi đó thống kê của tập kiểm tra đã lọt vào bước tiền xử lý, và con số đánh giá trở nên lạc quan giả tạo. Cách đúng là tính các tham số chuẩn hóa chỉ trên tập huấn luyện rồi áp đúng bộ tham số đó lên tập kiểm tra. Tương tự, mọi bước chọn đặc trưng hay điền giá trị thiếu đều phải học từ riêng tập huấn luyện, không được nhìn trước dữ liệu dùng để đánh giá.
Để chọn được độ đo đúng, cần phân biệt theo loại bài toán.
Với phân loại nhị phân, mọi độ đo dựng từ bốn loại kết quả: TP (dương đúng), FP
(dương sai), FN (âm sai), TN (âm đúng). Bốn con số này lập thành ma trận nhầm lẫn.
- Độ chính xác (accuracy): tỷ lệ dự đoán đúng, bằng
(TP + TN) / (TP + TN + FP + FN). Cẩn thận với dữ liệu mất cân bằng: nếu 99 phần trăm mẫu là âm thì cứ đoán luôn âm đã đạt độ chính xác 99 phần trăm nhưng vô dụng. - Precision: trong số được dự đoán dương, bao nhiêu thực sự dương, bằng
TP / (TP + FP). Quan trọng khi dương sai gây hại, ví dụ lọc thư rác không muốn chặn nhầm email quan trọng. - Recall: trong số thực sự dương, bao nhiêu được phát hiện, bằng
TP / (TP + FN). Quan trọng khi âm sai gây hại, ví dụ chẩn đoán ung thư không muốn bỏ sót bệnh nhân. - F1: trung bình điều hòa của precision và recall, bằng
2 * P * R / (P + R). Dùng khi cần cân bằng cả hai.
Với hồi quy, các độ đo thông dụng là MSE (trung bình bình phương sai số, phạt mạnh
lỗi lớn), RMSE (căn của MSE, cùng đơn vị với đầu ra nên dễ diễn giải), MAE (trung
bình trị tuyệt đối sai số, ít nhạy với điểm ngoại lai), và R2 (tỷ lệ phương sai được mô
hình giải thích, bằng 1 là hoàn hảo, bằng 0 là không hơn gì đoán bằng trung bình).
Không có độ đo đúng chung cho mọi bài toán. Chọn độ đo dựa trên tác hại của từng loại lỗi: chẩn đoán bệnh thì ưu tiên recall, lọc thư rác thì ưu tiên precision, dữ liệu mất cân bằng thì dùng F1 thay vì accuracy.
Mã nguồn Python
Đoạn dưới minh họa hai việc cốt lõi của phần đánh giá: chia train/test và tính độ chính xác đơn giản, viết tay hoàn toàn, không cần thư viện ngoài. Mô hình ở đây chỉ là một bộ phân loại ngưỡng rất thô để tập trung vào quy trình.
import random
# Du lieu gia: moi mau la (dac trung, nhan) voi nhan 0 hoac 1
# Nhan that: 1 neu dac trung > 5, nguoc lai 0 (co them chut nhieu)
def tao_du_lieu(n):
data = []
for _ in range(n):
x = random.uniform(0, 10)
y = 1 if x > 5 else 0
if random.random() < 0.1: # 10% nhieu: dao nhan
y = 1 - y
data.append((x, y))
return data
def chia_train_test(data, ti_le_test=0.2, seed=2026):
# Xao tron roi cat: phan dau lam train, phan sau lam test
rng = random.Random(seed)
data = data[:]
rng.shuffle(data)
n_test = int(len(data) * ti_le_test)
test = data[:n_test]
train = data[n_test:]
return train, test
def huan_luyen(train):
# "Hoc" mot nguong tot nhat tren tap train bang cach thu cac nguong
candidates = [i * 0.5 for i in range(0, 21)] # 0.0, 0.5, ..., 10.0
best_thr, best_acc = 0.0, -1.0
for thr in candidates:
dung = sum(1 for x, y in train if (1 if x > thr else 0) == y)
acc = dung / len(train)
if acc > best_acc:
best_acc, best_thr = acc, thr
return best_thr
def do_chinh_xac(thr, data):
# accuracy = so du doan dung / tong so mau
dung = sum(1 for x, y in data if (1 if x > thr else 0) == y)
return dung / len(data)
data = tao_du_lieu(500)
train, test = chia_train_test(data, ti_le_test=0.2)
thr = huan_luyen(train)
print("Nguong hoc duoc:", thr)
print("Do chinh xac tren train:", round(do_chinh_xac(thr, train), 3))
print("Do chinh xac tren test :", round(do_chinh_xac(thr, test), 3))
Đoạn này thể hiện trọn vẹn một vòng nhỏ của quy trình: chia dữ liệu, học tham số (ở đây là một ngưỡng) trên tập huấn luyện, rồi đo độ chính xác trên cả hai tập. Nếu độ chính xác trên train cao hẳn so với test, đó chính là dấu hiệu quá khớp như đã bàn ở trên.
Bài tập thực hành
Bài 1. Với mỗi bài toán sau, hãy cho biết nó thuộc dạng học máy nào và là phân loại, hồi quy hay phân cụm: (a) lọc thư rác, (b) dự đoán giá một căn nhà từ diện tích, (c) chia khách hàng của một siêu thị thành các nhóm theo hành vi mua.
Gợi ý
Hãy hỏi: có nhãn cho trước không? Nếu có và nhãn là loại thì phân loại; nếu có và nhãn là số thì hồi quy; nếu không có nhãn mà chỉ muốn gom nhóm thì phân cụm. (a) có giám sát, phân loại. (b) có giám sát, hồi quy. (c) không giám sát, phân cụm.
Bài 2. Một mô hình đạt độ chính xác trên tập huấn luyện là 0.99 nhưng trên tập kiểm
chứng chỉ 0.55. Đây là quá khớp hay chưa khớp? Nêu hai cách cải thiện.
Gợi ý
Lỗi huấn luyện rất thấp nhưng lỗi kiểm chứng cao, khoảng cách lớn, nên đây là quá khớp. Cách cải thiện: thêm dữ liệu huấn luyện, giảm độ phức tạp mô hình, thêm phạt (regularization), hoặc dừng sớm khi lỗi kiểm chứng bắt đầu tăng.
Bài 3. Trong bài toán phát hiện gian lận thẻ, chỉ khoảng 0.1 phần trăm giao dịch là gian lận. Vì sao không nên dùng độ chính xác (accuracy) làm độ đo chính? Độ đo nào phù hợp hơn?
Gợi ý
Vì dữ liệu rất mất cân bằng: chỉ cần đoán luôn không gian lận đã đạt accuracy 99.9 phần trăm nhưng không bắt được ca gian lận nào. Nên dùng recall (để không bỏ sót gian lận) cùng precision, hoặc gộp lại thành F1.
Tự kiểm tra
Vì sao tập kiểm tra chỉ được chạm một lần ở phút cuối?
Vì tập kiểm tra dùng để ước lượng trung thực hiệu năng trên dữ liệu chưa thấy. Nếu xem kết quả test rồi quay lại sửa mô hình cho đẹp hơn, ta đã để thông tin từ test rò rỉ vào quyết định thiết kế. Khi đó con số test không còn khách quan, thường lạc quan quá mức, và mô hình sẽ kém hơn dự kiến khi triển khai thật.
Một mô hình có lỗi huấn luyện 0.30 và lỗi kiểm chứng 0.32 đang gặp vấn đề gì?
Đây là chưa khớp. Lỗi cao trên cả hai tập và khoảng cách giữa chúng nhỏ cho thấy mô hình quá đơn giản, chưa nắm được quy luật của dữ liệu. Hướng xử lý là tăng độ phức tạp mô hình hoặc thêm đặc trưng tốt hơn, chứ không phải thêm dữ liệu hay regularization (vốn để chữa quá khớp).
Câu hỏi tự kiểm
- 1Theo đánh đổi thiên lệch và phương sai, mô hình quá phức tạp thường có đặc điểm gì?
- 2Cách làm nào dưới đây là rò rỉ dữ liệu?
- 3Với dữ liệu phát hiện gian lận chỉ 0.1 phần trăm là gian lận, vì sao accuracy là độ đo tồi?
- 4Định lý "không có bữa trưa miễn phí" nói gì?
Học máy là cho máy tự học quy luật từ ví dụ thay vì được lập trình từng luật. Ba dạng chính là có giám sát, không giám sát và tăng cường; ba bài toán con hay gặp là phân loại, hồi quy và phân cụm. Quy trình đi qua dữ liệu, đặc trưng, huấn luyện, đánh giá theo vòng lặp. Luôn chia train/test để đo tổng quát hóa, cảnh giác với quá khớp (giỏi trên train, dở trên dữ liệu mới) và chưa khớp (dở trên cả hai), và chọn độ đo đúng theo tác hại của từng loại lỗi chứ không mặc định dùng độ chính xác.