LSTM - Создание прогнозов на частичной последовательности - PullRequest
0 голосов
/ 19 ноября 2018

Этот вопрос продолжается до предыдущего вопроса , который я задал.

Я обучил модель LSTM прогнозированию двоичного класса (1 или 0) для партий по 100выборки с 3 признаками в каждой, то есть: форма данных (m, 100, 3), где m - количество партий.

Данные:

[
    [[1,2,3],[1,2,3]... 100 sampels],
    [[1,2,3],[1,2,3]... 100 sampels],
    ... avaialble batches in the training data
]

Цель:

[
   [1]
   [0]
   ...
]

Код модели:

def build_model(num_samples, num_features, is_training):
    model = Sequential()
    opt = optimizers.Adam(lr=0.0005, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0001)

    batch_size = None if is_training else 1
    stateful = False if is_training else True
    first_lstm = LSTM(32, batch_input_shape=(batch_size, num_samples, num_features), return_sequences=True,
                      activation='tanh', stateful=stateful)

    model.add(first_lstm)
    model.add(LeakyReLU())
    model.add(Dropout(0.2))
    model.add(LSTM(16, return_sequences=True, activation='tanh', stateful=stateful))
    model.add(Dropout(0.2))
    model.add(LeakyReLU())
    model.add(LSTM(8, return_sequences=False, activation='tanh', stateful=stateful))
    model.add(LeakyReLU())
    model.add(Dense(1, activation='sigmoid'))

    if is_training:
        model.compile(loss='binary_crossentropy', optimizer=opt,
                      metrics=['accuracy', keras_metrics.precision(), keras_metrics.recall(), f1])
    return model

Для этапа обучения модель НЕ с состоянием.При прогнозировании я использую модель с сохранением состояния , перебирая данные и выводя вероятность для каждой выборки:

for index, row in data.iterrows():
    if index % 100 == 0:
        predicting_model.reset_states()
    vals = np.array([[row[['a', 'b', 'c']].values]])
    prob = predicting_model.predict_on_batch(vals)

При просмотре вероятности в конце пакета онаэто именно то значение, которое я получаю при прогнозировании для всего пакета (не один за другим).Тем не менее, я ожидал, что вероятность будет всегда продолжаться в правильном направлении, когда новые образцы прибывают.На самом деле происходит то, что выходные данные вероятности могут приходить в неверный класс на произвольной выборке (см. Ниже).


Две выборки из 100 выборок за период прогнозирования (метка = 1):

enter image description here

и Label = 0: enter image description here

Есть ли способ добиться того, чего я хочу (избегать экстремальных скачков при прогнозировании вероятности), или это факт?

Любые объяснения, советы будут приветствоваться.


Обновление Благодаря @ сегодняшнему советуЯ пытался обучить сеть выводу скрытого состояния для каждого временного шага ввода, используя return_sequence=True на последнем слое LSTM.

Так что теперь метки выглядят так (форма (100,100)):

[[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
...]

Сводка модели:

Layer (type)                 Output Shape              Param #   
=================================================================
lstm_1 (LSTM)                (None, 100, 32)           4608      
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None, 100, 32)           0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 100, 32)           0         
_________________________________________________________________
lstm_2 (LSTM)                (None, 100, 16)           3136      
_________________________________________________________________
dropout_2 (Dropout)          (None, 100, 16)           0         
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU)    (None, 100, 16)           0         
_________________________________________________________________
lstm_3 (LSTM)                (None, 100, 8)            800       
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU)    (None, 100, 8)            0         
_________________________________________________________________
dense_1 (Dense)              (None, 100, 1)            9         
=================================================================
Total params: 8,553
Trainable params: 8,553
Non-trainable params: 0
_________________________________________________________________

Однако я получаю исключение:

ValueError: Error when checking target: expected dense_1 to have 3 dimensions, but got array with shape (75, 100)

Что мне нужно исправить?

1 Ответ

0 голосов
/ 19 ноября 2018

Примечание: это просто идея, и она может быть ошибочной.Попробуйте, если хотите, и я буду признателен за любые отзывы.


Есть ли способ достичь того, чего я хочу (избегать экстремальных скачков при прогнозировании вероятности), или этоданный факт?

Вы можете сделать это эксперимент : установить аргумент return_sequences последнего слоя LSTM на True и реплицировать метки каждого образца столько же, сколько и длинакаждого образца.Например, если образец имеет длину 100, а его метка равна 0, то создайте новую метку для этого образца, которая состоит из 100 нулей (вы, вероятно, можете легко сделать это, используя функцию numpy, например np.repeat).Затем переобучите свою новую модель и протестируйте ее на новых образцах.Я не уверен в этом, но я ожидал бы больше монотонно увеличивающихся / уменьшающихся графов вероятности на этот раз.


Обновление: Указанная вами ошибка вызвана тем, что меткидолжен быть трехмерным массивом (посмотрите на выходную форму последнего слоя в сводке модели).Используйте np.expand_dims, чтобы добавить еще одну ось размера один в конец.Правильный способ повторения меток будет выглядеть следующим образом, при условии, что y_train имеет форму (num_samples,):

rep_y_train = np.repeat(y_train, num_reps).reshape(-1, num_reps, 1)

Эксперимент с набором данных IMDB:

На самом деле,Я попробовал эксперимент, предложенный выше для набора данных IMDB, используя простую модель с одним слоем LSTM.Один раз я использовал по одной метке на каждый образец (как в оригинальном подходе @Shlomi), а в другой раз я копировал метки, чтобы по одной метке на каждый временной шаг образца (как я предложил выше).Вот код, если вы хотите попробовать его самостоятельно:

from keras.layers import *
from keras.models import Sequential, Model
from keras.datasets import imdb
from keras.preprocessing.sequence import pad_sequences
import numpy as np

vocab_size = 10000
max_len = 200
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=vocab_size)
X_train = pad_sequences(x_train, maxlen=max_len)

def create_model(return_seq=False, stateful=False):
    batch_size = 1 if stateful else None
    model = Sequential()
    model.add(Embedding(vocab_size, 128, batch_input_shape=(batch_size, None)))
    model.add(CuDNNLSTM(64, return_sequences=return_seq, stateful=stateful))
    model.add(Dense(1, activation='sigmoid'))

    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
    return model

# train model with one label per sample
train_model = create_model()
train_model.fit(X_train, y_train, epochs=10, batch_size=128, validation_split=0.3)

# replicate the labels
y_train_rep = np.repeat(y_train, max_len).reshape(-1, max_len, 1)

# train model with one label per timestep
rep_train_model = create_model(True)
rep_train_model.fit(X_train, y_train_rep, epochs=10, batch_size=128, validation_split=0.3)

Затем мы можем создать реплики с отслеживанием состояния моделей тренировок и запустить их на некоторых тестовых данных, чтобы сравнить их результаты:

# replica of `train_model` with the same weights
test_model = create_model(False, True)
test_model.set_weights(train_model.get_weights())
test_model.reset_states()

# replica of `rep_train_model` with the same weights
rep_test_model = create_model(True, True)
rep_test_model.set_weights(rep_train_model.get_weights())
rep_test_model.reset_states()

def stateful_predict(model, samples):
    preds = []
    for s in samples:
        model.reset_states()
        ps = []
        for ts in s:
            p = model.predict(np.array([[ts]]))
            ps.append(p[0,0])
        preds.append(list(ps))
    return preds

X_test = pad_sequences(x_test, maxlen=max_len)

На самом деле, первая выборка X_test имеет метку 0 (то есть принадлежит отрицательному классу), а вторая выборка X_test имеет метку 1 (то есть принадлежит положительному классу).Итак, давайте сначала посмотрим, как будет выглядеть прогноз состояния с состоянием test_model (то есть тот, который был обучен с использованием одной метки на выборку) для этих двух выборок:

import matplotlib.pyplot as plt

preds = stateful_predict(test_model, X_test[0:2])

plt.plot(preds[0])
plt.plot(preds[1])
plt.legend(['Class 0', 'Class 1'])

Результат:

imagetest_model stateful predictions">

Правильная метка (т. Е. Вероятность) в конце (т. Е. Временной шаг 200), но очень колючая и колеблющаяся между ними.Теперь давайте сравним его с предсказаниями состояния rep_test_model (то есть того, который был обучен с использованием одного ярлыка на каждый временной шаг):

preds = stateful_predict(rep_test_model, X_test[0:2])

plt.plot(preds[0])
plt.plot(preds[1])
plt.legend(['Class 0', 'Class 1'])

Результат:

imagerep_test_model stateful predictions">

Опять же, исправьте предсказание метки в конце, но на этот раз с гораздо более плавным и монотонным трендом, как и ожидалось.

Обратите внимание, что это был просто пример для демонстрации, и поэтому я использовал очень простую модель с одним слоем LSTM, и я вообще не пытался его настраивать.Я полагаю, что при лучшей настройке модели (например, при настройке количества слоев, количества единиц в каждом слое, используемых функций активации, типа и параметров оптимизатора и т. Д.) Вы можете получить гораздо лучшие результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...