08-Paddle 神经网络层入门:Linear、激活函数与 Sequential

概述

前面几篇文章已经讲过 Tensor、数学操作、自动微分和动态图。现在开始进入 paddle.nn:也就是用神经网络层来搭建模型。

如果只用 Tensor 和数学 API,也可以写出一个线性计算:

python 复制代码
y = paddle.matmul(x, w) + b

但真实模型通常不建议一直手写 wb。更常见的写法是:

python 复制代码
linear = paddle.nn.Linear(4, 3)
y = linear(x)

这一层 Linear 会帮你管理权重、偏置、初始化、参数注册和前向调用。后续优化器也能直接通过 model.parameters() 找到这些可训练参数。

本篇文章重点讲三类最基础但最常用的组件:

  • paddle.nn.Linear:全连接层,完成线性变换。
  • 激活函数 :给网络引入非线性,例如 ReLUSigmoidTanh
  • paddle.nn.Sequential:按顺序把多个层串起来。

读完这篇文章,你应该能看懂 Linear -> ReLU -> Linear 这种最基础神经网络结构,并能独立写出一个小型前馈网络。

背景:为什么不能只堆线性层

线性层的数学形式是:

Y=XW+b Y = XW + b Y=XW+b

如果连续堆多个线性层,但中间没有激活函数:

Y=((XW1+b1)W2+b2) Y = ((XW_1 + b_1)W_2 + b_2) Y=((XW1+b1)W2+b2)

它仍然可以合并成一个更大的线性变换:

Y=XW′+b′ Y = XW' + b' Y=XW′+b′

也就是说,多个线性层直接相连,本质上仍然只是线性模型。为了让神经网络拟合更复杂的非线性关系,需要在层与层之间加入激活函数。

典型结构:

text 复制代码
输入 x
    |
Linear
    |
ReLU
    |
Linear
    |
输出 logits

这就是很多神经网络最基本的骨架。

准备代码:统一打印 shape 和参数

本文示例可以在 CPU 上运行。先准备两个工具函数:

python 复制代码
import paddle
import paddle.nn as nn
import paddle.nn.functional as F


def show(name, tensor):
    print(f"\n{name}")
    print("-" * 60)
    print(tensor)
    print("shape:", tensor.shape)
    print("dtype:", tensor.dtype)
    print("stop_gradient:", tensor.stop_gradient)


def show_parameters(layer):
    print("\nparameters")
    print("-" * 60)
    for name, param in layer.named_parameters():
        print(name, param.shape, param.dtype, "stop_gradient=", param.stop_gradient)


paddle.seed(2026)

建议保存为:

text 复制代码
paddle_nn_linear_activation_sequential.py

学习神经网络层时,不要只看输出值,重点看:

text 复制代码
输入 shape -> 每一层输出 shape -> 参数 shape

这三件事一旦清楚,很多模型结构错误会很容易定位。

Linear 层:最基础的可学习变换

paddle.nn.Linear 是全连接层,也叫线性变换层。

官方 API 形式:

python 复制代码
paddle.nn.Linear(
    in_features,
    out_features,
    weight_attr=None,
    bias_attr=None,
    name=None,
)

它计算:

Out=XW+b Out = XW + b Out=XW+b

其中:

  • X 是输入 Tensor。
  • W 是可学习权重。
  • b 是可学习偏置。
  • in_features 是输入最后一维大小。
  • out_features 是输出最后一维大小。

最小示例:

python 复制代码
import paddle
import paddle.nn as nn

linear = nn.Linear(in_features=4, out_features=3)

x = paddle.randn([2, 4], dtype="float32")
y = linear(x)

show("x", x)
show("y", y)
show_parameters(linear)

形状关系:

text 复制代码
x: [2, 4]
Linear(4, 3)
y: [2, 3]

参数形状通常是:

text 复制代码
weight: [4, 3]
bias: [3]

这和公式完全对应:

text 复制代码
[2, 4] @ [4, 3] + [3] -> [2, 3]

Linear 的输入形状:最后一维必须匹配 in_features

官方文档说明,Linear 接受形状为:

text 复制代码
[batch_size, *, in_features]

的多维 Tensor,其中 * 可以是任意额外维度。输出形状为:

text 复制代码
[batch_size, *, out_features]

也就是说,Linear 只变换最后一维。

二维输入:

python 复制代码
linear = nn.Linear(4, 3)
x = paddle.randn([2, 4])
y = linear(x)

show("2D output", y)

形状:

text 复制代码
[2, 4] -> [2, 3]

三维输入:

python 复制代码
linear = nn.Linear(4, 3)
x = paddle.randn([2, 5, 4])
y = linear(x)

show("3D output", y)

形状:

text 复制代码
[2, 5, 4] -> [2, 5, 3]

这里 [2, 5] 会被保留,最后一维 4 被映射成 3

这个规则在 NLP 中非常常见。例如序列表示:

text 复制代码
[batch_size, seq_len, hidden_size]

经过:

python 复制代码
nn.Linear(hidden_size, num_classes)

会变成:

text 复制代码
[batch_size, seq_len, num_classes]

Linear 不关心前面有多少维,只要求输入最后一维等于 in_features

Linear 的权重和偏置:Parameter 与自动注册

Linear 内部包含可学习参数:

  • linear.weight
  • linear.bias

示例:

python 复制代码
linear = nn.Linear(4, 3)

print(linear.weight)
print(linear.bias)

这些参数是 Parameter,默认通常 stop_gradient=False,会参与自动微分和优化器更新。

可以查看参数:

python 复制代码
for name, param in linear.named_parameters():
    print(name, param.shape, param.stop_gradient)

输出类似:

text 复制代码
weight [4, 3] False
bias [3] False

这就是神经网络层比手写 Tensor 更方便的地方:

text 复制代码
层自己持有参数
    |
Layer 自动注册参数
    |
model.parameters() 可以收集参数
    |
optimizer 可以更新参数

如果你把 Linear 放到自定义 nn.Layerself.fc 上,它的参数也会被外层模型管理。

bias_attr:什么时候不需要偏置

默认情况下,Linear 会创建偏置。

python 复制代码
linear = nn.Linear(4, 3)
print(linear.bias is not None)

如果不想使用偏置,可以设置:

python 复制代码
linear_no_bias = nn.Linear(4, 3, bias_attr=False)
print(linear_no_bias.bias)

什么时候可能不需要偏置?

  • 后面紧跟某些归一化层,偏置可能被抵消。
  • 想严格复现某个论文结构。
  • 做简化实验,故意只观察权重影响。

入门阶段一般保留默认偏置即可。

自定义初始化:weight_attr 和 bias_attr

Linearweight_attrbias_attr 可以控制参数属性,例如初始化方式。

示例:把权重初始化为 0.5,偏置初始化为 1.0。

python 复制代码
import paddle
import paddle.nn as nn

weight_attr = paddle.ParamAttr(
    initializer=nn.initializer.Constant(value=0.5)
)
bias_attr = paddle.ParamAttr(
    initializer=nn.initializer.Constant(value=1.0)
)

linear = nn.Linear(
    in_features=2,
    out_features=4,
    weight_attr=weight_attr,
    bias_attr=bias_attr,
)

print(linear.weight)
print(linear.bias)

这样做适合教学和调试,因为参数值可控。真实训练中,通常使用默认初始化或根据模型结构选择合适初始化。

常见初始化包括:

  • Constant
  • Normal
  • Uniform
  • XavierUniform
  • XavierNormal
  • KaimingUniform
  • KaimingNormal

后续讲模型训练和调参时,再深入初始化策略。当前阶段先知道:层参数不是凭空来的,它们有初始化方式,并在训练中被优化器更新。

手写 Linear 对比 nn.Linear

为了彻底理解 Linear,可以手写一次。

python 复制代码
import paddle
import paddle.nn as nn

paddle.seed(2026)

x = paddle.randn([2, 4], dtype="float32")
linear = nn.Linear(4, 3)

y_layer = linear(x)
y_manual = paddle.matmul(x, linear.weight) + linear.bias

show("y_layer", y_layer)
show("y_manual", y_manual)
show("difference", y_layer - y_manual)

如果没有额外特殊设置,y_layery_manual 应该非常接近。

这说明:

text 复制代码
nn.Linear 不是魔法
它主要封装了参数管理、初始化和前向计算

理解这一点后,后续学习更复杂的层,比如 Conv2DEmbeddingBatchNorm,也会更容易。

激活函数:让网络具备非线性表达能力

激活函数通常放在线性层之后:

text 复制代码
Linear -> Activation -> Linear

如果没有激活函数,多层线性变换可以合并成一层线性变换。激活函数的作用是引入非线性,使网络能拟合更复杂的数据关系。

常见激活函数:

激活函数 Paddle 层 常见特点
ReLU nn.ReLU() 简单高效,深度学习中最常用
Sigmoid nn.Sigmoid() 输出范围 (0, 1)
Tanh nn.Tanh() 输出范围 (-1, 1)
LeakyReLU nn.LeakyReLU() 负半轴保留小斜率
GELU nn.GELU() Transformer 等模型中常见
Softmax nn.Softmax() 把分数转成概率分布

激活函数一般不会改变 Tensor 形状,只改变每个元素的数值。

ReLU:最常用的激活函数

ReLU 的公式是:

ReLU(x)=max(0,x) ReLU(x) = max(0, x) ReLU(x)=max(0,x)

Paddle 示例:

python 复制代码
import paddle
import paddle.nn as nn

x = paddle.to_tensor([-2.0, -0.5, 0.0, 1.0, 3.0])
relu = nn.ReLU()
y = relu(x)

show("x", x)
show("relu(x)", y)

输出含义:

text 复制代码
负数变成 0
0 保持 0
正数保持原值

ReLU 的优点:

  • 计算简单。
  • 收敛通常比较快。
  • 对正数区域梯度稳定。
  • 在 MLP、CNN 中非常常见。

缺点:

  • 负半轴梯度为 0。
  • 某些神经元可能长期输出 0,出现"死亡 ReLU"问题。

入门阶段优先掌握 ReLU,它是最常见的默认选择之一。

Sigmoid:把数值压到 0 到 1

Sigmoid 输出范围是 (0, 1)

Sigmoid(x)=11+e−x Sigmoid(x) = \frac{1}{1 + e^{-x}} Sigmoid(x)=1+e−x1

示例:

python 复制代码
import paddle
import paddle.nn as nn

x = paddle.to_tensor([-5.0, -1.0, 0.0, 1.0, 5.0])
sigmoid = nn.Sigmoid()
y = sigmoid(x)

show("sigmoid(x)", y)

常见用途:

  • 二分类概率输出。
  • 多标签分类中每个标签独立概率。
  • 门控结构,例如某些 RNN 或注意力机制。

注意:如果使用 Paddle 的某些二分类损失函数,可能要求输入是 logits,而不是已经过 Sigmoid 的概率。训练时要看损失函数要求,不要盲目先 Sigmoid

Tanh:把数值压到 -1 到 1

Tanh 输出范围是 (-1, 1)

python 复制代码
import paddle
import paddle.nn as nn

x = paddle.to_tensor([-5.0, -1.0, 0.0, 1.0, 5.0])
tanh = nn.Tanh()
y = tanh(x)

show("tanh(x)", y)

常见用途:

  • 某些循环神经网络结构。
  • 需要输出带正负号且范围受限的中间表示。
  • 传统神经网络中的隐藏层激活。

和 Sigmoid 类似,Tanh 在输入绝对值很大时容易饱和,梯度变小。因此现代前馈网络中,ReLU 及其变体更常见。

LeakyReLU 和 GELU:常见 ReLU 变体

LeakyReLU 在负半轴保留一个很小的斜率:

python 复制代码
import paddle
import paddle.nn as nn

x = paddle.to_tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
act = nn.LeakyReLU(negative_slope=0.1)
y = act(x)

show("leaky_relu(x)", y)

它可以缓解 ReLU 负半轴完全为 0 的问题。

GELU 在 Transformer、BERT、ERNIE 等模型里很常见:

python 复制代码
import paddle
import paddle.nn as nn

x = paddle.linspace(-3, 3, 7)
gelu = nn.GELU()
y = gelu(x)

show("gelu(x)", y)

入门阶段不需要记住所有公式,先知道它们都是"非线性变换",用于增强模型表达能力。

Softmax:分类概率不是普通激活函数

Softmax 会把一组分数转成概率分布,常用于分类推理。

python 复制代码
import paddle
import paddle.nn as nn

logits = paddle.to_tensor(
    [[0.1, 2.0, 0.3], [3.0, 0.5, 0.2]],
    dtype="float32",
)

softmax = nn.Softmax(axis=1)
prob = softmax(logits)

show("prob", prob)
show("prob sum", paddle.sum(prob, axis=1))

对于 [batch_size, num_classes] 的分类输出,通常在 axis=1 上做 Softmax。

重要提醒:

  • 推理展示概率时可以用 Softmax
  • 训练分类模型时,不一定要手动加 Softmax
  • 交叉熵损失通常更适合直接接收 logits,具体以 API 要求为准。

典型分类训练结构是:

text 复制代码
Linear -> ReLU -> Linear -> logits -> cross_entropy

而不是:

text 复制代码
Linear -> ReLU -> Linear -> Softmax -> cross_entropy

这是初学者非常常见的误区。

Layer 形式和 functional 形式:nn.ReLU 与 F.relu

Paddle 中很多激活函数既有 Layer 形式,也有 functional 形式。

Layer 形式:

python 复制代码
relu = nn.ReLU()
y = relu(x)

Functional 形式:

python 复制代码
y = F.relu(x)

两者常见区别:

写法 特点 适合场景
nn.ReLU() 是一个 Layer 对象,可以放进 Sequential 组网结构清晰
F.relu(x) 是函数调用,直接对 Tensor 计算 forward 中临时使用

示例:

python 复制代码
import paddle
import paddle.nn as nn
import paddle.nn.functional as F

x = paddle.to_tensor([-1.0, 0.0, 2.0])

relu_layer = nn.ReLU()
y1 = relu_layer(x)
y2 = F.relu(x)

show("y1", y1)
show("y2", y2)

一般建议:

  • Sequential 搭模型时,使用 nn.ReLU()
  • 在自定义 forward 中临时写激活,使用 F.relu(x) 也很自然。

Sequential:把层按顺序串起来

当模型就是简单的顺序结构时,可以使用 paddle.nn.Sequential

官方文档说明,Sequential 是顺序容器,子 Layer 会按构造函数参数顺序添加并执行。

示例:

python 复制代码
import paddle
import paddle.nn as nn

model = nn.Sequential(
    nn.Linear(4, 8),
    nn.ReLU(),
    nn.Linear(8, 3),
)

x = paddle.randn([5, 4])
logits = model(x)

show("logits", logits)
show_parameters(model)

执行顺序是:

text 复制代码
x
    |
Linear(4, 8)
    |
ReLU()
    |
Linear(8, 3)
    |
logits

形状变化:

text 复制代码
[5, 4] -> [5, 8] -> [5, 8] -> [5, 3]

Sequential 适合这类"从前到后一路执行"的模型。

Sequential 的命名写法

如果希望给每一层取名字,可以传入 (name, layer) 形式。

python 复制代码
import paddle.nn as nn

model = nn.Sequential(
    ("fc1", nn.Linear(4, 8)),
    ("relu1", nn.ReLU()),
    ("fc2", nn.Linear(8, 3)),
)

print(model)
print(model["fc1"])

这样做的好处:

  • 输出结构更清楚。
  • 可以通过名字访问子层。
  • 调试和保存结构时更容易阅读。

也可以通过索引访问:

python 复制代码
print(model[0])
print(model[1])

对简单模型来说,普通顺序写法已经足够;对稍复杂模型,命名写法更清晰。

Sequential 的适用边界

Sequential 很方便,但不是所有模型都适合它。

适合 Sequential

text 复制代码
x -> layer1 -> layer2 -> layer3 -> output

不适合单纯使用 Sequential

  • 有多个输入。
  • 有多个输出。
  • 有跳连或残差连接。
  • 有复杂条件分支。
  • 中间结果要复用。
  • 前向过程中需要动态循环。

例如残差结构:

text 复制代码
y = layer(x) + x

这种结构通常更适合自定义 nn.Layer

python 复制代码
class ResidualBlock(nn.Layer):
    def __init__(self, dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim, dim),
            nn.ReLU(),
            nn.Linear(dim, dim),
        )

    def forward(self, x):
        return self.net(x) + x

顺序结构用 Sequential,有分支、复用、跳连时用自定义 Layer

用 Layer 包装 Sequential:兼顾简洁和扩展性

真实项目中,经常把 Sequential 放进自定义 Layer 里。

python 复制代码
import paddle
import paddle.nn as nn


class MLP(nn.Layer):
    def __init__(self, input_dim, hidden_dim, num_classes):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, num_classes),
        )

    def forward(self, x):
        return self.net(x)


model = MLP(input_dim=4, hidden_dim=8, num_classes=3)
x = paddle.randn([5, 4])
logits = model(x)

show("logits", logits)
show_parameters(model)

这种写法的好处:

  • 内部结构简单,用 Sequential 表达。
  • 外部仍然是一个标准 nn.Layer
  • 后续可以方便地添加额外逻辑。
  • 参数能被 model.parameters() 正常收集。

当模型从简单顺序结构逐步变复杂时,这种写法很自然。

完整示例:用 Linear、ReLU、Sequential 训练一个小分类器

下面写一个完整可运行的小例子:随机生成二维点,根据 x1 + x2 > 0 判断类别。

python 复制代码
import paddle
import paddle.nn as nn
import paddle.nn.functional as F

paddle.seed(2026)

x = paddle.randn([128, 2], dtype="float32")
labels = (x[:, 0] + x[:, 1] > 0).astype("int64")

model = nn.Sequential(
    nn.Linear(2, 8),
    nn.ReLU(),
    nn.Linear(8, 2),
)

optimizer = paddle.optimizer.SGD(
    learning_rate=0.1,
    parameters=model.parameters(),
)

for step in range(80):
    logits = model(x)
    loss = F.cross_entropy(logits, labels)

    loss.backward()
    optimizer.step()
    optimizer.clear_grad()

    if step % 20 == 0:
        pred = paddle.argmax(logits, axis=1)
        acc = paddle.mean((pred == labels).astype("float32"))
        print(
            "step:", step,
            "loss:", float(loss.numpy()),
            "acc:", float(acc.numpy()),
        )

这个例子串起了本篇文章的核心:

text 复制代码
Linear(2, 8)
    |
ReLU
    |
Linear(8, 2)
    |
logits
    |
cross_entropy
    |
backward + optimizer

注意最后一层没有加 Softmax,因为训练时直接把 logits 交给 F.cross_entropy

如果要在训练后展示概率,可以这样写:

python 复制代码
model.eval()

with paddle.no_grad():
    logits = model(x[:5])
    prob = F.softmax(logits, axis=1)
    pred = paddle.argmax(prob, axis=1)

print(prob)
print(pred)

完整示例:对比有无激活函数

为了理解激活函数的重要性,可以对比两个模型。

没有激活函数:

python 复制代码
model_linear_only = nn.Sequential(
    nn.Linear(2, 8),
    nn.Linear(8, 2),
)

有激活函数:

python 复制代码
model_with_relu = nn.Sequential(
    nn.Linear(2, 8),
    nn.ReLU(),
    nn.Linear(8, 2),
)

第一个模型本质上仍然是线性变换的组合,表达能力有限;第二个模型引入了非线性,能拟合更复杂的边界。

如果数据本身线性可分,两者都可能表现不错;如果数据边界非线性,激活函数的价值会更明显。

构造一个异或数据例子:

python 复制代码
import paddle
import paddle.nn as nn
import paddle.nn.functional as F

x = paddle.to_tensor(
    [[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]],
    dtype="float32",
)
labels = paddle.to_tensor([0, 1, 1, 0], dtype="int64")

model = nn.Sequential(
    nn.Linear(2, 8),
    nn.Tanh(),
    nn.Linear(8, 2),
)

optimizer = paddle.optimizer.Adam(
    learning_rate=0.05,
    parameters=model.parameters(),
)

for step in range(300):
    logits = model(x)
    loss = F.cross_entropy(logits, labels)

    loss.backward()
    optimizer.step()
    optimizer.clear_grad()

pred = paddle.argmax(model(x), axis=1)
print("pred:", pred)

异或任务不是单层线性模型能解决的问题,中间激活函数是关键。

参数量计算:Linear 到底有多少参数

Linear(in_features, out_features) 的参数量是:

text 复制代码
weight: in_features * out_features
bias: out_features
total: in_features * out_features + out_features

例如:

python 复制代码
linear = nn.Linear(4, 8)

参数量:

text 复制代码
weight: 4 * 8 = 32
bias: 8
total: 40

可以用代码验证:

python 复制代码
linear = nn.Linear(4, 8)

total = 0
for name, param in linear.named_parameters():
    num = param.numel()
    total += num
    print(name, param.shape, num)

print("total:", total)

理解参数量很重要:

  • 参数越多,模型表达能力可能更强。
  • 参数越多,过拟合风险也可能更高。
  • 参数越多,显存和计算量通常越大。
  • 隐藏层宽度会直接影响参数量。

常见错误

错误一:Linear 的 in_features 和输入最后一维不匹配

错误示例:

python 复制代码
linear = nn.Linear(4, 3)
x = paddle.randn([2, 5])

y = linear(x)

Linear(4, 3) 要求输入最后一维是 4,但 x 的最后一维是 5。

排查方式:

python 复制代码
print(x.shape)
print(linear.weight.shape)

正确示例:

python 复制代码
linear = nn.Linear(5, 3)

错误二:误以为 Linear 只接受二维输入

Linear 可以接受多维输入,只要最后一维匹配。

python 复制代码
linear = nn.Linear(4, 3)
x = paddle.randn([2, 6, 4])
y = linear(x)

print(y.shape)

输出:

text 复制代码
[2, 6, 3]

错误三:分类训练最后手动加 Softmax

常见错误:

python 复制代码
model = nn.Sequential(
    nn.Linear(4, 8),
    nn.ReLU(),
    nn.Linear(8, 3),
    nn.Softmax(axis=1),
)

loss = F.cross_entropy(model(x), labels)

很多交叉熵实现期望输入是 logits,而不是 softmax 概率。更稳的训练结构是:

python 复制代码
model = nn.Sequential(
    nn.Linear(4, 8),
    nn.ReLU(),
    nn.Linear(8, 3),
)

logits = model(x)
loss = F.cross_entropy(logits, labels)

推理展示概率时再用:

python 复制代码
prob = F.softmax(logits, axis=1)

错误四:把 functional 激活函数放进 Sequential

错误倾向:

python 复制代码
model = nn.Sequential(
    nn.Linear(4, 8),
    F.relu,
    nn.Linear(8, 3),
)

Sequential 里应该放 Layer 对象。使用:

python 复制代码
model = nn.Sequential(
    nn.Linear(4, 8),
    nn.ReLU(),
    nn.Linear(8, 3),
)

如果想用 F.relu,放在自定义 forward 里:

python 复制代码
def forward(self, x):
    x = self.fc1(x)
    x = F.relu(x)
    x = self.fc2(x)
    return x

错误五:Sequential 不适合复杂分支结构

如果模型需要:

text 复制代码
y = branch1(x) + branch2(x)

不要硬塞进单一路径 Sequential。应该自定义 Layer

python 复制代码
class TwoBranch(nn.Layer):
    def __init__(self):
        super().__init__()
        self.branch1 = nn.Linear(4, 4)
        self.branch2 = nn.Linear(4, 4)

    def forward(self, x):
        return self.branch1(x) + self.branch2(x)

错误六:忘记查看参数是否被注册

如果训练时 loss 不下降,先检查参数是否存在:

python 复制代码
for name, param in model.named_parameters():
    print(name, param.shape, param.stop_gradient)

如果参数列表为空,通常说明子层没有挂到 self 上,或者模型结构写法有问题。

调试方法:神经网络层先看四类 shape

LinearSequential 时,优先检查:

  1. 输入 Tensor 的 shape。
  2. 每个 Linearweight.shape
  3. 每层输出的 shape。
  4. label 的 shape 和 dtype。

调试代码:

python 复制代码
def debug_model(model, x):
    print("input:", x.shape)
    out = x
    for idx, layer in enumerate(model):
        out = layer(out)
        print(idx, layer.__class__.__name__, out.shape)
    return out


model = nn.Sequential(
    nn.Linear(4, 8),
    nn.ReLU(),
    nn.Linear(8, 3),
)

x = paddle.randn([2, 4])
debug_model(model, x)

输出会清楚显示每一层如何改变 shape。

对于自定义 Layer,可以在 forward 中临时打印:

python 复制代码
print("after fc1:", x.shape)

等模型稳定后再删除。

总结

这一篇围绕 Paddle 神经网络层讲了几个核心点:

  • nn.Linear(in_features, out_features) 完成 Out = XW + b
  • Linear 要求输入最后一维等于 in_features
  • Linear 输出最后一维变成 out_features,前面维度保持不变。
  • weightbias 是可学习参数,会被 Layer 自动管理。
  • 激活函数为网络引入非线性,常见选择包括 ReLUSigmoidTanhLeakyReLUGELU
  • 分类训练通常输出 logits,推理展示概率时再使用 Softmax
  • nn.ReLU() 是 Layer 形式,F.relu(x) 是函数形式。
  • nn.Sequential 适合简单顺序结构。
  • 有分支、跳连、多输入、多输出时,应该自定义 nn.Layer

如果只能记住一句话,那就是:

Linear 负责可学习的线性变换,激活函数负责引入非线性,Sequential 负责把简单顺序结构清晰地串起来。

相关推荐
谁看我谁 狼家二丫2 小时前
机器学习漫游(1) 基本设定
人工智能·机器学习
workflower3 小时前
从幻觉,到现实
人工智能·深度学习·机器学习·设计模式·机器人
裁冰剪雪3 小时前
稀疏化(Sparsification)技术介绍
神经网络·芯片
有Li3 小时前
使用整合电子健康记录的大语言模型智能体实现前列腺癌患者教育个性化文献速递/医学智能体前沿
人工智能·python·机器学习·语言模型·医学生
-拟墨画扇-4 小时前
神经网络 | ⑬ 超参数验证:找到最佳学习超参数
深度学习·神经网络·学习
小小仙子17 小时前
矢量网络分析仪如何测试S参数的?
人工智能·算法·机器学习
金斗潼关20 小时前
使用MLP神经网络模型预测质数
人工智能·深度学习·神经网络