圖像增廣
sec_alexnet
提到過大型數據集是成功應用深度神經網絡的先決條件。
圖像增廣在對訓練圖像進行一系列的隨機變化之后,生成相似但不同的訓練樣本,從而擴大了訓練集的規模。
此外,應用圖像增廣的原因是,隨機改變訓練樣本可以減少模型對某些屬性的依賴,從而提高模型的泛化能力。
例如,我們可以以不同的方式裁剪圖像,使感興趣的對象出現在不同的位置,減少模型對于對象出現位置的依賴。
我們還可以調整亮度、顏色等因素來降低模型對顏色的敏感度。
可以說,圖像增廣技術對于AlexNet的成功是必不可少的。本節將討論這項廣泛應用于計算機視覺的技術。
%matplotlib inline
import torch
import torchvision
from torch import nn
from d2l import torch as d2l
常用的圖像增廣方法
在對常用圖像增廣方法的探索時,我們將使用下面這個尺寸為 400 × 500 400\times 500 400×500的圖像作為示例。
d2l.set_figsize()
img = d2l.Image.open('../img/cat1.jpg')
d2l.plt.imshow(img);
?
?
大多數圖像增廣方法都具有一定的隨機性。為了便于觀察圖像增廣的效果,我們下面定義輔助函數apply
。
此函數在輸入圖像img
上多次運行圖像增廣方法aug
并顯示所有結果。
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)
翻轉和裁剪
[左右翻轉圖像]通常不會改變對象的類別。這是最早且最廣泛使用的圖像增廣方法之一。
接下來,我們使用transforms
模塊來創建RandomFlipLeftRight
實例,這樣就各有50%的幾率使圖像向左或向右翻轉。
apply(img, torchvision.transforms.RandomHorizontalFlip())
?
?
[上下翻轉圖像]不如左右圖像翻轉那樣常用。但是,至少對于這個示例圖像,上下翻轉不會妨礙識別。接下來,我們創建一個RandomFlipTopBottom
實例,使圖像各有50%的幾率向上或向下翻轉。
apply(img, torchvision.transforms.RandomVerticalFlip())
?
?
在我們使用的示例圖像中,貓位于圖像的中間,但并非所有圖像都是這樣。
在 sec_pooling
中,我們解釋了匯聚層可以降低卷積層對目標位置的敏感性。
另外,我們可以通過對圖像進行隨機裁剪,使物體以不同的比例出現在圖像的不同位置。
這也可以降低模型對目標位置的敏感性。
下面的代碼將[隨機裁剪]一個面積為原始面積10%到100%的區域,該區域的寬高比從0.5~2之間隨機取值。
然后,區域的寬度和高度都被縮放到200像素。
在本節中(除非另有說明), a a a和 b b b之間的隨機數指的是在區間 [ a , b ] [a, b] [a,b]中通過均勻采樣獲得的連續值。
shape_aug = torchvision.transforms.RandomResizedCrop((200, 200), scale=(0.1, 1), ratio=(0.5, 2))
apply(img, shape_aug)
?
?
改變顏色
另一種增廣方法是改變顏色。
我們可以改變圖像顏色的四個方面:亮度、對比度、飽和度和色調。
在下面的示例中,我們[隨機更改圖像的亮度],隨機值為原始圖像的50%( 1 ? 0.5 1-0.5 1?0.5)到150%( 1 + 0.5 1+0.5 1+0.5)之間。
apply(img, torchvision.transforms.ColorJitter(brightness=0.5, contrast=0, saturation=0, hue=0))
?
?
同樣,我們可以[隨機更改圖像的色調]。
apply(img, torchvision.transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0.5))
?
?
我們還可以創建一個RandomColorJitter
實例,并設置如何同時[隨機更改圖像的亮度(brightness
)、對比度(contrast
)、飽和度(saturation
)和色調(hue
)]。
color_aug = torchvision.transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5)
apply(img, color_aug)
?
?
[結合多種圖像增廣方法]
在實踐中,我們將結合多種圖像增廣方法。比如,我們可以通過使用一個Compose
實例來綜合上面定義的不同的圖像增廣方法,并將它們應用到每個圖像。
augs = torchvision.transforms.Compose([torchvision.transforms.RandomHorizontalFlip(), color_aug, shape_aug])
apply(img, augs)
?
?
[使用圖像增廣進行訓練]
讓我們使用圖像增廣來訓練模型。
這里,我們使用CIFAR-10數據集,而不是我們之前使用的Fashion-MNIST數據集。
這是因為Fashion-MNIST數據集中對象的位置和大小已被規范化,而CIFAR-10數據集中對象的顏色和大小差異更明顯。
CIFAR-10數據集中的前32個訓練圖像如下所示。
all_images = torchvision.datasets.CIFAR10(train=True, root="../data",download=True)
d2l.show_images([all_images[i][0] for i in range(32)], 4, 8, scale=0.8);
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ../data/cifar-10-python.tar.gz0%| | 0/170498071 [00:00<?, ?it/s]Extracting ../data/cifar-10-python.tar.gz to ../data
為了在預測過程中得到確切的結果,我們通常對訓練樣本只進行圖像增廣,且在預測過程中不使用隨機操作的圖像增廣。
在這里,我們[只使用最簡單的隨機左右翻轉]。
此外,我們使用ToTensor
實例將一批圖像轉換為深度學習框架所要求的格式,即形狀為(批量大小,通道數,高度,寬度)的32位浮點數,取值范圍為0~1。
train_augs = torchvision.transforms.Compose([torchvision.transforms.RandomHorizontalFlip(),torchvision.transforms.ToTensor()])test_augs = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
接下來,我們[定義一個輔助函數,以便于讀取圖像和應用圖像增廣]。PyTorch數據集提供的transform
參數應用圖像增廣來轉化圖像。有關DataLoader
的詳細介紹,請參閱 :numref:sec_fashion_mnist
。
def load_cifar10(is_train, augs, batch_size):dataset = torchvision.datasets.CIFAR10(root="../data", train=is_train,transform=augs, download=True)dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,shuffle=is_train, num_workers=d2l.get_dataloader_workers())return dataloader
多GPU訓練
我們在CIFAR-10數據集上訓練 :numref:sec_resnet
中的ResNet-18模型。
回想一下 :numref:sec_multi_gpu_concise
中對多GPU訓練的介紹。
接下來,我們[定義一個函數,使用多GPU對模型進行訓練和評估]。
#@save
def train_batch_ch13(net, X, y, loss, trainer, devices):"""用多GPU進行小批量訓練"""if isinstance(X, list):# 微調BERT中所需X = [x.to(devices[0]) for x in X]else:X = X.to(devices[0])y = y.to(devices[0])net.train()trainer.zero_grad()pred = net(X)l = loss(pred, y)l.sum().backward()trainer.step()train_loss_sum = l.sum()train_acc_sum = d2l.accuracy(pred, y)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()):"""用多GPU進行模型訓練"""timer, num_batches = d2l.Timer(), len(train_iter)animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0, 1],legend=['train loss', 'train acc', 'test acc'])net = nn.DataParallel(net, device_ids=devices).to(devices[0])for epoch in range(num_epochs):# 4個維度:儲存訓練損失,訓練準確度,實例數,特點數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)metric.add(l, acc, labels.shape[0], labels.numel())timer.stop()if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:animator.add(epoch + (i + 1) / num_batches,(metric[0] / metric[2], metric[1] / metric[3],None))test_acc = d2l.evaluate_accuracy_gpu(net, test_iter)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)}')
現在,我們可以[定義train_with_data_aug
函數,使用圖像增廣來訓練模型]。該函數獲取所有的GPU,并使用Adam作為訓練的優化算法,將圖像增廣應用于訓練集,最后調用剛剛定義的用于訓練和評估模型的train_ch13
函數。
batch_size, devices, net = 256, d2l.try_all_gpus(), d2l.resnet18(10, 3)def init_weights(m):if type(m) in [nn.Linear, nn.Conv2d]:nn.init.xavier_uniform_(m.weight)net.apply(init_weights)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 = nn.CrossEntropyLoss(reduction="none")trainer = torch.optim.Adam(net.parameters(), lr=lr)train_ch13(net, train_iter, test_iter, loss, trainer, 10, devices)
讓我們使用基于隨機左右翻轉的圖像增廣來[訓練模型]。
train_with_data_aug(train_augs, test_augs, net)
loss 0.173, train acc 0.941, test acc 0.854
4183.9 examples/sec on [device(type='cuda', index=0), device(type='cuda', index=1)]
小結
- 圖像增廣基于現有的訓練數據生成隨機圖像,來提高模型的泛化能力。
- 為了在預測過程中得到確切的結果,我們通常對訓練樣本只進行圖像增廣,而在預測過程中不使用帶隨機操作的圖像增廣。
- 深度學習框架提供了許多不同的圖像增廣方法,這些方法可以被同時應用。