pytorch深度学习入门(2)之-张量与数据集加载

张量

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

张量与NumPy 的ndarray类似,不同之处在于张量可以在 GPU 或其他硬件加速器上运行。事实上,张量和 NumPy 数组通常可以共享相同的底层内存,从而无需复制数据,张量还针对自动微分进行了优化,如果您熟悉 ndarrays,那么您就会熟悉 Tensor API。如果没有,那就跟随吧!

import torch
import numpy as np

初始化张量
张量可以通过多种方式初始化。看看下面的例子:

直接来自数据

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

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

来自 NumPy 数组

张量可以从 NumPy 数组创建。

np_array = np.array(data)
x_np = torch.from_numpy(np_array)

从另一个张量:

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

x_ones = torch.ones_like(x_data) # retains the properties of x_data
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
print(f"Random Tensor: \n {x_rand} \n")
Ones Tensor:
 tensor([[1, 1],
        [1, 1]])

Random Tensor:
 tensor([[0.8823, 0.9150],
        [0.3829, 0.9593]])

使用随机值或常数值:

shape是张量维度的元组。在下面的函数中,它确定输出张量的维数。

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.3904, 0.6009, 0.2566],
        [0.7936, 0.9408, 0.1332]])

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

Zeros Tensor:
 tensor([[0., 0., 0.],
        [0., 0., 0.]])

张量的属性
张量属性描述了它们的形状、数据类型以及存储它们的设备。

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

张量运算
这里全面描述了 100 多种张量运算,包括算术、线性代数、矩阵操作(转置、索引、切片)、采样等。

这些操作中的每一个都可以在 GPU 上运行(速度通常高于 CPU)。如果您使用 Colab,请通过转至运行时 > 更改运行时类型 > GPU 来分配 GPU。

默认情况下,张量是在 CPU 上创建的。我们需要使用方法显式地将张量移动到 GPU .to(在检查 GPU 可用性之后)。请记住,跨设备复制大张量在时间和内存方面可能会很昂贵!

# 将张量专用到gpu进行运算

```python
if torch.cuda.is_available():
    tensor = tensor.to("cuda")

尝试列表中的一些操作。如果您熟悉 NumPy API,您会发现 Tensor API 使用起来非常简单。

标准的类似 numpy 的索引和切片:

```python
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.cat来沿给定维度连接一系列张量。另请参见torch.stack,这是另一个与 略有不同的张量连接运算符torch.cat。

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.]])

算术运算

# This computes the matrix multiplication between two tensors. y1, y2, y3 will have the same value
# ``tensor.T`` returns the transpose of a tensor
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)

y3 = torch.rand_like(y1)
torch.matmul(tensor, tensor.T, out=y3)
# This computes the element-wise product. z1, z2, z3 will have the same value
z1 = tensor * tensor
z2 = tensor.mul(tensor)

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

单元素张量如果您有一个单元素张量,例如通过将张量的所有值聚合为一个值,您可以使用以下方法将其转换为 Python 数值item()

agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))
12.0 <class 'float'>

就地运算 将结果存储到操作数中的操作称为就地运算。它们由_后缀表示。例如: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.]])

笔记

In-place operations 可以节省一些内存,但在计算导数时可能会出现问题,因为历史记录会立即丢失。因此,不鼓励使用它们。

与 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)
t = torch.from_numpy(n)

NumPy 数组中的变化反映在张量中。

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.DataLoader和torch.utils.data.Dataset 允许您使用预加载的数据集以及您自己的数据。 Dataset存储样本及其相应的标签,并DataLoader围绕 进行迭代Dataset以方便访问样本。

PyTorch 域库提供了许多预加载的数据集(例如 FashionMNIST),它们子类化torch.utils.data.Dataset并实现特定于特定数据的函数。它们可用于对您的模型进行原型设计和基准测试。您可以在这里找到它们:图像数据集、 文本数据集和 音频数据集

加载数据集
以下是如何从 TorchVision 加载Fashion-MNIST数据集的示例。Fashion-MNIST 是 Zalando 文章图像的数据集,由 60,000 个训练示例和 10,000 个测试示例组成。每个示例包含一个 28×28 灰度图像和来自 10 个类别之一的关联标签。

我们使用以下参数加载FashionMNIST 数据集:
root是存储训练/测试数据的路径,

train指定训练或测试数据集,

download=True如果 上没有数据,则从 Internet 下载数据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
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
)

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

如下,运行后加载数据过程:

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to data/FashionMNIST/raw/train-images-idx3-ubyte.gz

  0%|          | 0/26421880 [00:00<?, ?it/s]
  0%|          | 65536/26421880 [00:00<01:12, 366034.86it/s]
  1%|          | 229376/26421880 [00:00<00:38, 684873.31it/s]
  3%|3         | 884736/26421880 [00:00<00:10, 2481647.02it/s]
  7%|6         | 1835008/26421880 [00:00<00:05, 4208931.24it/s]
 13%|#3        | 3440640/26421880 [00:00<00:03, 7177552.93it/s]
 23%|##3       | 6094848/26421880 [00:00<00:01, 12236602.40it/s]
 31%|###1      | 8224768/26421880 [00:00<00:01, 13544879.04it/s]
 37%|###7      | 9895936/26421880 [00:01<00:01, 14156027.95it/s]
 44%|####3     | 11534336/26421880 [00:01<00:01, 14608940.92it/s]
 54%|#####3    | 14155776/26421880 [00:01<00:00, 17389560.03it/s]
 62%|######1   | 16252928/26421880 [00:01<00:00, 17589694.34it/s]
 68%|######8   | 18055168/26421880 [00:01<00:00, 17139468.01it/s]
 75%|#######4  | 19791872/26421880 [00:01<00:00, 17163042.84it/s]
 81%|########1 | 21528576/26421880 [00:01<00:00, 16903624.79it/s]
 91%|#########1| 24117248/26421880 [00:01<00:00, 19091471.08it/s]
100%|#########9| 26312704/26421880 [00:01<00:00, 19035749.70it/s]
100%|##########| 26421880/26421880 [00:01<00:00, 13493046.15it/s]
Extracting data/FashionMNIST/raw/train-images-idx3-ubyte.gz to data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw/train-labels-idx1-ubyte.gz

  0%|          | 0/29515 [00:00<?, ?it/s]
100%|##########| 29515/29515 [00:00<00:00, 330385.76it/s]
Extracting data/FashionMNIST/raw/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz

  0%|          | 0/4422102 [00:00<?, ?it/s]
  1%|1         | 65536/4422102 [00:00<00:11, 364325.69it/s]
  5%|5         | 229376/4422102 [00:00<00:06, 683672.36it/s]
 14%|#4        | 622592/4422102 [00:00<00:02, 1688465.21it/s]
 33%|###2      | 1441792/4422102 [00:00<00:00, 3363222.72it/s]
 57%|#####7    | 2523136/4422102 [00:00<00:00, 5144795.23it/s]
100%|##########| 4422102/4422102 [00:00<00:00, 5428079.80it/s]
Extracting data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz

  0%|          | 0/5148 [00:00<?, ?it/s]
100%|##########| 5148/5148 [00:00<00:00, 31988558.51it/s]
Extracting data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw

迭代和可视化数据集
我们可以Datasets像列表一样手动索引: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):
    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()

在这里插入图片描述

踝靴、衬衫、包、踝靴、裤子、凉鞋、外套、凉鞋、套头衫
为您的文件创建自定义数据集
自定义 Dataset 类必须实现三个函数:initlen__和__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):#获取数据,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

init 函数在实例化 Dataset 对象时运行一次。我们初始化包含图像、注释文件和两种转换的目录

labels.csv 文件如下所示:

tshirt1.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

len 函数返回数据集中的样本数。

例子:

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

getitem 函数加载并返回给定索引处的数据集的样本idx。基于索引,它识别图像在磁盘上的位置,使用 将其转换为张量read_image,从 中的 csv 数据中检索相应的标签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

使用 DataLoaders 准备数据以进行训练
它Dataset检索我们的数据集的特征并一次标记一个样本。在训练模型时,我们通常希望以“小批量”方式传递样本,在每个时期重新整理数据以减少模型过度拟合,并使用 Python 来加速multiprocessing数据检索。

DataLoader是一个可迭代对象,它通过一个简单的 API 为我们抽象了这种复杂性。

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)

遍历 DataLoader
我们已将该数据集加载到 中DataLoader,并且可以根据需要迭代数据集。下面的每次迭代都会返回一批train_features和train_labels(batch_size=64分别包含特征和标签)。因为我们指定了shuffle=True,所以在迭代所有批次后,数据将被打乱(为了更细粒度地控制数据加载顺序,请查看Samplers)。

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

Feature batch shape: torch.Size([64, 1, 28, 28])
Labels batch shape: torch.Size([64])
Label: 5
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农呆呆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值