หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

การพยากรณ์อนุกรมเวลา

ดูใน TensorFlow.org เรียกใช้ใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดสมุดบันทึก

บทช่วยสอนนี้เป็นบทนำเกี่ยวกับการพยากรณ์อนุกรมเวลาโดยใช้ TensorFlow มันสร้างรูปแบบที่แตกต่างกันสองสามแบบรวมถึง Convolutional และ Recurrent Neural Networks (CNNs และ RNNs)

เนื้อหานี้ครอบคลุมในสองส่วนหลักโดยมีส่วนย่อย:

  • คาดการณ์สำหรับการประทับเวลาเดียว:
    • คุณสมบัติเดียว
    • คุณสมบัติทั้งหมด
  • พยากรณ์หลายขั้นตอน:
    • Single-shot: ทำการคาดเดาทั้งหมดในครั้งเดียว
    • ตอบกลับอัตโนมัติ: ทำการคาดคะเนครั้งละหนึ่งรายการและป้อนเอาต์พุตกลับไปที่โมเดล

ติดตั้ง

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

ชุดข้อมูลสภาพอากาศ

บทแนะนำนี้ใช้ ชุดข้อมูลอนุกรมเวลาสภาพอากาศที่ บันทึกโดย Max Planck Institute for Biogeochemistry

ชุดข้อมูลนี้ประกอบด้วยคุณลักษณะต่างๆ 14 ประการเช่นอุณหภูมิของอากาศความดันบรรยากาศและความชื้น ข้อมูลเหล่านี้จะถูกรวบรวมทุกๆ 10 นาทีเริ่มตั้งแต่ปี 2003 เพื่อประสิทธิภาพคุณจะใช้เฉพาะข้อมูลที่รวบรวมระหว่างปี 2009 ถึง 2016 ส่วนของชุดข้อมูลนี้จัดทำโดยFrançois Chollet สำหรับหนังสือ Deep Learning with Python

zip_path = tf.keras.utils.get_file(
    origin='https://storage.googleapis.com/tensorflow/tf-keras-datasets/jena_climate_2009_2016.csv.zip',
    fname='jena_climate_2009_2016.csv.zip',
    extract=True)
csv_path, _ = os.path.splitext(zip_path)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/jena_climate_2009_2016.csv.zip
13574144/13568290 [==============================] - 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) คอลัมน์ -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]

ทำให้ข้อมูลเป็นปกติ

สิ่งสำคัญคือต้องปรับขนาดคุณสมบัติก่อนที่จะฝึกโครงข่ายประสาทเทียม Normalization เป็นวิธีทั่วไปในการปรับขนาดนี้ ลบค่าเฉลี่ยและหารด้วยค่าเบี่ยงเบนมาตรฐานของแต่ละคุณลักษณะ

ค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานควรคำนวณโดยใช้ข้อมูลการฝึกอบรมเพื่อให้แบบจำลองไม่สามารถเข้าถึงค่าในการตรวจสอบความถูกต้องและชุดทดสอบได้

นอกจากนี้ยังเป็นที่ถกเถียงกันอยู่ว่าแบบจำลองไม่ควรเข้าถึงค่าในอนาคตในชุดการฝึกอบรมเมื่อทำการฝึกอบรมและควรทำให้เป็นมาตรฐานโดยใช้ค่าเฉลี่ยเคลื่อนที่ นั่นไม่ใช่จุดสำคัญของบทช่วยสอนนี้และการตรวจสอบความถูกต้องและชุดทดสอบช่วยให้มั่นใจได้ว่าคุณจะได้รับเมตริกที่ซื่อสัตย์ (ค่อนข้าง) ดังนั้นเพื่อความเรียบง่ายบทช่วยสอนนี้จึงใช้ค่าเฉลี่ยอย่างง่าย

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. แบบจำลองที่ทำให้การทำนาย 1 ชม. ในอนาคตโดยที่ 6 ชม. ของประวัติศาสตร์จะต้องมีหน้าต่างแบบนี้:

    หนึ่งการทำนาย 1 ชม. สู่อนาคต

ส่วนที่เหลือของส่วนนี้กำหนดคลาส WindowGenerator ชั้นเรียนนี้สามารถ:

  1. จัดการดัชนีและการชดเชยตามที่แสดงในแผนภาพด้านบน
  2. แบ่งหน้าต่างของคุณสมบัติออกเป็นคู่ (features, labels)
  3. พล็อตเนื้อหาของหน้าต่างผลลัพธ์
  4. สร้างชุดของหน้าต่างเหล่านี้อย่างมีประสิทธิภาพจากข้อมูลการฝึกอบรมการประเมินผลและการทดสอบโดยใช้ tf.data.Dataset s

1. ดัชนีและการชดเชย

เริ่มต้นด้วยการสร้างคลาส WindowGenerator วิธี __init__ รวมตรรกะที่จำเป็นทั้งหมดสำหรับดัชนีอินพุตและป้ายกำกับ

นอกจากนี้ยังใช้รถไฟประเมินและทดสอบดาต้าเฟรมเป็นอินพุต สิ่งเหล่านี้จะถูกแปลงเป็น tf.data.Dataset ของ windows ในภายหลัง

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-timestep พร้อม 19 คุณสมบัติในแต่ละขั้นตอน โดยแบ่งออกเป็นชุดของ 6-timestep, 19 feature inputs และ 1-timestep 1-feature label ป้ายกำกับมีคุณลักษณะเดียวเท่านั้นเนื่องจาก 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(3, 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 ด้านบน เพิ่มชุดตัวอย่างมาตรฐานเพื่อให้เข้าถึงและวางแผนได้ง่าย:

@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 ทำให้ได้ 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) 1 ชั่วโมงในอนาคต

ทำนายขั้นตอนในครั้งต่อไป

กำหนดค่าออบเจ็กต์ WindowGenerator เพื่อสร้างคู่ขั้นตอนเดียว (input, label) เหล่านี้:

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

ออบเจ็กต์ window สร้าง tf.data.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 2ms/step - loss: 0.0128 - mean_absolute_error: 0.0785

ซึ่งพิมพ์เมตริกประสิทธิภาพบางอย่าง แต่ไม่ได้ให้ความรู้สึกว่าโมเดลทำงานได้ดีเพียงใด

WindowGenerator มีวิธีการพล็อต แต่พล็อตจะไม่น่าสนใจมากนักกับเพียงตัวอย่างเดียว ดังนั้นสร้าง WindowGenerator กว้างขึ้นซึ่งจะสร้างอินพุตและป้ายกำกับต่อเนื่องของ windows 24 ชั่วโมงในแต่ละครั้ง

wide_window ไม่เปลี่ยนวิธีการทำงานของโมเดล แบบจำลองยังคงทำการคาดการณ์ 1 ชั่วโมงในอนาคตตามขั้นตอนเวลาป้อนข้อมูลเดียว แกน time จะทำหน้าที่เหมือนแกน batch ที่นี่การคาดคะเนแต่ละครั้งจะทำอย่างอิสระโดยไม่มีการโต้ตอบระหว่างขั้นตอนเวลา

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

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

หน้าต่างที่ขยายออกนี้สามารถส่งผ่านไปยังโมเดล baseline เดียวกันได้โดยตรงโดยไม่มีการเปลี่ยนแปลงโค้ดใด ๆ สิ่งนี้เป็นไปได้เนื่องจากอินพุตและเลเบลมีจำนวนไทม์สเต็ปเท่ากันและพื้นฐานจะส่งต่ออินพุตไปยังเอาต์พุตเท่านั้น:

การทำนายหนึ่งครั้งในอนาคต 1 ชั่วโมงตลอดเวลาชั่วโมง

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 ชั่วโมง สิ่งนี้สมควรได้รับการอธิบาย:

  • บรรทัด "อินพุต" สีน้ำเงินแสดงอุณหภูมิอินพุตในแต่ละขั้นตอน โมเดลได้รับคุณสมบัติทั้งหมดพล็อตนี้แสดงเฉพาะอุณหภูมิเท่านั้น
  • จุด "ป้ายกำกับ" สีเขียวแสดงค่าการคาดคะเนเป้าหมาย จุดเหล่านี้จะแสดงตามเวลาคาดคะเนไม่ใช่เวลาป้อนข้อมูล นั่นคือสาเหตุที่ช่วงของป้ายกำกับเลื่อนไป 1 ขั้นเมื่อเทียบกับอินพุต
  • กากบาท "Predictions" สีส้มคือการคาดคะเนของโมเดลสำหรับแต่ละขั้นตอนเวลาที่ส่งออก หากแบบจำลองคาดการณ์ได้อย่างสมบูรณ์แบบการคาดการณ์จะไปที่ "ป้ายกำกับ" โดยตรง

แบบจำลองเชิงเส้น

รุ่นสุวินัยง่ายที่คุณสามารถนำไปใช้กับงานนี้คือการแทรกการเปลี่ยนแปลงเชิงเส้นตรงระหว่างอินพุตและเอาต์พุต ในกรณีนี้ผลลัพธ์จากขั้นตอนเวลาขึ้นอยู่กับขั้นตอนนั้นเท่านั้น:

การทำนายขั้นตอนเดียว

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 [==============================] - 5s 3ms/step - loss: 0.2864 - mean_absolute_error: 0.2848 - val_loss: 0.0163 - val_mean_absolute_error: 0.0975
Epoch 2/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0121 - mean_absolute_error: 0.0817 - val_loss: 0.0103 - val_mean_absolute_error: 0.0752
Epoch 3/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0103 - mean_absolute_error: 0.0749 - val_loss: 0.0098 - val_mean_absolute_error: 0.0738
Epoch 4/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0099 - mean_absolute_error: 0.0733 - val_loss: 0.0095 - val_mean_absolute_error: 0.0731
Epoch 5/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0096 - mean_absolute_error: 0.0721 - val_loss: 0.0092 - val_mean_absolute_error: 0.0719
Epoch 6/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0095 - mean_absolute_error: 0.0715 - val_loss: 0.0091 - val_mean_absolute_error: 0.0716
Epoch 7/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0094 - mean_absolute_error: 0.0710 - val_loss: 0.0091 - val_mean_absolute_error: 0.0716
Epoch 8/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0093 - mean_absolute_error: 0.0707 - val_loss: 0.0090 - val_mean_absolute_error: 0.0706
Epoch 9/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0092 - mean_absolute_error: 0.0704 - val_loss: 0.0090 - val_mean_absolute_error: 0.0712
Epoch 10/20
1534/1534 [==============================] - 5s 3ms/step - loss: 0.0092 - mean_absolute_error: 0.0703 - val_loss: 0.0091 - val_mean_absolute_error: 0.0715
439/439 [==============================] - 1s 2ms/step - loss: 0.0091 - mean_absolute_error: 0.0715

เช่นเดียวกับแบบจำลอง 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.0159 - mean_absolute_error: 0.0814 - val_loss: 0.0086 - val_mean_absolute_error: 0.0693
Epoch 2/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0079 - mean_absolute_error: 0.0645 - val_loss: 0.0076 - val_mean_absolute_error: 0.0629
Epoch 3/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0074 - mean_absolute_error: 0.0622 - val_loss: 0.0085 - val_mean_absolute_error: 0.0666
Epoch 4/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0072 - mean_absolute_error: 0.0608 - val_loss: 0.0071 - val_mean_absolute_error: 0.0593
Epoch 5/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0070 - mean_absolute_error: 0.0595 - val_loss: 0.0067 - val_mean_absolute_error: 0.0579
Epoch 6/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0068 - mean_absolute_error: 0.0588 - val_loss: 0.0072 - val_mean_absolute_error: 0.0594
Epoch 7/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0068 - mean_absolute_error: 0.0583 - val_loss: 0.0066 - val_mean_absolute_error: 0.0564
Epoch 8/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0066 - mean_absolute_error: 0.0576 - val_loss: 0.0078 - val_mean_absolute_error: 0.0637
Epoch 9/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0066 - mean_absolute_error: 0.0576 - val_loss: 0.0066 - val_mean_absolute_error: 0.0564
Epoch 10/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0065 - mean_absolute_error: 0.0569 - val_loss: 0.0062 - val_mean_absolute_error: 0.0551
Epoch 11/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0065 - mean_absolute_error: 0.0571 - val_loss: 0.0070 - val_mean_absolute_error: 0.0596
Epoch 12/20
1534/1534 [==============================] - 6s 4ms/step - loss: 0.0064 - mean_absolute_error: 0.0565 - val_loss: 0.0063 - val_mean_absolute_error: 0.0551
439/439 [==============================] - 1s 3ms/step - loss: 0.0063 - mean_absolute_error: 0.0551

หนาแน่นหลายขั้นตอน

โมเดลขั้นตอนเดียวไม่มีบริบทสำหรับค่าปัจจุบันของอินพุต ไม่สามารถดูได้ว่าคุณลักษณะการป้อนข้อมูลเปลี่ยนแปลงไปอย่างไรเมื่อเวลาผ่านไป เพื่อแก้ไขปัญหานี้โมเดลต้องเข้าถึงขั้นตอนเวลาหลายขั้นตอนเมื่อทำการคาดคะเน:

การทำนายแต่ละครั้งจะใช้ขั้นตอนสามขั้นตอน

โมเดล 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 เป็นเลเยอร์แรกของโมเดล:

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.0078 - mean_absolute_error: 0.0637

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)

InvalidArgumentError:Matrix size-incompatible: In[0]: [32,456], In[1]: [57,32] [Op:MatMul]

โมเดล Convolutional ในส่วนถัดไปจะแก้ไขปัญหานี้

โครงข่ายประสาท Convolution

เลเยอร์ Convolution (เลเยอร์ 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.0063 - mean_absolute_error: 0.0556

ความแตกต่างระหว่าง conv_model และโมเดล multi_step_dense คือสามารถรัน conv_model กับอินพุตที่มีความยาวเท่าใดก็ได้ เลเยอร์ Convolutional ถูกนำไปใช้กับหน้าต่างบานเลื่อนของอินพุต:

ดำเนินการแบบจำลอง Convolutional ตามลำดับ

หากคุณรันบนอินพุตที่กว้างขึ้นจะให้เอาต์พุตที่กว้างขึ้น:

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

เครือข่ายประสาทกำเริบ

Recurrent Neural Network (RNN) เป็นเครือข่ายประสาทชนิดหนึ่งที่เหมาะกับข้อมูลอนุกรมเวลา RNN จะประมวลผลอนุกรมเวลาทีละขั้นตอนโดยรักษาสถานะภายในตั้งแต่ขั้นตอนเวลาจนถึงขั้นตอนเวลา

สำหรับรายละเอียดเพิ่มเติมโปรดอ่าน บทแนะนำการสร้างข้อความ หรือ คู่มือ RNN

ในบทช่วยสอนนี้คุณจะใช้เลเยอร์ RNN ที่เรียกว่า Long Short Term Memory ( 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)
])

ด้วย 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.0057 - mean_absolute_error: 0.0523

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.0694
Dense       : 0.0566
Multi step dense: 0.0667
Conv        : 0.0572
LSTM        : 0.0528

รุ่นหลายเอาต์พุต

จนถึงตอนนี้แบบจำลองทั้งหมดคาดการณ์คุณสมบัติเอาต์พุตเดี่ยว 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 แกน 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 3ms/step - loss: 0.0706 - mean_absolute_error: 0.1362

ร.น.

%%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.0613 - mean_absolute_error: 0.1192

CPU times: user 6min 18s, sys: 1min 36s, total: 7min 55s
Wall time: 2min 53s

ขั้นสูง: การเชื่อมต่อที่เหลือ

แบบจำลอง 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.0619 - mean_absolute_error: 0.1181

CPU times: user 2min 2s, sys: 31.2 s, total: 2min 33s
Wall time: 56.9 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.1367
LSTM           : 0.1208
Residual LSTM  : 0.1197

ประสิทธิภาพข้างต้นเป็นค่าเฉลี่ยของเอาต์พุตแบบจำลองทั้งหมด

แบบจำลองหลายขั้นตอน

ทั้งแบบเอาต์พุตเดี่ยวและแบบหลายเอาต์พุตในส่วนก่อนหน้านี้ได้ทำการ คาดการณ์ขั้นตอนครั้งเดียว 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 ด้วย 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.2556 - mean_absolute_error: 0.3050

png

หนาแน่น

การเพิ่ม layers.Dense ความ 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 3ms/step - loss: 0.2192 - mean_absolute_error: 0.2807

png

ซีเอ็นเอ็น

แบบจำลอง Convolutional ทำการคาดการณ์ตามประวัติความกว้างคงที่ซึ่งอาจนำไปสู่ประสิทธิภาพที่ดีกว่าแบบจำลองที่หนาแน่นเนื่องจากสามารถดูว่าสิ่งต่างๆเปลี่ยนแปลงไปอย่างไรเมื่อเวลาผ่านไป:

แบบจำลอง Convolutional จะเห็นว่าสิ่งต่างๆเปลี่ยนแปลงไปอย่างไรเมื่อเวลาผ่านไป

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 3ms/step - loss: 0.2142 - mean_absolute_error: 0.2805

png

ร.น.

แบบจำลองที่เกิดซ้ำสามารถเรียนรู้ที่จะใช้ปัจจัยการผลิตที่มีมายาวนานหากมันเกี่ยวข้องกับการคาดคะเนที่โมเดลกำลังสร้างขึ้น ที่นี่โมเดลจะสะสมสถานะภายในเป็นเวลา 24 ชั่วโมงก่อนที่จะทำการทำนายเพียงครั้งเดียวใน 24 ชั่วโมงถัดไป

ในรูปแบบ single-shot 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 3ms/step - loss: 0.2146 - mean_absolute_error: 0.2829

png

ขั้นสูง: แบบจำลองอัตโนมัติ

แบบจำลองข้างต้นทั้งหมดคาดการณ์ลำดับเอาต์พุตทั้งหมดเป็นขั้นตอนเดียว

ในบางกรณีอาจเป็นประโยชน์สำหรับแบบจำลองในการแยกย่อยการคาดการณ์นี้ออกเป็นขั้นตอนเวลาแต่ละขั้นตอน จากนั้นเอาต์พุตของแต่ละโมเดลจะถูกป้อนกลับเข้าไปในตัวเองในแต่ละขั้นตอนและการคาดการณ์สามารถกำหนดเงื่อนไขในเวอร์ชันก่อนหน้าได้เช่นเดียวกับใน ลำดับการสร้าง แบบคลาสสิก ด้วย Recurrent Neural Networks

ข้อได้เปรียบที่ชัดเจนอย่างหนึ่งของโมเดลสไตล์นี้คือสามารถตั้งค่าให้ผลิตเอาต์พุตที่มีความยาวแตกต่างกันได้

คุณสามารถใช้โมเดลมัลติเอาท์พุตแบบขั้นตอนเดียวที่ได้รับการฝึกอบรมในช่วงครึ่งแรกของบทช่วยสอนนี้และเรียกใช้ในลูปการตอบกลับอัตโนมัติ แต่ที่นี่คุณจะมุ่งเน้นไปที่การสร้างแบบจำลองที่ได้รับการฝึกฝนมาอย่างชัดเจนให้ทำเช่นนั้น

ป้อนกลับเอาต์พุตของโมเดลไปยังอินพุต

ร.น.

บทช่วยสอนนี้สร้างเฉพาะโมเดล RNN อัตโนมัติเท่านั้น แต่รูปแบบนี้สามารถนำไปใช้กับโมเดลใดก็ได้ที่ออกแบบมาเพื่อส่งออกการประทับเวลาเดียว

โมเดลจะมีรูปแบบพื้นฐานเช่นเดียวกับโมเดล LSTM ขั้นตอน LSTM : LSTM ตามด้วย layers.Dense ความ layers.Dense ที่แปลงเอาต์พุต LSTM เป็นการคาดคะเนโมเดล

layers.LSTM คือ layers.LSTMCell อยู่ใน layers.RNN ระดับที่สูงขึ้น layers.RNN ที่จัดการสถานะและลำดับผลลัพธ์สำหรับคุณ (ดูรายละเอียดใน Keras RNN )

ในกรณีนี้โมเดลต้องจัดการอินพุตสำหรับแต่ละขั้นตอนด้วยตนเองดังนั้นจึงใช้ 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 ขั้นตอน 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 ของ RNN และการคาดคะเนเริ่มต้นตอนนี้คุณสามารถทำซ้ำแบบจำลองที่ให้อาหารการคาดการณ์ในแต่ละขั้นตอนย้อนกลับเป็นอินพุตได้

วิธีที่ง่ายที่สุดในการรวบรวมการคาดคะเนผลลัพธ์คือการใช้ python list และ 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 8ms/step - loss: 0.2352 - mean_absolute_error: 0.3116

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.2754
Conv    : 0.2724
LSTM    : 0.2770
AR LSTM : 0.3026

ผลกำไรที่ได้รับจากแบบจำลองที่หนาแน่นไปจนถึงแบบจำลองที่มีความซับซ้อนและเกิดซ้ำนั้นมีเพียงไม่กี่เปอร์เซ็นต์ (ถ้ามี) และแบบจำลองอัตโนมัติทำงานได้แย่ลงอย่างชัดเจน ดังนั้นแนวทางที่ซับซ้อนกว่านี้อาจไม่คุ้มค่าในขณะที่เกิดปัญหา นี้ แต่ไม่มีทางรู้ได้โดยไม่ต้องพยายามและโมเดลเหล่านี้อาจเป็นประโยชน์สำหรับปัญหา ของคุณ

ขั้นตอนถัดไป

บทช่วยสอนนี้เป็นบทแนะนำโดยย่อเกี่ยวกับการพยากรณ์อนุกรมเวลาโดยใช้ TensorFlow