質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.52%

ニューロンの工学的モデルー確率的2値モデル

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,588

jimmypage0311

score 20

#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;

double sigmoid(int sum,int theta)/*シグモイド関数*/
{
    double sig;
    sig=1/(1+exp(theta-sum));

    return sig;
}

int main()
{
    int x1=1;
    int x2=0;
    int x3=1;
    int sum;
    int cnt=0;
    int theta=1;
    //double gain=1.0;

    srand( (unsigned int)time( NULL ) );

    sum=x1*3+x2*2+x3*(-1);

    for(int i=0;i<1000;i++){

    if((double)rand()/(double)RAND_MAX<=sigmoid(sum,theta)){
        cnt++;
    }else{

    }
    }

    cout<<"実験値:"<<cnt<<endl;
    cout<<"理論値:"<<1000*sigmoid(sum,theta)<<endl;

    return 0;
}

ニューラル・ネットワークについての質問です。

以下のようなモデルのニューラル・ネットワークについてプログラムをしたのですが、回答と答えが合いません。
gain:1.0、繰り返し回数:1000回

イメージ説明

---結果---
実験値:730 理論値:731.059

このコードでは回答と結果が違ってきます。
シグモイド関数内のthetaとsumを入れ替えると結果が合うのですが、シグモイド関数自体が1/1+exp(-αz)であるため、符号はこのままで良いと思うのですが。
符号を変えると回答に近い結果が出ます。

わかる方がいらっしゃればよろしくお願いいたします。

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/* メインプログラム */
main()
{
int count_1, cycle, Num_of_cycle,  Output, ideal, unit_index;
double Input[3], weight[3], theta, gain, net_input;
int probablistic_unit( double* weight, double theta, double* Input, double gain );
double sigmoid( double ney_input, double gain );

/* ゲインを1に設定する。 */
gain = 1.0;

/* 繰り返し回数を設定する。 */
Num_of_cycle = 1000;

/* 重みと閾値を設定する。 */
weight[0] = 3; weight[1] = 2; weight[2] = -1; theta = 1.0;

/* 乱数の種を設定する。 */
srand( 0 );

/* 素子に (1, 0, 1) を入力する時 */
Input[0] = 1; Input[1] = 0; Input[2] = 1;

/* 理論値 */
net_input = 0;
for ( unit_index = 0; unit_index < 3; unit_index++ )
  {
   net_input = net_input + weight[unit_index] * Input[unit_index];
  }
net_input = net_input - theta;
ideal = (int) (Num_of_cycle * sigmoid( net_input, gain ));

/* 実験値 */
count_1 = 0;
for ( cycle = 0; cycle < Num_of_cycle; cycle++ )
  {
   Output = probablistic_unit(weight, theta, Input, gain);
   if ( Output == 1)
     count_1 = count_1 + 1;
  }
printf("----- 素子に (1, 0, 1) を入力する時 ----- ¥n");
printf("素子が 1 を出力する頻度(実測値): %d / %d ¥n", count_1, Num_of_cycle);
printf("素子が 1 を出力する頻度(理論値): %d / %d ¥n", ideal, Num_of_cycle);

/* 素子に (1, -1, 0) を入力する時 */
Input[0] = 1; Input[1] = -1; Input[2] = 0;

/* 理論値 */
net_input = 0;
for ( unit_index = 0; unit_index < 3; unit_index++ )
  {
   net_input = net_input + weight[unit_index] * Input[unit_index];
  }
net_input = net_input - theta;
ideal = (int) (Num_of_cycle * sigmoid( net_input, gain ));

/* 実験値 */
count_1 = 0;
for ( cycle = 0; cycle < Num_of_cycle; cycle++ )
  {
   Output = probablistic_unit(weight, theta, Input, gain);
   if ( Output == 1)
     count_1 = count_1 + 1;
  }

printf("¥n");
printf("----- 素子に (1, -1, 0) を入力する時 ----- ¥n");
printf("素子が 1 を出力する頻度(実測値): %d / %d ¥n", count_1, Num_of_cycle);
printf("素子が 1 を出力する頻度(理論値): %d / %d ¥n", ideal, Num_of_cycle);
}

/* 素子動作を計算する関数 */
int probablistic_unit( double* weight, double theta, double* Input, double gain )
{
int unit_index;
double net_input;

net_input = 0;
for ( unit_index = 0; unit_index < 3; unit_index++ )
  {
   net_input = net_input + weight[unit_index] * Input[unit_index];
  }
net_input = net_input - theta;

if ( rand() < sigmoid( net_input, gain ) * RAND_MAX )
   return 1;
else
   return 0;
}

/* シグモイド関数 */
double sigmoid( double z, double gain )
{
return 1.0 / (1.0 + exp( gain * z )); 
}

----------------------------------  プログラムの実行結果  ---------------------------------
----- 素子に (1, 0, 1) を入力する時 -----        
素子が 1 を出力する頻度(実測値): 237 / 1000     
素子が 1 を出力する頻度(理論値): 268 / 1000


上のプログラムが回答となります。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

checkベストアンサー

+2

機械学習を専攻してる学生です。

シグモイド関数は原点に置ける出力値対して点対称の関数ですべての実数の入力に対して0より大きく1より小さいの全ての値を出力として取ることが出来る関数です。ゲインパラメータは入力の対して勾配の変化をつけているものです。

ニューラルネットにおいては何に使うかと言いますと入力に対する答えが◯である確率×である確率を0から1の間で2つに分けるのに教師学習によって決定した入力値に対するシグモイド関数の値を使います。質問者さんのプログラムでは、その後一様乱数によってシグモイド関数より上の値の乱数が生成されたら◯、下なら×というラベルをつけて◯であった確率はどれくらいかというようなことを計算して出してる感じです。

シグモイドの入力は恣意的に決定しているので理論値はそのままシグモイド関数の値で◯と×の値の割合の区切り値になっているのでそれを試行回数でかければ◯か×の期待値になるという感じです。

シグモイド関数の厳密な定義に沿った一層パーセプトロンは質問者さんのプログラムが正しいです。

模範の方はシグモイドの入力の符号を入れ替えてますが符号を入れ替えるとシグモイドは原点に対する出力点に対して上下に値が入れ替わるだけなので◯と×の対応関係が入れ替わるだけで本質はどちらのコードも変わりません。

シグモイド関数より上を◯とする習慣があるので模範は逆シグモイド関数を使ってそれ以下の値を1として採用することで元のシグモイド関数より上の値を◯として扱うことと等価な実装になっていますね。これはモデル化の問題なので綺麗な実装になるように調節していい所だと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/01/01 14:07

    コメントありがとうございます。

    お返事遅くなり申し訳ありません。
    詳しいご解説ありがとうございます。
    参考にさせていただきます。

    よろしくお願いいたします。

    キャンセル

+1

こんにちは。

シグモイド関数についてはよく分かりませんが、単に0~1の間の単なる定数として使っているように見えます。
また、単純に試行回数が足りないだけと思います。モンテカルロ法で1000回の試行で3桁の精度を期待するのは無理なのでは? sumとthetaを入れ替えて結果が一致したのは単なる偶然かと。


【追記】
まだ「回答」を記載していなかったのですね。

前者のプログラムは下記ですね。

    理論値 = 1000 * (1/(1+exp(theta-sum)));

「回答」のnet_inputは最初のプログラムのsumと同じですね。ということは下記になります。

    ideal = 1000 * (1/(1+exp(sum-theta)));

最初のプログラムはtheta-sum、「回答」のプログラムはsum-thetaになっていますので、「シグモイド関数内のthetaとsumを入れ替えると結果が合う」のは当然と思います。

なお、ここは「プログラミング」に関するQAサイトですので、もし、シグモイド関数に関する質問でしたら掲示板違いに当たると思います。たまたま知っている人がいれば回答あるかもしれませんが、あまり期待できないかと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/26 22:41

    コメントありがとうございます。

    回答のプログラムを見ると試行回数が1000回であるということがわかります。

    よろしくお願いいたします。

    キャンセル

  • 2015/12/27 17:52

    コメントありがとうございます。

    また、ご指摘ありがとうございます。

    キャンセル

0

ループ回数増やせば大体近い値になるのでこんなもんじゃないでしょうか


追記見ました
2つのコードでシグモイド関数の定義が異なるようなので
結果が違うのはその辺りが原因です。

なにか前提条件があるか、回答コードが間違っているかです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/27 12:20

    コメントありがとうございます。

    キャンセル

0

シグモイド関数やニューロンについてはわからないのでコードの善し悪しも評価できないですが、少なくとも提示されたコードに於いて値が一致しないのは必然です。
なぜなら実験値はifの条件に一致した回数を数えているだけなので常に整数なのに対し
理論値はeに関する関数の戻り値を1000倍したものなので、整数になる事はないからです。

シグモイド関数内のthetaとsumを入れ替えると結果が合う

ですので、こちらは奇妙だなと思いました。
プログラムではsumは固定値で[2]、thetaは固定値で「1」です。
そうすると次のように、thetaとsumの順番にかかわらず理論値は整数になりません。

1000*sigmoid(sum,theta)
 = 1/(1+exp(theta-sum))*1000
 = (1/(1+e^(1-2)))*1000
 ≒ 731.05857863

1000*sigmoid(theta,sum)
 = 1/(1+exp(sum-theta))*1000
 = (1/(1+e^(2-1)))*1000
 ≒ 268.94142137

しかし実測値は整数なので完全一致はしないと思うのです。

もし質問が、「なぜ731ではなく730なのか?」ということであれば、他の回答者のおっしゃるように試行回数の問題かと思います。
このプログラムは実行するたびに実測値が変化すると思いますが、プログラムを何万回も実行してそれぞれの実測値の平均を取れば理論値に近い値が得られるような気がします。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

乱数でシミュレートして近似値を取得しているので、完全に一致するということは期待しない方がいいと思います。他の方が回答されているように、試行回数が少ないと誤差も大きくなります。

それと、この手の乱数生成にrand関数がふさわしいかどうかも気になります。処理系によってはRAND_MAXが32767と定義されている場合があります。
ですので、C++なら<random>を使うことをお勧めします。MT19937は周期が長くパフォーマンス的にも優れているので広く使われています。

0~1の乱数を取得する場合は以下のようなコードになります。

std::random_device seed;
std::mt19937 rndengine(seed());
std::uniform_real_distribution<double> unidist(0.0, 1.0);

double r = unidist(rndengine);        // 0.0~1.0の乱数を取得

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/26 22:39

    コメントありがとうございます。

    回答のプログラムも試行回数が1000回であるので、そこは大丈夫であると思います。
    また、回答でもrand()を使用しているのでそこは問題にならないかと

    キャンセル

  • 2015/12/26 23:24

    ああ、そういうことでしたか。回答というのは論理値のことではなくて追加されたコードの出力結果ということですね。
    すみません。シグモイド関数のことは判らないので回答は判る方にお任せします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる