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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

5回答

2678閲覧

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

jimmypage0311

総合スコア22

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

1グッド

1クリップ

投稿2015/12/26 11:57

編集2015/12/26 13:37
#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

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

ikuwow👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

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

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

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

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

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

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

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

投稿2015/12/27 15:43

編集2016/02/28 09:48
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimmypage0311

2016/01/01 05:07

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

0

こんにちは。

シグモイド関数についてはよく分かりませんが、単に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 12:15

編集2015/12/26 15:22
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimmypage0311

2015/12/26 13:41

コメントありがとうございます。 回答のプログラムを見ると試行回数が1000回であるということがわかります。 よろしくお願いいたします。
jimmypage0311

2015/12/27 08:52

コメントありがとうございます。 また、ご指摘ありがとうございます。
guest

0

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

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

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

C++

1std::random_device seed; 2std::mt19937 rndengine(seed()); 3std::uniform_real_distribution<double> unidist(0.0, 1.0); 4 5double r = unidist(rndengine); // 0.0~1.0の乱数を取得

投稿2015/12/26 13:27

catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimmypage0311

2015/12/26 13:39

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

2015/12/26 14:24

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

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

投稿2015/12/26 13:11

編集2015/12/26 13:26
hirohiro

総合スコア2068

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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


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

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

投稿2015/12/26 12:26

編集2015/12/27 01:01
ozwk

総合スコア13521

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimmypage0311

2015/12/27 03:20

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問