<p>The issue is when calling <code>self.inputs(inputs)</code>,(you can't use input layer in keras subclassing, only for sequential and functional API) but in general you're defining the layers and model wrongly. Here is one way to define a custom <code>keras.Model</code> (I've also cleaned up the code a bit and removed duplicated lines):</p>
<pre><code>import tensorflow as tf
import tensorflow.keras.layers as tfkl
from functools import partial

class ConvBlock(tfkl.Layer):
    def __init__(self, filters: int, **kwargs):
        super().__init__(**kwargs)

        self.conv1 = tfkl.Conv2D(filters=int(filters), kernel_size=3, use_bias=False)
        self.bn1 = tfkl.BatchNormalization()
        self.bn2 = tfkl.BatchNormalization()
        self.activation = tf.nn.relu
        self.conv2 = tfkl.Conv2D(filters=int(filters), kernel_size=3, use_bias=False)

    def call(self, inputs, **kwargs):
        x = self.conv1(inputs)
        x = self.bn1(x)
        x = self.activation(x)

        x = self.conv2(x)
        x = self.bn2(x)

        outputs = self.activation(x)
        return outputs

class UNet(tf.keras.Model):
    def __init__(self, input_shape=(572, 572, 1), filter_list=(64, 128, 256, 512),
                 crop_values=(4, 17, 40, 88), **kwargs):
        assert len(filter_list) == len(crop_values)

        # pre-define some layers
        max_pool = tfkl.MaxPool2D(pool_size=2, strides=2)
        concat = tfkl.Concatenate()

        ConvT = partial(tfkl.Conv2DTranspose, kernel_size=2, strides=2)
        Crop = partial(tfkl.Cropping2D)

        # model architecture
        inputs = tfkl.Input(shape=input_shape, name='image')
        x = inputs

        layers = []

        # conv_block -&gt; max-pool
        for filters in filter_list:
            x = ConvBlock(filters=filters)(x)
            layers.append(x)
            x = max_pool(x)

        x = ConvBlock(filters=1024)(x)

        # concat(conv_t, crop) -&gt; conv_block
        for filters, crop, layer in zip(reversed(filter_list), crop_values, reversed(layers)):
            print(filters, crop, layer.shape)
            x = concat([ConvT(filters=filters)(x), Crop(cropping=crop)(layer)])
            x = ConvBlock(filters=filters)(x)

        outputs = tfkl.Conv2D(filters=2, kernel_size=1, padding='same')(x)

        super().__init__(inputs=inputs, outputs=outputs, **kwargs)
</code></pre>
<p>But to make it have matching shapes, I had to change a bit the values you use for the crop layer:</p>
<pre><code>unet = UNet(input_shape=(572, 572, 3), crop_values=[4, 16, 40, 88])
print(unet(tf.random.uniform((2, 572, 572, 3))).shape)  # -&gt; TensorShape([2, 388, 388, 2])
</code></pre>
<p>Lastly, I got an output shape of <strong><code>(388, 388, 2)</code></strong> is that right for your problem?</p>

CC BY-SA 4.0
2023-04-16T12:50:26.907Z
`bn1` and `bn2` are two different instantiations of a batch-normalization layer, they are two *different* objects and you want to do that because each internally keeps statistics of the inputs. Same for `ConvBlock` they are the same set of layers but different instantiations (also with different parameters, i.e. `filters` for example). Basically, the syntax `ConvBlock(filters=...)` creates the instance of type `ConvBlock`, then the second call `(x)`  applies that to the layer `x` returning a symbolic tensor. Have a look at the [Keras Functional API](https://keras.io/guides/functional_api/).
Luca Anzalone
CC BY-SA 4.0
2023-04-17T11:58:22.120Z
Programming questions are off-topic here. Please, flag them as off-topic and encourage the user to ask them at Stack Overflow or Data Science SE. See our on-topic page for more info.
nbro
CC BY-SA 4.0
2023-04-15T17:49:22.043Z
2023-04-17T13:00:50.627Z
2023-04-17T13:00:50.627Z
Luca Anzalone
<p>Implementing UNet but getting an error: type error 'KerasTensor' object is not callable</p>
<pre><code>class UNet(keras.Model):
  &quot;&quot;&quot;
  argument: input_shape=(572, 572, 1) =&gt; default 
  &quot;&quot;&quot;
  def __init__(self, input_shape=(572, 572, 1), **kwargs):
    super().__init__(**kwargs)
    self.inputs = keras.layers.Input(shape=input_shape)
    self.maxpool = keras.layers.MaxPool2D(pool_size=2, strides=2)
    self.concat = keras.layers.Concatenate() # concats through depth
    self.conv_sz1 = keras.layers.Conv2D(filters=2, kernel_size=1, padding=&quot;same&quot;)

  class CONV2_BLOCK(keras.layers.Layer):
    def __init__(self, filters, **kwargs):
      super().__init__(**kwargs)
      self.filters = filters

    def call(self, inputs):
      x = keras.layers.Conv2D(filters=self.filters, kernel_size=3, use_bias=False)(inputs)
      x = keras.layers.BatchNormalization()(x)
      x = keras.layers.Activation(keras.activations.relu)(x)
      x = keras.layers.Conv2D(filters=self.filters, kernel_size=3, use_bias=False)(x)
      x = keras.layers.BatchNormalization()(x)
      outputs = keras.layers.Activation(keras.activations.relu)(x)
      return outputs
      

  class CONV_T(keras.layers.Layer):
    def __init__(self, filters, **kwargs):
      super().__init__(**kwargs)
      self.filters = filters

    def call(self, inputs):  
      outputs = keras.layers.Conv2DTranspose(filters=self.filters, kernel_size=2, stride=2)(inputs)
      return outputs

  class CROP(keras.layers.Layer):
    def __init__(self, cropping, **kwargs):
      super().__init__(**kwargs)
      self.cropping = cropping

    def call(self, inputs):
      outputs = keras.layers.Cropping2D(cropping=self.cropping)(inputs)
      return outputs      

  def call(self, inputs):
    # self.conv_arr = [64, 128, 256, 512, 1024]
    # self.crop_arr = [4, 17, 40, 88] down to up
    x1 = self.CONV2_BLOCK(filters=64)(self.inputs(inputs))
    x = self.maxpool(x1)

    x2 = self.CONV2_BLOCK(filters=128)(x)
    x = self.maxpool(x2) 

    x3 = self.CONV2_BLOCK(filters=256)(x)
    x = self.maxpool(x3) 

    x4 = self.CONV2_BLOCK(filters=512)(x)
    x = self.maxpool(x4)

    x = self.CONV2_BLOCK(filters=1024)(x)

    x = self.concat([self.CONV_T(filters=512)(x), self.CROP(cropping=4)(x4)])
    x = self.CONV2_BLOCK(filters=512)(x)

    x = self.concat([self.CONV_T(filters=216)(x), self.CROP(cropping=17)(x3)])
    x = self.CONV2_BLOCK(filters=256)(x)

    x = self.concat([self.CONV_T(filters=128)(x), self.CROP(cropping=40)(x2)])
    x = self.CONV2_BLOCK(filters=128)(x)

    x = self.concat([self.CONV_T(filters=64)(x), self.CROP(cropping=88)(x1)])
    x = self.CONV2_BLOCK(filters=64)(x)

    outputs = self.conv_sz1(x)

    return outputs

test_model = UNet(input_shape=(572, 572, 3))
print(test_model(tf.random.uniform(shape=(2, 572, 572, 3))).shape)
</code></pre>
<p>but getting error as</p>
<pre><code>---&gt; 48     x1 = self.CONV2_BLOCK(filters=64)(self.inputs(inputs))
     49     x = self.maxpool(x1)
     38 
</code></pre>
<blockquote>
<p>TypeError: Exception encountered when calling layer 'u_net_1' (type UNet).
'KerasTensor' object is not callable
Call arguments received by layer 'u_net' (type UNet):
• inputs=tf.Tensor(shape=(2, 572, 572, 3), dtype=float32)</p>
</blockquote>

<tensorflow><keras>
Keras Subclassing : TypeError: 'KerasTensor' object is not callable Call arguments received by layer
vivian.ai
# Keras Subclassing : TypeError: 'KerasTensor' object is not callable Call arguments received by layer

(Asked by: username_0 on 2023-04-15)

Implementing UNet but getting an error: type error 'KerasTensor' object is not callable
class UNet(keras.Model):
  """
  argument: input_shape=(572, 572, 1) => default 
  """
  def __init__(self, input_shape=(572, 572, 1), **kwargs):
    super().__init__(**kwargs)
    self.inputs = keras.layers.Input(shape=input_shape)
    self.maxpool = keras.layers.MaxPool2D(pool_size=2, strides=2)
    self.concat = keras.layers.Concatenate() # concats through depth
    self.conv_sz1 = keras.layers.Conv2D(filters=2, kernel_size=1, padding="same")

  class CONV2_BLOCK(keras.layers.Layer):
    def __init__(self, filters, **kwargs):
      super().__init__(**kwargs)
      self.filters = filters

    def call(self, inputs):
      x = keras.layers.Conv2D(filters=self.filters, kernel_size=3, use_bias=False)(inputs)
      x = keras.layers.BatchNormalization()(x)
      x = keras.layers.Activation(keras.activations.relu)(x)
      x = keras.layers.Conv2D(filters=self.filters, kernel_size=3, use_bias=False)(x)
      x = keras.layers.BatchNormalization()(x)
      outputs = keras.layers.Activation(keras.activations.relu)(x)
      return outputs
      

  class CONV_T(keras.layers.Layer):
    def __init__(self, filters, **kwargs):
      super().__init__(**kwargs)
      self.filters = filters

    def call(self, inputs):  
      outputs = keras.layers.Conv2DTranspose(filters=self.filters, kernel_size=2, stride=2)(inputs)
      return outputs

  class CROP(keras.layers.Layer):
    def __init__(self, cropping, **kwargs):
      super().__init__(**kwargs)
      self.cropping = cropping

    def call(self, inputs):
      outputs = keras.layers.Cropping2D(cropping=self.cropping)(inputs)
      return outputs      

  def call(self, inputs):
    # self.conv_arr = [64, 128, 256, 512, 1024]
    # self.crop_arr = [4, 17, 40, 88] down to up
    x1 = self.CONV2_BLOCK(filters=64)(self.inputs(inputs))
    x = self.maxpool(x1)

    x2 = self.CONV2_BLOCK(filters=128)(x)
    x = self.maxpool(x2) 

    x3 = self.CONV2_BLOCK(filters=256)(x)
    x = self.maxpool(x3) 

    x4 = self.CONV2_BLOCK(filters=512)(x)
    x = self.maxpool(x4)

    x = self.CONV2_BLOCK(filters=1024)(x)

    x = self.concat([self.CONV_T(filters=512)(x), self.CROP(cropping=4)(x4)])
    x = self.CONV2_BLOCK(filters=512)(x)

    x = self.concat([self.CONV_T(filters=216)(x), self.CROP(cropping=17)(x3)])
    x = self.CONV2_BLOCK(filters=256)(x)

    x = self.concat([self.CONV_T(filters=128)(x), self.CROP(cropping=40)(x2)])
    x = self.CONV2_BLOCK(filters=128)(x)

    x = self.concat([self.CONV_T(filters=64)(x), self.CROP(cropping=88)(x1)])
    x = self.CONV2_BLOCK(filters=64)(x)

    outputs = self.conv_sz1(x)

    return outputs

test_model = UNet(input_shape=(572, 572, 3))
print(test_model(tf.random.uniform(shape=(2, 572, 572, 3))).shape)

but getting error as
---> 48     x1 = self.CONV2_BLOCK(filters=64)(self.inputs(inputs))
     49     x = self.maxpool(x1)
     38 


TypeError: Exception encountered when calling layer 'u_net_1' (type UNet).
'KerasTensor' object is not callable
Call arguments received by layer 'u_net' (type UNet):
• inputs=tf.Tensor(shape=(2, 572, 572, 3), dtype=float32)

**Tags:** tensorflow, keras


---

## 1 answers

### Answer by username_1 on 2023-04-15. Score: 1 | Accepted answer

The issue is when calling self.inputs(inputs),(you can't use input layer in keras subclassing, only for sequential and functional API) but in general you're defining the layers and model wrongly. Here is one way to define a custom keras.Model (I've also cleaned up the code a bit and removed duplicated lines):
import tensorflow as tf
import tensorflow.keras.layers as tfkl
from functools import partial

class ConvBlock(tfkl.Layer):
    def __init__(self, filters: int, **kwargs):
        super().__init__(**kwargs)

        self.conv1 = tfkl.Conv2D(filters=int(filters), kernel_size=3, use_bias=False)
        self.bn1 = tfkl.BatchNormalization()
        self.bn2 = tfkl.BatchNormalization()
        self.activation = tf.nn.relu
        self.conv2 = tfkl.Conv2D(filters=int(filters), kernel_size=3, use_bias=False)

    def call(self, inputs, **kwargs):
        x = self.conv1(inputs)
        x = self.bn1(x)
        x = self.activation(x)

        x = self.conv2(x)
        x = self.bn2(x)

        outputs = self.activation(x)
        return outputs

class UNet(tf.keras.Model):
    def __init__(self, input_shape=(572, 572, 1), filter_list=(64, 128, 256, 512),
                 crop_values=(4, 17, 40, 88), **kwargs):
        assert len(filter_list) == len(crop_values)

        # pre-define some layers
        max_pool = tfkl.MaxPool2D(pool_size=2, strides=2)
        concat = tfkl.Concatenate()

        ConvT = partial(tfkl.Conv2DTranspose, kernel_size=2, strides=2)
        Crop = partial(tfkl.Cropping2D)

        # model architecture
        inputs = tfkl.Input(shape=input_shape, name='image')
        x = inputs

        layers = []

        # conv_block -> max-pool
        for filters in filter_list:
            x = ConvBlock(filters=filters)(x)
            layers.append(x)
            x = max_pool(x)

        x = ConvBlock(filters=1024)(x)

        # concat(conv_t, crop) -> conv_block
        for filters, crop, layer in zip(reversed(filter_list), crop_values, reversed(layers)):
            print(filters, crop, layer.shape)
            x = concat([ConvT(filters=filters)(x), Crop(cropping=crop)(layer)])
            x = ConvBlock(filters=filters)(x)

        outputs = tfkl.Conv2D(filters=2, kernel_size=1, padding='same')(x)

        super().__init__(inputs=inputs, outputs=outputs, **kwargs)

But to make it have matching shapes, I had to change a bit the values you use for the crop layer:
unet = UNet(input_shape=(572, 572, 3), crop_values=[4, 16, 40, 88])
print(unet(tf.random.uniform((2, 572, 572, 3))).shape)  # -> TensorShape([2, 388, 388, 2])

Lastly, I got an output shape of (388, 388, 2) is that right for your problem?

> **Comments:**
> **username_1**: `bn1` and `bn2` are two different instantiations of a batch-normalization layer, they are two *different* objects and you want to do that because each internally keeps statistics of the inputs. Same for `ConvBlock` they are the same set of layers but different instantiations (also with different parameters, i.e. `filters` for example). Basically, the syntax `ConvBlock(filters=...)` creates the instance of type `ConvBlock`, then the second call `(x)`  applies that to the layer `x` returning a symbolic tensor. Have a look at the [Keras Functional API](https://keras.io/guides/functional_api/).  
> **username_2**: Programming questions are off-topic here. Please, flag them as off-topic and encourage the user to ask them at Stack Overflow or Data Science SE. See our on-topic page for more info.  

---

