与えられたデータセットと評価値に近づくように重みとしきい値を
バックプロパゲーションで調整する3層パーセプトロン
/**
* バックプロパゲーションによるニューラルネットの学習
* 使い方
* $./backPropagation (変数の数) (中間層のセル数) (誤差の上限値) < (学習データセットのファイル名)
*誤差の推移や、学習結果となる結合係数などを出力します
**/
#include <iostream>
#include <vector>
#include <cstdlib>
#include <float.h>
#include <ctime>
#include <cmath>
using namespace std;
inline void initRand()
{
srand((unsigned int)time(NULL));
}
/**シグモイド関数**/
inline double sigmoid(double u)
{
return 1.0 / (1.0+exp(-u));
}
/**-1.0 - 1.0の実数乱数の生成**/
inline double randDouble()
{
return (2.0 / RAND_MAX * rand())-1.0;
}
/**中間層の重みと出力層の重みを初期化(ランダム)**/
void initWeight(const int &n, const int &numAlliance,
double **weightAlliance, double *weightReaction)
{
initRand();
for(int i=0; i<numAlliance; ++i){
for(int j=0; j<n+1; ++j){
weightAlliance[i][j] = randDouble();
}
weightReaction[i] = randDouble();
}
weightReaction[numAlliance] = randDouble();
}
/**学習データをdataに読み込む**/
void readData(vector< vector<double> > &data, const int &n)
{
// [n]は評価値
double *buffer = new double [n+1];
while(true){
for(int i=0; i<n+1; ++i) cin >> buffer[i];
if(cin.eof()) break;
vector<double> bufferVector;
for(int i=0; i<n+1; ++i) bufferVector.push_back(buffer[i]);
data.push_back(bufferVector);
}
}
/**順方向の出力の計算**/
double forward(double **weightAlliance, const double *weightReaction,
double *outAlliance, const vector<double> &dataField,
const int &n, const int &numAlliance)
{
double out = 0.0;
// outAllianceの計算
for(int i=0; i<numAlliance; ++i){
double u = 0; // 重み付き和
for(int j=0; j<n; ++j){
u += dataField[j] * weightAlliance[i][j];
}
u -= weightAlliance[i][n]; // しきい値の処理
outAlliance[i] = sigmoid(u);
}
// outの計算
for(int i=0; i<numAlliance; ++i)
out += outAlliance[i] * weightReaction[i];
out -= weightReaction[numAlliance]; // しきい値の処理
return sigmoid(out);
}
/**出力層の重みの調整**/
void learnReaction(double *weightReaction, double *outAlliance,
const vector<double> &dataField, double outReaction,
const int &n, const int &numAlliance, const int &ALPHA)
{
double diff; // 誤差
diff = (dataField[n] - outReaction) * outReaction * (1 - outReaction);
// 結合荷重の調整
for(int i=0; i<numAlliance; ++i)
weightReaction[i] += ALPHA * outAlliance[i] * diff;
// しきい値の調整
weightReaction[numAlliance] += ALPHA * (-1.0) * diff;
}
/**中間層の重みの調整**/
void learnAlliance(double **weightAlliance, double *weightReaction,
double *outAlliance, const vector<double> &dataField, double outReaction,
const int &n, const int &numAlliance, const int &ALPHA)
{
double diff; // 重みが考慮された誤差
// i番目の中間層セルについて
for(int i=0; i<numAlliance; ++i){
diff = outAlliance[i] * (1 - outAlliance[i]) * weightReaction[i] *
(dataField[n] - outReaction) * outReaction * (1 - outReaction);
// j番目の重みを調整
for(int j=0; j<n; ++j)
weightAlliance[i][j] += ALPHA * dataField[j] * diff;
weightAlliance[i][n] += ALPHA * (-1.0) * diff; // しきい値の調整
}
}
/**結合荷重の出力**/
void printWeight(double **weightAlliance, double *weightReaction,
const int &n, const int &numAlliance)
{
for(int i=0; i<numAlliance; ++i){
for(int j=0; j<n+1; ++j)
cout << weightAlliance[i][j] << " ";
}
cout << endl;
for(int i=0; i<numAlliance+1; ++i)
cout << weightReaction[i];
cout << endl;
}
int main(int argc, char *argv[])
{
// 定数
const int ALPHA = 10; // 学習係数
// 変数
int n; // 変数の数=入力層の数
int numAlliance; // 中間層のセル数
double errorLimit; // 誤差の上限値
vector< vector<double> > data; // 学習データセット
double **weightAlliance = 0; // 中間層層の重み
double *weightReaction = 0; // 出力層の重み
double *outAlliance = 0; // 中間層の出力
double outReaction = 0; // 出力層の出力
double error = DBL_MAX; // データとの誤差
int count = 0; // 繰り返し回数のカウンタ
// 引数のチェック
if(argc != 4){
cerr << "使い方 $./backPropagation "
<< "(変数の数) (中間層のセル数) (誤差の上限値) "
<< "< (入力ファイル)" << endl;
return -1;
}
if((n=atoi(argv[1])) < 1){
cerr << "変数の数の値が不適切です" << endl;
return -1;
}
if((numAlliance=atoi(argv[2])) < 1){
cerr << "中間層のセル数が不適切です" << endl;
return -1;
}
if((errorLimit=atof(argv[3])) == 0.0){
cerr << "収束しない可能性があります" << endl;
}
// get memory
weightAlliance = new double * [numAlliance];
weightReaction = new double [numAlliance+1]; // [numAlliance]はしきい値
for(int i=0; i<numAlliance; ++i) weightAlliance[i] = new double [n+1];
outAlliance = new double [numAlliance];
initWeight(n, numAlliance, weightAlliance, weightReaction);
readData(data, n);
// 学習
while(error > errorLimit){
error = 0.0;
for(int i=0; i<data.size(); ++i){
// 順方向の計算
outReaction = forward(weightAlliance, weightReaction, outAlliance,
data[i], n, numAlliance);
// 出力層の重みの調整
learnReaction(weightReaction, outAlliance,
data[i], outReaction, n, numAlliance, ALPHA);
// 中間層の重みの調整
learnAlliance(weightAlliance, weightReaction, outAlliance,
data[i], outReaction, n, numAlliance, ALPHA);
// 誤差の積算
error += (outReaction - data[i][n]) *
(outReaction - data[i][n]);
}
++count;
// 誤差の出力
cout << count << "t" << error << endl;
} // 学習終了
// 結果の出力
// 1.結合荷重の出力
printWeight(weightAlliance, weightReaction, n, numAlliance);
// 2.学習データに対する結果の出力
for(int i=0; i<data.size(); ++i){
cout << i << " ";
for(int j=0; j<n+1; ++j)
cout << data[i][j] << " ";
outReaction = forward(weightAlliance, weightReaction, outAlliance,
data[i], n, numAlliance);
cout << outReaction << endl;
}
// memory release
for(int i=0; i<numAlliance; ++i) delete [] weightAlliance[i];
delete [] weightAlliance;
delete [] weightReaction;
delete [] outAlliance;
return 0;
}
5章はちょっと習作を思いついたのでまた今度になるかも
コメント