CANN ops-nn 算子开发实战:aclnnMaxPoolingGrad 最大池化反向传播接口全解析
CANN ops-nn 算子开发实战aclnnMaxPoolingGrad 最大池化反向传播接口全解析【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn导读本文以 CANN ops-nn 开源算子库中 experimental/pooling/max_pooling_grad 目录下的 aclnnMaxPoolingGrad 接口文档 为主线系统讲解 MaxPoolingGrad 算子最大池化的反向传播计算输入梯度的数学原理、两段式 aclnn 接口原型、参数约束、返回码含义与完整调用示例并深入其 op_host / op_kernel 源码与单元测试揭示 shape 推导、多核 Tiling 切分与 AscendC 向量指令实现细节。读完本文你将掌握在 Atlas A2 系列产品上通过 aclnnMaxPoolingGradGetWorkspaceSize aclnnMaxPoolingGrad 两阶段接口完成梯度计算、验证与资源管理的完整方法。产品支持情况根据接口文档与算子 READMEaclnnMaxPoolingGrad 当前支持的产品范围如下产品是否支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品√从 op_host/max_pooling_grad_def.cpp 的算子定义源码可以看到该算子在注册时仅配置了ascend910bAscend910Barch22这一 AI Core 架构OpAICoreConfig aicoreConfig910B; aicoreConfig910B.DynamicCompileStaticFlag(true) .DynamicFormatFlag(false) .DynamicRankSupportFlag(false) .DynamicShapeSupportFlag(true) // 支持动态 shape .NeedCheckSupportFlag(false) .PrecisionReduceFlag(true); this-AICore().AddConfig(ascend910b, aicoreConfig910B);这意味着该算子在当前仓库中面向 910B对应 Atlas A2 系列平台适配且开启了动态 shape 支持。使用前请确认目标设备型号。功能说明与数学原理接口功能最大池化的反向传播计算输入梯度gradient w.r.t. input。在卷积神经网络训练过程中前向 max pooling 从每个窗口中选择最大值作为输出 $y$反向传播时需要把上游梯度 $\frac{\partial L}{\partial y}$ 回传只有被选中为最大值的那个输入元素能够接收到梯度其余元素的梯度为 0。对于非重叠窗口stride kernel_size窗口元素一一对应场景计算公式为$$ \frac{\partial L}{\partial x_i} \begin{cases} \frac{\partial L}{\partial y_i}, \text{if } x_i y_i \ 0, \text{otherwise} \end{cases} $$其中 $x_i$ 为前向输入元素$y_i$ 为前向输出该窗口的最大值$\frac{\partial L}{\partial y_i}$ 为上游梯度dy。本算子不直接接收 kernel_size / stride 等池化参数而是要求前向输出 $y$ 已按非重叠窗口展开到与 $x$ 完全相同的形状因此整个反向计算退化为逐元素的选择性传递dx[i] (x[i] y[i]) ? dy[i] : 0这一等价表达式在仓库源码中被多处明确记录例如 op_kernel/max_pooling_grad_tiling_data.h 的文件头注释以及 README.md 的约束说明。函数原型两段式接口每个算子采用两段式接口必须先调用aclnnMaxPoolingGradGetWorkspaceSize获取计算所需 workspace 大小以及包含算子计算流程的执行器executor再调用aclnnMaxPoolingGrad执行计算。aclnnStatus aclnnMaxPoolingGradGetWorkspaceSize( const aclTensor* dy, const aclTensor* x, const aclTensor* y, const aclTensor* dx, uint64_t* workspaceSize, aclOpExecutor** executor) aclnnStatus aclnnMaxPoolingGrad( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)为什么需要两段式接口从源码看Tiling 阶段op_host/max_pooling_grad_tiling.cpp会查询平台信息、计算多核切分与 UB tile 大小并为高级向量 API 申请系统 workspace// workspace: CompareScalar/Select 为高级向量 API需要系统 workspace size_t systemWorkspaceSize platform_ascendc::PlatformAscendC(context-GetPlatformInfo()).GetLibApiWorkSpaceSize(); currentWorkspace[0] systemWorkspaceSize;workspace 大小只有在 Tiling 完成后才能确定因此第一段接口负责完成入参校验、shape 推导、Tiling 计算并返回 workspaceSize 与 executor第二段接口才真正把计算任务下发到指定 stream。aclnnMaxPoolingGradGetWorkspaceSize 参数说明参数表参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensordy (aclTensor*)输入上游梯度 (upstream gradient)公式中的输入 $\frac{\partial L}{\partial y}$shape 需要与 x、y 保持一致FLOAT、FLOAT16ND1-8√x (aclTensor*)输入前向输入张量公式中的输入 $x$shape 需要与 dy、y 保持一致FLOAT、FLOAT16ND1-8√y (aclTensor*)输入前向输出最大值公式中的输入 $y$shape 需要与 dy、x 保持一致FLOAT、FLOAT16ND1-8√dx (aclTensor*)输出输入梯度公式中的输出 $\frac{\partial L}{\partial x}$shape 与 dy、x、y 一致数据类型与 dy 一致FLOAT、FLOAT16ND1-8√workspaceSize (uint64_t*)输出返回需要在 Device 侧申请的 workspace 大小-----executor (aclOpExecutor**)输出返回 op 执行器包含算子计算流程-----要点补充数据类型仅支持 FLOATfp32与 FLOAT16fp16不支持 BF16。这一点与 max_pooling_grad_def.cpp 中注册的{ge::DT_FLOAT16, ge::DT_FLOAT}完全一致。数据格式仅支持 ND 格式非连续张量详见 non_contiguous_tensor.md不支持 NCHW/NHWC 等维度重排格式。维度支持 1-8 维超过 8 维会在入参校验阶段报错。非连续 Tensor四个张量均支持传入非连续张量strides 不紧致这在拼接、切片后的子张量反向传播中很实用。返回值错误码第一段接口完成入参校验返回aclnnStatus状态码完整含义见 aclnn返回码。出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 dy、x、y 或 dx 是空指针时ACLNN_ERR_PARAM_INVALID161002dy 的数据类型不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002x、y 的数据类型和 dy 不同ACLNN_ERR_PARAM_INVALID161002dy、x、y 和 dx 的 shape 不一致ACLNN_ERR_PARAM_INVALID161002dy、x 或 y 的 shape 超过 8 维其中shape 不一致的校验在 Host 侧 shape 推导阶段同样存在查看 op_host/max_pooling_grad_infershape.cppInferShape 会严格检查三个输入 shape 必须相等否则返回失败OP_CHECK_IF(*xShape ! *dyShape || *yShape ! *dyShape, OP_LOGE(context-GetNodeName(), x/y shapes must equal dy shape), return ge::GRAPH_FAILED); *dxShape *dyShape; // 输出 dx 与输入同形恒等映射同时注册了InferOutDataTypeSameWithFirstInput()保证输出 dx 的数据类型与第一个输入 dy 相同。aclnnMaxPoolingGrad 参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnMaxPoolingGradGetWorkspaceSize 获取executor输入op 执行器包含算子计算流程stream输入指定执行任务的 Stream返回值aclnnStatus具体参见 aclnn返回码。约束说明使用该接口时必须满足以下约束适用于非重叠窗口stride kernel_size场景dy / x / y / dx 四者形状相同y为前向 max pooling 的输出每个窗口的最大值已按非重叠窗口展开到与x相同的形状算子在x与y同形前提下逐元素计算dx (x y) ? dy : 0仅支持 ND 格式不支持 BF16 数据类型确定性计算aclnnMaxPoolingGrad 默认为确定性实现确定性计算的概念可参考 determinism_compute.md同一输入在多次执行中结果可复现。调用示例完整可运行代码以下示例代码来自接口文档与 examples/test_aclnn_max_pooling_grad.cpp 同构后者使用 shape[64, 512]并演示了前向最大值位置梯度透传、非最大值位置梯度置零两种行为。编译与运行的具体过程请参考编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_max_pooling_grad.h #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vectorint64_t shape) { int64_t shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法资源初始化 auto ret aclInit(nullptr); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclInit failed. ERROR: %d\n, ret); return ret); ret aclrtSetDevice(deviceId); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSetDevice failed. ERROR: %d\n, ret); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); return ret); return 0; } template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); // 计算连续tensor的strides std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. device/stream初始化 int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出 std::vectorint64_t shape {2, 2}; void* dyDeviceAddr nullptr; void* xDeviceAddr nullptr; void* yDeviceAddr nullptr; void* dxDeviceAddr nullptr; aclTensor* dy nullptr; aclTensor* x nullptr; aclTensor* y nullptr; aclTensor* dx nullptr; // 测试数据: dy全1, x和y相同 (全为最大值), 期望dx全为1 std::vectorfloat dyHostData {1, 1, 1, 1}; std::vectorfloat xHostData {1, 2, 3, 4}; std::vectorfloat yHostData {1, 2, 3, 4}; std::vectorfloat dxHostData {0, 0, 0, 0}; // 创建dy aclTensor ret CreateAclTensor(dyHostData, shape, dyDeviceAddr, aclDataType::ACL_FLOAT, dy); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建x aclTensor ret CreateAclTensor(xHostData, shape, xDeviceAddr, aclDataType::ACL_FLOAT, x); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建y aclTensor ret CreateAclTensor(yHostData, shape, yDeviceAddr, aclDataType::ACL_FLOAT, y); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建dx aclTensor ret CreateAclTensor(dxHostData, shape, dxDeviceAddr, aclDataType::ACL_FLOAT, dx); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnMaxPoolingGrad第一段接口 ret aclnnMaxPoolingGradGetWorkspaceSize(dy, x, y, dx, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnMaxPoolingGradGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr nullptr; if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } // 调用aclnnMaxPoolingGrad第二段接口 ret aclnnMaxPoolingGrad(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnMaxPoolingGrad failed. ERROR: %d\n, ret); return ret); // 4. 同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值 auto size GetShapeSize(shape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), dxDeviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy result from device to host failed. ERROR: %d\n, ret); return ret); for (int64_t i 0; i size; i) { LOG_PRINT(dx[%ld] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor aclDestroyTensor(dy); aclDestroyTensor(x); aclDestroyTensor(y); aclDestroyTensor(dx); // 7. 释放device资源 aclrtFree(dyDeviceAddr); aclrtFree(xDeviceAddr); aclrtFree(yDeviceAddr); aclrtFree(dxDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }代码要点逐段拆解步骤 1device/stream 初始化aclInit初始化 ACL 运行时aclrtSetDevice指定设备默认 device 0aclrtCreateStream创建计算流。这是所有 aclnn 算子的固定前置流程。步骤 2构造张量核心工具函数CreateAclTensor完成三步aclrtMalloc申请 Device 内存 →aclrtMemcpyACL_MEMCPY_HOST_TO_DEVICE把 Host 数据拷入 → 计算连续张量 strides 后调用aclCreateTensor以ACL_FORMAT_ND格式创建aclTensor。四个张量必须使用相同 shape{2, 2}和相同数据类型ACL_FLOAT。步骤 3两段式调用先调用aclnnMaxPoolingGradGetWorkspaceSize获得workspaceSize与executor注意只有当 workspaceSize 0 时才需要aclrtMalloc申请 workspace随后调用aclnnMaxPoolingGrad(workspaceAddr, workspaceSize, executor, stream)下发计算。步骤 4-5同步与取数aclrtSynchronizeStream阻塞等待任务完成再用ACL_MEMCPY_DEVICE_TO_HOST把dx结果拷回 Host 并逐元素打印。步骤 6-7资源释放依次销毁四个aclTensor、释放四块 Device 内存与 workspace最后销毁 stream、复位设备并aclFinalize。任何一步遗漏都可能导致显存泄漏。对于本例数据x y每个元素都是最大值因此期望输出dx {1, 1, 1, 1}即梯度完整透传。若想验证非最大值位置梯度归零可参考 examples/test_aclnn_max_pooling_grad.cpp 中前 5 个元素的构造当x[4]1而y[4]2x ! y时即使dy[4]9.9输出dx[4]仍为 0。源码纵深从接口到 NPU 内核的实现链路1. 算子定义op_hostmax_pooling_grad_def.cpp 通过OpDef注册算子原型三个必选输入dy/x/y与一个必选输出dx数据类型限定{DT_FLOAT16, DT_FLOAT}格式限定FORMAT_ND并显式声明动态 shape 支持DynamicShapeSupportFlag(true)。2. Shape 推导op_hostmax_pooling_grad_infershape.cpp 实现恒等映射校验三输入 shape 相等后将dy的 shape 直接赋给输出dx。对应的 Host 侧单元测试 tests/ut/op_host/test_max_pooling_grad_infershape.cpp 构造[2, 1, 4, 6]四维输入断言 InferShape 返回成功且输出 shape 恒等于输入。3. Tiling 与多核切分op_hostmax_pooling_grad_tiling.cpp 是整个算子的性能关键核心思路以BLOCK_SIZE 256CompareScalar/Select 高级向量 API 的字节对齐要求为基本单位按(ubSize / BLOCK_SIZE / BUFFER_NUM) / UB_PART_NUM计算每个 UB tile 能容纳的 256B block 数进而得到tileDataNum多核非均匀切分将总数据按 256B 对齐后均匀分配到多个 AI Core前tailBlockNum个 core 作为big core多承担一个 block其余为small core以处理无法整除的余数边界钳位由于 256B 对齐会膨胀数据量Host 侧计算lastCoreValidDataNum保证最后一个 core 只处理真实有效数据避免越界读写kernel 侧对应逻辑见下通过context-SetBlockDim(coreNum)设置核数并返回高级向量 API 所需的系统 workspace 大小。TilingData 结构体定义在 op_kernel/max_pooling_grad_tiling_data.h包含smallCoreDataNum、bigCoreDataNum、ubPartDataNum、尾部元素数与循环次数等字段。4. 内核实现op_kernelmax_pooling_grad.cpp 是 kernel 入口通过DTYPE_DY宏默认half模板实例化NsMaxPoolingGrad::KernelMaxPoolingGrad。核心计算在 op_kernel/max_pooling_grad.h 的Compute中完成// diff x - y: 当 x 是最大值时 diff 0 Sub(diff, xLocal, yLocal, processDataNum); // selector (diff 0): 标记最大值位置 CompareScalar(selector, diff, zeroVal, CMPMODE::EQ, computeDataNum); // dx selector ? dy : 0 Select(dxLocal, selector, dyLocal, zeroTens, SELMODE::VSEL_TENSOR_TENSOR_MODE, computeDataNum);即先减法求差、再比较定位最大值、最后按掩码选择三步向量指令流水。值得一提的实现细节SelectorType特化fp16 类型下CompareScalar输出uint8_t掩码float 类型下输出T类型掩码通过模板特化适配两种指令行为Init中根据 coreIdx 判断 big/small core 并设置globalOffset最后一个 core 若命中lastCoreValidDataNum则重新计算 loopNum 与 tailDataNum防止 256B 对齐膨胀导致越界Process主循环按 tile 分块执行 CopyIn → Compute → CopyOut尾部数据量在最后一轮收敛。5. 内核单元测试tests/ut/op_kernel/test_max_pooling_grad.cpp 使用 gtest ICPU 仿真跑 kernel对 shape[2, 1, 4, 6]的 fp16 数据生成 dy/x/y 二进制数据gen_data.py手工构造 TilingDatasmallCoreDataNum128、ubPartDataNum128、lastCoreValidDataNum48等ICPU_RUN_KF单核执行后写回结果并与 golden 比对compare_data.py。测试用例刻意覆盖了对齐膨胀 边界钳位场景padding 区[48:128)不写入测试通过 memset 预置 0 保证与 golden 一致。常见问题与排查建议返回 161002ACLNN_ERR_PARAM_INVALID优先检查 dy/x/y/dx 是否同 shape、同 dtype仅 FLOAT16/FLOAT、维度是否 ≤ 8、格式是否为 ND。返回 161001ACLNN_ERR_PARAM_NULLPTR检查四个aclTensor*是否创建成功尤其aclCreateTensor失败场景以及workspaceSize、executor指针是否有效。输出 dx 全为 0 或不符合预期确认y确实是前向池化输出最大值且已展开为与x同形若使用非重叠窗口前向结果直接回传需先对y做形状展开。使用 BF16 数据接口不支持 BF16需先转换为 FLOAT16 或 FLOAT 再调用。workspace 申请仅当第一段接口返回的workspaceSize 0时才需要aclrtMalloc调用结束后务必aclrtFree。总结aclnnMaxPoolingGrad 是 CANN ops-nn 中面向 Atlas A2 系列产品提供的一个轻量、确定性的最大池化反向传播接口它将非重叠窗口、元素一一对应场景下的梯度回传简化为dx (x y) ? dy : 0的逐元素操作。通过本文你可以看到从 aclnn 两段式 API、Host 侧 shape 推导与 Tiling 多核切分到 Device 侧 AscendC 向量指令Sub / CompareScalar / Select的完整实现链路并可直接复用仓库中的 调用示例 与 单元测试 进行验证与二次开发。【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

OpenMythos:用“神话”重塑知识协作,让知识像活植物一样演化

OpenMythos:用“神话”重塑知识协作,让知识像活植物一样演化

不用怀疑,第一眼看到“OpenMythos”这个词,我就知道这不是那种靠 README 撑场面的项目。Mythos,希腊语里的“叙事”“传说”,加个 Open 前缀,摆明了是想做一套开放式的公共知识体系——不是某个人说了算的百科&#xf…

2026/9/23 8:52:14 阅读更多 →
集团低代码平台选型实战:避开五大坑,落地五年不后悔

集团低代码平台选型实战:避开五大坑,落地五年不后悔

过去大半年,我一直在帮一家横跨制造、贸易和投资的大型集团做低代码平台选型,前后对比了十多家产品,把厂商的销售都聊到怕了。但真正让我头疼的不是品牌之间的参数差异,而是团队内部对“低代码”这三个字的理解完全不一样&#xf…

2026/9/23 4:15:07 阅读更多 →
物联网考试判断题背后的硬核知识点:从RFID到三层架构

物联网考试判断题背后的硬核知识点:从RFID到三层架构

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

2026/9/22 20:35:39 阅读更多 →

最新新闻

microsandbox-agent-client:microsandbox Rust 侧 Agent 协议客户端的消息、流与握手全解

microsandbox-agent-client:microsandbox Rust 侧 Agent 协议客户端的消息、流与握手全解

Agent 沙箱虚拟化 【免费下载链接】microsandbox 🧱 fast branchable microVM for any workload 项目地址: https://gitcode.com/gh_mirrors/mon/microsandbox 点击查看 免费下载 microsandbox 是一个可快速分支(branchable)的 m…

2026/9/25 2:52:26 阅读更多 →
IronClaw ASCII Renderer 工具解析:WASM 纯计算扩展的设计、声明与安全模型

IronClaw ASCII Renderer 工具解析:WASM 纯计算扩展的设计、声明与安全模型

人工智能AI 应用交互助手AI Agent 【免费下载链接】ironclaw IronClaw is an Agent OS focused on privacy, security and extensibility 项目地址: https://gitcode.com/gh_mirrors/iro/ironclaw 点击查看 免费下载 导读 ascii-renderer.draw 是 IronClaw 中以 W…

2026/9/25 2:52:26 阅读更多 →
基于 embassy 的 STM32WBA USB DFU 应用开发:从分区布局到固件下载实战

基于 embassy 的 STM32WBA USB DFU 应用开发:从分区布局到固件下载实战

嵌入式物联网异步编程 【免费下载链接】embassy Modern embedded framework, using Rust and async. 项目地址: https://gitcode.com/gh_mirrors/em/embassy 点击查看 免费下载 导读 本文围绕 Embassy 仓库中 examples/boot/application/stm32wba-dfu/README.md 所…

2026/9/25 2:52:26 阅读更多 →
Spring Boot+Vue3接入DeepSeek:Spring AI全栈实战

Spring Boot+Vue3接入DeepSeek:Spring AI全栈实战

简介:面向后端开发者与AI应用初学者的 Spring Boot Spring AI DeepSeek 集成实战代码包,展示如何通过 Spring Boot 与 Spring AI 框架接入 DeepSeek 大模型,构建智能问答、文本生成和语义分析等功能。项目采用前后端分离与模块化设计&#…

2026/9/25 2:52:26 阅读更多 →
OptiScaler 使用教程:DLSS、FSR、XeSS 自由切换,老游戏还能补帧

OptiScaler 使用教程:DLSS、FSR、XeSS 自由切换,老游戏还能补帧

OptiScaler 使用教程:DLSS、FSR、XeSS 自由切换,老游戏还能补帧 【免费下载链接】OptiScaler OptiScaler bridges upscaling/frame gen across GPUs. Supports DLSS2/XeSS/FSR2 inputs, replaces native upscalers, enables FSR-FG/XeFG on non-FG title…

2026/9/25 2:52:26 阅读更多 →
Two.js 的 Two.Points 图元详解:从散点绘制到渲染器源码级剖析

Two.js 的 Two.Points 图元详解:从散点绘制到渲染器源码级剖析

图形学前端 【免费下载链接】two.js A renderer agnostic two-dimensional drawing api for the web 项目地址: https://gitcode.com/gh_mirrors/tw/two.js 点击查看 免费下载 Two.Points 是 Two.js 中专门用于快速绘制一组独立点的核心图元(primitive&…

2026/9/25 2:51:26 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →