Task02-Pytorch的各個(gè)組件和實(shí)戰(zhàn)

import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import torch.optim as optimizer

基本配置

# 批次大小
batch_size = 16
# 學(xué)習(xí)率
lr = 1e-4
# 最大迭代次數(shù)
max_epochs = 100
# 設(shè)置GPU
# 方式1
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'

# 方式2,后續(xù)對(duì)要使用GPU的變量用.to(device)
device = torch.device('cuda:1' if torch.cuda.is_available() else "cpu")

數(shù)據(jù)讀入

通過Dataset+DataLoader方式完成

Dataset類主要包含三個(gè)函數(shù):

  • init: 傳入外部參數(shù),初始化
  • getitem: 逐個(gè)讀取樣本
  • len: 返回樣本數(shù)
import torch
from torchvision import datasets

# 數(shù)據(jù)集
train_data = datasets.ImageFolder(train_path, transform=data_transform)
val_data = datasets.ImageFolder(val_path, transform=data_transform)

# 按批次讀入數(shù)據(jù)
from torch.utils.data import DataLoader

train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, num_workers=4, shuffle=False)
# 可視化下一個(gè)樣本圖片
import matplotlib.pyplot as plt
images, labels = next(iter(val_loader))
print(images.shape)
plt.imshow(images[0].transpose(1,2,0))
plt.show()
---------------------------------------------------------------------------

ModuleNotFoundError                       Traceback (most recent call last)

~\AppData\Local\Temp\ipykernel_21680\1158052838.py in <module>
      1 # 可視化下一個(gè)樣本圖片
----> 2 import matplotlib.pyplot as plt
      3 images, labels = next(iter(val_loader))
      4 print(images.shape)
      5 plt.imshow(images[0].transpose(1,2,0))


ModuleNotFoundError: No module named 'matplotlib'
class MyDataset(Dataset):
    def __init__(self, data_dir, info_csv, image_list, transform=None):
        """
        Args:
            data_dir: path to image directory
            info_csv: path to labels
            image_list: path to txt file contains image names
            transform: optional transform to be applied on a sample
        """
        label_info = pd.read_csv(info_csv)
        image_file = open(image_list).readlines()
        self.data_dir = data_dir
        self.image_file = image_file
        self.label_info = label_info
        self.transform = transform
        
    def __getitem__(self, index):
        image_name = self.image_file[index].strip('\n')
        raw_label = self.label_info.loc[self.label_info['Image_index'] == image_name]
        label = raw_label.iloc[:,0]
        image_name = os.path.join(self.data_dir, image_name)
        image = Image.open(image_name).convert('RGB')
        if self.transform is not None:
            image = self.transform(image)
        return image, label

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

模型構(gòu)建

神經(jīng)網(wǎng)絡(luò)的構(gòu)造

import torch
from torch import nn

class MLP(nn.Module):
    # 聲明帶有模型參數(shù)的層
    def __init__(self, **kwargs):
        # 繼承父類時(shí)初始化
        super(MLP, self).__init__(**kwargs)
        self.hidden = nn.Linear(784, 256)  # 輸入樣本784個(gè)var, 輸出n * 256
        self.act = nn.ReLU()  # Relu激活函數(shù)
        self.output = nn.Linear(256, 10)  # 輸出n * 10
        
    # 定義模型的前向計(jì)算,輸入x計(jì)算返回值
    def forward(self, x):
        o = self.act(self.hidden(x))
        return self.output(o)
    
X = torch.rand(2, 784)
net = MLP()
print(net)
net(X)
MLP(
  (hidden): Linear(in_features=784, out_features=256, bias=True)
  (act): ReLU()
  (output): Linear(in_features=256, out_features=10, bias=True)
)





tensor([[-0.1357, -0.1145,  0.0626,  0.0197,  0.0813,  0.0714, -0.0941, -0.0946,
         -0.2517,  0.0006],
        [ 0.0474, -0.1632,  0.1313, -0.0293,  0.1087,  0.0678, -0.0179, -0.2159,
         -0.1594, -0.1324]], grad_fn=<AddmmBackward0>)

神經(jīng)網(wǎng)絡(luò)常見的層

自定義層

# 不含參數(shù)的層
import torch 
from torch import nn

class MyLayer(nn.Module):
    def __init__(self, **kwargs):
        super(MyLayer, self).__init__(**kwargs)
        
    def forward(self, x):
        return x - x.mean()
    
layer = MyLayer()
layer(torch.tensor([1, 2, 3, 4, 5], dtype=torch.float))
tensor([-2., -1.,  0.,  1.,  2.])
# 含模型參數(shù)的層 
class MyListDense(nn.Module):
    def __init__(self):
        super(MyListDense, self).__init__()
        # 也可以用nn.ParameterDict
        self.params = nn.ParameterList([nn.Parameter(torch.randn(4, 4)) for i in range(3)])
        self.params.append(nn.Parameter(torch.randn(4, 1)))
        
    def forward(self, x):
        for i in range(len(self.params)):
            x = torch.mm(x, self.params[i])
        return x

二維卷積層

import torch
from torch import nn
def corr2d(X, K):
    h, w = K.shape
    X, K = X.float(), K.float()
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            Y[i, j] = (X[i:i+h, j:j+w] * K).sum()
    return Y        
    
# 二維卷積層
class Conv2D(nn.Module):
    def __init__(self, kernel_size):
        super(Conv2D, self).__init__()
        self.weight = nn.Parameter(torch.randn(kernel_size))
        self.bias = nn.Parameter(torch.randn(1))
    
    def forward(self, x):
        return corr2d(x, self.weight) + self.bias
import torch
from torch import nn

# 定義一個(gè)函數(shù)來計(jì)算卷積層。它對(duì)輸入和輸出做相應(yīng)的升維和降維
import torch
from torch import nn

# 定義一個(gè)函數(shù)來計(jì)算卷積層。它對(duì)輸入和輸出做相應(yīng)的升維和降維
def comp_conv2d(conv2d, X):
    # (1, 1)代表批量大小和通道數(shù)
    X = X.view((1, 1) + X.shape)  # 拼接成4維數(shù)組
    Y = conv2d(X)
    return Y.view(Y.shape[2:]) # 排除不關(guān)心的前兩維:批量和通道


# 注意這里是兩側(cè)分別填充1?或列,所以在兩側(cè)一共填充2?或列
conv2d = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3,padding=1)

X = torch.rand(8, 8)
comp_conv2d(conv2d, X).shape
torch.Size([8, 8])

池化層

import torch
from torch import nn

def pool2d(X, pool_size, mode='max'):
    p_h, p_w = pool_size
    Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            if mode == 'max':
                Y[i, j] = X[i: i + p_h, j: j + p_w].max()
            elif mode == 'avg':
                Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
    return Y
X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]], dtype=torch.float)
pool2d(X, (2, 2))
tensor([[4., 5.],
        [7., 8.]])

AlexNet模型示例

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(1, 96, 11, 4), # in_channels, out_channels, kernel_size, stride, padding
            nn.ReLU(),
            nn.MaxPool2d(3, 2), # kernel_size, stride
            # 減小卷積窗口,使用填充為2來使得輸入與輸出的高和寬一致,且增大輸出通道數(shù)
            nn.Conv2d(96, 256, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(3, 2),
            # 連續(xù)3個(gè)卷積層,且使用更小的卷積窗口。除了最后的卷積層外,進(jìn)一步增大了輸出通道數(shù)。
            # 前兩個(gè)卷積層后不使用池化層來減小輸入的高和寬
            nn.Conv2d(256, 384, 3, 1, 1),
            nn.ReLU(),
            nn.Conv2d(384, 384, 3, 1, 1),
            nn.ReLU(),
            nn.Conv2d(384, 256, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(3, 2)
        )
         # 這里全連接層的輸出個(gè)數(shù)比LeNet中的大數(shù)倍。使用丟棄層來緩解過擬合
        self.fc = nn.Sequential(
            nn.Linear(256*5*5, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            # 輸出層。由于這里使用Fashion-MNIST,所以用類別數(shù)為10,而非論文中的1000
            nn.Linear(4096, 10),
        )

    def forward(self, img):
        feature = self.conv(img)
        output = self.fc(feature.view(img.shape[0], -1))
        return output
net = AlexNet()
print(net)
AlexNet(
  (conv): Sequential(
    (0): Conv2d(1, 96, kernel_size=(11, 11), stride=(4, 4))
    (1): ReLU()
    (2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
    (3): Conv2d(96, 256, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (4): ReLU()
    (5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Conv2d(256, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (7): ReLU()
    (8): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (9): ReLU()
    (10): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU()
    (12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (fc): Sequential(
    (0): Linear(in_features=6400, out_features=4096, bias=True)
    (1): ReLU()
    (2): Dropout(p=0.5, inplace=False)
    (3): Linear(in_features=4096, out_features=4096, bias=True)
    (4): ReLU()
    (5): Dropout(p=0.5, inplace=False)
    (6): Linear(in_features=4096, out_features=10, bias=True)
  )
)

模型初始化

torch.nn.init

import torch
import torch.nn as nn

conv = nn.Conv2d(1,3,3)
linear = nn.Linear(10,1)

# 查看隨機(jī)初始化的conv參數(shù)
print(conv.weight.data)
# 查看linear的參數(shù)
print(linear.weight.data)
tensor([[[[ 0.1268, -0.0083, -0.1545],
          [ 0.2284,  0.0016, -0.1577],
          [ 0.0579, -0.0849, -0.2911]]],



        [[[ 0.0605,  0.1680,  0.1811],
          [-0.1920, -0.1370,  0.2808],
          [ 0.0944,  0.1277, -0.1246]]],



        [[[ 0.2819, -0.2207,  0.0406],
          [ 0.2280, -0.0377,  0.2743],
          [ 0.3203, -0.0189, -0.0889]]]])
tensor([[-0.1381, -0.1385,  0.1530,  0.0018, -0.0307, -0.0804, -0.2288,  0.1726,
          0.0550,  0.1364]])
# 對(duì)conv進(jìn)行kaiming初始化
torch.nn.init.kaiming_normal_(conv.weight.data)
print(conv.weight.data)
# 對(duì)linear進(jìn)行常數(shù)初始化
torch.nn.init.constant_(linear.weight.data,0.3)
print(linear.weight.data)
tensor([[[[-0.0482,  0.4568, -0.0451],
          [ 0.5877,  0.1045, -0.0692],
          [ 0.6888,  0.0516,  0.1558]]],



        [[[ 0.2490, -0.1846, -0.2468],
          [-0.1517, -1.1788,  0.1218],
          [ 0.3340, -0.4299,  0.1132]]],



        [[[ 0.1487, -0.4090,  0.6382],
          [ 0.4713, -0.3765,  0.4769],
          [ 0.0353,  0.2126, -0.0899]]]])
tensor([[0.3000, 0.3000, 0.3000, 0.3000, 0.3000, 0.3000, 0.3000, 0.3000, 0.3000,
         0.3000]])

初始化函數(shù)的封裝

def initialize_weights(self):
    for m in self.modules():
        # 判斷是否屬于Conv2d
        if isinstance(m, nn.Conv2d):
            torch.nn.init.xavier_normal_(m.weight.data)
            # 判斷是否有偏置
            if m.bias is not None:
                torch.nn.init.constant_(m.bias.data,0.3)
        elif isinstance(m, nn.Linear):
            torch.nn.init.normal_(m.weight.data, 0.1)
            if m.bias is not None:
                torch.nn.init.zeros_(m.bias.data)
        elif isinstance(m, nn.BatchNorm2d):
            m.weight.data.fill_(1)       
            m.bias.data.zeros_()    
# 模型的定義
class MLP(nn.Module):
  # 聲明帶有模型參數(shù)的層,這里聲明了兩個(gè)全連接層
  def __init__(self, **kwargs):
    # 調(diào)用MLP父類Block的構(gòu)造函數(shù)來進(jìn)行必要的初始化。這樣在構(gòu)造實(shí)例時(shí)還可以指定其他函數(shù)
    super(MLP, self).__init__(**kwargs)
    self.hidden = nn.Conv2d(1,1,3)
    self.act = nn.ReLU()
    self.output = nn.Linear(10,1)
    
   # 定義模型的前向計(jì)算,即如何根據(jù)輸入x計(jì)算返回所需要的模型輸出
  def forward(self, x):
    o = self.act(self.hidden(x))
    return self.output(o)

mlp = MLP()
print(list(mlp.parameters()))
print("-------初始化-------")

initialize_weights(mlp)
print(list(mlp.parameters()))
[Parameter containing:
tensor([[[[-0.0073,  0.1146, -0.0839],
          [ 0.2442, -0.1130,  0.0334],
          [-0.2217,  0.1572,  0.1452]]]], requires_grad=True), Parameter containing:
tensor([0.1457], requires_grad=True), Parameter containing:
tensor([[ 0.2057, -0.1126, -0.2133, -0.1641,  0.0404, -0.0776, -0.2792, -0.2329,
          0.0734, -0.0483]], requires_grad=True), Parameter containing:
tensor([-0.2791], requires_grad=True)]
-------初始化-------
[Parameter containing:
tensor([[[[ 0.2118, -0.0682,  0.1048],
          [ 0.1412,  0.4079,  0.0789],
          [ 0.2269, -0.1477, -0.2181]]]], requires_grad=True), Parameter containing:
tensor([0.3000], requires_grad=True), Parameter containing:
tensor([[-0.1874,  0.3172, -0.6525, -1.3967,  0.7327,  0.5498, -0.3125,  0.5182,
          0.2366,  0.0422]], requires_grad=True), Parameter containing:
tensor([0.], requires_grad=True)]

損失函數(shù)

二分類交叉熵?fù)p失函數(shù)-示例

import torch.nn as nn

m = nn.Sigmoid()
loss = nn.BCELoss()
input = torch.randn(3, requires_grad=True)
target = torch.empty(3).random_(2)
output = loss(m(input), target)
output.backward()

print('BCELoss損失函數(shù)的計(jì)算結(jié)果為',output)
BCELoss損失函數(shù)的計(jì)算結(jié)果為 tensor(1.3916, grad_fn=<BinaryCrossEntropyBackward0>)

各種損失函數(shù)

# 二分類交叉熵?fù)p失函數(shù)
torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
# 交叉熵?fù)p失函數(shù)
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
# L1損失函數(shù)
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
# MSE損失函數(shù)
torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
# 平滑L1 (Smooth L1)損失函數(shù)
torch.nn.SmoothL1Loss(size_average=None, reduce=None, reduction='mean', beta=1.0)
# 目標(biāo)泊松分布的負(fù)對(duì)數(shù)似然損失
torch.nn.PoissonNLLLoss(log_input=True, full=False, size_average=None, eps=1e-08, reduce=None, reduction='mean')
# KL散度
torch.nn.KLDivLoss(size_average=None, reduce=None, reduction='mean', log_target=False)
# MarginRankingLoss:計(jì)算兩個(gè)向量之間的相似度,用于排序任務(wù)
torch.nn.MarginRankingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')
# 多標(biāo)簽邊界損失函數(shù)-多標(biāo)簽邊界損失函數(shù)
torch.nn.MultiLabelMarginLoss(size_average=None, reduce=None, reduction='mean')
# 二分類損失函數(shù)
torch.nn.SoftMarginLoss(size_average=None, reduce=None, reduction='mean')
# 多分類的折頁損失
torch.nn.MultiMarginLoss(p=1, margin=1.0, weight=None, size_average=None, reduce=None, reduction='mean')
# 三元組損失
torch.nn.TripletMarginLoss(margin=1.0, p=2.0, eps=1e-06, swap=False, size_average=None, reduce=None, reduction='mean')
# HingEmbeddingLoss-對(duì)輸出的embedding結(jié)果做Hing損失計(jì)算
torch.nn.HingeEmbeddingLoss(margin=1.0, size_average=None, reduce=None, reduction='mean')
# 余弦相似度
torch.nn.CosineEmbeddingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')
# CTC損失函數(shù)
torch.nn.CTCLoss(blank=0, reduction='mean', zero_infinity=False)
CTCLoss()

訓(xùn)練和評(píng)估

def train(epoch):
    model.train()
    train_loss = 0
    for data, label in train_loader:
        data, label = data.cuda(), label.cuda()
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(label, output)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()*data.size(0)
    train_loss = train_loss/len(train_loader.dataset)
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))

def val(epoch):       
    model.eval()
    val_loss = 0
    with torch.no_grad():
        for data, label in val_loader:
            data, label = data.cuda(), label.cuda()
            output = model(data)
            preds = torch.argmax(output, 1)
            loss = criterion(output, label)
            val_loss += loss.item()*data.size(0)
            running_accu += torch.sum(preds == label.data)
    val_loss = val_loss/len(val_loader.dataset)
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, val_loss))

Pytorch優(yōu)化器

  • torch.optim.ASGD
  • torch.optim.Adadelta
  • torch.optim.Adagrad
  • torch.optim.Adam
  • torch.optim.AdamW
  • torch.optim.Adamax
  • torch.optim.LBFGS
  • torch.optim.RMSprop
  • torch.optim.Rprop
  • torch.optim.SGD
  • torch.optim.SparseAdam

Optimizer有三個(gè)屬性:

  • defaults:存儲(chǔ)的是優(yōu)化器的超參數(shù)
  • state:參數(shù)的緩存
  • param_groups:管理的參數(shù)組,是一個(gè)list,其中每個(gè)元素是一個(gè)字典,順序是params,lr,momentum,dampening,weight_decay,nesterov

還有以下方法:

  • zero_grad(): 清空所管理參數(shù)的梯度,PyTorch的特性是張量的梯度不自動(dòng)清零,因此每次反向傳播后都需要清空梯度。
  • step():執(zhí)行一步梯度更新,參數(shù)更新
  • load_state_dict() :加載狀態(tài)參數(shù)字典,可以用來進(jìn)行模型的斷點(diǎn)續(xù)訓(xùn)練,繼續(xù)上次的參數(shù)進(jìn)行訓(xùn)練
  • state_dict():獲取優(yōu)化器當(dāng)前狀態(tài)信息字典

每個(gè)優(yōu)化器都是一個(gè)類,我們一定要進(jìn)行實(shí)例化才能使用

optimizer在一個(gè)神經(jīng)網(wǎng)絡(luò)的epoch中需要實(shí)現(xiàn)下面兩個(gè)步驟:

梯度置零

梯度更新

for epoch in range(EPOCH):
    ...
    optimizer.zero_grad()  #梯度置零
    loss = ...             #計(jì)算loss
    loss.backward()        #BP反向傳播
    optimizer.step()       #梯度更新

基礎(chǔ)實(shí)戰(zhàn)—FashionMNIST時(shí)裝分類

import os
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader

配置訓(xùn)練環(huán)境和超參數(shù)

# 配置GPU,這里有兩種方式
## 方案一:使用os.environ
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
# 方案二:使用“device”,后續(xù)對(duì)要使用GPU的變量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")

## 配置其他超參數(shù),如batch_size, num_workers, learning rate, 以及總的epochs
batch_size = 4
num_workers = 0   # 對(duì)于Windows用戶,這里應(yīng)設(shè)置為0,否則會(huì)出現(xiàn)多線程錯(cuò)誤
lr = 1e-4
epochs = 2

數(shù)據(jù)讀入和加載

# 首先設(shè)置數(shù)據(jù)變換
from torchvision import transforms

image_size = 28
data_transform = transforms.Compose([
    transforms.ToPILImage(),  
     # 這一步取決于后續(xù)的數(shù)據(jù)讀取方式,如果使用內(nèi)置數(shù)據(jù)集讀取方式則不需要
    transforms.Resize(image_size),
    transforms.ToTensor()
])
## 讀取方式一:使用torchvision自帶數(shù)據(jù)集,下載可能需要一段時(shí)間
from torchvision import datasets

train_data = datasets.FashionMNIST(root='./', train=True, download=True, transform=data_transform)
test_data = datasets.FashionMNIST(root='./', train=False, download=True, transform=data_transform)
## 讀取方式二:讀入csv格式的數(shù)據(jù),自行構(gòu)建Dataset類
# csv數(shù)據(jù)下載鏈接:https://www.kaggle.com/zalando-research/fashionmnist
class FMDataset(Dataset):
    def __init__(self, df, transform=None):
        self.df = df
        self.transform = transform
        self.images = df.iloc[:,1:].values.astype(np.uint8)
        self.labels = df.iloc[:, 0].values
        
    def __len__(self):
        return len(self.images)
    
    def __getitem__(self, idx):
        image = self.images[idx].reshape(28,28,1)
        label = int(self.labels[idx])
        if self.transform is not None:
            image = self.transform(image)
        else:
            image = torch.tensor(image/255., dtype=torch.float)
        label = torch.tensor(label, dtype=torch.long)
        return image, label

train_df = pd.read_csv("../data/FashionMNIST/fashion-mnist_train.csv")
test_df = pd.read_csv("../data/FashionMNIST/fashion-mnist_test.csv")
train_data = FMDataset(train_df, data_transform)
test_data = FMDataset(test_df, data_transform)
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=num_workers, drop_last=True)
test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=num_workers)
# 這里不知道為啥總是服務(wù)器掛掉,后面再看看
import matplotlib.pyplot as plt
image, label = next(iter(train_loader))
print(image.shape, label.shape)
plt.imshow(image[0][0], cmap="gray")
torch.Size([4, 1, 28, 28]) torch.Size([4])
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(1, 32, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2),
            nn.Dropout(0.3),
            nn.Conv2d(32, 64, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2),
            nn.Dropout(0.3)
        )
        self.fc = nn.Sequential(
            nn.Linear(64*4*4, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )
        
    def forward(self, x):
        x = self.conv(x)
        x = x.view(-1, 64*4*4)
        x = self.fc(x)
        # x = nn.functional.normalize(x)
        return x

model = Net()
model = model.cuda()
# model = nn.DataParallel(model).cuda()   # 多卡訓(xùn)練時(shí)的寫法,之后的課程中會(huì)進(jìn)一步講解

設(shè)定損失函數(shù)

criterion = nn.CrossEntropyLoss()

設(shè)定優(yōu)化器

optimizer = optim.Adam(model.parameters(), lr=0.001)

訓(xùn)練和測試驗(yàn)證

def train(epoch):
    model.train()
    train_loss = 0
    for data, label in train_loader:
        data, label = data.cuda(), label.cuda()
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, label)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()*data.size(0)
    train_loss = train_loss/len(train_loader.dataset)
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))
    
def val(epoch):       
    model.eval()
    val_loss = 0
    gt_labels = []
    pred_labels = []
    with torch.no_grad():
        for data, label in test_loader:
            data, label = data.cuda(), label.cuda()
            output = model(data)
            preds = torch.argmax(output, 1)
            gt_labels.append(label.cpu().data.numpy())
            pred_labels.append(preds.cpu().data.numpy())
            loss = criterion(output, label)
            val_loss += loss.item()*data.size(0)
    val_loss = val_loss/len(test_loader.dataset)
    gt_labels, pred_labels = np.concatenate(gt_labels), np.concatenate(pred_labels)
    acc = np.sum(gt_labels==pred_labels)/len(pred_labels)
    print('Epoch: {} \tValidation Loss: {:.6f}, Accuracy: {:6f}'.format(epoch, val_loss, acc))
for epoch in range(1, epochs+1):
    train(epoch)
    val(epoch)
Epoch: 1    Training Loss: 0.505714
Epoch: 1    Validation Loss: 0.371850, Accuracy: 0.864200
Epoch: 2    Training Loss: 0.393027
Epoch: 2    Validation Loss: 0.329903, Accuracy: 0.878400

模型保存

save_path = "./FahionModel.pkl"
torch.save(model, save_path)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容