0
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?

【C++】初心者による一種の数値を二次元データから判別するパーセプトロンAI

Posted at

はじめに

こんにちは 高専一年、ロボコン部部員です

タイトルの通り、ユーザが決めて覚えさせる一種類の数値を、
二次元データから、その数値が書かれているか、書かれていないか
判別するパーセプトロンを使用したAIを作りました

※僕にはまだ手描きできるキャンパスの出し方も画像認識も分からないので、
今回は二次元データをcinで入れる形にしています

コード

#includeおよび#define

#include <iostream>
#include <stdlib.h>
#include <time.h>

#define ONESIDE 3
#define LEARNINGRATE 0.5
#define STUDYCOUNT 10000

マクロはそれぞれこれを表します:

  • ONESIDE → 二次元データの一辺の大きさ
  • LEARNINGRATE → 学習率
  • STUDYCOUNT → 学習回数

AI class

private変数、配列

それぞれの値域や意味するものを紹介します

double map[ONESIDE * ONESIDE] = { 0 };

こちらは二次元マップをそのまま表します
値域は0.1~1.0です(なにも書かれなかったマスも0.1として表す)
0.0~1.0でないのは
下記のstudy関数にもつながる話ですがmapの値が0だと
wやbが更新できなくなるためです

double ans;

こちらはAIの予想した値が入る変数です
詳しくはinput関数をご覧ください

double sum;

こちらは重み付き和sumが入る変数です
詳しくはforecast_return関数をご覧ください

double w[ONESIDE * ONESIDE] = { 0 };
double b = 0;

ご察しの通り重みや閾値の入る配列または変数です

public関数

rand_init関数

void rand_init(void) {
	srand((unsigned int)time(NULL));
}

rand関数の種を初期化します

w_init関数

void w_init(void) {
	for (int i = 0; i < ONESIDE * ONESIDE; i++) {
		w[i] = (rand() % 201 - 100)/100.00;
	}
}

マスごとに対応したおもみwを初期化します
ここでは-1.00~1.00 の乱数をとっていますね

input関数

void input(double *outside_map,int outside_ans) {
	for (int i = 0; i < ONESIDE * ONESIDE; i++) {
		map[i] = outside_map[i];
	}
	ans = outside_ans;
}

引数として与えられた配列、変数をメンバ変数(配列)にコピーします

forecast_return関数

double forecast_return(void) {		//学習時、判断時、両方使用可
	sum = 0;
	for (int i = 0; i < ONESIDE * ONESIDE; i++) {
		sum += w[i] * map[i];
	}
	if (sum > b) {
		return 1;
	}else if (sum <= b) {
		return -1;
	}
}

mapの値とwの積を出し合計sumを求め、閾値bとの差分から
『指定された値を示しているか否か』を返す関数です
示していると判断されると1、判断されないと-1が返されます

ここで出された値がw,bの更新にもそのまま使われます

study関数

void study(int ai_ans) {
	for (int i = 0; i < ONESIDE * ONESIDE; i++) {
		w[i] += LEARNINGRATE*(ans - ai_ans) * map[i];
	}
	b += LEARNINGRATE*(ans - ai_ans)*-1;
}

AIの予想した値(1 or -1)と正解値(1 or -1)の値、mapの値からwとbの更新を行います
ansは正解の値、ai_ansはAI予測の値を表します

Qiita 2025-12-03 165624.png

main関数

int main(void) {
	double outside_map[ONESIDE * ONESIDE] = { 0 };
	int ans,sum;
	AI ai;
	ai.rand_init();
	ai.w_init();
	for (int i = 0; i < STUDYCOUNT; i++) {
		std::cout <<i+1<<"回目" << std::endl;
		study_test(outside_map, &ans);

		ai.input(outside_map, ans);
		sum = ai.forecast_return();
		std::cout<<sum<<std::endl;
		ai.study(sum);
		std::cout << "------------------------------------" << std::endl;
	}
	std::cout << "学習終了" << std::endl;
	return 0;
}

クラスを使用するためのループが入っています

study_test関数

void study_test(double* outside_map, int* ans) {
	for (int i = 0; i < ONESIDE * ONESIDE; i++) {
		std::cin >> outside_map[i];
	}
	std::cout << "このデータは何?1or-1" << std::endl;
	std::cin >> *ans;
}

今回はcinでmapやans(正答)に値を代入しています

全文

#include <iostream>
#include <stdlib.h>
#include <time.h>

#define ONESIDE 3
#define LEARNINGRATE 0.5

#define STUDYCOUNT 10000

class AI {
public:
	void rand_init(void) {
		srand((unsigned int)time(NULL));
	}
	void w_init(void) {
		for (int i = 0; i < ONESIDE * ONESIDE; i++) {
			w[i] = (rand() % 201 - 100)/100.00;
		}
	}
	void input(double *outside_map,int outside_ans) {
		for (int i = 0; i < ONESIDE * ONESIDE; i++) {
			map[i] = outside_map[i];
		}
		ans = outside_ans;
	}
	double forecast_return(void) {		//学習時、判断時、両方使用可
		sum = 0;
		for (int i = 0; i < ONESIDE * ONESIDE; i++) {
			sum += w[i] * map[i];
		}
		if (sum > b) {
			return 1;
		}else if (sum <= b) {
			return -1;
		}
	}

	void study(int ai_ans) {
		for (int i = 0; i < ONESIDE * ONESIDE; i++) {
			w[i] += LEARNINGRATE*(ans - ai_ans) * map[i];
		}
		b += LEARNINGRATE*(ans - ai_ans)*-1;
	}

private:
	double map[ONESIDE * ONESIDE] = { 0 }; //0.1~1 何も書いていないマスも0.1にする。0にしてしまうとwの更新が起こらないから
	double ans;
	double sum;
	double w[ONESIDE * ONESIDE] = { 0 };	//初期は-1.00~1.00
	double b = 0;
};

void study_test(double* outside_map, int* ans);

int main(void) {
	double outside_map[ONESIDE * ONESIDE] = { 0 };
	int ans,sum;
	AI ai;
	ai.rand_init();
	ai.w_init();
	for (int i = 0; i < STUDYCOUNT; i++) {
		std::cout <<i+1<<"回目" << std::endl;
		study_test(outside_map, &ans);

		ai.input(outside_map, ans);
		sum = ai.forecast_return();
		std::cout<<sum<<std::endl;
		ai.study(sum);
		std::cout << "------------------------------------" << std::endl;
	}
	std::cout << "学習終了" << std::endl;
	return 0;
}


void study_test(double* outside_map, int* ans) {
	for (int i = 0; i < ONESIDE * ONESIDE; i++) {
		std::cin >> outside_map[i];
	}
	std::cout << "このデータは何?1or-1" << std::endl;
	std::cin >> *ans;
}

さいごに

ここまで読んでくださってありがとうございました
次の課題点としては以下のものが挙げられます

  • forecast関数から返される値に幅を持たせる(0.86などの小数)
  • 画像データから学習可能にする
0
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
0
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?