Pytorch入门教程

Pytorch入门教程

👨‍💻 作者简介:大数据专业硕士在读,CSDN人工智能领域博客专家,阿里云专家博主,专注大数据与人工智能知识分享, 公众号:GoAI的学习小屋,免费分享书籍、简历、导图等资料,更有交流群分享AI和大数据,加群方式公众号回复”加群”或➡️点击链接
🎉 专栏推荐:➡️ 点击访问《计算机视觉》:长期更新不限于深度学习、OCR、目标检测、图像分类、分割等方向总结资料。 ➡️ 点击访问《深入浅出OCR》: 对标全网最全OCR教程。以上目前活动仅29.9,感兴趣小伙伴可关注下。
🎉 学习者福利:强烈推荐一个优秀AI学习网站,包括机器学习、深度学习等理论与实战教程,非常适合AI学习者。➡️网站链接
🎉 技术控福利:程序员兼职社区招募!技术范围广,CV、NLP方向均可,要求有一定基础,最好是研究生及以上或有工作经验,欢迎大佬加入!群内Python、c++、Matlab等各类编程语言单应有尽有, 资源靠谱、费用自谈,有意向直接➡️访问

✨Pytorch网站推荐:红黑鸟网,专注于高等数学和人工智能。

Pytorch学习总结:

  1. Pytorch实战笔记_GoAI的博客-CSDN博客

  2. Pytorch入门教程_GoAI的博客-CSDN博客

Pytorch笔记 目录:

PyTorch学习笔记(一):PyTorch环境安装

PyTorch学习笔记(二):简介与基础知识

PyTorch学习笔记(三):PyTorch主要组成模块

PyTorch学习笔记(四):PyTorch基础实战

PyTorch学习笔记(五):模型定义、修改、保存

PyTorch学习笔记(六):PyTorch进阶训练技巧

PyTorch学习笔记(七):PyTorch可视化

PyTorch学习笔记(八):PyTorch生态简介

Pytorch学习笔记总结_大数据与人工智能技术分享-CSDN博客

Pytorch实战总结:

pytorch实战教学(一篇管够)

B站深度学习Pytorch实战总结

PyTorchB站刘二大人学习笔记

Pytorch简介

  • 概念:由Facebook人工智能研究小组开发的一种基于Lua编写的Torch库的Python实现的深度学习库。
  • 优势:简洁、上手快、具有良好的文档和社区支持、项目开源、支持代码调试、丰富的扩展库

Pytorch基础知识

1.张量Tensor

  • 分类:0维张量(标量)、1维张量(向量)、2维张量(矩阵)、3维张量(时间序列)、4维张量(图像)、5维张量(视频)
  • 概念:一个数据容器,可以包含数据、字符串等

张量是一种特殊的数据结构,与数组和矩阵非常相似。在PyTorch中,我们使用张量对模型的输入和输出以及模型的参数进行编码。

张量类似于NumPy的 ndarray,除了张量可以在 GPU 或其他硬件加速器上运行。事实上,张量和NumPy数组通常可以共享相同的底层内存,从而无需复制数据。

引入相关的包
import torch
import numpy as np

1.1 初始化张量

直接从数据创建:

张量可以直接从数据中创建。数据类型是自动推断的。

data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(f"Tensor from Data:\n {x_data} \n")

Tensor from Data:
 tensor([[1, 2],
        [3, 4]])

从 NumPy 数组创建:

np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print(f"Tensor from Numpy:\n {x_np} \n")

Tensor from Numpy:
 tensor([[1, 2],
        [3, 4]], dtype=torch.int32)

根据另一个张量创建:

新张量保留参数张量的属性(形状、数据类型),除非显式覆盖。

x_ones = torch.ones_like(x_data) # 保留原有张量的形状和数据类型
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float) # 显式更改张量的数据类型
print(f"Random Tensor: \n {x_rand} \n")

Ones Tensor:
 tensor([[1, 1],
        [1, 1]])
#
Random Tensor:
 tensor([[0.5890, 0.7234],
        [0.7145, 0.5141]])

使用随机或恒定值创建:

shape是张量维度的元组,它决定了输出张量的形状。

shape = (2,3,) # 创建2行3列的张量
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")

Random Tensor:
 tensor([[0.4492, 0.6411, 0.9363],
        [0.0917, 0.1566, 0.1965]])
#
Ones Tensor:
 tensor([[1., 1., 1.],
        [1., 1., 1.]])
#
Zeros Tensor:
 tensor([[0., 0., 0.],
        [0., 0., 0.]])

1.2 张量的属性

张量属性包括形状、数据类型和存储设备等。

tensor = torch.rand(3,4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")

Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu

1.3 张量的操作

PyTorch中有100 多种张量运算,包括算术、线性代数、矩阵操作(转置、索引、切片)、采样等,而且这些操作中都可以在 GPU 上运行(通常以比 CPU 更高的速度)。

默认情况下,张量是在 CPU 上创建的。我们需要使用 .to方法明确地将张量移动到 GPU(在检查 GPU 可用性之后)。

将张量移动到GPU上
if torch.cuda.is_available():
    tensor = tensor.to("cuda")

类似 numpy 的索引和切片:

tensor = torch.ones(4, 4)
print(f"First row: {tensor[0]}")
print(f"First column: {tensor[:, 0]}")
print(f"Last column: {tensor[..., -1]}")
tensor[:,1] = 0
print(tensor)

First row: tensor([1., 1., 1., 1.])
First column: tensor([1., 1., 1., 1.])
Last column: tensor([1., 1., 1., 1.])
tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

连接张量:

可以用 torch.cattorch.stack来拼接张量。

t1 = torch.cat([tensor, tensor, tensor], dim=1) # 在第1个维度拼接,即水平方向
print(t1)

tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])

算术运算:

矩阵相乘,y1、y2和y3的值相同
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)

y3 = torch.rand_like(tensor)
torch.matmul(tensor, tensor.T, out=y3)
print(y1)

tensor([[3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.]])
矩阵逐元素相乘,z1、z2和z3的值相同
z1 = tensor * tensor
z2 = tensor.mul(tensor)

z3 = torch.rand_like(tensor)
torch.mul(tensor, tensor, out=z3)
print(z1)
tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

单元素张量:

只有一个值的张量,可以通过 item属性转换为数值。

agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))

12.0 <class 'float'>
</class>

就地操作:

将结果存储到操作数中的操作称为就地操作。它们由 _后缀表示。例如: x.copy_(y), x.t_(), 会变 x的值。

print(f"{tensor} \n")
tensor.add_(5)
print(tensor)

tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])
#
tensor([[6., 5., 6., 6.],
        [6., 5., 6., 6.],
        [6., 5., 6., 6.],
        [6., 5., 6., 6.]])

就地操作可以节省一些内存,但在计算导数时可能会出现问题,因为会立即丢失历史记录。因此不建议使用。

1.4 张量与Numpy

在CPU上的张量和NumPy数组共享它们的内存位置,改变一个会改变另一个。

张量转换为NumPy数组:

t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")

t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]

改变张量的值,numpy数组的值也随之更改。

t.add_(1)
print(f"t: {t}")
print(f"n: {n}")

t: tensor([2., 2., 2., 2., 2.])
n: [2. 2. 2. 2. 2.]

NumPy数组转换为张量:

n = np.ones(5)
print(f"n: {n}")
t = torch.from_numpy(n)
print(f"t: {t}")

n: [1. 1. 1. 1. 1.]
t: tensor([1., 1., 1., 1., 1.], dtype=torch.float64)

改变numpy数组的值,张量的值也随之更改。

np.add(n, 2, out=n)
print(f"t: {t}")
print(f"n: {n}")

t: tensor([3., 3., 3., 3., 3.], dtype=torch.float64)
n: [3. 3. 3. 3. 3.]

2.数据集和数据加载器

在PyTorch中, torch.utils.data.DataLoadertorch.utils.data.Dataset 可以让我们方便使用预加载的数据集或者自己的数据集。 Dataset存储数据样本及其对应的标签,而 DataLoaderDataset包裹起来,生成一个可迭代对象,以便轻松访问数据样本。

PyTorch提供了很多预加载好的数据集(例如FashionMNIST),它们都继承自 torch.utils.data.Dataset这个类。

2.1 加载数据集

我们从TorchVision加载Fashion-MNIST数据集,Fashion-MNIST是Zalando文章图像的一个数据集,包含60000个训练样本和10000 个测试样本。每个样本都包含28×28的灰度图和对应的标签(共10个类别)。

我们使用以下参数加载 FashionMNIST数据集:

  • root是存储训练/测试数据的路径;
  • train指定训练或测试数据集;
  • download=True如果本机没有该数据集,则会下载数据到 root路径下;
  • transform对样本数据进行相应的处理;
  • target_transform对标签进行相应的处理。
import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt

&#x8BAD;&#x7EC3;&#x6570;&#x636E;&#x96C6;
training_data = datasets.FashionMNIST(
    root="data", # &#x6570;&#x636E;&#x96C6;&#x4E0B;&#x8F7D;&#x8DEF;&#x5F84;
    train=True, # True&#x4E3A;&#x8BAD;&#x7EC3;&#x96C6;&#xFF0C;False&#x4E3A;&#x6D4B;&#x8BD5;&#x96C6;
    download=True, # &#x662F;&#x5426;&#x8981;&#x4E0B;&#x8F7D;
    transform=ToTensor() # &#x5BF9;&#x6837;&#x672C;&#x6570;&#x636E;&#x8FDB;&#x884C;&#x5904;&#x7406;&#xFF0C;&#x8F6C;&#x6362;&#x4E3A;&#x5F20;&#x91CF;&#x6570;&#x636E;
)
&#x6D4B;&#x8BD5;&#x6570;&#x636E;&#x96C6;
test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

2.2 可视化数据集

我们可以根据索引在 Dataset中找到某一样本,比如 training_data[index]。我们用 matplotlib来可视化训练数据中的一些样本。

&#x6807;&#x7B7E;&#x5B57;&#x5178;&#xFF0C;&#x4E00;&#x4E2A;key&#x952E;&#x5BF9;&#x5E94;&#x4E00;&#x4E2A;label
labels_map = {
    0: "T-Shirt",
    1: "Trouser",
    2: "Pullover",
    3: "Dress",
    4: "Coat",
    5: "Sandal",
    6: "Shirt",
    7: "Sneaker",
    8: "Bag",
    9: "Ankle Boot",
}
&#x8BBE;&#x7F6E;&#x753B;&#x5E03;&#x5927;&#x5C0F;
figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3
for i in range(1, cols * rows + 1):
    # &#x968F;&#x673A;&#x751F;&#x6210;&#x4E00;&#x4E2A;&#x7D22;&#x5F15;
    sample_idx = torch.randint(len(training_data), size=(1,)).item()
    # &#x83B7;&#x53D6;&#x6837;&#x672C;&#x53CA;&#x5176;&#x5BF9;&#x5E94;&#x7684;&#x6807;&#x7B7E;
    img, label = training_data[sample_idx]
    # &#x6DFB;&#x52A0;&#x5B50;&#x56FE;
    figure.add_subplot(rows, cols, i)
    # &#x8BBE;&#x7F6E;&#x6807;&#x9898;
    plt.title(labels_map[label])
    # &#x4E0D;&#x663E;&#x793A;&#x5750;&#x6807;&#x8F74;
    plt.axis("off")
    # &#x663E;&#x793A;&#x7070;&#x5EA6;&#x56FE;
    plt.imshow(img.squeeze(), cmap="gray")
plt.show()

Pytorch入门教程

2.3 自定义数据集

在定义自己的数据集时,需要继承Dataset类,并实现三个函数: __init____len____getitem__

  • __init__:实例化Dataset对象时运行,完成初始化工作。
  • __len__:返回数据集的大小。
  • __getitem__:根据索引返回一个样本(数据和标签)。
import os
import pandas as pd
from torchvision.io import read_image

class CustomImageDataset(Dataset):
    def __init__(self, annotations_file, img_dir, transform=None, target_transform=None):
        # &#x8BFB;&#x53D6;&#x6807;&#x7B7E;&#x6587;&#x4EF6;
        self.img_labels = pd.read_csv(annotations_file)
        # &#x8BFB;&#x53D6;&#x56FE;&#x7247;&#x5B58;&#x50A8;&#x8DEF;&#x5F84;
        self.img_dir = img_dir
        # &#x6570;&#x636E;&#x5904;&#x7406;&#x65B9;&#x6CD5;
        self.transform = transform
        # &#x6807;&#x7B7E;&#x5904;&#x7406;&#x65B9;&#x6CD5;
        self.target_transform = target_transform

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

    def __getitem__(self, idx):
        # &#x5355;&#x5F20;&#x56FE;&#x7247;&#x8DEF;&#x5F84;
        img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0])
        # &#x8BFB;&#x53D6;&#x56FE;&#x7247;
        image = read_image(img_path)
        # &#x83B7;&#x5F97;&#x5BF9;&#x5E94;&#x7684;&#x6807;&#x7B7E;
        label = self.img_labels.iloc[idx, 1]
        if self.transform:
            image = self.transform(image)
        if self.target_transform:
            label = self.target_transform(label)
        # &#x8FD4;&#x56DE;&#x4E00;&#x4E2A;&#x5143;&#x7EC4;
        return image, label

labels.csv 文件如下所示:

tshirt1.jpg, 0
tshirt2.jpg, 0
......

ankleboot999.jpg, 9

2.4 数据加载器

2.4.1 torch.utils.data.DataLoader

根据数据集生成一个可迭代的对象,用于模型训练。

常用参数:

  • dataset (Dataset) :定义好的数据集。
  • batch_size (int, optional):每次放入网络训练的批次大小,默认为1.

  • shuffle (bool, optional) :是否打乱数据的顺序,默认为False。一般训练集设置为True,测试集设置为False。

  • num_workers (int, optional) :线程数,默认为0。 在Windows下设置大于0的数可能会报错。
  • drop_last (bool, optional) :是否丢弃最后一个批次的数据,默认为False。

两个工具包,可配合DataLoader使用:

  • enumerate(iterable, start=0):输入是一个可迭代的对象和下标索引开始值;返回可迭代对象的下标索引和数据本身。
  • tqdm(iterable):进度条可视化工具包
from torch.utils.data import DataLoader

data_loader = DataLoader(
    dataset=MyDataset,
    batch_size=16,
    shuffle=True,
    num_workers=0,
    drop_last=False,
)

2.4.2 加载数据

在训练模型时,我们通常希望以 &#x5C0F;&#x6279;&#x91CF;的形式传递样本,这样可以减少模型的过拟合。

from torch.utils.data import DataLoader

train_dataloader = DataLoader(
    dataset=training_data,
    # &#x8BBE;&#x7F6E;&#x6279;&#x91CF;&#x5927;&#x5C0F;
    batch_size=64,
    # &#x6253;&#x4E71;&#x6837;&#x672C;&#x7684;&#x987A;&#x5E8F;
    shuffle=True)
test_dataloader = DataLoader(
    dataset=test_data,
    batch_size=64,
    shuffle=True)

2.4.3 遍历DataLoader

将数据加载到 DataLoader后,每次迭代一批样本数据和标签(这里批量大小为64),且样本顺序是被打乱的。

&#x5C55;&#x793A;&#x56FE;&#x7247;&#x548C;&#x6807;&#x7B7E;
train_features, train_labels = next(iter(train_dataloader))
(B,N,H,W)
print(f"Feature batch shape: {train_features.size()}")
print(f"Labels batch shape: {train_labels.size()}")
&#x83B7;&#x53D6;&#x7B2C;&#x4E00;&#x5F20;&#x56FE;&#x7247;&#xFF0C;&#x53BB;&#x9664;&#x7B2C;&#x4E00;&#x4E2A;&#x6279;&#x91CF;&#x7EF4;&#x5EA6;
img = train_features[0].squeeze()
label = train_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"Label: {label}")

Feature batch shape: torch.Size([64, 1, 28, 28])
Labels batch shape: torch.Size([64])
Label: 8

Pytorch入门教程

3.torchvision.transforms图片处理

原始的数据格式不一定符合模型训练所要求的输入格式,我们使用 torchvision.transforms来对数据进行一些操作并使其适合训练。

PyTorch官方的例子如下:

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

ds = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
    # Lambda&#x53D8;&#x6362;&#xFF0C;&#x5B9A;&#x4E49;&#x4E86;&#x4E00;&#x4E2A;&#x51FD;&#x6570;&#x6765;&#x5C06;&#x6574;&#x6570;&#x8F6C;&#x6362;&#x4E3A;one-hot&#x7F16;&#x7801;&#x5F20;&#x91CF;
    # &#x5B83;&#x9996;&#x5148;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x5927;&#x5C0F;&#x4E3A;10&#x7684;&#x96F6;&#x5F20;&#x91CF;&#xFF08;&#x6570;&#x636E;&#x96C6;&#x4E2D;&#x7684;&#x6807;&#x7B7E;&#x6570;&#x91CF;&#xFF09;&#x5E76;&#x8C03;&#x7528;scatter_&#xFF0C;&#x6839;&#x636E;&#x7D22;&#x5F15;y&#x5C06;&#x503C;&#x66F4;&#x6539;&#x4E3A;1
    target_transform = Lambda(lambda y: torch.zeros(10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1))
)

3.1 transfroms.ToTensor()

将PIL Image或者numpy.ndarray格式的数据转换为tensor格式,像素值大小缩放至区间[0., 1.]。

3.2 transforms.Normalize()

对输入进行标准化,传入均值(mean[1],…,mean[n])和标准差(std[1],…,std[n]),n与输入的维度相同。结果计算公式如下:

output[channel] = (input[channel] - mean[channel]) / std[channel]

3.3 transforms.ToPILImage()

将tensor或者numpy.ndarray格式的数据转换为PIL Image图片格式。

以下操作传入的输入格式可以为PIL Image或者tensor

3.4 transforms.Resize()

修改图片的尺寸。参数size可以是序列也可以是整数,如果传入序列,则修改后的图片尺寸和序列一致;如果传入整数,则等比例缩放图片。

原图:

Pytorch入门教程
from PIL import Image
from torchvision import transforms

img = Image.open('./images/cat.png')
resize = transforms.Resize(500) #  height < width, ouput size:(size, size * width / height)
img_resize=resize(img)
img_resize.show()

Pytorch入门教程
from PIL import Image
from torchvision import transforms

img = Image.open('./images/cat.png')
resize = transforms.Resize((500, 500)) # (Height,Width)
img_resize=resize(img)
img_resize.show()

Pytorch入门教程

3.5 transforms.CenterCrop()

中心裁剪图片。参数size可以是序列也可以是整数,如果传入序列,则裁剪后的图片尺寸和序列一致;如果传入整数,则裁剪尺寸长宽都为size的正方形。

from PIL import Image
from torchvision import transforms

img = Image.open('./images/cat.png')
centercrop = transforms.CenterCrop((400, 1000)) # (Height,Width)
img_centercrop=centercrop(img)
img_centercrop.show()

Pytorch入门教程
from PIL import Image
from torchvision import transforms

img = Image.open('./images/cat.png')
centercrop = transforms.CenterCrop(400)
img_centercrop=centercrop(img)
img_centercrop.show()

Pytorch入门教程

3.6 transforms.RandomCrop()

随机裁剪。参数size可以是序列也可以是整数,如果传入序列,则裁剪后的图片尺寸和序列一致;如果传入整数,则裁剪尺寸长宽都为size的正方形。

from PIL import Image
from torchvision import transforms

img = Image.open('./images/cat.png')
randomcrop = transforms.RandomCrop((400,500))
for i in range(5):
    img_randomcrop=randomcrop(img)
    img_randomcrop.show()

3.7 transforms.RandomResizedCrop()

将给定图像随机裁剪为不同的大小和宽高比,然后缩放所裁剪得到的图像为制定的大小。(即先随机采集,然后对裁剪得到的图像缩放为同一大小)

3.8 transforms.RandomHorizontalFlip()

有一定概率将图片水平翻转,默认概率为0.5。

3.9 transforms.RandomVerticalFlip()

有一定概率将图片垂直翻转,默认概率为0.5。

3.10 transforms.RandomRotation()

将图片旋转。参数degrees可以为序列或者数值,如果为序列,则旋转角度为(min_degree, max_degree);如果为数值,则旋转角度为(-degrees, +degrees)。

4.模型定义

torch.nn提供了构建神经网络所需的全部模块。

在接下来的部分中,我们将构建一个神经网络来对FashionMNIST数据集中的图像进行分类。

&#x5BFC;&#x5305;
import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

4.1 训练设备

在GPU或CPU上训练我们的模型。

device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using {device} device")

Using cuda device

4.2 定义模型

模型的定义需要继承基类 torch.nn.Module__init__函数初始化网络模型中的各种层; forward函数对输入数据进行相应的操作。

class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(in_features=28 * 28, out_features=512),
            nn.ReLU(),
            nn.Linear(in_features=512, out_features=512),
            nn.ReLU(),
            nn.Linear(in_features=512, out_features=10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

实例化 NeuralNetwork类,并将其移动到 device上。

model = NeuralNetwork().to(device)
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)
  )
)
#

我们可以将输入数据传入模型,会自动调用 forward函数。模型会返回一个10维张量,其中包含每个类的原始预测值。我们使用 nn.Softmax函数来预测类别的概率。

X = torch.rand(1, 28, 28, device=device)
logits = model(X) # &#x8C03;&#x7528;forward&#x51FD;&#x6570;
&#x5728;&#x7B2C;&#x4E00;&#x4E2A;&#x7EF4;&#x5EA6;&#x5E94;&#x7528;Softmax&#x51FD;&#x6570;
pred_probab = nn.Softmax(dim=1)(logits)
&#x6700;&#x5927;&#x6982;&#x7387;&#x503C;&#x5BF9;&#x5E94;&#x7684;&#x4E0B;&#x6807;
y_pred = pred_probab.argmax(1)
print(f"Predicted class: {y_pred}")

Predicted class: tensor([6], device='cuda:0')

4.3 网络模型中的各种层

我们随机生成3张大小为 28×28 的图像的小批量样本,观察每一层对输入数据处理的结果。

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

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

4.3.1 nn.Flatten

nn.Flatten层以将每个大小为28×28的图像转换为784个像素值的连续数组(保持批量维度(dim=0))。

flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size())

torch.Size([3, 784])

4.3.2 nn.Linear

线性层使用其存储的权重 w和偏差 b对输入应用线性变换。

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

torch.Size([3, 20])

4.3.3 nn.ReLU

在线性变换后应用以引入非线性,帮助神经网络学习各种现象。(为什么要非线性激活?)

在这个模型中,我们在线性层之间使用 nn.ReLU,但是还有其他非线性激活函数。

print(f"Before ReLU: {hidden1}\n\n")
hidden1 = nn.ReLU()(hidden1)
print(f"After ReLU: {hidden1}")

# Before ReLU: tensor([[-0.3507, -0.6369, -0.5940, -0.0117, -0.3082, -0.1038, -0.3883,  0.2571,
         -0.1133, -0.2097,  0.0790,  0.5428,  0.1568, -0.0711,  0.2261, -0.1539,
         -0.1647,  0.3561, -0.4815,  0.1023],
        [-0.3312, -0.5616, -0.4370, -0.1231, -0.3780, -0.1435, -0.0156,  0.1988,
          0.1918, -0.0118,  0.2887,  0.4736,  0.1734, -0.2748, -0.2104, -0.3475,
         -0.3081,  0.2804, -0.3496, -0.2153],
        [-0.3788, -0.5419, -0.3950, -0.2872, -0.3738, -0.1630, -0.4928,  0.1045,
         -0.0048,  0.0190,  0.1196,  0.5370,  0.1651, -0.0557,  0.0320, -0.2687,
         -0.2733,  0.0873, -0.4730, -0.1157]], grad_fn=<addmmbackward>)
#
#
After ReLU: tensor([[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.2571, 0.0000,
         0.0000, 0.0790, 0.5428, 0.1568, 0.0000, 0.2261, 0.0000, 0.0000, 0.3561,
         0.0000, 0.1023],
        [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1988, 0.1918,
         0.0000, 0.2887, 0.4736, 0.1734, 0.0000, 0.0000, 0.0000, 0.0000, 0.2804,
         0.0000, 0.0000],
        [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1045, 0.0000,
         0.0190, 0.1196, 0.5370, 0.1651, 0.0000, 0.0320, 0.0000, 0.0000, 0.0873,
         0.0000, 0.0000]], grad_fn=<relubackward0>)
</relubackward0></addmmbackward>

引入非线性激活函数的原因:

非线性激活函数可以使神经网络逼近复杂函数。没有激活函数带来的非线性,多层神经网络和单层神经网络没有差别。

4.3.4 nn.Sequential

nn.Sequential可以理解为网络层的容器,在其中我们定义各种网络层,数据会按照我们设置的顺序经过所有网络层。

seq_modules = nn.Sequential(
    flatten,
    layer1,
    nn.ReLU(),
    nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image)

4.3.5 nn.Softmax

神经网络的最后一个线性层返回的logits,取值为[-infty, infty] 。在经过 nn.Softmax函数后,logits的值收敛到[0, 1],表示模型对每个类别的预测概率。 dim参数指示值必须总和为 1 的维度。

softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)

4.4 模型参数

使用 parameters()named_parameters()方法可以查看模型的参数。

print(f"Model structure: {model}\n\n")

for name, param in model.named_parameters():
    print(f"Layer: {name} | Size: {param.size()} | Values : {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)
  )
)
#
#
Layer: linear_relu_stack.0.weight | Size: torch.Size([512, 784]) | Values : tensor([[-0.0288,  0.0188,  0.0250,  ...,  0.0046, -0.0274,  0.0146],
        [-0.0206, -0.0101,  0.0202,  ..., -0.0311,  0.0117, -0.0185]],
       device='cuda:0', grad_fn=<slicebackward>)
#
Layer: linear_relu_stack.0.bias | Size: torch.Size([512]) | Values : tensor([ 0.0138, -0.0163], device='cuda:0', grad_fn=<slicebackward>)
#
Layer: linear_relu_stack.2.weight | Size: torch.Size([512, 512]) | Values : tensor([[-0.0135,  0.0426, -0.0293,  ..., -0.0370,  0.0320, -0.0346],
        [ 0.0127, -0.0163,  0.0221,  ...,  0.0236,  0.0304, -0.0343]],
       device='cuda:0', grad_fn=<slicebackward>)
#
Layer: linear_relu_stack.2.bias | Size: torch.Size([512]) | Values : tensor([0.0144, 0.0258], device='cuda:0', grad_fn=<slicebackward>)
#
Layer: linear_relu_stack.4.weight | Size: torch.Size([10, 512]) | Values : tensor([[ 0.0431,  0.0326,  0.0083,  ...,  0.0208, -0.0148,  0.0081],
        [ 0.0027,  0.0393, -0.0123,  ..., -0.0282, -0.0144, -0.0176]],
       device='cuda:0', grad_fn=<slicebackward>)
#
Layer: linear_relu_stack.4.bias | Size: torch.Size([10]) | Values : tensor([ 0.0229, -0.0096], device='cuda:0', grad_fn=<slicebackward>)
</slicebackward></slicebackward></slicebackward></slicebackward></slicebackward></slicebackward>

5.自动微分

在训练神经网络时,最常用的算法是 &#x53CD;&#x5411;&#x4F20;&#x64AD;&#x7B97;&#x6CD5;,模型参数会根据损失函数回传的梯度进行调整。为了计算这些梯度,PyTorch 有一个内置的微分引擎,称为 torch.autograd. 它支持任何计算图的梯度自动计算。

下面定义了最简单的一层神经网络,具有输入 x、参数 wb以及一些损失函数。

import torch

x = torch.ones(5)  # input tensor
y = torch.zeros(3)  # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)

5.1 计算图

上方代码的计算图如下:

Pytorch入门教程

在这个网络中, wb是我们需要优化的 参数,设置了 requires_grad=True属性。(可以在创建张量时设置该属性,也可以使用 x.requires_grad_(True)来设置)

构建计算图的函数是 Function类的一个对象。这个对象知道如何计算正向的函数以及如何在反向传播步骤中计算导数,可以通过张量的 grad_fn属性查看。

print(f"Gradient function for z = {z.grad_fn}")
print(f"Gradient function for loss = {loss.grad_fn}")

Gradient function for z = <addbackward0 object at 0x000001767e5750a0>
Gradient function for loss = <binarycrossentropywithlogitsbackward object at 0x000001767e5750a0>
</binarycrossentropywithlogitsbackward></addbackward0>

5.2 计算梯度

为了优化神经网络中参数的权重,我们需要计算损失函数对参数的导数。我们可以调用 loss.backward()来完成这一操作,在 w.gradb.grad中可以查看相应的导数值。

loss.backward()
print(w.grad)
print(b.grad)

tensor([[0.0342, 0.1329, 0.2091],
        [0.0342, 0.1329, 0.2091],
        [0.0342, 0.1329, 0.2091],
        [0.0342, 0.1329, 0.2091],
        [0.0342, 0.1329, 0.2091]])
tensor([0.0342, 0.1329, 0.2091])

5.3 不使用梯度跟踪

默认情况下,所有张量的属性都设置为 requires_grad=True,用来跟踪它们的计算历史并支持梯度计算。但是,在某些情况下我们不需要这样做,例如,模型训练完成后将其用于预测时,只需要前向计算即可。具体操作如下:

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

另一种方法是使用 detach()方法:

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

False

6.优化模型参数

训练模型是一个迭代过程;在每次迭代(epoch)中,模型对输出进行预测,首先计算猜测值与真实值的误差(损失),然后计算误差关于其参数的导数,最后使用梯度下降法优化这些参数。

2.&#x6570;&#x636E;&#x96C6;&#x548C;&#x6570;&#x636E;&#x52A0;&#x8F7D;&#x5668;4.&#x5B9A;&#x4E49;&#x6A21;&#x578B;的代码整合如下:

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda

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

6.1 超参数

超参数是可调整的参数,不同的超参数值会影响模型训练和收敛速度。

这次训练,我们定义了以下超参数:

  • 训练次数epochs:迭代数据集的次数。
  • 批处理大小batch_size:每次传入网络中的样本数量。
  • 学习率learning_rate:在每个批次更新模型参数的程度。较小的值会产生较慢的学习速度,而较大的值可能会导致训练期间出现不可预测的行为。
learning_rate = 1e-3
batch_size = 64
epochs = 5

6.2 优化循环

设置好超参数后,我们就可以使用优化循环来训练和优化我们的模型。

每个epoch包括以下两个循环:

  • 训练循环:迭代训练数据集并尝试收敛到最佳参数。
  • 验证/测试循环:迭代测试数据集以检查模型性能是否正在改善。

6.2.1 损失函数

损失函数用来衡量模型预测得到的结果与真实值的差异程度,损失值越小越好。

常见的损失函数包括用于回归任务的 nn.MSELoss(均方误差)和用于分类的 nn.NLLLoss(负对数似然)。 nn.CrossEntropyLoss结合 nn.LogSoftmaxnn.NLLLoss

这里我们将模型的输出logits传递给 nn.CrossEntropyLoss,进行归一化并计算预测误差。

&#x521D;&#x59CB;&#x5316;&#x635F;&#x5931;&#x51FD;&#x6570;
loss_fn = nn.CrossEntropyLoss()

6.2.2 优化器

优化是在每个训练步骤中调整模型参数以减少模型误差的过程。在这里,我们使用SGD优化器; torch,optim中提供了很多优化器,

例如ADAM和RMSProp。

&#x4F20;&#x5165;&#x9700;&#x8981;&#x4F18;&#x5316;&#x7684;&#x53C2;&#x6570;&#x548C;&#x5B66;&#x4E60;&#x7387;
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

6.2.3 实践

在训练循环中,优化分三个步骤进行:

  • 调用 optimizer.zero_grad()将模型参数的梯度归零。默认情况下梯度会累加。
  • 调用 loss.backward()来反向传播预测损失。PyTorch存储每个参数的损失梯度。
  • 计算梯度完成后,调用 optimizer.step()来调整参数。
&#x4F18;&#x5316;&#x6A21;&#x578B;&#x53C2;&#x6570;
def train_loop(dataloader, model, loss_fn, optimizer, device):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X = X.to(device)
        y = y.to(device)
        # &#x524D;&#x5411;&#x4F20;&#x64AD;&#xFF0C;&#x8BA1;&#x7B97;&#x9884;&#x6D4B;&#x503C;
        pred = model(X)
        # &#x8BA1;&#x7B97;&#x635F;&#x5931;
        loss = loss_fn(pred, y)
        # &#x53CD;&#x5411;&#x4F20;&#x64AD;&#xFF0C;&#x4F18;&#x5316;&#x53C2;&#x6570;
        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}]")

&#x6D4B;&#x8BD5;&#x6A21;&#x578B;&#x6027;&#x80FD;
def test_loop(dataloader, model, loss_fn, device):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X = X.to(device)
            y = y.to(device)
            # &#x524D;&#x5411;&#x4F20;&#x64AD;&#xFF0C;&#x8BA1;&#x7B97;&#x9884;&#x6D4B;&#x503C;
            pred = model(X)
            # &#x8BA1;&#x7B97;&#x635F;&#x5931;
            test_loss += loss_fn(pred, y).item()
            # &#x8BA1;&#x7B97;&#x51C6;&#x786E;&#x7387;
            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")

我们初始化损失函数和优化器,并将其传递给 train_looptest_loop

loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)

epochs = 10
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train_loop(train_dataloader, model, loss_fn, optimizer, device)
    test_loop(test_dataloader, model, loss_fn, device)
print("Done!")

...

Epoch 5
-------------------------------
loss: 1.214354  [    0/60000]
loss: 1.228768  [ 6400/60000]
loss: 1.314466  [12800/60000]
loss: 1.234377  [19200/60000]
loss: 1.242174  [25600/60000]
loss: 1.027974  [32000/60000]
loss: 1.062843  [38400/60000]
loss: 1.157571  [44800/60000]
loss: 1.091189  [51200/60000]
loss: 1.143303  [57600/60000]
Test Error:
 Accuracy: 64.6%, Avg loss: 1.092479
#
Done!

7.保存和加载模型

&#x5BFC;&#x5305;
import torch
import torchvision.models as models

7.1 保存和加载模型权重

PyTorch模型将学习到的参数存储在内部状态字典中,称为 state_dict

可以通过 torch.save 方法保存: torch.save(model.state_dict(),model_path)

加载模型分为两步:

  1. 先加载模型中的state_dict参数, state_dict=torch.load(model_path)
  2. 然后加载state_dict到定义好的模型中, model.load_state_dict(state_dict,strict=True/False),strict表示是否严格加载模型参数,load_state_dict()会返回missing_keys和unexpected_keys两个参数
&#x6837;&#x4F8B;&#x4EE3;&#x7801;&#x5982;&#x4E0B;
model = models.vgg16(pretrained=True) # pretrained=True&#x52A0;&#x8F7D;&#x9884;&#x8BAD;&#x7EC3;&#x597D;&#x7684;&#x53C2;&#x6570;
torch.save(model.state_dict(), 'model_weights.pth')

&#x8981;&#x52A0;&#x8F7D;&#x6A21;&#x578B;&#x6743;&#x91CD;&#xFF0C;&#x9996;&#x5148;&#x9700;&#x8981;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x76F8;&#x540C;&#x6A21;&#x578B;&#x7684;&#x5B9E;&#x4F8B;&#xFF0C;&#x7136;&#x540E;&#x4F7F;&#x7528;load_state_dict()&#x65B9;&#x6CD5;&#x52A0;&#x8F7D;&#x53C2;&#x6570;&#x3002;
model = models.vgg16() # &#x4E0D;&#x52A0;&#x8F7D;&#x9884;&#x8BAD;&#x7EC3;&#x597D;&#x7684;&#x53C2;&#x6570;
model.load_state_dict(torch.load('model_weights.pth'))
model.eval() # &#x5C06;&#x6A21;&#x578B;&#x8BBE;&#x7F6E;&#x4E3A;&#x6D4B;&#x8BD5;&#x6A21;&#x5F0F;&#xFF0C;&#x907F;&#x514D;dropout&#x548C;batch normalization&#x5BF9;&#x9884;&#x6D4B;&#x7ED3;&#x679C;&#x9020;&#x6210;&#x7684;&#x5F71;&#x54CD;

7.2 保存和加载整个模型

保存模型的结构和参数:

torch.save(model, 'model.pth')

加载模型:

model = torch.load('model.pth')

:这种方法在序列化模型时使用Python pickle模块。

8.样例代码

目录结构

code/
    data/
        FashionMNIST/
            processed/
            raw/
    example.py
import os
import matplotlib.pyplot as plt
from torchvision.transforms import ToTensor
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

&#x8BAD;&#x7EC3;&#x6570;&#x636E;&#x96C6;
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()  # &#x5BF9;&#x6837;&#x672C;&#x6570;&#x636E;&#x8FDB;&#x884C;&#x5904;&#x7406;&#xFF0C;&#x8F6C;&#x6362;&#x4E3A;&#x5F20;&#x91CF;&#x6570;&#x636E;
)
&#x6D4B;&#x8BD5;&#x6570;&#x636E;&#x96C6;
test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()  # &#x5BF9;&#x6837;&#x672C;&#x6570;&#x636E;&#x8FDB;&#x884C;&#x5904;&#x7406;&#xFF0C;&#x8F6C;&#x6362;&#x4E3A;&#x5F20;&#x91CF;&#x6570;&#x636E;
)
&#x6807;&#x7B7E;&#x5B57;&#x5178;&#xFF0C;&#x4E00;&#x4E2A;key&#x952E;&#x5BF9;&#x5E94;&#x4E00;&#x4E2A;label
labels_map = {
    0: "T-Shirt",
    1: "Trouser",
    2: "Pullover",
    3: "Dress",
    4: "Coat",
    5: "Sandal",
    6: "Shirt",
    7: "Sneaker",
    8: "Bag",
    9: "Ankle Boot",
}
&#x8BBE;&#x7F6E;&#x753B;&#x5E03;&#x5927;&#x5C0F;
figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3
for i in range(1, cols * rows + 1):
    # &#x968F;&#x673A;&#x751F;&#x6210;&#x4E00;&#x4E2A;&#x7D22;&#x5F15;
    sample_idx = torch.randint(len(training_data), size=(1,)).item()
    # &#x83B7;&#x53D6;&#x6837;&#x672C;&#x53CA;&#x5176;&#x5BF9;&#x5E94;&#x7684;&#x6807;&#x7B7E;
    img, label = training_data[sample_idx]
    figure.add_subplot(rows, cols, i)
    # &#x8BBE;&#x7F6E;&#x6807;&#x9898;
    plt.title(labels_map[label])
    # &#x4E0D;&#x663E;&#x793A;&#x5750;&#x6807;&#x8F74;
    plt.axis("off")
    # &#x663E;&#x793A;&#x7070;&#x5EA6;&#x56FE;
    plt.imshow(img.squeeze(), cmap="gray")
plt.show()

&#x8BAD;&#x7EC3;&#x6570;&#x636E;&#x52A0;&#x8F7D;&#x5668;
train_dataloader = DataLoader(
    dataset=training_data,
    # &#x8BBE;&#x7F6E;&#x6279;&#x91CF;&#x5927;&#x5C0F;
    batch_size=64,
    # &#x6253;&#x4E71;&#x6837;&#x672C;&#x7684;&#x987A;&#x5E8F;
    shuffle=True)
&#x6D4B;&#x8BD5;&#x6570;&#x636E;&#x52A0;&#x8F7D;&#x5668;
test_dataloader = DataLoader(
    dataset=test_data,
    batch_size=64,
    shuffle=True)
&#x5C55;&#x793A;&#x56FE;&#x7247;&#x548C;&#x6807;&#x7B7E;
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()
label = train_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"Label: {label}")

&#x6A21;&#x578B;&#x5B9A;&#x4E49;
class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(in_features=28 * 28, out_features=512),
            nn.ReLU(),
            nn.Linear(in_features=512, out_features=512),
            nn.ReLU(),
            nn.Linear(in_features=512, out_features=10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

&#x4F18;&#x5316;&#x6A21;&#x578B;&#x53C2;&#x6570;
def train_loop(dataloader, model, loss_fn, optimizer, device):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X = X.to(device)
        y = y.to(device)
        # &#x524D;&#x5411;&#x4F20;&#x64AD;&#xFF0C;&#x8BA1;&#x7B97;&#x9884;&#x6D4B;&#x503C;
        pred = model(X)
        # &#x8BA1;&#x7B97;&#x635F;&#x5931;
        loss = loss_fn(pred, y)
        # &#x53CD;&#x5411;&#x4F20;&#x64AD;&#xFF0C;&#x4F18;&#x5316;&#x53C2;&#x6570;
        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}]")

&#x6D4B;&#x8BD5;&#x6A21;&#x578B;&#x6027;&#x80FD;
def test_loop(dataloader, model, loss_fn, device):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0

    with torch.no_grad():
        for X, y in dataloader:
            X = X.to(device)
            y = y.to(device)
            # &#x524D;&#x5411;&#x4F20;&#x64AD;&#xFF0C;&#x8BA1;&#x7B97;&#x9884;&#x6D4B;&#x503C;
            pred = model(X)
            # &#x8BA1;&#x7B97;&#x635F;&#x5931;
            test_loss += loss_fn(pred, y).item()
            # &#x8BA1;&#x7B97;&#x51C6;&#x786E;&#x7387;
            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")

if __name__ == '__main__':
    device = "cuda" if torch.cuda.is_available() else "cpu"
    print(f"Using {device} device")
    # &#x5B9A;&#x4E49;&#x6A21;&#x578B;
    model = NeuralNetwork().to(device)
    # &#x8BBE;&#x7F6E;&#x8D85;&#x53C2;&#x6570;
    learning_rate = 1e-3
    batch_size = 64
    epochs = 5
    # &#x5B9A;&#x4E49;&#x635F;&#x5931;&#x51FD;&#x6570;&#x548C;&#x4F18;&#x5316;&#x5668;
    loss_fn = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)
    # &#x8BAD;&#x7EC3;&#x6A21;&#x578B;
    for t in range(epochs):
        print(f"Epoch {t + 1}\n-------------------------------")
        train_loop(train_dataloader, model, loss_fn, optimizer, device)
        test_loop(test_dataloader, model, loss_fn, device)
    print("Done!")
    # &#x4FDD;&#x5B58;&#x6A21;&#x578B;
    torch.save(model.state_dict(), 'model_weights.pth')

本文参考:PyTorch入门学习教程_Kint的博客

pytorch基础入门教程/一小时学会pytorch_李弘宇的博客

Original: https://blog.csdn.net/qq_36816848/article/details/123347251
Author: GoAI
Title: Pytorch入门教程

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/726709/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 02-pytest中的fixture(固件)的运用

    一、固件(Fixture)是一些函数,pytest 会在执行测试函数之前(或之后)加载运行它们。我们可以利用固件做任何事情,其中最常见的可能就是数据库的初始连接和最后关闭操作。 在…

    Python 2023年9月13日
    034
  • java 入土–集合详解

    java 集合 集合是对象的容器,实现了对对象的常用的操作,类似数组功能。和数组的区别: 数组长度固定,集合长度不固定 数组可以存储基本类型和引用类型,集合只能存储引用类型 使用时…

    Python 2023年10月18日
    038
  • Python+Pytest+Allure+Json 最全Api自动化框架之邮件发送功能

    Python+Pytest+Allure+Json 最全Api自动化框架 分享后,受到许多小伙伴的关注,最近一直在完善这个 API 自动化框架的功能,准备在框架成熟后分享给更多需要…

    Python 2023年9月11日
    067
  • Python写的简单的坦克大战小游戏

    前言部分 鄙人本学期手贱选了一门Python,这也完全得益于公众号里铺天盖地的Python广告,让我对Python的印象深刻,觉得没有Python干不了的活。然而在下实在不才,一学…

    Python 2023年9月20日
    033
  • #Lua:Lua调用C++生成的DLL库

    需求:在之前的求解器中添加了Lua库,使得程序可以在Lua脚本中实现自定义函数功能,考虑到未来可能需要与第三方程序库进行耦合计算,现在想到可以借助Lua脚本,在脚本中调用第三方动态…

    Python 2023年10月21日
    035
  • Pandas-记录-DataFrame个体到组别维度数据聚合并生成新字段

    背景 当前有如图三个字段: “尺码系数”、”颜色汇总尺码系数”、”比例” 现已知这三个字段的数学关系: SU…

    Python 2023年8月8日
    051
  • Pyhton GUI之tkinter组件学习.md

    § Label 描述:标签控件,可以显示文本和位图。 语法: w = Label ( master, option, … ) master:框架的父容器 option:可选项,…

    Python 2023年11月3日
    028
  • KeeWiDB的高性能修炼之路:架构篇

    数据也有冷热之分,你知道吗? 根据访问的频率的高低可将数据分为热数据和冷数据,访问频率高的则为热数据,低为冷数据。如果热、冷数据不区分,一并存储,显然不科学。将冷数据也存储在昂贵的…

    Python 2023年10月15日
    032
  • 无助和愤怒都有

    要说曾经有多爱,现在就有多恨。 这几天很 焦躁,今天冷静下一想,不是因为别的,焦躁的主要原因是因为那种无助感。 我记得之前,家里有事,我去当地法院,我是被迫和解的,和解的过程中,法…

    Python 2023年6月15日
    068
  • python pandas.pivot_table透视表函数

    文章目录 一、官方文档 二、参数解析 三、案例解析 * 3.1 新建数据集 3.2 两种写法 3.3 columns参数 3.4 fill_value 缺失值填充 3.5 marg…

    Python 2023年8月8日
    081
  • Python中pygame模块pygame.sprite.groupcollision碰撞检测的详解与测试

    在游戏开发中,非常重要的编程工作就是进行碰撞检测。在python的pygame模块中的sprite精灵类提供了多种方便快捷的碰撞检测方法。比如矩形碰撞检测、圆形碰撞检测、遮罩碰撞检…

    Python 2023年9月20日
    062
  • python基础(待补充)

    第一篇:计算机的基础知识 编程语言的介绍 计算机介绍和五大组成 平台与软件跨平台介绍 CS、BS架构和网络通信协议 操作系统的介绍 cpu详解 存储器详解 操作系统启动流程和BIO…

    Python 2023年6月9日
    071
  • 用Python实现股价的简单移动平均值

    前言 最近有没有朋友想买股票和基金?今天我要教你们一件神奇的事情,如何计算平均值。没有人不喜欢钱。 [En] Are there any friends who want to b…

    Python 2023年5月24日
    067
  • 美多商城之商品(2)

    美多商城之商品(2) 1.商品搜索 * 1.1.全文检索方案Elasticsearch 1.2.Haystack扩展建立索引 1.3.渲染商品搜索结果 2.商品详情页 * 2.1….

    Python 2023年8月5日
    041
  • 科技云报道:ChatGPT要和搜索引擎抢饭碗?

    科技云报道原创。 近日,据The Information报道,两位知情人士透露,微软正准备推出新版本必应搜索引擎,使用聊天机器人ChatGPT背后的人工智能来回答一些搜索查询,而非…

    Python 2023年11月3日
    031
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球