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.

Phần lớn nội dung của chương này được sử dụng để tạo ra tệp dữ liệu lifesat.csv là tập dữ liệu chứa mức độ Hài lòng với cuộc sống (Life satisfaction) dựa trên GDP bình quân đầu người (GDP per capita) của các quốc gia từ các nguồn dữ liệu gốc. Những “phần chính” của hành trình Machine Learning thực sự sẽ bắt đầu từ chương tiếp theo.

Chúng ta cũng sẽ cùng tìm hiểu một số khái niệm cơ bản, và thực hành chạy các đoạn code. Các đoạn code cũng được tối ưu cho môi trường Google Colab - một nền tảng miễn phí do Google cung cấp.

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

Trước khi đi vào chi tiết, chúng ta nên đảm bảo môi trường lập trình đáp ứng đủ các yêu cầu cần thiết. Các đoạn code trong chương này này yêu cầu Python 3.10 trở lên để hoạt động ổn định.

Đoạn mã dưới đây sẽ kiểm tra phiên bản Python của hệ thố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 cần thư viện Scikit-Learn, một công cụ mạnh mẽ cho Machine Learning. Phiên bản yêu cầu là 1.6.1 trở lên.

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")

Để các biểu đồ trực quan hóa dữ liệu hiển thị đẹp mắt và rõ ràng, chúng ta sẽ thiết lập kích thước phông chữ mặc định cho thư viện vẽ đồ thị Matplotlib.

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

Trong ví dụ đầu tiên này, chúng ta sẽ xem xét một bài toán Machine Learning cơ bản: Dự đoán mức độ Hài lòng với cuộc sống (Life satisfaction) dựa trên GDP bình quân đầu người (GDP per capita).

Mô hình được sử dụng ở đây là Hồi quy tuyến tính (Linear Regression). Đây là một mô hình đơn giản, giả định rằng mối quan hệ giữa biến đầu vào (GDP) và biến đầu ra (Hài lòng cuộc sống) là một đường thẳng.

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

  1. Tải dữ liệu và chuẩn bị dữ liệu.
  2. Trực quan hóa dữ liệu dưới dạng biểu đồ phân tán (scatter plot).
  3. Lựa chọn mô hình.
  4. Huấn luyện mô hình (Training).
  5. Thực hiện dự đoán trên dữ liệ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

[[6.01610329]]

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

Nếu bạn muốn sử dụng một mô hình khác, ví dụ như Hồi quy k-Láng giềng gần nhất (k-Nearest Neighbors Regression), việc thay đổi rất đơn giản. Thay vì tìm một đường thẳng, mô hình này sẽ tìm điểm dữ liệu gần nhất với điểm cần dự đoán và lấy trung bình giá trị của chúng.

Trong ví dụ dưới đây, chúng ta chọn (3 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]]
[[5.73333333]]

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

Lưu ý: Phần này minh họa cách tạo ra tập dữ liệu lifesat.csv từ các nguồn dữ liệu thô ban đầu. Bạn có thể bỏ qua nếu chỉ quan tâm đến thuật toán, nhưng nó rất hữu ích để hiểu về quy trình làm sạch dữ liệu thực tế.

Trong thực tế, dữ liệu hiếm khi có sẵn ở định dạng hoàn hảo. Chúng ta thường phải thu thập từ nhiều nguồn, làm sạch và gộp chúng lại. Để tạo ra lifesat.csv, chúng tôi đã tải dữ liệu Chỉ số Cuộc sống Tốt đẹp hơn (Better Life Index - BLI) từ website của OECD và dữ liệu GDP bình quân đầu người từ Ngân hàng Thế giới (World Bank) thông qua OurWorldInData.org.

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

Đoạn mã sau sẽ tải hai tập dữ liệu thô về máy:

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")
Downloading oecd_bli.csv
Downloading gdp_per_capita.csv

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

Dữ liệu GDP chứa thông tin của nhiều năm. Ở ví dụ này, chúng ta sẽ chỉ quan tâm đến năm 2020. Đoạn mã dưới đây sẽ lọc lấy dữ liệu năm 2020, đổi tên cột cho dễ hiểu và thiết lập chỉ mục (index) theo tên quốc gia.

# 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()
                             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

Dữ liệu BLI chứa nhiều chỉ số khác nhau. Chúng ta cần lọc để lấy chỉ số “Life satisfaction” (Sự hài lòng cuộc sống) và loại bỏ các dữ liệu phân nhóm không cần thiết (chỉ giữ lại INEQUALITY="TOT" tức là tổng thể). Sau đó, chúng ta xoay bảng dữ liệu (pivot) để mỗi chỉ số trở thành một cộ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()
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ây giờ, chúng ta sẽ gộp hai bảng dữ liệu lại với nhau dựa trên tên quốc gia. Chúng ta chỉ giữ lại hai cột quan trọng là GDP và Life satisfaction.

# 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()
              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)

Để minh họa rủi ro của việc Quá khớp (Overfitting), trong hầu hết các biểu đồ của chương này, tôi chỉ sử dụng một phần dữ liệu (các quốc gia có GDP nằm trong khoảng min_gdpmax_gdp). Các quốc gia bị loại bỏ (như các nước quá nghèo hoặc quá giàu) sẽ được thảo luận sau, bởi chúng không tuân theo xu hướng tuyến tính chung.

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()
         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 kết quả ra file CSV để tiện sử dụng:

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

Đoạn mã dưới đây vẽ biểu đồ phân tán và đánh dấu tên một số quốc gia cụ thể để làm nổi bật vị trí của chúng trên đồ thị.

# 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à dữ liệu cụ thể của các quốc gia được đánh dấu:

highlighted_countries = country_stats.loc[list(position_text.keys())]
highlighted_countries[[gdppc_col, lifesat_col]].sort_values(by=gdppc_col)
               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)

Trong mô hình tuyến tính, công thức dự đoán có dạng: Life Satisfaction=θ0+θ1×GDP per capita\text{Life Satisfaction} = \theta_0 + \theta_1 \times \text{GDP per capita}

Mô hình này được điều khiển bởi hai tham số (parameters): (hệ số chắn - intercept) và (hệ số góc - slope). Thay đổi các tham số này sẽ tạo ra các đường thẳng khác nhau. Hình dưới đây minh họa ba đường thẳng tương ứng với ba bộ tham số khác nhau. Mục tiêu của Machine Learning là tìm ra bộ sao cho đường thẳng khớp nhất với dữ liệu.

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

Sử dụng thư viện Scikit-Learn, chúng ta có thể tìm ra giá trị tối ưu của các tham số này một cách chính xác.

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}")
θ0=3.75, θ1=6.78e-05

Bây giờ, hãy vẽ đường thẳng “tốt nhất” này chồng lên dữ liệu. Bạn có thể thấy nó đi qua trung tâm của đám mây điểm dữ liệu.

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 (Prediction)

Với mô hình đã huấn luyện, chúng ta có thể dự đoán mức độ hài lòng cuộc sống cho một quốc gia không có trong tập huấn luyện, ví dụ như Puerto Rico.

Đầu tiên, 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
np.float64(33442.8315702748)

Sau đó, đưa giá trị này vào mô hình để dự đoán:

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

Trực quan hóa kết quả dự đoán trên đồ thị:

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)

Điều gì sẽ xảy ra nếu chúng ta đưa thêm các quốc gia đã bị loại bỏ trước đó (những nước có GDP thấp hơn hoặc cao hơn khoảng xét) vào mô hình? Đây là các điểm dữ liệu “thiếu” (missing data).

# 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
              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

Bây giờ, hãy vẽ tất cả dữ liệu lên cùng một biểu đồ. Đường nét đứt màu xanh (b:) là mô hình được huấn luyện trên tập dữ liệu từng phần (partial data). Đường liền màu đen (k) là mô hình được huấn luyện trên toàn bộ dữ liệu (full data).

Bạn có thể thấy rằng khi thêm các quốc gia “ngoại lai” này vào, mối quan hệ tuyến tính không còn rõ ràng, và đường hồi quy thay đổi đáng kể.

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)

Đôi khi, mô hình quá phức tạp so với lượng dữ liệu hiện có. Ví dụ dưới đây sử dụng Hồi quy đa thức (Polynomial Regression) bậc cao (bậc 10). Mô hình này cố gắng uốn lượn để đi qua càng nhiều điểm dữ liệu càng tốt. Hậu quả là nó tạo ra một đường cong rất “điên rồ”, lên xuống thất thường, học cả những nhiễu (noise) trong dữ liệu thay vì học quy luật chung. Đây chính là Overfitting.

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

Dữ liệu có thể có những đặc thù lạ lùng. Ví dụ, hãy xem các quốc gia có chữ “W” trong tên.

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

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)
                            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)

Để giải quyết vấn đề Overfitting, chúng ta có thể sử dụng kỹ thuật Chính quy hóa (Regularization). Kỹ thuật này áp đặt các ràng buộc lên mô hình để ngăn nó trở nên quá phức tạp.

Ví dụ dưới đây sử dụng Ridge Regression (Hồi quy Ridge). Đây là một phiên bản của hồi quy tuyến tính nhưng có thêm một thành phần phạt vào hàm mất mát, buộc các tham số mô hình phải nhỏ lại, làm cho đường hồi quy “phẳng” hơn và ít nhạy cảm với các điểm dữ liệu nhiễu.

Trong biểu đồ dưới:

  • Đường chấm xanh dương (b:): Mô hình tuyến tính thường (Linear Regression) trên dữ liệu một phần.
  • Đường nét đứt xanh dương (b--): Mô hình Ridge (có chính quy hóa) trên dữ liệu một phần.
  • Đường đen (k-): Mô hình tuyến tính trên toàn bộ dữ liệu.

Bạn có thể thấy mô hình Ridge (đường đứt xanh) gần với mô hình toàn cục (đường đen) hơn so với mô hình tuyến tính thường, mặc dù nó cũng chỉ được học trên tập dữ liệu một phần. Điều này cho thấy khả năng tổng quát hóa tốt hơn.

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

1. Machine Learning là gì?

Machine Learning là việc xây dựng các hệ thống có khả năng học hỏi từ dữ liệu. “Học” ở đây nghĩa là cải thiện hiệu suất thực hiện một tác vụ nào đó, thông qua một thước đo hiệu suất cụ thể.

2. Machine Learning hữu dụng khi nào?

Nó tuyệt vời cho:

  • Các vấn đề phức tạp không có giải pháp thuật toán truyền thống.
  • Thay thế các danh sách dài các quy tắc được tinh chỉnh thủ công.
  • Xây dựng hệ thống thích ứng với môi trường biến động.
  • Hỗ trợ con người học hỏi và khám phá tri thức (khai phá dữ liệu - data mining).

3. Tập huấn luyện có nhãn (Labeled training set) là gì?

Là tập dữ liệu huấn luyện mà mỗi mẫu dữ liệu (instance) đều đi kèm với lời giải mong muốn (được gọi là nhãn - label).

4. Hai tác vụ phổ biến nhất của Học có giám sát (Supervised learning)?

Đó là Hồi quy (Regression) (dự đoán giá trị số) và Phân loại (Classification) (dự đoán nhãn/lớp).

5. Các tác vụ phổ biến của Học không giám sát (Unsupervised learning)?

Bao gồm: Phân cụm (Clustering), Trực quan hóa (Visualization), Giảm chiều dữ liệu (Dimensionality reduction), và Học luật kết hợp (Association rule learning).

6. Dùng thuật toán nào để robot học đi lại trên địa hình lạ?

Học tăng cường (Reinforcement Learning) là phù hợp nhất. Robot có thể học qua thử-và-sai để nhận phần thưởng hoặc hình phạt.

7. Phân nhóm khách hàng?

  • Nếu bạn chưa biết các nhóm là gì: Dùng thuật toán Phân cụm (Clustering) (học không giám sát).
  • Nếu bạn đã biết các nhóm mục tiêu: Dùng thuật toán Phân loại (Classification) (học có giám sát).

8. Phát hiện thư rác (Spam detection)?

Đây là bài toán Học có giám sát (Supervised learning) điển hình: mô hình được huấn luyện với các email đã được gán nhãn là “spam” hoặc “không phải spam”.

9. Hệ thống học trực tuyến (Online learning)?

Là hệ thống có thể học một cách tăng dần (incrementally), trái ngược với học theo lô (batch learning). Nó thích hợp khi dữ liệu thay đổi liên tục, cần thích ứng nhanh, hoặc lượng dữ liệu quá lớn không thể chứa hết trong bộ nhớ.

10. Out-of-core learning là gì?

Là giải pháp cho các thuật toán xử lý lượng dữ liệu khổng lồ không vừa bộ nhớ RAM. Nó chia nhỏ dữ liệu thành các mini-batches và sử dụng kỹ thuật học trực tuyến để học từng phần một.

11. Học dựa trên trường hợp (Instance-based learning)?

Hệ thống học thuộc lòng dữ liệu huấn luyện. Khi gặp dữ liệu mới, nó so sánh độ tương đồng (similarity) với các dữ liệu đã học để đưa ra dự đoán.

12. Tham số mô hình (Model parameter) vs Siêu tham số (Hyperparameter)?

  • Tham số mô hình: Là các biến nội tại mà mô hình tự điều chỉnh trong quá trình học (ví dụ: hệ số góc trong hồi quy tuyến tính).
  • Siêu tham số: Là tham số của chính thuật toán học, được thiết lập trước khi huấn luyện và không đổi trong quá trình học (ví dụ: số lượng láng giềng trong k-NN, hoặc hệ số học ).

13. Học dựa trên mô hình (Model-based learning)?

Thuật toán tìm kiếm các giá trị tối ưu cho tham số mô hình để mô hình có khả năng tổng quát hóa tốt trên dữ liệu mới. Quá trình này thường thực hiện bằng cách tối thiểu hóa hàm chi phí (cost function).

14. Các thách thức chính trong Machine Learning?

Thiếu dữ liệu, dữ liệu kém chất lượng, dữ liệu không đại diện, đặc trưng không hữu ích, mô hình quá đơn giản (underfitting), hoặc mô hình quá phức tạp (overfitting).

15. Overfitting và cách khắc phục?

Overfitting là khi mô hình học quá kỹ dữ liệu huấn luyện (bao gồm cả nhiễu) nên hoạt động kém trên dữ liệu mới. Giải pháp: Thêm dữ liệu, đơn giản hóa mô hình (giảm tham số, chọn mô hình khác), sử dụng chính quy hóa (regularization), hoặc giảm nhiễu dữ liệu.

16. Tập kiểm tra (Test set) dùng để làm gì?

Dùng để ước tính sai số tổng quát hóa (generalization error) của mô hình trên dữ liệu chưa từng thấy, trước khi đưa mô hình vào sử dụng thực tế.

17. Tập xác thực (Validation set) dùng để làm gì?

Dùng để so sánh các mô hình khác nhau và tinh chỉnh siêu tham số (hyperparameters), giúp chọn ra mô hình tốt nhất.

18. Train-dev set là gì?

Dùng khi có sự chênh lệch (mismatch) giữa dữ liệu huấn luyện và dữ liệu thực tế. Nó là một phần của tập huấn luyện được tách ra để đánh giá xem mô hình có bị overfitting trên tập huấn luyện hay không, hay vấn đề nằm ở sự khác biệt về phân phối dữ liệu.

19. Tại sao không dùng tập Test để tinh chỉnh siêu tham số?

Nếu làm vậy, bạn sẽ vô tình làm mô hình bị overfitting vào tập Test. Kết quả đánh giá cuối cùng sẽ quá lạc quan và không phản ánh đúng hiệu suất thực tế của mô hình.