いつもの作業の備忘録

作業を忘れがちな自分のためのブログ

Javascriptのmapの使い方

超初心者がReactを勉強する

初めてのReact「入門編」導入から基本まで〜TODOアプリを作ってを学ぼう! | 株式会社ウェブ企画パートナーズ の記事を参考に勉強しています。

以下のサンプルの部分でmapメソッドが出てくる。

  render() {
    const { todos } = this.state;
    return (<div>
      <input type="text" onInput={this.onInput} />
      <button onClick={this.addTodo} >登録</button>
      <ul>
        {todos.map((todo, index) => <li key={index}>
          {todo}
          <button onClick={() => { this.removeTodo(index) }}>削除</button>
        </li>)}
      </ul>
    </div>);
  }

mapメソッドは配列に対して使われ、配列の各要素に対してコールバック関数を実行するという動きをする。 今回のコールバック関数は(todo, index)から</div>);までで、引数のtodoindexに応じて<li>タグと<button>タグ を表示していることになる。

forEachでも似たようなことができるが、forEachは返り値を返せない。mapの場合はコールバック関数の 返り値をmapの返り値とすることができるという違いがある。

さらに、コールバック関数の第一引数はtodosの要素が代入されるが、indexはその要素の元配列(todos)におけるインデックスが 自動的に代入される。

Javascriptの分割代入

超初心者がReactを勉強する

初めてのReact「入門編」導入から基本まで〜TODOアプリを作ってを学ぼう! | 株式会社ウェブ企画パートナーズ の記事を参考に勉強しています。

記事の中で出てくる以下のような中かっこで括った定数の宣言方法(todos)を分割代入というらしい。

  constructor(props) {
    super(props);
    this.state = {
      todos: [],
      name: ''
    };
  }
・・・
  render() {
    const { todos } = this.state;
    return (<div>
      <input type="text" onInput={this.onInput} />
      <button onClick={this.addTodo} >登録</button>
      <ul>
        {todos.map((todo, index) => <li key={index}>
          {todo}
          <button onClick={() => { this.removeTodo(index) }}>削除</button>
        </li>)}
      </ul>
    </div>);
  }

この例ではstatetodosnameが存在することがコンストラクタを見ればわかるが、 分割代入によって、this.stateの要素を前から順番に定数に割り当てているらしい。 this.stateの最初の要素はthis.state.todosなので、renderメソッド内のtodos変数には this.state.todosが代入されている。

さらに以下の例では...が使われている。todosは配列なので、これにも分割代入が使われるわけだが、 ...がつくのは残余パターンと呼ばれ、分割したパターンの残余部分を表す。今回はtodos配列の すべてを残余パターンとして表すことで、todosの末尾にnameを追加している

  addTodo = () => {
    const { todos, name } = this.state;
    this.setState({
      todos: [...todos, name]
    });
  }

【Caffe】回帰問題を解く

0.課題

 GoogLeNetを回帰問題に応用する方法を紹介する。その例として、以下の図に示すような円の回帰を考える。中心座標(x,y)、半径d、色b、g、rの6つをランダムに決定して円を描画した224x224ピクセルの画像を入力し、6つの値x、y、d、b、g、rを回帰するモデルを作成する。示している図では、塗りつぶされた円が入力画像の円、その上に描画されている輪郭のみの円が回帰により得られた値を使って描画した円となっている。
f:id:whg_res:20170215000212p:plain
 完璧ではないにしても、ある程度の予測ができている。コード類はGitHubに掲載する。
https://github.com/whg-res/RegressionSample

1.データ作成

 特に限定はないので、OpenCV等を用いてランダムにパラメタx、y、d、b、g、rに従った円を描画する。せっかくなので、GitHubにコードを掲載する。
https://github.com/whg-res/roundGen
 作成されるデータの形式は以下の通り。

C:\DATA\000000.png	159	112	2	11	82	80
C:\DATA\000001.png	41	212	5	8	204	51
C:\DATA\000002.png	178	14	21	23	64	8
C:\DATA\000003.png	15	137	22	223	85	76

 それぞれ左から、画像のパス、x、y、d、b、g、rとする(画像パスは適宜変更されたい)。

2.prototxtの設定

 データの入力にはMemoryDataLayerを使う。MemoryDataLayer自身は3次元のデータと正解値となるラベル(長さ1のfloat値)の組しか表現できないため、複数の値の回帰には利用できない。そこで下記のサイトを参考にして、入力画像のデータレイヤと正解値のデータレイヤを別で用意することとする。それぞれのラベルは使わずに、ダミーデータとして出力しておく。
http://d.hatena.ne.jp/muupan/20141010/1412895321
 具体的には以下の通り、"data"レイヤの出力は画像情報"data"とラベル情報"dummy1"だが、"dummy1"は参照されない。その代り、"label"レイヤの出力である"label"がchannel数6となっており、この値と出力の誤差を計算することで複数の値の回帰を実現している。

layer {
  name: "data"
  type: "MemoryData"
  top: "data"
  top: "dummy1"
  include {
    phase: TRAIN
  }
  memory_data_param{
    batch_size: 5
    channels: 3
    height: 224
    width: 224
  }
}
layer {
  name: "label"
  type: "MemoryData"
  top: "label"
  top: "dummy2"
  include {
    phase: TRAIN
  }
  memory_data_param{
    batch_size: 5
    channels: 6
    height: 1
    width: 1
  }
}

 同様に、出力層の部分では以下の通り、出力の数を6個に設定し、lossレイヤで推定値と"label"の値の誤差を計算している。識別とは異なり実数値の推定なのでEuclideanLossを使っている。

layer {
  name: "loss3/classifier_"
  type: "InnerProduct"
  bottom: "pool5/7x7_s1"
  top: "loss3/classifier_"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 6
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "loss3/loss3"
  type: "EuclideanLoss"
  bottom: "loss3/classifier_"
  bottom: "label"
  top: "loss3/loss3"
  loss_weight: 1
}

3.ソースコードの解説

 学習、識別のソースコードは基本的には過去の記事と同様の構成になっている。
http://punyo-er-met.hateblo.jp/entry/2017/02/11/211650
 ただし、今回異なるのは、データをすべて0~1のfloat値で与える必要があることである。学習データのラベルをそのまま与えると学習誤差が発散してうまく学習できなかった。データのラベルを256で割り算することで今回のデータでは1以下に収まるので、以下の部分で対応している。

		for (int i = 0; i < TARGET_DIM; i++){
			label[n*TARGET_DIM + i] = stof(entry[i + 1]) / 256.0;
		}

 その他注意点として、今回データレイヤで利用しているダミーデータに対しても実体を与えて処理をかけなければならない。

	const auto train_input_layer = boost::dynamic_pointer_cast<MemoryDataLayer<float>>( net->layer_by_name("data") );
	const auto test_input_layer = boost::dynamic_pointer_cast<MemoryDataLayer<float>>( test_net[0]->layer_by_name("data") );
	float *train_dummy = new float[train_data_size];
	float *test_dummy = new float[test_data_size];
	for (int i = 0; i < train_data_size; i++)	train_dummy[i] = 0;[f:id:whg_res:20170215000212p:plain]
	for (int i = 0; i < test_data_size; i++)	test_dummy[i] = 0;
	train_input_layer->Reset((float*)train_input_data, (float*)train_dummy, train_data_size);
	test_input_layer->Reset((float*)test_input_data, (float*)test_dummy, test_data_size);

 また、テストの結果は1つの画像に対する6つの推定値が連続して存在し、一回のForward()でバッチ数分の推定値が得られていることを考慮してデータを取得する必要がある。

4.実行結果

 1000枚のデータを80epochほど学習させた場合の結果が以下のようなものである。
f:id:whg_res:20170215000212p:plain f:id:whg_res:20170216003257p:plain
左の画像では少しずれているが、だいたいの位置、大きさ、色が再現できているのが分かる。右の画像は高精度に推定で来た例である。定量評価はできていないが、ほとんどの画像は左の画像のように少しずれた結果となった。

※参考
http://d.hatena.ne.jp/muupan/20141010/1412895321