本次將一個使用Pytorch的一個實戰項目,記錄流程:自定義數據集->數據加載->搭建神經網絡->遷移學習->保存模型->加載模型->測試模型
自定義數據集
參考我的上一篇博客:自定義數據集處理
數據加載
默認小伙伴有對深度學習框架有一定的了解,這里就不做過多的說明了。
好吧,還是簡單的說一下吧:
我們在做好了自定義數據集之后,其實數據的加載和MNSIT 、CIFAR-10 、CIFAR-100等數據集的都是相似的,過程如下所示:
導入必要的包
import torch
from torch import optim, nn
import visdom
from torch.utils.data import DataLoader
1
2
3
4
加載數據
可以發現和MNIST 、CIFAR的加載基本上是一樣的
train_db = Pokemon('pokeman', 224, mode='train')
val_db = Pokemon('pokeman', 224, mode='val')
test_db = Pokemon('pokeman', 224, mode='test')
train_loader = DataLoader(train_db, batch_size=batchsz, shuffle=True,
num_workers=4)
val_loader = DataLoader(val_db, batch_size=batchsz, num_workers=2)
test_loader = DataLoader(test_db, batch_size=batchsz, num_workers=2)
1
2
3
4
5
6
7
搭建神經網絡
ResNet-18網絡結構:
在這里插入圖片描述
ResNet全名Residual Network殘差網絡。Kaiming He 的《Deep Residual Learning for Image Recognition》獲得了CVPR最佳論文。他提出的深度殘差網絡在2015年可以說是洗刷了圖像方面的各大比賽,以絕對優勢取得了多個比賽的冠軍。而且它在保證網絡精度的前提下,將網絡的深度達到了152層,后來又進一步加到1000的深度。論文的開篇先是說明了深度網絡的好處:特征等級隨着網絡的加深而變高,網絡的表達能力也會大大提高。因此論文中提出了一個問題:是否可以通過疊加網絡層數來獲得一個更好的網絡呢?作者經過實驗發現,單純的把網絡疊起來的深層網絡的效果反而不如合適層數的較淺的網絡效果。因此何愷明等人在普通平原網絡的基礎上增加了一個shortcut, 構成一個residual block。此時擬合目標就變為F(x),F(x)就是殘差:
在這里插入圖片描述
訓練模型
def evalute(model, loader):
model.eval()
correct = 0
total = len(loader.dataset)
for x, y in loader:
x, y = x.to(device), y.to(device)
with torch.no_grad():
logits = model(x)
pred = logits.argmax(dim=1)
correct += torch.eq(pred, y).sum().float().item()
return correct / total
def main():
model = ResNet18(5).to(device)
optimizer = optim.Adam(model.parameters(), lr=lr)
criteon = nn.CrossEntropyLoss()
best_acc, best_epoch = 0, 0
global_step = 0
viz.line([0], [-1], win='loss', opts=dict(title='loss'))
viz.line([0], [-1], win='val_acc', opts=dict(title='val_acc'))
for epoch in range(epochs):
for step, (x, y) in enumerate(train_loader):
x, y = x.to(device), y.to(device)
model.train()
logits = model(x)
loss = criteon(logits, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
viz.line([loss.item()], [global_step], win='loss', update='append')
global_step += 1
if epoch % 1 == 0:
val_acc = evalute(model, val_loader)
if val_acc > best_acc:
best_epoch = epoch
best_acc = val_acc
viz.line([val_acc], [global_step], win='val_acc', update='append')
print('best acc:', best_acc, 'best epoch:', best_epoch)
model.load_state_dict(torch.load('best.mdl'))
print('loaded from ckpt!')
test_acc = evalute(model, test_loader)
遷移學習
提升模型的准確率:
# model = ResNet18(5).to(device)
trained_model=resnet18(pretrained=True) # 此時是一個非常好的model
model = nn.Sequential(*list(trained_model.children())[:-1], # 此時使用的是前17層的網絡 0-17 *:隨機打散
Flatten(),
nn.Linear(512,5)
).to(device)
# x=torch.randn(2,3,224,224)
# print(model(x).shape)
optimizer = optim.Adam(model.parameters(), lr=lr)
criteon = nn.CrossEntropyLoss()
1
2
3
4
5
6
7
8
9
10
11
保存、加載模型
pytorch保存模型的方式有兩種:
第一種:將整個網絡都都保存下來
第二種:僅保存和加載模型參數(推薦使用這樣的方法)
# 保存和加載整個模型
torch.save(model_object, 'model.pkl')
model = torch.load('model.pkl')
1
2
3
# 僅保存和加載模型參數(推薦使用)
torch.save(model_object.state_dict(), 'params.pkl')
model_object.load_state_dict(torch.load('params.pkl'))
1
2
3
可以看到這是我保存的模型:
其中best.mdl是第二中方法保存的
model.pkl則是第一種方法保存的
在這里插入圖片描述
測試模型
這里是訓練時的情況
在這里插入圖片描述
看這個數據准確率還是不錯的,但是還是需要實際的測試這個模型,看它到底學到東西了沒有,接下來簡單的測試一下:
import torch
from PIL import Image
from torchvision import transforms
device = torch.device('cuda')
transform=transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485,0.456,0.406],
std=[0.229,0.224,0.225])
])
def prediect(img_path):
net=torch.load('model.pkl')
net=net.to(device)
torch.no_grad()
img=Image.open(img_path)
img=transform(img).unsqueeze(0)
img_ = img.to(device)
outputs = net(img_)
_, predicted = torch.max(outputs, 1)
# print(predicted)
print('this picture maybe :',classes[predicted[0]])
if __name__ == '__main__':
prediect('./test/name.jpg')
實際的測試結果:
在這里插入圖片描述
在這里插入圖片描述
效果還是可以的,完整的代碼:
https://github.com/huzixuan1/Loader_DateSet
數據集下載鏈接:
https://pan.baidu.com/s/12-NQiF4fXEOKrXXdbdDPCg