0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

PyTorch的簡單實現(xiàn)

jf_96884364 ? 來源:jf_96884364 ? 作者:jf_96884364 ? 2023-01-11 16:29 ? 次閱讀

1.必要的 PyTorch 背景

  1. PyTorch 是一個建立在 Torch 庫之上的 Python 包,旨在加速深度學習應(yīng)用。
  2. PyTorch 提供一種類似 NumPy 的抽象方法來表征張量(或多維數(shù)組),它可以利用 GPU 來加速訓練。

1.1 PyTorch 張量

PyTorch 的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)是張量,即多維數(shù)組。其功能與 NumPy 的 ndarray 對象類似,如下我們可以使用 torch.Tensor() 創(chuàng)建張量。如果你需要一個兼容 NumPy 的表征,或者你想從現(xiàn)有的 NumPy 對象中創(chuàng)建一個 PyTorch 張量,那么就很簡單了。

#構(gòu)建 2-D pytorch tensor 
pytorch_tensor = torch.Tensor(10, 20,20)
print("type: ", type(pytorch_tensor), " and size: ", pytorch_tensor.shape )

#將pytorch tensor 轉(zhuǎn)為 numpy array:
numpy_tensor = pytorch_tensor.numpy()
print("type: ", type(numpy_tensor), " and size: ", numpy_tensor.shape) 

#將numpy array 轉(zhuǎn)為 Pytorch Tensor:
pytorch_tensor = torch.Tensor(numpy_tensor)
print("type: ", type(pytorch_tensor), " and size: ", pytorch_tensor.shape)

1.2 PyTorch vs. NumPy

PyTorch 并不是 NumPy 的簡單替代品,但它實現(xiàn)了很多 NumPy 功能。其中有一個不便之處是其命名規(guī)則,有時候它和 NumPy 的命名方法相當不同。我們來舉幾個例子說明其中的區(qū)別:

#張量創(chuàng)建
t = torch.rand(2, 4, 3, 5)
a = np.random.rand(2, 4, 3, 5)

#張量分割
a = t.numpy()
pytorch_slice = t[0, 1:3, :, 4] #取t中的元素
numpy_slice = a[0, 1:3, :, 4]

#張量
Maskingt = t - 0.5
pytorch_masked = t[t > 0]
numpy_masked = a[a > 0]

#張量重塑
pytorch_reshape = t.view([6, 5, 4])
numpy_reshape = a.reshape([6, 5, 4])

1.3 PyTorch 變量

  1. PyTorch 張量的簡單封裝
  2. 幫助建立計算圖
  3. Autograd(自動微分庫)的必要部分
  4. 將關(guān)于這些變量的梯度保存在 .grad 中

結(jié)構(gòu)圖:

計算圖和變量:在 PyTorch 中,神經(jīng)網(wǎng)絡(luò)會使用相互連接的變量作為計算圖來表示。PyTorch 允許通過代碼構(gòu)建計算圖來構(gòu)建網(wǎng)絡(luò)模型;之后 PyTorch 會簡化估計模型權(quán)重的流程,例如通過自動計算梯度的方式。

舉例來說,假設(shè)我們想構(gòu)建兩層模型,那么首先要為輸入和輸出創(chuàng)建張量變量:

#將 PyTorch Tensor 包裝進 Variable 對象中:
from torch.autograd import Variable
import torch.nn.functional as F
x = Variable(torch.randn(4, 1), requires_grad=False)
y = Variable(torch.randn(3, 1), requires_grad=False)

#我們把 requires_grad 設(shè)置為 True,表明我們想要自動計算梯度,這將用于反向傳播中以優(yōu)化權(quán)重。

#現(xiàn)在我們來定義權(quán)重:
w1 = Variable(torch.randn(5, 4), requires_grad=True)
w2 = Variable(torch.randn(3, 5), requires_grad=True)

#訓練模型:
def model_forward(x):
    return F.sigmoid(w2 @ F.sigmoid(w1 @ x))
print (w1)
print (w1.data.shape)
print (w1.grad)  #gradient梯度

輸出為:

tensor([[-0.8571,  1.3199, -0.5086,  0.7253],
     [ 0.5370,  0.2830, -0.2245, -1.0154],
     [-0.9083, -0.8636,  0.3927,  0.5870],
     [ 0.2461, -0.0415,  1.9505, -0.1105],
     [ 0.7191, -0.9653,  0.3059, -1.1343]])
 torch.Size([5, 4])
 None

1.4 PyTorch 反向傳播

這樣我們有了輸入和目標、模型權(quán)重,那么是時候訓練模型了。我們需要三個組件:

  1. 損失函數(shù):描述我們模型的預測距離目標還有多遠;
  2. 優(yōu)化算法:用于更新權(quán)重;
  3. 反向傳播步驟:
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
import torch.nn.functional as F

x = Variable(torch.randn(4, 1), requires_grad=False)
y = Variable(torch.randn(3, 1), requires_grad=False)
w1 = Variable(torch.randn(5, 4), requires_grad=True)
w2 = Variable(torch.randn(3, 5), requires_grad=True)

def model_forward(x):
    return F.sigmoid(w2@ F.sigmoid(w1@ x))
    
#損失函數(shù):描述我們模型的預測距離目標還有多遠;
criterion = nn.MSELoss()

#優(yōu)化算法:用于更新權(quán)重;
optimizer = optim.SGD([w1, w2], lr=0.001)  #隨機最速下降法 (SGD) 

#反向傳播步驟:
for epoch in range(10):
    loss = criterion(model_forward(x), y)
optimizer.zero_grad() #將先前的梯度清零
loss.backward() #反向傳播
optimizer.step() #應(yīng)用這些梯度

1.5 PyTorch CUDA 接口

PyTorch 的優(yōu)勢之一是為張量和 autograd 庫提供 CUDA 接口。使用 CUDA GPU,你不僅可以加速神經(jīng)網(wǎng)絡(luò)訓練和推斷,還可以加速任何映射至 PyTorch 張量的工作負載。

你可以調(diào)用 torch.cuda.is_available() 函數(shù),檢查 PyTorch 中是否有可用 CUDA。

cuda_gpu = torch.cuda.is_available()
if (cuda_gpu):
    print("Great, you have a GPU!")
else:
    print("Life is short -- consider a GPU!")

如果有GPU,.cuda()之后,使用 cuda 加速代碼就和調(diào)用一樣簡單。如果你在張量上調(diào)用 .cuda(),則它將執(zhí)行從 CPU 到 CUDA GPU 的數(shù)據(jù)遷移。如果你在模型上調(diào)用 .cuda(),則它不僅將所有內(nèi)部儲存移到 GPU,還將整個計算圖映射至 GPU。

要想將張量或模型復制回 CPU,比如想和 NumPy 交互,你可以調(diào)用 .cpu()。

cuda_gpu = torch.cuda.is_available()
if (cuda_gpu):
    print("Great, you have a GPU!")
else:
    print("Life is short -- consider a GPU!")

if cuda_gpu:
    x = x.cuda()
    print(type(x.data))
else:
    x = x.cpu()
    print(type(x.data))

我們來定義兩個函數(shù)(訓練函數(shù)和測試函數(shù))來使用我們的模型執(zhí)行訓練和推斷任務(wù)。該代碼同樣來自 PyTorch 官方教程,我們摘選了所有訓練/推斷的必要步驟。

對于訓練和測試網(wǎng)絡(luò),我們需要執(zhí)行一系列動作,這些動作可直接映射至 PyTorch 代碼:

  1. 我們將模型轉(zhuǎn)換到訓練/推斷模式;
  2. 我們通過在數(shù)據(jù)集上成批獲取圖像,以迭代訓練模型;
  3. 對于每一個批量的圖像,我們都要加載數(shù)據(jù)和標注,運行網(wǎng)絡(luò)的前向步驟來獲取模型輸出;
  4. 我們定義損失函數(shù),計算每一個批量的模型輸出和目標之間的損失;
  5. 訓練時,我們初始化梯度為零,使用上一步定義的優(yōu)化器和反向傳播,來計算所有與損失有關(guān)的層級梯度;
  6. 訓練時,我們執(zhí)行權(quán)重更新步驟。
import torch 
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
import torch.nn.functional as F
 
x = Variable(torch.randn(4, 1), requires_grad=False)
y = Variable(torch.randn(3, 1), requires_grad=False)
w1 = Variable(torch.randn(5, 4), requires_grad=True)
w2 = Variable(torch.randn(3, 5), requires_grad=True)
 
def model_forward(x):
    return F.sigmoid(w2@ F.sigmoid(w1@ x))
    
#損失函數(shù):描述我們模型的預測距離目標還有多遠;
criterion = nn.MSELoss()
 
#優(yōu)化算法:用于更新權(quán)重;
optimizer = optim.SGD([w1, w2], lr=0.001)  #隨機最速下降法 (SGD) 
 
#反向傳播步驟:
for epoch in range(10):
    loss = criterion(model_forward(x), y)
optimizer.zero_grad() #將先前的梯度清零
loss.backward() #反向傳播
optimizer.step() #應(yīng)用這些梯度
 
 
cuda_gpu = torch.cuda.is_available()
if (cuda_gpu):
    print("Great, you have a GPU!")
else:
    print("Life is short -- consider a GPU!")
 
if cuda_gpu:
    x = x.cuda()
    print(type(x.data))
else:
    x = x.cpu()
    print(type(x.data))
 
def train(model, epoch, criterion, optimizer, data_loader):
    model.train()
    for batch_idx, (data, target) in enumerate(data_loader):
    #enumerate() 函數(shù)用于將一個可遍歷的數(shù)據(jù)對象(如列表、元組或字符串)組合為一個索引序列,
    #同時列出數(shù)據(jù)和數(shù)據(jù)下標,一般用在 for 循環(huán)當中
        if cuda_gpu:
            data, target = data.cuda(), target.cuda()
        model.cuda()
        data, target = Variable(data), Variable(target)
        output = model(data)
        optimizer.zero_grad() #將先前的梯度清零
        loss = criterion(output, target)
        loss.backward() #應(yīng)用這些梯度
        optimizer.step()
    if (batch_idx+1) % 400 == 0:
        print('Train Epoch: {} [{}/{} ({:.0f}%)]	Loss: {:.6f}'.format(
epoch, (batch_idx+1) * len(data), len(data_loader.dataset),
100. * (batch_idx+1) / len(data_loader), loss.data[0]))
def test(model, epoch, criterion, data_loader):
    model.eval()#eval() 函數(shù)用來執(zhí)行一個字符串表達式,并返回表達式的值。
    #output = model(data)
    test_loss = 0
    correct = 0
    for data, target in data_loader:
        test_loss += criterion(output, target).data[0]
        pred = output.data.max(1)[1] #獲取最大對數(shù)概率指標
        correct += pred.eq(target.data).cpu().sum()
    test_loss /= len(data_loader) #損失函數(shù)已經(jīng)超過批量大小
    acc = correct / len(data_loader.dataset)
    print('
Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)
'.format(
test_loss, correct, len(data_loader.dataset), 100. * acc))
    return (acc, test_loss)

2. 使用 PyTorch 進行數(shù)據(jù)分析

使用 torch.nn 庫構(gòu)建模型

使用 torch.autograd 庫訓練模型

將數(shù)據(jù)封裝進 torch.utils.data.Dataset 庫

使用 NumPy interface 連接你的模型、數(shù)據(jù)和你最喜歡的工具

在查看復雜模型之前,我們先來看個簡單的:簡單合成數(shù)據(jù)集上的線性回歸,我們可以使用 sklearn 工具生成這樣的合成數(shù)據(jù)集。

from sklearn.datasets import make_regression
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
sns.set()
x_train, y_train, W_target = make_regression(n_samples=100, n_features=1, noise=10, coef = True)
df = pd.DataFrame(data = {'X':x_train.ravel(), 'Y':y_train.ravel()})
sns.lmplot(x='X', y='Y', data=df, fit_reg=True)
plt.show()
x_torch = torch.FloatTensor(x_train)
y_torch = torch.FloatTensor(y_train)
y_torch = y_torch.view(y_torch.size()[0], 1)

PyTorch 的 nn 庫中有大量有用的模塊,其中一個就是線性模塊。如名字所示,它對輸入執(zhí)行線性變換,即線性回歸。

from sklearn.datasets import make_regression
import seaborn as sns  #seaborn能夠快速的繪制圖表
import pandas as pd
import matplotlib.pyplot as plt
sns.set()  #seaborn繪圖命令
#x_train為樣本特征,y_train為樣本輸出,W_target為回歸系數(shù),共100個樣本,每個樣本1個特征
x_train, y_train, W_target = make_regression(n_samples=100, n_features=1, noise=10, coef = True)
#make_regression:生成回歸模型數(shù)據(jù)    
#n_samples:生成樣本數(shù)    n_features:樣本特征數(shù)    noise:樣本隨機噪音    coef:是否返回回歸系數(shù)
df = pd.DataFrame(data = {'X':x_train.ravel(), 'Y':y_train.ravel()})
#DataFrame類似excel,是一種二維表,可以存放數(shù)值、字符串等
sns.lmplot(x='X', y='Y', data=df, fit_reg=True)# lmplot是用來繪制回歸圖的  fit_reg:是否顯示擬合的直線
plt.show() #顯示圖像
x_torch = torch.FloatTensor(x_train)  #類型轉(zhuǎn)換,將list,numpy轉(zhuǎn)化為tensor
y_torch = torch.FloatTensor(y_train)
y_torch = y_torch.view(y_torch.size()[0], 1) #view(n,m):排成n行m列

#PyTorch 的 nn 庫中有大量有用的模塊,其中一個就是線性模塊。
#如名字所示,它對輸入執(zhí)行線性變換,即線性回歸。
class LinearRegression(torch.nn.Module):
    def __init__(self, input_size, output_size):
        super(LinearRegression, self).__init__()#super():對繼承自父類的屬性進行初始化,用父類的初始化方法來初始化繼承的屬性
        self.linear = torch.nn.Linear(input_size, output_size)
        #torch.nn.Linear(in_features,out_features,bias = True)對傳入數(shù)據(jù)應(yīng)用線性變換:y=Ax+b
        #參數(shù):in_features:每個輸入樣本的大小  out_features:每個輸出樣本的大小
    def forward(self, x):
        return self.linear(x)
model = LinearRegression(1, 1) #__init__中input_size=1,output_size=1

criterion = torch.nn.MSELoss() #均方差損失函數(shù)
#構(gòu)建一個optimizer對象。這個對象能夠保持當前參數(shù)狀態(tài)并基于計算得到的梯度進行參數(shù)更新
optimizer = torch.optim.SGD(model.parameters(), lr=0.1) #隨機最速下降法(SGD) lr:學習率
for epoch in range(50):
    data, target = Variable(x_torch), Variable(y_torch) #Variable變量
output = model(data)
optimizer.zero_grad()#將先前的梯度清零
loss = criterion(output, target)
loss.backward()#反向傳播
optimizer.step()#應(yīng)用這些梯度
predicted = model(Variable(x_torch)).data.numpy()#擬合曲線

#現(xiàn)在我們可以打印出原始數(shù)據(jù)和適合 PyTorch 的線性回歸。
plt.plot(x_train, y_train, 'o', label='Original data')#原數(shù)據(jù)點
plt.plot(x_train, predicted, label='Fitted line')#擬合曲線
plt.legend() #圖片為默認格式
plt.show() #顯示

為了轉(zhuǎn)向更復雜的模型,我們下載了 MNIST 數(shù)據(jù)集至「datasets」文件夾中,并測試一些 PyTorch 中可用的初始預處理。PyTorch 具備數(shù)據(jù)加載器和處理器,可用于不同的數(shù)據(jù)集。數(shù)據(jù)集下載好后,你可以隨時使用。你還可以將數(shù)據(jù)包裝進 PyTorch 張量,創(chuàng)建自己的數(shù)據(jù)加載器類別。

批大?。╞atch size)是機器學習中的術(shù)語,指一次迭代中使用的訓練樣本數(shù)量。批大小可以是以下三種之一:

  1. batch 模式:批大小等于整個數(shù)據(jù)集,因此迭代和 epoch 值一致;
  2. mini-batch 模式:批大小大于 1 但小于整個數(shù)據(jù)集的大小。通常,數(shù)量可以是能被整個數(shù)據(jù)集整除的值。
  3. 隨機模式:批大小等于 1。因此梯度和神經(jīng)網(wǎng)絡(luò)參數(shù)在每個樣本之后都要更新。
train_loader = torch.utils.data.DataLoader(
   datasets.MNIST("data",train=True, download=True, transform=transforms.Compose([
       transforms.ToTensor(),
       transforms.Normalize((0.1307,), (0.3081,))
   ])), 
   batch_size=batch_num_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
   datasets.MNIST("data",train=False, transform=transforms.Compose([  #transforms.Compose把兩個步驟整合到一起
       transforms.ToTensor(), 
       #ToTensor()將shape為(H, W, C)的nump.ndarray或img轉(zhuǎn)為shape為(C, H, W)的tensor,
       #其將每一個數(shù)值歸一化到[0,1],其歸一化方法比較簡單,直接除以255即可。
       transforms.Normalize((0.1307,), (0.3081,))
       #Normalize():先將輸入歸一化到(0,1),再使用公式”(x-mean)/std”,將每個元素分布到(-1,1) 
   ])),

3. PyTorch 中的 LeNet 卷積神經(jīng)網(wǎng)絡(luò)(CNN)

現(xiàn)在我們從頭開始創(chuàng)建第一個簡單神經(jīng)網(wǎng)絡(luò)。該網(wǎng)絡(luò)要執(zhí)行圖像分類,識別 MNIST 數(shù)據(jù)集中的手寫數(shù)字。這是一個四層的卷積神經(jīng)網(wǎng)絡(luò)(CNN),一種分析 MNIST 數(shù)據(jù)集的常見架構(gòu)。該代碼來自 PyTorch 官方教程,你可以在這里(http://pytorch.org/tutorials/)找到更多示例。

  1. 我們將使用 torch.nn 庫中的多個模塊:
  2. 線性層:使用層的權(quán)重對輸入張量執(zhí)行線性變換;
  3. Conv1 和 Conv2:卷積層,每個層輸出在卷積核(小尺寸的權(quán)重張量)和同樣尺寸輸入?yún)^(qū)域之間的點積;
  4. Relu:修正線性單元函數(shù),使用逐元素的激活函數(shù) max(0,x);
  5. 池化層:使用 max 運算執(zhí)行特定區(qū)域的下采樣(通常 2x2 像素);
  6. Dropout2D:隨機將輸入張量的所有通道設(shè)為零。當特征圖具備強相關(guān)時,dropout2D 提升特征圖之間的獨立性;
  7. Softmax:將 Log(Softmax(x)) 函數(shù)應(yīng)用到 n 維輸入張量,以使輸出在 0 到 1 之間。
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        self.conv1 = nn.Conv2d(1,10,kernel_size=5) #輸入和輸出通道數(shù)分別為1和10
        self.conv2 = nn.Conv2d(10,20,kernel_size=5) #輸入和輸出通道數(shù)分別為10和20
        self.conv2_drop = nn.Dropout2d() #隨機選擇輸入的信道,將其設(shè)為0
        self.fc1 = nn.Linear(320,50) #輸入的向量大小和輸出的大小分別為320和50
        self.fc2 = nn.Linear(50,10)
    def forward(self,x):
        x = F.relu(F.max_pool2d(self.conv1(x),2)) #卷積--最大池化層--relu
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)),2)) #卷積--dropout--最大池化--relu
        x = x.view(-1, 320) #view(n,m):排成n行m列
        x = F.relu(self.fc1(x)) #fc--relu
        x = F.dropout(x, training=self.training) #dropout
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

#創(chuàng)建 LeNet 類后,創(chuàng)建對象并移至 GPU:
model = LeNet()
model.cuda()#沒有GPU則用model.cpu()代替model.cuda()
print ('MNIST_net model:
')
print (model)

輸出:

MNIST_net model:

LeNet(

(conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1))

(conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))

(conv2_drop): Dropout2d(p=0.5)

(fc1): Linear(in_features=320, out_features=50, bias=True)

(fc2): Linear(in_features=50, out_features=10, bias=True)

)

import os
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
import torch.nn.functional as F

cuda_gpu = torch.cuda.is_available()


def train(model, epoch, criterion, optimizer, data_loader):
    model.train()
    for batch_idx, (data, target) in enumerate(data_loader):
    #enumerate() 函數(shù)用于將一個可遍歷的數(shù)據(jù)對象(如列表、元組或字符串)組合為一個索引序列,
    #同時列出數(shù)據(jù)和數(shù)據(jù)下標,一般用在 for 循環(huán)當中
        if cuda_gpu:
            data, target = data.cuda(), target.cuda()
        model.cuda()
        data, target = Variable(data), Variable(target)
        output = model(data)
        optimizer.zero_grad() #將先前的梯度清零
        loss = criterion(output, target)
        loss.backward() #應(yīng)用這些梯度
        optimizer.step()
    if (batch_idx+1) % 400 == 0:
        print('Train Epoch: {} [{}/{} ({:.0f}%)]	Loss: {:.6f}'.format(
epoch, (batch_idx+1) * len(data), len(data_loader.dataset),
100. * (batch_idx+1) / len(data_loader), loss.data[0]))

def test(model, epoch, criterion, data_loader):
    model.eval()#eval() 函數(shù)用來執(zhí)行一個字符串表達式,并返回表達式的值
    test_loss = 0
    correct = 0
    for data, target in data_loader:
        if cuda_gpu:
            data, target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        output = model(data)
        test_loss += criterion(output, target).item()  #items()將字典中的每個項分別做為元組,添加到一個列表中形成一個新的列表容器
        pred = output.data.max(1)[1] #獲取最大對數(shù)概率指標
        correct += pred.eq(target.data).cpu().sum()
    test_loss /= len(data_loader) #損失函數(shù)已經(jīng)超過批量大小
    acc = correct / len(data_loader.dataset)
    print('
Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)
'.format(
test_loss, correct, len(data_loader.dataset), 100. * acc))
    return (acc, test_loss)



batch_num_size = 64
#DataLoader數(shù)據(jù)加載器,結(jié)合了數(shù)據(jù)集和取樣器,并且可以提供多個線程處理數(shù)據(jù)集
#在訓練模型時使用到此函數(shù),用來把訓練數(shù)據(jù)分成多個小組,此函數(shù)每次拋出一組數(shù)據(jù)
#直至把所有的數(shù)據(jù)都拋出。就是做一個數(shù)據(jù)的初始化
#此函數(shù)的參數(shù):
#dataset:包含所有數(shù)據(jù)的數(shù)據(jù)集
#batch_size :每一小組所包含數(shù)據(jù)的數(shù)量
#Shuffle : 是否打亂數(shù)據(jù)位置,當為Ture時打亂數(shù)據(jù),全部拋出數(shù)據(jù)后再次dataloader時重新打亂

train_loader = torch.utils.data.DataLoader(
   datasets.MNIST("data",train=True, download=True, transform=transforms.Compose([
       transforms.ToTensor(),
       transforms.Normalize((0.1307,), (0.3081,))
   ])), 
   batch_size=batch_num_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
   datasets.MNIST("data",train=False, transform=transforms.Compose([  #transforms.Compose把兩個步驟整合到一起
       transforms.ToTensor(), 
       #ToTensor()將shape為(H, W, C)的nump.ndarray或img轉(zhuǎn)為shape為(C, H, W)的tensor,
       #其將每一個數(shù)值歸一化到[0,1],其歸一化方法比較簡單,直接除以255即可。
       transforms.Normalize((0.1307,), (0.3081,))
       #Normalize():先將輸入歸一化到(0,1),再使用公式”(x-mean)/std”,將每個元素分布到(-1,1) 
   ])), 
   batch_size=batch_num_size, shuffle=True)



class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        self.conv1 = nn.Conv2d(1,10,kernel_size=5) #輸入和輸出通道數(shù)分別為1和10
        self.conv2 = nn.Conv2d(10,20,kernel_size=5) #輸入和輸出通道數(shù)分別為10和20
        self.conv2_drop = nn.Dropout2d() #隨機選擇輸入的信道,將其設(shè)為0
        self.fc1 = nn.Linear(320,50) #輸入的向量大小和輸出的大小分別為320和50
        self.fc2 = nn.Linear(50,10)
    def forward(self,x):
        x = F.relu(F.max_pool2d(self.conv1(x),2)) #卷積--最大池化層--relu
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)),2)) #卷積--dropout--最大池化--relu
        x = x.view(-1, 320) #view(n,m):排成n行m列
        x = F.relu(self.fc1(x)) #fc--relu
        x = F.dropout(x, training=self.training) #dropout
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

#創(chuàng)建 LeNet 類后,創(chuàng)建對象并移至 GPU:
model = LeNet()
if cuda_gpu:
    model.cuda()#沒有GPU則用model.cpu()代替model.cuda()
print ('MNIST_net model:
')
print (model)



#要訓練該模型,我們需要使用帶動量的 SGD,學習率為 0.01,momentum 為 0.5。
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(),lr = 0.005, momentum = 0.9)

#僅僅需要 5 個 epoch(一個 epoch 意味著你使用整個訓練數(shù)據(jù)集來更新訓練模型的權(quán)重)
#我們就可以訓練出一個相當準確的 LeNet 模型。這段代碼檢查可以確定文件中是否已有預訓練好的模型
#有則加載;無則訓練一個并保存至磁盤
epochs = 5
if (os.path.isfile('pretrained/MNIST_net.t7')):
    print ('Loading model')
    model.load_state_dict(torch.load('pretrained/MNIST_net.t7', map_location=lambda storage, loc: storage))
    acc, loss = test(model, 1, criterion, test_loader)
else:
    print ('Training model')
    for epoch in range(1, epochs + 1):
        train(model, epoch, criterion, optimizer, train_loader)
        acc, loss = test(model, 1, criterion, test_loader)
    torch.save(model.state_dict(), 'pretrained/MNIST_net.t7')

若無預訓練模型,輸出:

Training model

Test set: Average loss: 0.1348, Accuracy: 9578/10000 (0%)

Test set: Average loss: 0.0917, Accuracy: 9703/10000 (0%)

Test set: Average loss: 0.0746, Accuracy: 9753/10000 (0%)

Test set: Average loss: 0.0659, Accuracy: 9795/10000 (0%)

Test set: Average loss: 0.0553, Accuracy: 9828/10000 (0%)

現(xiàn)在我們來看下模型。首先,打印出該模型的信息。打印函數(shù)顯示所有層(如 Dropout 被實現(xiàn)為一個單獨的層)及其名稱和參數(shù)。同樣有一個迭代器在模型中所有已命名模塊之間運行。當你具備一個包含多個「內(nèi)部」模型的復雜 DNN 時,這有所幫助。在所有已命名模塊之間的迭代允許我們創(chuàng)建模型解析器,可讀取模型參數(shù)、創(chuàng)建與該網(wǎng)絡(luò)類似的模塊。

print ("Internal models:")
for idx, m in enumerate(model.named_modules()):
   print(idx, "->", m)
   print ("-------------------------------------------------------------------------")

輸出:

Internal models:

0 -> ('', LeNet(

(conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1))

(conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))

(conv2_drop): Dropout2d(p=0.5)

(fc1): Linear(in_features=320, out_features=50, bias=True)

(fc2): Linear(in_features=50, out_features=10, bias=True)

))


1 -> ('conv1', Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1)))


2 -> ('conv2', Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1)))


3 -> ('conv2_drop', Dropout2d(p=0.5))


4 -> ('fc1', Linear(in_features=320, out_features=50, bias=True))


5 -> ('fc2', Linear(in_features=50, out_features=10, bias=True))


你可以使用 .cpu() 方法將張量移至 CPU(或確保它在那里)?;蛘?,當 GPU 可用時(torch.cuda. 可用),使用 .cuda() 方法將張量移至 GPU。你可以看到張量是否在 GPU 上,其類型為 torch.cuda.FloatTensor。如果張量在 CPU 上,則其類型為 torch.FloatTensor。

t = torch.rand(2, 4, 3, 5)
print (type(t.cpu().data))
if torch.cuda.is_available():
   print ("Cuda is available")
   print (type(t.cuda().data))
else:
   print ("Cuda is NOT available")

輸出:

Cuda is available

如果張量在 CPU 上,我們可以將其轉(zhuǎn)換成 NumPy 數(shù)組,其共享同樣的內(nèi)存位置,改變其中一個就會改變另一個。

import torch 

t = torch.rand(2, 4, 3, 5)

if torch.cuda.is_available():
   try:
       print(t.data.numpy())
   except RuntimeError as e:
       "你不能將GPU張量轉(zhuǎn)換為numpy數(shù)組,你必須將你的權(quán)重tendor復制到cpu然后得到numpy數(shù)組"

print(type(t.cpu().data.numpy()))
print(t.cpu().data.numpy().shape)
print(t.cpu().data.numpy())

現(xiàn)在我們了解了如何將張量轉(zhuǎn)換成 NumPy 數(shù)組,我們可以利用該知識使用 matplotlib 進行可視化!我們來打印出個卷積層的卷積濾波器

import os
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
import torch.nn.functional as F

cuda_gpu = torch.cuda.is_available()


def train(model, epoch, criterion, optimizer, data_loader):
    model.train()
    for batch_idx, (data, target) in enumerate(data_loader):
    #enumerate() 函數(shù)用于將一個可遍歷的數(shù)據(jù)對象(如列表、元組或字符串)組合為一個索引序列,
    #同時列出數(shù)據(jù)和數(shù)據(jù)下標,一般用在 for 循環(huán)當中
        if cuda_gpu:
            data, target = data.cuda(), target.cuda()
        model.cuda()
        data, target = Variable(data), Variable(target)
        output = model(data)
        optimizer.zero_grad() #將先前的梯度清零
        loss = criterion(output, target)
        loss.backward() #應(yīng)用這些梯度
        optimizer.step()
    if (batch_idx+1) % 400 == 0:
        print('Train Epoch: {} [{}/{} ({:.0f}%)]	Loss: {:.6f}'.format(
epoch, (batch_idx+1) * len(data), len(data_loader.dataset),
100. * (batch_idx+1) / len(data_loader), loss.data[0]))

def test(model, epoch, criterion, data_loader):
    model.eval()#eval() 函數(shù)用來執(zhí)行一個字符串表達式,并返回表達式的值
    test_loss = 0
    correct = 0
    for data, target in data_loader:
        if cuda_gpu:
            data, target = data.cuda(), target.cuda()
        data, target = Variable(data), Variable(target)
        output = model(data)
        test_loss += criterion(output, target).item()  #items()將字典中的每個項分別做為元組,添加到一個列表中形成一個新的列表容器
        pred = output.data.max(1)[1] #獲取最大對數(shù)概率指標
        correct += pred.eq(target.data).cpu().sum()
    test_loss /= len(data_loader) #損失函數(shù)已經(jīng)超過批量大小
    acc = correct / len(data_loader.dataset)
    print('
Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)
'.format(
test_loss, correct, len(data_loader.dataset), 100. * acc))
    return (acc, test_loss)



batch_num_size = 64
#DataLoader數(shù)據(jù)加載器,結(jié)合了數(shù)據(jù)集和取樣器,并且可以提供多個線程處理數(shù)據(jù)集
#在訓練模型時使用到此函數(shù),用來把訓練數(shù)據(jù)分成多個小組,此函數(shù)每次拋出一組數(shù)據(jù)
#直至把所有的數(shù)據(jù)都拋出。就是做一個數(shù)據(jù)的初始化
#此函數(shù)的參數(shù):
#dataset:包含所有數(shù)據(jù)的數(shù)據(jù)集
#batch_size :每一小組所包含數(shù)據(jù)的數(shù)量
#Shuffle : 是否打亂數(shù)據(jù)位置,當為Ture時打亂數(shù)據(jù),全部拋出數(shù)據(jù)后再次dataloader時重新打亂

train_loader = torch.utils.data.DataLoader(
   datasets.MNIST("data",train=True, download=True, transform=transforms.Compose([
       transforms.ToTensor(),
       transforms.Normalize((0.1307,), (0.3081,))
   ])), 
   batch_size=batch_num_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
   datasets.MNIST("data",train=False, transform=transforms.Compose([  #transforms.Compose把兩個步驟整合到一起
       transforms.ToTensor(), 
       #ToTensor()將shape為(H, W, C)的nump.ndarray或img轉(zhuǎn)為shape為(C, H, W)的tensor,
       #其將每一個數(shù)值歸一化到[0,1],其歸一化方法比較簡單,直接除以255即可。
       transforms.Normalize((0.1307,), (0.3081,))
       #Normalize():先將輸入歸一化到(0,1),再使用公式”(x-mean)/std”,將每個元素分布到(-1,1) 
   ])), 
   batch_size=batch_num_size, shuffle=True)



class LeNet(nn.Module):
    def __init__(self):
        super(LeNet,self).__init__()
        self.conv1 = nn.Conv2d(1,10,kernel_size=5) #輸入和輸出通道數(shù)分別為1和10
        self.conv2 = nn.Conv2d(10,20,kernel_size=5) #輸入和輸出通道數(shù)分別為10和20
        self.conv2_drop = nn.Dropout2d() #隨機選擇輸入的信道,將其設(shè)為0
        self.fc1 = nn.Linear(320,50) #輸入的向量大小和輸出的大小分別為320和50
        self.fc2 = nn.Linear(50,10)
    def forward(self,x):
        x = F.relu(F.max_pool2d(self.conv1(x),2)) #卷積--最大池化層--relu
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)),2)) #卷積--dropout--最大池化--relu
        x = x.view(-1, 320) #view(n,m):排成n行m列
        x = F.relu(self.fc1(x)) #fc--relu
        x = F.dropout(x, training=self.training) #dropout
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

#創(chuàng)建 LeNet 類后,創(chuàng)建對象并移至 GPU:
model = LeNet()
if cuda_gpu:
    model.cuda()#沒有GPU則用model.cpu()代替model.cuda()
print ('MNIST_net model:
')
print (model)



#要訓練該模型,我們需要使用帶動量的 SGD,學習率為 0.01,momentum 為 0.5。
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(),lr = 0.005, momentum = 0.9)

#僅僅需要 5 個 epoch(一個 epoch 意味著你使用整個訓練數(shù)據(jù)集來更新訓練模型的權(quán)重)
#我們就可以訓練出一個相當準確的 LeNet 模型。這段代碼檢查可以確定文件中是否已有預訓練好的模型
#有則加載;無則訓練一個并保存至磁盤
epochs = 5
if (os.path.isfile('pretrained/MNIST_net.t7')):
    print ('Loading model')
    model.load_state_dict(torch.load('pretrained/MNIST_net.t7', map_location=lambda storage, loc: storage))
    acc, loss = test(model, 1, criterion, test_loader)
else:
    print ('Training model')
    for epoch in range(1, epochs + 1):
        train(model, epoch, criterion, optimizer, train_loader)
        acc, loss = test(model, 1, criterion, test_loader)
    torch.save(model.state_dict(), 'MNIST_net.t7')
t = torch.rand(2, 4, 3, 5)


if torch.cuda.is_available():
   try:
       print(t.data.numpy())
   except RuntimeError as e:
       "你不能將GPU張量轉(zhuǎn)換為numpy數(shù)組,你必須將你的權(quán)重tendor復制到cpu然后得到numpy數(shù)組"
print(type(t.cpu().data.numpy()))
print(t.cpu().data.numpy().shape)
print(t.cpu().data.numpy())

data = model.conv1.weight.cpu().data.numpy()
print (data.shape)
print (data[:, 0].shape)

kernel_num = data.shape[0]
fig, axes = plt.subplots(ncols=kernel_num, figsize=(2*kernel_num, 2))
for col in range(kernel_num):
   axes[col].imshow(data[col, 0, :, :], cmap=plt.cm.gray)
plt.show()

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • python
    +關(guān)注

    關(guān)注

    55

    文章

    4774

    瀏覽量

    84386
  • 數(shù)組
    +關(guān)注

    關(guān)注

    1

    文章

    412

    瀏覽量

    25883
  • pytorch
    +關(guān)注

    關(guān)注

    2

    文章

    802

    瀏覽量

    13121
收藏 人收藏

    評論

    相關(guān)推薦

    Image Style Transfer pytorch方式實現(xiàn)的主要思路

    深度學總結(jié):Image Style Transfer pytorch方式實現(xiàn),這個是非基于autoencoder和domain adversrial方式
    發(fā)表于 06-20 10:58

    PyTorch如何入門

    PyTorch 入門實戰(zhàn)(一)——Tensor
    發(fā)表于 06-01 09:58

    通過Cortex來非常方便的部署PyTorch模型

    該框架的 python 風格,其學習曲線的溫和性,以及它對快速和簡單原型的方便實現(xiàn),使 PyTorch 明顯成為研究人員的最愛。因此,它正在推動一些最酷的機器學習項目:Transformers
    發(fā)表于 11-01 15:25

    簡化版的XLNet在PyTorch Wrapper實現(xiàn)

    土豪有土豪的用法,窮人有窮人的訣竅。最近有個韓國小哥就成功將XLNet挪到了Pytorch框架上,可以在僅使用小規(guī)模訓練數(shù)據(jù)(批規(guī)模=1)的情況下,實現(xiàn)一個簡單的XLNet實例,并弄清XLNet架構(gòu)的預訓練機制。他將
    的頭像 發(fā)表于 07-07 09:15 ?4129次閱讀

    基于Cortex部署PyTorch模型

    該框架的 python 風格,其學習曲線的溫和性,以及它對快速和簡單原型的方便實現(xiàn),使 PyTorch 明顯成為研究人員的最愛。因此,它正在推動一些最酷的機器學習項目:
    的頭像 發(fā)表于 04-19 11:40 ?2555次閱讀

    基于PyTorch的深度學習入門教程之PyTorch簡單知識

    本文參考PyTorch官網(wǎng)的教程,分為五個基本模塊來介紹PyTorch。為了避免文章過長,這五個模塊分別在五篇博文中介紹。 Part1:PyTorch簡單知識 Part2:
    的頭像 發(fā)表于 02-16 15:20 ?2210次閱讀

    Pytorch實現(xiàn)MNIST手寫數(shù)字識別

    Pytorch 實現(xiàn)MNIST手寫數(shù)字識別
    發(fā)表于 06-16 14:47 ?7次下載

    pytorch實現(xiàn)斷電繼續(xù)訓練時需要注意的要點

    本文整理了pytorch實現(xiàn)斷電繼續(xù)訓練時需要注意的要點,附有代碼詳解。
    的頭像 發(fā)表于 08-22 09:50 ?1375次閱讀

    PyTorch教程3.2之面向?qū)ο蟮脑O(shè)計實現(xiàn)

    電子發(fā)燒友網(wǎng)站提供《PyTorch教程3.2之面向?qū)ο蟮脑O(shè)計實現(xiàn).pdf》資料免費下載
    發(fā)表于 06-05 15:48 ?0次下載
    <b class='flag-5'>PyTorch</b>教程3.2之面向?qū)ο蟮脑O(shè)計<b class='flag-5'>實現(xiàn)</b>

    PyTorch教程3.5之線性回歸的簡潔實現(xiàn)

    電子發(fā)燒友網(wǎng)站提供《PyTorch教程3.5之線性回歸的簡潔實現(xiàn).pdf》資料免費下載
    發(fā)表于 06-05 11:28 ?0次下載
    <b class='flag-5'>PyTorch</b>教程3.5之線性回歸的簡潔<b class='flag-5'>實現(xiàn)</b>

    PyTorch教程4.4之從頭開始實現(xiàn)Softmax回歸

    電子發(fā)燒友網(wǎng)站提供《PyTorch教程4.4之從頭開始實現(xiàn)Softmax回歸.pdf》資料免費下載
    發(fā)表于 06-05 15:37 ?0次下載
    <b class='flag-5'>PyTorch</b>教程4.4之從頭開始<b class='flag-5'>實現(xiàn)</b>Softmax回歸

    PyTorch教程13.6之多個GPU的簡潔實現(xiàn)

    電子發(fā)燒友網(wǎng)站提供《PyTorch教程13.6之多個GPU的簡潔實現(xiàn).pdf》資料免費下載
    發(fā)表于 06-05 14:21 ?0次下載
    <b class='flag-5'>PyTorch</b>教程13.6之多個GPU的簡潔<b class='flag-5'>實現(xiàn)</b>

    tensorflow和pytorch哪個更簡單?

    PyTorch簡單。選擇TensorFlow還是PyTorch取決于您的具體需求和偏好。如果您需要一個易于使用、靈活且具有強大社區(qū)支持的框架,PyTorch可能是一個更好的選擇。如果
    的頭像 發(fā)表于 07-05 09:45 ?719次閱讀

    基于PyTorch的卷積核實例應(yīng)用

    和應(yīng)用卷積操作變得簡單而高效。本文將以PyTorch為基礎(chǔ),深入探討卷積核的實例應(yīng)用,包括其定義、實現(xiàn)方式、以及在實際場景中的應(yīng)用。
    的頭像 發(fā)表于 07-11 15:19 ?371次閱讀

    PyTorch中搭建一個最簡單的模型

    PyTorch中搭建一個最簡單的模型通常涉及幾個關(guān)鍵步驟:定義模型結(jié)構(gòu)、加載數(shù)據(jù)、設(shè)置損失函數(shù)和優(yōu)化器,以及進行模型訓練和評估。
    的頭像 發(fā)表于 07-16 18:09 ?1726次閱讀