ろむめも

気になったこととか、調べたことをゆるくまとめます。主にプログラミング関連の話題が多いです。

gitとROSでよく使うコマンドをメモする

git

リモートレポジトリから最新のコードを取り込む

git pull

ローカルブランチを作成する

git branch [ブランチ名]

ブランチを確認する

git branch

ローカルブランチを切り替える

git checkout [ブランチ名]

ローカルブランチを作成して、切り替える

git checkout -b [ブランチ名]

ファイルを追加する

git add [ファイル名]

ファイルを削除する

git rm [ファイル名]

ディレクトリごと削除する

git rm -r [ディレクトリ名]

ローカルレポジトリにコミットする

git commit -m "コメント"

ブランチ同士をマージする

git checkout master
git merge[ブランチ名]

マージしたブランチを削除する

git branch -d [ブランチ名]

ブランチをリモートレポジトリにコミットする

git push

ROS

カーネルを起動する

roscore

rosモードに入る

roscd

Robotics③ Kinematics

以下に学習時のメモを記載します。

運動学

導入

重要な4つの概念。reference frame, generalized coordinates, degrees of freedom, homogeneous transform
Joint Space(theta) -> Forward Kinematics -> Cartesian Space(x,y,z)
Cartesian Space(x,y,z) -> Inverse Kinematics -> Joint Space(theta)
一般的に逆運動学を解くのは運動学を解くのよりも難しい。

Dofは独立でない一般化座標の数で表されます。独立でない一般化座標の数はその系を表すのに最低限必要な変数の数です。
robot工学において、configuration spaceやjoint spaceという言葉は重要です。これはアームがとりうる全てのconfigurationsを表します。path planningやobstacle avoidanceの中で使われます。

例えば3次元の世界座標系があってそこに一つの棒を置いたとします。この系を表すのに必要なのは6つの変数です。これは例えば棒の両端のx,y,zの座標やr,theta,phiで表されても良いです。二つの棒を置いた場合は12個の変数が必要になります。二つの棒を関節でつないだ場合は7Dofです。

回転およびプリズムジョイントだけを持っているアームの自由度はエンドエフェクタも含めた可動な関節の数と同じになります。例外として閉ループのアームの自由度は1です。もしアームが与えられたタスクに必要な自由度より多くの自由度を有している場合、運動学的冗長と言います。この冗長性はタスクを解くのに有利で、アームの動かし方に自由度があるという事になります。一方でコストと制御の難しさを考慮する必要があります。

一般的に産業界で使われるのは4か6自由度のアームです。特に有名なのはspherical wristです。先端にroll,yawを持っていて、次の関節にroll,yawがあり、最後にpitchを持ちます。最初の3つで先端の位置の制御をして、残りの3つで姿勢の制御をします。

関節構成のタイプを評価するのにworkspaceという概念を使うことがあります。workspaceは先端が到達可能な点の集合です。一般的にworkspaceには2つの意味に分解できて、到達可能なworkspaceと器用なworkspaceになります。器用なworkspaceとは任意の姿勢で先端部が到達できるかという追加の自由度の事です。

一般的な関節構成のタイプとしては、デカルト型(PPP)、円筒型(RPP)、擬人型(RRR)、スカラ型(RRP)、球状型(RRP)があります。また、平行リンク型も重要なタイプです。

運動学、逆運動学

空間上のパーティクルの位置は参照座標の単位ベクトルにパーティクルの座標のスカラをそれぞれ掛けた形で求められます。ここで一つの座標系を別の座標系に変換する方法を考えます。例えば原点を中心として回転させた場合、回転行列を使う事でその系の返還を表すことが出来ます。回転行列の特性としては以下のものがあります。
逆行列は転置で表すことができる
行列式は1
③行および列は直行する単位ベクトルを意味し、任意の行または列の大きさは1にひとしく、2つの行または列の内積は0になる
④列は元の座標系に対する回転した座標系の基底ベクトルを表す。
これらは3次元にしても同じです。

この計算を行うのに、今回はsympyを利用します。これはシンボルを定義して、式を抽象的に定義することができます。これは簡単なシステムにおいて方程式を簡潔に表すことができるし、浮動小数点演算においても誤差が少なくなる傾向にあります。
サンプルコード

from sympy import symbols, cos, sin, pi, simplify
from sympy.matrices import Matrix
import numpy as np
q1, q2, q3, q4 = symbols('q1:5')
A, R, O, C = symbols('A R O C')
rtd = 180./np.pi
dtr = np.pi/180.
R_x = Matrix([,,[]])
R_x.evalf(subs={q1: 45*dtr})

基本的には回転は順番によらないが、演算上の回転の順番は主に以下のものがある
Intransic Rotation = RzRyRx
Extransic Rotation = RxRyRz

回転行列を使う場合に注意しなければならないポイントは
①パフォーマンスとして3つ座標系なのに9つのパラメータが必要になる
②回転行列の反復乗算によって丸め誤差が発生し、時間と共に有効な回転行列でなくなってしまう。
③回転後に同じ平面に回転軸が来てしまった場合自由度が一つ減り、表現の自由度がうそなわれてしまう。これをgimbal lockと呼ぶ。これを防ぐにはそのような領域に近づかないようにすることです。

オイラー角を求める場合にはatan2を使う。

各関節の角度から先端の位置を求める場合に同次変換を利用する。これは位置(平行移動)のベクトルと姿勢(回転)の行列を一つの行列にまとめたものである。
同次変換を複数回繰り返した場合、結果は同じになるはずです。それは例えばA->Dに変換したいとしてA->B->Dと変換するのとA->C->Dと変換するのでは結果が変わらないことを意味します。

DHパラメータ

ここまでは各リンクの位置を求めるのに回転行列と平行移動を使ってきました。これを簡単にやる方法がDHパラメータの導出です。これまでは位置と姿勢の6つの変数を決める必要がありましたが、DHパラメータでは4つの変数を決めることで求めることができます。但し、この変数の取り方は系の取り方によって5パターン程度存在するので、あくまでも1例です。
・α=Y軸を中心としたZ軸の回転角度
・a=X軸方向の移動距離
・d=Z軸に沿って測定されたXの符号付き距離
・Θ=Z軸を中心としたX軸の回転角度
X軸方向の単位ベクトルはZi-1とZiの外積で求めることができます。DHパラメータを使って座標系を変換するときには、変換はリンクi-1の参照フレームを、リンクiの参照フレームと正確に一致するように移動するかという事を考えると良いです。
RRPR型のアームの場合を考えます。
①全ての関節に1から番号をつけます
②すべてのリンクに0から番号をつけます。0は接地部分です
③駆動軸に線を引きます
④DHパラメータが少なくなるように取り扱うリンク間の距離変数を決定します
⑤各軸の方向を決めます。例えば回転軸であれば反時計回りを正にします。そして右ねじの法則でZ軸の方向を決めます。
⑥X軸の方向を決めます。Xi-1からXiの方向に正とするのが一般的です。ただ、P型ジョイントではDHパラメータを減らすためにPの駆動方向とは垂直な方向にとることがあります。
⑦joint1の変数を減らすためframe0とframe1を一致させます。そうすることでα=a=0になり、もしjoint1がR型の関節であればd=0、P型の関節であればd=0とすることができます。
⑧先端部のX軸の方向を決めます。一般的には一つ前のframeと同じように決めます。
次にDHパラメータのテーブルを決める方法です。
α0はz0とz1の回転角度です。a0はz0からz1のx0軸上の距離です。z軸が平行であればα、x軸の原点が同じであればaは0になります。d1はx0からx1のz1軸上のsin距離です。これもx0とx1が平行であれば0になります。Θ1はx0とx1の間のz1軸周りの回転角度です。x軸が平行ではない場合、Θは0になる場合もありますが、一定のオフセットが乗る場合もあります。
0->1:x,z軸が平行なのでα,d=0、xの原点が同じなのでa=0となり、α,a,d,Θ=0,0,0,Θ1
1->2:x,z軸が平行なのでα,d=0、α,a,d,Θ=0,a1,0,Θ2
2->3:x,z軸が平行なのでα=0、x軸は平行だが関節がP型なのでdは変数となり向きに注意して、α,a,d,Θ=0,a2,-d3,Θ2
3->4:x,z軸が平行なのでα,d=0、xの原点が同じなのでa=0となり、α,a,d,Θ=0,0,0,Θ2

逆運動学

先端の位置が決まったときに、それぞれの関節をどの角度に曲げればよいでしょうか。逆運動学を解くというのは本質的にはそいう問題になります。ところが、先端の位置が決まったとしてもすべての関節の向きが一意に決まるわけではなく、複数のとりえるパターンが出来てしまったり、解が無い場合もありえます。これが逆運動学の難しさです。一つの方法としてはNewton-Raphson法を使った数値的解法です。初期値が解に十分近ければ早く収束します。ほかのポーズを求めたい場合は別の初期値を与える必要があります。ただし、同じアルゴリズムですべてのアームに利用できます。別の方法としては、閉形式解を求める方法です。ただし適用できるものは①3つの隣接する関節軸が一点で交差する②3つの隣接する関節軸が平行のどちらかを満たしている必要があります。一般的に、現在実用されている6DoFアームはどちらかを満たしています。特に最後の3つの関節は回転関節であることが多く、これは手首を再現したようなものです。そして最初の3つの関節は先端の位置を決める為に利用されます。そうすることで位置決めと先端部の操作を切り分ける事ができます。一般的な逆運動学の解法を以下に示します。
①DHパラメータテーブルを埋めます。(手首中心とframe4,5,6を一致させてとると良い)
②ベースフレームに対する手首中心の位置関係を明らかにする。
③式を満たすように各関節の位置(特に位置決め関節であるJ1,J2,J3)を決める。
④各関節の位置が求まったら、逆に先端の位置を導出する。
⑤回転行列を使って各関節におけるオイラー角のセット算出する
⑥出てきた解から最適なものを選ぶ

簡単な例としてRRP型のアームの場合を考える。
先端の位置=zcはわかっているとして、Θ1, Θ2, d3の導出をする。
Θ1 = atan2(yc,xc)
Θ2 = atan2(s, r); r^2 = xc^2 + yc^2, s = zc - d1
d3 = sqrt(r^2 + s^2) = sqrt(xc^2 + yc^2 + (zc - d1)^2)

Project

github.com

Gazeboは物理演算を含むシミュレーション環境です。内部ではgzserverとgzclientが動いていて、起動するにはまずserverを起動し、clientを起動する必要があります。いっぺんに起動する場合はgazeboを使います。

URDFはxmlファイルでrobotモデルを表すのに使います。例えば、関節の構成やリンクの長さを定義します。同じようにxacroファイルを使ってurdfファイルを複数のxacroファイルに分けることもできます。

RVizはROSを可視化するツールです。ロボットの予測、意思決定、応答のすべてを可視化することが出来ます。RVizを使うとセンサー、カメラ、Lidarデータの可視化をリアルタイムに行うことができます。RVizはシミュレータではないので物理エンジンは入っていません。そのため、衝突や力学は考慮されません。rvizを起動するには、roscoreを行ってからrosrun rviz rvizを行います。

MoveIt!は操作、運動学、制御のためのモーションプランニングフレームワークです。MoveIt!はそれ自体でモーションプランニング用のアルゴリズムを持っていませんが、インターフェースを提供しています。このインターフェースはrequest/responce方式で実現されています。特に、ロボットを干渉がない経路を生成し、予め設定した速度や加速度の制限に従って軌道を演算してくれます。

ROSのRobotStateクラスは運動学機能にアクセスできますが、逆運動学は独自で実装する必要があります。

move_groupノードはMoveIt!のプライマリノードで、パッケージの提供とセントラルハブとしての機能を持ち、ほぼすべてのコンポーネントとの通信を提供します。

Rvizを使ったtransform matrixの導出にはrosrun tf tf_echo [reference frame] [target frame]を使うと良い


続く。。。

Robotics② ROSの基礎

ROSの基礎

導入

概要

ROSはロボット開発のフレームワークとして開発された。特にアイデアを簡単にシェアするために移植性が重要なコンセプトになっている。

ノードとトピック

データ取得、意思決定、応答を行うためにそれぞれのプロセスはノードとして定義することが出来る。ノード間はROSmasterが定義するトピックと呼ばれるメッセージによってデータのやり取りを行う。メッセージには名前を付けることが出来る。ROSmasterの定義が終わったあとは、ノード間で独自にデータのやり取りが行われる。パラメータサーバーは中央集積所として働き、ノードがデータを必要に応じて見れるようにする。データを送信する側をpublisher、受信する側はsubscriberと呼ばれる。データはノード間で行われ、ROSmasterはそこに介在しない。一つのノードから複数のノードに送信することもできる。

メッセージ送信

メッセージはあらかじめ定義されているものがあり、センサーのデータや、画像のデータなどは既に用意されている。もちろん自分のデータタイプを定義することもできる。名前付きのメッセージであってもデータ型は何でも良い。

Service

リクエストレスポンス型のデータのやり取りもある。それはROSServiceと呼ばれる。例えば露光時間を送ったらその時間露光した画像を返すような仕組みもできる。

計算Graph

これらのノードはCompute Graphとして考えることが出来る。グラフはrqt_graphというツールを使って可視化することが出来る。

ROS環境構築

ROSの各ディストリビューションをインストールしたらsetup.bashというバッチファイルがあるのでそれを

source /**/setup.bash

で実行すると環境変数などが設定されます。この時、

./

を使って実行すると、新しいターミナルセッションが開き、そこで環境変数が設定されます。一方sourceを使うと、現在のターミナルセッション上で環境変数が設定されます。どちらも一時的な環境変数になってしまい、ターミナルを落としたら失われてしまいます。もしそれを避けたいならsetup.bashの内容をbashrcに書きましょう。それは

"source /**/setup.bash" >> ~/.bashrc

で実現できます。

ROSの実行

ROSを実行するにはマスタープロセスの実行が欠かせない。マスタープロセスは、実行中のノードに名前を与え、認証を行い、全てのノードを登録し、ログをまとめ、ノード間の接続を調整します。その起動にはターミナル上で

roscore

をタイプします。止めるにはctrl+cです。
次にノードを起動します。ノードの起動には

rosrun package_name node_name

を使います。packageは複数のnodeを含んだ概念です。上記のコマンドで起動したいノードを起動することができます。

どのノードが起動しているか見るには

rosnode list

をタイプします。所望のノードと、rosoutというものがあると思います。rosoutはログを管理しているノードでroscoreを実行すると自動的に起動します。

どういうTopicが飛び交っているか確認するには

rostopic list

をタイプします。/rosout_aggはrosoutが発行しているtopicです。それ以外はノードが発行しているTopicです。

どのTopicがどのノードからどのノードに発行されているか、どういう種類の情報か確認するには

rostopic info /nodename

を実行します。Topicに含まれるメッセージと、どのノードからどのノードに発行されているかが表示されます。

メッセージの中身の変数として何が含まれているか詳細な情報を知りたい場合は

rosmsg info message_type_name

を実行します。そのメッセージにどういう変数が含まれているかの情報が表示されます。同じようにそのメッセージの定義を確認したい場合は

rosed info message_type_name

を実行します。特にこのメッセージがどういう意図で使ってほしいかコメントで書かれていることが多いです。

実行中にTopicがどう変わっているか知りたい場合があります。その時は

rostopic echo node_name

を実行します。ROSの世界ではSI単位系が主に用いられています。

PackagesとCatkin workspace

導入

CatkinはPackageのマネージメントシステムです。そこではPackageを追加してコンパイルするようなことを行います。まずはworkspaceを作成しましょう

作り方

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
※この時CMakeLists.txtのシンボリックリンクが/opt/ros/kinetic/share/catkin/cmake/toplevel.cmakeに作られます。
cd ~/catkin_ws
catkin_make #ビルド。これはcatkin_wsで実行する。
lsを実行して、buildとdevelディレクトリがあればOK。buildはC++で書かれたパッケージ用のものであり、develにはsetup.bashが用意されるので、パッケージを使う前にsorceで実行する必要がある。
ビルドに失敗した理由がpackageが足りない場合もある。その時は
sudo apt-get install ros-**を実行して足りないパッケージをインストールする。

Tips

複数のnodeをrosrunで実行するよりも簡単な方法としてroslaunchが用意されている。roslaunchを使うにはcatkin_makeが実行されている必要がある。そしてその時にdevel/setup.bashを使うのでsource devel/setup.bashを行う。次に

roslaunch package_name *.launch

を実行する。そうするとlaunchファイルに書かれたnodeが実行される。

もしパッケージの依存関係を調べたり、ダウンロードしたりしたい場合はrosdepを使う。

rosdep check package_name

そうすると何が足りないか表示される。足りないパッケージをインストールするには

rosdep install -i package_name

を実行する。うまくいかない場合もあるのでもしだめならapt-getで所望のパッケージをインストールすると良い。

ROSのパッケージはsrcディレクトリに配置される必要がある。もし新しいパッケージを作る場合は

catkin_create_pkg package_name dependancy...

を実行する。また、もっと簡単に作る場合は

catkin_create_pkg first_package

を実行する。

package内のディレクトリには以下のフォルダが配置されます。

  • scriptsはpythonコード用
  • srcはC++ソースファイル
  • msgはカスタムメッセージの定義
  • srvはサービスメッセージの定義
  • includeは依存関係のあるもののheader/libraries
  • configはコンフィグファイル
  • launchはラウンチファイル
  • urdfはUniversal Robot Descriptionファイル
  • meshesはstl等のモデリングファイル
  • worldsはGazeboシミュレーションのXMLファイル

Write ROS Nodes

ROS Publishers

Publishersはtopicにmessagesを送ります。

pub1 = rospy.Publisher("/topic_name", message_type, queue_size=size)

引数は以下です。

  • /topic_name : publisherが持つtopicの内、送信するものの名前
  • message_type : topic_nameで定義された送信するメッセージのタイプ
  • queue_size:Noneか設定しなければ同期通信、値を入れていれば非同期送信となり保存するメッセージの数の意味

また、同期送信の場合は他のpublisherが既にtopicを送信済みであれば、自分のtopicはブロックされます。最初にtopicを送ったpublisherがメッセージをバッファにシリアル化し、バッファはそれぞれのtopicのsubscriberが書き込みます。非同期送信の場合はpublisherはメッセージを送れるようになるまでためておきます。queueの概念に基づいており、新しいメッセージが来たら古いものから消えていきます。publisherが作られ、データ型が定義されたら、以下で実際にメッセージを送ることが出来ます。

pub1.publish(message)

Create Nodes

ノードを作るにはまずcatkin_wsディレクトリ配下のsrc内にpackage_nameのディレクトリを作ります。例えばその中に簡単なbash scriptを置いて、chmod u+x script_nameで権限を与えて実行します。実行にはcatkin_wsディレクトリでcatkin_makeを実行します。そして、source devel/setup.bashで新しい環境を作り、rosrun package_name script_nameを実行します。もしpythonスクリプトを作るのであれば、package_nameディレクトリ配下のscriptsに移動してスクリプトファイルを生成します。そのスクリプトファイル名がノードの名前です。
スクリプトには以下の宣言が必要です

import rospy
from std_msgs.msg import Float64

2行目のコードはmessage_typeを定義するのに重要です。
include宣言を終えたら以下のようにtopicを宣言します。

pub = rospy.Publisher('/topic_name', Float64, queue_size=10)

次にclientノードを初期化し、master nodeに登録するために以下の関数を呼びます

rospy.init_node('node_name')

また、ROSのメインループの周期を以下で決定することが出来ます。例えば10と設定されていたら、10Hzということです。

rate = rospy.Rate(10)
...
while True:
pub.publish(message) # メインループ内で送信したいmessageを送る
rate.sleep() #メインループでsleep関数を呼ぶことで10Hz周期となる

pythonコードを書いたら、ノードを実行できます。まずはcatkin_makeでビルドします。自作ノードの実行にはlaunchファイルを起動して、元ある環境のノードを起動します。それからrosrun package_name node_nameで自作ノードを実行します。

Create Service

ROS Serviceはrequest/response方式の通信です。あるノードがrequestを受信したらresponseとしてrequestの送信元のノードに返します。Serviceは以下のように宣言します。

service = rospy.Service('service_name', serviceClassName, handler)

引数は以下です。

  • service_name:サービスの名前で他のノードからどのサービスか特定するために用います
  • serviceClassName:サービスが実装されているファイル名で、.srv形式のテキストファイル。requestとresponseの両方のメッセージの型が記載されます
  • handler:適切なresponseメッセージを返す

また、別のノードからROS serviceを呼ぶ為のAPIのようなものを作るにはServiceProxyを使います。

service_proxy = rospy.ServiceProxy('service_name', serviceClassName)

これを使ってresponseを返すには、まず新しいメッセージをserviceClassNameResponse()で作成して、それからメッセージをservice_proxy(msg)で送ります。

新しいserviceを作るにはcatkin_ws/src/packagename/srvディレクトリを作成し、そこに.srvファイルを用意します。そこは"---"で分割された2つのセクションに分かれていて、最初のセクションはリクエストメッセージの定義(型と変数名)を書きます。二つ目のセクションにはレスポンスメッセージを定義します。もちろん独自定義の型を使う事もできますがmsgディレクトリに.msgファイルを作成してそこに定義を書く必要があります。

次にCMakeLists.txtを編集する必要があります。CMakeListsはGNU makeのmakefileと同じようなコンセプトです。find_package()関数は必要なパッケージを定義するものです。次にadd_service_files()関数でどのファイルがコードを生成するために必要か定義します。最後にgenerate_messages()関数で実際にコードを生成します。

これで大体ビルドができるのですが、package.xmlについて述べておきます。このファイルはpackageの名前やバージョン、著者、依存性について定義しておくものです。パッケージにはビルド時の依存関係と、実行時の依存関係があるのですが、rosdepを使うとどちらの依存関係も調査することが出来ます。それぞれの依存関係はpackage.xmlファイルの中でで定義されます。必要に応じて追加することが出来ます。

以上のようなことがわかったらビルドします。catkin_wsに戻ってcatkin_makeです。これらが正常に動けばdevel/lib/python2.7/dist-packagesにパッケージが作られて、その中のsrv以下に作成したserviceが配置されます。これで作ったパッケージがPYTHONPATHであるdevel/lib/python2.7/dist-packages配下に置かれたことになり、使うことが出来るようになりました。

他のノードを作成するには同じようにpackage_name/scriptsの配下にノード名のファイルを追加します。

次にパラメータの値が決まっている場合、それはlaunchファイルに書くことが出来ます。launchファイルのタグの中にそれを記載することが出来ます。

serviceが実行中にパラメータを渡したい場合はrosservice callを使います。何かおかしい場合はroscoreのコンソールを見ると何が起こっているかが出力されています。他にもserviceのパラメータを変更したい場合にはrosparam setを使うこともできます。

ターミナルでエラーが出る場合はsource ~/catkin_ws/devel/setup.bashがそれぞれのターミナルウインドで実行されているか確認してみてください。

Subscribers

Subscriberは他のノードからの情報を受信するためのものです。

sub = rospy.Subscriber("/topic_name", message_type, callback_function)

/topic_nameは受信するtopicの名前です。
message_typeはtopic_nameの型です。
callback_functionは関数の名前で、メッセージが来た時に呼ばれる関数の名前です。メッセージは関数の引数にそのまま渡されます。

Subscriberノードを作る場合はPublisherと同じようにrospy.init_nodeで初期化します。rospy.spin()を使うとシャットダウン要求をノードが受信するまでブロックします。

Robotics① 導入

以下に学習時のメモを載せます。

概要

Lesson 2 ロボットとは

ロボットの定義

センサーがあって、意思決定が出来て、それに対して応答できるもの。
センサーはカメラ、Lidar、超音波、マイク、温度圧力センサー、においセンサーGPS等がある。磁気センサーも。
意思決定は決定木で表されることもあるし、DNNで表されることもある。
応答は、物理的な駆動もあるし、光、音等もので実現できる。

Project Search and Sample Return

シミュレータを使って、ローバーを操作します。ローバーはデータ取得、予見、行動というステップを踏んで動かします。データ取得ではカメラの画像を取得し、その画像の画素に閾値を設定して道と障害物を分けます。次にその画像をバードアイビューに変換する為に、Perspective Transformを当てはめます。opencvではgetPerspectiveTransform()で変換行列を求めて、warpPerspective()で画像を変換します。変換行列を求めるには、もとの画像のグリッドの交点の位置と、欲しい画像上でのグリッドの位置をgetPerspectiveTransform()に与えます。次にそのバードアイビューから全体の地図を生成するためにローバーの現在位置を原点とした座標系から、世界座標系に射影します。これには得た鳥観図を回転したり、縮尺を変えて整えます。これをローバーの動きに合わせて合成していくと、ローバーの画像から全体の地図が出来上がります。
次にどこに向かうかですが、今回は道と障害物を画像の閾値で分けたので、それを利用します。道と判定された領域の角度の平均をとりました。これで行くことのできる方角がおおよそ算出できます。その角度に合わせてローバーが向きを変えるように出力します。
GitHub - romth777/RoboND-Rover-Project: Project repository for the Unity rover search and sample return project.

身の程知らずの初心者がUdacityで機械学習を勉強した話 ②教師あり学習

以下に学習時のメモをまとめていきます。

Lesson1 導入

はじめに

教師あり学習はラベル付けされた例をモデルに与えます。ラベルを正しく予想できるかというのがこの方法のコアの部分です。ニューラルネットワークや、アンサンブルラーニング、回帰分析、クラス分類は教師あり学習のアプローチ方法の一つです。これらのどれをどういうデータに当てはめて、それをどう評価するかというのがこのコースのゴールです。

教師あり学習の例

教師あり学習は例えば、顔認識や、レコメンデーションシステムの中で利用されます。口座情報から詐欺かどうか調べたり、顧客情報をクラスタリングするのは教師なし学習です。

Lesson2 Regression(回帰)

線形回帰

線形回帰ではデータのすべての点にふさわしい多項式の線を引いて、その線からの距離が近くなるようにします。一次の多項式の場合は各点の平均を通る直線となります。
次数を上げていくと、与えられたデータに対しては良い結果を得られているように見えますが、その間の点にとってみるとふさわしくない直線となってしまいます。例えばデータの傾向とは異なる性質を示してしまうことになります。そういったことは避けるべきです。

多項式回帰

与えられたデータをxとしたときの係数を求めるのが多項式回帰です。計算には行列を使って、Xをデータの行列、wを係数のベクトル、yをラベルデータのように表します。

エラー

機械学習を使う場合に気にしなければいけない誤差は多岐にわたります。それは測定値自体のノイズ、悪いデータ、転記ミス、その他の影響など様々です。これらの誤差を除いた検知したいシグナルに対してモデルを当てはめる必要があります。

Cross Validation

モデルを複雑にするとあるところで当てはまらないデータが出てきてしまいます。機械学習に当てはめるデータは独立同一分布で取得されたものであるという仮説に基づいています。これが破れた場合、当てはめることはできません。交差検定ではデータをいくつかのFoldに分割して、そのFoldのうちいくつかを訓練データ、残りを試験データとします。この組み合わせを変えることで、モデルが過学習していないか確認します。即ち、いくつかの複雑度の違うモデルを用意して、それぞれのモデルを学習させて交差検定を行います。交差検定の結果、データのエラーが試験データで大きくなったとするとそれは過学習です。このように交差検定はモデルが過学習していないか検知するのに利用することが出来ます。

入力

入力はベクトルであっても良い。即ち、複数のスカラー量を入力としてモデルを構築し、その値を複合的に考慮して結果を出力することもできる。例えば、住宅価格を予想するときにその敷地面積だけでなく、駅からの距離も考慮して予測精度を高めることが出来る。

Lesson3 その他の回帰

Parametric regression

多項式によって近似するモデルであり、各次数のデータに対して係数を持っている。その係数を調整する。

K nearest neighbor

ある値を与えられたときに、そこから予測値算出方法を考えます。予測値はある値の付近の実際のデータのうち、k個のものを考え、そしてそれらの値の平均値を予測値にすることにします。これがKNNです。

Parametric or non

パラメトリックアプローチは学習は遅いが、モデルが出来てしまえば予測値の算出は早い。逆にノンパラメトリックアプローチは学習は速く、例えば新しいデータに対して学習するのは早いが、モデルが出来た後に予測値の算出は比較的遅い。

Lesson 4 Regression in sklearn

Continuous Supervised Learning

出力がYes/NoのようにBinaryであり離散的な値(Discrete)ではなく、連続的な値を取り扱う場合、Continuousという概念が重要。ContinuousはOrderdである場合が多く、DiscreteはNo Orderedである場合が多い。

Coding

1.1. Generalized Linear Models — scikit-learn 0.19.0 documentation

linear regressionを使うには以下のようにします。

from sklearn import linear_model
reg = linear_model.LinearRegression()
reg.fit(X_train, y_train)
reg.predict(X_test)

reg.coef_ ## slope
reg.intercept_ ## coefficient

reg.score(X_test, y_test) ## R^2

テストデータとトレーニングデータでscoreを出して、モデルが過学習していないか確認することもできる。scoreはR^2を算出する。

Linear Regression Errors

Error = actual - predictedで表される。データ全体のエラーを出すには符号の影響を抑えるために絶対値を取るか、二乗して、その和を取る。このエラーを最小化することがデータに対する最適な回帰モデルとなる。アルゴリズムとしては、Ordinary Least Squares(OLS)やGradient Descent等があり、SklearnではOLSがLinear Regressionに使われている。

Sum of Squared Error

SSEを最小化することが全てのデータ点からの距離が最適な位置に回帰直線を引くことが出来る。また、それは計算が容易なので好まれている。ただ、SSEは完璧ではなく、例えばデータが広範囲に分布しているものと、狭い範囲で分布しているものでモデルを作ったときに、どちらも同じような回帰直線が得られたする。広範囲のデータから学習したモデルではその直線はどの入力値においても比較的妥当な値を返すことができるが、狭い範囲のデータしか与えられなかったモデルではもし与えられたデータ以外の領域のデータが与えられた場合に、その振る舞いが正しくモデル化できていない可能性がある。そういう意味でSSEは完璧ではないといえる。また、データの数が増えれば増えるほどエラーの値が増えることになるので、データの数が違う場合に、そのSSEの値で単純に比較することが出来ない。

R squared

R二乗は0.0 < R^2 < 1.0の範囲にあり、1に近づくほどデータの特徴を捉えている回帰直線が作れたといえる。

Visualizing

データと回帰直線を重ねて表示するとわかりやすい。

Compare Classification and Regression

項目 | Classification | Regression
出力の種類 | descrete(class labels) | Continuous(number)
何を見つけたいか | decision boundary | best fit line
モデルの評価値 | accuracy | SSE

Multi-Variate Regression

入力の変数が複数あって、それらから出力値を予測するもの。y=ax1 + bx2 + cのような形。

Lesson 5 Decision Trees

*

ClassificationはTrueとFalseに分けたり、写真を分類したりする。Regressionは連続な値をとる関数。実際の点から関数を学び、新しい点に対応するようなもの。



続く。。。

異常検知と変化検知のまとめ

経緯

現在以下の本の輪読を行っています。

その内容について、全てではないですが学習した内容をまとめておくことにします。

9章 部分空間法による変化検知

特異スペクトル変換法

イメージとしてはPCAの拡張版である。PCAは正方行列に対して行うものであるが、データの特徴量は時系列データに適用する窓の数に依存する。したがって必ずしも正方形になるわけではなく長方形になる場合も存在する。そこでPCAを拡張して長方行列に対しても適用できるように考えた。

10章 疎構造による異常検知

基本的な考え方

ホテリングT二乗法はデータが通常は一定でそこから突然外れるような異常データの検出が得意ですが、ある程度ノイズを持った出力値からの異常検出は不得意です。そういった出力値の異常値を検知するには変数そのものではなく、変数の関係性に着目すると良い場合があります。
この時変数同士の関係は対マルコフネットワークや、対マルコフグラフといった形で表します。
直接相関と間接相関の話。実際3変数のデータを見ると相関があるように見えるが、それを輪切りにして2変数の関係をみたときには相関がないことがある。これを間接相関という。つまり、3次元目の変数を介して、残りの2変数が相関を持っているという事である。

正規分布に基づく対マルコフネットワーク

M次元の正規分布に現れる精度行列のうち、iとjが異なる位置の要素が0の時、これは直接相関があるといいます。
反対に精度行列は0だが、その逆行列である共分散行列が0ではない場合、間接相関があるといいます。
結果として、精度行列は直接相関を、共分散行列は直接相関と間接相関の和をあらわします。

疎なガウス型グラフィカルモデルの学習

一般的にグラフの構造を疎にしたいというモチベーションが存在します。これは異常値検出におけるノイズへの頑強性という意味でも重要です。
どのように疎な構造を獲得しましょうか。グラフィカルラッソという手法を紹介します。
疎な構造を獲得するために適当な閾値を設定することは適当ではありません。なぜなら精度行列の中身を適当な閾値で操作してしまっては、それはもう確率分布の精度行列を表すものとは言えなくなるからです。確率分布の特性が失われてしまいます。ですのでそのような手法は望ましくありません。
そこで、精度行列に疎な解を好むような事前確率を与えたものを考えます。結果的にそれは正則化項の形で表現されることになります。

次にその値の最適化を考えます。
まず勾配を考えて勾配が0になるような式を解くことにします。ですが、行列式の勾配を最適にしていく操作は複数の行について考えると複雑になってしまいます。そこで、例えば行列の特定の位置の行を一番下に持ってきて、その行を最適化し、その操作を全ての行が最適になるまで繰り返すことを考えます。これをブロック座標降下法と本書では呼んでいます。

疎構造学習に基づく異常度の計算

応用例としては外れ値解析、異常解析があります。

身の程知らずの初心者がUdacityで機械学習を勉強した話 ①機械学習とは

以下に学習時のメモをまとめていきます。

Lesson 1 ようこそMachine Learning Nano Degree Program(MLND)へ

はじめに

機械学習は様々な分野で活躍しています。このコースでは実際にどういう領域に機械学習が応用出来るのか、そしてプロフェッショナルな人達はどういうアルゴリズムをどういうデータに当てはめて、どういうツールを使って実現しているのか、それらに関する主要な考え方を学んでいきます。それらを学ぶために、このコースでは数学、例えば統計学線形代数幾何学の内容も含みます。

機械学習(ML)とは

MLは様々なものを描写します。その延長線上には人工知能の実現があるともいえます。データを処理し、理解し、賢く応答する。そういう事に機械学習は活用できます。また別の言い方をすると、統計学のように、実際のデータに発展的なアルゴリズムを当てはめて、そのデータを理解し、比較し、評価し、まとめるようなものです。あるいは、アルゴリズムのようにデータを入力すると動き、出力を返します。機械学習は様々な分野におけるこれまでの業績の次世代的な複合の成果とも言えます。

これまでのシステムとの違い

これまでのシステムはその判断というものを直接的にコードに書いていました。例えば、顔を見てその人が誰かを判別する際には、眼鏡をかけているか、目や髪の色は何色か等、そういうことをコードに直接書いてそれが誰なのか判断してきました。MLでは違います。これらはただ画像データをモデルに流し込むことで実現されます。それらの識別に使う特徴的な情報はデータセットの形で引き渡され、そのデータセットによってモデルは学習します。そして、今ではコンピュータの信頼度は増しているので、そういったデータというものが共有化されているのです。そういった事情を踏まえても、MLは非常に便利なツールになってきています。

コースのアウトライン

まずは一般的なモデルビルディングとバリデーションについて学習します。
次に、教師あり学習、教師なし学習、強化学習について学習します。これらは異なったフィールドに適用され、それらをどう使うべきか学習します。
そして、それぞれのパートには実習フェーズがあります。これらを通じて、機械学習を体験してください。

Lesson 2 機械学習とは何か

導入

機械学習とはコンピュータを訓練し、所定のタスクを実行できるようにすることです。訓練にはデータを使います。
データから過去の経験をコンピュータに学ばせてそれを現在のデータに当てはめます。

決定木(Desision Tree)

例えばアプリのレコメンデーションシステムを考えます。ユーザーの性別、年齢、それからダウンロードしたアプリケーションのレコードがあった場合に未知のユーザーにはどのアプリを推薦するのが良いでしょうか。
それを考えるために与えられたデータを見てみましょう。今回のケースではアプリは年齢に関係がありました。即ち例えば20歳以下はAというアプリをダウンロードしていて、20歳以上はBというアプリをダウンロードしているように見えます。
こういった場合に決定木型のモデルが適用できます。例えば、データを入力した時に年齢を見ます。年齢が20歳以下であればA、20歳以下であればBというアプリをダウンロードするように提案するシステムを作ることが出来ます。
これはもちろん人間がコードすることなく、学習によって取得される特性として獲得されます。
決定木型のモデルを使えばこういったことが実現できます。

Naive baise

例えばe-mailのスパム検出を見てみましょう。過去のメールにタグをつけて、それがスパムかそうでないかラベルを付けることにします。
それらのメールの内容を確認して例えばスパムメールの中で富士山という文字列が入っているメールの数を数えます。同時に非スパムメールの中で富士山が含まれるメールの数を数えます。
例えばスパムメールで20通、非スパムメールで5通だとすると、富士山という文字が入っているメールがスパムである確率は80%となります。
なぜなら富士山が含まれるメールの総数25通に対して20通がスパムなので80%となります。
これを富士山という文字列だけでなく、様々な文字に対して行います。また他にも誤字が含まれているか、タイトルが適切でないか等も含めてトータルで判断する場合もあります。
これらの確率を掛け合わせて、そのメールがスパムであるか、そうでないかを判断します。

Gradient descent(勾配降下法)

学習に際して、この概念は重要です。これは最もエラーを最小化する方向に山を降りていくイメージのものです。最適解を探す際に、どの方向に降りるべきか決める為に用いる手法です。
エラーの山があって、その勾配を計算し、その分下に降りて、山を降りきるまで続けます。これはとても重要な概念なので覚えることをお勧めします。
山としてどういうエラーを定義するのか、そしてどのように山を降りるのか、またどこがゴールなのかを定義することが機械学習において重要になります。

Linear Regression(線形回帰)

データが線形である場合、その解析には線形回帰を使うことが出来ます。
回帰ではデータのすべての距離を最小化するような直線を引きます。これはデータの2つ以上の特徴が線形であることが前提です。線形というのはy=ax+bで表すことが出来るという事です。
このような前提が成り立つ場合、その直線を求めることができれば、任意の地点の値を片方のデータから算出することが出来ます。これが線形回帰の目的です。
この線形回帰を学習する場合、そのエラーは直線と各データポイントの距離を求め、それをすべてのデータポイントに関して和をとった値として定義されます。
このエラーを最小化することが線形回帰で最適な直線=最適解を求める方法になります。
この最適解を求めるのに先程のGradient descentを活用することができ、エラーが最小になるように直線の傾きや切片などの値を変えていきます。

Logistic Regression(ロジスティック回帰)

例えば入学試験の点数と入学後のGPAの値の組み合わせから、その学生が卒業できそうか、そうでないかを考えます。
この時、訓練用データはそれぞれの値と、卒業できたかそうでないかが与えられているものとします。
これらをうまく分類する、即ち、点数とGPAが与えられたときに卒業の可否を返すようなものを作ろうと考えます。
そのような考えに基づき、2つの変数の関係から卒業可否の境界を直線で表し、例えば新しいデータポイントがその直線の上側にいたら卒業でき、下側にいたら卒業できないというように分類するモデルをロジスティック回帰モデルと言います。
この時ほとんどの訓練用データはその分類に従っていますが、その全てがその分類に従っているわけではないという事に注意してください。

このモデルは訓練用データからどのように最適な分類直線を導き出しているのでしょうか?
まずはランダムな直線をデータの分布上に引きます。この時ラインの上側が合格、下側が不合格と勝手に決めます。この時訓練用のデータはその分類に従っているでしょうか?ランダムに引いた線なのでまだ得たい結果にはなっていません。そして、例えば間違えて分類してしまったデータ、つまり、正解は合格なのに不合格と判定してしまったり、その逆のデータの数を数えます。
次に、線形回帰と同じように例えばGradient descent等を使って、その間違いが少なくなるように直線を動かします。これを繰り返して、エラーが最も少なくなるような直線の式を導き出します。これが学習のプロセスです。
一方で実際にはデータの数を数えるのではなく、Log Loss関数をエラーの算出に用います。これは間違えて判定してしまったデータ点に対して大きなペナルティを与え、正しいものには小さいペナルティしか与えないようにするものです。そして、それをすべてのデータ点に対して和を取り、それをエラー値とします。これを最小化するのです。このペナルティは分類した直線から離れれば離れるほど大きな値となり、正しい分類に近づき、そして正しい分類になれば小さい値となるようにします。そうすると正しい分類が得られたときには、その和が最小化されているという事になり、そこがゴールとなります。このようにして正しい分類方法を学習させるのです。もちろん最小化させるにはGradient descentを使います。

Support Vector Machine(サポートベクトルマシーン)

SVMの場合はエラー関数が若干異なります。そのエラー値はX-Y軸のどちらかに平行な境界との距離で求められるのではなく、分類直線に垂直な形で求められます。もちろんこれは簡単な例であって実際には単なる直線ではなく、超平面上の直線という事になります。また、その直線から離れたデータ点は考慮に入れず、境界にある範囲以内のデータ点のみを使ってエラーを算出します。その範囲のことをマージンと呼びます。また、学習途中の境界線とマージン内の各データの距離のうち最も最小のものに着目します。SVMではこの最も小さい距離が最大になるように境界線を決めます。即ち、マージンの範囲内にあるデータ点の境界との距離のうち、最も最小のものが境界線を変更する過程において最大になるように境界線を決めるのです。別の観点から境界線を見ると、マージンの範囲内において境界はラベルに関わらずどの点からも一定の距離を保っているようになります。もちろん距離を最大化するのにはGradient Descentを使います。この方法をSVMと呼びます。サポートという名前が付いたのは、各点が境界線から一定の距離を保ち、かつどちらの領域からもその間を通っているように見えるので、各点が境界線を支えているように見えるからです。

Neural Network

例えばデータが線形分離できないとしましょう。その場合これまでの直線で分類する方法では対応しきれません。どうしますか?円を使いますか?2本の直線を使いますか?これらはおそらくどちらもうまく機能するでしょう。例えば2本の直線を使ってみる事としてみます。2本の直線はGradient descentを使ってLog Loss関数で求めることとします。これをNeural Networkと呼びます。なんでそう呼ぶのでしょうか?それぞれの直線に分類条件があり、そのどちらもYesの場合にAに分類し、それ以外の場合、即ちどちらかがNoの場合にはBと分類するように組み合わせるのです。もう少し細かく見てみます。このそれぞれの直線の分岐条件を一つのノードに置き換えます。例えば一本目の直線の判定がYesなのかNoなのかと分類します。次に別のノードで二本目の直線の判定がYesなのかNoなのかと分類します。今回の場合は、2つのノードへの入力は同じデータであることとします。最後にそれら2つのノードの出力を入力とした新しいノードを用意し、Yes/YesであればYes、それ以外はNoと返すようなものを用意します。これは例えば1,1で1を、それ以外のパターンで0を返すようなノードを用意すれば実現できます。最終的にこれらを結合してモデルを完成させます。これが今回求めたかったモデルであり、このようなノードを組み合わせて実現するモデルのことをNeural Networkと呼びます。もちろんノードを追加することもできますし、レイヤーを増やすこともできます。特にレイヤーを深くして言った場合にDeepNeuralNetworkと呼ばれます。

Kernel Method

同じようにデータが線形分離できない場合のことを考えます。例えばそれぞれのデータから別の特徴量(例えばr^2=x^2+y^2のr等)を算出したとします。その値を使ってデータを分離できないか?というのがKernel methodの基本的な方針です。その値を使って分類できる場合、その別の特徴量空間の分類平面をもとの空間に戻した形で表現できれば、それが分類直線になります。これをKernel Trick(カーネルトリック)と呼び、SVMで使われる手法です。

小まとめ

ロジスティック回帰は忍者みたいなものです。データをラベルに基づいて一直線に切り分けます。SVMは境界付近のデータを注意深く見て、その境界を適切に切り分けます。ただし通常一直線に、です。Neural Networkは複数の忍者が存在し、それぞれ異なった切り分け方を行い、最終的にその結果をマージするようなものです。Kernel Trickは別の考えに基づいていて、そのデータをどうやって切り分けたら良いか考え、別の次元に持ち込んでそのデータを切り分けます。
ところでXORはどうやって実現しましょうか?ちょっと考えてみてください。

K-means Clustering(非階層的手法)

例えば町にピザ屋を作るとします。近所には3つの顧客の集合がまとまってあって、その近辺に3件のピザ屋を効率よく建てたいです。どうしましょうか?まずはランダムな位置にピザ屋を配置します。すると顧客はどのお店を選ぶかと言えば、最も近くのお店を選ぶと思います。そのまとまりを一つのClusterとしましょう。これが最適なクラスタリングかと言われるとまだ甘いように見えます。もっと最適なクラスタリングが出来るとしたらどのような形が良いでしょうか。そうです。お店の位置を動かして、それぞれの顧客の位置からお店までの距離が最も小さくなるようにすればよいのです。どのように動かすかというと、とりあえずまとめた一つのクラスタの重心位置にお店を動かすのです。そしてもう一度最も近いお店の顧客でまとめ直してみると結果が変わり、より適切な位置にお店を配置することが出来ていることがわかります。このようなステップを繰り返して、最適なお店の位置を探す操作のことをK-means Clusteringと呼びます。K-means Clusteringはクラスタの数がわかっている場合にとても効果を発揮します。じゃあ、クラスタの数がわかっていない場合にどうしますか?

Hierarchical Clustering(階層的手法)

クラスタの数がわからない場合にはどうしましょうか。同じようにピザ屋の例で考えます。とりあえずまとめていきたいので、最も近い2つの顧客をグループとみなします。近い順にこのグループ化作業を行っていきます。途中で既にグループ化されたものと別の顧客がヒットしました。この場合は既に存在しているグループにその顧客を追加します。グループ同士であってもまとめてしまいます。最終的に顧客間の距離がある程度より大きくなったらそこでグループ化作業をストップします。これはどのぐらい離れていれば別のクラスタと見なすかというパラメータによって判断します。このアルゴリズムをHierarchical Clusteringと呼びます。この方法はクラスタの数は未知だが、クラスタ同士がどれぐらい離れているかという事はわかっているときに有効です。

まとめ

ここまでいろいろ学んできましたが、実際どのアルゴリズムを使えばよいのでしょうか?アルゴリズムの選択には実行時間や精度も含めた検討が必要になってきます。アルゴリズムを結合することもできるでしょうか。もちろんできます。この先のレッスンでは、実際のデータを使って、その分析をやってみましょう。

Project: Titanic Survival Exploration

タイタニックの生存者を予想するあれです。
machine-learning/projects/titanic_survival_exploration at master · romth777/machine-learning · GitHub
以下の1~4に関してはチュートリアル通りに実施したものです。5は自分で特徴量を考えてそれを実装しました。

  1. 最初にメトリクスとしてaccuracy_score関数を作りました。まずは生存or死亡が予想と一致しているかその割合を精度のメトリクスとしました。
  2. 最初の予測関数に全ての乗客が生き残らなかったという結果を返す関数を用意しました。これはデータの調査の為という意味合いが強いです。結果61.62%を得ることができました。
  3. 性別毎にデータを見てみます。これは用意された関数を使いました。生存、死亡の数を比較すると、男性はの死亡が多く、女性は生存が多い事がわかりました。そこで男性であれば死亡、女性であれば生存と予測する予測関数を作りました。結果として78.68%と良くなりました。
  4. 次に男性のデータを年齢別に眺めてみます。10歳以下の男の子のほとんどが生き残っていることがわかります。これをそのまま予測関数に適応し、女性は先程と同じで、男性は10歳以下であれば全て生存、それ以外は死亡と予測する予測関数をつくりました。結果は79.35%になりました。
  5. 最後に色々データを眺めて、自分で特徴量をつくりました。私は10歳以下の男の子のデータをさらに細分化してPclass(階級)およびSibSp(搭乗する乗客の兄弟および配偶者の数)に応じて結果を変えるように予測関数を修正しました。結果として80.70%を得ることが出来ました。なぜ10歳以下の男性に着目したかと言えば、他の年齢では男性で生存している人の数が少なかったからであり、今は全て死亡で予測している関係上、スコアに与えるインパクトが小さいと考えたからです。データを調査した結果SibSpが小さい値の場合に多くの生存者がいることがわかったので、それを追加しました。2017/10/7時点でKaggleだと700位ぐらいのスコアですね。
  6. [まとめ]今回は自分で決定木を実装しました。条件式を使ってデータを小さいグループに分けることが相当します。以下のリンクが参考として挙げられていました。確かに特徴量の値に応じてデータを分類していく様子が可視化されていました。

A visual introduction to machine learning

Lesson 5 Training Models

導入

まずモデルがどれぐらいうまくかについて学びます。それはメトリクスを学び、モデルがうまくいっているかいっていないか判断できるようにするという事です。次にメトリクスに基づいてどうやってモデルを良くするかです。そこではモデルを評価(Evaluate)し、検証(Validate)し、改善します。

概要

問題があったときにどうやって解決するか考えます。問題がどのぐらい深刻で何をどれぐらい直したらよいのか考えるためにはそれを測定するツールが必要です。

前提知識

  • Mean(平均値), Median(中央値), Mode(最頻値)
  • Inter Quartile Range(Q3-Q1の四分位値), Outliers(外れ値), Standard Deviation(標準偏差), Bessel's Correction(サンプルデータから母集団データの分散を予測するために分散計算の分母をn-1にする補正。データが正規分布であれば中央付近のデータをとってきてしまい、すそ野のデータが含まれない可能性が高いのでその影響を考慮にいれたもの)
  • Pandas/Numpy/Scikit Learn

Lesson 6 Testing Model

概念

Regression(回帰)は値を予測します。一つの値が与えられたときにもう一方の値をそのデータが属している母集団の分布から予測します。Classification(クラス分類)はラベルを予測します。一つの値が与えられたときにそのデータがどのクラスに属しているか、訓練用のデータから予測します。モデルが良いかどうかを確認するためにテストをします。テストでは過学習が発生していないか確認することができます。全てのデータを訓練用、テスト用に分割し、両方のデータセットでいい予測結果が出ることが望ましいです。テスト用データを訓練用データに使うべきではありません。

実装

  • train_test_split(X, y, test_size=0.2)を使ってデータを分割します。

Lesson 7 Evaluation Metrics

Confusion Matrix

Positive/Negativeはラベルデータで、True/Falseは予測結果が正しいかどうか。TP=True Positiveは実際Positiveだし予測結果もTrue。TN=True Negativeは実際Negativeだし予測結果もTrue。FP=False Positiveは実際はNegativeなんだけどPositiveと予想し予想結果がFalseのもの。FN=False Negativeは実際はPositiveなんだけどNegativeと予想し予想結果がFalseのもの。

Accuracy

Accuracyはモデルがどれぐらい良いか測るもの。どれぐらい正確に分類できたか。どれぐらい正しいか。全体のデータ数のうち、正しく予測できたものつまりTrueのものの割合であり、Accuracy=(TP+TF)/ALLとなる。accuracy_score(y_true, y_pred)で計算可能。ただしモデルがほとんどの場合の予測は正しいが、たまに間違えるようなケースで、その間違いを指標で表したいとするとデータ数が多くなった時に誤りの数が極めて少ないと指標には現れません。その場合はAccuracyを使うことは不適当です。

Precision

例えばスパムメールにおいてはFP(=実際はスパムでないがスパムとして破棄してしまうこと)は許容できないが、FN(=実際はスパムだがスパムではないと分類すること)は許容できる。このFPを見る指標としてPrecisionを使うことが出来る。Precision=TP/(TP+FP)。TPをTPとFP(=実際はNegativeだがPositiveと予測したもの)で割る。HighRecallモデル(=FPは許容できる)ではこの値は重要ではない。

Recall

例えば診断においてはFP(=実際は病気ではないが病気と診断すること)は許容できるが、FN(=実際は病気なのに病気ではないと診断すること)は許容できない。このFNの具合を見る指標としてRecallを使うことが出来る。Recall=TP/(TP+FN)。TPをTPとFN(=実際はPositiveだがNegativeと予測したもの)で割る。HighPrecisionモデル(=FNは許容できる)ではこの値は重要ではない。

F1 Score

PrecisionとRecallを一つのスコアで表すことを考えたのがこのF1スコア。F1 Score = 2 * Precision * Recall / (Precision + Recall)。普通に平均を取ってしまったらあまり特徴を表さない値になってしまうので、調和平均を取っている。これは低い値に近づくような平均の取り方になっていて、どちらかが低いとそれがわかるようになっている。

F-beta Score

F1 Scoreをもう少し一般化する為に、例えばF0.5 Score等を考えます。これはFbeta = (1+beta^2) * Precision * Recall / (beta^2 * Precision + Recall)で表されます。beta→0にするとこれはPrecisionになります。逆にbeta→∞にするとRecallになります。特にbeta=1の場合は調和平均です。このbetaの値は対象とする問題によってどちらが重要か考え、それに基づいて調整することが望ましいです。

ROC(Receiver Operating Characteristic)

データの境界線を引くときにTPとFPがすべてのデータに対してどのぐらいあるかを考えます。そしてデータの境界線を動かしていきます。両端では(TP ratio , FP ratio) = (1,1), (0,0)となります。完全に分類できた場合は(1,0)になります。また境界線を動かした時の値をプロットし、それが囲む領域の面積を考えると、ランダムモデルの時は0.5、完全なモデルの時は1、良いモデルの時はその間の値となります。つまり、その面積を1に近づけるようにするのが望ましく、それをメトリクスとしてモデルを調整するという方法もあります。

回帰モデルのメトリクス

回帰式からの距離の絶対値の平均を求める方法があります。mean_absolute_error(y, guess)です。実際これは良い方法ですが、値は微分可能でない場合もあり、あまり良くありません。そこでmean squared error=MSEを考えます。mean_squared_error(y, guess)です。他にもR2スコアというよく使われるものもあります。R2スコアは最も簡単なモデルとの比較によって算出されるものです。最も簡単なモデルとしてはデータの平均値で水平に分割し、その水平線からのMSEを算出するようなモデルです。この簡単なモデルからのMSEがもっと詳細にデータを表す検討中のモデルのMSEよりも大きくなるというのが考え方のベースです。なので、それをalpha = 検討中のモデルのMSE / 最も簡単なモデルのMSEという比の形で表すと、R2 = 1 - alphaとすることができます。即ち、悪いモデルでは最も簡単なモデルのMSEと同じになるのでalpha=1でありR2=0となります。反対に良いモデルでは簡単なモデルよりもはるかに小さいMSEになることが考えられ、alpha=0となり、R2=1となります。sklearnではr2_score(y_true, y_pred)です。

Lesson 8 Detecting Errors

Types of Errors

モデルを簡単にしすぎるとUnderfittingが発生し、モデルを複雑にしすぎるとOverfittingが発生しやすくなります。UnderfittingはHigh Biasによって発生します。本当は多項式なのに、直線でフィッティングしてしまうようなものです。このとき、Trainingデータでの結果が悪くなり、同時にTestデータの結果も悪くなる傾向にあります。OerfittingのエラーはHigh Varianceによって発生します。本当は二次関数なのにより高次の多項式で表現してしまうようなものです。この時、Trainingデータでの結果が非常に良くなるが、Testデータでの結果が悪くなる傾向にあります。良いモデルはTraining/Testどちらのデータでも良い結果を出すモデルです。

Model Complexity Graph

モデルの複雑度を変えていった時のTraining/Testエラーの推移をみます。どちらのエラーも高い場合、それはUnderfittingです。Trainingエラーの値は低いが、Testエラーの値が大きい場合、それはOverfittingです。その丁度良いところを選ぶのが良いモデルの選択です。ただし、テスト用データでトレーニングしてはいけません。そこでデータを3つに分割し、Cross Validation用のデータを更に分けます。これはどのモデルを使うか判定するのに使います。Testエラーの値の代わりにこの値の推移をモデルの複雑度に合わせて確認します。良い位置の探し方は先程と同じようにして両方が良い値のところを探します。

K-Fold Cross Validation

データを複数のデータセットに分けて、それぞれのデータセットのTraining用データとTest用データへの割り当てを組み替えて学習させるやり方を使う場合もあります。特にデータセットに分ける代わりにランダムにピックアップする方法でも実現できます。これはKFold(12, 3, shuffle=True)です。

Learning Curves

High Bias(=Underfitting)モデルの場合、TrainingエラーとCVエラーはどこかの漸近線に収束しますが、どちらも高い値になります。良いモデルの場合、どちらのエラーも小さい値に収束します。High Variance(=Overfitting)モデルの場合、その2つの値にはギャップがあります。これらの情報からモデルがどのような状態か推察することが可能です。

Lesson 9 Putting it all together

Grid Search

データをTraining/CV/Testingに分けてモデルの複雑度を変えていきます。例えばメトリクスとしてF1Scoreを採用します。ロジスティック回帰の場合、回帰式の次数、決定木の場合はモデルの深さ、SVMの場合はKernelの種類とγの値を変えることでモデルの複雑度を変えることが出来ます。これらをテーブルにしてそれぞれの場合のスコアを求める方法をGrid Searchと呼びます。

Summary

ここまでで問題に対してアルゴリズムを選んで、そしてそのアルゴリズムのパフォーマンスを評価する指標を見てきました。指標はAccuracy, Precision, Recall, F1 Score等がありました。そしてモデルの学習を見比べる基準としてModel Complexity GraphやLearning Curves等がありました。これらを使って個別のタスクを勉強していきます。

Practice: Naive bayes

Naive bayesを使ったSpam判定です。以下はGitHubのリンクですが、作業は反映していません。
machine-learning/Naive_Bayes_tutorial.ipynb at master · romth777/machine-learning · GitHub

  1. データの前処理
  • まずデータを見て、ラベルデータを分類します。ham=0, spam=1とします。
  • Bag of Words(BoW)コンセプトを適用し、文章を単語ごとに分割し、その単語の出現頻度を数えます。順番は関係ありません。これを一文毎に行います。それにはsklearnのcount vectorizerを使うと便利です。これは単語にIDを割り振って、そのID毎に出現数を数えます。ただし、大文字と小文字の区別はデフォルトでは行いません(parameter:lowercase)。また句読点や感嘆符も無視します(parameter:token_pattern)。更に良くある単語、例えばam, an, and等は無視することもできます(parameter:stop_words)。無視する単語はあらかじめ設定されています。これらはcount_vector=CountVectorizer()とした後に、count_vector.fit(documents)でcount_vector.transform(documents).toarray()です。count_vector.get_feature_names()でtokenを取得することもできます。
  • これを自分で実装するにはまず文章をすべて小文字にして、句読点、感嘆符を削除し、スペースを区切り文字として単語を分割し、出現回数をカウントします。カウントにはCounterを使うと良いです。
  • 他にも同法の方法としてTfidfVectorizerというものもあります。
  1. トレーニング
  • train_test_splitでデータを分割する
  • 分割したデータに対してCountVectorizerを適用する
  1. モデルの作成
  • 今回はmultinormal Naive Bayesを使う。これは入力の特徴量が不連続な値(例えば整数だけとか)の場合に有効です。一方でGaussianNBは連続な値で、かつ入力データがガウス分布の場合に有効です。model=MultinormalNB()でmodel.fit(training_data, y_train)、model.predict(testing_data)とします。
  1. 評価
  • Accuracy, Precision, Recall, F1値を算出します。ACC=0.99, Pre=0.97, Rec=0.94, F1=0.96となりました。比較的すべての値が高いので、良いモデルの作成が成功しているように見えます。AccはTP+TN、PrecisionはFP、RecallはFNの割合をそれぞれ示しています。1に近ければ間違いが少ないといえます。
  1. まとめ
  • Naive Bayes分類モデルの特徴は大きな数の特徴量を扱えることです。今回の場合は1000ぐらいの異なる単語がありました。また、意味をなさない特徴量があっても使えます。そして、単純です。このモデルは通常、データの分布が分かっている場合を除いてパラメータ調整の必要がほとんどありません。さらに、トレーニング、テスト用の処理時間も高速です。

Project Predicting Boston Housing Prices

このプロジェクトではボストンの住宅価格を予測するモデルを作りました。
machine-learning/boston_housing.ipynb at master · romth777/machine-learning · GitHub

  1. 前処理
  • 住宅価格について最小、最大、平均、中央、分散を算出
  • それぞれの特徴量が住宅価格にどのような影響を及ぼすか仮説を立てました
  1. モデル開発
  • メトリクスとしてR^2を採用。今回は回帰分析タスクになる。sklearnではr2_score(y_true, y_predict)
  • train_test_splitでデータを分ける。引数のrandom_stateは固定値にしておくとデータの再現性ができて良い。test_sizeは0.2とした。
  • 今回は決定木モデルを使ことにした。その場合のスコアについて考察。それぞれのスコアの振る舞いに応じてモデルがUnderfittingかOverfittingか適切か考察した。
  • 同様にModelComplexityカーブを算出した。このカーブからは両方が高く、その差が少ないところが良いモデルだといえる。
  1. モデルパフォーマンスの評価
  • GridSearchCVを使ってモデルのパラメータ毎のメトリクスを評価した。メトリクスは前述の通りR^2スコアを用いた。
  • 新しいサンプルデータを使って売値の予想を行った。ここでは似たような元データを引っ張ってきてその特徴量と新しいサンプルデータの特徴量が大体同じもので大体同じ売値になったから妥当だといえばよかったかもしれない。
  • モデルのロバスト性について評価を行った。似たようなデータセットを与えてその時にデータがどの程度ばらつくか評価した。
  • 他にも考えられる事として、1978のデータから今日のデータを予測できるか、特徴量は十分か、モデルのロバスト性は十分か、ボストン以外の場所で取得したデータをこのモデルに当てはめるとどうなるかについて考察した

以上
②教師あり学習に続く......