本章節内容将在CIFAR10資料集上訓練一個簡單的CNN網絡:
- 基于CIFAR-10資料集,訓練一個簡單CNN網絡。
- 儲存訓練好的模型,測試。
- 使用GPU訓練。
CIFAR資料集
CIFAR資料集可分為CIFAR10, CIFAR100。 CIFAR-10是指包含10個種類, CIFAR-100包含100個種類。
CIFAR-10
特點:32x32 彩色圖像;10個類别;總共60000張圖像;50000張訓練樣本 + 10000張測試樣本;每個類别有6000張圖像, 10 x 6000 = 60000;
10個類别:airplane,automobile,bird,cat,deer,dog,frog,horse,ship,truck;
Tips:不需要手動下載下傳, 使用pytorch中的Dataset API自動下載下傳即可
實驗過程
準備資料集
這一步驟在pytorch中非常友善,pytorch已經為我們準備好了常見的資料集合,隻需要導入即可。
資料集在
torchvision.dataset
包裡面:
import torch
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
torchvision.dataset.CFAIR10
是一個類, 通過執行個體化該類的一個對象,就可以操作資料集。
參數:
root
-----資料集下載下傳後儲存的路徑
train
-----訓練or測試
download
----是否需要自動下載下傳
transform
----對圖像進行變換, 一般需要對原始圖像進行
ToTensor(), Normalize()
變換
之後,使用
DataLoader
類對資料集進行包裝,目的是為了友善讀取和使用,比如可以min_batch讀取, 采用多線程。
# --------------------準備資料集------------------
# Dataset, DataLoader
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), std =(0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
testset = torchvision.datasets.CIFAR10(root='./data',train=False,
transform=transform, download=True)
trainloader = DataLoader(dataset=trainset, batch_size=4, shuffle=True, num_workers=4)
testloader = DataLoader(dataset=testset, batch_size=4, shuffle=True, num_workers=4)
#
dataiter = iter(trainloader)
images, labels = dataiter.next()
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
定義CNN網絡
簡單起見,采用LeNet網絡,将第一個卷積層的輸入通道改為3,因為CIFAR-10是彩色3通道圖像。
#定義一個簡單的網絡
# LeNet -5
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
self.fc1 = nn.Linear(in_features=16 * 5 * 5,out_features=120)
self.fc2 = nn.Linear(in_features=120, out_features=84)
self.fc3 = nn.Linear(in_features=84, out_features=10)
def forward(self, x):
x = self.pool1(F.relu(self.conv1(x)))
x = self.pool1(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5) # reshape tensor
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
設定網絡的優化、疊代方法,訓練網絡
CNN網絡訓練本質上就是對一個目标函數(損失函數)求最小的問題,在數學中,對于一般的凸函數,優化方法有梯度下降法、牛頓法等。(除此之外還有啟發式搜尋,比如遺傳算法等)。 對于神經網絡的訓練,常用的優化方法為随機梯度下降法SGD。
-
定義損失函數,優化方法
采用交叉熵損失函數
采用SGD随機梯度下降法進行優化(帶動量項)
# 定義損失函數,優化方法
# 采用Cross-Entropy loss, SGD with moment
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
- 進行疊代優化,訓練
Iter------一次疊代,是指一個min_batch的一次forward+backward
Epoch------疊代完所有的訓練資料(1次),稱為一個epoch
這裡總共跑20個epoch。
# 訓練網絡
# 疊代epoch
for epoch in range(20):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the input
inputs, labels = data
# zeros the paramster gradients
optimizer.zero_grad() #
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels) # 計算loss
loss.backward() # loss 求導
optimizer.step() # 更新參數
# print statistics
running_loss += loss.item() # tensor.item() 擷取tensor的數值
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000)) # 每2000次疊代,輸出loss的平均值
running_loss = 0.0
print('Finished Training')
儲存模型
# --------儲存模型-----------
torch.save(net, './model/model_cfair10_2.pth') # 儲存整個模型,體積比較大
# torch.save(net.state_dict(), './model/model_cfair10.pth')
測試模型
import torch
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
CIFAR-10總共包含10個類别:
載入一張圖像,RBG,要屬于上述類别的某一類,不然識别不出來
# load a image
image = Image.open('/xxxx/image/dog.jpg')
對圖像進行相同的變換:
transform = transforms.Compose(
[transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Normalize(
mean=(0.5, 0.5, 0.5),
std=(0.5, 0.5, 0.5)
)])
image_transformed = transform(image)
print(image_transformed.size())
需要注意的地方
CNN網絡的輸入為4D Tensor (NxCxHxW), 轉換之後的圖像需要變換為4D
torsor1.unsqueeze(0)
即可增加一個次元,這樣輸入的tensor為: 1x3x32x32
#
transform = transforms.Compose(
[transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Normalize(
mean=(0.5, 0.5, 0.5),
std=(0.5, 0.5, 0.5)
)])
image_transformed = transform(image)
print(image_transformed.size())
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
self.fc1 = nn.Linear(in_features=16 * 5 * 5,out_features=120)
self.fc2 = nn.Linear(in_features=120, out_features=84)
self.fc3 = nn.Linear(in_features=84, out_features=10)
def forward(self, x):
x = self.pool1(F.relu(self.conv1(x)))
x = self.pool1(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5) # reshape tensor
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = torch.load('./model/model_cfair10.pth')
# print(net)
image_transformed = image_transformed.unsqueeze(0)
output = net(image_transformed)
predict_value, predict_idx = torch.max(output, 1) # 求指定次元的最大值,傳回最大值以及索引
plt.figure()
plt.imshow(np.array(image))
plt.title(CFAIR10_names[predict_idx])
plt.axis('off')
plt.show()
————————————————————————————————————————————————————————————————————————————————————————————————————————
GPU訓練、模型學習率調整
問題:
- 采用CPU模型訓練了20個Epoch之後,loss下降到了0.6左右,後來在之前訓練的基礎上疊代了20個Epoch,發現loss處于0.5~0.6之間。
- CPU上訓練确實很慢,跑20個Epoch基本上用了1h多(具體時間不記得),比較漫長。
使用GPU訓練模型
- 計算機配置 GPU: 1080
首先,需要安裝GPU版本的pytorch, 具體安裝步驟pytorch官網有。使用GPU訓練需要對代碼做一些小調整。
**step1:**在代碼中,首先使用pytorch中的函數判斷是否支援GPU
is_support = torch.cunda.is_available()
if is_support:
device = torch.device('cuda:0')
# device = torch.device('cuda:1')
else:
device = torch.device('cpu')
step2: 将CPU上的計算轉移到GPU上
net = Net()
net.to(device) # GPU模式需要添加
# 訓練網絡
# 疊代epoch
for epoch in range(20):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the input
inputs, labels = data
inputs = inputs.to(device) # GPU計算
labels = labels.to(device) # GPU計算
# zeros the paramster gradients
optimizer.zero_grad() #
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels) # 計算loss
loss.backward() # loss 求導
optimizer.step() # 更新參數
# print statistics
running_loss += loss.item() # tensor.item() 擷取tensor的數值
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000)) # 每2000次疊代,輸出loss的平均值
running_loss = 0.0
print('Finished Training')
run, 會發現疊代速度飛起,10min左右就可以完成20個Epoch疊代,速度非常快。
學習率調整
- 随機梯度下降法SGD重要的一個參數是:學習率 leaning_rate
上面的代碼采用的是固定學習率lr=0.001,。 剛開始疊代時候,學習率可以大一些,這樣收斂速度快,随着疊代次數增加,學習率應該減小防止loss震蕩。
簡單起見,本人将學習率調整為lr=0.0001,然後在之前模型的基礎上疊代20個Epoch。明顯發現Loss變為0.3, 0.2, 0.1。
雖然采用GPU訓練, lr減小為0.0001, loss也減少了(訓練集loss)。 在測試中,1個horse識别為deer, bird識别為cat。 因為,要訓練到一個适合模型,還需要其他政策,包括采用其他網絡模型。
在整個測試集上評價模型性能
- 計算Acc
import torch
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
CFAIR10_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'forg', 'horse', 'ship', 'truck']
# --------------測試資料集------------------------------
transform = transforms.Compose(
[transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Normalize(
mean=(0.5, 0.5, 0.5),
std=(0.5, 0.5, 0.5)
)])
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=4)
# -----------------網咯模型-------------------------------
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
self.fc1 = nn.Linear(in_features=16 * 5 * 5,out_features=120)
self.fc2 = nn.Linear(in_features=120, out_features=84)
self.fc3 = nn.Linear(in_features=84, out_features=10)
def forward(self, x):
x = self.pool1(F.relu(self.conv1(x)))
x = self.pool1(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5) # reshape tensor
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = torch.load('./model/model_cfair10_20.pth',map_location='cpu')
# ------------在整個測試集上測試-------------------------------------------
correct = 0
total = 0
count = 0
with torch.no_grad():
for sample_batch in testloader:
images = sample_batch[0]
labels = sample_batch[1]
# forward
out = net(images)
#
_, pred = torch.max(out, 1)
correct += (pred == labels).sum().item()
total += labels.size(0)
print('batch:{}'.format(count + 1))
count += 1
#
# Acc
accuracy = float(correct) / total
print('Acc = {:.5f}'.format(accuracy))
連結:https://www.jianshu.com/p/e704a6f6e8d3
來源:簡書