主頁 >  其他 > pytorch第05天 搭建神經網路

pytorch第05天 搭建神經網路

2021-08-21 06:47:56 其他

搭建第一個神經網路

  • 1 資料
    • (1)匯入資料
    • (2)資料集可視化
    • (3)為自己制作的資料集創建類
    • (4)資料集批處理
    • (5)資料預處理
  • 2 神經網路
    • (1)定義神經網路類
    • (2)神經網路組件
    • (3)模型引數
  • 3 最優化模型引數
    • (1)超引數
    • (2)損失函式
    • (3)優化方法
  • 4 模型的訓練與測驗
    • (1)訓練回圈與測驗回圈
    • (2)禁用梯度跟蹤
  • 5 模型的保存、匯入與GPU加速
    • (1)模型的保存與匯入
    • (2)GPU加速
  • 總結

1 資料

(1)匯入資料

我們以Fashion-MNIST資料集為例,介紹一下關于pytorch的資料集匯入,
PyTorch域庫提供許多預加載的資料集(如FashionMNIST),這些資料集是torch.utils.data.Dataset的子類,并實作了特定于指定資料的功能,
Fashion-MNIST是Zalando文章中的影像資料集,包含60,000個訓練示例和10,000個測驗示例,每個示例包括28×28灰度影像和來自10個類中的一個的關聯標簽,

import torch
from torchvision import datasets
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'	# 沒有這句會報錯,具體原因我也不知道

training_data = datasets.FashionMNIST(
    root="../data",
    train=True,
    download=True,
    transform=ToTensor()
)

test_data = datasets.FashionMNIST(
    root="../data",
    train=False,
    download=True,
    transform=ToTensor()
)

輸出(下面的截圖不完整)
在這里插入圖片描述

我們使用以下引數加載FashionMNIST資料集:
root是存盤訓練/測驗資料的路徑,
train指定訓練或測驗資料集,
download=True 如果資料集不存在于指定存盤路徑,那么就從網上下載,
transform和target_transform用于指定屬性和標簽轉換操作,這里所說的“轉換操作”,通常封裝在torchvision.transforms中,因此通常需要匯入torchvision.transforms,或者匯入這個包中的操作

(2)資料集可視化

我們可以像串列一樣手動索引資料集:training_data[index],我們使用matplotlib來可視化訓練資料中的一些示例,

labels_map = {
    0: "T-Shirt",
    1: "Trouser",
    2: "Pullover",
    3: "Dress",
    4: "Coat",
    5: "Sandal",
    6: "Shirt",
    7: "Sneaker",
    8: "Bag",
    9: "Ankle Boot",
}
figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3
for i in range(1, cols * rows + 1):
    # 從0-len(training_data)中隨機生成一個數字(不包括右邊界)
    sample_idx = torch.randint(len(training_data), size=(1,)).item() 
    img, label = training_data[sample_idx] # 獲得圖片和標簽
    figure.add_subplot(rows, cols, i)
    plt.title(labels_map[label])
    plt.axis("off")                        # 坐標軸不可見
    plt.imshow(img.squeeze(), cmap="gray") # 顯示灰度圖
plt.show()

輸出
在這里插入圖片描述
上面的程式中,有兩個地方指的注意,一個是可以求training_data的長度,另一個可以通過索引獲得單個樣本,當然這里的樣本已經被轉換成了張量,如下圖所示
在這里插入圖片描述

(3)為自己制作的資料集創建類

如果要匯入自己制作的資料集,需要撰寫一個類,這個類用于繼承torch.utils.data中的Dataset類,自制的資料集類必須實作三個函式:initlen__和__getitem,分別是初始化類,求長度len(obj),通過索引獲得單個樣本(像串列一樣),

import os
import pandas as pd
from torch.utils.data import Dataset
from torchvision.io import read_image

class CustomImageDataset(Dataset):
    def __init__(self, annotations_file, img_dir, transform=None, target_transform=None):
        self.img_labels = pd.read_csv(annotations_file)
        self.img_dir = img_dir
        self.transform = transform
        self.target_transform = target_transform

    def __len__(self):
        return len(self.img_labels)

    def __getitem__(self, idx):
        img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0])
        image = read_image(img_path)
        label = self.img_labels.iloc[idx, 1]
        if self.transform:
            image = self.transform(image)
        if self.target_transform:
            label = self.target_transform(label)
        return image, label

具體細節可以在pytorch的官網教程:https://pytorch.org/tutorials/beginner/basics/data_tutorial.html
Creating a Custom Dataset for your files

(4)資料集批處理

上面的程式中,雖然可以使用索引獲得樣本,但一次只能獲得單個樣本,無法像串列、張量、numpy切片一樣一次切出多個
在這里插入圖片描述

而在訓練模型的時候,我們希望能夠批處理,即一次處理若干個樣本,同時,我們希望資料在每次遍歷完之后打亂一次,以減少過擬合,并使用Python的多處理來加快資料提取,
pytorch中,專門有一個類可以實作上述功能,即torch.utils.data.DataLoader

下面的程式是將資料集匯入到DataLoader中

from torch.utils.data import DataLoader

train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True) 
# train_dataloader是一個DataLoader類的物件
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)

training_data和test_data就是前面匯入的資料集,由于我們指定了batch的大小是64,因為我們指定了shuffle=True,所以在遍歷所有batch之后,資料將被打亂,

此時training_data和test_data仍然不是可迭代物件,還需要將其變成可迭代物件,可以使用iter函式將每一個batch轉化成可迭代物件,或者enumerate函式將其的每個batch帶上序號變成元組

用iter函式

# Display image and label.
train_features, train_labels = next(iter(train_dataloader))
print(f"Feature batch shape: {train_features.size()}")
print(f"Labels batch shape: {train_labels.size()}")
img = train_features[0].squeeze()		# 將長度為1的維度去掉
label = train_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"Label: {label}")

輸出
在這里插入圖片描述
上面的程式中,train_features, train_labels都是包含64個樣本的張量

用enumerate函式

for batch_index, (features, label) in enumerate(train_dataloader):
    print(batch_index)
    print(f"Feature batch shape: {features.size()}")
    print(f"Labels batch shape: {label.size()}")
    img = features[0].squeeze()
    label = label[0]
    plt.imshow(img, cmap="gray")
    plt.show()
    print(f"Label: {label}")
    break

在這里插入圖片描述

(5)資料預處理

資料并不總是以訓練機器學習演算法所需的最終處理形式出現,因此我們需要對資料進行一些變換操作,使其適合于訓練,
所有的TorchVision資料集都有兩個引數,它們接受包含轉換邏輯的可呼叫物件:(1)transform用于修改特性,(2)target_transform用于修改標簽

torchvision.transforms模塊提供了多種常用的轉換,這里我們介紹一下ToTensor和Lambda,

為了進行訓練,我們需要將FashionMNIST中的特征轉化為normalized tensors,將標簽轉化為One-hot編碼的張量,為了完成這些變換,我們使用ToTensor和Lambda,

import torch
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda

ds = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
    target_transform=Lambda(lambda y: torch.zeros(10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1))
)

ToTensor將PIL影像或NumPy ndarray轉換為FloatTensor,并將影像的像素值(或者灰度值)縮放到[0, 1]區間

Lambda可以用于任何用戶定義的lambda函式,在這里,我們定義一個函式來將整數轉換為一個one-hot編碼張量,首先建立一個長度為10的0張量(之所以為10,是因為有10個類別),然后呼叫scatter_函式,把對應的位置換成1,scatter_函式的用法如下:
在這里插入圖片描述
更多torchvision.transforms的API詳見:https://pytorch.org/vision/stable/transforms.html

2 神經網路

神經網路由對資料進行操作的層/模塊組成,torch.nn提供了構建的神經網路所需的所有構建塊,PyTorch中的每個模塊都繼承了nn.Module,神經網路本身就是一個模塊,它由其他模塊(層)組成,這種嵌套結構允許輕松構建和管理復雜的體系結構,

在下面的小節中,我們將構建一個神經網路來對FashionMNIST資料集中的影像進行分類,

(1)定義神經網路類

我們通過繼承nn.Module來定義我們的神經網路類,并在__init__中初始化神經網路層,每一個nn.Module的子類在forward方法中繼承了對輸入資料的操作,

在初始化方法中搭建網路結構

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),		# 線性層,28*28=784,784是輸入資料的維度
            nn.ReLU(),					# 激活層
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
            nn.ReLU()
        )

    def forward(self, x):
        x = self.flatten(x)					# 打平層
        logits = self.linear_relu_stack(x)	# 線性激活層
        return logits

We create an instance of NeuralNetwork, and print its structure.
我們可以建立一個NeuralNetwork(即剛剛定義的類)的實體,并列印它的結構

model = NeuralNetwork()
print(model)

輸出

NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
    (5): ReLU()
  )
)

使用模型時,我們將輸入資料傳遞給它,這將執行模型的forward方法,以及一些背后的操作,注意:不要直接呼叫model.forward() !

定義好我們自己的神經網路類之后,我們可以隨機生成一個張量,來測驗一下輸出的size是否符合要求
每個樣本傳入模型后會得到一個10維的張量,將這個張量傳入nn.Softmax的實體中,可以得到每個類別的概率

X = torch.rand(1, 28, 28)	# 生成一個樣本
logits = model(X)							# 將樣本輸入到模型中,自動呼叫forward方法
print(logits.size())						
pred_probab = nn.Softmax(dim=1)(logits)		# 實體化一個Softmax物件,并通過物件呼叫
y_pred = pred_probab.argmax(1)				# 獲得概率最大索引
print(f"Predicted class: {y_pred}")

輸出

torch.Size([1, 10])
Predicted class: tensor([8])

(2)神經網路組件

上面搭建神經網路時,我們使用了打平函式、線性函式、激活函式,我們來看看這些函式的功能

打平層

input_image = torch.rand(3,28,28)
print(input_image.size())

輸出

torch.Size([3, 28, 28])

線性層

layer1 = nn.Linear(in_features=28*28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())

輸出

torch.Size([3, 20])

線性層其實就是實作了 y=w*x + b,其實是和下面的程式是等效的,但下面的程式不適合放在nn.Sequential中(但可以放在forward方法里)

w = torch.rand(784, 20)
b = torch.rand((1, 20))
hidden2 = flat_image @ w + b
print(hidden2.size())

輸出

torch.Size([3, 20])

nn.Sequential是一個模塊容器類,在初始化時,將各個模塊按順序放入容器中,呼叫模型時,資料按照初始化時的順序傳遞,
例如:

seq_modules = nn.Sequential(
    flatten,		# 在nn.Sequential中可以呼叫其他模塊
    layer1,
    nn.ReLU(),
    nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image)

在nn.Sequential中可以呼叫其他模塊,nn.Sequential定義的模塊也可以被其他模塊呼叫

(3)模型引數

神經網路中的許多層都是引數化的,也就是說,在訓練程序中會優化相關的權值和偏差,我們可以使用模型的parameters()或named_parameters()方法訪問所有引數,

model.parameters()回傳的是一個引數生成器,可以用list()將其轉化為串列,例如

para_list = list(model.parameters())

# 將引數生成器轉換成串列之后,串列的第一個元素是w,第二個元素是b
print(type(para_list[0]))
print(f'number of linear_layers :{len(para_list)/2}')
print('weights:')
print(para_list[0][:2])  # 只切出兩個樣本來顯示
print('bias:')
print(para_list[1][:2]) 
print('\nthe shape of first linear layer:', para_list[0].shape)

輸出

<class 'torch.nn.parameter.Parameter'>
number of linear_layers :3.0
weights:
tensor([[ 0.0135,  0.0206,  0.0051,  ..., -0.0184, -0.0131, -0.0246],
        [ 0.0127,  0.0337,  0.0177,  ...,  0.0304, -0.0177,  0.0316]],
       grad_fn=<SliceBackward>)
bias:
tensor([0.0333, 0.0108], grad_fn=<SliceBackward>)

the shape of first linear layer: torch.Size([512, 784])

named_parameters()方法回傳引數的名稱和引數張量,例如:

print("Model structure: ", model, "\n\n")

for name, param in model.named_parameters():
    print(f"Layer: {name} | Size: {param.size()} | Values : \n{param[:2]} \n")
    # 只切出前兩行顯示

輸出

Model structure:  NeuralNetwork(
  (flatten): Flatten(start_dim=1, end_dim=-1)
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
    (5): ReLU()
  )
) 


Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : 
tensor([[ 0.0135,  0.0206,  0.0051,  ..., -0.0184, -0.0131, -0.0246],
        [ 0.0127,  0.0337,  0.0177,  ...,  0.0304, -0.0177,  0.0316]],
       grad_fn=<SliceBackward>) 

Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : 
tensor([0.0333, 0.0108], grad_fn=<SliceBackward>) 

Layer: linear_relu_stack.2.weight | Size: torch.Size([512, 512]) | Values : 
tensor([[ 0.0338,  0.0266, -0.0030,  ..., -0.0163, -0.0096, -0.0246],
        [-0.0292,  0.0302, -0.0308,  ...,  0.0279, -0.0291, -0.0105]],
       grad_fn=<SliceBackward>) 

Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : 
tensor([ 0.0137, -0.0036], grad_fn=<SliceBackward>) 

Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : 
tensor([[ 0.0029, -0.0025,  0.0105,  ..., -0.0054,  0.0090,  0.0288],
        [-0.0391, -0.0088,  0.0405,  ...,  0.0376, -0.0331, -0.0342]],
       grad_fn=<SliceBackward>) 

Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : 
tensor([0.0406, 0.0369], grad_fn=<SliceBackward>) 

更多關于torch.nn的API請看:https://pytorch.org/docs/stable/nn.html

3 最優化模型引數

(1)超引數

在繪制計算圖之前,需要給出超引數,這里說的超引數,指的是學習率、批大小、迭代次數等

learning_rate = 1e-3
batch_size = 64
epochs = 5

(2)損失函式

回歸問題一般用nn.MSELoss,二分類問題一般用nn.BCELoss,多分類問題一般用nn.CrossEntropyLoss,這里FashionMNIST中,標簽有十個類別,因此這里我們用nn.CrossEntropyLoss

# Initialize the loss function
loss_fn = nn.CrossEntropyLoss()

(3)優化方法

這里我們用隨機梯度下降,即每傳入一個樣本,更新一次引數

optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

更多優化方法的API,可以看:https://pytorch.org/docs/stable/optim.html
常用的優化演算法原理,可以看:https://zhuanlan.zhihu.com/p/78622301

4 模型的訓練與測驗

(1)訓練回圈與測驗回圈

每個epoch包括兩個主要部分:
訓練回圈(train_loop)——遍歷訓練資料集并嘗試收斂到最優引數,
驗證/測驗回圈(test_loop)——遍歷測驗資料集以檢查模型性能是否正在改善,

我們先把上述兩個程序封裝成函式

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 * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")


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")

這里解釋一下“correct += (pred.argmax(1) == y).type(torch.float).sum().item()”
這里“pred.argmax(1)”其實是“pred.argmax(dim=1)”,dim表示維度方向,每個樣本預測值的長度都是10,argmax是求最大值所在的索引,索引對應了樣本的預測類別,用預測值和標簽值作比較,回傳的是bool型別的張量,.type(torch.float)將其轉化為單精度浮點型,.sum()求和,表示求有幾個樣本預測正確,

現在可以進行訓練了

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!")

輸出
在這里插入圖片描述

我們可以寫一段代碼,看看預測的圖片對不對

test_features, test_labels = next(iter(test_dataloader))
logits = model(test_features[0])
pred_probab = nn.Softmax(dim=1)(logits)
pred = pred_probab.argmax(1)

img = test_features[0].squeeze()    # 將長度為1的維度去掉
true_label = test_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"True label: {true_label}")
print(f"Predict: {pred.item()}")

輸出
在這里插入圖片描述

(2)禁用梯度跟蹤

在上面的測驗回圈中,使用了torch.no_grad()方法,在表示所在的with塊不對梯度進行記錄,
默認情況下,所有requires_grad=True的張量(在創建優化器的時候,內部就把里面的引數全部設定為了requires_grad=True)都跟蹤它們的計算歷史并支持梯度計算,但是,在某些情況下,我們并不需要這樣做,例如,測驗的時候,我們只是想通過網路進行正向計算,我們可以通過使用torch.no_grad()塊包圍計算代碼來停止跟蹤計算:

z = torch.matmul(x, w)+b
print(z.requires_grad)

with torch.no_grad():
    z = torch.matmul(x, w)+b
	print(z.requires_grad)

輸出

True
False

禁用梯度跟蹤主要用于下面兩種情況:
(1)將神經網路中的一些引數標記為凍結引數,這是對預先訓練過的網路進行微調的一個常見的場景
(2)當你只做正向傳遞時,為了加快計算速度,因為不跟蹤梯度的張量的計算會更有效率,

5 模型的保存、匯入與GPU加速

(1)模型的保存與匯入

PyTorch模型將學習到的引數存盤在一個內部狀態字典中,稱為state_dict,我們可以通過torch.save()將模型的引數保存到指定路徑,
保存了模型的引數,還需要保存模型的形狀(即模型的結構)

# 保存模型引數
torch.save(model.state_dict(), './model_weights.pth')
# 保存模型結構
torch.save(model, './model.pth')

匯入模型時,需要先匯入模型的結構,再匯入模型的引數,代碼如下:

# 匯入模型結構
model_loaded = torch.load('./model.pth') 
# 如果原來的model在cuda:0上,那么匯入之后,model_loaded也在cuda:0上

# 匯入模型引數
model_loaded.load_state_dict(torch.load('model_weights.pth'))

因為model是NeuralNetwork類的一個物件,所以在匯入狀態前,必須先有一個NeuralNetwork物件,要么實體化一個,要么通過匯入結構torch.load(’./model.pth’)匯入一個.

torch.load直接匯入模型,不是特別推薦,原因有以下兩點:

(1)如果A.py檔案中的程式保存了model.pth,如果檔案B.py想讀取這個模型,則不能直接用torch.load匯入模型結構,必須先實體化一個NeuralNetwork物件,要么從A.py或者從其他檔案中把NeuralNetwork類給導進來,要么這里重寫一個與A.py中一模一樣的NeuralNetwork類,類名也要一樣,否則報錯,

# 實體化一個NeuralNetwork物件
model_loaded = NeuralNetwork() # model_loaded默認是CPU中
# 匯入模型引數
model_loaded.load_state_dict(torch.load('./gdrive/MyDrive/model_weights.pth'))

(2)如果用torch.load匯入模型,當我們在cuda:0上訓練好一個模型并保存時,讀取出來的模型也是默認在cuda:0上的,如果訓練程序的其他資料被放到了如cuda:1上,則會報錯,而實體化創建模型,由于.load_state_dict可以跨設備,則無論原來的模型在什么設備上,都不妨礙把引數匯入到新創建的模型物件當中,

綜合上面兩點,torch.load慎用,最好是先實體化后再匯入模型狀態,

我們可以用匯入的模型做預測

test_features, test_labels = next(iter(test_dataloader))
logits = model_loaded(test_features[0])   # 使用匯入的模型
pred_probab = nn.Softmax(dim=1)(logits)
pred = pred_probab.argmax(1)

img = test_features[0].squeeze()    # 將長度為1的維度去掉
true_label = test_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"True label: {true_label}")
print(f"Predict: {pred.item()}")

輸出
在這里插入圖片描述

(2)GPU加速

默認情況下,張量和模型是在CPU上創建的,如果想讓其在GPU中操作,我們必須使用.to方法(確定GPU可用后)顯式地移動到GPU,需要注意的是,跨設備復制大張量在時間和記憶體上開銷都是很大的!

# We can move our tensor to the GPU if available
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print('Using {} device'.format(device))

輸出

Using cuda device

在初始化模型時,可以將模型放入GPU中

model = NeuralNetwork().to(device)

對于張量,可以在創建的時候指定為在GPU上創建,也可以在創建后轉移到GPU當中

X = torch.rand(1, 28, 28, device=device)	# 創建時指定設備
Y = torch.rand(10).to(device)				# 創建后轉移

當然,張量和模型也能從GPU轉移到CPU當中,我們可以用.device()來查看張量所在設備
在這里插入圖片描述

另外,需要注意的是,如果需要將模型送到GPU當中,必須在構建優化器之前,因為CPU和GPU中的模型,是兩個不同的物件,構建完優化器再將模型放入GPU,將導致優化器只優化CPU中的模型引數,

有些電腦有多張顯卡,那么.to(‘cuda’)默認是將張量或者模型轉移到第一張顯卡(編號為0)上,如果想轉移到其他顯卡上,則用下面的程式

device = torch.device(‘cuda:2)
# 2是設備號,假如有八張顯卡,那么編號就是0—7

關于多GPU資料并行處理,可以看這個教程:https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html

torch.save
至此,所有程式已經完成

總結

上面的程式有點亂,我這里綜合一下:

# coding=utf-8
import torch
import torch.nn as nn
from torchvision import datasets
from torch.utils.data import DataLoader
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'	# 沒有這句會報錯,具體原因我也不知道

# 匯入資料
training_data = datasets.FashionMNIST(
    root="../data",
    train=True,
    download=True,
    transform=ToTensor()
)
test_data = datasets.FashionMNIST(
    root="../data",
    train=False,
    download=True,
    transform=ToTensor()
)

# 定義超引數
# 之所以在這個地方定義,是因為在初始化DataLoader時需要用到batch_size
learning_rate = 1e-3
batch_size = 64
epochs = 5
train_dataloader = DataLoader(training_data, batch_size=batch_size, shuffle=True)
# train_dataloader是一個DataLoader類的物件
test_dataloader = DataLoader(test_data, batch_size=batch_size, shuffle=True)


# 搭建神經網路
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),
            nn.ReLU()
        )

    def forward(self, x):
        x = self.flatten(x)					# 打平層
        logits = self.linear_relu_stack(x)	# 線性激活層
        return logits

# 確定使用設備
device = 'cuda' if torch.cuda.is_available() else 'cpu'

# 實體化一個神經網路類
model = NeuralNetwork().to(device)

# 確定損失函式
loss_fn = nn.CrossEntropyLoss()

# 確定優化器
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)


# 封裝訓練程序
def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device) # 將樣本和標簽轉移到device中
        # 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 * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
            

# 封裝測驗程序
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:
            X, y = X.to(device), y.to(device)  # 將樣本和標簽轉移到device中
            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")

# 訓練
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!")

# 保存模型引數
torch.save(model.state_dict(), './model_weights.pth')
# 保存模型結構
torch.save(model, './model.pth')

# 匯入模型結構
model_loaded = torch.load('./model.pth')    # 模型自動匯入到GPU當中
# 匯入模型引數
model_loaded.load_state_dict(torch.load('model_weights.pth'))

# 用匯入的模型測驗
test_features, test_labels = next(iter(test_dataloader))
test_features = test_features.to(device)
logits = model_loaded(test_features[0])   # 使用匯入的模型
pred_probab = nn.Softmax(dim=1)(logits)
pred = pred_probab.argmax(1)

# 可視化
img = test_features[0].squeeze()    # 將長度為1的維度去掉
img = img.to('cpu')              # 繪圖時,需要將張量轉回到CPU當中
true_label = test_labels[0]   # 標簽是否轉移到CPU無所謂,因為沒有用于plt的方法中
plt.imshow(img, cmap="gray")
plt.show()
print(f"True label: {true_label}")
print(f"Predict: {pred.item()}")

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/295075.html

標籤:AI

上一篇:樹莓派4b基本配置(系統+無顯示幕+opencv)

下一篇:入門機器學習——從線性回歸開始理解機器學習

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more