PyTorch 笔记¶
模型¶
保存和加载模型¶
# 保存整个模型的结构和参数
torch.save(model, path)
# 加载整个模型的结构和参数
model = torch.load(path)
model.eval()
# 仅保存参数
model = nn.Model()
torch.save(model.state_dict(), path)
# 加载保存的参数到模型中
model = nn.Model()
model.load_state_dict(torch.load(path))
model.eval()
使用模型¶
from torchvision import models, transforms
# 加载模型
model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
model.eval()
# 图像预处理
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]
preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(norm_mean, norm_std),
])
img = Image.open(img_path)
img_tensor = preprocess(img)
img_input = img_tensor.unsqueeze(0)
# 获取标签
output = model(img_input)
predicted_index = torch.max(output, 1)[1]
predicted_label = labels[predicted_index.item()]
预训练模型¶
torchvision 中的模型¶
torchvision.models
中有许多模型的构建方法与预训练权重,每个模型构建方法(如 resnet50
)都有与之对应的 Enum 类(如 ResNet50_Weights
),每个 Enum 类都有一个 DEFAULT
的属性别名,指向对应模型的最佳预训练权重。
模型构建方法可用于构建模型,预训练权重可作为参数传递,例如:
from torchvision.models import resnet50, ResNet50_Weights
model = resnet50(weights=ResNet50_Weights.DEFAULT)
weights
参数的取值可以是:
None
默认值,随机初始化权重str
预训练权重的名称,如"ResNet50_Weights.IMAGENET1K_V1"
"IMAGENET1K_V1"
- 权重的 Enum 类,例如
ResNet50_Weights.IMAGENET1K_V1
此外,torchvision.models
中还有别的一些函数:
list_models()
可以列出所有可用的模型get_model_builder(name: str)
可以返回指定模型的构建方法,其中name
是模型的注册名称get_model(name: str, **config: Any)
可以返回指定模型的实例,其中config
是传递给构建方法的参数get_weight(name: str)
可以返回指定模型的预训练权重,其中name
是权重的名称get_model_weights(name: Union[Callable, str])
可以返回指定模型的所有预训练权重,其中name
是该模型的模型构建方法或注册名称
预训练权重的属性有:
name
预训练权重的名称url
预训练权重的 URLmeta
预训练权重的元数据,包括数据集的类别、参数数量等transforms
应用于输入图像的转换方法
Pytorch Hub 中的模型¶
查看预训练模型¶
torch.hub.list(
github,
force_reload=False,
skip_validation=False,
trust_repo=None,
)
torch.hub.help(
github,
model,
force_reload=False,
skip_validation=False,
trust_repo=None,
)
github
为repo_owner/repo_name[:ref]
的形式的字符串model
为 entrypoint 的名称,每个 entrypoint 是repo/hubconf.py
中的一个函数force_reload
若为 True,则会强制重新下载模型
加载预训练模型¶
torch.hub.load(
repo_or_dir,
model,
*args,
source='github',
trust_repo=None,
force_reload=False,
verbose=True,
skip_validation=False,
**kwargs,
)
# 加载模型
model = torch.hub.load("pytorch/vision", "resnet50", weights="IMAGENET1K_V2")
# 先加载预训练权重
weights = torch.hub.load("pytorch/vision", "get_weight", weights="ResNet50_Weights.IMAGENET1K_V2")
model = torch.hub.load("pytorch/vision", "resnet50", weights=weights)
- 若
source
为github
,则repo_or_dir
为repo_owner/repo_name[:ref]
- 若
source
为local
,则repo_or_dir
为本地路径 model
为 entrypoint 的名称,每个 entrypoint 是repo/hubconf.py
中的一个函数
torch.hub.load_state_dict_from_url(
url: str,
model_dir: Optional[str] = None,
map_location: Union[Callable[[torch.Tensor, str], torch.Tensor], torch.device, str, Dict[str, str], NoneType] = None,
progress: bool = True,
check_hash: bool = False,
file_name: Optional[str] = None,
) -> Dict[str, Any]
若下载的文件是一个压缩文件,它会被自动解压。若对象已经存在于 model_dir
中,则会直接加载对象。
url
模型的 URLmodel_dir
模型的保存路径,默认为<hub_dir>/checkpoints
hub_dir
为torch.hub.get_dir()
返回的目录- 如果没有使用
torch.hub.set_dir()
设置过目录,hub_dir
就是$TORCH_HOME/hub
$TORCH_HOME
的默认值是$XDG_CACHE_HOME/torch
$XDG_CACHE_HOME
的默认值是~/.cache
map_location
用于重映射存储位置的函数或字典progress
是否在 stderr 中显示进度条,默认为 Truecheck_hash
若为 True,则 URL 文件名部分的格式需符合filename-<sha256>.ext
,其中的哈希值会被用于验证文件file_name
保存的文件名,默认为url
中的文件名
数据¶
Dataset¶
自定义数据集¶
PyTorch 支持两种不同类型的数据集。
映射式数据集继承 torch.utils.data.Dataset,它必须重写 __getitem__() 方法,用以返回指定键的数据样本,同时可以可选地重写 __len__(),用以返回数据集的大小。
可迭代式数据集继承 torch.utils.data.IterableDataset,它必须重写 __iter__() 方法,用以返回数据样本的迭代器。这适用于随机读取代价很大,以及批量大小取决于获取的数据的情况。
下载数据集¶
MNIST/CIFAR10
torchvision.datasets.MNIST/CIFAR10(
root: str,
train: bool = True,
transform: Optional[Callable] = None,
target_transform: Optional[Callable] = None,
download: bool = False,
) -> None
root
若根目录下没有cifar-10-batches-py
且download
设为 True,下载并保存于此train
若为 True,则创建训练集,否则为测试集transform
将 PIL 图像转换的函数target_transform
将目标转换的函数download
若为 True 且数据集不存在,则从 Internet 下载
ImageNet
torchvision.datasets.ImageNet(
root: str,
split: str = 'train',
**kwargs: Any,
) -> None
split
支持train
和val
transform
将 PIL 图像转换的函数target_transform
将目标转换的函数loader
加载图像的函数,以图像路径为参数
Sampler¶
PyTorch 所有的采样器都是 torch.utils.data.Sampler 的子类,都要重写 __iter__(),用以返回一个迭代器,遍历数据集的所有索引;重写 __len__(),用以返回返回的迭代器的长度。
采样器不可用于可迭代式数据集,因为其不可使用索引。
torch.utils.data.SequentialSampler(data_source)
按原有顺序采样,每次返回一个索引torch.utils.data.RandomSampler(data_source, replacement=False, num_samples=None, generator=None)
随机采样,每次返回一个索引replacement
若为 True,则可以返回相同的索引num_samples
可以生成的样本数,默认为数据集的大小
torch.utils.data.BatchSampler(sampler, batch_size, drop_last)
批次采样,每次返回一批索引drop_last
若为 True,则会在最后一个批次的长度不足batch_size
时抛弃该批次
DataLoader¶
torch.utils.data.DataLoader(
dataset: torch.utils.data.dataset.Dataset[+T_co],
batch_size: Optional[int] = 1,
shuffle: Optional[bool] = None,
sampler: Union[torch.utils.data.sampler.Sampler, Iterable, NoneType] = None,
batch_sampler: Union[torch.utils.data.sampler.Sampler[Sequence], Iterable[Sequence], NoneType] = None,
num_workers: int = 0,
collate_fn: Optional[Callable[[List[~T]], Any]] = None,
pin_memory: bool = False,
drop_last: bool = False,
timeout: float = 0,
worker_init_fn: Optional[Callable[[int], NoneType]] = None,
multiprocessing_context=None,
generator=None,
*,
prefetch_factor: int = 2,
persistent_workers: bool = False,
pin_memory_device: str = '',
)
DataLoader 支持通过参数 batch_size, shuffle, batch_sampler, drop_last 和 collate_fn 实现自动批处理,将单个获取的数据样本整理成批次:
shuffle
若为 True,则会使用随机采样器sampler
用于batch_sampler
的构建,可由自己指定,不可与shuffle
冲突batch_sampler
可由自己指定,不可与batch_size
,shuffle
,sampler
和drop_last
冲突collate_fn
用于处理每批从数据集加载的数据
当 batch_size 和 batch_sampler 都为 None 时,自动批处理会被禁止,DataLoader 会逐个返回样本。
DataLoader 默认使用单进程数据加载。当 num_workers 为正整数时,会打开相同数量的子进程,进行多进程数据加载,此时 dataset, collate_fn 和 worker_init_fn 会被传递给每个子进程,用于初始化和处理数据。
对于映射式数据集,每个子进程要加载的数据的索引是由主进程的采样器分发的。对于可迭代式数据集,需要使用 torch.utils.data.get_worker_info()
或 worker_init_fn
来改变每个子进程迭代的数据范围,否则会返回重复的数据。
神经网络¶
自定义神经网络¶
PyTorch 提供了多种类用于自定义神经网络,其中最基础的就是 torch.nn.Module。所有神经网络都是 Module 的子类,必须重写 __init__() 和 forward() 方法。
此外,还有 Sequential, ModuleList, ModuleDict 等容器可用于快速构建网络。其中,Sequential 可用于顺序串联各层,直接构建网络:
model = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
model = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(1,20,5)),
('relu1', nn.ReLU()),
('conv2', nn.Conv2d(20,64,5)),
('relu2', nn.ReLU())
]))
ModuleList 可用于构建具有重复特性的网络,可像列表一样使用索引,其包含的模块会被正确注册:
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])
def forward(self, x):
for i, l in enumerate(self.linears):
x = self.linears[i // 2](x) + l(x)
return x
ModuleDict 可用于构建动态网络,可像字典一样使用索引,其包含的模块会被正确注册:
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.choices = nn.ModuleDict({
'conv': nn.Conv2d(10, 10, 3),
'pool': nn.MaxPool2d(3)
})
self.activations = nn.ModuleDict([
['lrelu', nn.LeakyReLU()],
['prelu', nn.PReLU()]
])
def forward(self, x, choice, act):
x = self.choices[choice](x)
x = self.activations[act](x)
return x
卷积层¶
nn.Conv2d(
in_channels: int,
out_channels: int,
kernel_size: Union[int, Tuple[int, int]],
stride: Union[int, Tuple[int, int]] = 1,
padding: Union[str, int, Tuple[int, int]] = 0,
dilation: Union[int, Tuple[int, int]] = 1,
groups: int = 1,
bias: bool = True,
padding_mode: str = 'zeros',
device=None,
dtype=None,
) -> None
kernel_size
若卷积核的宽和高相同,则可以用 int,否则必须用 tuple
池化层¶
nn.MaxPool2d(
kernel_size: Union[int, Tuple[int, ...]],
stride: Union[int, Tuple[int, ...], NoneType] = None,
padding: Union[int, Tuple[int, ...]] = 0,
dilation: Union[int, Tuple[int, ...]] = 1,
return_indices: bool = False,
ceil_mode: bool = False,
) -> None
stride
默认值为kernel_size
BN 层¶
nn.BatchNorm2d(
num_features: int,
eps: float = 1e-05,
momentum: float = 0.1,
affine: bool = True,
track_running_stats: bool = True,
device=None,
dtype=None,
) -> None
num_features
特征数,对于全连接层来说是向量长度,对于卷积层来说是通道数eps
用来防止归一化时除零
激活函数¶
torch.nn.ReLU(inplace=False)
inplace
若为 True,则会改变输入的原有值
torch.nn.LeakyReLU(negative_slope=0.01, inplace=False)
torch.nn.ELU(alpha=1.0, inplace=False)
alpha
小于零时的系数
torch.nn.PReLU(num_parameters=1, init=0.25, device=None, dtype=None)
num_parameters
要学习的 \(a\) (小于零时的系数) 的数量,只能等于 1 或者输入的通道数init
\(a\) 的初始值
torch.nn.Softmax(dim=None)
损失函数¶
-
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
L1 范数损失\[l_n = |x_n - y_n|\]size_average
已弃用reduce
已弃用reduction
指定对输出的处理'none'
不做处理,保持原有形状'mean'
取平均值'sum'
取总和
torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
均方误差损失
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=- 100, reduce=None, reduction='mean', label_smoothing=0.0)
交叉熵损失