13.1. Tăng cường Ảnh¶
Trong Section 7.1 chúng ta có đề cập đến việc các bộ dữ liệu lớn là điều kiện tiên quyết cho sự thành công của các mạng nơ-ron sâu. Kỹ thuật tăng cường ảnh giúp mở rộng kích thước của tập dữ liệu huấn luyện thông qua việc áp dụng một loạt thay đổi ngẫu nhiên trên các mẫu ảnh, từ đó tạo ra các mẫu huấn luyện tuy tương tự nhưng vẫn có sự khác biệt. Cũng có thể giải thích tác dụng của tăng cường ảnh là việc thay đổi ngẫu nhiên các mẫu dùng cho huấn luyện, làm giảm sự phụ thuộc của mô hình vào một số thuộc tính nhất định. Do đó giúp cải thiện năng lực khái quát hóa của mô hình.
Chẳng hạn, ta có thể cắt tập ảnh theo các cách khác nhau, để các đối tượng ta quan tâm xuất hiện ở các vị trí khác nhau, vì vậy giảm sự phụ thuộc của mô hình vào vị trí xuất hiện của đối tượng. Ta cũng có thể điều chỉnh độ sáng, màu sắc, và các yếu tố khác để giảm độ nhạy màu sắc của mô hình. Có thể khẳng định rằng kỹ thuật tăng cường ảnh đóng góp rất lớn cho sự thành công của mạng AlexNet. Tới đây, chúng ta sẽ thảo luận về kỹ thuật mà được sử dụng rộng rãi trong lĩnh vực thị giác máy tính này.
Trước tiên, thực hiện nhập các gói và mô-đun cần thiết.
%matplotlib inline
from d2l import mxnet as d2l
from mxnet import autograd, gluon, image, init, np, npx
from mxnet.gluon import nn
npx.set_np()
13.1.1. Phương pháp Tăng cường Ảnh Thông dụng¶
Trong phần thử nghiệm này, ta sẽ dùng một ảnh có kích thước \(400\times 500\) làm ví dụ.
d2l.set_figsize()
img = image.imread('../img/cat1.jpg')
d2l.plt.imshow(img.asnumpy());
Hầu hết các phương pháp tăng cường ảnh có một độ ngẫu nhiên nhất định.
Để giúp việc quan sát tính hiệu quả của nó dễ hơn, ta sẽ định nghĩa hàm
bổ trợ apply
. Hàm này thực hiện phương thức tăng cường ảnh aug
nhiều lần từ ảnh đầu vào img
và hiển thị tất cả kết quả.
def apply(img, aug, num_rows=2, num_cols=4, scale=1.5):
Y = [aug(img) for _ in range(num_rows * num_cols)]
d2l.show_images(Y, num_rows, num_cols, scale=scale)
13.1.1.1. Lật và Cắt ảnh¶
Lật hình ảnh sang trái và phải thường không thay đổi thể loại đối tượng.
Đây là một trong những phương pháp tăng cường ảnh được sử dụng sớm nhất
và rộng rãi nhất. Tiếp theo, chúng ta sử dụng mô-đun transforms
để
tạo thực thể RandomFlipLeftRight
, ngẫu nhiên lật hình ảnh sang trái
hoặc phải với xác suất 50%.
apply(img, gluon.data.vision.transforms.RandomFlipLeftRight())
Lật lên và xuống không được sử dụng phổ biến như lật trái và phải. Tuy
nhiên, ít nhất là đối với hình ảnh ví dụ này, lật lên xuống không gây
trở ngại cho việc nhận dạng. Tiếp theo, chúng tôi tạo thực thể
RandomFlipTopBottom
để lật hình ảnh lên và xuống với xác suất 50%.
apply(img, gluon.data.vision.transforms.RandomFlipTopBottom())
Trong ví dụ chúng ta sử dụng, con mèo nằm ở giữa ảnh, nhưng không phải tất cả các ảnh mèo khác đều sẽ như vậy. Section 6.5 có đề cập rằng tầng gộp có thể làm giảm độ nhạy của tầng tích chập với vị trí mục tiêu. Ngoài ra, chúng ta có thể làm cho các đối tượng xuất hiện ở các vị trí khác nhau trong ảnh theo tỷ lệ khác nhau bằng cách cắt ngẫu nhiên hình ảnh. Điều này cũng có thể làm giảm độ nhạy của mô hình với vị trí mục tiêu.
Trong đoạn mã sau, chúng tôi cắt ngẫu nhiên một vùng có diện tích từ 10% đến 100% diện tích ban đầu và tỷ lệ giữa chiều rộng và chiều cao của vùng được chọn ngẫu nhiên trong khoảng từ 0.5 đến 2. Sau đó, cả chiều rộng và chiều cao của vùng đều được biến đổi tỷ lệ thành 200 pixel. Trừ khi có quy định khác, giá trị ngẫu nhiên liên tục giữa \(a\) và \(b\) thu được bằng cách lấy mẫu đồng nhất trong khoảng \([a, b]\).
shape_aug = gluon.data.vision.transforms.RandomResizedCrop(
(200, 200), scale=(0.1, 1), ratio=(0.5, 2))
apply(img, shape_aug)
13.1.1.2. Đổi màu¶
Một phương pháp tăng cường khác là thay đổi màu sắc. Chúng ta có thể thay đổi bốn khía cạnh màu sắc của hình ảnh: độ sáng, độ tương phản, độ bão hòa và tông màu. Trong ví dụ dưới đây, chúng tôi thay đổi ngẫu nhiên độ sáng của hình ảnh với giá trị trong khoảng từ 50% (\(1-0.5\)) đến 150% (\(1+0.5\)) độ sáng của ảnh gốc.
apply(img, gluon.data.vision.transforms.RandomBrightness(0.5))
Tương tự vậy, ta có thể ngẫu nhiên thay đổi tông màu của ảnh.
apply(img, gluon.data.vision.transforms.RandomHue(0.5))
Ta cũng có thể tạo một thực thể RandomColorJitter
và thiết lập để
ngẫu nhiên thay đổi brightness
(độ sáng), contrast
(độ tương
phản), saturation
(độ bão hòa), và hue
(tông màu) của ảnh cùng
một lúc.
color_aug = gluon.data.vision.transforms.RandomColorJitter(
brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5)
apply(img, color_aug)
13.1.1.3. Kết hợp nhiều Phương pháp Tăng cường Ảnh¶
Trong thực tế, chúng ta sẽ kết hợp nhiều phương pháp tăng cường ảnh. Ta
có thể kết hợp các phương pháp trên và áp dụng chúng cho từng hình ảnh
bằng cách sử dụng thực thể Compose
.
augs = gluon.data.vision.transforms.Compose([
gluon.data.vision.transforms.RandomFlipLeftRight(), color_aug, shape_aug])
apply(img, augs)
13.1.2. Huấn luyện Mô hình dùng Tăng cường Ảnh¶
Tiếp theo, ta sẽ xem xét làm thế nào để áp dụng tăng cường hình ảnh trong huấn luyện thực tế. Ở đây, ta sử dụng bộ dữ liệu CIFAR-10, thay vì Fashion-MNIST trước đây. Điều này là do vị trí và kích thước của các đối tượng trong bộ dữ liệu Fashion-MNIST đã được chuẩn hóa và sự khác biệt về màu sắc và kích thước của các đối tượng trong bộ dữ liệu CIFAR-10 là đáng kể hơn. 32 hình ảnh huấn luyện đầu tiên trong bộ dữ liệu CIFAR-10 được hiển thị bên dưới.
d2l.show_images(gluon.data.vision.CIFAR10(
train=True)[0:32][0], 4, 8, scale=0.8);
Để có được kết quả cuối cùng trong dự đoán, ta thường chỉ áp dụng tăng
cường ảnh khi huấn luyện nhưng không sử dụng các biến đổi ngẫu nhiên
trong dự đoán. Ở đây, chúng ta chỉ sử dụng phương pháp lật ngẫu nhiên
trái phải đơn giản nhất. Ngoài ra, chúng ta sử dụng một thực thể
ToTensor
để chuyển đổi minibatch hình ảnh thành định dạng theo yêu
cầu của MXNet, tức là, tensor số thực dấu phẩy động 32-bit có kích thước
(kích thước batch, số kênh, chiều cao, chiều rộng) và phạm vi giá trị
trong khoảng từ 0 đến 1.
train_augs = gluon.data.vision.transforms.Compose([
gluon.data.vision.transforms.RandomFlipLeftRight(),
gluon.data.vision.transforms.ToTensor()])
test_augs = gluon.data.vision.transforms.Compose([
gluon.data.vision.transforms.ToTensor()])
Tiếp theo, ta định nghĩa một chức năng phụ trợ để giúp đọc hình ảnh và
áp dụng tăng cường ảnh dễ dàng hơn. Hàm transform_first
được cung
cấp bởi Gluon giúp thực thi tăng cường ảnh cho phần tử đầu tiên của mỗi
mẫu huấn luyện (hình ảnh và nhãn), tức là chỉ áp dụng lên phần ảnh. Để
biết thêm chi tiết về DataLoader
, hãy tham khảo
Section 3.5.
def load_cifar10(is_train, augs, batch_size):
return gluon.data.DataLoader(
gluon.data.vision.CIFAR10(train=is_train).transform_first(augs),
batch_size=batch_size, shuffle=is_train,
num_workers=d2l.get_dataloader_workers())
13.1.2.1. Sử dụng Mô hình Huấn luyện Đa GPU¶
Ta huấn luyện mô hình ResNet-18 như mô tả ở Section 7.6 trên
tập dữ liệu CIFAR-10. Cùng với đó ta áp dụng các phương pháp được mô tả
trong sec_multi_gpu_concise
và sử dụng mô hình huấn luyện đa
GPU.
Tiếp theo, ta định nghĩa hàm huấn luyện để huấn luyện và đánh giá mô hình sử dụng nhiều GPU.
#@save
def train_batch_ch13(net, features, labels, loss, trainer, devices,
split_f=d2l.split_batch):
X_shards, y_shards = split_f(features, labels, devices)
with autograd.record():
pred_shards = [net(X_shard) for X_shard in X_shards]
ls = [loss(pred_shard, y_shard) for pred_shard, y_shard
in zip(pred_shards, y_shards)]
for l in ls:
l.backward()
# The True flag allows parameters with stale gradients, which is useful
# later (e.g., in fine-tuning BERT)
trainer.step(labels.shape[0], ignore_stale_grad=True)
train_loss_sum = sum([float(l.sum()) for l in ls])
train_acc_sum = sum(d2l.accuracy(pred_shard, y_shard)
for pred_shard, y_shard in zip(pred_shards, y_shards))
return train_loss_sum, train_acc_sum
#@save
def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,
devices=d2l.try_all_gpus(), split_f=d2l.split_batch):
num_batches, timer = len(train_iter), d2l.Timer()
animator = d2l.Animator(xlabel='epoch', xlim=[0, num_epochs], ylim=[0, 1],
legend=['train loss', 'train acc', 'test acc'])
for epoch in range(num_epochs):
# Store training_loss, training_accuracy, num_examples, num_features
metric = d2l.Accumulator(4)
for i, (features, labels) in enumerate(train_iter):
timer.start()
l, acc = train_batch_ch13(
net, features, labels, loss, trainer, devices, split_f)
metric.add(l, acc, labels.shape[0], labels.size)
timer.stop()
if (i + 1) % (num_batches // 5) == 0:
animator.add(epoch + i / num_batches,
(metric[0] / metric[2], metric[1] / metric[3],
None))
test_acc = d2l.evaluate_accuracy_gpus(net, test_iter, split_f)
animator.add(epoch + 1, (None, None, test_acc))
print(f'loss {metric[0] / metric[2]:.3f}, train acc '
f'{metric[1] / metric[3]:.3f}, test acc {test_acc:.3f}')
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec on '
f'{str(devices)}')
Giờ ta có thể định nghĩa hàm train_with_data_aug
để áp dụng tăng
cường ảnh vào huấn luyện mô hình. Hàm này tìm tất cả các GPU có sẵn và
sử dụng Adam làm thuật toán tối ưu cho quá trình huấn luyện. Sau đó nó
áp dụng tăng cường ảnh vào tập huấn luyện, và cuối cùng gọi đến hàm
train_ch13
được định nghĩa ở trên để huấn luyện và đánh giá mô hình.
batch_size, devices, net = 256, d2l.try_all_gpus(), d2l.resnet18(10)
net.initialize(init=init.Xavier(), ctx=devices)
def train_with_data_aug(train_augs, test_augs, net, lr=0.001):
train_iter = load_cifar10(True, train_augs, batch_size)
test_iter = load_cifar10(False, test_augs, batch_size)
loss = gluon.loss.SoftmaxCrossEntropyLoss()
trainer = gluon.Trainer(net.collect_params(), 'adam',
{'learning_rate': lr})
train_ch13(net, train_iter, test_iter, loss, trainer, 10, devices)
Giờ ta huấn luyện mô hình áp dụng tăng cường ảnh qua phép lật ngẫu nhiên trái và phải.
train_with_data_aug(train_augs, test_augs, net)
loss 0.165, train acc 0.942, test acc 0.856
2276.0 examples/sec on [gpu(0)]
13.1.3. Tóm tắt¶
- Tăng cường ảnh sản sinh ra những ảnh ngẫu nhiên dựa vào dữ liệu có sẵn trong tập huấn luyện để đối phó với hiện tượng quá khớp.
- Để có thể thu được kết quả tin cậy trong quá trình dự đoán, thường thì ta chỉ áp dụng tăng cường ảnh lên mẫu huấn luyện, không áp dụng các biến đổi tăng cường ảnh ngẫu nhiên trong quá trình dự đoán.
- Mô-đun
transforms
của Gluon có các lớp thực hiện tăng cường ảnh.
13.1.4. Bài tập¶
- Huấn luyện mô hình mà không áp dụng tăng cường ảnh:
train_with_data_aug(no_aug, no_aug)
. So sánh độ chính xác trong huấn luyện và kiểm tra khi áp dụng và không áp dụng tăng cường ảnh. Liệu thí nghiệm so sánh này có thể hỗ trợ cho luận điểm rằng tăng cường ảnh có thể làm giảm hiện tượng quá khớp? Tại sao? - Sử dụng thêm các phương thức tăng cường ảnh khác trên tập dữ liệu CIFAR-10 khi huấn luyện mô hình. Theo dõi kết quả.
- Tham khảo tài liệu của MXNet và cho biết mô-đun
transforms
của Gluon còn cung cấp các phương thức tăng cường ảnh nào khác?
13.1.5. Thảo luận¶
13.1.6. Những người thực hiện¶
Bản dịch trong trang này được thực hiện bởi:
- Đoàn Võ Duy Thanh
- Nguyễn Mai Hoàng Long
- Trần Yến Thy
- Lê Khắc Hồng Phúc
- Nguyễn Văn Cường
- Phạm Hồng Vinh
- Đỗ Trường Giang
- Nguyễn Lê Quang Nhật