Предыдущая статья — Играем в GTA V c Python. Часть XI: Обучение нейронной сети для автопилота.
Добро пожаловать в двенадцатую часть серии статей про применение методов машинного обучения в игре Grand Theft Auto V. Мы продолжаем создавать наш беспилотный автомобиль в этой игре.
В предыдущей статье мы обучили нашу нейронную сеть на игровых данных и теперь мы готовы посмотреть, что из этого вышло. В процессе обучения нейронной сети мы сохраняли результаты в отдельном файле. Это позволит нам в любой момент вернуться к уже обученной модели и даже дообучить ее, если потребуется.
Чтобы загрузить эти данные из файла, нам нужно иметь уже заданную модель TensorFlow. Дело в том, что этот записанный файл содержит только сохраненные веса, соответствующие нашей модели нейронной сети, и ничего более. Поэтому, чтобы это все работало, модель нашей нейронной сети должна быть определена заранее.
Мы уже сохраняли эту модель в отдельный файл, но на всякий случай напомним еще раз:
# alexnet.py
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:
# test_model.py
import numpy as np
from PIL import ImageGrab
import cv2
import time
from directkeys import PressKey,ReleaseKey, W, A, S, D
from alexnet import alexnet
WIDTH = 80
HEIGHT = 60
LR = 1e-3
EPOCHS = 8
MODEL_NAME = 'pygta5-car-{}-{}-{}-epochs.model'.format(LR, 'alexnetv2',EPOCHS)
Затем наша модель будет выводить [1,0,0], [0,1,0] или [0,0,1]. Эти выходные данные соответствуют левому повороту, движению вперед или правому повороту, поэтому мы хотим, чтобы эти исходы были сопоставлены с некоторыми фактическими действиями, вот они:
def straight():
PressKey(W)
ReleaseKey(A)
ReleaseKey(D)
def left():
PressKey(W)
PressKey(A)
ReleaseKey(D)
def right():
PressKey(W)
PressKey(D)
ReleaseKey(A)
Эти сопоставления — только начало. Обратите внимание, что на самом деле мы всегда нажимаем «W», что означает нажатие на газ. Возможно, со временем нам потребуется настроить это по-другому, но на данный момент это так.
Теперь определим модель и загрузим наши обученные веса:
model = alexnet(WIDTH, HEIGHT, LR) model.load(MODEL_NAME)
Теперь мы начинаем цикл движения, который очень похож на нашу предыдущую поездку с беспилотным автомобилем. Отличие состоит в том, что мы принимаем решения, исходящие от нашей модели, через метод model.predict:
def main():
last_time = time.time()
for i in list(range(4))[::-1]:
print(i+1)
time.sleep(1)
while(True):
# 800x600 windowed mode
screen = np.array(ImageGrab.grab(bbox=(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, (80,60))
cv2.imshow('',screen)
moves = list(np.around(model.predict([screen.reshape(80,60,1)])[0]))
print(moves)
if moves == [1,0,0]:
left()
elif moves == [0,1,0]:
straight()
elif moves == [0,0,1]:
right()
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
Мы обнаружили, что в отличие от предыдущего сценария автономного вождения, этот, вероятно, следует сразу запускать на полной скорости. Вы должны запустить его, сразу же перейти к игре и начать действовать как можно быстрее.
В реальности, на скорости наша модель ведет себя весьма неплохо. Что довольно впечатляюще, так как после балансировки наш набор обучающих данных сократился до 20,000 фреймов.
Есть одна вещь, которая может всех раздражать — это запуск / остановка нашего AI в процессе тестирования. Сразу понятно, что эта штука требует доработки, посколько мы хотим иметь возможность быстро остановить автопилот и перейти к самостоятельному вождению. Поэтому давайте внесем небольшие изменения в наш файл test_model.py:
from getkeys import key_check
Теперь у нас есть возможность использовать определенную кнопку клавиатуры для приостановки работы автопилота. Далее давайте слегка изменим основную функцию программы:
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)))
print('loop took {} seconds'.format(time.time()-last_time))
last_time = time.time()
screen = cv2.cvtColor(screen, cv2.COLOR_BGR2GRAY)
screen = cv2.resize(screen, (80,60))
moves = list(np.around(model.predict([screen.reshape(80,60,1)])[0]))
if moves == [1,0,0]:
left()
elif moves == [0,1,0]:
straight()
elif moves == [0,0,1]:
right()
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)
Для остановки автопилота мы произвольно выбрали клавишу ‘T‘. Мы могли бы взять и ‘P‘, но тогда на паузу ставилась бы вся игра, чего нам бы не хотелось. Мы хотим остановить AI, сбросить все данные и начать все сначала без остановки самой игры. Поэтому пусть будет ‘T‘. Мы не очень этим заморачиваемся, но иногда действительно нужно поставить все на паузу и затем начать сначала. Для продолжения достаточно просто нажать эту клавишу еще раз.
Как бы то ни было, наш искусственный интеллект конечно не самый лучший, но вполне справляется с управлением даже на затемненных дорогах с отчетливо видимой разметкой. Для начала неплохо: видно, что мы на правильном пути.
Следующая статья — Играем в GTA V c Python. Часть XIII: улучшаем наш автопилот.

