Google I / O'daki önemli notları, ürün oturumlarını, atölyeleri ve daha fazlasını izleyin Oynatma listesine bakın

Zaman serisi tahmini

TensorFlow.org'da görüntüleyin Google Colab'de çalıştırın Kaynağı GitHub'da görüntüleyin Defteri indirin

Bu eğitim, TensorFlow kullanarak zaman serisi tahminine giriş niteliğindedir. Evrişimli ve Tekrarlayan Sinir Ağları (CNN'ler ve RNN'ler) dahil olmak üzere birkaç farklı model stili oluşturur.

Bu, alt bölümlerle birlikte iki ana bölümde ele alınmaktadır:

  • Tek bir zaman adımı için tahmin:
    • Tek bir özellik.
    • Tüm özellikler.
  • Birden çok adımı tahmin edin:
    • Tek seferlik: Tahminleri tek seferde yapın.
    • Otoregresif: Her seferinde bir tahmin yapın ve çıktıyı modele geri besleyin.

Kurulum

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

Hava durumu veri kümesi

Bu eğitim, Max Planck Biyojeokimya Enstitüsü tarafından kaydedilen bir hava durumu zaman serisi veri kümesini kullanır.

Bu veri seti, hava sıcaklığı, atmosferik basınç ve nem gibi 14 farklı özellik içerir. Bunlar, 2003'ten başlayarak her 10 dakikada bir toplandı. Verimlilik için, yalnızca 2009 ile 2016 arasında toplanan verileri kullanacaksınız. Veri kümesinin bu bölümü, François Chollet tarafından Python ile Derin Öğrenme kitabı için hazırlanmıştır.

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 [==============================] - 0s 0us/step

Bu eğitici yalnızca saatlik tahminleri ele alacaktır, bu nedenle verileri 10 dakikalık aralıklardan 1 saate kadar alt örnekleyerek başlayın:

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')

Verilere bir göz atalım. İşte ilk birkaç satır:

df.head()

İşte birkaç özelliğin zaman içindeki gelişimi.

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

İnceleyin ve temizleyin

Daha sonra veri kümesinin istatistiklerine bakın:

df.describe().transpose()

Rüzgar hızı

wv (m/s) gereken bir şey, rüzgar hızının min değeri, wv (m/s) ve max. wv (m/s) sütunları. Bu -9999 muhtemelen hatalı. Ayrı bir rüzgar yönü sütunu vardır, bu nedenle hız >=0 olmalıdır. Sıfırlarla değiştirin:

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

Özellik mühendisliği

Bir model oluşturmaya başlamadan önce verilerinizi anlamak ve modeli uygun şekilde biçimlendirilmiş verileri aktardığınızdan emin olmak önemlidir.

Rüzgar

Verilerin son sütunu olan wd (deg) rüzgar yönünü derece birimlerinde verir. Açılar iyi model girdileri yapmaz, 360 ° ve 0 ° birbirine yakın olmalı ve etrafı düzgünce sarılmalıdır. Rüzgar esmiyorsa yön önemli olmamalı.

Şu anda rüzgar verilerinin dağılımı şu şekildedir:

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

Ancak rüzgar yönü ve hız sütunlarını bir rüzgar vektörüne dönüştürürseniz, bunu modelin yorumlaması daha kolay olacaktır:

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)

Rüzgar vektörlerinin dağılımı, modelin doğru şekilde yorumlaması için çok daha basittir.

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

Zaman

Benzer şekilde, Date Time sütunu çok kullanışlıdır, ancak bu dize biçiminde değildir. Saniyeye dönüştürerek başlayın:

timestamp_s = date_time.map(datetime.datetime.timestamp)

Rüzgar yönüne benzer şekilde, saniye cinsinden süre kullanışlı bir model girdisi değildir. Hava durumu verisi olduğu için net günlük ve yıllık periyodikliğe sahiptir. Dönemsellikle başa çıkmanın birçok yolu vardır.

Bunu kullanılabilir bir sinyale dönüştürmenin basit bir yolu, "Günün saati" ve "Yılın saati" sinyallerini temizleyecek şekilde zamanı dönüştürmek için sin ve cos kullanmaktır:

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

Bu, modele en önemli frekans özelliklerine erişim sağlar. Bu durumda, hangi frekansların önemli olduğunu önceden biliyordunuz.

Bilmiyorsanız, bir fft kullanarak hangi frekansların önemli olduğunu belirleyebilirsiniz. Varsayımlarımızı kontrol etmek için, işte zaman içindeki sıcaklığın tf.signal.rfft . 1/year ve 1/day yakın frekanslardaki belirgin zirvelere dikkat edin:

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)')

png

Verileri böl

Eğitim, doğrulama ve test setleri için (70%, 20%, 10%) bölme kullanacağız. Rasgele bölme önce karıştırılan olmayan veriler edin. Bu iki nedenden dolayıdır.

  1. Verilerin ardışık örneklerin pencerelerine bölünmesinin hala mümkün olmasını sağlar.
  2. Model eğitildikten sonra toplanan veriler üzerinden değerlendirilerek doğrulama / test sonuçlarının daha gerçekçi olmasını sağlar.
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]

Verileri normalleştirin

Bir sinir ağını eğitmeden önce özellikleri ölçeklendirmek önemlidir. Normalleştirme, bu ölçeklendirmeyi yapmanın yaygın bir yoludur. Ortalamayı çıkarın ve her özelliğin standart sapmasına bölün.

Ortalama ve standart sapma, modellerin doğrulama ve test setlerindeki değerlere erişimi olmaması için yalnızca eğitim verileri kullanılarak hesaplanmalıdır.

Modelin eğitim sırasında eğitim setindeki gelecekteki değerlere erişememesi ve bu normalleştirmenin hareketli ortalamalar kullanılarak yapılması gerektiği de tartışılabilir. Bu eğitimin odak noktası bu değildir ve doğrulama ve test setleri (bir şekilde) dürüst ölçümler elde etmenizi sağlar. Dolayısıyla, basitlik açısından bu eğitimde basit bir ortalama kullanılmıştır.

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

Şimdi özelliklerin dağılımına göz atın. Bazı özelliklerin uzun kuyrukları vardır, ancak -9999 rüzgar hızı değeri gibi belirgin bir hata yoktur.

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

Veri pencereleme

Bu eğitimdeki modeller, verilerden ardışık örneklerden oluşan bir pencereye dayalı olarak bir dizi tahmin yapacaktır.

Giriş pencerelerinin ana özellikleri şunlardır:

  • Giriş ve etiket pencerelerinin genişliği (zaman adımlarının sayısı)
  • Aralarındaki zaman farkı.
  • Hangi özellikler giriş, etiket veya her ikisi olarak kullanılır.

Bu eğitim, çeşitli modeller (Doğrusal, DNN, CNN ve RNN modelleri dahil) oluşturur ve bunları her ikisi için kullanır:

  • Tek çıkışlı ve çoklu çıkışlı tahminler.
  • Tek zaman adımlı ve çok zaman adımlı tahminler.

Bu bölüm, tüm bu modeller için yeniden kullanılabilmesi için veri pencerelemesinin uygulanmasına odaklanmaktadır.

Göreve ve modelin türüne bağlı olarak, çeşitli veri pencereleri oluşturmak isteyebilirsiniz. İşte bazı örnekler:

  1. Örneğin, 24 saatlik geçmişe bakıldığında, geleceğe 24h tek bir tahmin yapmak için aşağıdaki gibi bir pencere tanımlayabilirsiniz:

    Geleceğe yönelik 24 saatlik bir tahmin.

  2. 6 saatlik tarih verildiğinde, geleceğe 1h tahmini yapan bir modelin böyle bir pencereye ihtiyacı olacaktır:

    Geleceğe yönelik 1 saat tahmini.

Bu bölümün geri kalanı bir WindowGenerator sınıfını tanımlar. Bu sınıf şunları yapabilir:

  1. Yukarıdaki diyagramlarda gösterildiği gibi indeksleri ve ofsetleri kullanın.
  2. Unsur pencerelerini bir (features, labels) çiftlerine ayırın.
  3. Ortaya çıkan pencerelerin içeriğini çizin.
  4. tf.data.Dataset s kullanarak eğitim, değerlendirme ve test verilerinden bu pencerelerin toplutf.data.Dataset verimli bir şekilde oluşturun.

1. Endeksler ve ofsetler

WindowGenerator sınıfını oluşturarak başlayın. __init__ yöntemi, giriş ve etiket indeksleri için gerekli tüm mantığı içerir.

Aynı zamanda veri çerçevelerini girdi olarak eğitme, değerlendirme ve test etme işlemlerini de alır. Bunlar daha sonratf.data.Dataset s 'e dönüştürülecektir.

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}'])

İşte bu bölümün başındaki şemalarda gösterilen 2 pencereyi oluşturmak için kod:

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. Böl

Ardışık girişlerin bir listesi verildiğinde, split_window yöntemi onları bir giriş penceresine ve bir etiket penceresine dönüştürecektir.

Yukarıdaki w2 örneği şu şekilde bölünecektir:

İlk pencere tüm ardışık örneklerdir, bu onu bir (girişler, etiketler) çiftlere böler

Göstermez Bu diyagram features veri eksenine, ancak bu split_window fonksiyonu da kolları label_columns bunun tek bir çıkış ve çoklu çıktı her iki örnek için de kullanılabilir, böylece.

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

Denemek:

# 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)

Tipik olarak TensorFlow'daki veriler, en dıştaki dizinin örnekler arasında olduğu diziler halinde paketlenir ("toplu iş" boyutu). Ortadaki endeksler "zaman" veya "boşluk" (genişlik, yükseklik) boyut (lar) ıdır. En içteki endeksler özelliklerdir.

Yukarıdaki kod, her adımda 19 özellik içeren, 3, 7 zamanlı aralıklardan oluşan bir grup aldı. Bunları 6 zaman adımlı bir grup, 19 özellik girişi ve 1 zaman adımlı 1 özellikli etikete ayırır. WindowGenerator , label_columns=['T (degC)'] ile başlatıldığından, etiketin yalnızca bir özelliği vardır. Başlangıçta bu eğitim, tek çıktı etiketlerini tahmin eden modeller oluşturacaktır.

3. Konu

Bölünmüş pencerenin basit bir şekilde görselleştirilmesine izin veren bir çizim yöntemi:

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

Bu grafik, girdileri, etiketleri ve (sonraki) tahminleri öğenin başvurduğu zamana göre hizalar:

w2.plot()

png

Diğer sütunların grafiğini w2 , ancak örnek pencere w2 yapılandırması yalnızca T (degC) sütunu için etiketlere sahiptir.

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

png

4.tf.data.Dataset s oluşturun

Nihayet bu make_dataset yöntemi zaman serilerini alacak DataFrame ve bir dönüştürmektf.data.Dataset arasında (input_window, label_window) kullanarak çiftleri preprocessing.timeseries_dataset_from_array fonksiyonu.

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 nesnesi eğitim, doğrulama ve test verilerini tutar. Yukarıdaki make_dataset yöntemini kullanarak tf.data.Datasets olarak tf.data.Datasets erişmek için özellikler ekleyin. Ayrıca kolay erişim ve çizim için standart bir örnek grup ekleyin:

@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

Artık WindowGenerator nesnesi,tf.data.Dataset nesnelerine erişmenizi sağlar, böylece veriler üzerinde kolayca yineleme yapabilirsiniz.

Dataset.element_spec özelliği, veri kümesi öğelerinin yapısını, dtypes ve şekillerini size söyler.

# 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))

Bir Dataset üzerinde yineleme yapmak somut gruplar verir:

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)

Tek adımlı modeller

Bu tür veriler üzerine inşa edebileceğiniz en basit model, tek bir özelliğin değerini, yalnızca mevcut koşullara dayalı olarak gelecekte 1 zaman aşımı (1 saat) tahmin eden bir modeldir.

Öyleyse, gelecekte 1h T (degC) değerini tahmin etmek için modeller oluşturarak başlayın.

Bir sonraki adımı tahmin edin

Bu tek adımlı (input, label) çiftlerini üretmek için bir WindowGenerator nesnesi yapılandırın:

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 nesnesi, eğitim, doğrulama ve test kümelerinden tf.data.Datasets oluşturarak veri yığınları üzerinde kolayca yineleme yapmanıza olanak tanır.

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)

Temel

Eğitilebilir bir model oluşturmadan önce, daha sonraki daha karmaşık modellerle karşılaştırma noktası olarak bir performans taban çizgisine sahip olmak iyi olacaktır.

Bu ilk görev, tüm özelliklerin mevcut değeri göz önüne alındığında gelecekte sıcaklığı 1h tahmin etmektir. Mevcut değerler, mevcut sıcaklığı içerir.

Öyleyse tahmin olarak mevcut sıcaklığı döndüren ve "Değişiklik yok" tahminini yapan bir modelle başlayın. Sıcaklık yavaş değiştiği için bu makul bir temeldir. Elbette, gelecekte daha fazla tahmin yaparsanız, bu taban çizgisi daha az işe yarayacaktır.

Girişi çıktıya gönder

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]

Bu modeli örnekleyin ve değerlendirin:

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

Bu, bazı performans ölçümlerini yazdırdı, ancak bunlar size modelin ne kadar iyi performans gösterdiğine dair bir fikir vermiyor.

WindowGenerator bir çizim yöntemine sahiptir, ancak grafikler yalnızca tek bir örnekle çok ilginç olmayacaktır. Bu nedenle, bir WindowGenerator ardışık giriş ve etiketlerin 24 saat pencerelerini oluşturan daha geniş bir WindowGenerator oluşturun.

Geniş wide_window , modelin çalışma şeklini değiştirmez. Model, tek bir giriş zaman adımına dayalı olarak geleceğe yönelik 1 saat tahminleri yapmaya devam eder. Burada time ekseni batch ekseni gibi davranır: Her tahmin, zaman adımları arasında etkileşim olmaksızın bağımsız olarak yapılır.

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)']

Bu genişletilmiş pencere, herhangi bir kod değişikliği olmaksızın doğrudan aynı baseline modele geçirilebilir. Bu mümkündür çünkü girişler ve etiketler aynı sayıda zaman adımına sahiptir ve taban çizgisi girişi çıktıya iletir:

Geleceğe 1 saat, her saat için tek bir tahmin.

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)

Temel modelin tahminlerini çizerken, bunun sadece 1 saat sağa kaydırılmış etiketler olduğunu görebilirsiniz.

wide_window.plot(baseline)

png

Yukarıdaki üç örneğin çizimlerinde, tek adımlı model 24 saat boyunca çalıştırılır. Bu biraz açıklamayı hak ediyor:

  • Mavi "Girişler" satırı, her zaman adımındaki giriş sıcaklığını gösterir. Model tüm özellikleri alıyor, bu grafik sadece sıcaklığı gösteriyor.
  • Yeşil "Etiketler" noktaları, hedef tahmin değerini gösterir. Bu noktalar, giriş zamanında değil, tahmin zamanında gösterilir. Bu nedenle etiket aralığı girişlere göre 1 adım kaydırılır.
  • Turuncu "Tahminler" çarpı, modelin her çıktı zaman adımı için tahminleridir. Model mükemmel bir şekilde tahmin ediyor olsaydı, tahminler doğrudan "etiketlere" konurdu.

Doğrusal model

Bu göreve uygulayabileceğiniz en basit eğitilebilir model, girdi ve çıktı arasına doğrusal dönüşüm eklemektir. Bu durumda, bir zaman adımının çıktısı yalnızca o adıma bağlıdır:

Tek adımlı bir tahmin

Bir layers.Dense , activation seti olmayan doğrusal bir modeldir. Katman, verilerin yalnızca son eksenini (batch, time, inputs) den (batch, time, units) , batch ve time eksenlerindeki her öğeye bağımsız olarak uygulanır.

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)
Output shape: (32, 1, 1)

Bu öğretici birçok modeli eğitir, bu nedenle eğitim prosedürünü bir işlev olarak paketleyin:

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

Modeli eğitin ve performansını değerlendirin:

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.3887 - mean_absolute_error: 0.3448 - val_loss: 0.0281 - val_mean_absolute_error: 0.1269
Epoch 2/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0230 - mean_absolute_error: 0.1127 - val_loss: 0.0132 - val_mean_absolute_error: 0.0867
Epoch 3/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0113 - mean_absolute_error: 0.0792 - val_loss: 0.0093 - val_mean_absolute_error: 0.0718
Epoch 4/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0092 - mean_absolute_error: 0.0707 - val_loss: 0.0087 - val_mean_absolute_error: 0.0689
Epoch 5/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0090 - mean_absolute_error: 0.0696 - val_loss: 0.0087 - val_mean_absolute_error: 0.0682
Epoch 6/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0090 - mean_absolute_error: 0.0695 - val_loss: 0.0089 - val_mean_absolute_error: 0.0685
Epoch 7/20
1534/1534 [==============================] - 4s 3ms/step - loss: 0.0090 - mean_absolute_error: 0.0696 - val_loss: 0.0087 - val_mean_absolute_error: 0.0682
439/439 [==============================] - 1s 2ms/step - loss: 0.0087 - mean_absolute_error: 0.0682

baseline model gibi, doğrusal model geniş pencereli gruplar üzerinde çağrılabilir. Bu şekilde kullanıldığında model, ardışık zaman adımlarında bir dizi bağımsız tahmin yapar. time ekseni, başka bir batch ekseni gibi davranır. Her adımda tahminler arasında etkileşim yoktur.

Tek adımlı bir tahmin

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)

İşte bunun geniş wide_window üzerindeki örnek tahminlerinin wide_window , birçok durumda tahminin sadece giriş sıcaklığını döndürmekten açıkça daha iyi olduğunu, ancak bazı durumlarda daha kötü olduğunu not edin:

wide_window.plot(linear)

png

Doğrusal modellerin bir avantajı, yorumlanmasının nispeten basit olmasıdır. Katmanın ağırlıklarını çıkarabilir ve her girişe atanan ağırlığı görebilirsiniz:

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

Bazen model en fazla ağırlığı T (degC) girişine bile T (degC) . Bu, rastgele başlatma risklerinden biridir.

Yoğun

Gerçekte birden çok zaman adımında çalışan modelleri uygulamadan önce, daha derin, daha güçlü, tek giriş adımlı modellerin performansını kontrol etmeye değer.

İşte linear modele benzer bir model, tek farkı, girdi ve çıktı arasında birkaç Dense katman barındırmasıdır:

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 4ms/step - loss: 0.0174 - mean_absolute_error: 0.0826 - val_loss: 0.0078 - val_mean_absolute_error: 0.0640
Epoch 2/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0079 - mean_absolute_error: 0.0643 - val_loss: 0.0079 - val_mean_absolute_error: 0.0656
Epoch 3/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0075 - mean_absolute_error: 0.0625 - val_loss: 0.0072 - val_mean_absolute_error: 0.0615
Epoch 4/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0073 - mean_absolute_error: 0.0614 - val_loss: 0.0073 - val_mean_absolute_error: 0.0631
Epoch 5/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0070 - mean_absolute_error: 0.0598 - val_loss: 0.0068 - val_mean_absolute_error: 0.0584
Epoch 6/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0069 - mean_absolute_error: 0.0593 - val_loss: 0.0079 - val_mean_absolute_error: 0.0645
Epoch 7/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0069 - mean_absolute_error: 0.0590 - val_loss: 0.0070 - val_mean_absolute_error: 0.0602
439/439 [==============================] - 1s 2ms/step - loss: 0.0070 - mean_absolute_error: 0.0602

Çok adımlı yoğun

Tek zaman adımlı bir model, girdilerinin geçerli değerleri için bağlam içermez. Giriş özelliklerinin zaman içinde nasıl değiştiğini göremez. Bu sorunu çözmek için modelin tahmin yaparken birden çok zaman adımına erişmesi gerekir:

Her tahmin için üç zaman adımı kullanılır.

baseline , linear ve dense modeller her zaman adımını bağımsız olarak ele aldı. Burada model, tek bir çıktı üretmek için girdi olarak birden çok zaman adımı alacaktır.

3 WindowGenerator giriş ve 1 WindowGenerator grupları oluşturacak bir WindowGenerator oluşturun:

Window shift parametresinin iki pencerenin sonuna göre olduğuna dikkat edin.

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 3h as input, predict 1h into the future.")
Text(0.5, 1.0, 'Given 3h as input, predict 1h into the future.')

png

layers.Flatten ekleyerek dense modeli çoklu giriş adımlı bir pencerede eğitebilirsiniz. Modelin ilk katmanı olarak 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.0546
conv_window.plot(multi_step_dense)

png

Bu yaklaşımın ana dezavantajı, ortaya çıkan modelin yalnızca tam olarak bu şekle sahip giriş pencerelerinde yürütülebilmesidir.

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)

Bir sonraki bölümde yer alan evrişimli modeller bu sorunu çözmektedir.

Evrişim sinir ağı

Bir evrişim katmanı ( layers.Conv1D ) ayrıca her tahmine girdi olarak birden çok zaman adımı alır.

Aşağıda, bir evrişim ile yeniden yazılmış multi_step_dense aynı model bulunmaktadır.

Değişiklikleri not edin:

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),
])

Modelin beklenen şekle sahip çıktılar ürettiğini görmek için örnek bir toplu iş üzerinde çalıştırın:

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)

Bunu conv_window eğitin ve değerlendirin; multi_step_dense modeline benzer bir performans 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 3ms/step - loss: 0.0072 - mean_absolute_error: 0.0611

Bu arasındaki fark conv_model ve multi_step_dense modeli olduğunu conv_model herhangi bir uzunlukta girdilerine çalıştırılabilir. Evrişimli katman, kayan bir girdi penceresine uygulanır:

Bir dizi üzerinde evrişimli model yürütme

Daha geniş bir girişte çalıştırırsanız, daha geniş çıktı üretir:

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)

Çıkışın girişten daha kısa olduğuna dikkat edin. Eğitim veya planlamanın işe yaraması için etiketlere ve aynı uzunlukta tahminlere ihtiyacınız var. Bu nedenle, birkaç ekstra giriş süresi WindowGenerator geniş pencereler oluşturmak için bir WindowGenerator , böylece etiket ve tahmin uzunlukları eşleşir:

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)

Artık modelin tahminlerini daha geniş bir pencerede çizebilirsiniz. İlk tahminden önceki 3 giriş zaman adımına dikkat edin. Buradaki her tahmin, önceki 3 zaman adımına dayanmaktadır:

wide_conv_window.plot(conv_model)

png

Tekrarlayan sinir ağı

Tekrarlayan Sinir Ağı (RNN), zaman serisi verilerine çok uygun bir sinir ağı türüdür. RNN'ler bir zaman serisini adım adım işler ve zaman adımından zaman adımına kadar dahili bir durumu korur.

Daha fazla ayrıntı için, metin oluşturma eğitimini veya RNN kılavuzunu okuyun .

Bu eğitimde, Uzun Kısa Süreli Bellek ( LSTM ) adlı bir RNN katmanı kullanacaksınız.

Tüm keras RNN katmanları için önemli bir yapıcı argüman, return_sequences argümanıdır. Bu ayar, katmanı iki yoldan biriyle yapılandırabilir.

  1. Varsayılan olan False , katman yalnızca son zaman adımının çıktısını döndürür ve modele tek bir tahmin yapmadan önce dahili durumunu ısıtması için süre verir:

Isınan ve tek bir tahmin yapan bir lstm

  1. True ise, katman her girdi için bir çıktı döndürür. Bu, aşağıdakiler için yararlıdır:
    • RNN katmanlarının istiflenmesi.
    • Bir modeli aynı anda birden fazla zaman adımı üzerinde eğitmek.

Her zaman adımından sonra bir tahmin yapan bir 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 ile model bir seferde 24 saatlik veri üzerinde eğitilebilir.

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 3ms/step - loss: 0.0056 - mean_absolute_error: 0.0515
wide_window.plot(lstm_model)

png

Verim

Bu veri kümesiyle, tipik olarak modellerin her biri bir öncekinden biraz daha iyi performans gösterir.

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.0666
Dense       : 0.0623
Multi step dense: 0.0552
Conv        : 0.0650
LSTM        : 0.0527

Çok çıkışlı modeller

Şimdiye kadar tüm modeller, tek bir zaman adımı için tek bir çıkış özelliği olan T (degC) öngördü.

Bu modellerin tümü, yalnızca çıktı katmanındaki birim sayısını değiştirerek ve eğitim pencerelerini labels tüm özellikleri içerecek şekilde ayarlayarak birden çok özelliği tahmin edecek şekilde dönüştürülebilir.

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)

Yukarıda, etiketlerin features ekseninin artık 1 yerine girişlerle aynı derinliğe sahip olduğuna dikkat edin.

Temel

Aynı temel model burada kullanılabilir, ancak bu sefer belirli bir label_index seçmek yerine tüm özellikleri tekrarlayın.

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 2ms/step - loss: 0.0886 - mean_absolute_error: 0.1589

Yoğun

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.0680 - mean_absolute_error: 0.1296

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 3ms/step - loss: 0.0615 - mean_absolute_error: 0.1204

CPU times: user 4min 32s, sys: 1min 2s, total: 5min 34s
Wall time: 1min 49s

Gelişmiş: Artık bağlantılar

Daha önceki Baseline model, dizinin zaman adımından zaman adımına büyük ölçüde değişmemesi gerçeğinden yararlandı. Şimdiye kadar bu eğitimde eğitilen her model rastgele başlatıldı ve ardından çıktının önceki zaman adımından küçük bir değişiklik olduğunu öğrenmek zorunda kaldı.

Bu sorunu dikkatli bir şekilde başlatma ile aşabilirsiniz, ancak bunu model yapısına yerleştirmek daha kolaydır.

Zaman serisi analizinde, bir sonraki değeri tahmin etmek yerine, bir sonraki zaman adımında değerin nasıl değişeceğini tahmin eden modeller oluşturmak yaygındır. Benzer şekilde, derin öğrenmedeki "Artık ağlar" veya "Yeniden Ağlar", her katmanın modelin biriken sonucuna eklendiği mimarileri ifade eder.

Değişimin küçük olması gerektiği bilgisinden bu şekilde faydalanırsınız.

Artık bağlantılı bir model

Esasen bu maç için bir model başlatır Baseline . Bu görev için modellerin biraz daha iyi performansla daha hızlı yakınlaşmasına yardımcı olur.

Bu yaklaşım, bu eğitimde tartışılan herhangi bir modelle bağlantılı olarak kullanılabilir.

Burada, LSTM modeline uygulanmaktadır, ilk tahmin edilen değişikliklerin küçük olmasını ve kalan bağlantıyı tf.initializers.zeros sağlamak için tf.initializers.zeros kullanımına dikkat edin. zeros yalnızca son katmanda kullanıldığından, buradaki gradyanlar için simetriyi bozan bir kaygı yoktur.

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 timestep 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
        # So 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 3ms/step - loss: 0.0621 - mean_absolute_error: 0.1178

CPU times: user 2min, sys: 27.8 s, total: 2min 28s
Wall time: 49.1 s

Verim

İşte bu çok çıkışlı modellerin genel performansı.

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.1317
LSTM           : 0.1219
Residual LSTM  : 0.1189

Yukarıdaki performansların tüm model çıktılarının ortalaması alınmıştır.

Çok adımlı modeller

Önceki bölümlerdeki hem tek çıktılı hem de çok çıktılı modeller, 1 saat ileriye dönük tek zaman adımlı tahminlerde bulundu .

Bu bölüm, birden çok zaman adımı tahmininde bulunmak için bu modellerin nasıl genişletileceğine bakar.

Çok adımlı bir tahminde, modelin bir dizi gelecekteki değeri tahmin etmeyi öğrenmesi gerekir. Bu nedenle, yalnızca tek bir gelecek noktasının tahmin edildiği tek adımlı bir modelden farklı olarak, çok adımlı bir model gelecekteki değerlerin bir dizisini tahmin eder.

Buna iki kaba yaklaşım var:

  1. Tüm zaman serilerinin aynı anda tahmin edildiği tek atış tahminleri.
  2. Modelin yalnızca tek adımlı tahminler yaptığı ve çıktısının girdi olarak geri beslendiği otoregresif tahminler.

Bu bölümde tüm modeller, tüm çıktı zaman adımları boyunca tüm özellikleri tahmin edecektir.

Çok adımlı model için, eğitim verileri yine saatlik örneklerden oluşur. Bununla birlikte, burada modeller, geçmişin 24 saati göz önüne alındığında geleceğin 24 saatini tahmin etmeyi öğrenecek.

İşte bu dilimleri veri kümesinden oluşturan bir Window nesnesi:

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

Temeller

Bu görev için basit bir temel, gerekli sayıda çıktı zaman adımı için son giriş zamanı adımını tekrar etmektir:

Her çıkış adımı için son girişi tekrarlayın

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 2ms/step - loss: 0.6285 - mean_absolute_error: 0.5007

png

Bu görev, 24 saat verilen 24 saati tahmin etmek olduğundan, başka bir basit yaklaşım, yarının benzer olacağını varsayarak önceki günü tekrar etmektir:

Önceki günü tekrarlayın

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

Tek seferlik modeller

Bu probleme yüksek seviyeli bir yaklaşım, modelin tüm dizi tahminini tek bir adımda yaptığı "tek vuruşlu" bir model kullanmaktır.

Bu, bir layers.Dense olarak verimli bir şekilde uygulanabilir. OUT_STEPS*features ile OUT_STEPS*features çıkış birimlerine sahiptir. Modelin sadece bu çıktıyı gereken şekilde (OUTPUT_STEPS, features) yeniden şekillendirmesi gerekir.

Doğrusal

Son giriş zaman adımına dayalı basit bir doğrusal model, her iki temelden daha iyi sonuç verir, ancak gücü yetersizdir. Modelin, doğrusal bir projeksiyonla tek bir giriş zaman adımından OUTPUT_STEPS zaman adımlarını tahmin OUTPUT_STEPS gerekir. Muhtemelen esas olarak günün saatine ve yılın saatine bağlı olarak davranışın yalnızca düşük boyutlu bir bölümünü yakalayabilir.

Son zaman adımından tüm zaman adımlarını tahmin et

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.2552 - mean_absolute_error: 0.3054

png

Yoğun

layers.Dense ekleme Giriş ve çıkış arasındaki layers.Dense , doğrusal modele daha fazla güç verir, ancak yine de yalnızca tek bir giriş zaman adımına dayanır.

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.2199 - mean_absolute_error: 0.2813

png

CNN

Evrişimli bir model, sabit genişlikli bir geçmişe dayalı tahminler yapar ve bu, zaman içinde işlerin nasıl değiştiğini görebildiği için yoğun modelden daha iyi performansa yol açabilir:

Evrişimli bir model, şeylerin zaman içinde nasıl değiştiğini görür

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.2162 - mean_absolute_error: 0.2817

png

RNN

Yinelenen bir model, modelin yaptığı tahminlerle alakalıysa, uzun bir girdi geçmişi kullanmayı öğrenebilir. Burada model, sonraki 24 saat için tek bir tahmin yapmadan önce 24 saat boyunca dahili durumu biriktirecektir.

Bu tek atım formatında, LSTM'nin yalnızca son zaman adımında bir çıktı üretmesi gerekir, bu nedenle return_sequences=False ayarlayın.

Lstm, durumu giriş penceresi üzerinde biriktirir ve sonraki 24 saat için tek bir tahmin yapar.

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.2141 - mean_absolute_error: 0.2844

png

Gelişmiş: Otoregresif model

Yukarıdaki modellerin tümü, tek bir adımda tüm çıktı dizisini tahmin eder.

Bazı durumlarda, modelin bu tahmini ayrı zaman adımlarına ayırması yararlı olabilir. Daha sonra, her modelin çıktısı her adımda kendi içine geri beslenebilir ve klasik Yinelenen Sinir Ağları ile Diziler Oluşturma'da olduğu gibi, bir öncekine göre tahminler koşullandırılabilir.

Bu model tarzının açık bir avantajı, değişen uzunlukta çıktı üretecek şekilde ayarlanabilmesidir.

Bu öğreticinin ilk yarısında eğitilmiş tek adımlı çok çıktılı modellerden herhangi birini alabilir ve otoregresif bir geri bildirim döngüsünde çalıştırabilirsiniz, ancak burada bunu yapmak için açıkça eğitilmiş bir model oluşturmaya odaklanacaksınız.

Bir modelin çıktısını kendi girdisine geri bildirme

RNN

Bu öğretici yalnızca otoregresif bir RNN modeli oluşturur, ancak bu model tek bir zaman adımı çıkarmak için tasarlanmış herhangi bir modele uygulanabilir.

Model, tek adımlı LSTM modelleriyle aynı temel forma sahip olacaktır: Bir LSTM takiben bir layers.Dense . LSTM çıktılarını model tahminlerine dönüştüren LSTM .

Bir layers.LSTM bir olduğunu layers.LSTMCell üst düzey sarılı layers.RNN sizin için devlet ve sıra sonuçlarını yöneten (Bkz Keras RNNs detaylar için).

Bu durumda modelin her adım için girdileri manuel olarak yönetmesi gerekir, böylece daha düşük seviye, tek zaman adımlı arayüz için layers.LSTMCell kullanır.

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)

Bu modelin ihtiyaç duyduğu ilk yöntem, girdilere dayalı olarak dahili durumunu başlatmak için bir warmup yöntemidir. Eğitildikten sonra bu durum, giriş geçmişinin ilgili kısımlarını yakalayacaktır. Bu, daha önceki tek adımlı LSTM modeline eşdeğerdir:

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

Bu yöntem, tek bir zaman adımı tahmini ve LSTM'nin dahili durumunu döndürür:

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

RNN durumu ve bir ilk tahmin ile artık her adımda girdi olarak tahminleri besleyen modeli yinelemeye devam edebilirsiniz.

Çıktı tahminlerini toplamanın en basit yolu, bir python listesi ve tf.stack sonra tf.stack kullanmaktır.

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

Bu modeli örnek girişler üzerinde test edin:

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

Şimdi modeli eğitin:

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.2274 - mean_absolute_error: 0.3005

png

Verim

Bu problemde model karmaşıklığının bir fonksiyonu olarak açıkça azalan getiriler vardır.

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

Bu öğreticinin ilk yarısındaki çoklu çıktı modelleri için ölçümler, tüm çıktı özelliklerinde ortalama performans gösterir. Bu performanslar benzerdir, ancak aynı zamanda çıktı zaman dilimlerinde de ortalaması alınır.

for name, value in multi_performance.items():
  print(f'{name:8s}: {value[1]:0.4f}')
Last    : 0.5157
Repeat  : 0.3774
Linear  : 0.2980
Dense   : 0.2751
Conv    : 0.2769
LSTM    : 0.2768
AR LSTM : 0.2936

Yoğun bir modelden evrişimli ve tekrarlayan modellere geçerken elde edilen kazançlar (varsa) sadece birkaç yüzde ve otoregresif model açıkça daha kötü performans gösterdi. Bu daha karmaşık yaklaşımlar bu sorun üzerinde değer varken olmayabilir, ama denemeden bilmek hiçbir yolu yoktu ve bu yüzden bu modeller sorununuza yardımcı olabilir.

Sonraki adımlar

Bu eğitim, TensorFlow kullanarak zaman serisi tahminine hızlı bir giriş niteliğindedir.