Image Classification
Phân loại hình ảnh
Bạn mở camera điện thoại, chĩa vào một chiếc xe máy trên phố Hà Nội. Ứng dụng hiện chữ 'Xe máy — 92%'. Nó đã làm gì?
Hình minh họa
Trình phân loại tương tác — chọn 1 ảnh
Bấm vào bất kỳ ảnh nào bên dưới. Mô hình sẽ trả về 5 nhãn có xác suất cao nhất (Top-5) cùng với ghi chú giải thích vì sao.
Xe máy trên phố Hà Nội
Nhãn đúng: Xe máy
Top-5 dự đoán
Dự đoán chắc chắn: xe máy chiếm 80% phương tiện tại Việt Nam, có nhiều trong tập huấn luyện.
Pipeline CNN: từ pixel đến xác suất
Ảnh đi qua từng lớp, thu nhỏ kích thước không gian nhưng tăng số kênh đặc trưng. Cuối cùng Softmax biến logits thành phân phối xác suất 1000 lớp.
Ma trận nhầm lẫn (1000 dự đoán thực tế)
Tổng hợp từ 1000 dự đoán ImageNet được gom vào 5 nhóm lớn. Hàng = nhãn thật, Cột = dự đoán. Đường chéo (xanh) là đúng, ngoài đường chéo (đỏ) là nhầm.
| ↓ Xe cộ | ↓ Động vật | ↓ Đồ ăn | ↓ Đồ vật | ↓ Thực vật | Acc | |
|---|---|---|---|---|---|---|
| Xe cộ → | 186 | 4 | 2 | 6 | 2 | 93.0% |
| Động vật → | 3 | 178 | 8 | 7 | 4 | 89.0% |
| Đồ ăn → | 1 | 9 | 162 | 12 | 16 | 81.0% |
| Đồ vật → | 8 | 5 | 10 | 170 | 7 | 85.0% |
| Thực vật → | 2 | 3 | 18 | 9 | 168 | 84.0% |
Mô hình thường nhầm Thực vật ↔ Đồ ăn vì nhiều ảnh rau củ có bối cảnh bếp giống ảnh đồ ăn. Đây là dấu hiệu cần bổ sung dữ liệu hoặc dùng hard-example mining.
So sánh ResNet50 · EfficientNet · ViT trên ImageNet
Accuracy tăng đi kèm với số tham số và FLOPs. ViT-L đạt đỉnh 87.8% Top-1 nhưng cần dữ liệu pretrain khổng lồ (JFT-300M) để không overfit.
CNN học đặc trưng theo thứ bậc giống cách thị giác của não người hoạt động: lớp đầu bắt cạnh và góc, lớp giữa ghép thành bộ phận (bánh xe, mắt, đèn pha), lớp cuối tổ hợp tất cả thành đối tượng hoàn chỉnh. Bạn không hề dạy mạng đâu là "bánh xe" — nó tự khám phá ra qua hàng triệu ảnh và backpropagation.
Một cách nhìn khác: phân loại ảnh = nén ảnh 224×224×3 ≈ 150 000 số thành 1 số nguyên (index lớp) — mất gần 100% thông tin pixel nhưng giữ đúng "khái niệm". Đó là sức mạnh của biểu diễn (representation learning).
Ảnh chụp một chiếc xe máy chở đầy hàng hoá, nhìn từ phía sau. Mô hình phân loại nhầm thành 'xe ba gác'. Nguyên nhân có thể là gì?
Giải thích
Image Classification (Phân loại hình ảnh) là tác vụ cơ bản nhất trong thị giác máy tính — gán một nhãn danh mục cho toàn bộ bức ảnh. Công thức chính thức:
Hàm với tham số nhận ảnh RGB (H×W×3) và trả về 1 trong K nhãn. Với ImageNet, K = 1000. Thực chất, mô hình cho ra phân phối xác suất K chiều, lớp có xác suất cao nhất được chọn qua .
Ảnh đầu vào (224×224×3) đi qua ba giai đoạn:
- Feature Extraction: các lớp Conv + Pool trích đặc trưng, giảm kích thước không gian (224 → 7) nhưng tăng số kênh (3 → 512).
- Flatten + FC: duỗi tensor 3D thành vector 1D (≈25 000 chiều), ánh xạ qua một hoặc nhiều lớp fully-connected.
- Softmax: chuyển logits thành phân phối xác suất trên K lớp. Lấy argmax để có dự đoán cuối cùng.
Hàm mất mát Cross-Entropy được dùng để huấn luyện. Với nhãn one-hot và dự đoán :
Vì là one-hot, tổng chỉ còn với c là lớp đúng. Mất mát càng nhỏ khi xác suất của lớp đúng càng gần 1. Đạo hàm của cross-entropy + softmax rất gọn: .
- Nhận diện loại phương tiện trên camera giao thông (xe máy chiếm 80% phương tiện) — phục vụ đếm lưu lượng, phân luồng.
- Phân loại ảnh sản phẩm trên Shopee, Tiki theo danh mục (thời trang, điện tử, đồ ăn…) để cải thiện tìm kiếm và gợi ý.
- Phân loại ảnh CCCD/CMND trong hệ thống eKYC ngân hàng — phát hiện ảnh giả, ảnh mờ, ảnh không đúng định dạng.
- Kiểm duyệt nội dung trên Zalo, VNG: phát hiện ảnh bạo lực, khoả thân, lừa đảo trước khi cho đăng tải.
- Y tế: phân loại ảnh X-quang phổi (có/không Covid, lao), ảnh da liễu (ung thư hắc tố vs bớt lành tính).
Các kiến trúc nổi tiếng:
- AlexNet (2012): mở đầu kỷ nguyên deep learning cho CV, thắng ImageNet với top-5 error 15.3% (giảm từ 26.2%).
- VGGNet (2014): kiến trúc đơn giản, chỉ dùng conv 3×3 và max pool 2×2 — sâu 16-19 lớp, đẹp về lý thuyết.
- GoogLeNet/Inception (2014): khối Inception với nhiều nhánh song song (1×1, 3×3, 5×5, pooling) để học đa tỉ lệ.
- ResNet (2015): skip connection cho phép huấn luyện mạng 152 lớp mà không bị gradient tắt. Cột mốc quan trọng nhất.
- DenseNet (2017): mỗi lớp nhận input từ tất cả lớp trước — tận dụng feature tối đa.
- EfficientNet (2019): compound scaling tối ưu đồng thời width, depth, resolution theo hệ số nhất quán.
- Vision Transformer (ViT) (2020): áp dụng Transformer cho ảnh bằng cách chia patch, không dùng convolution.
- ConvNeXt(2022): "hiện đại hoá" CNN để cạnh tranh với ViT, chứng minh convolution vẫn rất mạnh.
- Mobile/Edge (Raspberry Pi, JetsonNano): MobileNetV3, EfficientNet-Lite, hoặc quantized ResNet-18. Target: <50MB, <100ms inference.
- Server GPU (1×V100 hoặc A100): ResNet-50 (chuẩn), hoặc EfficientNet-B4/B5 cho accuracy tốt hơn mà vẫn nhanh.
- Tier cao — SOTA accuracy: ViT-L, Swin-L, ConvNeXt-L. Cần nhiều data (JFT hoặc LAION).
- Data ít (<10K ảnh): luôn fine-tune từ pretrained. Không bao giờ train-from-scratch — sẽ overfit ngay.
Một mô hình train chỉ 10 000 ảnh gốc có thể đạt accuracy của 1 triệu ảnh nếu dùng đúng augmentation. Các phép phổ biến:
- RandomCrop + Resize: cắt ngẫu nhiên một vùng rồi phóng về 224×224 — dạy mô hình bất biến theo vị trí/tỉ lệ.
- HorizontalFlip: lật ngang ngẫu nhiên (không áp dụng cho chữ viết hoặc ảnh y tế có hướng rõ ràng).
- ColorJitter: đổi brightness, contrast, saturation, hue ±30% — chịu được ánh sáng khác nhau.
- MixUp / CutMix: trộn 2 ảnh và 2 nhãn — giảm overfit cực mạnh, tăng 1-2% accuracy.
- RandAugment / AutoAugment: tự động chọn tổ hợp augmentation tối ưu qua search — tiên tiến nhất hiện nay.
import torch
from torchvision import models, transforms
from PIL import Image
# ── Load ResNet-50 đã pretrain trên ImageNet ────────────────
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
model.eval()
# ── Tiền xử lý: chuẩn theo ImageNet stats ──────────────────
transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225],
),
])
img = Image.open("xe_may.jpg").convert("RGB")
x = transform(img).unsqueeze(0) # (1, 3, 224, 224)
with torch.no_grad():
logits = model(x) # (1, 1000)
probs = torch.softmax(logits, dim=1)
top5 = torch.topk(probs, 5)
# ── In ra Top-5 cùng xác suất ──────────────────────────────
classes = models.ResNet50_Weights.IMAGENET1K_V2.meta["categories"]
for i in range(5):
idx = top5.indices[0][i].item()
p = top5.values[0][i].item()
print(f"{i+1}. {classes[idx]:30s} {p*100:5.2f}%")
# Output ví dụ:
# 1. moped 82.41%
# 2. mountain_bike 7.12%
# 3. motor_scooter 4.88%
# 4. unicycle 2.05%
# 5. tricycle 1.33%import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import models, datasets, transforms
# ── 1) Dataset: ảnh sản phẩm Shopee VN, 10 lớp ─────────────
train_tf = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(0.3, 0.3, 0.3),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
train_ds = datasets.ImageFolder("data/shopee_vn/train", transform=train_tf)
train_ld = DataLoader(train_ds, batch_size=64, shuffle=True, num_workers=4)
# ── 2) Load pretrained, thay lớp cuối ──────────────────────
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
num_classes = 10
model.fc = nn.Linear(model.fc.in_features, num_classes)
model = model.cuda()
# ── 3) Fine-tune toàn bộ với LR nhỏ cho backbone ──────────
optimizer = torch.optim.AdamW([
{"params": [p for n, p in model.named_parameters() if "fc" not in n], "lr": 1e-4},
{"params": model.fc.parameters(), "lr": 1e-3}, # head LR lớn hơn
], weight_decay=1e-4)
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20)
# ── 4) Vòng train ngắn gọn ────────────────────────────────
for epoch in range(20):
model.train()
for x, y in train_ld:
x, y = x.cuda(), y.cuda()
optimizer.zero_grad()
loss = criterion(model(x), y)
loss.backward()
optimizer.step()
scheduler.step()
print(f"epoch {epoch+1:02d} loss={loss.item():.4f}")Metric đánh giá thường dùng
| Metric | Công thức | Khi dùng |
|---|---|---|
| Accuracy | correct / total | Data cân bằng, nhiều lớp đều nhau |
| Top-5 Accuracy | y ∈ top5(ŷ) | ImageNet & task có nhiều lớp gần nhau |
| Precision/Recall | TP/(TP+FP), TP/(TP+FN) | Lớp mất cân bằng, cần đo riêng từng lớp |
| F1 (macro/weighted) | 2PR/(P+R) | Cân bằng giữa precision & recall, data imbalanced |
| Confusion Matrix | CM[i][j] | Debug xem mô hình nhầm cặp lớp nào |
| ROC-AUC (per class) | ∫ TPR d FPR | Khi cần threshold linh hoạt theo chi phí |
Pitfalls (cái bẫy) thường gặp
- Data leakage: ảnh của cùng một người/đồ vật xuất hiện ở cả train và test → accuracy ảo cao. Luôn split theo entity chứ không phải theo ảnh.
- Class imbalance:95% ảnh thuộc 1 lớp → mô hình học cách luôn predict lớp đó và đạt 95% accuracy "giả". Dùng oversampling, focal loss, hoặc macro F1.
- Label noise: nhãn sai ~5-10% là chuyện thường. Dùng
cleanlabhoặc huấn luyện với label smoothing để giảm ảnh hưởng. - Domain shift: train trên ảnh web, deploy trên camera điện thoại → performance rơi. Luôn thu thập một phần data từ môi trường triển khai thật.
- Adversarial examples: thay đổi vài pixel có thể lừa mô hình — quan trọng khi dùng cho bảo mật, eKYC. Xem topic adversarial-robustness.
- Test set quá nhỏ:<500 ảnh/lớp → accuracy dao động ±5% giữa các lần eval. Luôn báo cáo confidence interval.
Ứng dụng tiên tiến năm 2024+
- Zero-shot classification với CLIP:không cần train, chỉ cần viết tên lớp bằng tiếng Việt ("xe máy chở hàng", "xe ba gác"). Độ chính xác rất tốt cho nhãn tự do.
- Open-vocabulary: model như EVA-02, DINOv2 phân loại được bất kỳ khái niệm nào bạn mô tả bằng ngôn ngữ.
- Multimodal (VLM):không chỉ ra 1 nhãn mà trả lời "đây là gì và tại sao" — dùng GPT-4V, Gemini, Qwen-VL.
- Few-shot learning: cho 5 ảnh/lớp là mô hình học ngay nhờ meta-learning và pretrained features mạnh.
Bạn train một classifier với 95% accuracy trên validation. Khi deploy trên camera điện thoại thực tế, accuracy rớt còn 62%. Nguyên nhân chính là gì?
- Image Classification gán DUY NHẤT 1 nhãn cho toàn bộ ảnh — không tìm vị trí, không đếm số lượng (đó là các task khác).
- CNN trích đặc trưng theo thứ bậc: cạnh → hình dạng → bộ phận → đối tượng; Softmax chuyển logits thành xác suất, Cross-Entropy tính loss.
- ImageNet (1000 lớp, 1.2M ảnh) là benchmark chuẩn. Mô hình hiện đại đạt Top-1 85-88%, Top-5 96-98%.
- Kiến trúc lớn: ResNet (2015, 76%), EfficientNet (2019, 84%), ViT (2020-21, 88%). Accuracy tăng đi kèm tham số và tính toán.
- Confusion matrix cho thấy cặp lớp bị nhầm có hệ thống — công cụ debug quan trọng hơn chỉ nhìn accuracy.
- Trong thực tế, luôn dùng transfer learning + data augmentation; cẩn trọng data leakage, class imbalance, domain shift.
Kiểm tra hiểu biết
Image Classification khác gì so với Object Detection?