動手學深度學習基礎
第一次作業:深度學習基礎
1. 影象處理基本練習
展示不同通道上的影象
# 展示三通道影象
plt.subplot(121)
plt.imshow(colony[:,:,:])
plt.title('3-channel image')
plt.axis('off')
# 展示只有一個通道的影象
plt.subplot(122)
plt.imshow(colony[:,:,0])
plt.title('1-channel image')
plt.axis('off');
Canny 運算元用於邊緣檢測
from skimage.feature import canny from scipy import ndimage as ndi img_edges = canny(img) img_filled = ndi.binary_fill_holes(img_edges) # Plot plt.figure(figsize=(18, 12)) plt.subplot(121) plt.imshow(img_edges, 'gray') plt.subplot(122) plt.imshow(img_filled, 'gray')
2. Pytorch基礎
2.1 基本資料Tensor
Tensor是Pytorch中的基本操作物件,是儲存和變換資料的主要工具。與NumPy的ndarrays非常類似,不過Tensor還支援GPU運算。Tensor有多種建立方法,如基礎的Tensor(),還有多種與NumPy十分類似的方法,如ones()、eye()、zeros()和randn()等,其中PyTorch中預設的資料型別是 torch.FloatTensor。
a = torch.Tensor(2, 2) # Tensor()與tensor的區別在於Tensor返回的資料型別是預設的,而tensor型別根據資料進行推斷 # 引數可以為Python的list、NumPy的ndarray x = torch.tensor((2, 3, 4, 9)) # 使用ones()函式,所有元素均為1 x2 = torch.ones((2, 3)) # 通過現有的Tensor來建立,此方法會預設重用輸入Tensor的一些屬性,例如資料型別,除非自定義資料型別 # 使用dtype來定義資料型別 x3 = torch.zeros_like(x2, dtype=torch.long) # 使用randn()函式,生成標準分佈的隨機數矩陣 x4 = torch.randn(3, 4) # 使用arange(start, end, step)函式,表示從start到end,間距為step,一維向量 v = torch.arange(1,4) # 使用linspace(start, end, steps)函式,表示從start到end,一共steps份,一維向量 x5 = torch.linspace(10, 20, 15) # 檢視Tensor中總的元素個數 a.numel() # 通過shape或者size()來獲取Tensor的形狀或維度 print(x.size()) print(x.shape)
2.2 Tensor的各種操作
索引
可以使用類似NumPy的索引操作來訪問Tensor
的一部分,但是索引出來的結果與原資料共享記憶體,即修改一個,另一個會跟著修改。
Input: m = torch.tensor([[2, 3, 4],[5, 6, 7]]) print(m[:,1]) print(m[0]) print(m[0][1]) Output: tensor([3, 6]) tensor([2, 3, 4]) tensor(3) # 選擇符合條件的元素並返回 Input: a = torch.tensor([[1, 2], [3, 4]]) print(a > 2) ind = a > 2 print(a[ind]) OUtput: tensor([[False, False], [ True, True]]) tensor([3, 4])
改變形狀
變形操作則是指改變Tensor的維度,有四類不同的基本操作。
變形操作 | 功能 |
---|---|
view()、resize()、reshape() | 調整形狀同時元素總數相同 |
transpose()、permute() | 各維度之間的變換 |
squeeze()、unsqueeze() | 處理size為1的維度 |
expand()、expand_as() | 複製元素來擴充套件維度 |
# 將資料的第二維度與第三維度進行變換
Input:
a = torch.randn((2, 3, 2))
print(a)
res = a.transpose(1, 2)
print(res)
OUtput
tensor([[[-1.6705, -1.0614],
[ 0.9338, 1.2305],
[ 0.3439, -0.6650]],
[[-0.9470, -0.8022],
[ 0.8864, -0.6687],
[-0.8743, 2.5200]]])
tensor([[[-1.6705, 0.9338, 0.3439],
[-1.0614, 1.2305, -0.6650]],
[[-0.9470, 0.8864, -0.8743],
[-0.8022, -0.6687, 2.5200]]])
歸併運算
歸併運算可以沿著某一維度進行指定操作或者逐個元素進行操作。
# 按照第0維即按行選取最大值,將每一列的最大值選取出來
# 返回符合操作的數值及其下標
Input:
test = torch.tensor([[1, 8, 3],[4, 5,6]])
num, ind = torch.max(test, 0)
print(num,ind)
Output:
tensor([4, 8, 6]) tensor([1, 0, 1])
資料型別轉換
- GPU上Tensor資料與CPU上Tensor資料互相轉換,可以通過
data.cpu()
、data.cuda()
或data.to(device)
實現。
# 將CPU上的Tensor資料轉換為GPU 方法一
Input:
device = torch.device('cuda:0')
t1 = torch.tensor([1, 2, 3])
print(t1.type())
t1 = t1.to(device)
print(t1.type())
Output:
torch.LongTensor
torch.cuda.LongTensor
# 方法二
t1 = t1.cuda()
# 將GPU上的Tensor資料轉換為CPU
t2 = t1.cpu()
- Tensor與Numpy Array之間的轉換,CPU上Tensor資料可以通過
data.numpy()
進行轉換,但是CPU上Tensor資料要先轉換為CPU後在進行Numpy型別轉換。
# 將GPU上Tensor資料轉換為Numpy型別
t2 = t1.cpu().numpy()
# 將Numpy型別轉換為Tensor資料
Input:
t3 = torch.from_numpy(t2)
t3.type()
Output:
torch.LongTensor
- Tensor的基本型別t轉換,可以通過
torch.tensor(data, dtype=)
來實現
3. 螺旋資料分類
3.1 載入相應的包
!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
import torch
import random
import math
from torch import nn, optim
from IPython import display
from plot_lib import plot_data, plot_model, set_default
3.2 生成資料集
由於PyTorch為資料在GPU上執行提供了便利的操作。因此可以在GPU進行運算,torch.cuda.is_available()
可以用來判斷當前環境下GPU是否可用,使用tensor.to(device)
可以將資料轉移到GPU上。
# 將所有最開始讀取資料時的tensor變數copy一份到device所指定的GPU上去,之後的運算都在GPU上進行。
# 這句話需要寫的次數等於需要儲存GPU上的tensor變數的個數;
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device',device)
seed=1234
random.seed(seed)
torch.manual_seed(seed)
構造一個簡單的訓練資料集,訓練資料集樣本數為1000,特徵數為2,樣本標籤數為3
N = 1000 # 每類樣本的數量
D = 2 # 每個樣本的特徵維度
C = 3 # 樣本的類別
H = 100 # 神經網路裡隱層單元的數量
X = torch.zeros(N*C, D).to(device)
Y = torch.zeros(N*C, dtype=torch.long).to(device)
for c in range(C):
index = 0
t = torch.linspace(0, 1, N) # 在[0,1]間均勻的取10000個數,賦給t
# 下面的程式碼不用理解太多,總之是根據公式計算出三類樣本(可以構成螺旋形)
# torch.randn(N) 是得到 N 個均值為0,方差為 1 的一組隨機數,注意要和 rand 區分開
inner_var = torch.linspace( (2*math.pi/C)*c, (2*math.pi/C)*(2+c), N) + torch.randn(N) * 0.2
# 每個樣本的(x,y)座標都儲存在 X 裡
# Y 裡儲存的是樣本的類別,分別為 [0, 1, 2]
for ix in range(N * c, N * (c + 1)):
X[ix] = t[index] * torch.FloatTensor((math.sin(inner_var[index]), math.cos(inner_var[index])))
Y[ix] = c
index += 1
print("Shapes:")
print("X:", X.size())
print("Y:", Y.size())
資料如圖所示:
3.3 定義模型
torch.nn.Sequential
是一個Sequential容器,模組將按照在傳入構造器的順序依次被新增到計算圖中執行,當模型中只是簡單的前饋網路時,即上一層的輸出直接作為下一層的輸入,可以採用nn.Sequential()模組來快速搭建模型。
nn.optim
中包含了各種常見的優化演算法,包括隨機梯度下降演算法SGD、Adam等。SGD容易陷入區域性最優難以跳出,因此相對於SGD,Adam通常會有更好的效果。
# 網路模組
# # 每一個線性模型都包含 weight 和 bias
model = nn.Sequential(
nn.Linear(D, H),
nn.Linear(H, C)
)
model.to(device)
# 損失函式的定義,為交叉熵(cross entropy loss)損失函式
loss_dev = torch.nn.CrossEntropyLoss()
# 梯度下降的學習率
learnrate = 1e-3
decay = 1e-5
# 優化引數定義,為隨機梯度下降(stochastic gradient descent)
optimize = torch.optim.SGD(model.parameters(), lr=learnrate, weight_decay=decay)
3.4 模型訓練
for t in range(1000):
# 模型的預測輸出
y_hat = model(X)
# 求得預測輸出的結果值及其下標
score, predict = torch.max(y_hat, 1)
# 模型預測的準確率和損失
acc = (Y==predict).sum().float()/len(Y)
loss = loss_dev(y_hat, Y)
display.clear_output(wait=True)
if t % 100 == 0:
print('epoch:%d loss:%.6f accuracy:%.3f'%(t, loss.item(), acc))
# 反向傳播前把梯度置 0
optimize.zero_grad()
# # 反向傳播優化
loss.backward()
# 通過step函式來迭代模型引數
optimize.step()
最終執行結果為:
epoch:900 loss:0.772457 accuracy:0.502
模型的結構為
Sequential(
(0): Linear(in_features=2, out_features=100, bias=True)
(1): Linear(in_features=100, out_features=3, bias=True)
)
我們從model獲得需要的層,並訪問其權重(weight
)和偏差(bias
)
dense = model[0]
print(dense.weight.size())
print(dense.bias)
3.5 改變模型結構
由於之前的模型訓練出來的結果不太理想,所以這次改變了模型的結構,增加了ReLu(),ReLu啟用函式將模型從線性變成非線性,並將優化演算法變為了Adam。
model = nn.Sequential(
nn.Linear(D, H),
nn.ReLU(),
nn.Linear(H, C)
)
model.to(device)
loss_dev = torch.nn.CrossEntropyLoss()
optimize = torch.optim.Adam(model.parameters(), lr=learnrate, weight_decay=decay)
最終執行結果為:
epoch:900 loss:0.220317 accuracy:0.925
模型的結構為
Sequential(
(0): Linear(in_features=2, out_features=100, bias=True)
(1): ReLU()
(2): Linear(in_features=100, out_features=3, bias=True)
)
4. 迴歸分析
4.1 載入相應的包
import torch
import math
import random
from torch import nn, optim
from matplotlib import pyplot as plt
from IPython import display
from plot_lib import plot_data, plot_model, set_default
4.2 生成資料集
N = 1000 # 每類樣本的數量
D = 1 # 每個樣本的特徵維度
C = 1 # 類別數
H = 100 # 隱層的神經元數量
X = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1).to(device)
y = X.pow(3) + 0.3 * torch.rand(X.size()).to(device)
展示資料:
plt.figure(figsize=(6, 6))
plt.scatter(X.cpu().numpy(), y.cpu().numpy())
plt.axis('equal')
plt.show()
4.3 定義模型與訓練
模型與訓練過程與螺旋資料分析一樣
訓練結果:
epoch:900 loss:0.037766
畫圖展示訓練模型結果:
plt.figure(figsize=(6, 6))
plt.scatter(X.cpu().numpy(), y.cpu().numpy())
plt.plot(X.cpu().numpy(), y_hat.data.cpu().numpy(), 'r')
plt.axis('equal')
plt.show()
4.5 改變模型結構
第一種改變
在模型中加入了ReLu(),並將優化演算法變為了Adam。
model = nn.Sequential(
nn.Linear(D, H),
nn.ReLU(),
nn.Linear(H, C)
)
model.to(device)
訓練結果:
epoch:900 loss:0.006540
第二種改變
在模型中加入了Tanh(),並將優化演算法變為了Adam。
model = nn.Sequential(
nn.Linear(D, H),
nn.Tanh(),
nn.Linear(H, C)
)
model.to(device)
訓練結果:
epoch:900 loss:0.009457
畫圖展示兩種訓練模型結果: