Играем в GTA V c Python. Часть IX: тренировочные данные для нейронной сети автопилота

Предыдущая статья — Играем в GTA V c Python. Часть VIII: дальнейшие шаги по созданию беспилотного автомобиля.

Добро пожаловать в девятую часть серии статей про использование методов машинного обучения в игре GTA V. В данной статье мы расскажем, как будем строить наш датасет для обучения нейронной сети.

Для тренировки нейронной сети нам нужно иметь размеченные данные (когда определенным свойствам соответствуют определенные метки или, иначе говоря, целевые переменные). Нашими свойствами являются пиксели с экрана. А метками или целевыми переменными являются нажатия клавиш, которые должен делать за нас AI. На данном этапе у нас уже есть пиксельные данные, но пока нет способов сохранять клавиатурные вводы. Мы производим нажатия клавиатуры и их нужно сохранять.

Мы долго искали способ логировать нажатия клавиатуры в Python без всякой связи с тем, что происходит на экране, но все было безуспешно. И только задав вопрос в Твиттере, мы получили ответ от Jake B. Слегка видоизменив его под наши нужды, мы сохранили этот код в файле getkeys.py:

# getkeys.py
# Citation: Box Of Hats (https://github.com/Box-Of-Hats )

import win32api as wapi
import time

keyList = ["\b"]
for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ 123456789,.'APS$/\\":
    keyList.append(char)

def key_check():
    keys = []
    for key in keyList:
        if wapi.GetAsyncKeyState(ord(key)):
            keys.append(key)
    return keys 

Теперь мы можем в любой момент импортировать функцию key_check() и узнать, какие клавиши у нас нажимаются.

Отлично! У нас есть все инструменты, необходимые для сбора обучающих данных, теперь нам просто нужно собрать их вместе и получить эти самые данные!

Эта часть создавалась методом проб и ошибок. Данная задача не является полностью решенной. Все, что мы здесь делаем, вероятно, можно улучшить. Но мы расскажем хотя бы о том, что удалось обнаружить по пути. Для начала создадим новый файл с именем create_training_data.py:

import numpy as np
from grabscreen import grab_screen
import cv2
import time
from getkeys import key_check
import os


def keys_to_output(keys):
    '''
    Convert keys to a ...multi-hot... array

    [A,W,D] boolean values.
    '''
    output = [0,0,0]
    
    if 'A' in keys:
        output[0] = 1
    elif 'D' in keys:
        output[2] = 1
    else:
        output[1] = 1
    return output 

В нашей основной функции мы сразу же совершаем важный выбор. Во-первых, мы собираемся использовать создаваемый в реальном времени массив, чтобы определить выбор нашего движения. Мы можем просто идти вперед, а можем повернуть налево или направо.

На данном этапе мы игнорируем многие комбинации движений, такие как ускорение и поворот или замедление и поворот и т. д.

Мы решили поступить таким образом, чтобы попытаться собрать как можно больше данных за минимально возможное время. Оказывается, хорошо ездить в Grand Theft Auto V довольно скучно. Мы не планируем, что наш агент всегда будет вести себя так хорошо, но пока давайте будем держать ситуацию под контролем.

file_name = 'training_data.npy'

if os.path.isfile(file_name):
    print('File exists, loading previous data!')
    training_data = list(np.load(file_name))
else:
    print('File does not exist, starting fresh!')
    training_data = [] 

Итак, мы начинаем сбор наших обучающих данных. Мы не были уверены, что сохранять обучающие данные надо именно так , но подумали и решили просто запихнуть все это для начала в numpy-файл. Для большего количества обучающих данных нам бы потребовалась база данных или что-то в этом роде. Правда, в конечном итоге нам действительно понадобится какая-то база данных, если мы хотим иметь безупречный AI, поскольку для этого потребуются огромные объемы обучающих данных. Не стесняйтесь менять все, что вам нравится. По ходу дела мы стараемся разделить все что возможно, чтобы можно было легко применять новые методологии, или чтобы вы могли это сделать без нас.

def main():

    for i in list(range(4))[::-1]:
        print(i+1)
        time.sleep(1)
        
    while(True):
        # 800x600 windowed mode
        screen = grab_screen(region=(0,40,800,640))
        last_time = time.time()
        screen = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY)
        # resize to something a bit more acceptable for a CNN
        screen = cv2.resize(screen, (80,60))
        keys = key_check()
        output = keys_to_output(keys)
        training_data.append([screen,output])
        
        if cv2.waitKey(25) & 0xFF == ord('q'):
            cv2.destroyAllWindows()
            break

        if len(training_data) % 500 == 0:
            print(len(training_data))
            np.save(file_name,training_data) 

Из кода выше видно, как мы собираем данные. Мы добавляем их в один большой список, а затем каждые 500 его элементов сохраняем в файл NumPy.

Таким образом, вы можете играть сколько угодно, а когда увидите, что 500 элементов скинуты в файл, можете остановить игру. А затем можно начать все заново в любое удобное время.

Также в конце заметим, что мы ограничили разрешение экрана размером 800X600. Это сделано, чтобы нашей сверточной нейронной сети было проще обрабатывать данные. Как и ранее, можете этот параметр менять как вам угодно, но для начала такого разрешения будет достаточно.

Отлично, теперь приступим к делу! Создайте сами ваши данные! Мы рекомендуем, чтобы в них было не менее 100K элементов.

Следующая статья — Играем в GTA V c Python. Часть X: балансировка данных для обучения нейронной сети.