pytorch的圖像分類實踐
在學習pytorch的過程中我找到了關於圖像分類的很淺顯的一個教程上一次做的是pytorch的手寫數字圖片識別是灰度圖片,這次是彩色圖片的分類,覺得對於像我這樣的剛剛開始入門pytorch的小白來說很有意義,今天寫篇關於這個圖像分類的博客.
收獲的知識
1.torchvison
在深度學習中數據加載及預處理是非常復雜繁瑣的,但PyTorch提供了一些可極大簡化和加快數據處理流程的工具。同時,對於常用的數據集,PyTorch也提供了封裝好的接口供用戶快速調用,這些數據集主要保存在torchvison中,torchvison實現了常用的圖像數據加載功能,例如Imagenet、CIFAR10、MNIST等,以及常用的數據轉換操作,這極大地方便了數據加載,並且代碼具有可重用性。
2.transform = transforms.Compose([
transforms.ToTensor(), # 轉為Tensor
transforms.Normalize((0.5, 0.5, 0.5),(0.5, 0.5, 0.5)), # 歸一化
])
根據我手上的中文文檔,Compose方法大概是把多個transform組合起來使用
關於這個ToTensor()我自己跑了一下例子,這個方法就是根據圖像的像素,色彩情況把一張圖片轉化為pytorch中的一個tensor便於數據化處理圖像。
歸一化我的博客以前寫過就是一種處理數據的手段既可以讓數據保留特征又可以控制數據在一個很小的范圍,便於計算。
import torch
import torchvision
import numpy as np
data=np.random.randint(0,255,size=300)
img=data.reshape(10,10,3)
print(img.shape)
img_tensor=torchvision.transforms.ToTensor()(img)
print(img_tensor)
截圖:
3.pytorch中常用數據集的加載和使用
我是用的Jupter做的這個實踐,首先先把數據集下載到本地,最好直接下載到jupter的路徑里面,然后運行就可以發現開始下載了。
Dataset對象:我們事先會把下載好的數據集命名為 dataset和trainset
模型的訓練
使用自己的網絡
起初我看那個教程上的網絡使用的LeNet,我就把上次做手寫數字的模型拿過來改了一下,多加了兩個全連接層
import torch as t
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as Data
import torchvision as tv
import numpy
class CNN(nn.Module):#繼承了nn.Model
def __init__(self):
#繼承了 CNN 父類的的屬性
super(CNN, self).__init__()#用父類的初始化方式來初始化所繼承的來自父類的屬性
#按照網絡的前后順序定義1號網絡
self.conv1 = nn.Sequential( # input shape (3, 32, 32)
nn.Conv2d(#這里的nn.Conv2d使用一個2維度卷積
in_channels=3, #in_channels: 因CIFAR-10是3通道彩圖這里用3
out_channels=16, # out_channels:卷積產生的通道數,有多少個out_channels,就需要多少個一維卷積(也就是卷積核的數量)
kernel_size=5, #kernel_size:卷積核的尺寸;卷積核的第二個維度由in_channels決定,所以實際上卷積核的大小為kernel_size * in_channels
stride=1, #步長,每次移動的單位格子
padding=2, #padding:對輸入的每一條邊,補充0的層數
), # output shape (16, 32, 32)
nn.ReLU(), #激活函數ReLU # activation
#在2X2的池化層里選出最大值
nn.MaxPool2d(kernel_size=2), # choose max value in 2x2 area, output shape (16, 16, 16)
)
#按照網絡的前后順序定義2號網絡,
self.conv2 = nn.Sequential( # input shape (16, 16, 16)
#使用一個二維卷積
nn.Conv2d(16, 32, 5, 1, 2), # output shape (32, 16, 16)
nn.ReLU(), # activation
nn.MaxPool2d(2), # output shape (32, 8, 8)
)
#全連接層
self.fullconnect1 = nn.Linear(32 * 8 *8 , 120) #因為在pytorch中做全連接的輸入輸出都是二維張量,不同於卷積層要求輸入4維張量
self.fullconnect2 = nn.Linear(120,84)
self.fullconnect3 = nn.Linear(84,10)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1) # flatten the output of conv2 to (batch_size, 32 * 8 * 8)
x = self.fullconnect1(x)
x=self.fullconnect2(x)
x = self.fullconnect3(x)
return x
net=CNN()
print(net)
訓練效果是很糟糕的:
10%就是在猜,那么這個模型和猜的差不多
繼續使用自己的網絡把全連接層改成一層
效果依舊很糟糕
使用教程的網絡模型
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(x.size()[0], -1)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
print(net)
訓練的結果好很多:
代碼部分:
graph LR 使用torchvision加載並預處理CIFAR-10數據集 --> C(定義網絡) C --> D(定義損失函數和優化器) D-->F(訓練網絡並更新網絡參數) F-->測試網絡
import torch as t
import torchvision as tv
import torchvision.transforms as transforms
from torchvision.transforms import ToPILImage
show = ToPILImage() # 可以把Tensor轉成Image,方便可視化
# 第一次運行程序torchvision會自動下載CIFAR-10數據集,
# 大約100M,需花費一定的時間,
# 如果已經下載有CIFAR-10,可通過root參數指定
# 定義對數據的預處理
transform = transforms.Compose([
transforms.ToTensor(), # 轉為Tensor
transforms.Normalize((0.5, 0.5, 0.5),(0.5, 0.5, 0.5)), # 歸一化
])
# 訓練集
trainset = tv.datasets.CIFAR10(
root='./CIFAR10/',
train=True,
download=False,
transform=transform)
trainloader = t.utils.data.DataLoader(
trainset,
batch_size=4,
shuffle=True,
num_workers=2)
# 測試集
testset = tv.datasets.CIFAR10(
'./CIFAR10/',
train=False,
download=True,
transform=transform)
#數據集按照 bitch_size(批處理大小設置),shuffle(是否進行洗牌)把數據封裝成一個Batch Size大小的Tensor,用於后面的訓練。
testloader = t.utils.data.DataLoader(
testset,
batch_size=4,
shuffle=False,
num_workers=2)#是否進行多進程加載數據
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
import torch.nn as nn
import torch.nn.functional as F
#定義神經網絡
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5) #輸入通道3,彩色圖片的輸入通道要改成三,輸出通道為6,卷積核大小(5*5)
self.conv2 = nn.Conv2d(6, 16, 5) #第二個卷積層輸入通道是上一個卷積的輸出通道6,輸出通道為16,卷積核大小(5*5)
self.fc1 = nn.Linear(16*5*5, 120) #全連接層1 fullconnect
self.fc2 = nn.Linear(120, 84)#全連接層2
self.fc3 = nn.Linear(84, 10)#全連接層3
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(x.size()[0], -1) #把數據對整齊,防止輸入輸出不匹配
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
print(net)
#定義損失函數和優化器
from torch import optim
criterion = nn.CrossEntropyLoss() # 交叉熵損失函數
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
#訓練網絡更新網絡參數
t.set_num_threads(8)
for epoch in range(10):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# 輸入數據
inputs, labels = data
inputs, labels = Variable(inputs), Variable(labels)#轉換成Variable 可以自動微分求導
# 梯度清零
optimizer.zero_grad()
# forward + backward
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
# 更新參數
optimizer.step()
# 打印log信息
running_loss += loss.item()
if i % 2000 == 1999: # 每2000個batch打印一下訓練狀態
print('[第%d輪, 第%5d張圖片] 損失函數:loss: %.3f' \
% (epoch+1, i+1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
#測試網絡
dataiter = iter(testloader)
images, labels = dataiter.next() # 一個batch返回4張圖片
print('實際的label: ', ' '.join(\
'%08s'%classes[labels[j]] for j in range(4)))
#打印標簽對應的正確照片,前提是得復原成歸一化前的數據
show(tv.utils.make_grid(images / 2 - 0.5)).resize((400,100))
# 計算圖片在每個類別上的分數
outputs = net(Variable(images))
# 得分最高的那個類
_, predicted = t.max(outputs.data, 1)
print('預測結果: ', ' '.join('%5s'\
% classes[predicted[j]] for j in range(4)))
correct = 0 # 預測正確的圖片數
total = 0 # 總共的圖片數
for data in testloader:
images, labels = data
outputs = net(Variable(images))
_, predicted = t.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
print('10000張測試集中的准確率為: %d %%' % (100 * correct / total))
運行截圖:
網絡層數太少,訓練10輪效果和五輪差不多可能后面的訓練造成了過擬合。大家也可以動手做一下上面的小實驗。