はじめに
"F# for Machine Learning Essentials" (Sudipta Mukherjee 著 以下「原著」) の第3章 "Classification Techniques"(分類法)では、機械学習でよく用いられる分類法をF#で記述する方法について説明されています。最初に出てくるのが、以前にも紹介した k-nearest neighbour(k近傍法)を用いた分類法です。
今回は、Kaggleにある犬と猫の画像データを用いて、犬と猫の分類を行います。分類法は単純で、犬と猫では毛の色の感じが違うので、写真の色の分布をヒストグラムにして、その形から分類する、というものです。顔の形とかは全く考慮しません。
参考資料
残念ながら、今回は原著のコードがネットに見当たりませんでした。フルコードをご覧になりたい方は、原著をご覧ください。
ヒストグラム
原著では、以下の方法でヒストグラムを計算しています。
let getHist(values: float[]) =
values |> Seq.groupBy(fun color -> color)
|> Map.ofSeq
|> Map.map(fun t colors -> Seq.length colors)
|> Map.toArray
|> Array.map(fun colorCount -> float (snd colorCount))
Seq.groupBy(fun color -> color)
において、fun
はラムダ式を定義するためのキーワードです。fun parameter-list -> expression
という形式をとります。Seq.groupBy
は、ラムダ式でつくられるkeyをもとに、配列をグループ化します。MSDNの例を挙げておくと。
let sequence = seq { 1 .. 100 }
let sequences3 = Seq.groupBy (fun index ->
if (index % 3 = 0) then 0
elif (index % 3 = 1) then 1
else 2) sequence
この計算結果は、(1, seq [1; 4; 7; 10; ...]) (2, seq [2; 5; 8; 11; ...]) (0, seq [3; 6; 9; 12; ...])
になります。
Seq.groupByのあとは、Map.ofSeqで配列からmapを生成し、Map.map...でmapのcolorsに対して配列の長さの計算を適用し、Map.toArrayで配列に戻したあと、snd でタプルの二番目の要素を取り出しています。
Unityでの実行
UnityでF#を実行する際の基本的な手順に従ってください。このサンプルでは、System.Drawingという名前空間を使うので、そちらもSolution ExplorerのReferenceフォルダを右クリックし、
Add Referenceを選んで、下図の通り、Assemblies/FrameworkタブのSystem.Drawingを選んでOK。
これで、Imageクラスなどが使えるようになります。
そして、いつも通り、namespaceとmoduleを指定します。
namespace kNNDogCat
open System
open System.IO
open System.Drawing
module kNNDogCatEntry =
type Entry = {Label:string;Values:float[]}
\\ 以下略
あと、UnityでつかえるF#3.0ではList.itemが使えないので、List.nthで代用します。具体的には
\\ 略
let guess1 = fst (List.nth labels1 0)
\\ 略
のようになります。
また、原著では計算結果の表示はprintfnを使ってますが、ここでは、Unity側に出力しましょう。Unity C#のコードは以下の通りです
using UnityEngine;
using kNNDogCat;
public class ReadFSharp : MonoBehaviour
{
void Start()
{
Debug.Log(kNNDogCatEntry.labels1);
Debug.Log("The known dog is identified as a "+kNNDogCatEntry.guess1);
Debug.Log(kNNDogCatEntry.labels2);
Debug.Log("The known dog is identified as a "+kNNDogCatEntry.guess2);
}
}