Bootstrap

用神经网络自动玩游戏

用神经网络玩游戏
CartPole是OpenAI gym中的一个游戏测试,车上顶着一个自由摆动的杆子,实现杆子的平衡,杆子每次倒向一端车就开始移动让杆子保持动态直立的状态.
 


游戏地址:https://gymnasium.farama.org/env ... _control/cart_pole/

一、搭建游戏运行环境
 

pip install swig
pip install gymnasium[box2d]



CartPole 环境内置在 gym 中,直接安装 gym 即可。其环境 id 是CartPole-v0 。Gym是一个研究和开发强化学习相关算法的仿真平台。简单来说OpenAI Gym提供了许多问题和环境(或游戏)的接口,而用户无需过多了解游戏的内部实现,通过简单地调用就可以用来测试和仿真。

pip install gym



启动游戏完整代码:

import gym

# 创建 CartPole 环境
env = gym.make('CartPole-v1', render_mode="human")  # 使用新版本时,需要指定 render_mode

# 重置环境,准备开始游戏
observation, info = env.reset()

for _ in range(1000):
    # 随机选择一个动作(0或1)
    action = env.action_space.sample()

    # 应用动作到环境中,返回新的状态、奖励、完成标志和其他信息
    observation, reward, done, truncated, info = env.step(action)

    # 如果游戏结束或被截断,重置环境
    if done or truncated:
        observation, info = env.reset()

# 关闭游戏
env.close()


可以看到游戏在自动运行。

二、手动控制
增加键盘操作:
 

# 获取键盘按键状态
keys = pygame.key.get_pressed()

# 默认动作为随机生成 0 或 1,除非检测到按键输入
if not keys[K_LEFT] and not keys[K_RIGHT]:
    action = random.choice([0, 1])  # 随机选择 0 或 1

# 根据按键改变动作
if keys[K_LEFT]:
    print("Left arrow pressed")
    action = 0  # 向左移动
elif keys[K_RIGHT]:
    print("Right arrow pressed")
    action = 1  # 向右移动




完整代码如下:

import gym
import pygame
from pygame.locals import K_LEFT, K_RIGHT, QUIT
import time
import random  # 用于生成随机数

# 初始化 pygame
pygame.init()

# 设置帧率
FPS = 60
clock = pygame.time.Clock()

# 创建 CartPole 环境,使用 Gym 自带的渲染模式
env = gym.make('CartPole-v1', render_mode="human")

# 重置环境,准备开始游戏
observation, info = env.reset()

# 初始化游戏的运行标志
running = True

# 操作次数
action_count = 0

# 记录游戏开始的时间
start_time = time.time()

# 游戏循环
while running:
    # 处理事件队列,检测关闭窗口操作
    for event in pygame.event.get():
        if event.type == QUIT:  # 退出事件
            running = False
    time.sleep(0.3)

    # 获取键盘按键状态
    keys = pygame.key.get_pressed()

    # 默认动作为随机生成 0 或 1,除非检测到按键输入
    if not keys[K_LEFT] and not keys[K_RIGHT]:
        action = random.choice([0, 1])  # 随机选择 0 或 1

    # 根据按键改变动作
    if keys[K_LEFT]:
        print("Left arrow pressed")
        action = 0  # 向左移动
    elif keys[K_RIGHT]:
        print("Right arrow pressed")
        action = 1  # 向右移动

    # 执行动作并更新环境状态
    observation, reward, done, truncated, info = env.step(action)

    # 每执行一次动作,增加操作次数
    action_count += 1

    # 渲染环境,减少渲染频率(每隔 10 帧渲染一次)
    if action_count % 10 == 0:
        env.render()

    # 检查游戏是否结束
    if done or truncated:
        # 记录游戏结束的时间
        end_time = time.time()

        # 计算游戏持续的时间(秒)
        elapsed_time = end_time - start_time

        # 打印游戏信息
        print(f"游戏结束!总共操作了 {action_count} 次,持续时间为 {elapsed_time:.2f} 秒")

        # 重置环境
        observation, info = env.reset()

        # 重置计数器和时间
        action_count = 0
        start_time = time.time()

    # 控制帧率,确保游戏速度合适
    clock.tick(FPS)  # 设置帧率为 60 帧/秒

# 关闭游戏环境和 pygame
env.close()
pygame.quit()

最好成绩成功控制了57次。
 




三、用电脑控制

#电脑自嗨
if observation[2] <= 0:
    action = 0
else:
    action = 1


电脑居然控制了59步。

四、用神经网络控制

1.训练神经网络
 

pip install torch
import gym
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import random
from collections import deque

# 设置设备(如果有GPU则使用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 创建 CartPole 环境
env = gym.make('CartPole-v1')

# 超参数
GAMMA = 0.99  # 折扣因子
EPSILON_START = 1.0  # 初始探索率
EPSILON_END = 0.01  # 最低探索率
EPSILON_DECAY = 0.995  # 探索率衰减
LEARNING_RATE = 0.001  # 学习率
MEMORY_SIZE = 10000  # 经验回放的容量
BATCH_SIZE = 64  # 批量大小
TARGET_UPDATE = 10  # 每隔多少步更新目标网络
TRAIN_TARGET_REWARD = 5000  # 训练停止目标:游戏持续达到 5000 步

# DQN 网络结构
class DQN(nn.Module):
    def __init__(self, state_size, action_size):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_size, 24)
        self.fc2 = nn.Linear(24, 24)
        self.fc3 = nn.Linear(24, action_size)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

# 经验回放池
class ReplayMemory:
    def __init__(self, capacity):
        self.memory = deque(maxlen=capacity)

    def push(self, experience):
        self.memory.append(experience)

    def sample(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)

# 选择动作的epsilon-greedy策略
def select_action(state, policy_net, epsilon, n_actions):
    if random.random() < epsilon:
        return random.randrange(n_actions)  # 随机探索
    else:
        with torch.no_grad():
            state = torch.tensor(state, dtype=torch.float32).unsqueeze(0).to(device)
            return policy_net(state).argmax(dim=1).item()  # 利用网络选择最优动作

# 更新目标网络
def update_target_network(policy_net, target_net):
    target_net.load_state_dict(policy_net.state_dict())

# 训练 DQN 网络
def optimize_model(policy_net, target_net, memory, optimizer):
    if len(memory) < BATCH_SIZE:
        return
   
    # 从经验回放池中采样
    experiences = memory.sample(BATCH_SIZE)
   
    # 将经验解包为不同部分
    states, actions, rewards, next_states, dones = zip(*experiences)
   
    states = torch.tensor(states, dtype=torch.float32).to(device)
    actions = torch.tensor(actions, dtype=torch.int64).unsqueeze(1).to(device)
    rewards = torch.tensor(rewards, dtype=torch.float32).unsqueeze(1).to(device)
    next_states = torch.tensor(next_states, dtype=torch.float32).to(device)
    dones = torch.tensor(dones, dtype=torch.float32).unsqueeze(1).to(device)
   
    # 当前 Q 值
    q_values = policy_net(states).gather(1, actions)
   
    # 下一个状态的最大 Q 值(目标网络)
    with torch.no_grad():
        max_next_q_values = target_net(next_states).max(1)[0].unsqueeze(1)
        target_q_values = rewards + (GAMMA * max_next_q_values * (1 - dones))

    # 计算损失
    loss = nn.MSELoss()(q_values, target_q_values)
   
    # 反向传播优化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# 初始化 DQN 网络和目标网络
n_actions = env.action_space.n
state_size = env.observation_space.shape[0]

policy_net = DQN(state_size, n_actions).to(device)
target_net = DQN(state_size, n_actions).to(device)
update_target_network(policy_net, target_net)

# 优化器
optimizer = optim.Adam(policy_net.parameters(), lr=LEARNING_RATE)

# 经验回放
memory = ReplayMemory(MEMORY_SIZE)

# 训练主循环
epsilon = EPSILON_START
num_episodes = 1000

for episode in range(num_episodes):
    state, _ = env.reset()
    done = False
    total_steps = 0
    total_reward = 0

    while not done:
        # 选择动作
        action = select_action(state, policy_net, epsilon, n_actions)

        # 执行动作
        next_state, reward, done, truncated, _ = env.step(action)
        total_steps += 1
        total_reward += reward

        # 如果游戏结束,则给负奖励
        reward = reward if not done else -10

        # 将经验存入回放池
        memory.push((state, action, reward, next_state, done))

        # 更新状态
        state = next_state

        # 训练模型
        optimize_model(policy_net, target_net, memory, optimizer)

        # 停止训练条件:游戏累计达到5000步,保存模型
        if total_steps >= TRAIN_TARGET_REWARD:
            print(f"Training completed after {episode+1} episodes, reaching {total_steps} steps.")
            torch.save(policy_net.state_dict(), 'dqn_cartpole.pth')
            break

    # 每隔一定步数更新目标网络
    if episode % TARGET_UPDATE == 0:
        update_target_network(policy_net, target_net)

    # epsilon衰减
    epsilon = max(EPSILON_END, EPSILON_DECAY * epsilon)

    print(f"Episode {episode + 1}/{num_episodes},total_steps:{total_steps}, Total Reward: {total_reward}")

    # 检查是否已经达到目标
    if total_steps >= TRAIN_TARGET_REWARD:
        break

env.close()



保存模型的代码:

# 训练结束后保存模型
if total_steps >= TRAIN_TARGET_REWARD:
    print(f"Training completed after {episode+1} episodes, reaching {total_steps} steps.")
    torch.save(policy_net.state_dict(), 'dqn_cartpole.pth')
    break



模型保存成功,大小6kb

2.使用神经网络控制游戏:
调用神经网络决策关键代码:

    state_tensor = torch.tensor(observation, dtype=torch.float32).unsqueeze(0).to(device)
    action = policy_net(state_tensor).argmax(dim=1).item()



使用模型并调用神经网络控制完整代码如下:
 

import gym
import pygame
import torch
import torch.nn as nn
from pygame.locals import K_LEFT, K_RIGHT, QUIT
import time
import random  # 用于生成随机数

# 设置设备(如果有GPU则使用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 初始化 pygame
pygame.init()

# 设置帧率
FPS = 60
clock = pygame.time.Clock()

# 创建 CartPole 环境,使用 Gym 自带的渲染模式
env = gym.make('CartPole-v1', render_mode="human")

# 重置环境,准备开始游戏
observation, info = env.reset()

# 初始化游戏的运行标志
running = True

# 操作次数
action_count = 0

# 记录游戏开始的时间
start_time = time.time()

# DQN 网络结构
class DQN(nn.Module):
    def __init__(self, state_size, action_size):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(state_size, 24)
        self.fc2 = nn.Linear(24, 24)
        self.fc3 = nn.Linear(24, action_size)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

# 初始化 DQN 网络和目标网络
n_actions = env.action_space.n
state_size = env.observation_space.shape[0]

# 加载模型并使用神经网络控制游戏
policy_net = DQN(state_size, n_actions).to(device)

# 加载训练好的模型权重
MODEL_PATH = 'dqn_cartpole.pth'
policy_net.load_state_dict(torch.load(MODEL_PATH))

# 设置为评估模式
policy_net.eval()

# 游戏循环
while running:
    # 处理事件队列,检测关闭窗口操作
    for event in pygame.event.get():
        if event.type == QUIT:  # 退出事件
            running = False
    time.sleep(0.3)

    # 获取键盘按键状态
    keys = pygame.key.get_pressed()

    # 默认动作为随机生成 0 或 1,除非检测到按键输入
    if not keys[K_LEFT] and not keys[K_RIGHT]:
        action = random.choice([0, 1])  # 随机选择 0 或 1

    # 根据按键改变动作
    if keys[K_LEFT]:
        print("Left arrow pressed")
        action = 0  # 向左移动
    elif keys[K_RIGHT]:
        print("Right arrow pressed")
        action = 1  # 向右移动
        
    state_tensor = torch.tensor(observation, dtype=torch.float32).unsqueeze(0).to(device)
    action = policy_net(state_tensor).argmax(dim=1).item()

    # 执行动作并更新环境状态
    observation, reward, done, truncated, info = env.step(action)

    # 每执行一次动作,增加操作次数
    action_count += 1

    # 渲染环境,减少渲染频率(每隔 10 帧渲染一次)
    if action_count % 10 == 0:
        env.render()

    # 检查游戏是否结束
    if done or truncated:
        # 记录游戏结束的时间
        end_time = time.time()

        # 计算游戏持续的时间(秒)
        elapsed_time = end_time - start_time

        # 打印游戏信息
        print(f"游戏结束!总共操作了 {action_count} 次,持续时间为 {elapsed_time:.2f} 秒")

        # 重置环境
        observation, info = env.reset()

        # 重置计数器和时间
        action_count = 0
        start_time = time.time()

    # 控制帧率,确保游戏速度合适
    clock.tick(FPS)  # 设置帧率为 60 帧/秒

# 关闭游戏环境和 pygame
env.close()
pygame.quit()

每次都能顺利通关
 


更详细:

用神经网络自动玩游戏
https://www.jinshuangshi.com/forum.php?mod=viewthread&tid=353
(出处: 金双石科技)
 

;