機械学習の1、k-近傍法

概要

k-近傍法(k-NearestNeighbor、kNN)アルゴリズムは、ある入力インスタンスに対して、トレーニングデータセット内のインスタンスに最も近いK個のインスタンスを見つけて、k個のインスタンスの多数がAクラスに属すると、入力インスタンスもこのAクラスに分類される。近いかの判断基準である距離がユークリッド距離\(\sqrt{\sum(x_i-y_i)^2}\)とする。k-近傍法がほとんどの場合に使えそうで、シンプルな分類法である。実生活で少数派が多数派に従うという考え方に似ている。

実装の例

k-近傍法を利用して、0~9の手書き数字を判別する。ソースコードは下記出典から借用とする。

'''
Created on Sep 16, 2010
kNN: k Nearest Neighbors
Input:      inX: vector to compare to existing dataset (1xN)
            dataSet: size m data set of known vectors (NxM)
            labels: data set labels (1xM vector)
            k: number of neighbors to use for comparison (should be an odd number)
            
Output:     the most popular class label
@author: pbharrin
'''
from numpy import *
import operator
from os import listdir

def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()     
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

def img2vector(filename):
    returnVect = zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect

def handwritingClassTest():
    hwLabels = []
    trainingFileList = listdir('trainingDigits')           #load the training set
    m = len(trainingFileList)
    trainingMat = zeros((m,1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]     #take off .txt
        classNumStr = int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
    testFileList = listdir('testDigits')        #iterate through the test set
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]     #take off .txt
        classNumStr = int(fileStr.split('_')[0])
        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
        if (classifierResult != classNumStr): errorCount += 1.0
    print "\nthe total number of errors is: %d" % errorCount
    print "\nthe total error rate is: %f" % (errorCount/float(mTest))

 if __name__ == '__main__':
    handwritingClassTest()

ソースコードhttps://github.com/soarbear/Machine_Learning/blob/master/kNN/kNN.py

結果

the classifier came back with: 0, the real answer is: 0
...
the classifier came back with: 9, the real answer is: 9
the total number of errors is: 12.0
the total error rate is: 0.012684989429175475

手書きの例では、正確率98.7%の結果となったので、kNNが手書き数字の認識に使えそうな分類法だと分かる。kの選択について、通常小さな値を選択してよく実験して最適なk値を探す。つまり実験を通してkを選定する。入力インスタンスにもっとも近いk個のインスタンスを見つけるのがポイントである。トレーニングデータセット内のインスタンス毎との距離を計算するため、トレーニングデータセットの量が増えると計算量も増えて、無駄が多くなる。いかに計算のコストを下げて、効率よくkインスタンスをみつけるのが課題となり、k-dimension treeつまりkd-treeの出番となる。

出典

「Machine Learning in Action」、Peter Harrington氏

1+

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

深層学習の3、複数人体検出実験

概要

yolo_v3を用いて、動画から複数人体の検出を行ってみる。

実装

GPUの確認

!nvidia-smi
nvidia_tesla_k80_spec
nvidia_tesla_k80_spec

Darknetリポジトリのクローン作成

import os
os.environ['PATH'] += ':/usr/local/cuda/bin'
!git clone https://github.com/AlexeyAB/darknet/

コンパイル環境のインストール

!apt install gcc-5 g++-5 -y
!ln -s /usr/bin/gcc-5 /usr/local/cuda/bin/gcc 
!ln -s /usr/bin/g++-5 /usr/local/cuda/bin/g++

ライブラリのコンパイル

cd darknet
!sed -i ‘s/GPU=0/GPU=1/g’ Makefile
!sed -i ‘s/OPENCV=0/OPENCV=1/g’ Makefile
!make

yolov3重みのゲット

!wget https://pjreddie.com/media/files/yolov3.weights
!chmod a+x ./darknet

動画mp4ファイルのアップコード

from google.colab import files
uploaded = files.upload()

人体検出

!./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights -dont_show fifa.mp4 -i 0 -out_filename fifa.avi -thresh 0.7

Jupyter NotebookファイルはGithubへ公開 https://github.com/soarbear/YOLOv3_detection

結果

以下の画像をクリックすると、画面がyoutube動画サイトへ遷移する。

動画から人体検出
動画から人体検出

参考文献

https://pjreddie.com/darknet/yolo/

2+

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

機械学習の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翔・電子部品ストア