aclnn两阶段调用详解:GetWorkspaceSize与Execute的完整指南
第一次接触 aclnn 的人十有八九都会卡在同一个地方文档里明明写着aclnnXxxGetWorkspaceSize和aclnnXxx两个接口注释却说得不清不楚——到底先调哪个、中间那个 workspace 要多大、executor 又是什么我当时也是翻了好几天源码最后是 ops-nn 仓库里的标准实现把我从靠猜的状态拉回来的。这篇就顺着仓库给出的标准答案把两阶段调用这件事从头到尾讲透它为什么存在、接口怎么拆、代码怎么写、坑在哪里。适合正在写自定义算子、或者要在训练/推理框架里集成单个算子的同学参考。1. 两阶段调用为什么会存在一次 aclnn 执行的完整旅程1.1 从一个看起来很简单的算子调用说起在 CUDA 的世界里调用一个 kernel 就是一行kernelgrid, block(args)顶多再算一下 grid 和 block 的尺寸。所以很多人刚转到 aclnn 时看到两阶段调用第一反应是方案设计过度一个函数能搞定的事为什么要拆成两个这个想法很自然但忽略了一个关键差异kernel之所以简单是因为它把准备和执行都压在了那一行里而准备阶段做的事情很少——block 数、线程数、参数指针基本都能在编译期或调用前算清楚。可真正在异构 AI 芯片上跑一个经过充分优化的算子要处理的事情远不止这些。我在 ops-nn 仓库里看到的每一个算子无论多简单其执行前都要回答至少四个问题输入输出 tensor 的 shape、format、dtype 是否合法且互相匹配这个算子内部要用哪种算法分支不同 shape 下最优算法可能完全不同该算法分支需要多大的临时工作区也就是 workspace以及最终要往哪条 stream 上排一个什么粒度的任务。这四个问题里shape 推导和算法选择往往不是常数时间能算完的workspace 需求量更是直接跟输入 shape 相关。把这些东西全部塞进一个直接执行的接口里会导致一个恶性循环要么接口内部偷偷做一堆准备工作让执行这个动作变得不可预测要么调用者根本不知道要给算子准备多少临时内存只能按最大值猜白白浪费显存。1.2 GetWorkspaceSize 和 Execute 各自承担了什么aclnn 的两阶段本质就是把上面说的准备和执行显式拆开。阶段一叫aclnnXxxGetWorkspaceSize它的职责是在 host 侧完成 shape 校验、算法选取、workspace 需求量计算然后把这些打包成一个executor交给你。阶段二叫aclnnXxx名字跟算子名一致它的职责是拿着阶段一算好的 workspace 和 executor把 kernel 任务真正排到指定的 stream 上。这个设计可以用一个日常场景类比你要坐飞机出门先要查一下航班有没有免费托运行李额度、自己要带多少行李这是 GetWorkspaceSize然后才去值机托运这是 Execute。如果你是先冲到登机口再开始研究行李额度要么超重被拦要么当场手忙脚乱就是这个道理。重点在于阶段二不是同步执行的。aclnnXxx返回成功只代表任务已经被成功放入 stream 队列设备侧可能还没开始干活。所以严格来说两阶段是host 侧计算准备 host 侧异步入队真正的算子执行发生在设备侧由 stream 的推进决定。1.3 两阶段不是 aclnn 独创而是一套通用解法有人会觉得这种设计很啰嗦但翻一下其他加速库就会发现workspace 协商机制几乎是标配。cuDNN 里有cudnnGetConvolutionForwardWorkspaceSize之类的接口执行前也要先查 workspace 大小oneDNN 里的 primitive 更是把描述、创建、执行拆成三个明确阶段。ops-nn 仓库只是把这种成熟做法固化成了自己的标准接口形态。想通这一层再看两阶段就不会觉得它繁琐了。它不是拍脑袋设计出来的两个函数而是把计算资源需求和实际执行解耦的标准手段。你只要接受这个心智模型后面所有代码写起来都是顺的。2. ops-nn 仓库给出的标准答案接口骨架的解剖2.1 仓库里一个算子的典型目录与文件职责ops-nn 仓库最大的价值不在于某个具体算子的优化有多惊艳而在于它的目录组织和代码风格极其统一。随便打开一个算子目录几乎都是同一套结构ops-nn/ xxx_op/ op_api/ aclnn_xxx.h # 对外的两阶段接口声明 aclnn_xxx.cpp # 两阶段逻辑的实现 op_kernel/ xxx.cpp # 设备侧 kernel 实现 xxx_tiling.cpp # tiling 计算决定 block 怎么切 test/ main.cpp # 可直接编译运行的最小调用样例op_kernel涉及的是设备侧代码真正在芯片上算数据的逻辑在里面而op_api就是外部调用者唯一需要关心的层。你会发现仓库里所有算子的op_api文件对外接口形态几乎一模一样——都是 GetWorkspaceSize 加 Execute 的成对函数。这种一致性就是标准答案最好的体现你学会一个就学会了一百个。2.2 头文件里的标准声明以仓库里常见算子的头文件为例关键代码就两段#ifndef ACLNN_XXX_H #define ACLNN_XXX_H #include acl/acl.h #include aclnn/aclnn_tensor.h #ifdef __cplusplus extern C { #endif aclnnStatus aclnnXxxGetWorkspaceSize( const aclTensor *input, const aclTensor *weight, aclTensor *output, uint64_t *workspaceSize, aclnnXxxExecutor **executor); aclnnStatus aclnnXxx( void *workspace, uint64_t workspaceSize, aclnnXxxExecutor *executor, aclrtStream stream); #ifdef __cplusplus } #endif #endif这里Xxx换成实际算子名。第一个函数多了workspaceSize和executor两个出参第二个函数多了workspace、workspaceSize、executor三个入参。两个函数一拼正好是两阶段的完整签名。不同 ACL 版本的aclCreateTensor参数个数略有差异但这对接口的形态是稳定的。aclnnXxxExecutor这个类型尤其容易让人误解。它不是你在设备上开的一块内存而是阶段一在 host 侧构造出的一个执行描述对象里面封装了算法选择结果、kernel 的启动参数这些信息。它的生命周期管理在不同版本里略有差异有的算子要求执行后由框架统一回收有的需要调用对应的销毁接口具体以仓库头文件注释为准。我见过不少人把这个指针当裸指针 free结果炸得莫名其妙。2.3 实现文件里两阶段函数的分工看实现文件更能理解仓库的套路。GetWorkspaceSize 的实现内部通常是把外部传入的 tensor 描述解析成内部统一的算子输入结构然后调用一个负责 shape 推导和内存规划的公共函数最后把算出来的 workspace 大小和构造好的 executor 填到出参里。Execute 的实现则简单得多把 workspace、executor、stream 三样东西交给 kernel 启动逻辑做一层参数合法性校验后直接入队。所以从代码量上看两个函数往往不成比例——GetWorkspaceSize 可能几百行Execute 可能就几十行。这不是仓库偷懒而是本来就该这样复杂度和不确定性都被前置到阶段一解决了阶段二自然就薄了。这也是我强烈建议新手直接抄 ops-nn 仓库样例的原因。网上很多文章只告诉你要调两个函数但不会告诉你这两个函数在仓库实现里的真实分工。照着仓库的test/main.cpp走一遍你对哪一步做了什么会有比看十篇文档都清楚的理解。3. 手写最小复现把两阶段调用完整跑起来3.1 初始化与 Tensor/DataBuffer 准备拿一个最简单的算子示例完整走一遍两阶段流程。首先是初始化环境和创建 tensor 描述。不同版本里aclCreateTensor的参数细节不一样所以我在示例里用一个CreateTensor辅助函数把它包起来这个函数在 ops-nn 仓库几乎所有 test 目录里都有直接抄过来改改就行#include cstdio #include vector #include aclnn_xxx.h static void Check(aclnnStatus status) { if (status ! ACLNN_SUCCESS) { printf(aclnn failed with status%d\n, status); exit(-1); } } int main() { aclInit(nullptr); aclrtSetDevice(0); aclrtContext context nullptr; aclrtCreateContext(context, 0); aclrtStream stream nullptr; aclrtCreateStream(stream); // 准备输入数据并拷贝到设备侧 int64_t num 1024; std::vectorfloat hostInput(num, 1.0f); void *deviceInput nullptr; aclrtMalloc(deviceInput, num * sizeof(float), ACL_MEM_MALLOC_NORMAL_ONLY); aclrtMemcpyAsync(deviceInput, num * sizeof(float), hostInput.data(), num * sizeof(float), ACL_MEMCPY_HOST_TO_DEVICE, stream); void *deviceOutput nullptr; aclrtMalloc(deviceOutput, num * sizeof(float), ACL_MEM_MALLOC_NORMAL_ONLY); int64_t shape[1] {num}; aclTensor *input CreateTensor(shape, 1, ACL_FLOAT, deviceInput); aclTensor *output CreateTensor(shape, 1, ACL_FLOAT, deviceOutput);这里有一个很多人第一次写都会忽略的点H2D 拷贝和后续的算子执行必须放在同一条 stream 上。因为 stream 内部的推进是保序的H2D 的拷贝任务排在 kernel 前面kernel 才能真正读到新数据。如果你这里用了默认 stream执行时又传了自定义 stream就会出现kernel 跑完了、数据还没拷过去的诡异现象。3.2 阶段一获取 workspaceSize 与 executor接着就是第一次接触两阶段的时刻uint64_t workspaceSize 0; aclnnXxxExecutor *executor nullptr; aclnnStatus status aclnnXxxGetWorkspaceSize(input, output, workspaceSize, executor); Check(status); printf(workspaceSize%lu bytes\n, workspaceSize);注意workspaceSize的初始值我习惯显式设成 0executor设成nullptr这是为了防止某些异常路径下接口不写这两个出参导致你拿垃圾值去判断。阶段一返回后workspaceSize就是这个算子针对当前输入 shape 需要的最小临时内存字节数它完全可能等于 0——说明算子内部可以原地计算不需要额外 scratch。executor的值也要看仔细。它不是每次调用都必须新建的在固定 shape 的循环推理场景里如果仓库实现允许缓存 executor你就可以把 executor 存下来复用省掉重复构造的开销。但如果 shape 会变或者算子内部有依赖 shape 的算法选择那就必须每次重新走阶段一。3.3 阶段二提交执行并等待结果拿到 workspaceSize 之后按需分配 workspace然后调用真正的执行接口void *workspace nullptr; if (workspaceSize 0) { aclrtMalloc(workspace, workspaceSize, ACL_MEM_MALLOC_NORMAL_ONLY); } status aclnnXxx(workspace, workspaceSize, executor, stream); Check(status); aclrtSynchronizeStream(stream);我在实际项目里踩过最痛的一跤就是把aclrtSynchronizeStream省了。当时是想当然地以为aclnnXxx返回成功就是算完了直接去读输出结果读到的是上一轮残留数据。后来才彻底记住aclnnXxx只是把任务排进 stream从入队到设备真正执行完之间隔着异步鸿沟。必须同步之后才能安全地在 host 侧读取结果。读取结果时用同步版本aclrtMemcpy从 deviceOutput 拷回 host。拷贝完成、确认数据没问题之后再进入收尾阶段。3.4 收尾释放顺序不能乱清理代码看似不起眼但顺序错了同样会炸aclrtSynchronizeStream(stream); // 保证所有异步任务真正结束 DestroyTensor(output); DestroyTensor(input); if (workspace ! nullptr) { aclrtFree(workspace); } aclrtFree(deviceOutput); aclrtFree(deviceInput); aclrtDestroyStream(stream); aclrtDestroyContext(context); aclrtResetDevice(0); aclFinalize(); return 0; }核心原则只有一条所有指向设备内存的对象必须在确认设备侧没有任务引用它们之后才能释放。异步任务还在排队时如果你提前 free 了输入输出 buffer表面上看一切正常实际会在某个随机时刻触发设备侧内存越界这种 bug 极难排查因为它不报错在这个调用点而是报到几秒之后的另一个 kernel 上。4. workspace 大小的来源与优化空间4.1 workspace 到底用来存什么理解 workspace 的构成有助于判断某个算子的 workspaceSize 为什么是那么大。设备侧 kernel 运行时不能像 host 侧一样随心所欲 malloc因为设备上的动态内存分配既不高效也容易导致内存碎片。所以框架约定算子需要的临时存储统一由 host 侧提前分配好以 workspace 指针的形式传给 kernel。常见的 workspace 用途有三类。第一类是格式转换的临时 buffer比如输入是 NHWCkernel 内部要做 NC1HWC0 的转换就需要一块中间内存先放转换结果。第二类是分段计算的暂存区典型的如大矩阵的 split-reduce 类算子每一段算完的中间结果要先存进 workspace最后再做一次合并。第三类是算法分支的备用区某些算子会按 shape 在多个算法里选一个非最优分支需要额外的 scratch 空间。所以当你看到某个算子的 workspaceSize 是几 MB 甚至几十 MB不要慌张先看算子的算法逻辑。越是做了融合的算子workspace 越可能大因为它把多个算子的中间数据都摊在了一块显存上用空间换访存效率。4.2 动态 shape 下 workspace 如何变化workspaceSize 和输入 shape 的关系是很多人没想清楚的。同一个算子batch1 时 workspaceSize 可能是 0 或几 KBbatch64 时可能就跳到几十 MB。因为 workspace 的分配公式里通常包含维度乘数比如batch * channels * splitNum * sizeof(dtype)。这在动态 shape 场景下特别危险。如果你的模型输入 batch 是不固定的而你在 host 侧缓存了上一次的 workspace下一次 shape 变大后还拿旧值去分配kernel 执行时就会把数据写到 workspace 边界之外。设备侧内存越界不像 host 那样报段错误它往往表现为数据错乱、偶发 hang或者更惨——把旁边另一块 tensor 的数据覆盖掉最后算出来的结果怎么都不对。ops-nn 仓库里处理这个问题的方式很朴素每次输入 shape 发生变化时老老实实重新调一次 GetWorkspaceSize拿新的 workspaceSize 重新分配。你可以在自己的代码里用一个 shape 签名做缓存 keyshape 一样就复用shape 变了就重新协商这样既避免了重复计算也不会拿过期尺寸去冒险。4.3 缓存 workspace 和 executor 的取舍既然 GetWorkspaceSize 也有 host 侧计算开销高频调用场景下到底缓存什么、不缓存什么值得仔细盘一盘。我的建议是分开看。workspace 本身可以缓存前提是缓存大小不低于实际需求的 workspaceSize所以每次 GWS 之后要比较一下新值小于等于缓存容量就可以接着用。executor 则要谨慎有的算子 executor 是一次性的执行完就失效了有的算子可以反复执行但前提是输入输出的 tensor 描述没变。判断依据还是仓库头文件或实现里的注释不看清楚就复用等于在赌运气。一个实用的缓存结构长这样struct OpCache { uint64_t workspaceCapacity 0; // 当前已分配 workspace 的容量 void *workspace nullptr; aclnnXxxExecutor *executor nullptr; bool executorValid false; }; OpCache gCache; // shape 没变时走快速路径 if (shapeChanged) { uint64_t newSize 0; aclnnXxxExecutor *newExecutor nullptr; auto ret aclnnXxxGetWorkspaceSize(input, output, newSize, newExecutor); Check(ret); if (newSize gCache.workspaceCapacity) { if (gCache.workspace) aclrtFree(gCache.workspace); aclrtMalloc(gCache.workspace, newSize, ACL_MEM_MALLOC_NORMAL_ONLY); gCache.workspaceCapacity newSize; } gCache.executor newExecutor; gCache.executorValid true; }缓存的收益在循环推理里非常明显。固定 shape 的 batch 推理第一次跑慢一点没关系后续迭代全走快速路径host 侧开销能降一个数量级。但代价是代码复杂度上来了如果项目规模不大我反而建议先别急着缓存把正确性跑通了再说优化。5. 两阶段调用绕不开的坑从现象到根因5.1 症状一结果全零或跟预期完全不对这是新手最常见的问题大概率不是算子本身算错了而是 host 侧的同步和拷贝顺序出了问题。排查链路按顺序走先确认 H2D 拷贝和aclnnXxx是不是同一条 stream再确认是否调用了aclrtSynchronizeStream最后确认 D2H 拷贝是不是用的同步版本并且拷贝发生在同步之后。有一个细节很少被文档强调就算你调用了aclrtSynchronizeStream它同步的也只是指定 stream 上的任务。如果你 A 操作在 stream A、B 操作在 stream B两个 stream 之间没有任何 event 做依赖host 端等 stream A 是等不到 stream B 的。多流场景下的结果错乱十有八九都是这种跨流依赖没有表达清楚。5.2 症状二偶发内存越界、设备复位这类问题最耗人因为它不稳定复现可能跑几万次迭代才冒出来一次。我复盘下来根因通常集中在两个地方一是 workspace 复用时 shape 变了但容量没跟上二是设备内存被提前释放。针对 workspace 容量问题可以在每次 shape 变化后强制重新走一次 GetWorkspaceSize并把新 size 和缓存容量打印出来比对肉眼就能看出差距。针对提前释放问题宁可多同步一次也不要省。设备侧对非法内存访问的报错往往不在肇事点而在之后某个 kernel 的启动阶段给你一种别人把我内存踩了的错觉其实祸首就是自己。5.3 症状三接口调用明显变慢、host 时间占比高性能问题往往不是 Execute 慢而是 GetWorkspaceSize 和同步操作拖了后腿。如果每个迭代都调一次 GWS而且每次还要aclrtSynchronizeStream等结果那 host 侧就把设备侧的计算潜力全浪费了流水线完全拉不起来。处理思路是分层优化shape 稳定时缓存 workspace 和 executor避免重复协商允许异步的环节去掉同步改成靠 stream 顺序自然推进真正需要跨步依赖时再用 event 表达而不是粗暴地同步整条流。我实际测过一个固定 shape 的循环样例去掉每轮 GWS 和同步之后host 侧耗时降了接近一个数量级设备侧的排空率明显改善。5.4 我的调试习惯与最终建议遇到两阶段相关的问题我一般按这个顺序动手先打开工具链的 debug 日志开关看 GetWorkspaceSize 返回码和 workspaceSize 有没有异常然后把问题算子从完整模型里剥离出来用 ops-nn 仓库 test 目录下的最小样例、用一个固定小 shape 去复现复现成功后把输入 shape、workspaceSize、stream id 一起打点对比正常路径和异常路径的差异。调用aclnnXxxGetWorkspaceSize之后的返回码是很多人会忽略的情报来源。哪怕你觉得参数肯定没问题也值得用Check包一层因为内部 shape 校验失败的报错信息比你在上层猜半天要精准得多。最后再说一条个人体会。两阶段调用不是一个需要克服的麻烦而是一个天然的调试分界线阶段一负责这个 shape 能不能算、需要多少资源阶段二负责把资源变成真正的计算。遇到问题先定位是阶段一报错还是阶段二报错排查范围直接小一半。我在实际项目里把这个原则贯彻下来之后自定义算子集成到训练流程的成功率高了很多踩坑的次数也明显变少了。

相关新闻

OpenClaw销售自动化实战:线索挖掘、客户跟进与方案生成的工程化落地

OpenClaw销售自动化实战:线索挖掘、客户跟进与方案生成的工程化落地

销售自动化的活儿,我做了不少,但真正把Agent拉进真实销售流程里当“半个员工”用,还是最近这几个月的事。项目代号就叫它“某公司销售中台Agent化改造”吧,核心平台是基于OpenClaw这个开源的多智能体通用计算平台。不是拿它做个聊…

2026/10/19 3:52:25 阅读更多 →
BES SDK源码开发实战:TWS蓝牙耳机从编译到二次开发

BES SDK源码开发实战:TWS蓝牙耳机从编译到二次开发

简介:面向真无线耳机开发者的BES2500/BES2600系列原生软件开发包源码,来源于BES官方SDK,可适配官方开发板,支持蓝牙双连、蓝牙抢连、低功耗蓝牙等关键特性,适用于真无线项目二次开发、底层驱动移植及蓝牙协议调试等场景…

2026/10/19 3:52:24 阅读更多 →
Qt6 做 HTML5 网页前端?三种落地路线与踩坑实录

Qt6 做 HTML5 网页前端?三种落地路线与踩坑实录

如果你和我一样,写了好几年 Qt 桌面程序,第一次听到“Qt6居然也可以做HTML5网页前端了”这句话,第一反应很可能是:这俩东西怎么扯到一起的?一个靠 C 吃饭的桌面应用框架,跑去和 HTML、JavaScript 抢饭碗&am…

2026/10/19 3:52:24 阅读更多 →

最新新闻

Keras实现DCGAN:从零搭建稳定图像生成模型

Keras实现DCGAN:从零搭建稳定图像生成模型

1. 从零理解 DCGAN:为什么它比普通 GAN 更值得上手搞深度学习的人多少都碰过 GAN,但真正自己动手从零搭一个能跑出结果的,比例其实不高。原因也简单——GAN 的训练出了名的难伺候,判别器和生成器稍微不平衡就崩,要么生…

2026/10/19 4:28:45 阅读更多 →
从零实现DCGAN:Keras生成对抗网络实战与调参避坑指南

从零实现DCGAN:Keras生成对抗网络实战与调参避坑指南

1. 从零理解DCGAN:为什么它值得你花时间动手搭一遍如果你刚接触生成模型,大概率第一个听到的名词就是GAN。而DCGAN,全称Deep Convolutional Generative Adversarial Networks,是把卷积神经网络真正跑通、跑稳的那套方案。我最早接…

2026/10/19 4:28:45 阅读更多 →
AI Agent从零搭建指南:模型、记忆、工具与规划全解析

AI Agent从零搭建指南:模型、记忆、工具与规划全解析

先泼一盆冷水:如果你以为AI Agent是装个框架、填个API Key、写几句Prompt就能跑起来的玩意儿,那这篇文章可能不太适合你。如果你想知道为什么同样用大模型,别人能做出自动整理周报、主动查数据、自己决策调度的Agent,而你的项目却…

2026/10/19 4:28:45 阅读更多 →
Flutter布局基石:Row、Column、Container从入门到实战

Flutter布局基石:Row、Column、Container从入门到实战

1. 内容整体设计与思路拆解1.1 为什么布局基础要从这三个组件开始入行Flutter的人,第一个绕不开的坎就是布局。官方文档里布局相关的组件有几十个,Stack、Flex、Wrap、Flow、CustomMultiChildLayout……名字都能念,真到写界面的时候&#xff…

2026/10/19 4:28:45 阅读更多 →
Java常量与数据类型全解析

Java常量与数据类型全解析

1.对于字面常量, 咱们得弄明白这是个啥回事儿, 这里面分好了几种类型, 第一类叫做字符串常量, 第二类叫做整形常量,第三类叫做浮点型常量, 第四类叫做字符常量, 第五类叫做布尔常量, 还有一类叫做空常量。被final修饰之后, size变量就变成字面常量了, 它是不可以再进行修改的。…

2026/10/19 4:28:45 阅读更多 →
OpenCode终端AI编程助手:从安装到Agent模式实战全指南

OpenCode终端AI编程助手:从安装到Agent模式实战全指南

最近大半年,我几乎把一半的编码时间都交给了终端里的 AI 助手。OpenCode 算是我试过的工具里,最接近“直接在项目里部署一个能帮你写代码的同事”的那一个。它本质是一个运行在终端里的开源 AI 编程助手,不走网页、不靠 IDE 插件,…

2026/10/19 4:27:44 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 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/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 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/19 2:54:50 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →