雑記

Qiita に投稿しましたが、後半文字だらけで見づらいのと、肝心なHTMLソース部分の説明がないので後日絵や解説を入れるなどリファクタリングできたらいいなあと思います。qiita.com
後半文字だらけで見づらいですが、この分野を知らない人にもわかるようにどのような仮定を置いているかをきちんと書こうとしています。それで肝心の二項ツリーの説明までたどり着けていないんですが、二項ツリーで置いている仮定は既に説明している「無裁定条件」「無リスク金利で貸し借りができる相手の存在」に加えて、

  • 時間  \Delta t だけ先に株価は今より  u 倍に上がるか  d 倍に下がるかの2択とする(から二項ツリーになるのですが)。
  •  ud =1 とする。
  •  \Delta t の1次以上の項は無視できるとする。

辺りになると思います。そうなると株価のボラティリティ  \sigma と矛盾しないように  u d が決まります。このとき二項ツリーによるオプション価格の求め方は以下の手順になります。

  • 満期日までの時間をじゅうぶん細かい  \Delta t ずつに区切る。
  • ツリーの根元のノードに現在の株価  S_0 を記入し、 u 倍や  d 倍しながら末端のノードまで株価を埋める。
  • 次に、株価の下にオプション価格を書いていく。ツリーの末端のノードについては容易に埋まる(例えばコール・オプションであれば行使価格  K として  \max (S_T-K, 0) を書き入れるだけ)。
  • 末端ではないノードのオプション価格については以下の手順で書き入れる。
    • 2つの子ノードを見て「どちらの未来になろうとも同じ価格になる、株とオプションからなるポートフォリオ」を構成する。
    • そのポートフォリオは無リスクなので、親ノードでのそのポートフォリオの価格は無裁定条件より決定する。
    • 親ノードの株価は既に埋まっているので、親ノードでのオプション価格を得る。

この要領で一番根元まで戻ってこれれば現在時点でのオプション価格が求まります。気分転換で数理ファイナンスの記事を書いてみましたが、次以降は統計や機械学習の記事を書きたいです。


それで、どのような仮定を置いているかをきちんとしたいと書いたのですが、少し前に twitter で話題になっていた(少し前というより定期的に話題になっているようですが)以下の確率の問題は仮定が足りない例です(文章は意訳です)。
Aさんには子どもが2人います。Aさんに「子どもたちに女の子はいますか?」と訊いたところAさんは「はい」と答えました。もう1人が女の子である確率はいくらでしょう。
「情報が足りない」「解釈による」といえばそれまでですが、どのように仮定を補うとどのように解けるのかを考えてみるのも面白いと思います。以下の1番下の図は答えが 1/3 となる考え方の1つの例です。ここで置いている仮定は、

  • Aさんに質問する前の子どもの性別の組合せの事前分布を P(男男)=P(男女)=P(女男)=P(女女)=0.25 とする。
  • 「もう1人」とは、女の子が少なくとも1人いると判明した後に、女の子を1人選び、残ったもう1人のこととする。

の2つになります。この問題でよく「こう解釈できるのでは?」争点になっているのは後者ですが、自分がどう解釈したかによって「このように仮定する」とすれば大丈夫です。それより前者について、「男女が生まれる確率が同じとする」のように表現しようとする回答が見受けられますが、その表現は以下の可能性を否定しないので同値ではないです。「独立な」とまでいうと1つ目の可能性のみ排除できますが、2~4つ目の可能性は残るのでやはり不十分です。

  • 第1子と第2子の性別に相関がある可能性。
  • 子どもが男でも女でもない性別である可能性。
  • 子どもの性別が出生後に変化する可能性。
  • Aさんに質問する前からAさんの子どもの性別の情報を(部分的に)知っている可能性。

この問題を「1/2 と 1/3 の2通りの解釈がある」と結論付ける回答もあるのですが、2通りどころか、仮定によって 0~1 のどの値にもなります。それどころか、Aさんの2人の子どもの性別の組合せの確率分布について事前に本当に全く何もわからないという立場を取るならば、1人が女の子だとわかったところでもう1人については「やはり何もわからない」という回答もあると思います。だから何なのかというと、「男女の出生率は等しく第n子にかかわらず独立」というのは、数理ファイナンスにおける「裁定機会は存在しない」「無リスク金利で貸し借りができる」などと似ていて、それなりには現実に近く、いつの間にかそれが当然のように受け入れがちな仮定なのですが(?)、厳密には成り立たないので、いつ仮定が利用されたかを把握していないと危ういんじゃないかな(まあ男女の出生率やその相関を厳密に考える場面などたぶん訪れないんですけど)という回りくどい話でした。

f:id:cookie-box:20171002221943p:plain

Keras で特定の軸方向に softmax したい

Keras で特定の軸方向に softmax したいときとかあると思います。

f:id:cookie-box:20170815221557p:plain:w720

Keras のドキュメントによるとどの軸に沿って正規化するかを指定する axis というのを渡せるようです。
Activations - Keras Documentation
ただ、渡し方がよくわかりません。以下の箇所に axis=2 と書いてもエラーになります。

from keras.models import Sequential
from keras.layers import Dense, Reshape, Activation

if __name__ == '__main__':
  model = Sequential()
  model.add(Dense(3*4*5, activation='relu', input_shape=(10,)))
  model.add(Reshape((3, 4, 5)))
  model.add(Activation('softmax', axis=2)) # ERROR
  model.compile(loss='mean_squared_error', optimizer='rmsprop')
  print(model.summary())

しょうがないので softmax をするだけの自作レイヤーをつくります。これはエラーになりません。

from keras.engine.topology import Layer
from keras.models import Sequential
from keras.layers import Dense, Reshape
from keras.activations import softmax

# いまやりたい softmax をするためだけのレイヤー
class MySoftmax(Layer):
  def __init__(self, **kwargs):
    super(MySoftmax, self).__init__(**kwargs)
  def call(self, x):
    return(softmax(x, axis=2))

if __name__ == '__main__':
  model = Sequential()
  model.add(Dense(3*4*5, activation='relu', input_shape=(10,)))
  model.add(Reshape((3, 4, 5)))
  model.add(MySoftmax())
  model.compile(loss='mean_squared_error', optimizer='rmsprop')
  print(model.summary())

本当に希望の軸方向に正規化されているか確認してみます。何でもよいので適当な入力を流し込めばよいです。

  x, y = [], []
  # 1だらけの適当なデータ
  for i in range(20):
    x.append(numpy.ones((10)))
    y.append(numpy.ones((3,4,5)))
  x = numpy.array(x)
  y = numpy.array(y)
  
  model.fit(x, y)
  y_test = model.predict(x)
  print(y_test[0])
[[[ 0.21292862  0.24584062  0.2997719   0.15851443  0.25      ]
  [ 0.30612475  0.24584062  0.22358862  0.32254457  0.25      ]
  [ 0.16930516  0.24584062  0.22358862  0.25293246  0.25      ]
  [ 0.31164148  0.26247811  0.25305092  0.26600859  0.25      ]]

 [[ 0.18996513  0.24999966  0.18364088  0.25715023  0.3020851 ]
  [ 0.23858465  0.25000107  0.27690381  0.21845251  0.27901751]
  [ 0.25863758  0.24999966  0.35581443  0.30594474  0.19078693]
  [ 0.31281266  0.24999966  0.18364088  0.21845251  0.22811046]]

 [[ 0.21095483  0.36942184  0.22160934  0.2253582   0.24394487]
  [ 0.26182058  0.22892946  0.22160934  0.32392535  0.24394487]
  [ 0.31626979  0.159567    0.335172    0.2253582   0.24394487]
  [ 0.21095483  0.24208161  0.22160934  0.2253582   0.26816541]]]

出力の shape が (3, 4, 5) で、axis=2 としたので、5つの要素の和を取る方向がこれで正規化されて…いません。
和が1になったのは4つの要素の和を取る方向でした。
axis=1, 2, 3 についてどの軸の方向が正規化されるのか調べると以下でした。5つの要素の和を取る方向を正規化したかったら axis=3 とすべきようです。

f:id:cookie-box:20170815225335p:plain:w750

axis=0, -1 のときは以下でした。axis=0 は 4x5 の面内の和が1になりました。axis=-1 はむしろ当初やりたかった softmax が達成されていました。-1 はデフォルトのはずで、じゃあわざわざカスタムレイヤつくる必要なかったのでしょうか。
f:id:cookie-box:20170815230442p:plain:w500

これでやりたい softmax ができたので、今度はこの softmax に合わせた cross entropy を用意しなくては、と思っているのですがもしかしてそれもわざわざやらなくてよいのでしょうか。

Keras で顔写真から年齢が学習できるのか

深層学習で回帰をしてみたいときとかあると思います。そこで、顔写真からその人の年齢を回帰してみます。ただ、何のデータをつかえばいいかわからなかったので、Berryz工房のジャケ写からキャプチャした画像をつかうことにします。結論を先にいうとこの記事の学習は全然駄目です。全然駄目ですがデータが決定的に足りないのとプレ処理が雑なのでその辺を改善すれば何とかなるのかもしれません。何とかならないかもしれません。

データの準備

データはWebの画像検索から調達します。ベリメンの年齢は4学年にわたるので、だいたい3, 4年置きのシングルのジャケ写から顔写真をキャプチャすればだいたいの年齢がカバーできると思います。なので、適当に「あなたなしでは生きてゆけない」「告白の噴水広場」「本気ボンバー!!」「ロマンスを語って」を訓練データにチョイスします。テストデータは「ライバル」にします。適当です。テストデータ数をちょうど30にしたかったので、「ライバル」の梨沙子のデータだけ訓練データに含めます。なお、キャプチャはだいたい正方形になるように手動で切り取っています。訓練時に 128x128 にリサイズします。

訓練

面倒なので以下の記事のネットワーク構造を使いまわします。変更点は、今回は年齢そのものの出力を目指すので最終層を活性化しないというところです。損失関数と最適化アルゴリズムも適当に変更します。
 Keras で少ない画像データから学習したい - クッキーの日記

結果

何かを学習したような雰囲気はありつつも概して駄目でした。データが足りません。
全体的に幼い方に誤っているので最終層のバイアスを大きくするだけでもっと最小2乗誤差が小さくなりそうです(そうなっていないのは学習を早々に止めすぎました)。

f:id:cookie-box:20170814235401p:plain:w560

スクリプト

画像は最初に全て読み込まず、ミニバッチ学習のたびにミニバッチの分だけ読み込むという方法にしています(というか今回の目的がそのような処理の練習でした)。keras.preprocessing.image.ImageDataGenerator の flow_from_directory や flow を活用したかったのですが上手くできなかったので愚直な書き方になりました(flow_from_directory はクラス分類学習の際に cat や dog などというサブフォルダを切って猫画像データや犬画像データを配置しておけば勝手にミニバッチ毎の読み込みを正解データ生成含めてやってくれますが、今回はクラス分類ではなく回帰であり、個々の画像に固有の数値の正解データを生成したいため利用できませんでした)。

from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Dropout, Flatten, Dense
from keras import backend as K
import os
import numpy
import cv2
import pandas

# 顔画像ファイル名から年齢を抽出
def filename2age(filename):
  tempstr = filename.replace('.PNG', '')
  tempstrs = tempstr.split('_')
  return(int(tempstrs[2]))

# ファイル名のリストからデータをロード
def load_data(dirname, filenames, img_width, img_height):
  x = []
  y = []
  for filename in filenames:
    image = cv2.imread(dirname + filename)
    image = cv2.resize(image, (img_width, img_height))
    age = filename2age(filename)
    x.append(image)
    y.append(age)
  x = numpy.array(x)
  x = x.astype('float32') / 255.
  y = numpy.array(y)
  return(x, y)

# メイン処理
if __name__ == '__main__':
  img_width, img_height = 128, 128
  if K.image_data_format() == 'channels_first':
    input_shape = (3, img_width, img_height)
  else:
    input_shape = (img_width, img_height, 3)
  
  # モデル構築
  model = Sequential()
  model.add(Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Conv2D(32, (3, 3), activation='relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Conv2D(64, (3, 3), activation='relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Flatten())
  model.add(Dense(64, activation='relu'))
  model.add(Dense(1))
  model.compile(loss='mean_squared_error', optimizer='rmsprop')
  
  # 学習
  filenames = numpy.array(os.listdir('data/')) # 30 files
  batch_size = 10
  n_batch_loop = 3 # 30 / 10
  epochs = 50
  for e in range(epochs):
    print('Epoch', e)
    indices = numpy.repeat(range(n_batch_loop), batch_size)
    numpy.random.shuffle(indices)
    print(indices)
    for i_batch_loop in range(n_batch_loop):
      filenames_temp = filenames[indices == i_batch_loop]
      x, y = load_data('data/', filenames_temp, img_width, img_height)
      model.fit(x, y, epochs=1, batch_size=batch_size)
  
  # 学習結果の確認 (訓練データ)
  x, y = load_data('data/', filenames, img_width, img_height)
  result = pandas.DataFrame(filenames, columns = ['filename'])
  result['actual'] = y
  result = pandas.concat([result,
    pandas.DataFrame(model.predict(x), columns = ['predict'])], axis=1)
  result.to_csv('train.csv', index=False, encoding='utf-8')
  
  # 学習結果の確認 (テストデータ)
  filenames = numpy.array(os.listdir('test_data/'))
  x, y = load_data('test_data/', filenames, img_width, img_height)
  result = pandas.DataFrame(filenames, columns = ['filename'])
  result['actual'] = y
  result = pandas.concat([result,
    pandas.DataFrame(model.predict(x), columns = ['predict'])], axis=1)
  result.to_csv('test.csv', index=False, encoding='utf-8')

Keras で少ない画像データから学習したい

深層学習で画像分類をしてみたいときとかあると思います。でも画像を用意するのが面倒です。すると Keras ブログに「少ないデータから強力な画像分類」とあります。GitHubスクリプトもあります。これを使ってみます。
Building powerful image classification models using very little data
fchollet/classifier_from_little_data_script_1.py - GitHub

データの準備

Google 検索で出てきた順にカレーとラーメンの画像を保存しました。でも時間がなかったので訓練用とテスト用に各クラス10枚ずつしか保存しませんでした。Keras ブログに書いてあるように、「少ないデータ」とは少ないといえども "just a few hundred or thousand pictures" ということなので、真面目にやる場合はきちんと用意してください。これらの画像は Keras ブログの指示通り、data/train/curry/ のようにフォルダを切った下に置きます。

訓練

このカレーとラーメンの画像分類を、畳み込み層とプーリング層を積み上げたニューラルネットワークで訓練するわけですが、少ないデータを最大限に活用して学ぶために、画像を常にランダムに少しの回転や平行移動、シア変形(平行四辺形のように歪める変形)、拡大縮小、左右反転などをさせながらモデルに入力します(どれだけの変形まで許容するかは自分で指定します)。こうすることによって過学習が抑制されモデルの汎化性能が上がるそうです。

結果

訓練データが20枚というやる気のなさでもテストデータの9割を正解してくれました(下表)。何回か訓練を試行するとテスト正解率が100%になることも多かったです。

f:id:cookie-box:20170810000634p:plain:w480
なお「カレー」と誤判断されたラーメンは以下のサイトの「天日地鶏」というお店のもので、ニューラルネットはこのチャーシューがカレールーに、麺とネギがライスに見えたのかもしれません(?)。
キングオブ静岡ラーメン~人気ランキングTOP10|静岡新聞SBS-アットエス
「ラーメン」と誤判断されたカレーは以下でした。何がラーメン要素だったのかよくわかりません。
​新潟発。衝撃200円カレーが東京に初進出 - エキサイトニュース(1/2)
逆に以下のあまりカレーに見えない MOKUBAZA のチーズキーマカレーはカレーに判定されました。ラーメンにも見えませんが。
【保存版】2015年のカレーまとめ / もっとも印象に残ったお店20選 | ロケットニュース24

スクリプト

ほぼ GitHubスクリプトの通りですが、サンプル数を合わせてあるのと、最後に個別データの確率を csv 出力する部分を追加してあります。

from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K
import pandas

if __name__ == '__main__':
  img_width, img_height = 150, 150 # 訓練時の画像サイズ
  
  train_data_dir = 'data/train'
  validation_data_dir = 'data/validation'
  nb_train_samples = 20
  nb_validation_samples = 20
  epochs = 50
  batch_size = 5
  
  if K.image_data_format() == 'channels_first':
    input_shape = (3, img_width, img_height)
  else:
    input_shape = (img_width, img_height, 3)
  
  # モデル構築
  model = Sequential()
  model.add(Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Conv2D(32, (3, 3), activation='relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Conv2D(64, (3, 3), activation='relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Flatten())
  model.add(Dense(64, activation='relu'))
  model.add(Dropout(0.5))
  model.add(Dense(1, activation='sigmoid'))
  
  model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

  # 訓練データのプレ処理の設定: RGB値のスケーリングに加え、シア変形や拡大縮小によって訓練増強
  train_datagen = ImageDataGenerator(rescale=1. / 255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
  # テストデータのプレ処理の設定: こちらはRGB値のスケーリングのみ
  test_datagen = ImageDataGenerator(rescale=1. / 255)
  
  # 訓練データをディレクトリ内のデータから随時作成するジェネレータ
  train_generator = train_datagen.flow_from_directory(train_data_dir,
    target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary')
  # テストデータ作成をディレクトリ内のデータから随時作成するジェネレータ
  validation_generator = test_datagen.flow_from_directory(validation_data_dir,
    target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary')
  
  # 訓練
  model.fit_generator(train_generator, steps_per_epoch=nb_train_samples//batch_size,
    epochs=epochs, validation_data=validation_generator, validation_steps=nb_validation_samples//batch_size)
  # 訓練済重みの保存
  model.save_weights('weight.h5')
  
  # 具体的に個々のデータに対してどのような確率分布になったかの出力
  train_generator = test_datagen.flow_from_directory(train_data_dir,
    target_size=(img_width, img_height), batch_size=1, class_mode='binary', shuffle=False)
  validation_generator = test_datagen.flow_from_directory(validation_data_dir,
    target_size=(img_width, img_height), batch_size=1, class_mode='binary', shuffle=False)
  
  predict = model.predict_generator(train_generator, steps=nb_train_samples)
  prob = pandas.DataFrame(predict, columns = ['curry'])
  prob['ramen'] = 1.0 - prob['curry']
  prob['predict'] = prob.idxmax(axis=1)
  prob.to_csv('train.csv', index=False, encoding='utf-8')
  
  predict = model.predict_generator(validation_generator, steps=nb_validation_samples)
  prob = pandas.DataFrame(predict, columns = ['curry'])
  prob['ramen'] = 1.0 - prob['curry']
  prob['predict'] = prob.idxmax(axis=1)
  prob.to_csv('test.csv', index=False, encoding='utf-8')

雑記: 最近のふぁぼより

後で読もうとふぁぼって結局読まないこととかあると思います。なので最近どんなことをふぁぼったか復習してみたんですが結局まだ読んでいません。

「A Distributional Perspective on Reinforcement Learning

 [1707.06887] A Distributional Perspective on Reinforcement Learning

強化学習において Bellman 最適方程式による行動価値の更新のとき、期待値に更新するのではなく分布のまま扱おうという話のようです。期待値で更新する方法でもリスク回避的な学習にはできるけどって言っていると思います。行動価値を分布として保持するのはイメージが湧くような気がしないでもないですがそれでどう方策を決めるのかはまだ全然読んでいないのでわかりません。確率的に行動するんだろうと思います。

「Reducing Dimensionality from Dimensionality Reduction Techniques」

 Reducing Dimensionality from Dimensionality Reduction Techniques

次元削減手法(PCA、t-SNE、自己符号化器)に関する解説のようです。

「深層学習とベイズ統計」

 深層学習とベイズ統計

@yutakashino 様のスライドです。決定論的モデルであるニューラルネットを確率論的モデルに modify したいため、ニューラルネットの各重みパラメータ  \theta を「ある分布からの実現値」として取り扱おうという話だと思います。
_人人人人人人人人人人人人人人人人_
> 学習の収束のふるまいがキモい < (スライド13頁)
  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

「Deep Reinforcement Learning, Decision Making, and Control」

 https://sites.google.com/view/icml17deeprl

深層強化学習のチュートリアルのようです。ICML2017 で講演があったのでしょうか。

「物理のいらない量子アニーリング入門」

 物理のいらない量子アニーリング入門 - Platinum Data Blog by BrainPad

量子アニーリング入門ということです。github でコードが公開されているということです。
GitHub - ohtaman/anneal

「Fundamentals of Nonparametric Bayesian Inference (Cambridge Series in Statistical and Probabilistic Mathematics)」

Fundamentals of Nonparametric Bayesian Inference (Cambridge Series in Statistical and Probabilistic Mathematics)Fundamentals of Nonparametric Bayesian Inference (Cambridge Series in Statistical and Probabilistic Mathematics)
Subhashis Ghosal Aad van der Vaart

Cambridge University Press 2017-06-26
売り上げランキング : 2101

Amazonで詳しく見る
by G-Tools

比較的最近出た本らしいです。670ページらしいです。購入したいですが置く場所がありません。

「On the State of the Art of Evaluation in Neural Language Models」

 [1707.05589] On the State of the Art of Evaluation in Neural Language Models

自然言語処理には結局 LSTM がよいという話のようです。なんかもう state-of-the-art がタイトルになってしまいました。

「~簡単!炎上のさせ方講座~」

@jagarikin 様のツイートより。炎上のさせ方が簡単にわかります。
ただ臨場感あふれる炎上にするにはコツが要るらしく、適当な実装では上手くいきませんでした(下図)。なんか地面が全体的にのっぺり燃えているようになってしまい、点々と炎を吹いているようにならなかったです。炎上のさせ方は奥が深いようです。また頑張ります。

f:id:cookie-box:20170808113251g:plain

あと PyTorch とか最近話題ですね。