CNN 与围棋的结合
当 DeepMind 选择用**卷积神经网络(CNN)**来处理围棋时,这是一个天才的设计决策。
CNN 原本是为图像识别设计的。为什么它也适合围棋?这篇文章将深入探讨 CNN 的运作原理,以及它与围棋的完美契合。
为什么 CNN 适合棋盘?
棋盘是「图像」
从某种角度看,19×19 的围棋棋盘就是一张图像:
| 图像 | 围棋棋盘 |
|---|---|
| 像素 | 交叉点 |
| RGB 通道 | 特征平面(黑、白、空...) |
| 224×224 | 19×19 |
| 辨识猫狗 | 判断好棋坏棋 |
这个类比并非偶然。CNN 擅长图像的原因,也让它擅长棋盘。
三个关键特性
CNN 有三个特性,让它特别适合棋盘类型的数据:
1. 局部连接(Local Connectivity)
CNN 的卷积核只看局部区域,这与围棋的特性完美匹配:
图像识别 vs 围棋局部特征比较:
| 图像识别 | 围棋 |
|---|---|
| 猫耳朵是局部特征 | 「眼」是局部棋形 |
| 不需要看整张图 | 不需要看整个棋盘 |
3×3 区域示例(眼位):
| ○ | ● | ○ |
| ● | · | ● |
| ○ | ● | ○ |
很多围棋概念都是「局部」的:
- 眼:2×2 或 3×3 的区域
- 叫吃:3×3 的区域
- 接、断:2×2 的区域
2. 权重共享(Weight Sharing)
同一个卷积核会扫描整个棋盘,这意味着:
棋盘左上角的「眼」和右下角的「眼」,用同样的方式识别
这是合理的——围棋规则不因位置而异(边角例外,但可以用边角特征平面处理)。
权重共享也大幅减少了参数量:
| 方法 | 参数量 |
|---|---|
| 全连接网络 | 361 × 361 × 通道数 = 数千万 |
| CNN | 3 × 3 × 通道数 × 滤波器数 = 数百万 |
3. 平移等变性(Translation Equivariance)
如果输入平移,CNN 的输出也会相应平移:
输入: 输出 (高概率区域):
A B C D E A B C D E
1 . . . . . 1 . . . . .
2 . ● . . . → 2 . * . . .
3 . . . . . 3 . . . . .
输入平移后: 输出也平移:
A B C D E A B C D E
1 . . . . . 1 . . . . .
2 . . . . . → 2 . . . . .
3 . . ● . . 3 . . * . .
这对围棋很重要:相同的局部棋形,无论出现在棋盘哪里,应该有类似的评估。
卷积运算
基本原理
卷积运算是 CNN 的核心。它是一种「滑动窗口」操作:
计算过程(以中心点为例):
输出[2,2] = 1×1 + 1×0 + 1×1 +
1×0 + 1×1 + 1×0 +
1×1 + 1×0 + 1×1
= 1 + 0 + 1 + 0 + 1 + 0 + 1 + 0 + 1
= 5
多通道卷积
当输入有多个通道(如 48 个特征平面)时,卷积核也变成 3D:
每个卷积核会跨所有输入通道计算,产生一个输出通道。
多个滤波器
AlphaGo 使用 192 个滤波器,每个滤波器学习不同的特征:
每个滤波器可能学到不同的棋形:
- 滤波器 1:眼位检测
- 滤波器 2:断点检测
- 滤波器 3:连接检测
- ...
- 滤波器 192:某种复杂模式
感受野
什么是感受野?
**感受野(Receptive Field)**是指输出的一个位置,受到输入的哪些位置影响。
单层卷积
使用 3×3 卷积核时,输出的每个位置只受输入 3×3 区域影响:
多层卷积
堆叠多层卷积后,感受野会扩大:
| 层数 | 感受野 | 计算 |
|---|---|---|
| 1 | 3×3 | 3 |
| 2 | 5×5 | 3 + (3-1) = 5 |
| 3 | 7×7 | 5 + (3-1) = 7 |
| ... | ... | ... |
| 12 | 25×25 | 3 + 11×2 = 25 |
AlphaGo 的 12 层卷积给出 25×25 的感受野,已经超过 19×19 的棋盘!
这意味着:
- 输出的每个位置都能「看到」整个棋盘
- 但「看」的方式不同:近处细节清楚,远处概括
- 这与人类棋手的思维方式类似
感受野与围棋
感受野的概念解释了为什么 AlphaGo 能处理「全局」问题:
局部问题(3×3 感受野): 全局问题(25×25 感受野):
- 这里有眼吗? - 这块棋有眼位吗?
- 可以叫吃吗? - 征子有利吗?
- 能接上吗? - 全局形势如何?
浅层处理局部特征,深层处理全局特征。
局部 vs 全局特征
CNN 的层次结构
CNN 自然形成层次结构:
输入层: 黑子、白子、空点
↓
浅层 (1-3): 眼、接、断、叫吃
↓
中层 (4-8): 棋形、活棋、死棋
↓
深层 (9-12): 势力、厚薄、大场
↓
输出层: 落子概率 / 胜率
这与人类学习围棋的过程惊人地相似:
- 先学规则(哪里有子)
- 再学战术(如何吃子)
- 然后学棋形(什么是好形)
- 最后学大局观(全局判断)
可视化隐藏层
研究人员发现,CNN 的隐藏层确实学到了有意义的特征:
浅层滤波器
滤波器 A(眼位检测):
| + | - | + |
| - | + | - |
| + | - | + |
滤波器 B(叫吃检测):
| + | + | + |
| + | - | - |
| + | + | + |
深层滤波器
深层的滤波器更抽象,难以直接解释,但它们捕捉了复杂的棋形模式。
激活函数的选择
ReLU:简单而有效
AlphaGo 在所有卷积层后使用 ReLU(Rectified Linear Unit):
def relu(x):
return max(0, x)
ReLU 函数特性:
- 当输入 x ≤ 0 时,输出为 0(水平线)
- 当输入 x > 0 时,输出等于 x(斜率为 1 的直线)
- 图形呈「折线」状:在原点左侧为零,右侧为线性递增
为什么不用其他函数?
| 激活函数 | 公式 | 优点 | 缺点 |
|---|---|---|---|
| ReLU | max(0, x) | 计算快、梯度好 | 负值死亡 |
| Sigmoid | 1/(1+e^-x) | 输出有界 | 梯度消失 |
| Tanh | (e^x-e^-x)/(e^x+e^-x) | 零中心 | 梯度消失 |
| LeakyReLU | max(0.01x, x) | 解决死亡问题 | 多一个超参数 |
对于深度网络,ReLU 的优势明显:
- 计算简单:只是比较和取最大值
- 梯度不消失:正区间梯度恒为 1
- 稀疏激活:很多神经元输出 0,提高效率
ReLU 在围棋中的含义
ReLU 的稀疏性在围棋中有有趣的解释:
某个滤波器检测「断点」:
- 有断点 → 正值输出(激活)
- 无断点 → 零输出(不激活)
这就像棋手只关注「有事」的位置
批次正规化
什么是批次正规化?
**批次正规化(Batch Normalization)**是一种技术,让每层的输出维持稳定的分布:
def batch_norm(x, gamma, beta):
# 计算批次的均值和标准差
mean = x.mean(axis=0)
std = x.std(axis=0)
# 正规化
x_norm = (x - mean) / (std + 1e-8)
# 缩放和平移
return gamma * x_norm + beta
为什么需要?
内部协变量偏移
当网络训练时,每层的输入分布会随着前面层的权重变化而改变。这被称为「内部协变量偏移」:
第一层权重更新 → 第一层输出分布改变
↓
第二层输入分布改变 → 第二层需要重新适应
↓
... (传递下去)
批次正规化通过强制每层输入有固定的分布(均值 0,标准差 1),来稳定训练。
在 AlphaGo 中的应用
AlphaGo 在每个卷积层后、激活函数前使用批次正规化:
Conv → BatchNorm → ReLU → Conv → BatchNorm → ReLU → ...
好处:
- 训练更快:可以使用更大的学习率
- 更稳定:减少对初始化的敏感性
- 正则化效果:有轻微的 dropout 效果
推理时的处理
训练时,使用当前批次的统计量。推理时,使用整个训练集的统计量(移动平均):
# 训练时
mean = batch_mean
var = batch_var
# 推理时
mean = running_mean # 训练期间累积的均值
var = running_var # 训练期间累积的方差
AlphaGo 的具体配置
完整架构
输入: 19×19×48
第 1 层:
Conv2D(5×5, 192 filters, padding='same')
BatchNorm
ReLU
输出: 19×19×192
第 2-12 层 (共 11 层):
Conv2D(3×3, 192 filters, padding='same')
BatchNorm
ReLU
输出: 19×19×192
输出层 (Policy):
Conv2D(1×1, 1 filter)
Flatten
Softmax
输出: 361 维概率
输出层 (Value):
Conv2D(1×1, 1 filter)
Flatten
Dense(256)
ReLU
Dense(1)
Tanh
输出: 单一数值
参数配置
| 参数 | 数值 | 说明 |
|---|---|---|
| 输入通道 | 48 | 特征平面数 |
| 滤波器数 | 192 | 每层的通道数 |
| 卷积核大小 | 3×3(第一层 5×5) | 感受野 |
| 层数 | 13(含输出层) | 深度 |
| 激活函数 | ReLU | 非线性 |
| 正规化 | BatchNorm | 稳定训练 |
PyTorch 实现
import torch
import torch.nn as nn
class AlphaGoCNN(nn.Module):
def __init__(self, input_channels=48, num_filters=192, num_layers=12):
super().__init__()
# 第一层(5×5 卷积)
self.conv1 = nn.Sequential(
nn.Conv2d(input_channels, num_filters, kernel_size=5, padding=2),
nn.BatchNorm2d(num_filters),
nn.ReLU(inplace=True)
)
# 中间层(3×3 卷积)
self.conv_layers = nn.Sequential(*[
nn.Sequential(
nn.Conv2d(num_filters, num_filters, kernel_size=3, padding=1),
nn.BatchNorm2d(num_filters),
nn.ReLU(inplace=True)
)
for _ in range(num_layers - 1)
])
# Policy 输出头
self.policy_head = nn.Sequential(
nn.Conv2d(num_filters, 1, kernel_size=1),
nn.Flatten(),
nn.Softmax(dim=1)
)
# Value 输出头
self.value_head = nn.Sequential(
nn.Conv2d(num_filters, 1, kernel_size=1),
nn.Flatten(),
nn.Linear(361, 256),
nn.ReLU(inplace=True),
nn.Linear(256, 1),
nn.Tanh()
)
def forward(self, x):
# 共享特征提取
x = self.conv1(x)
x = self.conv_layers(x)
# 分头输出
policy = self.policy_head(x)
value = self.value_head(x)
return policy, value
与其他架构的比较
全连接网络
如果用全连接网络处理围棋:
| 特性 | 全连接 | CNN |
|---|---|---|
| 参数量 | 极大(数亿) | 较小(数百万) |
| 位置不变性 | 无 | 有 |
| 局部特征 | 难学 | 自然捕捉 |
| 训练效率 | 低 | 高 |
全连接网络无法利用棋盘的空间结构,效率极低。
循环神经网络(RNN)
RNN 适合序列数据(如棋局历史),但:
| 特性 | RNN | CNN |
|---|---|---|
| 空间处理 | 弱 | 强 |
| 序列处理 | 强 | 弱(需要历史平面) |
| 并行化 | 难 | 易 |
| 长距离依赖 | 需要 LSTM | 深层即可 |
AlphaGo 选择 CNN + 历史平面,而非 CNN + RNN。
残差网络(ResNet)
AlphaGo Zero 升级为 ResNet:
普通 CNN: ResNet:
x x
↓ ↓
Conv Conv
↓ ↓
ReLU ReLU
↓ ↓
Conv Conv
↓ ↓
y y + x ← 残差连接
残差连接让梯度更容易流动,可以训练更深的网络(40 层 vs 12 层)。
详见 双头网络与残差网络。
可视化理解
卷积过程
输入棋盘(简化为 5×5):
| A | B | C | D | E | |
|---|---|---|---|---|---|
| 1 | · | · | · | · | · |
| 2 | · | ● | · | · | · |
| 3 | · | · | ○ | · | · |
| 4 | · | · | · | ● | · |
| 5 | · | · | · | · | · |
滤波器(3×3,检测「十字形」):
| 0 | 1 | 0 |
| 1 | 1 | 1 |
| 0 | 1 | 0 |
卷积输出(中心有强响应,十字形匹配):
| A | B | C | D | E | |
|---|---|---|---|---|---|
| 1 | 0 | 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 | 0 | 0 |
| 3 | 0 | 0 | 1 | 0 | 0 |
| 4 | 0 | 0 | 0 | 0 | 0 |
| 5 | 0 | 0 | 0 | 0 | 0 |
多层特征
第 1 层输出(192 个通道中的 4 个):
通道 1(眼位):
| 0 | 0 | 0 | 0 |
| 0 | 0.9 | 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
通道 2(边线):
| 0.8 | 0 | 0 | 0 |
| 0.8 | 0 | 0 | 0 |
| 0.8 | 0 | 0 | 0 |
| 0.8 | 0 | 0 | 0 |
通道 3(断点):
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0.7 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
通道 4(连接):
| 0 | 0 | 0 | 0 |
| 0 | 0.5 | 0 | 0 |
| 0 | 0.8 | 0 | 0 |
| 0 | 0.5 | 0 | 0 |
这些特征在更深层会被组合成更复杂的概念...
动画对应
本文涉及的核心概念与动画编号:
| 编号 | 概念 | 物理/数学对应 |
|---|---|---|
| 🎬 D9 | 卷积运算 | 滤波器响应 |
| 🎬 D10 | 感受野 | 局部→全局 |
| 🎬 D11 | 批次正规化 | 分布稳定 |
| 🎬 D1 | 多通道输入 | 张量运算 |
延伸阅读
关键要点
- CNN 天然适合棋盘:局部连接、权重共享、平移等变性
- 卷积提取局部特征:3×3 区域的模式识别
- 深层网络获得全局视野:12 层 → 25×25 感受野
- ReLU 快速有效:简单的非线性激活
- BatchNorm 稳定训练:标准化每层输出
CNN 让 AlphaGo 能够「看」棋盘——就像人类用眼睛看图像一样自然。
参考资料
- LeCun, Y., Bengio, Y., & Hinton, G. (2015). "Deep learning." Nature, 521, 436-444.
- He, K., et al. (2015). "Deep Residual Learning for Image Recognition." CVPR.
- Ioffe, S., & Szegedy, C. (2015). "Batch Normalization: Accelerating Deep Network Training." ICML.
- Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). "ImageNet Classification with Deep Convolutional Neural Networks." NeurIPS.