5. Pythonによる機械学習

Pythonへのデータ読み込みと学習

今回の学習はランダムフォレスト(のみ)でやることにします。Python上での基本的な文法は一緒なので、ほかにためしたい学習器があれば、各自でお試しください(元ネタの方ではいろんな学習器を比較もしているので、興味があればそちらへ。xgboostとかも良さそうですね)。

はい、ランダムフォレスト完了です。いい時代ですね。いちおう解説しておきますと、

完了です、とさらっと言いましたが、本来であれば木の数やノードの切り方など、ハイパーパラメータのチューニングもやるのが望ましくはあります。まあ今回は紹介なのでいいでしょう。

精度評価もしておきましょう。多クラス分類ではエラーマトリクス[1]を作るのが通例なのでそうしてみます。

エラーマトリクスの出力は以下のようになりました。

[[ 64   8   0   0   5   3   1   0   0   0]

 [  3 114   1   0   1   1   0   1   0   1]

 [  0   4  17   0   0   0   1   0   0   0]

 [  0   0   0   7   2   0   0   2   6   0]

 [  3   2   0   0 253   3   0  42   7   0]

 [ 10   2   1   0  39  62   1  11   2   2]

 [  0   0   0   0   5   0   9   0   0   0]

 [  1   1   1   0  24   3   0 299   1   0]

 [  0   0   0   0   7   0   0   7  89   0]

 [  0   0   0   0   6   0   0   0   2  14]]

その他スコアも含め、表の見かたを間違えないようにきれいにまとめ直すと、以下のようになります[2](自動化してもいいですが、とりあえずコピペ&手打ちしました)。よこ(列)がランダムフォレストにより予測(作成)されたデータ集計で、たて(行)がリファレンスデータの集計です。

croplandとgrassland, barrenとurban, waterとcroplandあたりを間違えがちなようです。このあたりについては、時系列データを追加したりすれば改善しそうな気がしますね。


精度指標の定義まとめ

ここで、精度指標については紛らわしいので定義を確認しておきましょう。

まず全データ数Nのうち、予測データとリファレンスデータのカテゴリが一致したもの(エラーマトリクスの斜めマスの合計)のしめる割合を「全体精度(Overall accuracy; OA)」といいます。「正解率」とか単に「精度(accuracy)」といわれることもあります。データ間の単純な一致率をみるもっとも基本的な精度指標です。


カテゴリごとに予測がうまくいっているのかどうかをより詳細に見るためには、以下のようにします。

注目カテゴリに分類されたデータを正例とか陽性(positive case)、それ以外に分類されたデータを負例とか陰性(negative case)と呼び、さらに

とかき分けることにします。このとき、


用語が多くて「もう全体精度だけみればよくない?」と思った人もいるかもしれません。そういう人は、以下の事例を考えてみてください。

正例と負例だけの単純な2カテゴリをもつ、100個のデータがあるとします。ただし正例の出現率はめっぽう低く、100個のデータ中、1個だけが正例、残り99個は負例だったとします。このとき私は、全体精度99%を達成するさいきょーのAIを1秒で作ることができます。以下のようなものです。

さいきょーAI:入力された特徴ベクトルxがなんであろうと、常に「負例!」と元気よく出力する。

このAIで100個の負例が出力され、うち99個のリファレンスデータは負例で一致しますので、全体精度は99%です。強いですね。

…そういうわけで、全体精度はカテゴリ間のサンプルサイズの偏りに弱いので[3]、別途カテゴリごとに精度はどうか、とみていく必要があるわけですね。当該さいきょーAIのrecallは0%、見逃しエラー100%です。正例の抽出に使いたいのであれば、まったく虚無のAIということになります。


もう一つだけコメントしておくと、precisionとrecallは一般にトレードオフの関係にあります。極端な例ですが、上記で、正例を抽出したいからと常に「正例」と出力することにすれば、見逃しはなくなります(recall=100%)が、precisionは1%で驚異的なオオカミ少年になります。

このあたりを考慮し、2/(1/precision+1/recall) と調和平均をとったF1値もよく使われます。precisionとrecallがともに高いモデルはいいモデルと言えます。


GRASSとの連携

学習済みのAIをつかって、マップ全体を分類することにしましょう。grassのターミナル上でpythonを立ち上げていれば、grass.scriptというライブラリがpythonにインストールでき、GRASS上のラスタをpythonのnumpy配列として読むことができるようになります。以下のような具合です。

あとは、GRASS側で適当に可視化してやります。

得られたマップは右のとおりです。念のため、カテゴリの対応は

1:DF(落葉林) 2:EF(常緑林) 3:bamboo(竹林) 4:barren(地)

5:croplands(耕作地) 6:grasslands(草地) 7:orchard(果樹園)

8:rice(水田) 9:urban(都市) 10:water(水域)

筑波山の尾根付近に落葉林、その周りは常緑林、霞ヶ浦はもちろん水域、そこから桜川沿いに上ると水田、つくば市街、という感じで雰囲気はわかります。詳細は実際に散歩して検証してみたり、Google EarthやGoogleストリートビューで引きこもり散歩して検証してみるとよいでしょう(耕作地がやたら多いような気もします)。

[1]  エラーマトリクス、混同行列、混合行列、対応行列、 error matrix、confusion matrix、contingency matrixなどなどいろんな呼び方があります

[2] ヒートマップ的に自動で可視化してくれるConfusionMatrixDisplayというのもあります。https://scikit-learn.org/stable/modules/generated/sklearn.metrics.ConfusionMatrixDisplay.html#sklearn.metrics.ConfusionMatrixDisplay

[3] このあたりの "対策" として、以前は全体精度から「偶然の一致率」がおよぼす影響をのぞいた「κ係数」というのもよく使われていました。しかしよくよく考えると、いうほどカテゴリ間インバランスの影響を軽減できる指標でもないし、意味ないのでは?という話(Pontius & Millones, 2011)もでてきており、最近ではあまり見かけることはなくなったように思います。