1. 程式人生 > >pytorch:基本操作

pytorch:基本操作

import torch
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt

pytorch中有兩種變數型別,一個是Tensor,一個是Variable。

  • Tensor: 就像ndarray一樣,一維Tensor叫Vector,二維Tensor叫Matrix,三維及以上稱為Tensor
  • Variable:是Tensor的一個wrapper,不僅儲存了值,而且儲存了這個值的creator,需要BP的網路都是Variable參與運算
# torch.Tensor(shape) 創建出一個未初始化的Tensor,但是還是可以打印出值的,這個應該是這塊記憶體之前的資料
x = torch.Tensor(2,3,4) # 這種方式創建出來的Tensor更多是用來接受其他資料的計算值的 x
(0 ,.,.) = 
  2.8404e+29  6.7111e+22  1.3556e-19  1.3563e-19
  1.3563e-19  1.3563e-19  1.3563e-19  4.4591e+30
  3.9173e-02  3.0386e+29  1.2134e+19  1.1625e+27

(1 ,.,.) = 
  4.5435e+30  7.1429e+31  1.8467e+20  7.1220e+28
  1.3556e-19  1.3563e-19  1.3563e-19  1.3563e-19
  1.3563e-19  1.3563e-19  1.3563e-19  1.3563e-19
[torch.FloatTensor of size 2x3x4]
x.size()
torch.Size([2, 3, 4])
a = torch.rand(2,3,4)
b = torch.rand(2,3,4)
# 使用Tensor()方法創建出來的Tensor用來接收計算結果,當然torch.add(..)也會返回計算結果的
_=torch.add(a,b,out=x)
x
(0 ,.,.) = 
  1.2776  1.5515  1.6725  1.5813
  1.8088  1.0150  0.4805  0.7575
  1.4111  1.1892  1.4516  1.4634

(1 ,.,.) = 
  1.5148  1.5504  1.2196  1.8959
  0.7730  1.4018  0.9021  1.6071
  1.4863  0.6581  0.8338  0.8901
[torch.FloatTensor of size 2x3x4]
"""所有帶 _ 的operation,都會更改呼叫物件的值,"""
a.add_(b)
#例如 a=1;b=2;a就是3了,沒有 _ 的operation就沒有這種效果,只會返回運算結果
(0 ,.,.) = 
  1.2776  1.5515  1.6725  1.5813
  1.8088  1.0150  0.4805  0.7575
  1.4111  1.1892  1.4516  1.4634

(1 ,.,.) = 
  1.5148  1.5504  1.2196  1.8959
  0.7730  1.4018  0.9021  1.6071
  1.4863  0.6581  0.8338  0.8901
[torch.FloatTensor of size 2x3x4]
a
(0 ,.,.) = 
  1.2776  1.5515  1.6725  1.5813
  1.8088  1.0150  0.4805  0.7575
  1.4111  1.1892  1.4516  1.4634

(1 ,.,.) = 
  1.5148  1.5504  1.2196  1.8959
  0.7730  1.4018  0.9021  1.6071
  1.4863  0.6581  0.8338  0.8901
[torch.FloatTensor of size 2x3x4]
torch.cuda.is_available()
False

自動求導

  • pytorch的自動求導工具包在torch.autograd中
from torch.autograd import Variable
x = torch.rand(5)
x = Variable(x, requires_grad = True)
y = x * 2
#如果y是scalar的話,那麼直接y.backward(),然後通過x.grad方式,就可以得到var的梯度
#如果y不是scalar,那麼只能通過傳參的方式給x指定梯度
grads = torch.FloatTensor([1,2,3,4,5])
y.backward(grads)
x.grad 
Variable containing:
  2
  4
  6
  8
 10
[torch.FloatTensor of size 5]

numpy array 和 torch tensor可以自由的轉換

import torch
import numpy as np
np_data = np.arange(6).reshape((2,3))
torch_data = torch.from_numpy(np_data)
tensor2array = torch_data.numpy()
print(
    '\nnumpy array:', np_data,  
    '\ntorch tensor:', torch_data,
    '\ntensor to array:', tensor2array,
)
numpy array: [[0 1 2]
 [3 4 5]] 
torch tensor: 
 0  1  2
 3  4  5
[torch.IntTensor of size 2x3]

tensor to array: [[0 1 2]
 [3 4 5]]
# abs 絕對值計算
data = [-1, -2, 1, 2]
tensor = torch.FloatTensor(data)  # 轉換成32位浮點 tensor
print(
    '\nabs',
    '\nnumpy: ', np.abs(data),          # [1 2 1 2]
    '\ntorch: ', torch.abs(tensor)      # [1 2 1 2]
)
abs 
numpy:  [1 2 1 2] 
torch:  
 1
 2
 1
 2
[torch.FloatTensor of size 4]
# sin   三角函式 sin
print(
    '\nsin',
    '\nnumpy: ', np.sin(data),      # [-0.84147098 -0.90929743  0.84147098  0.90929743]
    '\ntorch: ', torch.sin(tensor)  # [-0.8415 -0.9093  0.8415  0.9093]
)
sin 
numpy:  [-0.84147098 -0.90929743  0.84147098  0.90929743] 
torch:  
-0.8415
-0.9093
 0.8415
 0.9093
[torch.FloatTensor of size 4]
# mean  均值
print(
    '\nmean',
    '\nnumpy: ', np.mean(data),         # 0.0
    '\ntorch: ', torch.mean(tensor)     # 0.0
)
mean 
numpy:  0.0 
torch:  0.0

矩陣的乘法

# matrix multiplication 矩陣點乘
data = [[1,2], [3,4]]
tensor = torch.FloatTensor(data)  # 轉換成32位浮點 tensor
# correct method
print(
    '\nmatrix multiplication (matmul)',
    '\nnumpy: ', np.matmul(data, data),     # [[7, 10], [15, 22]]
    '\ntorch: ', torch.mm(tensor, tensor)   # [[7, 10], [15, 22]]
)

# !!!!  下面是錯誤的方法 !!!!
data = np.array(data)
print(
    '\nmatrix multiplication (dot)',
    '\nnumpy: ', data.dot(data),        # [[7, 10], [15, 22]] 在numpy 中可行
    '\ntorch: ', tensor.dot(tensor)     # torch 會轉換成 [1,2,3,4].dot([1,2,3,4) = 30.0
)
#新版本中(>=0.3.0), 關於 tensor.dot() 有了新的改變, 它只能針對於一維的陣列. 所以上面的有所改變.
matrix multiplication (matmul) 
numpy:  [[ 7 10]
 [15 22]] 
torch:  
  7  10
 15  22
[torch.FloatTensor of size 2x2]




---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

<ipython-input-44-e307c2bb5181> in <module>()
     14     '\nmatrix multiplication (dot)',
     15     '\nnumpy: ', data.dot(data),        # [[7, 10], [15, 22]] 在numpy 中可行
---> 16     '\ntorch: ', tensor.dot(tensor)     # torch 會轉換成 [1,2,3,4].dot([1,2,3,4) = 30.0
     17 )
     18 #新版本中(>=0.3.0), 關於 tensor.dot() 有了新的改變, 它只能針對於一維的陣列. 所以上面的有所改變.


RuntimeError: Expected argument self to have 1 dimension(s), but has 2 at c:\miniconda2\conda-bld\pytorch-cpu_1519449358620\work\torch\csrc\generic\TensorMethods.cpp:25700

變數

  • Variable:裝雞蛋的籃子,雞蛋數目不停變動
  • Tensor:雞蛋
import torch
# torch中Variable模組
from torch.autograd import Variable

#先生雞蛋
tensor = torch.FloatTensor([[1,2],[3,4]])
#把雞蛋放到籃子裡, requires_grad是要不要計算梯度
variable = Variable(tensor, requires_grad=True)

print(tensor,variable)
 1  2
 3  4
[torch.FloatTensor of size 2x2]
 Variable containing:
 1  2
 3  4
[torch.FloatTensor of size 2x2]

variable計算梯度

t_out = torch.mean(tensor*tensor) #x^2
v_out = torch.mean(variable*variable)
print(t_out,v_out)
7.5 Variable containing:
 7.5000
[torch.FloatTensor of size 1]
v_out.backward()    # 模擬 v_out 的誤差反向傳遞
# 下面兩步看不懂沒關係, 只要知道 Variable 是計算圖的一部分, 可以用來傳遞誤差就好.
# v_out = 1/4 * sum(variable*variable) 這是計算圖中的 v_out 計算步驟
# 針對於 v_out 的梯度就是, d(v_out)/d(variable) = 1/4*2*variable = variable/2
 # 初始 Variable 的梯度
print(variable.grad)  
 0.5000  1.0000
 1.5000  2.0000
[torch.FloatTensor of size 2x2]

獲取 Variable 裡面的資料

  • 直接print(variable)只會輸出 Variable 形式的資料, 在很多時候是用不了的
  • (比如想要用 plt 畫圖), 所以我們要轉換一下, 將它變成 tensor 形式.
#Variable形式
print(variable) 
#tensor形式
print(variable.data)
#numpy形式
print(variable.data.numpy())
Variable containing:
 1  2
 3  4
[torch.FloatTensor of size 2x2]


 1  2
 3  4
[torch.FloatTensor of size 2x2]

[[1. 2.]
 [3. 4.]]

Torch 中的激勵函式

import torch
import torch.nn.functional as F #啟用函式都在這
from torch.autograd import Variable

# 做一些假資料來觀看影象
x = torch.linspace(-5, 5, 200)  # x data (tensor), shape=(100, 1)
x = Variable(x)
x_np = x.data.numpy() # 

#幾種常用的啟用函式
y_relu = F.relu(x).data.numpy()
y_sigmoid =F.sigmoid(x).data.numpy()
y_tanh = F.tanh(x).data.numpy()
y_softplus = F.softplus(x).data.numpy()
# y_softmax = F.softmax(x)  softmax 比較特殊, 不能直接顯示, 不過他是關於概率的, 用於分類
plt.figure(1, figsize=(8, 6))
plt.subplot(221)
plt.plot(x_np, y_relu, c='red', label='relu')
plt.ylim((-1, 5))
plt.legend(loc='best')

plt.subplot(222)
plt.plot(x_np, y_sigmoid, c='red', label='sigmoid')
plt.ylim((-0.2, 1.2))
plt.legend(loc='best')

plt.subplot(223)
plt.plot(x_np, y_tanh, c='red', label='tanh')
plt.ylim((-1.2, 1.2))
plt.legend(loc='best')

plt.subplot(224)
plt.plot(x_np, y_softplus, c='red', label='softplus')
plt.ylim((-0.2, 6))
plt.legend(loc='best')
<matplotlib.legend.Legend at 0x164816efe48>

這裡寫圖片描述

關係擬合 (迴歸)

  • 我們建立一些假資料來模擬真實的情況. 比如一個一元二次函式: y = a * x^2 + b, 我們給 y 資料加上一點噪聲來更加真實的展示它.

  • 建立資料集

x = torch.unsqueeze(torch.linspace(-1,1,100),dim=1) #x data (tensor), shape=(100, 1)
y = x.pow(2) + 0.2*torch.rand(x.size())  # noisy y data (tensor), shape=(100, 1)

#用variable來修飾這些資料tensor
x, y = Variable(x),Variable(y)

#畫圖
plt.scatter(x.data.numpy(), y.data.numpy())
plt.show()

這裡寫圖片描述

  • 建立神經網路
class Net(torch.nn.Module): # 繼承 torch 的 Module
    def __init__(self,n_feature,n_hidden,n_output):
        super(Net,self).__init__() #繼承__init__功能
        #定義每層用什麼樣的形式
        self.hidden = torch.nn.Linear(n_feature, n_hidden) #隱藏層
        self.predict = torch.nn.Linear(n_hidden, n_output) #輸出層

    def forward(self, x): # Module中的forward功能
        #正向傳播輸入值,神經網路分析出輸出值
        x = F.relu(self.hidden(x))# 激勵函式(隱藏層的線性值)
        x = self.predict(x)# 輸出值
        return x
net = Net(n_feature=1,n_hidden=10,n_output=1)
print(net)
Net(
  (hidden): Linear(in_features=1, out_features=10, bias=True)
  (predict): Linear(in_features=10, out_features=1, bias=True)
)

訓練網路

#optimizer 是訓練工具

optimizer = torch.optim.SGD(net.parameters(),lr=0.5) #傳入net的所有引數,學習率
loss_func = torch.nn.MSELoss() #預測值和真實值的誤差計算公式(均方差)

for i in range(100):
    prediction = net(x) #餵給net訓練資料x,輸出預測值

    loss = loss_func(prediction, y) #計算兩者的誤差

    optimizer.zero_grad() #清空上一步的殘餘更新引數值
    loss.backward() #誤差反向傳播,計算引數更新值
    optimizer.step() # 將引數更新值施加到net的parameters上

視覺化訓練過程

plt.ion()
plt.show()
for t in range(100):
    prediction = net(x) #餵給net訓練資料x,輸出預測值

    loss = loss_func(prediction, y) #計算兩者的誤差

    optimizer.zero_grad() #清空上一步的殘餘更新引數值
    loss.backward() #誤差反向傳播,計算引數更新值
    optimizer.step() # 將引數更新值施加到net的parameters上

    if t % 5 == 0:
        # plt and show learning process
        plt.cla()
        plt.scatter(x.data.numpy(), y.data.numpy())
        plt.plot(x.data.numpy(),prediction.data.numpy(),'r-',lw=5)
        plt.text(0.5, 0, 'Loss=%.4f' % loss.data[0], fontdict={'size': 20, 'color':  'red'})
        plt.pause(0.1)

這裡寫圖片描述

分類

  • 建立資料集
# 假資料
n_data = torch.ones(100, 2)         # 資料的基本形態
n_data.numpy().shape
(100, 2)
x0 = torch.normal(2*n_data, 1)      # 型別0 x data (tensor), shape=(100, 2)
y0 = torch.zeros(100)               # 型別0 y data (tensor), shape=(100, 1)
x1 = torch.normal(-2*n_data, 1)     # 型別1 x data (tensor), shape=(100, 2)
y1 = torch.ones(100)                # 型別1 y data (tensor), shape=(100, 1)
# 注意 x, y 資料的資料形式是一定要像下面一樣 (torch.cat 是在合併資料)
x = torch.cat((x0, x1), 0).type(torch.FloatTensor)  # FloatTensor = 32-bit floating
y = torch.cat((y0, y1), ).type(torch.LongTensor)    # LongTensor = 64-bit integer
# torch 只能在 Variable 上訓練, 所以把它們變成 Variable
x, y = Variable(x), Variable(y)
# 畫圖
plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=y.data.numpy(), s=100, lw=0, cmap='RdYlGn')
plt.show()

這裡寫圖片描述

建立神經網路

  • 建立一個神經網路我們可以直接運用 torch 中的體系. 先定義所有的層屬性(init()),
  • 然後再一層層搭建(forward(x))層於層的關係連結. 這個和我們在前面 regression 的時候的神經網路基本沒差.
import torch
import torch.nn.functional as F

class Net(torch.nn.Module):
    def __init__(self,n_feature, n_hidden,n_output):
        super(Net,self).__init__() #繼承__init__功能
        self.hidden = torch.nn.Linear(n_feature, n_hidden) #隱藏層線性輸出
        self.out = torch.nn.Linear(n_hidden,n_output)

    def forward(self,x):
        x = F.relu(self.hidden(x))
        x = self.out(x)
        return x


net = Net(n_feature=2, n_hidden=10, n_output=2) #幾個類別就幾個output

print(net)
Net(
  (hidden): Linear(in_features=2, out_features=10, bias=True)
  (out): Linear(in_features=10, out_features=2, bias=True)
)

訓練網路

optimizer = torch.optim.SGD(net.parameters(), lr=0.02)
# 算誤差的時候, 注意真實值!不是! one-hot 形式的, 而是1D Tensor, (batch,)
# 但是預測值是2D tensor (batch, n_classes)
loss_func = torch.nn.CrossEntropyLoss()

for t in range(100):
    out =net(x)

    loss = loss_func(out, y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step() #將引數更新值施加到net的parameter上
    # 接著上面來
    if t % 2 == 0:
        plt.cla()
        # 過了一道 softmax 的激勵函式後的最大概率才是預測值
        prediction = torch.max(F.softmax(out), 1)[1]
        pred_y = prediction.data.numpy().squeeze()
        target_y = y.data.numpy()
        plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=pred_y, s=100, lw=0, cmap='RdYlGn')
        accuracy = sum(pred_y == target_y)/200  # 預測中有多少和真實值一樣
        plt.text(1.5, -4, 'Accuracy=%.2f' % accuracy, fontdict={'size': 20, 'color':  'red'})
        plt.pause(0.1)
plt.ioff()  # 停止畫圖
plt.show()
C:\Users\dell\Anaconda3\lib\site-packages\ipykernel_launcher.py:18: UserWarning: Implicit dimension choice for softmax has been deprecated. Change the call to include dim=X as an argument.

快速搭建

#我們用 class 繼承了一個 torch 中的神經網路結構, 然後對其進行了修改, 不過還有更快的一招, 
#用一句話就概括了上面所有的內容!
net2 = torch.nn.Sequential(
    torch.nn.Linear(1, 10),
    torch.nn.ReLU(),
    torch.nn.Linear(10, 1)
)
print(net2)
Sequential(
  (0): Linear(in_features=1, out_features=10, bias=True)
  (1): ReLU()
  (2): Linear(in_features=10, out_features=1, bias=True)
)

儲存

torch.manual_seed(1) #reproducible

#假資料
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1) # x data(tensor)
y = x.pow(2) + 0.2 * torch.rand(x.size()) #noisy y data(tensor)
x, y = Variable(x, requires_grad=False),Variable(y,requires_grad=False)

def save():
    net1 = torch.nn.Sequential(
        torch.nn.Linear(1, 10),
        torch.nn.ReLU(),
        torch.nn.Linear(10, 1)
    )
    optimizer = torch.optim.SGD(net1.parameters(),lr=0.5)
    loss_func = torch.nn.MSELoss()

    #訓練
    for t in range(100):
        prediction = net1(x)
        loss = loss_func(prediction, y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    torch.save(net1, 'net.pkl')  # 儲存整個網路
    torch.save(net1.state_dict(), 'net_params.pkl')   # 只儲存網路中的引數 (速度快, 佔記憶體少)
save()
torch.unsqueeze?

提取網路

def restore_net():
    #restore entire net1 to net2
    net2 = torch.load('net.pkl')
    prediction = net2(x)

只提取網路引數:這種方式將會提取所有的引數, 然後再放到你的新建網路中.

def restore_params():
    #新建 net3
    net3 = torch.nn.Sequential(
        torch.nn.Linear(1, 10),
        torch.nn.ReLU(),
        torch.nn.Linear(10, 1)
    )
    """得建立一個和之前訓練相同的model"""
    #將儲存的引數複製到net3
    net3.load_state_dict(torch.load('net_params.pkl'))
    prediction = net3(x)

批訓練

import torch
import torch.utils.data as Data
torch.manual_seed(1) #reproducible
BATCH_SIZE = 5 #批訓練的資料個數

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

#先轉換torch能識別的Dataset
torch_dataset = Data.TensorDataset(data_tensor=x,target_tensor=y)

#把dataset放入DataLoader
loader = Data.DataLoader(
    dataset = torch_dataset,#torch TensorDateset format
    batch_size = BATCH_SIZE, #mini batch size
    shuffle = True, #是否打亂資料
    num_workers = 2 , # 多執行緒來讀資料
)

for epoch in range(3): #訓練所有!整套資料3次
    for step, (batch_x, batch_y) in enumerate(loader): #每次loader釋放一小批資料用來學習
        # 假設這裡就是你訓練的地方...
        # 打出來一些資料
        print('Epoch: ', epoch, '| Step: ', step, '| batch x: ',
              batch_x.numpy(), '| batch y: ', batch_y.numpy())
Epoch:  0 | Step:  0 | batch x:  [ 4.  3.  6.  9. 10.] | batch y:  [ 4.  3.  6.  9. 10.]
Epoch:  0 | Step:  1 | batch x:  [8. 7. 2. 1. 5.] | batch y:  [8. 7. 2. 1. 5.]
Epoch:  1 | Step:  0 | batch x:  [1. 5. 4. 8. 9.] | batch y:  [1. 5. 4. 8. 9.]
Epoch:  1 | Step:  1 | batch x:  [ 6.  3. 10.  2.  7.] | batch y:  [ 6.  3. 10.  2.  7.]
Epoch:  2 | Step:  0 | batch x:  [ 5. 10.  8.  9.  7.] | batch y:  [ 5. 10.  8.  9.  7.]
Epoch:  2 | Step:  1 | batch x:  [1. 2. 3. 6. 4.] | batch y:  [1. 2. 3. 6. 4.]

加速神經網路訓練 (Speed Up Training)

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)    # reproducible

LR = 0.01
BATCH_SIZE = 32
EPOCH = 12

# fake dataset
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)
y = x.pow(2) + 0.1*torch.normal(torch.zeros(*x.size()))

# plot dataset
plt.scatter(x.numpy(), y.numpy())
plt.show()

# 使用上節內容提到的 data loader
torch_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2,)
#每個優化器優化一個神經網路
# 預設的 network 形式
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(1, 20)   # hidden layer
        self.predict = torch.nn.Linear(20, 1)   # output layer

    def forward(self, x):
        x = F.relu(self.hidden(x))      # activation function for hidden layer
        x = self.predict(x)             # linear output
        return x

# 為每個優化器建立一個 net
net_SGD         = Net()
net_Momentum    = Net()
net_RMSprop     = Net()
net_Adam        = Net()
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]
# different optimizers
opt_SGD         = torch.optim.SGD(net_SGD.parameters(), lr=LR)
opt_Momentum    = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.8)
opt_RMSprop     = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
opt_Adam        = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]

loss_func = torch.nn.MSELoss()
losses_his = [[], [], [], []]   # 記錄 training 時不同神經網路的 loss

for epoch in range(EPOCH):
    print('Epoch: ', epoch)
    for step, (batch_x, batch_y) in enumerate(loader):
        b_x = Variable(batch_x)  # 務必要用 Variable 包一下
        b_y = Variable(batch_y)

        # 對每個優化器, 優化屬於他的神經網路
        for net, opt, l_his in zip(nets, optimizers, losses_his):
            output = net(b_x)              # get output for every net
            loss = loss_func(output, b_y)  # compute loss for every net
            opt.zero_grad()                # clear gradients for next train
            loss.backward()                # backpropagation, compute gradients
            opt.step()                     # apply gradients
            l_his.append(loss.data[0])     # loss recoder
Epoch:  0
Epoch:  1
Epoch:  2
Epoch:  3
Epoch:  4
Epoch:  5
Epoch:  6
Epoch:  7
Epoch:  8
Epoch:  9
Epoch:  10
Epoch:  11

參考:莫煩python