機械学習の0、分類~回帰

概要

機械学習が解決可能な問題が、出力データにより分類か回帰カテゴリーに切り分ける。離散データ出力の場合、分類問題となり、連続データ出力の場合、回帰問題となる。それぞれの方法論もあるし、共通な方法論もある。

主な方法論

分類問題カテゴリー

ロジスティック回帰、sigmoid関数を介して線形回帰\((wx+b)\)を\(0|1\)にマッピングして2クラス問題に適用される。Nクラス分類問題の場合、\((wx+b)\)のNセットを取得してから例えばsoftmax関数で振り分ける多重分類問題に適用される。「回帰」との文字があるのに、実は分類法である。

サポートベクターマシンSVM、各サンプルポイントとの距離が最大となるマージン最大化超平面を利用した分類法である。

kNN、ユークリッド距離を利用した分類法である。

kd-tree、decision-tree、二分木を利用した分類法である。

回帰問題カテゴリー

線形回帰、\(wx+b\)の値を出力する。これは連続値であるため、回帰問題に適用される。

サポートベクター回帰SVR、出力する\(wx+b\)の値は、サンプルポイントから分類表面までの距離であり、連続値であるため、回帰モデルである。

共通カテゴリー

単純ベイズ、分類と回帰に適用される。
分類の場合、yは離散カテゴリであるため、xを指定して離散\(p(y|x)\)を取得すると事後確率が得られる。回帰の場合、確率密度関数\(p(y|x)\)を取得すると事後確率関数が得られる。

畳み込みニューラルネットワークCNN、分類と回帰に適用される。
回帰の場合、入力層~中間層~最後1つのニューロンに接続されて、\(wx+b\)を出力すると回帰問題の処理流れとなる。Nクラス分類の場合、m個のニューロンがN個のニューロンに接続されているため、異なるw値を持つ\(wx+b\)のN個のグループがあります。softmaxを使用してはNクラスの確率を得られる。

再帰ニューラルネットワークRNN、分類と回帰に適用される。回帰および分類の場合、CNNと同様に回帰、分類問題に適用されるが、違いはRNNの入力、出力が時系列データである。

参考文献

「Statictics_Learning Method」、Lihang氏

追記

サポートベクターマシンSVM、分類のみならず回帰にも適用される。

1+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア

深層学習の2、LSTM Seq2Seqモデル実装

概要

株価、為替、天気、動画など時系列データの予測でよく使われるディープラーニングの代表的手法RNN(再帰型ニューラルネットワーク)の拡張バージョンに、LSTM(Long short-term memory)と呼ばれるモデルがある。今回は、LSTM Seq2Seq、Many to Manyモデルを実装して、円の第4象限の一部(訓練未実施)に対して、時系列データの予測を行ってみる。

環境

keras/LSTM, Google Colab CPU

モデル

lstm_seq2seq_model
lstm_seq2seq_model

以下モデルの概要を説明する。
・Sequentialは、あるレイヤの全ノードと、次レイヤの全ノードをつなぐRNNのモデル。
・RNNの第1 Layerとして、LSTMを追加する。第1引数は出力の次元数、activationは活性化関数で、input_shapeは入力データのフォーマット(in_time_steps x in_features)となる。このlayerがEncoderのように見える。
・RepeatVectorにより、出力を入力として繰り返す。ここでの繰り返し回数は予測範囲(out_vectors)となる。
・再度のLSTM、ただし、ここではシーケンス出力まさにmany outputつまり、return_sequencesをTrueに指定する。このlayerがDecoderのように見える。
・TimeDistributedを指定し、かつ、Dense(out_features)で、出力の次元数を指定する。
・最後にcompileメソッドで、学習時の最適化手法や、損失関数を指定する。ここでは最適化手法としてAdamを、損失関数としてMSE(Mean Squared Errorつまり平均2乗誤差)に指定する。

実装

# -*- coding: utf-8 -*-
# Brief: A Seq2Seq Model Implementation by keras Recurrent Neural Network LSTM.
# Author: Tateo_YANAGI @soarcloud.com
#
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

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

PI = 3.1415926535
EPOCHS = 100
TRAIN_SPLIT = 0.98

#
# Class Prediction for lstm model.
#
class Prediction:
  def __init__(self):
    # Array dimension of input&output:(batch_size, time_steps or vectors, features).
    self.batch_size = 48
    self.in_time_steps = 6
    self.in_features = 2
    self.out_vectors = 3
    self.out_features = 2
    self.hidden_neurons = 100 # Neurons of hidden layer.
    self.epochs = EPOCHS # Iteration times.
    self.train_split = TRAIN_SPLIT # Partition of learning dataset.
    self.activation_hidden = 'tanh' # Activation function for hidden units.
    self.activation_output = 'linear' # Activation function for output units.
    self.optimizer= 'adam' # Optimization function.
    self.loss = 'mse' # Loss(cost) function.

#
# Create dataset.
#
  def create_dataset(self, train_split=0.8, in_time_steps=10, out_vectors=4, in_features=2, out_features=2):
    a = np.array([[np.cos(0.0), np.sin(0.0)]])
    for i in range(1, int(2*PI*10)):
      a = np.append(a, np.array([[np.cos(i*0.1), np.sin(i*0.1)]]), axis=0)
    print(f'[Debug-0]\nlen(a):{len(a)}\na:\n{a}')

    # Create dataset for train and test
    # Initialize x_train and y_train.
    x_total = a[0:in_time_steps, 0:in_features]
    y_total = a[in_time_steps:in_time_steps+out_vectors, 0:in_features]

    # Calculate length for x_total and y_total.
    total_len = a.shape[0] - in_time_steps - out_vectors + 1
    print(f'[Debug-1]\ntotal_len:{total_len}')

    # Fill out x_total and y_total.
    for i in range(1, total_len):
      x_total = np.append(x_total, a[i:i+in_time_steps], axis=0)
      y_total = np.append(y_total, a[i+in_time_steps:i+in_time_steps+out_vectors], axis=0)
    print(f'[Debug-2]\nx_total.shape[0]):{x_total.shape[0]}\nx_total:\n{x_total}\ny_total.shape[0]:{y_total.shape[0]}\ny_total:\n{y_total}')

    # Reshape x_toal and y_total from 2D to 3D.
    x_total = np.reshape(x_total[0:x_total.shape[0]], (-1, in_time_steps, in_features))
    y_total = np.reshape(y_total[0:y_total.shape[0]], (-1, out_vectors, out_features))

    # Split dataset for train and test
    x_train = x_total[0:int(x_total.shape[0]*train_split)]
    y_train = y_total[0:int(x_total.shape[0]*train_split)]
    #x_test  = x_total[x_train.shape[0]:]
    #y_test  = y_total[y_train.shape[0]:]
    x_test  = x_total[-2:]
    y_test  = y_total[-2:]
    print(f'[Debug-3]\nx_train.shape[0]:{x_train.shape[0]}\nx_train:\n{x_train}\ny_train.shape[0]:{y_train.shape[0]}\ny_train:\n{y_train}')
    print(f'[Debug-4]\nx_test.shape[0]:{x_test.shape[0]}\nx_test:\n{x_test}\ny_test.shape[0]:{y_test.shape[0]}\ny_test:\n{y_test}')

    return x_train, y_train, x_test, y_test

#
# Create lstm model.
#
  def create_model(self) :
    model = Sequential()
    # Encoder
    model.add(LSTM(self.hidden_neurons, activation=self.activation_hidden, input_shape=(self.in_time_steps, self.in_features)))
    # Output used as Input.
    model.add(RepeatVector(self.out_vectors))
    # Decoder, output sequence
    model.add(LSTM(self.hidden_neurons, activation=self.activation_hidden, return_sequences=True))
    model.add(TimeDistributed(Dense(self.out_features, activation=self.activation_output)))
    #model.add(Activation(self.activation_output))   
    model.compile(optimizer=self.optimizer, loss=self.loss, metrics=['accuracy'])
    return model

  def train_model(self,model, x_train, y_train) :
    model.fit(x_train, y_train, epochs=self.epochs, verbose=2, batch_size=self.batch_size)
    return model

#
# main()
#
if __name__ == "__main__":
  predict = Prediction()
  train_in_data = None
  # Create dataset
  x_train, y_train, x_test, y_test = predict.create_dataset(predict.train_split, predict.in_time_steps,\
                                                            predict.out_vectors, predict.in_features, predict.out_features)
  # Create model
  model = predict.create_model()
  # Train model
  model = predict.train_model(model, x_train, y_train)
  print(model.summary())
  # Test
  predicted = model.predict(x_test, verbose=1)
  print(f'[info_1]predicted:\n{predicted}')
  # Plot result
  predicted = np.reshape(predicted, (-1, predict.out_features))
  x_test = np.reshape(x_test, (-1, predict.in_features))
  y_test = np.reshape(y_test, (-1, predict.out_features))
  x_train = np.reshape(x_train, (-1, predict.in_features))
  y_train = np.reshape(y_train, (-1, predict.out_features))
  plt.figure(figsize=(8, 8))
  plt.scatter(x_train[:,0], x_train[:,1])
  plt.scatter(x_test[:,0], x_test[:,1])
  plt.scatter(y_test[:,0], y_test[:,1])
  plt.scatter(predicted[:,0], predicted[:,1])
  plt.show()

※ソースコード公開→
https://github.com/soarbear/lstm_seq2seq_model_prediction

結果

lstm_seq2seq_model_prediction
lstm_seq2seq_model_prediction

今回、epochsとneuronsを増やすことで、上図のとおり精度において納得いく結果を得た。これでコスト関数、活性化関数の選定はじめ、ハイパーパラメータの調整が精度にいかに重要なことを分かる。熱伝播、振動、柔軟ロボット関節の駆動力など元々フーリエ変換、ラプラス変換、Z変換しか解けない偏微分方程式が、LSTMモデルを適用できたらどうかと今度試してみる。

参考文献

1、LSTMネットワークを理解する(英文原稿)、Christopher Olah氏
2、Keras公式資料

2+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア

深層学習の1、上がる株みつかる

概要

株価、為替、天気、動画など時系列データの予測でよく使われるディープラーニングの代表的手法RNN(再帰型ニューラルネットワーク)の拡張バージョンに、LSTM(Long short-term memory)と呼ばれるモデルがある。今回はLSTM Many to Oneモデルを実装して、複数銘柄(例:10銘柄)の株価から翌日の上がる株を探ってみる。

環境

keras 2.2.5 LSTM
Google Colab CPU/GPU/TPU
Ubuntu 18.04.3 LTS
Python 3.6.8
Numpy 1.17.3
Pandas 0.25.2
sklearn 0.21.3

実装

Many-to-Oneモデルの例として、以下2銘柄から翌日の上がる株を探る。

# -*- coding: utf-8 -*-
import numpy
import pandas
import matplotlib.pyplot as plt

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

class Predict:
  def __init__(self):
    self.length_of_sequences = 10
    self.in_out_neurons = 1
    self.hidden_neurons = 300
    self.batch_size = 32
    self.epochs = 100
    self.percentage = 0.8

  # データ用意
  def load_data(self, data, n_prev):
    x, y = [], []
    for i in range(len(data) - n_prev):
      x.append(data.iloc[i:(i+n_prev)].values)
      y.append(data.iloc[i+n_prev].values)
    X = numpy.array(x)
    Y = numpy.array(y)
    return X, Y

  # モデル作成
  def create_model(self) :
    Model = Sequential()
    Model.add(LSTM(self.hidden_neurons, batch_input_shape=(None, self.length_of_sequences, self.in_out_neurons), return_sequences=False))
    Model.add(Dense(self.in_out_neurons))
    Model.add(Activation("linear"))
    Model.compile(loss="mape", optimizer="adam")
    return Model

  # 学習
  def train(self, x_train, y_train) :
    Model = self.create_model()
    Model.fit(x_train, y_train, self.batch_size, self.epochs)
    return Model

if __name__ == "__main__":

  predict = Predict()
  nstocks = 2;

  # 銘柄毎に学習、予測、表示
  for istock in range(1, nstocks + 1):
    # データ準備
    data = None
    data = pandas.read_csv('/content/drive/My Drive/LSTM/csv/' + str(istock) + '_stock_price.csv')
    data.columns = ['date', 'open', 'high', 'low', 'close']
    data['date'] = pandas.to_datetime(data['date'], format='%Y-%m-%d')

    # 終値のデータを標準化
    data['close'] = preprocessing.scale(data['close'])
    data = data.sort_values(by='date')
    data = data.reset_index(drop=True)
    data = data.loc[:, ['date', 'close']]

    # 割合で学習、試験データ分割
    split_pos = int(len(data) * predict.percentage)
    x_train, y_train = predict.load_data(data[['close']].iloc[0:split_pos], predict.length_of_sequences)
    x_test, y_test = predict.load_data(data[['close']].iloc[split_pos:], predict.length_of_sequences)

    # 学習
    model = predict.train(x_train, y_train)

    # 試験
    predicted = model.predict(x_test)
    result = pandas.DataFrame(predicted)
    result.columns = [str(istock) + '_predict']
    result[str(istock) + '_actual'] = y_test

    # 表示
    result.plot()
    plt.show()

    # 翌日株価比較
    current = result.iloc[-1][str(istock) + '_actual']
    predictable = result.iloc[-1][str(istock) + '_predict']
    if (predictable - actual) > 0:
      print(f'{istock} stock price of the next day INcreases: {predictable-actual:.2f}, predictable:{predictable:.2f}, current:{current:.2f}')
    else:
      print(f'{istock} stock price of the next day DEcreases: {actual-predictable:.2f}, predictable:{predictable:.2f}, current:{current:.2f}')

またソースコード、csvファイルは、https://github.com/soarbear/stocks-lstm-kerasへ公開した。

結果

keras lstmで複数銘柄の株価予測
keras lstmで複数銘柄の株価予測

【追記】再現性

毎回学習してから予測の結果が変わるので、再現性、生産性がない。ニューロンの重みやバイアスの初期値など【Randomness in Initialization, Regularization, Layers, Optimization】がRandomであり、以下の例として、SEED_IDを固定することによって、予測結果の再現が確認できた。ただしepochsは、SEED_IDを固定しないRandomのほうよりlossがいかに最小まで収束するかを確かめる必要がある。

import numpy as np
import tensorflow as tf
import random as rn
import os
os.environ['PYTHONHASHSEED'] = '0'
from keras import backend as K
session_conf = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)
sess = tf.Session(graph=tf.get_default_graph(), config=session_conf)
K.set_session(sess)
......
def fix_seed():
  np.random.seed(SEED_ID)
  rn.seed(SEED_ID)
  tf.set_random_seed(SEED_ID)
......
def create_model(self) :
  Model = Sequential()
  fix_seed()
  Model.add(LSTM(self.hidden_neurons, batch_input_shape=(None, self.length_of_sequences, self.in_out_neurons), return_sequences=False))
  fix_seed()
  Model.add(Dense(self.in_out_neurons))
  Model.add(Activation("linear"))
  Model.compile(loss="mape", optimizer="adam")
  return Model
......

また、2のKeras公式資料もあわせて確認しておく。
しかし、ニューロンの重みやバイアスの初期値が固定するより、ランダムのほうは結果の多様性が現われる。複数のモデル(30以上?)より交差検証の手法で良いモデルを選ぶのが、まさに統計の務めではないかと考えさせられる機会となる。

【追記】学習状況をスマホに記録

学習状況をスマホでモニタしたい際、アプリHyperdashが使える。以下コードを実行する。学習状況の履歴がスマホに残る。

!pip install hyperdash
from hyperdash import monitor_cell
!hyperdash signup --github
%%monitor_cell 'xxx'

参考文献

1、LSTMネットワークを理解する(英文原稿)、Christopher Olah氏
2、開発中にKerasを用いて再現可能な結果を得るには?、Keras公式資料

3+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア

深層学習の0、RNN~LSTM勾配消失なくなる

問題の提出

勾配がなくなると、ニューラルネットワーク層の重みが更新されなくなり、学習が停止する。逆に勾配発散により学習が不安定になり、パラメータの変化が大きくなりすぎて、最適なパラメータを取得できなくなる。

RNN勾配消失

時刻\(t\)におけるコスト関数\(L_t\)が重み\(W_x,W_o\)に関する偏微分つまり勾配が以下の式で表される。
$$ \frac{\partial L_t}{\partial W_x} = \sum_{k=0}^{t}\frac{\partial L_t}{\partial O_t} \frac{\partial O_t}{\partial S_t}(\prod_{j=k+1}^{t} \frac{\partial S_j}{\partial S_{j-1}}) \frac{\partial S_k}{\partial W_x} \\ \frac{\partial L_t}{\partial W_s} = \sum_{k=0}^{t}\frac{\partial L_t}{\partial O_t} \frac{\partial O_t}{\partial S_t}(\prod_{j=k+1}^{t} \frac{\partial S_j}{\partial S_{j-1}}) \frac{\partial S_k}{\partial W_s} \\ S_j = tanh(W_x X_j + W_s S_{j-1} +b)$$
よって、$$ D = \prod_{j=k+1}^{t} \frac{\partial S_j}{\partial S_{j-1}} = \prod_{j=k+1}^{t} W_s \cdot tanh’ $$
\(tanh\)の微分は常に1未満、\(W_s\)も0より大きく1より小さい値である場合、\(t\)が大きくなると、\(D\)は0に近くなり、勾配消失の問題につながる。\(W_s\)が大きい場合、\(D\)はますます大きくなり、勾配爆発が発生する。

LSTMに勾配消失問題が消える

LSTMの場合、活性化関数\(S_t\)は以下の式で表される。
$$ \small S_t = tanh(\sigma(W_f X_t + b_f)S_{t-1} + \sigma(W_i X_t + b_i)X_{t}) $$\(S_{j-1}\)に関する\(S_j\)の偏微分の総乗は以下の式で表される。
$$ \small \prod_{j=k+1}^{t} \frac{\partial S_j}{\partial S_{j-1}} = \prod_{j=k+1}^{t} \sigma(W_f X_t + b_f) \cdot tanh’ \doteq 0|1 $$
これにより、RNNの勾配消失、発散の問題がなくなったと見られる。

参考文献

LSTMネットワークを理解する(英文原稿)、Christopher Olah氏
その和訳その中訳

3+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア

カスタムメニューのカテゴリーに記事数の表示

本サイト左側サイドメニューのように、カテゴリーの後に記事数(投稿数)を表示するには、funcitons.phpの最後へ下記ソースコードを追加する。

add_filter('wp_nav_menu_objects','my_nav_count');
function my_nav_count($items){
  foreach ($items as $item){
    if($item->object == 'category'){
      $item->title .= " (". get_term($item->object_id,'category')->count .")";
      $args[] = $item;
    }
    else {
      $args[] = $item;
    }
  }
  return $args;
}

以上

3+

ロボット・ドローン部品お探しなら
ROBOT翔・電子部品ストア