type
status
date
slug
summary
tags
category
icon
password

什么是神经网络

神经网络是一种模拟人脑神经元连接方式的数学模型: 广泛用于机器学习中的模式识别、分类、回归等任务.

核心概念

  • 输入层: 接收外部数据: 如图片像素、文本向量等.
  • 隐藏层: 由多个“神经元”组成: 每个神经元执行线性变换加非线性激活(如 ReLU).
  • 输出层: 给出最终预测结果: 如分类标签或数值输出.

工作机制(以分类为例)

  1. 输入向量 x 进入网络;
  1. 每一层执行计算:
    1. y = 激活函数(W·x + b);
  1. 最后一层输出分类结果(如是“猫”还是“狗”);
  1. 使用损失函数衡量预测误差;
  1. 通过反向传播算法更新权重.

应用领域

  • 图像识别(如人脸识别)
  • 自然语言处理(如翻译、聊天机器人)
  • 游戏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的工作原则, 即这位厨师的烹饪秘诀:
  1. 精选少量食材(稀疏非零输入): 厨师一次只处理当前菜肴必需的几种食材, 而不是把所有可能的食材都拿出来. 这使得NNUE在进行评估时, 只需关注棋盘上发生变化的少量信息, 从而大幅提升速度.
  1. 保留食材原味(增量更新): 当需要微调菜肴口味时, 厨师不会重新做整道菜, 而是在现有基础上巧妙地增减调料. 这对应NNUE的高效更新能力, 当棋局仅发生一步变化时, 它无需从零开始计算, 只需更新与该步棋相关的部分.
  1. 厨具简约高效(简单的网络结构): 厨师使用最基础的厨房设备(常见的硬件)就能展现高超厨艺. 这使得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更深层次的知识.
  • 缺点:
      1. 梯度消失: 在函数曲线的两端, 变化非常平缓. 这好比厨师用一把极细的勺子调味, 每次只能加一点点, 导致学习(训练)过程极其缓慢.
      1. 计算昂贵: 指数运算在CPU上效率不高.
为了解决的效率问题, NNUE采用了一种巧妙的近似替代方案: . 它使用一个分段二次函数来模拟的形状, 这个函数只涉及加法、乘法和位移等整数运算, 效率极高.
notion image
虽然在数学上不平滑, 但在实际应用中, 这种近似带来的误差可以忽略不计.

池化层(Pooling Layer)

池化层就像一个“缩减食材”的计划. 当厨师手头的食材(神经元输入)太多, 而厨房空间(下一网络层空间)有限时, 他可以选择只保留每种食材最精华的部分(最大池化), 或取其平均价值(平均池化), 从而在不丢失关键信息的前提下, 有效降低数据维度.

核心机制: 累加器与增量更新

这是NNUE实现高效更新的“独门秘籍”.
想象一下, 厨师(神经网络)需要为一道复杂的菜肴(棋局评估)混合大量食材(特征).
  • 传统方法: 每次棋局变化, 都从零开始, 重新混合所有食材. 这非常耗时.
  • 累加器方法: 厨师使用一个“存储罐”(累加器, 即第一层隐藏神经元的激活值). 这个罐子保存了当前棋局所有食材混合后的初步成品. 当棋局发生一步棋的变化时:
    • 移除旧食材: 从“存储罐”中减去被移走棋子所对应的影响.
    • 加入新食材: 向“存储罐”中加入新位置上棋子所对应的新影响.
通过这种方式, 计算量从“处理所有食材”锐减为“处理发生变化的几个食材”, 极大地提升了效率.
使用累加器面临的挑战:
  1. 浮点数累积误差: 如果使用浮点数, 每次加减都会引入微小的计算误差, 长期累积可能导致结果不准.
  1. 整数溢出风险: 如果使用量化后的整数, 需要确保“存储罐”的容量足够大, 否则在连续添加食材时可能会“溢出”, 导致结果完全错误. 解决方案是精心设计量化方案, 从数学上保证任何合法棋局都不会导致溢出.

HalfKP特征集: 为国际象棋量身定制

HalfKP(Half King-Pawn)是为国际象棋NNUE设计的一种经典特征集, 它就像一份精心挑选的“食材清单”. 其核心思想是: 从黑白双方的两个不同视角来看待同一个棋盘.
  • 多视角, 多累加器: 网络会维护两个独立的“存储罐”(累加器), 一个用于白方视角, 一个用于黑方视角. 这意味着第一层的计算量和参数量都会翻倍.
  • 合并视角: 在进入后续网络层之前, 需要将两个视角的累加器结果合并成一个向量. 合并方法有多种, 例如:
    • 简单堆叠: 将白方向量和黑方向量直接拼接.
    • 交替堆叠: 将两个向量的元素交错排列.
  • 权重共享:
    • 共享权重: 可以只用一套权重(食谱)来处理两个视角的特征. 但这要求对其中一个视角的特征进行“变换”(例如, 翻转棋盘坐标、交换棋子颜色), 使其与另一个视角的特征在结构上保持一致.
    • 独立权重: 也可以为两个视角分别使用不同的权重. 这种方法更简单直接, 避免了复杂的特征变换, 但需要更多的模型参数.
HalfKP示例:
假设白王在A1, 黑兵在D4.
  • 白方视角: 特征是(白王, A1, 黑兵, D4).
  • 黑方视角: 为了使用相同的网络, 需要对棋盘进行“对称变换”. 棋盘上下翻转后, A1变为 A8, D4变为D5. 特征变为(黑王, A8, 白兵, D5).
通过这种方式, 无论当前轮到谁走棋, 网络都可以使用同一套对称的“食谱”来进行评估.

实现细节: 前向传播与训练

前向传播(Forward Propagation)

这是“烹饪”的全过程, 即根据输入数据计算出最终评估值的流程. 以一个典型的三层网络为例:
  1. L0(线性层): 将稀疏的HalfKP输入特征转换为密集的向量(累加器).
  1. C0(Clipped ReLU): 对累加器的结果进行非线性过滤.
  1. L1(线性层): 将 L0 的输出(通常是黑白两个视角拼接后)进行第二次变换.
  1. C1(Clipped ReLU): 再次进行非线性过滤.
  1. L2(线性层): 进行最后一次变换, 输出最终的评估分数.
在代码实现中, 权重矩阵的布局(行优先或列优先)会影响性能. 对于稀疏输入的第一层, 列优先布局更高效, 因为它便于直接索引和更新与特定输入特征相关的权重(特征是列向量).

使用PyTorch进行训练

训练NNUE网络就像是教会这位“厨师”新的菜谱. 通常使用像PyTorch这样的深度学习框架. 关键挑战在于数据流水线: 如何将海量的训练数据(通常存储在特定格式的文件中)高效地喂给Python端的训练脚本.
标准做法是使用C++编写一个快速的数据加载器, 负责解析训练数据文件并将其准备成稀疏张量格式, 然后通过Ctypes等桥接技术, 将这些数据批量传递给PyTorch进行模型训练.

高级主题: 因子分解与损失函数

  • 特征因子分解(Feature Factorization): 这是一种高级优化技巧. 可以先在一个更大的特征空间(例如, 将HalfKP特征和另一组P特征合并)中进行训练, 让网络学习不同特征间的关联. 训练完成后, 可以将这种学到的关联“烘焙”回模型参数中, 而在实际推理时, 仍然只使用原始的、更简单的特征集, 从而在不牺牲太多精度的情况下提高效率.
  • 损失函数(Loss Function): 这是衡量厨师“厨艺”(网络预测)与“标准口味”(真实对局结果)之间差距的标尺. 常用的损失函数是均方误差(MSE), 它计算预测值与目标值之差的平方, 为梯度下降提供了平滑的优化路径. 训练目标通常是让网络输出的评估值与最终的对局结果(赢、输、和)尽可能一致.

终极技巧: Stockfish的量化方案

这是将整个网络从浮点数转换为高效整数运算的实用“菜谱”. 目标是尽可能使用int8(8位整数)和int16(16位整数)来完成所有计算.
其核心思想是为网络的不同部分设置不同的量化尺度(Scale).
  1. 特征转换器(输入层):
      • 目标: 将范围的激活值映射到.
      • 方法: 由于此层只有加法, 只需将原始的浮点权重和偏置都乘以127并四舍五入为整数即可.
  1. 线性层(以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. 除法通过高效的位移运算实现.
  1. Clipped ReLU层:
      • 操作非常简单. 之前是将值限制在, 量化后则是限制在.
通过这套精密的量化方案, NNUE可以在标准的CPU上实现惊人的计算速度, 成为现代棋类引擎中不可或缺的核心技术.
写真之道NJUSE-2025软件工程与计算II复习
Loading...