1. Dataset & DataLoader??
在
PyTorch
中,Dataset
和DataLoader
是用來(lái)處理數(shù)據(jù)的重要工具。它們的作用分別如下:Dataset
: Dataset 用于存儲(chǔ)數(shù)據(jù)樣本及其對(duì)應(yīng)的標(biāo)簽。在使用神經(jīng)網(wǎng)絡(luò)訓(xùn)練時(shí),通常需要將原始數(shù)據(jù)集轉(zhuǎn)換為 Dataset 對(duì)象,以便能夠通過(guò) DataLoader 進(jìn)行批量讀取數(shù)據(jù),同時(shí)也可以方便地進(jìn)行數(shù)據(jù)增強(qiáng)、數(shù)據(jù)預(yù)處理等操作。DataLoader
: DataLoader 用于將 Dataset 封裝成一個(gè)可迭代對(duì)象,以便輕松地訪問(wèn)數(shù)據(jù)集中的樣本。通過(guò)設(shè)置 batch_size 參數(shù),DataLoader 可以將數(shù)據(jù)集分成若干個(gè)批次,每個(gè)批次包含指定數(shù)量的樣本。此外,DataLoader 還支持對(duì)數(shù)據(jù)進(jìn)行 shuffle、多線程讀取等操作,使得訓(xùn)練過(guò)程更加高效。
使用 Dataset 和 DataLoader 可以使得數(shù)據(jù)處理過(guò)程更加模塊化和可維護(hù),同時(shí)也可以提高訓(xùn)練效率。分別封裝在torch.utils.data.Dataset
和torch.utils.data.DataLoader
。
class MyDataset(Dataset):
def __init__(self):
def __len__(self):
def __getitem__(self):
這是一個(gè)定義了自定義數(shù)據(jù)集類(lèi) MyDataset 的模板代碼,它繼承了 PyTorch 中的
Dataset
類(lèi),其中包含了三個(gè)必要的函數(shù):__init__
:用于初始化數(shù)據(jù)集,可以在這個(gè)函數(shù)中讀取數(shù)據(jù)、進(jìn)行預(yù)處理等操作。__len__
:用于返回?cái)?shù)據(jù)集中樣本的數(shù)量。__getitem__
:用于根據(jù)給定的索引 index 返回對(duì)應(yīng)的樣本及其標(biāo)簽。在這個(gè)函數(shù)中,需要根據(jù)索引從數(shù)據(jù)集中讀取相應(yīng)的樣本和標(biāo)簽,并進(jìn)行相應(yīng)的預(yù)處理和轉(zhuǎn)換。
需要在這個(gè)模板代碼中添加具體的代碼實(shí)現(xiàn),以實(shí)現(xiàn)自定義數(shù)據(jù)集的功能。
from torch.utils.data import DataLoader
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)
使用
DataLoaders
準(zhǔn)備訓(xùn)練和測(cè)試數(shù)據(jù)。在訓(xùn)練模型時(shí),我們通常希望以“小批量(minibatches)”方式傳遞樣本,每個(gè) epoch 重新洗牌數(shù)據(jù)以減少模型過(guò)擬合,DataLoader 是一個(gè)可迭代對(duì)象。
next(iter(train_dataloader))
iter(train_dataloader)
將 train_dataloader 轉(zhuǎn)換為一個(gè)迭代器對(duì)象,可以通過(guò)next
函數(shù)逐一獲取 DataLoader 中的數(shù)據(jù)。因此,next(iter(train_dataloader))
將返回一個(gè)包含一個(gè) batch 數(shù)據(jù)的元組。
具體來(lái)說(shuō),next 函數(shù)會(huì)從 train_dataloader 中獲取下一個(gè) batch 的數(shù)據(jù),并將其轉(zhuǎn)換為一個(gè)元組 (batch_data, batch_labels),其中 batch_data 是一個(gè)張量(tensor),形狀為 [batch_size, input_size],表示一個(gè) batch 中所有樣本的輸入特征;batch_labels 也是一個(gè)張量,形狀為 [batch_size, output_size],表示一個(gè) batch 中所有樣本的輸出標(biāo)簽,下面再舉個(gè)例子吧。
my_list = [1, 2, 3, 4, 5]
my_iterator = iter(my_list)
print(next(my_iterator)) # 輸出 1
print(next(my_iterator)) # 輸出 2
print(next(my_iterator)) # 輸出 3
在上面的例子中,my_list 是一個(gè)列表對(duì)象,通過(guò) iter() 函數(shù)將其轉(zhuǎn)換為迭代器 my_iterator。然后通過(guò) next() 函數(shù)依次獲取 my_iterator 中的每一個(gè)元素。
DataLoader
在創(chuàng)建時(shí)可以指定多個(gè)參數(shù)來(lái)控制數(shù)據(jù)的加載方式,常用的參數(shù)如下:dataset
:指定要加載的數(shù)據(jù)集。batch_size
:指定每個(gè) batch 中樣本的數(shù)量。shuffle
:指定是否在每個(gè) epoch 開(kāi)始時(shí)洗牌數(shù)據(jù)集。sampler
:指定一個(gè)自定義的數(shù)據(jù)采樣器,用于控制每個(gè) batch 中的樣本順序。batch_sampler
:指定一個(gè)自定義的 batch 采樣器,用于控制 batch 的順序和樣本數(shù)量。num_workers
:指定數(shù)據(jù)加載時(shí)的線程數(shù),用于加速數(shù)據(jù)讀取。collate_fn
:指定一個(gè)自定義的函數(shù),用于將一個(gè) batch 中的多個(gè)樣本拼接為一個(gè)張量(tensor)。pin_memory
:指定是否將數(shù)據(jù)加載到 GPU 的顯存中,以加速數(shù)據(jù)讀取。drop_last
:指定在數(shù)據(jù)集大小不是 batch_size 的倍數(shù)時(shí),是否丟棄最后一個(gè)不足 batch_size 的 batch。
2. Build Model??
import torch
from torch import nn
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using {device} device")
我們通過(guò)繼承
nn.Module
來(lái)定義神經(jīng)網(wǎng)絡(luò),并在__init__
中初始化神經(jīng)網(wǎng)絡(luò)的層。每個(gè)nn.Module
子類(lèi)在forward
方法中實(shí)現(xiàn)對(duì)輸入數(shù)據(jù)的操作。
class NeuralNetwork(nn.Module):
def __init__(self):
super().__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
這段代碼定義了一個(gè)名為 NeuralNetwork 的神經(jīng)網(wǎng)絡(luò)類(lèi),它繼承自
nn.Module
。
這個(gè)神經(jīng)網(wǎng)絡(luò)包含一個(gè) Flatten 層和一個(gè)由3個(gè)線性層和2個(gè) ReLU 激活函數(shù)組成的神經(jīng)網(wǎng)絡(luò)層。__init__
方法:在Python
中,當(dāng)一個(gè)類(lèi)繼承自另一個(gè)類(lèi)時(shí),它會(huì)繼承該類(lèi)的所有屬性和方法。在PyTorch
中,當(dāng)你定義一個(gè)自己的神經(jīng)網(wǎng)絡(luò)類(lèi)時(shí),你通常會(huì)繼承nn.Module
這個(gè)基類(lèi),因?yàn)?nn.Module
已經(jīng)定義好了很多用于搭建神經(jīng)網(wǎng)絡(luò)的基本組件和方法。
當(dāng)你定義自己的神經(jīng)網(wǎng)絡(luò)類(lèi)時(shí),你需要調(diào)用基類(lèi)的構(gòu)造函數(shù)來(lái)繼承基類(lèi)的屬性和方法。super().__init__()
就是調(diào)用基類(lèi)(nn.Module)的構(gòu)造函數(shù),并返回一個(gè)代表基類(lèi)實(shí)例的對(duì)象,這樣你的神經(jīng)網(wǎng)絡(luò)類(lèi)就可以使用 nn.Module 的所有屬性和方法了。forward
方法:就是神經(jīng)網(wǎng)絡(luò)的前向傳播過(guò)程。
model = NeuralNetwork().to(device)
這行代碼創(chuàng)建了一個(gè)名為 model 的神經(jīng)網(wǎng)絡(luò)模型實(shí)例,使用了前面定義的 NeuralNetwork 類(lèi),并將其移動(dòng)到了特定的設(shè)備(CPU 或 GPU)上。使用
to()
方法可以將模型移動(dòng)到特定的設(shè)備上,從而利用 GPU 加速模型的訓(xùn)練和推理。如果設(shè)備是 GPU,則模型的所有參數(shù)和緩存都會(huì)復(fù)制到 GPU 上,如果設(shè)備是 CPU,則會(huì)復(fù)制到系統(tǒng)內(nèi)存中。
3. Optimization??
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor()
)
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor()
)
train_dataloader = DataLoader(training_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
model = NeuralNetwork()
使用
FashionMNIST
數(shù)據(jù)集,和之前描述的Datasets & DataLoaders
和Build Model
。
learning_rate = 1e-3
batch_size = 64
epochs = 5
learning_rate
:在每個(gè) batch/epoch 更新模型參數(shù)的量。較小的值會(huì)導(dǎo)致較慢的學(xué)習(xí)速度,而較大的值可能會(huì)在訓(xùn)練過(guò)程中產(chǎn)生不可預(yù)測(cè)的行為。batch_size
:在更新參數(shù)之前,通過(guò)網(wǎng)絡(luò)傳播的數(shù)據(jù)樣本數(shù)量。epochs
:迭代數(shù)據(jù)集的次數(shù)。
def train_loop(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
# Compute prediction and loss
pred = model(X)
loss = loss_fn(pred, y)
# Backpropagation
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch % 100 == 0:
loss, current = loss.item(), (batch + 1) * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
定義模型訓(xùn)練函數(shù):
在for
循環(huán)中,我們使用enumerate
函數(shù)遍歷 dataloader 中的每個(gè)批次(batch),并將批次索引(batch index)和包含輸入數(shù)據(jù)和標(biāo)簽的元組解壓縮為 X 和 y。
然后計(jì)算出當(dāng)前批次中的預(yù)測(cè)(prediction)和損失(loss),以便我們可以通過(guò)優(yōu)化器(optimizer)調(diào)整模型的參數(shù)以最小化損失。
其次的三行執(zhí)行反向傳播(backpropagation
)并使用優(yōu)化器更新模型的參數(shù)。optimizer.zero_grad()
將優(yōu)化器的梯度歸零,否則梯度會(huì)出現(xiàn)累加現(xiàn)象。然后使用backward
函數(shù)計(jì)算損失相對(duì)于模型參數(shù)的梯度,最后使用step
函數(shù)將優(yōu)化器的梯度更新應(yīng)用到模型的參數(shù)上。
這個(gè)if
語(yǔ)句在每100個(gè)批次之后打印出當(dāng)前的損失和訓(xùn)練樣本數(shù)量,以便我們可以了解模型的訓(xùn)練進(jìn)度。
def test_loop(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
test_loss, correct = 0, 0
with torch.no_grad():
for X, y in dataloader:
pred = model(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= num_batches
correct /= size
print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
定義模型測(cè)試函數(shù):
在前三行中,我們計(jì)算出數(shù)據(jù)集的大小和批次數(shù)量,并初始化測(cè)試損失(test loss)和正確分類(lèi)數(shù)量(correct)。
這個(gè)with
語(yǔ)句在上下文中禁用梯度計(jì)算,因?yàn)闇y(cè)試階段不需要計(jì)算梯度,以便我們可以僅使用模型的前向傳遞(forward pass)進(jìn)行測(cè)試。在這個(gè) for 循環(huán)中,我們遍歷 dataloader 中的每個(gè)批次,使用模型計(jì)算出預(yù)測(cè),計(jì)算當(dāng)前批次的測(cè)試損失,并使用 argmax 函數(shù)找到每個(gè)樣本的預(yù)測(cè)標(biāo)簽,然后將正確分類(lèi)的數(shù)量累加到 correct 變量中。
計(jì)算出平均測(cè)試損失和正確分類(lèi)的比例,并打印出測(cè)試結(jié)果。我們將測(cè)試損失除以批次數(shù)量來(lái)得到平均測(cè)試損失,并將正確分類(lèi)的數(shù)量除以數(shù)據(jù)集大小來(lái)得到正確分類(lèi)的比例。最后,我們打印出測(cè)試結(jié)果,其中包括正確分類(lèi)的百分比和平均測(cè)試損失。
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
這行代碼有點(diǎn)抽象:
這行代碼的作用是計(jì)算當(dāng)前批次中正確分類(lèi)的數(shù)量,它可以分為幾個(gè)步驟來(lái)理解:
首先,pred.argmax(1)
用來(lái)計(jì)算模型預(yù)測(cè)的最大概率值對(duì)應(yīng)的類(lèi)別,其中1表示按行計(jì)算最大值,即計(jì)算每個(gè)樣本最有可能屬于哪個(gè)類(lèi)別。
接下來(lái),pred.argmax(1) == y
用于將預(yù)測(cè)類(lèi)別與真實(shí)類(lèi)別進(jìn)行比較,生成一個(gè)大小為批次大小的布爾張量,表示哪些樣本被正確分類(lèi)了。
然后,(pred.argmax(1) == y).type(torch.float)
將布爾張量轉(zhuǎn)換為浮點(diǎn)數(shù)張量,其中正確分類(lèi)的樣本對(duì)應(yīng)的元素值為1,錯(cuò)誤分類(lèi)的樣本對(duì)應(yīng)的元素值為0。
最后,.sum().item()
用于將正確分類(lèi)的樣本的元素值求和,并將結(jié)果轉(zhuǎn)換為 Python 數(shù)值類(lèi)型。
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
train_loop(train_dataloader, model, loss_fn, optimizer)
test_loop(test_dataloader, model, loss_fn)
print("Done!")
定義了一個(gè)交叉熵?fù)p失函數(shù)和一個(gè)隨機(jī)梯度下降(SGD)優(yōu)化器。交叉熵?fù)p失通常用于多類(lèi)別分類(lèi)問(wèn)題,而 SGD 優(yōu)化器是一種基本的梯度下降算法,用于更新模型的參數(shù),使其逐漸逼近最優(yōu)值。
這里定義了一個(gè)循環(huán),用于多次訓(xùn)練和測(cè)試模型。具體來(lái)說(shuō),循環(huán)會(huì)運(yùn)行 epochs 次,其中每次循環(huán)代表一個(gè)“訓(xùn)練周期”(epoch),在每個(gè)訓(xùn)練周期中,代碼會(huì)先調(diào)用 train_loop() 函數(shù)來(lái)訓(xùn)練模型,然后調(diào)用 test_loop() 函數(shù)來(lái)測(cè)試模型在測(cè)試集上的性能。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-405015.html
4. Save & Load Model??
# Additional information
# 記錄模型的相關(guān)訓(xùn)練信息
EPOCH = 5
PATH = "model.pt"
LOSS = 0.4
torch.save({
'epoch': EPOCH,
'model_state_dict': net.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': LOSS,
}, PATH)
下面是模型的加載。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-405015.html
model = Net() # 自己定義的網(wǎng)絡(luò)
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
checkpoint = torch.load(PATH)
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']
model.eval()
# - or -
model.train()
到了這里,關(guān)于PyTorch 神經(jīng)網(wǎng)絡(luò)搭建模板的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!