Bài 5: Transfer learning
Bài này mình sẽ hướng dẫn sử dụng transfer learning trong Pytorch. Trước khi bắt đầu mọi người nên xem lại các kiến thức về transfer learning ở đây. Phần đầu mình sẽ hướng dẫn sử dụng pre-trained model để dự đoán, phần sau mình sẽ hướng dẫn fine-tune model.
Nội dung
Sử dụng pre-trained model để dự đoán
Các bước dùng pretrained model để dự đoán các giá trị mới bao gồm:
- Load ảnh cần dự đoán
- Thực hiện các phép transformation trên ảnh
- Cho ảnh qua model để dự đoán
Load pretrained model
Thư viện torchvision hỗ trợ rất nhiều model
from torchvision import models
dir(models)
# output:
['AlexNet',
'DenseNet',
'GoogLeNet',
'GoogLeNetOutputs',
'Inception3',
'InceptionOutputs',
'MNASNet',
'MobileNetV2',
'MobileNetV3',
'ResNet',
'ShuffleNetV2',
'SqueezeNet',
'VGG',
'alexnet',
'densenet',
'densenet121',
'densenet161',
'densenet169',
'densenet201',
'resnet',
'resnet101',
'resnet152',
'resnet18',
'resnet34',
'resnet50',
'detection',
'googlenet',
'inception',
'inception_v3',
]
Mọi người thấy hầu hết các model phổ biến như resnet, densenet đều được được implement sẵn trong torchvision. Ở phần này mình sẽ dùng pretrained resnet model để dự đoán.
Từ torchvision.models mình có thể load các model, ví dụ mình dùng resnet50. Ở trong có tham số pretrained = True, ý là mình dùng pretrained model resnet50 với weight được train với bộ dữ liệu ImageNet. Còn nếu pretrained = False, thì mình sẽ dùng resnet50 model với weight được khởi tạo ngẫu nhiên.
resnet = models.resnet50(pretrained=True)
Để xem các layer trong resnet model mình có thể in ra
print(resnet)
Image Transformation
Khi đã load xong model, mình cần transform ảnh input cho giống với các thuộc tính của ảnh trong bộ ImageNet mà trước mình dùng để train model resnet50 như:
- Ảnh chuyển về kích thước 224 * 224
- Normalize ảnh theo mean và standard deviation của bộ dữ liệu ImageNet.
- Chuyển về Torch tensor để cho vào model
from torchvision import transforms
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]
)])
Sau đó mình sẽ load ảnh và thực hiện transform ảnh.
from PIL import Image
img = Image.open("cat.jpg")
Tiếp theo mình sẽ thực hiện transform ảnh
img_t = transform(img)
batch_t = torch.unsqueeze(img_t, 0)
Prediction
Tiếp đó mình sẽ cho ảnh qua model để được kết quả dự đoán
resnet.eval()
out = resnet(batch_t)
out.argmax() # 281
Nếu mọi người xem label của ImageNet ở đây, thì 281 tương ứng với tabby, con mèo.
Code phần này mọi người xem ở đây.
Finetuning model
Phần này mình sẽ hướng dẫn fine tune pretrained model resnet50 cho dữ liệu cifar100.
Dataset
Dữ liệu CIFAR-100 bao gồm 60000 hình ảnh màu (50000 ảnh tập train và 10000 ảnh tập test) có kích thước 32 × 32 pixel. Dữ liệu được chia thành 100 lớp.
Data Augmentation
Vì mình dùng resnet pretrained model với bộ dữ liệu ImageNet, nên mình sẽ transform ảnh về kích thước 224 * 224 cho giống với ảnh trong dữ liệu ImageNet.
train_transform = transforms.Compose([
transforms.RandomResizedCrop(size=256),
transforms.RandomRotation(degrees=15),
transforms.RandomHorizontalFlip(),
transforms.CenterCrop(size=224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
])
Tiếp đó mình dùng một số kĩ thuật để augumentation ảnh để tránh overfitting như xoay ảnh, lật ảnh ngẫu nhiên. Và cuối cùng mình normalize ảnh về standard normal distribution theo mean và standard deviation của bộ dữ liệu ImageNet.
Transfer Learning
Đầu tiên mình cần load pretrained model resnet50
resnet50 = models.resnet50(pretrained=True)
Mình sẽ freeze các parameter trong model resnet50, freeze ở đây ý là không update hệ số W và b trong quá trình thực hiện gradient descent.
# Freeze model parameters
for param in resnet50.parameters():
param.requires_grad = False
Tiếp đó mình sẽ thêm các lớp mới của mình vào sau resnet50
fc_inputs = resnet50.fc.in_features
resnet50.fc = nn.Sequential(
nn.Linear(fc_inputs, 256),
nn.ReLU(),
nn.Dropout(0.4),
nn.Linear(256, 100), # Since 100 possible outputs
nn.LogSoftmax(dim=1) # For using NLLLoss()
)
Mình sẽ cho output của resnet50 model lần lượt qua lớp Dense (256), rồi Dense (100), vì bài này mình làm bài toán phân loại 100 lớp. Sau đó dùng softmax activation ở output layer. Ở giữa 2 lớp Dense mình để 1 lớp Dropout để giảm overfitting của mô hình.
Learning rate schedule
Khi train model, xu hướng là càng về các epoch sau thì giảm learning rate sẽ dễ hội tụ hơn. Trong bài này mình có dùng MultiStepLR để giảm learning rate theo epoch.
optimizer = optim.Adam(resnet50.parameters(), lr=0.005)
scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[10,20], gamma=0.1)
Như ở trên thì mình dùng optimizer Adam với learning rate ban đầu là 0.005. Sau đó mình dùng scheduler với 2 milestons là 10, 20 thì cứ đến milestons, learning_rate = gamma * learning_rate
- Từ epoch 1->9: learning rate là 0.005
- Từ epoch 10->19: learning rate là 0.0005
- Từ epoch 20 trở đi: learning rate là 0.00005
Cuối cùng mình đạt 60% accuracy trên tập validation trên tập cifar100. Model vẫn có xu hướng train tốt, nhưng do mỗi epoch mất tầm 5 phút nên mình để việc train tiếp cho bạn đọc.
Code phần này mọi người xem ở đây.