はじめに
こんにちは 高専一年、ロボコン部部員です
タイトルの通り、ユーザが決めて覚えさせる一種類の数値を、
二次元データから、その数値が書かれているか、書かれていないかを
判別するパーセプトロンを使用した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予測の値を表します
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などの小数)
- 画像データから学習可能にする
