Trong bài trước, chúng ta đã biết cách tìm ra một mô hình hồi quy tuyến tính dựa trên việc tối ưu hóa một hàm mục tiêu trên bộ dữ liệu huấn luyện. Tuy nhiên, việc chọn dạng hàm tuyến tính chúng ta thực hiện tương đối khiên cưỡng, nhưng mặc dù vậy, thật may là dữ liệu mà chúng ta chọn trong bài toán lại có dạng quan hệ khá tuyến tính vì vậy kết quả dự đoán có vẻ khá tốt. Tuy nhiên, không phải lúc nào chúng ta cũng may mắn như vậy, trong nhiều trường hợp (như hình vẽ dưới đây), hàm tuyến tính không phải là dạng tốt nhất để biểu diễn mối quan hệ giữa các thành phần trong dữ liệu. Trong những trường hợp như vậy, chúng ta gặp phải hai bài toán cốt lõi trong machine learning đó là overfitting (quá khớp) và underfitting (chưa khớp) và Bias-Variance Tradeoff (Đánh đổi thiên vị và phương sai). Trong bài viết này, chúng ta sẽ tìm hiểu về các khái niệm này và tiến hành thử nghiệm nó trên Python.
Overfitting và underfitting
Như đã học ở bài trước, mục tiêu của machine learning là tìm cách tổng quát hóa mô hình vô hạn thông qua một số hữu hạn các quan sát được cho trước. Tức là tìm cách khôi phục lại được hàm \(f^*\) gốc bằng một mô hình \(f_w\) một cách chính xác nhất có thể. Tuy nhiên, về mặt bản chất, chúng ta lại không biết dạng chính xác của hàm \(f^*\) là gì (nếu biết thì cần gì dự đoán nữa !!!), do đó từ “chính xác nhất” không thể lượng hóa được, bởi đáp án chúng ta còn không biết thì lấy gì để so sánh?
Do đó, thay vì so sánh với hàm gốc \(f^*\), chúng ta thường chuyển sang so sánh kết quả của mô hình dự đoán với chính dữ liệu thu quan sát trên thực tế. Vấn đề bắt đầu phát sinh từ đây. Vậy chúng ta so sánh như thế nào và so sánh với cái gì?
Trên thực tế chúng ta biết rằng, cho dù có tồn tại một hàm \(f^*\) bí ẩn sinh ra các cặp dữ liệu quan sát \((x, y= f^*(x))\) thì khi đo đạc và thu thập dữ liệu trong thực tế cũng không bao giờ phản ánh chính xác được mối quan hệ này. Bởi chúng ta không làm việc trên toán học mà chúng ta đang làm việc trên thực nghiệm do đó luôn luôn tồn tại những lỗi thực nghiệm không thể loại bỏ!!! Chẳng hạn, chúng ta biết phương trình Newton: \(F=ma\), nhưng khi đo đạc chúng ta không thể nào biểu diễn lại chính xác mối quan hệ này trong dữ liệu được (nếu không tin bạn có thể tìm cách đo!!!).

Hình 1. Overfitting và Underfitting
Nhìn vào ví dụ ở Hình 1 ở trên, đường màu xanh lá là quan hệ chính xác biểu diễn dữ liệu, nó sinh ra 9 điểm dữ liệu quan sát màu xanh dương, ta thấy rằng các điểm này không nằm chính xác hoàn toàn trên đường màu xanh lá đã cho mà tồn tại một lượng sai số nhỏ làm nó lệch đi khỏi đường gốc ban đầu. Nguyên nhân gây ra điều này có thể là do sai số trong dụng cụ đo, do quá trình thu thập dữ liệu, phương pháp thu thập hoặc đơn giản là do tự nhiên mang tính ngẫu nhiên. Về mặt toán học trên thực tế, chúng ta không đo được chính xác giá trị sinh ra do quan hệ gốc \((x,y)\) mà chúng ta chỉ đo được các cặp giá trị nhiễu \((x+\epsilon_x, y+ \epsilon_y)\), với \(\epsilon_x, \epsilon_y\) thường được xem là các biến ngẫu nhiên (random variable), thay đổi tùy theo từng cặp \((x,y)\). Điều này làm cho mối quan hệ giữa các quan sát và nhãn thực tế trở nên phức tạp hơn quan hệ thực sự ban đầu giữa chúng.
Với 9 điểm dữ liệu quan sát đã cho, chúng ta cố gắng xây dựng một mô hình dự đoán mối quan hệ gốc theo bốn cách khác nhau (các đường màu đỏ) bằng 4 đa thức hay 4 mô hình khác nhau: bậc 0 (đường thẳng song song với trục hoành, bậc 1 (đường thẳng tuyến tính như mô hình hồi quy tuyến tính), bậc 3 (gần sát với đường gốc màu xanh lá) và bậc 9 (đi qua toàn bộ 9 điểm dữ liệu).
Về mặt trực quan, chúng ta thấy rằng rõ ràng đường bậc 3 có dạng gần đúng nhất với dạng của hàm gốc nhưng do sai số đo đạc trong dữ liệu nên nếu như chúng ta tối ưu lỗi huấn luyện trên dữ liệu quan sát gồm 9 điểm đã cho thì đường bậc 9 mới là đường có lỗi dự đoán ít nhất (bằng 0). Ở đây, nếu chỉ nhìn vào lỗi dự đoán trên 9 điểm này, ta có thể thấy rằng hàm bậc 9 sẽ cho kết quả tốt nhất, bởi nó đi qua hết tất cả các điểm và sai số bằng 0. Một cách tổng quát, chỉ cần có bao nhiêu điểm dữ liệu, chúng ta hoàn toàn có thể xây dựng một mô hình dạng đa thức có độ chính xác lên đến 100% đi qua toàn bộ các điểm dữ liệu này thông qua phương pháp xấp xỉ nội suy Lagrange hoặc Newton trong giải tích và đa thức này có bậc chính là số điểm của dữ liệu.
Đến đây, vấn đề nảy sinh bắt đầu xảy ra, rõ ràng dạng hàm gốc ban đầu sát với hàm bậc ba, nhưng chúng ta dựa trên kỹ thuật tối ưu hóa lỗi trên dữ liệu quan sát như ở bài trước do đó lại tạo ra một mô hình dự đoán dạng đa thức bậc 9. Và điều này tất nhiên là sai toét so với mục tiêu của chúng ta mong muốn. Với một điểm dữ liệu mới vào, chúng ta chắc chắn sẽ đưa ra một sai số lớn khủng khiếp (chẳng hạn với \(x=10\) thì đáng ra giá trị dự đoán của chúng ta kỳ vọng phải nằm đâu đó xung quanh giá trị 1000, nhưng kết quả dự đoán của mô hình lại bùng nổ đến \(10^9\) một con số lớn khủng khiếp (lớn gấp hàng triệu lần so với kỳ vọng!!!!). Và nếu càng nhiều điểm dữ liệu (cho đa thức dự đoán bậc càng cao) thì kết quả chênh lệch còn lớn khủng khiếp hơn nữa.
Trong trường hợp này, chúng ta gặp phải vấn đề Overfitting (quá khớp) trong machine learning, tức là dữ liệu quá khớp với dữ liệu hay mô hình “học vẹt” tức là chỉ nhớ dữ liệu chứ không có khả năng tổng quát hóa để dự đoán các dữ liệu chưa được quan sát trên thực tế sử dụng. Nó có thể rất đúng trên dữ liệu quan sát có sẵn (100%) nhưng lại sai rất lớn trên các dữ liệu mới được đưa vào mô hình mà không nằm trong tập dữ liệu quan sát đã có.
Ngược lại, khi chúng ta xây dựng một mô hình khá đơn giản (bậc 0 và bậc 1) ở trên thì cũng không tốt. Ta thấy, sai số của nó vẫn rất lớn. Giả sử ta cần dự đoán giá trị tương ứng với \(x=100\) thế thì đường bậc 0 cho chúng ta giá trị là 100 còn đường bậc 1 cho giá trị cũng tương tự nhân thêm 1 hằng số nhân, trong khi giá trị kỳ vọng phải là \(100^3\), tức là sai số chẳng kém gì trường hợp overfitting. Trong trường hợp này, chúng ta gặp phải vấn đề Underfitting (Không khớp).
Nếu ví mô hình overfitting là một anh sinh viên học tủ, học vẹt thì underfitting là một anh sinh viên chẳng biết gì, bởi anh ta thậm chí còn chẳng làm tốt trên cả bài tủ (dữ liệu quan sát đã cho) và cũng do chẳng thể làm cả bài cơ bản, nên anh ta cũng chẳng thể vận dụng và làm các bài nâng cao (dữ liệu mới chưa được quan sát).
Overfitting và Underfitting xảy ra là do trên thực tế chúng ta không thể biết được dạng của hàm dự đoán là hàm gì mà chỉ có thể biết được một số điểm hữu hạn dữ liệu quan sát (dữ liệu này còn chứa nhiễu do đo đạc, thống kê hoặc sự ngẫu nhiên mặc định trong dữ liệu). Dữ liệu hữu hạn không đủ tốt sẽ không thể đại diện được cho phân phối của toàn bộ dữ liệu cho bài toán dẫn đến các dự đoán sai lầm.
Bias-Variance Tradeoff
Giả sử, chúng ta muốn dự đoán một biến \(y\) theo \(x\). Chúng ta giả sử quan hệ chính xác giữa chúng là \(y=f^*(x)\). Tuy nhiên, do nhiễu đo đạc mà kết quả dự đoán chính xác của nó sẽ là \(y=f^*(x) + \epsilon\). Trong đó, \(\epsilon\) là nhiễu ồn trắng tức là có phân phối chuẩn và kỳ vọng 0. Chúng ta xây dựng một mô hình dự đoán \(f_w(x)\) cho quan hệ trên, khi đó, kỳ vọng lỗi bình phương tại điểm \(x\) được tính bằng:
\(Err(x) = E [(y-f_w(x))^2]\)
Và, lỗi này có thể được tách thành:
\(Err(x) = E[(f^*(x) + \epsilon – f_w(x))^2] = (E[f_w(x) – f^*(x)])^2 + E[(f_w(x) – E[f_w(x)])^2] + \sigma_\epsilon ^2\)
Hay
\(Err(x) = Bias^2 + Variance + noise\)
Tức là, các sai lầm trong kết quả dự đoán của mô hình gây nên bởi 3 thành phần:
- Sai lầm do sự thiên vị (Bias error)
- Sai lầm do phương sai dữ liệu (Variance error)
- Sai lầm do các nhiễu ngẫu nhiên (noise)
Trong khi, như đã nói ở trên, các nhiễu ngẫu nhiên là luôn luôn tồn tại và là các sai lầm không thể giảm được thì hai dạng sai lầm phía trên là bias và variance là các lỗi có thể giảm thiểu được mà chúng ta hướng đến việc cực tiểu hóa các lỗi này. Mục tiêu của các mô hình machine learning là giảm thiểu các lỗi dự đoán, tuy nhiên các lỗi do nhiễu ngẫu nhiên là không thể giảm được vì vậy việc tối ưu mô hình chỉ hướng đến việc tìm được một mô hình “đủ tốt” tức là chấp nhận được các nhiễu ngẫu nhiên trong khi cố gắng loại bỏ các sai lầm do phương sai và thiên vị. Tuy vậy, bias và variance là hai đại lượng ảnh hưởng qua lại do đó chúng ta không thể đồng thời giảm cả hai giá trị này. Điều này dẫn đến một bài toán quan trọng trong machine learning là: Bias-Variance Tradeoff hay đánh đổi giữa thiên vị và phương sai.
Bias là gì?
Bias là độ đo sự chênh lệch kỳ vọng giữa dự đoán trung bình của mô hình của chúng ta và giá trị chính xác mà chúng ta đang cố gắng dự đoán. Điều này giống như việc chúng ta muốn đoán giá trị \(f^*(x) = 5\) nhưng mô hình lại luôn đoán ra các giá trị xoanh quanh 4 (100 lần dự đoán đều là 4.1,4.2, 3.9,3.8… chẳng hạn), tức là độ thiên lệch ở đây là 1. Mô hình chúng ta như một cung thủ muốn dự đoán tâm của một bia ngắm, nhưng mà bắn đến 100 lần thì đều vào vòng 8 (dù có lệch đi đôi chút).
Bias sinh ra do các giả định sai lầm trong mô hình. Kiểu như chúng ta thiên vị và nghĩ mô hình này là tốt mà không cần phải xem xét các điều kiện khác (dữ liệu thực tế). Mô hình có bias cao quan tâm rất ít đến dữ liệu huấn luyện (tức là không quan tâm đến thực tế mà chỉ tin vào thiên kiến cá nhân từ trước) và do đó có thể khiến thuật toán bỏ lỡ các mối quan hệ liên quan giữa đầu vào (thuộc tính) và đầu ra mục tiêu cần dự đoán. Chẳng hạn do chúng ta chỉ học hồi quy tuyến tính nên mô hình nào dù dữ liệu ra sao chúng ta cũng ốp hồi quy tuyến tính vào mà không cần biết dữ liệu phân bố như thế nào, và do đó, trong nhiều trường hợp là sai toét !!!. Bias cao luôn dẫn đến sai số cao trong cả dữ liệu huấn luyện lẫn dữ liệu mới chưa được quan sát.
Quay lại ví dụ trên, rõ ràng phân bố của dữ liệu có dạng đường cong “uốn lượn” khá rõ ràng, nhưng chúng ta cứ khăng khăng rằng nó có dạng tuyến tính (như ở trường hợp 1 và 2) thì kết quả dự đoán sẽ rất tệ. Điều này ngược với phương sai.
Phương sai (Variance) là gì?
Phương sai là một sai số từ độ nhạy đối với các dao động nhỏ trong tập huấn luyện cho phép đo mức độ tản mát của kết quả dự đoán. Phương sai cao đồng nghĩa với việc một sai số dù là rất nhỏ trong đầu vào cũng sẽ dẫn đến những sai lệch rất lớn trên kết quả dự đoán đầu ra. Ví dụ, giá trị đầu vào của một biến \(x=1\) cho kết quả dự đoán $y_1=1$, nhưng do sai số đo đạc rất nhỏ \(x=1.01\) nhưng lại tạo ra kết quả dự đoán \(y_2=10\) chẳng hạn.
Một mô hình có phương sai cao, đồng nghĩa với việc nó hoạt động không chính xác, và việc dự đoán của nó có vẻ cho kết quả rất ngẫu nhiên chứ không phải kết quả là đầu ra dự kiến. Chẳng hạn, chúng ta cần dự đoán giá trị là 5, tuy nhiên các lần dự đoán đều cho ra giá trị là 10, 0, 11, 1,… có độ lệch rất nhiều so với giá trị được kỳ vọng chỉ vì giá trị 5 được đo đạc có sai số nhỏ.
Hình dưới đây mô tả ý nghĩa của bias và variance

Biểu diễn quan hệ của Bias và Variance (Nguồn Domingo 2012 – Medium)
Trong hình trên, tâm của hình tròn thể hiện giá trị đúng kỳ vọng của dự đoán, đi càng xa ra tâm thì mô hình dự đoán có kết quả sai lệch càng lớn và đồng nghĩa với càng tệ. Trong trường hợp đầu tiên góc trên bên trái với Phương sai thấp các giá trị dự đoán rất tập trung, tuy nhiên Bias lại cao do đó các điểm dự đoán này cách xa so với tâm kỳ vọng. Sang bên phải, variance lại cao do đó các điểm lại rất tản mát, và nó vẫn cách rất xa với tâm. Ở góc dưới bên phải, các điểm khá tản mát xoay quanh tâm, nhưng vẫn không chính xác, Góc dưới bên trái các điểm khá ổn định trong tâm và đây là trường hợp tốt nhất: Bias thấp và Variance cũng thấp.
Nguyên nhân gây ra phương sai cao trong kết quả dự đoán là do chúng ta dự đoán trên giá trị nhiễu và cố gắng mô tả nhiễu hơn là dữ liệu. Điều này làm cho kết quả dự đoán cũng kèm một lượng nhiễu rất lớn làm tăng variance của việc dự đoán. Thường thì, điều này xảy ra khi mô hình quá tập trung vào dữ liệu huấn luyện và không khái quát hóa trên dữ liệu mà nó chưa từng thấy trước đây. Kết quả là, các mô hình như vậy hoạt động rất tốt trên dữ liệu huấn luyện nhưng có tỷ lệ lỗi cao trên dữ liệu thực tế mà nó chưa thấy. Điều này gây ra hiện tượng overfitting đã nói ở trên, tức là chúng ta bám quá sát vào dữ liệu huấn luyện mà quene rằng chính dữ liệu huấn luyện cũng có nhiễu và sai lệch so với dữ liệu thực tế.
Ngược lại, bias cao như đã nói ở trên là do chúng ta thiên vị một số dữ liệu mà bỏ quên quan hệ giữa dữ liệu hoặc ước đoán sai mô hình. Điều này làm cho cả kết quả dự đoán trên tập huấn luyện lẫn dữ liệu mới chưa từng thấy đều không tốt. Đây là nguyên nhân dẫn đến underfitting.
Do đó, muốn giảm cả phương sai lẫn thiên lệch, chúng ta vừa phải quan tâm đến dữ liệu huấn luyện lại vừa không được quan tâm quá đến nó. Tức là tìm được điểm cân bằng “vừa đủ” cho sự “quan tâm” này tức là ước lượng được đâu là nhiễu, đâu là dữ liệu chính xác kỳ vọng. Điều này trên thực tế là rất khó. Tức là chúng ta rất khó để đạt đến việc thu được cả bias và variance đều thấp. Chúng ta gọi vấn đề này là vấn đề Bias-Variance Tradeoff (hay sự đánh đổi giữa thiên vị và phương sai). Đây là một trong những vấn đề trọng tâm trong machine learning.
Mối quan hệ giữa Bias-Variance Tradeoff và Overfitting/ Underfitting
Như ở trên đã phân tích, underfitting xảy ra khi một mô hình không có khả năng mô hình hóa chính xác các mẫu trong dữ liệu. Các mô hình này thường có giá trị bias cao và variance thấp. Nó xảy ra khi chúng ta có rất ít dữ liệu để xây dựng một mô hình chính xác hoặc khi chúng ta chọn các mô hình có dạng biểu diễn quá đơn giản so với quan hệ trong dữ liệu (chẳng hạn sử dụng mô hình tuyến tính để mô hình hóa các dữ liệu có quan hệ phi tuyến). Các mô hình này quá đơn giản để có thể mô tả được các mẫu dữ liệu phức tạp. Chẳng hạn như các mô hình hồi quy tuyến tính hoặc hồi quy logistic.
Ngược lại, overfitting lại xảy ra khi mô hình được xây dựng mô hình hóa cả nhiễu của dữ liệu vào trong các quy luật học được. Nó xảy ra khi chúng ta sử dụng một bộ dữ liệu có rất nhiều nhiễu để huấn luyện và thuật toán được sử dụng để huấn luyện thì lại bám quá sát vào dữ liệu (có độ phức tạp cao). Những mô hình như vậy có bias thấp và variance rất cao. Những mô hình rất phức tạp như Cây quyết định (Decision Tree) hay mạng nơ ron sâu (deep learning) thường khá nhạy cảm với dữ liệu nhiễu và có thể làm cho mô hình dự đoán bị sai lệch khi có quá ít dữ liệu và dữ liệu lại chứa quá nhiều nhiễu.
Các phương pháp học có phương sai cao có thể thể hiện tốt trên tập dữ liệu huấn luyện nhưng có nguy cơ quá khớp với dữ liệu (khớp luôn cả vào nhiễu hoặc các dữ liệu không có giá trị đại diện cho bài toán). Ngược lại, các thuật toán có bias cao thường tạo ra các mô hình đơn giản hơn nhưng lại có thể không nắm bắt được các mẫu quan trọng (tức là không phù hợp) trong dữ liệu.

Mối quan hệ giữa Underfitting/Overfitting và Bias-Variance Tradeoff
Nhìn chung, nếu mô hình của chúng ta quá đơn giản và có rất ít tham số thì chúng có thể có bias cao còn variance thấp (underfitting) và ngược lại, khi mô hình quá phức tạp, với nhiều tham số thì chúng lại có thể có variance cao còn bias thấp (overfitting). Do đó, chúng ta cần phải tìm ra sự cân bằng cho mô hình trong cả hai phía để cho mô hình huấn luyện của chúng ta không bị overfittting hoặc underfitting.
Ví dụ về underfitting và overfitting với Python
Để hiểu hơn về underfitting và overfitting, chúng ta cùng bắt đầu với một ví dụ khá cơ bản trên Python: Ước lượng một hàm đa thức trên dữ liệu quan sát cho trước.
Đầu tiên, trước khi đi vào ví dụ chi tiết, ta gọi vào các thư viện cần thiết: numpy cho xử lý tính toán, matplotlib để hiển thị dữ liệu và kết quả:
1 2 3 |
import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl |
Đầu tiên, chúng ta giả sử chúng ta biết một hàm bí ẩn \(f^*(x) = -x^2+10-12\). Chúng ta giấu hàm này đi để mô hình tìm lại hàm này thông qua 20 điểm dữ liệu được cung cấp sẵn. Để sinh ra 20 điểm dữ liệu này, đầu tiên chúng ta chọn 20 giá trị đầu vào \(x\) tương ứng nằm trong khoảng [0,10). Tính các giá trị đầu ra tương ứng (cộng thêm nhiễu): \(y=-x^2+10-12+\epsilon\), với \(\epsilon\) là nhiễu có phân phối chuẩn với kỳ vọng 0 và phương sai 2 (\(\epsilon \sim \mathcal{N}(0,2)\)).
1 2 3 4 5 6 7 8 9 |
np.random.seed(seed=100) n = 20 x = np.random.uniform(0, 10, n) y = -x*x + 10*x-12 + np.random.normal(0, 2, n) mpl.rcParams['figure.dpi'] = 150 plt.scatter(x, y) plt.xticks([]) plt.yticks([]) plt.ylim([-20, 20]) |
Đến đây chúng ta đã có bộ dữ liệu quan sát giả định sinh ra từ hàm gốc ban đầu. Ở đoạn code trên, để cố định các điểm sinh ra sau mỗi lần chạy ta gán luôn seed=100, để đảm bảo mọi lần sinh dữ liệu ngẫu nhiên có kết quả như nhau để tiện cho việc so sánh. Chúng ta đặt figure.dpi=150 để các chấm biểu diễn to hơn cho phép phân biệt nó dễ hơn xem đường hồi quy có đi qua nó hay không.
Dữ liệu phân bố trên có dạng trông giống như hình dạng của một đường cong parabol bậc 2. Tuy nhiên, chúng ta cũng có thể thấy do nhiễu được thêm vào nên trên thực tế là không phải tất cả các điểm đều sẽ nằm hoàn toàn dọc theo một parabol.
Trong ví dụ trên, chúng ta đã biết trước quy tắc sinh dữ liệu là đường bậc 2. Tuy nhiên, chú ý là trên thực tế, khi xây dựng mô hình học máy, quá trình sinh dữ liệu cũng như hàm sinh này chúng ta hoàn toàn không biết. Và do đó, ở đây chúng ta cũng giả sử rằng chúng ta không biết quá trình sinh này. Thay vào đó, chúng ta thử xây dựng một số mô hình ứng viên để giải thích và tìm lại mối quan hệ xấp xỉ với quan hệ sinh ban đầu.
Đầu tiên, chúng ta xét mô hình đơn giản dạng đa thức bậc 1, hay nói cách khác là mô hình tuyến tính, với hàm polyfit trong numpy:
1 2 |
lin_fit = np.polyfit(x, y, 1) print(lin_fit) |
Hàm này sẽ cho kết quả là một hàm tuyến tính có dạng phù hợp nhất cho những dữ liệu đã cho. Biểu diễn hàm hồi quy này trên đồ thị để so sánh kết quả.
1 2 3 4 5 6 7 |
cmap = mpl.cm.get_cmap('tab10') plt.scatter(x, y, label='Data', color=cmap(0)) plt.plot(x, np.polyval(lin_fit, x), label='Mô hình Underfit', color=cmap(2)) plt.legend(loc=[0.3, 0.1]) plt.xticks([]) plt.yticks([]) plt.ylim([-20, 20]) |
Chúng ta thấy nó lệch khá nhiều so với phân bố của dữ liệu, gần như các dự đoán (nếu có) đều cách rất xa điểm dữ liệu gốc. Do đó, ta gắn nhãn mô hình này là Underfitting. Đây là một ví dụ đơn giản về Undertfitting khi mô hình quá đơn giản và không thể hiện được quan hệ trong dữ liệu (dù là dữ liệu huấn luyện).
Mô hình tuyến tính có vẻ quá đơn giản, do đó, bây giờ chúng ta cố gắng biểu diễn nó dưới dạng đa thức bậc cao hơn của \(x\) chẳng hạn: \(x^2, x^3, \ldots, x^20\). Chúng ta chọn tạm một đa thức bậc 15 và tiến hành xây dựng mô hình hồi quy đa thức bậc 15, sau đó hiển thị nó lên đồ thị:
1 2 3 4 5 6 7 8 9 |
high_poly_fit = np.polyfit(x, y, 15) high_poly_fit curve_x = np.linspace(0,11,100) plt.plot(curve_x, np.polyval(high_poly_fit, curve_x), label='Mô hình Overfitting', color=cmap(3)) plt.legend(loc=[0.3, 0.1]) plt.xticks([]) plt.yticks([]) plt.ylim([-20, 20]) |
Đây là một ví dụ kinh điển cho việc nâng độ phức tạp của mô hình lên quá mức để tránh underfitting. Chúng ta thấy rằng mô hình đã hết underfitting nhưng nó lại overfitting gần như hoàn hảo qua toàn bộ các điểm dữ liệu trong tập huấn luyện. Mặc dù vậy, có thể dễ dàng nhận thấy rằng đối với các giá trị ở khoảng giữa các điểm liên tiếp, mô hình overfitting trông khác hoàn toàn so với dữ liệu thực tế của quá trình tạo dữ liệu. Trong trường hợp này, mô hình overfitting đã bị điều chỉnh theo nhiễu của dữ liệu đào tạo. Điều này phù hợp với định nghĩa của phương sai cao được đưa ra ở trên. Bạn có thể thấy: một thay đổi nhỏ trong đầu vào \(x\) có thể dẫn đến thay đổi lớn trong đầu ra \(y\).
Trong trường hợp, nếu chúng ta tạo một tập dữ liệu tổng hợp giả định mới, sử dụng cùng một hàm bậc hai ở trên \(y = -x^2+10x-12\), và cũng thêm nhiễu mới được tạo ngẫu nhiên theo cùng một phân phối mà chúng ta đã sử dụng ở trên, sau đó lấy mẫu ngẫu nhiên 20 điểm và tìm một đa thức bậc cao để hồi quy quan hệ giữa chúng thì mô hình tìm được sẽ có hệ số khác rất nhiều so với mô hình ở trên. Lặp lại quá trình này với các mẫu 20 điểm khác nhau, sẽ tiếp tục dẫn đến các ước lượng hệ số thay đổi rất nhiều. Nói cách khác, các hệ số sẽ có phương sai cao giữa các mẫu dữ liệu được sử dụng để đào tạo mô hình từ cùng một phân phối. Dù nó vẫn đi qua hoàn hảo các điểm dữ liệu huấn luyện tương ứng. Đây cũng là một cách để định nghĩa khác về mô hình có phương sai cao.
Bậc thấp không ổn, bậc cao cũng không xong, vậy bây giờ ta đi về với đúng đúng bậc của nó là đa thức bậc 2. Chúng ta sẽ có thể thấy mức độ phù hợp của đa thức bậc 2 đối với dữ liệu so với các mô hình bị underfitting và overfitting. Chú ý rằng, ở đây không hẳn là chúng ta đoán bừa hoặc biết trước là đa thức có bậc 2, mà chúng ta có thể nhìn vào dạng phân bố của dữ liệu. Nó có dạng khá gần giống với đường bậc 2 (thêm chút nhiễu). Kết quả như sau:
1 2 3 4 5 6 |
print(np.polyfit(x,y,2)) plt.plot(curve_x, np.polyval(np.polyfit(x, y, 2), curve_x), label='Mô hình tốt nhất', color=cmap(6)) plt.legend(loc=[0.3, 0.1]) plt.xticks([]) plt.yticks([]) plt.ylim([-20, 20]) |
Hệ số của mô hình thu được là:
1 |
[ -1.02219803 10.35559107 -12.60547939] |
Có vẻ khá gần với hàm gốc ban đầu đúng không ạ !!!.
Trên thực tế, việc xây dựng và tìm được dạng của mô hình phù hợp để tránh overfitting và underfitting là một vấn đề không dễ dàng. Để xây dựng một mô hình machine learning tốt, chúng ta cần tìm một điểm cân bằng giữa bias và variance sao cho cực tiểu hóa được tổng lỗi dự đoán:
\(Err(x) = Bias^2 + Variance + noise\)
Việc tìm điểm cân bằng này khá phức tạp đó là lý do tại sao chúng ta gọi nó là bài toán đánh đổi giữa bias và variance. Một thuật toán tốt cần phải không phức tạp quá mức cần thiết nhưng lại cũng không được đơn giản quá mức cần thiết. Đây là một thể hiện của nguyên lý lát cắt Occam’s razor, tức là hãy giữ mọi thứ đơn giản nhất có thể.
Độ phức tạp của mô hình và nguyên lý Occam’s razor
Chắc hẳn chúng ta đều rất hay nghe thấy một số định luật kiểu “hãy đơn giản hóa vấn đề đi”, “anh hiểu vấn đề thì anh mới giải thích nó dưới dạng đơn giản được!!!”… Tất cả đều hướng đến một quy luật nổi tiếng được đặt tên theo nhà triết học William of Occam, đó là quy tắc Occam’s razor:
Entities must not be multiplied beyond necessity.
Tức là sự vật không cần thiết phải phức tạp hóa quá mức cần thiết. Điều này, cũng được thể hiện khá nhiều trong khoa học. Chính nhờ quy luật này mà các bạn nhìn thấy các phương trình kiểu như: \(E=mc^2\) hay $F=ma$ chứ không phải là một lô một lốc các tham số, các hệ số, phương trình với hàm số mũ khủng khiếp phức tạp khác. Và nếu các bạn chịu khó để ý lại những gì chúng ta đã học trong khoa học, gần như các phương trình biểu diễn các định luật nổi tiếng không bao giờ có dạng của hàm quá phức tạp.
Như Albert Einstein từng phát biểu:
Everything should be made as simple as possible, but no simpler. (Mọi thứ nên được tối giản hóa hết mức có thể, nhưng không nên quá mức).
Nguyên tắc này cũng được áp dụng trong machine learning, nó là một tôn chỉ để giải quyết vấn đề Bias-Variance Tradeoff hay Underfitting/Overffiting trong machine learning. Có nghĩa là:
Trong tất cả các mô hình có thể giải thích được dữ liệu, ta nên chọn mô hình đơn giản nhất có thể.
Ở đây, một điều rất lưu ý đó là từ “có thể”, chúng ta cần lưu ý rằng mô hình ở đây không phải là mô hình đơn gián nhất mà là “mô hình đơn gián nhất có thể” vẫn giải thích được dữ liệu. Bởi mô hình đơn giản nhất luôn là mô hình kiểu dự đoán à uôm, thích đoán gì thì đoán lấy. Chẳng hạn trong bài toán phân lớp mã độc và tệp lành tính, chúng ta có thể tạo ra một mô hình phân lớp ngẫu nhiên tức là gán nhãn bừa cho mỗi tệp. Mô hình này “đơn giản nhất” thật đấy, nhưng nó lại sai bét và vi phạm nguyên tắc Occam’s razor vì nó không thể giải thích được sự khác biệt giữa file độc hại và file lành tính.
Vậy vấn đề ở đây là: “Đâu là mô hình đơn giản nhất có thể???”.
Xét bất đẳng thức xác định cận trên của sai số thực tế của mô hình dự đoán dưới đây:
\(R(\alpha) \leq R_{emp}(\alpha) + \sqrt{\frac{h\big(ln\big(\frac{l}{h}\big)+1\big) – ln\big(\frac{\phi}{4}\big)}{l}}\)
trong đó, \(R(\alpha)\) là sai số dự đoán thực sự của mô hình \(f(x)\) so với hàm gốc \(f^*(x)\), nhưng chúng ta không biết dạng chính xác của hàm gốc \(f^*(x)\) là gì, do đó, chúng ta chỉ có thể ước lượng cận trên của nó theo công thức phía trên với:
\(R_{emp}(\alpha)\): sai số dự đoán của mô hình trên dữ liệu quan sát thực nghiệm được cung cấp,
\(h\): độ phức tạp của mô hình,
\(l\): số lượng dữ liệu quan sát được cung cấp để xây dựng mô hình,
\(\phi \in (0,1)\): là một giá trị tham số tùy bài toán.
Từ công thức này, ta thấy rằng mô hình càng phức tạp thì giới hạn của sai số thực tế khi dự đoán càng cao còn ngược lại dữ liệu quan sát được cung cấp càng nhiều thì giới hạn sai số thực tế lại càng nhỏ. Tức là có một quan hệ tỷ lệ nghịch ở đây, khi mô hình phức tạp ta có thể tăng lượng dữ liệu để giảm giới hạn sai số và ngược lại khi dữ liệu ít ta cũng cần giảm độ phức tạp của mô hình.
Đến đây chắc hẳn sẽ rất nhiều bạn hỏi tại sao lại có công thức trên và lôi ở đâu ra các đại lượng này. Ở đây do tính chất cơ bản của Series này, ta sẽ không nhắc đến chứng minh lý thuyết của nó ở đây. Nếu bạn nào muốn biết tại sao có thể tham khảo bài viết này.
Về cơ bản, đến đây chúng ta tạm hiểu rằng do có công thức trên nên mô hình càng phức tạp thì sai số dự đoán thực tế lại càng cao. Chặn trên của lỗi (bound on the risk) được tính là tổng của lỗi thực nghiệm (empirical risk) và khoảng tin cậy (confidence interval). Khi độ phức tạp của mô hình/giả thuyết \(h\) của cấu trúc hàm \(S\) tăng cao thì lỗi thực nghiệm (hay lỗi huấn luyện) có thể giảm nhưng khoảng tin cậy lại tăng và làm cho cận trên cũng tăng. Mô hình có (cận trên) lỗi nhỏ nhất \(h^*\) là mô hình có cấu trúc hợp lý \(S^*\) vừa không quá phức tạp vừa có lỗi huấn luyện đủ nhỏ.
Hay, chuyển sang dạng biểu diễn của Bias-Variance Trade off và Overfitting/Underfitting ta có:
Các mô hình tốt nằm ở vùng màu trắng ở giữa, còn các mô hình underfitting nằm bên trái với bias cao, vùng overfitting nằm bên phải với variance cao. Mô hình phức tạp thì lỗi huấn luyện (training error) sẽ giảm, nhưng ngược lại lại bị overfitting.
Nguyên nhân gây ra Overfitting, Underfitting và cách giảm thiểu
Ở trên ta đã phân tích các vấn đề về Bias-Variance tradeoff và Underfitting/ Overfitting. Trong phần này, chúng ta sẽ tổng kết lại những nguyên nhân gây ra các hiện tượng này và cách khắc phục.
Đầu tiên, do dữ liệu quan sát luôn chứa nhiễu (do bản chất ngẫu nhiên hoặc do các phương thức hoặc công cụ đo đạc). Khi nhiễu có trong tập dữ liệu quan sát thì kết quả dự đoán trên thực tế khi đo đạc đã có sai lệch (do nhiễu là biến ngẫu nhiên). Sai lệch này nhiều hay ít sẽ do mô hình quyết định. Khi mô hinh cố gắng bám sát nhiễu thì khi dự đoán trên các dữ liệu chưa thấy sẽ bị sai lệch khá nhiều. Đó là lí do các mô hình bị overfitting cho kết quả dự đoán khá tồi. Nguyên nhân dẫn đến việc bám sát dữ liệu nhiễu là do độ phức tạp của mô hình được chọn. Độ phức tạp này có thể là do:
- Số lượng các thuộc tính (feature) quá lớn, khi số lượng các thuộc tính lớn làm cho việc mô phỏng quan hệ của các thuộc tính trở nên phức tạp, trong đó lại có rất nhiều các thuộc tính không cần thiết, gây nhiễu hoặc có tương quan mạnh với nhau thì mô hình càng dễ bị overfitting. Để khắc phục vấn đề này, chúng ta thường sử dụng các phương pháp tiền xử lý để trích chọn đặc trưng, phân tích thành phần chính, phân tích thành phần độc lập,…để tìm ra những đặc trưng cốt lõi nhất của dữ liệu từ đó làm giảm khả năng overfitting của mô hình.
- Mô hình được sử dụng quá phức tạp (quá nhiều tham số) chẳng hạn như: cây quyết định có độ sâu quá lớn hoặc mạng nơ ron sâu. Các mô hình có nhiều tham số (độ phức tạp quá lớn) thường là một nguyên nhân chính dẫn đến overfitting. Trong trường hợp này, việc chúng ta có thể làm để hạn chế overfitting là làm giảm độ phức tạp của mô hình sử dụng, hoặc sử dụng các phương pháp Regularization để phạt bớt độ khớp của mô hình vào dữ liệu. Ngoài ra, chúng ta cũng có thể nâng số lượng dữ liệu huấn luyện lên để tăng tính tổng quát hóa của mô hình, tức là đảm bảo số lượng dữ liệu có độ lớn phù hợp so với độ phức tạp của mô hình.
Ngoài ra có một vấn đề mà rất ít các tài liệu đề cập đến dẫn đến overfitting đó là vấn đề dữ liệu quá nhiễu hoặc các dữ liệu ngoại lai. Chất lượng dữ liệu quá thấp có thể dẫn đến các mô hình dù tốt, dữ liệu dù nhiều nhưng mô hình huấn luyện được vẫn sai bét trên thực tế. Đây là một trong những vấn đề quan trọng trong machine learning. Đó là làm thế nào đảm bảo được chất lượng của dữ liệu. Chúng ta sẽ có hẳn một Phần lớn trong Series này để nói về các vấn đề xử lý dữ liệu và làm sạch dữ liệu. Chúng ta sẽ được đi chi tiết hơn trong các bài sau.
Ở chiều ngược lại, các mô hình underfitting không khớp với ngay cả dữ liệu huấn luyện và có độ sai lệch (bias) khá cao. Nguyên nhân gây ra hiện tượng này là:
- Mô hình quá đơn giản để mô hình hóa các dữ liệu quan sát phức tạp. Mức độ phức tạp ở đây được hiểu như trên tức là bao gồm cả độ phức tạp về số lượng các thuộc tính lẫn mô hình (số tham số của mô hình). Để giải quyết điều này, tất nhiên chúng ta có thể nâng cao độ phức tạp của mô hình lên, hoặc làm lại dữ liệu, thu thập thêm các thuộc tính của dữ liệu.
- Nguyên nhân thứ hai dẫn đến underfitting là số lượng dữ liệu quan sát được cung cấp là hữu hạn và ít hơn rất nhiều so với dữ liệu cần dự đoán (có thể là vô hạn), do đó nhiều khả năng chúng ta sẽ bỏ sót dữ liệu hoặc dữ liệu quan sát không đại diện được cho toàn bộ quan hệ trong bài toán. Chẳng hạn, chúng ta điều tra mức độ hài lòng của nhân viên theo mức lương nhưng chỉ hỏi các vị trí cấp lãnh đạo, chắc hẳn sẽ có vấn đề sai sót. Trong trường hợp này, dù mô hình của chúng ta có tốt đến thế nào cũng chẳng thể tạo ra các dự đoán chính xác trên dữ liệu mới được. Để giải quyết vấn đề này lại rất khó, đó là chúng ta cần phải có những cách thu thập dữ liệu sao cho nó đại diện được cho toàn bộ phân phối của bài toán. Một bài toán khá hóc búa, bởi chúng ta còn chẳng biết dạng phân phối của nó như thế nào!!! Đến đây, có lẽ luật số lớn và các phương pháp thống kê toán học, độ tin cậy…sẽ cho chúng ta nhiều gợi ý.
Lưu ý về độ phức tạp của mô hình:
Các mô hình phương sai cao là ‘phức tạp’ theo một nghĩa nào đó, nhưng điều ngược lại không chắc đã đúng. Chúng ta vẫn thường sai lầm khi cho rằng các mô hình phức tạp phải có phương sai cao, thực tế cũng có rất nhiều mô hình “đơn giản” nhưng phương sai vẫn cao; Ngoài ra, độ phức tạp của mô hình cũng không dễ để xác định. Rất nhiều các mô hình độ phức tạp của nó khó mà xác định được theo số lượng các tham số sử dụng. Chẳng hạn, xét ví dụ: Mô hình \(f_{a, b} (x) = a \sin (bx)\) chỉ có hai tham số (a, b) nhưng nó có thể nội suy một số lượng điểm bất kỳ (10, 1000, 100000…điểm) chỉ bằng cách tăng mức độ dao động với mức tần suất đủ cao. Điều này dẫn đến cả bias và variance đều cao!!! Dù chỉ có 2 tham số !!!! Do đó, hãy rất cẩn thận khi đánh giá độ phức tạp của mô hình qua số tham số. Dù rằng hầu hết các mô hình có độ phức tạp cao thì đều có nhiều tham số.
Làm sao để xác định Underfitting và Overffiting
Trong các phần trên, chúng ta đã nắm được các khái niệm liên quan đến underfitting và overfitting, cũng như bias – variance tradeoff. Tuy nhiên, làm thế nào để phát hiện ra mô hình của chúng ta đang gặp phải vấn đề này?
Về mặt trực quan, các mô hình hồi quy trong ví dụ trên rất dễ dàng để phát hiện mô hình underfitting hay overfitting. Tuy nhiên trên thực tế không phải mô hình nào cũng có thể trực quan như vậy, thậm chí không phải bao giờ cũng có thể biểu diễn để quan sát được quan hệ của dữ liệu. Thêm vào đó, chúng ta cũng chẳng biết mô hình gốc cần dự đoán của nó là gì, do vậy việc xác định overfitting và underfitting là không phải điều dễ dàng.
Chúng ta không thể huấn luyện một đống mô hình sau đó đưa ra để thử trên thực tế được, bởi có nhiều bài toán thử là chết! Như y dược chẳng hạn. Vậy làm thế nào?
Cách đơn giản nhất là chúng ta kiếm một bộ dữ liệu kiểm thử hoặc chọn ra một bộ dữ liệu kiểm thử để tiến hành đối chiếu lại độ chính xác của mô hình. Đây là lí do tại sao chúng ta có khái niệm tập kiểm thử (testing set) và tập huấn luyện (training set).
Trong bài sau, chúng ta sẽ tìm hiểu kỹ hơn về các khái niệm này, cũng như cách thức và phương pháp để đánh giá hiệu quả của các mô hình machine learning.
Tóm lại trong bài này, chúng ta tóm lại một số điểm chính sau:
- Overfitting là trường hợp mô hình quá khớp với dữ liệu huấn luyện mà lại làm việc quá tệ trên dữ liệu thực tế chưa được quan sát, ngược lại underfitting thì tệ trên cả dữ liệu huấn luyện lẫn thực tế.
- Overfitting sinh ra do mô hình quá phức tạp dẫn đến việc bám theo cả nhiễu trong dữ liệu. Underfitting thì do mô hình quá đơn giản nên không biểu diễn được đầy đủ quan hệ trong dữ liệu.
- Bias là độ lệch của dự đoán so với kỳ vọng, bias cao do mô hình thiên vị và không quan tâm đến phân phối của dữ liệu
- Variance là mức độ thay đổi của mô hình trên các bộ dữ liệu huấn luyện khác nhau cùng phân phối, hoặc mức độ thay đổi của dự đoán khi đầu vào của dự đoán chỉ sai khác một lượng rất nhỏ. Variance cao thường do mô hình quá phức tạp làm bắt cả nhiễu trong dữ liệu huấn luyện.
- Mô hình tốt là mô hình cân bằng được các yếu tố bias và variance lúc đó nó sẽ tránh được bị underfitting lẫn overfitting.
Trong bài sau chúng ta sẽ tiếp tục với hai khái niệm Training set và Testing set, cũng như những lưu ý khi đánh giá hiệu quả của mô hình machine learning.