Reputation: 179
I am trying to make a model in tensorflow using the keras subclasses method.
Q1) I am correctly calling layers as layers = []
and then using layers.append(GTLayer....)
?
Q2) calling GTLayer in init of GTN will run class GTLayer and will it call self.conv1 (which will return a tensor A from GTNconv) and self.conv2 (which will again return a tensor A from GTNconv)and then start the call
mrthod of GTLayer to H,W
, Am I right?
Q3) What happens to the returned H and W
from 'Q2' will it store in layers[]
list ? and then when we further call the GTNs call
method it will bring up those layer? Am I correct?
Q4)Later in the GTN
s call
method I had to implement linear layers and thus I defined model = tf.keras.models.Sequential()
and after theat initialised self.linear1
and self.linear2
, this way I have implemented subclassing and sequential both! Is that correct?
Q5) I will finally get loss, y, Ws
from calling GTN , now if I assign my model = GTN(arguments..)
how will I do the training and back-propagation steps? using an optimiser and loss function? will it follow model.compile()
and model.fit
? Or can we make it any different in the sub-classing method of keras?
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
class GTN(layers.Layer):
def __init__(self, num_edge, num_channels,num_layers,norm):
super(GTN, self).__init__()
self.num_edge = num_edge
self.num_channels = num_channels
self.num_layers = num_layers
self.is_norm = norm
layers = []
for i in tf.range(num_layers):
if i == 0:
layers.append(GTLayer(num_edge, num_channels, first=True))
else:
layers.append(GTLayer(num_edge, num_channels, first=False))
model = tf.keras.models.Sequential()
self.loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
self.linear1 = model.add(tf.keras.layers.Dense(self.w_out, input_shape=(self.w_out*self.num_channels,), activation=None))
self.linear2 = model.add(tf.keras.layers.Dense(self.num_class, input_shape=(self.w_out,), activation=None))
def gcn_conv(self,X,H):
X = tf.matmul(X, self.weight)
H = self.norm(H, add=True)
return tf.matmul(tf.transpose(H),X)
def call(self, A, X, target_x, target):
A = tf.expand_dims(A, 0)
Ws = []
for i in range(self.num_layers):
H = self.normalization(H)
H, W = self.layers[i](A, H)
Ws.append(W)
for i in range(self.num_channels):
X_tmp = tf.nn.relu(self.gcn_conv(X,H[i])).numpy()
X_ = tf.concat((X_,X_tmp), dim=1)
X_ = self.linear1(X_)
X_ = tf.nn.relu(X_).numpy()
y = self.linear2(X_[target_x])
loss = self.loss(y, target)
return loss, y, Ws
class GTLayer(keras.layers.Layer):
def __init__(self, in_channels, out_channels, first=True):
super(GTLayer, self).__init__()
self.in_channels = in_channels
self.out_channels = out_channels
self.conv1 = GTConv(in_channels, out_channels)
self.conv2 = GTConv(in_channels, out_channels)
def call(self, A, H_=None):
a = self.conv1(A)
b = self.conv2(A)
H = tf.matmul( a, b)
W = [tf.stop_gradient(tf.nn.softmax(self.conv1.weight, axis=1).numpy()),
tf.stop_gradient(tf.nn.softmax(self.conv2.weight, axis=1).numpy()) ]
return H,W
class GTConv(keras.layers.Layer):
def __init__(self, in_channels, out_channels):
super(GTConv, self).__init__()
def call(self, A):
A = tf.add_n(tf.nn.softmax(self.weight))
return A
Upvotes: 0
Views: 466
Reputation: 86600
No. There are two possibilities here
1 - If you want to access a standard layers
property of Keras models:
Model
has a layers
property, a keras.layers.Layer
doesn't have this propertylayers
property of a Model
, you should just read itlayers
is not a property of your class because you did not use self.layers
.2 - If you just want a list named layers
for personal use in your class:
myLayers
or something like that to avoid confusion.layers
you created is not being used anywhere else in your code, you just created it and never used.layers = []
just creates a local variable, while self.layers = []
creates a property in your class that can be used in other methods inside your classYou are not "calling" GTLayer
, you are "creating" GTLayer
. This means that you are running GTLayer.__init__()
.
This distinction is important in Keras:
layer_instance = GTLayer(...)
, which runs __init__
layer_instance(input_tensors)
, which runs __call__
(which will eventually run call
as defined by you)You can do both in the same line as output_tensors = GTLayer(...)(input_tensors)
So, this is happening in GTN.__init__
:
GTLayer
.GTLayer.__init__()
for each instanceself.conv1 = GTConv(in_channels, out_channels)
and self.conv2 = GTConv(in_channels, out_channels)
GTConv
.self.conv1
and self.conv2
are "Layer" instances, not tensors.No tensor is produced here because you never "called" any layer in GTN.__init__()
.
(And this is ok. Usually, you "create" layers inside __init__()
and "call" layers inside call
.)
Your layers
local variable will have "instances of GTLayer
".
You mixed two approaches in a strange way.
You can, of course, use a Sequential
model if you want, but it's not necessary, and you're not using it correcly.
If in call
you are calling each layer (that is X_ = self.linear1(X_)
and y = self.linear2(X_[target_x])
), you don't need a Sequential
model at all, and you can just have the following in GTN.__init__()
(this is the best approach for subclassing):
self.linear1 = tf.keras.layers.Dense(self.w_out, input_shape=(self.w_out*self.num_channels,), activation=None)
self.linear2 = tf.keras.layers.Dense(self.num_class, input_shape=(self.w_out,), activation=None)
But you could have self.submodel = Sequential(...)
and then use self.submodel
in GTN.call()
. But having a Model
inside a layer sounds weird and might cause some strange behavior in specific cases. And, of course, the ReLUs should be a part of this submodel.
I will finally get loss, y, Ws
from calling GTN
That loss and weights coming from call
is a "very very" strange thing. I never saw this and I don't understand why you're doing it this way. This is not standard use of Keras and only in very specific and otherwise unsolvable cases you'd try something like this. I cannot say it will work.
How will I do the training and back-propagation steps?
You should have implemented a keras.models.Model
, not a keras.layers.Layer
. Only models have the ability to compile and train.
Usually, you'd not create a loss in call, you'd create a loss in model.compile
, unless you're dealing with unconventional losses, like weight or activity regularization, things that really depend on the layer and not on the model's inputs/outputs.
There is no need to create custom layers if you're not going to create custom trainable weights. It's not wrong, of course, but also not necessary. It can help organize your code, or just add extra complication.
You are trying to use weight
from your layers, but you never defined any weight anywhere.
I'm pretty sure there is a better way to achieve what you want, but I don't know what you want (and that would be something for another question, I think...)
This might be a good reading for subclassing: https://www.tensorflow.org/guide/keras/custom_layers_and_models?hl=en
Upvotes: 2