Edited at

スーパーファミコンのソフトをTensorFlowで学習させてみる!

More than 1 year has passed since last update.


はじめに

今回スーパファミコン(SNES)からROMを抜き出し、強化学習 + 深層学習で学習させAIで操作したときの技術メモ。

構築方法などを書きます。

今回使用したAIフレームワークはTensorFlow。

tensorflowはこちら

ROMの抜き出し方法はこちらの記事に書いてあります

IMG_3730.JPG


事前準備


環境

以下のマシン環境を実施

環境

OS
Ubuntu 16.04 LTS

CPU
i7

MEM
16GB

GPU
NVIDIA GeForce GTX 980

開発言語
エミュレータやROMを操作するのはC++、deeplearningはpython

IMG_3677.JPG


学習方法

ゲームキャプチャ + アクションで報酬(game score)を与えそれを学習させる。

game scoreの取得については、ROMのバイナリ情報を解析する必要があります。

この辺はあまり詳しくありませんが海外にROMの情報が出ているとのことです。


ニューラルネットワークアーキテクチャ

学習関数は定番のReLU。教科学習のアルゴリズムは勾配降下法(Adam)でやってます。

ReLU

勾配降下法


ニューラルネットワークの外観

qiita2.001.jpeg

qiita.002.jpeg


手順


ⅰ.エミュレータの準備

エミュレータの動作に関しては処理が複雑なのでgitにあげておきます。

https://github.com/tsunaki00/siva_game

※ 他のマシンで試してないので動くかわかりません。

エミュに興味あるかたは海外のgitなどをぐぐってみてください!


ⅱ.ROMの抜き出し

上記にも記載しましたが、ROMの抜き出し方法はこちらの記事に書いてあります。

※ 注意 ROMについては購入したものを抜くのは問題ありませんが、再配布等は禁止です。

6085634a-0ce5-105f-be0f-59293e5c6a3c.jpeg


ⅲ.プログラム

エミュレータの仕様上、画面キャプチャが224 × 256なのでそちらを入力にして学習する。

時間あるときにopenCVに変更します。

※※※追記

画像が大きくて遅い問題はopencvで84 * 84にresizeしたら快速になりました。

 screen = self.rle.getScreenGrayscale()

screen = cv2.resize(screen, (84, 84), interpolation=cv2.INTER_LINEAR)
screen = np.reshape(screen, (84, 84, 1))


execute.py

#!/usr/bin/env python

import tensorflow as tf
import sys
import numpy as np
from random import randrange
import rle_python_interface

class Game:

def __init__(self) :
if len(sys.argv) < 2:
print ('Usage:', sys.argv[0], 'rom_file', 'core_file')
sys.exit()
self.rle = rle_python_interface.RLEInterface()
self.rle.setInt('random_seed', 123)

def weight_variable(self, shape):
initial = tf.truncated_normal(shape, stddev = 0.01)
return tf.Variable(initial)

def bias_variable(self, shape):
initial = tf.constant(0.01, shape = shape)
return tf.Variable(initial)

def conv2d(self, x, W, stride):
return tf.nn.conv2d(x, W, strides = [1, stride, stride, 1], padding = "SAME")

def max_pool_2x2(self, x):
return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = "SAME")

def createNetwork(self, action_size):
# network weights
n = 256
W_conv1 = self.weight_variable([56, 64, 4, 16])
b_conv1 = self.bias_variable([16])
W_conv2 = self.weight_variable([28, 32, 64, 32])
b_conv2 = self.bias_variable([32])
W_conv3 = self.weight_variable([7, 8, 32, 16])
b_conv3 = self.bias_variable([16])
W_fc1 = self.weight_variable([n, action_size])
b_fc1 = self.bias_variable([action_size])
s = tf.placeholder("float", [None, 224, 256, 4])
# hidden layers
h_conv1 = tf.nn.relu(self.conv2d(s, W_conv1, 4) + b_conv1)
h_pool1 = self.max_pool_2x2(h_conv1)
h_conv2 = tf.nn.relu(self.conv2d(h_pool1, W_conv2, 2) + b_conv2)
h_pool2 = self.max_pool_2x2(h_conv2)
h_conv3 = tf.nn.relu(self.conv2d(h_pool2, W_conv3, 1) + b_conv3)
h_pool3 = self.max_pool_2x2(h_conv3)

h_conv3_flat = tf.reshape(h_pool3, [-1, n])
readout = tf.matmul(h_conv3_flat, W_fc1) + b_fc1
return s, readout

def trainNetwork(self, s, readout, sess):
minimal_actions = self.rle.getMinimalActionSet()
a = tf.placeholder("float", [None, len(minimal_actions)])
y = tf.placeholder("float", [None, 1])
readout_action = tf.reduce_sum(tf.multiply(readout, a), reduction_indices = 1)
cost = tf.reduce_mean(tf.square(y - readout_action))
train_step = tf.train.AdamOptimizer(1e-6).minimize(cost)
saver = tf.train.Saver()
sess.run(tf.initialize_all_variables())

checkpoint = tf.train.get_checkpoint_state("./saved_networks/checkpoints")
if checkpoint and checkpoint.model_checkpoint_path:
saver.restore(sess, checkpoint.model_checkpoint_path)
print "Successfully loaded:", checkpoint.model_checkpoint_path
else:
print "Could not find old network weights"

# Play 100 episodes
for episode in xrange(100):
total_reward = 0
act_count = 1
actions = []
rewards = []
images = []
while not self.rle.game_over():
if episode >= 1 :
readout_t = readout.eval(feed_dict = {s : [self.rle.getScreenRGB()]})[0]
print(readout_t)
action_index = np.argmax(readout_t)
else :
action_index = randrange(len(minimal_actions))

action = minimal_actions[action_index]
# Apply an action and get the resulting reward
action_array = np.zeros(len(minimal_actions))
action_array[action_index] = 1
actions.append(action_array)
reward = self.rle.act(action)
total_reward += reward
array = np.zeros(1)
array[0] = float(reward)
rewards.append(array)
images.append(self.rle.getScreenRGB())
print ('Episode', episode, 'REWARD:', reward, 'Action', action)

# mini_batch
if act_count % 50 == 0 :
train_step.run(feed_dict = {
a : actions,
y : rewards,
s : images
})
actions = []
rewards = []
images = []
act_count += 1

print ('Episode', episode, 'ended with score:', total_reward)
saver.save(sess, 'saved_networks/model-dqn', global_step = episode)
self.rle.reset_game()

def playGame(self):
USE_SDL = True
if USE_SDL:
if sys.platform == 'darwin':
import pygame
pygame.init()
self.rle.setBool('sound', False) # Sound doesn't work on OSX
#elif sys.platform.startswith('linux'):
# rle.setBool('sound', True)
self.rle.setBool('display_screen', True)
# rle.setBool('two_players', True)
self.rle.loadROM(sys.argv[1], sys.argv[2])

# Get the list of legal actions
minimal_actions = self.rle.getMinimalActionSet()
#minimal_actions = rle.getAllActionSet()

sess = tf.InteractiveSession()
s, readout = self.createNetwork(len(minimal_actions))
self.trainNetwork(s, readout, sess)

if __name__ == '__main__' :
game = Game()
game.playGame()



実行

以下のコマンドを実行。

 $ python execute.py [ROMファイル] snes 

処理が重いけど学習はOKでした。

IMG_3727.JPG


[今回の結果]

いろいろ試した結果グラディウスの結果が良かったです!

IMG_3740.JPG

今後、学習結果については動画などでUPします!!


最後に

他にtensorflowで学習したマリオの動画はこちら

https://www.youtube.com/watch?v=T4dO1GKPx4Y

また、ディープラーニングで競馬予想なども実施していますので、いいね!フォローお願いします。

[facebook]

https://www.facebook.com/AIkeiba/

[Twitter]

https://twitter.com/Siva_keiba