一. 圖像增強

的方法

一直以來,圖像識別這一計算機視覺的核心問題都面臨很多挑戰,同一個物體在不同情況下都會得出不同的結論。對於一張圖片,我們看到的是一些物體,而計算機看到的是一些像素點。

如果拍攝照片的照相機位置發生了改變,那麼拍得的圖片對於我們而言,變化很小,但是對於計算機而言,圖片得像素變化是很大得。拍照時得光照條件也是很重要的一個影響因素:光照太弱,照片裏的物體會和背景融為一體,它們的像素點就會很接近,計算機就無法正確識別出物體。除此之外,物體本身的變形也會對計算機識別造成障礙,比如一隻貓是趴著的,計算機能夠識別它,但如果貓換個姿勢,變成躺著的狀態,那麼計算機就無法識別了。最後,物體本身會隱藏在一些遮蔽物中,這樣物體只呈現出局部的信息,計算機也難以識別。

針對這些問題,我們希望可以對原始圖片進行增強,在一定程度上解決部分問題。在PyTorch中已經內置了一些圖像增強的方法,不需要再繁瑣地去實現,只需要簡單的調用。

torchvision.transforms包括所有圖像增強的方法:

  • 第一個函數是 Scale,對圖片的尺寸進行縮小或者放大;
  • 第二個函數是 CenterCrop,對圖像正中心進行給定大小的裁剪;
  • 第三個函數是 RandomCrop,對圖片進行給定大小的隨機裁剪;
  • 第四個函數是 RandomHorizaontalFlip,對圖片進行概率為0.5的隨機水平翻轉;
  • 第五個函數是 RandomSizedCrop,首先對圖片進行隨機尺寸的裁剪,然後再對裁剪的圖片進行一個隨機比例的縮放,最後將圖片變成給定的大小,這在InceptionNet中比較流行;
  • 最後一個是 pad,對圖片進行邊界零填充;

上面介紹了PyTorch內置的一些圖像增強的方法,還有更多的增強方法,可以使用OpenCV或者PIL等第三方圖形庫實現。在網路的訓練的過程中圖形增強是一種常見、默認的做法,對多任務進行圖像增強之後能夠在一定程度上提升任務的準確率。

二. 實現 CIFAR-10 分類

cifar 10數據集有60000張圖片,每張圖片都是 32x32 的三通道的彩色圖,一共是10個類別,每種類別有6000張圖片。下面實現ResNet來處理cifar 10數據集,完成圖像分類。

注意的是下面的代碼只對訓練圖片進行圖像增強,提高其泛化能力,對於測試集,僅對其中心化,不做其他的圖像增強。

import torch
from torch import nn, optim
from torchvision import transforms, datasets
from torch.utils.data import DataLoader
from torchstat import stat
from torch.autograd import Variable

# 讀數據
def get_data():
train_dataset = datasets.CIFAR10(root=./data, train=True, transform=train_transform, download=True)
test_dataset = datasets.CIFAR10(root=./data, train=False, transform=test_transform, download=True)
print(len(train_dataset))
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, drop_last=True)
test_loader = DataLoader(test_dataset, batch_size=1, shuffle=True, drop_last=True)
return train_loader, test_loader

def conv3x3(in_channels, out_channels, stride=1):
return nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)

# Residual Block
class ResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1, downsample=None):
super(ResidualBlock, self).__init__()
self.conv1 = conv3x3(in_channels, out_channels, stride)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(True)
self.conv2 = conv3x3(out_channels, out_channels)
self.bn2 = nn.BatchNorm2d(out_channels)
self.downsample = downsample
def forward(self, x):
residual = x

out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample:
residual = self.downsample(x)
out += residual
out = self.relu(out)
return out

# 構建網路
class ResNet(nn.Module):
def __init__(self, block, layers, num_classes=10):
super(ResNet, self).__init__()
self.in_channels = 16 # 64, 3, 32, 32
self.conv = conv3x3(3, 16) # 64, 16, 32, 32
self.bn = nn.BatchNorm2d(16)
self.relu = nn.ReLU(True)
self.layer1 = self.make_layer(block, 16, layers[0]) # 64, 16, 32, 32
self.layer2 = self.make_layer(block, 32, layers[0], 2) # 64, 32, 16, 16
self.layer3 = self.make_layer(block, 64, layers[1], 2) # 64, 64, 8, 8
self.avg_pool = nn.AvgPool2d(8) # 64, 64, 1, 1
self.fc = nn.Linear(64, num_classes)

def make_layer(self, block, out_channles, blocks, stride=1):
downsample = None
if out_channles != self.in_channels or stride != 1:
downsample = nn.Sequential(conv3x3(self.in_channels, out_channles, stride=stride), nn.BatchNorm2d(out_channles))
layers = []
layers.append(block(self.in_channels, out_channles, stride, downsample))
self.in_channels = out_channles
for i in range(1, blocks):
layers.append(block(out_channles, out_channles))
return nn.Sequential(*layers)

def forward(self, x):
out = self.conv(x)
out = self.bn(out)
out = self.relu(out)
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.avg_pool(out)
out = out.view(out.size(0), -1)
out = self.fc(out)
return out

if __name__ == "__main__":
# 超參數配置
batch_size = 64
learning_rate = 1e-2
num_epoches = 100
# 訓練圖片的預處理方式
train_transform = transforms.Compose([transforms.Scale(40), transforms.RandomHorizontalFlip(), transforms.RandomCrop(32),
transforms.ToTensor(), transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])
test_transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])
# 載入數據集
train_dataset, test_dataset = get_data()
# 構建模型
# model = ResNet(ResidualBlock, [3, 4])
model = torch.load(resnet_model.pth)
stat(model, (3, 32, 32))
if torch.cuda.is_available():
model = model.cuda()
# 定義損失函數和優化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)
schedule_lr = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)
# 開始訓練
for i in range(num_epoches):
j = 0
for img, label in train_dataset:
model.train()
schedule_lr.step()
img = Variable(img)
label = Variable(label)
# forward
out = model(img)
loss = criterion(out, label)
# backward
optimizer.zero_grad()
loss.backward()
optimizer.step()
# print
print("epoch= {},j= {}, loss is {}".format(i, j, loss))
#print(list(model.children())[-1].weight)
j += 1
if j % 100 == 0:
torch.save(model, ./resnet_model.pth)

# test
model.eval()
count = 0
print(len(test_dataset))
for img, label in test_dataset:
img = Variable(img)
out = model(img)
_, predict = torch.max(out, 1)
if predict == label:
count += 1
print(count / len(test_dataset))

推薦閱讀:

相關文章