TensorFlow入門(7)

TensorFlow入門(7)

時系列データの予測を行う深層学習(RNN)を作成してみよう(TensorFlow編)

Tutor 2018年4月26日

ディープラーニングの代表的手法「RNN」により時系列データの予測を行う機械学習モデルを構築してみる。RNNによる深層学習がどのようなものか体験しよう。

安部晃生 DATUM STUDIO株式会社 安部 晃生

 前回はRNNの概要を説明した。今回のベースとなる知識なので、本稿で作業を始める前に一読してほしい。今回はRNNを使って深層学習を試してみる。本稿のPythonコードは、Jupyter Notebook上で実行すればよい。

RNNの学習

モデル

 今回は、tf.nn.rnn_cell.BasicRNNCellクラスを用いて、単純なRNNのモデルを作成する。

 tf.nn.rnn_cell.BasicRNNCellクラスは、セルの内部構造が単層で全結合のニューラルネットワークを表すクラスである。インスタンス化の際にニューロンの数をnum_units引数に、活性化関数をactivation引数に指定すればよい。activation引数は省略した場合は、tanh関数という-11の範囲をとる関数がデフォルトで設定される。特に理由がなければtanh関数のままでよい。この構造を表すコードについては後述する。

データセットの準備

 データはUCI Machine Learning RepositoryよりAir Quality Data Set(センサーデバイスにより収集された大気質の時系列データセット)を用いる。前回のMNISTデータのようにTensorFlowパッケージに含まれているわけではないので、手動でダウンロードする必要がある。Webブラウザーからダウンロードしてもよいが、Pythonでダウンロードする場合は次のようにできる。Jupyter Notebookで新規ノートを作成してリスト1のコードを実行すればよい。

Python
import os
from urllib.request import urlretrieve
from urllib.parse import urlparse
from zipfile import ZipFile

def download_file(url, output_dir, overwrite=False):
  # URLからファイル名を取得
  parse_result = urlparse(url)
  file_name = os.path.basename(parse_result.path)
  # 出力先ファイルパス
  destination = os.path.join(output_dir, file_name)

  # 無意味なダウンロードを防ぐため、上書き(overwrite)の指定か未ダウンロードの場合のみダウンロードを実施する
  if overwrite or not os.path.exists(destination):
    # 出力先ディレクトリの作成
    os.makedirs(output_dir)
    # ダウンロード
    urlretrieve(url, destination)

  return destination

zip_file = download_file('https://archive.ics.uci.edu/ml/machine-learning-databases/00360/AirQualityUCI.zip', 'UCI_data/')
リスト1 Air Qualityデータセットのダウンロード

 ダウンロードしたAirQualityUCI.zipファイル中にはAirQualityUCI.csvAirQualityUCI.xlsxという2つのファイルが含まれているが、いずれも同じデータである。

 TensorFlowで直接CSVを読み込んで利用する方法もあるが、本データには欠損値missing dataNAと表すことが多い)が含まれていたり、CSVの数値の小数点がカンマであったりして取り扱いが複雑である。本稿では簡単のためにPandasパッケージ*1を利用して、Excel用の.xlsxファイルからデータをデータフレームDataFrame)形式(1行に各データポイントを含むデータセット)で読み込む。データフレームからテンソルへの変換は容易である。

 PandasでExcelからデータを読み込むために、Pandasパッケージxlrdパッケージ*2のインストールが必要である。

  • *1 Pandasは、データサイエンス/機械学習の分野で非常によく利用されるデータ解析用Pythonライブラリで、例えば時系列データなどの解析やグラフ図生成によるデータの可視化などが行える。
  • *2 xlrdは、Excelデータを読む込むためのPythonライブラリ。
Bash
$ pip install pandas xlrd
リスト2 Pandas&xlrdパッケージをインストールするコマンド

 インストールが完了したら、Jupyter Notebook上でリスト3のコードを実行し、データフレームにデータを読み込んでほしい。このコードにより、先ほどダウンロードした.zipファイルからAirQualityUCI.xlsxファイルを抽出して読み込んでいる。

Python
import pandas as pd

with ZipFile(zip_file) as z:
  with z.open('AirQualityUCI.xlsx') as f:
    air_quality = pd.read_excel(
      f,
      index_col=0, parse_dates={'DateTime': [0, 1]}, #1
      na_values=[-200.0],                            #2
      convert_float=False                            #3
    )
リスト3 Air Qualityデータセットのデータフレームへの読み込み

read_excelメソッドでExcelファイル(f)からデータを読み込んでいる。

  • 1データの最初の2列には日付(Date)と時刻(Time)のデータが含まれているので、引数parse_datesで日時データとして結合して[DateTime]列にまとめる。これをインデックスとして実際のデータポイントとは別に扱う。
  • 2-200が欠損値(NA Values)として入力されているため、読み込み時にこれを指定する。
  • 3Falseがされると、すべてのデータがfloat値のまま読み込まれる。デフォルトではTrueになっており、この場合はint値に変換されてしまうため。

【コラム】時系列データの可視化

 Pandasでデータフレームを読み込んだら、まず可視化を行うべきである。データの傾向をつかみ、どのような前処理を行い、どのような分析を行うか、といった道筋を立てることができるからだ。

 このコラムでは、Pandasのデータフレームの可視化に便利なCufflinksというパッケージを紹介する。

 Cufflinksは、Plotlyというデータの可視化をサポートするプラットフォームおよびパッケージ(多言語対応)をバックエンドとしている。グラフ(ここでは一般的な図のこと)を公開するためのSaaS(=オンライン)も提供しているが、Jupyter Notebookのようなオフラインでの利用もできる。Plotly単体でもデータフレームは可視化できるが、「データフレームを扱う」という処理のみにフォーカスすれば、Cufflinksを使うことで複雑なもろもろの処理を省くことができる。ちょうど前々回紹介したTensorFlowとKerasの関係に似ている。

 Cufflinksパッケージのインストールはpipにより行える。このとき同時に依存するPlotlyパッケージもインストールされる。

Bash
$ pip install cufflinks
リスト4 Cufflinksパッケージをインストールするコマンド

 データを可視化することや可視化されたグラフのことをプロットplot)と呼ぶ。Cufflinksでは、Jupyter Notebook上でプロットを行うために、Pandasのデータフレーム(DataFrameクラス)でiplotという拡張メソッドを提供している。Jupyter Notebookのようなオフライン(=ローカル)でのプロットには、iplotメソッドのonline引数にFalseを与えるか、事前にgo_offlineメソッドを呼び出す必要がある(リスト5)。

 Cufflinksを使ってAir Qualityデータセットを可視化するには、次のようにする。

Python
import cufflinks as cf
cf.go_offline()

air_quality.iplot(
  # 列ごとにグラフを縦に並べて描画する
  subplots=True,
  shape=(len(air_quality.columns), 1),
  # グラフのx軸を共有する
  shared_xaxes=True
)
リスト5 Air Qualityデータセットの可視化

iplotメソッドの引数の意味は、help(air_quality.iplot)を使って調べられる。もしくは「cufflinks/Cufflinks Tutorial - Plotly.ipynb at master · santosjorge/cufflinks」を参照してもよい。

 列によりスケール(数値の大きさの程度)が大きく異なるため、それぞれの別のグラフとして表示した方が見やすい(subplots=Trueで別グラフとし、shape(<プロットの行数>,<列数>)でデータ項目数分を縦にグラフとして並べている。データフレームのcolumns属性でデータ項目を取得し、それをlen関数の引数に指定することでデータ項目の数を取得している。インデックスはデータ項目には含まれない)。

 また、Plotlyはグラフの拡大・縮小を行えるが、可視化の際にすべてのグラフで同じ時間帯のグラフを表示できるように、x軸の共有を行っている(shared_xaxes=True)。

 リスト5を実行すると図ex1のようなグラフが出力される。

図ex1 Air Qualityデータセットのプロット
図ex1 Air Qualityデータセットのプロット

 プロットの見た目を洗練させるために、サイズや色の調整を行いたい場合は、iplotメソッドのasFigure引数にTrueを与え、その戻り値の辞書オブジェクトを修正し、その辞書オブジェクトに対してiplotメソッドを呼べばよい。次のコードは、背景色を白(カラーコードで#FFFFFF)にして縦幅を1600pxに設定する例だ。レイアウトの設定はPlotlyのLayoutを参照してほしい。図2はその結果である。

Python
plot_data = air_quality.iplot(
  asFigure=True,
  subplots=True,
  shared_xaxes=True,
  shape=(len(air_quality.columns), 1)
)
plot_data['layout']['height'] = 1600
plot_data['layout']['paper_bgcolor'] = '#FFFFFF'
plot_data.iplot()
リスト6 グラフの外観のカスタマイズ
図ex2 見た目の調整を行ったAir Qualityデータセットのプロット
図ex2 見た目の調整を行ったAir Qualityデータセットのプロット

 時系列データ、特にセンサーデータについては欠損値が含まれるのは一般的な事象であり、Air Qualityデータセットにも欠損値が含まれる。欠損値の統計上の取り扱いについては専門書が出版されるほどであるが、本稿でそれを説明するには記事が長くなりすぎる。ここでは簡単に、欠損値データについては学習に利用しないこととする。

 また、本稿ではAir Qualityデータセット中の一部の列のみ利用する(表1)。

利用内容
CO(GT)一酸化炭素(CO)時間平均濃度[mg/m3
PT08.S1(CO)一酸化炭素(CO)に対する酸化スズの時間平均値
NMHC(GT)非メタン炭化水素(NMHC)時間平均濃度[microg/m3
C6H6(GT)ベンゼン(C6H6)時間平均濃度[microg/m3
PT08.S2(NMHC)非メタン炭化水素(NMHC)に対する酸化チタンの時間平均値
NOx(GT)窒素酸化物(NOx)時間平均濃度[ppb]
PT08.S3(NOx)窒素酸化物(NOx)に対する酸化タングステンの時間平均値
NO2(GT)二酸化窒素(NO2)時間平均濃度[microg/m3
PT08.S4(NO2)二酸化窒素(NO2)に対する酸化タングステンの時間平均値
PT08.S5(O3)オゾン(O3)に対する酸化インジウムの時間平均値
T温度(Temperature)[℃]
RH相対湿度(Relative Humidity)[%]
AH絶対湿度(Absolute Humidity)
表1 Air Quality中の利用するデータ

上記の[内容]はあくまで参考情報である。より厳密には公式ページの説明を当たってほしい。

 一部の列のみをデータ項目として利用するには、リスト7のコードを記述すればよい。

Python
# 不要列の除去
target_columns = ['T', 'AH', 'PT08.S1(CO)', 'PT08.S2(NMHC)', 'PT08.S3(NOx)', 'PT08.S4(NO2)']
air_quality = air_quality[target_columns]
リスト7 利用する列と順番を再定義してデータフレームを再構築

 前々回(CNN)で使ったMNISTデータセットのnext_batchメソッドのような機能を持つ、データセットを扱うための便利クラスを作成しておく(リスト8)。

 時系列データの学習では、連続したデータポイントを訓練データとする。連続したデータポイントをランダムに切り出すためのメソッドとしてnext_batchを作成している。

 next_batchメソッドは、length引数とbatch_size引数をとり、ランダムな「連続したlength分のデータポイント」および「その次のデータポイント」を返す。前述のように、本稿では欠損値は無視して扱う。すなわち、ランダムな連続データポイント中に欠損値が含まれていた場合は、再度、ランダムな連続データポイントを取得する*2

  • *2 本稿の実装では、連続データポイント中に必ず欠損値が含まれてしまうような場合では、所望の連続データポイントが切り出せずに無限ループに陥る可能性がある。実際のデータセットの場合は、もう少しきちんとした処理を記述する必要がある。しかし本稿では、そのような処理は省略している。
Python
import numpy as np

# 乱数シードの初期化(数値は何でもよい)
np.random.seed(12345)

# クラス定義
class TimeSeriesDataSet:

  def __init__(self, dataframe):
    self.feature_count = len(dataframe.columns)
    self.series_length = len(dataframe)
    self.series_data = dataframe.astype('float32')

  def __getitem__(self, n):
    return TimeSeriesDataSet(self.series_data[n])

  def __len__(self):
    return len(self.series_data)

  @property
  def times(self):
    return self.series_data.index

  def next_batch(self, length, batch_size):
    """
    連続したlength時間のデータおよび1時間の誤差測定用データを取得する。
    最後の1時間は最終出力データ。
    """
    max_start_index = len(self) - length
    design_matrix = []
    expectation = []
    while len(design_matrix) < batch_size:
      start_index = np.random.choice(max_start_index)
      end_index = start_index + length + 1
      values = self.series_data[start_index:end_index]
      if (values.count() == length + 1).all():  # 切り出したデータ中に欠損値がない
        train_data = values[:-1]
        true_value = values[-1:]
        design_matrix.append(train_data.as_matrix())
        expectation.append(np.reshape(true_value.as_matrix(), [self.feature_count]))
    return np.stack(design_matrix), np.stack(expectation)

  def append(self, data_point):
    dataframe = pd.DataFrame(data_point, columns=self.series_data.columns)
    self.series_data = self.series_data.append(dataframe)

  def tail(self, n):
    return TimeSeriesDataSet(self.series_data.tail(n))

  def as_array(self):
    return np.stack([self.series_data.as_matrix()])
リスト8 データセットを扱うための便利クラス

 Air Qualityデータセットは、2004年3月10日から2005年4月4日までの範囲である。本稿では2004年のデータのみを訓練(train)時に利用し、2005年のデータは予測の際にのみ利用する精度検証用のテスト(test)データとする(リスト9)。本稿ではCNNと同様に、訓練データから精度検証(validation)用データを切り分けずに、テストデータのみを精度検証に利用する。

Python
dataset = TimeSeriesDataSet(air_quality)
train_dataset = dataset[dataset.times.year < 2005]
test_dataset = dataset[dataset.times.year >= 2005]
リスト9 データセットを訓練データとテストデータに分離

RNNの実行(学習)

 RNNの全体像を先に示す。本稿では、連続した72個のデータポイント(3日間)を用いてRNNを構築する(図3)。

図3 今回作成するRNNのネットワーク構造

 入力は時刻 \(t\) における観測値、出力は時刻 \(t+1\) における予測値となる。RNNのセルは全結合層であり、ここでは20ニューロンとする。図4は時刻 \(t\) における入力と出力を図示したものである。最終的に出力される \(x_{73}\) ――すなわち入力となる連続した72時間の次の1時間のデータ――の誤差を最小化するように学習を行う。

図4 時刻tの入出力

 これをコードにしていこう。まずは、TensroFlowのセッションを開始し、各種パラメーターと、データ入力用のプレースホルダーを定義する(リスト10)。

Python
import tensorflow as tf
sess = tf.InteractiveSession()

# 再現性の確保のために乱数シードを固定
tf.set_random_seed(12345)

# パラメーター
# 学習時間長
SERIES_LENGTH = 72
# 特徴量数
FEATURE_COUNT = dataset.feature_count

# 入力(placeholderメソッドの引数は、データ型、テンソルのサイズ)
# 訓練データ
x = tf.placeholder(tf.float32, [None, SERIES_LENGTH, FEATURE_COUNT])
# 教師データ
y = tf.placeholder(tf.float32, [None, FEATURE_COUNT])
リスト10 セッションの開始と、各パラメーター&入力プレースホルダーの定義

 次にRNNのセルを定義する。リスト11を見てほしい。

Python
# RNNセルの作成
cell = tf.nn.rnn_cell.BasicRNNCell(20)
initial_state = cell.zero_state(tf.shape(x)[0], dtype=tf.float32)
outputs, last_state = tf.nn.dynamic_rnn(cell, x, initial_state=initial_state, dtype=tf.float32)
リスト11 RNNのセルを定義

 前述のようにRNNのセルには、tf.rnn.rnn_cell.BasicRNNCellクラスを用いればよい。num_units引数に20(ニューロン)を指定し、activation引数は指定せずデフォルトのまま(tanh関数)とする。

 RNNセルは再帰的にネットワークを構築するが、一番上のセル(tf.shape(x)[0])に対する入力が必要となる(前掲の図3には表現されていないので注意)。つまりRNNセルの初期状態(initial_state)について定義しなければならない。ここでは単純にゼロの初期状態を与えるものとし、BasicRNNCellクラスのzero_stateメソッドを用いる。

 ネットワークの入出力はtf.nn.dynamic_rnnメソッドを使う*3tf.nn.dynamic_rnn関数の戻り値は、各セルのニューロンの状態(outputs)と、最後のセルのニューロンの状態(last_state)である。これらを用いて、おのおのの出力を求めることができる。

  • *3 tf.nn.static_rnn関数という関数も存在する。入力が固定長のリストである場合は、こちらを選んでもよい。tf.nn.dynamic_rnn関数は入力が可変長の場合に使われるのが本来の用途だが、そこまで厳密に気にしなくてもよいだろう。

 先に示したように、今回は最終的な出力――すなわち入力の72時間の次の時間のデータ――の予測値を、最適化に利用する(リスト12)。

Python
# 全結合
# 重み
w = tf.Variable(tf.zeros([20, FEATURE_COUNT]))
# バイアス
b = tf.Variable([0.1] * FEATURE_COUNT)
# 最終出力(予測)
prediction = tf.matmul(last_state, w) + b

# 損失関数(平均絶対誤差:MAE)と最適化(Adam)
loss = tf.reduce_mean(tf.map_fn(tf.abs, y - prediction))
optimizer = tf.train.AdamOptimizer().minimize(loss)
リスト12 全結合と最適化

 オプティマイザーは前々回と同じくAdamを用いる。損失関数は、平均絶対誤差MAEMean Absolute Error)――つまり正解値と予測値との各変数の差(y - prediction)の絶対値(tf.abs)の平均値(tf.reduce_mean)――をとる(tf.map_fn)ことにする。

 学習については前回とほとんど同じでオプティマイザーの評価を繰り返せばよいのだが、1つだけ注意すべき点がある。損失関数を「各変数の差の絶対値の平均値」としたが、[PT08.]で始まる4つの変数が1000以上の値をとりうるのに対し、[T]は高々40、[AH]にいたっては2程度である。このようなスケールの違うものについて予測して誤差を見ると、[PT08.]で始まる変数の誤差に比べて、[T]や[AH]の誤差は無視できるほど小さい。これではうまく最適化が行われない。

 そこで、学習前に各変数の値を平均0、分散1に変換してスケールを揃える。この操作を標準化Standardization)と呼ぶ。数式で表せば次のようになる。

\[z = \frac{x - \mu}{\sigma}\]

  \(x\) は個々のデータ、 \(\mu\) は平均値、 \(\sigma\) は標準偏差である。標準化された値のことをZ値などと呼ぶこともある。

 リスト8で示したTimeSeriesDataSetクラスに、標準化のためのメソッドを追加する(リスト13)。具体的には、

  • mean: 平均値を求めるメソッド
  • std: 標準偏差を求めるメソッド
  • standardize: 標準化を行うメソッド

を新たに追加している。standardizeメソッドが、引数に平均値meanと標準偏差stdをとるようにしているが、これは訓練時ではなく、検証時に訓練時と同じ平均値と標準偏差を用いて標準化するためである。

Python
class TimeSeriesDataSet:
  ……省略(リスト8参照)……

  def mean(self):
    return self.series_data.mean()

  def std(self):
    return self.series_data.std()

  def standardize(self, mean=None, std=None):
    if mean is None:
      mean = self.mean()
    if std is None:
      std = self.std()
    return TimeSeriesDataSet((self.series_data - mean) / std)
リスト13 標準化のためのメソッドをまとめたクラス

 学習を実行するコードは次の通り。標準化を行っているところを除き、前回と大きく変わっているところはないはずだ。

Python
# バッチサイズ
BATCH_SIZE = 16
# 学習回数
NUM_TRAIN =  10_000
# 学習中の出力頻度
OUTPUT_BY = 500

# 標準化
train_mean = train_dataset.mean()
train_std = train_dataset.std()
standardized_train_dataset = train_dataset.standardize()

# 学習の実行
sess.run(tf.global_variables_initializer())
for i in range(NUM_TRAIN):
  batch = standardized_train_dataset.next_batch(SERIES_LENGTH, BATCH_SIZE)
  mae, _ = sess.run([loss, optimizer], feed_dict={x: batch[0], y: batch[1]})
  if i % OUTPUT_BY == 0:
    print('step {:d}, error {:.2f}'.format(i, mae))
リスト14 RNNによる学習

 ここまでできたら、上から順番に実行してネットワークに学習させ、モデルを作成しよう。

RNNの実行(予測)

 それでは、学習済みのモデルを用いて予測を行う。予測は、「予測対象となる時刻の前72時間のデータを入力データとして予測する」という操作を繰り返して行う(図5)。

図5 予測の方法

 前述の通り、入力データは学習時データ(2004年のデータ)の平均値と標準偏差を用いて標準化を行う。RNNの出力を、標準化操作と同じ平均値と標準偏差を用いて標準化の逆操作を行えば予測値を求めることができる。具体的には、次のコードになる。

Python
def rnn_predict(input_dataset):
  # 標準化
  previous = TimeSeriesDataSet(input_dataset).tail(SERIES_LENGTH).standardize(mean=train_mean, std=train_std)
  # 予測対象の時刻
  predict_time = previous.times[-1] + np.timedelta64(1, 'h')

  # 予測
  batch_x = previous.as_array()
  predict_data = prediction.eval({x: batch_x})

  # 結果のデータフレームを作成
  df_standardized  = pd.DataFrame(predict_data, columns=input_dataset.columns, index=[predict_time])
  # 標準化の逆操作
  return train_mean + train_std * df_standardized

predict_air_quality = pd.DataFrame([], columns=air_quality.columns)
for current_time in test_dataset.times:
  predict_result = rnn_predict(air_quality[air_quality.index < current_time])
  predict_air_quality = predict_air_quality.append(predict_result)
リスト15 予測を行い、各時間の結果値を変数predict_air_qualityに保存するコード

 結果が分かりやすいように、予測結果を可視化しよう。可視化の方法については、リスト16を参考にしてほしい(詳細は前掲のコラムを参照)。

Python
# インポート&実行済みの場合、以下の3行はなくてもよい
import pandas as pd
import cufflinks as cf
cf.go_offline()

# 正解データと予測データ
correct_2005_year = dataset[dataset.times.year >= 2005].series_data
predict_2005_year = predict_air_quality

# 2005年3月分のデータに絞るには、コメントアウトを外す
#dt_2005march = pd.date_range('20050301','20050401', freq="H")
#correct_2005_year = correct_2005_year.reindex(dt_2005march)
#predict_2005_year = predict_2005_year.reindex(dt_2005march)

for feature in air_quality.columns:
    plot_data = pd.DataFrame({
        '正解': correct_2005_year[feature],
        '予測': predict_2005_year[feature]
    }).iplot(
      asFigure = True,
      title = feature
    )
    plot_data['layout']['paper_bgcolor'] = '#FFFFFF'
    plot_data.iplot()
リスト16 予測結果を可視化するサンプルコード

 以下がリスト16を実行した例である。図6は2005年全体、図7は見やすいように2005年3月のみを抽出している。欠損値がある部分については予測が行えていないが、そうでない部分に関しては精度良く予測できている様子が見える。

図6 2005年の予測結果
図7 2005年3月の予測結果

 余裕がある読者は、1時間先を予測するだけではなく、数時間先(例えば12時間)まで予測してみてほしい。2時間先のデータを予測するためには、まず1時間先を予測し、この予測値を入力値として利用すればよく、同様に繰り返していけば任意の未来の時間まで予測ができる。どうしても分からなければ、以下のコードをヒントにしてほしい。6時間先までを予測するサンプルコードである。

Python
from functools import reduce
import re
# インポート&実行済みの場合、以下の3行はなくてもよい
import pandas as pd
import cufflinks as cf
cf.go_offline()

##############################################

def rnn_predict(input_dataset, ahead=1):
  # 標準化
  buffer = TimeSeriesDataSet(input_dataset).tail(SERIES_LENGTH).standardize(mean=train_mean, std=train_std)
  # 予測対象の時刻
  last_time = buffer.times[-1]

  # 予測
  predict_data = []
  for i in range(0, ahead):
    batch_x = buffer.tail(SERIES_LENGTH).as_array()
    result_array = prediction.eval({x: batch_x})
    predict_data.append(result_array)
    buffer.append(result_array)
  predict_data = reduce(lambda acc, x: np.concatenate((acc, x)), predict_data)

  # インデックス時刻の作成
  hour = np.timedelta64(1, 'h')
  first_predict_time = input_dataset.index[-1] + hour
  index = np.arange(first_predict_time, first_predict_time + ahead * hour, step=hour)

  # 結果のデータフレームを作成
  df_standardized = pd.DataFrame(predict_data, columns=input_dataset.columns, index=index)
  # 標準化の逆操作
  return train_mean + train_std * df_standardized

###############################################

predict_air_quality_1 = pd.DataFrame([], columns=air_quality.columns)
predict_air_quality_2 = pd.DataFrame([], columns=air_quality.columns)
predict_air_quality_3 = pd.DataFrame([], columns=air_quality.columns)
predict_air_quality_4 = pd.DataFrame([], columns=air_quality.columns)
predict_air_quality_5 = pd.DataFrame([], columns=air_quality.columns)
predict_air_quality_6 = pd.DataFrame([], columns=air_quality.columns)

for current_time in dataset.times[dataset.times.year >= 2005]:
  # 6時間先まで予測(結果は6レコード)
  predict_result = rnn_predict(air_quality[air_quality.index < current_time], 6)
  # 1時間先の予測レコード
  predict_air_quality_1 = predict_air_quality_1.append(predict_result[0:1])
  # 2時間先の予測レコード
  predict_air_quality_2 = predict_air_quality_2.append(predict_result[1:2])
  # 3時間先の予測レコード
  predict_air_quality_3 = predict_air_quality_3.append(predict_result[2:3])
  # 4時間先の予測レコード
  predict_air_quality_4 = predict_air_quality_4.append(predict_result[3:4])
  # 5時間先の予測レコード
  predict_air_quality_5 = predict_air_quality_5.append(predict_result[4:5])
  #6時間先の予測レコード
  predict_air_quality_6 = predict_air_quality_6.append(predict_result[5:6])

# 1つのデータフレームにまとめる
predict_air_quality = pd.concat(
  [
    dataset[dataset.times.year >= 2005].series_data,
    predict_air_quality_1,
    predict_air_quality_2,
    predict_air_quality_3,
    predict_air_quality_4,
    predict_air_quality_5,
    predict_air_quality_6
  ],
  axis='columns'
)

labels = ['正解', '予測(1時間)', '予測(2時間)', '予測(3時間)', '予測(4時間)', '予測(5時間)', '予測(6時間)']
predict_air_quality.columns = ['{}_{}'.format(x, y) for y in labels for x in air_quality.columns]

###############################################

for feature in air_quality.columns:
  columns_regex = '^{}_'.format(re.escape(feature))
  plot_data = predict_air_quality.filter(regex=columns_regex).iplot(
    asFigure = True,
    title = feature
  )
  plot_data['layout']['paper_bgcolor'] = '#FFFFFF'
  plot_data.iplot()
リスト17 6時間先までを予測するサンプルコード

チューニングのための情報

RNNの応用

 今回は、基本的なRNNの予測のみで良い結果が得られたためチューニングは特に行わないが、チューニングのための情報としていくつか記しておく。

 今回用いたBasicRNNCellは単純なニューラルネットワークの隠れ層であったが、このセルを複雑なものに変更することもできる。例えば単純なRNNは長期依存の時系列(ある時点における出力が、その時点よりかなり前の時点の入力に依存するもの)についての学習はうまくいかないことが知られている。この欠点を解決する方法としてLong Short-Term MemoryLSTM)という手法がある。

 LSTMは、RNNと同様に特定の入出力の構造を繰り返す。LSTMの構造は本稿では説明しないが、TensorFlowでLSTMを用いるには、tf.nn.BasicRNNCellクラスと同様に、tf.nn. rnn_cell.BasicLSTMCellクラスを使えばよい。

 RNNのキモは「繰り返し構造をどのように構築するか」ということになるが、LSTMを始めとしてさまざまな手法が提案されている。TensorFlowにも多くのセルが提供されているので、公式ドキュメントの「RNN and Cells」を参考にしてみるとよいだろう。

 次回は本連載の最終回として、TensorFlowを活用する際に役立つ「TensorBoard」というツールを紹介する。今回のプログラムとデータは、次回で再利用するので消さずに保存しておいてほしい。

【TL;DR】時系列データの予測

  • tf.nn.rnn_cell.BasicRNNCellクラス: 単純なRNNのモデルを作成できるクラス
  • tf.nn.rnn_cell.BasicLSTMCellクラス: セルが複雑なRNN(LSTM)のモデルを作成できるクラス
  • Air Qualityデータセット: センサーデバイスにより収集された大気質の時系列データセット。手動でダウンロードする必要がある
  • Pandasパッケージ: データ解析用Pythonライブラリ。時系列データの解析やグラフ図生成によるデータの可視化などが行える
  • xlrdパッケージ: Excelデータを読む込むためのPythonライブラリ
  • Cufflinksパッケージ: データフレームを簡単にグラフとして可視化できるパッケージ。データの可視化をサポートするプラットフォーム&パッケージであるPlotlyをバックエンドに使用
  • プロット: データを可視化すること、もしくは可視化されたされたグラフのこと
  • RNNにおける訓練データ: 時系列データの学習では、連続したデータポイントを訓練データとする。今回は連続したデータポイントをランダムに切り出すためのnext_batchメソッドを自作している
  • データ分割: 今回はデータセットを、訓練(train)データと、精度検証用のテスト(test)データに分離する
  • RNNのセル: tf.rnn.rnn_cell.BasicRNNCellクラスを使用。全結合層で、今回は「20」ニューロンとした
  • 損失関数とオプティマイザー: 平均絶対誤差(MAE:Mean Absolute Error)とAdamを使用した
  • 標準化: 学習前に各変数の値を平均「0」、分散「1」に変換して、値のスケールを揃えるテクニック
  • このエントリーをはてなブックマークに追加

※以下では、本稿の前後を合わせて5回分(第4回~第8回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載目次]を参照してください。

TensorFlow入門(7)
4. CNN(Convolutional Neural Network)を理解しよう(TensorFlow編)

画像認識でよく使われるディープラーニングの代表的手法「CNN」を解説。「畳み込み」「プーリング」「活性化関数」「CNNのネットワーク構成」「ソフトマックス関数」といった基礎と、注意点を押さえよう。

TensorFlow入門(7)
5. 画像認識を行う深層学習(CNN)を作成してみよう(TensorFlow編)

ディープラーニングの代表的手法「CNN」により画像認識を行う機械学習モデルを構築してみる。CNNによる深層学習がどのようなものか体験しよう。

TensorFlow入門(7)
6. RNN(Recurrent Neural Network)の概要を理解しよう(TensorFlow編)

時系列データの予測でよく使われるディープラーニングの代表的手法「RNN」を解説。そもそも時系列データとは何か? RNNの特徴や、通常のニューラルネットワークの相違点についても押さえよう。

TensorFlow入門(7)
7. 【現在、表示中】≫ 時系列データの予測を行う深層学習(RNN)を作成してみよう(TensorFlow編)

ディープラーニングの代表的手法「RNN」により時系列データの予測を行う機械学習モデルを構築してみる。RNNによる深層学習がどのようなものか体験しよう。

TensorFlow入門(7)
8. TensorBoardとは? スカラー値やデータフローグラフの可視化

TensorFlowを活用するうえで、TensorBoardは非常に役立つツールだ。スカラー値やデータフローグラフをログファイルとして出力し、可視化する方法を説明する。

Deep Insider の SNS :