.. raw:: html .. _sec_image_augmentation: Tăng cường Ảnh ============== .. raw:: html Trong :numref:`sec_alexnet` 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. .. raw:: html Trước tiên, thực hiện nhập các gói và mô-đun cần thiết. .. code:: python %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() .. raw:: html Phương pháp Tăng cường Ảnh Thông dụng ------------------------------------- .. raw:: html Trong phần thử nghiệm này, ta sẽ dùng một ảnh có kích thước :math:`400\times 500` làm ví dụ. .. code:: python d2l.set_figsize() img = image.imread('../img/cat1.jpg') d2l.plt.imshow(img.asnumpy()); .. figure:: output_image-augmentation_vn_3e478f_3_0.svg .. raw:: html 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ả. .. code:: python 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) .. raw:: html Lật và Cắt ảnh ~~~~~~~~~~~~~~ .. raw:: html 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%. .. code:: python apply(img, gluon.data.vision.transforms.RandomFlipLeftRight()) .. figure:: output_image-augmentation_vn_3e478f_7_0.svg .. raw:: html 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%. .. code:: python apply(img, gluon.data.vision.transforms.RandomFlipTopBottom()) .. figure:: output_image-augmentation_vn_3e478f_9_0.svg .. raw:: html 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. :numref:`sec_pooling` 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. .. raw:: html 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 :math:`a` và :math:`b` thu được bằng cách lấy mẫu đồng nhất trong khoảng :math:`[a, b]`. .. code:: python shape_aug = gluon.data.vision.transforms.RandomResizedCrop( (200, 200), scale=(0.1, 1), ratio=(0.5, 2)) apply(img, shape_aug) .. figure:: output_image-augmentation_vn_3e478f_11_0.svg .. raw:: html Đổi màu ~~~~~~~ .. raw:: html 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% (:math:`1-0.5`) đến 150% (:math:`1+0.5`) độ sáng của ảnh gốc. .. code:: python apply(img, gluon.data.vision.transforms.RandomBrightness(0.5)) .. figure:: output_image-augmentation_vn_3e478f_13_0.svg .. raw:: html Tương tự vậy, ta có thể ngẫu nhiên thay đổi tông màu của ảnh. .. code:: python apply(img, gluon.data.vision.transforms.RandomHue(0.5)) .. figure:: output_image-augmentation_vn_3e478f_15_0.svg .. raw:: html 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. .. code:: python color_aug = gluon.data.vision.transforms.RandomColorJitter( brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5) apply(img, color_aug) .. figure:: output_image-augmentation_vn_3e478f_17_0.svg .. raw:: html Kết hợp nhiều Phương pháp Tăng cường Ảnh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. raw:: html 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``. .. code:: python augs = gluon.data.vision.transforms.Compose([ gluon.data.vision.transforms.RandomFlipLeftRight(), color_aug, shape_aug]) apply(img, augs) .. figure:: output_image-augmentation_vn_3e478f_19_0.svg .. raw:: html Huấn luyện Mô hình dùng Tăng cường Ảnh -------------------------------------- .. raw:: html 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. .. code:: python d2l.show_images(gluon.data.vision.CIFAR10( train=True)[0:32][0], 4, 8, scale=0.8); .. figure:: output_image-augmentation_vn_3e478f_21_0.svg .. raw:: html Để 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. .. code:: python 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()]) .. raw:: html 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 :numref:`sec_fashion_mnist`. .. code:: python 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()) .. raw:: html Sử dụng Mô hình Huấn luyện Đa GPU ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. raw:: html Ta huấn luyện mô hình ResNet-18 như mô tả ở :numref:`sec_resnet` 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 :numref:`sec_multi_gpu_concise` và sử dụng mô hình huấn luyện đa GPU. .. raw:: html 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. .. code:: python #@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 .. code:: python #@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)}') .. raw:: html 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. .. code:: python 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) .. raw:: html 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. .. code:: python train_with_data_aug(train_augs, test_augs, net) .. parsed-literal:: :class: output loss 0.165, train acc 0.942, test acc 0.856 2276.0 examples/sec on [gpu(0)] .. figure:: output_image-augmentation_vn_3e478f_32_1.svg Tóm tắt ------- .. raw:: html - 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. Bài tập ------- .. raw:: html 1. 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? 2. 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ả. 3. 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? Thảo luận --------- - `Tiếng Anh - MXNet `__ - `Tiếng Việt `__ 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