说明¶
说明
tiny_layer 定义了所有神经网络层的抽象基类 Layer,并提供三个无参数的工具层:ActivationLayer、Flatten、GlobalAvgPool。这些工具层不需要可学习权重,但保持与 Layer 接口一致,便于在 Sequential 中直接堆叠。
Layer — 所有神经网络层的基石
tiny::Layer 是抽象基类。理解它,你就理解了整个网络是如何连接的。
算法直觉¶
前向传播 = 数据流过网络¶
forward() 接收输入张量,经过层内的计算,输出下一个层需要的张量。
想象生产线:传感器读数 → 归一化 → 特征提取 → 分类器 → 输出结果。每个站就是一层。
反向传播(训练时)¶
backward() 接收损失对输出的梯度,计算损失对输入的梯度(传到上一层)和对参数的梯度(更新权重)。
梯度沿着网络反向流动
最后一层从损失函数拿到梯度,逐层往前传。每层先算自己对参数的梯度(更新自己),再传梯度给前一层。
内置层组件¶
除了自定义层外,库提供三个无需训练参数的实用层:
| 层 | 功能 | 形状变化 |
|---|---|---|
| ActivationLayer | 包装激活函数 | 形状不变 |
| Flatten | 将多维展平成 1D | [B,C,L]→[B, C*L] |
| GlobalAvgPool | 每个通道取平均 | [B,C,L]→[B,C] |
Layer 抽象基类¶
class Layer
{
public:
const char *name; // 用于 summary() 打印
bool trainable; // 是否包含可学习参数
explicit Layer(const char *name = "layer", bool trainable = false)
: name(name), trainable(trainable) {}
virtual ~Layer() {}
virtual Tensor forward(const Tensor &x) = 0;
#if TINY_AI_TRAINING_ENABLED
virtual Tensor backward(const Tensor &grad_out) = 0;
virtual void collect_params(std::vector<ParamGroup> &groups) {}
#endif
};
约定:
forward(x):必须返回新张量。backward(grad_out):必须返回dL/dx,并更新本层的dweight、dbias等成员(梯度累加)。collect_params():仅可训练层需要重写,把(param, grad)压入groups。trainable:用于Sequential::collect_params()提前剪掉不带参数的层(如Flatten)。
ActivationLayer¶
把无状态激活函数包成 Layer,可以直接在 Sequential 里堆。
class ActivationLayer : public Layer
{
public:
explicit ActivationLayer(ActType type, float alpha = 0.01f);
Tensor forward (const Tensor &x) override;
Tensor backward(const Tensor &grad_out) override;
};
实现细节:
- 缓存策略:
forward()内部根据激活类型决定缓存输入还是输出- Sigmoid / Tanh / Softmax:缓存输出
y,反向时直接用。 - ReLU / LeakyReLU / GELU:缓存输入
x。
- Sigmoid / Tanh / Softmax:缓存输出
- alpha 默认 0.01:仅对 LeakyReLU 生效。
Flatten¶
把 [batch, ...] 形状压成 [batch, flat]:
class Flatten : public Layer
{
Tensor forward (const Tensor &x) override; // [B, ...] → [B, B/size]
Tensor backward(const Tensor &grad_out) override;
};
- 计算
flat = size / batch,调用reshape_2d。 - 反向时把梯度还原回原始
(in_ndim, in_shape)。 Flatten不保留权重,trainable = false,因此不参与collect_params。
CNN1D 的标准用法:
tiny_cnn.cpp 在构造时就把 Flatten 接在卷积块之后。
GlobalAvgPool¶
跨序列维度求均值,常用于 Transformer 风格输出(把 [B, S, F] 聚合成 [B, F]):
class GlobalAvgPool : public Layer
{
Tensor forward (const Tensor &x) override; // [B, S, F] → [B, F]
Tensor backward(const Tensor &grad_out) override;
};
- 前向:对
s维求平均。 - 反向:把
grad_out平均分配到S个序列位置。
example_attention.cpp 中的串接:
与 Sequential 的关系¶
Sequential m;
m.add(new Dense(F, H));
m.add(new ActivationLayer(ActType::RELU));
m.add(new Dense(H, C));
m.add(new ActivationLayer(ActType::SOFTMAX));
Sequential拥有所有用add()注册的Layer*,析构时统一delete。forward(x)顺序调用每层的forward;backward(grad_out)倒序调用每层的backward。- 仅
trainable == true的层会被Sequential::collect_params()调用。
自定义层¶
继承 Layer,实现 forward / backward / collect_params 即可。例:
class Scale : public Layer
{
public:
Tensor scale; // [feat]
#if TINY_AI_TRAINING_ENABLED
Tensor dscale;
#endif
Scale(int feat) : Layer("scale", true), scale(feat)
{
scale.fill(1.0f);
#if TINY_AI_TRAINING_ENABLED
dscale = Tensor::zeros_like(scale);
#endif
}
Tensor forward(const Tensor &x) override
{
Tensor out = x.clone();
// ...逐元素乘 scale...
return out;
}
#if TINY_AI_TRAINING_ENABLED
Tensor backward(const Tensor &grad_out) override { /* ... */ return grad_out; }
void collect_params(std::vector<ParamGroup> &g) override
{
g.push_back({&scale, &dscale});
}
#endif
};