Bridge over troubled Techs.

オープンストリーム CTO 寺田英雄の個人的ブログ

メモ:xavier_initializer_conv2d()とは何か?

TensorFlow(DeepLearning)を今日も勉強中。いろんなサンプルコードを解読しているが、まだまだ分からないことだらけである。今日も未知の関数に出くわした。それは、

tf.contrib.layers.xavier_initializer_conv2d() 

というやつである。こういうのを1個1個調べていくと、時間かかるけど大変勉強にはなる。せっかくなので調べた結果をメモしておく。

  • この関数の目的は、重み行列の初期化関数を与えることである。たいていはコンボリューションに使うフィルタ行列変数の初期化(tf.Variable(initializer))で使われる。
  • この関数は初期化の仕方に特徴があり、行列の各要素をランダムに初期化するが、そこに次のような制約を用いる:
    1. 各階層の勾配スケールを全てだいたい同じにする。
    2. オプションで一様分布(uniform)を指定したときは、要素値xの範囲は [-x, x] ただし、x = sqrt(6./ (in+out))である。
    3. オプションで正規分布(normal)を指定したときは、要素値xの範囲は [-x, x] ただし、x=sqrt(3./(in+out))である。
  • 'Xavier' という名前は、この初期化方法の発案者に由来する。原論文はこちら。和風発音だと「ザビエル」だが、たぶん正確には「ゼイヴィア」が近い。

AIはニーズ指向かシーズ指向か?

<FBとクロスポスト

現状のAI活用に関しては、ニーズ指向は結構無理があるのではと思っている。

現場のニーズに合わせてAIを設計しようとしても、苦労のわりに思ったほど高い性能が発揮できない可能性が高い。現状の『現場』は当然ながら人間が仕事をする前提で制度や空間が設計されているからである。

私は、現状のAIの特性を深く理解し、それが上手く行かせる新しい機能=ビジネスを発想するというアプローチを中心に考えたいと思っている。いわゆるシーズ指向である。

たとえばコールセンターの応答をAIに代替させようという話しが良くあるが、そうではなくて、AIを違う角度で膨らませたアーキテクチャーを考えて、コールセンターそのものが不要になるようなビジネススキームを発想する、というイメージである。

仏像の歴史と、システム開発の歴史

(以下の話は、ラジオかなにかで聞きかじった話で、私も歴史学や考古学の専門家ではないので、細部には誤りもあるかと思うので、その点ご注意ください)

 

日本の仏像製作は、仏教伝来した6世紀の飛鳥時代から始まり、鎌倉時代運慶/快慶で有名な慶派が登場したときに、完成度や芸術性などがピークに達したとされている。

写真:東大寺の阿形像(運慶・快慶作)

 

この時代の仏師は、一人で仏像の全身を彫り上げることができる力量をもっており、深い精神性や芸術性を感じさせる仏像を製作することができた。仏師は今で言う芸術家=アーティストに近い存在だったのだ。

 

その後、室町時代以降は、いろいろな理由から仏像製作のレベルは低下していき、徐々に仏師も『職業仏師』化していった。すなわち、仏像製作の分業化が進み、頭部だけ作る仏師やら、胴体だけを作る仏師やらに別れ、仏像の全身を彫り上げることのできる仏師は激減したらしい。その後現代にいたるまで、作品のレベルは鎌倉時代を超えることはできなくなった。

 

・・・この話を聞いて思ったのは、『これってソフト開発・システム開発の歴史に似ているな』ということである。

 

ITという言葉も産業としても存在しなかった初期の時代、コンピュータに取り組んでソフトを作るとなれば、一人で何でもやるのが普通だった。情報も少なかったが、ハードもソフトも何でも知り尽くしてやろう、という情熱のある人ばかりだった。そこに未来と可能性を感じた一部の人が、熱狂的に取組むのがコンピュータ・ソフト開発だったのだ。

 

現在のソフト開発は大規模化・複雑化していて、細かく分業して実施するのが普通になった。初期のころほどの熱気や、全体を深く見通してやろう、という人は相対的に少なくなった。これはコンピュータに限らず、どんな分野でも産業化にともなって必ず起きることで(例:ロック)、しょうがないのだけど、ちょっと寂しさを感じるのも事実。

 

・・・だからどうだというわけではないのですが、仏像の話でふと浮かんだので書き留めてみました。

読書メモ:『考える脳 考えるコンピュータ』

Numenta の HTM理論に興味をもったので読んでみた。結果的には非常に楽しく読めた。

考える脳 考えるコンピューター

考える脳 考えるコンピューター

 
  • 著者ジェフ・ホーキンスさんは超一流のエンジニア&起業家。一旦いくつかのビジネスを成功させてから、脳を模範にした汎用人工知能を開発するため大学に入り直して神経科学を学んでいる。この気合の入り方が凄い。
  • 2005年の出版だと考えると、その後今日に至るAI発展の萌芽になる話がいろいろ含まれているのが興味深い。Deep Learning(という言葉はもちろん使われていないが)の原型である脳の視覚野の話なども概説されている。
  • 彼のアイデアの根幹は、大脳新皮質の柱状構造をモデルにした人工知能。実はこの本全体がこの人工知能モデルの基本設計書みたいな雰囲気がある。執筆当時の最先端の神経科学の知見を援用しつつも、エンジニアらしい割り切りで『仕様』を決めている。現在 Numentaが取り組んでいるHTMもこの延長線上にある。
  • 近年急速に研究が進んだとはいえ、脳の内部構造は超絶に複雑で、まだまだ謎だらけである。しかし彼は『一見複雑に見えても、なんらかのシンプルな基本原理によって駆動されているはず』という信念にもとづき、いくつか仮説を導入しつつも設計をまとめている。
  • よく知られているように、大脳新皮質はいくつかの機能別の領野にわかれているが、実際に観察すると神経細胞の構造はどこも同じに見える。したがって脳の情報処理は、機能別に別のアルゴリズムを使うのではなく、統一された一つのアルゴリズムで動いているというのが彼の主張。
  • いくつかの神経科学の知見をもちいて、大脳新皮質は記憶にもとづく予測器の役割を果たしているとしている。この部分はいま自分が一番問題意識を持っているテーマに繋がるので興味深く読んだ。
  • 脳神経の活動のスパース性についても言及がある。
  • 『意識』がなぜ生じるのかにつても、大胆な?仮説を展開している。私はかなり説得力のある面白い説明だと思った。

 

今後は、NumentaのHTM(NuPIC)の具体的な中身を調べてみよう。

 

ディープラーニング ResNet のヒミツ

 先日、当社と共同研究をしている庄野研のゼミに参加させてもらった。その日は論文の輪講の日だった。そこでM2のSさんがレクチャーしてくれた Deep Residual Learning の話が面白かったので、以下メモとして記してみる。

#なお、このメモはDLについての基本的な仕組みは知っている人を前提に書いている。

 ResNetとは?

 もの凄い勢いで活発に研究されている Deep Learning機械学習であるが、昨年 ILSVRC'2015 という学会のコンペで、一般物体認識で最高性能を叩き出した ResNet (Deep Residual Net)という学習・識別器がある。当時 Microsoft Research にいた Kaiming He 氏が開発した、152層!のニューラルネットである。その論文はこちら

 多層ネットワークの勾配消失/発散問題

 昨今のDL研究で分かってきたDLの性質の一つとして、"Wide より Deep"というものがある(追記:さらに最近では、Wideも重要という論文も出てきている)。同じニューロン数で学習するなら、ネットワークの各層の幅(ニューロン数)をワイドにするよりも、階層の深さをよりディープにしたほうが性能があがるという性質である。

#なお、これは経験則に近いものらしく、理論的になぜそうなのかというのは、あまり良く分かってはいないらしい。 

 ならば、『どんどん階層を増やせば良いじゃない』か、と誰でも考えるが、問題はそれほど単純ではない。従来の方式で単純に階層を増やしすぎると勾配消失(vanishing)勾配発散(exploding)という問題が発生してしまって、うまく学習できなくなるのである。なお、この勾配の消失や発散をまとめて degradation と呼ぶらしい。de-gradationというニュアンスであろうか。 

勾配消失が起きる理由

 DLで学習する際には各層ごとに活性化関数を微分して勾配を求める。まず入力層に近い『浅い』層ではその層への入力と出力の差(accuracy)が大きいため、微分操作は有効に働く。しかし、学習が層の『深い』段階へと進むにつれ、(当たり前だが)学習が収束に近づくと入力から出力への変換精度がどんどん上がっていくため、入力と出力の差は極めて小さくなり、勾配を取りにくくなる。層から層への伝播は掛け算の性質をもっているため、この差の減少傾向は指数関数的になる。つまり、層を増やしていくと、あっという間に勾配が消えてなくなってしまうのである。

勾配発散が起きる理由

 DLでは、ある層のニューロンは次の層のニューロンと重みWで積和をとって結合している。この重みが1以上ならば、多層になると『重み✕重み✕重み・・・』が発散する可能性があることがわかる。

Residual Learning(残差学習)

 普通のDLの各階層では、入力信号 x をそのままネットワークに入力して出力 H(x) を計算し学習する。

 ResNetでは、出力から入力を引いた残差(Residual) F(x)=H(x)-x を学習する形にする。なぜそんなことをするのか?・・・前述の勾配消失の説明を思い出してほしい。DLでは深い層にいくほど、入力と出力の差が小さくなる、つまりH(x)はxに極めて近い値になる。しかし H(x) は有限なNNによる非線形写像であり、綺麗にxに近づけることは難しい場合が多い。むしろ、xに極めて近い出力が欲しいのだから、単純にxをそのまま出力し、それと H(x) の差 F(x) をNNで表現して写像すれば良いのではないか、という理屈が成り立ちそうだからである。(これがこの論文で実証したい中心仮説である。)

 これを実装するには、F(x)を直接計算しようとするよりも、下図のようなショートカット接続をネットワークに導入すれば良い。これにより weight layer(NN階層)はF(x)を学習する形になる。

f:id:terada-h:20161213165701p:plain

図1 残差学習の基本ユニット(これを何層も重ねる)

f:id:terada-h:20161213190641p:plain

図2 ResNetの実装例(34層)。左:従来の方式、右:ResNet方式

 ResNetの結果

 論文にある実験結果によれば、ResNetは素晴らしい効果を産んでおり、従来方式だと階層を増やしていくと、むしろ精度(エラー率)が悪化していたが、ResNet だとそのようなことはなく、層を増やせば増やすほどエラー率が低下する傾向となっている。

 たとえば、18層と34層を比較した結果が図3である。従来方式だと34層のほうが18層よりエラーが増えてしまっている。ResNetでは34層の方が良い結果となっている。

f:id:terada-h:20161213191516p:plain

図3 学習エラーの比較。左:従来方式、右:ResNet

さらに多階層もOK

 論文によれば、筆者らはさらに実験を続け、1202層!のResNetでも同様の傾向、つまり、degradation に邪魔されることなく学習が可能で、さらにエラー率が低下することを確認しているそうである(訓練誤差0.1%、テスト誤差7.93%)。

 この手法の登場により、理屈の上では飛躍的にDLの階層を増やせることになった。計算量は膨大になっていくが、計算機にお金を掛けられる人達であれば、どんどん階層を増やして精度を上げられるのである。すでに画像のパターン分類のようなタスクであれば、人間を超える認識精度に達していると言われるDLであるが、さらなる精度向上が何をもたらすのか興味深い所である。

昔ながらのプログラム設計論(スマホアプリ系)

0.いきさつ

先日、社内で新人に説明するために、基本的なプログラムのモジュール設計法について大急ぎでメモにまとめた。もしかしたら他にも誰かの役に立つかもしれない。せっかくなのでブログのネタにしてしまおう、ということでブログに掲載してみることにした。

ここに書いたことは全然目新しくはなくて、昔からある考え方であるが、現在でも有効なものである。非常に基礎的な考え方なので、オブジェクト指向設計でも、構造化設計でも、(場合によっては関数型でも?、)有効な考え方である。プログラム設計を分割統治法で考えようとするとき、この考え方をマスターしておくと、品質の高いプログラムを実装するのがたやすくなるはずである。

意外に、最近はこうしたことをハッキリ説明してくれている文献があまり見当たらない。特に新人が最初に参考にするような書籍やネット記事などでも触れられていることが少ないのだが、何故だろう? 非常に大事なプログラマの『感覚』なのではないかと思うのだが。

 

1. プログラムのモジュール(クラス・メソッド)分割方針について

プログラムを設計していく場合、クラスなりメソッドなり、何かのカタマリ(=ここではモジュールと呼びます)に分割していくわけですが、そこには経験的に上手くいく指針があります。それらを以下に説明します。

1.1. 常に『このモジュール(クラス・メソッド)の責務(Responsibility)は何か?』を明確にして分割する

  • 『責務』という日本語だと分かりにくいですが、Responsibilityという英語だと、プログラマの直感に近いニュアンスです。
  • Responsibilityは、'Response + ablility' という構成の語であり、『(ある要求に)反応・応答する能力を持っている』という意味です。
  • クラスやメソッドを設計するときのプログラマの意識は、Responsibility=『外部からの要求を受けて、決められた仕事を実施して結果を返すユニット』にあります。
  • このとき、そのモジュールの責務=『どの範囲の要求に応答し、どこまで仕事をさせることにするか』を明確に決めておくことが大事です。
  • 責務を曖昧にしていると、どっちつかずで位置づけが分かりにくく、デバッグがしにくかったり、将来の機能拡張や引継ぎのときに混乱を生じる原因になったりします。

1.2. 一つのモジュールには、明確にシンプルに説明できる一つの責務だけを持たせる

  • 一つのクラスや一つのメソッドにたくさんの機能を盛り込むと、ややこしくなってバグの元になります。
  • 例えば、『なにかを計算して、その結果を表示する、エラーならエラー表示を行う』という機能を一つのメソッドに詰め込むのは、原則として良くない設計です。
    • 以下の4つの単機能の下位メソッドを作り、それらをまとめて制御する上位モジュールを作るのが基本です。
      • 計算する
      • 計算結果を判定する
      • 結果を表示する
      • エラーを表示する

1.3. モジュール間の上下関係・主従関係を明確にする

  • 上位(主位)モジュールは:
    • いろいろな下位モジュールを呼び出します。
    • 全体の状況(状態)を把握しています。
    • 状態を持ち、その更新に責任を持ちます。
    • スレッドやタイマーを管理し、システムのさまざまな処理を起動するタイミングを統括します。
    • メモリやDB,リソースの初期化・確保・削除・解放に責任を持ちます。
    • 別名:Activeモジュールといいます。
  • 下位(従位)モジュールは:
    • 上位モジュールから呼び出されたら、単純な一つの責務を実行します。
    • いつ呼び出されるかは知りません。
    • なぜ呼び出されるかも知りません。
    • 上位モジュールの存在は知りません。
    • 他の下位モジュールの存在も知りません。
    • リソースの確保や解放はしません。
    • 『副作用』や『状態を持つ・状態を更新する』のはナシにするのが理想です。副作用があったとしても最小限かつ明確なものにします。
    • 別名:Passiveモジュールと呼びます

f:id:terada-h:20161026182230p:plain

図:上位モジュールは、下位モジュール呼び出す

 

f:id:terada-h:20161026182328p:plain


図:下位モジュールは、上位モジュールを呼び出さない。下位モジュール同士の直接のやりとりは、原則やらない。

 

f:id:terada-h:20161026182416p:plain

図:下位モジュールは単純・単機能な責務にする。上位モジュールは状態を管理し、全体を統括する。

2. オブザーバ(Delegate,Listener)パターンについて

オブザーバパターンでは、プログラムコード上は、下位が上位を呼び出す形になる場合がある。ただし、呼び出し先のオブザーバ(delegate, listener)はプロトコル(Swift)やインターフェイス(Java)として抽象化されており、具体的な特定のクラスを差していないので、上記ような意味で上位クラスを呼び出すのとは異なっている。

3. iOSアプリでの上下関係の例

  • 最上位モジュール:AppDelegate
  • 上位モジュール:各画面の ViewController
    • VC内のメソッドの上下関係
      • 上位メソッド
        • 状態を把握・状態遷移を行う
        • クラスのプロパティを変更しても良い。
        • 例:
          • viewDidLoad()
          • viewWillAppear()
          • viewDidAppear()
          • viewWillDisappear()
          • UIボタンやセンサーのイベントハンドラ
            • ただし、ハンドラに長々と制御コードを書くのは好ましくない。制御を集約した別クラスを作って、それを呼び出す形にする、など。
          • ・・・
        • ユニットテストしにくい
      • 下位メソッド
        • 状態を持たない(状態値を変更しない、参照しない)
        • クラスのプロパティを変更しない。
        • 入力=引数のみに依存し、結果は全て return 値だけに反映する。
        • 例:
          • 自分で定義する各種処理ルーチン
        • ユニットテストがやりやすい

4. どこで深刻なバグが起きやすいか

  • 上記説明における、下位モジュールでは、あまり深刻なバグは生まれません。
  • ほとんどの深刻なバグは上位モジュールの状態管理や条件判定、あるいはスレッドやタイマーの制御、メモリやリソースの管理に起因します。
  • したがって、デバッグしやすくするには以下の原則を守ることが大事です。
    • スレッドやタイマーの制御方針を明確に、一貫したものに。
    • 状態の管理や状態遷移の方法を明確に、一貫したものに。できるだけ一箇所・ワンパターンに集約する。
    • 安易にフラグ制御を増やさない。
    • メモリ・リソースの確保・解放は同一レイヤー(階層)に揃える。

以上