Ứng dụng mạng MobileNet vào phân loại hình ảnh

1. Bài toán

Xây dựng một hệ thống nhận diện và phân loại hình ảnh dựa trên mạng neural MobileNet. Hệ thống này sẽ phân tích bức ảnh đầu vào và xác định đối tượng chính xuất hiện trong ảnh.

aicandy.vn

2. Thực hiện

2.1. Cấu trúc chương trình

Với mục đích dễ quản lý source code, chúng ta có thể đặt chương trình như sau:

root@aicandy:/aicandy/projects/AIcandy_RetinaNet_ObjectDetection_mqeprgnq# tree
.
├── aicandy_output_ntroyvui
│   └── aicandy_pretrain_resnet_rvkndbxy.pth
├── aicandy_retinanet_test_cliaskyp.py
├── aicandy_retinanet_train_rpnekclt.py
├── aicandy_utils_src_obilenxc
│   ├── anchors.py
│   ├── arial.ttf
│   ├── evaluate.py
│   ├── dataloader.py
│   ├── losses.py
│   └── model.py
└── image_test.jpg

Trong đó:

     – File aicandy_mobilenet_model_mhgmyhay.py chứa model

     – File aicandy_mobilenet_train_enrnptys.py chứa chương trình train

     – File aicandy_mobilenet_test_vtvlmtxo.py chứa chương trình để test.

    – File aicandy_mobilenet_convert_onnx_ydlekvna.py chứa chương trình chuyển đổi từ model pytorch sang model onnx phục vụ triển khai model trên nhiều thiết bị khác nhau.

2.2. Dữ liệu

Dữ liệu phục vụ cho việc train gồm các ảnh đã được phân loại trước và được lưu trong các folder theo đối tượng, tên của folder là tên của đối tượng. Ví dụ trong bài này chúng ta có folder ‘dogs’ để chứa tất cả các ảnh có hình ảnh ‘dog’ và folder ‘cats’ để chứa tất cả các hình ảnh có ‘cat’. 

Bộ dataset có cấu trúc như sau:

root@aicandy:/aicandy/datasets/aicandy_motorcycle_humukdiy# ls
annotations train2017 val2017
root@aicandy:/aicandy/datasets/aicandy_motorcycle_humukdiy# ls annotations/
instances_train2017.json instances_val2017.json
root@aicandy:/aicandy/datasets/aicandy_motorcycle_humukdiy#
root@aicandy:/aicandy/datasets/aicandy_motorcycle_humukdiy# cd train2017/
root@aicandy:/aicandy/datasets/aicandy_motorcycle_humukdiy/train2017# tree | head -n 6
.
├── 000000000073.jpg
├── 000000000086.jpg
├── 000000000529.jpg
├── 000000000629.jpg
├── 000000000656.jpg
root@aicandy:/aicandy/datasets/aicandy_motorcycle_humukdiy/train2017# cd ../val2017/
root@aicandy:/aicandy/datasets/aicandy_motorcycle_humukdiy/val2017# tree | head -n 6
.
├── 000000007386.jpg
├── 000000007816.jpg
├── 000000008211.jpg
├── 000000011149.jpg
├── 000000011511.jpg
root@aicandy:/aicandy/datasets/aicandy_motorcycle_humukdiy/val2017#

Bộ dữ liệu sử dụng trong bài gồm bộ aicandy_cats_mkemktch và bộ aicandy_dogs_lpmdvpox được download miễn phí tại đây

2.3. Build model

Xây dựng mô hình gồm các thành phần chính:

def conv_dw(in_channels, out_channels, stride):

conv_dw là một hàm tiện ích được định nghĩa bên trong lớp để xây dựng một khối Convolution đặc trưng của MobileNet, còn gọi là Depthwise Separable Convolution.

nn.Conv2d(in_channels, in_channels, 3, stride, 1, groups=in_channels, bias=False),

Lớp nn.Conv2d đầu tiên có groups=in_channels, nghĩa là mỗi kênh đầu vào sẽ có một bộ lọc riêng biệt (depthwise convolution). Điều này giảm đáng kể số lượng tham số so với một convolution thông thường. Kích thước bộ lọc là 3×3, với stride và padding được chỉ định. Sau lớp convolution là một lớp Batch Normalization (nn.BatchNorm2d) và một hàm kích hoạt ReLU.

nn.Conv2d(in_channels, out_channels, 1, 1, 0, bias=False),

Lớp nn.Conv2d thứ hai thực hiện một phép 1×1 convolution (pointwise convolution), dùng để kết hợp các đặc trưng từ bước Depthwise Convolution. Sau lớp convolution là một lớp Batch Normalization (nn.BatchNorm2d) và một hàm kích hoạt ReLU.

Tạo một chuỗi các lớp neural network được sắp xếp tuần tự sử dụng nn.Sequential.

Các lớp chính bao gồm:

  • Conv2d đầu tiên: Áp dụng một convolution với 32 bộ lọc 3×3, stride 2, và padding 1 trên đầu vào 3 kênh (ảnh RGB).
  • Các khối conv_dw: Các khối Depthwise Separable Convolution được tạo bằng cách gọi hàm conv_dw. Các khối này có số lượng đầu vào và đầu ra kênh khác nhau, với stride khác nhau (1 hoặc 2), giúp giảm kích thước không gian của đầu vào.
  • Adaptive Average Pooling: Lớp nn.AdaptiveAvgPool2d(1) thực hiện pooling để giảm kích thước không gian xuống 1×1, bất kể kích thước đầu vào trước đó.

self.fc = nn.Linear(1024, num_classes)

Lớp Fully Connected (nn.Linear) thực hiện phép chiếu tuyến tính từ đầu ra của phần convolution (1024 đặc trưng) thành số lượng lớp phân loại được chỉ định (num_classes).

[conv_dw(512, 512, 1) for _ in range(5)]

Sử dụng 5 chuỗi khối ‘conv_dw” để cho phép mô hình học các đặc trưng sâu hơn ở cùng một kích thước không gian nhưng với nhiều phép biến đổi hơn. Sử dụng ‘*’ để chuyển đổi danh sách các phần tử thành các phần tử độc lập để truyền vào nn.Sequential.

2.4. Chương trình train

Bước 1:  import một số thư viện để xử lý các tác vụ liên quan tới tệp và đường dẫn (os, sys), thư viện xây dựng và tối ưu hóa mô hình (torch, torch.optim, torch.nn), các thư viện xử lý hình ảnh (datasets, transform, random_split, train_test_split, Dataloader).

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
from torchvision import datasets, transforms
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader, random_split
import os
import numpy as np
from aicandy_model_src_eboxesox.aicandy_mobilenet_model_mhgmyhay import CustomMobileNet

Bước 2: lựa chọn sử dụng CPU hay GPU để train, chúng ta sử dụng câu lệnh:

device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)

Bước 3: tăng cường dữ liệu bằng sử dụng transforms, chia ra transforms cho tập ‘train’ và transforms cho tập ‘val’ với mục đích:

transforms  cho tập ‘train’: Mục tiêu chính của tập huấn luyện là giúp mô hình học được các đặc trưng hữu ích và tổng quát. Data augmentation giúp làm phong phú tập dữ liệu bằng cách tạo ra nhiều biến thể của các hình ảnh gốc. Điều này làm cho mô hình bớt nhạy cảm với các thay đổi nhỏ và học được các đặc trưng mạnh mẽ hơn.

transforms  cho tập ‘val’: Trong quá trình kiểm tra/đánh giá, mục tiêu là đánh giá mô hình trên các dữ liệu chưa được nhìn thấy (unseen data) mà không thay đổi bản chất của hình ảnh. Vì vậy, các phép biến đổi cần bảo toàn hình ảnh nguyên gốc để kết quả đánh giá được chính xác và phản ánh đúng khả năng của mô hình.

transforms.RandomResizedCrop(224): thực hiện việc cắt (crop) ngẫu nhiên một vùng từ ảnh gốc, sau đó thay đổi kích thước vùng cắt đó về kích thước 224×224 pixel.

transforms.RandomHorizontalFlip(): Lật ngẫu nhiên hình ảnh theo chiều ngang. Tăng cường dữ liệu bằng cách tạo ra các phiên bản lật của hình ảnh, giúp mô hình học được các đặc điểm của hình ảnh bất kể hướng của chúng.

transforms.ToTensor(): Chuyển đổi hình ảnh từ dạng PIL hoặc NumPy array thành một tensor PyTorch. Tensor là định dạng dữ liệu mà PyTorch yêu cầu để xử lý dữ liệu trong quá trình huấn luyện.

transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]): Chuẩn hóa các giá trị pixel của hình ảnh. Đưa các giá trị pixel về phạm vi có giá trị trung bình là 0 và độ lệch chuẩn là 1 cho từng kênh màu (RGB). Điều này giúp mô hình huấn luyện hiệu quả hơn vì các giá trị đầu vào được chuẩn hóa.

Bước 4: Load data và phân chia thành 2 tập train và val để đánh giá model

train_dataset, val_dataset = random_split(dataset, [train_size, val_size])

Chia một tập dữ liệu (dataset) thành hai tập con: một tập dùng để huấn luyện (train_dataset) và một tập dùng để kiểm tra hoặc xác thực (val_dataset). Thông thường tập train chiếm 80% và tập val chiếm 20% tổng mẫu.

train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)

Sử dụng DataLoader để tải dữ liệu từ một đối tượng Dataset.

  • batch_size=batch_size: Xác định số lượng mẫu trong mỗi lô. Giá trị batch_size được xác định trước đó trong mã của bạn. thông thường hay chọnbatch_size là 16 hoặc 32.
  • shuffle=True: Tùy chọn shuffle=True sẽ xáo trộn dữ liệu mỗi lần trước khi tạo ra các lô mới trong quá trình huấn luyện. Điều này giúp giảm thiểu khả năng mô hình nhớ vị trí của các mẫu và dẫn đến overfitting (quá khớp). Xáo trộn dữ liệu trong quá trình huấn luyện là một thực hành tốt giúp mô hình học được các đặc trưng một cách tổng quát hơn.

Bước 5: Lưu thông tin các đối tượng có trong dataset

with open(‘label.txt’, ‘w’) as f:
     for idx, class_name in enumerate(dataset.classes):
     f.write(f'{idx}: {class_name}\n’)

Chương trình sẽ lưu id và tên của đối tượng vào file label.txt.

Bước 6: Tạo model, tối ưu và tạo hàm mất mát

model = CustomMobileNet(num_classes=num_classes).to(device)

Khởi tạo mô hình MobileNet với các tham số cụ thể và chuyển nó sang thiết bị (GPU hoặc CPU) để thực thi.

criterion = nn.CrossEntropyLoss():

Hàm mất mát này đo lường độ sai lệch giữa phân phối xác suất dự đoán của mô hình và phân phối nhãn thật. Giá trị mất mát này càng nhỏ thì mô hình dự đoán càng chính xác.

optimizer = optim.Adam(model.parameters(), lr=0.001):

  • optim.Adam:
    • Adam là một thuật toán tối ưu hóa rất phổ biến trong huấn luyện mô hình học sâu, kết hợp giữa phương pháp tối ưu hóa theo momentum và phương pháp tối ưu hóa theo hàm điều chỉnh AdaGrad.
    • Adam tự động điều chỉnh tốc độ học (learning rate) cho từng tham số dựa trên các biến động trong quá trình huấn luyện, giúp tăng tốc độ hội tụ và thường cho kết quả tốt hơn so với các bộ tối ưu hóa khác như SGD.
  • model.parameters():
    • Tham số này lấy tất cả các tham số có thể huấn luyện được của mô hình DenseNet mà bạn đã tạo trước đó. Bộ tối ưu hóa sẽ cập nhật những tham số này trong quá trình huấn luyện.
  • lr=0.001:
    • lr là tốc độ học (learning rate), một tham số quan trọng quyết định mức độ thay đổi của các tham số mô hình sau mỗi bước cập nhật. Ở đây, tốc độ học được đặt là 0.001, một giá trị thường dùng để khởi tạo trong nhiều bài toán học sâu.

optimizer.zero_grad():

Trước khi thực hiện tính toán gradients, cần đặt lại các gradients cũ (nếu có) bằng zero_grad(). Điều này đảm bảo rằng gradients không bị cộng dồn từ các vòng lặp trước đó.

loss = criterion(outputs, labels):

Hàm mất mát (criterion) so sánh đầu ra dự đoán (outputs) với nhãn thực tế (labels) và tính toán giá trị mất mát (loss), biểu thị độ sai lệch của mô hình.

loss.backward():

backward() tính toán gradients của hàm mất mát đối với các tham số của mô hình. Đây là bước đầu tiên trong quá trình cập nhật các tham số dựa trên gradients.

_, preds = torch.max(outputs, 1):

torch.max(outputs, 1) lấy giá trị lớn nhất (có xác suất cao nhất) và chỉ số tương ứng từ outputs dọc theo chiều thứ nhất (class dimension). preds là dự đoán của mô hình về lớp đầu ra.

correct += predicted.eq(labels).sum().item()

predicted: Đây là các dự đoán của mô hình, thường là các chỉ số lớp (class indices) với giá trị lớn nhất sau khi mô hình áp dụng một hàm kích hoạt như softmax hoặc argmax trên đầu ra (output).

eq: Đây là một phương thức của torch.Tensor, viết tắt của “equals”. Phương thức này thực hiện so sánh từng phần tử của tensor predicted với tensor labels và trả về một tensor Boolean có cùng kích thước, trong đó mỗi phần tử là True nếu các phần tử tương ứng bằng nhau và False nếu khác nhau.

Bước 8: Hiện thị thông tin kết quả train và lưu model

val_loss = val_loss / len(val_dataset)
val_acc = 100. * correct / total

print(f’Epoch [{epoch+1}/{num_epochs}], Train Loss: {train_loss:.4f}, Train Accuracy: {train_acc:.2f}%, Val Loss: {val_loss:.4f}, Val Accuracy: {val_acc:.2f}%’)

  • val_acc: Tính toán và lưu trữ độ chính xác của mô hình trên tập kiểm tra.
  • val_loss: Tính toán và lưu trữ mất mát trung bình của mô hình trên tập kiểm tra.
  • print(…): Hiển thị kết quả của epoch bao gồm: mất mát và độ chính xác trên cả tập huấn luyện và tập kiểm tra. Điều này giúp bạn theo dõi quá trình huấn luyện và kiểm tra mô hình, xác định xem mô hình có cải thiện hay không qua các epoch.

if val_acc > best_acc:
     best_acc = val_acc
     torch.save(model.state_dict(), model_path)
    print(f’Model saved with accuracy: {best_acc:.2f}%’)

Sau mỗi epoch train, kiểm tra tính chính xác, nếu độ chính xác tốt lên thì sẽ lưu model với bộ trọng số này.

2.5. Chương trình test

Bước 1: Cần import một số thư viện và cấu trúc model

import torch
import torch.nn.functional as F
from torchvision import transforms
from PIL import Image
import numpy as np
from aicandy_model_src_eboxesox.aicandy_mobilenet_model_mhgmyhay import CustomMobileNet

Bước 2: Xác định tên đối tượng có trong model

with open(label_path, ‘r’) as f:
     labels = {int(line.split(“: “)[0]): line.split(“: “)[1].strip() for line in f}

Đoạn trên sẽ trả về cho labels một từ điển với cặp key-value, trong đó:

  • key là ID của nhãn (dạng số nguyên).
  • value là tên của nhãn (dạng chuỗi).

Bước 3: Load model

model = CustomMobileNet(num_classes=num_classes)
model.load_state_dict(torch.load(model_path))
model = model.to(device)
model.eval()

Tạo cấu trúc model khi test giống như cấu trúc model khi train.

model.load_state_dict(torch.load(model_path))

  • Tải trọng số của mô hình từ tệp model_path. Trọng số này là kết quả của quá trình huấn luyện trước đó.

model.eval()

Chuyển mô hình sang chế độ đánh giá (evaluation mode). Trong chế độ này, các lớp như DropoutBatchNorm sẽ hoạt động khác so với khi ở chế độ huấn luyện.

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

Đoạn mã này định nghĩa một chuỗi các bước tiền xử lý (preprocessing) được áp dụng cho các hình ảnh trước khi chúng được đưa vào mô hình học sâu. Các bước tiền xử lý này giúp chuẩn hóa dữ liệu đầu vào và chuyển đổi hình ảnh sang định dạng phù hợp cho mô hình. Trong đó:

  • Normalize là bước chuẩn hóa từng kênh màu của hình ảnh (R, G, B) bằng cách trừ đi giá trị trung bình (mean) và chia cho độ lệch chuẩn (std).
  • [0.485, 0.456, 0.406]: Đây là các giá trị trung bình của các kênh màu (R, G, B) dựa trên bộ dữ liệu ImageNet, một bộ dữ liệu lớn phổ biến được dùng để huấn luyện các mô hình học sâu.
  • [0.229, 0.224, 0.225]: Đây là các giá trị độ lệch chuẩn tương ứng của các kênh màu (R, G, B), cũng dựa trên bộ dữ liệu ImageNet.
  • Chuẩn hóa này giúp đảm bảo rằng dữ liệu đầu vào có phân phối ổn định, giúp mô hình học tốt hơn và tăng tốc độ hội tụ.

Bước 4: Dự đoán kết quả

with torch.no_grad():
    outputs = model(image)
_, predicted = outputs.max(1)

with torch.no_grad():

  • Đây là một ngữ cảnh (context) trong PyTorch, trong đó các phép tính bên trong sẽ không yêu cầu lưu trữ thông tin về gradient.
  • Điều này giúp tiết kiệm bộ nhớ và tăng tốc độ tính toán khi thực hiện dự đoán, vì không cần tính toán và lưu trữ gradient như khi huấn luyện mô hình.
  • Chế độ này thường được sử dụng trong quá trình dự đoán (inference) hoặc đánh giá mô hình.

_, predicted = outputs.max(1)

outputs.max(1): Trả về hai giá trị: giá trị lớn nhất và chỉ số của giá trị đó. Mục đích là xác định lớp mà mô hình dự đoán có xác suất cao nhất.

2.6. Chương trình chuyển đổi model

Bước 1: Load model

model = CustomMobileNet(num_classes=num_classes)
state_dict = torch.load(model_path, map_location=’cpu’)
model.load_state_dict(state_dict)
model.eval()

Load model customMobileNet đã được định nghĩa ở trên.

  • torch.load(model_path): Tải trọng số đã được huấn luyện của mô hình từ tệp model_path. Trọng số này được lưu trước đó sau khi quá trình huấn luyện mô hình hoàn tất.
  • map_location=’cpu’: Chỉ định rằng trọng số sẽ được tải vào bộ nhớ CPU, ngay cả khi chúng được huấn luyện trên GPU. Điều này hữu ích nếu bạn chỉ có CPU trong môi trường hiện tại hoặc không cần GPU cho việc dự đoán.
  • state_dict chứa tất cả các tham số của mô hình được lưu dưới dạng từ điển, và hàm load_state_dict gán những giá trị này vào các lớp tương ứng trong mô hình.

Bước 2: Convert model

dummy_input = torch.randn(1, 3, 224, 224):

torch.randn tạo ra một tensor với các giá trị ngẫu nhiên tuân theo phân phối chuẩn (mean=0, std=1). Kích thước của tensor này là (1, 3, 224, 224)

torch.onnx.export(model, # mô hình đang chạy
        dummy_input, # input tensor
        onnx_path, # nơi lưu file onnx
        export_params=True, # lưu các trọng số đã training
        opset_version=11, # phiên bản ONNX
        do_constant_folding=True, # tối ưu hoá mô hình
        input_names = [‘input’], # tên của input
        output_names = [‘output’], # tên của output
        dynamic_axes={‘input’ : {0 : ‘batch_size’}, ‘output’ : {0 : ‘batch_size’}})

  • torch.onnx.export:
    • Đây là hàm dùng để xuất mô hình PyTorch sang định dạng ONNX.
  • dummy_input:
    • Đây là đầu vào giả lập mà mô hình sẽ sử dụng để theo dõi các phép toán và xây dựng biểu đồ tính toán trong định dạng ONNX.
  • onnx_path:
    • Đường dẫn đến file mà mô hình ONNX sẽ được lưu (file có phần mở rộng là .onnx).
  • input_names=[‘input’]:
    • Tên của các đầu vào trong mô hình ONNX. Ở đây, đầu vào của mô hình sẽ được đặt tên là “input”.
  • output_names=[‘output’]:
    • Tên của các đầu ra trong mô hình ONNX. Ở đây, đầu ra của mô hình sẽ được đặt tên là “output”.

3. Kết quả train

Thực hiện train với epoch 50, độ chính xác là 77.6%, đạt được ở epoch 48. Đây là chương trình mẫu, để tăng độ chính xác, chúng ta cũng cần điều chỉnh thêm một số tham số, tăng epoch cũng như tăng số lượng mẫu. Dưới đây là log train:

root@aicandy:/aicandy/projects/AIcandy_MobileNet_ImageClassification_gargdlos# python aicandy_mobilenet_train_enrnptys.py --train_dir ../dataset --num_epochs 50 --batch_size 32 --model_path aicandy_model_out_tdtagoyx/aicandy_model_pth_bmdmrcav.pth
Epoch [1/50], Train Loss: 0.6915, Train Accuracy: 58.60%, Val Loss: 0.6794, Val Accuracy: 58.20% 
Model saved with accuracy: 58.20% 
Epoch [2/50], Train Loss: 0.6299, Train Accuracy: 64.30%, Val Loss: 0.6438, Val Accuracy: 64.46% 
Model saved with accuracy: 64.46% 
Epoch [3/50], Train Loss: 0.5912, Train Accuracy: 68.56%, Val Loss: 0.6113, Val Accuracy: 66.71% 
Model saved with accuracy: 66.71% 
Epoch [4/50], Train Loss: 0.5233, Train Accuracy: 74.57%, Val Loss: 0.6232, Val Accuracy: 66.83% 
Model saved with accuracy: 66.83% 
Epoch [5/50], Train Loss: 0.4670, Train Accuracy: 77.26%, Val Loss: 0.6771, Val Accuracy: 65.96% 
Epoch [6/50], Train Loss: 0.4164, Train Accuracy: 80.74%, Val Loss: 0.6082, Val Accuracy: 67.21% 
Model saved with accuracy: 67.21% 
Epoch [7/50], Train Loss: 0.3943, Train Accuracy: 82.52%, Val Loss: 0.6392, Val Accuracy: 66.46% 
Epoch [8/50], Train Loss: 0.3573, Train Accuracy: 84.50%, Val Loss: 0.6657, Val Accuracy: 69.71% 
Model saved with accuracy: 69.71% 
Epoch [9/50], Train Loss: 0.2623, Train Accuracy: 89.57%, Val Loss: 0.7066, Val Accuracy: 69.59% 
Epoch [10/50], Train Loss: 0.2442, Train Accuracy: 89.88%, Val Loss: 0.8039, Val Accuracy: 69.96% 
Model saved with accuracy: 69.96% 
Epoch [11/50], Train Loss: 0.1741, Train Accuracy: 92.98%, Val Loss: 0.8408, Val Accuracy: 71.59% 
Model saved with accuracy: 71.59% 
Epoch [12/50], Train Loss: 0.2232, Train Accuracy: 90.89%, Val Loss: 0.7181, Val Accuracy: 72.72% 
Model saved with accuracy: 72.72% 
Epoch [13/50], Train Loss: 0.1589, Train Accuracy: 94.02%, Val Loss: 0.7917, Val Accuracy: 73.47% 
Model saved with accuracy: 73.47% 
Epoch [14/50], Train Loss: 0.1431, Train Accuracy: 94.39%, Val Loss: 0.8002, Val Accuracy: 73.09% 
Epoch [15/50], Train Loss: 0.1542, Train Accuracy: 93.58%, Val Loss: 0.9225, Val Accuracy: 71.59% 
Epoch [16/50], Train Loss: 0.1391, Train Accuracy: 93.99%, Val Loss: 0.8496, Val Accuracy: 73.59% 
Model saved with accuracy: 73.59% 
Epoch [17/50], Train Loss: 0.1059, Train Accuracy: 95.96%, Val Loss: 1.0258, Val Accuracy: 74.47% 
Model saved with accuracy: 74.47% 
Epoch [18/50], Train Loss: 0.1176, Train Accuracy: 95.33%, Val Loss: 0.9818, Val Accuracy: 68.71% 
Epoch [19/50], Train Loss: 0.0854, Train Accuracy: 96.65%, Val Loss: 1.1962, Val Accuracy: 72.72% 
Epoch [20/50], Train Loss: 0.0847, Train Accuracy: 96.74%, Val Loss: 1.0784, Val Accuracy: 73.47% 
Epoch [21/50], Train Loss: 0.0927, Train Accuracy: 96.18%, Val Loss: 0.9523, Val Accuracy: 75.47% 
Model saved with accuracy: 75.47% 
Epoch [22/50], Train Loss: 0.0938, Train Accuracy: 96.24%, Val Loss: 1.0460, Val Accuracy: 72.47% 
Epoch [23/50], Train Loss: 0.0580, Train Accuracy: 97.90%, Val Loss: 1.0497, Val Accuracy: 74.47% 
Epoch [24/50], Train Loss: 0.0521, Train Accuracy: 98.15%, Val Loss: 1.1509, Val Accuracy: 74.84% 
Epoch [25/50], Train Loss: 0.0856, Train Accuracy: 96.90%, Val Loss: 0.9426, Val Accuracy: 72.97% 
Epoch [26/50], Train Loss: 0.0687, Train Accuracy: 97.31%, Val Loss: 0.9311, Val Accuracy: 74.22% 
Epoch [27/50], Train Loss: 0.0574, Train Accuracy: 97.56%, Val Loss: 1.1725, Val Accuracy: 71.84% 
Epoch [28/50], Train Loss: 0.0787, Train Accuracy: 96.96%, Val Loss: 1.0270, Val Accuracy: 72.97% 
Epoch [29/50], Train Loss: 0.0637, Train Accuracy: 97.84%, Val Loss: 0.9170, Val Accuracy: 73.84% 
Epoch [30/50], Train Loss: 0.0773, Train Accuracy: 96.93%, Val Loss: 0.9678, Val Accuracy: 73.09% 
Epoch [31/50], Train Loss: 0.0491, Train Accuracy: 98.21%, Val Loss: 1.1778, Val Accuracy: 73.34% 
Epoch [32/50], Train Loss: 0.0431, Train Accuracy: 98.43%, Val Loss: 1.1537, Val Accuracy: 73.09% 
Epoch [33/50], Train Loss: 0.0391, Train Accuracy: 98.72%, Val Loss: 1.0644, Val Accuracy: 73.72% 
Epoch [34/50], Train Loss: 0.0404, Train Accuracy: 98.65%, Val Loss: 1.1872, Val Accuracy: 73.84% 
Epoch [35/50], Train Loss: 0.0352, Train Accuracy: 98.68%, Val Loss: 1.2288, Val Accuracy: 74.09% 
Epoch [36/50], Train Loss: 0.0820, Train Accuracy: 96.71%, Val Loss: 0.9780, Val Accuracy: 72.22% 
Epoch [37/50], Train Loss: 0.1001, Train Accuracy: 95.93%, Val Loss: 1.0107, Val Accuracy: 75.09% 
Epoch [38/50], Train Loss: 0.0638, Train Accuracy: 97.71%, Val Loss: 0.9970, Val Accuracy: 74.47% 
Epoch [39/50], Train Loss: 0.0440, Train Accuracy: 98.62%, Val Loss: 1.0139, Val Accuracy: 75.22% 
Epoch [40/50], Train Loss: 0.0398, Train Accuracy: 98.65%, Val Loss: 1.0311, Val Accuracy: 74.84% 
Epoch [41/50], Train Loss: 0.0325, Train Accuracy: 98.84%, Val Loss: 1.1197, Val Accuracy: 73.34% 
Epoch [42/50], Train Loss: 0.0465, Train Accuracy: 98.34%, Val Loss: 1.0813, Val Accuracy: 75.34% 
Epoch [43/50], Train Loss: 0.0578, Train Accuracy: 97.78%, Val Loss: 1.4150, Val Accuracy: 72.97% 
Epoch [44/50], Train Loss: 0.0583, Train Accuracy: 97.53%, Val Loss: 0.9240, Val Accuracy: 76.35% 
Model saved with accuracy: 76.35% 
Epoch [45/50], Train Loss: 0.0524, Train Accuracy: 98.12%, Val Loss: 0.9629, Val Accuracy: 77.35% 
Model saved with accuracy: 77.35% 
Epoch [46/50], Train Loss: 0.0529, Train Accuracy: 98.09%, Val Loss: 1.0918, Val Accuracy: 74.97% 
Epoch [47/50], Train Loss: 0.0342, Train Accuracy: 98.87%, Val Loss: 0.9452, Val Accuracy: 75.84% 
Epoch [48/50],Train Loss: 0.0221, Train Accuracy: 99.44%, Val Loss: 1.0298, Val Accuracy: 77.60%
Model saved with accuracy: 77.60%
Epoch [49/50], Train Loss: 0.0243, Train Accuracy: 99.12%, Val Loss: 1.0938, Val Accuracy: 75.97%
Epoch [50/50], Train Loss: 0.0150, Train Accuracy: 99.50%, Val Loss: 1.2014, Val Accuracy: 74.84%
root@aicandy:/aicandy/projects/AIcandy_MobileNet_ImageClassification_gargdlos#

Model saved with accuracy: 77.60% Epoch [48/50],Train Loss: 0.0221, Train Accuracy: 99.44%, Val Loss: 1.0298, Val Accuracy: 77.60%

root@aicandy:/aicandy/projects/AIcandy_MobileNet_ImageClassification_gargdlos#

Model được lưu tại: aicandy_model_out_tdtagoyx/aicandy_model_pth_bmdmrcav.pth

root@aicandy:/aicandy/projects/AIcandy_MobileNet_ImageClassification_gargdlos# ls aicandy_model_out_tdtagoyx
aicandy_model_pth_bmdmrcav.pth
root@aicandy:/aicandy/projects/AIcandy_MobileNet_ImageClassification_gargdlos#

4. Kết quả test

Thử nghiệm test với hình ảnh có ‘dog’, chương trình nhận dạng đúng.

aicandy.vn

root@aicandy:/aicandy/projects/AIcandy_MobileNet_ImageClassification_gargdlos# python aicandy_mobilenet_test_vtvlmtxo.py --image_path ../aicandy_true_dog.jpg --model_path aicandy_model_out_tdtagoyx/aicandy_model_pth_bmdmrcav.pth --label_path label.txt
labels: {0: 'cats', 1: 'dogs'}
Predicted class: dogs
root@aicandy:/aicandy/projects/AIcandy_MobileNet_ImageClassification_gargdlos#

5. Source code

Toàn bộ source code được public miễn phí tại đây

Chúc bạn thành công trong hành trình khám phá và ứng dụng trí tuệ nhân tạo vào học tập và công việc. Đừng quên truy cập thường xuyên để cập nhật thêm kiến thức mới tại AIcandy