鱈魚NNUE文件
原文地址:official-stockfish/nnue-pytorch 的 nnue.md
前言
這個文件包含的內容:
- 技術內容
- NNUE及其原理的詳細描述
- 線性代數快速複習
- 輸入定義和因式分解
- 適用於NNUE網路的元件(層)
- 推理程式碼和最佳化
- 量化數學及其實現
- 幾乎可用於生產的最佳化程式碼
- PyTorch訓練器實現(+ 重要的CUDA核心)
- 架構考慮和歷史
這個文件不包含的內容:
- 網路訓練教程(請參閱wiki)
- 資料集、最佳化器、超參數
- 實驗結果日誌
基礎
什麼是NNUE?
NNUE(ƎUИИ Efficiently Updatable Neural Network)廣義上講是一種神經網路架構,它利用了在連續評估之間網路輸入的最小變化。它由Yu Nasu發明,用於將其整合到Motohiro Isozaki在2018年5月開發的Shogi軟體YaneuraOu中,後來在2019年6月由Hisayori Noda移植到國際象棋引擎Stockfish中,但也適用於許多其他棋類遊戲,甚至可能在其他領域中應用。NNUE遵循以下原則:
- 網路應具有相對較少的非零輸入。
- 在連續評估之間,輸入應儘可能少地改變。
- 網路應足夠簡單,以便在整數域中實現低精度推理。
遵循第一個原則意味著,當網路規模擴大時,輸入必須變得稀疏。當前最佳架構的輸入稀疏度約為0.1%。少量非零輸入對需要完整評估的情況下所需的時間設定了一個低上限。這是NNUE網路在可以非常快速評估的情況下仍然可以很大的主要原因。
遵循第二個原則(在遵循第一個原則的前提下)建立了一種高效更新網路(或至少是其昂貴部分)的方法,而不是重新評估整個網路。這利用了單一移動只會略微改變棋盤狀態的事實。這比第一個原則重要性低,對於實現來說是完全可選的,但在確實利用它的實現中,仍然可以提供可測量的改進。
遵循第三個原則可以在常見硬體上實現最大效能,並使模型特別適合低延遲CPU推理,這是傳統象棋引擎所必需的。
總體而言,NNUE原則也適用於昂貴的深度網路,但它們在快速淺層網路中表現出色,適合低延遲CPU推理,無需批處理和加速器。目標效能是每執行緒每秒進行數百萬次評估。這是一個極端的用例,需要極端的解決方案,最重要的是量化。
量化 101 及其重要性
量化是將神經網路模型的域從浮點數更改為整數的過程。NNUE 網路設計為在低精度整數域中快速評估,並能夠充分利用現代 CPU 的 int8/int16 效能。浮點數在實現最大引擎強度方面並不是一個選擇,因為它犧牲了過多的速度,卻只獲得了很少的準確性提升(儘管由於其簡單性,一些引擎使用浮點表示)。量化不可避免地會引入誤差,網路越深誤差累積越多,但對於相對淺層的 NNUE 網路來說,這種誤差是可以忽略的。量化將在本文件的後面詳細描述。在此之前,本文件將使用浮點數而不是整數,直到我們實際進行程式碼最佳化時這才會變得重要。插入這段內容的目的是讓讀者意識到 NNUE 的最終目標,因為這是塑造 NNUE 模型的最大因素,並決定了什麼是可能的,什麼是不可能的。
NNUE 中哪些層有用?
NNUE 依賴於可以在低精度環境中使用簡單算術實現的簡單層。這意味著線性層(全連線,基本上是矩陣乘法)和 ClippedReLU(clamp(0, 1))層特別適合它。池化層(乘法/平均/最大)或更復雜啟用函式(如 sigmoid)的近似也適用,但不常用。
通常,這種網路保持淺層(2-4 層),因為大部分知識儲存在第一層(利用輸入稀疏性保持效能),在第一層之後網路需要急劇減少其寬度(在網路後部更深部分的好處將被前面大層的影響所主導)以維持效能要求。
線性層
線性(全連線)層只是一個簡單的矩陣乘法。它可以高效實現,支援稀疏輸入,並提供良好的容量。它接收 in_features 值作為輸入,併產生 out_features 值作為輸出。操作為 y = Ax + b,其中:
x:大小為in_features的輸入列向量A:大小為(out_features, in_features)的權重矩陣b:大小為out_features的偏置列向量y:大小為out_features的輸出列向量
具有稀疏輸入的線性層
乘法 Ax 可以概念上簡化為「如果 x[i] 不為零,則取 A 的第 i 列,將其乘以 x[i] 並將其加到結果中」。現在應該很明顯,每當輸入的一個元素為零時,我們可以跳過處理權重矩陣的整行。這意味著我們只需處理 A 的與輸入向量中的非零值相對應的列。即使權重矩陣中可能有數萬個列,我們每個位置只關注其中的少數幾個!這就是為什麼第一層可以如此大的原因。
Clipped ReLU 層
這是基於普通 ReLU 的啟用函式,不同之處在於它在上下限都有界限。公式為 y = min(max(x, 0), 1)。
這一層的目的是為網路新增非線性。如果只有線性層,它們都可以被摺疊成一個,因為矩陣可以直接相乘。
理想情況下,ClippedReLU 將被 ReLU 替代,但激進的量化要求減少隱藏層輸入的動態範圍,因此將值限制在 1 對效能很重要。
Sigmoid
這是一個與 [clipped] ReLU 相反的平滑啟用函式。公式為 y = 1/(1+e^-kx),其中 k 是確定形狀「拉伸」程度的參數。
