对 LSTM 模型的 Jacobian 使用渐变带 - Python

Using Gradient Tape for Jacobian of LSTM model - Python

我正在使用 LSTM 构建一个序列到一个模型的预测。我的数据有 4 个输入变量和 1 个需要预测的输出变量。数据是时间序列数据。数据的总长度为 38265(时间步长总数)。总数据在一个大小为 38265 *5

的数据框中

我想使用 4 个输入变量的前 20 个时间步数据来预测我的输出变量。为此,我正在使用以下代码。

model = Sequential()

model.add(LSTM(units = 120, activation ='relu', return_sequences = False,input_shape = 
(train_in.shape[1],5)))
model.add(Dense(100,activation='relu'))
model.add(Dense(50,activation='relu'))

model.add(Dense(1))

我想使用 tf.Gradient 磁带计算输出变量 w.r.t LSTM 模型函数的雅可比行列式。谁能帮我解决这个问题??

分离关于 LSTM 输入的输出的 Jacobian 的解决方案可以如下完成:

  1. 使用tf.GradientTape(),我们可以计算由梯度流产生的雅可比矩阵。

  2. 然而,为了获得 Jacobian,输入需要采用 tf.EagerTensor 的形式,这通常在我们想要查看输出的 Jacobian 时可用(在执行 y=model 之后(X))。以下代码片段分享了这个想法:

#Get the Jacobian for each persistent gradient evaluation
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(2,activation='relu'))
model.add(tf.keras.layers.Dense(2,activation='relu'))
x = tf.constant([[5., 6., 3.]])

with tf.GradientTape(persistent=True,watch_accessed_variables=True) as tape:
  # Forward pass
  tape.watch(x)
  y = model(x)
  loss = tf.reduce_mean(y**2)
print('Gradients\n')
jacobian_wrt_loss=tape.jacobian(loss,x)
print(f'{jacobian_wrt_loss}\n')
jacobian_wrt_y=tape.jacobian(y,x)
print(f'{jacobian_wrt_y}\n')
  1. 但是为了获得中间输出,例如在这种情况下,有很多样本使用 。当我们分离来自 model.layers.output 的输出时,我们得到的类型是 Keras.Tensor 而不是 EagerTensor。 然而,为了创建 Jacobian,我们需要 Eager Tensor。 (在多次尝试使用 @tf.function 包装失败后,因为 TF>2.0 中已经存在急切执行)

  2. 因此,或者,可以使用所需的层创建辅助模型(在这种情况下,只有输入层和 LSTM 层)。该模型的输出将是 tf.EagerTensor,其中将对雅可比张量的创建很有用。此代码段中显示了以下内容:

#General Syntax for getting jacobians for each layer output
import numpy as np
import tensorflow as tf
tf.executing_eagerly()
x=tf.constant([[15., 60., 32.]])
x_inp = tf.keras.layers.Input(tensor=tf.constant([[15., 60., 32.]]))
model=tf.keras.Sequential()
model.add(tf.keras.layers.Dense(2,activation='relu',name='dense_1'))
model.add(tf.keras.layers.Dense(2,activation='relu',name='dense_2'))

aux_model=tf.keras.Sequential()
aux_model.add(tf.keras.layers.Dense(2,activation='relu',name='dense_1'))
#model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])

with tf.GradientTape(persistent=True,watch_accessed_variables=True) as tape:
  # Forward pass
  tape.watch(x)
  x_y = model(x)
  act_y=aux_model(x)
  print(x_y,type(x_y))
  ops=[layer.output for layer in model.layers]
    
# ops=[layer.output for layer in model.layers]
# inps=[layer.input for layer in model.layers]
print('Jacobian of Full FFNN\n')
jacobian=tape.jacobian(x_y,x)
print(f'{jacobian[0]}\n')

print('Jacobian of FFNN with Just first Dense\n')
jacobian=tape.jacobian(act_y,x)
print(f'{jacobian[0]}\n')

这里我使用了一个由 2 个 Dense 层组成的简单 FFNN,但我想评估 w.r.t 第一个 Dense 层的输出。因此,我创建了一个只有 1 个密集层的辅助模型,并从中确定了雅可比行列式的输出。

详情可见here.

在@Abhilash Majumder 的帮助下,我已经这样做了。我将其发布在这里,以便将来对某人有所帮助。 将 numpy 导入为 np 将 pandas 导入为 pd 将 tensorflow 导入为 tf tf.compat.v1.enable_eager_execution() #This will enable eager execution which is must.

tf.executing_eagerly() #check if eager execution is enabled or not. Should give "True"

data = pd.read_excel("FileName or Location ")
#My data is in the from of dataframe with 127549 rows and 5 columns(127549*5)

a = data[:20]  #shape is (20,5)
b = data[50:70] # shape is (20,5)
A = [a,b]  # making a list
A = np.array(A) # convert into array size (2,20,5) 

At = tf.convert_to_tensor(A, np.float32) #convert into tensor
At.shape # TensorShape([Dimension(2), Dimension(20), Dimension(5)])

model = load_model('EKF-LSTM-1.h5') # Load the trained model
# I have a trained model which is shown in the question above. 
# Output of this model is a single value

with tf.GradientTape(persistent=True,watch_accessed_variables=True) as tape:

tape.watch(At)
y1 = model(At) #defining your output as a function of input variables
print(y1,type(y1)

#output 
tf.Tensor([[0.04251503],[0.04634088]], shape=(2, 1), dtype=float32) <class 
'tensorflow.python.framework.ops.EagerTensor'>

jacobian=tape.jacobian(y1,At) #jacobian of output w.r.t both inputs
jacobian.shape 

输出

TensorShape([Dimension(2), Dimension(1), Dimension(2), Dimension(20), Dimension(5)])

这里我计算了 Jacobian w.r.t 2 个输入,每个输入的大小为 (20,5)。如果你只想计算 w.r.t 到一个大小为 (20,5) 的输入,那么使用这个

jacobian=tape.jacobian(y1,At[0]) #jacobian of output w.r.t only 1st input in 'At'
jacobian.shape 

输出

TensorShape([Dimension(1), Dimension(1), Dimension(1), Dimension(20), Dimension(5)])

对于那些希望在 input[i]output[j]i != j 的一系列相互独立的输入和输出上计算雅可比矩阵的人,请考虑 batch_jacobian方法。

这会将计算出的雅可比张量的维数减少一维,这可能是 运行 内存不足与未内存不足的区别。

请参阅:TensorFlow GradientTape docs 中的 batch_jacobian