アホが自動運転をしたら盛大に事故るので気をつけたほうがいい

はじめに

最近は社会的にも自動運転という技術が注目されており、多くの自動車メーカーが必死で研究開発を行なっている。
Googleも手を出してはいたが、諦めた的なニュースをどこかで見たのも記憶に新しい現時点では色々な課題があり、まだまだ実現が難しいと考えられている分野だと思う。
私は免許こそ持ってはいるが、自分への自信が全くないので人をはねてしまうのではないかと不安になってしまうため、車を運転することが大の苦手だ。そんな私にとっては自動運転とは夢の技術であり、マイスイートハニーと私を乗せ、綺麗な海岸沿いを華麗に自動運転してくれる超クールな車を手に入れたいと思うのは仕方がないことだと思う。
そこで、今回は車の購入なしでかつ、年間8万円の自動車保険に入らずに自動運転を楽しめる超エキサイティンなツール(?)を紹介する。

udacity drive講座

udacity driveとは

知っている人は知っているかとは思うが、現在udacity driveという下記の講座があり、そこでは自動運転とそれに必要な機械学習の技術を学べるような内容となっているっぽい(受ける予定も特にないので、あまり詳しく調べていない)
www.udacity.com
この講座の受講料は800ドルと比較的お安い講座な方ではあると思うが、日々クレジットカードの引き落としで毎月15000円くらいしか手持ちに残らない極貧民には少しお高い。
だが、現在この講座で使用されるっぽい自動車シミュレーターのソースコードが一部github(下記)にて公開されている。本当に素晴らしい。
github.com

講座で使う機械学習の環境構築

環境構築は極めて簡単。下記でanacondaの環境は整う。
(参照:https://github.com/udacity/CarND-Term1-Starter-Kit/blob/master/doc/configure_via_anaconda.md)

git clone https://github.com/udacity/CarND-Ter​​m1-Starter-Kit.git
cd CarND-Ter​​m1-Starter-Kit
conda env create -f environment.yml
conda info --envs
source activate carnd-term1

anacondaが嫌な方向けにdockerイメージも用意されている。
dockerはあまりよくわからないので、やり方は下記を参照して欲しい。
CarND-Term1-Starter-Kit/configure_via_docker.md at master · udacity/CarND-Term1-Starter-Kit · GitHub

講座で使う自動車シミュレーターのインストール

さて、次は自動車シミュレーターのインストールに移りたいと思う。
下記にインストールの仕方が載っている。コンパイル済みのものもあるが、Mac初心者なので「身元がわからないから開いてやらねー」という感じのエラーが出てよくわからなかったので、ここでは説明しない。
github.com

1. 下記のgit lfsをインストールしてローカルにダウンロードする。
Git Large File Storage - Git Large File Storage (LFS) replaces large files such as audio samples, videos, datasets, and graphics with text pointers inside Git, while storing the file contents on a remote server like GitHub.com or GitHub Enterprise.

git lfs clone https://github.com/udacity/self-driving-car-sim.git

2. Unityを使っているので、インストールがまだなようであれば、Unityをインストールする。
3. Unityを起動して、self-driving-car-simフォルダを選択してロードする。
4. 左下の[プロジェクト]タブに移動し、Asset/1_SelfDrivingCar/Scenesフォルダに移動してSceneをロードします。
湖トラックを走りたい場合は、LakeTrackTraining.unityファイルをロードする。
5. 再生ボタンのようなものをクリックすると、ゲームがスタートする。

とここまでで簡単にゲームができるまでにはなっていると思う。
ちなみに操作方法は上矢印で進む、下矢印でバック&ブレーキ、左右で曲がるというシンプルなものになっている。

運転データの作り方

ゲームを開始すると、右上にRECORDと記載された赤丸がある。そこをクリックすると運転データの保存先の選択画面が出る。
ここで、保存したい場所を選択して、[select]ボタンを押し、もう一度右上の赤丸をクリックすると赤丸が一時停止のアイコンに変わる。そうなったら、運転データの保存が開始されている状況となっている。
ある程度運転データが取れたら、右上の一時停止のアイコンをクリックする。すると、リプレイみたいなのが流れ始めて、順次ファイルへと保存されていく。

自動運転とは?

自動運転をさせるためのスクリプト

この自動車シミュレーターを用いて、自動運転を実現するためのスクリプトは下記にある。
github.com

具体的には、次の2つのファイルがアップロードされている。
・video.py(録画用のスクリプト
・drive.py(車を運転するスクリプト

video.pyを動かすには下記のパッケージが必要なので、インストールすべし。
Installing imageio — imageio 2.1.2dev documentation
インストールしたら、pythonを起動して、下記を実行する。

>>> import imageio
>>> imageio.plugins.ffmpeg.download()

README.mdを読む限りはこの2つだけではなく、下記のファイルを作ってもらいたいらしい。
・model.py(モデルの作成と訓練に使用されるスクリプト
・model.h5(訓練されたKerasモデル)

このmodel.pyとmodel.h5をどのようにして作ればいいのかを考える。
drive.pyを確認してみると、何やらそれっぽいmodel.predictがある。

@sio.on('telemetry')
def telemetry(sid, data):
    if data:
        # The current steering angle of the car
        steering_angle = data["steering_angle"]
        # The current throttle of the car
        throttle = data["throttle"]
        # The current speed of the car
        speed = data["speed"]
        # The current image from the center camera of the car
        imgString = data["image"]
        image = Image.open(BytesIO(base64.b64decode(imgString)))
        image_array = np.asarray(image)
        steering_angle = float(model.predict(image_array[None, :, :, :], batch_size=1))

        throttle = controller.update(float(speed))

        print(steering_angle, throttle)
        send_control(steering_angle, throttle)

send_controlに操舵角度(steering_angle)とスロットル(throttle)の情報を送れば動くっぽいことがわかる。
READMEにも画像データから操舵角度を予測するモデルを設計、訓練、検証すると書いているし、drive.pyにmodel.predictによる予測値が格納されているのは操舵角度だけなので、操舵角度を予測するのが目的で正しいはず。
※操舵角度だけでなく、スロットルとかも予測させることで難易度を高めることはできそうだが、ここでは取り扱わないことにする。

KerasどころかChainerもろくに触っていない初心者がとりあえず、書いてみたのが下記のコードとなる。

import numpy as np
from PIL import Image
import csv

from keras import backend as K
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Convolution2D, MaxPooling2D
from keras.optimizers import Adam
from keras.preprocessing.image import load_img, img_to_array

batch_size = 32
nb_classes = 1
nb_epoch = 5 

# 入力画像の大きさ
img_rows, img_cols = 160, 320
# 畳み込みフィルタの値
nb_filters = 32
# max poolingのサイズ
pool_size = (2, 2)
# 畳み込みカーネルのサイズ
kernel_size = (3, 3)

imgs = []
val = []
# とりあえずcsvのログを開く
with open("./data/driving_log.csv","r") as f:
  reader = csv.reader(f)
  for row in reader:
    # リストで画像を格納する
    imgs.append(np.array(Image.open(row[0])))
    # 操舵角度の情報を格納する
    val.append(np.float32(row[3]))
# numpyでappendを頑なに拒み、ここで変換するアプローチをとった
val = np.array(val)
imgs = np.array(imgs)

input_shape = (img_rows, img_cols, 3)

# この辺はKerasのチュートリアルのCNNの実装をパクった記憶がある
model = Sequential()
model.add(Convolution2D(nb_filters, kernel_size[0], kernel_size[1],
                        border_mode='valid',
                        input_shape=input_shape))
model.add(Activation('relu'))
model.add(Convolution2D(nb_filters, kernel_size[0], kernel_size[1]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=pool_size))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dropout(0.5))
# よくわからんけどとりあえず、Denseで1次元の出力作ればいいだろと思った結果
model.add(Dense(nb_classes))
# よくわからんけど(ry
model.add(Activation('linear'))
adam = Adam()
model.compile(loss='mean_squared_error', optimizer=adam)
# なんか画像のCNNで正規化してるのを見つけたので、なんとなく追加した気がする
imgs = imgs.astype('float32')
imgs /= 255

model.fit(imgs, val, batch_size=32, nb_epoch=nb_epoch)
model.save('model_data.h5') 

ログはdriving_log.csvという名前のcsvファイルとして指定した場所に保存され、中身の要素は多分下記のように並んでいる。

列の情報 左側のカメラ 中央のカメラ 右側のカメラ 多分操舵角 わからん わからん 多分速度
データの型 画像ファイルへの絶対パス(文字列) 画像ファイルへの絶対パス(文字列) 画像ファイルへの絶対パス(文字列) 数値 数値 数値 数値

7000枚程度の画像データをCPUで走らせて、待つこと約4日。モデルが完成!
実際に走らせてみる。走らせる方法は簡単で、シミュレーターを起動し、[ESC]キーを押して、AUTONOMOUS MODEを選択し、下記のコマンドを実行する。(順序は逆でも問題ない)

python drive.py [作成したKerasのモデル] [ここにディレクトリを指定すると正面のカメラ画像が出力される]

python video.py [正面カメラの画像を置いてあるディレクトリ]を指定することで、mp4の動画データを作成できる。

結果

出来上がった録画データをGoogleフォトにアップロードした結果がこれである。
https://goo.gl/photos/pXqraNuZz24CV5W98

心なしか左に曲がってはいる気がするが、最終的には急カーブを曲がり切れず、湖にドボンしてしまった。
ディープラーニング弱者には自動運転なんて夢のまた夢ということがわかったので、これから精進していきたいと思った。
とりあえず、皆さんもチャレンジしてみてはいかがだろうか。私はもうDeep Learningはいいです。GPU買ったらやります。

参考文献

Kerasのドキュメント
Keras Documentation