Предыдущая статья — Играем в GTA V c Python. Часть XII: тестируем нейронную сеть для автопилота.
Добро пожаловать в тринадцатую часть серии статей про применение методов машинного обучения в игре Grand Theft Auto V. Мы решили, что кататься на скутере для сбора тренировочных данных слишком скучно, и проапгрейдили его до спортивного байка! Тем более, после того как нам удалось достигнуть отличных результатов при весьма скудных тренировочных данных, мы решили, что дальнейшее его обучение не составляет никакого труда. А это слишком скучно, нам нужен вызов! Поэтому вместо этого мы вернули обратно трафик и решили научить AI ездить на высоких скоростях, отслеживая всех участников дорожного движения. Очевидно, это будет несколько сложнее.
В этот раз мы тренировали AI на обучающей выборке, которая содержала 300 тысяч элементов уже после балансировки. И все прошло замечательно. Конечно, возникло довольно много мелких затруднений, но в целом мы остались довольны. Основная проблема, с которой мы столкнулись, это сами элементы управления. Мы очень хотим ПИД-контроллер, так что это будет следующее, на чем мы сосредоточимся. Мы рассчитываем, что это может дать хорошие результаты, но посмотрим. На настоящий момент код имеет следующий вид:
# 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 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 = [] def main(): for i in list(range(4))[::-1]: print(i+1) time.sleep(1) paused = False while(True): if not paused: # 800x600 windowed mode screen = grab_screen(region=(0,40,800,640)) last_time = time.time() screen = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY) screen = cv2.resize(screen, (160,120)) # resize to something a bit more acceptable for a CNN keys = key_check() output = keys_to_output(keys) training_data.append([screen,output]) if len(training_data) % 1000 == 0: print(len(training_data)) np.save(file_name,training_data) keys = key_check() if 'T' in keys: if paused: paused = False print('unpaused!') time.sleep(1) else: print('Pausing!') paused = True time.sleep(1) main()
Файл create_training_data.py
используется для записи ваших действий и кадров из игры, сохраняя их в numpy-файле.
# balance_data.py import numpy as np import pandas as pd from collections import Counter from random import shuffle train_data = np.load('training_data.npy') df = pd.DataFrame(train_data) print(df.head()) print(Counter(df[1].apply(str))) lefts = [] rights = [] forwards = [] shuffle(train_data) for data in train_data: img = data[0] choice = data[1] if choice == [1,0,0]: lefts.append([img,choice]) elif choice == [0,1,0]: forwards.append([img,choice]) elif choice == [0,0,1]: rights.append([img,choice]) else: print('no matches') forwards = forwards[:len(lefts)][:len(rights)] lefts = lefts[:len(forwards)] rights = rights[:len(forwards)] final_data = forwards + lefts + rights shuffle(final_data) np.save('training_data.npy', final_data)
Файл balance_data.py
используется для получения обучающих данных и их равномерного распределения между действиями, которые необходимо предпринять. Каждый раз, когда размер файла доходил до ~ 100 МБ
, мы сохраняли сбалансированный файл с добавленным к нему идентификатором и начинали писать в новый. Это сделано для того, чтобы при обучении сети мы могли просто загружать блоки по 100 МБ
за раз.
# train_model.py import numpy as np from alexnet import alexnet WIDTH = 160 HEIGHT = 120 LR = 1e-3 EPOCHS = 10 MODEL_NAME = 'pygta5-car-fast-{}-{}-{}-epochs-300K-data.model'.format(LR, 'alexnetv2',EPOCHS) model = alexnet(WIDTH, HEIGHT, LR) hm_data = 22 for i in range(EPOCHS): for i in range(1,hm_data+1): train_data = np.load('training_data-{}-balanced.npy'.format(i)) train = train_data[:-100] test = train_data[-100:] X = np.array([i[0] for i in train]).reshape(-1,WIDTH,HEIGHT,1) Y = [i[1] for i in train] test_x = np.array([i[0] for i in test]).reshape(-1,WIDTH,HEIGHT,1) test_y = [i[1] for i in test] model.fit({'input': X}, {'targets': Y}, n_epoch=1, validation_set=({'input': test_x}, {'targets': test_y}), snapshot_step=500, show_metric=True, run_id=MODEL_NAME) model.save(MODEL_NAME) # tensorboard --logdir=foo:C:/path/to/log
Этот код производит обучение модели. Здесь мы полагаем, что используется нейронная сеть ALexNet, но, конечно, можно использовать и любые другие сети. Если у вас еще нет кода AlexNet, то вот он:
# alexnet.py """ AlexNet. Applying 'Alexnet' to Oxford's 17 Category Flower Dataset classification task. References: - Alex Krizhevsky, Ilya Sutskever and Geoffrey E. Hinton. ImageNet Classification with Deep Convolutional Neural Networks. NIPS, 2012. Links: - [AlexNet Paper](http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf) """ import tflearn from tflearn.layers.conv import conv_2d, max_pool_2d from tflearn.layers.core import input_data, dropout, fully_connected from tflearn.layers.estimator import regression from tflearn.layers.normalization import local_response_normalization def alexnet(width, height, lr): network = input_data(shape=[None, width, height, 1], name='input') network = conv_2d(network, 96, 11, strides=4, activation='relu') network = max_pool_2d(network, 3, strides=2) network = local_response_normalization(network) network = conv_2d(network, 256, 5, activation='relu') network = max_pool_2d(network, 3, strides=2) network = local_response_normalization(network) network = conv_2d(network, 384, 3, activation='relu') network = conv_2d(network, 384, 3, activation='relu') network = conv_2d(network, 256, 3, activation='relu') network = max_pool_2d(network, 3, strides=2) network = local_response_normalization(network) network = fully_connected(network, 4096, activation='tanh') network = dropout(network, 0.5) network = fully_connected(network, 4096, activation='tanh') network = dropout(network, 0.5) network = fully_connected(network, 3, activation='softmax') network = regression(network, optimizer='momentum', loss='categorical_crossentropy', learning_rate=lr, name='targets') model = tflearn.DNN(network, checkpoint_path='model_alexnet', max_checkpoints=1, tensorboard_verbose=2, tensorboard_dir='log') return model
После обучения модель можно протестировать следующим образом:
# test_model.py import numpy as np from grabscreen import grab_screen import cv2 import time from directkeys import PressKey,ReleaseKey, W, A, S, D from alexnet import alexnet from getkeys import key_check import random WIDTH = 160 HEIGHT = 120 LR = 1e-3 EPOCHS = 10 MODEL_NAME = 'pygta5-car-fast-{}-{}-{}-epochs-300K-data.model'.format(LR, 'alexnetv2',EPOCHS) t_time = 0.09 def straight(): ## if random.randrange(4) == 2: ## ReleaseKey(W) ## else: PressKey(W) ReleaseKey(A) ReleaseKey(D) def left(): PressKey(W) PressKey(A) #ReleaseKey(W) ReleaseKey(D) #ReleaseKey(A) time.sleep(t_time) ReleaseKey(A) def right(): PressKey(W) PressKey(D) ReleaseKey(A) #ReleaseKey(W) #ReleaseKey(D) time.sleep(t_time) ReleaseKey(D) model = alexnet(WIDTH, HEIGHT, LR) model.load(MODEL_NAME) def main(): last_time = time.time() for i in list(range(4))[::-1]: print(i+1) time.sleep(1) paused = False while(True): if not paused: # 800x600 windowed mode #screen = np.array(ImageGrab.grab(bbox=(0,40,800,640))) screen = grab_screen(region=(0,40,800,640)) print('loop took {} seconds'.format(time.time()-last_time)) last_time = time.time() screen = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY) screen = cv2.resize(screen, (160,120)) prediction = model.predict([screen.reshape(160,120,1)])[0] print(prediction) turn_thresh = .75 fwd_thresh = 0.70 if prediction[1] > fwd_thresh: straight() elif prediction[0] > turn_thresh: left() elif prediction[2] > turn_thresh: right() else: straight() keys = key_check() # p pauses game and can get annoying. if 'T' in keys: if paused: paused = False time.sleep(1) else: paused = True ReleaseKey(A) ReleaseKey(W) ReleaseKey(D) time.sleep(1) main()
Если вам нужны обучающие данные и обученная модель, вам повезло: забирайте! Вот они.
Следующая статья — Играем в GTA V c Python. Часть XIV: обнаружения объектов при помощи TensorFlow.