⭐ Vision Engineer · Chặng 10Bài 1/12Bài học~60 phút

Machine Learning là gì? Luật viết tay, học máy và các loại bài toán nhà máy

Phân biệt lập trình truyền thống và học máy qua ví dụ OK/NG chạy được, gọi đúng tên bài toán phân loại, hồi quy, phân cụm, bất thường và quy trình dự án ML.

Machine Learning là gì? Luật viết tay, học máy và các loại bài toán nhà máy

Học xong bài này, bạn sẽ

  • Phân biệt lập trình truyền thống và học máy
  • Biết các loại bài toán học máy
  • Hiểu quy trình một dự án ML

Nên học trước: Dự án chặng 9: trạm vision hoàn chỉnh với PLC mô phỏng

Nội dung bài viết
  1. 1. Lập trình truyền thống và học máy
  2. 2. Ví dụ chạy được: luật một ngưỡng so với mô hình học
  3. 3. Thuật ngữ cần nắm
  4. 4. Các loại bài toán học máy
  5. 5. AI, Machine Learning và Deep Learning
  6. 6. Quy trình một dự án Machine Learning trong nhà máy
  7. 7. Khi nào không nên dùng học máy
  8. 8. Sai lầm thường gặp
  9. 9. Bài tập
  10. Tóm tắt

Chín chặng trước, mọi quyết định OK/NG trong trạm vision của bạn đều do bạn viết luật: ngưỡng nhị phân, diện tích blob, khoảng cách đo được so với dung sai. Cách đó nhanh, dễ giải thích và vẫn là lựa chọn số một khi luật rõ ràng. Nhưng có những bài toán mà chính người kiểm tra lành nghề cũng không diễn đạt được luật thành con số: vết xước "trông lạ", bề mặt vân gỗ tự nhiên có vết nứt, mối hàn "không đẹp". Chặng 10 mở ra hướng thứ hai: cho máy học luật từ dữ liệu có đáp án. Bài này đặt nền móng cho cả chặng: học máy (Machine Learning) khác lập trình truyền thống ở đâu, có những loại bài toán nào và gọi tên chúng bằng ví dụ nhà máy, quy trình một dự án ML từ lúc nhận yêu cầu đến lúc giám sát trên dây chuyền. Bài sau, "Gradient descent từ đầu với NumPy", mở hộp đen để xem máy "học" bằng phép tính nào.

Mục tiêu cuối bàiGiải thích được khác biệt giữa luật viết tay và mô hình học từ dữ liệu, và tự chạy một ví dụ so sánh hai cách trên cùng bộ dữ liệu OK/NG; gọi đúng tên bài toán (phân loại, hồi quy, phân cụm, phát hiện bất thường; với ảnh: classification, detection, segmentation); nắm các thuật ngữ feature, label, train, inference; mô tả được các bước của một dự án ML trong nhà máy và các điểm hay hỏng. Code dùng scikit-learn 1.9 và NumPy.
pip install numpy scikit-learn

1. Lập trình truyền thống và học máy

Hai cách làm khác nhau ở chỗ ai viết ra luật:

Lập trình truyền thống:   dữ liệu + LUẬT (kỹ sư viết)        →  máy  →  kết quả (OK/NG)
Học máy (huấn luyện):     dữ liệu + KẾT QUẢ ĐÚNG (nhãn)      →  máy  →  LUẬT (mô hình)
Học máy (khi chạy):       dữ liệu mới + mô hình đã học       →  máy  →  kết quả

Ví dụ trạm đo trục: "đường kính ngoài 25,00 ± 0,05 mm" là một luật rõ ràng, viết một dòng if là xong; dùng học máy ở đây chỉ thêm rủi ro. Ngược lại, trạm kiểm tra bề mặt có vết bẩn, vết xước, vết lõm với đủ hình dạng: luật "diện tích > 50 px là NG" sẽ bỏ sót vết xước dài mảnh nhưng diện tích nhỏ, và loại nhầm vết bẩn lớn nhưng mờ. Người QC nhìn là biết, nhưng họ không viết ra được công thức. Đó là chỗ của học máy: bạn đưa cho thuật toán vài trăm ví dụ đã được QC gán nhãn, thuật toán tự tìm luật phân biệt.

Luật viết tay (rule-based)Học máy
Đầu vào của kỹ sưHiểu biết về quy trình, tiêu chuẩnDữ liệu có nhãn đúng, đủ nhiều và đủ đa dạng
Khi điều kiện đổi (lô vật liệu mới)Sửa ngưỡng bằng tayThu thêm dữ liệu, huấn luyện lại, nghiệm thu lại
Giải thích quyết địnhDễ: "diện tích 63 px > 50 px"Khó hơn, nhất là mạng nơ-ron sâu
Mạnh ởĐo kích thước, đếm, kiểm tra có/không, tiêu chí trong bản vẽLỗi bề mặt biến dạng nhiều, nền phức tạp, tiêu chí "nhìn là biết"
Cái giáThời gian kỹ sư dò ngưỡng cho từng trường hợpThời gian thu thập, gán nhãn dữ liệu; cần quy trình kiểm soát phiên bản mô hình

2. Ví dụ chạy được: luật một ngưỡng so với mô hình học

Giả sử trạm của bạn đã có bước xử lý ảnh cổ điển (chặng 3–4): tìm vết tối lớn nhất trên chi tiết và đo hai đặc trưng (feature): diện tích vết (pixel) và độ tương phản của vết so với nền (mức xám). QC xem lại 400 chi tiết và gán nhãn OK/NG. Tiêu chí thật của QC (mà không ai viết ra) là "vết nhìn thấy rõ": vết nhỏ nhưng rất tối là NG, vết to nhưng mờ cũng là NG. Thêm vào đó khoảng 3% nhãn bị gán nhầm, giống dữ liệu thật. Ta mô phỏng dữ liệu này, chia làm hai phần: 400 mẫu để học (train) và 400 mẫu khác để kiểm tra (test).

import numpy as np

rng = np.random.default_rng(0)


def tao_du_lieu(n):
    """Mỗi mẫu: [diện tích vết (px), độ tương phản (mức xám)] và nhãn 0 = OK, 1 = NG."""
    dien_tich = rng.uniform(0, 120, n)
    tuong_phan = rng.uniform(5, 60, n)
    ng = (dien_tich * tuong_phan > 3000).astype(int)     # tiêu chí "nhìn thấy rõ" của QC
    nham = rng.random(n) < 0.03                           # 3% nhãn gán nhầm
    ng[nham] = 1 - ng[nham]
    return np.column_stack([dien_tich, tuong_phan]), ng


X_train, y_train = tao_du_lieu(400)
X_test, y_test = tao_du_lieu(400)
print("X_train:", X_train.shape, "| y_train:", y_train.shape)
print("5 mẫu đầu:\n", np.round(X_train[:5], 1), y_train[:5])
print(f"tỉ lệ NG: train {y_train.mean():.1%}, test {y_test.mean():.1%}")
X_train: (400, 2) | y_train: (400,)
5 mẫu đầu:
 [[76.4 16.1]
 [32.4 56.6]
 [ 4.9 10.2]
 [ 2.   5.3]
 [97.6 22.8]] [0 0 0 0 0]
tỉ lệ NG: train 28.5%, test 21.5%

Tỉ lệ NG cao (20–30%) vì đây là các chi tiết đã bị trạm cũ đánh dấu có vết, đưa cho QC xem lại; trên cả dây chuyền tỉ lệ NG thường thấp hơn nhiều. Mỗi hàng của X là một mẫu (sample), mỗi cột là một đặc trưng; y là nhãn (label). Đây là cách tổ chức dữ liệu chuẩn của mọi thư viện học máy.

Cách 1, luật viết tay. Kỹ sư chọn đặc trưng dễ hiểu nhất là diện tích, và dò ngưỡng tốt nhất trên tập train. Cách 2, học máy. Đưa cả hai đặc trưng và nhãn cho hai thuật toán phổ biến của scikit-learn: cây quyết định (decision tree) và hồi quy logistic (logistic regression). Cả hai chỉ cần hai dòng: tạo mô hình, gọi fit. Ta đánh giá cả ba trên tập test, theo ngôn ngữ nhà máy: bao nhiêu NG bị lọt (escape) và bao nhiêu OK bị loại nhầm (overkill).

from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier


def danh_gia(ten, du_doan, y):
    lot = int(((du_doan == 0) & (y == 1)).sum())          # NG bị cho qua
    nham = int(((du_doan == 1) & (y == 0)).sum())         # OK bị loại
    print(f"{ten:28s} đúng {np.mean(du_doan == y):6.1%} | lọt NG {lot:3d}/{int(y.sum())}"
          f" | loại nhầm OK {nham:3d}/{int((y == 0).sum())}")


# Cách 1: luật một ngưỡng diện tích, dò ngưỡng tốt nhất trên tập train
nguong = max(range(0, 121), key=lambda a: np.mean((X_train[:, 0] > a) == y_train))
print(f"Luật: NG nếu diện tích > {nguong} px")
danh_gia("Luật diện tích", (X_test[:, 0] > nguong).astype(int), y_test)

# Cách 2: học máy, máy tự tìm luật từ (X_train, y_train)
cay = DecisionTreeClassifier(max_depth=4, random_state=0).fit(X_train, y_train)
danh_gia("Cây quyết định (sâu 4)", cay.predict(X_test), y_test)
logistic = LogisticRegression().fit(np.log1p(X_train), y_train)   # log: tích thành tổng
danh_gia("Logistic trên log đặc trưng", logistic.predict(np.log1p(X_test)), y_test)
Luật: NG nếu diện tích > 100 px
Luật diện tích               đúng  80.8% | lọt NG  44/86 | loại nhầm OK  33/314
Cây quyết định (sâu 4)       đúng  94.2% | lọt NG  15/86 | loại nhầm OK   8/314
Logistic trên log đặc trưng  đúng  95.8% | lọt NG  11/86 | loại nhầm OK   6/314

Luật một ngưỡng sai nhiều vì tiêu chí thật phụ thuộc đồng thời hai đặc trưng. Cây quyết định tự tìm ra các ngưỡng kết hợp; hồi quy logistic trên log đặc trưng gần như tìm đúng tiêu chí thật vì log(diện tích) + log(tương phản) > log(3000) là một đường thẳng trong không gian log. Hai bài học nhỏ:

  • Mô hình học máy không "thông minh" hơn kỹ sư; nó chỉ thử được nhiều tổ hợp ngưỡng hơn, nhanh hơn, dựa trên nhãn.
  • Chọn đặc trưng tốt (ở đây là lấy log) giúp mô hình đơn giản làm việc tốt. Deep Learning, chủ đề của cả chặng này, đi xa hơn một bước: tự học luôn đặc trưng từ pixel, không cần bạn đo diện tích hay tương phản.

Cây quyết định có ưu điểm là in được luật nó học ra dưới dạng đọc được. Với độ sâu 2 cho dễ đọc:

from sklearn.tree import DecisionTreeClassifier, export_text

cay2 = DecisionTreeClassifier(max_depth=2, random_state=0).fit(X_train, y_train)
print(export_text(cay2, feature_names=["dien_tich", "tuong_phan"], class_names=["OK", "NG"]))
|--- tuong_phan <= 35.51
|   |--- dien_tich <= 98.64
|   |   |--- class: OK
|   |--- dien_tich >  98.64
|   |   |--- class: OK
|--- tuong_phan >  35.51
|   |--- dien_tich <= 51.60
|   |   |--- class: OK
|   |--- dien_tich >  51.60
|   |   |--- class: NG

Đây chính là một "chương trình if/else" mà máy viết ra từ dữ liệu, thay vì bạn viết: NG khi tương phản > 35,51 và diện tích > 51,60 px. (Nhánh trái tách thêm một lần nhưng hai lá đều là OK; cây chỉ tối ưu độ "thuần" của từng nhóm, không quan tâm hai lá cùng kết luận.) Với độ sâu 4, cây có thêm nhiều ngưỡng kết hợp và bám sát tiêu chí thật hơn. Với mạng nơ-ron, "chương trình" là hàng triệu con số trọng số và không in ra đọc được như vậy.

3. Thuật ngữ cần nắm

Thuật ngữNghĩaTrong ví dụ trên
Mẫu (sample)Một đối tượng cần dự đoánMột chi tiết (hoặc một ảnh)
Đặc trưng (feature)Con số mô tả mẫu, đầu vào của mô hìnhDiện tích, độ tương phản; với Deep Learning là chính các pixel
Nhãn (label)Đáp án đúng do con người cung cấpOK = 0, NG = 1 do QC gán
Mô hình (model)Hàm biến đặc trưng thành dự đoán, có các tham số chỉnh đượcCây quyết định, hồi quy logistic, CNN
Tham số (parameter)Các con số mô hình học raCác ngưỡng trong cây; trọng số của logistic
Huấn luyện (training, fit)Chỉnh tham số để dự đoán khớp nhãn trên tập traincay.fit(X_train, y_train)
Suy luận (inference, predict)Dùng mô hình đã học cho mẫu mới; đây là phần chạy trên trạmcay.predict(X_test)
Siêu tham số (hyperparameter)Lựa chọn do kỹ sư đặt trước khi huấn luyệnmax_depth=4
Tổng quát hóa (generalization)Dự đoán đúng trên dữ liệu chưa từng thấyKết quả trên tập test, không phải tập train
Đánh giá trên dữ liệu đã học là tự lừa mìnhMột cây quyết định không giới hạn độ sâu có thể đúng 100% trên tập train bằng cách "học thuộc" từng mẫu, kể cả 3% nhãn sai. Con số có ý nghĩa duy nhất là kết quả trên dữ liệu mô hình chưa thấy. Bài "Overfitting và chia train/val/test" ở cuối chặng dạy cách chia dữ liệu đúng.

4. Các loại bài toán học máy

Phân loại theo kiểu dữ liệu huấn luyện có ba nhóm chính:

NhómDữ liệu huấn luyệnBài toánVí dụ nhà máy
Học có giám sát (supervised)Có nhãn cho từng mẫuPhân loại (classification): dự đoán một nhómẢnh OK hay NG; loại lỗi: xước, lõm, bẩn
Hồi quy (regression): dự đoán một con sốĐộ mòn dao cắt; số giờ đèn còn dùng được; độ nhám bề mặt từ ảnh
Học không giám sát (unsupervised)Không có nhãnPhân cụm (clustering): gom mẫu giống nhauGom 2.000 ảnh NG thành các nhóm lỗi để QC đặt tên
Phát hiện bất thường (anomaly detection): học "bình thường", báo cái lạChỉ có ảnh OK, lỗi hiếm và muôn hình dạng; giám sát rung động máy
Học tăng cường (reinforcement)Phần thưởng từ môi trường qua thử và saiHọc chính sách hành độngRobot học gắp vật trong mô phỏng; ít gặp ở trạm kiểm tra

Phát hiện bất thường thường được huấn luyện chỉ trên dữ liệu OK (không cần nhãn từng lỗi), nên hay được xếp vào nhóm không giám sát hoặc "bán giám sát". Đây là hướng rất quan trọng với nhà máy vì ảnh NG luôn hiếm.

Ba đoạn code ngắn dưới đây cho bạn thấy ba loại còn lại "trông" thế nào trong code. Đầu tiên là hồi quy: độ sáng trung bình ảnh của trạm giảm dần theo số giờ chạy của đèn LED; dự đoán khi nào đèn xuống dưới mức 180 để lên lịch thay đèn trước khi tỉ lệ loại nhầm tăng.

import numpy as np
from sklearn.linear_model import LinearRegression

rng = np.random.default_rng(1)
gio = np.arange(0, 6000, 250).reshape(-1, 1)                    # đặc trưng: số giờ chạy đèn
do_sang = 215 - 0.004 * gio.ravel() + rng.normal(0, 1.5, len(gio))   # nhãn: mức xám trung bình đo được

hoi_quy = LinearRegression().fit(gio, do_sang)
a, b = hoi_quy.coef_[0], hoi_quy.intercept_
print(f"mô hình: độ sáng ≈ {b:.1f} {a * 1000:+.2f} × (giờ / 1000)")
print("dự đoán ở 7000 giờ:", round(float(hoi_quy.predict([[7000]])[0]), 1))
print("đèn chạm mức 180 sau khoảng", round((180 - b) / a), "giờ")
mô hình: độ sáng ≈ 215.2 -4.01 × (giờ / 1000)
dự đoán ở 7000 giờ: 187.1
đèn chạm mức 180 sau khoảng 8779 giờ

Đầu ra của hồi quy là một số thực, không phải một nhóm. Bài sau sẽ tự cài thuật toán tìm a, b này bằng gradient descent thay vì gọi LinearRegression.

Tiếp theo là phân cụm: bạn có đặc trưng của 300 vết lỗi chưa ai phân loại (diện tích, độ dài/độ rộng, độ tối). K-means gom chúng thành 3 cụm; thuật toán không biết tên lỗi, chính bạn nhìn tâm cụm và đặt tên.

import numpy as np
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

rng = np.random.default_rng(2)
xuoc = np.column_stack([rng.normal(40, 8, 100), rng.normal(8, 1.5, 100), rng.normal(50, 8, 100)])
lom = np.column_stack([rng.normal(90, 15, 100), rng.normal(1.3, 0.2, 100), rng.normal(30, 6, 100)])
bui = np.column_stack([rng.normal(6, 2, 100), rng.normal(1.5, 0.4, 100), rng.normal(70, 10, 100)])
vet = np.vstack([xuoc, lom, bui])                  # [diện tích, dài/rộng, độ tối]; không có nhãn

Z = StandardScaler().fit_transform(vet)            # đưa các cột về cùng thang đo
km = KMeans(n_clusters=3, n_init=10, random_state=0).fit(Z)
for k in range(3):
    tam = vet[km.labels_ == k].mean(axis=0)
    print(f"cụm {k}: {np.sum(km.labels_ == k):3d} vết | diện tích {tam[0]:5.1f} px, "
          f"dài/rộng {tam[1]:4.1f}, độ tối {tam[2]:4.1f}")
cụm 0: 100 vết | diện tích  90.8 px, dài/rộng  1.3, độ tối 29.5
cụm 1: 100 vết | diện tích   6.1 px, dài/rộng  1.5, độ tối 70.8
cụm 2: 100 vết | diện tích  39.9 px, dài/rộng  8.0, độ tối 48.8

Nhìn tâm cụm, kỹ sư nhận ra ngay: cụm có tỉ lệ dài/rộng khoảng 8 là vết xước, cụm diện tích lớn và tròn là vết lõm, cụm nhỏ và tối là bụi. Phân cụm hay được dùng ở bước khám phá dữ liệu, trước khi viết quy chuẩn gán nhãn.

Cuối cùng là phát hiện bất thường: chỉ huấn luyện trên chi tiết OK, rồi để mô hình chấm điểm "lạ" cho chi tiết mới.

import numpy as np
from sklearn.covariance import EllipticEnvelope

rng = np.random.default_rng(3)
ok_train = rng.normal([150, 12, 0.5], [3, 1.5, 0.1], (500, 3))   # [độ sáng, độ nhám, diện tích vết lớn nhất]
ok_moi = rng.normal([150, 12, 0.5], [3, 1.5, 0.1], (200, 3))
ng_moi = np.vstack([rng.normal([150, 12, 4.0], [3, 1.5, 1.0], (10, 3)),    # có vết lớn
                    rng.normal([132, 12, 0.5], [3, 1.5, 0.1], (10, 3))])   # bạc màu, tối hơn

mo_hinh = EllipticEnvelope(contamination=0.01, random_state=0).fit(ok_train)   # chỉ thấy OK
bao_ok = np.mean(mo_hinh.predict(ok_moi) == -1)    # -1 = bất thường
bao_ng = np.mean(mo_hinh.predict(ng_moi) == -1)
print(f"OK mới bị báo bất thường: {bao_ok:.1%} | NG mới bị phát hiện: {bao_ng:.0%}")
OK mới bị báo bất thường: 1.0% | NG mới bị phát hiện: 100%

EllipticEnvelope học trung bình và hiệp phương sai của dữ liệu OK, tức là một "vùng bình thường" hình elip, rồi báo bất thường mọi điểm nằm quá xa vùng đó. Mô hình chưa từng thấy một mẫu NG nào nhưng vẫn bắt được cả hai loại lỗi, vì chúng nằm ngoài vùng bình thường. Cái giá là một tỉ lệ nhỏ OK bị báo nhầm, chỉnh bằng tham số contamination (thực chất là chọn ngưỡng). Lưu ý: ví dụ này dễ vì lỗi cách xa vùng OK; lỗi nhỏ nằm sát biên mới là thử thách thật. Chặng 12 có bài "Anomaly detection: học từ ảnh OK với PaDiM, PatchCore" áp dụng ý tưởng này trực tiếp trên ảnh.

Bài toán trên ảnh: classification, detection, segmentation

Với Deep Learning trên ảnh, các bài toán được gọi tên theo dạng đầu ra. Chọn đúng loại ngay từ đầu quyết định cách gán nhãn, chi phí gán nhãn và cách đánh giá:

Bài toánĐầu ra cho mỗi ảnhNhãn cần gánDùng khi
Phân loại ảnh (classification)Một lớp và điểm số: "NG, 0,93"Một nhãn cho cả ảnh: rẻ nhấtROI đã cắt sẵn, chỉ cần biết OK/NG hoặc loại lỗi. Dự án cuối chặng này
Phát hiện đối tượng (object detection)Danh sách khung chữ nhật + lớp + điểm sốVẽ khung quanh từng vật/lỗiCần vị trí, số lượng: đếm linh kiện, tìm lỗi trên tấm lớn, YOLO
Phân đoạn (segmentation)Nhãn cho từng pixel (mặt nạ)Tô từng pixel lỗi: đắt nhấtCần diện tích, hình dạng chính xác của lỗi để so tiêu chí "vết ≥ 0,3 mm²"
Phát hiện bất thường trên ảnhĐiểm bất thường + bản đồ nhiệtChỉ cần ảnh OKLỗi hiếm, không đoán trước được hình dạng
OCRChuỗi ký tựVăn bản đúng cho từng ảnhĐọc số seri, hạn sử dụng

5. AI, Machine Learning và Deep Learning

  • AI (trí tuệ nhân tạo) là lĩnh vực rộng nhất: máy làm những việc cần "trí thông minh". Một hệ luật viết tay cũng có thể gọi là AI.
  • Machine Learning là nhánh của AI học luật từ dữ liệu. Các thuật toán "cổ điển" (cây quyết định, random forest, gradient boosting, SVM, logistic) cần bạn cung cấp đặc trưng.
  • Deep Learning là nhánh của ML dùng mạng nơ-ron nhiều lớp, tự học đặc trưng từ dữ liệu thô (pixel). Cần nhiều dữ liệu và tính toán hơn, nhưng vượt trội với ảnh có biến động lớn.

Với dữ liệu dạng bảng (thông số máy, kết quả đo, đặc trưng blob), ML cổ điển thường vẫn là lựa chọn tốt nhất: nhanh, cần ít dữ liệu, dễ giải thích. Với ảnh thô, Deep Learning chiếm ưu thế. Trong một trạm thực tế, ba cách thường kết hợp: luật cổ điển định vị chi tiết và đo kích thước, mạng nơ-ron phân loại lỗi bề mặt trong ROI, luật nghiệp vụ tổng hợp kết quả gửi PLC. Bài "Rule-based hay Deep Learning?" ở chặng 11 đi sâu vào cách chọn.

Deep Learning cho Computer Vision: máy "nhìn" ảnh như thế nào?

Hệ thống thị giác của người có hai phần: mắt thu ánh sáng (thiết bị cảm nhận) và não diễn giải tín hiệu thành "một cái bát, vài quả cam, quả chuối". Hệ thống thị giác máy có cấu trúc y hệt: camera là thiết bị cảm nhận, thuật toán chạy trên máy tính là thiết bị diễn giải, đầu ra là dữ liệu (nhãn, tọa độ, mặt nạ) để PLC hay phần mềm dùng tiếp. Phần camera, ống kính, chiếu sáng bạn đã học ở chặng 6; phần khó là "bộ não".

Với máy tính, ảnh chỉ là mảng số (bài Ảnh là mảng số ở chặng 1): ảnh xám là ma trận H×W giá trị 0–255, ảnh màu là khối H×W×3. Cùng một con vít nhưng mọi con số đổi hoàn toàn khi: góc nhìn đổi, ánh sáng đổi, vật bị biến dạng hoặc che khuất một phần, nền lẫn lộn, hay đơn giản là các con vít trong cùng một lớp khác nhau chút ít. Khoảng cách giữa "bảng số" và "ý nghĩa" này gọi là khoảng cách ngữ nghĩa (semantic gap). Không thể viết tay luật cho mọi trường hợp, nên ta để thuật toán học từ ảnh đã gán nhãn.

Pipeline ML truyền thống và Deep Learning trên ảnh

ML truyền thốngDeep Learning
Quy trìnhẢnh → trích đặc trưng thủ công (HOG, SIFT, LBP, histogram màu, đặc trưng blob) → bộ phân loại (SVM, random forest) → nhãnẢnh → một mạng nhiều lớp học đồng thời cả trích đặc trưng lẫn phân loại (end-to-end) → nhãn
Ai thiết kế đặc trưngKỹ sưMạng tự học từ dữ liệu
Dữ liệu cầnÍt đến vừaNhiều, hoặc ít nếu dùng transfer learning (dự án cuối chặng)
Phần cứngCPU là đủGPU gần như bắt buộc khi huấn luyện
Ảnh biến động lớnĐộ chính xác chững lại sớmTiếp tục tăng khi thêm dữ liệu
Giải thíchDễ hơnKhó hơn, cần công cụ như Grad-CAM

Vì sao "sâu" lại hiệu quả? Mạng học đặc trưng phân cấp: các lớp đầu học cạnh, đốm màu; lớp giữa ghép chúng thành góc, vân, bộ phận; lớp sau ghép thành đối tượng như "vết xước", "con vít". Mốc lịch sử: năm 2012, mạng AlexNet thắng cuộc thi ImageNet (ILSVRC) với top-5 error khoảng 15,3%, bỏ xa đội hạng nhì (khoảng 26,2%) dùng đặc trưng thủ công. Từ đó Deep Learning trở thành cách làm mặc định của Computer Vision (bài Các kiến trúc CNN kinh điển kể tiếp câu chuyện này).

Các bộ dữ liệu bạn sẽ gặp

DatasetNội dungDùng để
MNIST / Fashion-MNIST70.000 ảnh xám 28×28, 10 lớp (chữ số / quần áo)Bài tập nhập môn
CIFAR-1060.000 ảnh màu 32×32, 10 lớpThử nhanh một CNN
ImageNet (ILSVRC)Khoảng 1,28 triệu ảnh huấn luyện, 1.000 lớpBenchmark phân loại; nguồn trọng số pretrained
Pascal VOC, MS COCO20 lớp / 80 lớp, có hộp và mặt nạChuẩn đánh giá phát hiện và phân đoạn
MVTec ADẢnh sản phẩm công nghiệp OK/lỗi, 15 loạiPhát hiện bất thường (chặng 12)

Đọc sâu hơn: Tổng quan Deep Learning cho Computer Vision (bám theo trang 1–8, 60–61 của slide bài giảng).

6. Quy trình một dự án Machine Learning trong nhà máy

BướcViệc chínhCâu hỏi phải trả lời
1. Xác định bài toánChọn loại bài toán, định nghĩa lỗi bằng văn bản và ảnh mẫu, đặt chỉ tiêuLọt lỗi tối đa bao nhiêu? Loại nhầm tối đa bao nhiêu? Cycle time bao nhiêu ms? Luật cổ điển có làm được không?
2. Thu thập dữ liệuChụp trên chính trạm, đủ ca, đủ lô, đủ loại lỗi; ghi lại mã lô, ngày, ca cho từng ảnhDữ liệu có đại diện cho mọi biến động thật không? Có đủ ảnh NG mỗi loại không?
3. Gán nhãnViết quy chuẩn gán nhãn, cho 2 người gán độc lập một phần ảnh để đo độ nhất quánẢnh "lưng chừng" gán thế nào? Ai là người quyết định cuối?
4. Chia dữ liệuTrain / validation / test, chia theo lô hoặc theo ngàyẢnh của cùng một chi tiết có nằm ở hai tập không?
5. Huấn luyệnBắt đầu từ mô hình đơn giản (baseline), rồi mới thử mô hình lớn hơnMô hình phức tạp có thực sự tốt hơn baseline trên tập validation không?
6. Đánh giá, chọn ngưỡngMa trận nhầm lẫn, escape, overkill trên tập test chưa từng dùng; chọn ngưỡng theo chi phíĐạt chỉ tiêu bước 1 chưa? Những ảnh sai có điểm chung gì?
7. Triển khaiXuất mô hình (ONNX), tích hợp với trạm và PLC, đo cycle time, chạy song song với QCKết quả trên trạm có giống trên máy huấn luyện không?
8. Giám sát, cải tiếnTheo dõi tỉ lệ NG, phân bố điểm số, lưu ảnh khó; huấn luyện lại khi cầnDữ liệu hôm nay có còn giống dữ liệu đã huấn luyện không?

Trong thực tế, các bước 2–4 (dữ liệu) thường chiếm phần lớn thời gian dự án, và quy trình là một vòng lặp: kết quả bước 6 hay bước 8 thường đưa bạn quay lại bước 2 để thu thêm loại ảnh còn thiếu.

Bước 8 hay bị bỏ qua nhất. Đoạn code sau cho thấy vì sao: dùng lại mô hình logistic ở mục 2, giả sử sau ba tháng người ta thay đèn mới sáng hơn, mọi vết đo được tương phản cao hơn khoảng 8 mức xám, trong khi tiêu chí NG của QC không đổi.

X_moi, y_moi = tao_du_lieu(400)                  # lô mới, tiêu chí QC như cũ
X_den_moi = X_moi.copy()
X_den_moi[:, 1] += 8                             # đèn mới: tương phản đo được tăng 8 mức xám
danh_gia("Trước khi thay đèn", logistic.predict(np.log1p(X_moi)), y_moi)
danh_gia("Sau khi thay đèn", logistic.predict(np.log1p(X_den_moi)), y_moi)
Trước khi thay đèn           đúng  95.8% | lọt NG   8/98 | loại nhầm OK   9/302
Sau khi thay đèn             đúng  87.2% | lọt NG   4/98 | loại nhầm OK  47/302

Mô hình không hề "hỏng", chương trình không báo lỗi gì, nhưng số chi tiết OK bị loại nhầm tăng từ 9 lên 47 (gấp 5 lần), vì mọi vết giờ "trông" rõ hơn với mô hình. Số NG lọt giảm nhẹ không phải tin tốt: ngưỡng hiệu dụng đã dịch chuyển mà không ai quyết định việc đó. Hiện tượng dữ liệu đầu vào thay đổi so với lúc huấn luyện gọi là data drift (trôi dữ liệu). Cách phòng: khóa cấu hình quang học, theo dõi tỉ lệ NG theo ca và phân bố đặc trưng/điểm số, và bất kỳ thay đổi nào ở camera, đèn, vật liệu đều phải kiểm tra lại mô hình trên bộ ảnh vàng.

7. Khi nào không nên dùng học máy

  • Luật rõ ràng và đo được: kích thước theo bản vẽ, có/không có linh kiện, đếm số lỗ. Viết luật vài dòng, dễ nghiệm thu hơn nhiều.
  • Không có dữ liệu đại diện: chưa có dây chuyền, chưa có ảnh lỗi thật, không có người gán nhãn đáng tin.
  • Khách hàng yêu cầu giải thích từng quyết định theo tiêu chuẩn đo lường, mà mô hình không đáp ứng được.
  • Sai một lần là hậu quả nghiêm trọng và không có lớp kiểm soát thứ hai (kiểm tra lặp, QC lấy mẫu).
  • Điều kiện chụp chưa ổn định: hãy sửa chiếu sáng và cơ khí trước. Học máy không thay thế được một hệ quang học tốt; nó chỉ làm tốt hơn khi đầu vào đã ổn định.

8. Sai lầm thường gặp

  • Dùng học máy cho việc luật làm được, rồi mất hàng tuần thu thập dữ liệu cho một phép đo kích thước.
  • Đánh giá trên dữ liệu đã dùng để huấn luyện, báo cáo 99% rồi thất vọng trên dây chuyền.
  • Chỉ nhìn độ chính xác (accuracy). Với 2% NG, mô hình luôn trả lời "OK" đã đúng 98% mà không bắt được lỗi nào. Hãy báo cáo lọt NG và loại nhầm OK riêng.
  • Nhãn không nhất quán: hai QC đánh giá khác nhau cùng một vết. Mô hình không thể tốt hơn chất lượng nhãn.
  • Không lưu thông tin lô, ngày, ca cho từng ảnh, nên không chia dữ liệu đúng được và không truy được nguyên nhân khi mô hình kém.
  • Coi triển khai là kết thúc, không giám sát data drift.

9. Bài tập

Bài 1. Xác định loại bài toán (phân loại, hồi quy, phân cụm hay phát hiện bất thường) cho 5 yêu cầu sau: (a) dự đoán độ dày lớp sơn (µm) từ ảnh màu của tấm thép; (b) phân biệt viên thuốc OK, sứt mẻ, sai màu; (c) nhà máy có 50.000 ảnh bo mạch OK và gần như không có ảnh lỗi, muốn báo động khi có gì khác thường; (d) có 3.000 ảnh NG chưa phân loại, cần biết có những nhóm lỗi nào để viết quy chuẩn; (e) dự đoán máy ép sẽ hỏng trong 7 ngày tới hay không từ dữ liệu rung. Sau đó làm tương tự với 5 bài toán ở nhà máy bạn biết.

Xem đáp án
Yêu cầuLoạiGiải thích
(a) Độ dày sơnHồi quyĐầu ra là một số thực (µm). Nhãn lấy từ máy đo độ dày chuẩn.
(b) Viên thuốcPhân loại (3 lớp)Có nhãn, đầu ra là một trong ba nhóm.
(c) Bo mạchPhát hiện bất thườngChỉ có dữ liệu OK; mô hình học "bình thường" và báo cái lạ.
(d) 3.000 ảnh NGPhân cụmKhông có nhãn loại lỗi; gom nhóm để con người đặt tên.
(e) Máy épPhân loại (2 lớp: hỏng trong 7 ngày hay không)Nếu đổi thành "còn bao nhiêu ngày đến khi hỏng" thì thành hồi quy. Cách đặt câu hỏi quyết định loại bài toán.

Với 5 bài toán của bạn, hãy viết thêm cho mỗi bài: đặc trưng đầu vào là gì, nhãn lấy từ đâu và ai gán, và luật viết tay có làm được không. Nếu câu trả lời cuối là "có", hãy dùng luật.

Bài 2. Một tập test có 1.000 chi tiết, trong đó 20 NG. Mô hình A luôn trả lời OK. Mô hình B bắt được 18 NG và loại nhầm 30 OK. Tính accuracy, số lọt NG và số loại nhầm của mỗi mô hình. Mô hình nào tốt hơn cho một trạm mà lọt lỗi bị khách hàng phạt nặng?

Xem đáp án

A: đúng 980/1000 = 98,0%, lọt 20/20 NG, loại nhầm 0. B: đúng (18 + 950)/1000 = 96,8%, lọt 2/20 NG, loại nhầm 30/980 ≈ 3,1%. A có accuracy cao hơn nhưng vô dụng: nó không bắt được lỗi nào. B tốt hơn hẳn cho trạm này; 30 chi tiết loại nhầm có thể được QC kiểm lại. Bài học: luôn báo cáo lọt NG và loại nhầm OK riêng, không chỉ accuracy.

import numpy as np

y = np.array([1] * 20 + [0] * 980)
a = np.zeros(1000, int)
b = np.array([1] * 18 + [0] * 2 + [1] * 30 + [0] * 950)
for ten, p in (("A", a), ("B", b)):
    print(ten, f"đúng {np.mean(p == y):.1%}", "| lọt", int(((p == 0) & (y == 1)).sum()),
          "| loại nhầm", int(((p == 1) & (y == 0)).sum()))
A đúng 98.0% | lọt 20 | loại nhầm 0
B đúng 96.8% | lọt 2 | loại nhầm 30

Bài 3. Trong ví dụ mục 2, hãy thử cây quyết định với max_depth=None (không giới hạn). In độ chính xác trên tập train và tập test. Giải thích vì sao hai con số khác nhau.

Xem đáp án
from sklearn.tree import DecisionTreeClassifier

cay_sau = DecisionTreeClassifier(max_depth=None, random_state=0).fit(X_train, y_train)
print("độ sâu:", cay_sau.get_depth(), "| số lá:", cay_sau.get_n_leaves())
print(f"đúng trên train: {cay_sau.score(X_train, y_train):.1%}")
print(f"đúng trên test : {cay_sau.score(X_test, y_test):.1%}")
độ sâu: 9 | số lá: 39
đúng trên train: 100.0%
đúng trên test : 91.5%

Cây không giới hạn chia đến khi mỗi lá chỉ còn mẫu một lớp, nên đúng 100% trên train, kể cả các mẫu bị gán nhãn nhầm: nó đã học thuộc nhiễu. Trên test, những nhánh học thuộc đó không giúp gì, thậm chí gây sai. Đây là hiện tượng overfitting; chính vì thế ta chỉ tin con số trên dữ liệu mô hình chưa thấy.

Tóm tắt

  • Lập trình truyền thống: kỹ sư viết luật. Học máy: đưa dữ liệu và nhãn, thuật toán tự tìm luật (mô hình); khi chạy trên trạm, mô hình chỉ suy luận.
  • Dùng học máy khi luật khó viết ra (lỗi bề mặt đa dạng), dùng luật khi tiêu chí rõ ràng (kích thước, có/không).
  • Học có giám sát: phân loại (nhóm) và hồi quy (con số). Không giám sát: phân cụm và phát hiện bất thường (chỉ cần dữ liệu OK). Với ảnh: classification, detection, segmentation khác nhau ở dạng đầu ra và chi phí gán nhãn.
  • Thuật ngữ cốt lõi: mẫu, đặc trưng, nhãn, tham số, huấn luyện (fit), suy luận (predict), tổng quát hóa.
  • Dự án ML là vòng lặp: định nghĩa bài toán và chỉ tiêu, thu thập và gán nhãn, chia dữ liệu, huấn luyện từ baseline, đánh giá bằng escape/overkill, triển khai, giám sát data drift.
  • Luôn đánh giá trên dữ liệu chưa dùng để huấn luyện, và báo cáo lọt NG, loại nhầm OK thay vì chỉ accuracy.

Bài tiếp theo: Gradient descent từ đầu với NumPy, nơi bạn tự viết thuật toán giúp mô hình "học" từ dữ liệu.

Bài tập thực hành

  1. Với 5 bài toán trong nhà máy bạn biết, xác định đó là phân loại, hồi quy, phân cụm hay phát hiện bất thường.

Làm xong bài tập mới chuyển sang bài tiếp theo: tự viết code là cách duy nhất để nhớ lâu.

Bài tiếp theo
Ban biên tập HọcFree

Chúng tôi viết những bài hướng dẫn dễ hiểu, thực tế để ai cũng có thể tự học lập trình và ngoại ngữ, hoàn toàn miễn phí.