一文实践强化学习训练游戏ai--doom枪战游戏实践

一文实践强化学习训练游戏ai--doom枪战游戏实践

上次文章写道下载doom的环境并尝试了简单的操作,这次让我们来进行对象化和训练、验证,如果你有基础,可以直接阅读本文,不然请你先阅读Doom基础知识,其中包含了下载、动作等等的基础知识。

本次与之前的马里奥训练不同,马里奥是已经有做好的step等函数的,而这个doom没有,但也因此我们可以更好的一窥训练的过程。
完整代码在最后,可以复制执行。

文章目录

一、训练模型

1、vizdoom_train类

这是我们的训练基类,由于我们想用openai gym环境,因此我们需要手写此环境必须的__init__、step等函数

1)init

在这个函数中,我们要定义训练的观察空间(即游戏图像)、动作空间(即ai可以执行的操作)和一些基础设置。

py 复制代码
        VizDoom_basic_cfg = r"C:/Users/tttiger/Desktop/ViZDoom-master/ViZDoom-master/scenarios/basic.cfg"
        self.game = vizdoom.DoomGame()
        self.game.load_config(VizDoom_basic_cfg)

        if render == False: 
            self.game.set_window_visible(False)
        else:
            self.game.set_window_visible(True)
        self.game.init()

此处,我们先指定游戏文件的位置,然后加一个判断,决定是否允许游戏窗口显示出来,最后初始化游戏。

初始化后,我们就要规定观察空间和动作空间了

python 复制代码
        self.observation_space = Box(low=0, high=255, shape=(100,160,1), dtype=np.uint8) 
        self.action_space = Discrete(3)

观察空间是我们游戏的界面,这是一个灰化后的图像,所以维度为1,大小为100*160

动作空间为离散空间3,即可以选取动作0、1、2,我们只需要在后续的代码中指定数字指代的动作就可以了。

2)step

step函数非常关键,这是ai执行动作的时候会调用的函数。

首先,我们定义我们的动作

py 复制代码
        actions = np.identity(3,dtype=np.uint8)
        reward = self.game.make_action(actions[action], 4) 

这部分的详细解释在Doom基础知识中,事实上就是定义一个矩阵,调用游戏文件中的左移、右移和射击。

接下来,我们要获取当前的状态,比如得分,游戏图像等,这样我们才可以训练。

py 复制代码
        try:
            state = self.game.get_state()
            img = state.screen_buffer
            img = self.togray(img)
            info = state.game_variables[0]
        except:
            img = np.zeros(self.observation_space.shape)
            info = 0 
        finally:
            info = {"info":info}
            done = self.game.is_episode_finished()
        #img_show(img)
        return img,reward,done,info

使用try,是因为gameover时有些内容获取不到,为了防止程序因此暂停,用try。最后,我们要把info变成字典形式,这是因为openai gym环境时这么要求的。为了方便理解,这里可以调用imgshow,查看目前的图像,其实现如下。

py 复制代码
def img_show(img):
    plt.imshow(img)
    plt.show()
    time.sleep(5)

3)togray

灰度化图像,我们知道,彩色图像时由rgb三个颜色矩阵组成,但这么大的数据量给我们的训练增添的很多负担,于是我们采用灰度图。同时,我们缩小图像,这样可以训练的更快。

py 复制代码
    def togray(self,observation):
        gray = cv2.cvtColor(np.moveaxis(observation, 0, -1), cv2.COLOR_BGR2GRAY)
        resize = cv2.resize(gray, (160,100), interpolation=cv2.INTER_CUBIC)
        state = np.reshape(resize, (100,160,1))
        return state

observation 是一个 NumPy 数组,通常表示图像数据。

np.moveaxis 是 NumPy 库中的一个函数,用于重新排列数组的轴。

参数 0 表示将第0轴(通常是颜色通道)移动到新位置的最后一个轴位置(即 -1)。

例如,如果 observation 的形状是 (C, H, W)(即颜色通道在第一个维度),经过 np.moveaxis 后,形状将变为 (H, W, C),这对于 OpenCV 处理图像更为常见,因为 OpenCV 期望颜色通道是图像的最后一个维度。

cv2.cvtColor 是 OpenCV 中用于颜色空间转换的函数。

第一个参数是输入图像(在这里是经过 np.moveaxis 处理后的图像)。

第二个参数 cv2.COLOR_BGR2GRAY 指定了将图像从 BGR 颜色空间转换为灰度图像。

4)其他函数

我们要定义一个关闭游戏的函数close

py 复制代码
   def close(self):
        self.game.close()

以及一个reset函数,用于在结束一个游戏后,重置状态,继续下一轮训练

py 复制代码
    def reset(self):
        state = self.game.new_episode()
        state = self.game.get_state()
        return self.togray(state.screen_buffer)

至此,我们已经成功地把这个独立游戏包装成了可以使用openai gym的环境的游戏。

2、保存模型函数

py 复制代码
class TrainAndLoggingCallback(BaseCallback):

    def __init__(self, check_freq, save_path, verbose=1):
        super(TrainAndLoggingCallback, self).__init__(verbose)
        self.check_freq = check_freq
        self.save_path = save_path

    def _init_callback(self):
        if self.save_path is not None:
            os.makedirs(self.save_path, exist_ok=True)

    def _on_step(self):
        if self.n_calls % self.check_freq == 0:
            model_path = os.path.join(self.save_path, 'best_model_{}'.format(self.n_calls))
            self.model.save(model_path)

        return True

我们使用这段代码来存档数据,这部分复制即可

3、训练模型

首先,我们指定训练结果保存的路径

py 复制代码
    CHECKPOINT_DIR = './train/train_basic'
    LOG_DIR = './logs/log_basic'
    callback = TrainAndLoggingCallback(check_freq=10000, save_path=CHECKPOINT_DIR)    ```

然后我们调用训练函数进行训练

py 复制代码
    env = vizdoom_train(render=False)
    model = PPO('CnnPolicy', env, tensorboard_log=LOG_DIR, verbose=1, learning_rate=0.0001, n_steps=2048)
    model.learn(total_timesteps=100000, callback=callback)

这里我们使用PPO这个强化学习算法。

经过几十分钟的等待,就得到训练好的模型了

二、成果验收

训练好模型后,我们要使用模型,看看效果。

首先,我们载入训练好的模型

py 复制代码
model = PPO.load('./train/train_basic/best_model_100000')

然后测试以下模型的平均得分

mean_reward, _ = evaluate_policy(model, env, n_eval_episodes=5)

这样还不够直观,我们让ai玩给我们看

py 复制代码
for episode in range(100): 
    obs = env.reset()
    done = False
    total_reward = 0
    while not done: 
        action, _ = model.predict(obs)
        obs, reward, done, info = env.step(action)
        time.sleep(0.20)
        total_reward += reward
    print('Total Reward for episode {} is {}'.format(total_reward, episode))
    time.sleep(2)

我们首先让ai模型预测,然后将预测的动作输入step函数,然后展示页面。

如图所示,平均得分非常高,基本能快速索敌,然后一枪秒杀。

至此,我们完成了ai的训练。

完整代码

训练代码

py 复制代码
from gym import Env
from gym.spaces import Discrete,Box
import cv2
from vizdoom import *
import vizdoom
import random
import time
import numpy as np
#DIS离散空间 作用类似于random
#box用来装游戏图像
from matplotlib import pyplot as plt
class vizdoom_train(Env):
    def __init__(self, render=True):
        super().__init__()
        VizDoom_basic_cfg = r"C:/Users/tttiger/Desktop/ViZDoom-master/ViZDoom-master/scenarios/basic.cfg"
        self.game = vizdoom.DoomGame()
        self.game.load_config(VizDoom_basic_cfg)

        if render == False: 
            self.game.set_window_visible(False)
        else:
            self.game.set_window_visible(True)
        self.game.init()

        self.observation_space = Box(low=0, high=255, shape=(100,160,1), dtype=np.uint8) 
        self.action_space = Discrete(3)
    def step(self,action):
        actions = np.identity(3,dtype=np.uint8)
        reward = self.game.make_action(actions[action], 4) 
        try:
            state = self.game.get_state()
            img = state.screen_buffer
            img = self.togray(img)
            info = state.game_variables[0]
        except:
            img = np.zeros(self.observation_space.shape)
            info = 0 
        finally:
            info = {"info":info}
            done = self.game.is_episode_finished()
        #img_show(img)
        return img,reward,done,info
    
    def close(self):
        self.game.close()

    def reset(self):
        state = self.game.new_episode()
        state = self.game.get_state()
        return self.togray(state.screen_buffer)
    
    def togray(self,observation):
        gray = cv2.cvtColor(np.moveaxis(observation, 0, -1), cv2.COLOR_BGR2GRAY)
        resize = cv2.resize(gray, (160,100), interpolation=cv2.INTER_CUBIC)
        state = np.reshape(resize, (100,160,1))
        return state
    
def img_show(img):
    plt.imshow(img)
    plt.show()
    time.sleep(5)


# Import os for file nav
import os 
# Import callback class from sb3
from stable_baselines3.common.callbacks import BaseCallback
class TrainAndLoggingCallback(BaseCallback):

    def __init__(self, check_freq, save_path, verbose=1):
        super(TrainAndLoggingCallback, self).__init__(verbose)
        self.check_freq = check_freq
        self.save_path = save_path

    def _init_callback(self):
        if self.save_path is not None:
            os.makedirs(self.save_path, exist_ok=True)

    def _on_step(self):
        if self.n_calls % self.check_freq == 0:
            model_path = os.path.join(self.save_path, 'best_model_{}'.format(self.n_calls))
            self.model.save(model_path)

        return True
    
if __name__ == "__main__":

    CHECKPOINT_DIR = './train/train_basic'
    LOG_DIR = './logs/log_basic'
    callback = TrainAndLoggingCallback(check_freq=10000, save_path=CHECKPOINT_DIR)    

    train = vizdoom_train()

    from stable_baselines3 import PPO

    env = vizdoom_train(render=False)
    model = PPO('CnnPolicy', env, tensorboard_log=LOG_DIR, verbose=1, learning_rate=0.0001, n_steps=2048)
    model.learn(total_timesteps=100000, callback=callback)

测试代码

py 复制代码
from stable_baselines3.common.evaluation import evaluate_policy
from stable_baselines3 import PPO
import time
model = PPO.load('./train/train_basic/best_model_100000')

from second_gym import vizdoom_train
env = vizdoom_train(render=True)

mean_reward, _ ,_= evaluate_policy(model, env, n_eval_episodes=5)

print(mean_reward)

for episode in range(100): 
    obs = env.reset()
    done = False
    total_reward = 0
    while not done: 
        action, _ = model.predict(obs)
        obs, reward, done, info = env.step(action)
        time.sleep(0.20)
        total_reward += reward
    print('Total Reward for episode {} is {}'.format(total_reward, episode))
    time.sleep(2)
相关推荐
ersaijun1 小时前
【Obsidian】当笔记接入AI,Copilot插件推荐
人工智能·笔记·copilot
格林威2 小时前
Baumer工业相机堡盟工业相机如何通过BGAPISDK使用短曝光功能(曝光可设置1微秒)(C语言)
c语言·开发语言·人工智能·数码相机·计算机视觉
学术头条2 小时前
【直播预告】从人工智能到类脑与量子计算:数学与新计算范式
人工智能·科技·安全·语言模型·量子计算
有Li3 小时前
《PneumoLLM:利用大型语言模型的力量进行尘肺病诊断》|文献速递--基于深度学习的医学影像病灶分割
人工智能·深度学习·语言模型
格林威3 小时前
Baumer工业相机堡盟工业相机如何通过BGAPI SDK设置相机的图像剪切(ROI)功能(C语言)
c语言·开发语言·人工智能·数码相机·计算机视觉
Beginner x_u3 小时前
线性代数 第六讲 特征值和特征向量_相似对角化_实对称矩阵_重点题型总结详细解析
人工智能·线性代数·机器学习·矩阵·相似对角化
Roc_z73 小时前
从虚拟现实到元宇宙:Facebook引领未来社交的下一步
人工智能·facebook·社交媒体·隐私保护
苦瓜汤补钙4 小时前
论文阅读:3D Gaussian Splatting for Real-Time Radiance Field Rendering
论文阅读·人工智能·算法·3d
Limiiiing4 小时前
【论文阅读】DETRs Beat YOLOs on Real-time Object Detection
论文阅读·人工智能·目标检测