1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

じゃんけん最強NNを作ってみた

1
Last updated at Posted at 2026-02-02

雑い記事です すみません...

はじめに

今回は多層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;
}
1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?