算子融合:从数学等价到编译器合法性的严格推导

本文围绕四个典型融合模式展开:

  1. MatMul → Bias Add → ReLU
  2. Conv → Bias → ReLU
  3. Add → ReLU
  4. Residual Add → LayerNorm

重点回答两个问题:

  • 为什么这些算子在数学和数据依赖上可以融合?
  • 哪些情况不能融合,或者虽然能融合但不应该融合?

1. 算子融合的本质

假设原计算图是:

text 复制代码
X
↓
P
↓
T
↓
C
↓
Y

其中:

  • P:Producer,生产者;
  • C:Consumer,消费者;
  • T:中间 Tensor。

数学上:

T=P(X) T=P(X) T=P(X)

Y=C(T) Y=C(T) Y=C(T)

代入:

Y=C(P(X)) Y=C(P(X)) Y=C(P(X))

如果编译器能够在同一个 Kernel 内保持完全相同的计算语义,那么中间结果 T 就不一定需要写回 HBM/DDR。

原始执行:

text 复制代码
X
↓
P
↓
T 写 HBM
↓
C 从 HBM 读 T
↓
Y

融合执行:

text 复制代码
X
↓
P
↓
Register / Accumulator / SRAM 中的临时值
↓
C
↓
Y

因此:

算子融合不是消除数据依赖,而是在保持数据依赖不变的前提下,消除不必要的 Kernel 边界和中间 Tensor 的物化(materialization)。


2. 算子能够融合的基本合法性条件

两个算子能够融合,通常至少要满足以下条件。

2.1 数据依赖必须能够在同一个 Kernel 内保持

例如:

text 复制代码
MatMul → Bias → ReLU

依赖顺序仍然必须是:

text 复制代码
MatMul
↓
Bias
↓
ReLU

融合并不允许错误重排。

2.2 中间结果不需要被程序外部观察

例如:

text 复制代码
%0 = matmul(...)
%1 = relu(%0)
return %1

如果 %0 除了给 relu 使用之外没有其他用途,那么 %0 可以只存在于寄存器或片上 SRAM 中。

但如果:

text 复制代码
          ┌→ Consumer A
MatMul → T
          └→ Consumer B

那么直接把 T 完全消掉就没有那么简单,因为两个 Consumer 都需要它。

2.3 算子不能有不可忽略的副作用

纯数学算子:

text 复制代码
Add
Mul
MatMul
Conv
ReLU
GELU

一般比较容易融合。

但如果算子涉及:

text 复制代码
文件 I/O
Host callback
外部设备状态
不可重放随机状态
特殊原子更新

就不能简单按照纯函数进行重排或消除。

2.4 同一个 Kernel 必须能够完成所需同步

例如:

text 复制代码
Add → ReLU

不需要线程间同步。

而:

text 复制代码
Residual Add → LayerNorm

需要:

text 复制代码
Reduction
+
线程间同步

如果 Reduction 的范围超过单个 CTA/Workgroup 能够同步的范围,就可能不能在一个普通 Kernel 内完成。

2.5 融合后的资源必须可接受

融合可能增加:

text 复制代码
Register Pressure
Shared Memory / SRAM
Live Range
同步开销
代码尺寸

所以:

"数学上可以融合"不等于"性能上一定应该融合"。


3. MatMul → Bias Add → ReLU

这是典型的 Epilogue Fusion(尾处理融合 / 后处理融合)。

3.1 MatMul

设:

A∈RM×K A\in \mathbb{R}^{M\times K} A∈RM×K

B∈RK×N B\in \mathbb{R}^{K\times N} B∈RK×N

矩阵乘法:

$$

C_{ij}

\sum_{k=0}^{K-1}A_{ik}B_{kj}

其中: * iii:输出行; * jjj:输出列; * kkk:Reduction 维。 ### 3.2 Bias Add 设 Bias: b∈RN b\\in\\mathbb{R}\^{N} b∈RN 对输出列进行广播: ##

D_{ij}

C_{ij}+b_j

将 MatMul 代入: ##

D_{ij}

\sum_kA_{ik}B_{kj}+b_j

### 3.3 ReLU ReLU 定义: ReLU⁡(x)=max⁡(0,x) \\operatorname{ReLU}(x)=\\max(0,x) ReLU(x)=max(0,x) 所以: ##

Y_{ij}

\max(0,D_{ij})

继续代入: ##

\boxed{
Y_{ij}

\max

\left(

0,

\sum_k A_{ik}B_{kj}+b_j

\right)

}

这说明三个算子可以组成一个完全确定的复合函数。 ### 3.4 为什么适合 Kernel Fusion GEMM 内部本来就存在 accumulator: ```cpp acc = 0; for (k) acc += A[i][k] * B[k][j]; ``` 当 K Reduction 完成: ```text acc = C[i,j] ``` 传统执行: ```text acc ↓ store C 到 HBM ↓ Bias Kernel 再读 C ↓ 写 D ↓ ReLU Kernel 再读 D ↓ 写 Y ``` 融合以后: ```cpp acc = 0; for (k) acc += A[i][k] * B[k][j]; acc += bias[j]; acc = max(acc, 0); Y[i][j] = acc; ``` 数据路径变成: ```text A/B ↓ MMA / MAC ↓ Accumulator ↓ + Bias ↓ ReLU ↓ 最终 Store ``` 中间 `C`、`D` 都不必完整写回 HBM。 ### 3.5 为什么 ReLU 不能一般性折叠进 MatMul 权重 MatMul + Bias 是仿射变换: AB+b AB+b AB+b 但 ReLU: max⁡(0,x) \\max(0,x) max(0,x) 是非线性函数。 一般不存在固定矩阵 W′W'W′、Bias b′b'b′,使得: ReLU⁡(AB+b)=AW′+b′ \\operatorname{ReLU}(AB+b)=AW'+b' ReLU(AB+b)=AW′+b′ 对任意输入都成立。 因此: > ReLU 不能一般性地"折叠进权重",但可以作为同一 Kernel 的 Epilogue 融合执行。 *** ** * ** *** ## 4. Conv → Bias → ReLU 它和 GEMM 的结构非常相似。 ### 4.1 Conv 以二维卷积为例。 输入: Xn,c,h,w X_{n,c,h,w} Xn,c,h,w 权重: Wo,c,r,s W_{o,c,r,s} Wo,c,r,s 输出: ##

C_{n,o,p,q}

\sum_{c,r,s}

X_{n,c,;pS_h+rD_h-P_h,;qS_w+sD_w-P_w}

W_{o,c,r,s}

这里: * nnn:batch; * ooo:output channel; * ccc:input channel; * p,qp,qp,q:输出空间位置; * r,sr,sr,s:卷积核位置。 ### 4.2 Bias Bias 通常沿 output channel: ##

D_{n,o,p,q}

C_{n,o,p,q}+b_o

代入: ##

D_{n,o,p,q}

\sum_{c,r,s}XW+b_o

### 4.3 ReLU ##

Y_{n,o,p,q}

\max(0,D_{n,o,p,q})

因此: ##

\boxed{
Y_{n,o,p,q}

\max

\left(

0,

\sum_{c,r,s}

X_{...}W_{o,c,r,s}+b_o

\right)

}

### 4.4 为什么能融合 Conv 的一个输出元素也是 Reduction: ```text 很多 X × W ↓ Accumulator ↓ 一个 C[n,o,p,q] ``` 因此可以在输出仍然位于 accumulator 时: ```text Conv Accumulator ↓ + Bias ↓ ReLU ↓ Store ``` 所以这是典型的 **Conv Epilogue Fusion**。 *** ** * ** *** ## 5. Add → ReLU 这是最简单的 **Elementwise Fusion(逐元素融合 / 元素级融合)**。 ### 5.1 Add Zi=Ai+Bi Z_i=A_i+B_i Zi=Ai+Bi ### 5.2 ReLU ##

Y_i

\max(0,Z_i)

代入: Yi=max⁡(0,Ai+Bi) \\boxed{ Y_i=\\max(0,A_i+B_i) } Yi=max(0,Ai+Bi) 每个位置 iii 完全独立。 ```cpp float a = A[i]; float b = B[i]; float z = a + b; float y = max(z, 0); Y[i] = y; ``` 中间 `z` 只存在于寄存器。 因此没有: ```text 线程间 Reduction 线程间通信 Barrier ``` 所以它是最容易融合的一类。 *** ** * ** *** ## 6. Residual Add → LayerNorm 这是比前三者复杂得多的 **Reduction Fusion(归约融合)**。 ### 6.1 Residual Add 假设一个 token 的 hidden vector 为: Xn=\[Xn,0,Xn,1,...,Xn,D−1\] X_n=\[X_{n,0},X_{n,1},...,X_{n,D-1}\] Xn=\[Xn,0,Xn,1,...,Xn,D−1\] Residual branch: Rn=\[Rn,0,Rn,1,...,Rn,D−1\] R_n=\[R_{n,0},R_{n,1},...,R_{n,D-1}\] Rn=\[Rn,0,Rn,1,...,Rn,D−1\] Residual Add: ##

Z_{n,j}

X_{n,j}+R_{n,j}

注意: > "Residual"虽然来源于残差 F(x)=H(x)−xF(x)=H(x)-xF(x)=H(x)−x,实际残差连接的计算形式是 H(x)=x+F(x)H(x)=x+F(x)H(x)=x+F(x)。 ### 6.2 LayerNorm 的均值 LayerNorm 沿 hidden dimension DDD 求均值: ##

\mu_n

\frac{1}{D}

\sum_{j=0}^{D-1}

Z_{n,j}

把 Residual Add 代入: ##

\boxed{
\mu_n

\frac1D

\sum_j

(X_{n,j}+R_{n,j})

}

这里: ```text D 个 Z 值 ↓ Sum Reduction ↓ 1 个 sum ↓ 除以 D ↓ μ_n ``` ### 6.3 LayerNorm 的方差 ##

\sigma_n^2

\frac1D

\sum_j

(Z_{n,j}-\mu_n)^2

代入: ##

\boxed{
\sigma_n^2

\frac1D

\sum_j

(X_{n,j}+R_{n,j}-\mu_n)^2

}

LayerNorm 不是把数据"变成正态分布",而是对这一组实际数值做标准化。 ### 6.4 Normalize 标准化: ##

\hat Z_{n,j}

\frac{

Z_{n,j}-\mu_n

}{

\sqrt{\sigma_n^2+\epsilon}

}

不是"除以方差",而是除以标准差: σ2+ϵ \\sqrt{\\sigma\^2+\\epsilon} σ2+ϵ 代入 Residual: ##

\hat Z_{n,j}

\frac{

X_{n,j}+R_{n,j}-\mu_n

}{

\sqrt{\sigma_n^2+\epsilon}

}

### 6.5 Scale + Shift LayerNorm 最终还有可学习参数: ##

Y_{n,j}

\gamma_j\hat Z_{n,j}+\beta_j

所以完整公式: ##

\boxed{
Y_{n,j}

\gamma_j

\frac{

X_{n,j}+R_{n,j}-\mu_n

}{

\sqrt{\sigma_n^2+\epsilon}

}

+\beta_j

}

其中: ##

\mu_n

\frac1D\sum_j(X_{n,j}+R_{n,j})

##

\sigma_n^2

\frac1D

\sum_j

(X_{n,j}+R_{n,j}-\mu_n)^2

### 6.6 为什么 Residual Add 可以和 LayerNorm 融合 原始计算: ```text X ─┐ ├→ Add → Z 写 HBM R ─┘ ↓ LayerNorm 再读 Z ↓ Y ``` 但 LayerNorm 真正需要的是: ```text Z[j] = X[j] + R[j] ``` 因此完全可以在 LayerNorm Kernel 内边计算 `X[j] + R[j]`,边参与 `sum / sumsq` Reduction。 例如第一阶段: ```cpp for (j) { z = X[j] + R[j]; sum += z; sumsq += z * z; } ``` 得到均值和方差。实际高质量实现通常会考虑数值稳定性,例如使用 Welford 算法。 ### 6.7 为什么这种融合更复杂 对于: ```text Add → ReLU ``` 算出 `Z[0]` 后就可以立刻得到 `Y[0]`。 但 LayerNorm 不行,因为: Yn,0 Y_{n,0} Yn,0 需要: μn,σn2 \\mu_n,\\sigma_n\^2 μn,σn2 而这两个量依赖整行: ```text Z[0] Z[1] ... Z[D-1] ``` 所以需要: ```text Z[0] ─┐ Z[1] ─┤ Z[2] ─┼→ Reduction → μ, σ² ... ─┤ Z[D] ─┘ ↓ Normalize 所有元素 ``` 因此需要: ```text 线程间 Reduction + 同步 + 寄存器 / SRAM / Shared Memory ``` *** ** * ** *** ## 7. 四种情况的统一分类 | 计算模式 | 典型类型 | 核心特征 | |--------------------------|--------------------|-------------------------| | Add → ReLU | Elementwise Fusion | 每个元素独立 | | MatMul → Bias → ReLU | Epilogue Fusion | 主计算后利用 accumulator 做后处理 | | Conv → Bias → ReLU | Epilogue Fusion | Reduction 主计算后做逐元素后处理 | | Residual Add → LayerNorm | Reduction Fusion | 需要跨元素 Reduction 和同步 | *** ** * ** *** ## 8. 哪些算子通常容易融合 ### 8.1 Elementwise → Elementwise 例如: ```text Add → ReLU Mul → Add Add → GELU Bias → SiLU Scale → Clamp ``` 原因: Yi=f(g(Xi)) Y_i=f(g(X_i)) Yi=f(g(Xi)) 每个元素独立。 ### 8.2 GEMM / Conv → Elementwise Epilogue 例如: ```text MatMul → Bias MatMul → Bias → ReLU MatMul → Bias → GELU Conv → Bias → ReLU ``` 原因: ```text Reduction 主计算 ↓ 输出已经在 accumulator ↓ 继续做逐元素后处理 ``` ### 8.3 Producer → Reduction 例如: ```text Residual Add → LayerNorm Add → ReduceSum Sub → ReduceMax ``` 可以融合,但实现复杂,需要: ```text Reduction 同步 片上缓存 ``` ### 8.4 Prologue Fusion 例如: ```text Dequantize → MatMul Cast → MatMul Scale → MatMul ``` 可以边加载数据边完成前处理,不必把转换后的完整 Tensor 写入 HBM。 *** ** * ** *** ## 9. 哪些情况不能直接融合 需要区分: 1. **语义上不能直接融合** 2. **语义上可以,但实现或性能上不值得融合** *** ** * ** *** ## 10. 情况一:需要全局同步,但单 Kernel 无法提供 假设: ```text Huge Tensor ↓ Global Reduce ↓ 后续所有元素都依赖 Global Reduce 的结果 ``` 例如 Reduction 范围跨很多 CTA: ```text CTA0 ─┐ CTA1 ─┤ CTA2 ─┼→ Global Sum CTA3 ─┘ ↓ 后续计算 ``` 普通 GPU Kernel 内的 block-level barrier 只能同步一个 CTA 内线程。 如果没有合适的 Grid-level synchronization,就必须拆成多个 Kernel: ```text Kernel 1: 各 CTA 算 partial reduction ↓ 写 partial result Kernel 2: 做 global reduction ↓ 得到最终统计值 Kernel 3: 消费统计值 ``` *** ** * ** *** ## 11. 情况二:中间 Tensor 被多个 Consumer 使用 例如: ```text ┌→ ReLU MatMul → C ──┤ └→ AnotherOp ``` 如果把 MatMul + ReLU 融合,并完全不保存 C,那么 AnotherOp 就失去输入。 可选方案: ```text A. 仍然 materialize C B. 重复计算 MatMul C. 把更多 Consumer 一起融合 ``` 但都会增加成本。 *** ** * ** *** ## 12. 情况三:不可忽略的副作用 例如: ```text 文件 I/O 全局状态更新 Host callback 特殊设备操作 ``` 这些不能当作普通纯函数随意删除、重排或融合。 *** ** * ** *** ## 13. 情况四:内存别名导致依赖不明确 例如: ```cpp *p = value; x = *q; ``` 如果: ```text p == q ``` 那么存在 Memory Dependency。 编译器如果不能证明: ```text p != q ``` 就不能随便交换相关操作。 这依赖 Alias Analysis。 *** ** * ** *** ## 14. 情况五:严格浮点语义不允许改变 Reduction 顺序 浮点加法不满足严格结合律: (a+b)+c≠a+(b+c) (a+b)+c \\neq a+(b+c) (a+b)+c=a+(b+c) Reduction Fusion 为了并行,可能把: ```text a + b + c + d ``` 改成树形: ```text (a+b) + (c+d) ``` 如果编译模式要求严格保持原运算顺序,那么某些 Reduction 重排就不合法。 所以: ```text fast-math ``` 与: ```text strict floating-point ``` 的优化空间不同。 *** ** * ** *** ## 15. 情况六:训练时中间结果必须给 Backward 使用 Forward: ```text X ↓ Op ↓ T ↓ NextOp ``` Backward 可能需要 `T`。 如果 Forward Fusion 完全不保存 T,那么 Backward 就拿不到它。 可选: ```text 保存 T ``` 或者: ```text Rematerialization / Recomputation ``` 即反向时重新计算。 因此: > Forward Fusion 和 Activation Saving 是典型的计算---内存权衡。 *** ** * ** *** ## 16. 可以融合但不应该融合的典型情况 ### 16.1 Register Pressure 过高 融合后大量中间值同时存活: ```text Live Range ↑ ↓ Register Pressure ↑ ↓ Occupancy ↓ ``` 严重时: ```text Register Spill ↓ Local Memory ``` 性能可能更差。 ### 16.2 Shared Memory / SRAM 超限 例如: ```text Producer Tile + Consumer Tile + Double Buffer + Reduction Buffer ``` 全部融合后: ```text Shared Memory > Hardware Limit ``` Kernel 可能无法 Launch,或者只能严重降低并发度。 ### 16.3 Occupancy 太低 例如: ```text 融合前: 8 CTA / SM 融合后: 1 CTA / SM ``` 虽然减少 HBM Traffic,但 TLP 大幅下降。 ### 16.4 重复计算成本大于内存收益 如果融合要求 Producer 重复执行: ```text Add ``` 通常问题不大。 但如果 Producer 是: ```text 大型 MatMul 大型 Conv ``` 重新计算可能得不偿失。 ### 16.5 Layout 不兼容 例如 Producer 最优 Layout 和 Consumer 最优 Layout 不一致。 融合后如果需要昂贵的: ```text Transpose Shuffle Shared Memory Exchange ``` 融合未必更快。 ### 16.6 不同设备执行 例如: ```text Op A → GPU Op B → NPU ``` 两个算子属于不同设备,通常不能融合成一个硬件 Kernel。 *** ** * ** *** ## 17. Fusion 与 Folding 的区别 ### Fusion 数学运算仍然存在,只是放进一个 Kernel。 例如: ```text MatMul + Bias + ReLU ``` 仍然执行这三个运算,只是不再产生多个 Kernel 和中间 HBM Tensor。 ### Folding 直接改变参数,使某个算子数学上消失。 典型: ```text Conv → BatchNorm ``` Conv: z=Wx+b z=Wx+b z=Wx+b BatchNorm: y=γz−μσ2+ϵ+β y= \\gamma \\frac{z-\\mu}{\\sqrt{\\sigma\^2+\\epsilon}} +\\beta y=γσ2+ϵ z−μ+β 定义: α=γσ2+ϵ \\alpha= \\frac{\\gamma}{\\sqrt{\\sigma\^2+\\epsilon}} α=σ2+ϵ γ 得到: y=α(Wx+b−μ)+β y= \\alpha(Wx+b-\\mu)+\\beta y=α(Wx+b−μ)+β 定义: W′=αW W'=\\alpha W W′=αW b′=α(b−μ)+β b'=\\alpha(b-\\mu)+\\beta b′=α(b−μ)+β 于是: y=W′x+b′ \\boxed{ y=W'x+b' } y=W′x+b′ 因此: ```text Conv + BN ``` 可以变成: ```text New Conv ``` BN 运算本身被消掉。 *** ** * ** *** ## 18. 算子融合常见分类 ### Elementwise Fusion 中文: > **逐元素融合 / 元素级融合** 例如: ```text Add → ReLU ``` ### Epilogue Fusion 中文: > **尾处理融合 / 后处理融合** 例如: ```text MatMul → Bias → ReLU Conv → Bias → GELU ``` ### Prologue Fusion 中文: > **前处理融合** 例如: ```text Dequant → MatMul ``` ### Reduction Fusion 中文: > **归约融合** 例如: ```text Residual Add → LayerNorm ``` ### Producer-Consumer Fusion 中文: > **生产者---消费者融合** 例如: ```text Conv → ReLU ``` ### Vertical Fusion 中文: > **纵向融合 / 链式融合** 沿依赖链: ```text A → B → C ``` 进行融合。 ### Horizontal Fusion 中文: > **横向融合 / 并行融合** 多个原本独立的 Kernel 合并执行,以减少 Launch 开销。 *** ** * ** *** ## 19. 实用的 Fusion 判断流程 看到: ```text Op A ↓ Op B ``` 可以按下面顺序判断。 ### 第一步:数据依赖 问: ```text B 到底需要 A 的哪些数据? ``` 如果只是一个元素,最容易融合。 如果需要整个 Tile,难度上升。 如果需要全局 Tensor 的统计结果,可能需要多阶段 Kernel。 ### 第二步:中间 Tensor 是否有其他 Consumer ```text A → B ``` 最好。 如果: ```text A → B → C ``` 融合难度明显上升。 ### 第三步:有没有 Reduction ```text 没有 Reduction → 通常容易 有 CTA 内 Reduction → 可以,但需要同步 有跨 CTA Global Reduction → 可能需要多个 Kernel ``` ### 第四步:能否留在片上 判断: ```text Register Shared Memory SRAM Accumulator ``` 是否能保存所需中间数据。 ### 第五步:资源代价 检查: ```text Register Pressure Shared Memory Occupancy Code Size Synchronization ``` ### 第六步:Cost Model 比较: ```text 减少的 HBM Traffic + 减少的 Kernel Launch ``` 与: ```text 增加的 Register + SRAM + 同步 + 重复计算 + Occupancy 损失 ``` 只有收益大于成本,才应该执行融合。 *** ** * ** *** ## 20. 四个例子的最终统一数学表达 ### MatMul + Bias + ReLU ##

\boxed{
Y_{ij}

\operatorname{ReLU}

\left(

\sum_kA_{ik}B_{kj}+b_j

\right)

}

类型: > Epilogue Fusion ### Conv + Bias + ReLU ##

\boxed{
Y_{n,o,p,q}

\operatorname{ReLU}

\left(

\sum_{c,r,s}XW+b_o

\right)

}

类型: > Epilogue Fusion ### Add + ReLU ##

\boxed{
Y_i

\operatorname{ReLU}(A_i+B_i)

}

类型: > Elementwise Fusion ### Residual Add + LayerNorm Zn,j=Xn,j+Rn,j Z_{n,j}=X_{n,j}+R_{n,j} Zn,j=Xn,j+Rn,j ##

\mu_n

\frac1D

\sum_j Z_{n,j}

##

\sigma_n^2

\frac1D

\sum_j

(Z_{n,j}-\mu_n)^2

最终: ##

\boxed{
Y_{n,j}

\gamma_j

\frac{

X_{n,j}+R_{n,j}-\mu_n

}{

\sqrt{\sigma_n^2+\epsilon}

}

+\beta_j

}

$$

类型:

Reduction Fusion


21. 最核心结论

算子融合的核心不是:

text 复制代码
两个 Op 名字看起来能不能放一起

而是:

text 复制代码
Data Dependency
        ↓
Iteration Domain
        ↓
Reduction Dependency
        ↓
Memory Lifetime
        ↓
Synchronization Scope
        ↓
Hardware Resource
        ↓
Cost Model

最终决定:

text 复制代码
能不能融合
+
值不值得融合

可以浓缩成一句话:

只要 Producer 的输出能够在不改变程序语义的情况下,直接作为 Consumer 的片上输入,并且同步范围、内存容量、寄存器压力和执行资源都允许,就可以进行 Kernel Fusion;是否真正执行融合,则还要由 Cost Model 判断性能收益。

相关推荐
桃西西呀41 分钟前
语义缓存:命中率上去了,却把问「今天天气」的人答成了「明天天气」
人工智能·llm·ai编程
GlobalInfo42 分钟前
机器人强化学习平台 专精特新小巨人专项报告:细分市场占有率、行业位次与主导地位证明
人工智能·ai·机器人
Highcharts.js1 小时前
AI图表开发工具Highcharts Assist|自然语言生成图表代码
javascript·人工智能·highcharts·开发神器
嘟哩DuliDuli1 小时前
AI创作工作区的项目上下文与多模态任务边界
android·人工智能·安全·ai·软件工程
const_qiu1 小时前
第 2.5 章 2.5.4-2.5.5:JWT 双 Token 与声明式权限守卫
java·人工智能
阿Q的AI笔记1 小时前
13.7个百分点、6天16.7万星、99.9百分位会话超45分钟:Harness Engineering完全指南
人工智能·深度学习
chien0111 小时前
算力效率的边界:3 个有价值观点 + 4 类遗留问题(AI Infra 技术解读)
人工智能
叶子丶苏1 小时前
VibeCoding实战三个AI小项目-VibeCoding入门笔记02
大数据·人工智能·笔记
不是万能的豆学长1 小时前
AI时代程序员职业前景重塑逻辑
人工智能·ai