重构论文结构,更新章节内容

This commit is contained in:
hisatri
2025-07-25 10:36:25 +08:00
parent 4410defbe5
commit 94a7371822
8 changed files with 170 additions and 74 deletions

View File

@@ -8,3 +8,4 @@
\end{abstract}
\textbf{关键词:} 2048游戏、深度强化学习、相对位置编码、self-attention、估值策略网络

View File

@@ -12,12 +12,9 @@
\item 难以处理不同位置间的长距离依赖关系
\end{itemize}
Self-attention机制的出现为解决这些问题提供了新的思路。通过引入2D相对位置编码我们可以
\begin{itemize}
\item 显式建模网格中任意两个位置间的关系
\item 捕获全局的状态信息
\item 学习位置无关的特征表示
\end{itemize}
Self-attention机制的出现为解决这些问题提供了新的思路。
通过引入2D相对位置编码我们可以在捕获全局状态信息的同时对棋盘任意两个位置的关系进行建模
同时学习到位置无关的特征表示。
\subsection{符号定义}
\begin{itemize}

View File

@@ -18,14 +18,10 @@
为了解决此问题,我们可以对棋盘状态进行对数变换。
令棋盘状态为一个矩阵 $K \in \mathbb{N}$
其中 $K_{i,j}$ 表示在 $(i,j)$ 位置的数字,空位记为 $0$
其中 $K_{i,j}$ 表示在 $(i,j)$ 位置的数字,空位记为 $1$ 2048游戏中不会有1出现同时用1做掩码无需额外判断
我们定义一个新的状态表示矩阵 $K'$,其元素 $K'_{i,j}$ 通过以下映射获得:
\begin{equation}
K'_{i,j} =
\begin{cases}
\log_2(K_{i,j}) & \text{if } K_{i,j} > 0 \\
0 & \text{if } K_{i,j} = 0
\end{cases}
K'_{i,j} = \log_2(K_{i,j})
\end{equation}
通过这种对数变换,我们可以将指数增长的数值尺度压缩到一个线性、紧凑的整数范围。
这种方法同样适用于任意边长的矩形棋盘。
@@ -119,4 +115,6 @@
\begin{equation}
V(N) = (\log_2(N) - 1) \cdot N
\end{equation}
\begin{equation}
V(N') = (N'-1) \cdot 2^{N'} \label{eq:log_value_formula}
\end{equation}

View File

@@ -0,0 +1,59 @@
\section{数值设计}
我们使用\texttt{SQLite}存储数据,这是因为\texttt{SQLite}在反序列化对象时的性能消耗可以接受,其相关生态也更加丰富和成熟。
\subsection{棋盘状态表示}
如前文所言,对于一个$H \times W$的2048游戏棋盘其方块数字的最大值$N_{\text{max}} = 2^{(H \times W + 1)}$
对数变化后的方块最大值即为$N'_{\text{max}} = H \times W + 1$
对于$10 \times 10$的棋盘,$N'_{\text{max}} = 101$\texttt{INT8}足以表示其范围。
因此,棋盘状态矩阵可以设计为一个\texttt{numpy.ndarray},数据类型为\texttt{np.int8}
\subsection{动作价值表示}
动作价值,实际上代表当前动作进行到终局时,棋盘的状态价值。
数字$N$的累计价值为 $V(N) = (\log_2(N) - 1) \cdot N$
对于一个$H \times W$的棋盘,其方块数字的最大值$N_{\text{max}} = 2^{(H \times W + 1)}$,则该方块的最大累计价值为:
\begin{equation}
V(N_{\text{max}}) = (\log_2(2^{(H \times W + 1)}) - 1) \cdot 2^{(H \times W + 1)} = (H \times W) \cdot 2^{(H \times W + 1)}
\end{equation}
整个棋盘的最大累计价值理论上为:
\begin{align}
V_{\text{total}} &= \sum_{i=1}^{H} \sum_{j=1}^{W} V(i,j) \\
&= V(N_{\text{max}}) + V(N_{\text{max}}/2) + V(N_{\text{max}}/4) + \dots + V(8) + V(4) \\
&\lesssim V(2 \times N_{\text{max}}) \\
&= (H \times W + 1) \cdot 2^{(H \times W + 2)}
\end{align}
对于一个$4 \times 4$的棋盘,其最大累计价值为$17 \times 2^{18} = 4,456,448$,虽然在\texttt{INT32}范围内,但当拓展到$10 \times 10$的棋盘时,其累计价值上界为$101 \times 2^{102} \approx 5.15 \times 10^{32}$,远超\texttt{INT64}的表示范围(约$9.22 \times 10^{18}$)。
然而,在实际游戏过程中,$V_{\text{total}}$的取值具有高度的稀疏性:
由于2048游戏的合并规则棋盘上的数字呈现指数级分布大部分位置的数值相对较小
只有少数位置达到较高数值。因此,实际的累计价值远小于理论上界。
同时,相同的分数差距,随着总分的增长,其在游戏能力上的区分度会逐渐降低。
通过对数变换$\log_2(V_{\text{total}})$,可以将数值范围压缩至$[0, \log_2(101 \times 2^{102})] = [0, 108.66]$
完全在\texttt{FP32}的有效精度范围内,且能保持足够的数值精度用于价值函数的区分。
因此,动作价值矩阵可以设计为一个\texttt{numpy.ndarray},数据类型为\texttt{np.float32},存储对数变换后的价值。
\subsection{最大价值(\texttt{MAX\_VALUE}}
这是一个64位高精度浮点数它用于准确地代表当前状态-策略对的价值,即这个策略所模拟到的最高分。
它不参与训练和推理,当传入的状态-策略对与现有的缓存中的某个状态重复时,这个值用于判断是否更新缓存中的价值向量。
我们认为具有更高终局分数的状态-策略对更有价值。
\subsection{棋盘尺寸}
这是一个16位整数用于代表棋盘的尺寸。其前8位二进制为代表棋盘的高度后8位代表棋盘的宽度。
例如,一个$7 \times 5$的棋盘,其尺寸编码为\texttt{0b0000011100000101},或是\texttt{0x0705}
\subsection{注释}
游戏策略代码从设计之初就考虑到使用对数0,1,2,3……$N'$表示棋盘上的数字,因此无需进行转换。
但是分数计算需要使用公式~\eqref{eq:log_value_formula}计算后取对数。

View File

@@ -1,60 +0,0 @@
\section{模型训练}
为了训练一个强大的估值决策模型我们采用蒙特卡洛树搜索MCTS生成高质量的训练数据并设计一个轻量级的残差卷积网络RNCNN进行初步学习与迭代。
\subsection{蒙特卡洛树搜索策略}
采用纯MCTSPure MCTS为初始模型生成训练数据。MCTS的每次迭代包含四个核心步骤以当前棋盘状态为根节点不断扩展搜索树。
\begin{itemize}
\item \textbf{选择 (Selection)}: 从根节点开始根据UCTUpper Confidence bounds applied to Trees公式递归选择子节点直到达到一个未完全扩展的叶子节点。UCT公式平衡了节点的探索Exploration与利用Exploitation
\[ \text{UCT} = \underset{i}{\arg\max} \left( \bar{v}_i + C \cdot \sqrt{\frac{\ln(N)}{n_i}} \right) \]
其中 $\bar{v}_i$ 是子节点 $i$ 的平均价值,$N$ 是父节点访问次数,$n_i$ 是子节点 $i$ 访问次数,$C$ 是探索常数。
\item \textbf{扩展 (Expansion)}: 当选择过程到达一个叶子节点 $L$ 时,如果该节点代表的局面不是终局,则为其创建一个或多个子节点,对应于从 $L$ 出发所有合法的移动。
\item \textbf{模拟 (Simulation)}: 从新扩展的子节点中选择一个开始进行模拟也称Rollout。在此阶段我们采用快速的随机策略例如在所有合法移动中随机选择一个持续进行游戏直到达到终局状态。终局的分数将作为本次模拟的价值。
\item \textbf{反向传播 (Backpropagation)}: 将模拟得到的终局分数 $v$ 从该叶子节点开始,沿着选择路径反向传播回根节点。路径上的每一个节点都会更新其访问次数 $n$ 和累积价值 $V$,并重新计算其平均价值 $\bar{v} = V/n$
\end{itemize}
\subsection{数据结构}
通过大量的MCTS模拟我们将叶子节点的信息转换为神经网络可学习的训练样本。每个样本包含
\begin{itemize}
\item 棋盘状态 $S$:一个 $H \times W$ 的矩阵,代表游戏局面。
\item 策略-价值对 $(\pi, v)$:其中 $\pi$ 是从状态 $S$ 出发的一个合法移动(例如:上、下、左、右),$v$ 是在该分支下通过后续模拟所能达到的最高分。
\end{itemize}
我们将 $(S, \pi)$ 作为键key当一次完整的MCTS模拟结束后解析所有扩展出的叶子节点。对于每个叶子节点 $(S, \pi)$,其价值 $v$ 等于它后续所有模拟游戏中的最高分。
\begin{itemize}
\item 如果一个键 $(S, \pi)$ 没有命中缓存,则写入 $(S, \pi) \rightarrow v$
\item 如果一个叶子节点的新分数大于缓存中的值,则更新 $(S, \pi) \rightarrow v$
\end{itemize}
在进行指定次数的MCTS模拟后导出所有缓存的 $(S, \pi) \rightarrow v$ 对,作为最终的训练数据集。
\subsection{残差卷积网络L0}
RNCNN\_L0 是一个极小的、用于快速推理和迭代的估值决策模型。其设计目标是在有限的计算资源下,学习到基本的局面评估能力。其结构定义如下:
\begin{itemize}
\item \textbf{输入层 (Input Layer)}:
棋盘状态 $S$(一个 $H \times W$ 的矩阵)首先进行特征化处理。我们将每个格子的值 $V$ 转换为 $C$ 个特征平面channel每个平面代表一个特定的瓦片值例如$V \rightarrow \log_2(V)$,然后进行独热编码)。输入张量的维度为 $(H, W, C)$
\item \textbf{卷积主干 (Convolutional Body)}:
输入张量首先通过一个卷积层,然后送入一个由 $N$ 个残差块Residual Block组成的序列。
\begin{itemize}
\item \textbf{初始卷积层}: 一个$3 \times 3$的卷积核输出64个特征图进行批量归一化Batch Normalization和ReLU激活。
\item \textbf{残差块}: 每个残差块包含两个卷积层。输入通过第一个 $3 \times 3$ 卷积层BN+ReLU再通过第二个 $3 \times 3$ 卷积层BN然后将结果与块的输入相加残差连接最后通过一个ReLU激活函数。所有卷积层保持64个通道数。
\end{itemize}
\item \textbf{输出头 (Output Head)}:
残差主干的输出特征图被送入一个最终的输出模块,该模块直接预测四个动作的价值。
\begin{itemize}
\item 一个 $1 \times 1$ 的卷积层将通道数从64降至16进行BN和ReLU激活。
\item 将特征图展平Flatten成一维向量。
\item 一个全连接层Fully Connected Layer将向量映射到4个输出神经元分别对应四个移动方向上、下、左、右的预测价值。
\end{itemize}
\end{itemize}
模型的损失函数采用均方误差Mean Squared Error计算网络预测的四通道价值与MCTS生成数据中对应动作的价值 $v$ 之间的差距。
\subsection{模型初始化}
我们计划在3$\times$3的小棋盘上依靠纯蒙特卡洛树的快速搜索能力生成大量的初始数据。小棋盘状态空间较小MCTS能更快地收敛到有意义的策略为模型提供高质量的初始训练样本。
\subsection{L0迭代}
在纯蒙特卡洛生成的数据上学习到的大量初始数据将提供给RNCNN\_L0进行监督学习训练。训练完成的L0模型可以反过来指导MCTS中的选择和模拟阶段形成一个自我博弈self-play的增强回路不断迭代优化模型性能。

View File

@@ -0,0 +1,99 @@
\section{模型训练}
为了训练一个强大的估值决策模型我们采用蒙特卡洛树搜索MCTS生成高质量的训练数据
并设计一个轻量级的残差卷积网络RNCNN进行初步学习与迭代。
\subsection{蒙特卡洛树搜索策略}
我们采用纯蒙特卡洛树搜索Pure MCTS为初始模型生成训练数据。
MCTS的每次迭代包含四个核心步骤以当前棋盘状态为根节点不断扩展搜索树。
\begin{itemize}
\item \textbf{选择 (Selection)}:
%% 修改点将UCT公式本身与选择策略分开表述更严谨
从根节点开始根据UCTUpper Confidence bounds applied to Trees选择策略递归选择子节点
直到达到一个未完全扩展的叶子节点。该策略会选择具有最大UCT值的子节点 $i$。节点 $i$ 的UCT值计算公式为
\[ \text{UCT}_i = \bar{v}_i + C \cdot \sqrt{\frac{\ln(N)}{n_i}} \]
其中,$\bar{v}_i$ 是子节点 $i$ 的平均回报mean return$N$ 是其父节点的访问次数,$n_i$ 是子节点 $i$ 的访问次数,
$C$ 是平衡探索与利用的常数。$i$ 遍历当前节点所有已发现的子节点。
\item \textbf{扩展 (Expansion)}: 当选择过程到达一个叶子节点 $L$ 时,若该节点所代表的局面非终局,
则为其创建一个或多个子节点,对应于从状态 $L$ 出发的所有合法走法。
\item \textbf{模拟 (Simulation)}:
从新扩展的子节点中选择一个开始进行快速走子Rollout。在此阶段我们采用一个快速的默认策略
(例如,在所有合法移动中均匀随机选择)进行游戏,直至达到终局状态。终局的收益(如 胜/负/平 对应 +1/-1/0或游戏得分
被记录为本次模拟的回报 $z$
\item \textbf{反向传播 (Backpropagation)}:
将模拟得到的回报 $z$ 从该叶子节点开始,沿选择路径反向传播至根节点。
路径上的每个节点 $j$ 都会更新其统计量:访问次数 $n_j \leftarrow n_j + 1$
总回报 $V_j \leftarrow V_j + z$。其平均回报也相应更新为 $\bar{v}_j = V_j / n_j$
\end{itemize}
\subsection{数据结构}
通过大量的MCTS模拟我们将每个访问过的状态及其MCTS分析结果转化为神经网络可学习的训练样本。
我们为每个经过充分模拟的状态 $S$ 生成一个训练目标。
该目标是一个动作价值向量 $\mathbf{\pi}(S) \in \mathbb{R}^{|\mathcal{A}|}$
其中 $\mathcal{A} = \{\text{上, 下, 左, 右}\}$ 是动作空间。
向量的每个分量 $\pi_a(S)$ 代表在状态 $S$ 下,
执行动作 $a$MCTS估算出的期望回报。
\begin{itemize}
\item 对于不合法的移动或在MCTS中未被探索到的动作 $a$
我们将其价值设为一个特殊的掩码值$\pi_a = -1$,并在计算损失函数时忽略这些项。
\item 所有状态 $S$ 及其对应的动作价值向量 $\mathbf{\pi}(S)$ 被存储在一个持久化的缓存中。
该缓存结构为 $\text{Cache}: \mathcal{S} \rightarrow \mathbb{R}^{|\mathcal{A}|}$
其中 $\mathcal{S}$ 是所有遇到过的状态集合。为了高效检索,我们使用状态的哈希值作为键。
\end{itemize}
缓存的更新策略如下:
\begin{itemize}
\item \textbf{初始写入:} 若状态 $S$ 不在缓存中,则在完成对 $S$ 的MCTS模拟后将映射 $S \mapsto \mathbf{\pi}(S)$ 添加到缓存中。
\item \textbf{缓存更新:}
我们定义状态 $S$ 的(估计)价值为其最优动作的价值,即 $v_{\text{state}}(S) = \max_{a \in \mathcal{A}} \pi_a(S)$
若状态 $S$ 已在缓存中但在新一轮MCTS模拟后得到了新的价值向量 $\mathbf{\pi}'(S)$,且其状态价值 $v'_{\text{state}}(S) > v_{\text{state}}(S)$,则我们将缓存中的条目更新为 $S \mapsto \mathbf{\pi}'(S)$。此策略旨在保留能导向更优结果的搜索信息。
\end{itemize}
最终,我们将缓存中的数据转换为一个训练集 $\mathcal{D}$。对于缓存中的每一个 $(S, \mathbf{\pi}(S))$ 对,
我们将其展开为多个独立的样本,形成训练集 $\mathcal{D} = \{(S, a, \pi_a(S)) \mid \forall S \in \text{Cache}, \forall a \in \text{valid\_actions}(S)\}$
\subsection{残差卷积网络L0}
RNCNN\_L0 是一个为快速推理和迭代设计的轻量级估值决策模型。其结构如下:
\begin{itemize}
\item \textbf{输入层 (Input Layer)}:
棋盘状态 $S \in \mathbb{R}^{H \times W}$ 首先经过特征化处理。
将每个非空格子的值 $V$ 通过对数函数(如 $k = \log_2(V)$)映射为整数索引,
再进行独热编码,形成 $C$ 个特征平面channel。最终的输入张量维度为 $(H, W, C)$,其中 $C$ 是编码后的特征总数。
\item \textbf{卷积主干 (Convolutional Body)}:
输入张量经过以下处理:
\begin{itemize}
\item \textbf{初始卷积层}: 一个 $3 \times 3$ 的填充卷积层保持空间维度不变后接批量归一化Batch Normalization和ReLU激活函数。
\item \textbf{残差块序列}: 两个连续的残差块,每个残差块包含两个卷积层。输入首先通过一个 $3 \times 3$ 卷积层BN+ReLU
再通过第二个 $3 \times 3$ 卷积层BN然后将结果与块的输入进行逐元素相加残差连接最后通过一个ReLU激活函数。
\end{itemize}
\item \textbf{输出头 (Output Head)}:
卷积主干输出的特征图被送入一个专门设计的输出模块:
\begin{itemize}
\item 四个并行的 $1 \times 1$ 卷积层,每个对应一个方向(上、下、左、右),将特征图扩展为 $(H, W, 4)$ 的张量。
\item 对每个方向通道分别应用全局最大池化Global Max Pooling得到四个标量值分别对应四个方向的动作价值。
\end{itemize}
\end{itemize}
模型的损失函数采用均方误差Mean Squared Error, MSE
对于训练集中的每个样本 $(S, a, \pi_a(S))$,损失函数计算网络对状态 $S$ 预测的动作 $a$ 的价值与MCTS提供的目标价值 $\pi_a(S)$ 之间的差距。
\subsection{模型初始化}
我们在 $3 \times 3$ 的小棋盘上依靠纯MCTS的快速搜索能力生成大量的初始训练数据。
通过约200,000次完整的self-play对局每局平均约50步我们累积了约1000万个棋盘状态对作为训练样本。
由于小棋盘的状态空间有限MCTS能够更快地收敛到高质量的策略从而为模型提供优质的初始训练样本。
\subsection{L0迭代与迁移}
RNCNN\_L0模型在纯MCTS生成的初始数据上进行监督学习训练。
训练完成的L0模型可以反过来指导MCTS的选择与模拟阶段以替代纯随机的Rollout策略
从而形成一个自我对弈self-play的强化学习回路持续迭代并提升模型性能。
L0模型的轻量级设计使其能够直接迁移到 $4 \times 4$$5 \times 5$ 的棋盘上运行,
为更大规模棋盘上的MCTS提供基础的估值和策略指导。由于其架构中使用了填充卷积和全局池化操作
模型可以自然地适应不同尺寸的输入,无需额外的结构调整。