FC2ブログ
スポンサーサイト
カテゴリ: スポンサー広告
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
編集 / --.--.-- / コメント: - / トラックバック: - / PageTop↑
活動報告 No.150 完全畳み込みニューラルネットワーク(仮)をC言語で実装する際にやってしもたミスとその他 for ROBO-ONE auto
カテゴリ: 未分類

どうも、おはこんばんにちは。キャンパーレベル24の2年マエダです。
ポケ森と全然関係ないんですが、先日の東京ビッグサイトで開催された国際ロボット展に行きました。私は初日に参加しましたが平日というだけあって99%はビジネスマンだったので、学生に対してビラ配り的なやつは大抵無視でしたorz。
ま、しゃーない。

んでんで、私が更新するブログは1ヶ月ぶりですかね。ここ数週間はヒュー研内部で来年度に向けての云々やらタイトル通りのことをやっておりました。
完全畳み込みニューラルネットワークでよくやってしもたミスを上げて行くので、全結合層については何もありません。畳み込み層と最大プーリング層についてだけです。
あと、今回も機体は登場しません、ごめんなさい。次の日曜日にロボジェネレーションという大会があるので、来週か再来週はそれに関連してちゃんとロボットの画像載せます。
ロボジェネレーションのリンク:
http://denken.ws.hosei.ac.jp/robot_generation/




ちょっと前置き...

私はPythonを使ってきた身なので、ニューラルネットワークなんて大規模なCプログラムを書いてきたことはありません。2ヶ月ほど前に物体検出の勉強を論文や解説サイト等でやり始め、実際にCプログラムで構築し始めたのはほんの数週間前です。

Cを本格的に使い始めてちょっと経ちますが、Pythonより文法というか面倒なお約束事が多いのでサクサク記述できない所を不満に思います。しかし計算問題に関してはPythonより覚えること少なくて、「こんな記述初めて見た!」なところが少ないと思いました。Pythonはライブラリ関連が超強力ですが、それらがブラックボックスと化しているのでこう思うのも必然ですかね。





ミス1:mallocの初期値


mallocは配列に使用されるメモリの量をこちらで指定して確保する際に使います。たとえば、


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

int main(void)
{
int num = 10;
double *array = (double *)malloc(sizeof(double) * 10);

for (int i = 0; i < num; i++) {
printf("%f ", array[i]);
}

return 0;
}

てな感じでstdlib.hを宣言してからmallocを使うと上の例では要素数10の配列を確保できます。
私はトライ&エラーな人間なので、書き方だけどこかのサイトで見たらあとは自習していくスタイルなので、とりあえず実行してみました。結果は以下です。


no150-1.png 

!?!?


全部 0 でした。当時の私はこう思いました。

「あ、mallocって宣言したら全部 0 で初期化されるんだ、便利だなぁ〜」
...実際はそんなことありません。上のように解釈して数週間もこの違う事実に気づかなかったせいで地獄のコアダンプ不回避事件が起きました。


複数回コンパイル&実行を繰り返すとわかるのですが、


no150-2.png no150-3.png 

このように不特定な数が出ることもあります。ニューラルネットには数千万、ときには億単位もの要素が必要とされるので、、、


Layer *l;
l = (Layer *)malloc(sizeof(Layer) * LAYER_NUM);
for (i = 0; i < LAYER_NUM; i++) {
l[i].input_data = (double *)malloc(sizeof(double)
* CFG[i][0] * CFG[i][0] * CFG[i][1]);
l[i].filter = (double *)malloc(sizeof(double)
* CFG[i][2] * CFG[i][2] * CFG[i][1] * CFG[i][4]);
l[i].bias = (double *)malloc(sizeof(double)
* CFG[i][1] * CFG[i][4]);
l[i].output_data = (double *)malloc(sizeof(double)
* CFG[i][3] * CFG[i][3] * CFG[i][4]);
}

※ 横幅が入るように変なところで改行しています。


もうこれLAYER_NUMが156層だったりしたらえらいことですよ。まあ数層でもえらいことになりますが。。



というわけで...

mallocも通常配列同様に初期化しよう。





ミス2:プーリング層で逆伝播用配列の要素数を間違えた


ネットワークを構成する際、以下の様に層番号と層における処理の種類、入出力サイズ、フィルタサイズの情報を配列に格納し、それをもとに順伝播と逆伝播をfor文で回しています。

あと、出力テンソルのサイズがアレなのは物体検出アルゴリズムのYOLOを参考にしているからです。


// { 0 , 1 , 2 , 3 ,
// { number, type, input_size, input_channel,
// 4 , 5 , 6 }
// filter_size, output_size, output_channel }
// type : 0 = convolition, 1 = max_pooling
int LAYER_INFO[LAYER_NUM][7] = {
    { 0, 0, 416, 3, 3, 416, 32 },

    { 1, 1, 416, 32, 0, 208, 32 },

    { 2, 0, 208, 32, 3, 208, 64 },

    { 3, 1, 208, 64, 0, 104, 64 },

    { 4, 0, 104, 64, 3, 104, 128 },
    { 5, 0, 104, 128, 1, 104, 64 },
    { 6, 0, 104, 64, 3, 104, 128 },

    { 7, 1, 104, 128, 0, 52, 128 },

    { 8, 0, 52, 128, 3, 52, 256 },
    { 9, 0, 52, 256, 1, 52, 128 },
    { 10, 0, 52, 128, 3, 52, 256 },

    { 11, 1, 52, 256, 0, 26, 256 },

    { 12, 0, 26, 256, 3, 26, 512 },
    { 13, 0, 26, 512, 1, 26, 256 },
    { 14, 0, 26, 256, 3, 26, 512 },
    { 15, 0, 26, 512, 1, 26, 256 },
    { 16, 0, 26, 256, 3, 26, 512 },

    { 17, 1, 26, 512, 0, 13, 512 },

    { 18, 0, 13, 512, 3, 13, 1024 },
    { 19, 0, 13, 1024, 3, 13, 512 },
    { 20, 0, 13, 512, 3, 13, 1024 },
    { 21, 0, 13, 1024, 3, 13, 512 },
    { 22, 0, 13, 512, 3, 13, 1024 },
    { 23, 0, 13, 1024, 3, 13, 1024 },
    { 24, 0, 13, 1024, 3, 13, 1024 },
    { 25, 0, 13, 1024, 3, 13, (BBOX * 5 + CLASS) }
};


このとき逆伝播用の配列の要素数は LAYER_INFO[][5] * LAYER_INFO[][5] * LAYER_INFO[][6] で決定されます。畳み込み層は。。


私は畳み込み層しか見ていなかったのでプーリング層にも上の法則を適用してしまいました。そのため、上位層に伝播できる誤差が本来の4分の1となってしまい、うまく学習できませんでした。

というわけで...

プーリングを挟んだ畳み込み付近で間違いを犯しやすいので、怪しい挙動をみせたら誤差をちゃんと上位層に伝播できているのか、配列要素数のレベルから見直そう。




ミス(というかオススメ)3:ノート、大切


ライブラリや小規模プログラムしかしていなかった人間なので、その場で考えながらポチポチとコードを組んでいると多チャンネル対応の畳み込みでfor文回す際や順伝播・逆伝播の変数と関数の引数との関係が意味わからなくなります。

IMG_0245 (1) 

上は左側が下位層、右側が上位層で逆伝播をしています。薄く囲ってある範囲が各層のパラメータの塊です。下位層からの誤差データと上位層からの順伝播データを用いてフィルタの勾配を求め、誤差データと転置フィルタを用いて上位層へ伝播する誤差データを生成します。


実際の問題は上の画像のように単純ではなくて、畳み込み・活性化関数・バッチ正規化・バイアス加算を経ているので、逆伝播用の関数内はえらい関数の羅列になり、各関数の引数を考えると、とても頭の中だけでどうこう出来るお話にはなりません(たぶん)。






なんか題名の割に内容薄くてスミマセン。。あと1つか2つくらい書こうかなと思いましたが、早く家に帰りたいのでこれまでにします。
冒頭でも触れた通り、明日はロボジェネがあるので来週か再来週にはその記事を書こうと思います。


昨年まではヒュー研の部内戦的な何かでしたが、今年は元部長がニソコンで声掛けした結果なのか千葉工さんから4台ほど出場すると聞きました。盛んになるといいっすなぁ。


ロボジェネ終わった後ですが、最近CUDAプログラミングに手を出したので、今後もその点などコアなお話が続くと思います。
なんかこう、技術的なお話よりも雑談の方が得意なので、ヒュー研ブログではその雑談が多くなっちゃうと思います。技術的なお話しは私の個人ブログの方で勘弁してください。「シミズさんのたわごと」でググれば多分出てきます。ヤホーはわかりません。

ほんじゃまったのー(=゚ω゚)ノ



スポンサーサイト
編集 / 2017.12.16 / コメント: 0 / トラックバック: 0 / PageTop↑
コメント
 
Title
 
 
 
 
 
 
Secret 


Pagetop↑
トラックバック
Pagetop↑
プロフィール

ヒュー研の中の人

Author:ヒュー研の人
このブログは東京電機大学理工学部ヒューマノイド研究部の公式ブログです。2012年から部に昇格しました!
その日の活動や大会の記録をできるだけ更新していきたいです!!

☆だいたい金曜日前後に更新します☆

FC2カウンター
カレンダー
10 | 2018/11 | 12
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 -
リンク
ブロとも申請フォーム
携帯でみるには↓
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。