Ứng dụng mạng GoogleNet 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 GoogleNet. 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_googlenet_model_ealuvpor.py chứa model

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

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

   – File aicandy_googlenet_convert_onnx_rtdytday.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:

class InceptionModule(nn.Module)

module hạt nhân, mỗi InceptionModule bao gồm nhiều nhánh (branch) song song với các loại tích chập khác nhau và một lớp pooling, sau đó kết hợp các đầu ra của chúng lại với nhau.

self.branch1 = nn.Conv2d(in_channels, out_1x1, kernel_size=1)

Đây là một lớp tích chập 1×1. Nhánh này thực hiện tích chập trực tiếp với kích thước kernel là 1×1, giữ nguyên kích thước không gian nhưng thay đổi số lượng kênh từ in_channels thành out_1x1.

self.branch2 = nn.Sequential(
     nn.Conv2d(in_channels, red_3x3, kernel_size=1),
     nn.Conv2d(red_3x3, out_3x3, kernel_size=3, padding=1)
)

Nhánh này thực hiện tích chập với kernel 3×3. Đầu tiên, nó giảm chiều số lượng kênh bằng một lớp 1×1 Conv (với red_3x3 kênh đầu ra), sau đó thực hiện tích chập 3×3 để trích xuất các đặc trưng cục bộ. Padding=1 giúp giữ nguyên kích thước không gian.

self.branch3 = nn.Sequential(
     nn.Conv2d(in_channels, red_5x5, kernel_size=1),
     nn.Conv2d(red_5x5, out_5x5, kernel_size=5, padding=2)
)

Nhánh này tương tự nhánh 2 nhưng sử dụng tích chập với kernel 5×5. Đầu tiên, nó giảm số lượng kênh bằng lớp 1×1 Conv, sau đó thực hiện tích chập 5×5 với padding=2 để giữ nguyên kích thước không gian.

self.branch4 = nn.Sequential(
     nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
     nn.Conv2d(in_channels, out_pool, kernel_size=1)
)

Nhánh này sử dụng pooling để giảm bớt sự nhiễu động của đầu vào (max pooling với kernel 3×3 và stride 1), sau đó áp dụng tích chập 1×1 để điều chỉnh số lượng kênh.

class GoogleNet(nn.Module): định nghĩa model GoogleNet

self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)

conv1: Lớp tích chập đầu tiên áp dụng kernel 7×7 với stride 2 để giảm kích thước của đầu vào. Từ một ảnh RGB (3 kênh), nó tạo ra 64 kênh đặc trưng.

self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

maxpool1: Lớp max pooling với kernel 3×3 và stride 2, tiếp tục giảm kích thước không gian.

self.conv2 = nn.Conv2d(64, 192, kernel_size=3, padding=1)

conv2: Lớp tích chập thứ hai áp dụng kernel 3×3 với padding 1, tăng số lượng kênh đặc trưng lên 192.

self.maxpool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

maxpool2: Lớp max pooling thứ hai, tương tự như maxpool1, giúp giảm kích thước không gian.

self.inception3a = InceptionModule(192, 64, 96, 128, 16, 32, 32)

self.inception3b = InceptionModule(256, 128, 128, 192, 32, 96, 64)

self.maxpool3 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

  • inception3a inception3b: Đây là các module Inception, trong đó inception3a nhận 192 kênh đầu vào và tạo ra tổng cộng 256 kênh đầu ra, còn inception3b nhận 256 kênh đầu vào và tạo ra 480 kênh đầu ra. Các module Inception cho phép mô hình học nhiều loại đặc trưng khác nhau từ cùng một đầu vào.
  • maxpool3: Lớp max pooling thứ ba, tiếp tục giảm kích thước không gian của các đặc trưng.

self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.dropout = nn.Dropout(0.4)
self.fc = nn.Linear(1024, num_classes)

  • avgpool: Lớp adaptive average pooling, giảm kích thước không gian của đặc trưng về 1×1, chuẩn bị cho bước phân loại cuối cùng.
  • dropout: Lớp dropout với tỷ lệ 0.4, giúp giảm overfitting bằng cách ngẫu nhiên bỏ bớt một số kết nối trong mạng.
  • fc: Lớp fully connected với đầu vào là 1024 (số lượng kênh từ lớp trước) và đầu ra là num_classes (số lượng lớp trong bài toán phân loại).

x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.dropout(x)
x = self.fc(x)

Quá trình xử lý đầu ra của mạng sau khi các lớp Convolutional và các mô-đun Inception đã được áp dụng. Đây là những bước cuối cùng trước khi mạng đưa ra dự đoán.

  • x = self.avgpool(x):
    • Đây là bước áp dụng lớp Average Pooling. avgpool là một lớp pooling trung bình, giúp giảm kích thước của đầu ra từ các lớp trước đó. Lớp này sẽ lấy trung bình của các vùng trong đầu ra để giảm kích thước không gian của đặc trưng (features), thường để thu nhỏ kích thước và giảm số lượng tham số trước khi đưa vào các lớp fully connected (dày đặc).
  • x = torch.flatten(x, 1):
    • Lệnh này thực hiện flattening, biến đầu ra từ lớp pooling thành một vectơ một chiều. torch.flatten(x, 1) chuyển đổi tensor x thành dạng vector một chiều bắt đầu từ chiều thứ hai (chiều 1), giữ nguyên kích thước của chiều đầu tiên (batch size). Điều này cần thiết vì các lớp fully connected yêu cầu đầu vào là vectơ một chiều.
  • x = self.dropout(x):
    • Đây là bước áp dụng dropout, một kỹ thuật regularization để ngăn overfitting. dropout ngẫu nhiên đặt một tỷ lệ phần trăm của các giá trị trong tensor đầu vào thành 0 trong quá trình huấn luyện, giúp cải thiện khả năng tổng quát của mô hình.
  • x = self.fc(x):
    • Cuối cùng, self.fc là một lớp fully connected (dense layer). Nó thực hiện phép nhân ma trận với đầu vào đã được flatten và áp dụng hàm kích hoạt để đưa ra dự đoán cuối cùng. Lớp này giúp mô hình tạo ra dự đoán từ các đặc trưng đã được rút trích và xử lý.

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, Dataloader).

import torch
import torch.optim as optim
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision.datasets as datasets
from torch.utils.data import DataLoader
from torch.utils.data import DataLoader, random_split
from aicandy_model_src_igrxgxxe.aicandy_googlenet_model_ealuvpor import GoogleNet
import os

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 với:

transforms.Resize((224, 224)): Thay đổi kích thước hình ảnh thành kích thước cố định (224×224) pixel. Đảm bảo rằng tất cả các hình ảnh đầu vào đều có cùng kích thước, phù hợp với yêu cầu của mạng nơ-ron.

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

dataset = datasets.ImageFolder(root=train_dir, transform=transform)

  • datasets.ImageFolder: Đây là một tiện ích của PyTorch để tải ảnh từ một thư mục. Thư mục này cần được tổ chức theo cấu trúc, mỗi thư mục con đại diện cho một lớp (class) khác nhau.
  • root=train_dir: train_dir là đường dẫn tới thư mục chứa các ảnh huấn luyện.
  • transform=transform: transform là các phép biến đổi (ví dụ: thay đổi kích thước, chuẩn hóa, tăng cường dữ liệu) được áp dụng lên các ảnh khi chúng được tải lên.

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

  • DataLoader: Một tiện ích của PyTorch để tải dữ liệu theo từng batch, cung cấp một iterator (bộ lặp) qua tập dữ liệu.
  • train_loader: Dùng để tải dữ liệu huấn luyện theo từng batch.
    • batch_size=batch_size: Xác định số lượng mẫu trong mỗi batch.
    • shuffle=True: Trộn ngẫu nhiên dữ liệu vào đầu mỗi epoch để ngăn mô hình học theo thứ tự của dữ liệu.
  • val_loader: Dùng để tải dữ liệu kiểm tra theo từng batch.
    • shuffle=False: Không trộn dữ liệu kiểm tra, giữ nguyên thứ tự của các mẫu.
  • num_workers=4: Xác định số lượng luồng con (subprocesses) để tải dữ liệu. Số lượng luồng con cao hơn có thể tăng tốc độ tải dữ liệu.

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 = GoogleNet(num_classes=num_classes).to(device)
model.fc = nn.Linear(model.fc.in_features, num_classes)
model.to(device)

GoogleNet(num_classes=num_classes): Tạo một đối tượng của lớp mô hình GoogLeNet với số lượng lớp đầu ra (số lớp phân loại) được xác định bởi biến num_classes. num_classes là số lượng lớp mà mô hình sẽ phân loại.

nn.Linear(model.fc.in_features, num_classes): Thay thế lớp fully connected cuối cùng bằng một lớp mới, với số lượng đầu vào (in_features) là số lượng đặc trưng đầu ra từ lớp trước đó và số lượng đầu ra là num_classes. Điều này thay đổi số lượng lớp đầu ra của mô hình để phù hợp với bài toán phân loại hiện tại.

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 GoogleNet đã 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.

Bước 7: Dự đoán nhãn và tính toán độ chính xác

_, 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 == labels).sum().item()

Tính toán số lượng dự đoán chính xác trong mô hình.

predicted == labels so sánh từng phần tử trong predicted với từng phần tử trong labels, tạo ra một tensor boolean cùng kích thước với predicted và labels, với giá trị True nếu dự đoán đúng và False nếu không đúng.

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

val_acc = 100 * val_correct / val_total

val_loss /= len(val_loader)

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’Saved best model 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 torchvision.transforms as transforms
from PIL import Image
from aicandy_model_src_igrxgxxe.aicandy_googlenet_model_ealuvpor import GoogleNet

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 = GoogleNet(num_classes=num_classes).to(device)
model.load_state_dict(torch.load(model_path, map_location=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, map_location=device))

  • 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 đó.
  • map_location=device: Đảm bảo rằng trọng số được tải lên thiết bị (device) cụ thể (CPU hoặc GPU), giúp tránh lỗi khi sử dụng mô hình trên một thiết bị khác so với thiết bị đã huấn luyện.

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 = torch.max(outputs, 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.

torch.max(outputs, 1):

  • Hàm torch.max tìm giá trị lớn nhất trong tensor outputs dọc theo chiều thứ nhất (dim=1).
  • Trong ngữ cảnh phân loại, mỗi giá trị trong outputs đại diện cho xác suất hoặc độ tin cậy rằng ảnh thuộc về một lớp cụ thể.
  • Kết quả trả về là một tuple với hai phần tử:
    • Phần tử đầu tiên (_) là giá trị lớn nhất (có thể bỏ qua ở đây).
    • Phần tử thứ hai (predicted) là chỉ số (index) của giá trị lớn nhất, tương ứng với lớp mà mô hình dự đoán.

predicted_class = labels[predicted.item()]

predicted.item():

  • Phương thức item() chuyển đổi một tensor có kích thước 1 (một giá trị duy nhất) thành một giá trị số Python thông thường (integer).
  • sử dụng chỉ số từ predicted.item() để truy cập vào labels, sau đó lấy nhãn lớp tương ứng với chỉ số dự đoán.

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

Bước 1: Load model

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

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

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

  • 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 đó.
  • map_location=’cpu’: Đảm bảo rằng trọng số được tải lên cụ thể (CPU), giúp tránh lỗi khi sử dụng mô hình trên một thiết bị khác so với thiết bị đã huấn luyện.

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.

Bước 2: Convert model

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

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 100, độ chính xác là 81.33%, đạt được ở epoch 100. Đâ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 ở 50 epoch cuối:

Epoch [50/100], Train Loss: 0.5240, Train Accuracy: 73.26%, Val Loss: 0.5612, Val Accuracy: 74.19%
Epoch [51/100], Train Loss: 0.5279, Train Accuracy: 73.29%, Val Loss: 0.5514, Val Accuracy: 71.43%
Epoch [52/100], Train Loss: 0.5296, Train Accuracy: 73.73%, Val Loss: 0.5449, Val Accuracy: 72.31%
Epoch [53/100], Train Loss: 0.5274, Train Accuracy: 74.23%, Val Loss: 0.5735, Val Accuracy: 70.30%
Epoch [54/100], Train Loss: 0.5384, Train Accuracy: 71.98%, Val Loss: 0.5352, Val Accuracy: 73.43%
Epoch [55/100], Train Loss: 0.5228, Train Accuracy: 74.70%, Val Loss: 0.5461, Val Accuracy: 72.31%
Epoch [56/100], Train Loss: 0.5218, Train Accuracy: 74.51%, Val Loss: 0.5144, Val Accuracy: 74.44%
Epoch [57/100], Train Loss: 0.5212, Train Accuracy: 74.86%, Val Loss: 0.6016, Val Accuracy: 67.92%
Epoch [58/100], Train Loss: 0.5300, Train Accuracy: 73.92%, Val Loss: 0.5819, Val Accuracy: 73.06%
Epoch [59/100], Train Loss: 0.5295, Train Accuracy: 73.98%, Val Loss: 0.5252, Val Accuracy: 74.19%
Epoch [60/100], Train Loss: 0.5091, Train Accuracy: 75.11%, Val Loss: 0.5193, Val Accuracy: 75.19%
Saved best model with accuracy: 75.19%
Epoch [61/100], Train Loss: 0.5076, Train Accuracy: 75.20%, Val Loss: 0.5326, Val Accuracy: 73.81%
Epoch [62/100], Train Loss: 0.5195, Train Accuracy: 75.64%, Val Loss: 0.5632, Val Accuracy: 73.18%
Epoch [63/100], Train Loss: 0.5108, Train Accuracy: 74.55%, Val Loss: 0.4970, Val Accuracy: 77.07%
Saved best model with accuracy: 77.07%
Epoch [64/100], Train Loss: 0.5030, Train Accuracy: 75.77%, Val Loss: 0.4916, Val Accuracy: 76.82%
Epoch [65/100], Train Loss: 0.4854, Train Accuracy: 76.55%, Val Loss: 0.5234, Val Accuracy: 74.81%
Epoch [66/100], Train Loss: 0.4965, Train Accuracy: 75.89%, Val Loss: 0.6611, Val Accuracy: 69.67%
Epoch [67/100], Train Loss: 0.5009, Train Accuracy: 76.30%, Val Loss: 0.5737, Val Accuracy: 73.93%
Epoch [68/100], Train Loss: 0.5279, Train Accuracy: 74.67%, Val Loss: 0.5398, Val Accuracy: 71.55%
Epoch [69/100], Train Loss: 0.5455, Train Accuracy: 73.45%, Val Loss: 0.6805, Val Accuracy: 69.67%
Epoch [70/100], Train Loss: 0.5379, Train Accuracy: 73.42%, Val Loss: 0.5212, Val Accuracy: 73.68%
Epoch [71/100], Train Loss: 0.4822, Train Accuracy: 77.24%, Val Loss: 0.5493, Val Accuracy: 72.18%
Epoch [72/100], Train Loss: 0.4867, Train Accuracy: 75.95%, Val Loss: 0.6054, Val Accuracy: 70.30%
Epoch [73/100], Train Loss: 0.4988, Train Accuracy: 75.80%, Val Loss: 0.4814, Val Accuracy: 77.19%
Saved best model with accuracy: 77.19%
Epoch [74/100], Train Loss: 0.4837, Train Accuracy: 77.05%, Val Loss: 0.5076, Val Accuracy: 76.94%
Epoch [75/100], Train Loss: 0.4788, Train Accuracy: 76.86%, Val Loss: 0.5052, Val Accuracy: 75.31%
Epoch [76/100], Train Loss: 0.4709, Train Accuracy: 77.71%, Val Loss: 0.5586, Val Accuracy: 71.30%
Epoch [77/100], Train Loss: 0.4764, Train Accuracy: 76.77%, Val Loss: 0.5458, Val Accuracy: 74.31%
Epoch [78/100], Train Loss: 0.4685, Train Accuracy: 77.46%, Val Loss: 0.5202, Val Accuracy: 74.94%
Epoch [79/100], Train Loss: 0.4633, Train Accuracy: 78.90%, Val Loss: 0.5324, Val Accuracy: 74.81%
Epoch [80/100], Train Loss: 0.4689, Train Accuracy: 78.08%, Val Loss: 0.4876, Val Accuracy: 76.19%
Epoch [81/100], Train Loss: 0.4631, Train Accuracy: 77.83%, Val Loss: 0.5994, Val Accuracy: 72.06%
Epoch [82/100], Train Loss: 0.4673, Train Accuracy: 77.68%, Val Loss: 0.5099, Val Accuracy: 75.56%
Epoch [83/100], Train Loss: 0.4620, Train Accuracy: 78.27%, Val Loss: 0.5305, Val Accuracy: 72.93%
Epoch [84/100], Train Loss: 0.4689, Train Accuracy: 77.36%, Val Loss: 0.5149, Val Accuracy: 74.19%
Epoch [85/100], Train Loss: 0.4479, Train Accuracy: 79.05%, Val Loss: 0.5200, Val Accuracy: 74.44%
Epoch [86/100], Train Loss: 0.4553, Train Accuracy: 78.37%, Val Loss: 0.5206, Val Accuracy: 74.69%
Epoch [87/100], Train Loss: 0.4607, Train Accuracy: 78.65%, Val Loss: 0.5143, Val Accuracy: 74.94%
Epoch [88/100], Train Loss: 0.4281, Train Accuracy: 80.09%, Val Loss: 0.4525, Val Accuracy: 78.20%
Saved best model with accuracy: 78.20%
Epoch [89/100], Train Loss: 0.4686, Train Accuracy: 77.68%, Val Loss: 0.5453, Val Accuracy: 74.56%
Epoch [90/100], Train Loss: 0.4505, Train Accuracy: 79.40%, Val Loss: 0.6440, Val Accuracy: 68.42%
Epoch [91/100], Train Loss: 0.4324, Train Accuracy: 79.49%, Val Loss: 0.4853, Val Accuracy: 75.81%
Epoch [92/100], Train Loss: 0.4225, Train Accuracy: 80.75%, Val Loss: 0.4642, Val Accuracy: 77.57%
Epoch [93/100], Train Loss: 0.4128, Train Accuracy: 82.12%, Val Loss: 0.5540, Val Accuracy: 74.06%
Epoch [94/100], Train Loss: 0.4288, Train Accuracy: 81.15%, Val Loss: 0.4906, Val Accuracy: 76.44%
Epoch [95/100], Train Loss: 0.4152, Train Accuracy: 80.96%, Val Loss: 0.4894, Val Accuracy: 79.32%
Saved best model with accuracy: 79.32%
Epoch [96/100], Train Loss: 0.4034, Train Accuracy: 81.40%, Val Loss: 0.4642, Val Accuracy: 78.45%
Epoch [97/100], Train Loss: 0.4161, Train Accuracy: 80.71%, Val Loss: 0.5029, Val Accuracy: 77.19%
Epoch [98/100], Train Loss: 0.4119, Train Accuracy: 81.59%, Val Loss: 0.5192, Val Accuracy: 75.31%
Epoch [99/100], Train Loss: 0.4130, Train Accuracy: 80.96%, Val Loss: 0.4737, Val Accuracy: 78.57%
Epoch [100/100], Train Loss: 0.3695, Train Accuracy: 83.53%, Val Loss: 0.4409, Val Accuracy: 81.33%
Saved best model with accuracy: 81.33%
root@aicandy:/aicandy/projects/AIcandy_GoogleNet_ImageClassification_issabxru#

Model được lưu tại: aicandy_model_out_bretqhex/aicandy_model_pth_syliacip.pth

root@aicandy:/aicandy/projects/AIcandy_GoogleNet_ImageClassification_issabxru# ls aicandy_model_out_bretqhex
aicandy_model_pth_silsegko.pth
root@aicandy:/aicandy/projects/AIcandy_GoogleNet_ImageClassification_issabxru#

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_GoogleNet_ImageClassification_issabxru# python aicandy_googlenet_test_mucssnkn.py --image_path ../aicandy_true_dog.jpg --model_path aicandy_model_out_bretqhex/aicandy_model_pth_syliacip.pth --label_path label.txt
labels: {0: 'cats', 1: 'dogs'}
Predicted class: dogs
root@aicandy:/aicandy/projects/AIcandy_GoogleNet_ImageClassification_issabxru#

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