トレックゲーム


トレックゲーム


この記事では、私はトレックスゲームをプレイするモデルを作成しました.私は、このモデルは強化学習(RL)モデルではないことを説明する必要があります.これは、オブジェクトとキーボードの動作を予測する簡単なCNNモデルです.

内容:



1 )データ取得

2 ) CNNモデル

3 )訓練されたモデルをゲームでテストする

データ取得


最初にデータを取得し始めました.そのためにいくつかのライブラリを使いました.
import keyboard
import uuid
import time
from PIL import Image
from mss import mss
キーボードと呼ばれるライブラリを使用して、私は私の行動を保存しました.
これをインポートするには、まずライブラリをインストールしなければなりません:
pip install keyboard
MSSと呼ばれるこのライブラリを使用して、私はゲームの画面を記録することができます.私はすべての行を実行すると、私はゲーム画面と私が得たデータで訓練されているモデルを切り替えるには、画像に応じて行動を予測します.また、このライブラリ(MSS)は、画面の一部の領域をカットするのに役立ちます.このように、モデルは決定された領域だけに集中することができます.しかし、最初は、このライブラリを他のようにインストールする必要があります.そのために
pip install mss
インポートライブラリの後に、画面の中にはたくさんの無用なものがありますので、削除するためにペイントを使って座標を決定します.

mon = {"top":370,
       "left":700,
       "width":200,
       "height":145}
MSSというライブラリを使って、モデルを見たいエリアを簡単に遮断できます.だから私はMSS
sct = mss()
今、私は録音に使用する機能を作成します.
i = 0
def record_screen(record_id, key):
    global i # I will use this i inside and outside of the function. (I have an other i)

    i += 1
    print(f"{key}, {i}") #key: char of keyboard, i: num of press for char
    img = sct.grab(mon)
    im = Image.frombytes("RGB", img.size, img.rgb)
    im.save(f"data/img/{key}_{record_id}_{i}.png")
このレコード関数の後、exitの他の関数を定義する必要があります.録画で終了したいときは、ESCを押して終了関数をコールします.
そのために
is_exit = False

def exit():
    global is_exit
    is_exit = True

keyboard.add_hotkey("esc", exit)
結局、私は最後のものをセットすることができます.ここで定義します
record_id = uuid.uuid4()
while True:

    if is_exit: break

    try:
        if keyboard.is_pressed(keyboard.KEY_UP):
            record_screen(record_id=record_id, key="up")
            time.sleep(0.1)

        elif keyboard.is_pressed(keyboard.KEY_DOWN):
            record_screen(record_id=record_id, key="down")
            time.sleep(0.1)

        elif keyboard.is_pressed("right"):
            record_screen(record_id=record_id, key="right")
            time.sleep(0.1)

    except RuntimeError: continue
今、私はゲームでデータを得る準備ができています.

2 ) CNNモデル


このコンテンツでは、私が得たデータを使ってCNNモデルをトレーニングします.モデルは、イメージに従ってキーボード動作を予測します.例えば、モデルがサボテンを認識するならば、行動は「アップ」です、あるいは、モデルが鳥を認識するならば、行動は「ダウン」または「UP」です.そのためには、まずそのインポートライブラリから始めます.
import glob
import os
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from PIL import Image
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
ライブラリの後、画像のパスを定義します.
imgs = glob.glob("data/img/*.png")
私のイメージを変更します
width = 125
height = 50
トレーニングする前に、リサイズ、正規化のようないくつかの操作を適用する必要があります.
ここでは、ラベルは「up」、「down」「right」のようなキーボード操作です.このアクションに到達するには、次の手順に従います
まずファイル名を見つける必要があります.
filename = os.path.basename(img)

OUTPUT: "down_022f78bc-435f-4978-8524-ff1ea1a40d9a_1.png"


それから、私はスプリットメソッドを使用します、そして、彼らを第1のインデックスが我々のラベルであるあと、彼らを「アンク」として分けてください.
label = filename.split("_")[0]

OUTPUT: "down"


また、最初のセクションで画像を保存する方法については、RecordRank画面機能を確認することができます.
ラベルを決めた後、リサイズと正規化イメージ
im = np.array(Image.open(img).convert("L").resize((width, height)))
ここでは、私が上記したものをまとめてまとめます
X = [] # images ("cactus", "bird")
y = [] # labels ("up", "right", "down")

for img in imgs:

    filename = os.path.basename(img)
    label = filename.split("_")[0] # up, down, right
    im = np.array(Image.open(img).convert("L").resize((width, height)))
    im = im/255 # normalization
    X.append(im)
    y.append(label)
列車やテストとして滑りデータについては
X = np.array(X)
X = X.reshape(X.shape[0], width, height, 1)
さて、まずラベルエンコーディングを適用し、Yにラベルをつける.
したがって、最初のラベルは、例えば、数値です
up -- 0
ダウン-- 1
右--> 2
ラベルエンコード後、1つのホットエンコーディングを適用します
したがって、ラベルはバイナリ値、例えば、
0 -> 000
1 -- 010
2 -> 001
そのために関数を定義します:
def one_hot_labels(values):

    # Label Encoding -> One Hot Encoding

    label_encoder = LabelEncoder()
    integer_encoded = label_encoder.fit_transform(values)
    integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)

    onehot_encoder = OneHotEncoder(sparse=False)
    onehot_encoded = onehot_encoder.fit_transform(integer_encoded)

    return onehot_encoded

# One Hote Encoding
Y = one_hot_labels(y)
XとYを使用して、私のデータを0.25レートで分割し、75 %の部分が列車になり、他の25 %がテストサイズになります.
# train test split
train_X, test_X, train_y, test_y = train_test_split(X, Y , test_size = 0.25, random_state = 2)
分割データの後、畳み込み型ニューラルネットワークを作成する
# CNN Model
model = Sequential()
model.add(Conv2D(32, kernel_size = (3,3), activation = "relu", input_shape = (width, height, 1)))
model.add(Conv2D(64, kernel_size = (3,3), activation = "relu"))
model.add(MaxPooling2D(pool_size = (2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation = "relu"))
model.add(Dropout(0.4))
model.add(Dense(3, activation = "softmax"))

model.compile(loss="categorical_crossentropy",
              optimizer="Adam",
              metrics=["acc"])

# training            
model.fit(train_X, train_y, epochs = 35, batch_size = 64)              
トレーニングの後、私は電車とテスト得点を評価します
score_train = model.evaluate(train_X, train_y)
print("Training Score: %", score_train[1]*100)

score_test = model.evaluate(test_X, test_y)
print("Test Score: %", score_test[1]*100)

ゲームでトレーニングモデルを使用するには、私はそれを保存する必要があります.
# save weights
open("trex_model.json","w").write(model.to_json())
model.save_weights("trex_weight.h5")
そして最後に、私は訓練されたCNNモデルを持っています.

3 )訓練されたモデルをゲームでテストする


このセクションでは、私はリアルタイムでゲームで私のモデルをテストします.
そのために、まず私のライブラリをインポートします
from keras.models import model_from_json
import numpy as np
from PIL import Image
import keyboard
import time
import os
from mss import mss
画面用にサイズを設定します
mon = {"top":370,
       "left":700,
       "width":200,
       "height":145}

sct = mss()

# size of images
width = 125
height = 50
今、私は私の訓練CNNモデルをアップロードすることができます
# load model
model = model_from_json(open("trex_model.json", "r").read())
model.load_weights("trex_weight.h5")
モデルがイメージを予測するので、私は再び私のラベルを決定します.私はキーボードに触れないでください、モデルはそうします.
#down:0, right:1, up:2
labels = ["Down", "Right", "Up"]
framerate_time = time.time()
counter = 0
i = 0
delay = 0.4
key_down_pressed = False
while True:

    img = sct.grab(mon)
    im = Image.frombytes("RGB", img.size, img.rgb)
    im2 = np.array(im.convert("L").resize((width, height)))
    im2 = im2 / 255  # normalization

    X = np.array([im2])
    X = X.reshape(X.shape[0], width, height, 1)
    r = model.predict(X)

    result = np.argmax(r)

    if result == 0: #down: 0
        keyboard.press(keyboard.KEY_DOWN)
        key_down_pressed = True

    elif result == 2: #up: 2

        if key_down_pressed:
            keyboard.release(keyboard.KEY_DOWN)
            time.sleep(delay)

        keyboard.press(keyboard.KEY_UP)

        if i < 1500:
            time.sleep(0.3)

        elif 1500 < i and i < 5000:
            time.sleep(0.2)

        else:
            time.sleep(0.17)

        keyboard.press(keyboard.KEY_DOWN)
        keyboard.release(keyboard.KEY_DOWN)

    counter += 1

    if (time.time() - framerate_time) > 1:

        counter = 0
        framerate_time = time.time()

        if i <= 1500:
            delay -= 0.003

        else:
            delay -= 0.005

        if delay < 0:
            delay = 0

        print("----------------")
        print(f"Down: {r[0][0]}\nRight: {r[0][1]}\nUp: {r[0][2]}")

        i += 1
今、私はループ中にこれを説明する必要があります.
まず、ピクセル(mon)に従ってエリアを取得し、変換しました.変換後、リサイズと正規化を適用しました
    img = sct.grab(mon)
    im = Image.frombytes("RGB", img.size, img.rgb)
    im2 = np.array(im.convert("L").resize((width, height)))
    im2 = im2 / 255  # normalization
これらの手順の後、私はそれを配列に変えて、形を変えました.入力がモデルで知られているので.
npargmax ()は、軸に沿った最大値のインデックスを返します.私はラベルの最大確率を見つけるためにそれを使用します.
    X = np.array([im2])
    X = X.reshape(X.shape[0], width, height, 1)
    r = model.predict(X)

    result = np.argmax(r)
結果が0の場合は、キーボードライブラリを使用して、ダウンを意味、モデルがダウンします.
そして、その後、私はKeychen
    if result == 0: #down: 0
    keyboard.press(keyboard.KEY_DOWN)
    key_down_pressed = True
結果が2であるならば、再びキーボードライブラリモデルを使用することを意味します
ここで、< 1500は乱数です.(1500枠)
    elif result == 2: #up: 2

        if key_down_pressed:
            keyboard.release(keyboard.KEY_DOWN)
            time.sleep(delay)

        keyboard.press(keyboard.KEY_UP)

        if i < 1500:
            time.sleep(0.3)

        elif 1500 < i and i < 5000:
            time.sleep(0.2)

        else:
            time.sleep(0.17)
その後、恐竜が再びダウンしますが、私はそれをリリースする必要がありますので、私はしない場合は、恐竜はダウンしてください.
        keyboard.press(keyboard.KEY_DOWN)
        keyboard.release(keyboard.KEY_DOWN)
今ここで、私は遅延を設定
    counter += 1

    if (time.time() - framerate_time) > 1:

        counter = 0
        framerate_time = time.time()

        if i <= 1500:
            delay -= 0.003 # 3 msec

        else:
            delay -= 0.005 # 5 msec

        if delay < 0:
            delay = 0

        print("----------------")
        print(f"Down: {r[0][0]}\nRight: {r[0][1]}\nUp: {r[0][2]}")

        i += 1

プロジェクトのリンクを見つけることができます.
https://github.com/ierolsen/Trex-Game-with-CNN