Aidemy Blog

株式会社アイデミーのブログです。機械学習・ディープラーニング関連技術の活用事例や実装方法をまとめる技術記事や、キャリア記事等を発信しています

機械学習で金の価格を予想してみた

 こんにちは。Aidemy研修生の藤川です。

 

機械学習というと何かを予測するということを考える人が多いのではないでしょうか。

 

株やFX、仮想通貨の価格を予想しているブログも多々ありますが、金の価格はどうでしょうか。
f:id:t_aisu_ke:20180912011005p:plain
 

なぜ金か?

というと、以下のリンクにある通り、金の価格予想自体は昔からされています。


jpyforecast.com


 

しかし、機械学習を使った予測はどうやらあまり行われていないようです。

 

ということで、この価格予想に負けじと、機械学習を用いて予測を行ってみたいと思います!



 今回はGoogle Colaboratoryを使用しています。
もし使用する場合は、ランタイムの変更でGPUを指定しておくことを忘れないようにしてください。

実行環境(lshwを使用して確認)

OS : ugbuntu17.10
CPU : Intel(R) Xeon(R) CPU @ 2.20GHz
GPU : Tesla K80
メモリ : 12GB

このようにある程度のスペックの環境であることがわかります。
自分のPCのスペックが足りない場合には非常に役立ちそうですね。



import部分はこのようになっています。

import pandas as pd
import numpy as np
import io
from google.colab import files
import matplotlib.pyplot as plt
import statsmodels.api as sm
from datetime import datetime
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.arima_model import ARIMA
import warnings
import itertools
%matplotlib inline

 

データの取得

まず、金の価格のデータを取得しなければいけません。

データは以下のサイトから取得できます。
lets-gold.net

今回は2016年と2017年のデータをもとに学習を進めていきます。
 

データの読み込みを行います。時系列解析では連続した日付のデータが求められますが、
金の価格は土日・祝日に更新がされないので日付を適当につけなおしてあげます。

#ファイルをアップロードする
uploaded = files.upload()

#アップロードされたファイルを読み込む
gold_data = pd.read_csv("historical_data_2016.csv")
gold_data2 = pd.read_csv("historical_data_2017.csv")

#必要のない列は削除しておく。
gold_ = gold_data.drop(columns = ["DATE","PT_TOKYO","GOLD_NY","PT_NY","USDJPY"])
gold_.dropna()
gold2_ = gold_data2.drop(columns = ["DATE","PT_TOKYO","GOLD_NY","PT_NY","USDJPY"])
#データの結合
gold = pd.concat([gold_,gold2_])

#ここで日付を付け直してあげる
gold.index = pd.date_range("2016-01-01","2017-05-06",freq = "D")


 

データの関係を調べる

ここで、データの関係を確認することにします。
このブログを参考に進めていきます。
www.kumilog.net

まずは、自己相関係数を求めます。
簡単に言うと、自己相関係数を調べることで何個前のデータが大きな影響を及ぼしているかがわかります。

fig=plt.figure(figsize=(12, 8))
ax = fig.add_subplot(212)
#自己相関係数
sm.graphics.tsa.plot_acf(gold_data["GOLD_TOKYO"], lags=80,ax = ax) 
plt.show()

f:id:t_aisu_ke:20180912001857p:plain

ここで、色が濃くなっているところが95%信頼区間です。
だいたい10個前までのデータに大きく影響を受けていることがわかります。

次に偏自己相関係数を求めます。

fig=plt.figure(figsize=(12, 8))
ax = fig.add_subplot(212)
#偏自己相関係数
fig = sm.graphics.tsa.plot_pacf(gold_data["GOLD_TOKYO"], lags=80, ax=ax)
plt.show()

f:id:t_aisu_ke:20180912002520p:plain

偏自己相関係数では、ある点とある点の間の関係をダイレクトに調べることが出来ます。
例えば、今日と2日前のデータの関係を調べる時、1日前のデータの影響も受けますが、
偏自己相関係数を用いると1日前の影響を取り除いて考えることが出来ます。
この結果を見る限り、前日の影響を大きく受けることがわかります。
また、6日目もある程度大きな影響を与えていることもわかります。
69日目は95%信頼区間をギリギリ超えています。ここも何か関係がある可能性がありますね。


最後に、ADF検定を行います。
ADF検定では単位根過程でないかどうかを判定できます。
詳しくは以下の記事をご覧ください。
logics-of-blue.com

pythonではstatsmodelsで簡単にADF検定を行うことが出来ます。

#ADF検定を行う
adf_result = sm.tsa.stattools.adfuller(gold_data["GOLD_TOKYO"],autolag='AIC')
adf = pd.Series(adf_result[0:4], index=['Test Statistic','p-value','#Lags Used','Number of Observations Used'])
print(adf)  

結果は以下のようになります

Test Statistic                  -3.089297
p-value                          0.027355
#Lags Used                       5.000000
Number of Observations Used    239.000000

p値は0.027355と、 p < 0.05であるので単位根過程ではないことがわかります。
単位根過程ではないということでここから安心して学習を進めていきます。

モデルの決定・学習

今回は時系列のデータなので、1番基礎となるSARIMAモデルを使用します。
SARIMAモデルについてはこちらのサイトでよく理解できると思います。
deepage.net
学習部分についてはこのようにスマートにかくことが出来ます。

N = 420
#420点までを学習とし、その先50点を予測する
test = gold[:]
gold = gold[:N]
#グラフよりs=1,6,69あたりが良い?
SARIMA_gold = sm.tsa.statespace.SARIMAX(gold,order=(1, 0, 1),seasonal_order = (1,1,1,69), enforce_stationarity = False, enforce_invertibility = False,trend = "n").fit(trend='nc',disp=False)
print(SARIMA_gold.summary())
pred = SARIMA_gold.predict()
pred2 = SARIMA_gold.forecast(50)

自己相関係数より、q = 1
偏自己相関係数より p = 1

gold_diff = gold_.diff()
gold_diff.index = gold_data.index
gold_diff = gold_diff.dropna()

また、季節成分はありませんが、1回階差の季節成分を抽出し、グラフを参照します。
f:id:t_aisu_ke:20180912225747p:plain

また、移動平均を青色で表示します。
sin波とならべてみると、微妙に季節性を感じます。
f:id:t_aisu_ke:20180913125348p:plain

グラフをよく見ると3ヶ月ごとの周期を感じるので、周期s = 69を決定しました。

以上よりSARIMA(1,0,1)(1,1,1,69)を決定しました。

モデルの学習結果はこのようになります。
f:id:t_aisu_ke:20180912230131p:plain
AIC,BICともに2000超えという非常に残念な結果となっています。
一般に、AIC、BICは小さい方が良いモデルとされる傾向があります。
AIC,BICについては以下の記事に詳しく説明があります。
www.atmarkit.co.jp


一応、データをプロットしてみます。

plt.plot(gold_,color = "b")
plt.plot(gold2_,color = "b")
plt.plot(pred,color = "r")
plt.plot(pred2,color="y")

plt.xlim(["2016-05-01","2017-06-06"])
plt.ylim([4400,5200])
plt.show()

青が学習データ、赤が学習させた結果、そして黄色が予測です。
f:id:t_aisu_ke:20180912231602p:plain

あまりグラフの形は似ていませんね、改善が必要なようです。

騰落も調べてみます。

data_score = 0
for i in range(49):
  if pred[i+1] - pred[i] > 0:
    if test.values[N+1+i] - test.values[N+i] > 0:
      data_score += 1
  if pred[i+1] - pred[i] < 0:
    if test.values[N+1+i] - test.values[N+i] < 0:
      data_score += 1

print("正解率:" + str(100*data_score/50) + "%")

正解率は50%となり、ランダムウォークですね...といった結果です...

モデルの精度を上げる

標準化を行います。
標準化を行うので、データを \frac{X-μ}{σ}に従わせます。

gold_mean = np.mean(gold.values)
gold_std = np.std(gold.values)
gold_b = (gold - gold_mean)/gold_std
SARIMA_std = sm.tsa.statespace.SARIMAX(gold_b,order=(1, 0, 1),seasonal_order = (1,1,0,69), enforce_stationarity = False, enforce_invertibility = False,trend = "n").fit(trend='nc',disp=False)
print(SARIMA_std.summary())
pred_b = SARIMA_std.predict()
pred2_b = SARIMA_std.forecast(50)

モデルの学習結果です。
f:id:t_aisu_ke:20180912231407p:plain
AICは4,BICは24まで落ちました!!!
この数値は小さい方が良いので標準化が非常に効果的であることが確認できました。

グラフを表示します。
f:id:t_aisu_ke:20180912231447p:plain
赤が標準化する前の予測で、黄色が標準化したあとの予測です。
グラフの形はあまりフィットしていませんね。もう少しうまくいくといいのですが・・・
騰落の正解率は54%!!!!!!!!
少しだけ精度が上がっているようです。

ランダムウォークを実装

実際の金の価格もランダムウォークとなっています。

こちらの記事を参考にランダムウォークをSARIMAモデルで実装してみます。
stats.stackexchange.com

SARIMA(0,1,0)(1,1,1,69)としてみます。

f:id:t_aisu_ke:20180913151348p:plain

比較的フィットするグラフを描くことができました。

Introduction to ARIMA models
こちらの記事を参考にすると、ARIMA(0,1,0)はAR(1)過程に従うようです。
d.hatena.ne.jp

たしかに今回のデータも自己相関係数がこちらの記事の通りの形となっています。


番外編

 ランダムフォレストで学習を進めてみました。
今回は、4日前までの価格をもとに学習を進めています。

#4日目くらいまでのデータに意味がありそうだったので使ってみる
gold["lag1"] = gold["GOLD_TOKYO"].shift(1)
gold["lag2"] = gold["GOLD_TOKYO"].shift(2)
gold["lag3"] = gold["GOLD_TOKYO"].shift(3)
gold["lag4"] = gold["GOLD_TOKYO"].shift(4)
gold.dropna()

N = 420

X_train = np.delete(gold[['lag1', 'lag2', 'lag3']][:N].values,[0,1,2],0)
X_test = np.delete(gold[['lag1', 'lag2', 'lag3']][N:].values,[0,1,2],0)
y_train = np.delete(gold['GOLD_TOKYO'][:N].values,[0,1,2],0)                
y_test = np.delete(gold['GOLD_TOKYO'][N:].values,[0,1,2],0)

from sklearn.ensemble import RandomForestRegressor
r_forest = RandomForestRegressor(
            n_estimators=100,
            criterion='mse',
            random_state=1,
            n_jobs=-1
)
r_forest.fit(X_train, y_train)
y_train_pred = r_forest.predict(X_train)
y_test_pred = r_forest.predict(X_test)

結果は、、、
f:id:t_aisu_ke:20180912015038p:plain
赤が元データ、黄色が予測です。

めちゃくちゃフィットしてる...!!!!

...騰落の正解率は26.25%...

上下動の精度は落ちるがグラフにはフィットしています。

まとめ


ランダムウォークについてですが、こちらのブログを参考に考えてみます。
omedstu.jimdo.com

たしかに、金の価格は前日の影響を非常に大きく受けるため、AR(1)過程が最もフィットするというのも納得です。
これは金だけでなく、株やFX、仮想通貨でも同様にAR(1)過程での予測も効果的なのではないでしょうか。


ただ、実際株やFX、仮想通貨、金などの予測は単一の手法では十分な予測精度が得られないと思います。
いくつかのモデルを構築し、総合的な判断をすることが必要となります。
正解率を中心に結果を見ていますが、実際に金の投資として考えた時、長期的にどれほど上昇するかが大事になります。
つまり、ランダムフォレストのような形式で長期の見立てをつけ、SARIMAモデルなどで上昇の信ぴょう性を見ていくことがもっとも良いのではないでしょうか。


今回は手軽に行えるSARIMAモデル、ランダムフォレストを紹介しましたが、
先日のブログのようにLSTMを使用した方が良い結果が得られると思います。
前処理としてFFTでローパスフィルタをかける方法も金融工学では一般的なようです。
blog.aidemy.net
こちらのブログを参考に、是非LSTMも実装してみてください。



 

CNNで好きなアーティストを分類させてAIに良さを伝えたい

初めまして、AIdemy研修生のまえくら(@R25fgtSx)です。

皆さんは好きなアーティストもちろんいますよね!?

しかし、自分の好きなアーティストが全て同じ人に会ったことのある人はめちゃくちゃ少ないと思います.

そこで僕は何でも理解してくれる?コンピュータにその良さを伝えたいと思いました。伝えると言っても、楽曲データを学習させて分類させるだけですが...

ここで、僕の好きなアーティスト5組を紹介します(唐突)。

  1. AAA

    f:id:RyoMa:20180907193845j:plain

    https://avex.jp/aaa/aaak/
  2. UNISON SQUARE GARDEN

    f:id:RyoMa:20180907194015j:plain

    https://realsound.jp/2016/05/post-7509.html
  3. f:id:RyoMa:20180907194153j:plain

    https://www.johnnys-net.jp/page?id=artistTop&artist=10
  4. NEWS

    f:id:RyoMa:20180907194352j:plain

    https://kyun2-girls.com/archives/164
  5. SEKAI NO OWARI

    f:id:RyoMa:20180907194324j:plain

    https://abematimes.com/posts/2675644

これでいかに僕がイケメン好きか分かりましたね(苦笑)。

前置きが長くなりましたがいよいよ本題に入ります。

学習データの用意

まずはスマホの中にあるアーティスト5組の楽曲をPCに移します。今回はそれぞれに対して10曲ずつ用意しました。

f:id:RyoMa:20180907205543p:plain

f:id:RyoMa:20180907210156p:plain

 10曲では少ない(過学習を起こすため)、曲の長さが異なるので水増しと長さを揃えるために10秒ずつにデータを分割します。ここでは、音声信号処理に良く使われるlibrosaとpydubというライブラリを用いています。

import librosa
from pydub import AudioSegment

artist = ["AAA", "UNISON_SQUARE_GARDEN", "嵐", "NEWS", "SEKAI_NO_OWARI"]

for i in  range(len(artist)):
    file_index = 0
    pre_time = 0
    rea_time = 10000

    for j in range(10): 
        file_dir = "./music_data/" + artist[i] + "/raw_data/raw_" + str(j) + ".wav"
        music, fs = librosa.audio.load(file_dir)
        play_time = int(librosa.samples_to_time(len(music), fs) / 10)
        
        for k in range(play_time):
            audio_data = AudioSegment.from_file(file_dir, format="wav")
            split_audio = audio_data[pre_time:rea_time]
            pre_time += 10000
            rea_time += 10000
            
            split_audio.export("./music_data/" + artist[i] + "/split_data/" + str(file_index) + ".wav", format = "wav")
            
            file_index += 1

 次に10秒に分割した楽曲データをメル周波数スペクトルという音声認識に良く使われる特徴量をスペクトル画像として保存します。メル周波数について少し説明をします。

まずメル尺度というものがあります。

メル尺度とは音高の知覚的尺度のことで、メル尺度の差が同じであれば人間が感じる音高の差が同じになることを意味します。

メル周波数スペクトルを求めるには、大まかに以下のような流れになります。

  1. 窓関数を使って信号処理をした後に離散フーリエ変換する。
  2. メル周波数で均等にスムージングする。

詳しくはこのページを参考にしてください。

aidiary.hatenablog.com

文字だけだと分かりづらいので可視化してみます。

普通の音声データはこんな感じです。(AAAのNo Way Backという曲の冒頭10秒)

f:id:RyoMa:20180909231451p:plain

 

この信号をメル周波数スペクトルに変換すると...

f:id:RyoMa:20180909231502p:plain

 librosaを使ったコードは以下のように書きました。

split_num = [266, 273, 250, 232, 292]

for i in range(len(artist)):
    for j in range(split_num[i]):
        split_file_dir = "./music_data/" + artist[i] + "/split_data/" + str(j) + ".wav"
        export_dir = "./music_data/" + artist[i] + "/spectrum/" + str(j) + ".jpg"
        audio_data, sr = librosa.load(split_file_dir)
        S = librosa.feature.melspectrogram(audio_data, sr = sr, n_mels = 128)
        log_S = librosa.power_to_db(S)
        
        plt.figure(figsize=(8, 8))
        librosa.display.specshow(y=audio_data, sr=sr, hop_length = 2068)
        plt.savefig(export_dir)

次にOpenCVとnumpyを使って保存したスペクトルをRGB画像に変換して,学習データを作っていきます。ラベル作成も同時にやります。

from keras.utils.np_utils import to_categorical
improt cv2
import numpy as np

spe_dir = "./music_data/AAA/spectrum/"
X_train=[]
y_train=[]

for i in range(266):
    img = cv2.imread(spe_dir + str(i) + ".png")
    b,g,r = cv2.split(img)
    img = cv2.merge([r,g,b])
    X_train.append(img)
    y_train.append(0)

これを各データに対して行い,最後にX_trainをnumpyのリストにしy_trainは転置してOne-hot-表現に変換します.

X_train = np.array(X_train)
y_train = np.array(y_train)
y_train = y_train.T
y_train = to_categorical(y_train)

 これで学習データが完成しました。テストデータも今までの流れと同じようにして作成できます。

次はいよいよ学習モデルの構築です。今回はCNNで分類を行うことにしました。

理由としてはコーネル大学が発表した"Deep convolutional networks on the pitch spiral for musical instrument recognition"という論文を少し読んだからです。なので、この記事は僕の検証記事でもあるわけですね(笑)。

[1605.06644] Deep convolutional networks on the pitch spiral for musical instrument recognition

モデル作成にはkerasを使いました。以下がモデル構築のコードになっています。

from keras.layers import Activation, Conv2D, Dense, Flatten, MaxPooling2D
from keras.models import Sequential, load_model
from keras.utils.np_utils import to_categorical
from keras import optimizers
from keras.layers import Dense, Dropout, Flatten, Input, BatchNormalization
from keras.models import Model, Sequential

model = Sequential()
model.add(Conv2D(input_shape=(576, 576, 3), filters=32,kernel_size=(2, 2), strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(2, 2), strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(2, 2), strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
model.add(Dense(128))
model.add(Activation('relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(5))
model.add(Activation('softmax'))

次はデータを学習させます。kerasに最適化のライブラリは色々用意されているのですが一番精度の良かった"Adam"にし、損失関数は"categorical_crossentropy"にしました。

ここで少し最適化について説明します。

最適化とは与えられた条件のもとで何らかの関数を最小化(もしくは最大化)することです。機械学習で言えば損失関数(ここでは交差エントロピー)は最小にしたい(過学習防止)のですが、その時の最適解を求める方法をoptimizerで指定しています。

デフォルトでは"SGD(確率的勾配降下法)"が指定されています。

詳しい数式などの説明は以下のリンクからお願いします。また、kerasで使える最適化アルゴリズム一覧(Keras Documentation)も貼っておきます。

qiita.com

最適化 - Keras Documentation

model.compile(optimizer='Adam', loss='categorical_crossentropy',metrics=['accuracy'])
model.fit(X_train, y_train, batch_size=32, epochs=10

f:id:RyoMa:20180909234342p:plain

 学習データに対しては98%くらいの精度が出ていますね。最初の方は19%をうろうろしていたので大きな進歩です。肝心のテストデータに対しては...

score = model.evaluate(X_test, y_test, verbose=1)
print()
print("Test loss:", score[0])
print("Test accuracy:", score[1])

f:id:RyoMa:20180909235911p:plain

 テストデータに対しても92%くらいになりました。

これで僕の好きなアーティストの良さが伝わったと思います。AIもイケメン好きになったかな??

これからは音楽の話はこのAIとしかしません(大嘘)。

感想として、普段は学習データ,テストデータが用意されているので前処理を行うだけですが、今回はデータセットの作成から行うことができたので苦労はしましたが自信がつきました。今後はアーティストの数を増やして分類させてみたいです。また、音声信号処理に興味が出てきたのでもう少し勉強したいと思います。

研修生の方が書いた前回の記事と組み合わせて良いサービス作れそう...

blog.aidemy.net

iOSDC2018参加レポート!Swift × 機械学習トーク!

アイデミーの川内です。8/30~9/2に行われたiOSDCに参加してきたのでそのことについてブログを書こうと思います!!

iOSDCとは?

iOSDCとはiOS関連をテーマにした技術者のためのカンファレンスです。アイデミーではiOS関連のタスクはしてないですが以前のインターン先でiOSを開発していて、興味があったので参加してみることにしました!そこで色々調べてみるとWantedlyさんがスカラシップスポンサーというのをしており無料で学生をiOSDCに参加させてくれるというプログラムをしていたので応募してみたところ見事行けることになったのです!

iosdc.jp
www.wantedly.com

機械学習とiOS(swift)

swiftというとiOSの開発で主に使われる言語で機械学習とswiftってあんまり結びつかないなって方もいると思います。iOSDCではswiftと機械学習に関するトークがあったので紹介しようと思います。

t.co


強化学習の基礎的な理論からswiftによる実装までをテーマした発表でした!
既存ライブラリを使いこなすというのはエンジニアにとって不可欠なスキルですがスクラッチでも実装できるスキルも大切なんだなと思いました。
githubにコードを挙げてくださってるそうなので早速僕も写経しようと思います!
github.com
GitHub - yukinaga/CartPoleDeepSwift: Cart-Pole Balancing with Deep Q-Network(DQN) written with Swift.

speakerdeck.com

こちらの発表はswiftからpythonのライブラリを使用して機械学習をするというテーマの発表でした。
機械学習の基礎を分かりやすくまとめていてそこからswiftによる機械学習の実装についての今後についての発表でした!
swiftの生みの親であるクリス・ラトナーは現在Google Brainでswift for tensorflowというswift用のtensorflowの開発をしているそうです。
swiftで機械学習がやりにくいならその環境を作り出せばいいというような姿勢がかっこいいなと思いました。
www.youtube.com

iOSDCに参加してみて

こういった技術カンファレンスに参加するのが初めてで僕もプログラミングを初めて一年ちょっと経ってそこそこ出来るようになってきたかなという根拠のない自信を良い意味でへし折ってくれました。
コンパイラに関するトークもあったりして普段当たり前のように使っている技術もこんなに奥深いのかということを学べました。技術を使いこなすということを前提として技術を理解する、技術を作り出すという段階にまで早くステップアップしていこうと思いました!

Spotify APIで好きなアーティストの繋がりを可視化してディグる

初めまして、Aidemy研修生の照屋と申します。

突然ですが、皆さんは好きなアーティストのディグり方(探し方)ってどんな方法を使っていますか?

f:id:teruya6350:20180831000229j:plain

最近ではYouTubeや音楽発見サービス(Spotify、Apple Music…)、人によってはレコード店で物色という人もいると思います。私も色々な所にアンテナを立てて日々好きなジャンルのアーティスト達を探しています。

その中でも音楽発見サービスのSpotifyをよく利用するのですが、Spotifyが選んでくれるおすすめアーティストでたまに「えっこの人?」っていう人が入ってきたりします。実際に聞いてみても「うーん、好きなあのアーティストとテイストは似てるんだけど、違うんだよなあ」ってことがザラにあります。(笑)

そこで自分の好きなアーティストと繋がりのあるアーティストを可視化出来れば面白いディグりが出来るんじゃないかと思い、今回はSpotify APIというものを使って好きなアーティストの可視化したネットワークグラフを作っていきたいと思います。

0.Python使用環境

・Jupyter Notebook  1.0.0

・Spotipy 2.4.4
・pandas as pd  0.23.4
・matplotlib.pyplot as plt 2.2.3
・networkx as nx 2.1
・python_louvain 0.11

 

import部分ははこのようになっています。

import spotipy
import json
import pandas as pd
import matplotlib.pyplot as plt
import networkx as nx
from community import community_louvain
from spotipy.oauth2 import SpotifyClientCredentials

 

 

1.準備

まずはSpotify APIのspotipyを使用する準備をしていきます。

下記のサイトに飛んで、Spotifyのアカウントを登録して、

client_idとclient_secretを取得します。

https://developer.spotify.com/dashboard/login

# Spotify APIの準備
client_id = 'your id in here'
client_secret = 'your secret in here'
client_credentials_manager = spotipy.oauth2.SpotifyClientCredentials(client_id, client_secret)
spotify = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

これでspotipyを使える状態になりました。

アーティストの情報をpandasのデータフレームにいれて処理していくので、まずデータフレームを定義します。

# アーティスト情報を格納するDataFrameを定義
artist_df = pd.DataFrame(columns=['artist_name','artist_ID','genre','popularity','related_artist_names'])

これで準備はできました、早速ディグっていきましょう。

2.最初のアーティストを検索する

アーティストをディグる方法として、自分の知っているアーティストに関連した(曲のテイストやルーツなど)アーティストを調べていくという方法があります。今回は、この方法に沿って、最初に知っているアーティストを一つ挙げて、そこから関連を調べていくことにします。

今回は、ここ数年巷を騒がせているSuchmosを入れてみました。Suchmosは茅ヶ崎生まれの横浜シティボーイな感じで、幅広い層におすすめできるアーティストなので、聞いたことない人はぜひ聞いてみて下さい。(笑)

open.spotify.com

知っているアーティストの名前をspotify上で検索すると、そのアーティストの固有IDを取得することができます。IDには、そのアーティストの名前のほかに、配信しているアルバムのタイトルや音楽ジャンル、認知度、関連するアーティストのIDなどが紐づけられています。

ここでは、その紐づいたデータを読み込んで、最初のアーティストのレコードとしてDataFrameに格納します。

# 最初のアーティストを取得
name = 'Suchmos'
spotapi_out = spotify.search(q='artist:' + name, type='artist')
artist_items = spotapi_out['artists']['items'][0]
artist_id = artist_items['id']
artid_list = [artist_id]
artname_related_list = []
spotapi_out_related = spotify.artist_related_artists(artist_id)
for artname_related in spotapi_out_related['artists']:
    artname_related_list.append(artname_related['name'])
s = pd.Series([artist_items['name'], artist_items['id'], artist_items['genres'],artist_items['popularity'],artname_related_list],index=artist_df.columns)
artist_df = artist_df.append(s,ignore_index=True)

これで最初のアーティストとそのアーティストに関連するアーティストの情報を取得できました。

3.関連アーティストの関連アーティストを探していく

次に、先ほど取得した最初のアーティストに関連するアーティストのIDを使って、さらに関連の関連…といったように調べていきます。家系図をたどるような作業で、とてもワクワクしますね。 このように、2章での処理と同様、DataFrameにアーティストの情報を格納していきます。

artid_list_tail = 0

for i in range(relation_size):
    artid_list_head = artid_list_tail
    artid_list_tail = len(artid_list)
    for artid in artid_list[artid_list_head:artid_list_tail]:
        spotapi_out = spotify.artist_related_artists(artid)
        for artid_related in spotapi_out['artists']:
            artist_df_bool = artist_df['artist_ID']==artid_related['id']
            if artist_df_bool.sum()==0 and artid_related['popularity']>=Threshold:
                # 類似のアーティストリストを作成
                spotapi_out_related = spotify.artist_related_artists(artid_related['id'])
                artname_related2_list = []
                for artname_related2 in spotapi_out_related['artists']:
                    artname_related2_list.append(artname_related2['name'])
                artid_list.append(artid_related['id'])
                s = pd.Series([artid_related['name'], artid_related['id'], artid_related['genres'], 
                               artid_related['popularity'], artname_related2_list], index=artist_df.columns)
                artist_df = artist_df.append(s,ignore_index=True)

ただし、関連の関連の…と続けていくとアーティストの数は指数的に増大していくので、繰り返し数を決定するrelation_sizeはあまり大きい値にしないほうがいいです。最初のアーティストの認知度にもよりますが、5以上では処理が全然終わらなくなることが多い印象でした。

4.アーティスト同士の関連情報を辞書に格納

 ここでは、取得した大量のアーティスト情報をネットワークグラフに表すためにアーティスト同士の関係を作らないといけないので、その準備をしていきます。

まず、どのアーティストが誰と関連しているかを表す辞書を作ります。

アーティスト関連辞書={アーティストの名前:[関連アーティストの名前1,関連アーティストの名前2,.....] }となるようにデータを格納していきます。

# アーティストの関係の辞書を作る
plt.figure(figsize = (50, 50))
artdic = {}
for i in range(len(artid_list)):
    artdic[artist_df.iloc[i,0]] = []
for i in range(len(artid_list)):
    for artname_related in artist_df.iloc[i,4]:
        artdic[artist_df.iloc[i,0]].append(artname_related)

 

5.アーティスト間ネットワークを可視化する

最後に取得したアーティスト同士の関連情報を、ネットワークとして可視化します。可視化にはnetworkxというライブラリを使います。各アーティストを、その媒介中心性に比例した大きさを持つノードとし、エッジをつないでネットワークを描きます。

# グルーピング してグラフを表示
G = nx.Graph(artdic)
pos = nx.spring_layout(G)
partition = community_louvain.best_partition(G)
betcent = nx.communicability_betweenness_centrality(G)
node_size = [10000 * size for size in list(betcent.values())]
nx.draw_networkx(G, pos ,node_color=[partition[node] for node in G.nodes()], node_size=node_size, cmap=plt.cm.RdYlBu)
plt.axis('off') 
plt.show()

 

では、可視化したネットワークの全体をご覧ください!

 

f:id:teruya6350:20180831132730j:plain

 みっ、見えない(笑)。

数が多すぎて、細かいところはわかりませんが、アーティスト同士がクラスタとしてグルーピングされていることがわかりますね。

では最初のアーティストにしたSuchmosの周囲のネットワークを見てみます。

f:id:teruya6350:20180831132042j:plain

ターゲットのSuchmosの周りには今のシティポップ界隈を賑やかすNulbarichやYogee New Wavesなどがいます。

その中でもHouse Popに寄ったIriやChelmicoといったアーティストを介することでtofuBeatsやPUNPEEといったPopsの面々に繋がります。

f:id:teruya6350:20180831131854j:plain

Suchmosからくるりを介することでエレファントカシマシやチャットモンチー、ELLEGARDEN、アジカンといったJ-pop,J-rockに繋がっていくことが分かりますね。

f:id:teruya6350:20180831132353j:plain

 

まとめ

今回は、見た目のわかりやすさとグラフの描画限界などを考慮して邦楽で関連アーティストの取得量にも制限を掛けていますが、本当にSpotifyの全力を出して制限を掛けずにグラフを描画出来たら、音楽体系のネットワークをグラフに出来るのでもっと面白いものが見れると思います。

今後はアーティストごとのジャンル情報や楽曲の音データの特徴量などの要素も考慮したネットワークを可視化してさらに、ディグりツールとしての性能を追求していきたいと思います。

ここまでご覧くださってありがとうございました。

Pythonエンジニアから機械学習エンジニアに転職! 【Aidemy Interview】

 

こんにちは、DAIです。今回は、Aidemy Premiumを受講されてから、未経験で機械学習エンジニアとして転職された宮本さんにインタビューをしました。

 

宮本さん

北海道出身。現在25才。関西の大学に入学。大学時代の専攻は経営学。在学中、東京でエンジニアインターンを経験。その後、ITベンチャーにてPythonエンジニアとして就職。社会人2年目で、ポテンシャル採用で機械学習エンジニアとして転職。

 

DAI:現在のご職業を教えてください

 

宮本:今は無職です。もともとは新卒でPythonエンジニアとして就職しました。大学時代にはインターンでSwiftをかいて、スマホアプリケーションを作成していて、新卒ではじめてPythonを書きました。chatbotを作成していました。

 

その後、会社のプロジェクトの終了が契機に、転職を決意し、就職先が決まる前に退職しました。

 

DAI:以前インタビューした寺田さんとは同じ会社の同期だったんですね。ということは、一緒に転職活動をしていたという感じですかね。

 

blog.aidemy.net

 

宮本:そういうことになりますね。会社をやめる事が決まった次の日に二人ともAidemy Premiumの無料カウンセリング受けました。

 

DAI:Aidemy Premiumを学習される前には、どのようなことを学習されていましたか?


宮本:機械学習はCouseraと、深層学習の本を利用していました。寺田と一緒に教えあったり、社内で機械学習に興味がある人と一緒に勉強していましたね。

 

DAI:プレミアムを受講した理由について教えてください。

 

宮本:もともと、自然言語処理に興味がありましたので、受講しました。前職ではチャットボットでやっていて、機械が人間の言語を解釈するのが面白いんですよね。

 

DAI:転職活動について教えてください。

 

宮本:基本的には、エージェントを利用していました。もともとビズリーチを利用していて、ビズリーチからFor Startups、Robot Job Japan、タリスマンというエージェントを教えていただき、それらのエージェントを利用して学習を進めました。Robot Job Japanは機械学習に強いエージェントでした。ただ、ポートフォリオがないときついですが。Githubにソースコードをあげてポートフォリオにしていました。 

 

www.gc-agent.com

 

 

DAI:どんな試験が多かったですか。

 

宮本:基本的には、一次面接に関しては、本人のやる気を聞かれるような、基本的な志望理由等を聞かれることが多かったです。

 

二次面接までいくと、「弊社のサービスに機械学習を導入するとしたら、どういう風にするか」「それらのデータをどのように取得するか」等技術的なことを聞かれることが多かったです。

 

技術面接では、「ツイッターのデータを自然言語処理で分類」、「機械学習の論文を読んで10分で発表」など実際のアウトプットを要求する課題がありました。

 

DAI:それってかなり大変じゃなかったですか?

 

宮本:そうですね。ですので、Aidemy Premium Planでわからないことに関してはメンターの人に聞いて、必死に対応しましたね。急遽カウンセリングをお願いして、1時間くらい対応してもらいました。その後も丁寧にSlackで対応してもらったのよかったです。

 

DAI:何社くらい受けましたか?

 

宮本:面接までいったのが10社くらいで、最終までが5社です。

 

DAI:すごいですね、未経験で5社までもいけるものなのですね。 

 

宮本:基本的なプログラムの知識があり、勉強意欲・業界への強い興味があればポテンシャル採用を考えてくださる企業はあるかと思います。

 

DAI:ちなみに、転職先には機械学習のエンジニアはいるのでしょうか。

 

宮本:いますね。事前にその会社に機械学習エンジニアがいるかWantedlyなどで確認していましたね。

 

DAI : 次の転職先について教えてください。どのような職種で、どのような業務でしょうか。

 

宮本:機械学習エンジニアとして、産業機械/医療/Webサービス分野における画像診断/自然言語処理/統計解析等を行う予定です。

 

DAI: AI関連の仕事に着くのに、特に評価されたことはなんですか?

 

宮本:ポテンシャル採用だったため、会社の事業分野への興味とこれまでのコミットが評価されました。特に向こうが見ているのは、未知の分野の内容をキャッチアップできるかというところでしたね。なので、初めての分野をどうやって勉強してきたかを伝えたり、自分で解きたい課題がある時は、どういう風に解決したかをアピールしました。


DAI: Aidemy Premiumを受講して、良かった点はありますか?

 

宮本:良かった点としては、まずテキストがよかったです。コースの内容が、無料のチュートリアルとかと比べたら断然にわかりやすかったです。また、しっかりと実用的で、読んでいるだけで、応用がききそうでわくわくしましたね。また、自分がやりたいことがことだけができるの魅力的でした。自然言語処理だけをやりたかったので、それに特化したものを学べたのがよかったです。また、チューターの方から私に必要なコースを提案してくれた事も有難かったです。もともと教師あり学習を学ぶ予定はなかったのですが、チューターの方が学んだ方がよいと提案してくれたので、取り入れることができました。

 

また、アイデミーで学んでいること以外に関しても、いろいろと質問ができたのがよかったです。

 

DAI:ありがとうございました。

 

Aidemy Premium Planとは

 

Aidemy Premium Planとは、集中的な8週間のマンツーマンレッスンで、最先端のAIエンジニアを目指す学習コースです。データ分析コース、自然言語処理コース、カスタマイズコースからコースを選び、完全オンライン上でプログラミングを学ぶことができます。

 

 

 

詳細情報は、こちらのURLからホームページを見ることができるので、ぜひ読んでみてください!

 

aidemy-premium.net

 

雲画像から降水判断

 

はじめまして、Aidemy研修生のryotake0404です。今回は衛星画像の雲から降水判断を行います。

 

<はじめに>

近年、雲(水蒸気)のデータは異常気象予測に注目されていますが、専門的でない自分にはなかなかデータを読み取るのが難しいと感じました。そのため、気象データにくわしくない自分でもわかるやり方で雲を識別し、データとしてみました。衛星画像は地上で衛星から送られてきたデータを基に作成されますが、作成される経緯はよくわからないので、作成された画像から別の方法で雲をデータにおこそうという考えです。おそらく世界で誰もやったことがないと思いますが、もしいたらぜひ一緒に飲みに行きたいです。

 

<データの引用元・詳細>

① ひまわり8号の衛星画像downloadページ:

 ※ 2017年8月全日(但7日除く)、2018年8月1~20と2018年6月21から7月31までの雨が降っている日(降水量0.5以上)の衛星画像(日本列島付近)をダウンロードしました。

https://seg-web.nict.go.jp/wsdb_osndisk/shareDirDownload/bDw2maKV

② 気象庁 過去の気象データ・ダウンロード: 

 ※ ①に対応した日(但2018年8月11,12日を除く)分の東京の降水量をダウンロードしました。

https://www.data.jma.go.jp/gmd/risk/obsdl/index.php

 

※ 雨と晴れの比率は1:1になるようにデータをとっています。

 

<環境>

jupyter notebook 5.5.0

Python 3.6.5

mac 詳細 

f:id:ryotake0404:20180826172718p:plain

 

<流れ>

0. Neuronを作成し、Simple sigmoid modelを定義する

1. ひまわり8号からダウンロードしてきた衛星画像を読み込む

2. 画像を切り取って軽くして、いい感じにマスクかけて雲とそれ以外に色を分ける

3. 2の画像をlistにし、全画像分のlistが入った大きなlistを作成する

4. 気象庁からダウンロードしてきた降水量を元に、label作成

5. 3と4を1つのtupleに入れ、neuronに実行できる形にする

6. 必要情報をあてはめ、trainingを開始する

7. trainingし終わったmodelをもとにtest dataにあてはめて、どれくらいの精度で判断できたかをみる

 

※データは時間やパソコンの調子の関係でtrainingとtestそれぞれに約1ヶ月分しか用いられませんでした。次回機会があるなら数年、数十年分のデータで行ってみたいです。

 

<補足説明>

説明はほとんどcode内にあります。epoch数、どういうpredict方法か、loss functionはどういう定義にしたのか、などなどcode内に書き込んであります。ただ、途中の画像処理やデータの調整などは記載していないため、ここに画像で貼ります。

 

 

まず、ひまわり8号からとってくる画像は下の画像

f:id:ryotake0404:20180821190240p:plain

そして、きりとって軽くすると下のような感じ

f:id:ryotake0404:20180821190305p:plain

そして肝のマスクはこんな感じ。ここで雲とそれ以外にわけるのでマスクの数値は大切です。

f:id:ryotake0404:20180821190356p:plain

次に、気象庁からのデータですが、ダウンロードして何も調整していない状態だと下のような感じです(画像は例としてあげているものなので実際に使用したものとは必要な情報や日付などすこし違います)。

f:id:ryotake0404:20180826181504p:plain

そして、使用しない項目を減らし、今回は降水量をPrecipitation(mm)に変更したので、下のような感じ(例です)。

f:id:ryotake0404:20180826182001p:plain

 

<Code>

Codeは下の通り。

 

 実際にrunすると、下のように表示されます。下のときは最終的な精度として71.4%でしたが、10回おこなった結果、60%~83%まで変動しました。平均はちょうど70.0%で最頻は67.9%でした。

f:id:ryotake0404:20180826182642p:plain

結果をみると、マスクをかけた白黒の画像から70%の精度で判断ができるのは意外ですね、とてもおもしろい結果となりました。

 

<改善点>

改善点をあげるとすれば、データ量、画像の切り取り方、雲認識のためのマスクの数値などをいじることだと思います。また、ここから1日単位ではなく、数分単位で観察して予測につなげるためには、夜の画像を用いなければならないので肉眼で認識するこの方法は使えないです。赤外画像を用いればなんとかなるのかなとは思いますが、future workとし、試してはいないです。

 

<おまけ>

完全におまけですが、下記のサイトを参考に気象庁のデータ20年分をもちいてロジスティック回帰を行うと、85%を超える精度がでました。やはり雲から判断するよりも10~20%くらい高い精度ですね。ちなみにデータは下のように調整しております。

 

f:id:ryotake0404:20180826184247p:plain

特徴量は画像の1行目のとおりです。気象庁の②と同じサイトから、1998/1/1~2017/12/31のデータをダウンロードしております。

そして、表示された結果はこんな感じです。

f:id:ryotake0404:20180826185434p:plain

 

 

※ codeの説明はcode内に書いてあります。流れとしては、データを読み取り、降水量からlabelを作成。そして欠損やいらない項を削除し、train と test を8:2で分けた後、sklearn.linear_modelのLogisticRegressionを用いて回帰を行い、最後にcross validationを行っています。

 

<最後に>

ありがとうございました、自分としては、70%の精度でいろいろ改善点があるものの、おもしろいことができたんじゃないかなと思います。

機械学習で仮想通貨を予測してみた!

f:id:masaruyagi:20180824234754j:plain

こんにちは、Aidemy研修生の八木です。

皆さんは楽してお金が欲しいと思ったことはないでしょうか?

もし仮想通貨の未来の価格が予想できたら一生遊んで暮らせそうですよね。

そこで、今回はAidemyで学んだ時系列解析を生かし、仮想通貨の予測に挑戦してみました!

また、データの加工でどのように予測が変わるか検証してみました。

 

対象者

  • 株、FX、仮想通貨などに興味がある方
  • それらを機械学習で予測してみたい方

概要

BCHのチャートデータをPoloniexのAPIから取得。

kerasのLSTMモデルを構築し、BCHの回帰予測を行う。

その結果から次の時間の騰落予測し、正答確率を算出する。

 

目次

 

python構築環境

  • MacBook Air (プロセッサ:  2.2 GHz Intel Core i7)
  • Python3.6.0
  • Jupyter4.4.0 

また、以下のモジュールを使用しました。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import poloniex
import time

#TensorFlow
import tensorflow as tf

#Keras
from __future__ import print_function
from keras.layers.core import Activation
from keras.layers.core import Dense
from keras.layers.core import Dropout
from keras.models import Sequential
from keras.optimizers import Adam

#LSTM
from keras.layers.recurrent import LSTM
from keras.callbacks import EarlyStopping

データの取得

まず、通貨価格の価格データを取得するのですが、取引所であるPoloniexがAPIを公開しているためそこからデータを取得することができます。

また、それをPython でさらに使いやすくしたpython-poloniexがGitHubで公開されていて、今回はそれを利用しました。

python-poloniexの導入は以下のようにできます。

  1. GitHubのサイトからzipファイルをダウンロード 

    github.com

  2. ダウンロードしたzipを解凍し、ファイル内で以下を実行
    python setup.py install

これでpython-poloniexを使うことができます。

 

では実際にpoloniexを使いデータを取得して行きます。

今回は、BCH(ビットコインキャッシュ)の4時間おきの価格を500日分取得しました。

BCHを選んだ理由は昔買ったことがあるからです(笑)

データの日数は実際にチャートを見て適当な範囲で決定しました。

polo = poloniex.Poloniex()

chart_data = polo.returnChartData('USDT_BCH', period= 7200, start=time.time()-polo.DAY*500, end=time.time())  #データ取得
df = pd.DataFrame(chart_data)  #dataframe化

polo.returnCharatDataには、通貨ペア、periodに取得間隔、startに開始時刻、endに終了時刻を指定します。

また、polo.DAYで1日分を指定できます。

取得したデータはpandasのdataframeに変換して扱います。

 

では次に、取得したデータをプロットして正しく取得できているかを確認してみます。 

data = df["close"].astype("float32")

#グラフ設定
rcParams['figure.figsize'] = 15, 6
plt.title("BCH_Chart")
plt.xlabel("date")
plt.ylabel("price")
plt.grid(True)

#プロット
plt.plot(data)
plt.show()

f:id:masaruyagi:20180821132724p:plain

正しくプロットすることができました!

 

データの加工

次にデータを加工し学習しやすいようにしていきます。

時系列データでは移動平均を求めることによってグラフ滑らかにすることがよく行われます。

今回は4時間おきでデータを集めたため、6つ分のデータで平均を取り1日幅での移動平均を求めました。

#移動平均
data1 = df["close"].rolling(6).mean() 
data1 = data1.fillna(data1[5])

#プロット
plt.plot(data)
plt.plot(data1)

plt.grid(True)
plt.show()

6つ毎のデータで移動平均を取ると初めの5つの値が空値になってしまうため、最も近い値で埋めることでデータの長さが変わらないようにしています。

f:id:masaruyagi:20180823191136p:plain

このように多少滑らかな線に変わりました。

 

では次にこのデータから入力変数と出力変数を取り出し訓練データとテストデータに分割するところまで行います。

今回は変換するデータを変えながら検証を行なったため関数化しておきました。

#データから変数とラベルを生成し、訓練データとテストデータに分割する
def data_split(data, v_size=30, train_split_latio=0.7):
    data = data.astype("float32")    #データをfloat32型に変換
    x, t = [], []
    data_len = len(data)    #総データ数
    
    #変数とラベルの生成
    for i in range(data_len - v_size):
        x_valu = data[i : i+v_size]    #連続したmax_len個の値
        t_valu = data[i+v_size]    #x_valuの次の値
        
        x.append(x_valu)    #入力変数ベクトル
        t.append(t_valu)    #出力変数ベクトル

    #ndarray型に変換し形を直す
    x = np.array(x).reshape(data_len-v_size, v_size, 1)
    t = np.array(t).reshape(data_len-v_size, 1)

    #訓練データとテストデータに分割
    border = int(data_len * train_split_latio)    #分割境界値
    x_train, x_test = x[: border], x[border :]    #訓練データ
    t_train, t_test = t[: border], t[border :]     #テストデータ
    
    return x_train, x_test, t_train, t_test

入力変数は与えられたサイズの幅で1つずつずらしながら取集していき、出力変数はその直後の値としています。

また、今回学習に使うLSTMモデル用に、入力変数は3次元ベクトル、出力変数が2次元ベクトルに型を直しています。

訓練データとテストデータの分割は、与えられた割合で前半のデータを訓練用、後半のデータをテスト用に分けています。

モデル定義

今回の学習ではKerasのLSTMモデルを採用しました。

時系列データのような、前のデータが次のデータに影響を与えてしまうデータは通常の学習モデルではうまく学習ができません。

そのため、時系列データの解析ではそういった場合にも対応して学習ができるRNN(リカレントネットワーク)が採用されることが多いです。

近年ではその中でもLSTMモデルがよく使われているため、今回はそれを使って学習を行います。

#LSTMモデルの生成
def create_LSTM(v_size, in_size, out_size, hidden_size):
    tf.set_random_seed = (20180822)
    model = Sequential()
    model.add(LSTM(hidden_size, batch_input_shape = (None, v_size, in_size), 
                   recurrent_dropout = 0.5))
    model.add(Dense(out_size))
    model.add(Activation("linear"))
    
    return model

このモデルは入力データ幅、入力数、出力数、隠れ層の数を指定することで使うことができます。

今回は回帰予測を行うため、活性化関数は'linear'を指定しています。

パラメータの定義

モデルも定義したし早速学習に移りたいのですが、その前にパラメータをまとめて定義しておきましょう。

この部分をいじることで後の学習精度に大きく影響します。

#各パラメータの定義
now_data = data           #扱う元データ
v_size = 30               #入力データ幅
train_split_ratio = 0.7   #訓練データとテストデータの分割割合
x_train, x_test, t_train, t_test= data_split(now_data, v_size, train_split_ratio)  #データの分割

mean = np.mean(x_train)           #平均値の保存
std = np.std(x_train)             #標準偏差の保存
x_train = (x_train - mean) / std  #標準化
x_test = (x_test - mean) / std

tmean = np.mean(t_train)
tstd = np.std(t_train)
t_train = (t_train - tmean) / tstd #出力変数も同じように標準化
t_test = (t_test - tmean) / tstd

in_size = x_train.shape[2]   #入力数
out_size = t_train.shape[1]  #出力数
hidden_size = 300            #隠れ層の数
epochs = 100                 #エポック数
batch_size = 30              #バッチサイズ

見てわかる通り訓練データとテストデータの標準化をこの部分で行なっています。

標準化とはデータを平均が0、分散を1にすることで、値のサイズを小さくし扱いやすくします。

標準化により学習精度も上がります。

データの学習と予測

では学習を始めましょう!

# 学習
early_stopping = EarlyStopping(patience=10)  #ストップカウント                                                              
model = create_LSTM(v_size, in_size, out_size, hidden_size)  #インスタンス生成
model.compile(loss="mean_squared_error", optimizer = Adam(0.0001))  #損失関数定義
model.fit(x_train, t_train, batch_size = batch_size, epochs = epochs, shuffle = True, callbacks = [early_stopping], validation_split = 0.1)  #学習

EarlyStoppingを使うことで、損失が減少しなくなってから一定カウント進んだとことで学習をストップできます。

これにより過学習を防ぐことができます。

今回は回帰問題を扱うため"mean_squared_error"と"Adom"を使用しました。

では早速学習したデータで予測し、プロットしてみましょう!

#予測
pred_train = model.predict(x_train)     #訓練データ予測
pred_train = pred_train * tstd + tmean  #標準化したデータを戻す

a = np.zeros((v_size, 1))
b = pred_train
pred_train = np.vstack((a, b))   #データ長を合わせるため0ベクトルと結合

#プロット
plt.figure()
plt.plot(pred_train, color="r", label="predict")
plt.plot(now_data, color="b", label="real")
plt.grid(True)
plt.legend()
plt.show()


pred_test = model.predict(x_test)     #テストデータ予測
pred_test = pred_test * tstd + tmean  #標準化したデータを戻す

a = np.zeros((v_size + x_train.shape[0], 1))
b = pred_test
pred_test = np.vstack((a, b))   #データ長を合わせるため0ベクトルと結合

#プロット
plt.figure()
plt.plot(pred_test, color="r", label="predict")
plt.plot(now_data, color="b", label="real")
plt.grid(True)
plt.legend()
plt.show()

f:id:masaruyagi:20180823140129p:plain

f:id:masaruyagi:20180823140216p:plain

上が訓練データに対する回帰、下がテストデータに対する回帰となっています。

実測値線に沿った予測値線が引けたのではないでしょうか!

結果解釈

しかし、よく見てみると実測値線と少しずれて予測値線が構成されているようにも見えます。

つまり、今回は30サンプル取って学習しているわけですが、その中の直前の値を参考に次の値の予測を行なっているということです。

実は、仮想通貨や株のチャートはランダムウォークといい、値が上下する確率はほぼ五分五分になっていることが知られています。

そのため、予測をする直前の値の依存性が高くなり、予測値線が実測値線から少しずれたようになっています。

試しに次の値の騰落(値が上がったか下がったか)の予測が当たっている確率を出してみたいと思います。

#trainデータ騰落正答確率測定
data_score = 0
for i in range(pred_train.shape[0] - v_size - 1):
    if pred_train[i + v_size + 1] - pred_train[i + v_size] > 0:
        if data[i + v_size + 1]-data[i + v_size] > 0:
            data_score += 1
    if pred_train[i + v_size + 1] - pred_train[i + v_size] < 0:
        if data[i + v_size + 1] - data[i + v_size] < 0:
            data_score += 1
print("train score: {}".format(data_score / (pred_train.shape[0] - v_size - 1)))

#testデータ騰落正答確率測定
N = x_train.shape[0] + v_size
data_score = 0
for i in range(pred_test.shape[0] - N - 1):
    if pred_test[i + N + 1] - pred_test[i + N] > 0:
        if data[i + N + 1] - data[i + N] > 0:
            data_score += 1
    if pred_test[i + N + 1] - pred_test[i + N] < 0:
        if data[i + N + 1] - data[i + N] < 0:
            data_score += 1
print("test score: {}".format(data_score / (pred_test.shape[0] - N - 1)))

f:id:masaruyagi:20180823160729p:plain

騰落予測結果はランダムウォークの性質通り50%付近になってしまいました。

しかも50%を下回っています。

やはり仮想通貨の騰落を予測するのは難しい......

使用データを変えて再学習

実測値で学習をした場合では正答確率46%という結果になってしまいました。

では、はじめに作った1日幅で移動平均をとったデータで学習した場合はどうでしょうか?

データが滑らかになった分、精度が上がるかもしれません。

学習データを変えるには、パラメータ定義の"now_data"の部分を変更するだけです。

結果をプロットして見ましょう。

 

f:id:masaruyagi:20180823155420p:plain

f:id:masaruyagi:20180823155432p:plain

どうでしょうか。

さっきよりも実測値線に近くプロットされているように見えます。

では騰落予測の正答確率も出して見ます。

ソースコードは先ほどと同じものを使用しました。

f:id:masaruyagi:20180823161815p:plain

先ほどよりも精度をあげることができました!

しかし結果としてはやはり五分五分というところです。

 

まとめ

今回はBCHの500日分のデータを使い回帰予測と騰落予測を行なってみました!

回帰に関しては、おおよそ実測値線に沿った予測値線を引くことができましたが、騰落予測は正答確率約50%というところです。

初めは正答確率50%を切っていましたが、移動平均と標準化により越えることができました。

しかし高い確率とは言えないため、綺麗に引けた予測線も、結局は直前の値に大きく影響を受けた線と言えそうですね......

やはり、前の値と依存関係のある時系列データの解析に使えるLSTMモデルでも、ランダムウォークである仮想通貨の予測は難しかったようです。

理由として、仮想通貨はその通貨の価格データだけでなく、他の通貨の情報や世界情勢など多くの要因で変動してしまうことが挙げられます。

また株などのデータと比べ、激しく価格が上下することも解析を難しくしていると考えられます。

ただこのモデルを使えば、どんなに投資が下手な人でも50%の確率では騰落を当てられるのではないでしょうか(笑)