LoginSignup
8
1

More than 3 years have passed since last update.

誰でもできちゃうSVMによるパターン認識

Posted at

はじめに

最近、機械学習に興味を持ち、少しずつですが勉強しています。
ただ、モチベーションを維持するのって結構難しいですよね...
そこで、モチベーションを維持するために、実際に動くものを作って遊んでみました。

この記事では、Node.jsでSVM(サポートベクターマシン)を簡単に実装できる手順を公開します。

対象者

  • Node.jsを触ったことがある人
  • npmの知識が多少ある人
  • 機械学習に興味のあるけど、モチベーションが維持できない人
  • (SVMの説明はこの記事では省略します)

今回作るもの

学習データからモデルを生成し、そのモデルからテストデータの分類を行うプログラムを作ります。
使用するモジュールとして、node-svmを使います。

バージョン

$ node -v
v10.16.0
$ npm -v
6.9.0

Node.jsのバージョンによって、node-svmをインストールできないので注意が必要です。
私は、v12.6.0でインストールできませんでした。

手順

準備

  • 作業するディレクトリを作成
  • npm initで初期化
$ mkdir test_svm
$ cd test_svm
$ npm init

モジュールのインストール

node-svm以外に、以下のモジュールをインストールします。
- 学習データやテストデータを読み込むためにfs
- 読み込んだデータをパースするためにcsv-parse

$ npm install --save fs
$ npm install --save csv-parse
$ npm install --save node-svm

実装

main.js
// モジュールの読み込み
const fs = require("fs");
const csvSync = require("csv-parse/lib/sync");
const svm = require("node-svm");

// 学習データ、テストデータのパス
const learningDataPath = "learning_data.csv";
const testDataPath = "test_data.csv";

// 生成したモデルを格納するための変数
var newModel = null;

// メイン処理
if (require.main === module) {
    var arrLD = readFile(learningDataPath);
    var normArrLD = normalizeArr(arrLD);
    var arrTD = readFile(testDataPath);
    initModel(normArrLD).then(() => {   // 学習データからモデルの生成
        estimate(arrTD);                // モデル生成完了後、テスト
    })
}

/**
 * ファイルを読み込み、配列として返す関数
 * @param {string} filePath 読み込むファイルのパス
 * @return {Array.<Array.<number>>} 学習データの二次元配列
 */
function readFile(filePath) {
    var file = fs.readFileSync(filePath);
    var arr = csvSync(file);
    // 以下の処理で、配列内の値が文字列のため数値にパース
    var columnNum = arr.length;
    var rowNum = arr[0].length;
    for(var i=0; i<columnNum; i++) {
        for(var j=0; j<rowNum; j++) {
            arr[i][j] = parseFloat(arr[i][j]);
        }
    }
    return arr;
}

/**
 * 二次元配列を入力データと正解データに分割し、正規化する関数
 * @param {Array.<Array.<number>>} arr 正規化前の配列
 * @return {Array.<Array.<number>, number>} 正規化後の配列
 */
function normalizeArr(arr) {
    var columnNum = arr.length;
    var rowNum = arr[0].length;
    var normArr = Array(columnNum);     // 長さががデータ数の配列を用意
    for (var i=0; i<columnNum; i++) {
        normArr[i] = Array(2);          // 学習データと正解データを分割するための配列を用意 normArr[i]=[学習データ,正解データ]
        normArr[i][0] = Array(rowNum-1);// 長さが学習データの次元数の配列を用意 normArr[i]=[[学習データ[0],...,学習データ[rowNum-1]],正解データ]
        // 以下の処理で、正規化
        for (var j=0; j<rowNum-1; j++) {
            normArr[i][0][j] = arr[i][j];
        }
        normArr[i][1] = arr[i][rowNum-1];
    }
    return normArr;
}

/**
 * 正規化された配列からモデルを生成する関数
 * @param {Array.<Array.<number>, number>} arr 正規化された配列
 * @return {Promise} Promiseのインスタンス
 */
function initModel(arr) {
    return new Promise(function (resolve, reject) {
        var clf = new svm.CSVC();         // 分類器の生成(今回の設定はデフォルト)
        clf.train(arr).spread(function (model, report) {
            newModel = svm.restore(model);// モデルの生成
            resolve("モデル生成完了");      // モデル生成完了後、thenメソッドが呼ばれる
        });
    });
}

/**
 * モデルからテストデータの分類を行う関数
 * @param {Array.<Array.<number>>} テストデータ
 */
function estimate(arr) {
    if(newModel) {  // モデルのnullチェック
        var result = Array(arr.length);
        for(var i=0; i<arr.length; i++) {
            result[i] = newModel.predictSync(arr[i]);   // モデルからテストデータを分類
            console.log(result[i]);                     // 結果の表示
        }
    }
}

モデルの生成が非同期で行われるため、Promiseを使って「モデル生成 -> テストデータの分類」の順で処理します。

学習データの準備

今回は、RGBの値から色を分類するための学習データを作ります。
左からR値、G値、B値、正解データとなっています。
正解データは、1が赤、2が緑、3が青とラベル付けしてあります。

learning_data.csv
255, 49, 44, 1
235, 114, 0, 1
185, 0, 0, 1
180, 9, 72, 1
92, 194, 24, 2
0, 144, 66, 2
69, 193, 112, 2
0, 228, 69, 2
0, 9 ,72, 3
65, 164, 255, 3
107, 180, 231, 3
108, 81, 194, 3

テストデータの準備

test_data.csv
228,79,107

test_red.png
上の色をテストデータとしてセットします。
赤ぽいので、1(赤)と分類されると嬉しいですね。

学習データとテストデータはmain.jsと同じ階層に置きます。
ここまでで、ファイル構成が以下のようになると思います。

test_svm/
├ main.js
├ node_modules/
├ package.json
├ learning_data.csv
└ test_data.csv

実行

$ node main.js 
1

今回生成したモデルは、[228,79,107]を1(赤)と分類しました。
期待通りの結果ですね!

最後に

私はSVMについてあまり知識がありませんが、モジュールを使うことで簡単に実装できました。
やっぱり実際に動いているものを見ると、中で何が行われているか気になりますよね〜
本記事では、色の分類をするモデルを生成しましたが、他のものを分類することもできます。
よかったら遊んでください。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1