雑い記事です すみません...
はじめに
今回は多層NNと誤差逆伝播法を用いてじゃんけんに強いNNを作ったのでそのクラスを共有したいと思います。
仕組み
グー、チョキ、パー3つそれぞれを判断するNNに分け、
過去の手とそれぞれの勝敗のデータから次のプレイヤーの手を予測します
学習データ
手×その勝敗の過去5回分を入力ソートに入れます
それぞれのNNの対応する手か否かの2値、
つまり手を1.0/0.0で表します
勝敗は プレイヤーの勝ち/あいこ/敗北 で表します
それぞれの数値は0.1/0.55/1.0です
教師データ
それぞれの手を次に出したかどうかで表します
今回は1/-1です
コード本文
冒頭
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <fstream>
#include <string>
#include <Windows.h>
#define layer 3 //from input to (output-1)
#define sort 5 //before_hands(1 or 0) * before_results(0.1 ~ 1.1)
#define teach_true 1
#define teach_false -1
#define learningrate 0.4
#define study_count 12 //sort * study_count
マクロを紹介します
-
layer
隠れ層と入力層合わせての層数です -
sort
過去何手のデータをとるかを表します -
teach_true
教師データの「正しい」を表します -
teach_false
教師データの「誤り」を表します -
learningrate
学習率です -
study_count
(作成したビッグデータの個数) / (6セットの数) を表します
NNクラス
publicゾーン
NNコンストラクタ
すべてのメンバ変数、配列を初期化します
重みw は最初はランダムな値をとります
NN() {
for (int i = 0; i < layer; i++) {
for (int i2 = 0; i2 < sort; i2++) {
for (int i3 = 0; i3 < sort; i3++) {
w[i][i2][i3] = (rand() % 201 - 100) / 100.0f;
}
before_hands_[i2] = 0.0f;
b[i][i2] = 0.0f;
u[i][i2] = 0.0f;
y[i][i2] = 0.0f;
delta[i][i2] = 0.0f;
last_w[i2] = (rand() % 201 - 100) / 100.0f;
}
}
last_b = 0.0f;
last_u = 0.0f;
last_y = 0.0f;
last_delta = 0.0f;
ans = 0.0f;
}
デストラクタ
~NN() {}
hands_input関数
入力配列Aに手の値 1or0 を入れます
このとき、NNはグーチョキパーそれぞれ別のインスタンスを用意することを前提としています
void hands_input(float* before_hands) {
for (int i = 0; i < sort; i++) {
before_hands_[i] = before_hands[i];
}
}
results_input関数
入力配列Bにそれぞれの手の勝敗 0.1 or 0.55 or 1.0 を入れます
neuralnetworkの入力セルにはこれと手を示す値の積を入れます
過去sort手のうち、
どれほど勝てば次にその手を出しやすくなるか、
またはどれほど負ければ次にその手を出しやすくなるか
を学習させるためです
void results_input(float* before_results) {
for (int i = 0; i < sort; i++) {
u[0][i] = before_hands_[i] * before_results[i];
y[0][i] = before_hands_[i] * before_results[i];
}
}
out_ans_input関数
正解ラベルを入力します
手の値 teach_true or teach_false となります
void out_ans_input(float out_ans) {
if (out_ans > 0) { ans = teach_true; }
else if (out_ans < 0) { ans = teach_false; }
std::cout << "This label is..." << ans << std::endl;
}
uy_decide関数
neuralnetworkが入力から予想を出力する関数です
基本的にyが活性化関数を通ったものでuは一層前のyの和です
void uy_decide(void) {
uy_init();
for (int i = 1; i < layer; i++) {
for (int i2 = 0; i2 < sort; i2++) {
for (int i3 = 0; i3 < sort; i3++) {
u[i][i2] += y[i - 1][i3] * w[i][i2][i3];
}
y[i][i2] = sigmoid(u[i][i2] - b[i][i2]);
}
}
for (int i = 0; i < sort; i++) {
last_u += y[layer - 1][i] * last_w[i];
}
last_y = sigmoid(last_u - last_b);
}
delta_decide関数、study_w関数
学習を行う関数たちです
今回は最急降下法を使った学習を行っています
閾値の更新は残念ながら未実装です。勉強次第追加します
void delta_decide(void) {
delta_init();
last_delta = E_diff(last_y);
for (int i = 0; i < sort; i++) {
delta[layer - 1][i] = last_delta * sigmoid_diff(last_u) * last_w[i];
}
for (int i = layer - 2; i > 0; i--) {
for (int i2 = 0; i2 < sort; i2++) { //j
for (int i3 = 0; i3 < sort; i3++) { //k
delta[i][i2] += delta[i + 1][i3] * sigmoid_diff(u[i + 1][i3]) * w[i + 1][i3][i2];
}
}
}
}
void study_w(void) { //Life-Shattering Zone
for (int i = 0; i < sort; i++) {
last_w[i] += -1 * learningrate * last_delta * sigmoid_diff(last_u) * y[layer - 1][i];
}
for (int i = 1; i < layer; i++) {
for (int i2 = 0; i2 < sort; i2++) { //j
for (int i3 = 0; i3 < sort; i3++) { //i
w[i][i2][i3] += -1 * learningrate * delta[i][i2] * sigmoid_diff(u[i][i2]) * y[i - 1][i3];
}
}
}
}
print_last_y関数、return_last_y関数
最終出力をそれぞれ表示、返信します
void print_last_y(void) {
std::cout << last_y << std::endl;
}
float return_last_y(void) {
return last_y;
}
privateゾーン
float before_hands_[sort];
float ans;
float w[layer][sort][sort]; //to cell to cell from cell
float b[layer][sort]; //to cell
float last_w[sort]; //from cell
float last_b; //to cell
float u[layer][sort]; //to cell
float y[layer][sort]; //to cell
float last_u;
float last_y;
float delta[layer][sort]; //layer cell
float last_delta;
float sigmoid(float num) {
return ((1 / (1 + exp(-1 * num))) - 0.5) * 2.0f;
}
float sigmoid_diff(float num) {
return 2.0f * exp(-1 * num) / ((exp(-1 * num) + 1) * (exp(-1 * num) + 1));
}
float E_diff(float num) {
return num - ans;
}
void uy_init(void) {
for (int i = 1; i < layer; i++) {
for (int i2 = 0; i2 < sort; i2++) {
u[i][i2] = 0.0f;
y[i][i2] = 0.0f;
}
}
last_u = 0.0f;
last_y = 0.0f;
}
void delta_init(void) {
for (int i = 1; i < layer; i++) {
for (int i2 = 0; i2 < sort; i2++) {
delta[i][i2] = 0.0f;
}
}
last_delta = 0.0f;
}