人妖在线一区,国产日韩欧美一区二区综合在线,国产啪精品视频网站免费,欧美内射深插日本少妇

新聞動(dòng)態(tài)

CNN的Pytorch實(shí)現(xiàn)(LeNet)

發(fā)布日期:2022-01-29 12:25 | 文章來(lái)源:源碼中國(guó)

CNN的Pytorch實(shí)現(xiàn)(LeNet)

  上次寫(xiě)了一篇CNN的詳解,可是累壞了老僧我。寫(xiě)完后拿給朋友看,朋友說(shuō)你這Pytorch的實(shí)現(xiàn)方式對(duì)于新人來(lái)講會(huì)很不友好,然后反問(wèn)我說(shuō)里面所有的細(xì)節(jié)你都明白了嗎。我想想,的確如此。那個(gè)源碼是我當(dāng)時(shí)《動(dòng)手學(xué)pytorch》的時(shí)候整理的,里面有很多包裝過(guò)的函數(shù),對(duì)于新入門的人來(lái)講,的確是個(gè)大問(wèn)題。于是,痛定思痛的我決定重新寫(xiě)Pytorch實(shí)現(xiàn)這一部分,理論部分我就不多講了,咱們直接分析代碼,此代碼是來(lái)自Pytorch官方給出的LeNet Model。你可以使用Jupyter Notebook一行一行的學(xué)習(xí),也可以使用Pycharm進(jìn)行斷點(diǎn)訓(xùn)練和Debug來(lái)學(xué)習(xí)。

沒(méi)有看過(guò)理論部分的同學(xué)可以看我上篇文章:一文帶你了解CNN(卷積神經(jīng)網(wǎng)絡(luò))。

  在整個(gè)講解的過(guò)程中,其中的一些比較重要的代碼我會(huì)引入一些例子來(lái)進(jìn)行解釋它的功能,如果你想先直接跑通代碼,可以直接跳到代碼匯總部分,Here we go~

1. 任務(wù)目標(biāo)

  這是一個(gè)對(duì)于彩色圖的10分類的問(wèn)題,具體種類有:'plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck',訓(xùn)練一個(gè)能夠?qū)ζ溥M(jìn)行分類的分類器。

2. 庫(kù)的導(dǎo)入

這一部分咱們就不說(shuō)太多了吧,直接上code:

import torch # 張量的有關(guān)運(yùn)算,如創(chuàng)建、索引、連接、轉(zhuǎn)置....和numpy的操作很像
import torch.nn as nn # 八廓搭建神經(jīng)網(wǎng)絡(luò)層的模塊、loss等等
import torch.nn.functional as F # 常用的激活函數(shù)都在這里面
import torchvision # 專門處理圖像的庫(kù)
import torch.optim as optim # 各種參數(shù)優(yōu)化方法,SGD、Adam...
import torchvision.transforms as transforms # 提供了一般的圖像轉(zhuǎn)換操作的類,也可以用于圖像增強(qiáng)
import matplotlib.pyplot as plt 
import numpy as np 

3. 模型定義

  我們?cè)诙x自己網(wǎng)絡(luò)的時(shí)候,需要繼承nn.Module類,并重新實(shí)現(xiàn)構(gòu)造函數(shù)__init__和forward兩個(gè)方法。forward方法是必須要重寫(xiě)的,它是實(shí)現(xiàn)模型的功能,實(shí)現(xiàn)各個(gè)層之間的連接關(guān)系的核心。如果你是用我下面的這個(gè)方法來(lái)定義的模型,在forward中要去連接它們之間的關(guān)系;如果你是用Sequential的方法來(lái)定義的模型,一般來(lái)講可以直接在構(gòu)造函數(shù)定義好后,在foward函數(shù)中return就行了(如果模型比較復(fù)雜就另當(dāng)別論)。

class LeNet(nn.Module):
 """
 	下面這個(gè)模型定義沒(méi)有用Sequential來(lái)定義,Sequential的定義方法能夠在init中就給出各個(gè)層
 	之間的關(guān)系,我這里是根據(jù)是否有可學(xué)習(xí)的參數(shù)。我將可學(xué)習(xí)參數(shù)的層(如全連接、卷積)放在構(gòu)造函數(shù)
 	中(其實(shí)你想把不具有參數(shù)的層放在里面也可以),把不具有學(xué)習(xí)參數(shù)的層(如dropout,
 	ReLU等激活函數(shù)、BN層)放在forward。
 
 """
 def __init__(self):
  super(LeNet,self).__init__()
  # 第一個(gè)卷積塊,這里輸入的是3通道,彩色圖。
  self.conv1 = nn.Conv2d(3,16,5)
  self.pool1 = nn.MaxPool2d(2,2)
  # 第二個(gè)卷積塊
  self.conv2 = nn.Conv2d(16,32,5)
  self.pool2 = nn.MaxPool2d(2,2)
  # 稠密塊,包含三個(gè)全連接層
  self.fc1 = nn.Linear(32*5*5,120)
  self.fc2 = nn.Linear(120,84)
  self.fc3 = nn.Linear(84,10)
  pass
 def forward(self,x):
  # x是輸入數(shù)據(jù),是一個(gè)tensor
  # 正向傳播
  x = F.relu(self.conv1(x)) # input(3, 32, 32) output(16, 28, 28)
  x = self.pool1(x)# output(16, 14, 14)
  x = F.relu(self.conv2(x)) # output(32, 10, 10)
  x = self.pool2(x)# output(32, 5, 5)
  x = x.view(-1, 32*5*5) # output(32*5*5)
  # 數(shù)據(jù)通過(guò)view展成一維向量,第一個(gè)參數(shù)-1是batch,自動(dòng)推理;32x5x5是展平后的個(gè)數(shù)
  x = F.relu(self.fc1(x))# output(120)
  x = F.relu(self.fc2(x))# output(84)
  x = self.fc3(x)  # output(10)
  # 為什么沒(méi)有用softmax函數(shù) --- 在網(wǎng)絡(luò)模型中已經(jīng)計(jì)算交叉熵以及概率
  return x

我們還可以隨便看一下可訓(xùn)練參數(shù):

model = LeNet()
for name,parameters in model.named_parameters():
 if param.requires_grad: 
  print(name,':',parameters.size())

看一下實(shí)例化的模型:

import torch
input1 = torch.rand([32,3,32,32])
model = LeNet() # 模式實(shí)例化
print(model) # 看一下模型結(jié)構(gòu)
output = model(input1)

這里就不再拓展了,我發(fā)4我發(fā)4,我會(huì)專門再寫(xiě)一篇使用pytorch查看特征矩陣 和卷積核參數(shù)的文章。

4. 數(shù)據(jù)加載、處理

# 調(diào)用設(shè)備內(nèi)的GPU并打印出來(lái)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print("using {} device.".format(device))
# 定義圖像數(shù)據(jù)的數(shù)據(jù)預(yù)處理方式
transform = transforms.Compose(
 [transforms.ToTensor(),
  transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 如果是第一次運(yùn)行代碼,沒(méi)有下載數(shù)據(jù)集,則將download調(diào)制為True進(jìn)行下載,并加載訓(xùn)練集
# transform是選擇數(shù)據(jù)預(yù)處理的方式,我們已經(jīng)提前定義
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
 download=False,transform=transform)
# 如果你是windows系統(tǒng),一定要記得把num_workers設(shè)置為0,不然會(huì)報(bào)錯(cuò)。
# 這個(gè)是將數(shù)據(jù)集劃為為n個(gè)批次,每個(gè)批次的數(shù)據(jù)集有batchSize張圖片,shuffle是打亂數(shù)據(jù)集
train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
shuffle=True, num_workers=0)
# 上面已經(jīng)下載過(guò)的話,download設(shè)置為False
val_set = torchvision.datasets.CIFAR10(root='./data', train=False,
  download=False, transform=transform)
# 驗(yàn)證集不用打亂,把batchsize設(shè)置為1,每次拿出1張來(lái)驗(yàn)證
val_loader = torch.utils.data.DataLoader(val_set, batch_size=1
 shuffle=False, num_workers=0)
# 定義classes類別
classes = ('plane', 'car', 'bird', 'cat',
  'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
val_data_iter = iter(val_loader) # 轉(zhuǎn)換成可迭代的迭代器
val_image, val_label = val_data_iter.next()
# 定義imshow函數(shù)顯示圖像
def imshow(img):
 img = img / 2 + 0.5  # unnormalize -> 反標(biāo)準(zhǔn)化處理
 npimg = img.numpy() # numpy和tensor的通道順序不同 tensor是通道度、寬度,numpy是高、寬、通
 
 # 使用transpose調(diào)整維度
 plt.imshow(np.transpose(npimg, (1, 2, 0))) #(1,2,0)-> 代表高度、寬度 通道
 plt.show()
imshow(torchvision.utils.make_grid(val_image))
# 顯示圖像結(jié)果:

在這個(gè)圖像加載部分,我做了些其它的嘗試,想要去發(fā)現(xiàn)train_set和train_loader之間的不同。這里你可以逐行取消我注釋的代碼,然后去觀察,去對(duì)比,你就知道有哪些不一樣了。

"""
	train_set:
 總結(jié):經(jīng)過(guò)多次嘗試,發(fā)現(xiàn)train_set是用一個(gè)Dataset包裝起來(lái),用索引來(lái)提取第n個(gè)數(shù)據(jù),提出的數(shù)據(jù)是一個(gè)元組。
 元組的第一個(gè)索引是Tensor的圖像數(shù)據(jù),(channel,height,width),索引的第二個(gè)數(shù)據(jù)是標(biāo)簽 int類型。
 可以選擇用enumerate迭代器,也可以直接進(jìn)行索引,這里因?yàn)闆](méi)有batchsize的維度,所以可以直接調(diào)用自己寫(xiě)的
 imshow函數(shù)來(lái)顯示圖片
"""
for i,data in enumerate(train_set):
 if i == 7:
#imshow(data[0])
#print(data[0])
#print(train_set[i][0]) # 查看train-set第七張圖元組 的 索引0
  print(train_set[i][0].shape)
  print(train_set[i][1]) # 查看train-set第七張圖元組 的 索引1
#imshow(train_set[i][0])
  print(type(train_set[i][1]))
#print(train_set[i].shape)
  print(data[0])
  print(data[0].shape)
#print(type(data[i]))
"""
	train_loader
 總結(jié):和Dataset類型不一樣,DataLoader不能夠直接用索引獲取數(shù)據(jù)。需要用enumerate迭代器來(lái)獲取 或者 iter.
 經(jīng)過(guò)enumerate索引后,得到的data類型是擁有兩個(gè)變量的列表類型。第一個(gè)變量是Tensor類型,用[batchSize,channel,height,width]表示
 批圖像數(shù)據(jù),里面是有batchsize張圖的。第二個(gè)變量也是Tensor類型,是代表每張圖像的標(biāo)簽,是個(gè)一維torch
 
"""
for i,data in enumerate(train_loader):
 if i == 7:
  print(type(data))
  print(len(data))
  print(type(data[0]))
  print(type(data[1]))
  print(data[0].shape)
  print(data[1].shape)
  print(type(data[1]))
#print(data[0])
  print(data[1])
#print(type(data[2]))

5.模型訓(xùn)練

# 用GPU訓(xùn)練
import time
torch.cuda.synchronize()
start = time.time()
net = LeNet()
net.to(device) #使用GPU時(shí)把網(wǎng)絡(luò)分配到指定的device中
loss_function = nn.CrossEntropyLoss() 
optimizer = optim.Adam(net.parameters(),lr=0.001) # Adam優(yōu)化器
Loss = []
for epoch in range(5):
 # 這里就只訓(xùn)練5個(gè)epoch,你可以試試多個(gè)
 running_loss = 0.0
 for step,data in enumerate(train_loader,start=0):
  inputs,labels = data # data是一個(gè)列表,[數(shù)據(jù),標(biāo)簽]
  
  # 清除歷史梯度,加快訓(xùn)練
  optimizer.zero_grad()
  
  outputs = net(inputs.to(device)) # 將輸入的數(shù)據(jù)分配到指定的GPU中
  
  loss = loss_function(outputs,labels.to(device)) # 將labels分配到指定的device
  
  loss.backward() # loss進(jìn)行反向傳播
  optimizer.step() # step進(jìn)行參數(shù)更新
  
  # 打印數(shù)據(jù)
  running_loss += loss.item() # 每次計(jì)算完loss后加入到running_loss中
  if step % 500 == 499: # 每500個(gè)mini-batches 就打印一次
with torch.no_grad(): 
 outputs = net(val_image.to(device))
 # outputs的shape = [32,10]
 # dim是max函數(shù)索引的維度,0是每列最大值,1是每行最大值
 predict_y = torch.max(outputs,dim=1)[1] # max函數(shù)返回的每個(gè)batchSize的最大值 + 索引。獲取索引[1]
  
 # == 來(lái)比較每個(gè)batchSize中的訓(xùn)練結(jié)果標(biāo)簽和原標(biāo)簽是否相同,如果預(yù)測(cè)正確就返回1,否則返回0,并累計(jì)正確的數(shù)量。
 # 得到的是tensor,用item轉(zhuǎn)成數(shù)字,CPU時(shí)使用
 accuracy = (predict_y == val_label.to(device)).sum().item()/val_label.size(0) 
 # val_label.size是驗(yàn)證集中batchSize的大小
 print('[%d %5d] train_loss: %.3f test_accuracy:%.3f' % (epoch+1,step+1,
running_loss/500,accuracy))
 Loss.append(running_loss)
 running_loss = 0.0
print('Finished Training')

torch.cuda.synchronize()
end = time.time()
print("訓(xùn)練用時(shí):",end-start,'s')

五個(gè)epoch在我的GPU上訓(xùn)練了68s。

整個(gè)代碼

model.py

import torch.nn as nn
import torch.nn.functional as F

class LeNet(nn.Module):
 # 要繼承于nn.Moudule父類
 def __init__(self):
  # 初始化函數(shù)
  super(LeNet, self).__init__()
  # 使用super函數(shù),解決多繼承可能遇到的一些問(wèn)題;調(diào)用基類的構(gòu)造函數(shù)

  self.conv1 = nn.Conv2d(3, 16, 5) # 調(diào)用卷積層 (in_channels,out_channels(也是卷積核個(gè)數(shù)。輸出的通道數(shù)),kernel_size(卷積核大?。?stride)
  self.pool1 = nn.MaxPool2d(2, 2)  # 最大池化層,進(jìn)行下采樣
  self.conv2 = nn.Conv2d(16, 32, 5) # 輸出的通道數(shù)為32
  self.pool2 = nn.MaxPool2d(2, 2)
  self.fc1 = nn.Linear(32*5*5, 120) # 全連接層輸入是一維向量,這里是32x5x5,我們要展平,120是節(jié)點(diǎn)的個(gè)數(shù)
  # 32是通道數(shù)
  # Linear(input_features,output_features)
  self.fc2 = nn.Linear(120, 84)
  self.fc3 = nn.Linear(84, 10)
 def forward(self, x):
  # x是輸入數(shù)據(jù),是一個(gè)tensor
  # 正向傳播
  x = F.relu(self.conv1(x)) # input(3, 32, 32) output(16, 28, 28)
  x = self.pool1(x)# output(16, 14, 14)
  x = F.relu(self.conv2(x)) # output(32, 10, 10)
  x = self.pool2(x)# output(32, 5, 5)
  x = x.view(-1, 32*5*5) # output(32*5*5)
  # 數(shù)據(jù)通過(guò)view展成一維向量,第一個(gè)參數(shù)-1是batch,自動(dòng)推理;32x5x5是展平后的個(gè)數(shù)
  x = F.relu(self.fc1(x))# output(120)
  x = F.relu(self.fc2(x))# output(84)
  x = self.fc3(x)  # output(10)
  # 為什么沒(méi)有用softmax函數(shù) --- 在網(wǎng)絡(luò)模型中已經(jīng)計(jì)算交叉熵以及概率
  return x
import torch
input1 = torch.rand([32,3,32,32])
model = LeNet() # 模式實(shí)例化
print(model) # 看一下模型結(jié)構(gòu)
output = model(input1)

train.py

import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
def main():
 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
 print("using {} device.".format(device))
 transform = transforms.Compose(
  [transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
 # 50000張訓(xùn)練圖片
 # 第一次使用時(shí)要將download設(shè)置為True才會(huì)自動(dòng)去下載數(shù)據(jù)集
 train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
  download=False, transform=transform)
 train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
 shuffle=True, num_workers=0)
 # 把訓(xùn)練集讀取,別分成一個(gè)一個(gè)批次的,shuffle可用于隨機(jī)打亂;batch_size是一次處理36張圖像
 # num_worker在windows下只能設(shè)置成0

 # 10000張驗(yàn)證圖片
 # 第一次使用時(shí)要將download設(shè)置為True才會(huì)自動(dòng)去下載數(shù)據(jù)集
 val_set = torchvision.datasets.CIFAR10(root='./data', train=False,
download=False, transform=transform)
 val_loader = torch.utils.data.DataLoader(val_set, batch_size=5000,
  shuffle=False, num_workers=0)
 # 驗(yàn)證集 一次拿出5000張1出來(lái)驗(yàn)證,不用打亂
 val_data_iter = iter(val_loader) # 轉(zhuǎn)換成可迭代的迭代器
 val_image, val_label = val_data_iter.next()
 # 轉(zhuǎn)換成迭代器后,用next方法可以得到測(cè)試的圖像和圖像的標(biāo)簽值
 
 classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

 # 這一部分用來(lái)看數(shù)據(jù)集
 # def imshow(img):
 #  img = img / 2 + 0.5  # unnormalize -> 反標(biāo)準(zhǔn)化處理
 #  npimg = img.numpy()
 #  plt.imshow(np.transpose(npimg, (1, 2, 0))) #(1,2,0)-> 代表高度、寬度 通道
 #  plt.show()
 #
 # # print labels
 # print(' '.join('%5s' % classes[val_label[j]] for j in range(4)))
 # imshow(torchvision.utils.make_grid(val_image))

 net = LeNet()
 net.to(device)  # 使用GPU時(shí)將網(wǎng)絡(luò)分配到指定的device中,不使用GPU注釋
 loss_function = nn.CrossEntropyLoss() # 已經(jīng)包含了softmax函數(shù)
 optimizer = optim.Adam(net.parameters(), lr=0.001) #Adam優(yōu)化器
 for epoch in range(5):  # loop over the dataset multiple times
  running_loss = 0.0
  for step, data in enumerate(train_loader, start=0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# 一般batch_size根據(jù)硬件設(shè)備來(lái)設(shè)置的,這個(gè)清楚歷史梯度,不讓梯度累計(jì),可以讓配置低的用戶加快訓(xùn)練
# forward + backward + optimize 、、、、、CPU
# outputs = net(inputs)
# loss = loss_function(outputs, labels)
# GPU使用時(shí)添加,不使用時(shí)注釋
outputs = net(inputs.to(device))  # 將inputs分配到指定的device中
loss = loss_function(outputs, labels.to(device))  # 將labels分配到指定的device中
loss.backward() # loss進(jìn)行反向傳播
optimizer.step() # step進(jìn)行參數(shù)更新
# print statistics
running_loss += loss.item() # m每次計(jì)算完后就加入到running_loss中
if step % 500 == 499: # print every 500 mini-batches
 with torch.no_grad(): # 在測(cè)試、預(yù)測(cè)過(guò)程中,這個(gè)函數(shù)可以優(yōu)化內(nèi)存,防止爆內(nèi)存
  # outputs = net(val_image)  # [batch, 10]
  outputs = net(val_image.to(device))  # 使用GPU時(shí)用這行將test_image分配到指定的device中
  predict_y = torch.max(outputs, dim=1)[1] #dim=1,因?yàn)閐im=0是batch;[1]是索引,最大值在哪個(gè)位置
  # accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)
  # eq用來(lái)比較,如果預(yù)測(cè)正確返回1,錯(cuò)誤返回0 -> 得到的是tensor,要用item轉(zhuǎn)成數(shù)值 CPU時(shí)使用
  accuracy = (predict_y==val_label.to(device)).sum().item() / val_label.size(0)
  print('[%d, %5d] train_loss: %.3f  test_accuracy: %.3f' %  (epoch + 1, step + 1, running_loss / 500, accuracy))
  running_loss = 0.0
 print('Finished Training')
 save_path = './Lenet.pth'
 torch.save(net.state_dict(), save_path)

if __name__ == '__main__':
 main()

Tips:數(shù)據(jù)集在當(dāng)前目錄下創(chuàng)建一個(gè)data文件夾,然后在train_set導(dǎo)入數(shù)據(jù)那里的download設(shè)置為True就可以下載了。如果你沒(méi)有GPU的話,你可以使用CPU訓(xùn)練,只需要把代碼中標(biāo)記的GPU部分注釋,注釋掉的CPU部分取消注釋就ok了。有條件還是GPU吧,CPU太慢了。

引用:

pytorch官方model

到此這篇關(guān)于CNN的Pytorch實(shí)現(xiàn)(LeNet)的文章就介紹到這了,更多相關(guān)CNN的Pytorch實(shí)現(xiàn)內(nèi)容請(qǐng)搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!

海外穩(wěn)定服務(wù)器

版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。

相關(guān)文章

實(shí)時(shí)開(kāi)通

自選配置、實(shí)時(shí)開(kāi)通

免備案

全球線路精選!

全天候客戶服務(wù)

7x24全年不間斷在線

專屬顧問(wèn)服務(wù)

1對(duì)1客戶咨詢顧問(wèn)

在線
客服

在線客服:7*24小時(shí)在線

客服
熱線

400-630-3752
7*24小時(shí)客服服務(wù)熱線

關(guān)注
微信

關(guān)注官方微信
頂部