【PyTorch學習】PyTorch基礎知識

目錄

一、Pytorch介紹

1.1 什麼是Pytorch

1.2 爲何要使用Pytorch

1.3 配置Pytorch深度學習環境

二、Pytorch基礎

2.1 Tensor(張量)

2.2 Variable(變量)

2.3 Dataset(數據集)

1. torch.utils.data.Dataset

2. torch.utils.data.TensorDataset

3. torch.utils.data.DataLoader

4. torchvision.datasets.ImageFolder

2.4 nn.Module(模組)

2.5 torch.optim(優化)

1. 一階優化算法

2. 二階優化算法

2.6 模型的保存和加載


一、Pytorch介紹

1.1 什麼是Pytorch

PyTorch 是 Torch7 團隊開發的,從它的名字就可以看出,其與 Torch 的不同之處在於 PyTorch 使用了 Python 作爲開發語言。

PyTorch 既可以看做加入了 GPU 支持的 numpy,同時也可以看 成一個擁有自動求導功能的強大的深度神經網絡,除了 Facebook之外,它還已經被 Twitter、CMU 和 Salesforce 等機構採用。

1.2 爲何要使用Pytorch

  1. 掌握一個框架並不能一勞永逸,現在深度學習並沒有誰擁 有絕對的壟斷地位。多學一個框架,以備不時之需。
  2. Tensorflow 與 Caffe 都是命令式的編程語言,而且是靜態的,首先必須構建一個神經網絡,然後一次又一次使用同樣的結構, 如果想要改變網絡的結構,就必須從頭開始。但是對於 PyTorch,通過一種反向自動求導的技術,可以讓你零延遲地任意改變神經網絡的行爲,儘管這項技術不是 Pytorch 獨有,但目前爲止它實現是最快的,能夠爲你任何瘋狂想法的實現獲得最高的速度和最佳的靈活性, 這也是 PyTorch 對比 Tensorflow 最大的優勢。
  3. Pytorch 的設計思路是線性、直觀且易於使用的。
  4. Pytorch 的代碼相對於 Tensorflow 而言,更加簡潔直觀,同 時對於 Tensorflow 高度工業化的很難看懂的底層代碼,Pytorch的源代碼就要友好得多,更容易看懂。

PyTorch的特點:

  1. 支持GPU
  2. 動態神經網絡
  3. Python優先
  4. 命令式體驗
  5. 輕鬆擴展

1.3 配置Pytorch深度學習環境

安裝GPU版本的Pytorch鏈接: 

https://blog.csdn.net/weixin_40431584/article/details/105119633

 

二、Pytorch基礎

2.1 Tensor(張量)

PyTorch 裏面處理的最基本的操作對象就是 Tensor,Tensor 是張量的英文,表示的是一個多維的矩陣,比如零維就是一個點,一維就是向量,二維就是一般的矩陣,多維就相當於一個多維的數組,這和 numpy 是對應的,而且 PyTorch 的 Tensor 可以和 numpy的ndarray相互轉換,唯一不同的是PyTorch可以在GPU上運行,而numpy的ndarray只能在CPU上運行

常用的不同數據類型的Tensor:

  • 32位浮點型torch.FloatTensor
  • 64位浮點型 torch.DoubleTensor
  • 16位整型torch.ShortTensor
  • 32位整型torch.IntTensor
  • 64位整型torch.LongTensor
import torch

# 定義一個三行兩列給定元素的矩陣,並且顯示出矩陣的元素和大小
a = torch.Tensor([[2, 3], [4, 8], [7, 9]])
print('a is: {}'.format(a))
print('a size is: {}'.format(a.size()))
a is: tensor([[2., 3.],
        [4., 8.],
        [7., 9.]])
a size is: torch.Size([3, 2])

torch.Tensor默認的是torch.FloatTensor數據類型,也可以定義我們想要的數據類型,如下:

b = torch.LongTensor([[2, 3], [4, 8], [7, 9]])
print('b is: {}'.format(b))
b is: tensor([[2, 3],
        [4, 8],
        [7, 9]])

也可以創建一個全是0的空Tensor或者取一個正態分佈作爲隨機初始值:

c = torch.zeros((3, 2))
print('zero tensor: {}'.format(c))

d = torch.randn((3, 2))
print('normal random is:{}'.format(d))
zero tensor: tensor([[0., 0.],
        [0., 0.],
        [0., 0.]])
normal random is:tensor([[-0.7224,  0.6373],
        [-0.0982,  0.1669],
        [-1.4247,  0.5982]])

我們也可以像numpy一樣通過索引的方式取得其中的元素,同時也可以改變它的值,比如將a的第一行第二列改變爲100。

a[0, 1] = 100
print('changed a is: {}'.format(a))
changed a is: tensor([[  2., 100.],
        [  4.,   8.],
        [  7.,   9.]])

除此之外,還可以在Tensor與numpy.ndarray之間相互轉換:

import numpy as np

numpy_b = b.numpy()
print('conver to numpy is \n {}'.format(numpy_b))

e = np.array([[2, 3], [4, 5]])
torch_e = torch.from_numpy(e)
print('from numpy to torch.Tensor is \n {}'.format(torch_e))
f_torch_e = torch_e.float()
print('cahnge data type to float tensor \n {}'.format(f_torch_e))
conver to numpy is 
 [[2 3]
 [4 8]
 [7 9]]
from numpy to torch.Tensor is 
 tensor([[2, 3],
        [4, 5]], dtype=torch.int32)
cahnge data type to float tensor 
 tensor([[2., 3.],
        [4., 5.]])

通過簡單的b.numpy(),就能將b轉換爲numpy數據類型,同時使用 torch.from_numpy()就能將numpy轉換爲tensor,如果需要更改tensor的數據類型,只需要在轉換後的tensor後面加上你需要的類型,比如想將a的類型轉換成float,只需a.float()就可以了。

如果你的電腦支持GPU加速,還可以將Tensor放到GPU上。首先通過torch.cuda.is_available()判斷一下是否支持GPU,如果想把tensor a放到GPU上,只需a.cuda()就能夠將tensor a放到GPU上了。

if torch.cuda.is_available():
    a_cuda = a.cuda()
    print(a_cuda)
tensor([[  2., 100.],
        [  4.,   8.],
        [  7.,   9.]], device='cuda:0')

2.2 Variable(變量)

這個在numpy裏面就沒有了,是神經網絡計算圖裏特有的一個概念,就是Variable提供了自動求導的功能。

Variable和Tensor本質上沒有區別,不過Variable會被放入一個計算圖中,然後進行前向傳播,反向傳播,自動求導。首先Variable是在torch.autograd.Variable中,要將一個tensor變成Variable也非常簡單,比如想讓一個tensor a變成Variable,只需要 Variable(a)就可以了。

Variable有三個比較重要的組成屬性

  • data:通過data可以取出Variable裏面的tensor數值。
  • grad_fn:grad_fn表示的是得到這個Variable的操作,比如通過加減還是乘除來得到的。
  • grad:grad是這個Variabel的反向傳播梯度。

下面通過例子來具體說明一下:

from torch.autograd import Variable # torch中Variable模塊

# 創建變量
x = Variable(torch.Tensor([1]), requires_grad=True)
w = Variable(torch.Tensor([2]), requires_grad=True)
b = Variable(torch.Tensor([3]), requires_grad=True)

# 建立一個計算圖
y = w * x + b   

# 計算梯度
y.backward()

print(x.grad)
print(w.grad)
print(b.grad)
tensor([2.])
tensor([1.])
tensor([1.])

構建Variable,要注意得傳入一個參數requires_grad=True,這個參數表示是否對這個變量求梯度,默認的是False,也就是不對這個變量求梯度,這裏我們希望得到這些變量的梯度,所以需要傳入這個參數。

從上面的代碼中,我們注意到了一行y.backward(),這一行代碼就是所謂的自動求導,這其實等價y.backward(torch.FloatTensor([1])),只不過對於標量求導裏面的參數就可以不寫了,自動求導不需要你再去明確地寫明哪個函數對哪個函數求導,直接通過這行代碼就能對所有的需要梯度的變量進行求導,得到它們的梯度,然後通過x.grad可以得到x的梯度。

上面是標量的求導,同時也可以做矩陣求導,比如:

x = torch.randn(3)
x = Variable(x, requires_grad=True)

y = x * 2
print(y)

y.backward(torch.FloatTensor([1, 0.1, 0.01]))
print(x.grad)
tensor([ 2.3863,  1.3822, -2.5512], grad_fn=<MulBackward0>)
tensor([2.0000, 0.2000, 0.0200])

相當於給出了一個三維向量去做運算,這時候得到的結果y就是一個向量,這裏對這個向量求導就不能直接寫成 y.backward(),這樣程序是會報錯的。這個時候需要傳入參數聲明,比如y.backward(torch.FloatTensor([1, 1, 1])),這樣得到的結果就是它們每個分量的梯度,或者可以傳入y.backward(torch.FloatTensor([1, 0.1, 0.01])),這樣得到的梯度就是它們原本的梯度分別乘上1,0.1和0.01。

2.3 Dataset(數據集)

在處理任何機器學習問題之前都需要數據讀取,並進行預處理。PyTorch提供了很多工具使得數據的讀取和預處理變得很容易。接下來介紹 Dataset,TensorDataset,DataLoader,ImageFolder的簡單用法。

1. torch.utils.data.Dataset

它是代表這一數據的抽象類。你可以自己定義你的數據類,繼承和重寫這個抽象類,非常簡單,只需要定義__len__和__getitem__這兩個函數:

from torch.utils.data import Dataset
import pandas as pd

class myDataset(Dataset):
    def __init__(self, csv_file, txt_file, root_dir, other_file):
        self.csv_data = pd.read_csv(csv_file)
        with open(txt_file, 'r') as f:
            data_list = f.readlines()
        self.txt_data = data_list
        self.root_dir = root_dir
        
    def __len__(self):
        return len(self.csv_data)
    
    def __getitem__(self, idx):
        data = (self.csv_data[idx], self.txt_data[idx])
        return data

通過上面的方式,可以定義我們需要的數據類,可以通過迭代的方式來取得每一個數據,但是這樣很難實現取batch,shuffle或者是多線程去讀取數據。

2. torch.utils.data.TensorDataset

它繼承自Dataset,新版把之前的data_tensor和target_tensor去掉了,輸入變成了可變參數,也就是我們平常使用*args。

# 原版使用方法

train_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)

# 新版使用方法

train_dataset = Data.TensorDataset(x, y)
import torch
import torch.utils.data as Data

BATCH_SIZE = 5

x = torch.linspace(1, 10, 10)
y = torch.linspace(10, 1, 10)

torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(
    dataset=torch_dataset,
    batch_size=BATCH_SIZE,
    shuffle=True,
    num_workers=0,
)

for epoch in range(3):
    for step, (batch_x, batch_y) in enumerate(loader):
        print('Epoch: ', epoch, '| Step: ', step, '| batch x: ', batch_x.numpy(), '| batch y: ', batch_y.numpy())
Epoch:  0 | Step:  0 | batch x:  [6. 8. 9. 2. 5.] | batch y:  [5. 3. 2. 9. 6.]
Epoch:  0 | Step:  1 | batch x:  [10.  4.  7.  3.  1.] | batch y:  [ 1.  7.  4.  8. 10.]
Epoch:  1 | Step:  0 | batch x:  [ 5. 10.  2.  6.  7.] | batch y:  [6. 1. 9. 5. 4.]
Epoch:  1 | Step:  1 | batch x:  [9. 1. 8. 4. 3.] | batch y:  [ 2. 10.  3.  7.  8.]
Epoch:  2 | Step:  0 | batch x:  [ 5.  7.  1. 10.  9.] | batch y:  [ 6.  4. 10.  1.  2.]
Epoch:  2 | Step:  1 | batch x:  [6. 8. 2. 4. 3.] | batch y:  [5. 3. 9. 7. 8.]

3. torch.utils.data.DataLoader

PyTorch中提供了一個簡單的辦法來做這個事情,通過torch.utils.data.DataLoader來定義一個新的迭代器,如下:

from torch.utils.data import DataLoader

dataiter = DataLoader(myDataset,batch_size=32,shuffle=True,collate_fn=defaulf_collate)

其中的參數都很清楚,只有collate_fn是標識如何取樣本的,我們可以定義自己的函數來準確地實現想要的功能,默認的函數在一般情況下都是可以使用的。

(需要注意的是,Dataset類只相當於一個打包工具,包含了數據的地址。真正把數據讀入內存的過程是由Dataloader進行批迭代輸入的時候進行的。)

4. torchvision.datasets.ImageFolder

另外在torchvison這個包中還有一個更高級的有關於計算機視覺的數據讀取類:ImageFolder,主要功能是處理圖片,且要求圖片是下面這種存放形式:

root/dog/xxx.png

root/dog/xxy.png

root/dog/xxz.png

root/cat/123.png

root/cat/asd/png

root/cat/zxc.png

之後這樣來調用這個類:

from torchvision.datasets import ImageFolder

dset = ImageFolder(root='root_path', transform=None, loader=default_loader)

其中 root 需要是根目錄,在這個目錄下有幾個文件夾,每個文件夾表示一個類別:transform 和 target_transform 是圖片增強,後面我們會詳細介紹;loader是圖片讀取的辦法,因爲我們讀取的是圖片的名字,然後通過 loader 將圖片轉換成我們需要的圖片類型進入神經網絡。

2.4 nn.Module(模組)

在PyTorch裏面編寫神經網絡,所有的層結構和損失函數都來自於torch.nn,所有的模型構建都是從這個基類nn.Module繼承的,於是有了下面這個模板。

import torch.nn as nn

class net_name(nn.Module):
    def __init__(self, other_arguments):
        super(net_name, self).__init__()
        self.convl = nn.Conv2d(in_channels, out_channels, kernel_size)
        # 其他網路層
        
    def forward(self, x):
        x = self.convl(x)
        return x

這樣就建立了一個計算圖,並且這個結構可以複用多次,每次調用就相當於用該計算圖定義的相同參數做一次前向傳播,這得益於PyTorch的自動求導功能,所以我們不需要自己編寫反向傳播,而所有的網絡層都是由nn這個包得到的,比如線性層nn.Linear。

定義完模型之後,我們需要通過nn這個包來定義損失函數。常見的損失函數都已經定義在了nn中,比如均方誤差、多分類的交叉熵,以及二分類的交叉熵等等,調用這些已經定義好的損失函數也很簡單:

criterion = nn.CrossEntropyLoss()

loss = criterion(output, target)

criterion = nn.CrossEntropyLoss() loss = criterion(output, target)

2.5 torch.optim(優化)

在機器學習或者深度學習中,我們需要通過修改參數使得損失函數最小化(或最大化),優化算法就是一種調整模型參數更新的策略。優化算法分爲兩大類。

1. 一階優化算法

這種算法使用各個參數的梯度值來更新參數,最常用的一階優化算法是梯度下降。所謂的梯度就是導數的多變量表達式,函數的梯度形成了一個向量場,同時也是一個方向,這個方向上方向導數最大,且等於梯度。梯度下降的功能是通過尋找最小值,控制方差,更新模型參數,最終使模型收斂,網絡的參數更新公式是:

\Theta = \Theta - \eta \times \frac{\partial J(\Theta )}{\partial \Theta }

其中 \eta η 是學習率,\frac{\partial J(\Theta )}{\partial \Theta }是函數的梯度。

2. 二階優化算法

二階優化算法使用了二階導數(也叫做Hessian方法)來最小化或最大化損失函數,主要基於牛頓法,但是由於二階導數的計算成本很高,所以這種方法並沒有廣泛使用。torch.optim是一個實現各種優化算法的包,大多數常見的算法都能夠直接通過這個包來調用,比如隨機梯度下降,以及添加動量的隨機梯度下降,自適應學習率等。在調用的時候將需要優化的參數傳入,這些參數都必須是Variable,然後傳入一些基本的設定,比如學習率和動量等。

optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)

這樣我們就將模型的參數作爲需要更新的參數傳入優化器,設定學習率是0.01,動量是0.9隨機梯度下降,在優化之前需要先將梯度歸零,即optimizer.zeros(),然後通過loss.backward()反向傳播,自動求導得到每個參數的梯度,最後只需要optimizer.step()就可以通過梯度作一步參數更新。

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

torch.manual_seed(1)   

LR = 0.01
BATCH_SIZE = 32
EPOCH = 12

# 模擬數據
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)
y = x.pow(2) + 0.1*torch.normal(torch.zeros(*x.size()))

# 繪製數據集
plt.scatter(x.numpy(), y.numpy())
plt.show()

2.6 模型的保存和加載

在PyTorch裏面使用torch.save來保存模型的結構和參數,有兩種保存方式:

  1. 保存整個模型的結構信息和參數信息,保存的對象是模型 model;
  2. 保存模型的參數,保存的對象是模型的狀態model.state_dict()。

可以這樣保存,save的第一個參數是保存對象,第二個參數是保存路徑及名稱:

torch.save(model, './model.pth')

torch.save(model.state_dict(), './model_state.pth')

加載模型有兩種方式對應於保存模型的方式:

  1. 加載完整的模型結構和參數信息,使用 load_model=torch.load('model.pth'),在網絡較大的時候加載的時間比較長,同時存儲空間也比較大;
  2. 加載模型參數信息,需要先導入模型的結構,然後通過 model.load_state_dic(torch.load('model_state.pth'))來導入。

 

 

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