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

  • 257
    いいね
  • 1
    コメント

はじめに

今回スーパファミコン(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