バナナでもわかる話

計量経済学・統計学が専門の大学院生です。統計学・経済学・投資理論・マーケティング等々に関する勉強・解説ブログ。ときどき趣味も。極力数式は使わずイメージで説明出来るよう心掛けていますが、時々暴走します。

C++を使ってディープラーニングの基本を試す

最近、Rだけで統計解析したりすることに限界を感じているので、そうだ!C++を勉強しようと思い立ちました。

そこでとりあえず下の本を参考に、ディープラーニングの実装しながらC++を勉強しようなんていう次第です。

コードをそのまま写経するのも勉強にはなるのですが、私がやりたいのはディープラーニングを学ぶことではなく、
C++を学ぶことにあるので、少し修正する形でコードを組みなおしてみました。

スポンサーリンク



単純パーセプトロン

まず簡単に単純パーセプトロンの説明を。
 t_i=0 1かを分類する問題を考えます。

ある信号(ヒント?) X_iが与えられたら、出力される t_iを考えたいわけです。

で、何個か教師データを与えてやって学習させます。
例えば X_1=(1,0,0,1)の時は t_1=0になりましたよーみたいなデータを事前にたくさん学習させ、分類器を考えます。

何のモデルも無く分類器を考えることは出来ないので次のようなモデルを仮定します。

 W_i・X_i≧0ならば z_i=1
 W_i・X_i<0ならば z_i=0
 W_i=(w_0,w_1,...w_s)
ただし、 X_iの第一項は1


という分類器を作るわけですね。 zが予測値です。

昔、サポートベクターマシンのハードマージンに関する記事で線形分離可能の話をしましたが、この単純パーセプトロンでも線形分離可能を仮定します。
【初心者向け】2値分類SVM(サポートベクターマシン)の考え方と最適化問題 - バナナでもわかる話

で、実際の正解値と予測値の差が0になるような W_iを求めます。
線形分離可能なのでそういう組が見つかるはずですね。

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なので、訓練の末、パラメータ wが収束して、予測値も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となってうまくいっています。
パラメータも w0=-0.1,w1=0.1,w2=0.1,w3=0で止まっていますね。