📰 来源: 博客园
课程:《Python程序设计》
班级: 2534
姓名: 吕俊孜
学号:20253431
实验教师:王志强
实验日期:2026年5月25日
必修/选修: 公选课
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
本来是写了一个关于word文档格式判断的小工具,想着有时候还能用到,但是后来发现好像并不切题……就很坏了
ps:如果您乐意看看,这里有链接
所以在思考了很久以后,我决定写一个简单的贪吃蛇小游戏!
但是光有这么个小游戏有什么意思,现在可是大ai时代,那我就打算训练一个简单的ai来玩这个小游戏,感觉有种电子宠物的乐趣了对吧
我们不难想到,要完成这样一个实验,在代码阶段,我们只需要写一个简单的贪吃蛇和一个ai训练模型,接下来就只需要等ai训练完成就好了。
所以我们的目标就一目了然了。
经过查阅资料,使用DQN作为ai的模型似乎是不错的选择,由于在这方面网上有一些先例,所以我们就可以借鉴一下前人的思路
以下是对DQN的介绍,如果您了解DQN,那么请移步2.3
DQN(Deep Q-Network)大概就是用一个函数Q(s,a)来反馈自己当前的动作有多好(奖励),通过一个很复杂的公式然后慢慢优化自己的算法
有没有感觉很像在训狗?狗狗乖的时候就给他点吃的(奖励),让他能优化自己的动作
由于我们的状态变量维度比较多,所以我们需要创建一个神经网络来近似Q函数
我们会把拿到奖励的经历放在一起,训练时抽取,来让他反复学习为什么这里得到了奖励
我们复制一份神经网络,这一份当做目标网络,当一个“靶子”,让神经网络有一个训练的目标
接下来我们进行一步误差处理:
Loss = [Q(s, a) - (r + γ × max Q_target(s', a'))]²
这一步反正就是在这里,我也不知道为什么是这样的公式,反正就是处理误差的一步
这样就可以进行迭代了,很简单吧(?)
总之接下来是我们具体的操作了
贪吃蛇作为一个老牌游戏,写起来虽然不简单,但也不是特别复杂
我们创建一个snake_game.py文件,准备开始写代码
首先,为了让我们最终能看得到ai训练的结果,我们一定需要可视化,那么我们选用最简单的tk来完成
由于贪吃蛇的代码不是我们的重点,在此我大概介绍一下写的过程:
框架:首先我们要先想好加入的库,因为是一个比较简单的游戏,我只选择了pygame(提供游戏基础),random(随机位置生成食物)和sys(提供退出键)这三个库
然后我们设定常量:其中包括屏幕设置,颜色,方向和游戏速度,这些都是我们写在函数之前的
接下来就该重头了,函数部分:
函数:首先要明确,我们不把函数直接写在文件中,而是选择创建一个类,将其他函数写为这个类的方法,这样会让后续ai训练的代码能直接创建实例来调用我们写的游戏函数,非常方便
其次,我们要写的函数大概有:
初始化函数init
重置游戏函数reset
生成食物函数(顺便附带游戏结算)generate_food
改变方向函数change_direction
执行游戏函数step
绘制地图,蛇等等可视化元素的函数(好多好多)
渲染函数render
游玩函数human_play
具体代码请点击这里~
ps:这部分代码有ai生成的部分
接下来才是本次实验的重头戏:训练ai的代码:
我们导入了一堆模块,抛去我们常用的pygame,random,os,numpy,datetime,csv以外,还有:
collections.deque——双端队列,可以自动淘汰新数据,用来充当经验池
matplotlib.pyplot——用于绘图,生成训练曲线(这个是问了ai,说这样结果会比较直观)
torch.PyTorch——深度学习框架,提供张量运算和自动求导
ps:张量就是三维及以上的数字容器
snake_game.py——引入游戏的代码,方便让ai直接进行游玩
这一系列模块,让我们能够实现ai的训练
接下来,我们要配置一系列参数,包括:
STATE_DIM = 11 # 状态维度
ACTION_DIM = 4 # 动作维度
MEMORY_SIZE = 50000 # 经验池大小
BATCH_SIZE = 64 # 批次大小
GAMMA = 0.9 # 折扣因子
EPSILON_START = 1.0 # 初始探索率
EPSILON_END = 0.01 # 最终探索率
EPSILON_DECAY = 200000 # 探索率衰减步数
LEARNING_RATE = 0.0003 # 学习率
TARGET_UPDATE = 2000 # 目标网络更新步数
TRAIN_STEPS = 500000 # 总训练步数
SAVE_INTERVAL = 50000 # 模型保存间隔
FRAME_SKIP = 2 # 帧跳过
LOG_INTERVAL = 100 # 数据记录间隔
STATE_DIM = 11
我们设计了十一个状态维度,分别是:
食物与蛇头的相对位置,2个维度
各个方向是否有危险,4个维度
当前移动方向,4个维度
蛇身长度占最大长度的比例,1个维度
这十一个状态维度将作为神经网络的输入
ACTION_DIM = 4
动作维度作为神经网络的输出,反映了ai模型对于当前状况的反应,分别对应了四个方向
MEMORY_SIZE = 50000
经验池大小设定为50000条数据,超出时自动删去最旧的
BATCH_SIZE = 64
每一批次随机抽取64条经验
GAMMA = 0.9
折扣因子决定了ai模型更关注短期还是长期,我们设定为0.9,让ai模型较少地考虑长期奖励。这是因为贪吃蛇本身是一个较为看重短期奖励的游戏,如果是棋牌博弈类的游戏,则更看重长期的奖励
类似于:我们在玩贪吃蛇的时候,不需要考虑一百步后会发生什么,但是下棋、打牌的时候就需要更长远的考虑
而我们并没有一杆子把长期奖励的权重拉的太低,太低的话会导致ai模型完全靠当前的反应操作,而没有了我们想要的“智能”效果
EPSILON_START
开始时 100% 随机探索,让ai模型尝试各种动作,快速积累各种经验
EPSILON_END
最终只剩 1% 随机动作,其余靠网络决策
EPSILON_DECAY
在 20 万步内从 1.0 线性衰减到 0.01
LEARNING_RATE
学习率 0.0003,较小值让训练更稳定
TARGET_UPDATE
每 2000 步同步一次目标网络
TRAIN_STEPS
总共训练 50 万步
FRAME_SKIP
每 2 帧执行一次动作,加快训练
我们定义了一个简单的神经网络,作为我们的策略网络和目标网络:
class DQN(nn.Module):
def __init__(self, state_dim=11, a
🔗 原文链接: 点击阅读原文
文章评论