سخنرانی ها ، جلسات محصول ، کارگاه ها و موارد دیگر را از لیست پخش Google I / O مشاهده کنید

پیش بینی سری زمانی

مشاهده در TensorFlow.org در Google Colab اجرا کنید مشاهده منبع در GitHub دانلود دفترچه یادداشت

این آموزش مقدمه ای برای پیش بینی سری زمانی با استفاده از TensorFlow است. چند مدل مختلف از جمله شبکه های عصبی Convolutional و Recurrent (CNN ها و RNN ها) را ایجاد می کند.

این در دو بخش اصلی ، با زیر بخش ها پوشش داده شده است:

  • پیش بینی برای یک بازه زمانی:
    • یک ویژگی واحد.
    • همه ویژگی ها
  • پیش بینی چندین مرحله:
    • تک شات: پیش بینی ها را یک باره انجام دهید.
    • خود رگرسیون: هر بار یک پیش بینی انجام دهید و خروجی را به مدل بازگردانید.

برپایی

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

مجموعه داده های هوا

این آموزش از مجموعه داده های سری زمانی آب و هوا که توسط موسسه بیوژئوشیمی ماکس پلانک ثبت شده استفاده می کند.

این مجموعه داده شامل 14 ویژگی مختلف مانند درجه حرارت هوا ، فشار اتمسفر و رطوبت است. این کار از هر سال 10 دقیقه ، از سال 2003 جمع آوری می شود. برای کارایی ، شما فقط از داده های جمع آوری شده بین 2009 و 2016 استفاده خواهید کرد. این بخش از مجموعه داده توسط François Chollet برای کتاب عمیق یادگیری با پایتون تهیه شده است .

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

این آموزش فقط با پیش بینی های ساعتی سروکار دارد ، بنابراین با استفاده از نمونه برداری فرعی از داده ها از فواصل 10 دقیقه تا 1 ساعت شروع کنید:

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) ستون های 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) ، جهت باد را به واحد درجه نشان می دهد. زاویه ها ورودی مدل خوبی ایجاد نمی کنند ، 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(datetime.datetime.timestamp)

مشابه جهت باد زمان در ثانیه ورودی مدل مفیدی نیست. به عنوان داده های آب و هوا ، دارای تناوب روزانه و سالانه است. روش های زیادی وجود دارد که می توانید با تناوب مقابله کنید.

یک روش ساده برای تبدیل آن به یک سیگنال قابل استفاده ، استفاده از sin و cos برای تبدیل زمان برای پاک کردن سیگنال های "Time of day" و "Time of year" است:

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

این امکان دسترسی به مدل را به مهمترین ویژگیهای فرکانس می دهد. در این حالت شما پیش از موعد می دانستید کدام فرکانس ها مهم هستند.

اگر نمی دانید ، می توانید فرکانس های مهم را با استفاده از fft . برای بررسی فرضیات ما ، در اینجا tf.signal.rfft دما در طول زمان است. به قله های واضح در فرکانس های نزدیک به 1/year و 1/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)')

png

داده ها را تقسیم کنید

ما برای مجموعه های آموزشی ، اعتبار سنجی و آزمون از تقسیم بندی (70%, 20%, 10%) استفاده خواهیم کرد. توجه داشته باشید که داده ها قبل از تقسیم به طور تصادفی تغییر نمی کنند . این به دو دلیل است.

  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

پنجره سازی داده ها

مدل های موجود در این مجموعه آموزشی مجموعه ای از پیش بینی ها را بر اساس پنجره ای از نمونه های متوالی داده ها انجام می دهند.

ویژگی های اصلی پنجره های ورودی عبارتند از:

  • عرض (تعداد مراحل زمان) پنجره های ورودی و برچسب
  • فاصله زمانی بین آنها.
  • کدام ویژگی ها به عنوان ورودی ، برچسب یا هر دو مورد استفاده قرار می گیرند.

این آموزش مدلهای متنوعی را ایجاد می کند (از جمله مدلهای Linear ، DNN ، CNN و RNN) و از آنها برای هر دو استفاده می کند:

  • پیش بینی های تک خروجی و چند خروجی .
  • پیش بینی های یک مرحله ای و چند مرحله ای .

این بخش بر اجرای پنجره سازی داده ها متمرکز است تا بتوان از آن برای همه مدل ها استفاده مجدد کرد.

بسته به وظیفه و نوع مدلی که می خواهید انواع مختلفی از پنجره های داده را ایجاد کنید. در اینجا چند نمونه آورده شده است:

  1. به عنوان مثال ، برای پیش بینی واحد 24 ساعته در آینده ، با توجه به 24 ساعت تاریخ ممکن است پنجره ای مانند این تعریف کنید:

    یک پیش بینی 24 ساعته به آینده.

  2. مدلی که با توجه به 6 ساعت تاریخ ، یک ساعت آینده را پیش بینی کند ، به پنجره ای مانند این نیاز دارد:

    یک پیش بینی 1 ساعته به آینده.

بقیه این بخش کلاس WindowGenerator را تعریف می کند. این کلاس می تواند:

  1. همانطور که در نمودارهای بالا نشان داده شده است ، شاخص ها و جبران ها را کنترل کنید.
  2. پنجره های ویژگی ها را به یک جفت (features, labels) .
  3. محتوای پنجره های حاصل را رسم کنید.
  4. با استفاده ازtf.data.Dataset s دسته ای از این پنجره ها را از طریق آموزش ، ارزیابی و داده های آزمایشیtf.data.Dataset .

1. شاخص ها و جبران ها

با ایجاد کلاس WindowGenerator شروع کنید. روش __init__ شامل تمام منطق لازم برای شاخص های ورودی و برچسب است.

همچنین از فریم داده های قطار ، ارزیابی و آزمایش به عنوان ورودی استفاده می شود. بعداً به پنجره هایtf.data.Dataset تبدیل می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 به آرایه هایی بسته می شوند که بیرونی ترین شاخص در نمونه ها باشد (بعد "دسته ای"). شاخص های میانی ابعاد "زمان" یا "فضا" (عرض ، ارتفاع) هستند. درونی ترین شاخص ها ویژگی ها هستند.

کد فوق دسته ای از 3 ویندوز 7 گام 7 برابری را شامل می شود که دارای 19 ویژگی در هر مرحله است. آنها را به دسته ای از 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 s

سرانجام این روش make_dataset به یک سری زمان DataFrame و آن را به یک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.Datasets با استفاده از روش make_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 ساختار ، dtypes و شکل عناصر مجموعه داده را به شما می گوید.

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

مدل های تک مرحله ای

ساده ترین مدلی که می توانید بر اساس این نوع داده ها بسازید ، مدلی است که مقدار یک ویژگی را تنها در شرایط فعلی ، 1 برابر گام (1 ساعت) در آینده پیش بینی می کند.

بنابراین با ساختن مدل هایی برای پیش بینی مقدار T (degC) 1h در آینده شروع کنید.

مرحله بعدی را پیش بینی کنید

یک شی 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.Datasets از مجموعه های آموزش ، اعتبار سنجی و آزمایش ایجاد می کند ، به شما امکان می دهد به راحتی در دسته های داده تکرار کنید.

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 گسترده تر ایجاد کنید که 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 بدون هیچگونه تغییر کد منتقل شود. این امکان وجود دارد زیرا ورودی ها و برچسب ها تعداد دفعاتی برابر دارند و خط پایه ورودی را به خروجی هدایت می کند:

یک ساعت پیش بینی برای آینده ، هر ساعت.

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

در نمودارهای فوق از سه مثال ، مدل تک مرحله ای در طول 24 ساعت اجرا می شود. این سزاوار توضیح است:

  • خط آبی "ورودی" دمای ورودی را در هر مرحله نشان می دهد. مدل تمام ویژگی ها را بازیابی می کند ، این نمودار فقط دما را نشان می دهد.
  • نقاط سبز "Labels" مقدار پیش بینی هدف را نشان می دهد. این نقاط در زمان پیش بینی نشان داده می شوند نه زمان ورودی. به همین دلیل دامنه برچسب ها نسبت به ورودی ها 1 مرحله تغییر مکان می یابد.
  • ضربدرهای نارنجی "پیش بینی ها" پیش بینی مدل برای هر مرحله زمان خروجی است. اگر مدل کاملاً پیش بینی می کرد ، پیش بینی ها مستقیماً روی "برچسب ها" قرار می گرفتند.

مدل خطی

ساده ترین مدل قابل آموزش که می توانید برای این کار اعمال کنید ، قرار دادن تحول خطی بین ورودی و خروجی است. در این حالت خروجی از یک مرحله زمانی فقط به آن مرحله بستگی دارد:

پیش بینی یک مرحله

یک layers.Dense . layers.Dense و بدون مجموعه activation یک مدل خطی است. این لایه فقط آخرین محور داده را از (batch, time, inputs) به (batch, time, units) ، به طور مستقل برای هر مورد در محورهای batch و time .

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)

این آموزش بسیاری از مدل ها را آموزش می دهد ، بنابراین روش آموزش را به صورت تابعی بسته بندی کنید:

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.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 ، مدل خطی را می توان در دسته پنجره های عریض فراخوانی کرد. با استفاده از این روش ، مدل مجموعه ای از پیش بینی های مستقل را در مراحل زمانی متوالی انجام می دهد. محور 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

یک مزیت مدل های خطی این است که تفسیر آنها نسبتاً ساده است. می توانید وزن های لایه را بیرون بکشید و وزن اختصاص داده شده به هر ورودی را ببینید:

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) نمی گذارد. این یکی از خطرات اولیه سازی تصادفی است.

متراکم

قبل از استفاده از مدل هایی که در واقع در چند مرحله زمانی کار می کنند ، ارزش بررسی عملکرد مدل های عمیق تر ، قدرتمندتر و تک مرحله ای ورودی را دارد.

در اینجا یک مدل شبیه به مدل 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 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

چند مرحله ای متراکم

مدل تک مرحله ای هیچ زمینه ای برای مقادیر فعلی ورودی های خود ندارد. نمی تواند ببیند که ویژگی های ورودی با گذشت زمان چگونه تغییر می کنند. برای پرداختن به این مسئله ، مدل هنگام پیش بینی نیاز به دسترسی به مراحل زمانی متعدد دارد:

برای هر پیش بینی سه مرحله زمانی استفاده می شود.

مدل های baseline ، linear و dense هر مرحله به طور مستقل انجام می شوند. در اینجا مدل چندین مرحله زمان را به عنوان ورودی برای تولید یک خروجی واحد برمی دارد.

WindowGenerator ایجاد کنید که دسته هایی از 3 ساعت ورودی و 1 ساعت برچسب ها را تولید کند:

توجه داشته باشید که پارامتر shift Window نسبت به انتهای دو پنجره است.

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

با افزودن لایه ها می توانید یک مدل dense را روی پنجره چند مرحله ای layers.Flatten به عنوان اولین لایه مدل 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

نکته اصلی اصلی این روش این است که مدل حاصل فقط روی پنجره های ورودی دقیقاً به این شکل قابل اجرا است.

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)

مدل های کانولوشن در بخش بعدی این مشکل را برطرف می کنند.

شبکه عصبی کانولوشن

یک لایه کانولوشن (لایه های 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 3ms/step - loss: 0.0072 - mean_absolute_error: 0.0611

تفاوت بین این conv_model و multi_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 را بخوانید.

در این آموزش ، شما از یک لایه RNN به نام حافظه کوتاه مدت بلند ( LSTM ) استفاده خواهید کرد.

یک آرگومان سازنده مهم برای تمام لایه های keras RNN آرگومان return_sequences است. این تنظیم می تواند لایه را به یکی از دو روش پیکربندی کند.

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

With 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 3ms/step - loss: 0.0056 - mean_absolute_error: 0.0515
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.0666
Dense       : 0.0623
Multi step dense: 0.0552
Conv        : 0.0650
LSTM        : 0.0527

مدل های چند خروجی

مدل ها تاکنون همه یک ویژگی خروجی واحد ، T (degC) برای یک گام واحد پیش بینی کرده اند.

همه این مدل ها را می توان فقط با تغییر تعداد واحد در لایه خروجی و تنظیم پنجره های آموزش برای درج تمام ویژگی ها در 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 دارای عمق ورودی هستند.

پایه

در اینجا می توان از همان مدل پایه استفاده کرد ، اما این بار به جای انتخاب یک 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 2ms/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.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

پیشرفته: اتصالات باقیمانده

مدل Baseline از این واقعیت استفاده کرد که توالی از مرحله به مرحله به مرحله تغییر نمی کند. هر مدل آموزش دیده در این آموزش تاکنون بصورت تصادفی اولیه سازی شده بود ، و سپس باید یاد می گرفت که خروجی نسبت به مرحله زمان قبلی تغییر کمی دارد.

اگرچه می توانید با مقداردهی اولیه دقیق از پس این مسئله برآیید ، ساخت آن در ساختار مدل ساده تر است.

در ساخت سری های زمانی معمول است که مدل هایی ساخته می شوند که به جای پیش بینی مقدار بعدی ، چگونگی تغییر مقدار در گام بعدی را پیش بینی کنند. به طور مشابه ، "شبکه های باقیمانده" یا "ResNets" در یادگیری عمیق به معماری هایی اطلاق می شوند که هر لایه به نتیجه جمع شده مدل اضافه می کند.

اینگونه است که شما از دانش کوچک بودن تغییر استفاده می کنید.

مدلی با اتصال باقیمانده

اساساً این مدل را برای مطابقت با 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 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

کارایی

در اینجا عملکرد کلی این مدلهای چند خروجی آورده شده است.

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

عملکردهای فوق در تمام خروجی های مدل به طور متوسط ​​است.

مدل های چند مرحله ای

هر دو مدل تک خروجی و چند خروجی در بخش های قبلی ، پیش بینی های مرحله یک زمانه را برای 1 ساعت در آینده انجام می دهند.

این بخش به چگونگی گسترش این مدل ها برای پیش بینی گام های زمانی متعدد می پردازد.

در یک پیش بینی چند مرحله ای ، مدل باید یاد بگیرد که طیف وسیعی از مقادیر آینده را پیش بینی کند. بنابراین ، برخلاف یک مدل تک مرحله ای ، که فقط یک نقطه آینده واحد پیش بینی می شود ، یک مدل چند مرحله ای توالی مقادیر آینده را پیش بینی می کند.

دو رویکرد خشن وجود دارد:

  1. پیش بینی های تک شات که در آن یکباره کل سری زمانی پیش بینی می شود.
  2. پیش بینی های خود متجاوز که در آن مدل فقط پیش بینی های تک مرحله ای را انجام می دهد و خروجی آن به عنوان ورودی خود بازگردانده می شود.

در این بخش ، تمام مدل ها تمام ویژگی ها را در تمام مراحل زمان خروجی پیش بینی می کنند.

برای مدل چند مرحله ای ، داده های آموزش دوباره از نمونه های ساعتی تشکیل شده است. با این حال ، در اینجا ، مدل ها می توانند با توجه به 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 2ms/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

مدل های تک شات

یک رویکرد سطح بالا برای این مشکل استفاده از مدل "تک شات" است ، که در آن مدل کل پیش بینی توالی را در یک مرحله انجام می دهد.

این را می توان بصورت کارآمد بصورت layers.Dense اجرا کرد. layers.Dense با OUT_STEPS*features واحد خروجی OUT_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.2552 - mean_absolute_error: 0.3054

png

متراکم

افزودن یک 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.2199 - mean_absolute_error: 0.2813

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

png

RNN

اگر مربوط به پیش بینی های مدل باشد ، یک مدل مکرر می تواند یاد بگیرد که از سابقه طولانی ورودی استفاده کند. در اینجا مدل قبل از اینکه برای 24 ساعت بعدی پیش بینی کند ، حالت داخلی را برای 24 ساعت جمع می کند.

در این قالب تک شات ، LSTM فقط باید در آخرین مرحله زمان خروجی تولید کند ، بنابراین return_sequences=False .

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

png

پیشرفته: مدل خود متجاوز

مدل های فوق همه توالی خروجی را در یک مرحله پیش بینی می کنند.

در برخی موارد ممکن است تجزیه این پیش بینی در مراحل زمانی منفرد برای مدل مفید باشد. سپس می توان در هر مرحله خروجی هر مدل را به خودی خود بازگرداند و می توان پیش بینی آن را به شرط قبلی ، مانند توالی های تولید کلاسیک با شبکه های عصبی عودکننده ، مشروط کرد.

یک مزیت بارز در این سبک از مدل این است که می توان آن را برای تولید خروجی با طول متفاوت تنظیم کرد.

شما می توانید هر یک از مدلهای چند مرحله ای تک مرحله ای را که در نیمه اول این آموزش آموزش دیده است ، انجام دهید و در یک حلقه بازخورد خود رگرسیون اجرا کنید ، اما در اینجا شما بر ساخت مدلی تمرکز خواهید کرد که به صراحت برای انجام آن آموزش داده شده است.

بازده یک مدل را به ورودی آن بازخورد کنید

RNN

این آموزش فقط یک مدل RNN خود متجاوز ایجاد می کند ، اما این الگو را می توان برای هر مدلی که برای تولید یک گام زمان طراحی شده است ، اعمال کرد.

این مدل همان شکل اولیه را دارد که مدلهای LSTM تک مرحله ای LSTM : LSTM دنبال آن layers.Dense هایی ارائه می شود. LSTM خروجی های LSTM به پیش بینی های مدل تبدیل می کند.

یک layers.LSTM . layers.LSTM یک layers.LSTM است. layers.LSTMCell در لایه های سطح بالاتر قرار دارد. layers.RNN که نتایج وضعیت و ترتیب را برای شما مدیریت می کند (برای جزئیات بیشتر به RNN های Keras مراجعه کنید).

در این حالت مدل مجبور است ورودی ها را برای هر مرحله به صورت دستی مدیریت کند بنابراین از layers.LSTMCell استفاده می کند. 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 و پیش بینی اولیه ، اکنون می توانید تکرار مدل تغذیه کننده پیش بینی ها را در هر مرحله به عنوان ورودی ادامه دهید.

ساده ترین روش برای جمع آوری پیش بینی های خروجی استفاده از لیست پایتون و 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.2274 - mean_absolute_error: 0.3005

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.2980
Dense   : 0.2751
Conv    : 0.2769
LSTM    : 0.2768
AR LSTM : 0.2936

دستاوردهایی که از یک مدل متراکم به مدلهای کانولوشن و تکرار شونده بدست آمده تنها چند درصد است (در صورت وجود) و عملکرد خود رگرسیون به وضوح بدتر است. بنابراین این رویکردهای پیچیده تر ممکن است ارزش استفاده از این مشکل را نداشته باشند ، اما راهی برای دانستن بدون تلاش وجود نداشت و این مدل ها می توانند برای مشکل شما مفید باشند.

مراحل بعدی

این آموزش مقدمه ای سریع برای پیش بینی سری های زمانی با استفاده از TensorFlow بود.