目录
torch.utils.data.TensorDataset
数据
训练模型前一般要先处理数据的输入和预处理,Pytorch分别提供了两个工具(常用):
- torch.utils.data.Dataset 类
- torch.utils.data.DataLoader 类
流程是先把原始数据转变成 torch.utils.data.Dataset 类,随后再把得到的 torch.utils.data.Dataset 类当作一个参数传递给 torch.utils.data.DataLoader 类,得到一个数据加载器,这个数据加载器每次可以返回一个Batch的数据供模型训练使用
torch.utils.data.Dataset
1、用法
from torch.utils.data import Dataset
如果要自定义读取数据的方法,就需要继承类 torch.utils.data.Dataset ,并将其封装到DataLoader 中。
Dataset 是一个数据集抽象类,它是其他所有数据集类的父类(所有其他数据集类都应继承它),继承时需要重写方法
- 自定义的类要继承Dataset类
- __init__ 用于初始化数据集,在这里可以加载数据文件、初始化变量等
- __len__ 返回自定义数据集的大小
- __getitem__ 通过索引下标访问数据
2、例子
例1:实现将一组Tensor数据对封装成Tensor数据集,能够通过index得到数据集的数据、通过len得到数据集的大小
from torch.utils.data import Dataset
import torch
class TensorDataset(Dataset):
# TensorDataset继承Dataset, 重载了__init__, __getitem__, __len__
def __init__(self, data_tensor, target_tensor):
self.data_tensor = data_tensor
self.target_tensor = target_tensor
def __getitem__(self, index):
return self.data_tensor[index], self.target_tensor[index]
def __len__(self):
return self.data_tensor.size(0) # size(0)返回当前张量维数的第一维
# 生成数据
data_tensor = torch.randn(4, 3) # 4行3列,服从正态分布的张量
print(data_tensor)
target_tensor = torch.rand(4) # 4个元素,服从均匀分布的张量
print(target_tensor)
# 将数据封装成 Dataset (用TensorDataset类)
tensor_dataset = TensorDataset(data_tensor, target_tensor)
# 可使用索引调用数据
print('tensor_data[0]: ', tensor_dataset[0])
# 可返回数据len
print('len os tensor_dataset: ', len(tensor_dataset))
输出结果:
tensor([[ 0.8618, 0.4644, -0.5929],
[ 0.9566, -0.9067, 1.5781],
[ 0.3943, -0.7775, 2.0366],
[-1.2570, -0.3859, -0.3542]])
tensor([0.1363, 0.6545, 0.4345, 0.9928])
tensor_data[0]: (tensor([ 0.8618, 0.4644, -0.5929]), tensor(0.1363))
len os tensor_dataset: 4
例2:重写 __init__ 实现用 pd.read_csv 读取 csv 文件
from torch.utils.data import Dataset
import pandas as pd
# 继承Dataset,定义自己的数据集类 mydataset
class mydataset(Dataset):
def __init__(self, csv_file): # self参数必须,其他参数及其形式随程序需要而不同,比如(self, *inputs)
self.csv_data = pd.read_csv(csv_file)
def __len__(self):
return len(self.csv_data)
def __getitem__(self, idx):
return self.csv_data.values[idx]
data = mydataset('spambase.csv')
print(data[3])
print(len(data))
输出结果:
[0.000e+00 0.000e+00 0.000e+00 0.000e+00 6.300e-01 0.000e+00 3.100e-01
6.300e-01 3.100e-01 6.300e-01 3.100e-01 3.100e-01 3.100e-01 0.000e+00
0.000e+00 3.100e-01 0.000e+00 0.000e+00 3.180e+00 0.000e+00 3.100e-01
0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00
0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00
0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00
0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00 0.000e+00
1.370e-01 0.000e+00 1.370e-01 0.000e+00 0.000e+00 3.537e+00 4.000e+01
1.910e+02 1.000e+00]
4601
torch.utils.data.TensorDataset
from torch.utils.data import TensorDataset
1、用法
将多个张量组合成一个数据集,每个张量的第一个维度相同,表示数据的数量。
import torch
from torch.utils.data import TensorDataset
# 创建一个包含数据和标签的TensorDataset
data_tensor = torch.tensor([[1, 2], [3, 4], [5, 6], [7, 8]])
label_tensor = torch.tensor([0, 1, 0, 1])
dataset = TensorDataset(data_tensor, label_tensor)
多个张量的情况:还可以创建包含数据、标签和额外信息(如掩码等)的TensorDataset
2、例子
训练集,包含输入数据 train_r、输入掩码 train_mask_r、标签 train_labels,将它们组合成一个TensorDataset
import torch
from torch.utils.data import TensorDataset
import numpy as np
train_r = np.random.randn(100, 10) # 100 个样本,每个样本有 10 个特征
# 0或1
train_mask_r = np.random.randint(0, 2, size=(100, 10)) # 100 个样本的掩码
train_labels = np.random.randint(0, 2, size=100) # 100 个样本的标签
# 将 NumPy 数组转换为 PyTorch 张量
train_r_tensor = torch.from_numpy(train_r).float()
train_mask_r_tensor = torch.from_numpy(train_mask_r).float()
train_labels_tensor = torch.from_numpy(train_labels).long()
# 创建 TensorDataset
dataset = TensorDataset(train_r_tensor, train_mask_r_tensor, train_labels_tensor)
torch.utils.data.DataLoader
1、用法
from torch.utils.data import DataLoader
数据加载器,结合了数据集和取样器,并且可以提供多个线程处理数据集。
把训练数据分成多个小组(batch),每次抛出一组数据,直至把所有的数据都抛出。可以快速迭代数据
2、例子
情况1:BATCH_SIZE 刚好整除数据量
- 共有10条数据,设置 BATCH_SIZE 为 5 进行划分,能划分为 2 组(step 为 0 和 1)。这两组数据互斥
import torch
import torch.utils.data as Data
"""
批训练,把数据变成一小批一小批进行训练。
DataLoader就是用来包装所使用的数据,每次抛出一批数据
"""
BATCH_SIZE = 5 # 批训练的数据个数
x = torch.linspace(1, 10, 10) # 训练数据
print(x) # tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
y = torch.linspace(10, 1, 10) # 标签
print(y) # tensor([10., 9., 8., 7., 6., 5., 4., 3., 2., 1.])
torch_dataset = Data.TensorDataset(x, y) # 对给定的tensor数据,将他们包装成dataset
loader = Data.DataLoader(
# 从数据库中每次抽出batch size个样本
dataset=torch_dataset,
batch_size=BATCH_SIZE,
shuffle=True, # 要不要打乱数据 (打乱比较好)
num_workers=2, # 多线程来读数据 (Windows环境设为0,Linux才能支持多线程)
)
def show_batch():
for epoch in range(3):
for step, (batch_x, batch_y) in enumerate(loader):
# training
print("step:{}, batch_x:{}, batch_y:{}".format(step, batch_x, batch_y))
show_batch()
输出结果:
step:0, batch_x:tensor([10., 1., 3., 7., 6.]), batch_y:tensor([ 1., 10., 8., 4., 5.])
step:1, batch_x:tensor([8., 5., 4., 9., 2.]), batch_y:tensor([3., 6., 7., 2., 9.])
step:0, batch_x:tensor([ 9., 3., 10., 1., 5.]), batch_y:tensor([ 2., 8., 1., 10., 6.])
step:1, batch_x:tensor([2., 6., 8., 4., 7.]), batch_y:tensor([9., 5., 3., 7., 4.])
step:0, batch_x:tensor([ 2., 10., 9., 6., 1.]), batch_y:tensor([ 9., 1., 2., 5., 10.])
step:1, batch_x:tensor([8., 3., 4., 7., 5.]), batch_y:tensor([3., 8., 7., 4., 6.])
情况2:BATCH_SIZE 不整除数据量,会输出余下所有数据
- 共有10条数据,设置 BATCH_SIZE 为 4 来进行划分,能划分为 3 组(step 为 0 、1、2),分别有 4、4、2 条数据
将上述代码中的 BATCH_SIZE 改为4,输出结果:
step:0, batch_x:tensor([1., 5., 3., 2.]), batch_y:tensor([10., 6., 8., 9.])
step:1, batch_x:tensor([7., 8., 4., 6.]), batch_y:tensor([4., 3., 7., 5.])
step:2, batch_x:tensor([10., 9.]), batch_y:tensor([1., 2.])
step:0, batch_x:tensor([ 7., 10., 5., 2.]), batch_y:tensor([4., 1., 6., 9.])
step:1, batch_x:tensor([9., 1., 6., 4.]), batch_y:tensor([ 2., 10., 5., 7.])
step:2, batch_x:tensor([8., 3.]), batch_y:tensor([3., 8.])
step:0, batch_x:tensor([10., 3., 2., 8.]), batch_y:tensor([1., 8., 9., 3.])
step:1, batch_x:tensor([1., 7., 5., 9.]), batch_y:tensor([10., 4., 6., 2.])
step:2, batch_x:tensor([4., 6.]), batch_y:tensor([7., 5.])
优化器 optimizer.param_groups
1、用法
optimizer.param_groups:是一个list,长度为1,其中的元素是一个字典
[{'params': [Parameter containing:
tensor([[-0.1548, -0.2069, -0.2007, -0.0577, -0.2824, 0.1292, 0.1056, 0.2825,
0.1069, -0.0987]], requires_grad=True), Parameter containing:
tensor([-0.0470], requires_grad=True)], 'lr': 0.01, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'maximize': False, 'foreach': None, 'differentiable': False}]
optimizer.param_groups[0]:是一个字典,key一般包括params、lr、momentum、weight_decay等参数(不同优化器包含的参数可能略有不同,且可以额外人为添加键值对)
{'params': [Parameter containing:
tensor([[-0.2488, 0.2737, -0.0199, -0.0528, -0.2339, -0.0749, -0.1017, 0.2590,
0.2526, -0.2583]], requires_grad=True), Parameter containing:
tensor([0.2086], requires_grad=True)], 'lr': 0.01, 'momentum': 0, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'maximize': False, 'foreach': None, 'differentiable': False}
人为额外添加键值对的函数为 optimizer.add_param_group():
optimizer.add_param_group({"params":model.fc.parameters(),"lr":0.09})
optimizer.param_groups['params']:Pytorch的优化器中都有一个参数:params,即为网络中需要优化的参数,传入的网络参数必须是可迭代的对象,如model.parameters()
Parameter containing:
tensor([[ 0.0075, -0.1876, -0.0677, -0.1628, 0.2620, 0.2361, 0.2415, -0.3089,
-0.0936, -0.2149]], requires_grad=True)
Parameter containing:
tensor([-0.0566], requires_grad=True)
如果想取tensor部分的值,可以用 optimizer.param_groups['params'].data:
tensor([[-0.0751, -0.2141, -0.2035, 0.1249, -0.0248, 0.2066, 0.1604, -0.3064,
0.2822, -0.0026]])
tensor([0.0822])
2、例子
例1:对优化器参数中的二维张量进行转置,计算其元素的平方和
import torch
import torch.optim as optim
# 假设我们有一个简单的模型
model = torch.nn.Linear(10, 1)
# 创建优化器
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 初始化 temp 变量
temp = 0.0
# 遍历优化器的参数组
for param_group in optimizer.param_groups:
# 遍历每个参数组中的参数
for param in param_group['params']:
print(param.data)
# 检查参数是否是二维张量
if param.dim() == 2:
# 对二维张量进行转置,计算其元素的平方和,并将结果累加到 temp 中
temp += param.T.pow(2).sum().item()
print(temp)
输出结果:
# w
tensor([[ 0.0840, 0.3101, 0.2821, -0.0384, 0.0747, 0.2369, -0.0436, 0.1418,
0.3083, -0.0079]])
# b
tensor([0.2341])
# temp
0.36307135224342346
例2:给网络不同模块设置不同学习率,或动态调整学习率
import torch
class Resnet(torch.nn.Module):
def __init__(self):
super(Resnet, self).__init__()
self.block1 = torch.nn.Sequential(
torch.nn.Conv2d(1, 10, 5),
torch.nn.MaxPool2d(2),
torch.nn.ReLU(True),
torch.nn.BatchNorm2d(10),
)
self.block2 = torch.nn.Sequential(
torch.nn.Conv2d(10, 20, 5),
torch.nn.MaxPool2d(2),
torch.nn.ReLU(True),
torch.nn.BatchNorm2d(20),
)
self.fc = torch.nn.Sequential(
torch.nn.Flatten(),
torch.nn.Linear(320, 10)
)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.fc(x)
return x
model = Resnet()
(1)正常的优化器设置方式:
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.5)
(2)对网络不同模块设置不同学习率:
params = [{"params":model.block1.parameters()}, # 其采用默认的学习率
{"params":model.block2.parameters(),"lr":0.08},]
optimizer = torch.optim.SGD(params, lr=0.1,) # 此处的lr是默认的学习率
'''
optimizer.param_groups[1]在这对应的就是
{"params":model.block2.parameters(),"lr":0.08}
'''
(3)动态调整学习率:
start_lr = [0.1, 0.08, 0.09] # 不同层的初始学习率
def adjust_learning_rate(optimizer, epoch, start_lr):
for index, param_group in enumerate(optimizer.param_groups):
lr = start_lr[index] * (0.9 ** (epoch // 1)) # 每1个eporch学习率改变为上一个eporch的 0.9倍
param_group['lr'] = lr
- 0.9 ** (epoch // 1):这是一个幂运算,用于计算当前 epoch 对应的学习率衰减系数
- 这个公式的作用是每经过一个 epoch,学习率将乘以0.9
例3:如果只是想优化一个网络,就把一整个网络看做一个param_groups,参数params赋值为model.parameters()
import torch
import torch.nn as nn
# 定义网络
class Test(nn.Module):
def __init__(self):
super(Test, self).__init__()
self.conv = nn.Sequential(
nn.Conv2d(3, 8, 3),
nn.ReLU(),
)
self.fc = nn.Sequential(
nn.Linear(288, 5)
)
def forward(self, x):
x = self.conv(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
# 实例化网络
model = Test()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
print("只有一个网络时optimizer.param_groups(list类型)的长度:{}".format(len(optimizer.param_groups)))
输出结果:
只有一个网络时optimizer.param_groups(list类型)的长度:1
例4:如果是同时优化多个网络的参数
(1)可以将多个网络的参数合并,当成一个网络的参数来优化
# 此时有两个网络需要优化
model_1 = Test()
model_2 = Test()
optimizer = torch.optim.Adam([*model_1.parameters(), *model_2.parameters()], lr=0.01)
print("两个网络时optimizer.param_groups(list类型)的长度:{}".format(len(optimizer.param_groups)))
输出结果:
两个网络时optimizer.param_groups(list类型)的长度:1
(2)多个网络分开优化,且可以使用不同的学习率(每个param_groups中可以单独定义学习率,如果没有指定,默认采取后面的学习率)
# 此时有两个网络需要优化
model_1 = Test()
model_2 = Test()
optimizer = torch.optim.Adam([{'params':model_1.parameters()},{'params':model_2.parameters(), 'lr': 0.2}], lr=0.01)
print("优化器里有多少个param_groups: ", len(optimizer.param_groups))
print("网络1的学习率为: ", optimizer.param_groups[0]['lr'])
print("网络2的学习率为: ", optimizer.param_groups[1]['lr'])
输出结果:
优化器里有多少个param_groups: 2
网络1的学习率为: 0.01
网络2的学习率为: 0.2
参考
【PyTorch】torch.utils.data.Dataset 介绍与实战-CSDN博客
【PyTorch】torch.utils.data.DataLoader 简单介绍与使用-CSDN博客