Back to Blog

[ML101] Chương 1: Tổng quan về Machine Learning

Giới thiệu tổng quan về Machine Learning, các loại hình học máy và ứng dụng thực tế

Bài viết có tham khảo, sử dụng và sửa đổi tài nguyên từ kho lưu trữ handson-mlp, tuân thủ giấy phép Apache‑2.0. Chúng tôi chân thành cảm ơn tác giả Aurélien Géron (@aureliengeron) vì sự chia sẻ kiến thức tuyệt vời và những đóng góp quý giá cho cộng đồng.

Chào mừng bạn đến với chương đầu tiên của loạt bài về Machine Learning.

Trong chương này, chúng ta sẽ tập trung vào nền tảng của quy trình xử lý dữ liệu và xây dựng mô hình cơ bản. Phần lớn nội dung sẽ được dành cho việc tiền xử lý (pre-processing) để tạo ra tập dữ liệu lifesat.csv – một tập dữ liệu thực tế mô tả mối quan hệ giữa mức độ Hài lòng với cuộc sống (Life satisfaction) và GDP bình quân đầu người (GDP per capita). Đây là bước đệm quan trọng, bởi trong khoa học dữ liệu, việc hiểu và chuẩn bị dữ liệu chiếm phần lớn thời gian và quyết định sự thành bại của mô hình.

Chúng ta sẽ tiếp cận các khái niệm dưới góc độ toán học thống kê và thực hành chạy code trên môi trường Google Colab.

Bạn có thể chạy trực tiếp các đoạn mã code tại: Google Colab.

1. Thiết lập môi trường trên Google Colab

Để đảm bảo tính nhất quán và khả năng tái lập (reproducibility) của các thí nghiệm, việc kiểm soát phiên bản phần mềm là nên làm. Các thuật toán và API trong Machine Learning thay đổi thường xuyên; do đó, chúng ta cần xác nhận môi trường thực thi đáp ứng đúng yêu cầu.

Đoạn mã dưới đây kiểm tra phiên bản Python. Chúng ta yêu cầu Python 3.10+ để tận dụng các tính năng mới về type hinting và tối ưu hóa hiệu năng:

import sys

# Kiểm tra xem phiên bản Python có lớn hơn hoặc bằng 3.10 không
assert sys.version_info >= (3, 10)

Tiếp theo, chúng ta kiểm tra thư viện Scikit-Learn (sklearn). Đây là thư viện thông dụng cho các thuật toán Machine Learning cổ điển. Phiên bản 1.6.1 được yêu cầu để đảm bảo các hàm như LinearRegression hay KNeighborsRegressor hoạt động chính xác như trong Chương này.

from packaging.version import Version
import sklearn

# Kiểm tra phiên bản của thư viện Scikit-Learn
assert Version(sklearn.__version__) >= Version("1.6.1")

Chúng ta cũng thiết lập cấu hình cho Matplotlib để đảm bảo các biểu đồ hiển thị rõ ràng, hỗ trợ việc phân tích định lượng trên đồ thị.

import matplotlib.pyplot as plt

# Thiết lập kích thước phông chữ chung là 12
plt.rc('font', size=12)
# Thiết lập kích thước phông chữ cho nhãn trục (x, y) là 14
plt.rc('axes', labelsize=14, titlesize=14)
# Thiết lập kích thước phông chữ cho chú thích (legend) là 12
plt.rc('legend', fontsize=12)
# Thiết lập kích thước phông chữ cho các vạch chia trên trục x và y là 10
plt.rc('xtick', labelsize=10)
plt.rc('ytick', labelsize=10)

2. Hồi quy tuyến tính đơn giản (Simple Linear Regression)

Chúng ta bắt đầu với bài toán Hồi quy (Regression). Trong thống kê học, hồi quy là phương pháp mô hình hóa mối quan hệ giữa một biến phụ thuộc yy (biến mục tiêu - Life satisfaction) và một hoặc nhiều biến độc lập XX (biến giải thích - GDP per capita).

Mô hình Hồi quy tuyến tính (Linear Regression) giả định mối quan hệ này là tuyến tính, được biểu diễn bằng phương trình:

y^=θ0+θ1x\hat{y} = \theta_0 + \theta_1 x

Trong đó:

  • y^\hat{y} là giá trị dự đoán.
  • xx là giá trị đầu vào (GDP).
  • θ0\theta_0 (bias/intercept) và θ1\theta_1 (weight/slope) là các tham số mô hình cần tìm.

Quy trình thực nghiệm bao gồm:

  1. Data Loading: Tải dữ liệu vào bộ nhớ.
  2. Exploratory Data Analysis (EDA): Trực quan hóa để cảm nhận phân phối dữ liệu.
  3. Model Selection: Chọn giả thuyết (hypothesis space) phù hợp.
  4. Training: Tìm tham số tối ưu θ\theta.
  5. Inference: Dự đoán trên mẫu mới.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression

# Tải và chuẩn bị dữ liệu
data_root = "https://github.com/ageron/data/raw/main/"
# Đọc file CSV chứa dữ liệu về sự hài lòng cuộc sống và GDP
lifesat = pd.read_csv(data_root + "lifesat/lifesat.csv")

# Tách biệt biến đầu vào (X) và biến mục tiêu (y)
# X là GDP bình quân đầu người
X = lifesat[["GDP per capita (USD)"]].values
# y là Chỉ số hài lòng cuộc sống
y = lifesat[["Life satisfaction"]].values

# Trực quan hóa dữ liệu
# Vẽ biểu đồ phân tán (scatter plot) để xem sự phân bố của dữ liệu
lifesat.plot(kind='scatter', grid=True,
             x="GDP per capita (USD)", y="Life satisfaction")
# Thiết lập giới hạn cho các trục để tập trung vào vùng dữ liệu quan trọng
plt.axis([23_500, 62_500, 4, 9])
plt.show()

# Lựa chọn mô hình tuyến tính
model = LinearRegression()

# Huấn luyện mô hình
# Hàm fit() sẽ tìm ra các tham số tối ưu cho mô hình dựa trên dữ liệu X và y
model.fit(X, y)

# Thực hiện dự đoán cho Puerto Rico
# Puerto Rico có GDP bình quân đầu người năm 2020 là khoảng 33,442.8 USD
X_new = [[33_442.8]]
print(model.predict(X_new)) # Kết quả dự đoán: [[6.01610329]]

png

output:
[[6.01610329]]

Phân tích Code và Kết quả:

  • X được định dạng là mảng 2 chiều (matrix) [[value1], [value2], ...] vì Scikit-Learn yêu cầu đầu vào là ma trận đặc trưng (feature matrix) kích thước (nsamples,nfeatures)(n_{samples}, n_{features}).
  • model.fit(X, y): Bản chất toán học ở đây là giải bài toán tối ưu hóa. Cụ thể, thuật toán tìm θ0,θ1\theta_0, \theta_1 để tối thiểu hóa hàm mất mát (Cost Function), thường là Sai số bình phương trung bình (MSE): MSE=1mi=1m(θ0+θ1x(i)y(i))2\text{MSE} = \frac{1}{m} \sum_{i=1}^{m} (\theta_0 + \theta_1 x^{(i)} - y^{(i)})^2
  • Kết quả dự đoán 6.016 cho thấy với mức GDP ~33k USD, mô hình kỳ vọng mức độ hài lòng cuộc sống là khoảng 6/10.

Thay thế bằng mô hình k-Láng giềng gần nhất (k-Nearest Neighbors)

Hồi quy tuyến tính thuộc nhóm Học dựa trên mô hình (Model-based learning) (học tham số). Một cách tiếp cận khác là Học dựa trên trường hợp (Instance-based learning), điển hình là k-NN.

Thay vì cố gắng tìm một hàm toán học tổng quát, k-NN ghi nhớ dữ liệu huấn luyện. Khi cần dự đoán cho điểm mới xnewx_{new}, nó tìm kk điểm dữ liệu gần nhất trong không gian đặc trưng và tính trung bình giá trị mục tiêu của chúng:

y^=1kiNk(xnew)yi\hat{y} = \frac{1}{k} \sum_{i \in N_k(x_{new})} y_i

Trong đó Nk(xnew)N_k(x_{new}) là tập hợp kk láng giềng gần nhất.

# Chọn mô hình Hồi quy 3-Láng giềng gần nhất (3-Nearest Neighbors regression)
from sklearn.neighbors import KNeighborsRegressor

# Khởi tạo mô hình với tham số n_neighbors = 3
model = KNeighborsRegressor(n_neighbors=3)

# Huấn luyện mô hình
# Tương tự như trên, hàm fit() sẽ lưu trữ dữ liệu để dùng cho việc tra cứu sau này
model.fit(X, y)

# Thực hiện dự đoán cho Puerto Rico
print(model.predict(X_new)) # Kết quả dự đoán: [[5.73333333]]
output:
[[5.73333333]]

Phân tích:

  • n_neighbors=3: Đây là một siêu tham số (hyperparameter). Nếu k=1k=1, mô hình rất nhạy với nhiễu (overfitting). Nếu kk quá lớn, mô hình trở nên quá mượt và mất đi các chi tiết cục bộ (underfitting).
  • Kết quả dự đoán là 5.733, thấp hơn một chút so với hồi quy tuyến tính, phản ánh sự khác biệt trong cách tiếp cận của hai thuật toán đối với cấu trúc dữ liệu cục bộ.

3. Quy trình xử lý và tạo dữ liệu (Data Engineering Pipeline)

Phần này minh họa quy trình ETL (Extract, Transform, Load) để tạo ra tập dữ liệu lifesat.csv. Đây là kỹ năng cốt lõi của Data Scientist.

Dữ liệu thực tế thường phân tán, nhiễu và không đồng nhất. Để xây dựng tập dữ liệu sạch, chúng ta cần kết hợp dữ liệu từ OECD (Chỉ số cuộc sống) và World Bank (GDP).

Tải và chuẩn bị dữ liệu

Sử dụng thư viện chuẩn urllib để tải dữ liệu thô từ repository.

from pathlib import Path
import urllib.request

# Tạo đường dẫn thư mục để lưu dữ liệu
datapath = Path() / "datasets" / "lifesat"
datapath.mkdir(parents=True, exist_ok=True)

data_root = "https://github.com/ageron/data/raw/main/"

# Vòng lặp tải các file csv cần thiết nếu chưa tồn tại
for filename in ("oecd_bli.csv", "gdp_per_capita.csv"):
    if not (datapath / filename).is_file():
        print("Downloading", filename)
        url = data_root + "lifesat/" + filename
        urllib.request.urlretrieve(url, datapath / filename)

# Đọc dữ liệu vào DataFrame của pandas
oecd_bli = pd.read_csv(datapath / "oecd_bli.csv")
gdp_per_capita = pd.read_csv(datapath / "gdp_per_capita.csv")
output:
Downloading oecd_bli.csv
Downloading gdp_per_capita.csv

Tiền xử lý dữ liệu GDP

Dữ liệu chuỗi thời gian (time-series) của GDP cần được cắt lát (slicing) để lấy một thời điểm cụ thể (snapshot) nhằm phù hợp với bài toán hồi quy tĩnh.

# Chỉ lấy dữ liệu năm 2020
gdp_year = 2020
gdppc_col = "GDP per capita (USD)"
lifesat_col = "Life satisfaction"

# Lọc dữ liệu theo năm
gdp_per_capita = gdp_per_capita[gdp_per_capita["Year"] == gdp_year]
# Bỏ các cột không cần thiết
gdp_per_capita = gdp_per_capita.drop(["Code", "Year"], axis=1)
# Đổi tên cột
gdp_per_capita.columns = ["Country", gdppc_col]
# Đặt tên quốc gia làm chỉ mục (index)
gdp_per_capita.set_index("Country", inplace=True)

# Hiển thị 5 dòng đầu tiên
gdp_per_capita.head()
output:
                             GDP per capita (USD)
Country                                          
Afghanistan                           1978.961579
Africa Eastern and Southern           3387.594670
Africa Western and Central            4003.158913
Albania                              13295.410885
Algeria                              10681.679297

Tiền xử lý dữ liệu OECD BLI

Bảng dữ liệu OECD BLI đang ở dạng “dài” (long format), trong đó các chỉ số nằm trong cùng một cột Indicator. Chúng ta cần xoay (pivot) bảng này sang dạng “rộng” (wide format) để mỗi chỉ số (như Life Satisfaction) trở thành một đặc trưng (feature) riêng biệt.

# Lọc dữ liệu tổng thể (TOT)
oecd_bli = oecd_bli[oecd_bli["INEQUALITY"]=="TOT"]
# Xoay bảng dữ liệu để mỗi chỉ báo (Indicator) thành một cột
oecd_bli = oecd_bli.pivot(index="Country", columns="Indicator", values="Value")

# Hiển thị 5 dòng đầu tiên
oecd_bli.head()
output:
Indicator  Air pollution  Dwellings without basic facilities  \
Country                                                        
Australia            5.0                                 NaN   
Austria             16.0                                 0.9   
Belgium             15.0                                 1.9   
Brazil              10.0                                 6.7   
Canada               7.0                                 0.2   

Indicator  Educational attainment  Employees working very long hours  \
Country                                                                
Australia                    81.0                              13.04   
Austria                      85.0                               6.66   
Belgium                      77.0                               4.75   
Brazil                       49.0                               7.13   
Canada                       91.0                               3.69   

Indicator  Employment rate  Feeling safe walking alone at night  \
Country                                                           
Australia             73.0                                 63.5   
Austria               72.0                                 80.6   
Belgium               63.0                                 70.1   
Brazil                61.0                                 35.6   
Canada                73.0                                 82.2   

Indicator  Homicide rate  Household net adjusted disposable income  \
Country                                                              
Australia            1.1                                   32759.0   
Austria              0.5                                   33541.0   
Belgium              1.0                                   30364.0   
Brazil              26.7                                       NaN   
Canada               1.3                                   30854.0   

Indicator  Household net wealth  Housing expenditure  ...  Personal earnings  \
Country                                               ...                      
Australia              427064.0                 20.0  ...            49126.0   
Austria                308325.0                 21.0  ...            50349.0   
Belgium                386006.0                 21.0  ...            49675.0   
Brazil                      NaN                  NaN  ...                NaN   
Canada                 423849.0                 22.0  ...            47622.0   

Indicator  Quality of support network  Rooms per person  Self-reported health  \
Country                                                                         
Australia                        95.0               NaN                  85.0   
Austria                          92.0               1.6                  70.0   
Belgium                          91.0               2.2                  74.0   
Brazil                           90.0               NaN                   NaN   
Canada                           93.0               2.6                  88.0   

Indicator  Stakeholder engagement for developing regulations  Student skills  \
Country                                                                        
Australia                                                2.7           502.0   
Austria                                                  1.3           492.0   
Belgium                                                  2.0           503.0   
Brazil                                                   2.2           395.0   
Canada                                                   2.9           523.0   

Indicator  Time devoted to leisure and personal care  Voter turnout  \
Country                                                               
Australia                                      14.35           91.0   
Austria                                        14.55           80.0   
Belgium                                        15.70           89.0   
Brazil                                           NaN           79.0   
Canada                                         14.56           68.0   

Indicator  Water quality  Years in education  
Country                                       
Australia           93.0                21.0  
Austria             92.0                17.0  
Belgium             84.0                19.3  
Brazil              73.0                16.2  
Canada              91.0                17.3  

[5 rows x 24 columns]

Gộp dữ liệu (Merging)

Bước này thực hiện phép giao (intersection) giữa hai tập dữ liệu dựa trên khóa chính là Country.

# Gộp hai bảng dữ liệu dựa trên index (tên quốc gia)
full_country_stats = pd.merge(left=oecd_bli, right=gdp_per_capita,
                              left_index=True, right_index=True)
# Sắp xếp theo GDP tăng dần
full_country_stats.sort_values(by=gdppc_col, inplace=True)
# Chỉ giữ lại hai cột quan trọng
full_country_stats = full_country_stats[[gdppc_col, lifesat_col]]

# Hiển thị 5 dòng đầu tiên
full_country_stats.head()
output:
              GDP per capita (USD)  Life satisfaction
Country                                              
South Africa          11466.189672                4.7
Colombia              13441.492952                6.3
Brazil                14063.982505                6.4
Mexico                17887.750736                6.5
Chile                 23324.524751                6.5

Lọc dữ liệu để minh họa vấn đề Quá khớp (Overfitting)

Chúng ta chủ đích loại bỏ các giá trị ngoại lai (outliers) hoặc các điểm dữ liệu cực đoan (quá giàu hoặc quá nghèo) trong bước này. Mục đích sư phạm là để tạo ra một tập dữ liệu có xu hướng tuyến tính rõ ràng, từ đó dễ dàng minh họa mô hình Linear Regression. Nếu giữ lại toàn bộ, mối quan hệ sẽ phi tuyến tính phức tạp hơn nhiều.

min_gdp = 23_500
max_gdp = 62_500

# Lọc các quốc gia có GDP nằm trong khoảng xác định
country_stats = full_country_stats[(full_country_stats[gdppc_col] >= min_gdp) &
                                   (full_country_stats[gdppc_col] <= max_gdp)]
country_stats.head()
output:
         GDP per capita (USD)  Life satisfaction
Country                                         
Russia           26456.387938                5.8
Greece           27287.083401                5.4
Turkey           28384.987785                5.5
Latvia           29932.493910                5.9
Hungary          31007.768407                5.6

Lưu trữ dữ liệu đã xử lý để đảm bảo tính tái lập cho các thí nghiệm sau:

country_stats.to_csv(datapath / "lifesat.csv")
full_country_stats.to_csv(datapath / "lifesat_full.csv")

Trực quan hóa dữ liệu đã lọc

Biểu đồ này đánh dấu vị trí của một số quốc gia đại diện. Việc này giúp chúng ta có cái nhìn trực quan về mối tương quan dương (positive correlation) giữa GDP và độ hài lòng cuộc sống trong khoảng thu nhập trung bình-cao này.

# Vẽ biểu đồ phân tán cơ bản
country_stats.plot(kind='scatter', figsize=(5, 3), grid=True,
                   x=gdppc_col, y=lifesat_col)

min_life_sat = 4
max_life_sat = 9

# Danh sách vị trí text chú thích cho các quốc gia
position_text = {
    "Turkey": (29_500, 4.2),
    "Hungary": (28_000, 6.9),
    "France": (40_000, 5),
    "New Zealand": (28_000, 8.2),
    "Australia": (50_000, 5.5),
    "United States": (59_000, 5.3),
    "Denmark": (46_000, 8.5)}

# Vòng lặp để vẽ mũi tên và tên quốc gia
for country, pos_text in position_text.items():
    pos_data_x = country_stats[gdppc_col].loc[country]
    pos_data_y = country_stats[lifesat_col].loc[country]
    country = "US" if country == "United States" else country
    plt.annotate(country, xy=(pos_data_x, pos_data_y),
                 xytext=pos_text, fontsize=12,
                 arrowprops=dict(facecolor='black', width=0.5,
                                 shrink=0.08, headwidth=5))
    plt.plot(pos_data_x, pos_data_y, "ro") # Đánh dấu điểm màu đỏ

plt.axis([min_gdp, max_gdp, min_life_sat, max_life_sat])
plt.show()

png

Dưới đây là bảng dữ liệu chi tiết cho các điểm được đánh dấu trên biểu đồ:

highlighted_countries = country_stats.loc[list(position_text.keys())]
highlighted_countries[[gdppc_col, lifesat_col]].sort_values(by=gdppc_col)
output:
               GDP per capita (USD)  Life satisfaction
Country                                               
Turkey                 28384.987785                5.5
Hungary                31007.768407                5.6
France                 42025.617373                6.5
New Zealand            42404.393738                7.3
Australia              48697.837028                7.3
Denmark                55938.212809                7.6
United States          60235.728492                6.9

4. Tham số mô hình (Model Parameters)

Một mô hình tuyến tính được định nghĩa hoàn toàn bởi các tham số của nó. Với một biến đầu vào, chúng ta có phương trình đường thẳng: Life Satisfaction=θ0+θ1×GDP per capita\text{Life Satisfaction} = \theta_0 + \theta_1 \times \text{GDP per capita}

  • θ0\theta_0 (Intercept): Điểm cắt trục tung. Giá trị dự đoán khi GDP = 0.
  • θ1\theta_1 (Slope): Độ dốc. Mức tăng kỳ vọng của độ hài lòng khi GDP tăng thêm 1 đơn vị.

Mục tiêu của thuật toán học là tìm bộ tham số (θ0,θ1)(\theta_0, \theta_1) tối ưu. Hình dưới đây minh họa ba giả thuyết (hypothesis) khác nhau. Đường màu xanh dương có vẻ khớp dữ liệu nhất, trong khi đường đỏ và xanh lá cây có sai số rất lớn.

country_stats.plot(kind='scatter', figsize=(5, 3), grid=True,
                   x=gdppc_col, y=lifesat_col)

X = np.linspace(min_gdp, max_gdp, 1000)

# Đường màu đỏ: tham số ngẫu nhiên
w1, w2 = 4.2, 0
plt.plot(X, w1 + w2 * 1e-5 * X, "r")
plt.text(40_000, 4.9, fr"$\theta_0 = {w1}$", color="r")
plt.text(40_000, 4.4, fr"$\theta_1 = {w2}$", color="r")

# Đường màu xanh lá: tham số ngẫu nhiên khác
w1, w2 = 10, -9
plt.plot(X, w1 + w2 * 1e-5 * X, "g")
plt.text(26_000, 8.5, fr"$\theta_0 = {w1}$", color="g")
plt.text(26_000, 8.0, fr"$\theta_1 = {w2} \times 10^{{-5}}$", color="g")

# Đường màu xanh dương: tham số có vẻ hợp lý hơn
w1, w2 = 3, 8
plt.plot(X, w1 + w2 * 1e-5 * X, "b")
plt.text(48_000, 8.5, fr"$\theta_0 = {w1}$", color="b")
plt.text(48_000, 8.0, fr"$\theta_1 = {w2} \times 10^{{-5}}$", color="b")

plt.axis([min_gdp, max_gdp, min_life_sat, max_life_sat])
plt.show()

png

Tìm tham số tối ưu (Optimization)

Trong Scikit-Learn, hàm fit() sử dụng phương pháp Bình phương Tối thiểu thường (Ordinary Least Squares - OLS). Về mặt toán học, nó giải hệ phương trình để tìm nghiệm chính xác giúp giảm thiểu tổng bình phương sai số dư (Residual Sum of Squares - RSS).

from sklearn import linear_model

X_sample = country_stats[[gdppc_col]].values
y_sample = country_stats[[lifesat_col]].values

# Khởi tạo và huấn luyện mô hình
lin1 = linear_model.LinearRegression()
lin1.fit(X_sample, y_sample)

# Lấy ra tham số đã học được
t0, t1 = lin1.intercept_[0], lin1.coef_[0][0]
print(f"θ0={t0:.2f}, θ1={t1:.2e}")
output:
θ0=3.75, θ1=6.78e-05

Kết quả θ16.78×105\theta_1 \approx 6.78 \times 10^{-5} cho thấy một mối tương quan dương nhỏ: khi GDP tăng, sự hài lòng có xu hướng tăng, nhưng độ dốc không quá lớn do đơn vị của GDP là USD (giá trị lớn).

Dưới đây là trực quan hóa đường hồi quy tối ưu chồng lên dữ liệu thực tế:

country_stats.plot(kind='scatter', figsize=(5, 3), grid=True,
                   x=gdppc_col, y=lifesat_col)

X = np.linspace(min_gdp, max_gdp, 1000)

# Vẽ đường hồi quy tuyến tính với tham số t0, t1 vừa tìm được
plt.plot(X, t0 + t1 * X, "b")

plt.text(max_gdp - 20_000, min_life_sat + 1.9,
         fr"$\theta_0 = {t0:.2f}$", color="b")
plt.text(max_gdp - 20_000, min_life_sat + 1.3,
         fr"$\theta_1 = {t1 * 1e5:.2f} \times 10^{{-5}}$", color="b")

plt.axis([min_gdp, max_gdp, min_life_sat, max_life_sat])
plt.show()

png

Dự đoán (Inference)

Khi mô hình đã học được θ\theta, việc dự đoán chỉ đơn giản là phép nhân ma trận (hoặc thay số vào công thức).

Lấy dữ liệu GDP của Puerto Rico:

puerto_rico_gdp_per_capita = gdp_per_capita[gdppc_col].loc["Puerto Rico"]
puerto_rico_gdp_per_capita
output:
np.float64(33442.8315702748)

Áp dụng mô hình:

puerto_rico_predicted_life_satisfaction = lin1.predict(
    [[puerto_rico_gdp_per_capita]])[0, 0]
puerto_rico_predicted_life_satisfaction
output:
np.float64(6.016105434473317)

Hình dưới đây minh họa quá trình suy diễn: tìm giá trị xx của Puerto Rico trên trục hoành, gióng lên đường hồi quy (đường xanh), và tìm giá trị yy tương ứng trên trục tung.

country_stats.plot(kind='scatter', figsize=(5, 3), grid=True,
                   x=gdppc_col, y=lifesat_col)

X = np.linspace(min_gdp, max_gdp, 1000)
plt.plot(X, t0 + t1 * X, "b")

plt.text(min_gdp + 22_000, max_life_sat - 1.1,
         fr"$\theta_0 = {t0:.2f}$", color="b")
plt.text(min_gdp + 22_000, max_life_sat - 0.6,
         fr"$\theta_1 = {t1 * 1e5:.2f} \times 10^{{-5}}$", color="b")

# Vẽ đường gióng thể hiện dự đoán cho Puerto Rico
plt.plot([puerto_rico_gdp_per_capita, puerto_rico_gdp_per_capita],
         [min_life_sat, puerto_rico_predicted_life_satisfaction], "r--")
plt.text(puerto_rico_gdp_per_capita + 1000, 5.0,
         fr"Prediction = {puerto_rico_predicted_life_satisfaction:.2f}",
         color="r")
plt.plot(puerto_rico_gdp_per_capita, puerto_rico_predicted_life_satisfaction,
         "ro")

plt.axis([min_gdp, max_gdp, min_life_sat, max_life_sat])
plt.show()

png

5. Sự đại diện của dữ liệu (Data Representativeness)

Một vấn đề cốt yếu trong thống kê là Bias mẫu (Sampling Bias). Mô hình của chúng ta hoạt động tốt trên các quốc gia trung bình-khá, nhưng liệu nó có tổng quát hóa được cho toàn thế giới?

Hãy xem xét các quốc gia “ngoại lai” mà chúng ta đã lọc bỏ trước đó (các nước rất nghèo hoặc rất giàu).

# Lấy các quốc gia nằm ngoài khoảng min_gdp và max_gdp
missing_data = full_country_stats[(full_country_stats[gdppc_col] < min_gdp) |
                                  (full_country_stats[gdppc_col] > max_gdp)]
missing_data
output:
              GDP per capita (USD)  Life satisfaction
Country                                              
South Africa          11466.189672                4.7
Colombia              13441.492952                6.3
Brazil                14063.982505                6.4
Mexico                17887.750736                6.5
Chile                 23324.524751                6.5
Norway                63585.903514                7.6
Switzerland           68393.306004                7.5
Ireland               89688.956958                7.0
Luxembourg           110261.157353                6.9

Khi đưa các dữ liệu này vào biểu đồ, chúng ta thấy rõ tính phi tuyến tính.

  • Ở các nước nghèo, GDP tăng nhẹ cũng làm tăng đáng kể sự hài lòng.
  • Ở các nước rất giàu, quy luật “thu hoạch giảm dần” (diminishing returns) xuất hiện: GDP tăng thêm không làm tăng nhiều sự hài lòng, thậm chí có thể giảm.

Biểu đồ dưới so sánh hai mô hình:

  1. Đường chấm xanh (Partial Data): Mô hình cũ, bị bias do thiếu dữ liệu.
  2. Đường đen (Full Data): Mô hình mới huấn luyện trên toàn bộ dữ liệu.

Đường đen có độ dốc thấp hơn và chặn trên cao hơn, cố gắng trung hòa giữa các nhóm dữ liệu nhưng vẫn không thể khớp tốt hoàn toàn do bản chất dữ liệu là phi tuyến tính (cong), trong khi mô hình là tuyến tính (thẳng). Đây là ví dụ về Underfitting (Mô hình quá đơn giản so với dữ liệu).

position_text_missing_countries = {
    "South Africa": (20_000, 4.2),
    "Colombia": (6_000, 8.2),
    "Brazil": (18_000, 7.8),
    "Mexico": (24_000, 7.4),
    "Chile": (30_000, 7.0),
    "Norway": (51_000, 6.2),
    "Switzerland": (62_000, 5.7),
    "Ireland": (81_000, 5.2),
    "Luxembourg": (92_000, 4.7),}

full_country_stats.plot(kind='scatter', figsize=(8, 3),
                        x=gdppc_col, y=lifesat_col, grid=True)

# Đánh dấu các quốc gia bị thiếu
for country, pos_text in position_text_missing_countries.items():
    pos_data_x, pos_data_y = missing_data.loc[country]
    plt.annotate(country, xy=(pos_data_x, pos_data_y),
                 xytext=pos_text, fontsize=12,
                 arrowprops=dict(facecolor='black', width=0.5,
                                 shrink=0.08, headwidth=5))
    plt.plot(pos_data_x, pos_data_y, "rs")

# Đường hồi quy trên tập dữ liệu một phần (cũ)
X = np.linspace(0, 115_000, 1000)
plt.plot(X, t0 + t1 * X, "b:")

# Huấn luyện mô hình mới trên toàn bộ dữ liệu
lin_reg_full = linear_model.LinearRegression()
Xfull = np.c_[full_country_stats[gdppc_col]]
yfull = np.c_[full_country_stats[lifesat_col]]

lin_reg_full.fit(Xfull, yfull)
t0full, t1full = lin_reg_full.intercept_[0], lin_reg_full.coef_[0][0]

# Đường hồi quy trên toàn bộ dữ liệu (mới)
X = np.linspace(0, 115_000, 1000)
plt.plot(X, t0full + t1full * X, "k")

plt.axis([0, 115_000, min_life_sat, max_life_sat])
plt.show()

png

6. Vấn đề Quá khớp (Overfitting)

Ngược lại với Underfitting là Overfitting. Điều này xảy ra khi mô hình quá phức tạp, có quá nhiều bậc tự do (degrees of freedom), dẫn đến việc nó “học thuộc lòng” cả những nhiễu (noise) trong dữ liệu huấn luyện thay vì học quy luật tổng quát.

Ví dụ dưới đây sử dụng Hồi quy đa thức (Polynomial Regression) bậc 10: y=θ0+θ1x+θ2x2++θ10x10y = \theta_0 + \theta_1 x + \theta_2 x^2 + \dots + \theta_{10} x^{10}

Với bậc cao như vậy, đường cong có thể uốn lượn dữ dội để đi qua hầu hết các điểm dữ liệu. Mặc dù sai số trên tập huấn luyện rất thấp, nhưng khả năng dự đoán trên dữ liệu mới (ví dụ: các khoảng trống giữa các điểm) sẽ cực kỳ tệ.

Code sử dụng Pipeline để kết hợp các bước:

  1. PolynomialFeatures: Tạo ra các biến mũ x2,x3...x^2, x^3....
  2. StandardScaler: Chuẩn hóa dữ liệu (quan trọng với hồi quy đa thức để tránh bùng nổ giá trị số).
  3. LinearRegression: Hồi quy tuyến tính trên không gian đặc trưng mới.
from sklearn import preprocessing
from sklearn import pipeline

full_country_stats.plot(kind='scatter', figsize=(8, 3),
                        x=gdppc_col, y=lifesat_col, grid=True)

# Tạo đặc trưng đa thức bậc 10
poly = preprocessing.PolynomialFeatures(degree=10, include_bias=False)
scaler = preprocessing.StandardScaler()
lin_reg2 = linear_model.LinearRegression()

# Tạo một pipeline xử lý: Tạo đa thức -> Chuẩn hóa dữ liệu -> Hồi quy tuyến tính
pipeline_reg = pipeline.Pipeline([
    ('poly', poly),
    ('scal', scaler),
    ('lin', lin_reg2)])

# Huấn luyện
pipeline_reg.fit(Xfull, yfull)
curve = pipeline_reg.predict(X[:, np.newaxis])

# Vẽ đường cong dự đoán
plt.plot(X, curve)
plt.axis([0, 115_000, min_life_sat, max_life_sat])
plt.show()

png

Overfitting cũng có thể xảy ra khi chúng ta cố tìm các quy luật ngẫu nhiên không có ý nghĩa thống kê. Ví dụ: “Các quốc gia có chữ W trong tên thì có mức độ hài lòng cao”. Đây là sự trùng hợp ngẫu nhiên (spurious correlation).

w_countries = [c for c in full_country_stats.index if "W" in c.upper()]
full_country_stats.loc[w_countries][lifesat_col]
output:
             Life satisfaction
Country                       
New Zealand                7.3
Sweden                     7.3
Norway                     7.6
Switzerland                7.5
dtype: float64

Nhưng khi nhìn vào dữ liệu toàn cầu, quy luật này vỡ vụn (các nước nghèo như Rwanda, Zimbabwe cũng có chữ W). Nếu mô hình học quy luật này, nó sẽ dự đoán sai hoàn toàn cho Rwanda.

all_w_countries = [c for c in gdp_per_capita.index if "W" in c.upper()]
gdp_per_capita.loc[all_w_countries].sort_values(by=gdppc_col)
output:
                            GDP per capita (USD)
Country                                         
Malawi                               1486.778248
Rwanda                               2098.710362
Zimbabwe                             2744.690758
Africa Western and Central           4003.158913
Papua New Guinea                     4101.218882
Lower middle income                  6722.809932
Eswatini                             8392.717564
Low & middle income                 10293.855325
Arab World                          13753.707307
Botswana                            16040.008473
World                               16194.040310
New Zealand                         42404.393738
Sweden                              50683.323510
Norway                              63585.903514
Switzerland                         68393.306004

7. Chính quy hóa (Regularization)

Để chống lại Overfitting mà không cần giảm bậc của mô hình một cách thủ công, chúng ta sử dụng Chính quy hóa (Regularization). Kỹ thuật này thêm một thành phần phạt (penalty term) vào hàm mất mát để hạn chế độ lớn của các tham số θ\theta.

Ridge Regression (Hồi quy Ridge) tối thiểu hóa hàm chi phí sau: J(θ)=MSE(θ)+α12i=1nθi2J(\theta) = \text{MSE}(\theta) + \alpha \frac{1}{2} \sum_{i=1}^{n} \theta_i^2

Trong đó:

  • Thành phần đầu là sai số dự đoán.
  • Thành phần sau là hình phạt L2 (bình phương trọng số).
  • α\alpha (alpha) là siêu tham số kiểm soát mức độ phạt.

Nếu α\alpha rất lớn, thuật toán buộc các θi\theta_i phải rất nhỏ (gần bằng 0), làm cho đường hồi quy trở nên phẳng hơn, ít nhạy cảm với dữ liệu huấn luyện, từ đó giảm phương sai (variance) nhưng tăng độ lệch (bias).

Trong biểu đồ dưới:

  • blue dotted: Mô hình tuyến tính thường trên dữ liệu cục bộ (Rất dốc, biased).
  • blue dashed: Mô hình Ridge (α=109.5\alpha=10^{9.5}) trên dữ liệu cục bộ. Đường này phẳng hơn và gần với đường đen (mô hình toàn cục) hơn. Điều này chứng minh Regularization giúp mô hình tổng quát hóa tốt hơn ngay cả khi học trên dữ liệu không đầy đủ.
country_stats.plot(kind='scatter', x=gdppc_col, y=lifesat_col, figsize=(8, 3))
missing_data.plot(kind='scatter', x=gdppc_col, y=lifesat_col,
                  marker="s", color="r", grid=True, ax=plt.gca())

X = np.linspace(0, 115_000, 1000)

# Vẽ mô hình tuyến tính thường (trên dữ liệu một phần)
plt.plot(X, t0 + t1*X, "b:", label="Linear model on partial data")
# Vẽ mô hình tuyến tính trên toàn bộ dữ liệu
plt.plot(X, t0full + t1full * X, "k-", label="Linear model on all data")

# Huấn luyện mô hình Ridge với alpha lớn (chính quy hóa mạnh)
ridge = linear_model.Ridge(alpha=10**9.5)
X_sample = country_stats[[gdppc_col]]
y_sample = country_stats[[lifesat_col]]
ridge.fit(X_sample, y_sample)

# Lấy tham số và vẽ
t0ridge, t1ridge = ridge.intercept_[0], ridge.coef_[0]
plt.plot(X, t0ridge + t1ridge * X, "b--",
         label="Regularized linear model on partial data")

plt.legend(loc="lower right")
plt.axis([0, 115_000, min_life_sat, max_life_sat])
plt.show()

png

8. Ôn tập kiến thức cốt lõi

1. Định nghĩa Machine Learning (ML)? ML là lĩnh vực nghiên cứu giúp máy tính có khả năng học từ dữ liệu mà không cần được lập trình rõ ràng cho từng quy tắc cụ thể. Theo Tom Mitchell: Một chương trình được coi là học từ kinh nghiệm E đối với tác vụ T và đo lường hiệu suất P, nếu hiệu suất P của nó ở tác vụ T được cải thiện theo kinh nghiệm E.

2. Ứng dụng chính của ML? Giải quyết các bài toán phức tạp không có thuật toán cố định (nhận dạng giọng nói), môi trường thay đổi liên tục, hoặc khai phá tri thức từ dữ liệu lớn (Data Mining).

3. Labeled training set là gì? Là tập dữ liệu trong Học có giám sát (Supervised Learning), nơi mỗi mẫu dữ liệu (x)(x) đi kèm với nhãn đúng (y)(y) mong muốn.

4. Hai dạng bài toán phổ biến nhất của Học có giám sát?

  • Regression (Hồi quy): Dự đoán giá trị liên tục (số).
  • Classification (Phân loại): Dự đoán nhãn rời rạc (lớp).

5. Các tác vụ của Học không giám sát (Unsupervised Learning)? Clustering (Phân cụm), Visualization (Trực quan hóa), Dimensionality Reduction (Giảm chiều dữ liệu), Anomaly Detection (Phát hiện bất thường), Association Rule Learning (Học luật kết hợp).

6. Thuật toán cho robot tự hành? Reinforcement Learning (Học tăng cường). Robot học thông qua cơ chế Thưởng/Phạt (Reward/Penalty) khi tương tác với môi trường.

7. Phân nhóm khách hàng (Customer Segmentation)? Dùng Clustering nếu bạn chưa biết các nhóm là gì. Dùng Classification nếu bạn đã định nghĩa trước các nhóm (ví dụ: “VIP”, “Tiềm năng”, “Rời bỏ”) và có dữ liệu lịch sử.

8. Spam Detection là bài toán gì? Supervised Learning (Classification: Spam hoặc Non-Spam).

9. Online Learning (Học trực tuyến)? Hệ thống học liên tục từ dòng dữ liệu đến (data stream). Thích hợp cho dữ liệu biến đổi theo thời gian hoặc khi dữ liệu quá lớn không thể chứa trong RAM (Out-of-core learning).

10. Out-of-core learning? Kỹ thuật xử lý dữ liệu lớn hơn bộ nhớ chính bằng cách chia nhỏ dữ liệu, huấn luyện từng phần (thường dùng Online Learning algorithms).

11. Instance-based Learning? Hệ thống không xây dựng mô hình tổng quát mà ghi nhớ dữ liệu. Dự đoán dựa trên độ đo tương đồng (similarity measure) với các dữ liệu đã nhớ (ví dụ: k-NN).

12. Model parameter vs. Hyperparameter?

  • Model parameter: Được học từ dữ liệu trong quá trình training (ví dụ: weights θ\theta).
  • Hyperparameter: Được thiết lập trước khi training, điều khiển hành vi của thuật toán (ví dụ: learning rate α\alpha, số láng giềng kk, độ mạnh regularization).

13. Model-based Learning? Xây dựng một mô hình toán học đại diện cho dữ liệu. Quá trình học là tối ưu hóa các tham số của mô hình để giảm thiểu hàm mục tiêu (cost function).

14. Thách thức chính của ML? Dữ liệu thiếu (Insufficient quantity), dữ liệu kém chất lượng (Non-representative, Noise), Overfitting (Mô hình quá phức tạp), Underfitting (Mô hình quá đơn giản).

15. Cách xử lý Overfitting?

  • Thu thập thêm dữ liệu.
  • Giảm độ phức tạp mô hình (giảm bậc đa thức, chọn mô hình đơn giản hơn).
  • Regularization (L1, L2).
  • Giảm nhiễu (làm sạch dữ liệu).

16. Test Set dùng để làm gì? Đánh giá khách quan hiệu suất của mô hình cuối cùng (Generalization Error). Không bao giờ được huấn luyện trên tập này.

17. Validation Set dùng để làm gì? Dùng để so sánh hiệu suất giữa các mô hình khác nhau và tinh chỉnh Hyperparameters.

18. Train-Dev Set? Dùng khi dữ liệu training và dữ liệu thực tế (production) có phân phối khác nhau. Giúp chẩn đoán xem lỗi đến từ Overfitting hay do Data Mismatch.

19. Tại sao không chỉnh Hyperparameter trên Test Set? Vì điều đó sẽ làm mô hình “học lỏm” tập Test (Data Leakage), dẫn đến kết quả đánh giá quá lạc quan so với thực tế.