ドローンと機械学習(AI)。忘れ物を探してくれるドローンへの道。
本日は「ドローンと機械学習(AI)」というテーマが進めたいと思います。
今朝、奥さんと朝ごはんを食べながら、今回は「ドローンと機械学習(AI)」というテーマでやろうと思っているんだけど、皆さん興味持ってくれますかね?という話をしました。
奥さんによると、「機械学習」という言葉は、スーパーコンピュータのような難しい計算のイメージが浮かぶので、プログラミングをやっている人ならまだしも、趣味でドローンをやっている人には馴染みのない印象を受けるのではないかとのこと(´・ω・`)ショボーン
確かに、うちは家族のためのドローン道場なので、「家族のためのプログラミング入門」とか「ドローンで動物を探してみよう」みたいなキャッチにしないと、皆さんに興味すら持ってもらえないのではないかと思いました。いやはや、勉強になりますm(_ _)m
○忘れ物を探してくれるドローンをつくる
さて、気を取り直して、今やるべきことを進めていきたいと思います。
空道黎明館では、シナリオを作成して、そのシナリオに出てくるミッションをクリアーするかたちで、じぶんの道を探求するというアプローチを取っています。
くぼーんのシナリオは「忘れ物を探してくれるドローンをつくる」ことです。手書きの雑な図で申し訳ないのですが、下図のようなイメージで忘れ物を探してくれるドローンをつくりたいと思います。
前提条件として、久保家では夫婦そろってよく忘れ物をします(笑)ですので、この忘れ物を探してくれるドローンというのは、家族の課題でもあるんですねw
それでは、どのような流れで忘れ物を探してくれるのか考えていきましょう。
○忘れ物を探してくれるドローンの仕組み(案)
まず、忘れ物に気づくのは、外出先になりますから、遠隔操作で自宅につなげる必要があります。スマホを使って、自宅に安全な通信(VPN)で接続します。この時の接続先は自宅のホームゲートウェイ(兼VPNルータ)になります。
安全な通信を確立した後、スマホに例えば「財布を探して」と音声入力すると、財布を探してくれるようなインターフェースにしたいと思います。
次に、「財布を探して」という命令をドローンに伝える必要があります。ホームゲートウェイには、WEB通信で接続するとして、そのゲートウェイからドローンにミッションを出すことになります。
このミッションに「財布を探して」という命令を入れるのか、ドローンには部屋を探索して映像を送ってねという命令だけにするのかで、アーキテクチャが変わってきます。機械学習には大きなCPUパワーが必要になりますので、今回は後者のアプローチを取り、ホームゲートウェイで物体検出を行う構成にしたいと思います。
さて、単に部屋を探索して映像を送るだけでも自動化するのは大変です。最初は、もっとも単純なモデルで、一部屋だけを探索するモデルを考えてます。ドローンの電源が入っているという想定で、プログラミングで離陸(テイクオフ)します。その後、決められたルートを巡回して、カメラで撮影した動画をホームゲートウェイに送信します。最後は、元に位置に戻って着陸(ランディング)です。
ホームゲートウェイ側では、ドローンから送信されたきた動画を解析して、物体検出を行います。映像の中に財布があれば、その部分を切り取って、電子メールで外出先のくぼーんに送信します。
「ドローンの電源切れたらアウトでしょw」「ふすまが閉まっていたらクラッシュだよねw」「ワイフを探してと聞き間違えたりしてw」みたいなツッコミどころは満載ですが、この単純なモデルでも実装するのは難しいのですよねw 機械学習やメカニズムを学べば学ぶほど、人間とか生き物ってよくできているなぁと感じます。
○物体を検出するためのディープ・ラーニングについて
さて、目標としては「忘れ物を探してくれるドローンをつくる」なのですが、そのために必要になるのが物体を検知するための仕組みです。ここで、ディープ・ラーニングの技術を使うことになります。
世の中には、似たようなことを考える人がいます。こちらをご覧ください。
こちらはMavic+360度カメラを使って物体検出を行っています。
検出方法は詳しく書かれていませんが、Mask_RCNNを使ってみたいです。
こちらはリアルタイムの物体検出のアーキテクチャも書かれています。
ふむふむ、DJIのドローンアプリからRTMPでローカルPC上のWEBサーバ(Nginx)に配送する。OSは、ubuntu 16.04 LTSで、アプリにはAnacondaとAnaconda / Python 3.5,TensorFlow 1.2,OpenCV 3.0 。その環境上でリアルタイムの物体検出のプログラミングを動かしていますね。
MavicだとDJIのSDKが使えるのですが、TELLOを使った方法もあるようです。理系のOLさんが物体検出にチャレンジしているみたいです。
TELLOのプログラミングについては別の記事にまとめてみましたので、そちらをご覧ください。
○人工知能、機械学習、ディープ・ラーニング
人工知能、機械学習、ディープ・ラーニングという言葉が出てきたので、それぞれの意味を整理しておきましょう。LEAPMINDさんのブログで詳しく解説されていましたので、そちらを元に解説します。
まず、人工知能(AI)ですが、巷では何にでもAIと使われがちで、学者先生の間でも統一的見解はないため、極めて抽象的な言葉になっています。ここでは、人工知能学会の「大量の知識データに対して、高度な推論を的確に行うことを目指したもの 」という定義で考えたいと思います。
AIは、大まかに「弱いAI」と「強いAI」の2つに分類することができます。弱いAIの中には、特化型と汎用型があり、ニュースでよく聞く「囲碁AI」などは特化型です。汎用型AIは、ドラえもんみたいなイメージですが、現実的にはまだ難しいと言われています。
続いて、「機械学習」ですが、これは「教師あり学習」と「教師なし学習」に別れます。
「教師あり学習」には、株価予測などを行う「回帰(予測)」とメールのスパム検出などを行う「分類」の2種類があります。いずれも、正解(正しい出力)があるデータですので、人間が答えを見て、「ふむふむ、よく分類できているね」という感じで判断することができます。
これに対して、「教師なし学習」はクラスリングとも呼ばれ、正解を必要とせず、膨大なデータから自動的に算出した特徴量から構造や傾向、法則などを導く学習方法です。例えば、未知のウイルスを発見する場合などに利用されます。
「強化学習」は、自ら試行錯誤して最適な行動を見つける学習で、直近の目標を達成し、報酬を与えることで上達していく方法です。インベーダーゲームで名古屋撃ちを学習させた動画が有名ですが、最近ではストリートファイターIIも強いそうですw
最後に、ディープ・ラーニングですが、これは人工知能がはじまって50年来のブレークスルーだそうです。東京大学の松尾豊先生のプレゼン資料がわかりやすいです。
最近は、日本ディープラーニング協会(以下JDLA)が活発に活動しているようです。こちらの記事もおもしろい。
今までの人工知能というのは、すべて「人間」が現実世界を一生懸命観察して、どこが重要かというのを見抜いてモデルを作っていたんですね。いったんモデルを作ることができると、その後の処理はいくらでも自動化できました。
ところが、モデルを作る行為そのものは、一向に自動化できなかったわけです。どんな場合でも、人間が世界を一生懸命観察して「ここらへんが重要だ」というのをズバッと見抜く必要がありました。肝心要の部分が「職人芸」になっていたわけです。
ここで技術的なブレークスルーが生まれます。人工知能が長年抱えてきた問題を解決する「ディープラーニング」の登場です。なにができるのかというと、「認識」「運動の習熟」「言語の意味理解」です。
「認識」というのは、画像認識ですね。先程のハスキー犬の例にあるように、今までコンピュータが苦手だった画像認識ができるようになります。「運動の習熟」というのは、ロボット、機械が練習して上達することができるようになることです。これは製造業に革新を起こしそうですね。最後に、「言語の意味理解」というのは、言葉の意味を本当にコンピューターがわかるようになるということです。これには、もう少し時間がかかりそうです。
ディープ・ラーニングについては、東京大学の松尾豊さんのページを元に記事を書きましたので、ご覧いただけば幸いです。
○実際に手を動かして機械学習を学んでみよう!
理論だけでは記憶に残りませんので、実際に手を動かして機械学習を学んでみましょう。
本日は、Googleが械学習の教育、研究用に使われることを目的に無料で提供している「Colaboratory」を使います。クラウドアプリケーションのPython/ 機械学習版で、Pythonのソースコードを対話型で実行できるだけでなく、作ったものを共有したり、
共同編集したりすることができます。控えめに言ってもすごすぎますヽ(=´▽`=)ノ
それでは、実際に始めていきましょう。
初めて使う方は、環境構築不要でPython入門!Google Colaboratoryの使い方を分かりやすく説明|はやぶさの技術ノートの記事がおすすめです!
GoogleアカウントとChromeブラウザが使える前提で話を進めます。
ChromeブラウザからGoogleアカウントにログインした状態で
Google Colaboratoryにアクセスしてください。
新しいノートブックを作成します。「ファイル」→「Python 3の新しいノートブック」をクリックします。ちなみに、「ランタイム」→「ランタイムのタイプを変更」を押すとハードウェアのタイプを選択できて、機械学習エンジニアが憧れるNvidia社のTesla K80 GPUが無料で使えてしまいます!! どうかしてるぜGoogle先生w
下図の真っ新なノートブックが開かれるので、枠内の”セル”と呼ばれる部分にpythonコードを書き込みます。
import numpy as np
import matplotlib.pylab as plt
def numerical_diff(f, x):
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2*h)
def function_1(x):
return 0.01*x**2 + 0.1*x
def tangent_line(f, x):
d = numerical_diff(f, x)
print(d)
y = f(x) - d*x
return lambda t: d*t + y
x = np.arange(0.0, 20.0, 0.1)
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
tf = tangent_line(function_1, 5)
y2 = tf(x)
plt.plot(x, y)
plt.plot(x, y2)
plt.show()
おお、いきなり微分のグラフが描けました!
高校数学が懐かしいですね。人生の挫折を味わうターニングポイントですw
機械学習では、微分積分の数式が大量に出てきます。
学習するとは分けることです。数学が苦手な人は、とりあえず数式は見ないで、分類するために分けているんだなというイメージを掴んでください。
さて、Colabに戻りましょう。このColabの環境はLinuxで動いているので、
Linuxコマンドを使えます。便利な世の中になったなぁ。
最後に、ディープ・ラーニングのフレームワークを使って、物体検出をやってみましょう。こちらの記事を参考にしました。
最近の物体検出手法であるMask R-CNN(keras版)を動かしてみます。
こんな感じで、物体検出、セグメンテーションのみならず、人の骨格推定も可能なようです。
出典:https://arxiv.org/pdf/1703.06870v1.pdf
Google Colaboratoryのノートブックを新規作成し、「ランタイム」「ランタイムのタイプを変更」で、python3/GPUを選択します。
Githubからソースをダウンロードしてきましょう。
# gitからソースを取得
!git clone https://github.com/matterport/Mask_RCNN.git
# 確認
!ls
次に、Mask_RCNN-master/requirements.txt に記載されている関連ライブラリをインストールします。
# フォルダへ移動
%cd Mask_RCNN
# 確認
!ls
# ライブラリを取得
!pip install -r requirements.txt
Mask_RCNN-master/setup.py を実行します。
# setup.pyを実行
%run -i setup.py install
学習済の重み (mask_rcnn_coco.h5)を取得します。
COCOは、画像の認識、セグメンテーション、キャプショニングがされているデータセットです。画像処理をしたいと思っても最初に衝突する問題がデータセットの問題ですが、Microsoft COCOには必要なデータセットが用意されており、pythonとMatlabのAPIも提供されているため、使用するのが
簡単なのが特徴です。
Microsoft COCO が提供する画像データセット用APIをインストールします。
# COCO 用ソースを取得
!git clone https://github.com/waleedka/coco.git
# 確認
!ls
# python用APIをインストール
%cd coco/PythonAPI
%run -i setup.py build_ext --inplace
%run -i setup.py build_ext install
%cd ../../
インストールしたpycocotoolsがない、とエラーになるのでパスを設定します。
import sys
sys.path.append('/content/Mask_RCNN/coco/PythonAPI')
https://github.com/matterport/Mask_RCNN/blob/master/samples/demo.ipynb をコピペして実行していきます。
import os
import sys
import random
import math
import numpy as np
import skimage.io
import matplotlib
import matplotlib.pyplot as plt
# Root directory of the project
ROOT_DIR = os.path.abspath("../")
# Import Mask RCNN
sys.path.append(ROOT_DIR) # To find local version of the library
from mrcnn import utils
import mrcnn.model as modellib
from mrcnn import visualize
# Import COCO config
sys.path.append(os.path.join(ROOT_DIR, "samples/coco/")) # To find local version
import coco
%matplotlib inline
# Directory to save logs and trained model
MODEL_DIR = os.path.join(ROOT_DIR, "logs")
# Local path to trained weights file
COCO_MODEL_PATH = os.path.join(ROOT_DIR, "mask_rcnn_coco.h5")
# Download COCO trained weights from Releases if needed
if not os.path.exists(COCO_MODEL_PATH):
utils.download_trained_weights(COCO_MODEL_PATH)
# Directory of images to run detection on
IMAGE_DIR = os.path.join(ROOT_DIR, "images")
あれ?いきなり、COCOがないと怒られますので、COCOをインポートします。
# import coco
from samples.coco import coco
続いて、GPUのパラメータをコピペ。
class InferenceConfig(coco.CocoConfig):
# Set batch size to 1 since we'll be running inference on
# one image at a time. Batch size = GPU_COUNT * IMAGES_PER_GPU
GPU_COUNT = 1
IMAGES_PER_GPU = 1
config = InferenceConfig()
config.display()
時間がないので、どんどんいきますw
# Create model object in inference mode.
model = modellib.MaskRCNN(mode="inference", model_dir=MODEL_DIR, config=config)
# Load weights trained on MS-COCO
model.load_weights(COCO_MODEL_PATH, by_name=True)
認識するクラスをコピペ。
# COCO Class names
# Index of the class in the list is its ID. For example, to get ID of
# the teddy bear class, use: class_names.index('teddy bear')
class_names = ['BG', 'person', 'bicycle', 'car', 'motorcycle', 'airplane',
'bus', 'train', 'truck', 'boat', 'traffic light',
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird',
'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear',
'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie',
'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball',
'kite', 'baseball bat', 'baseball glove', 'skateboard',
'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup',
'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza',
'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed',
'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote',
'keyboard', 'cell phone', 'microwave', 'oven', 'toaster',
'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors',
'teddy bear', 'hair drier', 'toothbrush']
最後に物体検出です!
# Load a random image from the images folder
file_names = next(os.walk(IMAGE_DIR))[2]
image = skimage.io.imread(os.path.join(IMAGE_DIR, random.choice(file_names)))
# Run detection
results = model.detect([image], verbose=1)
# Visualize results
r = results[0]
visualize.display_instances(image, r['rois'], r['masks'], r['class_ids'],
class_names, r['scores'])
あれれ?イメージフォルダのパス間違ってなくね?
# Directory of images to run detection on
IMAGE_DIR = os.path.join(ROOT_DIR, "Mask_RCNN/images")
気を取り直して、物体検出です!
# Load a random image from the images folder
file_names = next(os.walk(IMAGE_DIR))[2]
image = skimage.io.imread(os.path.join(IMAGE_DIR, random.choice(file_names)))
# Run detection
results = model.detect([image], verbose=1)
# Visualize results
r = results[0]
visualize.display_instances(image, r['rois'], r['masks'], r['class_ids'],
class_names, r['scores'])
おけおけ、動いた動いた。
何をやっているかについては、論文を読んでくださいw
くぼーんもお勉強中の身でございますm(_ _)m
ずいぶんと長い記事になっていました。
忘れ物を探してくれるドローンへの道のりは長そうです。
ゆったり、のんびり、ほどほどに、プロセスを楽しみながら道を歩んでいきたいと思います。