tensorflow/addons

changing the state matrix spectral radius doesn't affect the ESN behaviour

pfaz69 opened this issue · 1 comments

pfaz69 commented

System information

  • OS Platform and Distribution (e.g., Linux Ubuntu 16.04): Linux Ubuntu 20.04
  • TensorFlow version and how it was installed (source or binary): 2.7.0, binary
  • TensorFlow-Addons version and how it was installed (source or binary): 0.15.0, binary
  • Python version: 3.9.15
  • Is GPU used? (yes/no): no

Describe the bug
When changing the spectral radius of the ESN state matrix, a following dense layer learns always the same weights, while such weights should change to reflect the changed ESN state matrix.

Code to reproduce the issue

#Import the relevant modules
import numpy as np
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense
from tensorflow_addons.layers import ESN
from tensorflow_addons.rnn import ESNCell
from keras.layers import RNN
from sklearn.preprocessing import MinMaxScaler
from tensorflow import random as rnd

#Fix the seed
rnd.set_seed(0)

#The dataset can be downloaded from https://mantas.info/wp/wp-content/uploads/simple_esn/MackeyGlass_t17.txt
data = np.loadtxt('MackeyGlass_t17.txt')

#Normalize
scaler = MinMaxScaler(feature_range=(0, 1))
scaled = scaler.fit_transform(data.reshape(-1, 1))

#Split Dataset in Train and Test
train, test = scaled[0:-100], scaled[-100:]

#Split into input and output
train_X, train_y = train[:, :-1], train[:, -1:]
test_X, test_y = test[:, :-1], test[:, -1:]

#Reshaping
train_X = train_X.reshape((train_X.shape[0], 1, train_X.shape[1]))
test_X = test_X.reshape((test_X.shape[0], 1, test_X.shape[1]))

#Batch and epochs
batch_size = 20
epochs = 15

#changing spectral_radius doesn't affect the weights of the dense layer
spectral_radius = 0.9 #Try 0.1 or any other value
#---------------------------------------------------------------------------

#Design and run the model
model = Sequential()
#model.add(ESN(units = 12, spectral_radius = spectral_radius, leaky=0.75, connectivity = 0.9)) # this line works exactly like the next one
model.add(RNN(ESNCell(12, spectral_radius = spectral_radius, leaky=0.75, connectivity = 0.9)))
model.add(Dense(train_y.shape[1]))
model.compile(loss='huber', optimizer='adam')

model.fit(train_X, train_y, epochs=epochs, batch_size=batch_size, validation_data=(test_X, test_y), verbose=0, shuffle=False)

#Print the weights of the dense layer
print(model.layers[1].get_config(), model.layers[1].get_weights())

Other info / logs

Checking the call() method of class ESNCell (file: tensorflow_addons/rnn/esn_cell.py) it appears that the argument state is always passed as a tuple full of zeros. This way even though the state matrix is correctly generated (self.recurrent_kernel) its contribution gets always erased.

The dense layer weights I get (either with spectral_radius = 0.9 or 0.1) are:

[array([[-0.4414041 ],
[-0.24732435],
[ 0.48309386],
[-0.11242288],
[-0.35881412],
[-0.37281674],
[ 0.13455302],
[ 0.44686365],
[ 0.13275969],
[-0.01452708],
[-0.52097714],
[-0.12454855]], dtype=float32), array([0.55140686]

pfaz69 commented

This is due to this piece of code that prescribes only one time step for the input:

#Split into input and output
train_X, train_y = train[:, :-1], train[:, -1:]
test_X, test_y = test[:, :-1], test[:, -1:]

#Reshaping
train_X = train_X.reshape((train_X.shape[0], 1, train_X.shape[1]))
test_X = test_X.reshape((test_X.shape[0], 1, test_X.shape[1]))

so it should be replaced by:

def get_XY(dat, time_steps):
    # Indices of target array
    Y_ind = np.arange(time_steps, len(dat), time_steps)
    Y = dat[Y_ind]
    # Prepare X
    rows_x = len(Y)
    X = dat[range(time_steps*rows_x)]
    X = np.reshape(X, (rows_x, time_steps, 1))    
    return X, Y
 
time_steps = 12
train_X, train_y = get_XY(train, time_steps)
test_X, test_y = get_XY(train, time_steps)