NNによるsin波の学習について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
リョースK

NNによるsin波の学習について

#1

投稿記事 by リョースK » 3年前

現在、NNによるsin波の学習を行うプログラムを作成しているのですが、学習後の出力が全てnanになったり、全て1になったりとどうにもうまくいかず、原因もわかりません。
NNのレイヤー数は3,入力はxと1(バイアス),中間層のニューロン数は10となっております。
以下にプログラムを載せておきますので、どうか相談に乗っていただけると助かります。


code
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define ITERATIONS (100000)//繰り返し回数


double sigmoid(double net){// sigmoid関数により各層,各ニューロンのoutを返す。
return 1/(1+exp(- net));
}

//重みの初期値設定(乱数)
/*保留:Haivierhの初期値
double Uniform( void ){
return ((double)rand()+1.0)/((double)RAND_MAX+2.0);
}
double rand_normal( double mu, double sigma ){//mu:平均,sigma:標準偏差
double z=sqrt( -2.0*log(Uniform()) ) * sin( 2.0*M_PI*Uniform() );
return mu + sigma*z;
}
*/
double default_value(){//重みを−1~1で初期設定
return (rand() / (double)RAND_MAX)*2-1;
}


int main() {
srand((unsigned) time(NULL));
double num = -M_PI;
FILE*fp;
fp = fopen("data.txt","w");
//教師データ
double x[50];
double y[50];
int count = 0;
//入力信号,教師データ生成
for(;num <= M_PI;num = num + (double)(2*M_PI/49)){
x[count] = num;
y[count] = sin(num);
count++;
}

//バイアス・重みの初期値設定
double bias[9] = {0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1};//{0,0,0,0,0,0,0,0,0};
double w1[9]; //入力層→隠れ層の重み
double w2[10];//隠れ層→出力層の重み(w_2[0]はバイアス)
for(count = 0;count<=9;count++){
if(count == 10){
w2[count] = default_value();
}
else{
w1[count] = default_value();
w2[count] = default_value();
}
}
w2[0] = 0;//バイアスの初期値は0に設定
/*重みの初期値の確認
double w_11 = 0;
double w_22 = 0;
for(count = 0;count<10;count++){
w_11 += w1[count];
w_22 += w2[count];
printf("%lf と %lf\n",w1[count],w2[count]);
}
*/
double p = 0.01;//学習率
double net2[9];//隠れ層への入力
double out2[10];//出力層への出力
out2[0] = 1/(1 + exp(-1));
double net3 = 0;//出力層への入力
double out3[50];//出力信号
double E = 0;//二乗誤差
double delta3;
//double delta2;
//double del_E;//∂E/∂w(or ∂E/∂bias)
int i = 0;
for(count = 0;count<50;count++){//サンプルされた50点のに対するそ処理
for(int learing_count=0;learing_count < ITERATIONS+1;learing_count++){//学習回数10万回についての処理,*「ITERATIONS+1」にした理由→10万回学習させた後に最終的な出力を出すため
for(i = 0;i < 9;i++){
net2 = w1*x[count] + bias;
out2[i+1] = 1/(1 + exp(-net2));
}

for(i=0;i < 10;i++){
net3 += w2*out2;
}
out3[count] = 1/(1 + exp(-net3));
if(learing_count % 10000 == 0){
printf("%lf %lf %lfだよ\n",net2[3],out2[3],out3[count]);
}
//二乗誤差
E =0.50*(y[count] - out3[count])*(y[count] - out3[count]);

if(E == 0.0){
//printf("%d %lf\n",count,out3[count]);
break;
}
else{
//NR
}
//重み更新(出力~隠れ層間)
delta3 = (out3[count] - y[count])*out3[count]*(1-out3[count]);//(out3[count] - y[count])*exp(-net3)/((1+exp(-net3))*(1+exp(-net3)));

for(i = 0;i < 10;i++){//w_2の各重み更新
w2 = w2 - p*delta3*out2;
}
for(i = 0;i < 9;i++){
bias = bias[i] - p*delta3 * w2[i+1]*out2[count]*(1-out2[count]);//bias[i] - p*delta3 * w2[i+1]*exp(-net2[i])/((1+exp(-net2[i]))*(1+exp(-net2[i])));
w1[i] = w1[i] - p*x[count]*delta3 * w2[i+1]*out2[count]*(1-out2[count]);//w1[i] - p*x[count]*delta3 * w2[i+1]*exp(-net2[i])/((1+exp(-net2[i]))*(1+exp(-net2[i])));
}
if(learing_count % 10000 == 0){
printf("%lf %lf %lf %lf %lf\n",E,delta3,bias[3],w1[3],w2[3]);
}
}


}

for(count = 0;count<50;count++){
fprintf(fp,"%lf\t%lf\t%lf\n",x[count],y[count],out3[count]);
}
return 0;
}
/code

tanu_kichi
記事: 15
登録日時: 4年前

Re: NNによるsin波の学習について

#2

投稿記事 by tanu_kichi » 3年前

とりあえず、気付いた所を指摘します。
(時間が確保できれば、修正済みCソースをアップしたいと思います)
リョースK さんが書きました:
3年前
for(count = 0;count<50;count++){
for(int learing_count=0;learing_count < ITERATIONS+1;learing_count++){
...
}
}
は、

コード:

    for(int learing_count=0;learing_count < ITERATIONS+1;learing_count++){
        for(count = 0;count<50;count++){
            ... 
        }
    }
ではないでしょうか?
リョースKさんのコードだと、50個の入出力それぞれに対する重みとバイアスが計算されます。
リョースK さんが書きました:
3年前
for(i = 0;i < 9;i++){
net2 = w1*x[count] + bias;
out2[i+1] = 1/(1 + exp(-net2));
}

は、

コード:

            for(i = 0;i < 9;i++){
                net2[i] = 0.0; // 初期化
            }
            for(i = 0;i < 9;i++){
                net2[i] += w1[i]*x[count] + bias[i];
            }
            out2[i+1] = 1/(1 + exp(-net2[i]));
ではないでしょうか?
活性化関数は、sum( w1 * x[count] + bias ) の値に対して計算されるべきです。
リョースK さんが書きました:
3年前
for(i=0;i < 10;i++){
net3 += w2*out2;
}
out3[count] = 1/(1 + exp(-net3));

すみません、ここから以降は、まだ理解できてません。

その他:
1)y = sin(x) の y は -1.0 〜 1.0 の範囲の値を取るため、活性化関数は、2 * sigmoid(x) - 1.0 か tanh(x) を
使った方が計算が楽になるのではないかな〜と思います。
( 0.0 <= sigmoid(x) <= 1.0 / -1.0 <= tanh(x) <= 1.0 のため)
plot_sigmoid.png
点線は勾配
plot_sigmoid.png (19.51 KiB) 閲覧数: 11698 回
plot_tanh.png
点線は勾配
plot_tanh.png (23.84 KiB) 閲覧数: 11697 回
2)同一層のニューロンの個々のバイアスを計算する例を、私は未だ見たことがありません。
過去に私自身、同一層のニューロンの個々のバイアスをパラメータにして、計算したことはあったのですが、
収束しませんでした。
NNをやる上で、数学的に個々のバイアスを求めてはいけない理由があるのかもしれませんが、
ぐぐっても、情報が見つかりませんでした。

以上

tanu_kichi
記事: 15
登録日時: 4年前

Re: NNによるsin波の学習について

#3

投稿記事 by tanu_kichi » 3年前

コード:

            for(i = 0;i < 9;i++){
                net2[i] = 0.0; // 初期化
            }
            for(i = 0;i < 9;i++){
                net2[i] += w1[i]*x[count] + bias[i];
            }
            out2[i+1] = 1/(1 + exp(-net2[i]));

コード:

            if(count == 0) {
                for(i = 0;i < 9;i++){
                    net2[i] = 0.0; // 初期化
                }
            }
            for(i = 0;i < 9;i++){
                net2[i] += w1[i]*x[count] + bias[i];
            }
            if(count == 49) {
                for(i = 0;i < 9;i++){
                    out2[i+1] = 1/(1 + exp(-net2[i]));
                }
            }

tanu_kichi
記事: 15
登録日時: 4年前

Re: NNによるsin波の学習について

#4

投稿記事 by tanu_kichi » 3年前

teratail
https://teratail.com/questions/265214

>> sigmoid使って組め、と仕様が決められてしまっているんですよね…
という書き込みがあり、この質問は何かの課題と思われます。

課題出題者の趣旨を考慮して、直接、私が修正ソースコードをここに書き込むのを辞めます。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: NNによるsin波の学習について

#5

投稿記事 by みけCAT » 3年前

リョースK さんが書きました:
3年前

コード:

    double w1[9]; //入力層→隠れ層の重み
    double w2[10];//隠れ層→出力層の重み(w_2[0]はバイアス)
    for(count = 0;count<=9;count++){
        if(count == 10){
            w2[count] = default_value();
        }
        else{
            w1[count] = default_value();
            w2[count] = default_value();
        }
    }
この部分では、範囲外であるw1[9]へのアクセスが発生してしまい、危険です。
また、このループ条件ではcount == 10が真になることは無いはずなので、
意図とコードがずれている可能性が考えられます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: NNによるsin波の学習について

#6

投稿記事 by みけCAT » 3年前

リョースK さんが書きました:
3年前

コード:

    for(count = 0;count<10;count++){
        w_11 +=  w1[count];
        w_22 +=  w2[count];
        printf("%lf と %lf\n",w1[count],w2[count]);
    }
この部分でも、範囲外であるw1[9]へのアクセスが発生してしまい、危険です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

返信

“C言語何でも質問掲示板” へ戻る