Advertisement
Guest User

Untitled

a guest
Nov 12th, 2016
126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.90 KB | None | 0 0
  1. #!/usr/bin/env python
  2. """
  3. Example of using Keras to implement a 1D convolutional neural network (CNN) for timeseries prediction.
  4. """
  5.  
  6. from __future__ import print_function, division
  7.  
  8. import numpy as np
  9. from keras.layers import Convolution1D, Dense, MaxPooling1D, Flatten
  10. from keras.models import Sequential
  11.  
  12.  
  13. __date__ = '2016-07-22'
  14.  
  15.  
  16. def make_timeseries_regressor(window_size, filter_length, nb_input_series=1, nb_outputs=1, nb_filter=4):
  17. """:Return: a Keras Model for predicting the next value in a timeseries given a fixed-size lookback window of previous values.
  18.  
  19. The model can handle multiple input timeseries (`nb_input_series`) and multiple prediction targets (`nb_outputs`).
  20.  
  21. :param int window_size: The number of previous timeseries values to use as input features. Also called lag or lookback.
  22. :param int nb_input_series: The number of input timeseries; 1 for a single timeseries.
  23. The `X` input to ``fit()`` should be an array of shape ``(n_instances, window_size, nb_input_series)``; each instance is
  24. a 2D array of shape ``(window_size, nb_input_series)``. For example, for `window_size` = 3 and `nb_input_series` = 1 (a
  25. single timeseries), one instance could be ``[[0], [1], [2]]``. See ``make_timeseries_instances()``.
  26. :param int nb_outputs: The output dimension, often equal to the number of inputs.
  27. For each input instance (array with shape ``(window_size, nb_input_series)``), the output is a vector of size `nb_outputs`,
  28. usually the value(s) predicted to come after the last value in that input instance, i.e., the next value
  29. in the sequence. The `y` input to ``fit()`` should be an array of shape ``(n_instances, nb_outputs)``.
  30. :param int filter_length: the size (along the `window_size` dimension) of the sliding window that gets convolved with
  31. each position along each instance. The difference between 1D and 2D convolution is that a 1D filter's "height" is fixed
  32. to the number of input timeseries (its "width" being `filter_length`), and it can only slide along the window
  33. dimension. This is useful as generally the input timeseries have no spatial/ordinal relationship, so it's not
  34. meaningful to look for patterns that are invariant with respect to subsets of the timeseries.
  35. :param int nb_filter: The number of different filters to learn (roughly, input patterns to recognize).
  36. """
  37. model = Sequential((
  38. # The first conv layer learns `nb_filter` filters (aka kernels), each of size ``(filter_length, nb_input_series)``.
  39. # Its output will have shape (None, window_size - filter_length + 1, nb_filter), i.e., for each position in
  40. # the input timeseries, the activation of each filter at that position.
  41. Convolution1D(nb_filter=nb_filter, filter_length=filter_length, activation='relu', input_shape=(window_size, nb_input_series)),
  42. MaxPooling1D(), # Downsample the output of convolution by 2X.
  43. Convolution1D(nb_filter=nb_filter, filter_length=filter_length, activation='relu'),
  44. MaxPooling1D(),
  45. Flatten(),
  46. Dense(nb_outputs, activation='linear'), # For binary classification, change the activation to 'sigmoid'
  47. ))
  48. model.compile(loss='mse', optimizer='adam', metrics=['mae'])
  49. # To perform (binary) classification instead:
  50. # model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['binary_accuracy'])
  51. return model
  52.  
  53.  
  54. def make_timeseries_instances(timeseries, window_size):
  55. """Make input features and prediction targets from a `timeseries` for use in machine learning.
  56.  
  57. :return: A tuple of `(X, y, q)`. `X` are the inputs to a predictor, a 3D ndarray with shape
  58. ``(timeseries.shape[0] - window_size, window_size, timeseries.shape[1] or 1)``. For each row of `X`, the
  59. corresponding row of `y` is the next value in the timeseries. The `q` or query is the last instance, what you would use
  60. to predict a hypothetical next (unprovided) value in the `timeseries`.
  61. :param ndarray timeseries: Either a simple vector, or a matrix of shape ``(timestep, series_num)``, i.e., time is axis 0 (the
  62. row) and the series is axis 1 (the column).
  63. :param int window_size: The number of samples to use as input prediction features (also called the lag or lookback).
  64. """
  65. timeseries = np.asarray(timeseries)
  66. assert 0 < window_size < timeseries.shape[0]
  67. X = np.atleast_3d(np.array([timeseries[start:start + window_size] for start in range(0, timeseries.shape[0] - window_size)]))
  68. y = timeseries[window_size:]
  69. q = np.atleast_3d([timeseries[-window_size:]])
  70. return X, y, q
  71.  
  72.  
  73. def evaluate_timeseries(timeseries, window_size):
  74. """Create a 1D CNN regressor to predict the next value in a `timeseries` using the preceding `window_size` elements
  75. as input features and evaluate its performance.
  76.  
  77. :param ndarray timeseries: Timeseries data with time increasing down the rows (the leading dimension/axis).
  78. :param int window_size: The number of previous timeseries values to use to predict the next.
  79. """
  80. filter_length = 5
  81. nb_filter = 4
  82. timeseries = np.atleast_2d(timeseries)
  83. if timeseries.shape[0] == 1:
  84. timeseries = timeseries.T # Convert 1D vectors to 2D column vectors
  85.  
  86. nb_samples, nb_series = timeseries.shape
  87. print('\n\nTimeseries ({} samples by {} series):\n'.format(nb_samples, nb_series), timeseries)
  88. model = make_timeseries_regressor(window_size=window_size, filter_length=filter_length, nb_input_series=nb_series, nb_outputs=nb_series, nb_filter=nb_filter)
  89. print('\n\nModel with input size {}, output size {}, {} conv filters of length {}'.format(model.input_shape, model.output_shape, nb_filter, filter_length))
  90. model.summary()
  91.  
  92. X, y, q = make_timeseries_instances(timeseries, window_size)
  93. print('\n\nInput features:', X, '\n\nOutput labels:', y, '\n\nQuery vector:', q, sep='\n')
  94. test_size = int(0.01 * nb_samples) # In real life you'd want to use 0.2 - 0.5
  95. X_train, X_test, y_train, y_test = X[:-test_size], X[-test_size:], y[:-test_size], y[-test_size:]
  96. model.fit(X_train, y_train, nb_epoch=25, batch_size=2, validation_data=(X_test, y_test))
  97.  
  98. pred = model.predict(X_test)
  99. print('\n\nactual', 'predicted', sep='\t')
  100. for actual, predicted in zip(y_test, pred.squeeze()):
  101. print(actual.squeeze(), predicted, sep='\t')
  102. print('next', model.predict(q).squeeze(), sep='\t')
  103.  
  104.  
  105. def main():
  106. """Prepare input data, build model, evaluate."""
  107. np.set_printoptions(threshold=25)
  108. ts_length = 1000
  109. window_size = 50
  110.  
  111. print('\nSimple single timeseries vector prediction')
  112. timeseries = np.arange(ts_length) # The timeseries f(t) = t
  113. evaluate_timeseries(timeseries, window_size)
  114.  
  115. print('\nMultiple-input, multiple-output prediction')
  116. timeseries = np.array([np.arange(ts_length), -np.arange(ts_length)]).T # The timeseries f(t) = [t, -t]
  117. evaluate_timeseries(timeseries, window_size)
  118.  
  119.  
  120. if __name__ == '__main__':
  121. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement