Pytorch 官方文档教程整理 (一)

Pytorch 官方文档教程整理 (一)

对应官方的 Instuction to Pytorch 前半部分

运行的Python版本: 3.9.12

所使用的库:

numpy                     1.23.0
pandas                    1.4.3
pip                       21.2.4
tensorboard               2.9.1
torch                     1.12.0+cu116
torchaudio                0.12.0+cu116
torchvision               0.13.0+cu116

注:后面涉及的链接均不再附上 都可以在官方文档找到

大多数机器学习工作涉及:数据、模型建立、模型参数优化、保存模型

本教程将根据流程的pytorch实现来进行教学

学习者应有一定的Python和深度学习基础

张量是一种特殊的数据结构,类似于NumPy的ndarrays,与NumPy数组通常可以共享相同的底层内存,从而消除了复制数据的需要。

张量提供了 GPU或者其他硬件的加速、从而也对自动微分(automatic differentiation)进行了优化

import torch
import numpy as np
  • *Initializing a Tensor

data = [[1,2],[3,4]]
x_data = torch.tensor(data)
print(x_data)

np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print('\n', x_np)

x_ones = torch.ones_like(x_data)
x_rand = torch.rand_like(x_data, dtype=torch.float)
print(f"\nOnes Tensor: \n {x_ones} \n")
print(f"Random Tensor: \n {x_rand} \n")
tensor([[1, 2],
        [3, 4]])

 tensor([[1, 2],
        [3, 4]])

Ones Tensor:
 tensor([[1, 1],
        [1, 1]])

Random Tensor:
 tensor([[0.7730, 0.6609],
        [0.8345, 0.5773]])

shape = (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.9749, 0.8934, 0.1137],
        [0.1913, 0.0333, 0.6570]])

Ones Tensor:
 tensor([[1., 1., 1.],
        [1., 1., 1.]])

Zeros Tensor:
 tensor([[0., 0., 0.],
        [0., 0., 0.]])
  • *Attributes of a Tensor

张量的性质包括:shape(形状), datatype(数据类型), device(运行设备cpu,gpu)

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
  • *Operations on Tensors

对于张量的操作有一百多种,比如:算术、线性代数、矩阵操作(转置、索引、切片)、采样等等(更多介绍见官网)

每一种操作都可以在GPU完成

默认张量的操作在CPU完成,我们需要用 .to方法将张量转移到GPU(但是需要注意的是在不同的device之间转移大型张量是很浪费时间和资源的)


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

tensor = tensor.to(device)
print(tensor.device)
Using device cuda!

cuda:0

tensor = torch.ones(4, 4)
print(tensor)

print(f"First row: {tensor[0]}")
print(f"First column: {tensor[:, 0]}")
print(f"Last column: {tensor[..., -1]}")
tensor[:,1] = 0
print(tensor)
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])
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.]])
t1 = torch.cat([tensor, tensor, tensor], dim=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.]])
print(tensor)

y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)

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

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.]])
tensor([[3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.]])
tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])
agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))
12.0 <class 'float'>
</class>
  • In-place operations 节约了内存但是会导致导数丢失没办法进行方向传播之类的操作 要注意使用
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.]])
  • *Bridge with NumPy

在CPU上的张量可以和numpy数组公用一个底层内存


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

t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]
t: tensor([2., 2., 2., 2., 2.])
n: [2. 2. 2. 2. 2.]

n = np.ones(5)
t = torch.from_numpy(n)

np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
n: [2. 2. 2. 2. 2.]

为了将数据代码和模型代码分离,获得更好的可读性和模块化pytorch提供了 torch.utils.data.DataLoadertorch.utils.data.Dataset以便于预处理好数据、便于使用时便捷的访问数据

pytorch本身提供了许多音频、图像、文本等等数据集可以直接下载成一个Dataset对象

下面以Fashion-Mnist数据集为例来演示
该数据包含60000个训练样本,10000个测试样本
每个样本由一个 28×28 的灰度图像和一个对应标签组成

下面通过以下参数来加载一个数据集

  • root 下载路径
  • train 是否为训练集(测试\训练)
  • download 是否下载 (未下载则下载 已下载则直接加载
  • transform and target_transform 对数据、标签进行transform
import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt

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

test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)
  • *Iterating and Visualizing the Dataset

我们可以对dataset进行索引
然后通过matplolib进行样本可视化

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):
    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()
  • *Creating a Custom Dataset for your files

创建自己的数据集

建立自己的数据集需要实现 __init__ __len__ __getitem__方法
(假设)FashionMNIST的图像样例存储在 img_dir 他们对应的标签存储在 annotations_file

下面来看看这些方法内部的实现过程

import os
import pandas as pd
from torchvision.io import read_image

标签文件格式如下

shirt1.jpg, 0
tshirt2.jpg, 0

ankleboot999.jpg, 9


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)

根据文件路径和参数 idx返回图片样本和标签

如果有transform那么进行transform后返回

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

完整实现如下

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):
        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
  • *Preparing your data for training with DataLoaders

Dataset一次只返回一个样本

我们在训练时一次update往往需要一个minibatch个样本 将全部数据都update一遍就称为一个epoch

为了避免过拟合每个epoch都通过dataloader进行reshuffle

同时也采用Python的多线程来加速数据检索

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)
  • *Iterate through the DataLoader

dataloader每次迭代都会返回minibatch个样本特征和其对应的label
因为shuffle=true所以每次遍历完所有的数据之后就会将顺序重新打乱


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()
print(f"img shape: {img.size()}")
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])
img shape: torch.Size([28, 28])
Label: 8

你使用的或者下载来的数据样本和其标签不一定满足你训练模型所需要的格式
我们可以使用transforms来对他们进行一些变化

所有的Torchvision的dataset都提供了 transformtarget_transforms分别对特征和标签进行变化
torchvision.transforms提供了一些变化方法

FashionMNIST 的特征是PIL image 标签是整数格式
为了适应训练模型,使用 ToTensor方法和 Lamda方法进行转化

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_(0, torch.tensor(y), value=1))
)
  • *ToTensor()

ToTensor 将PIL image图像或者numpy数组转化成FloatTensor型数据
并且将图片每个像素限制在[0,1]范围内

  • *Lamda Transforms

用户自定义一个转换方式
上文中将整数转换成了独热码张量

target_transform = Lambda(lambda y: torch.zeros(
    10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1))

未完待续

Original: https://blog.csdn.net/qq_53580131/article/details/126009967
Author: 说梦人丶
Title: Pytorch 官方文档教程整理 (一)

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

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

(0)

大家都在看

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