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

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

詳細はこちら
深層学習

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

機械学習

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

C++

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

Q&A

解決済

1回答

1959閲覧

C++ ディープラーニングの実装に失敗します

fps1

総合スコア0

深層学習

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

機械学習

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

C++

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

0グッド

0クリップ

投稿2020/11/29 15:08

前提・実現したいこと

ゼロから作るDeepLearningを読みながらC++に変換して試しています。
第4章を実装はしたのですが数値微分での学習のため時間がかかり
確認できていません。
第5章の誤差逆伝播法を実装したのですが、学習しません。
コードを見直して、分かった範囲で修正したのですが、根本の原因が分かりません。
修正箇所を教えてください。

発生している問題・エラーメッセージ

30700 / 1000000 : (回/ループ) 損失率 = 2.35 検出 答え↓ 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 予想↑

上記のように、予想が常に「0 1 0 0 0 0 0 0 0 0」と出ます。
また、損失率が下がりません。

該当のソースコード

行列計算クラス

C++

1matrixクラスの定義  2(行列演算クラス)

C++

1//main.cpp 2constexpr void Line(int Num = 1) { 3 for (int i = 0; i < Num; i++) { 4 std::cout << "ーーーーーーーーーーーーーー" << std::endl; 5 } 6} 7 8 9//出力関数 10namespace Output { 11 12 13 inline std::vector<double> Max_softmax(const matrix<double>& a) { 14 15 std::vector<double> max;// (a[0].size(), 0); 16 max.resize(a.Size_nrow()); 17 18 for (int i = 0; i < a.Size_nrow();i++) { 19 max[i] = a[0][i]; 20 } 21 22 for (size_t i = 0; i < a.Size_nrow(); i++) { 23 for (size_t j = 0; j < a.Size_ncol(); j++) { 24 if (max[i] < a[i][j]) { 25 max[i] = a[i][j]; 26 } 27 } 28 } 29 return max; 30 } 31 32 inline matrix<double> Softmax(const matrix<double>& a) { 33 34 matrix<double> exp_a = a, tmp = a; 35 double sum_exp_a = 0.0; 36 const std::vector<double> C = Max_softmax(a); 37 38 for (size_t i = 0; i < a.Size_nrow(); i++) { 39 for (size_t j = 0; j < a.Size_ncol(); j++) { 40 exp_a[i][j] = std::exp(a[i][j] - C[i]); 41 } 42 } 43 44 for (size_t i = 0; i < a.Size_nrow(); i++) { 45 for (size_t j = 0; j < a.Size_ncol(); j++) { 46 sum_exp_a += exp_a[i][j]; 47 } 48 } 49 50 51 for (size_t i = 0; i < a.Size_nrow(); i++) { 52 for (size_t j = 0; j < a.Size_ncol(); j++) { 53 tmp[i][j] = exp_a[i][j] / sum_exp_a; 54 } 55 } 56 57 return tmp; 58 } 59 60} 61 62//損失関数 63 64namespace LossFunction { 65//交差エントロピー誤差 66 67 double CrossEntropyError(const matrix<double>& y, const matrix<double >& t) { 68 69 //assert(y.Size_nrow() == 1 && t.Size_nrow() == 1); 70 assert(y.Size_ncol() == t.Size_ncol()); 71 72 constexpr double delta = 1e-5; 73 74 double tmpX = 0.0; 75 76 for (int i = 0; i < y.Size_nrow(); i++) { 77 for (int j = 0; j < y.Size_ncol(); j++) { 78 //数値が入っているか判定 79 if (t[i][j]) { 80 tmpX += t[i][j] * std::logl(y[i][j] + delta); 81 } 82 } 83 } 84 85 tmpX *= -1; 86 87 return tmpX; 88 } 89} 90 91//誤差逆伝播法 92namespace ErrorBack { 93//layer 94 95 class LastLayer { 96 protected: 97 matrix<double>* w, * b, x; 98 public: 99 matrix<double> dw, db; 100 protected: 101 matrix<double> mask; //forwardの結果を保存 102 public: 103 virtual ~LastLayer() {}; 104 virtual matrix<double> forward(const matrix<double>&) = 0; 105 virtual matrix<double> backward(const matrix<double>& dout) = 0; 106 }; 107 108 109 //ReLUレイヤ 110 class ReLU final :public LastLayer { 111 112 public: 113 114 matrix<double> forward(const matrix<double>& x) override { 115 116 matrix<double> mat = x; 117 mask = mat; 118 119 for (int i = 0; i < mat.Size_nrow(); i++) { 120 for (int j = 0; j < mat.Size_ncol(); j++) { 121 if (0 >= mask[i][j]) { 122 mat[i][j] = 0; 123 } 124 } 125 } 126 return mat; 127 } 128 129 matrix<double> backward(const matrix<double>& dout)override { 130 131 matrix<double> dx = dout; 132 133 for (size_t i = 0; i < dout.Size_nrow(); i++) { 134 for (size_t j = 0; j < dout.Size_ncol(); j++) { 135 if (0 >= mask[i][j]) { 136 dx[i][j] = 0; 137 } 138 } 139 } 140 141 return dx; 142 } 143 }; 144 145//Affine 146class Affine final :public LastLayer { 147 public: 148 Affine() = delete; 149 150 Affine(matrix<double>* w, matrix<double>* b) { this->w = w; this->b = b; } 151 152 matrix<double> forward(const matrix<double>& x) { 153 154 this->x = x; 155 156 matrix<double> out = x * *this->w + *this->b; 157 158 159 return out; 160 } 161 162 matrix<double> backward(const matrix<double>& dout) { 163 164 //1時保存 165 matrix<double> W = *w, X = x; 166 167 168 //回転 169 W.permutation(); 170 X.permutation(); 171 172 173 matrix<double> dx = dout * W; 174 175 this->dw = X * dout; 176 177 178 179 std::vector<double> tmpB; 180 181 for (int i = 0; i < dout.Size_ncol(); i++) { 182 double tmp = 0.0; 183 for (int j = 0; j < dout.Size_nrow(); j++) { 184 tmp += dout[j][i]; 185 } 186 tmpB.push_back(tmp); 187 } 188 189 db = matrix<double>(std::vector<std::vector<double>>(1, tmpB)); 190 191 return dx; 192 } 193 194 }; 195 196 197 //Softmax-with-Losslayer 198 class SoftmaxWithLoss { 199 matrix<double> y, t; 200 double loss; 201 public: 202 double forward(matrix<double>& x, const matrix<double>& t) { 203 204 this->t = t; 205 this->y = Output::Softmax(x); 206 //バッチ処理 207 this->loss = LossFunction::CrossEntropyError(this->y, this->t); 208 209 return this->loss; 210 } 211 212 matrix<double> backward(double dout = 1) { 213 214 double batch_size = t.Size_nrow(); 215 216 return (this->y - this->t) / batch_size; 217 218 } 219 }; 220 221 222 class Net { 223 public: 224 //種類の違う行列を入れる 225 std::unordered_map<std::string, matrix<double>> params; 226 227 //layerを入れる 228 std::vector< LastLayer*> layers; 229 230 231 private: 232 //入力層の入力数 233 const double Size_Input; 234 //隠れ層のニューロン数 235 const double Size_Hidden; 236 //出力層のニューロン数 237 const double Size_Output; 238 //学習率 239 const double learning_rate; 240 241 SoftmaxWithLoss lastLayer; 242 243 public: 244 Net() = delete; 245 246 //初期化 247 /* 248 引数  249 1:入力層の入力数 250 2:隠れ層のニューロン数 251 3:出力層のニューロン数 252 4:重みを小さな値にして過学習を抑える 253 254 */ 255 Net(int input_size, int hidden_size, int output_size, double weight_init_std = 0.01) 256 :Size_Input(input_size), Size_Hidden(hidden_size), Size_Output(output_size), learning_rate(weight_init_std) { 257 258 259 //matrixの作成 & 乱数をいれる 260 auto F = [=](double One, double Two)->matrix<double> { 261 262 std::vector<std::vector<double>> Vec(One, std::vector<double>(Two)); 263 264 for (auto& vec : Vec) { 265 for (auto& v : vec) { 266 v = ((double)rand() / RAND_MAX); 267 } 268 } 269 270 return matrix<double>(Vec); 271 }; 272 273 //1層目 274 275 //乱数の代入 入力層×中間層の数 276 params["W1"] = F(Size_Input, Size_Hidden) * learning_rate; 277 //B1 「0」クリア :バッチサイズ×中間層の数 278 params["B1"] = matrix<double>(std::vector<std::vector<double>>(1, std::vector<double>(hidden_size, 0))); 279 280 //2層目 281 282 //乱数の代入 中間層の数×出力層 283 params["W2"] = F(Size_Hidden, Size_Output) * learning_rate; 284 285 //「0」クリア :バッチサイズ×出力数 286 params["B2"] = matrix<double>(std::vector<std::vector<double>>(1, std::vector<double>(output_size, 0))); 287 288 //レイヤの生成 289 layers.push_back(new Affine(&params["W1"], &params["B1"])); 290 layers.push_back(new ReLU()); 291 layers.push_back(new Affine(&params["W2"], &params["B2"])); 292 293 } 294 295 ~Net() { 296 //delete Layers; 297 298 for (auto& vec : layers) { 299 delete vec; 300 } 301 } 302 303 matrix<double> predict(const matrix<double>& x)const { 304 auto X = x; 305 306 for (const auto& vec : layers) { 307 X = vec->forward(X); 308 } 309 return X; 310 } 311 312 double loss(const matrix<double>& x, const matrix<double>& t) { 313 auto y = predict(x); 314 315 return lastLayer.forward(y, t); 316 } 317 318 auto test(const matrix<double>& x, const matrix<double>& y) { 319 return predict(x); 320 } 321 322 //誤差逆伝播法 323 std::unordered_map<std::string, matrix<double>> gradient(const matrix<double>& x, const matrix<double>& t) { 324 325 326 //forward 327 loss(x, t); 328 329 330 //backward 331 double dout = 1.0; 332 matrix<double> mdout = lastLayer.backward(dout); 333 334 //ひっくり返す 335 std::reverse(layers.begin(), layers.end()); 336 337 for (const auto& vec : layers) { 338 mdout = vec->backward(mdout); 339 } 340 341 //ひっくり返す 元に戻す 342 std::reverse(layers.begin(), layers.end()); 343 344 345 std::unordered_map<std::string, matrix<double>> grads; 346 347 348 grads["W1"] = layers[0]->dw; 349 grads["B1"] = layers[0]->db; 350 grads["W2"] = layers[2]->dw; 351 grads["B2"] = layers[2]->db; 352 353 return grads; 354 355 } 356 357 }; 358} 359 360 361int main() { 362//誤差逆伝播法 TwoLayers 363{ 364 365 constexpr size_t iters_num = 1000000; //繰り返し回数 366 constexpr int batch_size = 1; 367 double input_size = data[0].size(), hidden_size = 3, output_size = label[0].size(); 368 constexpr double learning_rate = 0.001; //学習率 369 370 371 //ネットワーク 372 ErrorBack::Net network(input_size, hidden_size, output_size); 373 374 for (int i = 0; i < iters_num; i++) { 375 //ミニバッチの取得 376 std::vector<int> rund_batch_mask; //乱数の数 377 //乱数の取得 378 for (int j = 0; j < batch_size; j++) { 379 rund_batch_mask.push_back(i); 380 } 381 382 383 384 385 //学習データの取得 386 matrix<double> x_batch(batch_size, input_size, 0); 387 //正解データの保存 388 matrix<double> t_batch(batch_size, output_size, 0); 389 390 //バッチの取得 391 for (int j = 0; j < batch_size; j++) { 392 x_batch[j] = (data[rund_batch_mask[j]]); 393 t_batch[j] = (label[rund_batch_mask[j]]); 394 } 395 396 397 398 //学習 399 auto grad_backprop = network.gradient(x_batch, t_batch); 400 401 std::vector<std::string> Key = { "W1", "B1", "W2", "B2" }; 402 //更新 403 for (auto key : Key) { 404 405 network.params[key] -= (grad_backprop[key] * learning_rate); 406 407 } 408 409 const double LOSS = network.loss(x_batch, t_batch); 410 log.write(LOSS); 411 412 //1回目のLossで(更新後)エラーでる 413 if ((i + 1) % 100 == 0 || i == 0) { 414 415 416 std::cout << i + 1 << " / " << iters_num << " : (回/ループ)" << std::endl; 417 std::cout << "損失率 = " <<LOSS << std::endl; 418 std::cout << "検出" << std::endl; 419 420 auto F = Output::Softmax(network.test(x_batch, t_batch)); 421 422 std::cout << "答え↓" << std::endl; 423 t_batch.show(); 424 425 426 double kk = 0.0; 427 for (int j = 0; j < F.Size_nrow(); j++) { 428 for (int k = 0; k < F.Size_ncol(); k++) { 429 if (kk < F[j][k]) { 430 kk = k; 431 } 432 F[j][k] = 0; 433 } 434 } 435 436 F[0][kk] = 1; 437 for (int j = 0; j < F.Size_nrow(); j++) { 438 for (int k = 0; k < F.Size_ncol(); k++) { 439 440 std::cout << F[j][k] << " "; 441 442 } 443 } 444 std::cout << std::endl << "予想↑" << std::endl; 445 446 std::cout << std::endl; 447 448 Line(2); 449 } 450 } 451 } 452}

試したこと

AFFINEクラス、ReLu関数、Softmax-with-Lossクラスの
forward関数、backward関数の出力結果が正しいか確認しましたが
問題はなかった。

補足情報(FW/ツールのバージョンなど)

C++17
VisualStudio2019

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

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

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

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

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

guest

回答1

0

自己解決

見直したら、複数個所でコードミスがありました。
修正したら学習しました。
1.Sigmoid関数でバッチ対応していませんでした
2.AffineクラスでB1・B2の足算でミスがありました(Matrixクラス)
3.クロスエントロピー誤差の戻り値でバッチ数での割算を忘れていました。
4.「0 1 0 0 0 0 0 0 0 0」と表示されるのは、コードミスでした。

ありがとうございます

投稿2020/12/02 11:48

fps1

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問