Skip to main content

Pytorch nn AND nn.Functional

🚀 PyTorch API 技术指南:从核心构建到高级操作原理与实践

你好,PyTorch 探索者!无论你是刚入门的新手还是寻求进阶的开发者,掌握 PyTorch 丰富的 API 都是提升你模型构建能力的关键。

本指南将一些强大而常用的 PyTorch API 分旨在五大类,从模型的基础骨架到高效的数据操作,为你开发者提供一份清晰、易懂关于 PyTorch 核心 API 技术参考。让我们开始吧!内容涵盖了从模型架构定义到高级张量操作的常用接口,并对每个 API 的基本原理和标准用法进行了解析,辅以简明的代码示例。


第一部分:核心模型架构 (torch.nn)

torch.nn 命名空间提供了构建神经网络所需核心构建所有基础模 🏗️

这些模块构建任何面向对象的设计,封装了可学习的参数和相应的计算逻辑。

1.1 nn.Module: 所有神经网络模的基石。

1. nn.Module:万物之基

原理: nn.Module 是 PyTorch 中所有神经网络块的父类。当你构建的核心。任何自定义的网络层或完整的模型或层时,都应继承此类。提供了参数跟踪、子模块注册、设备转移 (.to(device))、模式切换 (.train()/.eval()) 等一系列基础功能

核心功能:

  • 自动参数跟踪: 任何被定义为 nn.Parameter 的属性都会被自动添加到模型的参数列表中。
  • 子模块管理: 它可以像容器一样持有其他 nn.Module,形成一个嵌套的树状结构。
  • 便捷功能: 提供了如 .to(device)(移动模型到 CPU/GPU)、.train()(设为训练模式)、.eval()(设为评估模式)等实用方法。

用法示例:

import torch
import torch.nn as nn

# 创建一个简单的自定义模型
class MySimpleNet(CustomModel(nn.Module):
    def __init__(self, input_size,in_features, output_size)out_features):
        super(MySimpleNet,CustomModel, self).__init__()
        # 将 nn.Linear 层作为子模块
        self.linearlayer = nn.Linear(input_size,in_features, output_size)out_features)

    def forward(self, x):
        return self.linear(layer(x)

# 实例化模型
model = MySimpleNet(input_size=10,CustomModel(128, output_size=2)10)
print(model)
# MySimpleNet(输出:
# CustomModel(
#   (linear)layer): Linear(in_features=10,128, out_features=2,10, bias=True)
# )

2.1.2 nn.Parameter:模型的: 可学习参数的封装

原理: nn.Parametertorch.Tensor 的一个特殊包装类。当一个 nn.Parameter 对象被赋值为 nn.Module 的属性时,它自动被注册为模型的可学习参数这意味着在反向传播后,优化器optimizer.step() 时自动计算并更新其梯度

核心功能:

  • nn.Module 注册一个需要计算梯度的张量。
  • 可以通过 model.parameters() 访问到。

用法示例:

class MyCustomLayer(CustomLayer(nn.Module):
    def __init__(self, input_features, output_features)size):
        super(MyCustomLayer,CustomLayer, self).__init__()
        # 手动定义一个可学习的权重矩阵缩放因子
        self.weightscale = nn.Parameter(torch.randn(output_features, input_features))
        # 也可以定义一个偏置
        self.bias = nn.Parameter(torch.zeros(output_features)ones(size))

    def forward(self, x):
        return x * self.scale

# 使用自定义访问参数进行操作
        return F.linear(x, self.weight, self.bias)
layer = MyCustomLayer(5, 3)
# 打印所有可学习参数CustomLayer(5)
for name, param in layer.named_parameters(parameters():
    print(f"Name: {name},Parameter Shape: {param.shape}, Requires Grad: {param.requires_grad}")

1.3

第二部分:张量初始化:播下成功的种子nn.Linear: 🌱

全连接层

正确的初始化是模型成功训练的第原理: nn.Linear 对输入数据应用个线性变换:$y = xA^T + b$。它内部维护了权重矩阵 A 和偏置向量 b 作为可学习参数

1. torch.empty() & torch.zeros():创建张量画布

  • torch.empty(*size): 创建一个指定形状的张量,但不进行任何初始化。它的值是分配内存时存在的任意值,因此速度很快,但使用前必须填充数据。
  • torch.zeros(*size): 创建一个指定形状且所有元素都为 0 的张量。

用法示例:

# 创建一个未初始化的输入: 2x3(Batch 张量Size, uninitialized_tensorin_features)
input_tensor = torch.empty(2,randn(64, 3)128)
linear_layer = nn.Linear(in_features=128, out_features=10)
output = linear_layer(input_tensor)
print(f"Output shape: {output.shape}"Empty Tensor:\n", uninitialized_tensor)) # 创建一个全为torch.Size([64, 0 的 2x3 张量
zero_tensor = torch.zeros(2, 3)
print("Zero Tensor:\n", zero_tensor)10])

2.1.4 torch.nn.init.kaiming_uniform_Conv2d:智能的权重初始化: 二维卷积层

这是一种专门为使用 ReLU 及其变体激活函数的网络设计的权重初始化方法(也称 He 初始化)。它可以帮助缓解梯度消失或爆炸问题。函数名末尾的下划线原理: _nn.Conv2d 表示它是一个 in-place 操作,会直接修改输入的二维信号(如图像)上应用二维卷积操作。它通过滑动一个可学习的卷积核(kernel)来提取局部特征。关键参数包括 in_channels(输入通道数)、out_channels(输出通道数,即卷积核数)、kernel_size(卷积核尺寸)、stride 和 padding

用法示例:

import torch.nn.init as init
import math

# 首先创建一个权重张量输入: weight(Batch, Channels, Height, Width)
input_image = torch.empty(randn(16, 3, 5)224, 224)
conv_layer = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1, padding=1)
feature_maps = conv_layer(input_image)
print(f"Feature map shape: {feature_maps.shape}") # torch.Size([16, 32, 224, 224])

1.5 nn.Sequential: 顺序容器

原理: nn.Sequential 是一个模块容器,它会按照模块被传入构造函数的顺序,依次将输入数据传递给每个模块。这是一种快速构建简单线性堆叠模型的便捷方式。

示例:

model = nn.Sequential(
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 10)
)
input_data = torch.randn(64, 784)
output = model(input_data)
print(f"Sequential model output shape: {output.shape}") # torch.Size([64, 10])

1.6 nn.Dropout: Dropout 正则化

原理: Dropout 是一种在训练期间使用的正则化技术。它会以指定的概率 Kaimingp 均匀将输入张量中的部元素随机置为零,其余元素则按 1/(1-p) 的比例进行缩放,以保持整体期望值不变。这能有效防止神经元之间的共适应,降低过拟合风险。在评估模式 in-place(.eval())下,nn.Dropout 初始化不会执行任何操作。

init.kaiming_uniform_(weight,

示例:

a=math.sqrt(
dropout_layer = nn.Dropout(p=0.5)
input_tensor = torch.ones(1, 10)
dropout_layer.train() # 切换到训练模式
output_train = dropout_layer(input_tensor)
print(f"Train mode output (example): {output_train}"Kaiming) Initialized# Weight:\n",约一半元素为0,其余为2.0

weight)dropout_layer.eval() # 切换到评估模式
output_eval = dropout_layer(input_tensor)
print(f"Eval mode output: {output_eval}") # 所有元素仍为1.0

部分:常用神经网络函数式接口 🧠(torch.nn.functional)

这些函数位于 torch.nn.functional(通常别名简写F提供了一系列无状态的函数它们构建网络计算图 nn 模块核心部分底层实现。这些函数不包含可学习参数,调用时需要显式传入所有输入,包括权重

1.2.1 F.linear(input,relu: weight,ReLU bias=None):无状态的线性变换激活函数

原理: nn.修正线性单元(Rectified Linear 模块不同,F.linear Unit)是一个纯种分段线性函数,其数学表达式为 $f(x) = \max(0, x)$。它不持任何内部状态(如权重或偏置)效缓解梯度消失问题你需要手动将这些参数传入。这在权重是动态生成或需要共享时非常有用且计算开销小

用法示例:

import torch.nn.functional as F

input_tensor = torch.randn(4,tensor([-1.0, 5)0.0, # (batch_size, in_features)
weight_matrix = torch.randn(3, 5) # (out_features, in_features)
bias_vector = torch.randn(3)      # (out_features)

# 应用线性变换2.0])
output = F.linear(input_tensor, weight_matrix, bias_vector)relu(input_tensor)
print(f"ReLU output: {output}"Output shape:", output.shape)) # torch.Size(tensor([4,0., 3]0., 2.])

2.2 torch.Tensor.softmax(dim)F.softmax:将输出转换为概率: Softmax 激活函数

原理: Softmax 函数将一个实向量转换为一个概率分布。它对向量中的每个元素应用指数函数其中然后将结果归一化,使得所有元素的和为 1。dim 参数至关重要,它指定了在哪进行归化的维度上进行 Softmax 操作

用法示例:

# 假设这是模型对3个类别的原始输出(logits)
logits = torch.tensor([[1.0, 2.0, 0.5]3.0], [3.1.0, 1.0, 1.5]0]])

# 沿着最后一个维度(类别维度)计算概率
probabilities = logits.F.softmax(logits, dim=-1)
print(f"Softmax probabilities:\n{probabilities}"Probabilities:\n")
# 输出:
# tensor([[0.0900, 0.2447, 0.6652],
probabilities)#         print("Sum[0.3333, of0.3333, probabilities per sample:", probabilities.sum(dim=-1)0.3333]])

3.2.3 F.one_hot(tensor,cross_entropy: num_classes=-1):分类任务的好帮手

这个函数可以将一个包含类别索引的整数张量转换为 one-hot 编码的张量。这在计算交叉熵损失或需要二进制

原理: 交叉熵损失函数通常用于多分类任务。它在内部高效地集成了 LogSoftmax 和 NLLLoss(负对数似然损失)。它衡量的是模型预测的概率分布与真实的类别表示时非标签(通有用是 one-hot 形式,但函数接口接收类别索引)之间的差异

用法示例:

# 类别模型输出 (logits) 和真实标签
(batch_size=4)logits = torch.randn(4, 5) # 4个样本, 5个类别
labels = torch.tensor([1, 0, 2,4, 1, 3]2]) # 转换为 one-hot 编码,假设总共有 5 样本的真实类别索引

one_hot_labelsloss = F.one_hot(labels,cross_entropy(logits, num_classes=5)labels)
print(f"Cross Entropy Loss: {loss.item()}"One-Hot)
Labels:\n",
one_hot_labels)

2.4 #F.mse_loss: 均方误差损失

原理: 均方误差(Mean Squared Error)用于回归任务。它计算的是模型预测值与真实值之间差的平方的平均值。

示例:

predictions = torch.tensor([1.5, 2.8, 4.1])
targets = torch.tensor([1, 1.0, 3.0, 0,4.0])
0],loss #= [0,F.mse_loss(predictions, 0,targets)
1,print(f"Mean 0,Squared 0],Error #Loss: [0, 1, 0, 0, 0],
#         [0, 0, 0, 1, 0]]{loss.item()}")

部分:张量变形初始化数学运算 📐操作

3.1 torch.nn.init.*: 权重初始化

高效原理: torch.nn.init 模块提供了多种权重初始化策略,以帮助模型更好地收敛。kaiming_uniform_ 是其中一种,它根据 He 初始化策略从均匀分布中采样,适用于配合 ReLU 激活函数。函数名末尾数据下划线 _ 表示这是一个原地(in-place)操作是 PyTorch 的一大优势

示例:

import torch.nn.init as init
import math

weight = torch.empty(3, 5)
init.kaiming_uniform_(weight, a=math.sqrt(5))
print("Kaiming Initialized Weight (sample):\n", weight)

1.3.2 torch.Tensor.view(*shape)view:高效的形状: 张量视图重塑

原理: .view() 方法可以在不改变底层数据存储的情况下,高效地改变张量的形状,只要新旧形状的元素总数相同即可必须一致它非常高效,因为它返回的是一个共享原始数据内存的新张量视图”,而不是一个数据副本。-1 可以用作占位符,自动推断该维度的大小

用法示例:

x = torch.randn(4, 6)arange(12) # 原始形状tensor([ #0,  重塑为1, 8x3..., 11])
y = x.view(8,3, 3)

# 使用 -1 自动推断
z = x.view(2, 2, -1) # z 的形状将是 (2, 2, 6)4)
print(f"Reshaped tensor:\n{y}"Original shape:", x.shape)
print("View (8, 3) shape:", y.shape)
print("View (2, 2, -1) shape:", z.shape))

2.3.3 torch.topk(input,topk: k,查找 dim=None):寻找最重要的K个Top-K 元素

原理: torch.topk 函数用于查找张量在指定维度上查找输入张量中最大(或最小)的 k元素及其对应的索引。

用法示例:

x = torch.tensor([[1, 5,9, 2], [9,8, 3, 6]5]])

# 寻找每行中最大的 2 个元素
values, indices = torch.topk(x, k=2, dim=1)
print("f"Top-2 Values:\n", values){values}")   # tensor([[5,9, 2], [9,8, 6]5]])
print("f"Top-2 Indices:\n", indices){indices}") # tensor([[1, 2], [0, 2]])

3.4 sum()torch.Tensor.scatter_add_,: mean(), div_():基础数学高级索引聚合

原理: scatter_add_ 是一个原地操作,它将一个源张量 src 中的值,根据 index 张量指定的位置,累加到目标张量 self 中。这对于实现复杂的稀疏更新或自定义聚合操作非常有用。

示例:

# 目标: 计算每个类别的得分总和
num_classes = 3
scores = torch.tensor([0.9, 0.1, 0.8, 0.5, 0.7])
class_indices = torch.tensor([0, 1, 0, 2, 1])

# 初始化类别总分张量
total_scores = torch.zeros(num_classes)

# 聚合分数
total_scores.scatter_add_(dim=0, index=class_indices, src=scores)
print(f"Total scores per class: {total_scores}") # tensor([1.7000, 0.8000, 0.5000])

第四部分:高级与专用网络层 (torch.nn)

本部分将介绍更专门化的 nn.Module,它们是构建现代深度学习架构(如 CNNs、RNNs 和 Transformers)的关键组件。

4.1 nn.BatchNorm2d: 二维批量归一化

原理: 批量归一化(Batch Normalization)是一种用于稳定和加速深度神经网络训练的技术。nn.BatchNorm2d 专门用于四维输入(典型的如图像数据 [N, C, H, W])。在训练时,它对一个 mini-batch 内的数据,沿着通道(Channel)维度进行归一化,使其均值为0,方差为1。此外,它还包含两个可学习的仿射变换参数($\gamma$ 和 $\beta$),用于恢复网络的表达能力。在评估模式下,它会使用在训练过程中累积的全局均值和方差进行归一化。

示例:

# 4个样本, 3个通道, 32x32的图像
input_tensor = torch.randn(4, 3, 32, 32)
# 归一化的特征数量必须与输入通道数匹配
batch_norm_layer = nn.BatchNorm2d(num_features=3)

# 在训练模式下
batch_norm_layer.train()
output_train = batch_norm_layer(input_tensor)
# output_train 的每个通道在 batch 维度上均值接近0,方差接近1

# 在评估模式下
batch_norm_layer.eval()
output_eval = batch_norm_layer(input_tensor)
print(f"Output shape remains the same: {output_eval.shape}")

4.2 nn.LayerNorm: 层归一化

原理: 层归一化(Layer Normalization)是另一种归一化技术。与批量归一化不同,它在单个样本内部对特征进行归一化,因此其计算完全独立于 batch 中的其他样本。这使得它非常适用于序列数据(如 NLP 任务中的 RNN 和 Transformer),其中序列长度可变,使用批量归一化会很棘手。它对指定 normalized_shape 的维度进行归一化。

示例:

# 2个样本, 序列长度10, 特征维度20
input_seq = torch.randn(2, 10, 20)
# 对最后一个维度(特征维度)进行归一化
layer_norm = nn.LayerNorm(normalized_shape=20)
output_norm = layer_norm(input_seq)

# output_norm 中每个样本的每个时间步的特征向量均值为0,方差为1
print(f"LayerNorm output shape: {output_norm.shape}") # torch.Size([2, 10, 20])

4.3 nn.Embedding: 嵌入层

原理: nn.Embedding 本质上是一个大型的查找表(lookup table)。它存储了固定大小词汇表或类别库的密集向量(embeddings)。当输入一个由类别索引组成的列表时,它会返回这些索引对应的嵌入向量。这在自然语言处理中用于将单词ID映射为词向量,或在推荐系统中将用户/物品ID映射为特征向量。其内部维护一个大小为 (num_embeddings, embedding_dim) 的可学习权重矩阵。

示例:

# 假设词汇表大小为100, 每个词用一个32维向量表示
embedding_layer = nn.Embedding(num_embeddings=100, embedding_dim=32)

# 输入是一个由单词索引组成的张量 (2个句子, 每个句子4个词)
input_indices = torch.LongTensor([[1, 2, 4, 5], [4, 3, 2, 9]])
embedded_vectors = embedding_layer(input_indices)

print(f"Embedded vectors shape: {embedded_vectors.shape}") # torch.Size([2, 4, 32])

4.4 nn.LSTM: 长短期记忆网络层

原理: LSTM (Long Short-Term Memory) 是一种特殊的循环神经网络(RNN),旨在解决传统 RNN 中的长期依赖和梯度消失问题。它通过引入一个单元状态(cell state)和三个门(遗忘门、输入门、输出门)来精细地控制信息的流动。这使得模型能够选择性地记忆、更新和输出信息,从而在长序列上表现更佳。

示例:

# 输入特征维度10, 隐藏状态维度20, 堆叠2层
lstm_layer = nn.LSTM(input_size=10, hidden_size=20, num_layers=2)

# 输入序列: 长度为5, batch大小为3, 特征维度10
input_seq = torch.randn(5, 3, 10)
# 初始隐藏状态和单元状态
h0 = torch.randn(2, 3, 20) # (num_layers, batch, hidden_size)
c0 = torch.randn(2, 3, 20)

# 前向传播
output, (hn, cn) = lstm_layer(input_seq, (h0, c0))

print(f"Output sequence shape: {output.shape}") # (seq_len, batch, hidden_size) -> (5, 3, 20)
print(f"Final hidden state shape: {hn.shape}") # (num_layers, batch, hidden_size) -> (2, 3, 20)

第五部分:高级张量操作与数据处理

本部分关注于对张量进行结构性变换和条件化处理的函数,这些是数据预处理和模型内部数据流控制的基础。

5.1 torch.cat: 张量拼接

原理: torch.cat 用于将一个张量序列沿着一个已存在的维度进行拼接。所有待拼接的张量在非拼接维度最常必须具有相同的尺寸。

示例:

x = torch.randn(2, 3)
y = torch.randn(4, 3)
z = torch.randn(2, 2)

# 沿维度0 (行) 拼接, 要求列数相同
cat_dim0 = torch.cat([x, y], dim=0)
print(f"Concat on dim 0 shape: {cat_dim0.shape}") # torch.Size([6, 3])

# 沿维度1 (列) 拼接, 要求行数相同
cat_dim1 = torch.cat([x, z], dim=1)
print(f"Concat on dim 1 shape: {cat_dim1.shape}") # torch.Size([2, 5])

5.2 torch.stack: 张量堆叠

原理: torch.stack 于将一个张量序列沿着一个新数学运算:维度进行堆叠。所有待堆叠的张量必须具有完全相同的尺寸。这相当于在拼接前为每个张量增加一个新维度。

示例:

a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])

# 沿新的维度0进行堆叠
stacked_tensor = torch.stack([a, b], dim=0)
print(f"Stacked tensor:\n{stacked_tensor}")
print(f"Stacked tensor shape: {stacked_tensor.shape}") # torch.Size([2, 3])

5.3 torch.squeeze & torch.unsqueeze: 维度压缩与扩展

原理:

  • .sum()torch.unsqueeze(input, dim): 求和在指定维度 dim 上为输入张量增加一个大小为1的新维度
  • .mean()torch.squeeze(input, dim=None): 求平均值。
  • .div_(): In-place 法。带下划线的方法会直接修改输入张量本身中所有大小为1的维度。如果指定了 dim,则只移除该维度(如果其大小为1)

这两个操作对于调整张量维度以满足特定层的输入要求(如卷积层或批处理)至关重要。

用法示例:

x = torch.randn(3, 4) # Shape: [3, 4]
# 在维度0增加一个批处理维度
y = torch.unsqueeze(x, 0)
print(f"Unsqueeze shape: {y.shape}") # Shape: [1, 3, 4]

# 移除所有大小为1的维度
z = torch.squeeze(y)
print(f"Squeeze shape: {z.shape}") # Shape: [3, 4]

5.4 torch.where: 条件化选择

原理: torch.where(condition, x, y) 是一个元素级的条件判断函数。它返回一个新的张量,其元素根据 condition 张量(一个布尔张量)来选择:如果 condition 中对应位置的元素为 True,则从 x 中取值;否则从 y 中取值。

示例:

x = torch.tensor([[1.-2.0, 2.0],0.0, [3.0, 4.-1.0]])
# 沿行求和将所有负数替换为0 (dim=1),并保持维度类似于 row_sumReLU 的一种实现)
output = x.sum(dim=1,torch.where(x keepdim=True)> 0, x, torch.tensor(0.))
print(f"torch.where output: {output}"Row Sum (keepdim=True):\n", row_sum)) # 计算所有元素的平均值
total_mean = x.mean()
print("Total Mean:"tensor([0., total_mean)

# 对 x 进行 in-place 除以 2
x.div_(2)
print("x after in-place division:\n"0., x)3., 0.])

5.5

第五部分:高级索引:scatter_add_torch.permute: 维度重排

原理: torch.permute 用于根据指定魔力 ✨

这是维度顺序,对张量的维度进行重排。它返回一个功能强大但相对复杂共享底层数据存储新视图。这在需要将数据格式从 in-place(N, 操作,H, W, C) 转换为 (N, C, H, W)(PyTorch 标准)时非常有于实现高级的聚合功能

torch.Tensor.scatter_add_(dim, index, src)

它的工作方式可以理解为“根据 index 将 src 张量中的值加到 self 张量中”。

  • self: 目标张量,它将被修改。
  • dim: 进行操作的维度。
  • index: 索引张量,指示 src 中的每个值应该被添加到 self 的哪个位置。
  • src: 源张量,其值将被“散布”出去。

一个直观的: 假设你想统计一个班级中,来自不同城市(用 ID 表示)的学生人数。

# 假设有一个典型的 3"channels-last" 个城市格式的图像 batch
x_channels_last = torch.randn(16, 32, 32, 3) # (ID:N, H, W, C)

# 重排为 "channels-first"
x_channels_first = x_channels_last.permute(0, 3, 1, 2),初始化计数器
city_counts = torch.zeros(3, dtype=torch.long) # 8个学生,他们各自的城市ID(N, #C, 学生0来自城市1,学生1来自城市0,学生2来自城市1...H, student_city_ids = torch.tensor([1, 0, 1, 2, 0, 0, 1, 2])

# 我们要为每个学生加 1
ones_to_add = torch.ones(8, dtype=torch.long)

# 使用 scatter_add_ 进行聚合
# 沿着维度 0,根据 student_city_ids,将 ones_to_add 的值加到 city_counts
city_counts.scatter_add_(dim=0, index=student_city_ids, src=ones_to_add)W)
print(f"城市Permuted 0 的学生人数:shape: {city_counts[0]}x_channels_first.shape}") # 3torch.Size([16, print(f"城市3, 132, 的学生人数: {city_counts[1]}"32]) # 3
print(f"城市 2 的学生人数: {city_counts[2]}") # 2

希望这份指南能帮助你更自如地运用 PyTorch。祝你编码愉快!