Pytorch学习

quickstart

Loaddata

PyTorch有两大原始方法处理数据,分别为torch.utils.data.DataLoadertorch.utils.data.Dataset 。其中Dataset 存储样例和相应的标签,而DataLoaderDataset中包装了一个可以迭代的对象。

PyTorch有很多特定领域的库,这里我们主要使用TorchVision这个数据“库”。

torchvision.datasets 包含了很多真实世界可视化数据,在这里,我们主要使用FashionMNIST数据库。每一个TorchVision数据库都包含两大参数:transformtarget_transform 来分别调整样例和标签。

# Download training data from open datasets
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
)

# Download testing data from open datasets
testing_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor(),
)

当传递Dataset作为一个参数给DataLoader。会基于我们的数据库包装一个可以迭代的对象,用于automatic batching, sampling,shuffling,multiprocess data loading.

batch_size = 64

# Create data loaders
train_dataloader = DataLoader(training_data,batch_size=batch_size)
test_dataloader = DataLoader(testing_data,batch_size=batch_size)

for X, y in test_dataloader:
  print('Shape of X [N, C, H, W]: ', X.shape)
  print('Shape of y : ', y.shape, y.dtype)
  break

Creating Models

在PyTorch中定义一个神经网络,我们需要构建一个继承nn.Module的类。在__init__ 函数中定义网络层,在forward函数中指定数据如何通过网络传递。 如果GPU可以使用的话,我们可以在神经网络中使用GPU来加速我们的操作。

Optimizing the Model Parameters

训练一个模型,我们需要损失函数和一个优化器

# CrossEntropyLoss()  交叉熵损失函数
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)

在一个单一的训练循环,模型可以在训练集做出预测(基于之前的批处理数量),同时反向传播预测的失误来调整模型的参数设定

def train(dataloader, model, loss_fn, optimizer):
  size = len(dataloader.dataset)
  for batch, (X, y) in enumerate(dataloader):
    X, y = X.to(device), y.to(device)

    # Compute prediction error
    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(dataloader, model):
  size = len(dataloader.dataset)
  model.eval()
  test_loss, correct = 0, 0
  with torch.no_grad():
    for X, y in dataloader:
      X, y = X.to(device), y.to(device)
      pred = model(X)
      test_loss +=loss_fn(pred, y).item()
      correct += (pred.argmax(1) ==y).type(torch.float).sum().item()
  test_loss /= size
  correct /= size
  print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss:{(test_loss):>8f} \n")

训练过程是在每次迭代中进行的,在每一次迭代过程中,模型会学习新的参数进而做出更好的预测效果。通常,我们会选择打印每次迭代过程模型的准确度和损失——观察每次迭代过程中准确度的增加和损失的减少

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

Saving Models

一个通用保存模型的方法就是序列化内部状态字典(包括模型参数)

torch.save(model.state_dict(),"model.pth")
print("Saved Pytorch Model State to model.pth")

加载一个模型

加载一个模型的过程包括再创一个模型结构和加载一个状态字典到里面

model = NeuralNetwork()
model.load_state_dict(torch.load("model.pth"))

该模型现在可以用来预测:

classes = [
    "T-shirt/top",
    "Trouser",
    "Pullover",
    "Dress",
    "Coat",
    "Sandal",
    "Shirt",
    "Sneaker",
    "Bag",
    "Ankle boot",
]

model.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
  pred = model(X)
  predicted, actual = classes[pred[0].argmax(0)], classes[y]
  print(f'Predicted: "{predicted}", Actual: "{actual}"')

为文件构建一个自定义数据库

一个自定义的自定义的数据库类必须使用三个函数功能:__init__,__len__,__getitem__。下面自定义类中,FashionMNIST 图片集会存储在目录 img_dir中,标签存储在CSV格式文件annotations_file中。

下面,会一一解答各个函数发生的作用

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:
      image = self.target_transform(label)
    sample = {"image": image, "label": label}
    return sample

__init__ :

当实例化Dataset对象时,会调用一次__init__函数。我们会初始化包含图像、注释文件和两个转换的目录。

__len__ :

返回数据库中样本的数量数

__getitem__ :

该函数是从数据库特定的索引idx加载和返回一个样例。基于这个样例,它标识硬盘中图片的位置,使用read_image函数转换为张量格式,从self.img_labelscsv数据中检索对应的标签,调用转换函数,返回张量图片和Python 字典序中对应的标签。

使用DataLoaders准备数据用来训练

Dataset检索我们的数据集特征并一次性标注我们的样本。训练模型时,通常希望小批量进行传递样本,在迭代过程中整理数据以减少模型过拟合,使用Python‘s multiprocessing加快数据的检索。

DataLoader是一个迭代的对象。我们使用简单的API抽象了它的复杂性。

from torch.utils.data import DataLoader

train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(testing_data, batch_size=64, shuffle=True)

使用DataLoader迭代

我们把Dataset放入到DataLoader,如果需要的话通过数据库进行迭代。因为我们指定了shuffle=True,所以数据会进行清洗。下面的每一次迭代过程都会返回一批train_featurestest_features(分别是包含特征和标签的batch_size=64

# 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()
label = train_labels[0]
plt.imshow(img, cmap='gray')
plt.show()
print(f"Label: {label}")
数据转换

所有的 TorchVision 数据库有两大参数——transform调整特征/target_transform 调整标签。torchvision.transforms 模块提供了几种常用的转换。

FashionMNIST 特征是 PIL格式,标签为整数。训练时,我们需要把特征作为常规张量,标签作为one-hot解码张量。做这些转换,我们使用ToTensorLambda

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图像 和 Numpy类型 为 FloatTensor。并规范图像点密度值在[0,1]。

Lambda Transforms

Lambda 转换适用于任何一个用户自定义函数。这上述操作中,我们定义了一个把整数转换为one-hot解码张量的函数。首先创建一个大小为10的0张量,然后调用scatter_然后在标签y给定的索引中上分配一个值1。

构建神经网络

神经网络由对数据执行操作的层和模块构成。在下面的模块中,我们会使用FashionMNIST数据库来搭建神经网络以分类图像。

选定训练设备

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

定义特定的类

通过继承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),
        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中,然后打印它的结构。

model = NeuralNetwork().to(device)
print(model)

不需要直接调用forward()函数!

在输入上调用模型将返回一个10维张量,其中包含每个类的原始预测值。 我们通过将其传递给nn.Softmax模块的实例来获得预测概率。

X = torch.rand(1, 28, 28, device=device)
logits = model(X)
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1)
print(f"Predicted class: {y_pred}")

模型层

使用FashionMNIST模型分解每一层。为了说明这个,我们准备了3张 28x28 尺寸的图片样本并观察我们传递图片数据到神经网络时发生了什么?

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

nn.Flatten

初始化nn.Flatten层从而转换每一张2D 28*28图片到784像素的连续数组(并且微型批次 维度保存不变(at dim=0))

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

nn.Linear

linear layer时使用保存的权重和偏差(biases)把对输入数据进行线性变换。

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

nn.ReLU

非线性激活会在模型的输入和输出之间创建复杂的映射。在线性变换之后将它们应用以引入非线性,从而帮助神经网络学习各种各样的现象。

在这个模型中,在各个线性层中使用nn.ReLU,但也可以使用在你的模型中引入其他激活非线性层的关系。

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

nn.Sequential

nn.Sequential 是模块的有序容器。通过定义的一致顺序的全部模块传递数据,可以使用顺序容器将诸如seq_modules之类的快速网络组合在一起。

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

nn.Softmax

神经网络的最后一个线性层返回[-infty,infty]原始logits-raw值并传入到nn.Softmax模块中。将logit缩放为[0,1]值,该值表示每个类别的模型的预测概率。dim参数指示必须将值相加为1的维度。

softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)
print(f"pred_probab: {pred_probab}")

Model Parameters

神经网络中很多的内部层数作为参数,训练过程中,优化相关的权重和偏差。在模型对象中,继承nn.Module自动跟踪已经定义的全部领域,使用parameters() or named_parameters()访问你的模型的全部参数。

该实例中,遍历每一个参数,并打印其大小和值的预览。

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

for name, param in model.named_parameters():
  print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
张量简介

Tensor(张量)是数组、矩阵类似的数据结构。在pytorch中,通常对一个模型,或则模型参数进行编码。

事实上,除了张量可以加快在GPU或其他特定硬件的运算,其他的都和NumPy的数组类似。

数据加载

这里主要是以TorchVision中的Fashion-MNIST为案例作为研究。

Fashion-MNIST主要包含60000个训练数据和10000个测试数据。每一个数据都包括 28*28个灰度图像和来着10个类别之一的关联标签。

我们会通过以下参数来加载FashionMNIST Dataset数据:

  • root 为我们训练集/测试集 存储的路径。
  • train 指定 训练集或测试集。
  • download=True 表明当root不能获取数据时,从网络中下载数据。
  • transform and target_transform 指定特征和标签转换

下载数据

import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda
import matplotlib.pyplot as plt

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

testing_data = datasets.FashionMNIST(
    root='data',
    train=False,
    download=True,
    transform=ToTensor()
)

迭代-可视化 数据库

我们可以像一个列表一样为Dataset创建索引:training_data[index],并使用matplotlib 可视化我们训练集数据的样例。

# Iterating and Visualizing the Dataset
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=(10,10))
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.imshow(img.squeeze(), cmap="gray")
plt.show()

AUTOGRAD

torch.autograd is PyTorch’s automatic differentiation engine that powers neural network training.

背景:

训练神经网络时,Forward Propagation,Backward Propagation。

Forward Propagation主要是先确定函数,Backward Propagation 是梯度下降进行修复。

定义

import torch

x = torch.ones(5)
y = torch.zeros(3)
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)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A5FjTg3Y-1616932330016)(E:\博客图片\CSDN\comp-graph.png)]

在这个网络中,wb 都是需要进行优化的参数。因此,我们可以计算这些变量构成损失函数的梯度。为了完成这个,我们需要设定这些张量的requires_grad属性。

Notes : 创建张量的时候,设定requires_grad的值,或则后面同故宫x.requires_grad_(True)方法可以进行设定。

我们应用于张量以构造计算图的函数实际上是Function类的对象。这个对象知道如何在前方向计算函数,也知道如何在后向传播中计算求导。向后传播函数的参考存储在张量的grad_fn属性中。比如:

print('Gradient function for z = ', z.grad_fn)
print('Gradient function for loss = ', loss.grad_fn)

计算梯度

优化神经网络中参数的权重,我们需要计算与之对应参数的损失函数的求导导数。To compute those derivatives, we call loss.backward(), and then retrieve the values from w.grad and b.grad:

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

注:

  1. 我们只可以获得计算的图的叶子节点的grad属性(这些属性设定requires_gradTrue).对于我们图中的其他节点,梯度并不可行。
  2. 对于一个给定的图,因为性能的原因,只使用一次backward用来执行梯度计算。

参考

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值