HGNN - HalfGammon Neural Network -
Copyright (C) 2020 by N.Tsuda

概要

「HGNN - HalfGammon Neural Network -」は、人工ニューラルネット(ANN)を使用し、ハーフギャモンの状態から得点期待値を予測する

リンク

目次

HGNN 使用方法

ビルド環境:Visual Studio 2019、C++

HGNN の使用方法は以下の通り

  1. 人工ニューラルネット(ANN)構築
  2. 入力・教師値データを使って学習
  3. 入力を与え、結果を予測

ニューラルネット構築

ニューラルネットを構築するコード例を以下に示す。

#include <vector>
#include "HGNNet.h"
using namespace std;
.....
    HGNNet nn;     // ニューラルネットオブジェクト生成
    nn.init(vector<int>{2, 10}, TANH);   // 入力層:2ノード、1隠れ層・10ノード、活性化関数:tanh

最初に標準ライブラリの vector をインクルードする。これはニューラルネットの層数・各層のノード数を指定するために使用する。

ニューラルネットクラス HGNNet のヘッダもインクルードしておく。

「using namespace std;」は好みで記述するといいだろう。記述しておけば、以下「std::」を省略できる。

「HGNNet nn;」で、ニューラルネットオブジェクトを生成する。引数は特に必要ない。

ついで、HGNNet::init(vector<int>&, ActFunc) をコールし、ニューラルネットを初期化する。重み係数は [-1, +1] でランダムに初期化される。
第一引数は層数・各層のノード数を指定する。HGNNet は出力に回帰だけをサポートしており、出力ノードは1固定なので、出力ノードは指定しない。 入力層ノード数に続けて、隠れ層の分だけ各層のノード数を記述する。下記のように vector オブジェクトの生成を分けてもいいが、1行で書く方が簡潔で筆者の好みだ。

    vector<int> nodes = {2, 10};
    nn.init(nodes, TANH);

隠れ層が複数ある場合は、その数だけ隠れ層のノード数を記述する。下記は隠れ層が2層の場合の例だ。

    nn.init(vector<int>{2, 10, 10}, TANH);

HGNNet::init() の第2引数には活性化関数を指定する。SIGMOID, TANH, RELU が指定可能だ。各々、シグモイド関数、tanh関数、ReLU関数を示す。
これらの活性化関数は隠れ層の演算の時に用いられる。HGNNet の出力は回帰なので、活性化関数は適用されない(あえて言えば恒等関数だ)。

学習

    vector<data_t> input;
    input に入力データを設定
    data_t T = 教師値;
    nn.train(input, T);

学習(トレーニング)を行うには、上記の様に、入力データを vector<data_t> に設定し、それと教師値を引数にして HGNNet::train() をコールする。
data_t は HGNNet.h で宣言されたデータ型識別子で、double が割り当てられている。メモリが不足する場合などは float に定義し直すという選択肢もある。
学習を行うと誤差逆伝播法を用いてネットワークの重み係数が教師値に近づくように少し変化する。

train() の第3引数で係数修正の度合いを学習率で指定できる。デフォルトは 0.01 である。この値が大きいと学習が速く進むが、その反面発散確率が高まる。
なので、徐々に学習率を下げながら学習を行うというテクニック(adagrad 等)も存在する。

    vector<data_t> input;
    input に入力データを設定
    data_t T = 教師値;
    nn.train(input, T, 0.001);       //  学習係数指定

予測

学習が終わったネットワークに対し、nn.predict() をコールすることで、入力値から教師値に近い値を予測することができる。

コードは以下のように、vector<data_t> に入力データを設定し、predict() をコールすると、予測値を返してくれる。

    vector<data_t> input;
    input に入力データを設定
    auto v = nn.predict(input);

HGNN 使用例

線形関数

「y = 3*x1 - 2*x2 + 1」、2層NN

data_t linearFunc(data_t x1, data_t x2) { return x1*3 - x2*2 + 1; }
double linearRMS(HGNNet& nn, int N_LOOP = 100)
{
    vector<double> input(2);
    double sum2 = 0;
    for (int i = 0; i < N_LOOP; ++i) {
        input[0] = g_rand11(g_mt);       // [-1, +1]
        input[1] = g_rand11(g_mt);       // [-1, +1]
        double err = nn.predict(input) - linearFunc(input[0], input[1]);
        sum2 += err * err;
    }
    return sqrt(sum2 / N_LOOP);
}
void test_linearFunc()
{
    HGNNet nn;
    cout << "# node of layers: {2 1}\n\n";
    nn.init(vector<int>{2}, SIGMOID);    // 2入力のみ(隠れ層無し)
    vector<double> input(2);
    // 学習・評価
    cout << "N\tRMS\n";
    cout << "------- ----------\n";
    for (int cnt = 1; cnt <= 10000; ++cnt) {
        input[0] = g_rand11(g_mt);       // [-1, +1]
        input[1] = g_rand11(g_mt);       // [-1, +1]
        nn.train(input, linearFunc(input[0], input[1]));
        if( log10(cnt) == (int)log10(cnt) )
        {
            cout << "10^" << log10(cnt) << "\t" << linearRMS(nn) << endl;
        }
    }
    cout << "\n" << nn.dump() << "\n";
}

sin関数