从零手写神经网络框架:普通显卡上的自动求导与显存优化实践

1. 为什么想不开要自己写神经网络

先说结论:我花了大概三周业余时间,从零手写了一个能跑、能训练、能在普通消费级显卡上收敛的小型神经网络框架。不是调库,不是套壳,是从张量结构、自动求导、反向传播到优化器全部自己实现的那种。这篇文章就是把整个过程拆开揉碎讲清楚,包括我为什么这么干、踩了哪些坑、哪些设计决策是拍脑袋定的、哪些是反复权衡后的结果。

你可能会问,现在深度学习框架已经非常成熟了,为什么还要自己造轮子。这个问题我在动手之前也问过自己。原因有三个:第一,我想彻底搞明白反向传播到底在算什么,而不是只知道调loss.backward();第二,我想验证一个假设——一个结构精简、只保留核心功能的神经网络实现,到底需要多少显存和算力,能不能在普通显卡上跑起来;第三,我想做一个完全可控的实验平台,方便后续做一些非标准结构的尝试,比如自定义激活函数、非典型连接方式等。

这个项目适合谁看?如果你已经用过主流框架训练过模型,但对底层机制一知半解,那这篇文章正好对味。如果你是完全零基础,建议先跑通一个手写数字识别的教程再回来,不然有些概念会比较吃力。另外,如果你手头只有一张普通显卡甚至核显,也想折腾一下底层实现,那我的经验应该能帮你省不少时间。

核心关键词先摆出来:自研神经网络、普通显卡训练、自动求导、反向传播、显存优化、混合精度、梯度累积。这些词后面都会反复出现,每一个我都会给出具体的实现思路和参数选择依据。

2. 整体架构设计与技术选型

2.1 为什么选择纯Python加NumPy做原型

一开始我考虑过用C++写底层、Python做接口,但很快放弃了。原因很简单:开发效率太低,调试成本太高。我的目标是验证算法逻辑,不是做生产级框架。所以第一版直接用纯Python加NumPy实现,所有运算都在CPU上跑。这样做的好处是调试极其方便,任何中间变量都能直接打印出来看,梯度算错了立刻就能定位到具体是哪一层的问题。

但纯NumPy有个致命问题:速度太慢。一个三层全连接网络在MNIST上跑一个epoch要十几分钟,完全没法做实验。所以第二版我引入了GPU加速。这里没有用CUDA C,而是用了PyTorch的底层张量库作为计算后端——注意,我只用了它的张量运算和GPU搬运功能,自动求导、网络层、优化器全部是自己写的。这样既保证了速度,又保留了核心逻辑的完全可控。

提示:用现成的张量库做后端不丢人。你要学的是神经网络的原理,不是CUDA编程。把精力花在刀刃上。

2.2 计算图的设计:动态图还是静态图

这是第一个重大设计决策。静态图需要先定义再运行,调试麻烦但优化空间大;动态图边运行边构建,灵活但开销高。我最终选了动态图,理由很实际:我的网络结构会频繁改动,静态图每次改结构都要重新编译,太折腾。而且动态图的调试体验好太多,出错时能直接看到是哪一步运算出了问题。

具体实现上,每个张量对象都带一个requires_grad标志和一个grad属性。前向传播时,每个运算都会记录自己的输入和输出,形成一个隐式的计算图。反向传播时从损失函数开始,沿着计算图反向遍历,逐层计算梯度。这个设计跟主流框架的思路一致,但我的实现精简了很多,没有处理复杂的控制流和动态形状,只支持我实际用到的运算。

2.3 显存优化的核心策略

普通显卡的显存通常只有4GB到8GB,要训练一个像样的网络,显存管理必须精打细算。我用了三个策略:

第一,梯度累积。这是最关键的一招。假设你想用batch size 128训练,但显存只够放32个样本,那就分4次前向传播,每次用32个样本,把梯度累加起来,第4次之后再统一更新参数。数学上等价于batch size 128,但显存占用只有四分之一。我实测下来,梯度累积步数设为4的时候,训练稳定性和直接大batch几乎没有区别。

第二,混合精度训练。前向传播用16位浮点数,反向传播和参数更新用32位。这样显存占用直接减半,速度还能提升20%到30%。但要注意,16位浮点数的动态范围小,容易溢出,所以损失缩放(loss scaling)是必须的。我的做法是初始缩放因子设为1024,每2000步检查一次梯度是否溢出,如果连续多次没溢出就翻倍,溢出了就减半。

第三,及时释放中间变量。前向传播产生的中间激活值在反向传播用完之后立刻释放,不要留着占显存。这个在纯Python里需要手动管理,我写了一个简单的引用计数机制,当某个中间张量的引用数为零时自动回收。

2.4 参数初始化与数值稳定性

参数初始化看着简单,实际上对训练能否收敛影响巨大。我试过全零初始化,结果所有神经元的梯度完全一样,网络根本学不到东西。也试过标准差为1的高斯初始化,结果第一层输出就爆炸了。最后用的是He初始化:权重服从均值为0、标准差为sqrt(2/fan_in)的高斯分布,其中fan_in是输入维度。偏置全部初始化为0。

数值稳定性方面,我在softmax和log损失函数里做了特殊处理。标准的softmax公式是exp(x)/sum(exp(x)),当x很大的时候exp(x)会溢出。我的做法是先减去最大值:exp(x-max(x))/sum(exp(x-max(x)))。这样指数部分永远不超过0,不会溢出。log损失函数也做了类似处理,避免log(0)的情况。

3. 核心模块的详细实现

3.1 张量类的设计与实现

张量是整个框架的基础。我的张量类包含这几个核心属性:data存储实际数值,grad存储梯度,requires_grad标记是否需要计算梯度,_backward是一个函数,定义了如何从输出梯度计算输入梯度,_prev记录了产生这个张量的前驱节点。

前向传播时,每个运算都会创建一个新的张量,并把_backward函数和_prev设置好。反向传播时,从损失张量开始,按照拓扑排序的顺序依次调用每个张量的_backward函数。拓扑排序这一步很关键,必须保证在计算某个张量的梯度之前,所有依赖它的张量的梯度都已经算完了。

这里有个容易踩的坑:如果同一个张量被多次使用,梯度需要累加而不是覆盖。比如y = x * x,x被用了两次,反向传播时x的梯度应该是2x,而不是x。我的实现里,每次反向传播都是grad += new_grad,而不是直接赋值。这个细节如果搞错了,梯度会偏小,训练效果会差很多。

3.2 全连接层的实现细节

全连接层的前向传播就是矩阵乘法加偏置:output = input @ weight + bias。反向传播需要计算三个梯度:对输入的梯度、对权重的梯度、对偏置的梯度。对输入的梯度是grad_output @ weight.T,对权重的梯度是input.T @ grad_output,对偏置的梯度是grad_output在batch维度上求和。

这里有个性能优化的点:矩阵乘法的顺序会影响速度。input.T @ grad_output和grad_output.T @ input数学上等价,但实际运行速度可能差很多,取决于矩阵的形状和内存布局。我实测下来,当batch size较大时,先转置再乘比先乘再转置快大概15%。这个差异在大规模训练时不可忽视。

权重的存储格式也值得一说。我一开始用行优先存储,后来发现列优先在GPU上做矩阵乘法更快,因为GPU的内存访问模式对连续性更敏感。改成列优先之后,单层的前向传播速度提升了大约10%。

3.3 激活函数的选择与实现

我实现了四个激活函数:ReLU、LeakyReLU、Sigmoid、Tanh。ReLU是默认选择,计算简单、梯度不饱和,但有个问题是负半轴梯度为零,可能导致神经元“死亡”。LeakyReLU解决了这个问题,负半轴给一个很小的斜率,我设的是0.01。Sigmoid和Tanh现在用得少了,因为容易梯度消失,但我在某些特定层还是保留了Sigmoid,比如需要输出概率分布的时候。

ReLU的反向传播很简单:输入大于零的地方梯度为1,小于等于零的地方梯度为0。但这里有个细节:如果输入恰好等于零,梯度取0还是取1?我取的是0,因为ReLU在零点的导数不存在,取0更安全。LeakyReLU的反向传播类似,只是负半轴的梯度是斜率值而不是零。

注意:激活函数的反向传播必须缓存前向传播的输入,不能只缓存输出。因为ReLU的梯度依赖于输入的正负,而不是输出的值。这个坑我踩过,当时缓存了输出,结果梯度全算错了。

3.4 损失函数与优化器

损失函数我实现了两个:均方误差和交叉熵。均方误差用于回归任务,交叉熵用于分类任务。交叉熵的实现里,我把softmax和log合并在一起计算,这样数值更稳定,梯度也更简洁。合并后的梯度就是softmax_output - target,非常干净。

优化器我实现了三个:随机梯度下降、带动量的随机梯度下降、Adam。SGD最简单,但收敛慢。动量法通过累积历史梯度来加速收敛,动量系数我设的是0.9。Adam结合了动量和自适应学习率,通常是首选,但要注意它的权重衰减实现和L2正则化不完全等价,我用的是一种修正后的AdamW变体。

学习率调度也很重要。我用了余弦退火:初始学习率设为0.001,每经过一个epoch,学习率按余弦曲线下降到初始值的0.01倍。这样训练前期收敛快,后期稳定。实测下来比固定学习率的效果好不少,最终准确率能高1到2个百分点。

4. 训练流程与实操记录

4.1 数据准备与预处理

我用的是手写数字识别数据集,6万张训练图、1万张测试图,每张28x28像素。预处理做了三件事:第一,把像素值从0到255归一化到0到1之间,这样输入数值范围一致,梯度不会因为输入尺度差异而爆炸;第二,把标签转成独热编码,方便计算交叉熵;第三,把训练集随机打乱,避免同一类别的样本连续出现导致梯度方向偏置。

数据加载我写了一个简单的批次生成器,每次返回一个batch的输入和标签。这里有个细节:最后一个batch可能不足设定的batch size,我的处理是直接丢弃,因为不足一个batch的样本对梯度贡献小,而且会导致形状不一致的麻烦。如果数据集很小,丢弃最后一个batch可能损失不少数据,那就需要特殊处理,但我这个场景下6万张图丢几十张无所谓。

4.2 网络结构的设计与调整

我最终用的网络结构是:输入层784维,第一个隐藏层256个神经元,第二个隐藏层128个神经元,输出层10维。激活函数全部用ReLU,输出层用softmax。这个结构不算深,但足够验证我的框架是否正确。

为什么选256和128?其实是试出来的。我试过512和256,参数量太大,普通显卡训练慢,而且容易过拟合。也试过128和64,欠拟合,训练准确率上不去。256和128在验证集上的表现最好,参数量大约23万,显存占用不到1GB,完全在普通显卡的能力范围内。

层数方面,我试过加第三层隐藏层,但效果没有明显提升,反而训练时间增加了30%。所以最终定在两层隐藏层。这个经验不一定通用,但对于这个规模的数据集和任务,两层已经足够了。

4.3 训练循环的完整实现

训练循环的伪代码大概长这样:

for epoch in range(num_epochs): for batch_x, batch_y in data_loader: # 前向传播 output = model(batch_x) loss = cross_entropy(output, batch_y) # 反向传播 model.zero_grad() loss.backward() # 梯度累积 if step % accumulation_steps == 0: optimizer.step() scheduler.step() optimizer.zero_grad() # 记录日志 if step % log_interval == 0: print(f"Epoch {epoch}, Step {step}, Loss {loss.item()}")

这里有几个实操细节。第一,zero_grad的调用时机很关键。如果放在反向传播之后、参数更新之前,那梯度累积就失效了,因为每次反向传播前梯度都被清零了。正确的做法是在参数更新之后清零,或者用两个独立的梯度缓冲区交替使用。第二,梯度累积的步数要和学习率配合调整。累积步数翻倍,等效batch size翻倍,学习率通常也要相应增大,但增大的幅度不是线性的,我一般按sqrt(2)来调。

训练过程中我监控三个指标:训练损失、验证损失、验证准确率。训练损失持续下降但验证损失开始上升,说明过拟合了,需要加正则化或减少参数量。两个损失都下降但准确率不涨,可能是学习率太小或者网络容量不够。两个损失都不降,那大概率是梯度算错了或者学习率太大导致震荡。

4.4 普通显卡上的性能实测

我的测试环境是一张4GB显存的普通显卡,具体型号就不说了,反正不是高端卡。在这个环境下,我做了几组对比实验:

配置batch size显存占用每epoch耗时最终准确率
基线320.8GB45秒97.2%
梯度累积32x40.9GB52秒97.5%
混合精度640.6GB35秒97.3%
混合精度+梯度累积64x20.7GB40秒97.6%

从表里能看出来,混合精度对速度和显存的改善最明显,梯度累积对准确率有小幅提升。两者结合是最优方案,显存占用只有基线的不到一半,速度还快了10%以上。最终我用的配置是混合精度加梯度累积步数2,等效batch size 128,训练20个epoch大约15分钟,最终测试集准确率97.6%。

这个结果说明一个事情:普通显卡完全有能力训练中小规模的神经网络,关键不在于硬件多强,而在于显存管理和计算效率的优化。很多人的显卡其实够用,只是没有把资源利用好。

5. 踩坑记录与排查技巧

5.1 梯度消失与梯度爆炸

这是训练中最常见的问题。梯度消失的表现是训练损失几乎不降,梯度爆炸的表现是损失变成NaN。我的排查方法是:在反向传播之后打印每一层梯度的均值和标准差。如果某一层的梯度均值接近零且标准差极小,那就是梯度消失了;如果梯度值特别大,比如超过100,那就是爆炸了。

梯度消失的解决方案通常是换激活函数,把Sigmoid换成ReLU。梯度爆炸的解决方案是梯度裁剪,我设的阈值是5.0,超过这个值的梯度会被缩放到5.0。梯度裁剪的实现很简单:计算所有参数梯度的范数,如果超过阈值就按比例缩小。这个操作在反向传播之后、参数更新之前执行。

还有一个隐蔽的坑:参数初始化不当也会导致梯度问题。如果初始权重太大,前向传播的输出会爆炸,反向传播的梯度也会爆炸。如果初始权重太小,前向传播的输出会趋近于零,梯度也会消失。所以He初始化不是随便选的,它是根据激活函数的特性推导出来的。

5.2 损失函数不收敛的排查思路

损失不收敛的原因很多,我总结了一个排查顺序:

  1. 先检查数据。把输入和标签打印出来看,确认没有乱码、没有标签错位、没有归一化遗漏。我遇到过一次标签编码搞反了,0变成9、1变成8,训练了半天准确率一直在10%左右徘徊。

  2. 再检查梯度。用数值梯度验证解析梯度是否正确。具体做法是:对某个参数加上一个很小的扰动,计算损失变化,除以扰动值,得到数值梯度;然后跟反向传播算出来的解析梯度对比。如果差异很大,说明反向传播实现有bug。

  3. 然后检查学习率。学习率太大会震荡,太小会收敛慢。我一般先用一个较大的学习率跑几百步,看损失是否下降,如果下降就逐步减小,直到找到稳定的值。

  4. 最后检查网络结构。层数太多、参数量太大可能导致难以训练。可以先用一个很浅的网络跑通,再逐步加深。

5.3 显存溢出的应急处理

显存溢出报错通常发生在训练过程中,而不是一开始。因为一开始只加载了模型参数,训练过程中中间激活值会逐渐累积。应急处理方法有几个:

  • 立即减小batch size,这是最直接有效的。
  • 检查是否有不必要的张量被保留。比如在验证阶段,应该用torch.no_grad()或者手动设置requires_grad=False,避免计算和存储梯度。
  • 检查是否有内存泄漏。Python的垃圾回收不是实时的,有时候需要手动调用gc.collect()。
  • 如果用了梯度累积,检查累积的梯度是否及时释放。累积的梯度应该存在固定的缓冲区里,而不是每次新建张量。

提示:显存溢出时不要慌,先看报错信息里说的哪个操作导致的,然后顺着计算图往回找,通常能找到那个不该保留的大张量。

5.4 常见问题速查表

问题现象可能原因排查方法解决方案
损失不下降学习率太小、梯度消失、数据有问题打印梯度、检查数据调大学习率、换ReLU、重新预处理
损失变NaN学习率太大、梯度爆炸、数值溢出打印损失和梯度减小学习率、梯度裁剪、检查softmax实现
准确率不涨过拟合、欠拟合、标签错误对比训练和验证指标加正则化、增加容量、检查标签
显存溢出batch太大、中间变量未释放查看显存占用曲线减小batch、及时释放、用混合精度
训练速度慢数据加载瓶颈、矩阵乘法低效计时各阶段耗时预加载数据、优化矩阵顺序、用GPU

6. 后续扩展与个人体会

这个框架目前只支持全连接层,后续我打算加上卷积层和循环层。卷积层的实现比全连接层复杂不少,主要是im2col和col2im的转换,以及反向传播时对卷积核的梯度计算。循环层则涉及时间步的展开和梯度的跨时间传播,又是另一个维度的复杂度。不过有了现在这个基础,扩展起来应该不会太吃力。

另一个想做的方向是量化。把32位浮点数量化成8位整数,模型大小直接缩小到四分之一,推理速度也能提升好几倍。但量化会损失精度,需要在训练时做量化感知训练,让网络适应低精度表示。这个我还在调研阶段,等有结果了再分享。

最后说点个人体会。自己写神经网络这件事,最大的收获不是代码本身,而是对训练过程的直觉。以前调库的时候,遇到不收敛就瞎试,改学习率、改batch size、改网络结构,碰运气。现在自己实现了反向传播,知道梯度是怎么算出来的,遇到问题能顺着计算图一步步排查,效率高了很多。而且很多在框架里看起来理所当然的设计,自己实现一遍才知道背后的权衡和取舍。

如果你也想动手写一个,我的建议是从最简单的开始:一个线性层、一个ReLU、一个均方误差损失,在二维数据上跑通。然后逐步加层、加激活函数、加优化器。每加一个模块就验证一次梯度,确保正确再往下走。不要一上来就搞复杂的网络,那样出了问题根本不知道是哪里的错。

相关新闻

51单片机外部中断0实战:从点灯到事件驱动机制全解析

51单片机外部中断0实战:从点灯到事件驱动机制全解析

1. 从一次"点灯自由"聊起:为什么连中断都算项目?如果你玩过单片机,多半经历过这样的过程:先是点亮一颗LED,然后让灯闪烁,接着按键控制灯亮灭,再往后就轮到外部中断登场了。市面上大量…

2026/10/20 16:31:22 阅读更多 →
AI+Wireshark智能抓包:从过滤表达式到异常定位的排障实战

AI+Wireshark智能抓包:从过滤表达式到异常定位的排障实战

Wireshark 用了快十年,前几年我碰上问题,基本都是靠逮住一个包啃半天来定位。但这段时间我的工作习惯变了:AI 和 Wireshark 抓包放在一起之后,很多过去靠肉眼硬看才能发现的规律,秒级就能出来。这篇文章不是讲怎么装 W…

2026/10/20 16:30:14 阅读更多 →
GPIO驱动能力与输出摆率详解:信号质量、功耗与EMI的平衡秘诀

GPIO驱动能力与输出摆率详解:信号质量、功耗与EMI的平衡秘诀

你打开单片机厂商的图形化配置工具,看到一个引脚的下拉菜单里躺着“驱动能力”和“输出摆率”两个选项,旁边默认写着 Low 或 High。大部分人跟着感觉选了最大档,觉得“越大越稳”。我在项目里吃过亏之后才意识到,这两个参数是 GPI…

2026/10/20 16:30:14 阅读更多 →

最新新闻

企业信息收集一步到位:ENScan_GO 备案、APP、公众号聚合实战指南

企业信息收集一步到位:ENScan_GO 备案、APP、公众号聚合实战指南

企业信息收集一步到位:ENScan_GO 备案、APP、公众号聚合实战指南 【免费下载链接】ENScan_GO 一款基于各大企业信息API的工具,解决在遇到的各种针对国内企业信息收集难题。一键收集控股公司ICP备案、APP、小程序、微信公众号等信息聚合导出。支持MCP接入…

2026/10/20 19:21:40 阅读更多 →
风扇选型软件:从P-Q曲线到工作点计算与批量筛选

风扇选型软件:从P-Q曲线到工作点计算与批量筛选

简介:Optimiser风扇选型软件是Multi-Wing官方推出的一款轴流风扇选型工具,面向暖通空调、工业通风、冷却散热等领域的工程师与设备选型人员。使用者只需输入应用所需的风量、压力与能耗等参数,软件即可自动匹配莫迪温风扇型号,帮助…

2026/10/20 19:21:40 阅读更多 →
AI辅助项目管理实操:用CodeBuddy打造可对话的“活档案”

AI辅助项目管理实操:用CodeBuddy打造可对话的“活档案”

说实话,以前我对“用AI做项目管理”这事挺抵触的。项目管理讲究的是人对人的协作、决策和沟通,AI能插得上什么手?直到我连续带了三周迭代,发现自己一半精力被会议、同步、验收这些琐事磨掉了,才下定决心让CodeBuddy进项…

2026/10/20 19:21:40 阅读更多 →
免费开源 3D 打印切片软件 Bambu Studio:3 步出件,自动支撑、多色打印一次配齐

免费开源 3D 打印切片软件 Bambu Studio:3 步出件,自动支撑、多色打印一次配齐

免费开源 3D 打印切片软件 Bambu Studio:3 步出件,自动支撑、多色打印一次配齐 【免费下载链接】BambuStudio PC Software for BambuLab and other 3D printers 项目地址: https://gitcode.com/GitHub_Trending/ba/BambuStudio Bambu Studio 是一…

2026/10/20 19:21:40 阅读更多 →
SodaDownloader:汽水音乐链接解密提取FLAC/M4A的完整教程

SodaDownloader:汽水音乐链接解密提取FLAC/M4A的完整教程

简介:SodaDownloader是一款面向汽水音乐平台用户的便捷下载工具,只需复制分享链接即可解析歌曲信息,并支持FLAC无损音频和M4A高质量格式的解密与下载,让用户不进入平台也能获取高品质音乐文件。该源码工程压缩包共26个文件、仅72K…

2026/10/20 19:21:40 阅读更多 →
pandas动态时间窗口实现多周期销量统计

pandas动态时间窗口实现多周期销量统计

1. 项目概述:为什么“近X天销量统计”是业务分析的起点而非终点在某电商公司做数据支持的那两年,我每天早上八点准时打开BI看板,第一眼不是看总GMV,而是盯着“近7天销量趋势”那个小卡片——它像血压计一样,能快速判断…

2026/10/20 19:20:37 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →