Как интерпретировать веса в слое LSTM в Керасе

В настоящее время я тренирую рекуррентную нейронную сеть для прогнозирования погоды, используя слой LSTM. Сама сеть довольно проста и выглядит примерно так:

model = Sequential()  
model.add(LSTM(hidden_neurons, input_shape=(time_steps, feature_count), return_sequences=False))  
model.add(Dense(feature_count))  
model.add(Activation("linear"))  

Веса слоя LSTM имеют следующие формы:

for weight in model.get_weights(): # weights from Dense layer omitted
    print(weight.shape)

> (feature_count, hidden_neurons)
> (hidden_neurons, hidden_neurons)
> (hidden_neurons,)
> (feature_count, hidden_neurons)
> (hidden_neurons, hidden_neurons)
> (hidden_neurons,)
> (feature_count, hidden_neurons)
> (hidden_neurons, hidden_neurons)
> (hidden_neurons,)
> (feature_count, hidden_neurons)
> (hidden_neurons, hidden_neurons)
> (hidden_neurons,)

Короче говоря, похоже, что в этом слое LSTM есть четыре "элемента". Теперь мне интересно, как их интерпретировать:

  • Где time_steps параметр в этом представлении? Как это влияет на вес?

  • Я читал, что LSTM состоит из нескольких блоков, таких как вход и ворота забывания. Если они представлены в этих весовых матрицах, то какая матрица принадлежит каким воротам?

  • Есть ли способ узнать, чему научилась сеть? Например, сколько это займет с последнего временного шага (t-1 если мы хотим прогнозировать t) и сколько из t-2 так далее? Было бы интересно узнать, можем ли мы считать из весов, что вход t-5 совершенно не имеет значения, например.

Разъяснения и подсказки будут с благодарностью.

2 ответа

Если вы используете Keras 2.2.0

Когда вы печатаете

print(model.layers[0].trainable_weights)

вы должны увидеть три тензора: lstm_1/kernel, lstm_1/recurrent_kernel, lstm_1/bias:0Одно из измерений каждого тензора должно быть произведением

4 * число_единиц

где number_of_units - это количество нейронов. Пытаться:

units = int(int(model.layers[0].trainable_weights[0].shape[1])/4)
print("No units: ", units)

Это потому, что каждый тензор содержит веса для четырех единиц LSTM (в этом порядке):

i (вход), f (забыть), c (состояние ячейки) и o (выход)

Поэтому для извлечения весов вы можете просто использовать оператор среза:

W = model.layers[0].get_weights()[0]
U = model.layers[0].get_weights()[1]
b = model.layers[0].get_weights()[2]

W_i = W[:, :units]
W_f = W[:, units: units * 2]
W_c = W[:, units * 2: units * 3]
W_o = W[:, units * 3:]

U_i = U[:, :units]
U_f = U[:, units: units * 2]
U_c = U[:, units * 2: units * 3]
U_o = U[:, units * 3:]

b_i = b[:units]
b_f = b[units: units * 2]
b_c = b[units * 2: units * 3]
b_o = b[units * 3:]

Источник: код keras

Я, вероятно, не смогу ответить на все ваши вопросы, но я могу предоставить больше информации о ячейке LSTM и различных компонентах, из которых она сделана.

Этот пост на github предлагает способ увидеть имя параметров при их печати:

model = Sequential()
model.add(LSTM(4,input_dim=5,input_length=N,return_sequences=True))
for e in zip(model.layers[0].trainable_weights, model.layers[0].get_weights()):
    print('Param %s:\n%s' % (e[0],e[1]))

Вывод выглядит так:

Param lstm_3_W_i:
[[ 0.00069305, ...]]
Param lstm_3_U_i:
[[ 1.10000002, ...]]
Param lstm_3_b_i:
[ 0., ...]
Param lstm_3_W_c:
[[-1.38370085, ...]]
...

Теперь вы можете найти здесь больше информации об этих различных весах. Они имеют имена W, U, V и b с разными индексами.

  • W матрицы - это те, которые преобразуют входные данные в некоторые другие внутренние значения. Они имеют форму [input_dim, output_dim],
  • Матрицы U - это те, которые преобразуют предыдущее скрытое состояние в другое внутреннее значение. Они имеют форму [output_dim, output_dim],
  • Векторы b - это смещение для каждого блока. Все они имеют форму [output_dim]
  • V используется только в выходном вентиле, он выбирает, какие значения выводить из нового внутреннего состояния. Имеет форму [output_dim, output_dim]

Короче говоря, у вас действительно есть 4 разных "блока" (или внутренних слоя).

  • gate gate: решает, основываясь на предыдущем скрытом состоянии (h_{t-1}) и входе (x), значения которого нужно забыть из предыдущего внутреннего состояния ячейки (C_ {t-1}):

    f_t = сигмоид (W_f * x + U_f * h_ {t-1} + b_f)

    f_t - это вектор значений между 0 и 1, который будет кодировать, что оставить (=1) и что забыть (=0) из предыдущего состояния ячейки.

  • Входной вентиль: он решает, основываясь на предыдущем скрытом состоянии (h_{t-1}) и входе (x), какие значения использовать из ввода (x):

    i_t = сигмоид (W_i * x + U_i * h_ {t-1} + b_i)

    i_t - это вектор значений между 0 и 1, который будет кодировать, какие значения использовать для обновления нового состояния ячейки.

  • Значение кандидата: Мы создаем новые значения кандидатов, чтобы обновить внутреннее состояние ячейки, используя input (x) и предыдущее скрытое состояние (h_{t-1}):

    Ct_t = tanh (W_c * x + U_c * h_ {t-1} + b_c)

    Ct_t - это вектор, содержащий потенциальные значения для обновления состояния ячейки (C_{t-1}).

Мы используем эти три значения для создания нового внутреннего состояния ячейки (C_t):

C_t = f_t * C_ {t-1} + i_t * Ct_t

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

  • Выходные ворота: мы не хотим выводить состояние ячейки, так как это может рассматриваться как абстракция того, что мы хотим вывести (h_t). Итак, мы строим h_t, вывод для этого шага на основе всей имеющейся у нас информации:

    h_t = W_o * x + U_o * h_ {t-1} + V_o * C_t + b_o

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

Что касается ваших вопросов, у меня теперь есть идея, как отследить то, что использовалось из входных данных для изменения состояния. В конечном итоге вы можете посмотреть на различные W-матрицы, так как именно они обрабатывают ввод. W_c предоставит вам информацию о том, что потенциально может использоваться для обновления состояния ячейки. W_o может дать вам некоторую информацию о том, что используется для создания выходных данных... Но все это будет относительно других весов, так как предыдущие состояния также оказывают влияние.

Однако, если вы видите некоторые сильные веса в W_c, это может ничего не значить, потому что входные ворота (i_t) могут быть полностью закрыты и уничтожить обновление состояния ячейки... Это сложно, область математики, которая прослеживает то, что происходит в нейронной сети, действительно сложно.

Нейронные сети - это действительно черные ящики для наиболее общего случая. Вы можете найти в литературе некоторые случаи, когда они прослеживают информацию от вывода до ввода, но это в очень особых случаях из того, что я прочитал.

Надеюсь, это поможет:-)

Другие вопросы по тегам