type
status
date
slug
summary
tags
category
icon
password
什么是神经网络核心概念工作机制(以分类为例)应用领域总结神经网络相关前置概念NNUE, 一位追求极致效率的“算法厨师”量化: 从浮点到整数的烹饪艺术NNUE的核心组件: 有效的网络层级深入解析: 各层级的功能与实现线性层(Linear Layer)Clipped ReLU 层Sigmoid与Quantmoid4池化层(Pooling Layer)核心机制: 累加器与增量更新HalfKP特征集: 为国际象棋量身定制实现细节: 前向传播与训练前向传播(Forward Propagation)使用PyTorch进行训练高级主题: 因子分解与损失函数终极技巧: Stockfish的量化方案
什么是神经网络
神经网络是一种模拟人脑神经元连接方式的数学模型: 广泛用于机器学习中的模式识别、分类、回归等任务.
核心概念
- 输入层: 接收外部数据: 如图片像素、文本向量等.
- 隐藏层: 由多个“神经元”组成: 每个神经元执行线性变换加非线性激活(如 ReLU).
- 输出层: 给出最终预测结果: 如分类标签或数值输出.
工作机制(以分类为例)
- 输入向量 x 进入网络;
- 每一层执行计算:
y = 激活函数(W·x + b);
- 最后一层输出分类结果(如是“猫”还是“狗”);
- 使用损失函数衡量预测误差;
- 通过反向传播算法更新权重.
应用领域
- 图像识别(如人脸识别)
- 自然语言处理(如翻译、聊天机器人)
- 游戏AI(如AlphaGo)
- 医疗诊断、推荐系统等
总结
神经网络就是一种能自动从数据中学习特征和规律的函数近似器: 其强大之处在于层层组合与非线性变换带来的强大表达能力.
神经网络相关前置概念
以下是神经网络的几个核心概念简要介绍:
1.神经元(Neuron)
模拟生物神经元的数学模型: 接收多个输入, 加权求和后通过激活函数输出结果.
2.层/网络层(Layer)
- 输入层: 接收原始数据.
- 隐藏层: 进行特征提取和变换.
- 输出层: 给出最终预测结果.
3.权重和偏置(Weights & Biases)
- 权重: 表示输入的重要性: 参与输入加权求和.
- 偏置: 帮助模型拟合更复杂的函数.
4.激活函数(Activation Function)
引入非线性因素: 使神经网络能够学习复杂模式. 常见的有:
- ReLU
- Sigmoid
- Tanh
激活函数是神经网络中每个神经元的“开关”, 它的作用是:
引入非线性, 使神经网络可以拟合和学习复杂的函数.
为什么需要激活函数?
如果没有激活函数, 神经网络每层都只是线性变换(如 y = Wx + b), 多层堆叠仍然是线性的, 不能处理复杂问题(比如图像识别、语言理解).
激活函数让网络具备逼近任意非线性函数的能力, 这是深度学习的关键.
常见激活函数及其特点:
名称 | 公式 | 特点 |
ReLU(线性整流) | 简单高效, 收敛快, 常用于隐藏层 | |
Sigmoid | 输出在, 容易饱和, 常用于二分类 | |
Tanh | 输出在, 中心对称, 仍可能饱和 | |
Softmax | 将输出转为概率分布, 常用于多分类输出层 |
类比理解(开关)
可以把激活函数理解为神经元是否“被激活”的规则:
- 输入太小(负的、没意义), 可能被压到0(如 ReLU) → 表示该神经元不参与当前任务;
- 输入合适, 就传递信号到下一层.
总结一句话:
激活函数让神经网络拥有“智能”与“非线性”, 是深度学习中不可或缺的一环.
5.前向传播(Forward Propagation)
输入数据从输入层流向输出层, 经过一系列加权、激活, 生成预测结果.
6.损失函数(Loss Function)
衡量模型输出与真实值的差距. 例如:
- 均方误差(MSE)
- 交叉熵损失
7.反向传播(Backpropagation)
通过梯度下降算法调整参数: 最小化损失函数. 核心是链式法则求导.
8.训练与迭代(Training & Epochs)
使用大量数据反复训练神经网络: 不断更新参数以提高准确率.
9.过拟合与正则化(Overfitting & Regularization)
过拟合是模型在训练集上表现好但泛化能力差的现象. 可用:
- Dropout
- L1/L2正则化
- 数据增强
等手段缓解.
10.常见结构
- 全连接网络(MLP)
- 卷积神经网络(CNN)
- 循环神经网络(RNN)
- Transformer(用于序列建模)
NNUE, 一位追求极致效率的“算法厨师”
我们可以将NNUE(Efficiently Updatable Neural Network)比作一位技艺高超、追求极致效率的厨师. 他的独门绝技是在极短时间内, 利用简单的厨具, 为客人烹制出大量高品质的美味佳肴. 这正是NNUE作为一种神经网络评估函数的核心优势.
NNUE的工作原则, 即这位厨师的烹饪秘诀:
- 精选少量食材(稀疏非零输入): 厨师一次只处理当前菜肴必需的几种食材, 而不是把所有可能的食材都拿出来. 这使得NNUE在进行评估时, 只需关注棋盘上发生变化的少量信息, 从而大幅提升速度.
- 保留食材原味(增量更新): 当需要微调菜肴口味时, 厨师不会重新做整道菜, 而是在现有基础上巧妙地增减调料. 这对应NNUE的高效更新能力, 当棋局仅发生一步变化时, 它无需从零开始计算, 只需更新与该步棋相关的部分.
- 厨具简约高效(简单的网络结构): 厨师使用最基础的厨房设备(常见的硬件)就能展现高超厨艺. 这使得NNUE网络足够简单, 可以在没有特殊加速器、低延迟的CPU上快速运行.
虽然NNUE也能应用于复杂的深度网络, 但它在专为低延迟CPU推理设计的浅层网络中表现最佳. 其性能目标是达到每核心每秒百万次评估——如同厨师瞬间变出百万道菜. 要实现这一极限目标, 最关键的技术就是“量化”.
量化: 从浮点到整数的烹饪艺术
想象一下, 神经网络模型中的各种参数是厨师面前的食材.
- 浮点数(Floating-Point Numbers): 如同未经处理的整块原生食材. 它们保留了最完整的“风味”(精度), 但处理起来费时费力, 需要精雕细琢.
- 整数(Integers): 如同将食材切成标准尺寸的小块, 便于快速烹饪.
量化(Quantization)的过程, 就是将浮点数转换为整数. NNUE这位“快炒厨师”极其擅长在热锅中快速翻炒
int8
/ int16
这样的“食材小块”, 以追求极致的速度与效率. 当然, 切割食材(量化)不可避免地会损失一些原味(产生误差). 在步骤繁多的复杂菜肴(深度网络)中, 这些微小误差可能累积并影响最终口感. 但在NNUE这种速度至上的“快餐厨房”里, 这种微小的精度损失几乎可以忽略不计, 换来的却是巨大的性能提升.
NNUE的核心组件: 有效的网络层级
在NNUE的厨房中, 厨师偏爱使用一组简单而高效的工具(网络层), 通常一个完整的“菜谱”(网络结构)只包含2-4道核心工序.
- 线性层(Linear Layer): 这是厨师手中的“砧板和刀”, 通过基础的乘法和加法(即线性变换)处理食材. 它是网络的核心计算单元.
- Clipped ReLU层: 这像是厨师控制调味料的“量勺”, 确保味道(数值)不会过重或过淡, 通常将结果限制在或量化后的某个整数范围内.
池化层(Pooling Layer)和更复杂的激活函数(如Sigmoid)虽然也能用, 但它们好比搅拌机或特殊调味瓶, 并非日常烹饪的首选(开销太大, 精度过剩).
一个典型的NNUE网络设计哲学是: 第一层包含绝大部分知识, 后续层的神经元数量(复杂度)应逐渐减少, 以保持整体高效. 如果在后期加入过多复杂步骤, 最终的评估结果会过度受第一层影响, 反而可能降低性能.
深入解析: 各层级的功能与实现
线性层(Linear Layer)
线性层是NNUE的基本计算单元, 其过程如同遵循一道数学公式
y = Ax + b
的食谱. x
: 输入的食材清单(输入特征向量).
A
: 食谱中的配料表(权重矩阵), 规定了每种食材的使用量.
b
: 额外的调味品(偏置项).
y
: 烹饪出的半成品(输出向量).
处理稀疏输入: NNUE的一个关键优势在于第一层的输入是稀疏的. 这意味着在食材清单
x
中, 大部分值为零(即棋盘上大部分位置没有发生变化). 厨师只需处理那些“非零”的食材, 从而跳过权重矩阵A
中的对应列. 这就是为什么第一层可以非常庞大(拥有数千万个参数)却依然高效的原因. Clipped ReLU 层
Clipped ReLU(Rectified Linear Unit)的作用像一个“过滤器”, 其规则是. 所有负数输入都变成0, 所有大于1的输入都被削减到1, 即将向量的所有分量都从左右两侧压缩到区间内.
它为纯线性的计算加入了非线性元素. 如果没有非线性层, 多层线性网络可以合并成一个单层, 无法学习复杂模式. Clipped ReLU的存在使得网络能够分层、精细地调整“菜肴风味”. 之所以选择“Clipped”(有上限的)ReLU, 是为了限制数值范围, 这对于后续的强力量化至关重要.
Sigmoid与Quantmoid4
函数 像一位烹饪手法极其细腻的厨师.
- 优点: 平滑的特性使其能学习比ReLU更深层次的知识.
- 缺点:
- 梯度消失: 在函数曲线的两端, 变化非常平缓. 这好比厨师用一把极细的勺子调味, 每次只能加一点点, 导致学习(训练)过程极其缓慢.
- 计算昂贵: 指数运算在CPU上效率不高.
为了解决的效率问题, NNUE采用了一种巧妙的近似替代方案: . 它使用一个分段二次函数来模拟的形状, 这个函数只涉及加法、乘法和位移等整数运算, 效率极高.

虽然在数学上不平滑, 但在实际应用中, 这种近似带来的误差可以忽略不计.
池化层(Pooling Layer)
池化层就像一个“缩减食材”的计划. 当厨师手头的食材(神经元输入)太多, 而厨房空间(下一网络层空间)有限时, 他可以选择只保留每种食材最精华的部分(最大池化), 或取其平均价值(平均池化), 从而在不丢失关键信息的前提下, 有效降低数据维度.
核心机制: 累加器与增量更新
这是NNUE实现高效更新的“独门秘籍”.
想象一下, 厨师(神经网络)需要为一道复杂的菜肴(棋局评估)混合大量食材(特征).
- 传统方法: 每次棋局变化, 都从零开始, 重新混合所有食材. 这非常耗时.
- 累加器方法: 厨师使用一个“存储罐”(累加器, 即第一层隐藏神经元的激活值). 这个罐子保存了当前棋局所有食材混合后的初步成品. 当棋局发生一步棋的变化时:
- 移除旧食材: 从“存储罐”中减去被移走棋子所对应的影响.
- 加入新食材: 向“存储罐”中加入新位置上棋子所对应的新影响.
通过这种方式, 计算量从“处理所有食材”锐减为“处理发生变化的几个食材”, 极大地提升了效率.
使用累加器面临的挑战:
- 浮点数累积误差: 如果使用浮点数, 每次加减都会引入微小的计算误差, 长期累积可能导致结果不准.
- 整数溢出风险: 如果使用量化后的整数, 需要确保“存储罐”的容量足够大, 否则在连续添加食材时可能会“溢出”, 导致结果完全错误. 解决方案是精心设计量化方案, 从数学上保证任何合法棋局都不会导致溢出.
HalfKP特征集: 为国际象棋量身定制
HalfKP(Half King-Pawn)是为国际象棋NNUE设计的一种经典特征集, 它就像一份精心挑选的“食材清单”. 其核心思想是: 从黑白双方的两个不同视角来看待同一个棋盘.
- 多视角, 多累加器: 网络会维护两个独立的“存储罐”(累加器), 一个用于白方视角, 一个用于黑方视角. 这意味着第一层的计算量和参数量都会翻倍.
- 合并视角: 在进入后续网络层之前, 需要将两个视角的累加器结果合并成一个向量. 合并方法有多种, 例如:
- 简单堆叠: 将白方向量和黑方向量直接拼接.
- 交替堆叠: 将两个向量的元素交错排列.
- 权重共享:
- 共享权重: 可以只用一套权重(食谱)来处理两个视角的特征. 但这要求对其中一个视角的特征进行“变换”(例如, 翻转棋盘坐标、交换棋子颜色), 使其与另一个视角的特征在结构上保持一致.
- 独立权重: 也可以为两个视角分别使用不同的权重. 这种方法更简单直接, 避免了复杂的特征变换, 但需要更多的模型参数.
HalfKP示例:
假设白王在A1, 黑兵在D4.
- 白方视角: 特征是(
白王, A1, 黑兵, D4)
.
- 黑方视角: 为了使用相同的网络, 需要对棋盘进行“对称变换”. 棋盘上下翻转后, A1变为 A8, D4变为D5. 特征变为(
黑王, A8, 白兵, D5)
.
通过这种方式, 无论当前轮到谁走棋, 网络都可以使用同一套对称的“食谱”来进行评估.
实现细节: 前向传播与训练
前向传播(Forward Propagation)
这是“烹饪”的全过程, 即根据输入数据计算出最终评估值的流程. 以一个典型的三层网络为例:
- L0(线性层): 将稀疏的HalfKP输入特征转换为密集的向量(累加器).
- C0(Clipped ReLU): 对累加器的结果进行非线性过滤.
- L1(线性层): 将 L0 的输出(通常是黑白两个视角拼接后)进行第二次变换.
- C1(Clipped ReLU): 再次进行非线性过滤.
- L2(线性层): 进行最后一次变换, 输出最终的评估分数.
在代码实现中, 权重矩阵的布局(行优先或列优先)会影响性能. 对于稀疏输入的第一层, 列优先布局更高效, 因为它便于直接索引和更新与特定输入特征相关的权重(特征是列向量).
使用PyTorch进行训练
训练NNUE网络就像是教会这位“厨师”新的菜谱. 通常使用像PyTorch这样的深度学习框架. 关键挑战在于数据流水线: 如何将海量的训练数据(通常存储在特定格式的文件中)高效地喂给Python端的训练脚本.
标准做法是使用C++编写一个快速的数据加载器, 负责解析训练数据文件并将其准备成稀疏张量格式, 然后通过Ctypes等桥接技术, 将这些数据批量传递给PyTorch进行模型训练.
高级主题: 因子分解与损失函数
- 特征因子分解(Feature Factorization): 这是一种高级优化技巧. 可以先在一个更大的特征空间(例如, 将HalfKP特征和另一组P特征合并)中进行训练, 让网络学习不同特征间的关联. 训练完成后, 可以将这种学到的关联“烘焙”回模型参数中, 而在实际推理时, 仍然只使用原始的、更简单的特征集, 从而在不牺牲太多精度的情况下提高效率.
- 损失函数(Loss Function): 这是衡量厨师“厨艺”(网络预测)与“标准口味”(真实对局结果)之间差距的标尺. 常用的损失函数是均方误差(MSE), 它计算预测值与目标值之差的平方, 为梯度下降提供了平滑的优化路径. 训练目标通常是让网络输出的评估值与最终的对局结果(赢、输、和)尽可能一致.
终极技巧: Stockfish的量化方案
这是将整个网络从浮点数转换为高效整数运算的实用“菜谱”. 目标是尽可能使用
int8
(8位整数)和int16
(16位整数)来完成所有计算. 其核心思想是为网络的不同部分设置不同的量化尺度(Scale).
- 特征转换器(输入层):
- 目标: 将范围的激活值映射到.
- 方法: 由于此层只有加法, 只需将原始的浮点权重和偏置都乘以127并四舍五入为整数即可.
- 线性层(以Stockfish为例):
- 目标: 用
int8
的输入和int8
的权重, 计算出int32
的输出, 同时保持精度. - 数学原理: 原始浮点计算为
C_float = A_float * B_float
. 量化后, 我们希望通过整数计算C_int ≈(A_int * B_int) / Scale
来近似它. - Stockfish方案:
- 输入激活值
A_int
的范围是[0, 127]
(量化尺度QA = 127
). - 权重
B_int
的范围是[-127, 127]
. 这里引入一个额外的缩放因子QB
(例如64
), 使得B_float = B_int / QB
. 这意味着权重的最大浮点值被限制在127/64 ≈ 2.0
左右. - 输出
C_int
的计算公式为C_int =(A_int * B_int + bias_int) / QB
. 除法通过高效的位移运算实现.
- Clipped ReLU层:
- 操作非常简单. 之前是将值限制在, 量化后则是限制在.
通过这套精密的量化方案, NNUE可以在标准的CPU上实现惊人的计算速度, 成为现代棋类引擎中不可或缺的核心技术.
- 作者:JAY
- 链接:https://notion-next-353pmh21m-jays-projects-ab02da23.vercel.app//article/21d3690b-3830-80d2-a5c0-ef742909a88b
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。