時系列予測

TensorFlow.orgで表示 GoogleColabで実行 GitHubでソースを表示ノートブックをダウンロード

このチュートリアルは、TensorFlowを使用した時系列予測の概要です。畳み込みニューラルネットワーク(CNNおよびRNN)を含むいくつかの異なるスタイルのモデルを構築します。

これは、サブセクションで2つの主要な部分でカバーされています。

  • 単一のタイムステップの予測:
    • 単一の機能。
    • すべての機能。
  • 複数のステップを予測する:
    • シングルショット:一度にすべての予測を行います。
    • 自己回帰:一度に1つの予測を行い、出力をモデルにフィードバックします。

設定

import os
import datetime

import IPython
import IPython.display
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import tensorflow as tf

mpl.rcParams['figure.figsize'] = (8, 6)
mpl.rcParams['axes.grid'] = False
2021-08-03 01:29:35.290679: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0

気象データセット

このチュートリアルでは使用しています天候の時系列データセットによって記録された生物地球化学のためのマックス・プランク研究所

このデータセットには、気温、大気圧、湿度などの14の異なる機能が含まれています。これらは効率のために、あなただけのデータがデータセットのこのセクションでは、彼の本のためにフランソワ・Cholletすることにより調製した2009年から2016年の間の収集に使用されます2003年に始まり、10分ごとに収集したのPythonとの深い学習

zip_path = tf.keras.utils.get_file(
    origin='https://storage.googleapis.com/tensorflow/tf-keras-datasets/jena_climate_2009_2016.csv.zip',
    fname='jena_climate_2009_2016.csv.zip',
    extract=True)
csv_path, _ = os.path.splitext(zip_path)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/jena_climate_2009_2016.csv.zip
13574144/13568290 [==============================] - 1s 0us/step

このチュートリアルでは、ちょうど1時間毎に10分間隔からのデータをサブサンプリングすることにより開始ので、時間ごとの予測に対処します:

df = pd.read_csv(csv_path)
# Slice [start:stop:step], starting from index 5 take every 6th record.
df = df[5::6]

date_time = pd.to_datetime(df.pop('Date Time'), format='%d.%m.%Y %H:%M:%S')

データを見てみましょう。最初の数行は次のとおりです。

df.head()

これは、時間の経過に伴ういくつかの機能の進化です。

plot_cols = ['T (degC)', 'p (mbar)', 'rho (g/m**3)']
plot_features = df[plot_cols]
plot_features.index = date_time
_ = plot_features.plot(subplots=True)

plot_features = df[plot_cols][:480]
plot_features.index = date_time[:480]
_ = plot_features.plot(subplots=True)

png

png

検査とクリーンアップ

次に、データセットの統計を確認します。

df.describe().transpose()

風速

目立つべきことの一つは、 min風速(の値wv (m/s)と最大値( max. wv (m/s)カラム。この-9999おそらく誤りです。

そこ別風向列ですので、速度は(ゼロより大きくなければならない>=0 )。ゼロに置き換えます。

wv = df['wv (m/s)']
bad_wv = wv == -9999.0
wv[bad_wv] = 0.0

max_wv = df['max. wv (m/s)']
bad_max_wv = max_wv == -9999.0
max_wv[bad_max_wv] = 0.0

# The above inplace edits are reflected in the DataFrame.
df['wv (m/s)'].min()
0.0

機能工学

モデルを構築するために飛び込む前に、データを理解し、モデルに適切にフォーマットされたデータを渡していることを確認することが重要です。

データの最後の列は、 wd (deg)度の単位で風向を-gives。角度は適切なモデル入力にはなりません。360°と0°は互いに近く、スムーズにラップアラウンドする必要があります。風が吹いていない場合は方向は関係ありません。

現在、風データの分布は次のようになっています。

plt.hist2d(df['wd (deg)'], df['wv (m/s)'], bins=(50, 50), vmax=400)
plt.colorbar()
plt.xlabel('Wind Direction [deg]')
plt.ylabel('Wind Velocity [m/s]')
Text(0, 0.5, 'Wind Velocity [m/s]')

png

しかし、これはあなたが風ベクトルに風向・風速列を変換する場合、モデルが解釈するのが容易になります。

wv = df.pop('wv (m/s)')
max_wv = df.pop('max. wv (m/s)')

# Convert to radians.
wd_rad = df.pop('wd (deg)')*np.pi / 180

# Calculate the wind x and y components.
df['Wx'] = wv*np.cos(wd_rad)
df['Wy'] = wv*np.sin(wd_rad)

# Calculate the max wind x and y components.
df['max Wx'] = max_wv*np.cos(wd_rad)
df['max Wy'] = max_wv*np.sin(wd_rad)

風ベクトルの分布は、モデルが正しく解釈するためにはるかに簡単です。

plt.hist2d(df['Wx'], df['Wy'], bins=(50, 50), vmax=400)
plt.colorbar()
plt.xlabel('Wind X [m/s]')
plt.ylabel('Wind Y [m/s]')
ax = plt.gca()
ax.axis('tight')
(-11.305513973134667, 8.24469928549079, -8.27438540335515, 7.7338312955467785)

png

時間

同様に、 Date Time列はなく、この文字列の形式で、非常に便利です。それを秒に変換することから始めます:

timestamp_s = date_time.map(pd.Timestamp.timestamp)

風向と同様に、秒単位の時間は有用なモデル入力ではありません。気象データであるため、日次および年次の周期性が明確です。周期性に対処する方法はたくさんあります。

正弦および余弦変換を使用して「時刻」および「時刻」信号をクリアすることにより、使用可能な信号を取得できます。

day = 24*60*60
year = (365.2425)*day

df['Day sin'] = np.sin(timestamp_s * (2 * np.pi / day))
df['Day cos'] = np.cos(timestamp_s * (2 * np.pi / day))
df['Year sin'] = np.sin(timestamp_s * (2 * np.pi / year))
df['Year cos'] = np.cos(timestamp_s * (2 * np.pi / year))
plt.plot(np.array(df['Day sin'])[:25])
plt.plot(np.array(df['Day cos'])[:25])
plt.xlabel('Time [h]')
plt.title('Time of day signal')
Text(0.5, 1.0, 'Time of day signal')

png

これにより、モデルは最も重要な周波数機能にアクセスできます。この場合、どの周波数が重要であるかを事前に知っていました。

あなたがその情報を持っていない場合は、との特徴を抽出することにより、重要である周波数を決定することができます高速フーリエ変換します。仮定を確認するには、ここにあるtf.signal.rfft時間にわたる温度の。近くの周波数で明らかにピークに注意してください1/year1/day

fft = tf.signal.rfft(df['T (degC)'])
f_per_dataset = np.arange(0, len(fft))

n_samples_h = len(df['T (degC)'])
hours_per_year = 24*365.2524
years_per_dataset = n_samples_h/(hours_per_year)

f_per_year = f_per_dataset/years_per_dataset
plt.step(f_per_year, np.abs(fft))
plt.xscale('log')
plt.ylim(0, 400000)
plt.xlim([0.1, max(plt.xlim())])
plt.xticks([1, 365.2524], labels=['1/Year', '1/day'])
_ = plt.xlabel('Frequency (log scale)')
2021-08-03 01:29:41.976610: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-08-03 01:29:42.626738: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:42.627640: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-03 01:29:42.627697: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-03 01:29:42.630402: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-08-03 01:29:42.630533: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-08-03 01:29:42.631505: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-08-03 01:29:42.631863: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-08-03 01:29:42.632610: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-08-03 01:29:42.633312: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-08-03 01:29:42.633541: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-08-03 01:29:42.633650: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:42.634551: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:42.635454: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-03 01:29:42.636067: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-08-03 01:29:42.636598: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:42.637440: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-03 01:29:42.637536: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:42.638404: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:42.639205: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-03 01:29:42.639284: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-03 01:29:43.206803: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-08-03 01:29:43.206840: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-08-03 01:29:43.206848: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-08-03 01:29:43.207063: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:43.208016: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:43.208856: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-03 01:29:43.209685: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 14646 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)
2021-08-03 01:29:43.470908: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10

png

データを分割する

あなたは使います(70%, 20%, 10%)トレーニング、検証、およびテストセットの分割を。ランダムに分割する前にシャッフルされていないデータを注意してください。これには2つの理由があります。

  1. これにより、データを連続するサンプルのウィンドウに分割することが可能になります。
  2. これにより、モデルのトレーニング後に収集されたデータで評価され、検証/テスト結果がより現実的になります。
column_indices = {name: i for i, name in enumerate(df.columns)}

n = len(df)
train_df = df[0:int(n*0.7)]
val_df = df[int(n*0.7):int(n*0.9)]
test_df = df[int(n*0.9):]

num_features = df.shape[1]

データを正規化する

ニューラルネットワークをトレーニングする前に、特徴をスケーリングすることが重要です。正規化は、このスケーリングを行う一般的な方法です。平均を減算し、各機能の標準偏差で除算します。

モデルが検証セットとテストセットの値にアクセスできないように、平均と標準偏差はトレーニングデータを使用してのみ計算する必要があります。

また、トレーニング時にモデルがトレーニングセットの将来の値にアクセスできないようにする必要があり、この正規化は移動平均を使用して実行する必要があることも議論の余地があります。これはこのチュートリアルの焦点では​​ありません。検証とテストセットにより、(ある程度)正直なメトリックを確実に取得できます。したがって、簡単にするために、このチュートリアルでは単純な平均を使用します。

train_mean = train_df.mean()
train_std = train_df.std()

train_df = (train_df - train_mean) / train_std
val_df = (val_df - train_mean) / train_std
test_df = (test_df - train_mean) / train_std

次に、機能の分布を確認します。一部の機能は、長い尾を持っていますが、のような明らかなエラーがない-9999風速値は。

df_std = (df - train_mean) / train_std
df_std = df_std.melt(var_name='Column', value_name='Normalized')
plt.figure(figsize=(12, 6))
ax = sns.violinplot(x='Column', y='Normalized', data=df_std)
_ = ax.set_xticklabels(df.keys(), rotation=90)

png

データウィンドウ処理

このチュートリアルのモデルは、データからの連続サンプルのウィンドウに基づいて一連の予測を行います。

入力ウィンドウの主な機能は次のとおりです。

  • 入力ウィンドウとラベルウィンドウの幅(タイムステップ数)。
  • それらの間の時間オフセット。
  • 入力、ラベル、またはその両方として使用される機能。

このチュートリアルでは、さまざまなモデル(線形、DNN、CNN、RNNモデルを含む)を作成し、それらを両方に使用します。

  • シングル出力、およびマルチ出力予測。
  • シングル時間ステップマルチ時間ステップの予測。

このセクションでは、データウィンドウを実装して、それらすべてのモデルで再利用できるようにすることに焦点を当てます。

タスクとモデルのタイプに応じて、さまざまなデータウィンドウを生成することができます。ここではいくつかの例を示します。

  1. たとえば、24時間の履歴を前提として、24時間先の単一の予測を行うには、次のようなウィンドウを定義します。

    24時間先の1つの予測。

  2. 6時間の履歴が与えられた場合、1時間先の予測を行うモデルには、次のようなウィンドウが必要になります。

    1時間先の1つの予測。

このセクションの残りの部分は、定義WindowGeneratorクラスを。このクラスは次のことができます。

  1. 上の図に示すように、インデックスとオフセットを処理します。
  2. 機能の分割ウィンドウ(features, labels)のペア。
  3. 結果のウィンドウのコンテンツをプロットします。
  4. 効率的に使用して、トレーニング、評価、およびテストデータからこれらのウィンドウのバッチを生成tf.data.Dataset秒。

1.インデックスとオフセット

作成から始めWindowGeneratorクラスを。 __init__方法は、入力とラベルインデックスに必要な全てのロジックを含みます。

また、入力としてDataFrameのトレーニング、評価、およびテストを行います。これらは、に変換されますtf.data.Datasetの窓の後に。

class WindowGenerator():
  def __init__(self, input_width, label_width, shift,
               train_df=train_df, val_df=val_df, test_df=test_df,
               label_columns=None):
    # Store the raw data.
    self.train_df = train_df
    self.val_df = val_df
    self.test_df = test_df

    # Work out the label column indices.
    self.label_columns = label_columns
    if label_columns is not None:
      self.label_columns_indices = {name: i for i, name in
                                    enumerate(label_columns)}
    self.column_indices = {name: i for i, name in
                           enumerate(train_df.columns)}

    # Work out the window parameters.
    self.input_width = input_width
    self.label_width = label_width
    self.shift = shift

    self.total_window_size = input_width + shift

    self.input_slice = slice(0, input_width)
    self.input_indices = np.arange(self.total_window_size)[self.input_slice]

    self.label_start = self.total_window_size - self.label_width
    self.labels_slice = slice(self.label_start, None)
    self.label_indices = np.arange(self.total_window_size)[self.labels_slice]

  def __repr__(self):
    return '\n'.join([
        f'Total window size: {self.total_window_size}',
        f'Input indices: {self.input_indices}',
        f'Label indices: {self.label_indices}',
        f'Label column name(s): {self.label_columns}'])

このセクションの冒頭の図に示されている2つのウィンドウを作成するコードは次のとおりです。

w1 = WindowGenerator(input_width=24, label_width=1, shift=24,
                     label_columns=['T (degC)'])
w1
Total window size: 48
Input indices: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
Label indices: [47]
Label column name(s): ['T (degC)']
w2 = WindowGenerator(input_width=6, label_width=1, shift=1,
                     label_columns=['T (degC)'])
w2
Total window size: 7
Input indices: [0 1 2 3 4 5]
Label indices: [6]
Label column name(s): ['T (degC)']

2.分割

連続した入力のリストを考えると、 split_window方法は、入力のウィンドウとラベルの窓に変換します。

w2以前の定義は次のように分割されます:

初期ウィンドウはすべて連続したサンプルであり、これにより(入力、ラベル)ペアに分割されます

この図は示していないfeaturesデータの軸線が、このsplit_window機能も処理label_columns 、それが単一の出力と多出力例の両方のために使用することができるように。

def split_window(self, features):
  inputs = features[:, self.input_slice, :]
  labels = features[:, self.labels_slice, :]
  if self.label_columns is not None:
    labels = tf.stack(
        [labels[:, :, self.column_indices[name]] for name in self.label_columns],
        axis=-1)

  # Slicing doesn't preserve static shape information, so set the shapes
  # manually. This way the `tf.data.Datasets` are easier to inspect.
  inputs.set_shape([None, self.input_width, None])
  labels.set_shape([None, self.label_width, None])

  return inputs, labels

WindowGenerator.split_window = split_window

やってみて:

# Stack three slices, the length of the total window.
example_window = tf.stack([np.array(train_df[:w2.total_window_size]),
                           np.array(train_df[100:100+w2.total_window_size]),
                           np.array(train_df[200:200+w2.total_window_size])])

example_inputs, example_labels = w2.split_window(example_window)

print('All shapes are: (batch, time, features)')
print(f'Window shape: {example_window.shape}')
print(f'Inputs shape: {example_inputs.shape}')
print(f'Labels shape: {example_labels.shape}')
All shapes are: (batch, time, features)
Window shape: (3, 7, 19)
Inputs shape: (3, 6, 19)
Labels shape: (3, 1, 1)

通常、TensorFlowのデータは、最も外側のインデックスが例全体にある配列にパックされます(「バッチ」ディメンション)。中央のインデックスは、「時間」または「スペース」(幅、高さ)の次元です。最も内側のインデックスは機能です。

上記のコードは、各タイムステップで19の機能を備えた3つの7タイムステップウィンドウのバッチを取りました。それらを6回のステップ19機能入力のバッチと1回のステップ1機能ラベルに分割します。ので、ラベルは唯一の特徴を有するWindowGenerator用いて初期化されたlabel_columns=['T (degC)']最初に、このチュートリアルでは、単一の出力ラベルを予測するモデルを構築します。

3.プロット

分割ウィンドウの簡単な視覚化を可能にするプロット方法は次のとおりです。

w2.example = example_inputs, example_labels
def plot(self, model=None, plot_col='T (degC)', max_subplots=3):
  inputs, labels = self.example
  plt.figure(figsize=(12, 8))
  plot_col_index = self.column_indices[plot_col]
  max_n = min(max_subplots, len(inputs))
  for n in range(max_n):
    plt.subplot(max_n, 1, n+1)
    plt.ylabel(f'{plot_col} [normed]')
    plt.plot(self.input_indices, inputs[n, :, plot_col_index],
             label='Inputs', marker='.', zorder=-10)

    if self.label_columns:
      label_col_index = self.label_columns_indices.get(plot_col, None)
    else:
      label_col_index = plot_col_index

    if label_col_index is None:
      continue

    plt.scatter(self.label_indices, labels[n, :, label_col_index],
                edgecolors='k', label='Labels', c='#2ca02c', s=64)
    if model is not None:
      predictions = model(inputs)
      plt.scatter(self.label_indices, predictions[n, :, label_col_index],
                  marker='X', edgecolors='k', label='Predictions',
                  c='#ff7f0e', s=64)

    if n == 0:
      plt.legend()

  plt.xlabel('Time [h]')

WindowGenerator.plot = plot

このプロットは、アイテムが参照する時間に基づいて、入力、ラベル、および(後で)予測を調整します。

w2.plot()

png

あなたは他の列をプロットすることができますが、例えば、ウィンドウw2設定はのみのラベルを持っているT (degC)の列。

w2.plot(plot_col='p (mbar)')

png

4.作成tf.data.Dataset

最後に、このmake_dataset方法は、時系列データフレームを取り、それを変換しますtf.data.Dataset(input_window, label_window)使用してペアpreprocessing.timeseries_dataset_from_array機能を:

def make_dataset(self, data):
  data = np.array(data, dtype=np.float32)
  ds = tf.keras.preprocessing.timeseries_dataset_from_array(
      data=data,
      targets=None,
      sequence_length=self.total_window_size,
      sequence_stride=1,
      shuffle=True,
      batch_size=32,)

  ds = ds.map(self.split_window)

  return ds

WindowGenerator.make_dataset = make_dataset

WindowGeneratorオブジェクトは、トレーニング、検証、テストデータを保持しています。

それらにアクセスするためのプロパティを追加しますtf.data.Dataset使用してのmake_dataset以前の定義されたメソッドを。また、簡単にアクセスしてプロットできるように、標準のサンプルバッチを追加します。

@property
def train(self):
  return self.make_dataset(self.train_df)

@property
def val(self):
  return self.make_dataset(self.val_df)

@property
def test(self):
  return self.make_dataset(self.test_df)

@property
def example(self):
  """Get and cache an example batch of `inputs, labels` for plotting."""
  result = getattr(self, '_example', None)
  if result is None:
    # No example batch was found, so get one from the `.train` dataset
    result = next(iter(self.train))
    # And cache it for next time
    self._example = result
  return result

WindowGenerator.train = train
WindowGenerator.val = val
WindowGenerator.test = test
WindowGenerator.example = example

さて、 WindowGeneratorオブジェクトはあなたにアクセスできますtf.data.Dataset使用すると、簡単にデータを反復することができますので、オブジェクト。

Dataset.element_specプロパティを使用すると、データセット要素の構造、データ型、および形状を伝えます。

# Each element is an (inputs, label) pair.
w2.train.element_spec
(TensorSpec(shape=(None, 6, 19), dtype=tf.float32, name=None),
 TensorSpec(shape=(None, 1, 1), dtype=tf.float32, name=None))

繰り返し処理Dataset具体的なバッチが得られます。

for example_inputs, example_labels in w2.train.take(1):
  print(f'Inputs shape (batch, time, features): {example_inputs.shape}')
  print(f'Labels shape (batch, time, features): {example_labels.shape}')
Inputs shape (batch, time, features): (32, 6, 19)
Labels shape (batch, time, features): (32, 1, 1)
2021-08-03 01:29:50.245637: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-08-03 01:29:50.246163: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000179999 Hz

シングルステップモデル

この種のデータに基づいて構築できる最も単純なモデルは、単一の機能の値を予測するモデルです。つまり、現在の条件のみに基づいて、将来の1タイムステップ(1時間)です。

だから、予測するモデルを構築することから始めT (degC)未来に値1の時間を。

次のタイムステップを予測する

設定WindowGeneratorこれらのシングルステップ生成するオブジェクト(input, label)対。

single_step_window = WindowGenerator(
    input_width=1, label_width=1, shift=1,
    label_columns=['T (degC)'])
single_step_window
Total window size: 2
Input indices: [0]
Label indices: [1]
Label column name(s): ['T (degC)']

windowオブジェクトが作成されますtf.data.Dataset 、簡単にデータのバッチを反復処理することができ、トレーニング、検証、およびテスト・セットから複数可。

for example_inputs, example_labels in single_step_window.train.take(1):
  print(f'Inputs shape (batch, time, features): {example_inputs.shape}')
  print(f'Labels shape (batch, time, features): {example_labels.shape}')
Inputs shape (batch, time, features): (32, 1, 19)
Labels shape (batch, time, features): (32, 1, 1)

ベースライン

トレーニング可能なモデルを構築する前に、後のより複雑なモデルと比較するためのポイントとしてパフォーマンスベースラインを用意しておくとよいでしょう。

この最初のタスクは、すべての機能の現在の値を前提として、1時間先の気温を予測することです。現在の値には、現在の温度が含まれます。

したがって、「変化なし」を予測して、現在の温度を予測として返すモデルから始めます。温度はゆっくり変化するため、これは妥当なベースラインです。もちろん、将来さらに予測を行うと、このベースラインはうまく機能しなくなります。

入力を出力に送信します

class Baseline(tf.keras.Model):
  def __init__(self, label_index=None):
    super().__init__()
    self.label_index = label_index

  def call(self, inputs):
    if self.label_index is None:
      return inputs
    result = inputs[:, :, self.label_index]
    return result[:, :, tf.newaxis]

このモデルをインスタンス化して評価します。

baseline = Baseline(label_index=column_indices['T (degC)'])

baseline.compile(loss=tf.losses.MeanSquaredError(),
                 metrics=[tf.metrics.MeanAbsoluteError()])

val_performance = {}
performance = {}
val_performance['Baseline'] = baseline.evaluate(single_step_window.val)
performance['Baseline'] = baseline.evaluate(single_step_window.test, verbose=0)
439/439 [==============================] - 1s 1ms/step - loss: 0.0128 - mean_absolute_error: 0.0785

それはいくつかのパフォーマンスメトリクスを印刷しましたが、それらはモデルがどれだけうまく機能しているかについての感覚をあなたに与えません。

WindowGenerator 、プロットの方法がありますが、プロットは、単一のサンプルで非常に興味深いものではないだろう。

だから、より広い作成WindowGeneratorウィンドウを一度に連続した入力とラベルの24時間を生成します。新しいwide_window変数は、モデルの動作方法を変更しません。モデルは、単一の入力時間ステップに基づいて、1時間先の予測を行います。ここで、 time軸のように作用するbatch軸:各予測は、時間ステップ間の相互作用なしで独立して行われます。

wide_window = WindowGenerator(
    input_width=24, label_width=24, shift=1,
    label_columns=['T (degC)'])

wide_window
Total window size: 25
Input indices: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
Label indices: [ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24]
Label column name(s): ['T (degC)']

この拡大ウィンドウは同じに直接渡すことができbaselineの任意のコードを変更せずにモデル。これが可能なのは、入力とラベルのタイムステップ数が同じであり、ベースラインが入力を出力に転送するだけだからです。

1時間先、1時間ごとの1つの予測。

print('Input shape:', wide_window.example[0].shape)
print('Output shape:', baseline(wide_window.example[0]).shape)
Input shape: (32, 24, 19)
Output shape: (32, 24, 1)

ベースラインモデルの予測をプロットすることにより、ラベルが1時間右にシフトされているだけであることに注意してください。

wide_window.plot(baseline)

png

上記の3つの例のプロットでは、シングルステップモデルが24時間にわたって実行されています。これはいくつかの説明に値します:

  • 青色のInputsラインは、各時間ステップでの入力温度を示しています。モデルはすべての特徴を受け取ります。このプロットは温度のみを示しています。
  • 緑色のLabelsドットは、目標予測値を示しました。これらのドットは、入力時間ではなく、予測時間に表示されます。そのため、ラベルの範囲は入力に対して1ステップシフトされます。
  • オレンジ色のPredictions十字は、各出力時間ステップのためのモデルの予測のです。モデルは完全に予測された場合は予測が上で直接上陸うLabels

線形モデル

あなたは、このタスクに適用することができ、最も簡単なトレーニング可能なモデルは、入力と出力の間の線形変換を挿入することです。この場合、タイムステップからの出力は、そのステップにのみ依存します。

シングルステップ予測

tf.keras.layers.Denseない層を有するactivationセットは、線形モデルです。層のみからのデータの最後の軸変換(batch, time, inputs)(batch, time, units) 。これは、全体ですべての項目に対して独立して適用されるbatchtime軸。

linear = tf.keras.Sequential([
    tf.keras.layers.Dense(units=1)
])
print('Input shape:', single_step_window.example[0].shape)
print('Output shape:', linear(single_step_window.example[0]).shape)
Input shape: (32, 1, 19)
2021-08-03 01:29:52.088029: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
Output shape: (32, 1, 1)
2021-08-03 01:29:52.467708: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11

このチュートリアルでは多くのモデルをトレーニングするため、トレーニング手順を関数にパッケージ化します。

MAX_EPOCHS = 20

def compile_and_fit(model, window, patience=2):
  early_stopping = tf.keras.callbacks.EarlyStopping(monitor='val_loss',
                                                    patience=patience,
                                                    mode='min')

  model.compile(loss=tf.losses.MeanSquaredError(),
                optimizer=tf.optimizers.Adam(),
                metrics=[tf.metrics.MeanAbsoluteError()])

  history = model.fit(window.train, epochs=MAX_EPOCHS,
                      validation_data=window.val,
                      callbacks=[early_stopping])
  return history

モデルをトレーニングし、そのパフォーマンスを評価します。

history = compile_and_fit(linear, single_step_window)

val_performance['Linear'] = linear.evaluate(single_step_window.val)
performance['Linear'] = linear.evaluate(single_step_window.test, verbose=0)
Epoch 1/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.3780 - mean_absolute_error: 0.3770 - val_loss: 0.0286 - val_mean_absolute_error: 0.1310
Epoch 2/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0156 - mean_absolute_error: 0.0945 - val_loss: 0.0105 - val_mean_absolute_error: 0.0761
Epoch 3/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0102 - mean_absolute_error: 0.0750 - val_loss: 0.0093 - val_mean_absolute_error: 0.0718
Epoch 4/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0095 - mean_absolute_error: 0.0717 - val_loss: 0.0089 - val_mean_absolute_error: 0.0700
Epoch 5/20
1534/1534 [==============================] - 4s 2ms/step - loss: 0.0092 - mean_absolute_error: 0.0704 - val_loss: 0.0088 - val_mean_absolute_error: 0.0697
Epoch 6/20
1534/1534 [==============================] - 4s 2ms/step - loss: 0.0091 - mean_absolute_error: 0.0700 - val_loss: 0.0087 - val_mean_absolute_error: 0.0694
Epoch 7/20
1534/1534 [==============================] - 4s 2ms/step - loss: 0.0091 - mean_absolute_error: 0.0697 - val_loss: 0.0087 - val_mean_absolute_error: 0.0697
Epoch 8/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0091 - mean_absolute_error: 0.0697 - val_loss: 0.0088 - val_mean_absolute_error: 0.0698
439/439 [==============================] - 1s 2ms/step - loss: 0.0088 - mean_absolute_error: 0.0698

同様にbaselineモデル、線形モデルは、大きな窓のバッチで呼び出すことができます。このように使用すると、モデルは連続するタイムステップで一連の独立した予測を行います。 time軸は別のような役割を果たしbatch軸。各タイムステップでの予測間に相互作用はありません。

シングルステップ予測

print('Input shape:', wide_window.example[0].shape)
print('Output shape:', baseline(wide_window.example[0]).shape)
Input shape: (32, 24, 19)
Output shape: (32, 24, 1)

ここでは上の例の予測をプロットしたものであるwide_window :多くの場合、予測は明らかに優れちょうど入力温度を返すよりますが、いくつかのケースでは、それは悪いことですかノート、

wide_window.plot(linear)

png

線形モデルの利点の1つは、解釈が比較的簡単なことです。レイヤーの重みを引き出して、各入力に割り当てられた重みを視覚化できます。

plt.bar(x = range(len(train_df.columns)),
        height=linear.layers[0].kernel[:,0].numpy())
axis = plt.gca()
axis.set_xticks(range(len(train_df.columns)))
_ = axis.set_xticklabels(train_df.columns, rotation=90)

png

時にはモデルでも入力に最も体重をかけないT (degC)これは、ランダムな初期化のリスクの1つです。

密集

実際に複数のタイムステップで動作するモデルを適用する前に、より深く、より強力な、単一入力ステップモデルのパフォーマンスを確認する価値があります。

こちらのモデルは、に似てlinearそれはいくつかのいくつかのスタックを除き、モデルDense入力と出力の間の層を:

dense = tf.keras.Sequential([
    tf.keras.layers.Dense(units=64, activation='relu'),
    tf.keras.layers.Dense(units=64, activation='relu'),
    tf.keras.layers.Dense(units=1)
])

history = compile_and_fit(dense, single_step_window)

val_performance['Dense'] = dense.evaluate(single_step_window.val)
performance['Dense'] = dense.evaluate(single_step_window.test, verbose=0)
Epoch 1/20
1534/1534 [==============================] - 6s 3ms/step - loss: 0.0143 - mean_absolute_error: 0.0809 - val_loss: 0.0078 - val_mean_absolute_error: 0.0638
Epoch 2/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0081 - mean_absolute_error: 0.0655 - val_loss: 0.0086 - val_mean_absolute_error: 0.0695
Epoch 3/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0075 - mean_absolute_error: 0.0627 - val_loss: 0.0070 - val_mean_absolute_error: 0.0597
Epoch 4/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0072 - mean_absolute_error: 0.0607 - val_loss: 0.0067 - val_mean_absolute_error: 0.0583
Epoch 5/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0071 - mean_absolute_error: 0.0603 - val_loss: 0.0066 - val_mean_absolute_error: 0.0572
Epoch 6/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0069 - mean_absolute_error: 0.0592 - val_loss: 0.0065 - val_mean_absolute_error: 0.0570
Epoch 7/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0068 - mean_absolute_error: 0.0585 - val_loss: 0.0072 - val_mean_absolute_error: 0.0608
Epoch 8/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0068 - mean_absolute_error: 0.0583 - val_loss: 0.0066 - val_mean_absolute_error: 0.0578
439/439 [==============================] - 1s 2ms/step - loss: 0.0066 - mean_absolute_error: 0.0578

マルチステップ高密度

シングルタイムステップモデルには、入力の現在の値のコンテキストがありません。入力機能が時間の経過とともにどのように変化しているかを確認することはできません。この問題に対処するには、モデルは予測を行うときに複数のタイムステップにアクセスする必要があります。

各予測には3つの時間ステップが使用されます。

baselinelinear及びdenseモデルは、それぞれ独立にタイムステップを取り扱います。ここで、モデルは単一の出力を生成するために入力として複数の時間ステップを取ります。

作成WindowGenerator 3時間の入力と1つの時間のラベルのバッチを生成します。

なお、 Windowshiftパラメータは、二つのウィンドウの端に相対的です。

CONV_WIDTH = 3
conv_window = WindowGenerator(
    input_width=CONV_WIDTH,
    label_width=1,
    shift=1,
    label_columns=['T (degC)'])

conv_window
Total window size: 4
Input indices: [0 1 2]
Label indices: [3]
Label column name(s): ['T (degC)']
conv_window.plot()
plt.title("Given 3 hours of inputs, predict 1 hour into the future.")
Text(0.5, 1.0, 'Given 3 hours of inputs, predict 1 hour into the future.')

png

あなたは訓練可能性がdense追加することによって、複数の入力ステップのウィンドウ上のモデルをtf.keras.layers.Flattenモデルの第一層として:

multi_step_dense = tf.keras.Sequential([
    # Shape: (time, features) => (time*features)
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(units=32, activation='relu'),
    tf.keras.layers.Dense(units=32, activation='relu'),
    tf.keras.layers.Dense(units=1),
    # Add back the time dimension.
    # Shape: (outputs) => (1, outputs)
    tf.keras.layers.Reshape([1, -1]),
])
print('Input shape:', conv_window.example[0].shape)
print('Output shape:', multi_step_dense(conv_window.example[0]).shape)
Input shape: (32, 3, 19)
Output shape: (32, 1, 1)
history = compile_and_fit(multi_step_dense, conv_window)

IPython.display.clear_output()
val_performance['Multi step dense'] = multi_step_dense.evaluate(conv_window.val)
performance['Multi step dense'] = multi_step_dense.evaluate(conv_window.test, verbose=0)
438/438 [==============================] - 1s 2ms/step - loss: 0.0061 - mean_absolute_error: 0.0539
conv_window.plot(multi_step_dense)

png

このアプローチの主な欠点は、結果のモデルが正確にこの形状の入力ウィンドウでのみ実行できることです。

print('Input shape:', wide_window.example[0].shape)
try:
  print('Output shape:', multi_step_dense(wide_window.example[0]).shape)
except Exception as e:
  print(f'\n{type(e).__name__}:{e}')
Input shape: (32, 24, 19)

ValueError:Input 0 of layer dense_4 is incompatible with the layer: expected axis -1 of input shape to have value 57 but received input with shape (32, 456)

次のセクションの畳み込みモデルは、この問題を修正します。

畳み込みニューラルネットワーク

畳み込み層( tf.keras.layers.Conv1D )、各予測への入力として複数の時間段階を要します。

以下は、同じモデルであるmulti_step_denseコンボリューションで再書かれました、。

変更点に注意してください。

conv_model = tf.keras.Sequential([
    tf.keras.layers.Conv1D(filters=32,
                           kernel_size=(CONV_WIDTH,),
                           activation='relu'),
    tf.keras.layers.Dense(units=32, activation='relu'),
    tf.keras.layers.Dense(units=1),
])

サンプルバッチで実行して、モデルが期待される形状の出力を生成することを確認します。

print("Conv model on `conv_window`")
print('Input shape:', conv_window.example[0].shape)
print('Output shape:', conv_model(conv_window.example[0]).shape)
Conv model on `conv_window`
Input shape: (32, 3, 19)
Output shape: (32, 1, 1)

電車とでそれを評価conv_window 、それはと同様の性能与える必要がありmulti_step_denseモデルを。

history = compile_and_fit(conv_model, conv_window)

IPython.display.clear_output()
val_performance['Conv'] = conv_model.evaluate(conv_window.val)
performance['Conv'] = conv_model.evaluate(conv_window.test, verbose=0)
438/438 [==============================] - 1s 2ms/step - loss: 0.0063 - mean_absolute_error: 0.0550

この違いconv_modelmulti_step_denseモデルがということですconv_model任意の長さの入力に実行することができます。畳み込み層は、入力のスライディングウィンドウに適用されます。

シーケンスで畳み込みモデルを実行する

より広い入力で実行すると、より広い出力が生成されます。

print("Wide window")
print('Input shape:', wide_window.example[0].shape)
print('Labels shape:', wide_window.example[1].shape)
print('Output shape:', conv_model(wide_window.example[0]).shape)
Wide window
Input shape: (32, 24, 19)
Labels shape: (32, 24, 1)
Output shape: (32, 22, 1)

出力は入力よりも短いことに注意してください。トレーニングまたはプロットを機能させるには、ラベルと予測が同じ長さである必要があります。だから、構築WindowGeneratorラベルと予測長さが一致していくつかの余分な入力の時間ステップで広い窓を生成します:

LABEL_WIDTH = 24
INPUT_WIDTH = LABEL_WIDTH + (CONV_WIDTH - 1)
wide_conv_window = WindowGenerator(
    input_width=INPUT_WIDTH,
    label_width=LABEL_WIDTH,
    shift=1,
    label_columns=['T (degC)'])

wide_conv_window
Total window size: 27
Input indices: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25]
Label indices: [ 3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]
Label column name(s): ['T (degC)']
print("Wide conv window")
print('Input shape:', wide_conv_window.example[0].shape)
print('Labels shape:', wide_conv_window.example[1].shape)
print('Output shape:', conv_model(wide_conv_window.example[0]).shape)
Wide conv window
Input shape: (32, 26, 19)
Labels shape: (32, 24, 1)
Output shape: (32, 24, 1)

これで、モデルの予測をより広いウィンドウにプロットできます。最初の予測の前の3つの入力時間ステップに注意してください。ここでのすべての予測は、先行する3つのタイムステップに基づいています。

wide_conv_window.plot(conv_model)

png

リカレントニューラルネットワーク

リカレントニューラルネットワーク(RNN)は、時系列データに適したニューラルネットワークの一種です。 RNNは時系列を段階的に処理し、時間ステップごとに内部状態を維持します。

あなたにはもっと学ぶことができるRNNのとテキスト生成チュートリアルとKerasのでリカレントニューラルネットワーク(RNN)ガイド。

このチュートリアルでは、長期短期記憶(と呼ばれるRNN層に使用するtf.keras.layers.LSTM )。

など、すべてのKeras RNN層、のための重要なコンストラクタ引数tf.keras.layers.LSTM 、あるreturn_sequences引数。この設定では、次の2つの方法のいずれかでレイヤーを構成できます。

  1. 場合False 、デフォルトでは、層は、単一の予測を行う前に、その内部状態をウォームアップするためのモデル時間を与え、最終時間ステップの出力を返します。

LSTMのウォーミングアップと単一の予測

  1. 場合はTrue 、層は、各入力に対する出力を返します。これは次の場合に役立ちます。
    • RNNレイヤーのスタッキング。
    • 複数のタイムステップで同時にモデルをトレーニングします。

すべてのタイムステップの後に予測を行うLSTM

lstm_model = tf.keras.models.Sequential([
    # Shape [batch, time, features] => [batch, time, lstm_units]
    tf.keras.layers.LSTM(32, return_sequences=True),
    # Shape => [batch, time, features]
    tf.keras.layers.Dense(units=1)
])

return_sequences=True 、モデルが一度にデータの24時間で訓練することができます。

print('Input shape:', wide_window.example[0].shape)
print('Output shape:', lstm_model(wide_window.example[0]).shape)
Input shape: (32, 24, 19)
Output shape: (32, 24, 1)
history = compile_and_fit(lstm_model, wide_window)

IPython.display.clear_output()
val_performance['LSTM'] = lstm_model.evaluate(wide_window.val)
performance['LSTM'] = lstm_model.evaluate(wide_window.test, verbose=0)
438/438 [==============================] - 1s 2ms/step - loss: 0.0056 - mean_absolute_error: 0.0514
wide_window.plot(lstm_model)

png

パフォーマンス

このデータセットでは、通常、各モデルは前のモデルよりもわずかに優れています。

x = np.arange(len(performance))
width = 0.3
metric_name = 'mean_absolute_error'
metric_index = lstm_model.metrics_names.index('mean_absolute_error')
val_mae = [v[metric_index] for v in val_performance.values()]
test_mae = [v[metric_index] for v in performance.values()]

plt.ylabel('mean_absolute_error [T (degC), normalized]')
plt.bar(x - 0.17, val_mae, width, label='Validation')
plt.bar(x + 0.17, test_mae, width, label='Test')
plt.xticks(ticks=x, labels=performance.keys(),
           rotation=45)
_ = plt.legend()

png

for name, value in performance.items():
  print(f'{name:12s}: {value[1]:0.4f}')
Baseline    : 0.0852
Linear      : 0.0682
Dense       : 0.0600
Multi step dense: 0.0549
Conv        : 0.0548
LSTM        : 0.0527

マルチ出力モデル

モデルは、これまでのすべての単一の出力機能、予測されたT (degC)単一の時間ステップのために、。

これらのモデルはすべて、単に出力層のユニット数を変更し、中にすべての機能が含まれるようにトレーニング窓を調整することにより、複数の機能を予測するために変換することができlabelsexample_labels ):

single_step_window = WindowGenerator(
    # `WindowGenerator` returns all features as labels if you 
    # don't set the `label_columns` argument.
    input_width=1, label_width=1, shift=1)

wide_window = WindowGenerator(
    input_width=24, label_width=24, shift=1)

for example_inputs, example_labels in wide_window.train.take(1):
  print(f'Inputs shape (batch, time, features): {example_inputs.shape}')
  print(f'Labels shape (batch, time, features): {example_labels.shape}')
Inputs shape (batch, time, features): (32, 24, 19)
Labels shape (batch, time, features): (32, 24, 19)

なお、上記featuresの代わりに、ラベルの軸が現在入力と同じ深さを有している1

ベースライン

同じベースラインモデル( Baseline )ここで使用することができますが、今回はすべての機能を繰り返す代わりに、特定の選択label_index

baseline = Baseline()
baseline.compile(loss=tf.losses.MeanSquaredError(),
                 metrics=[tf.metrics.MeanAbsoluteError()])
val_performance = {}
performance = {}
val_performance['Baseline'] = baseline.evaluate(wide_window.val)
performance['Baseline'] = baseline.evaluate(wide_window.test, verbose=0)
438/438 [==============================] - 1s 1ms/step - loss: 0.0886 - mean_absolute_error: 0.1589

密集

dense = tf.keras.Sequential([
    tf.keras.layers.Dense(units=64, activation='relu'),
    tf.keras.layers.Dense(units=64, activation='relu'),
    tf.keras.layers.Dense(units=num_features)
])
history = compile_and_fit(dense, single_step_window)

IPython.display.clear_output()
val_performance['Dense'] = dense.evaluate(single_step_window.val)
performance['Dense'] = dense.evaluate(single_step_window.test, verbose=0)
439/439 [==============================] - 1s 2ms/step - loss: 0.0690 - mean_absolute_error: 0.1302

RNN

%%time
wide_window = WindowGenerator(
    input_width=24, label_width=24, shift=1)

lstm_model = tf.keras.models.Sequential([
    # Shape [batch, time, features] => [batch, time, lstm_units]
    tf.keras.layers.LSTM(32, return_sequences=True),
    # Shape => [batch, time, features]
    tf.keras.layers.Dense(units=num_features)
])

history = compile_and_fit(lstm_model, wide_window)

IPython.display.clear_output()
val_performance['LSTM'] = lstm_model.evaluate( wide_window.val)
performance['LSTM'] = lstm_model.evaluate( wide_window.test, verbose=0)

print()
438/438 [==============================] - 1s 2ms/step - loss: 0.0617 - mean_absolute_error: 0.1210

CPU times: user 3min 47s, sys: 51.9 s, total: 4min 38s
Wall time: 1min 29s

高度:残りの接続

Baseline以前からモデルはシーケンスは時間ステップに時間ステップから劇的に変化していないという事実を利用しました。これまでにこのチュートリアルでトレーニングされたすべてのモデルはランダムに初期化され、出力が前のタイムステップからの小さな変更であることを学習する必要がありました。

注意深く初期化することでこの問題を回避できますが、これをモデル構造に組み込む方が簡単です。

時系列分析では、次の値を予測する代わりに、次のタイムステップで値がどのように変化するかを予測するモデルを構築するのが一般的です。同様に、残留ネットワークResNets-の深い学習-orは、それぞれの層は、モデルの蓄積の結果に追加されますアーキテクチャを参照してください。

このようにして、変化は小さいはずであるという知識を活用します。

残余接続のあるモデル

基本的に、これが一致するように、モデル初期化Baseline 。このタスクでは、モデルの収束が速くなり、パフォーマンスがわずかに向上します。

このアプローチは、このチュートリアルで説明する任意のモデルと組み合わせて使用​​できます。

ここでは、使用に注意し、LSTMモデルに適用されているtf.initializers.zeros初期予測の変化が小さく、残留接続圧倒しないようにすることを。以来、グラデーションの対称性破りの懸念が、ここではありませんzeros唯一の最後の層の上に使用されています。

class ResidualWrapper(tf.keras.Model):
  def __init__(self, model):
    super().__init__()
    self.model = model

  def call(self, inputs, *args, **kwargs):
    delta = self.model(inputs, *args, **kwargs)

    # The prediction for each time step is the input
    # from the previous time step plus the delta
    # calculated by the model.
    return inputs + delta
%%time
residual_lstm = ResidualWrapper(
    tf.keras.Sequential([
    tf.keras.layers.LSTM(32, return_sequences=True),
    tf.keras.layers.Dense(
        num_features,
        # The predicted deltas should start small.
        # Therefore, initialize the output layer with zeros.
        kernel_initializer=tf.initializers.zeros())
]))

history = compile_and_fit(residual_lstm, wide_window)

IPython.display.clear_output()
val_performance['Residual LSTM'] = residual_lstm.evaluate(wide_window.val)
performance['Residual LSTM'] = residual_lstm.evaluate(wide_window.test, verbose=0)
print()
438/438 [==============================] - 1s 2ms/step - loss: 0.0619 - mean_absolute_error: 0.1180

CPU times: user 1min 49s, sys: 25.1 s, total: 2min 14s
Wall time: 43.4 s

パフォーマンス

これらのマルチ出力モデルの全体的なパフォーマンスは次のとおりです。

x = np.arange(len(performance))
width = 0.3

metric_name = 'mean_absolute_error'
metric_index = lstm_model.metrics_names.index('mean_absolute_error')
val_mae = [v[metric_index] for v in val_performance.values()]
test_mae = [v[metric_index] for v in performance.values()]

plt.bar(x - 0.17, val_mae, width, label='Validation')
plt.bar(x + 0.17, test_mae, width, label='Test')
plt.xticks(ticks=x, labels=performance.keys(),
           rotation=45)
plt.ylabel('MAE (average over all outputs)')
_ = plt.legend()

png

for name, value in performance.items():
  print(f'{name:15s}: {value[1]:0.4f}')
Baseline       : 0.1638
Dense          : 0.1321
LSTM           : 0.1229
Residual LSTM  : 0.1191

上記のパフォーマンスは、すべてのモデル出力で平均化されています。

マルチステップモデル

前のセクションの両方において単一出力と多出力モデルは、将来に単一の時間ステップの予測、一時間を作りました。

このセクションでは、複数の時間ステップの予測を行うために、これらのモデルを拡張する方法を調べます。

マルチステップ予測では、モデルは将来の値の範囲を予測することを学習する必要があります。したがって、単一の将来のポイントのみが予測されるシングルステップモデルとは異なり、マルチステップモデルは一連の将来の値を予測します。

これには2つの大まかなアプローチがあります。

  1. 時系列全体が一度に予測されるシングルショット予測。
  2. モデルがシングルステップ予測のみを行い、その出力が入力としてフィードバックされる自己回帰予測。

このセクションでは、すべてのモデルは、すべての出力時間ステップ全体のすべての機能を予測します。

マルチステップモデルの場合、トレーニングデータも1時間ごとのサンプルで構成されます。ただし、ここでは、過去24時間の場合、モデルは24時間先の予測を学習します。

ここでWindowデータセットからこれらのスライスを生成したオブジェクトは:

OUT_STEPS = 24
multi_window = WindowGenerator(input_width=24,
                               label_width=OUT_STEPS,
                               shift=OUT_STEPS)

multi_window.plot()
multi_window
Total window size: 48
Input indices: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
Label indices: [24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47]
Label column name(s): None

png

ベースライン

このタスクの単純なベースラインは、必要な数の出力タイムステップに対して最後の入力タイムステップを繰り返すことです。

出力ステップごとに、最後の入力を繰り返します

class MultiStepLastBaseline(tf.keras.Model):
  def call(self, inputs):
    return tf.tile(inputs[:, -1:, :], [1, OUT_STEPS, 1])

last_baseline = MultiStepLastBaseline()
last_baseline.compile(loss=tf.losses.MeanSquaredError(),
                      metrics=[tf.metrics.MeanAbsoluteError()])

multi_val_performance = {}
multi_performance = {}

multi_val_performance['Last'] = last_baseline.evaluate(multi_window.val)
multi_performance['Last'] = last_baseline.evaluate(multi_window.test, verbose=0)
multi_window.plot(last_baseline)
437/437 [==============================] - 1s 1ms/step - loss: 0.6285 - mean_absolute_error: 0.5007

png

このタスクは、過去24時間の場合、24時間先を予測することであるため、別の簡単なアプローチは、明日が同様であると想定して、前日を繰り返すことです。

前日を繰り返す

class RepeatBaseline(tf.keras.Model):
  def call(self, inputs):
    return inputs

repeat_baseline = RepeatBaseline()
repeat_baseline.compile(loss=tf.losses.MeanSquaredError(),
                        metrics=[tf.metrics.MeanAbsoluteError()])

multi_val_performance['Repeat'] = repeat_baseline.evaluate(multi_window.val)
multi_performance['Repeat'] = repeat_baseline.evaluate(multi_window.test, verbose=0)
multi_window.plot(repeat_baseline)
437/437 [==============================] - 1s 2ms/step - loss: 0.4270 - mean_absolute_error: 0.3959

png

単発モデル

この問題に対する高レベルのアプローチの1つは、「シングルショット」モデルを使用することです。このモデルでは、シーケンス全体の予測が1つのステップで行われます。

これは、次のように効率的に実装することができtf.keras.layers.DenseOUT_STEPS*features出力ユニットを。モデルは、単に必要にその出力を再構築する必要がある(OUTPUT_STEPS, features)

線形

最後の入力時間ステップに基づく単純な線形モデルは、どちらのベースラインよりも優れていますが、パワーが不足しています。モデルが予測する必要がOUTPUT_STEPS線状突起を有する単一の入力の時間ステップから時間ステップを。これは、主に時刻と時刻に基づいて、動作の低次元のスライスのみをキャプチャできます。

最後のタイムステップからのすべてのタイムステップを予測します

multi_linear_model = tf.keras.Sequential([
    # Take the last time-step.
    # Shape [batch, time, features] => [batch, 1, features]
    tf.keras.layers.Lambda(lambda x: x[:, -1:, :]),
    # Shape => [batch, 1, out_steps*features]
    tf.keras.layers.Dense(OUT_STEPS*num_features,
                          kernel_initializer=tf.initializers.zeros()),
    # Shape => [batch, out_steps, features]
    tf.keras.layers.Reshape([OUT_STEPS, num_features])
])

history = compile_and_fit(multi_linear_model, multi_window)

IPython.display.clear_output()
multi_val_performance['Linear'] = multi_linear_model.evaluate(multi_window.val)
multi_performance['Linear'] = multi_linear_model.evaluate(multi_window.test, verbose=0)
multi_window.plot(multi_linear_model)
437/437 [==============================] - 1s 2ms/step - loss: 0.2555 - mean_absolute_error: 0.3052

png

密集

追加tf.keras.layers.Dense入力と出力の間には線形モデルより多くの電力を与えるが、依然として単一の入力の時間ステップに基づいています。

multi_dense_model = tf.keras.Sequential([
    # Take the last time step.
    # Shape [batch, time, features] => [batch, 1, features]
    tf.keras.layers.Lambda(lambda x: x[:, -1:, :]),
    # Shape => [batch, 1, dense_units]
    tf.keras.layers.Dense(512, activation='relu'),
    # Shape => [batch, out_steps*features]
    tf.keras.layers.Dense(OUT_STEPS*num_features,
                          kernel_initializer=tf.initializers.zeros()),
    # Shape => [batch, out_steps, features]
    tf.keras.layers.Reshape([OUT_STEPS, num_features])
])

history = compile_and_fit(multi_dense_model, multi_window)

IPython.display.clear_output()
multi_val_performance['Dense'] = multi_dense_model.evaluate(multi_window.val)
multi_performance['Dense'] = multi_dense_model.evaluate(multi_window.test, verbose=0)
multi_window.plot(multi_dense_model)
437/437 [==============================] - 1s 2ms/step - loss: 0.2214 - mean_absolute_error: 0.2850

png

CNN

畳み込みモデルは、固定幅の履歴に基づいて予測を行います。これにより、時間の経過とともに状況がどのように変化するかを確認できるため、密なモデルよりもパフォーマンスが向上する可能性があります。

畳み込みモデルは、物事が時間とともにどのように変化するかを確認します

CONV_WIDTH = 3
multi_conv_model = tf.keras.Sequential([
    # Shape [batch, time, features] => [batch, CONV_WIDTH, features]
    tf.keras.layers.Lambda(lambda x: x[:, -CONV_WIDTH:, :]),
    # Shape => [batch, 1, conv_units]
    tf.keras.layers.Conv1D(256, activation='relu', kernel_size=(CONV_WIDTH)),
    # Shape => [batch, 1,  out_steps*features]
    tf.keras.layers.Dense(OUT_STEPS*num_features,
                          kernel_initializer=tf.initializers.zeros()),
    # Shape => [batch, out_steps, features]
    tf.keras.layers.Reshape([OUT_STEPS, num_features])
])

history = compile_and_fit(multi_conv_model, multi_window)

IPython.display.clear_output()

multi_val_performance['Conv'] = multi_conv_model.evaluate(multi_window.val)
multi_performance['Conv'] = multi_conv_model.evaluate(multi_window.test, verbose=0)
multi_window.plot(multi_conv_model)
437/437 [==============================] - 1s 2ms/step - loss: 0.2136 - mean_absolute_error: 0.2788

png

RNN

反復モデルは、モデルが行っている予測に関連している場合、入力の長い履歴を使用することを学習できます。ここで、モデルは24時間内部状態を蓄積してから、次の24時間の単一の予測を行います。

このシングルショット形式で、LSTMは、最後の時間ステップので、セットの出力を生成するために必要return_sequences=Falsetf.keras.layers.LSTM

LSTMは入力ウィンドウ全体で状態を蓄積し、次の24時間の単一の予測を行います

multi_lstm_model = tf.keras.Sequential([
    # Shape [batch, time, features] => [batch, lstm_units].
    # Adding more `lstm_units` just overfits more quickly.
    tf.keras.layers.LSTM(32, return_sequences=False),
    # Shape => [batch, out_steps*features].
    tf.keras.layers.Dense(OUT_STEPS*num_features,
                          kernel_initializer=tf.initializers.zeros()),
    # Shape => [batch, out_steps, features].
    tf.keras.layers.Reshape([OUT_STEPS, num_features])
])

history = compile_and_fit(multi_lstm_model, multi_window)

IPython.display.clear_output()

multi_val_performance['LSTM'] = multi_lstm_model.evaluate(multi_window.val)
multi_performance['LSTM'] = multi_lstm_model.evaluate(multi_window.test, verbose=0)
multi_window.plot(multi_lstm_model)
437/437 [==============================] - 1s 2ms/step - loss: 0.2147 - mean_absolute_error: 0.2866

png

高度:自己回帰モデル

上記のモデルはすべて、単一のステップで出力シーケンス全体を予測します。

場合によっては、モデルがこの予測を個々の時間ステップに分解すると役立つことがあります。次に、各モデルの出力は、各ステップで自身にフィードバックすることができ、予測は古典のように、以前のものに条件付け行うことができるの生成の配列を有するリカレントニューラルネットワーク

このスタイルのモデルの明らかな利点の1つは、さまざまな長さの出力を生成するように設定できることです。

このチュートリアルの前半でトレーニングされたシングルステップマルチ出力モデルのいずれかを使用して、自己回帰フィードバックループで実行できますが、ここでは、それを行うように明示的にトレーニングされたモデルの構築に焦点を当てます。

モデルの出力を入力にフィードバックする

RNN

このチュートリアルでは、自己回帰RNNモデルのみを構築しますが、このパターンは、単一のタイムステップを出力するように設計された任意のモデルに適用できます。

モデルは、以前からシングルステップLSTMモデルと同じ基本形状を有することになる。 tf.keras.layers.LSTM層が続くtf.keras.layers.Dense変換層LSTMモデル予測にレイヤの出力を。

A tf.keras.layers.LSTMあるtf.keras.layers.LSTMCell 、より高いレベルに包まれtf.keras.layers.RNNチェックアウト(あなたのための状態やシーケンスの結果を管理Kerasとリカレントニューラルネットワーク(RNN)を詳細についてはガイド)。

この場合、モデルは、それが使用するように手動で、各ステップのための入力を管理しなければならないtf.keras.layers.LSTMCellより低いレベル、単一の時間ステップのインタフェースに直接。

class FeedBack(tf.keras.Model):
  def __init__(self, units, out_steps):
    super().__init__()
    self.out_steps = out_steps
    self.units = units
    self.lstm_cell = tf.keras.layers.LSTMCell(units)
    # Also wrap the LSTMCell in an RNN to simplify the `warmup` method.
    self.lstm_rnn = tf.keras.layers.RNN(self.lstm_cell, return_state=True)
    self.dense = tf.keras.layers.Dense(num_features)
feedback_model = FeedBack(units=32, out_steps=OUT_STEPS)

このモデルが必要最初の方法は、 warmup入力に基づいてその内部状態を初期化する方法。トレーニングが完了すると、この状態は入力履歴の関連部分をキャプチャします。これは、シングルステップに相当しLSTM以前からのモデル:

def warmup(self, inputs):
  # inputs.shape => (batch, time, features)
  # x.shape => (batch, lstm_units)
  x, *state = self.lstm_rnn(inputs)

  # predictions.shape => (batch, features)
  prediction = self.dense(x)
  return prediction, state

FeedBack.warmup = warmup

この方法は、単一の時間ステップの予測との内部状態を返しLSTM

prediction, state = feedback_model.warmup(multi_window.example[0])
prediction.shape
TensorShape([32, 19])

RNNの状態、および初期予測あなたは今、入力として、各ステップバックで予測を送りモデルを反復継続することができます。

出力予測を収集するための最も簡単な方法は、Pythonのリストと使用することですtf.stackループの後に。

def call(self, inputs, training=None):
  # Use a TensorArray to capture dynamically unrolled outputs.
  predictions = []
  # Initialize the LSTM state.
  prediction, state = self.warmup(inputs)

  # Insert the first prediction.
  predictions.append(prediction)

  # Run the rest of the prediction steps.
  for n in range(1, self.out_steps):
    # Use the last prediction as input.
    x = prediction
    # Execute one lstm step.
    x, state = self.lstm_cell(x, states=state,
                              training=training)
    # Convert the lstm output to a prediction.
    prediction = self.dense(x)
    # Add the prediction to the output.
    predictions.append(prediction)

  # predictions.shape => (time, batch, features)
  predictions = tf.stack(predictions)
  # predictions.shape => (batch, time, features)
  predictions = tf.transpose(predictions, [1, 0, 2])
  return predictions

FeedBack.call = call

入力例でこのモデルをテスト実行します。

print('Output shape (batch, time, features): ', feedback_model(multi_window.example[0]).shape)
Output shape (batch, time, features):  (32, 24, 19)

次に、モデルをトレーニングします。

history = compile_and_fit(feedback_model, multi_window)

IPython.display.clear_output()

multi_val_performance['AR LSTM'] = feedback_model.evaluate(multi_window.val)
multi_performance['AR LSTM'] = feedback_model.evaluate(multi_window.test, verbose=0)
multi_window.plot(feedback_model)
437/437 [==============================] - 3s 7ms/step - loss: 0.2267 - mean_absolute_error: 0.3020

png

パフォーマンス

この問題のモデルの複雑さの関数として、収穫逓減が明らかにあります。

x = np.arange(len(multi_performance))
width = 0.3

metric_name = 'mean_absolute_error'
metric_index = lstm_model.metrics_names.index('mean_absolute_error')
val_mae = [v[metric_index] for v in multi_val_performance.values()]
test_mae = [v[metric_index] for v in multi_performance.values()]

plt.bar(x - 0.17, val_mae, width, label='Validation')
plt.bar(x + 0.17, test_mae, width, label='Test')
plt.xticks(ticks=x, labels=multi_performance.keys(),
           rotation=45)
plt.ylabel(f'MAE (average over all times and outputs)')
_ = plt.legend()

png

このチュートリアルの前半のマルチ出力モデルのメトリックは、すべての出力機能にわたって平均されたパフォーマンスを示しています。これらのパフォーマンスは似ていますが、出力タイムステップ全体で平均化されています。

for name, value in multi_performance.items():
  print(f'{name:8s}: {value[1]:0.4f}')
Last    : 0.5157
Repeat  : 0.3774
Linear  : 0.2984
Dense   : 0.2800
Conv    : 0.2727
LSTM    : 0.2755
AR LSTM : 0.2903

密なモデルから畳み込みモデルおよび反復モデルに移行することで達成される利益は、(もしあれば)ほんの数パーセントであり、自己回帰モデルのパフォーマンスは明らかに劣っています。これらのより複雑なアプローチがこの問題の価値がないかもしれませんが、しようとせず知る方法はありませんでした、ので、これらのモデルは、あなたの問題のために有用である可能性があります。

次のステップ

このチュートリアルは、TensorFlowを使用した時系列予測の簡単な紹介でした。

詳細については、以下を参照してください。

また、あなたがどんな実装できることを覚えて、古典的な時系列モデルでTensorFlowを-このチュートリアルでは、ちょうどTensorFlowの組み込み機能に焦点を当てています。