page contents

100行Python代码,带你优雅地搭建神经网络

哎我跟你说啊,最近不是老有人问“神经网络到底咋搭起来的”…我就想起我当年刚学的时候,也是被一堆框架名词绕晕,什么 Layer 啊 Module 啊 Optimizer 啊,听起来很高级,结果一看源码:哦…就是一堆矩阵乘法加上链式求导嘛,对吧。

attachments-2026-03-SPI6hTy869aa2e04514d9.png哎我跟你说啊,最近不是老有人问“神经网络到底咋搭起来的”…我就想起我当年刚学的时候,也是被一堆框架名词绕晕,什么 Layer 啊 Module 啊 Optimizer 啊,听起来很高级,结果一看源码:哦…就是一堆矩阵乘法加上链式求导嘛,对吧。

然后我就干了个很“土”的事:把框架先扔一边,用 100行左右的 Python 手搓一个小 MLP(两层全连接),能跑、能训、loss 真能下的那种。你别说,这玩意跟线上排查故障一个味儿:先把链路拉直了,再谈“优雅”。(就跟我以前查那种“日志里看着都正常,结果是隐藏默认配置坑你”的感觉一模一样…)

我先把场景说清楚哈:我们就拿 XOR 当小白鼠。XOR 这玩意你用一层线性回归怎么都学不会,但两层神经网络一下就“开窍”,特别适合验证你写的反向传播是不是写对了。你要是能把 XOR 训到接近 0 的 loss,基本就说明:前向、反向、参数更新、广播维度……都没踩坑。

下面这段代码,我自己写的,尽量压到 100 行上下(含空行注释差不多就那样,你别拿尺子量我哈…),核心点就三块: 1)线性层 forward/backward 2)ReLU、Sigmoid 3)BCE loss(带数值稳定)+ SGD 更新

# 100-ish lines: tiny neural net from scratch (numpy)import numpy as np

def sigmoid(x):
    x = np.clip(x, -5050)
    return 1 / (1 + np.exp(-x))

def relu(x):
    return np.maximum(0, x)

class Linear:
    def __init__(self, in_dim, out_dim):
        # He init-ish for relu
        self.W = np.random.randn(in_dim, out_dim) * np.sqrt(2.0 / in_dim)
        self.b = np.zeros((1, out_dim))
        self.x = None
        self.dW = None
        self.db = None

    def forward(self, x):
        self.x = x
        return x @ self.W + self.b

    def backward(self, grad_out):
        # grad_out: dL/dy
        self.dW = self.x.T @ grad_out
        self.db = grad_out.sum(axis=0, keepdims=True)
        return grad_out @ self.W.T  # dL/dx

class MLP:
    def __init__(self, in_dim, hidden_dim, out_dim):
        self.l1 = Linear(in_dim, hidden_dim)
        self.l2 = Linear(hidden_dim, out_dim)
        self.z1 = None
        self.a1 = None
        self.z2 = None
        self.yhat = None

    def forward(self, x):
        self.z1 = self.l1.forward(x)
        self.a1 = relu(self.z1)
        self.z2 = self.l2.forward(self.a1)
        self.yhat = sigmoid(self.z2)
        return self.yhat

    def backward(self, x, y):
        # Binary cross entropy: L = -[ y log(p) + (1-y) log(1-p) ]
        eps = 1e-8
        p = np.clip(self.yhat, eps, 1 - eps)

        # dL/dz2 for sigmoid + BCE simplifies nicely: (p - y)
        grad_z2 = (p - y) / y.shape[0]

        grad_a1 = self.l2.backward(grad_z2)

        grad_z1 = grad_a1.copy()
        grad_z1[self.z1 <= 0] = 0  # ReLU'

        _ = self.l1.backward(grad_z1)

    def step(self, lr):
        for layer in (self.l1, self.l2):
            layer.W -= lr * layer.dW
            layer.b -= lr * layer.db

def bce_loss(y, p):
    eps = 1e-8
    p = np.clip(p, eps, 1 - eps)
    return -np.mean(y * np.log(p) + (1 - y) * np.log(1 - p))

if __name__ == "__main__":
    np.random.seed(7)

    # XOR dataset
    X = np.array([[0,0],[0,1],[1,0],[1,1]], dtype=np.float32)
    y = np.array([[0],[1],[1],[0]], dtype=np.float32)

    net = MLP(in_dim=2, hidden_dim=8, out_dim=1)
    lr = 0.3

    for epoch in range(15001):
        p = net.forward(X)
        loss = bce_loss(y, p)
        net.backward(X, y)
        net.step(lr)

        if epoch % 500 == 0:
            pred = (p > 0.5).astype(int)
            acc = (pred == y).mean()
            print(f"epoch={epoch:4d} loss={loss:.4f} acc={acc:.2f}")

    print("final prob:", net.forward(X).ravel())

你跑起来大概会看到 loss 一路往下掉,acc 最后基本 1.00。要是你跑出来 loss 卡住不动、或者 acc 永远 0.50,那八成是下面这种坑(我当年也…嗯踩过):

  • 维度广播b 一定要 (1, out_dim),别写成 (out_dim,) 然后你以为没事,结果反向 db 求和的时候就怪怪的。
  • 梯度平均:我在 grad_z2 = (p - y) / batch_size 这里除了一下,不然 lr 你得重新调。
  • 数值稳定log(0) 会直接炸,clip 一下别硬刚。
  • 初始化:你要是全 0 初始化,网络就“集体摆烂”,学不动(参数对称性那事儿…懂的都懂)。

然后你会突然发现,所谓“优雅搭建神经网络”,真不是花里胡哨的 API,而是你把这条链路心里有数: 前向就是堆算子;反向就是把每个算子的局部导数乘回去;更新就是 param -= lr * grad。框架做的事情,本质就是把这套东西封装成积木,还顺便给你做了 autograd、混精、分布式、编译加速这些“工程活”。

你要是想再往“优雅”靠一步(但还不引入 PyTorch 那种重量级),可以干两件事: 1)给每个算子都统一成 forward/backward 接口,再写个 Sequential 去串起来(代码会更像框架) 2)把参数收集做成 parameters(),优化器单独写个 SGD(momentum=0.9),训练循环就会很“顺手”

不过我建议你先别急着抽象,先把上面这段跑通,跑通以后你再回头看任何框架的 nn.Linear / nn.ReLU / loss.backward(),你会有一种……怎么说呢,“哦原来你也就这点事”的通透感。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2026-03-06 09:29
  • 阅读 ( 32 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1875 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2228 文章
  3. Pack 1875 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章