This Keras solution uses a shared sub-model, and then applies it to each of the input image's channels separately. The architecture is very similar to a Siamese network.
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras.layers import Input, InputLayer, Dense, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D
import tensorflow.keras.backend as K
res = 128
sub_model = Sequential([
InputLayer((res,res,1)),
Conv2D(16, 5, activation='relu'),
MaxPooling2D(2),
Conv2D(16, 5, activation='relu'),
MaxPooling2D(2),
Conv2D(16, 5, activation='relu'),
MaxPooling2D(2),
Flatten(),
Dense(512, activation='relu'),
Dense(128, activation='relu'),
])
sub_model.summary()
Summary:
Model: "sequential_8"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_19 (Conv2D) (None, 124, 124, 16) 416
_________________________________________________________________
max_pooling2d_17 (MaxPooling (None, 62, 62, 16) 0
_________________________________________________________________
conv2d_20 (Conv2D) (None, 58, 58, 16) 6416
_________________________________________________________________
max_pooling2d_18 (MaxPooling (None, 29, 29, 16) 0
_________________________________________________________________
conv2d_21 (Conv2D) (None, 25, 25, 16) 6416
_________________________________________________________________
max_pooling2d_19 (MaxPooling (None, 12, 12, 16) 0
_________________________________________________________________
flatten_7 (Flatten) (None, 2304) 0
_________________________________________________________________
dense_4 (Dense) (None, 512) 1180160
_________________________________________________________________
dense_5 (Dense) (None, 128) 65664
=================================================================
Total params: 1,259,072
Trainable params: 1,259,072
Non-trainable params: 0
_________________________________________________________________
Then the combined model:
n_channels = 4
inp = Input((res,res,n_channels))
x = K.concatenate([sub_model(inp[:,:,:,i:i+1])[:,:,None] for i in range(n_channels)])
model = Model(inp, x)
x.shape
# TensorShape([None, 128, 4])
Naturally it is up to you whether to have those Dense
layers in the sub_model
or not, and which extra operations you'll do to the concatenated x
.