最近、Rだけで統計解析したりすることに限界を感じているので、そうだ!C++を勉強しようと思い立ちました。
そこでとりあえず下の本を参考に、ディープラーニングの実装しながらC++を勉強しようなんていう次第です。
コードをそのまま写経するのも勉強にはなるのですが、私がやりたいのはディープラーニングを学ぶことではなく、
C++を学ぶことにあるので、少し修正する形でコードを組みなおしてみました。
スポンサーリンク
単純パーセプトロン
まず簡単に単純パーセプトロンの説明を。
かかを分類する問題を考えます。
ある信号(ヒント?)が与えられたら、出力されるを考えたいわけです。
で、何個か教師データを与えてやって学習させます。
例えばの時はになりましたよーみたいなデータを事前にたくさん学習させ、分類器を考えます。
何のモデルも無く分類器を考えることは出来ないので次のようなモデルを仮定します。
ならば
ならば
ただし、の第一項は1
という分類器を作るわけですね。が予測値です。
昔、サポートベクターマシンのハードマージンに関する記事で線形分離可能の話をしましたが、この単純パーセプトロンでも線形分離可能を仮定します。
【初心者向け】2値分類SVM(サポートベクターマシン)の考え方と最適化問題 - バナナでもわかる話
で、実際の正解値と予測値の差が0になるようなを求めます。
線形分離可能なのでそういう組が見つかるはずですね。
C++コード
ということでコードを本を参考に少々修正しながら書いてみました。
ビジュアルスタジオのコンソールを使っています。
とりあえず名前空間stdを使うことにします。
#include "pch.h" #include <iostream> using namespace std;
parcepクラスを定義して単純パーセプトロンを行うクラスを設定してみました。
まだクラスを置くことの利点はよくわかってませんが、とりあえずC++の練習と言うことでw
class parcep { public: float dot(float *v1, float *v2, int len); float step(float v); float forward(float *x, float *w, int len); void train(float *w, float *x, float t, float e, int len); }; //2種類のベクトルの内積をとる関数 float parcep::dot(float *v1, float *v2, int len) { float sum = 0; for (int i = 0; i < len; i++) { sum += v1[i] * v2[i]; } return sum; } //内積が0を超えたら1,超えなかったら0を出す関数 float parcep::step(float v) { return v > 0 ? 1 : 0; } //内積を取って、0か1かを出力する関数 float parcep::forward(float *x, float *w, int len) { float u = parcep::dot(x, w, len); return parcep::step(u); } //正解データtとデータによる出力zを比べて差がなくなるまでwをチューニングするトレーニング関数 void parcep::train(float *w, float *x, float t, float e, int len) { float z = parcep::forward(x, w, len); for (int j = 0; j < len; j++) { w[j] += (t - z)*x[j] * e; } } //データ数DATA_NUMSの定義 //ウエイトの個数WEIGH_NUMSの定義 #define DATA_NUMS 5 #define WEIGH_NUMS 4 //試してみる int main() { parcep pa; //学習率を定義 float e = 0.1; //入力データ float x[DATA_NUMS][WEIGH_NUMS] = { {1,0,0,1},{1,0,1,1},{1,0,1,0},{1,1,1,1},{1,0,0,0}}; //正解データ float t[DATA_NUMS] = { 0,0,0,1,0 }; //重みの初期値 float w[WEIGH_NUMS] = { 0,0,0,0 }; //トレーニング回数の設定 int time = 10; for (int i = 0; i < time; i++) { cout << "time" << i; for (int j = 0; j < DATA_NUMS; j++) { pa.train(w, x[j], t[j], e, WEIGH_NUMS); } for (int j = 0; j < WEIGH_NUMS; j++) { cout <<endl<< "w" << j << ":" << w[j]; } cout << endl; } for (int i = 0; i < DATA_NUMS; i++) { cout << pa.forward(x[i], w, WEIGH_NUMS); } }
出力はこんな感じ
正解データが00010なので、訓練の末、パラメータが収束して、予測値も00010になったら正解です。
time0 w0:0 w1:0.1 w2:0.1 w3:0.1 time1 w0:-0.1 w1:0.1 w2:0.1 w3:0 time2 w0:-0.1 w1:0.1 w2:0.1 w3:0 time3 w0:-0.1 w1:0.1 w2:0.1 w3:0 time4 w0:-0.1 w1:0.1 w2:0.1 w3:0 time5 w0:-0.1 w1:0.1 w2:0.1 w3:0 time6 w0:-0.1 w1:0.1 w2:0.1 w3:0 time7 w0:-0.1 w1:0.1 w2:0.1 w3:0 time8 w0:-0.1 w1:0.1 w2:0.1 w3:0 time9 w0:-0.1 w1:0.1 w2:0.1 w3:0 00010
実際の予測データも00010となってうまくいっています。
パラメータもで止まっていますね。