🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
深層学習

深層学習は、多数のレイヤのニューラルネットワークによる機械学習手法。人工知能研究の一つでディープラーニングとも呼ばれています。コンピューター自体がデータの潜在的な特徴を汲み取り、効率的で的確な判断を実現することができます。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

1768閲覧

ニューラルネットワーク 学習が上手くいかない

Watching

総合スコア56

深層学習

深層学習は、多数のレイヤのニューラルネットワークによる機械学習手法。人工知能研究の一つでディープラーニングとも呼ばれています。コンピューター自体がデータの潜在的な特徴を汲み取り、効率的で的確な判断を実現することができます。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2021/02/24 18:53

自分でニューラルネットワークを作っています。手始めに、論理回路を学習させてみようと思い、入力層2ノード、隠れ層30ノード、出力層1ノードの三層のニューラルネットワークを構築しました。重みの初期値は-1.0から1.0までの値を偏りなくランダムに設定します。学習効率は0.03で、学習は、(1,1)(0,1)(1,0)(0,0)の入力と答えを順番に学習させ、これを1000回繰り返します。
この時、ANDやORは正しく?学習できましたが、XORやEXORの学習は上手くいきませんでした。
左の値は入力値で右の値は出力値です。いくつか結果を挙げます。

AND

1(1,1) = 0.750126 2(1,0) = 0.188411 3(0,1) = 0.187444 4(0,0) = 0.00650456

OR

1(1,1) = 0.982309 2(1,0) = 0.887062 3(0,1) = 0.891423 4(0,0) = 0.173294

XOR

1(1,1) = 0.511378 2(1,0) = 0.510599 3(0,1) = 0.500787 4(0,0) = 0.48574

EXOR

1(1,1) = 0.514631 2(1,0) = 0.500916 3(0,1) = 0.497562 4(0,0) = 0.493797
//一番目の値=出力値 (1,1) = 0.903502 (1,0) = 0.926415 (0,1) = 0.0982237 (0,0) = 0.0923838

グラフ上で線形に答えが区切れる場合だけ正しく学習出来ているように思えます。何が問題だと考えられますか?

参考のため、以下に全コードを張り付けておきます。

c++

1//main.cpp 2#include <array> 3#include "neural.h" 4int main() { 5 NeuralNetwork<2, 30, 1> logic(0.03); 6 array<double, 1> result; 7 for (size_t i = 0; i < 1000; i++) 8 { 9 logic.study({ {1,1} }, { {1} }); 10 logic.study({ {1,0} }, { {1} }); 11 logic.study({ {0,1} }, { {0} }); 12 logic.study({ {0,0} }, { {0} }); 13 } 14 result = logic.calculate({ {1,1} }); 15 std::cout << "(1,1) = " << result[0] << endl; 16 result = logic.calculate({ {1,0} }); 17 std::cout << "(1,0) = " << result[0] << endl; 18 result = logic.calculate({ {0,1} }); 19 std::cout << "(0,1) = " << result[0] << endl; 20 result = logic.calculate({ {0,0} }); 21 std::cout << "(0,0) = " << result[0] << endl; 22}

c++

1//neural.h 2#include <random> 3#include <cmath> 4#include <sstream> 5#include <array> 6#include <functional> 7 8template <typename _Ty, size_t _column, size_t _row> class matrix { 9private: 10 std::array<std::array<_Ty, _row>, _column> content; 11public: 12 static void foreach(std::function<void(size_t colmun, size_t row)> doeach, std::function<void(size_t colmun)>doeachendofcolumn = [](size_t colmun) {}) { 13 for (size_t i = 0; i < _column; i++) 14 { 15 for (size_t j = 0; j < _row; j++) 16 { 17 doeach(i, j); 18 } 19 doeachendofcolumn(i); 20 } 21 } 22 matrix() { foreach([&](size_t i, size_t j) {content[i][j] = _Ty(); }); }; 23 matrix(std::array<std::array<_Ty, _row>, _column> i) { content = i; } 24 constexpr size_t row() const { return _row; } 25 constexpr size_t column() const { return _column; } 26 matrix<_Ty, _row, _column> transpose() const { matrix<_Ty, _row, _column>re; foreach([&](size_t i, size_t j) {re[j][i] = content[i][j]; }); return re; } 27 std::array<_Ty, _row> const& operator [](size_t t) const { return content[t]; } 28 std::array<_Ty, _row>& operator [](size_t t) { return content[t]; } 29 matrix<_Ty, _column, _row>& operator +=(matrix<_Ty, _column, _row> const& m) { 30 foreach([&](size_t i, size_t j) {content[i][j] = content[i][j] + m[i][j]; }); 31 return *this; 32 } 33 matrix<_Ty, _column, _row>& operator -=(matrix<_Ty, _column, _row> const& m) { 34 foreach([&](size_t i, size_t j) {content[i][j] = content[i][j] - m[i][j]; }); 35 return *this; 36 } 37 matrix<_Ty, _column, _row>& operator *=(size_t i) { 38 foreach([&](size_t i, size_t j) {content[i][j] *= i; }); 39 return *this; 40 } 41 template<typename _nTy> 42 operator _nTy() const { matrix<_nTy, _column, _row> re; foreach([&](size_t i, size_t j) {re[i][j] = (_nTy)content[i][j]; }); return re; } 43 std::string str() const { 44 std::stringstream ss; 45 foreach([&](size_t i, size_t j) {ss << content[i][j] << ",\t"; }, [&](size_t) { ss << "\n"; }); 46 return ss.str(); 47 } 48}; 49template <typename _Ty, size_t _column, size_t _row> 50const matrix<_Ty, _column, _row> operator +(matrix<_Ty, _column, _row> const& f, matrix<_Ty, _column, _row> const& s) { 51 return matrix<_Ty, _column, _row>(f) += s; 52} 53template <typename _Ty, size_t _column, size_t _row> 54const matrix<_Ty, _column, _row> operator -(matrix<_Ty, _column, _row> const& f, matrix<_Ty, _column, _row> const& s) { 55 return matrix<_Ty, _column, _row>(f) -= s; 56} 57template <typename _Ty, size_t _column, size_t _row_scolumn, size_t _srow> 58const matrix<_Ty, _column, _srow> operator *(matrix<_Ty, _column, _row_scolumn> const& f, matrix<_Ty, _row_scolumn, _srow> const& s) { 59 matrix<_Ty, _column, _srow> re; 60 matrix<_Ty, _column, _row_scolumn>::foreach([&](size_t i, size_t j) { 61 for (size_t k = 0; k < _srow; k++) { 62 re[i][k] += f[i][j] * s[j][k];//配列を横に動いてから縦に動くのでメモリアクセス的に高速なはず 63 } 64 }); 65 66 return re; 67} 68template <typename _Ty, size_t _column, size_t _row> 69const matrix<_Ty, _column, _row> operator *(size_t i, matrix<_Ty, _column, _row> const& m) { 70 return matrix<_Ty, _column, _row>(m) *= i; 71} 72template <typename _Ty, size_t _column, size_t _row> 73const matrix<_Ty, _column, _row> operator *(matrix<_Ty, _column, _row> const& m, size_t i) { 74 return m * i; 75} 76 77std::random_device seed_gen; 78std::default_random_engine engine(seed_gen()); 79constexpr double sigmoid_range = 34.538776394910684; 80double sigmoid(double d) { 81 if (d > sigmoid_range) d = sigmoid_range; 82 if (d < -sigmoid_range) d = -sigmoid_range; 83 return 1 / (1 + std::exp(-d)); 84} 85void avoid_flow(double& d, double upper_range = 1.0e300, double under_range = 1.0e-300) { 86 if (d < -upper_range) d = -upper_range; 87 if (d > -under_range && d < under_range) d = 0; 88 if (d > upper_range) d = upper_range; 89} 90template<size_t nodecount> 91class Layer { 92private: 93 matrix<double, nodecount, 1> nodes; 94public: 95 Layer() {}; 96 Layer(matrix<double, nodecount, 1> m) { nodes = m; } 97 operator matrix<double, nodecount, 1>() { return nodes; } 98 Layer(std::array<double, nodecount> ar) { 99 for (size_t i = 0; i < nodecount; i++) 100 { 101 nodes[i][0] = ar[i]; 102 } 103 } 104 operator std::array<double, nodecount>() { 105 std::array<double, nodecount> re; 106 for (size_t i = 0; i < nodecount; i++) { 107 re[i] = nodes[i][0]; 108 } 109 return re; 110 } 111 double& operator[](size_t i) { return nodes[i][0]; } 112 void activation() { 113 for (size_t i = 0; i < nodecount; i++) 114 { 115 nodes[i][0] = sigmoid(nodes[i][0]); 116 } 117 } 118}; 119template<size_t inputnodecount, size_t outputnodecount> 120class Weight { 121private: 122 matrix<double, outputnodecount, inputnodecount> weight; 123public: 124 Weight() {}; 125 Weight(matrix<double, outputnodecount, inputnodecount> const& m) { weight = m; }; 126 void renew(Layer<inputnodecount> input, Layer<outputnodecount> delta, double learning_rate) { 127 weight.foreach([&](size_t i, size_t j) {weight[i][j] -= learning_rate * delta[i] * input[j]; avoid_flow(weight[i][j]); }); 128 } 129 Weight<outputnodecount, inputnodecount> transpose() { return weight.transpose(); }; 130 operator matrix<double, outputnodecount, inputnodecount>() { return weight; } 131 std::array<double, inputnodecount> operator[](size_t i) { return weight[i]; } 132 void randamize() { 133 std::uniform_real_distribution<> rnd(-1.0, 1.0); 134 weight.foreach([&](size_t i, size_t j) {weight[i][j] = rnd(engine); }); 135 } 136}; 137template<size_t inputnodecount, size_t outputnodecount> 138Layer<outputnodecount> const operator*(Weight<inputnodecount, outputnodecount> weight, Layer<inputnodecount> layer) { return (matrix<double, outputnodecount, inputnodecount>)weight * (matrix<double, inputnodecount, 1>)layer; } 139template<size_t inputnodecount, size_t hiddennodecount, size_t outputnodecount> 140class NeuralNetwork { 141private: 142 double learning_rate; 143 Weight<inputnodecount, hiddennodecount> input_to_hidden; 144 Weight<hiddennodecount, outputnodecount> hidden_to_output; 145 std::tuple<Layer<inputnodecount>, Layer<hiddennodecount>, Layer<outputnodecount>> calc(Layer<inputnodecount> input) { 146 Layer<hiddennodecount> hidden = input_to_hidden * input; 147 hidden.activation(); 148 Layer<outputnodecount> output = hidden_to_output * hidden; 149 output.activation(); 150 std::tuple<Layer<inputnodecount>, Layer<hiddennodecount>, Layer<outputnodecount>> re = std::make_tuple(input, hidden, output); 151 return re; 152 } 153public: 154 explicit NeuralNetwork(double rate = 0.3) :learning_rate(rate) { input_to_hidden.randamize(); hidden_to_output.randamize(); }; 155 std::array<double, outputnodecount> calculate(std::array<double, inputnodecount> in) { 156 return std::get<2>(calc(in)); 157 } 158 void study(std::array<double, inputnodecount> in, std::array<double, outputnodecount> answer) { 159 Layer<inputnodecount> input; 160 Layer<hiddennodecount> hidden; 161 Layer<outputnodecount> output; 162 std::tie(input, hidden, output) = calc(in); 163 Layer<outputnodecount> delta_out; 164 for (size_t i = 0; i < outputnodecount; i++) 165 { 166 delta_out[i] = (output[i] - answer[i]) * output[i] * (1.0 - output[i]); 167 } 168 hidden_to_output.renew(hidden, delta_out, learning_rate); 169 Layer<hiddennodecount> delta_hidden = hidden_to_output.transpose() * delta_out; 170 input_to_hidden.renew(input, delta_hidden, learning_rate); 171 } 172};

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

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

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

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

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

guest

回答2

0

ベストアンサー

質問者様のコードを直接読み解いたわけではありませんが、tensorflowで同じモデルを組んでみたところ、optimizerによってはxorは学習しきれない、という事象が出ました。基本的なSGDだとダメで、Adamにするとうまくいきます。これをヒントに、見直してみるとよいでしょう。

発生している事象から考えると、活性化層がうまく機能しておらず非線形が表現できていないのかと思いました。ところが、コードを少し読み解くと、sigmoidが入っているようでした。よって、別要因だと思い、tensorflowで試してみたわけです。

追記: optimizerはSGDのままでも、入力層の活性化をsigmoidからreluに変えてみたところ、学習ができました。こちらの方が「普通」の実装ですし、かつ試しやすいものと思います。sigmoidを重ねることによる勾配消失が、SGDと相性が悪いのかな、と思います。

なお、フレームワークに頼らずご自分で機械学習をゼロから作られるツワモノな方が時々いらっしゃいます。しかし、その動作で疑問が出た際には、ご自分のコードを確かめる前に、まずはフレームワークで同じことをやってみてどうか、を確認するとよいと思います。

試したコードは以下

Python

1from tensorflow.keras import Sequential, optimizers 2from tensorflow.keras.layers import Dense 3 4# データ作成 5x, y, = [], [] 6for _ in range(1000): 7 x.append([1, 1]) 8 x.append([0, 1]) 9 x.append([1, 0]) 10 x.append([0, 0]) 11 # y.extend([1, 0, 0, 0]) # and 12 # y.extend([1, 1, 1, 0]) # or 13 y.extend([0, 1, 1, 0]) # xor 14 15# モデル作成 16model = Sequential([ 17 Dense(30, input_dim=2, activation='sigmoid'), 18 Dense(1, activation='sigmoid') 19]) 20optimizer = optimizers.SGD(lr=0.03) 21# optimizer = optimizers.Adam(lr=0.03) 22model.compile(optimizer=optimizer, loss='mse', metrics='mse') 23 24# 学習と推論 25result = model.fit(x, y, batch_size=1, epochs=1) 26y_pred = model.predict([[1, 1], [0, 1], [1, 0], [0, 0]]) 27print(y_pred)

実行結果

# and、optimizer.SGD (学習を増やすともっと精度が高まる) [[0.6408773 ] [0.20385471] [0.21725073] [0.03093234]] # or、optimizer.SGD (学習を増やすともっと精度が高まる) [[0.9758366] [0.831491 ] [0.8254561] [0.2677223]] # xor、optimizer.SGD (学習を増やしても精度が高まらない) [[0.5727341 ] [0.56092936] [0.5583111 ] [0.5451822 ]] # xor、optimizer.Adam [[0.01439664] [0.9902004 ] [0.99029773] [0.0064072 ]]

投稿2021/02/24 22:53

編集2021/02/24 23:04
toast-uz

総合スコア3266

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

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

Watching

2021/02/25 07:07 編集

学習に使ってるアルゴリズムがSDGですらないただの最急降下法だから、正しい最小値に到達できていないっていうのが1番ありそうですね。XORをグラフとして考えてみると、極小値と極小値の間に山があるのが容易に想像できます… 自分のプログラムがどこか間違っているとずっと思い込んでました。 学習の方法などを変えていろいろ試してみようと思います。
Watching

2021/02/25 17:57

隠れ層の活性化関数をReLUにしたところ上手くXORの学習ができました。 やった!と思って、今度は手書き数字を学習させてみると、どんな入力でも9割同じ答えになってしまうように学習されてしまう... 今度は逆にReLUからsigmoidに戻してみたらなぜか8割近い精度が出るようになり...
guest

0

間違えたところに投稿したので削除

投稿2021/02/24 23:54

編集2021/02/24 23:56
Watching

総合スコア56

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問