データサイエンティスト(仮)

元素粒子論博士。今はデータサイエンティスト(仮)。

Memo:MacOS SierraでXGboostをpipで入れる

XGboostを自宅のMacに入れようとしても入らなかったので、調べてみたことを備忘録として残しておきます。
以前との差分を考えてみたら、MacOSSierraにアップデートしてたことに気が付き、調べると以下の記事がヒットしました。

qiita.com

上の記事では、clang-ompのエイリアスの設定もやっているんですが、自分の場合はそれをやらなくてもXGboostがpipで入れられることを確認しました。またまたちょっと調べると、LLVM 8.1から公式のclangがOpenMPに対応したため、clang-ompはこれ以上更新されないと。そのため最新のLLVMを導入したほうがいいとのこと。自分はclang-ompを入れていないので、気にしなくても大丈夫(だったと理解)。

qiita.com


というわけで、OSが変わると色々面倒なことがあるということを再確認したのでした。

NN論文の読み会で発表した

はじめて外部勉強会なるもので発表をしました。
tfug-tokyo.connpass.com

今回の論文のテーマはDeep Learning + 自然言語処理系で、私は全然キャッチアップしてなかったところだったので、勉強(炎上ラーニング)を兼ねて申し込んでみました。私が選んだ論文は、Amazonが行っているソーシャルボット(対話型ボット)のコンペ出場チームの報告論文です。

www.slideshare.net

昔ながらの人工無脳から、NNを使った最近の手法をふんだんに使って複数の会話の応答候補を出力し、その中でよいものを深層強化学習(実際にはそこまで強化学習してない)で最適化するという内容で、いろいろ知識を得るのに非常に役立ちました。2週間くらいで前提知識ほぼゼロからキャッチアップして発表というスケジュールだったので、非常に苦しかったですが(笑)

発表はあまりうまくできなかった気がしますが、いろいろな人からたくさん質問をいただいて、非常に楽しい時間でした。また次回以降、もしくは別の勉強会でも発表して多くの人と交流していきたいと思いました。

社内でKDD2017論文を紹介した

社内の有志でKDD2017の論文紹介をしました。
紹介した内容はスパース推定に関するアルゴリズムの話です。
発表資料をslideshareに公開したので、そのリンクをこちらにもはっておきます。
Qualityがよくないかもしれませんので、ご質問やご意見がありましたらご連絡いただけると幸いです。

www.slideshare.net

KerasでDeep Learning:LSTMで日経平均株価を予測してみる

導入

前回までで、画像データに関してDeep Learningを試してきました。画像データは、各データが独立と期待されるようなタイプのデータです。しかしながら、Deep Learningはこのような各データが独立であるような場合だけでしかできないというわけではありません。データ間に相関がある場合の代表例として、時系列データがあります。今回は、時系列データに対して威力を発揮するネットワークをKerasで実装してみます。

使用データ

人工データを使うのもあれなので、より現実的で身近なデータを使ってみます。今回は、日経平均株価終値(日次)を使います。日経平均株価のデータは、以下のサイトからダウンロードしました。
日経平均株価 1時間足 時系列データ CSVダウンロード
あるだけ(2007年以降)全てダウンロードし、それらを結合して一つのファイルを作っておきます。

終値(finish)をプロットすると、以下のようになっています。
f:id:tekenuko:20170725001823p:plain
今回は、このデータの90%を学習と検証に使用し、残り10%で予実比較をします。

参考

ネットワークの組み方は、以下を参考にしました。
qiita.com
ただし、こちらのKerasのVersionは古いので、いくつかオプション名が変わっています。そこはdocumentなどを参照して適宜書き換えます。

前処理

今回、予測の仕方を少しだけ工夫します。直前の50営業日の株価データを用いて、次の営業日の株価を予想する、という方法です。その用途に即したデータ加工を以下の関数で行います。

import numpy as np
import pandas as pd
from pandas import Series, DataFrame

# 直前のn_prev 日分のデータと、その次の営業日のデータを生成
def _load_data(data, n_prev = 50):  
   
    docX, docY = [], []
    for i in range(len(data)-n_prev):
        docX.append(data.iloc[i:i+n_prev].as_matrix())
        docY.append(data.iloc[i+n_prev].as_matrix())
    alsX = np.array(docX)
    alsY = np.array(docY)

    return alsX, alsY
# 学習用とテスト用データを分割、ただし分割する際に_load_data()を適用
def train_test_split(df, test_size=0.1, n_prev = 50):  
    """
    This just splits data to training and testing parts
    """
    ntrn = round(len(df) * (1 - test_size))
    ntrn = int(ntrn)
    X_train, y_train = _load_data(df.iloc[0:ntrn], n_prev)
    X_test, y_test = _load_data(df.iloc[ntrn:], n_prev)

    return (X_train, y_train), (X_test, y_test)

train_test_split()にしかるべき株価データを投入すれば、所定の形式の学習・テストデータを生成することができます。株価データは、dfというDataFrameに格納されており、終値カラム名はfinishであるとします。スケールを狭めるのに、全体の平均値で割った操作をした終値を目的変数とします。

# 全体の平均値で割る
df['obs'] = df['finish'] / df['finish'].mean()
length_of_sequences = 50
(X_train, y_train), (X_test, y_test) = train_test_split(df[['obs']], test_size = 0.1, n_prev = length_of_sequences)

LSTM

ここでは、時系列データを扱うネットワークであるRecurrent Neural Network(RNN)の一つである、LSTMというネットワークを用いてモデル化します。LSTMに関する詳しい説明は、例えば以下の書籍やサイトなどを参照ください*1
bookclub.kodansha.co.jp
qiita.com

Kerasで必要なライブラリをインポートし、前回までと同じような感じでネットワークを組んでみます。

from keras.models import Sequential  
from keras.layers.core import Dense, Activation  
from keras.layers.recurrent import LSTM

in_out_neurons = 1
hidden_neurons = 100

model = Sequential()  
model.add(LSTM(hidden_neurons, batch_input_shape=(None, length_of_sequences, in_out_neurons), return_sequences=False))  
model.add(Dense(in_out_neurons))  
model.add(Activation("linear"))  

これは可視化すると以下のようなネットワークを組んだことになっています。
f:id:tekenuko:20170725003656p:plain
書籍などで勉強するとわかりますが、このLSTMというところは、中身は結構複雑です。

学習をさせてみます。

model.compile(loss="mean_squared_error", optimizer="adam")
history = model.fit(X_train, y_train, batch_size=600, epochs=50, validation_split=0.2)
Train on 1822 samples, validate on 456 samples
Epoch 1/50
1822/1822 [==============================] - 2s - loss: 0.5133 - val_loss: 0.4263
Epoch 2/50
1822/1822 [==============================] - 2s - loss: 0.0918 - val_loss: 0.0283
Epoch 3/50
1822/1822 [==============================] - 2s - loss: 0.0574 - val_loss: 0.0162
Epoch 4/50
1822/1822 [==============================] - 2s - loss: 0.0108 - val_loss: 0.0325
Epoch 5/50
1822/1822 [==============================] - 2s - loss: 0.0109 - val_loss: 0.0707
Epoch 6/50
1822/1822 [==============================] - 2s - loss: 0.0179 - val_loss: 0.0485
Epoch 7/50
1822/1822 [==============================] - 2s - loss: 0.0080 - val_loss: 0.0116
Epoch 8/50
1822/1822 [==============================] - 1s - loss: 0.0025 - val_loss: 0.0016
Epoch 9/50
1822/1822 [==============================] - ETA: 0s - loss: 0.006 - 2s - loss: 0.0065 - val_loss: 0.0016
Epoch 10/50
1822/1822 [==============================] - 2s - loss: 0.0043 - val_loss: 0.0070
Epoch 11/50
1822/1822 [==============================] - 2s - loss: 0.0020 - val_loss: 0.0177
Epoch 12/50
1822/1822 [==============================] - 2s - loss: 0.0032 - val_loss: 0.0188
Epoch 13/50
1822/1822 [==============================] - 2s - loss: 0.0028 - val_loss: 0.0108
Epoch 14/50
1822/1822 [==============================] - 2s - loss: 0.0018 - val_loss: 0.0049
Epoch 15/50
1822/1822 [==============================] - 2s - loss: 0.0019 - val_loss: 0.0039
Epoch 16/50
1822/1822 [==============================] - 2s - loss: 0.0018 - val_loss: 0.0056
Epoch 17/50
1822/1822 [==============================] - 2s - loss: 0.0015 - val_loss: 0.0075
Epoch 18/50
1822/1822 [==============================] - 2s - loss: 0.0015 - val_loss: 0.0072
Epoch 19/50
1822/1822 [==============================] - 2s - loss: 0.0014 - val_loss: 0.0057
Epoch 20/50
1822/1822 [==============================] - 2s - loss: 0.0013 - val_loss: 0.0045
Epoch 21/50
1822/1822 [==============================] - 1s - loss: 0.0012 - val_loss: 0.0041
Epoch 22/50
1822/1822 [==============================] - 2s - loss: 0.0012 - val_loss: 0.0039
Epoch 23/50
1822/1822 [==============================] - 2s - loss: 0.0011 - val_loss: 0.0037
Epoch 24/50
1822/1822 [==============================] - 2s - loss: 0.0010 - val_loss: 0.0036
Epoch 25/50
1822/1822 [==============================] - 2s - loss: 9.9451e-04 - val_loss: 0.0035
Epoch 26/50
1822/1822 [==============================] - 2s - loss: 9.4513e-04 - val_loss: 0.0028
Epoch 27/50
1822/1822 [==============================] - 2s - loss: 9.0099e-04 - val_loss: 0.0026
Epoch 28/50
1822/1822 [==============================] - 2s - loss: 8.5874e-04 - val_loss: 0.0023
Epoch 29/50
1822/1822 [==============================] - 1s - loss: 8.1982e-04 - val_loss: 0.0020
Epoch 30/50
1822/1822 [==============================] - 2s - loss: 7.9007e-04 - val_loss: 0.0018
Epoch 31/50
1822/1822 [==============================] - 2s - loss: 7.5337e-04 - val_loss: 0.0019
Epoch 32/50
1822/1822 [==============================] - 2s - loss: 7.1939e-04 - val_loss: 0.0019
Epoch 33/50
1822/1822 [==============================] - 2s - loss: 6.9869e-04 - val_loss: 0.0016
Epoch 34/50
1822/1822 [==============================] - 2s - loss: 6.7346e-04 - val_loss: 0.0013
Epoch 35/50
1822/1822 [==============================] - 2s - loss: 6.7397e-04 - val_loss: 0.0013
Epoch 36/50
1822/1822 [==============================] - 2s - loss: 6.4443e-04 - val_loss: 0.0013
Epoch 37/50
1822/1822 [==============================] - 2s - loss: 6.2074e-04 - val_loss: 0.0014
Epoch 38/50
1822/1822 [==============================] - 2s - loss: 6.3547e-04 - val_loss: 0.0014
Epoch 39/50
1822/1822 [==============================] - 2s - loss: 6.0775e-04 - val_loss: 0.0012
Epoch 40/50
1822/1822 [==============================] - 2s - loss: 6.3238e-04 - val_loss: 0.0012
Epoch 41/50
1822/1822 [==============================] - 2s - loss: 6.0300e-04 - val_loss: 0.0014
Epoch 42/50
1822/1822 [==============================] - 2s - loss: 6.1697e-04 - val_loss: 0.0012
Epoch 43/50
1822/1822 [==============================] - 2s - loss: 6.3159e-04 - val_loss: 0.0012
Epoch 44/50
1822/1822 [==============================] - 2s - loss: 6.1184e-04 - val_loss: 0.0013
Epoch 45/50
1822/1822 [==============================] - 2s - loss: 6.1628e-04 - val_loss: 0.0013
Epoch 46/50
1822/1822 [==============================] - 2s - loss: 5.9740e-04 - val_loss: 0.0012
Epoch 47/50
1822/1822 [==============================] - 2s - loss: 6.0511e-04 - val_loss: 0.0012
Epoch 48/50
1822/1822 [==============================] - 2s - loss: 6.0267e-04 - val_loss: 0.0014
Epoch 49/50
1822/1822 [==============================] - 2s - loss: 6.1846e-04 - val_loss: 0.0012
Epoch 50/50
1822/1822 [==============================] - 2s - loss: 6.0198e-04 - val_loss: 0.0012

途中少しぶれがあれど、train、validationともにepoch数が大きくなるにつれてlossが減少しています。

念のため可視化すると以下の感じになっています。
f:id:tekenuko:20170725004114p:plain

予測

いよいよ予実比較してみます。

# 予測値算出
predicted = model.predict(X_test) 
# 実績と比較
dataf =  pd.DataFrame(predicted)
dataf.columns = ["predict"]
dataf["input"] = y_test
dataf.plot(figsize=(15, 5))

可視化すると以下のようになっています。
f:id:tekenuko:20170725004300p:plain

うーん、見た目的には似てる感じですが、これをもって予測がうまくいっていると言えるのか…?色々試して考察しなきゃいけない事案な気がするので、いったんはコメントを控えておこう(笑)ここでのメッセージはKerasを使うとDeep Learningによる株価予測をお手軽にできるよ、ということで。

まとめ

今回は株価データに対してLSTMを適用してみました。今回はかなり単純なセットアップをとったので、まだまだやりようはありそうです*2。そういった試行錯誤は気が向いたらやります(笑)

次回以降はまた画像に戻って画像生成とか考えてみようかな、と妄想中です。

*1:説明を放棄してしまいました。まあ世の中にはたくさん解説があるので、あえてここで説明しなくてもよいかな、と思ったということで(笑)

*2:過去X日分のデータからY日間の平均を予測、翌日株価が上がったか下がっただけに着目、日経平均以外のデータを考慮などです。

KerasでDeep Learning:CNNを組んでみる

導入

前回はMNISTデータに対してネットワークを構築して、精度を見ました。
tekenuko.hatenablog.com
今回は、より画像処理に特化したネットワークを構築してみて、その精度検証をします。

参考

KerasのGithubにあるexampleのほぼ丸パクリです。
github.com

畳み込みニューラルネットワーク

畳み込みニューラルネットワーク(Convolutional Neural Network, 以下CNN)は、畳み込み層とプーリング層というもので構成されるネットワークです。CNNは画像データに対して威力を発揮します。このあたりの詳しい説明は講談社の書籍や、ネット上の解説で見ることができるので、省略します。
bookclub.kodansha.co.jp
postd.cc

ざっくりとは、ニューラルネットのリンク部分を全結合にしないで、うまく一部だけ結合させ、その一部の平均なり最大値なりをとるなどして画像の特徴をうまく抽出する手法になっています。このようなネットワークを何層も組むと、入力層に近い層では原始的な特徴(エッジ部分など)を、より深い層では抽象的な特徴(猫っぽいなど)が表現されるようです。

データ加工

今回も前回と同様、MNISTデータを使います。ただし、前回は28ピクセル×28ピクセルのデータを784次元のベクトルに変換して入力としましたが、今回は行列として入力します。

# 必要なライブラリのインポート
import keras
from keras.datasets import mnist
from keras import backend as K

#Kerasの関数でデータの読み込み。データをシャッフルして学習データと訓練データに分割
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 行列として入力するための加工
batch_size = 128
num_classes = 10
epochs = 20

img_rows, img_cols = 28, 28

# 場合分け
if K.image_data_format() == 'channels_first':
    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)

ここで、引数の与え方に関して、注意点があります。それを確認するのに、以下のjsonファイルを見てみます。

$HOME/.keras/keras.json

私の環境では、中身は以下のようになっています。

{
    "image_data_format": "channels_last",
    "epsilon": 1e-07,
    "floatx": "float32",
    "backend": "tensorflow"
}

このindexの詳細は以下です(Kerasの公式ページより)。

  • image_data_format: 文字列,"channels_last" か "channels_first" のいずれか.Kerasが従うデータのフォーマット規則を指定します. (keras.backend.image_data_format() がこれを返します.)
  • 2次元データ (例えば画像) に対しては, "channels_last" は (rows, cols, channels) とみなし,"channels_first" は (channels, rows, cols)とみなします.
  • 3次元データに対しては, "channels_last" は (conv_dim1, conv_dim2, conv_dim3, channels) とみなし, "channels_first" は (channels, conv_dim1, conv_dim2,conv_dim3) とみなします.
  • epsilon: float,いくつかの操作で0除算を避けるために使う微小量定数.floatx: 文字列,"float16","float32",か "float64".デフォルトの浮動小数点精度.
  • backend: 文字列,"tensorflow" か "theano" か "cntk".

この説明から、画像データの場合、"image_data_format"が"channels_first"か"channels_last"かによって指定する引数の順序が異なります。今回は"channels_last"だったので、先ほどのコードの後者の加工がされるようになっています。

最後に、入力データの正規化とラベルの処理を行っておきます。

# 入力データ
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

# ラベルはone-hot encodingを施す
y_train = y_train.astype('int32')
y_test = y_test.astype('int32')
y_train = keras.utils.np_utils.to_categorical(y_train, num_classes)
y_test =  keras.utils.np_utils.to_categorical(y_test, num_classes)

ネットワーク構築

Kerasのexampleに従い、2回畳み込みをしてPoolingを行うようなネットワークを組んでみます。

# 必要なパッケージのインポート
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

2次元画像データに対する畳み込みを行うということで、Conv2D()を使用します。kernel_sizeはフィルタリングの際に着目するピクセルのサイズになっています。プーリングに関しては、MaxPooling2D()を使用しています。pool_sizeで最大値を取る領域を指定しています。

この畳み込みとプーリングですが、明示的にオプションとして指定していないものがいくつかあります。つまり、Kerasではデフォルト設定でよしなにやってくれている操作がいくつかあります。このあたりはTensorflowを使用している人が見るととても気になる点のようです*1。私は、このあたりはモデルを作りこんでいく際に細かく見ていけばよい点かなと思っており、まずは見よう見まねでもネットワークを組んで試せるようになることが大事だと思っておりますので、細かいオプションについてはここでは述べないことにします。

ネットワークを組んだら、あとは前回までと同様にモデルをコンパイルして学習させればOKです。

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])
history = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs,
          verbose=1, validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
Train on 60000 samples, validate on 10000 samples
Epoch 1/20
60000/60000 [==============================] - 60s - loss: 0.3299 - acc: 0.9012 - val_loss: 0.0805 - val_acc: 0.9744
Epoch 2/20
60000/60000 [==============================] - 60s - loss: 0.1168 - acc: 0.9653 - val_loss: 0.0548 - val_acc: 0.9819
Epoch 3/20
60000/60000 [==============================] - 60s - loss: 0.0889 - acc: 0.9730 - val_loss: 0.0445 - val_acc: 0.9853
Epoch 4/20
60000/60000 [==============================] - 60s - loss: 0.0732 - acc: 0.9785 - val_loss: 0.0382 - val_acc: 0.9870
Epoch 5/20
60000/60000 [==============================] - 60s - loss: 0.0617 - acc: 0.9816 - val_loss: 0.0343 - val_acc: 0.9884
Epoch 6/20
60000/60000 [==============================] - 60s - loss: 0.0553 - acc: 0.9831 - val_loss: 0.0315 - val_acc: 0.9890
Epoch 7/20
60000/60000 [==============================] - 60s - loss: 0.0515 - acc: 0.9850 - val_loss: 0.0301 - val_acc: 0.9899
Epoch 8/20
60000/60000 [==============================] - 60s - loss: 0.0477 - acc: 0.9856 - val_loss: 0.0310 - val_acc: 0.9894
Epoch 9/20
60000/60000 [==============================] - 60s - loss: 0.0437 - acc: 0.9869 - val_loss: 0.0289 - val_acc: 0.9900
Epoch 10/20
60000/60000 [==============================] - 60s - loss: 0.0417 - acc: 0.9874 - val_loss: 0.0261 - val_acc: 0.9917
Epoch 11/20
60000/60000 [==============================] - 60s - loss: 0.0376 - acc: 0.9888 - val_loss: 0.0271 - val_acc: 0.9913
Epoch 12/20
60000/60000 [==============================] - 60s - loss: 0.0382 - acc: 0.9882 - val_loss: 0.0274 - val_acc: 0.9914
Epoch 13/20
60000/60000 [==============================] - 60s - loss: 0.0364 - acc: 0.9892 - val_loss: 0.0253 - val_acc: 0.9918
Epoch 14/20
60000/60000 [==============================] - 60s - loss: 0.0343 - acc: 0.9898 - val_loss: 0.0266 - val_acc: 0.9914
Epoch 15/20
60000/60000 [==============================] - 60s - loss: 0.0346 - acc: 0.9897 - val_loss: 0.0262 - val_acc: 0.9915
Epoch 16/20
60000/60000 [==============================] - 60s - loss: 0.0319 - acc: 0.9905 - val_loss: 0.0274 - val_acc: 0.9916
Epoch 17/20
60000/60000 [==============================] - 60s - loss: 0.0330 - acc: 0.9898 - val_loss: 0.0263 - val_acc: 0.9918
Epoch 18/20
60000/60000 [==============================] - 60s - loss: 0.0320 - acc: 0.9903 - val_loss: 0.0284 - val_acc: 0.9906
Epoch 19/20
60000/60000 [==============================] - 60s - loss: 0.0312 - acc: 0.9907 - val_loss: 0.0285 - val_acc: 0.9915
Epoch 20/20
60000/60000 [==============================] - 60s - loss: 0.0309 - acc: 0.9905 - val_loss: 0.0268 - val_acc: 0.9906
Test loss: 0.0267664063053
Test accuracy: 0.9906

テストデータで約99%のAccuracyをたたき出しました。前回組んだネットワークよりも精度が上がっています。

epochごとにAccuracyとLossがどう変化していくかも確認しておきましょう。

#Accuracy
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
#loss
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()

f:id:tekenuko:20170707105655p:plain
f:id:tekenuko:20170707105710p:plain

train, validationともにかなり良い性能を持っていそうな振舞いをしています。Early stoppingとか気にしなくていっか、と思ってしまいました(笑)

まとめ

駆け足でしたが、今回はMNISTという画像データに対してCNNを構築して精度を見てみました。Kerasのexampleをまねただけですが、後はこれをベースにいろいろと試行錯誤していけばよいのかな、という感じです。

画像データに関する手法ですが、ベーシックな話はいったんここまでにします。次回以降は未定ですが、時系列に特化したネットワークの話や、画像に関して応用的な話などを候補として考えています。

*1:ストライドとかどうなってるの?パディングは?などです。