こんにちは。システム開発部の諸です。 突然ですが、世界で一番イノベーションを起こしている企業といったら、みなさんはどの会社を思い浮かべるでしょうか?
私はGoogleだと思っています。(タダのGoogle好き) そのGoogleの社内では、イノベーションを起こすために日本では考えられない奇抜なルールがいくつか存在します。 その中の一つに「20%ルール」というものがあります。
ざっくり言えば、普段の業務のうち「80%を普段の業務、20%を自由な発想(イノベーション)に割り当てよう!」というルールです。 これは人事評価の対象になり、あのGmail や AdSenseなどもこのルールから生まれたそうです。
日本では考えられない話ですね~。 そういうワケで今回のサブタイのマネックスで「ひとり20%ルールやってみた」をやってみました。
当然、普段の業務で20%も使えないので、業務の空き時間を使いKerasでそっくりな株式チャート取得してみました。
環境・ライブラリ
・Windows 7
・Anaconda3-2019.03-Windows-x86_64.exe
・tensorflow
・keras
Anacondaは下記のサイトからダウンロードできます。
Anaconda | Individual Edition
Keras
ケラス?カラス?なんじゃそれ?
という方は、下記のサイトがわかりやすく解説してくれています。
Kerasでそっくりな株価チャート取得してみた
もしこんなテクニカル分析投資家がいたとしましょう。 「○○株式会社の株価チャートとそっくりなチャートの会社を知りたい!」
色々ググりながら、やっつけで作ってみました。
今回、使用したアルゴリズムはCNN(Convolutional Neural Network)です。
活性化関数には畳み込みの際にはRelu関数を使用し、最終的なクラス分類の際には分類でよく使われるSoftmax関数を使用しました。
ソース全文
#!/usr/bin/env python # coding: utf-8 import os import glob import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt from keras.preprocessing.image import load_img, img_to_array, array_to_img from keras.preprocessing.image import random_rotation, random_shift, random_zoom from keras.layers.convolutional import Conv2D from keras.layers.pooling import MaxPooling2D from keras.layers.core import Activation from keras.layers.core import Dense from keras.layers.core import Dropout from keras.layers.core import Flatten from keras.models import Sequential from keras.models import model_from_json from keras.callbacks import LearningRateScheduler from keras.callbacks import ModelCheckpoint from keras.optimizers import Adam from keras.utils import np_utils PATH_DELIMITER = os.sep # パス区切り文字 ALF_MAX_NUM = 26 # アルファベットA-Zの個数 ALF_BEGIN_NUM = 65 # ASCIIコードのアルファベットの開始値 company_name = "" # 会社名 NPY_EXTENSION_NAME = ".npy" DIR_PATH = "teach" LABEL_NAME = ["reverse_u", "right_down", "right_up", "v"] # ラベル付けの種類 IMAGE_HEIGHT = 32 IMAGE_WIDTH = 32 # ランダムウォークしたチャートを生成 def out_random_chart(loop, dir="teach") : count = 0 # アルファベットのカウント for i in range(loop) : if count < ALF_MAX_NUM : company_name = chr(ALF_BEGIN_NUM + count) else : company_name = chr(ALF_BEGIN_NUM + (count // ALF_MAX_NUM - 1)) + chr(ALF_BEGIN_NUM + count % ALF_MAX_NUM) count += 1 if not os.path.isdir(dir) : os.mkdir(dir) plt.figure(figsize=(16,16)) step = np.random.choice([-1,1],500) arr = np.cumsum(step) plt.plot(arr) plt.show() plt.savefig(dir + PATH_DELIMITER + company_name) # 前処理 def preprocess(var_amount=4) : for dir in LABEL_NAME : num = 0 arrlist = [] files = glob.glob(DIR_PATH + PATH_DELIMITER + dir + "/*.png") for imgfile in files: img = load_img(imgfile, target_size=(IMAGE_HEIGHT, IMAGE_WIDTH)) # 画像ファイルの読み込み array = img_to_array(img) / 255 # 画像ファイルのnumpy化 arrlist.append(array) # numpy型データをリストに追加 for i in range(var_amount-1): arr2 = array arr2 = random_rotation(arr2, rg=90) arrlist.append(arr2) # numpy型データをリストに追加 num += 1 nplist = np.array(arrlist) np.save(dir + NPY_EXTENSION_NAME, nplist) print(">> " + dir + "から" + str(num) + "個のファイル読み込み成功") # 学習 def lerning(tsnum=30, nb_epoch=50, batch_size=8, learn_schedule=0.9): X_TRAIN_list = []; Y_TRAIN_list = []; X_TEST_list = []; Y_TEST_list = []; target = 0 for filename in LABEL_NAME : data = np.load(filename + NPY_EXTENSION_NAME) trnum = data.shape[0] - tsnum X_TRAIN_list += [data[i] for i in range(trnum)] Y_TRAIN_list += [target] * trnum X_TEST_list += [data[i] for i in range(trnum, trnum+tsnum)] Y_TEST_list += [target] * tsnum; target += 1 X_TRAIN = np.array(X_TRAIN_list + X_TEST_list) # 連結 Y_TRAIN = np.array(Y_TRAIN_list + Y_TEST_list) # 連結 print(">> 学習サンプル数 : ", X_TRAIN.shape) y_train = np_utils.to_categorical(Y_TRAIN, target) # 自然数をベクトルに変換 valrate = tsnum * target * 1.0 / X_TRAIN.shape[0] # 学習率の変更 class Schedule(object): def __init__(self, init=0.001): # 初期値定義 self.init = init def __call__(self, epoch): # 現在値計算 lr = self.init for i in range(1, epoch+1): lr *= learn_schedule return lr def get_schedule_func(init): return Schedule(init) lrs = LearningRateScheduler(get_schedule_func(0.001)) mcp = ModelCheckpoint(filepath='best.hdf5', monitor='val_loss', verbose=1, save_best_only=True, mode='auto') # create model ipshape=(X_TRAIN.shape[1], X_TRAIN.shape[2], X_TRAIN.shape[3]) num_classes=target model = Sequential() model.add(Conv2D(24, 3, padding='same', input_shape=ipshape)) model.add(Activation('relu')) model.add(Conv2D(48, 3)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.5)) model.add(Conv2D(96, 3, padding='same')) model.add(Activation('relu')) model.add(Conv2D(96, 3)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.5)) model.add(Flatten()) model.add(Dense(128)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(num_classes)) model.add(Activation('softmax')) adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08) model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy']) print(">> 学習開始") hist = model.fit(X_TRAIN, y_train, batch_size=batch_size, verbose=1, epochs=nb_epoch, validation_split=valrate, callbacks=[lrs, mcp]) json_string = model.to_json() json_string += '##########' + str(LABEL_NAME) open('model.json', 'w').write(json_string) model.save_weights('last.hdf5') # TEST def test_process(imgname): modelname_text = open("model.json").read() json_strings = modelname_text.split('##########') textlist = json_strings[1].replace("[", "").replace("]", "").replace("\'", "").split() model = model_from_json(json_strings[0]) model.load_weights("last.hdf5") # best.hdf5 で損失最小のパラメータを使用 img = load_img(imgname, target_size=(IMAGE_HEIGHT, IMAGE_WIDTH)) TEST = img_to_array(img) / 255 pred = model.predict(np.array([TEST]), batch_size=1, verbose=0) return str(pred), textlist[np.argmax(pred)].replace(",", "")
ランダムウォークしたチャートを作成
実際の株式市場のチャートを使用するのはやっぱり面倒くさかったので、ランダムウォークな株価チャートを作って架空の会社のチャートを作ってみました。
Pythonをインタラクティブ・モードで実行して以下を打ちましょう。
それぞれteachフォルダに教師データを100枚、testフォルダにテストデータを50枚用意しました。
100枚!
50枚!
今回の分類は以下の4パターンに分けて分類してみました。
チャートの見た目が「逆U字型、 右下がり型、右上がり型、V字型」のものです。
ラベル付は手作業でやりました。
誰か自動化してくれー。
前処理開始!
無事終わりました!
学習
学習できました。
テスト!
今回は以下の画像とそっくりな画像を取ってこれるかテストします。
以下のソースコードで実行しました。
#!/usr/bin/env python # coding: utf-8 import os import os.path as op import glob import keras_chart_cnn as kcc PATH_DELIMITER = os.sep # パス区切り文字 img_class_list = [] same_class_list = [] if __name__ == "__main__" : test_img_list = glob.glob("." + PATH_DELIMITER + "test" + PATH_DELIMITER + "*.png") for test_img in test_img_list : ret = kcc.test_process(test_img) ret += (test_img,) img_class_list.append(ret) print("===============================================================================================================") print(ret) print("===============================================================================================================") tgt_img_path = input("\n>> input : ") while (True) : if op.isfile(tgt_img_path) : break else : print(">> そのファイルは存在しません!") tgt_img_path = input("\n>> input : ") tgt_ret = kcc.test_process(tgt_img_path) for img_class in img_class_list : class_name = img_class[1] class_file_path = img_class[2] tgt_class_name = tgt_ret[1] if class_name == tgt_class_name : if op.basename(class_file_path) != op.basename(tgt_img_path) : same_class_list.append(class_file_path) print("**********************************************************************************************************") for same_file in same_class_list : print(same_file) print("**********************************************************************************************************")
結果発表~
以下のような結果が出ました。
"*"で囲まれた画像のパスがそっくりなチャートだとAI君は言っています。
実際の画像を見てみましょう。
う~ん。似ているのか?
けど、全く違うものは選ばれてないと思う!
微妙な感じになってしまった…
感想
①教師データの数やラベル付の種類を増やしたらもっと精度はあがるはず!
②イノベーションが起きたかはわからない。
以上、ありがとうございました。