🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

深層学習

深層学習は、多数のレイヤのニューラルネットワークによる機械学習手法。人工知能研究の一つでディープラーニングとも呼ばれています。コンピューター自体がデータの潜在的な特徴を汲み取り、効率的で的確な判断を実現することができます。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1919閲覧

kerasを用いて時系列予測を行いたい

stu824

総合スコア2

Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

深層学習

深層学習は、多数のレイヤのニューラルネットワークによる機械学習手法。人工知能研究の一つでディープラーニングとも呼ばれています。コンピューター自体がデータの潜在的な特徴を汲み取り、効率的で的確な判断を実現することができます。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2020/10/21 23:14

編集2020/10/24 07:36

前提・実現したいこと

kerasのlstmを用いて,将来の交通量予測を行うモデルを構築しているのですが,
作成したモデルが意図した予測を行っているかがわかりません。
モデルでは,30分先の交通量を予測するようにしているのですが,
問題点として,
(1)明らかに精度が高い(本当に意図した時間先を予測しているのか?)
(2)予測したい時間を変えたとき明らかに不自然な予測結果が得られる
特に12時間先のデータを用いて予測した際は予測結果(下記図2枚目)が不自然になり,
1日先のデータでの予測結果は(下記図3枚目)逆に精度が高すぎるといった結果になりました。
私の作成したプログラムが正しく意図した将来を予測できているのか判断がつきません。

追記
下記図に関して,
青線が予測値
オレンジ線が観測値
になります。
また,ソースコードにおける「pred_time」が予測したい時間先になっており,
pred_time=30で,正解データが30分先の値であることを意味しています。
この意味として,ある1時点のデータを学習データ,その時点から30分後の時点のデータを正解データとして
損失関数を小さくするような学習をしてほしいと考えています。

再追記
使用している交通量のデータは,
100日間1分間隔(144000行)で記録された時系列のデータになります。

該当のソースコード

Python

1#1変数のみを入力 2 3import os 4import numpy as np 5import pandas as pd 6import matplotlib.pyplot as plt 7 8import_file = 'C:/Users/temp/Desktop/week_day1.csv' #データファイルの読み込み 9 10#事前の決定 11test_rate = 0.2 #データの分割割合 12pred_time = 720 #何分先を予測するかの決定(データ:1分間隔/1時間先を予測:pred_time = 60) 13time = 1440 14 15#データのインポート 16df = pd.read_csv(import_file) 17 18#必要なデータの抽出 19date = df.iloc[:,0].values #データの1列目を抽出 20Pt = df.iloc[:, 3:4].values #データの4列目を抽出 21 22 23#データの分割 24from sklearn.model_selection import train_test_split 25date_1, date_2 = train_test_split(date, test_size=test_rate, shuffle=False) 26train, test = train_test_split(Pt,test_size=test_rate,shuffle=False) 27 28 29#教師ありデータに変換 30x_train = train[:-pred_time] #データの下からpred_time分を削って学習データに 31t_train = train[pred_time:] #データの上からpred_time分を削って正解データに 32x_test = test[:-pred_time] #データの下からpred_time分を削ってテストデータに 33t_test = test[pred_time:] #データの上からpred_time分を削って正解データに 34 35 36 37#2次元配列へ変換 38x_train = x_train.reshape(-1, 1) 39t_train = t_train.reshape(-1, 1) 40x_test = x_test .reshape(-1, 1) 41t_test = t_test .reshape(-1, 1) 42 43 44#0-1への正規化の定義 45from sklearn.preprocessing import MinMaxScaler 46 47def scale(x_train, x_test , t_train, t_test ): 48 49 # change type 50 x_train = x_train.astype(np.float32) 51 x_test = x_test .astype(np.float32) 52 t_train = t_train.astype(np.float32) 53 t_test = t_test .astype(np.float32) 54 55 # scale inputs 56 sclr = MinMaxScaler() 57 x_train = sclr.fit_transform(x_train) 58 x_test = sclr.transform(x_test ) 59 t_train = sclr.transform(t_train) 60 t_test = sclr.transform(t_test ) 61 62 return x_train, x_test , t_train, t_test , sclr 63 64#正規化 65x_train, x_test , t_train, t_test , sclr = scale(x_train, x_test , t_train, t_test ) 66 67#成形(change shape) 68x_train = np.reshape(x_train.astype("float32"), (x_train.shape[0],1,x_train.shape[1] )) 69x_test = np.reshape(x_test .astype("float32"), (x_test .shape[0],1,x_test .shape[1] )) 70 71 72#各種インポート 73import keras 74from keras.models import Sequential, Model 75from keras.layers import Input, Dense, Activation, LSTM 76from keras.callbacks import EarlyStopping 77import math 78from sklearn.metrics import mean_squared_error, mean_absolute_error 79 80 81#モデルの定義 82Inputs = Input(shape = (x_train.shape[1], x_train.shape[2]), dtype='float32') 83lstm = LSTM(128)(Inputs) 84Outputs = Dense(1, activation='linear')(lstm) 85 86model = Model(inputs=[Inputs], outputs=[Outputs]) 87model.compile(loss='mean_squared_error', optimizer='adam', metrics=['mean_squared_error']) 88 89es = EarlyStopping(monitor='val_loss', 90 patience=5, 91 verbose=1) 92 93#モデルの学習 94history = model.fit(x_train, t_train, epochs=10, batch_size=256, verbose=1, shuffle=False, validation_split = 0.1, callbacks=[es]) 95 96 97#予測 98pred = model.predict(x_test ) #予測値 99obs = t_test #観測値 100 101 102#正規化を戻す 103obs_unscale=sclr.inverse_transform(obs) 104pred_unscale=sclr.inverse_transform(pred) 105 106 107#モデルの評価指標 108RMSE = math.sqrt(mean_squared_error(pred_unscale,obs_unscale)) 109ape = mean_absolute_error(pred_unscale, obs_unscale) 110MAPE =ape * 100/len(obs_unscale) 111print("RMSE=",RMSE) 112print("MAPE=",MAPE) 113 114 115#配列 → DataFrame 116dfd = pd.DataFrame(date_2) #日付 117dfp = pd.DataFrame(pred_unscale) #予測値 118dfo = pd.DataFrame(obs_unscale) #観測値 119 120 121#最初の1日を削除 122dfd = dfd[int(time):] 123dfp = dfp[int(time):] 124dfo = dfo[int(time - pred_time):] 125 126 127#predとobsのDataFrameを一つにまとめる 128df = pd.concat([dfd, dfp, dfo], axis=1) #axis=1で横方向に連結 129 130 131#DataFrameの行名の変更 132df.columns = ['date_time', 'Pt_pred', 'Pt_obs'] 133 134 135#csvに書き出し 136df.to_csv('C:/Users/temp/Desktop/5_30_output.csv')

30分後を予測
半日後を予測
1日後を予測

試したこと

ハイパーパラメータの調整,スケーリングの調整,予測先の変更等を試してみましたが,うまくいきません。
コード自体が見当はずれなのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

予測以降の処理において、入力に対する正解の時間差pred_timeの扱いに間違っている部分があるようです。

以下のコードにおいて、predは予測値ですので、x_testをもとにpred_time後の予測となっています。
また、t_testはもともとpred_time後の正解値です。よって、predとobsの時間差は既に無い状態です。

Python

1pred = model.predict(x_test ) #予測値 2obs = t_test

にもかかわらず、以下のコードにおいてobsだけをpred_timeずらしています。この行為は意味がなく、これが結果に影響していると考えられます。

Python

1#最初の1日を削除 2dfd = dfd[int(time):] 3dfp = dfp[int(time):] 4dfo = dfo[int(time - pred_time):]

ただし、この後にconcatしていることで上記の「ずらし」が複雑に作用します。Pandasでは明示的にindex振り直しを指示しない限り、元のindexを維持する形で処理されます。よってconcatの時点で「ずらし」は補正されてしまいます。結局「ずらし」は、頭に余計にNanを作る効果しかもたらせていません。

擬似的な交通量を生成して、質問者様のコードを実行した結果、concat後のデータフレームは以下の形でした。(date_timeは日を単位とするfloatにしています)

date_time Pt_pred Pt_obs 720 NaN NaN 4211.390625 721 NaN NaN 4310.817383 722 NaN NaN 4800.063477 723 NaN NaN 3912.796387 724 NaN NaN 5271.840820 ... ... ... ... 28795 99.996528 NaN NaN 28796 99.997222 NaN NaN 28797 99.997917 NaN NaN 28798 99.998611 NaN NaN 28799 99.999306 NaN NaN

上記にもかかわらず、質問者様の半日後の結果がズレてみえるのは、csv保存以降の処理で上記の、Pt_predとPt_obsの頭のズレが影響しているように思われます。この点はコードが明示されていないので推測ですが、少なくとも意味の無い「ズラし」が入っているため、これが原因の可能性が高いです。

実際、擬似データをもとに、質問者様のコードに

Python

1plt.plot(df['date_time'], df[['Pt_pred', 'Pt_obs']]) 2plt.show()

を付け加えると、半日後であっても、それなりに予測〜観測が一致したグラフが出ます。

なお、本来あるべきコードは以下です。

Python

1#最初の1日を削除 2dfd = dfd[int(time + pred_time):].reset_index(drop=True) 3dfp = dfp[int(time):].reset_index(drop=True) 4dfo = dfo[int(time):].reset_index(drop=True) 5 6#predとobsのDataFrameを一つにまとめる 7df = pd.concat([dfd, dfp, dfo], axis=1).dropna()

「ズラし」はdfdのところだけを後ろに、dfpとdfoは余計にズラしません。またズラしが無効にならないようにreset_indexでindexを振りなおします。concatでまとめる時に、nanがある行は捨てるほうがよいです。

なお、全体の結果を擬似データで確認すると、30分後の予測、1日後の予測は観測との差が少なく、半日後の予測はそれなりではあるものの観測との差はやや大きい結果になるでしょう。これは当たり前のことで、LSTMはRNNよりは改善されていますが、それでも、昔よりは最近のことにより強く影響されるモデルだからです。1日単位の周期性があるデータは、半日後よりも1日後の方が「今」と類似しています。そのため、半日後予測よりも1日後予測の方が、正確に予測しているようにみえるわけです。

100日もデータがあるわけですので、単純なLSTMを適用するのではなく、モデルを工夫してチューニングしてみることをオススメします。個人的には、周期性を考えて、30分後、1日後、5日後(五十日?の周期)、1週間後(平日週末の周期)、などをアンサンブルしてみるとよいかと思います。

以下補足として、私が擬似データを作成するために使ったコードを示します。

Python

1import numpy as np 2from scipy.interpolate import interp1d 3 4# 擬似的にdays日分の1分間隔の交通量を生成 5days = 100 6# まずは1日分作成、1日4つの時点のみ、質問者様のグラフを見て目分量でプロット 7linear_interp = interp1d([0, 4, 7, 15, 24], [5000, 3000, 13000, 14000, 5000]) 8# 上記を直線で補間して1分間隔のデータにする 9linear_results = linear_interp(np.linspace(0, 24, 24*60))[:-1] 10# それを単純結合してdays日分にする 11linear_results_days = np.array(linear_results.tolist() * days) 12# ノイズを加える 13noise = np.random.normal(0, 500, linear_results_days.shape) 14Pt = linear_results_days + noise 15# dateは1日を1.0のスケールにする 16date = np.linspace(0, days, 24*60*days)[:-1]

投稿2020/10/22 00:20

編集2020/10/24 12:40
toast-uz

総合スコア3266

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

stu824

2020/10/22 00:59

回答ありがとうございます。バリデーションに使用するデータと予測に使用するデータをきちんと区別してみましたが,大きな変化はありませんでした。
toast-uz

2020/10/22 10:10

質問文を、修正後のコードや結果にしていただけますでしょうか。それをベースに確認したいです。
stu824

2020/10/23 02:25 編集

返答が遅くなってしまい申し訳ありません。 質問文を修正後のコードに変更いたしました。 確認していただけると幸いです。
toast-uz

2020/10/24 04:43 編集

図の作り方の問題のような気がしますが、提示された情報からでは判断できません。各図ともに赤線が予測線で青線が観測線でしょうか(この説明も無いですので書いて欲しいです)。予測線が3つの図で全て同じに見えることから、予測結果をちゃんと図に描けていないのが問題であるように思います。1日後をうまく予測しているのではなく、1日前の予測線モドキ(ちゃんと描けていない)が、1日後の観測線とタマタマ似ているだけに思います。3つの図とソースコードの関係を質問文に記載ください。ソースコードは1つの図にしか対応していないように思いますので、上記が判断できません。 また、そもそもLSTMの予測図とは、私の認識では、ある(予測を導き出す)時点を1つ決めて、それ以前の観測データから、それ以降の予測と観測を比較する形で作ると思います。https://qiita.com/yuyakato/items/ab38064ca215e8750865 (RNNですが)の最後の図のイメージです。 まずはこの図を作って判断したほうがよいのではないでしょうか。提示された図は別の切り口(予測を導き出す時点そのものをスライドさせようとしている?)なのでわかりにくいです。
stu824

2020/10/24 05:10

回答いただきありがとうございます。 情報として足りない部分を追記いたしました。 おっしゃる通り図の作成で至らない部分があったと思いますので取り組んでみます。
toast-uz

2020/10/24 05:30

確認しました。 データの後ろからpred_timeだけを削って学習データ(バリデーション含む)にしているのに、pred_time先の予測値を24時間分予測している、のは変ではないでしょうか?pred_time+24時間分を削らないと、予測する部分を学習データに使ってしまっています。 できれば、変数名を直して欲しいです。testとvalが紛らわしい。。。
toast-uz

2020/10/24 05:41

あ、すみません。学習とテストは、test_rateで分割していましたね。 #教師ありデータに変換 のところのコメントで混乱しました。
stu824

2020/10/24 05:48

変数の名前の定義は確かにややこしかったと思います。 申し訳ありません。
toast-uz

2020/10/24 09:24

最後の方のpandasのconcatのところで、その前で頭を削っていますがdfoだけ削り幅変えていますね。pandasだとindexを持っているので、せっかく削り幅を変えても、元のindex同士が連結されることになります。削り幅変えた後が揃うわけではありません。その点はご理解されていますか?
toast-uz

2020/10/24 11:51

csvをどう表示されているのか不明なので一部推測ですが、ほぼこれで当たりだろうという回答が作れました。ご確認ください。
stu824

2020/10/24 12:38

こちらの質問が至らない部分が多い中とても丁寧な対応をしていただきありがとうございました。大変助かりました。 回答いただいた内容確認してみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問