基於PyTorch的深度學習快速入門教程

最近小組彙報正好用到了pytorch,所以想把相關內容整理成博客(彙報ppt和演示代碼附在最後了,有需要的話可以自取)。主要參考了《Python深度學習:基於PyTorch》的前幾章和網上的一些入門教程,側重代碼。通過這篇博客 ,你可以:

  1. 對PyTorch框架有初步的瞭解

  2. 對PyTorch中的Tensor張量、autograd自動求導、反向傳播等概念有一定了解並掌握相關代碼

  3. 用PyTorch實現一個簡單的機器學習算法(函數擬合)

  4. 使用PyTorch神經網絡工具箱搭建一個簡單的卷積神經網絡模型(minist手寫數字識別)

  5. 訓練搭建的網絡並通過該模型來進行預測


一、PyTorch簡介

1.1 PyTorch簡介

PyTorch源於深度學習框架Torch,Torch使用了一種不是很大衆的語言Lua作爲接口,用的人不是很多,所以開發團隊在Torch的基礎上使用Python重寫的一個全新的深度學習框架,也就是PyTorch。
雖然PyTorch的前身是Torch,但其與Torch 的不同之處在於PyTorch 不僅更加靈活,支持動態圖,而且提供了Python接口。PyTorch 既可以看做加入了GPU 支持的numpy,同時也可以看成一個擁有自動求導功能的強大的深度神經網絡。它更像NumPy的替代產物,不僅繼承了NumPy的衆多優點,還支持GPU計算,在計算效率上要比NumPy有更明顯的優勢;不僅如此,PyTorch還有許多高級功能,比如擁有豐富的API,可以快速完成深度神經網絡模型的搭建和訓練。所以PyTorch一經發布,便受到了衆多開發人員和科研人員的追捧和喜愛,成爲 AI從業者的重要工具之一。

1.2 PyTorch優點

  • 簡潔
    PyTorch追求最少的封裝,儘量避免重複造輪子,不像TensorFlow中充斥着session、graph、operation、name_scope、variable、tensor、layer等全新的概念
    PyTorch的設計遵循代表高維數組( tensor )、自動求導( variable\autograd)和神經網絡( nn.Module )三個由低到高的抽象層次,而且這三個抽象之間聯繫緊密,可以同時進行修改和操作,其中nn.Module則是PyTorch中對所有模型對象的封裝
  • 易用
    現在的深度學習平臺在定義模型的時候主要用兩種方式:static computation graph(靜態圖模型) 和 dynamic computation graph(動態圖模型)。 絕大部分平臺都採用的是static的定義方式,包括TensorFlow, Theano, Caffe,Keras等
    靜態圖需要在處理數據前定義好一套完整的模型;而動態圖模型允許用戶先定義好一套基本的框架再根據數據來實時修正模型
    靜態圖定義的缺陷是在處理數據前必須定義好完整的一套模型,能夠處理所有的邊際情況,比如在聲明模型前必須知道整個數據中句子的最大長度。相反動態圖模型(現有的平臺比如PyTorch, Chainer, Dynet)能夠非常自由的定義模型
    PyTorch不僅定義網絡結構簡單,而且還很直觀靈活。它支持autograd,所以不用自己去定義和數學推導back-propagation,還支持動態圖模型,可以無縫銜接numpy
  • 速度快
    PyTorch的靈活性不以速度爲代價,在許多評測中,PyTorch的速度表現勝過TensorFlow和Keras等框架 。同樣的算法,使用PyTorch實現的更有可能快過用其他框架實現的
  • 社區活躍
    PyTorch提供了完整的文檔,有facebook的FAIR強力支持(FAIR是全球TOP3的AI研究機構),開源方案也很多

1.3 PyTorch的安裝

主要流程:

  1. 在anaconda中創建python環境,並將路徑添加到系統環境變量中
  2. 在pytorch官網複製安裝命令https://pytorch.org/get-started/locally/
  3. 在命令行中安裝pytorch
  4. Import torch測試安裝是否成功

具體可以參考博客https://blog.csdn.net/qq_38704904/article/details/95192856

二、PyTorch基礎

2.1 Numpy

NumPy是 Python 語言的一個擴展程序庫,支持大量的維度數組與矩陣運算,此外也針對數組運算提供大量的數學函數庫,在機器學習和深度學習中經常用到。

2.1.1 numpy數組的定義

  • 直接定義
import numpy as np
x1 = np.array([1.0, 2.0, 3.0])
X2=np.array((1.0, 2.0, 3.0))
  • 將列表list轉換爲numpy數組
b=[2.0,4.0,6.0]
y=np.array(a)
  • 將numpy數組轉換爲列表list
z= np.array([1.0, 2.0, 3.0]) 
c=list(z)

2.1.2 numpy數組的元素訪問

對於矩陣A=np.array([1,2,3],[4,5,6])

  • A[i]獲得矩陣A的第i行
  • A[i][j]獲得元素Aij
  • A[i][j:k]獲得數組A[i]的j到k-1元素

2.1.3 numpy數組的計算

  • 加法:x+y
  • 乘法:x*y
  • 廣播:x*10=[1.0, 2.0, 3.0]10=[1.0, 2.0, 3.0] [10, 10, 10]

2.2 Tensors張量

2.2.1 Tensors

2.2.2 Tensors的使用

  1. 導入包
import torch
  1. 構建一個5*3的矩陣
x = torch.Tensor(5, 3)  # 未初始化
y = torch.rand(5, 3)  # 隨機初始化
  1. 將torch的Tensor轉換爲numpy的array
a=x.numpy() # Tensor轉array
x=torch.from_numpy(a)  # array轉Tensor
  1. 運算:
  • 加減法:y.add_(x)、z=x+y、torch.add(x,y,out=z)、z=torch.sub(x,y)
  • 乘法: x*y、torch.mul(x,y)
  • 裁剪:y=torch.clamp(x,-0.1,0.1)
    更多運算可參考官方說明文檔
  1. CUDA Tensors:
    使用 .cuda 函數將Tensors移動到GPU
if torch.cuda.is_available():
    x = x.cuda()
    y = y.cuda()

2.3 autograd自動求導

2.3.1 Variable變量
將Tensor轉換爲Variable之後,可以裝載梯度信息,一旦前向計算,可以通過.backward()方法來自動計算所有的梯度

2.3.2 gradient descent梯度下降
損失函數關於模型參數的梯度指向一個可以降低損失函數值的方向,可以不斷地沿着梯度的方向更新模型從而最小化損失函數

2.3.3 Autograd自動求導
對於複雜的模型,例如多達數十層的神經網絡,手動計算梯度非常困難,因此PyTorch提供了Autograd包來自動化求導過程,它會有一個記錄我們所有執行操作的記錄器,之後再回放記錄來計算梯度
這一技術在構建神經網絡時尤其有效,因爲我們可以通過計算前路參數的微分來節省時間

2.4 利用Numpy實現函數擬合

import numpy as np
from matplotlib import pyplot as plt
# 生成 輸入數據x 及 目標數據y
np.random.seed(100)
x = np.linspace(-1,1,100).reshape(100,1)
y = 3*np.power(x,2)+2+0.2*np.random.rand(x.size).reshape(100,1)
# 查看x、y數據分佈情況
plt.scatter(x,y)
plt.show()
# 初始化權重參數
w1 = np.random.rand(1,1)
b1 = np.random.rand(1,1)
# 訓練模型
lr = 0.001 # 學習率
for i in range(800): #梯度下降
    y_pred = np.power(x,2)*w1+b1

    loss = 0.5*(y_pred - y)**2  # 損失函數
    loss = loss.sum()  # 方差
    # 梯度下降法
    grad_w = np.sum((y_pred - y)*np.power(x,2))
    grad_b = np.sum((y_pred - y))
    w1 -= lr*grad_w  # 將學習率看作步長
    b1 -= lr*grad_b
# 可視化結果
plt.plot(x,y_pred,'r-',label='predict')
plt.scatter(x,y,color='blue',marker='o',label='true') # true data
plt.xlim(-1,1)
plt.ylim(2,6)
plt.legend()
plt.show()
print(w1,b1)

目標函數設爲y_pred=w1 * x^2+b1,求解目標函數相當於求解參數w1和b1
這裏定義的loss函數爲0.5*(y_pred - y)^2之和,相當於方差(按照吳恩達視頻,乘以0.5可以方便求導時把次方2係數消掉,所以實際乘以了0.5),loss函數值越小,誤差也就越小,所以相當於求解使得loss值最小的w1和b1,這樣的目標函數和實際函數是最接近的。
grad_w是w1的梯度,相當於loss對w1的偏導,grad_b是b1的梯度,相當於loss對b1的偏導,沿着梯度方向可以最快到達loss最低點。
注意,這裏的梯度是我們自己手動計算得到的,大概這麼個過程:
在這裏插入圖片描述
因此

    grad_w = np.sum((y_pred - y)*np.power(x,2))
    grad_b = np.sum((y_pred - y))

然後讓w1和b1每次沿着梯度方向挪動一小步,這樣w1和w2的梯度會越來越小,當接近0的時候也就得到了loss可能的極小值點

    w1 -= lr*grad_w  # 將學習率看作步長
    b1 -= lr*grad_b

將計算過程循環800次,輸出不斷更新後的w1和b1,並輸出擬合圖像
運行結果:
原始數據
在這裏插入圖片描述
在這裏插入圖片描述
可以得出w1=2.98927619,b1=2.09818307,目標函數y_pred=2.98927619x^2+2.09818307。

2.5 利用PyTorch實現函數擬合

我們可以看到,當函數比較簡單時,梯度用手動計算也很方便,但是當函數比較複雜時,計算便會十分艱鉅,而pythorch的自動求導就完美解決了這一問題,只需給出正向計算過程,pytorch會自動爲你反向計算梯度,接下來還是以上一情況爲例,但是使用PyTorch來實現。

import numpy as np
import torch
from matplotlib import pyplot as plt
# 生成 輸入數據x 及 目標數據y
np.random.seed(100)
x = np.linspace(-1,1,100).reshape(100,1)
y = 3*np.power(x,2)+2+0.2*np.random.rand(x.size).reshape(100,1)
x=torch.tensor(x)
y=torch.tensor(y)
# 查看x、y數據分佈情況
plt.scatter(x,y)
plt.show()
# 初始化權重參數
w1 =torch.zeros(1,1,requires_grad=True)
b1 =torch.zeros(1,1,requires_grad=True)
# 訓練模型
lr = 0.001 # 學習率
cost = []
for i in range(800): #梯度下降
    y_pred = w1*x**2 + b1
    loss = torch.sum((y_pred - y) ** 2)
    loss.backward()
    # 參數更新
    print(w1.grad.data.item(),b1.grad.data.item())
    # 梯度下降法
    w1.data = w1.data - lr*w1.grad.data  # 將學習率看作步長
    b1.data = b1.data - lr*b1.grad.data
    w1.grad.data.zero_()  #梯度清零
    b1.grad.data.zero_()

# 可視化結果
plt.plot(x,y_pred.data,'r-',label='predict')
plt.scatter(x,y,color='blue',marker='o',label='true') # true data
plt.xlim(-1,1)
plt.ylim(2,6)
plt.legend()
plt.show()
print(w1.data,b1.data)

數據和處理過程和前面都差不多,主要來看自動求導這塊。首先在定義w1和w2的時候,w1 =torch.zeros(1,1,requires_grad=True),意思是初試化1行1列的tensor w1初值爲0,requires_grad默認值爲false,賦值爲True表示之後需要求解梯度

w1 =torch.zeros(1,1,requires_grad=True)
b1 =torch.zeros(1,1,requires_grad=True)

然後給出原始函數y_pred=w1 * x^2+b1,loss=Σ0.5*(y_pred - y)^2之後直接loss.backward(),表示從loss反向傳播,計算loss到w1和b1的偏導,機器的具體計算過程可以參考計算圖與自動求導,這樣就不用手動計算對應的梯度公式,直接w1.grad.data.item()獲得梯度的數值(w1.grad就可以獲得梯度,但是得到的結果是一個tensor變量)
注意,每次循環都需要對梯度進行清零,否則每輪次梯度會累加前面的梯度,越算越大,與梯度下降的目的相悖

w1.grad.data.zero_()  #梯度清零

三、PyTorch神經網絡工具箱

3.1 卷積神經網絡層次

卷積神經網絡是一種包含卷積計算操作且具有深度層次結構的前饋型神經網絡,與傳統神經網絡的區別在於卷積神經網絡的層和形式有了很大的變化,可以說是傳統神經網絡的一個改進。如下圖所示,傳統神經網絡主要包括一個輸入層、一個輸出層,還有若干中間層,而卷積神經網絡有許多傳統神經網絡沒有的層次。

傳統神經網絡結構
卷積神經網絡結構
在這裏插入圖片描述
輸入層也就是你喂進去的大量訓練數據,所以主要介紹pytorch對其他幾個層級的實現:

3.1.1 卷積層

卷積計算層是卷積神經網絡的核心層次,由若干卷積單元組成。在這個卷積層裏,主要包含兩個關鍵操作,一個關鍵操作是局部關聯,它將每個神經元看做一個濾波器(filter),另一個關鍵操作是窗口(receptive
field)滑動,讓filter對局部數據計算。卷積計算層由若干卷積單元組成,每個卷積單元是一個權值矩陣,它會在二維的輸入數據上每次滑動固定步長,然後將對應的窗口的元素值進行矩陣的乘法,把得到的計算結果輸出到像素。
如圖8就是一次卷積計算操作,其中左邊的矩陣是初始輸入的原始矩陣,中間矩陣爲卷積核(filter),右側是經過卷積計算後得到的輸出值。通過卷積運算可以提取輸入的不同特徵,而且逐層加強,比如第一層卷積計算層可能只能提取低級特徵,而更高層的網路可以從低級特徵中迭代提取更爲複雜的特徵。

在這裏插入圖片描述
pytoch實現:

一維卷積: 多用於處理文本,只計寬度不計高度

conv1 = nn.Conv1d(in_channels=256, out_channels=100,
kernel_size=2)
input = torch.randn(32, 35, 256)
input = input.permute(0, 2, 1)
output = conv1(input)

二維卷積: 多用於處理圖像


from PIL import Image
from torchvision.transforms
import ToTensor, ToPILImage
to_tensor = ToTensor()  # img ->tensor
to_pil = ToPILImage()  # tensor -> image
ll =Image.open('imgs/lena.png')  
input =to_tensor(lena).unsqueeze(0) 

3.1.2 激活層

在神經網絡中加入激活函數的話可以引入非線性因素,還能提高本模型的表達強度,縮小模型的訓練時間,使訓練成本下降,解決很多線性模型不能解決的問題。

pytoch實現 relu函數:
relu函數

relu = nn.ReLU(inplace=True)
output = relu(input)
# output = input.clamp(min=0)

3.1.3 池化層

池化層(Pooling layer)本質上是採樣操作,而上採樣(upsampling)是將feature map還原,與上採樣不同的是池化爲下采樣(subsampling)操作,一是壓縮數據量,即壓縮輸入的特徵圖像來縮小圖像尺寸來達到減小所需顯存的目的;二是將feature map變小,即壓縮輸入的圖像特徵中的特徵值減小計算量,去除特徵值中重複冗餘的信息以保留最主要的特徵,改善過擬合的情況。

常見的池化操作包括平均池化(average
pooling)和最大池化(max pooling),其中平均池化是將圖像區域的平均值當作該區域池化後的取值,平均池化能很好的保留背景但是會使圖片變得模糊,而最大池化則是選取圖像區域的最大值當作該區域池化後的取值,能比較好的保留圖片紋理特徵,一般來說最大池化比平均池化要更爲常用。

平均池化:
平均池化
最大池化:
最大池化
pytoch實現:

pool1= nn.AvgPool2d(2,2)  # 平均池化
pool2= nn. MaxPool2d(2,2)  # 最大池化
out   = pool1( V(input) )
out   = pool2( V(input) )

3.1.4 全連接層(輸出層)

在卷積神經網絡中,經過多個卷積層和池化層後卷積神經網絡的尾部一般會存在1個及以上的全連接層,主要負責與上一層的所有神經元進行全連接,整合卷積層和池化層中取得的局部特徵以得到最終的特徵圖像。

pytoch實現:

input = V(t.randn(2,3))
linear = nn.Linear(3,4)
h = linear(input)

3.2 PyTorch搭建卷積神經網絡——MNIST手寫數字識別

接下來以mnist手寫數字識別爲例使用PyTorch神經網絡工具箱搭建一個簡單的卷積神經網絡模型(完整代碼在最後)

首先獲取訓練數據集


# 獲取訓練集dataset
training_data = torchvision.datasets.MNIST(
    root='./data/',  # dataset存儲路徑
    train=True,  # True表示是train訓練集,False表示test測試集   
    transform=torchvision.transforms.ToTensor(),  # 將原數據規範化到(0,1)區間
    download=DOWNLOAD_MNIST,
)

# 打印MNIST數據集的訓練集及測試集的尺寸
print(training_data.data.size())   # torch.Size([60000, 28, 28])
print(training_data.targets.size())   # torch.Size([60000])
# 打印一張看看長啥樣
plt.imshow(training_data.data[0].numpy(), cmap='gray')
plt.title('simple')
plt.show()

#通過torchvision.datasets獲取的dataset格式可直接可置於DataLoader
train_loader = Data.DataLoader(dataset=training_data,
batch_size=BATCH_SIZE,shuffle=True)

# 獲取測試集dataset
test_data = torchvision.datasets.MNIST(root='./data/',train=False)

# 取前2000個測試集樣本
test_x = Variable(torch.unsqueeze(test_data.data, dim=1),volatile=True).type(torch.FloatTensor)[:2000] / 255
# (2000, 28, 28) to (2000, 1, 28, 28), in range(0,1)
test_y = test_data.targets[:2000]

這是pytorch自帶的數據集,圖片長這樣在這裏插入圖片描述
如果你沒有mnist數據集的話它會給你自動下載,但是下載會很慢,所以可以自行下載之後新建data目錄,然後把下載好的數據集放進去,節約時間

鏈接:https://pan.baidu.com/s/1TlvwqzkvfICdAceHITcMyw
提取碼:u0nk

在這裏插入圖片描述
在這裏插入圖片描述
可以看到MNIST目錄下有processed和raw兩個目錄,processed是用來放訓練過程中產生的訓練文件,不太用管,而raw目錄纔是存放訓練圖片的
在這裏插入圖片描述

然後設計cnn網絡結構,定義一個具有如下結構的卷積神經網絡
在這裏插入圖片描述


class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(  # (1,28,28)
            nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5,
                      stride=1, padding=2),  # (16,28,28)
            # 想要con2d卷積出來的圖片尺寸沒有變化, padding=(kernel_size-1)/2
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)  # (16,14,14)
        )
        self.conv2 = nn.Sequential(  # (16,14,14)
            nn.Conv2d(16, 32, 5, 1, 2),  # (32,14,14)
            nn.ReLU(),
            nn.MaxPool2d(2)  # (32,7,7)
        )
        self.out = nn.Linear(32 * 7 * 7, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(x.size(0), -1)  # 將(batch,32,7,7)展平爲(batch,32*7*7)
        output = self.out(x)
        return output

拆開細看,定義的cnn整體結構:

CNN(
(conv1): Sequential(
(0): Conv2d(1, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(conv2): Sequential(
(0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(out): Linear(in_features=1568, out_features=10, bias=True)
)

其中一共有兩大塊——conv1、conv2

conv1包含一個二維卷積層:

nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5,
                      stride=1, padding=2),  # (16,28,28)

一個激勵層:

nn.ReLU()

一個平均池化層:

nn.MaxPool2d(kernel_size=2)

而conv2同理。
最後是一個全連接層,也可以說是輸出層

self.out = nn.Linear(32 * 7 * 7, 10)

而forward是原始的計算過程,之後可以用來反向傳播

def forward(self, x):
    x = self.conv1(x)
    x = self.conv2(x)
    x = x.view(x.size(0), -1)  # 將(batch,32,7,7)展平爲(batch,32*7*7)
    output = self.out(x)
    return output

這裏的x相當於輸入層,先把x放入第一塊conv1(卷積——激勵——池化),之後把輸出結構再放入第二塊conv2(卷積——激勵——池化),再放入輸出層out,最後返回輸出結果output

3.3 模型訓練與預測

訓練搭建的網絡並通過該模型來預測

先實例化剛剛設計的cnn網絡cnn = CNN(),然後設置一個Adam優化器進行優化

optimizer = torch.optim.Adam(cnn.parameters(), lr=LR)
loss_function = nn.CrossEntropyLoss()

循環訓練


for epoch in range(EPOCH):
    for step, (x, y) in enumerate(train_loader):
        b_x = Variable(x)
        b_y = Variable(y)

        output = cnn(b_x)
        loss = loss_function(output, b_y)  #損失函數
        optimizer.zero_grad()   
        loss.backward()
        optimizer.step()

        if step % 100 == 0:
            test_output = cnn(test_x)
            pred_y = torch.max(test_output, 1)[1].data.squeeze()
            s1=sum(pred_y == test_y)
            s2=test_y.size(0)
            accuracy = s1/(s2*1.0)
            print('Epoch:', epoch, '|Step:', step,
                  '|train loss:%.4f' % loss.item(), '|test accuracy:%.4f' % accuracy)


其中optimizer.zero_grad()是清除之前的梯度,然後對loss調用backward(),最後,調用optimizer.step()將更新的值加到model的parameters上。
關於optimizer(torch.optim)的使用

每訓練100次就輸出當前的loss值和準確度,其中準確度accuracy = 預測結果和正確結果相同的總數/總數

Epoch: 0 |Step: 0 |train loss:2.3105 |test accuracy:0.0605
Epoch: 0 |Step: 100 |train loss:0.1290 |test accuracy:0.8735
Epoch: 0 |Step: 200 |train loss:0.4058 |test accuracy:0.9285
Epoch: 0 |Step: 300 |train loss:0.1956 |test accuracy:0.9440
Epoch: 0 |Step: 400 |train loss:0.1238 |test accuracy:0.9585
Epoch: 0 |Step: 500 |train loss:0.2217 |test accuracy:0.9630
Epoch: 0 |Step: 600 |train loss:0.0237 |test accuracy:0.9670
Epoch: 0 |Step: 700 |train loss:0.2158 |test accuracy:0.9700
Epoch: 0 |Step: 800 |train loss:0.0433 |test accuracy:0.9720
Epoch: 0 |Step: 900 |train loss:0.0564 |test accuracy:0.9770
Epoch: 0 |Step: 1000 |train loss:0.0320 |test accuracy:0.9760
Epoch: 0 |Step: 1100 |train loss:0.0233 |test accuracy:0.9825

可以看到梯度在不斷下降,而準確度在不斷增加。

然後拿訓練好的模型進行預測

test_output = cnn(test_x[:10])
pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze()
print(pred_y, 'prediction number')
print(test_y[:10].numpy(), 'real number')

for n in range(10):
    plt.imshow(test_data.data[n].numpy(), cmap='gray')
    plt.title('data[%i' % n+']:   test:%i' % test_data.targets[n]+'   pred:%i' % pred_y[n])
    plt.show()

在測試集中拿出前10張圖片放入訓練好的網絡中test_output = cnn(test_x[:10])(也可以裁取別的部分),獲得預測值pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze(),然後輸出這10張圖片的預測值和實際標籤

結果:
[7 2 1 0 4 1 4 9 5 9] prediction number
[7 2 1 0 4 1 4 9 5 9] real number

同時還可以在圖像中展示plt.imshow(test_data.data[n].numpy(), cmap='gray')

部分結果:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

可以看到,識別的還是蠻準確的。


附:手寫數字識別完整代碼:

import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as Data
import torchvision
import matplotlib.pyplot as plt

torch.manual_seed(1)

EPOCH = 1
BATCH_SIZE = 50
LR = 0.001
DOWNLOAD_MNIST = True

# 獲取訓練集dataset
training_data = torchvision.datasets.MNIST(
    root='./data/',  # dataset存儲路徑
    train=True,  # True表示是train訓練集,False表示test測試集
    transform=torchvision.transforms.ToTensor(),  # 將原數據規範化到(0,1)區間
    download=DOWNLOAD_MNIST,
)

# 打印MNIST數據集的訓練集及測試集的尺寸
print(training_data.data.size())
print(training_data.targets.size())
# torch.Size([60000, 28, 28])
# torch.Size([60000])
plt.imshow(training_data.data[0].numpy(), cmap='gray')
plt.title('simple')
plt.show()

# 通過torchvision.datasets獲取的dataset格式可直接可置於DataLoader
train_loader = Data.DataLoader(dataset=training_data, batch_size=BATCH_SIZE,
                               shuffle=True)

# 獲取測試集dataset
test_data = torchvision.datasets.MNIST(root='./data/', train=False)
# 取前2000個測試集樣本

test_x = Variable(torch.unsqueeze(test_data.data, dim=1),
                  volatile=True).type(torch.FloatTensor)[:2000] / 255
# (2000, 28, 28) to (2000, 1, 28, 28), in range(0,1)
test_y = test_data.targets[:2000]


class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(  # (1,28,28)
            nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5,
                      stride=1, padding=2),  # (16,28,28)
            # 想要con2d卷積出來的圖片尺寸沒有變化, padding=(kernel_size-1)/2
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)  # (16,14,14)
        )
        self.conv2 = nn.Sequential(  # (16,14,14)
            nn.Conv2d(16, 32, 5, 1, 2),  # (32,14,14)
            nn.ReLU(),
            nn.MaxPool2d(2)  # (32,7,7)
        )
        self.out = nn.Linear(32 * 7 * 7, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(x.size(0), -1)  # 將(batch,32,7,7)展平爲(batch,32*7*7)
        output = self.out(x)
        return output


cnn = CNN()
print(cnn)

optimizer = torch.optim.Adam(cnn.parameters(), lr=LR)
loss_function = nn.CrossEntropyLoss()

for epoch in range(EPOCH):
    for step, (x, y) in enumerate(train_loader):
        b_x = Variable(x)
        b_y = Variable(y)

        output = cnn(b_x)
        loss = loss_function(output, b_y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if step % 100 == 0:
            test_output = cnn(test_x)
            pred_y = torch.max(test_output, 1)[1].data.squeeze()
            s1=sum(pred_y == test_y)
            s2=test_y.size(0)
            accuracy = s1/(s2*1.0)
            print('Epoch:', epoch, '|Step:', step,
                  '|train loss:%.4f' % loss.item(), '|test accuracy:%.4f' % accuracy)

test_output = cnn(test_x[:10])
pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze()
print(pred_y, 'prediction number')
print(test_y[:10].numpy(), 'real number')

for n in range(10):
    plt.imshow(test_data.data[n].numpy(), cmap='gray')
    plt.title('data[%i' % n+']:   test:%i' % test_data.targets[n]+'   pred:%i' % pred_y[n])
    plt.show()

參考資料:

莫煩python的CNN小實現
PyTorch 深度學習:60分鐘快速入門(使用CIFAR10數據集實現圖像分類)
基於PyTorch的深度學習入門教程(四)——構建神經網絡
分別使用Numpy和Tensor及Antograd實現機器學習
基於PyTorch深度學習


我的彙報 ppt及演示代碼

鏈接:https://pan.baidu.com/s/1vZUmWc3o6BZw_6B3ArvzlA
提取碼:k0ap

鏈接:https://pan.baidu.com/s/1R9R_tYNerfbl71_WMZFS1w
提取碼:0rtt


最後,碼字不易,吐血整理的乾貨長文,如果有幫助的話可以點個贊呀,給你小心心~在這裏插入圖片描述在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章