Image classification

Phân loại hình ảnh với Mạng nơ-ron Tích chập

Phân loại hình ảnh là một trong những nhiệm vụ cơ bản nhất trong lĩnh vực thị giác máy tính, nơi thuật toán nhận dạng đối tượng xuất hiện trong hình ảnh. Kể từ khi học sâu bùng nổ, Mạng nơ-ron Tích chập (CNN) đã thống trị lĩnh vực này, đạt được độ chính xác đáng kinh ngạc trên các bộ dữ liệu hình ảnh phức tạp. Bài viết này khám phá phân loại hình ảnh, các phương pháp, thách thức và ứng dụng của nó.

Hiểu về Phân loại Hình ảnh

Phân loại hình ảnh là nhiệm vụ gán nhãn hoặc danh mục cho toàn bộ hình ảnh. Với một hình ảnh đầu vào, thuật toán dự đoán phân phối xác suất trên một tập hợp các lớp được xác định trước. Ví dụ, khi được hiển thị hình ảnh của một con mèo, một bộ phân loại được huấn luyện tốt sẽ xuất ra "mèo" với độ tin cậy cao.

Các loại Phân loại Hình ảnh

  1. Phân loại Nhị phân: Phân biệt giữa hai lớp (ví dụ: hình ảnh spam và không spam)

  2. Phân loại Đa lớp: Gán một nhãn duy nhất từ nhiều lớp (ví dụ: mèo, chó, chim)

  3. Phân loại Đa nhãn: Gán nhiều nhãn cho một hình ảnh duy nhất (ví dụ: hình ảnh chứa cả mèo và chó)

  4. Phân loại Phân cấp: Tổ chức các danh mục theo hệ thống phân cấp (ví dụ: động vật → động vật có vú → mèo → mèo Ba Tư)

Quy trình Phân loại Hình ảnh

Một quy trình phân loại hình ảnh điển hình bao gồm một số thành phần chính:

1. Thu thập và Chuẩn bị Dữ liệu

Dữ liệu huấn luyện chất lượng là yếu tố quyết định cho phân loại hình ảnh thành công:

  • Bộ dữ liệu đa dạng: Thu thập hình ảnh đại diện cho tất cả các lớp trong các điều kiện, góc độ và môi trường khác nhau

  • Làm sạch dữ liệu: Loại bỏ hình ảnh bị hỏng, trùng lặp hoặc không liên quan

  • Chú thích: Gán nhãn hình ảnh với các danh mục chính xác

  • Chia tập Train/Validation/Test: Thường là 70-80% cho huấn luyện, 10-15% cho kiểm định và 10-15% cho kiểm tra

2. Tiền xử lý

Trước khi đưa hình ảnh vào mô hình, một số bước tiền xử lý thường được áp dụng:

  • Điều chỉnh kích thước: Chuẩn hóa kích thước hình ảnh (thường là 224×224 hoặc 299×299 pixel)

  • Chuẩn hóa: Chia tỷ lệ giá trị pixel theo phạm vi tiêu chuẩn (thường là [0,1] hoặc [-1,1])

  • Tăng cường dữ liệu: Tạo các biến thể của hình ảnh huấn luyện thông qua các kỹ thuật như:

    • Cắt ngẫu nhiên

    • Lật ngang/dọc

    • Xoay

    • Thay đổi màu sắc

    • Xóa ngẫu nhiên

3. Trích xuất Đặc trưng và Kiến trúc Mô hình

CNN là mạng nơ-ron chuyên biệt được thiết kế cho xử lý hình ảnh:

  • Lớp tích chập: Áp dụng các bộ lọc để phát hiện đặc trưng như cạnh, kết cấu và mẫu

  • Lớp gộp (Pooling): Giảm kích thước không gian nhưng vẫn giữ thông tin quan trọng

  • Hàm kích hoạt: Đưa vào tính phi tuyến (ReLU phổ biến nhất)

  • Chuẩn hóa theo batch: Ổn định và tăng tốc quá trình huấn luyện

  • Dropout: Ngăn chặn overfitting bằng cách vô hiệu hóa ngẫu nhiên các nơ-ron trong quá trình huấn luyện

4. Đầu phân loại

Phần cuối cùng của mạng thực hiện phân loại thực tế:

  • Global Pooling: Giảm các feature map thành một vector kích thước cố định

  • Lớp kết nối đầy đủ: Chuyển đổi đặc trưng thành điểm số lớp

  • Hàm Softmax: Chuyển đổi điểm số thành phân phối xác suất trên các lớp

5. Huấn luyện

Quá trình tối ưu hóa mô hình:

  • Hàm mất mát: Cross-entropy phân loại cho các bài toán đa lớp

  • Bộ tối ưu hóa: Phương pháp như Adam, SGD với momentum

  • Lập lịch tốc độ học: Giảm dần tốc độ học

  • Dừng sớm: Ngăn chặn overfitting bằng cách giám sát hiệu suất trên tập validation

  • Giới hạn gradient: Xử lý hiện tượng gradient bùng nổ

6. Đánh giá

Đánh giá hiệu suất mô hình:

  • Độ chính xác: Tỷ lệ phần trăm hình ảnh được phân loại chính xác

  • Precision và Recall: Cân bằng giữa false positive và false negative

  • F1 Score: Trung bình điều hòa của precision và recall

  • Ma trận nhầm lẫn: Phân tích chi tiết dự đoán theo lớp

  • Độ chính xác Top-k: Liệu nhãn đúng có nằm trong k lớp có xác suất cao nhất không

Các kiến trúc CNN phổ biến cho Phân loại Hình ảnh

Một số kiến trúc đã đẩy ranh giới của phân loại hình ảnh:

Kiến trúc Cổ điển

  • LeNet-5 (1998): Kiến trúc CNN đầu tiên, được phát triển bởi Yann LeCun, được sử dụng để nhận dạng chữ số viết tay

  • AlexNet (2012): Mô hình đột phá thắng cuộc thi ImageNet, đánh dấu sự khởi đầu của kỷ nguyên học sâu trong thị giác máy tính

  • VGG (2014): Kiến trúc đơn giản, đồng nhất với các lớp tích chập 3×3 và pooling 2×2 lặp lại

  • GoogLeNet (Inception) (2014): Giới thiệu module Inception với các đường dẫn tích chập song song

Kiến trúc Hiện đại

  • ResNet (2015): Giới thiệu kết nối tắt (skip connections) để xây dựng mạng rất sâu lên đến 152 lớp

  • DenseNet (2017): Kết nối mỗi lớp với tất cả các lớp tiếp theo, tăng cường truyền đặc trưng và gradient

  • EfficientNet (2019): Sử dụng phương pháp mở rộng hỗn hợp để cân bằng độ sâu, chiều rộng và độ phân giải

  • Vision Transformers (ViT) (2020): Áp dụng kiến trúc Transformer từ NLP vào phân loại hình ảnh

  • ConvNeXt (2022): Kết hợp các ý tưởng từ Transformer với cấu trúc CNN truyền thống

Triển khai Phân loại Hình ảnh với PyTorch

Dưới đây là một ví dụ đơn giản về cách xây dựng pipeline phân loại hình ảnh sử dụng PyTorch:

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader

# 1. Định nghĩa các phép chuyển đổi dữ liệu
transform_train = transforms.Compose([
    transforms.Resize((224, 224)),  # Điều chỉnh kích thước ảnh
    transforms.RandomHorizontalFlip(),  # Lật ngang ngẫu nhiên
    transforms.RandomRotation(10),  # Xoay nhẹ
    transforms.ColorJitter(brightness=0.2, contrast=0.2),  # Thay đổi màu sắc
    transforms.ToTensor(),  # Chuyển đổi sang tensor
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  # Chuẩn hóa
])

transform_val = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

# 2. Tải bộ dữ liệu
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform_train)
trainloader = DataLoader(trainset, batch_size=32, shuffle=True, num_workers=2)

valset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                      download=True, transform=transform_val)
valloader = DataLoader(valset, batch_size=32, shuffle=False, num_workers=2)

# 3. Xây dựng mô hình CNN đơn giản
class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super(SimpleCNN, self).__init__()
        # Khối tích chập 1
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )
        # Khối tích chập 2
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 64, kernel_size=3, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )
        # Khối tích chập 3
        self.conv3 = nn.Sequential(
            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )
        # Global Average Pooling và đầu phân loại
        self.gap = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Linear(128, num_classes)
        
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.gap(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

# 4. Khởi tạo mô hình và thiết lập huấn luyện
model = SimpleCNN(num_classes=10)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min', patience=3, factor=0.5)

# 5. Hàm huấn luyện
def train(model, trainloader, criterion, optimizer, epochs=10):
    model.train()
    for epoch in range(epochs):
        running_loss = 0.0
        correct = 0
        total = 0
        
        for i, data in enumerate(trainloader, 0):
            inputs, labels = data
            
            # Zero the parameter gradients
            optimizer.zero_grad()
            
            # Forward + backward + optimize
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            
            # Print statistics
            running_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
            
            if i % 100 == 99:
                print(f'[{epoch + 1}, {i + 1}] loss: {running_loss / 100:.3f} | acc: {100 * correct / total:.2f}%')
                running_loss = 0.0
                
        # Validate after each epoch
        validate(model, valloader, criterion)
        
# 6. Hàm đánh giá
def validate(model, valloader, criterion):
    model.eval()
    val_loss = 0.0
    correct = 0
    total = 0
    
    with torch.no_grad():
        for data in valloader:
            images, labels = data
            outputs = model(images)
            loss = criterion(outputs, labels)
            
            val_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    
    accuracy = 100 * correct / total
    avg_loss = val_loss / len(valloader)
    print(f'Validation Loss: {avg_loss:.3f} | Accuracy: {accuracy:.2f}%')
    
    # Cập nhật scheduler
    scheduler.step(avg_loss)
    
    return accuracy

# 7. Bắt đầu huấn luyện
train(model, trainloader, criterion, optimizer, epochs=20)

Chiến lược nâng cao Phân loại Hình ảnh

Transfer Learning

Thay vì huấn luyện từ đầu, transfer learning sử dụng một mô hình đã được huấn luyện trước (pre-trained) trên một bộ dữ liệu lớn như ImageNet, sau đó tinh chỉnh (fine-tune) mô hình cho nhiệm vụ cụ thể:

import torchvision.models as models

# Tải mô hình ResNet50 đã huấn luyện trước
model = models.resnet50(weights='IMAGENET1K_V2')

# Đóng băng các lớp
for param in model.parameters():
    param.requires_grad = False
    
# Thay thế lớp fully connected cuối cùng
num_features = model.fc.in_features
model.fc = nn.Linear(num_features, num_classes)  # num_classes là số lớp trong bộ dữ liệu mới

# Chỉ cập nhật tham số của lớp fc mới
optimizer = optim.Adam(model.fc.parameters(), lr=0.001)

Lợi ích của transfer learning:

  • Giảm đáng kể thời gian huấn luyện

  • Hoạt động tốt với bộ dữ liệu nhỏ

  • Cải thiện hiệu suất trên nhiều nhiệm vụ khác nhau

Ensemble Learning

Kết hợp nhiều mô hình để cải thiện độ chính xác và độ tin cậy:

def ensemble_predict(models, inputs):
    # Tổng hợp dự đoán từ nhiều mô hình
    outputs = torch.zeros(inputs.size(0), num_classes)
    
    for model in models:
        model.eval()
        with torch.no_grad():
            output = model(inputs)
            outputs += nn.functional.softmax(output, dim=1)
    
    # Lấy trung bình các dự đoán
    outputs /= len(models)
    
    # Trả về lớp có xác suất cao nhất
    _, predicted = torch.max(outputs, 1)
    return predicted

Các phương pháp ensemble phổ biến:

  • Bagging: Huấn luyện nhiều mô hình trên các tập con dữ liệu khác nhau

  • Boosting: Huấn luyện mô hình mới tập trung vào các lỗi của mô hình trước

  • Stacking: Sử dụng một mô hình meta để kết hợp dự đoán từ các mô hình cơ sở

Knowledge Distillation

Chuyển kiến thức từ một mô hình lớn (teacher) sang mô hình nhỏ hơn (student):

def distillation_loss(student_outputs, teacher_outputs, labels, T=2.0, alpha=0.5):
    # T là tham số nhiệt độ, alpha là trọng số cân bằng
    
    # Mất mát cứng - sử dụng nhãn thật
    hard_loss = nn.CrossEntropyLoss()(student_outputs, labels)
    
    # Mất mát mềm - học từ phân phối xác suất của teacher
    soft_targets = nn.functional.softmax(teacher_outputs / T, dim=1)
    soft_prob = nn.functional.log_softmax(student_outputs / T, dim=1)
    soft_loss = nn.KLDivLoss(reduction='batchmean')(soft_prob, soft_targets) * (T * T)
    
    # Kết hợp hai loại mất mát
    return alpha * hard_loss + (1 - alpha) * soft_loss

Xử lý Mất cân bằng Lớp

Khi số lượng mẫu giữa các lớp không cân bằng:

  1. Điều chỉnh trọng số lớp:

# Tính trọng số ngược với tần suất lớp
class_counts = [len(trainset.dataset[trainset.dataset.targets == i]) for i in range(num_classes)]
weights = 1.0 / torch.tensor(class_counts, dtype=torch.float)
weights = weights / weights.sum() * num_classes
class_weights = weights.to(device)

# Sử dụng trọng số trong hàm mất mát
criterion = nn.CrossEntropyLoss(weight=class_weights)
  1. Lấy mẫu lại (Resampling):

    • Oversampling: Tăng mẫu từ các lớp thiểu số

    • Undersampling: Giảm mẫu từ các lớp đa số

    • SMOTE: Tạo mẫu tổng hợp cho các lớp thiểu số

Thách thức trong Phân loại Hình ảnh

1. Độ biến thiên nội tại

Các đối tượng cùng lớp có thể trông rất khác nhau:

  • Góc nhìn khác nhau

  • Điều kiện ánh sáng khác nhau

  • Kích thước và tỷ lệ khác nhau

  • Biến thể và phong cách khác nhau

Giải pháp: Tăng cường dữ liệu, thu thập dữ liệu đa dạng hơn

2. Giảm thiểu Overfitting

Mô hình phức tạp có thể ghi nhớ dữ liệu huấn luyện thay vì học các mẫu chung:

Giải pháp:

  • Dropout

  • Chuẩn hóa batch

  • Tăng cường dữ liệu

  • Regularization (L1, L2)

  • Early stopping

3. Khả năng Tổng quát

Mô hình thường hoạt động kém khi đối mặt với dữ liệu từ phân phối khác:

Giải pháp:

  • Domain adaptation

  • Huấn luyện đối nghịch (Adversarial training)

  • Tăng cường dữ liệu mạnh

  • Test-time augmentation

4. Hiệu quả Tính toán

Mô hình CNN sâu thường đòi hỏi tài nguyên tính toán lớn:

Giải pháp:

  • Distillation

  • Pruning (tỉa nhánh)

  • Quantization

  • Mô hình nhẹ như MobileNet, EfficientNet

Ứng dụng Phân loại Hình ảnh trong Thực tế

1. Y tế

  • Chẩn đoán hình ảnh y tế (X-quang, CT, MRI)

  • Phát hiện khối u và bất thường

  • Phân loại tế bào và mô

2. Nông nghiệp

  • Phát hiện bệnh cây trồng

  • Phân loại cây trồng và quả

  • Theo dõi sức khỏe của cây trồng

3. Bán lẻ

  • Hệ thống thanh toán tự động

  • Phân tích kệ hàng

  • Tìm kiếm hình ảnh sản phẩm

4. An ninh

  • Nhận dạng khuôn mặt

  • Giám sát và phát hiện đối tượng

  • Phát hiện vũ khí và vật nguy hiểm

5. Ô tô tự lái

  • Nhận diện đường phố, biển báo và phương tiện

  • Phát hiện người đi bộ

  • Hiểu cảnh quan xung quanh

Xu hướng mới trong Phân loại Hình ảnh

1. Mô hình Transformer

Vision Transformer (ViT) và các biến thể đã vượt qua hiệu suất CNN truyền thống trên nhiều benchmark:

import timm

# Tải mô hình Vision Transformer
model = timm.create_model('vit_base_patch16_224', pretrained=True, num_classes=num_classes)

2. Neural Architecture Search (NAS)

Tự động tìm kiếm kiến trúc tối ưu thay vì thiết kế thủ công:

  • EfficientNet và EfficientNetV2

  • NASNet

  • MnasNet

3. Học tự giám sát (Self-supervised Learning)

Học đặc trưng có ý nghĩa mà không cần nhãn:

  • Contrastive Learning (SimCLR, MoCo)

  • Masked Autoencoders (MAE)

  • DINO và DINO-v2

4. Học ít mẫu (Few-shot Learning)

Nhận dạng lớp mới với rất ít mẫu huấn luyện:

  • Prototypical Networks

  • Matching Networks

  • Model-Agnostic Meta-Learning (MAML)

Đánh giá và Phân tích Mô hình

1. Confusion Matrix

from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

def plot_confusion_matrix(true_labels, predictions, class_names):
    cm = confusion_matrix(true_labels, predictions)
    plt.figure(figsize=(10, 8))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=class_names, yticklabels=class_names)
    plt.xlabel('Dự đoán')
    plt.ylabel('Thực tế')
    plt.title('Ma trận nhầm lẫn')
    plt.show()

2. Activation Maps

Hiển thị vùng quan trọng khi mô hình ra quyết định (CAM, Grad-CAM):

from pytorch_grad_cam import GradCAM
from pytorch_grad_cam.utils.image import show_cam_on_image

def visualize_class_activation_map(model, img_tensor, target_layer):
    cam = GradCAM(model=model, target_layers=[target_layer])
    grayscale_cam = cam(input_tensor=img_tensor.unsqueeze(0))[0]
    
    # Chuyển đổi tensor thành ảnh numpy
    img = img_tensor.permute(1, 2, 0).numpy()
    
    # Chuẩn hóa lại ảnh để hiển thị
    img = (img - img.min()) / (img.max() - img.min())
    
    # Áp dụng heatmap lên ảnh gốc
    visualization = show_cam_on_image(img, grayscale_cam, use_rgb=True)
    
    return visualization

3. Phân tích lỗi

Hiểu tại sao mô hình phạm sai lầm:

  • Phân tích các trường hợp khó

  • Xác định các mẫu bị phân loại sai một cách nhất quán

  • Tìm kiếm sự tương đồng giữa các lớp hay bị nhầm lẫn

Kết luận

Phân loại hình ảnh đã phát triển vượt bậc nhờ sự tiến bộ của mạng nơ-ron tích chập và các kiến trúc mới như Transformer. Từ việc nhận dạng chữ số đơn giản trong LeNet, đến khả năng phân biệt hàng nghìn danh mục trong EfficientNet và ViT, lĩnh vực này tiếp tục phát triển.

Các xu hướng mới tập trung vào hiệu quả, khả năng mở rộng và học với ít dữ liệu có nhãn. Với sự kết hợp của CNN, Transformer và các kỹ thuật học tự giám sát, tương lai của phân loại hình ảnh hứa hẹn những ứng dụng mới và hiệu suất cao hơn nữa.

Để thành công trong phân loại hình ảnh, các nhà nghiên cứu và kỹ sư nên tập trung vào chất lượng dữ liệu, kỹ thuật tăng cường, lựa chọn kiến trúc phù hợp và chiến lược huấn luyện tối ưu. Đồng thời, việc hiểu rõ các chỉ số đánh giá và phân tích kỹ lưỡng lỗi cũng rất quan trọng để cải thiện liên tục hiệu suất mô hình.

Last updated