当前位置: 首页 > article >正文

从零开始:用NumPy手搓一个多层感知机(MLP),并和PyTorch结果对齐

从零构建MLP用NumPy实现与PyTorch对齐的神经网络训练全流程在深度学习框架高度封装的今天许多开发者已经习惯了调用现成的API搭建神经网络。但当你真正用NumPy从零实现一个多层感知机MLP并与PyTorch的结果进行严格对齐时会发现其中蕴含着对神经网络本质的深刻理解。本文将带你完整实现这一过程涵盖以下关键环节数学原理的代码化实现手动推导并编码前向传播、反向传播数值稳定性技巧处理梯度消失/爆炸的实际解决方案框架级功能复现实现PyTorch风格的自动微分机制严格验证体系建立与PyTorch结果的逐层对比测试1. 神经网络核心组件实现1.1 张量运算基础架构我们先构建最基础的张量操作类这是后续所有组件的基础class Tensor: def __init__(self, data, requires_gradFalse): self.data np.array(data, dtypenp.float32) self.grad None self.requires_grad requires_grad self._grad_fn None def __matmul__(self, other): return MatMul()(self, other) def __add__(self, other): return Add()(self, other) def __sub__(self, other): return Sub()(self, other) def backward(self, gradNone): if not self.requires_grad: return if grad is None: grad np.ones_like(self.data) if self._grad_fn is not None: self._grad_fn.backward(grad)1.2 自动微分系统实现实现类似PyTorch的自动微分机制需要构建运算图的节点关系class Function: def __call__(self, *inputs): self.inputs inputs output self.forward(*[x.data for x in inputs]) output Tensor(output) if any(x.requires_grad for x in inputs): output.requires_grad True output._grad_fn self return output def forward(self, *inputs): raise NotImplementedError def backward(self, grad): raise NotImplementedError class MatMul(Function): def forward(self, x, w): return x w def backward(self, grad): x, w self.inputs if x.requires_grad: x.grad grad w.data.T if w.requires_grad: w.grad x.data.T grad2. 完整MLP网络实现2.1 网络层抽象设计我们采用模块化设计使各层可以灵活组合class Module: def parameters(self): return [] def zero_grad(self): for p in self.parameters(): p.grad None def forward(self, x): raise NotImplementedError def __call__(self, x): return self.forward(x) class Linear(Module): def __init__(self, in_features, out_features): self.weight Tensor( np.random.randn(in_features, out_features) * np.sqrt(2./in_features), requires_gradTrue ) self.bias Tensor(np.zeros(out_features), requires_gradTrue) def forward(self, x): return x self.weight self.bias def parameters(self): return [self.weight, self.bias]2.2 激活函数实现实现常用激活函数及其梯度class ReLU(Function): def forward(self, x): self.mask x 0 return x * self.mask def backward(self, grad): return grad * self.mask def relu(x): return ReLU()(x) class Sigmoid(Function): def forward(self, x): self.out 1 / (1 np.exp(-x)) return self.out def backward(self, grad): return grad * self.out * (1 - self.out) def sigmoid(x): return Sigmoid()(x)3. 训练流程与优化器实现3.1 损失函数实现实现两种常用损失函数class MSELoss(Function): def forward(self, pred, target): self.diff pred - target return np.mean(self.diff**2) def backward(self, grad): return 2 * self.diff / np.prod(self.diff.shape) class CrossEntropyLoss(Function): def forward(self, x, target): exp np.exp(x - np.max(x, axis1, keepdimsTrue)) self.probs exp / np.sum(exp, axis1, keepdimsTrue) return -np.mean(np.log(self.probs[np.arange(len(target)), target.argmax(axis1)])) def backward(self, grad): grad_input self.probs.copy() grad_input[np.arange(len(self.inputs[1])), self.inputs[1].argmax(axis1)] - 1 return grad_input / len(self.inputs[1])3.2 优化器实现实现SGD优化器class SGD: def __init__(self, params, lr0.01): self.params params self.lr lr def step(self): for p in self.params: if p.grad is not None: p.data - self.lr * p.grad def zero_grad(self): for p in self.params: p.grad None4. 与PyTorch的严格对齐验证4.1 数值一致性测试方法为确保我们的实现与PyTorch完全一致需要建立严格的测试体系参数初始化对齐使用相同的随机种子初始化权重前向传播验证逐层比较输出值的数值差异梯度计算验证比较反向传播的梯度值训练过程验证比较多个epoch后的参数变化def test_forward_consistency(): # 初始化相同权重的网络 np.random.seed(42) our_net MLP([784, 128, 10]) torch.manual_seed(42) torch_net TorchMLP([784, 128, 10]) # 相同输入数据 x np.random.randn(32, 784) our_out our_net(Tensor(x)) torch_out torch_net(torch.tensor(x, dtypetorch.float32)) # 比较输出差异 max_diff np.max(np.abs(our_out.data - torch_out.detach().numpy())) print(f前向传播最大差异: {max_diff:.6f}) assert max_diff 1e-64.2 梯度计算验证梯度计算是神经网络的核心必须确保完全一致def test_backward_consistency(): # 初始化网络和相同输入数据 np.random.seed(42) our_net MLP([784, 128, 10]) torch_net TorchMLP([784, 128, 10]) x np.random.randn(32, 784) y np.random.randint(0, 10, size32) # 我们的实现 our_out our_net(Tensor(x)) loss CrossEntropyLoss()(our_out, Tensor(np.eye(10)[y])) loss.backward() # PyTorch实现 torch_out torch_net(torch.tensor(x, dtypetorch.float32)) torch_loss F.cross_entropy(torch_out, torch.tensor(y)) torch_loss.backward() # 比较梯度差异 for our_p, torch_p in zip(our_net.parameters(), torch_net.parameters()): max_diff np.max(np.abs(our_p.grad - torch_p.grad.detach().numpy())) print(f参数梯度最大差异: {max_diff:.6f}) assert max_diff 1e-65. 实际训练中的关键技巧5.1 学习率调度策略实现PyTorch风格的学习率调度class StepLR: def __init__(self, optimizer, step_size, gamma0.1): self.optimizer optimizer self.step_size step_size self.gamma gamma self.last_epoch 0 def step(self): self.last_epoch 1 if self.last_epoch % self.step_size 0: self.optimizer.lr * self.gamma class ExponentialLR: def __init__(self, optimizer, gamma): self.optimizer optimizer self.gamma gamma def step(self): self.optimizer.lr * self.gamma5.2 批归一化实现批归一化对训练深度网络至关重要class BatchNorm(Module): def __init__(self, num_features, eps1e-5, momentum0.1): self.gamma Tensor(np.ones(num_features), requires_gradTrue) self.beta Tensor(np.zeros(num_features), requires_gradTrue) self.eps eps self.momentum momentum self.running_mean np.zeros(num_features) self.running_var np.ones(num_features) def forward(self, x): if self.training: mean x.data.mean(axis0) var x.data.var(axis0) self.running_mean (1-self.momentum)*self.running_mean self.momentum*mean self.running_var (1-self.momentum)*self.running_var self.momentum*var x_norm (x.data - mean) / np.sqrt(var self.eps) else: x_norm (x.data - self.running_mean) / np.sqrt(self.running_var self.eps) return x_norm * self.gamma self.beta def parameters(self): return [self.gamma, self.beta]6. 完整训练流程示例下面展示如何在MNIST数据集上训练我们的NumPy MLPdef train_mnist(): # 数据准备 train_data, train_labels load_mnist(train) test_data, test_labels load_mnist(test) # 模型初始化 model Sequential( Linear(784, 256), ReLU(), Linear(256, 64), ReLU(), Linear(64, 10) ) # 优化器和损失函数 optimizer SGD(model.parameters(), lr0.1) scheduler StepLR(optimizer, step_size30, gamma0.1) criterion CrossEntropyLoss() # 训练循环 batch_size 64 for epoch in range(100): model.train() for i in range(0, len(train_data), batch_size): x Tensor(train_data[i:ibatch_size]) y Tensor(np.eye(10)[train_labels[i:ibatch_size]]) optimizer.zero_grad() output model(x) loss criterion(output, y) loss.backward() optimizer.step() scheduler.step() # 验证 model.eval() correct 0 for i in range(0, len(test_data), batch_size): x Tensor(test_data[i:ibatch_size]) pred model(x).data.argmax(axis1) correct (pred test_labels[i:ibatch_size]).sum() print(fEpoch {epoch}: Accuracy {correct/len(test_data):.4f})

相关文章:

从零开始:用NumPy手搓一个多层感知机(MLP),并和PyTorch结果对齐

从零构建MLP:用NumPy实现与PyTorch对齐的神经网络训练全流程 在深度学习框架高度封装的今天,许多开发者已经习惯了调用现成的API搭建神经网络。但当你真正用NumPy从零实现一个多层感知机(MLP),并与PyTorch的结果进行严…...

高光谱成像基础(十二)光谱重建(Spectral Reconstruction)卸

认识Pass层级结构 Pass范围从上到下一共分为5个层级: 模块层级:单个.ll或.bc文件 调用图层级:函数调用的关系。 函数层级:单个函数。 基本块层级:单个代码块。例如C语言中{}括起来的最小代码。 指令层级:单…...

你还在用传统线程池扛高并发?Java 25虚拟线程真实压测对比:错误率下降92.7%,但90%团队正踩这6个配置雷区

第一章:Java 25虚拟线程高并发架构演进全景图Java 25正式将虚拟线程(Virtual Threads)从预览特性转为稳定特性,并深度整合至JDK核心运行时与工具链,标志着JVM并发模型进入“轻量级线程即原语”新纪元。相比传统平台线程…...

银河麒麟系统部署Ollama:从依赖修复到服务自启全攻略

1. 银河麒麟系统部署Ollama的挑战与解决方案 国产操作系统银河麒麟以其安全稳定的特性受到越来越多企业和开发者的青睐。但在实际部署AI工具时,我们经常会遇到一些特有的兼容性问题。最近我在一个政府项目中部署Ollama大模型服务时就踩了个典型的坑——系统自带的li…...

PHP 8.9原生异步I/O终极调优清单(含12项基准测试指标、9类典型负载适配矩阵)

第一章:PHP 8.9原生异步I/O的核心演进与设计哲学PHP 8.9并未实际发布——截至2024年,PHP官方最新稳定版本为PHP 8.3,且PHP核心开发团队已明确表示**不会在PHP 8.x系列中引入原生异步I/O运行时**。该标题所指的“PHP 8.9”属于虚构版本&#x…...

Java 核心四大基石:从 Object 源码到包装类陷阱的全维度复盘技

MySQL 中的 count 三兄弟:效率大比拼! 一、快速结论(先看结论再看分析) 方式 作用 效率 一句话总结 count(*) 统计所有行数 最高 我是专业的!我为统计而生 count(1) 统计所有行数 同样高效 我是 count(*) 的马甲兄弟…...

从DINO Score到LLaVA:拆解SPAA论文如何用“双考官”机制筛选高质量AI修图

从DINO Score到LLaVA:构建AI图像编辑的"双考官"质量评估体系 在AI图像编辑技术快速发展的今天,如何系统评估生成结果的质量已成为产品落地的关键瓶颈。传统方法往往依赖人工审核或单一指标,既难以规模化又无法全面捕捉图像修改的语…...

给嵌入式开发者的698协议实战拆解:从报文抓包到C语言解析(附代码)

给嵌入式开发者的698协议实战拆解:从报文抓包到C语言解析(附代码) 在智能电表与集中器通信领域,698协议正逐渐成为主流标准。不同于传统645协议的简单数据标识,698协议采用面向对象的设计思想,为开发者提供…...

【hudi学习笔记】深入解析Hudi表设计:核心组件与高效索引机制

1. Hudi表设计的核心组件解析 第一次接触Hudi表设计时,我被它精巧的架构深深吸引。作为一个处理大规模数据湖的开源框架,Hudi通过三个核心组件构建了高效的数据管理机制,这就像建造一栋房子需要稳固的地基、承重墙和屋顶一样缺一不可。 时间轴…...

信号发生器与示波器阻抗匹配:为什么测量结果与预期不符?

1. 信号发生器与示波器的阻抗匹配基础 第一次用示波器测量信号发生器输出时,我盯着屏幕上的波形愣住了——明明设置了1V峰峰值,为什么示波器显示的是2V?这个问题困扰了我整整一个周末,直到弄明白阻抗匹配的原理才恍然大悟。 信号发…...

STM32内存优化实战:解决Keil5 L6406E报错与SRAM/FLASH分配策略

1. 认识Keil5 L6406E报错:内存不足的典型症状 第一次在Keil5里看到"Error: L6406E: No space in execution regions"这个红色报错时,我正把STM32F103的程序往STM32G0系列芯片移植。编译器的这个报错就像高速公路上的限高杆——明确告诉你装载的…...

抖音下载神器:5分钟学会批量下载无水印视频的终极指南

抖音下载神器:5分钟学会批量下载无水印视频的终极指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback suppo…...

Cuvil如何让PyTorch推理成本直降42%?揭秘LLM服务中被忽略的编译器级TCO压缩术

第一章:Cuvil编译器在Python AI推理中的应用Cuvil 是一款面向AI推理场景设计的轻量级领域专用编译器(DSL Compiler),专为将Python中基于NumPy/TensorFlow/PyTorch的模型前向逻辑高效编译为目标硬件指令而构建。它不替代完整框架&a…...

NPJ Precis Oncol 重庆大学附属肿瘤医院张久权教授团队:基于纵向MRI的分形分析预测乳腺癌新辅助化疗反应

01文献学习今天分享的文献是由重庆大学附属肿瘤医院张久权教授等团队于12月12日在肿瘤学顶刊《npj Precision Oncology》(中科院1区top,IF8)上发表的研究“Fractal analysis of longitudinal MRI for predicting response to neoadjuvant che…...

终极指南:如何使用Everything Claude Code实现Laravel验证循环的AI自动化

终极指南:如何使用Everything Claude Code实现Laravel验证循环的AI自动化 【免费下载链接】everything-claude-code The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, C…...

Windows任务栏美化终极指南:如何使用TranslucentTB实现透明化效果

Windows任务栏美化终极指南:如何使用TranslucentTB实现透明化效果 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 你是否厌倦…...

谷歌开发入门完整指南,从零开始入门,一分钟就能学会的开发者教程

谷歌开发入门指南涵盖领域极广,因其产品生态庞大,包括安卓(Android)、云平台(Google Cloud)、API服务、机器学习(TensorFlow)、Web技术等。本文将遵循与《鸿蒙开发者入门指南》类似的…...

HagiCode 为什么选择 Hermes 作为综合 Agent 核心一

1. 哑铃图是什么? 哑铃图(Dumbbell Plot),有时也称为DNA图或杠铃图,是一种用于比较两个相关数据点的可视化图表。 它源于人们对更有效数据比较方式的持续探索。 在传统的时间序列比较中,我们通常使用两条折…...

Java 响应式编程最佳实践:构建高效的异步应用

Java 响应式编程最佳实践:构建高效的异步应用别叫我大神,叫我 Alex 就好。一、引言 大家好,我是 Alex。响应式编程作为一种编程范式,已经在 Java 生态系统中变得越来越重要。随着 Spring WebFlux、Project Reactor 和 RxJava 等框…...

从原理到选型:WDM波分复用技术全解析与应用指南

1. WDM波分复用技术基础入门 第一次接触WDM这个概念是在2013年参加某运营商骨干网改造项目时。当时客户指着机房密密麻麻的光纤问我:"能不能在不更换现有光缆的情况下,把传输容量提升8倍?"这个问题直接把我问住了。后来在华为专家的…...

台达PLC与C#串口通信实时监控:XML配置监控地址,控件自动生成读写同步操作

台达plc与C#串口通信程序,可同步读写操作,实时监控。 监控地址通过xml文件配置,控件自动生成。折腾工控通信的老铁们肯定都遇到过这问题——PLC和上位机要实时互传数据还得保证不卡顿。今天咱们就用C#和台达PLC的串口通信来盘一盘这个需求&am…...

基于WRF-DNN融合的气象数据降尺度技术实践与优化

1. 气象数据降尺度为什么需要WRF和DNN联手? 每次打开天气预报App查看未来几天的温度变化时,你可能不会想到背后需要处理TB级的气象数据。专业领域使用的WRF模型就像个严谨的老教授,擅长用物理公式推演天气变化,但有个致命弱点——…...

崖山数据库的体系架构

一、 数据库与数据库实例YashanDB数据库,英文名称叫做Yashan Database。这里所说的数据库是一个物理上的概念,即指物理操作系统的文件或者磁盘的集合。换句话来说,YashanDB数据库是由物理硬盘上许多的文件组成。这些文件包含了数据文件、控制…...

中文Python游戏开发避坑指南:植物大战僵尸开发中的5个常见问题及解决方案

Python游戏开发实战:植物大战僵尸复刻中的5个关键技术挑战 在游戏开发领域,Python凭借其简洁语法和丰富的库支持,成为许多独立开发者的首选语言。植物大战僵尸作为一款经典的塔防游戏,其核心玩法看似简单,但在实际开发…...

Mojo模块如何无缝注入PyTorch训练循环:从Cython替代到JIT热重载的完整链路(仅限内测版工具链公开)

第一章:Mojo模块如何无缝注入PyTorch训练循环:从Cython替代到JIT热重载的完整链路(仅限内测版工具链公开)Mojo模块通过原生ABI兼容层与PyTorch C前端深度对齐,无需修改torch.compile()调用栈即可嵌入训练循环。其核心机…...

RFSOC XCZU47DR在5G射频基带开发中的实战应用(含代码示例)

RFSOC XCZU47DR在5G射频基带开发中的实战应用(含代码示例) 在5G通信系统的开发中,射频基带处理一直是工程师面临的核心挑战之一。Xilinx的RFSOC XCZU47DR凭借其独特的架构设计,将高性能RF数据转换器与可编程逻辑完美融合&#xff…...

全球真空电子方向研究单位集锦

摘要:微波电子管是雷达、卫星、导弹、核聚变的核心 "心脏",也是全球少数几个真正的 "卡脖子" 技术之一。全世界能独立研制高端微波管的国家不超过 5 个,顶尖研究机构更是屈指可数。本文承接《微波电真空器件硬核科普》系…...

MetalLB才是给Ingress这个老登做负重前行的那个男人棺

一、核心问题及解决方案(按踩坑频率排序) 问题 1:误删他人持有锁——最基础也最易犯的漏洞 成因:释放锁时未做身份校验,直接执行 DEL 命令删除键。典型场景:服务 A 持有锁后,业务逻辑耗时超过锁…...

IP地址什么?工业场景网络注意事项有哪些?僬

OCP原则 ocp指开闭原则,对扩展开放,对修改关闭。是七大原则中最基本的一个原则。 依赖倒置原则(DIP) 什么是依赖倒置原则 核心是面向接口编程、面向抽象编程, 不是面向具体编程。 依赖倒置原则的目的 降低耦合度&#…...

从模型下载到API服务:手把手教你用MS-Swift+VLLM部署Qwen2.5-VL,打造自己的图像理解服务

从模型下载到API服务:手把手教你用MS-SwiftVLLM部署Qwen2.5-VL,打造自己的图像理解服务 在人工智能技术快速发展的今天,多模态大模型正逐渐成为理解和处理图像、文本等复杂数据的关键工具。Qwen2.5-VL作为一款强大的视觉语言模型,…...