入门
安装与张量
通过 pip/conda 安装 PyTorch,建议按官网选择匹配 CUDA 版本的命令。torch.tensor() 总是复制数据,而 torch.as_tensor() 复用内存(更快)。张量类似 NumPy ndarray,但可在 GPU 上运算并支持自动求导。
# install PyTorch (CUDA 11.8)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
import torch
# create a tensor
x = torch.tensor([[1, 2], [3, 4]])
y = torch.zeros(3, 3)
z = torch.randn(2, 3) # standard normal
# tensor on GPU
device = "cuda" if torch.cuda.is_available() else "cpu"
x = x.to(device)张量属性
dtype 决定数值精度(默认 float32),device 决定 CPU/GPU 位置。requires_grad 启用自动求导。ndim 是维度数,shape 返回各维度大小。tensor.item() 仅适用于单元素张量,会触发同步。
x = torch.randn(3, 4, 5)
print(x.shape) # torch.Size([3, 4, 5])
print(x.dtype) # torch.float32
print(x.device) # cpu or cuda:0
print(x.requires_grad) # False
# specify dtype at creation
x = torch.zeros(3, dtype=torch.int64)
x = torch.tensor([1.0, 2.0], dtype=torch.float16)
# check number of elements
print(x.numel()) # 3张量创建
zeros/ones/empty 创建基础张量;arange/linspace 生成等差序列。*_like 函数沿用输入张量的形状与 dtype。randn 生成标准正态分布,rand 生成 [0,1) 均匀分布。empty 不初始化内存,可能含任意值。
# from Python lists
a = torch.tensor([1, 2, 3])
# special tensors
zeros = torch.zeros(2, 3)
ones = torch.ones(2, 3)
eye = torch.eye(3) # identity matrix
full = torch.full((2, 3), 7.0)
# ranges
arange = torch.arange(0, 10, 2) # [0, 2, 4, 6, 8]
linspace = torch.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1.0]
# random
rand = torch.rand(2, 3) # uniform [0, 1)
randn = torch.randn(2, 3) # normal N(0, 1)
randint = torch.randint(0, 10, (3, 3))类型转换
.to(dtype) 是推荐的类型转换方式,也可用 .float()/.long()/.bool() 等便捷方法。整型张量做除法用 //(地板除)或先转 float。device 也可用 .to('cuda') 同时转换。注意 dtype 不匹配会触发自动类型提升。
x = torch.tensor([1.5, 2.5, 3.5])
# change dtype
y = x.to(torch.int32) # tensor([1, 2, 3])
y = x.int() # shortcut
y = x.float()
y = x.bool()
y = x.long() # int64
# to numpy and back
arr = x.numpy() # shares memory (CPU)
t = torch.from_numpy(arr)
# 0-d tensor to Python scalar
val = x[0].item() # 1.5索引与切片
索引语义与 NumPy 一致。负索引从末尾开始。Advanced indexing(用张量/列表索引)会创建副本而非视图。使用 [...] 保留其余维度,用 : 选择整维。布尔掩码索引常用于过滤样本。
x = torch.arange(12).reshape(3, 4)
# tensor([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
x[0] # first row
x[1, 2] # element at (1, 2) -> 6
x[:, 1] # second column
x[0:2] # first two rows
x[:, ::2] # every other column
# boolean mask
mask = x > 5
x[mask] # tensor([6, 7, 8, 9, 10, 11])
# index with LongTensor
idx = torch.tensor([0, 2])
x[idx] # rows 0 and 2变形与视图
view 要求内存连续,否则需先 contiguous()。reshape 自动处理不连续情况。permute 调换维度顺序。squeeze/remove 移除大小为 1 的维度,unsqueeze 在指定位置插入维度。view 不复制数据,与原张量共享内存。
x = torch.arange(12)
# reshape (returns view when possible)
y = x.reshape(3, 4)
y = x.view(3, 4) # only for contiguous tensors
# add/remove dimensions
y = x.unsqueeze(0) # shape (1, 12)
y = x.squeeze() # remove size-1 dims
# transpose / permute
a = torch.randn(3, 4, 5)
b = a.permute(2, 0, 1) # shape (5, 3, 4)
b = a.transpose(0, 1) # swap dims 0 and 1
# flatten
flat = a.flatten() # shape (60,)
flat = a.reshape(-1) # equivalent张量运算
逐元素运算
运算符 +、-、*、/ 是逐元素的。** 是幂运算,@ 是矩阵乘法。torch.clamp 限制数值范围。原地运算(带 _ 后缀,如 add_)节省内存但会破坏自动求导,慎用。逐元素乘法 * 与矩阵乘法 @ 完全不同。
a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])
# element-wise ops
a + b # tensor([5., 7., 9.])
a * b # tensor([ 4., 10., 18.])
a / b # element-wise division
a ** 2 # tensor([1., 4., 9.])
# in-place (suffix _)
a.add_(1) # a = a + 1, modifies in place
a.mul_(2) # a = a * 2
# scalar ops
a + 10
b * 0.5归约运算
sum/mean/max/min 沿指定维度归约。keepdim=True 保留该维度为 1,便于广播。argmax 返回最大值索引(分类任务取预测类别常用)。mean 默认对整个张量求平均,注意整型需先转 float。
x = torch.tensor([-1.0, 0.0, 1.0, 2.0])
torch.abs(x) # absolute value
torch.sqrt(torch.abs(x))
torch.exp(x) # e^x
torch.log(x.abs() + 1e-8)
torch.sin(x)
torch.clamp(x, -0.5, 1.5) # clip values
# activation functions
torch.sigmoid(x) # 1 / (1 + e^-x)
torch.tanh(x)
torch.relu(x) # max(0, x)
# rounding
y = torch.tensor([1.4, 1.5, 2.6])
torch.round(y) # tensor([1., 2., 3.])
torch.floor(y)
torch.ceil(y)矩阵运算
@ 或 torch.matmul 支持批量矩阵乘法(最后两维)。mm/bmm 仅限 2D/3D。einsum 用爱因斯坦求和约定表达复杂运算(如转置、双线性、注意力分数),可读性强。torch.linalg.inv/svd/qr 提供线性代数运算。
x = torch.arange(12).reshape(3, 4).float()
x.sum() # 66.0 (scalar tensor)
x.sum(dim=0) # sum over rows -> shape (4,)
x.sum(dim=1) # sum over columns -> shape (3,)
x.mean(dim=1)
x.max() # returns value tensor
x.max(dim=1) # returns (values, indices)
x.argmin(dim=1) # indices of min along dim
# keep dimensions
x.sum(dim=1, keepdim=True) # shape (3, 1)
# norm
x.norm() # Frobenius norm
x.norm(dim=1) # per-row norm广播机制
广播规则与 NumPy 一致:从末尾维度对齐,维度为 1 或缺失时扩展。形状 (3,1) 与 (1,4) 广播为 (3,4)。避免意外的广播导致内存膨胀或错误结果。einsum 是显式维度的安全替代方案。
a = torch.tensor([1, 2, 3, 4])
b = torch.tensor([3, 2, 1, 4])
a == b # tensor([False, True, False, True])
a > b # tensor([False, False, True, False])
a != b
torch.equal(a, b) # False (single bool)
# where: pick from two tensors
torch.where(a > b, a, b) # element-wise max
# boolean helpers
(a > 2).any() # True
(a > 2).all() # False
(a == b).sum() # 2 (count of True)
# top-k
torch.topk(a, 2) # returns top 2 values + indices拼接与堆叠
cat 沿现有维度拼接,维度不变;stack 沿新维度堆叠,维度数 +1。cat 要求非拼接维度形状一致。stack 要求所有张量形状完全相同。split/chunk 是其逆操作。
a = torch.randn(2, 3)
b = torch.randn(3, 4)
# matrix multiplication
c = a @ b # shape (2, 4)
c = torch.matmul(a, b) # equivalent
# batched matmul
A = torch.randn(10, 2, 3)
B = torch.randn(10, 3, 4)
C = A @ B # shape (10, 2, 4)
# element-wise product
e = a * a # shape (2, 3)
# other ops
torch.inverse(a[:, :3]) # inverse of square slice
torch.det(a[:, :3])
torch.svd(a) # singular value decomposition
torch.einsum('bi,i->b', torch.randn(5,3), torch.randn(3)) # batched dot比较运算
比较运算返回布尔张量。torch.where(cond, x, y) 按条件选择元素。topk 返回前 k 大的值与索引。isnan/isinf 用于检测数值异常。eq/equal 做精确比较,allclose 做带容差的近似比较。
a = torch.randn(2, 3)
b = torch.randn(2, 3)
# concat along existing dim
c = torch.cat([a, b], dim=0) # shape (4, 3)
c = torch.cat([a, b], dim=1) # shape (2, 6)
# stack along new dim
s = torch.stack([a, b], dim=0) # shape (2, 2, 3)
# split / chunk
x = torch.arange(6).reshape(2, 3)
parts = torch.split(x, 2, dim=0) # split into chunks of size 2
chunks = torch.chunk(x, 3, dim=1) # split into 3 chunks
# repeat / expand
r = a.repeat(2, 3) # repeat whole tensor
e = a.unsqueeze(0).expand(4, 2, 3) # broadcast without copy自动求导
自动求导基础
requires_grad=True 启用梯度追踪。backward() 从标量损失反向传播,自动填充 .grad 属性。叶子张量(用户创建)才有 .grad,中间结果默认释放。grad_fn 指向生成该张量的反向函数。
import torch
# tensors that require gradient tracking
x = torch.tensor([2.0], requires_grad=True)
y = torch.tensor([3.0], requires_grad=True)
# build a computation graph
z = x * y + x ** 2 # z = xy + x^2
print(z.grad_fn) # <AddBackward0 object>
# backprop
z.backward()
# dz/dx = y + 2x = 3 + 4 = 7
print(x.grad) # tensor([7.])
# dz/dy = x = 2
print(y.grad) # tensor([2.])梯度计算
对非标量张量调用 backward 需传入 gradient 参数(与该张量同形状的权重)。更常见的做法是对标量 loss 直接 backward()。.grad 累加而非覆盖,每次反向前应 optimizer.zero_grad() 或 model.zero_grad()。
import torch
x = torch.linspace(-3, 3, steps=10, requires_grad=True)
y = torch.sin(x)
# sum needed for backward on non-scalar
y.sum().backward()
print(x.grad) # cos(x)
# gradient w.r.t. intermediate tensor
a = torch.tensor(1.0, requires_grad=True)
b = a * 2
c = b ** 2
grads = torch.autograd.grad(c, a)
print(grads) # (tensor(8.),) dc/da = 4b * 2 = 8计算图
PyTorch 默认动态图,每次前向都构建新图。retain_graph=True 保留图供多次反向(如双重反向求 Hessian)。 backward 后图默认释放以节省内存。retain_grad() 让非叶子张量也保存 .grad。
x = torch.tensor([1.0], requires_grad=True)
# option 1: context manager (preferred)
with torch.no_grad():
y = x * 2 # y.requires_grad == False
# option 2: decorator
@torch.no_grad()
def inference(x):
return model(x)
# option 3: detach from graph
y = (x * 2).detach() # y is a new tensor, no grad
# enable grad inside no_grad
with torch.no_grad():
with torch.enable_grad():
z = x * 3 # z requires grad禁用梯度
推理/验证时用 with torch.no_grad() 或 model.eval() 关闭梯度计算,显著降低内存与时间开销。@torch.no_grad() 装饰器形式用于函数。torch.enable_grad() 在 no_grad 上下文中重新启用。inference_mode 比 no_grad 更快但限制更多。
x = torch.tensor(0.5, requires_grad=True)
# first derivative
y = torch.sin(x)
y.backward(create_graph=True)
print(x.grad) # cos(0.5)
# second derivative (gradient of gradient)
x.grad.zero_()
g = torch.autograd.grad(torch.sin(x), x, create_graph=True)[0]
g2 = torch.autograd.grad(g, x)[0]
print(g2) # -sin(0.5)
# practical use: penalize gradient (e.g. gradient penalty)
loss = g.norm()自定义 Autograd 函数
继承 Function 并实现 forward 和 backward 静态方法。backward 接收上游梯度,返回各输入的梯度。ctx.save_for_backward 保存前向张量供反向使用。用于实现不可导为普通算子的自定义层(如分段线性、稀疏操作)。
x = torch.tensor([1.0, 2.0], requires_grad=True)
y = (x ** 2).sum()
# full backward hook on a tensor
def print_grad(grad):
print("grad:", grad)
return grad * 2 # can modify gradient
y.register_hook(print_grad)
y.backward()
print(x.grad) # 2x but doubled by hook
# module hook
def hook_fn(module, grad_input, grad_output):
print(module.__class__.__name__, grad_output)
model.layer.register_full_backward_hook(hook_fn)梯度钩子
register_hook 在反向传播时触发回调,可查看或修改梯度(返回新梯度即替换)。常用于梯度分析、调试 NaN、实现梯度惩罚(WGAN-GP)。张量钩子在第一次反向后自动移除,模块钩子持久。register_full_backward_hook 更精确。
x = torch.tensor(2.0, requires_grad=True)
w = torch.tensor(3.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
y = w * x + b
print(y.is_leaf) # False
print(x.is_leaf) # True
# retain graph for multiple backward passes
loss = (y - 5) ** 2
loss.backward(retain_graph=True)
# can call backward again
loss.backward()
# double backward
loss.backward(create_graph=True)
print(x.grad)神经网络模块
nn.Module 基础
所有自定义模型继承 nn.Module。在 __init__ 中定义子模块和参数,在 forward 中定义计算流。model.parameters() 迭代所有可学习参数,model.to(device) 递归迁移。模块可嵌套形成计算树。
from torch.utils.data import Dataset, DataLoader
# built-in datasets
from torchvision import datasets
mnist = datasets.MNIST("./data", train=True, download=True)
# wrap in DataLoader
loader = DataLoader(
mnist,
batch_size=64,
shuffle=True,
num_workers=4,
pin_memory=True,
)
for x, y in loader:
print(x.shape, y.shape) # (64, 1, 28, 28), (64,)
break常用层
Linear 做仿射变换(in_features → out_features)。Conv2d 用 in/out_channels 控制通道数。batch_first=True 让 RNN 输入为 (batch, seq, feature)。Embedding 将整数索引映射为稠密向量,权重即查找表。
from torch.utils.data import DataLoader, BatchSampler, RandomSampler
dataset = list(range(100))
sampler = RandomSampler(dataset)
batch_sampler = BatchSampler(sampler, batch_size=10, drop_last=True)
loader = DataLoader(dataset, batch_sampler=batch_sampler)
# custom sampler
class EvenSampler:
def __init__(self, data): self.data = data
def __iter__(self): return iter(range(0, len(self.data), 2))
def __len__(self): return len(self.data) // 2
# weighted sampling for imbalanced classes
from torch.utils.data import WeightedRandomSampler
weights = [0.1] * 50 + [1.0] * 50
sampler = WeightedRandomSampler(weights, num_samples=100)激活函数
ReLU 是 CNN 的默认选择,LeakyReLU 防止神经元死亡。GELU 在 Transformer 中流行。Sigmoid 输出 [0,1],Tanh 输出 [-1,1]。现代网络多用 inplace=True 节省内存,但慎用于需要原始输入的 autograd 场景。
from torch.utils.data import DataLoader, random_split
# train/val split
train, val = random_split(dataset, [80, 20])
train_loader = DataLoader(train, batch_size=16, shuffle=True)
val_loader = DataLoader(val, batch_size=32, shuffle=False)
# reproducible split
from torch.utils.data import Subset
import numpy as np
gen = torch.Generator().manual_seed(42)
idx = torch.randperm(len(dataset), generator=gen).tolist()
train_idx, val_idx = idx[:80], idx[80:]
train, val = Subset(dataset, train_idx), Subset(dataset, val_idx)Sequential API
Sequential 按顺序串联模块,前向自动依次调用。OrderedDict 可为每层命名便于访问(model.features,model.classifier)。适合线性堆叠的简单模型,复杂分支结构仍需自定义 forward。
from torch.utils.data import default_collate
# variable-length sequences: pad in collate
def pad_collate(batch):
# batch is list of (tensor, label)
seqs, labels = zip(*batch)
lens = torch.tensor([len(s) for s in seqs])
max_len = lens.max()
padded = torch.zeros(len(seqs), max_len)
for i, s in enumerate(seqs):
padded[i, :len(s)] = s
return padded, lens, torch.tensor(labels)
loader = DataLoader(dataset, batch_size=4, collate_fn=pad_collate)
# stack batch as dict
def dict_collate(batch):
return default_collate(batch)参数与缓冲区
nn.Parameter 是自动注册的 Tensor,requires_grad 默认 True。register_buffer 注册非学习但需随模型迁移的张量(如 BN 的 running_mean)。state_dict() 同时返回参数与缓冲区,便于保存/加载。
loader = DataLoader(
dataset,
batch_size=128,
num_workers=8, # one process per worker
pin_memory=True, # page-locked memory for fast H2D copy
persistent_workers=True, # keep workers alive across epochs
prefetch_factor=4, # batches prefetched per worker
)
# avoid CPU bottleneck
import os
os.cpu_count() # check available cores
# in Jupyter / Windows: set start method
import torch.multiprocessing as mp
# mp.set_start_method('spawn') # uncomment if needed损失函数
MSE 与 L1 损失
MSELoss 用于回归,对大误差更敏感(平方放大)。L1Loss 对异常值更鲁棒。SmoothL1Loss(Huber)在误差小时用平方、大时用线性,兼顾两者。reduction 默认为 'mean',求和用 'sum',不归约用 'none'。
import torch.nn as nn
class MLP(nn.Module):
def __init__(self, in_dim, hidden, out_dim):
super().__init__()
self.fc1 = nn.Linear(in_dim, hidden)
self.fc2 = nn.Linear(hidden, hidden)
self.fc3 = nn.Linear(hidden, out_dim)
self.act = nn.ReLU()
def forward(self, x):
x = self.act(self.fc1(x))
x = self.act(self.fc2(x))
return self.fc3(x) # logits
model = MLP(784, 128, 10)
print(model) # prints structure交叉熵损失
CrossEntropyLoss 内部已包含 LogSoftmax,不要在模型最后再加 Softmax!输入应为未归一化的 logits。label_smoothing>0 防止过拟合。忽略某些类别用 ignore_index(如 padding)。多标签分类应用 BCEWithLogitsLoss。
import torch.nn as nn
linear = nn.Linear(in_features=20, out_features=10, bias=True)
print(linear.weight.shape) # (10, 20)
print(linear.bias.shape) # (10,)
# common activations
nn.ReLU() # max(0, x)
nn.LeakyReLU(0.01)
nn.GELU() # smooth, used in transformers
nn.Sigmoid() # (0, 1)
nn.Tanh() # (-1, 1)
nn.Softmax(dim=1)
nn.ELU()
# functional API (no parameters)
import torch.nn.functional as F
out = F.relu(linear(x)) # same as nn.ReLU()(linear(x))二元交叉熵损失
BCEWithLogitsLoss 内置 Sigmoid,数值上比 Sigmoid+BCELoss 更稳定(避免 log(0))。pos_weight 处理类别不平衡(如正样本稀少)。多标签分类(每样本多类别独立为 0/1)用此损失,每维独立判断。
model = MLP(784, 128, 10)
# iterate parameters
for name, p in model.named_parameters():
print(name, p.shape, p.requires_grad)
# only trainable params
trainable = filter(lambda p: p.requires_grad, model.parameters())
# count parameters
n_params = sum(p.numel() for p in model.parameters())
print(f"{n_params:,} parameters")
# access submodules by name
model.fc1 # the Linear layer
model['fc1'] # equivalent if using nn.ModuleDict
# children vs modules
list(model.children()) # direct children
list(model.modules()) # recursively all sub-modules负对数似然损失
NLLLoss 要求模型最后输出 LogSoftmax。等价于 CrossEntropyLoss 但分开两步,便于自定义 log-softmax。ignore_index 可跳过不参与损失的样本(如 padding token)。旧代码中常见,新代码建议直接用 CrossEntropyLoss。
import torch.nn as nn
# simple stack
model = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(256, 10),
)
# named layers via OrderedDict
from collections import OrderedDict
model = nn.Sequential(OrderedDict([
('fc1', nn.Linear(784, 256)),
('relu', nn.ReLU()),
('dropout', nn.Dropout(0.5)),
('fc2', nn.Linear(256, 10)),
]))
print(model.fc1) # access by name
# append at runtime
model.append(nn.Softmax(dim=1))自定义损失
损失函数也继承 nn.Module,在 forward 中实现。所有运算用 torch 函数以保证自动求导可反向。inplace 操作可能破坏梯度。可学习损失(带参数)也用 nn.Parameter 注册。
model = MLP(784, 128, 10)
# move to device / dtype
model = model.to('cuda')
model = model.to(torch.float16)
# train vs eval mode
model.train() # enable dropout, update BN stats
model.eval() # disable dropout, freeze BN stats
# state dict
sd = model.state_dict()
model.load_state_dict(sd)
# apply a function to all submodules
model.apply(lambda m: print(type(m).__name__))
# set requires_grad on all parameters
for p in model.parameters():
p.requires_grad_(False)三元组与对比损失
TripletMarginLoss 用于度量学习,拉近锚点与正样本、推远与负样本。margin 是关键超参。CosineEmbeddingLoss 基于余弦相似度。对比学习(SimCLR/MoCo)常用 InfoNCE 损失,温度参数 τ 控制难度。
import torch.nn as nn
import torch.nn.init as init
class Net(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(100, 50)
self.fc2 = nn.Linear(50, 10)
self._init_weights()
def _init_weights(self):
init.kaiming_normal_(self.fc1.weight, nonlinearity='relu')
init.zeros_(self.fc1.bias)
init.xavier_uniform_(self.fc2.weight)
init.zeros_(self.fc2.bias)
# apply to existing model
def init_all(m):
if isinstance(m, nn.Linear):
init.kaiming_normal_(m.weight)
if m.bias is not None:
init.zeros_(m.bias)
model.apply(init_all)优化器
SGD
momentum 加速收敛并减少震荡,典型值 0.9。weight_decay 实现 L2 正则化(等价于梯度加上 λ·w)。Nesterov 在动量更新前先向前看一步,常略优。SGD+Momentum 泛化常优于 Adam,是大模型预训练首选。
import torch
import torch.nn as nn
# logits shape (N, C), targets shape (N,) with class indices
logits = torch.randn(4, 3) # 4 samples, 3 classes
targets = torch.tensor([0, 2, 1, 0])
criterion = nn.CrossEntropyLoss()
loss = criterion(logits, targets)
# equivalently, combine LogSoftmax + NLLLoss
log_softmax = nn.LogSoftmax(dim=1)
nll = nn.NLLLoss()
loss2 = nll(log_softmax(logits), targets)
# weight classes (handle imbalance)
weights = torch.tensor([1.0, 2.0, 1.0])
criterion = nn.CrossEntropyLoss(weight=weights)
# ignore padding index (NLP)
criterion = nn.CrossEntropyLoss(ignore_index=-100)Adam 与 AdamW
Adam 自适应学习率,收敛快,默认超参对大多任务可用。AdamW 修正了权重衰减的实现(与 L2 正则不同),是 Transformer 微调的标配。amsgrad 防止学习率单调不降导致的发散。betas 控制一二阶矩的平滑系数。
import torch.nn as nn
pred = torch.randn(4, 1)
target = torch.randn(4, 1)
# mean squared error (L2)
mse = nn.MSELoss()
loss = mse(pred, target) # mean over all elements
# L1 (mean absolute error)
l1 = nn.L1Loss()
loss = l1(pred, target)
# smooth L1 (Huber) — robust to outliers
smooth = nn.SmoothL1Loss(beta=0.1)
# reduction options
mse_sum = nn.MSELoss(reduction='sum')
mse_none = nn.MSELoss(reduction='none') # per-element学习率调度器
StepLR 按固定步长衰减。CosineAnnealingLR 平滑衰减到 eta_min。OneCycleLR 配合 warmup 实现超级收敛(先升后降)。ReduceLROnPlateau 根据指标停滞自动降学习率。调用 scheduler.step() 应在 optimizer.step() 之后。
import torch.nn as nn
# binary classification, multi-label, or sigmoid outputs
logits = torch.randn(4, 1)
target = torch.tensor([[1.0], [0.0], [1.0], [0.0]])
# preferred: combines sigmoid + BCE, numerically stable
criterion = nn.BCEWithLogitsLoss()
loss = criterion(logits, target)
# BCELoss expects probabilities (apply sigmoid first)
criterion = nn.BCELoss()
probs = torch.sigmoid(logits)
loss = criterion(probs, target)
# positive weight for imbalanced binary tasks
pos_weight = torch.tensor([5.0])
criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)逐层学习率
不同层用不同学习率常用于微调预训练模型(底层特征通用,学 习率小;新分类头大)。param_groups 是列表,每个组可单独设置 lr、weight_decay 等。也可为不同参数组设置不同 weight_decay(如 LayerNorm 和 Bias 不衰减)。
import torch
import torch.nn as nn
import torch.nn.functional as F
# as a function
def huber_loss(pred, target, delta=1.0):
error = pred - target
abs_err = error.abs()
quad = torch.where(abs_err <= delta,
0.5 * error ** 2,
delta * (abs_err - 0.5 * delta))
return quad.mean()
# as a module (so it can have parameters / state)
class FocalLoss(nn.Module):
def __init__(self, alpha=0.25, gamma=2.0):
super().__init__()
self.alpha = alpha
self.gamma = gamma
def forward(self, logits, targets):
bce = F.binary_cross_entropy_with_logits(logits, targets, reduction='none')
p = torch.sigmoid(logits)
pt = p * targets + (1 - p) * (1 - targets)
loss = self.alpha * (1 - pt) ** self.gamma * bce
return loss.mean()
criterion = FocalLoss()梯度裁剪
clip_grad_norm_ 按全局 L2 范数裁剪(更常用),clip_grad_value_ 按元素裁剪到 [-v, v]。防止梯度爆炸导致训练崩溃,RNN/Transformer 必备。在 backward() 之后、optimizer.step() 之前调用。
import torch.nn as nn
import torch
criterion = nn.CrossEntropyLoss(reduction='none')
loss_per = criterion(logits, targets) # shape (N,)
loss = loss_per.mean()
# class-wise mask
mask = targets != -100
loss = criterion(logits[mask], targets[mask])
# multi-task loss
loss_cls = nn.CrossEntropyLoss()(logits, labels)
loss_box = nn.L1Loss()(boxes_pred, boxes_gt)
total = 1.0 * loss_cls + 0.5 * loss_box
# gradient-weighted
total = loss_cls + 0.5 * loss_box
total.backward()优化器状态
Adam 的 state 保存一二阶矩的滑动平均。load_state_dict 恢复时检查参数形状匹配。state_dict 可单独保存用于断点续训。空 state_dict 表示从零开始。不同优化器的 state 结构不同,不可互换。