Update
Based on your query, it seems that the class number won't be different in Dataset2. At the same time, you also don't want to use image net weight. So, in that case, you don't need to map or store the weight (as described below). Just load the model and weight and train on Dataset2. Freeze the all trained layer from Dataset1 and train the last layer on Dataset2; really straight forward.
In my below response, though you're not needed the full information, I am keeping that anyway for future reference.
Here is a small demonstration of what you probably need. Hope it gives you some insight. Here we will train the CIRFAR data set which has 10 classes and try to use it for transfer learning with on different data set which probably has different input sizes and a different number of classes.
Preparing the CIFAR (10 Classes)
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
# train set / data
x_train = x_train.astype('float32') / 255
# validation set / data
x_test = x_test.astype('float32') / 255
# train set / target
y_train = tf.keras.utils.to_categorical(y_train, num_classes=10)
# validation set / target
y_test = tf.keras.utils.to_categorical(y_test, num_classes=10)
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)
'''
(50000, 32, 32, 3) (50000, 10)
(10000, 32, 32, 3) (10000, 10)
'''
Model
# declare input shape
input = tf.keras.Input(shape=(32,32,3))
# Block 1
x = tf.keras.layers.Conv2D(32, 3, strides=2, activation="relu")(input)
x = tf.keras.layers.MaxPooling2D(3)(x)
# Now that we apply global max pooling.
gap = tf.keras.layers.GlobalMaxPooling2D()(x)
# Finally, we add a classification layer.
output = tf.keras.layers.Dense(10, activation='softmax')(gap)
# bind all
func_model = tf.keras.Model(input, output)
'''
Model: "functional_3"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, 32, 32, 3)] 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 15, 15, 32) 896
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 32) 0
_________________________________________________________________
global_max_pooling2d_1 (Glob (None, 32) 0
_________________________________________________________________
dense_1 (Dense) (None, 10) 330
=================================================================
Total params: 1,226
Trainable params: 1,226
Non-trainable params: 0
'''
Run the model to get some weight matrices as follows:
# compile
print('\nFunctional API')
func_model.compile(
loss = tf.keras.losses.CategoricalCrossentropy(),
metrics = tf.keras.metrics.CategoricalAccuracy(),
optimizer = tf.keras.optimizers.Adam())
# fit
func_model.fit(x_train, y_train, batch_size=128, epochs=1)
Transfer Learning
Let's use it for MNIST. It also has 10 classes but for sake of need a different number of classes, we will make even and odd categories from it (2 classes). Below how we will prepare these data sets
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# train set / data
x_train = np.expand_dims(x_train, axis=-1)
x_train = np.repeat(x_train, 3, axis=-1)
x_train = x_train.astype('float32') / 255
# train set / target
y_train = tf.keras.utils.to_categorical((y_train % 2 == 0).astype(int),
num_classes=2)
# validation set / data
x_test = np.expand_dims(x_test, axis=-1)
x_test = np.repeat(x_test, 3, axis=-1)
x_test = x_test.astype('float32') / 255
# validation set / target
y_test = tf.keras.utils.to_categorical((y_test % 2 == 0).astype(int),
num_classes=2)
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)
'''
(60000, 28, 28, 3) (60000, 2)
(10000, 28, 28, 3) (10000, 2)
'''
If you're familiar with the usage of ImageNet pretrained weight in the keras model, you probably use include_top. By setting it False we can easily load a weight file that has no top information of the pretrained models. So here we need to manually (kinda) do that. We need to grab the weight matrices until the last activation layer (in our case which is Dense(10, softmax)). And put it in the new instance of the base model, and + we add a new classifier layer (in our case that will be Dense(2, softmax).
for i, layer in enumerate(func_model.layers):
print(i,'\t',layer.trainable,'\t :',layer.name)
'''
Train_Bool : Layer Names
0 True : input_1
1 True : conv2d
2 True : max_pooling2d
3 True : global_max_pooling2d # < we go till here to grab the weight and biases
4 True : dense # 10 classes (from previous model)
'''
Get Weights
sparsified_weights = []
for w in func_model.get_layer(name='global_max_pooling2d').get_weights():
sparsified_weights.append(w)
By that, we map the weight from the old model except for the classifier layers (Dense). Please note, here we grab the weight until the GAP layer, which is there right before the classifier.
Now, we will create a new model, the same as the old model except for the last layer (10 Dense), and at the same time add a new Dense with 2 unit.
predictions = Dense(2, activation='softmax')(func_model.layers[-2].output)
new_func_model = Model(inputs=func_model.inputs, outputs = predictions)
And now we can set weight as follows to the new model:
new_func_model.get_layer(name='global_max_pooling2d').set_weights(sparsified_weights)
You can check to verify as follows; all will be the same except the last layer.
func_model.get_weights() # last layer, Dense (10)
new_func_model.get_weights() # last layer, Dense (2)
Now you can train the model with new data set, in our case which was MNIST
new_func_model.compile(optimizer='adam', loss='categorical_crossentropy')
new_func_model.summary()
'''
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 32, 32, 3)] 0
_________________________________________________________________
conv2d (Conv2D) (None, 15, 15, 32) 896
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 5, 5, 32) 0
_________________________________________________________________
global_max_pooling2d (Global (None, 32) 0
_________________________________________________________________
dense_6 (Dense) (None, 2) 66
=================================================================
Total params: 962
Trainable params: 962
Non-trainable params: 0
'''
# compile
print('\nFunctional API')
new_func_model.compile(
loss = tf.keras.losses.CategoricalCrossentropy(),
metrics = tf.keras.metrics.CategoricalAccuracy(),
optimizer = tf.keras.optimizers.Adam())
# fit
new_func_model.fit(x_train, y_train, batch_size=128, epochs=1)
WARNING:tensorflow:Model was constructed with shape (None, 32, 32, 3) for input Tensor("input_1:0", shape=(None, 32, 32, 3), dtype=float32), but it was called on an input with incompatible shape (None, 28, 28, 3).
WARNING:tensorflow:Model was constructed with shape (None, 32, 32, 3) for input Tensor("input_1:0", shape=(None, 32, 32, 3), dtype=float32), but it was called on an input with incompatible shape (None, 28, 28, 3).
469/469 [==============================] - 1s 3ms/step - loss: 0.6453 - categorical_accuracy: 0.6447
<tensorflow.python.keras.callbacks.History at 0x7f7af016feb8>