柚子快報邀請碼778899分享:人工智能 Pytorch的學習
柚子快報邀請碼778899分享:人工智能 Pytorch的學習
1.基本數(shù)據(jù):Tensor
Tensor,即張量,是PyTorch中的基本操作對象,可以看做是包含單一數(shù)據(jù)類型元素的多維矩陣。從使用角度來看,Tensor與NumPy的ndarrays非常類似,相互之間也可以自由轉(zhuǎn)換,只不過Tensor還支持GPU的加速【重點】。
1.1 Tensor的創(chuàng)建
1.2 tensor的操作:
第一部分: 分為FloatTensor,IntTensor,randn,range,zeros,ones,empty
import torch
a = torch.FloatTensor(2,3) # 1.生成兩行三列的向量,3代表維度
b = torch.FloatTensor([2,3,4,5]) # 2.生成float類型的四個維度向量
a,b
(tensor([[1.0561e-38, 1.0102e-38, 9.6429e-39],
[8.4490e-39, 9.6429e-39, 9.1837e-39]]),
tensor([2., 3., 4., 5.]))
torch.IntTensor: 用于生成數(shù)據(jù)類型為整型的Tensor,傳遞給傳遞給torch.IntTensor的參數(shù)可以是列表,也可以是一個維度值。
import torch
a = torch.IntTensor(2,3)
b = torch.IntTensor([2,3,4,5])
a,b
torch.randn: 用于生成數(shù)據(jù)類型為浮點數(shù)且維度指定的隨機Tensor,隨機生成的浮點數(shù)的取值滿足均值為0,方差為1的正態(tài)分布。
import torch
a = torch.randn(2,3)
a
tensor([[-0.0067, -0.0707, -0.6682],
[ 0.8141, 1.1436, 0.5963]])
torch.zeros: torch.zeros用于生成數(shù)據(jù)類型為浮點型且維度指定的Tensor
import torch
a = torch.zeros(2,3)
a
tensor([[0., 0., 0.],
[0., 0., 0.]])
torch.abs 、torch.add、torch.clamp、torch.mm()計算: torch.clamp()的作用:對tensor中的數(shù)據(jù)進行裁剪。 torch.mm()的作用:將參數(shù)傳遞到torch.mm后返回輸入?yún)?shù)的求積結(jié)果作為輸出,不過這個求積的方式和之前的torch.mul運算方式不太一樣,torch.mm 運用矩陣之間的乘法規(guī)則進行計算 ,所以被傳入的參數(shù)會被當作矩陣進行處理,參數(shù)的維度自然也要滿足矩陣乘法的前提條件,即前一個矩陣的行數(shù)必須和后一個矩陣列數(shù)相等
# 這段代碼的目的是展示不同類型的張量的使用方法。下面是加上中文注釋后的代碼:
import torch
# 1.demo1
a = torch.FloatTensor(2, 3) # 創(chuàng)建一個大小為2x3的浮點型張量
b = torch.FloatTensor([2, 3, 4, 5]) # 創(chuàng)建一個大小為4的浮點型向量
c = torch.Tensor(2, 3) # 創(chuàng)建一個大小為2x3的任意類型張量
# print(a, b) # 輸出a和b
# print(c)
# 2.demo2
a = torch.IntTensor(2, 3)
b = torch.IntTensor([2, 3, 4, 5])
# print(a, b)
# 3.demo3:生成數(shù)據(jù)類型為浮點數(shù)且維度指定的隨機Tensor,取值滿足均值為0,方差為1的正態(tài)分布
a = torch.randn(2, 3)
b = torch.range(1, 20, 2) # torch.range用于生成數(shù)據(jù)類型為浮點型且起始范圍和結(jié)束范圍的Tensor
# print(a)
# print(b)
# 4.demo4: Tensor的計算
a = torch.randn(2, 3)
b = torch.abs(a) # 絕對值的輸出
# print(a)
# print(b)
a = torch.randn(2, 3)
b = torch.randn(2, 3)
c = torch.add(a, b)
# print(c)
d = torch.randn(2, 3)
e = torch.add(d, 10) # 增加標量
# print(e)
a = torch.randn(2, 3)
# print(a)
b = torch.clamp(a, -0.1, 0.1) # 卡位操作:限制張量里面每個值都在-0.1到0.1之間,如果<-0.1則設置未-0.1,如果>0.1,則設置未0.1
# print(b)
# 4.demo4:矩陣計算-torch.mm() (2x3)*(3x2)=2x2,如果是矩陣和向量之間的計算,舊用torch.mv()
a = torch.randn(2, 3)
print(a)
b = torch.rand(2, 3)
print(b)
c = torch.mm(a, b.T)
print(c)
torch.mv(): 將參數(shù)傳遞到torch.mv后返回輸入?yún)?shù)的求積結(jié)果作為輸出,torch.mv運用矩陣與向量之間的乘法規(guī)則進行計算,被傳入的第1個參數(shù)代表矩陣,第2個參數(shù)代表向量,循序不能顛倒。
a = torch.randn(2,3)
a
#我們得到a為:
#tensor([[ 1.0909, -1.1679, 0.3161],
# [-0.8952, -2.1351, -0.9667]])
b = torch.randn(3)
b
#我們得到b為:
#tensor([-1.4689, 1.6197, 0.7209])
#用產(chǎn)生的a,b進行矩陣乘法操作:
c = torch.mv(a,b)
c
#tensor([-3.2663, -2.8402])
2.神經(jīng)網(wǎng)絡工具箱torch.nn
torch.autograd 庫雖然實現(xiàn)了自動求導與梯度反向傳播。 torch.nn: 該接口構(gòu)建于 Autograd 之上,提供了網(wǎng)絡模組、優(yōu)化器和初始化策略等一系列功能 nn.Module類: nn.Module是PyTorch提供的神經(jīng)網(wǎng)絡類,并在類中實現(xiàn)了網(wǎng)絡各層的定義及前向計算與反向傳播機制。在實際使用時,如果想要實現(xiàn)某個神經(jīng)網(wǎng)絡,只需繼承nn.Module,在初始化中定義模型結(jié)構(gòu)與參數(shù),在函數(shù)**forward()**中編寫網(wǎng)絡前向過程即可。
1.nn.Parameter函數(shù)
2.forward()函數(shù)與反向傳播
3.多個Module的嵌套
4.nn.Module與nn.functional庫
5.nn.Sequential()模塊
import torch.nn as nn
import torch
class MLP(nn.Module):
"""
實現(xiàn)一個具有三層隱藏層的MLP模型。
參數(shù):
- in_dim (int): 輸入維度
- hid_dim1 (int): 第一層隱藏層的維度
- hid_dim2 (int): 第二層隱藏層的維度
- out_dim (int): 輸出維度
返回:
- 無
"""
def __init__(self, in_dim, hid_dim1, hid_dim2, out_dim):
super(MLP, self).__init__() # 1.調(diào)用父類 nn.Module 的初始化方法
# 2.定義一個包含三層線性變換和三層ReLU激活函數(shù)的序列模型
self.layers = nn.Sequential(
nn.Linear(in_dim, hid_dim1), # 3.第一層線性變換
nn.ReLU(), # 第一層激活函數(shù)
nn.Linear(hid_dim1, hid_dim2), # 第二層線性變換
nn.ReLU(), # 第二層激活函數(shù)
nn.Linear(hid_dim2, out_dim) # 第三層線性變換
# 通常在最后一層線性變換后不會再加 ReLU,因為這會影響輸出的范圍
)
def forward(self, x):
"""
定義網(wǎng)絡的前向傳播過程。
參數(shù):
- x (Tensor): 輸入的特征張量
返回:
- x (Tensor): 經(jīng)過模型處理后的輸出張量
"""
x = self.layers(x) # 輸入x依次經(jīng)過self.layers中定義的各層
return x
# 定義MLP模型輸入輸出的維度
in_dim = 10
hid_dim1 = 20
hid_dim2 = 15
out_dim = 5
# 實例化MLP模型
model = MLP(in_dim, hid_dim1, hid_dim2, out_dim)
# 得到輸入張量x,維度應該和模型model定義的輸入張量維度一樣的
x = torch.randn(32, in_dim)
# 將輸入張量 x 傳入模型得到輸出的維度,本質(zhì)就是傳遞給模型的 forward 方法(或者直接傳遞給模型實例,因為 forward 方法會被自動調(diào)用)
output = model(x)
print(output.shape)
3.搭建簡易的神經(jīng)網(wǎng)絡:
3.1下面我們用torch搭一個簡易神經(jīng)網(wǎng)絡: 1、【torch.autograd】包的主要功能就是完成神經(jīng)網(wǎng)絡后向傳播中的鏈式求導,手動去寫這些求導程序會導致重復造輪子的現(xiàn)象。 2、【自動梯度的功能過程大致為】:先通過輸入的Tensor數(shù)據(jù)類型的變量在神經(jīng)網(wǎng)絡的前向傳播過程中生成一張計算圖,然后根據(jù)這個計算圖和輸出結(jié)果精確計算出每一個參數(shù)需要更新的梯度,并通過完成后向傳播完成對參數(shù)的梯度更新。
import torch
from torch.autograd import Variable
batch_n = 100 # 一個批次輸入數(shù)據(jù)的數(shù)量
hidden_layer = 100
input_data = 1000 # 每個數(shù)據(jù)的特征為1000
output_data = 10
x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)
# 用Variable對Tensor數(shù)據(jù)類型變量進行封裝的操作。requires_grad如果是False,表示該變量在進行自動梯度計算的過程中不會保留梯度值。
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad=True)
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad=True)
# 學習率和迭代次數(shù)
epoch_n = 50
lr = 1e-6
for epoch in range(epoch_n):
h1 = x.mm(w1) # (100,1000)*(1000,100)-->100*100
print(h1.shape)
h1 = h1.clamp(min=0)
y_pred = h1.mm(w2)
# y_pred = x.mm(w1).clamp(min=0).mm(w2)
loss = (y_pred - y).pow(2).sum()
print("epoch:{},loss:{:.4f}".format(epoch, loss.data))
# grad_y_pred = 2*(y_pred-y)
# grad_w2 = h1.t().mm(grad_y_pred)
loss.backward() # 后向傳播
# grad_h = grad_y_pred.clone()
# grad_h = grad_h.mm(w2.t())
# grad_h.clamp_(min=0)#將小于0的值全部賦值為0,相當于sigmoid
# grad_w1 = x.t().mm(grad_h)
w1.data -= lr * w1.grad.data
w2.data -= lr * w2.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
# w1 = w1 -lr*grad_w1
# w2 = w2 -lr*grad_w2
3.2重寫前向傳播函數(shù): 該代碼并沒有繼承父類的model,初始化直接利用父類的model,關鍵點在于重寫了前向傳播算法
import torch
from torch.autograd import Variable
batch_n = 64#一個批次輸入數(shù)據(jù)的數(shù)量
hidden_layer = 100
input_data = 1000#每個數(shù)據(jù)的特征為1000
output_data = 10
class Model(torch.nn.Module):#完成類繼承的操作
def __init__(self):
super(Model,self).__init__()#類的初始化
def forward(self,input,w1,w2):
x = torch.mm(input,w1)
x = torch.clamp(x,min = 0)
x = torch.mm(x,w2)
return x
def backward(self):
pass
model = Model()
x = Variable(torch.randn(batch_n,input_data),requires_grad=False)
y = Variable(torch.randn(batch_n,output_data),requires_grad=False)
#用Variable對Tensor數(shù)據(jù)類型變量進行封裝的操作。requires_grad如果是F,表示該變量在進行自動梯度計算的過程中不會保留梯度值。
w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad=True)
w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad=True)
epoch_n=30
for epoch in range(epoch_n):
y_pred = model(x,w1,w2)
loss = (y_pred-y).pow(2).sum()
print("epoch:{},loss:{:.4f}".format(epoch,loss.data))
loss.backward()
w1.data -= lr*w1.grad.data
w2.data -= lr*w2.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
3.3 利用torch.nn.Sequential類(一種序列容器)進行神經(jīng)網(wǎng)絡的搭建: Sequential 類是torch.nn中的一種序列容器,通過在容器中嵌套各種實現(xiàn)神經(jīng)網(wǎng)絡模型的搭建,最主要的是,參數(shù)會按照我們定義好的序列自動傳遞下去。 Linear(input,hidden)的作用就是對輸入的數(shù)據(jù)進行線性變換,以下的權重和偏置會在訓練過程中通過反向傳播算法進行更新。 相關鏈接: 線性回歸 分類的基礎學習 線性和非線性
import torch
from torch.autograd import Variable
batch_n = 100 #一個批次輸入數(shù)據(jù)的數(shù)量
hidden_layer = 100
input_data = 1000 #每個數(shù)據(jù)的特征為1000
output_data = 10
x = Variable(torch.randn(batch_n,input_data),requires_grad=False)
y = Variable(torch.randn(batch_n,output_data),requires_grad=False)
#用Variable對Tensor數(shù)據(jù)類型變量進行封裝的操作。requires_grad如果是F,表示該變量在進行自動梯度計算的過程中不會保留梯度值。
models = torch.nn.Sequential(
torch.nn.Linear(input_data,hidden_layer),
torch.nn.ReLU(),
torch.nn.Linear(hidden_layer,output_data)
)
#torch.nn.Sequential括號內(nèi)就是我們搭建的神經(jīng)網(wǎng)絡模型的具體結(jié)構(gòu),Linear完成從隱藏層到輸出層的線性變換,再用ReLU激活函數(shù)激活
#torch.nn.Sequential類是torch.nn中的一種序列容器,通過在容器中嵌套各種實現(xiàn)神經(jīng)網(wǎng)絡模型的搭建,
#最主要的是,參數(shù)會按照我們定義好的序列自動傳遞下去。
4.一些計算損失函數(shù)的方法
4.1 MSE均方差
torch.nn.MSELoss類使用均方誤差函數(shù)對損失值進行計算,定義類的對象時不用傳入任何參數(shù),但在使用實例時需要輸入兩個維度一樣的參數(shù)方可進行計算。
用于場景: MSE 是預測值和實際值之間差值的平方的平均值。它主要用于回歸問題。
import torch
from torch.autograd import Variable
loss_f = torch.nn.MSELoss()
x = Variable(torch.randn(100,100)) # 100行100列的向量
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
loss.data
#tensor(1.9529)
4.2 L1平均絕對誤差
torch.nn.L1Loss類使用平均絕對誤差函數(shù)對損失值進行計算,定義類的對象時不用傳入任何參數(shù),但在使用實例時需要輸入兩個維度一樣的參數(shù)方可進行計算。 應用場景: L1 損失是預測值與實際值之間差的絕對值的平均值。它對異常值具有較好的魯棒性。
import torch
from torch.autograd import Variable
loss_f = torch.nn.L1Loss()
x = Variable(torch.randn(100,100))
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
loss.data
#tensor(1.1356)
4.3 交叉熵的計算
交叉熵+線性非線性 交叉熵一般就用于多分類的情況,(3,5)表示為3個樣本,每個樣本為5維的數(shù)據(jù),所以我們要給每個樣本附上一個標簽。
'''
交叉熵:用于多分類任務的損失函數(shù),它需要兩個輸入:模型的預測輸出和目標標簽
'''
loss_f = torch.nn.CrossEntropyLoss()
x = Variable(torch.randn(3, 5)) # 創(chuàng)建一個形狀為 (3, 5) 的隨機張量 x,表示 3 個樣本,每個樣本有 5 個特征。
y = Variable(torch.LongTensor(3).random_(5)) # 3個0-4的隨機數(shù)字(表特征),生成了一個包含 3 個隨機整數(shù)的張量,每個整數(shù)的范圍在 0 到 4 之間
print(y)
loss = loss_f(x, y)
print(loss.data)
5.使用pytorch搭建神經(jīng)網(wǎng)絡(識別數(shù)字)
首先介紹幾個常見的包:
5.1 torchvision
torchvision 是PyTorch中專門用來處理圖像的庫。這個包中有四個大類:
torchvision.datasetstorchvision.modelstorchvision.transformstorchvision.utils
1.torchvision.datasets torchvision.datasets可以實現(xiàn)對一些數(shù)據(jù)集的下載和加載如MNIST可以用torchvision.datasets.MNIST COCO、ImageNet、CIFCAR等都可用這個方法下載和載入。
這里用torchvision.datasets加載MNIST數(shù)據(jù)集:
data_train = datasets.MNIST(root="./data/",
transform=transform,
train = True,
download = True)
data_test = datasets.MNIST(root="./data/",
transform = transform,
train = False)
2.torchvision.models torchvision.models 中為我們提供了已經(jīng)訓練好的模型. torchvision.models模塊的 子模塊中包含以下模型結(jié)構(gòu)。如:
AlexNet VGG ResNet SqueezeNet DenseNet等
import torchvision.models as models
resnet18 = models.resnet18()
alexnet = models.alexnet()
squeezenet = models.squeezenet1_0()
densenet = models.densenet_161()
5.1.1 如何修改這些模型的參數(shù)?
比如修改模型的輸入層或輸出層。
示例: 修改ResNet18模型以適應具有100個輸出類別的任務:
import torchvision.models as models
import torch.nn as nn
resnet18 = models.resnet18(pretrained=True) # 加載預訓練的模型resnet18
num_ftrs = resnet18.fc.in_features # 獲取最后一個全連接層的輸入特征數(shù)量
resnet18.fc = nn.Linear(num_ftrs, 100) # 修改為100個輸出類別
凍結(jié)參數(shù),并更新全連接層(遷移學習)。
在遷移學習中,你可能希望凍結(jié)模型的一部分,只訓練模型的特定層。這通常通過設置參數(shù)的requires_grad屬性來實現(xiàn)。
示例: 凍結(jié)DenseNet161模型的所有參數(shù),只訓練最后的分類層: 目的: 1. 利用預訓練模型的特征提取能力:一個已經(jīng)在數(shù)據(jù)集上訓練過的模型(如DenseNet161)通過訓練已經(jīng)學會了如何從圖像中提取有用的特征,比如邊緣、紋理、形狀等。這些特征對于圖像識別任務是非常有用的。所以我們可以利用它已經(jīng)學習到的特征提取能力,利用模型已經(jīng)理解的復雜和通用的視覺特征,這些特征通常對于大多數(shù)視覺識別任務都是有效的。 2.只更新模型的最后部分:盡管預訓練模型在特征提取方面表現(xiàn)優(yōu)秀,但它最初是為處理其他類型的任務(如識別ImageNet數(shù)據(jù)庫中的1000個類別)而訓練的。如果我們要處理一個新的任務(例如分類100個不同的類別),我們通常需要調(diào)整模型的最后一層,使其輸出與新任務的類別數(shù)量相匹配。這樣的原因:模型的前面幾層通常負責提取圖像的通用特征,而最后幾層則更專注于將這些特征轉(zhuǎn)換成特定的預測輸出(比如分類)。 3.訓練速度和效果:顯著減少需要訓練的參數(shù)數(shù)量,因為只有最后一層是新的或被重新訓練的。
densenet = models.densenet161(pretrained=True) # 加載了一個預訓練的 DenseNet161 模型。這個模型已經(jīng)在大型的圖像數(shù)據(jù)集(通常是ImageNet)上進行了訓練,學習了很多圖像特征。
for param in densenet.parameters():
param.requires_grad = False # 遍歷 DenseNet161 模型中的所有參數(shù),這些參數(shù)在接下來的訓練過程中不會被更新(即它們被“凍結(jié)”了)
# 僅修改最后的分類層,重新啟用梯度
num_ftrs = densenet.classifier.in_features # 獲取 DenseNet161 模型中的最后一層全連接層(即為分類層)的輸入特征數(shù)量
densenet.classifier = nn.Linear(num_ftrs, 100) # 假設有100個類別
densenet.classifier.weight.requires_grad = True # 重新啟用了新分類器層權重的梯度計算。
還需要考慮一個點(遷移學習),即為任務的相似性: 如果新任務與原始任務在視覺上有一定的相似性,比如從識別一般物體到識別特定物體(例如從識別動物到識別特定的動物種類),那么原始模型的特征提取層可能仍然非常有效。然而,如果任務從識別日常物品變?yōu)樽R別細粒度的人臉特征,那么可能需要對這些層進行更細致的調(diào)整或重新訓練,因為這些任務在視覺特征上的需求可能有較大差異。
修改架構(gòu)。
對于一些模型,例如SqueezeNet,你可能需要修改其內(nèi)部架構(gòu),例如改變某些層的激活函數(shù)或調(diào)整層之間的連接方式。
squeezenet = models.squeezenet1_0(pretrained=True)
# 假設你想修改特定層的屬性
squeezenet.features[3].expand3x3.activation = nn.ReLU(inplace=False)
5.1.2 torch.transforms
torch.transforms中有大量數(shù)據(jù)變換類,如:
5.1.3.1 torchvision.transforms.Resize 用于對載入的圖片數(shù)據(jù)按照我們需求的大小進行縮放。傳遞的參數(shù)可以是一個整型數(shù)據(jù),也可以是一個類似于(h,w)的序列。h代表高度,w代表寬度,如果輸入的是整型數(shù)據(jù)那么h和w都等于這個數(shù)。
5.1.3.2 torchvision.transforms.Scale 用于對載入的圖片數(shù)據(jù)按照我們需求的大小進行縮放。和Resize類似。
5.1.3.3 torchvision.transforms.CenterCrop 用于對載入的圖片以圖片中心為參考點,按照我們需要的大小進行裁剪。傳遞給這個類的參數(shù)可以是一個整型數(shù)據(jù),也可以是一個類似于(h,w)的序列。
5.1.3.4 torchvision.transforms.RandomCrop 用于對載入的圖片按照我們需要的大小進行隨機裁剪。傳遞給這個類的參數(shù)可以是一個整型數(shù)據(jù),也可以是一個類似于(h,w)的序列。
5.1.3.5 torchvision.transforms.RandomHorizontalFlip 用于對載入的圖片按隨機概率進行水平翻轉(zhuǎn)。我們通過傳遞給這個類的自定義隨機概率,如果沒有定義,則使用默認的概率為0.5
5.1.3.6 torchvision.transforms.RandomVerticalFlip 用于對載入的圖片按隨機概率進行垂直翻轉(zhuǎn)。我們通過傳遞給這個類的自定義隨機概率,如果沒有定義,則使用默認的概率為0.5
5.1.3.7 torchvision.transforms.ToTensor 用于對載入的圖片數(shù)據(jù)進行類型轉(zhuǎn)換,將之前構(gòu)成PIL圖片數(shù)據(jù)轉(zhuǎn)換為Tensor數(shù)據(jù)類型的變量,讓PyTorch能夠?qū)ζ溥M行計算和處理。
5.1.3.8 torchvision.transforms.ToPILImage: 用于對Tensor變量的數(shù)據(jù)轉(zhuǎn)換成PIL圖片數(shù)據(jù),主要為方便圖片顯示
#torchvision.transforms: 常用的圖片變換,例如裁剪、旋轉(zhuǎn)等;
transform=transforms.Compose(
[transforms.ToTensor(),#將PILImage轉(zhuǎn)換為張量
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))#將[0, 1]歸一化到[-1, 1]
#前面的(0.5,0.5,0.5) 是 R G B 三個通道上的均值, 后面(0.5, 0.5, 0.5)是三個通道的標準差
])
#上述代碼我們可以將transforms.Compose()看作一種容器,它能夠同時對多種數(shù)據(jù)變換進行組合。
#傳入的參數(shù)是一個列表,列表中的元素就是對載入數(shù)據(jù)進行的變換操作。
5.1.4 torch.utils
關于torchvision.utils我們介紹一種用來對數(shù)據(jù)進行裝載的類:torch.utils.data.DataLoader和
dataset 參數(shù)指定我們載入的數(shù)據(jù)集的名稱; batch_size 參數(shù)設置每個包中圖片的數(shù)量; (每個批次的樣本數(shù)量) shuffle 設置為True代表在裝載的過程會將數(shù)據(jù)隨機打亂順序并進行打包(True為打亂增強泛化性)。
data_loader_train=torch.utils.data.DataLoader(dataset=data_train,
batch_size=64,#每個batch載入的圖片數(shù)量,默認為1,這里設置為64
shuffle=True,
#num_workers=2#載入訓練數(shù)據(jù)所需的子任務數(shù)
)
data_loader_test=torch.utils.data.DataLoader(dataset=data_test,
batch_size=64,
shuffle=True)
#num_workers=2)
5.1.5 torchvision.utils.make_grid將一個批次的圖片構(gòu)造成網(wǎng)格模式的圖片
1、iter和next:獲取一個批次的圖片數(shù)據(jù)和其對應的圖片標簽——>2、再使用torchvision.utils.make_grid:將一個批次的圖片構(gòu)造成網(wǎng)格模式 經(jīng)過torchvision.utils.make_grid后圖片維度變?yōu)閏hannel,h,w三維【 因為要用matplotlib將圖片顯示,我們要使用的數(shù)據(jù)要是數(shù)組且維度為(height,weight,channel)即色彩通道在最后】——> 3、因此我們需要用numpy和transpose完成原始數(shù)據(jù)類型的轉(zhuǎn)換和數(shù)據(jù)維度的交換。
#預覽
#在嘗試過多次之后,發(fā)現(xiàn)錯誤并不是這一句引發(fā)的,而是因為圖片格式是灰度圖只有一個channel,需要變成RGB圖才可以,所以將其中一行做了修改:
images,labels = next(iter(data_loader_train))
# dataiter = iter(data_loader_train) #隨機從訓練數(shù)據(jù)中取一些數(shù)據(jù)
# images, labels = dataiter.next()
img = torchvision.utils.make_grid(images)
img = img.numpy().transpose(1,2,0)
std = [0.5,0.5,0.5]
mean = [0.5,0.5,0.5]
img = img*std+mean
print([labels[i] for i in range(64)])
plt.imshow(img)
5.2 CNN網(wǎng)絡卷積層搭建
CNN的學習 ResNet的學習
import math
import torch
import torch.nn as nn
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
#構(gòu)建卷積層之后的全連接層以及分類器
self.conv1 = nn.Sequential(
# 輸入通道數(shù)、輸出通道數(shù)、卷積核大小[3x3]、卷積核移動步長和paddingde值(用于對邊界像素的填充)
nn.Conv2d(3,64,kernel_size=3,stride=1,padding=1),
nn.ReLU(),
nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
nn.ReLU(),
nn.MaxPool2d(stride=2,kernel_size=2)
)
self.dense = torch.nn.Sequential(
nn.Linear(14*14*128,1024),
nn.ReLU(),
nn.Dropout(p=0.5), # 防止卷積神經(jīng)網(wǎng)絡在訓練過程中發(fā)生過擬合,原理是以一定的隨機概率將卷積神經(jīng)網(wǎng)絡模型的部分參數(shù)歸零,以達到減少相鄰兩層神經(jīng)連接的目的[本質(zhì)就是減少參數(shù)]
nn.Linear(1024,10)
)
def forward(self,x):
x=self.conv1(x)
x=x.view(-1,14*14*128)
x=self.dense(x)
return x
6.完整代碼
import torch # 用于深度學習和張量計算
import torch.nn as nn
from torchvision import datasets, transforms, utils # 從Torchvision導入數(shù)據(jù)集、數(shù)據(jù)變換工具和utils
import torch.optim as optim
import torchvision # 導入Torchvision庫,用于計算機視覺學習任務。
from torchvision import datasets, transforms # 從Torchvision庫中導入數(shù)據(jù)集合數(shù)據(jù)變換工具
from torch.autograd import Variable # autograd包下自動計算梯度的工具
import numpy as np # 導入NumPy庫,用于數(shù)值計算
import matplotlib.pyplot as plt # 導入Matplotlib庫,用于繪圖片
'''
1.檢查是否可以使用GPU
'''
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print('設備狀態(tài):', device)
'''
2.定義數(shù)據(jù)變換操作:將圖像轉(zhuǎn)換為tensor進行標準化
2.1 x 是一個圖像張量,可能是一個單通道(灰度圖像)的張量
2.2 第一個維度是通道(channels):從 1 重復到 3,這意味著將單通道圖像轉(zhuǎn)換為 RGB 三通道圖像。
如果你有一個灰度圖像,這樣做會創(chuàng)建三個相同副本的通道,形成一個彩色圖像,每個通道都有相同的灰度值。
2.3 第二個維度是高度(height):1 表示不改變圖像的高度,即不重復。
2.4 第三個維度是寬度(width):同樣為 1,表示也不改變圖像的寬度,不進行重復。
'''
transform = transforms.Compose([
transforms.ToTensor(), # 將圖像轉(zhuǎn)換為tensor
transforms.Lambda(lambda x: x.repeat(3, 1, 1)), # 將單道系統(tǒng)重復成3條道,符合預期輸入格式
transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)) # 標準化操作,將圖像像素值范圍調(diào)整到[-1, 1]之間
])
'''
3.下載并加載MNIST數(shù)據(jù)集。
3.1 定義數(shù)據(jù)集位置、數(shù)據(jù)變換操作、是否訓練。
'''
data_train = datasets.MNIST(root='./data/',
transform=transform,
train=True,
download=True)
data_test = datasets.MNIST(root='./data/',
transform=transform,
train=False,
download=False)
'''
4.定義數(shù)據(jù)加載器。
'''
data_loader_train = torch.utils.data.DataLoader(dataset=data_train, # 定義訓練集位置
batch_size=64, # 每個批次加載64張圖片
shuffle=True) # shuffle=True,即打亂數(shù)據(jù)順序,增強泛化能力
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
batch_size=64,
shuffle=True)
'''
5.預覽數(shù)據(jù)
5.1:.transpose(1, 2, 0)操作的意義在于:(而matplotlib在展示圖像時,它期望的數(shù)據(jù)格式遵循(height, width, channels)的規(guī)則)
把原始張量的第二維度(高度,height)移動到新張量的第一維度。
把原始張量的第三維度(寬度,width)移動到新張量的第二維度。
把原始張量的第一維度(通道,channels)移動到新張量的第三維度。
5.2:反標準化操作的數(shù)學表達式:
假設原始圖像數(shù)據(jù)經(jīng)過了這樣的標準化處理:x_normalized = (x - mean) / std,其中x是原始像素值,x_normalized是標準化后的像素值,mean是平均值,std是標準差。
要從標準化后的數(shù)據(jù)恢復到原始數(shù)據(jù),就需要執(zhí)行反向操作:x = x_normalized * std + mean。
'''
images, labels = next(iter(data_loader_train)) # 獲取一個批次的圖像和標簽
img = utils.make_grid(images) # 將多個圖像images合并為一張大圖像,便于可視化
img = img.numpy().transpose(1, 2, 0) # 轉(zhuǎn)換圖片維度將其tensor轉(zhuǎn)為numpy形式,以符合matplotlib的要求
std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img * std + mean # 根據(jù)歸一化公式反標準化圖片,以恢復原始圖像的像素值范圍,方便展示圖像。
print([labels[i] for i in range(64)]) # 打印圖片的標簽,用于檢查數(shù)據(jù)加載是否正確
plt.imshow(img) # 顯示圖片
plt.show() # 確保圖片顯示
'''
6.定義卷積神經(jīng)網(wǎng)絡
'''
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__() # 繼承父類nn.Module的初始化方法
'''
6.1定義卷積層。
6.1.1:因為之前數(shù)據(jù)預覽的時候(一批次),圖層是8x8=64的,我們的這里卷積核是3x3的,并且步長stride=1,所以要充分讀取圖層信息,故需要將padding填充設置為1
6.1.2:nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1): 這是第二層卷積層,與第一層類似,但輸出通道數(shù)增加到128,進一步提取更復雜的特征。
64個濾波器,每個濾波器都會生成一個不同的特征圖(feature map),kernel_size=3,這表示每個濾波器的尺寸是3x3
6.1.3:用激活函數(shù)Rule:第一是緩解了梯度消失的影響,第二是保證正值不變,負值替換為0,幫助網(wǎng)絡更好的學習圖像特征(關鍵信息)。
6.1.4:nn.MaxPool2d(stride=2, kernel_size=2): 這是最大池化層,通常用于降低特征圖的空間維度,減少計算量,同時保持重要特征。
'''
self.conv1 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1), # 第一層卷積,輸入3通道,輸出64通道,步長為1,填充為1
nn.ReLU(), # ReLU激活函數(shù),增加模型的非線性
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1), # 第二層卷積,輸入64通道,輸出128通道
nn.ReLU(), # ReLU激活函數(shù)
nn.MaxPool2d(stride=2, kernel_size=2) # 最大化池化層,減少特征圖尺寸,保持重要特征
)
'''
6.2 定義全連接層
6.2.1:14*14*128:是經(jīng)過池化層后變?yōu)?4*14的特征圖,然后每個特征有128個通道得到
1024:是全連接層中隱藏單元數(shù)
6.2.2:nn.Linear(1024, 10): 這是第二個全連接層,也是模型的輸出層。輸入維度為1024,
與前一層的輸出維度相對應。輸出維度為10,對應MNIST數(shù)據(jù)集的10個類別(0-9的數(shù)字)
'''
self.dense = nn.Sequential(
nn.Linear(14 * 14 * 128, 1024), # 全連接層,輸入14*14*128,輸出1024
nn.ReLU(), # ReLU激活函數(shù)
nn.Dropout(p=0.5), # Dropout層,防止過擬合
nn.Linear(1024, 10) # 輸出層,10個類別對應MNIST數(shù)據(jù)集的10個數(shù)字
)
'''
6.3 定義前向傳播
'''
def forward(self, x):
x = self.conv1(x) # 前向傳播:卷積層
x = x.view(-1, 14 * 14 * 128) # 將特征圖轉(zhuǎn)為一維張量
x = self.dense(x) # 前向傳播:全連接層
return x # 返回輸出
model = Model().to(device) # 實例化模型并轉(zhuǎn)移到GPU上。
cost = nn.CrossEntropyLoss() # 定義交叉熵損失函數(shù),用于多分類任務
optimizer = optim.Adam(model.parameters()) # 定義Adam優(yōu)化器,用于更新模型參數(shù)
print(model) # 打印模型結(jié)構(gòu)
'''
7.訓練模型
'''
n_epochs = 3
for epoch in range(n_epochs):
running_loss = 0.0
running_correct = 0
print("Epoch {}/{}".format(epoch, n_epochs)) # 打印輪數(shù)
print("-" * 10)
for data in data_loader_train:
X_train, y_train = data
print("訓練數(shù)據(jù)X_train,y_train:")
print(X_train, y_train)
X_train, y_train = Variable(X_train).to(device), Variable(y_train).to(device) # 將數(shù)據(jù)轉(zhuǎn)移到GPU
print("轉(zhuǎn)為GPU后:")
print(X_train, y_train)
outputs = model(X_train) # 前向傳播,計算模型輸出
_, pred = torch.max(outputs.data, 1) # 獲取預測結(jié)果
optimizer.zero_grad() # 梯度清零,防止累積
loss = cost(outputs, y_train) # 計算損失
loss.backward() # 反向傳播,計算梯度
optimizer.step() # 更新參數(shù)
running_loss += loss.data # 累加損失
running_correct += torch.sum(pred == y_train.data) # 計算訓練準確率
testing_correct = 0
for data in data_loader_test: # 使用測試集來監(jiān)控訓練進程,有助于及時調(diào)整訓練策略或提前停止訓練,從而節(jié)省資源并避免過擬合。
X_test, y_test = data
X_test, y_test = Variable(X_test).to(device), Variable(y_test).to(device) # 將數(shù)據(jù)轉(zhuǎn)移到GPU
outputs = model(X_test) # 前向傳播,計算測試集上的模型輸出
_, pred = torch.max(outputs.data, 1) # 獲取預測結(jié)果
testing_correct += torch.sum(pred == y_test.data) # 計算測試準確率
print("Loss is:{:4f}, Train Accuracy is:{:.4f}%, Test Accuracy is:{:.4f}".format(running_loss / len(data_train),
100 * running_correct / len(
data_train),
100 * testing_correct / len(
data_test))) # 輸出訓練損失和準確率
'''
8.測試模型:
8.1: torch.max(pred, 1)會返回一個包含這些索引的張量[2, 4],其中_會捕獲每行的最大值,
但由于前面使用了下劃線(_)作為變量名,這意味著我們【忽略了這些最大值】,【只關心索引】
8.2:batch_size=4說明每次處理4個樣本,每個樣本10維向量【4x10】,如果有1000個樣本,則每個epoch需要處理250批次
8.3:X_test 的維度通常是 (batch_size, channels, height, width):batch_size 是每個批次的樣本數(shù)。channels 表示圖像的通道數(shù)(對于灰度圖像是1,對于彩色圖像是3)。height 和 width 表示圖像的高度和寬度。
8.4:y_test 的維度通常是 (batch_size,),包含每個圖像對應的類別標簽
8.5:pred為預測標簽
'''
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
batch_size=4,
shuffle=True) # 重新定義測試數(shù)據(jù)加載器,用于測試
X_test, y_test = next(iter(data_loader_test)) # 獲取一個批次的測試數(shù)據(jù)進行預測,X_test是4個樣本的張量
inputs = Variable(X_test).to(device) # 將數(shù)據(jù)轉(zhuǎn)移到GPU
pred = model(inputs) # 前向傳播,計算預測結(jié)果
_, pred = torch.max(pred, 1) # 獲取預測標簽
print("預測標簽:")
print(pred)
print("Predict Label is:", [i for i in pred.data.cpu()]) # 打印預測標簽
print("Real Label is:", [i for i in y_test]) # 打印真實標簽
img = torchvision.utils.make_grid(X_test.cpu()) # 將4個測試樣本的圖像拼接成了一張2x2的網(wǎng)格圖,所以你會看到4個數(shù)字
img = img.numpy().transpose(1, 2, 0) # 轉(zhuǎn)換圖片維度順序
std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img * std + mean # 反標準化圖片
plt.imshow(img) # 顯示測試圖片及其標簽
plt.show() # 確保圖片顯示
柚子快報邀請碼778899分享:人工智能 Pytorch的學習
推薦閱讀
本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權,聯(lián)系刪除。