Loading...
正在加载...
请稍候

《GoMLX从入门到精通》第10章:神经网络——层、激活函数与前向传播

QianXun (QianXun) 2026年06月13日 09:23

第10章:神经网络——层、激活函数与前向传播


10.1 线性模型的致命短板

第 8 章的线性回归:y = w*x + b。它学到的是一条直线。

现实世界几乎没有"直线问题"。你让一个线性回归判断一张图片里有没有猫——给定 32×32×3 = 3072 个像素值,输出"猫/非猫"——y = w·x + b 无论怎么训练都搞不定。不是参数不够多,是结构本身处理不了非线性边界

需要神经网络,原因就在这里。


10.2 层的直觉:空间扭曲

一个神经网络层做的事情可以这样理解:

输入的原始数据 → [线性变换] → [非线性扭曲] → 输出

线性变换(矩阵乘法):W·x + b——把数据从一个空间投影到另一个空间。

非线性激活函数:ReLU(W·x + b)——把投影后的空间"弯折"一下。

一层 = 一次投影 + 一次弯折。多层 = 多次投影 + 多次弯折。原本线性不可分的数据,在新的空间里变得线性可分了。

关键证明:如果没有非线性,多层等于单层。两层线性变换的复合仍然是线性变换:W₂(W₁·x + b₁) + b₂ = (W₂·W₁)·x + (W₂·b₁ + b₂)——等效于一层。非线性才是让"深度"有意义的东西。


10.3 GoMLX 的层体系

GoMLX 的 layers 包提供了一组建筑积木:

函数 干什么
Dense layers.Dense(ctx, input, useBias, units) 全连接层
DenseWithBias layers.DenseWithBias(ctx, input, units) 带偏置的全连接层
Dropout layers.DropoutStatic(ctx, input, rate) 训练时随机丢弃神经元
BatchNorm batchnorm.New(ctx, input, axis).Done() 批归一化
LayerNorm layers.LayerNormalization(ctx, input, axis).Done() 层归一化——Transformer 标配

每个层函数的第一个参数都是 ctx *context.Context


10.4 激活函数:非线性的来源

GoMLX 内置的激活函数在 activations 子包里:

import "github.com/gomlx/gomlx/pkg/ml/layers/activations"
激活函数 函数 特点 什么时候用
ReLU activations.Relu(x) max(0, x) 隐藏层默认选择
Sigmoid activations.Sigmoid(x) 压到 (0,1) 二分类输出层
Tanh activations.Tanh(x) 压到 (-1,1) RNN 隐层
GeLU activations.Gelu(x) 光滑版 ReLU Attention 模型
Swish activations.Swish(x) x·Sigmoid(x) EfficientNet

选型建议:隐藏层用 ReLU。输出层根据任务选——二分类用 Sigmoid,多分类用 Softmax(activations.Softmax)。


10.5 搭一个两层 MLP(MNIST 风格)

import (
    "github.com/gomlx/gomlx/pkg/ml/context"
    "github.com/gomlx/gomlx/pkg/core/graph"
    "github.com/gomlx/gomlx/pkg/ml/layers"
    "github.com/gomlx/gomlx/pkg/ml/layers/activations"
)

func MNISTModel(ctx *context.Context, input *graph.Node) *graph.Node {
    flat := graph.Reshape(input, -1, 784)
    hidden := layers.DenseWithBias(ctx.In("hidden"), flat, 128)
    hidden = activations.Relu(hidden)
    output := layers.DenseWithBias(ctx.In("output"), hidden, 10)
    return output
}

MNIST:输入 [batch, 28, 28, 1] → Flatten → [batch, 784] → Dense 128 + ReLU → Dense 10。10 个输出对应 0-9 十个数字类别。


10.6 Dropout:别让神经元偷懒

Dropout 在训练时随机把一部分神经元的输出置零。每步训练随机选不同的神经元休眠——相当于每步在训练一个"子网络"。

为什么有效:防止神经元之间形成"共谋"——比如神经元 A 只负责猫的左耳,B 只负责猫的右耳,两个永远同时激活。一旦测试时耳朵被遮挡,两个同时废。Dropout 强制每个神经元必须独立学习有用特征。

// 静态 dropout(rate 为常量)
hidden = layers.DropoutStatic(ctx.In("dropout"), hidden, 0.5)

Dropout 只在训练时生效。测试时自动关闭,所有权重会按比例缩放以保证输出幅度一致。rate=0.5 意味着每步随机丢掉一半神经元。


10.7 BatchNorm:让训练稳如老狗

BatchNorm 在一个 batch 内对每个特征做标准化:减均值、除标准差,再乘 γ 加 β。

效果:

  1. 让每一层的输入分布稳定——无论前面层的参数怎么变
  2. 可以用更高的学习率——加速收敛 5-10 倍
  3. 自带轻微正则化——有时可以省掉 Dropout
import "github.com/gomlx/gomlx/pkg/ml/layers/batchnorm"

hidden = batchnorm.New(ctx.In("bn"), hidden, -1).Done()
hidden = activations.Relu(hidden)

特征轴 -1:对最后一个维度做标准化。注意 batchnorm.New 返回一个 Builder,必须调 .Done() 才得到 *graph.Node


10.8 收束

你现在有了搭神经网络的积木——层、激活函数、Dropout、BatchNorm。第 11 章教你用更高级的积木:CNN(图像专用层)和 Attention(序列专用层)。


作者:步子哥,资深AI研究员 | 连载中:《GoMLX从入门到精通》
上一章:第9章 训练管线
下一章预告:第11章 CNN与Attention——参数共享与动态加权

讨论回复

加载中...
正在加载回复...

正在加载回复...

推荐
智谱 GLM-5 已上线

我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。

领取 2000万 Tokens 通过邀请链接注册即可获得大礼包,期待和你一起在 BigModel 上畅享卓越模型能力
登录