Я пытаюсь построить conditional CNN
модель. Модель,
В first stage
моей модели я передаю свои данные в Model 1
, затем, based on the prediction of Model 1
, я хочу train the model to Conditional Cat model or Conditional Dog model
и, наконец, выдаю результат из модели условной кошки или модели условной собаки. Как я могу это сделать?
Примечание: Мои усилия,
import keras
from keras.layers import *
from keras.models import *
from keras.utils import *
img_rows,img_cols,number_of_class = 256,256,2
input = Input(shape=(img_rows,img_cols,3))
#----------- main model (Model 1) ------------------------------------
conv_01 = Convolution2D(64, 3, 3, activation='relu',name = 'conv_01') (input)
conv_02 = Convolution2D(64, 3, 3, activation='relu',name = 'conv_02') (conv_01)
skip_dog = conv_02
conv_03 = Convolution2D(64, 3, 3, activation='relu',name = 'conv_03') (conv_02)
skip_cat = conv_03
conv_04 = Convolution2D(64, 3, 3, activation='relu',name = 'conv_04') (conv_03)
flatten_main_model = Flatten() (conv_04)
Output_main_model = Dense(units = number_of_class , activation = 'softmax', name = "Output_layer")(flatten_main_model)
#----------- Conditional Cat model ------------------------------------
conv_05 = Convolution2D(64, 3, 3, activation='relu',name = 'conv_05') (skip_cat)
flatten_cat_model = Flatten() (conv_05)
Output_cat_model = Dense(units = number_of_class , activation = 'softmax', name = "Output_layer_cat")(flatten_cat_model)
#----------- Conditional Dog model ------------------------------------
conv_06 = Convolution2D(64, 3, 3, activation='relu',name = 'conv_06') (skip_dog)
flatten_dog_model = Flatten() (conv_06)
Output_dog_model = Dense(units = number_of_class , activation = 'softmax', name = "Output_layer_dog")(flatten_dog_model)
#----------------------------- My discrete 3 models --------------------------------
model_01 = Model(inputs = input , outputs = Output_main_model,name = 'model_main')
model_02_1 = Model(inputs = input , outputs = Output_cat_model ,name = 'Conditional_cat_model')
model_02_2 = Model(inputs = input , outputs = Output_dog_model ,name = 'Conditional_dog_model')
Как я могу объединить эти 3 модели (model_01, model_02_1, model_02_2
) на основе этих условий?
**Условия: **
model_01
model_01
данных подачи результатов в model_02_1 or model_02_2
model_02_1 or model_02_2
На всякий случай комментарий был удален, а другой добавлен.
Ответ Алекса К. был в правильном направлении. Что именно с ним не так? Вы пытались использовать `keras.backend.equal()`?
@PoeDator Да, у меня проблема с K.switch
. Мне не удалось назначить это, а также сравнение тензора слоев. Я застреваю, чтобы сравнить и назначить.
Чтобы построить condition-based CNN
, мы могли бы передать полный пакет входных данных каждой подмодели в Model2 и выбрать желаемые выходные данные из всех выходных данных подмоделей на основе условий (которые модели, которые вы определили в вопросе), или мы можем выберите более быстрый способ, следуя шагу условий (это три условия, которые вы указали)
Пример кода для отображения механизма условия:
# Mimic the test dataset and labels
batch = tf.constant([[1, 2, 3], [2, 3, 1], [3, 1, 2]])
y_all = [tf.one_hot(i, number_of_class, dtype=tf.float32) for i in range(number_of_class)]
# Mimic the outputs of model_01
y_p = tf.constant([[0.9, 0.1], [0.1, 0.9], [0.3, 0.7]])
y_p = tf.one_hot(tf.math.argmax(y_p, axis=1), number_of_class, dtype=tf.float32)
# Mimic the conditions by choose the samples from batch base on if prediction is equal to label wrt each class
for y in y_all:
condition = tf.reduce_all(tf.math.equal(y_p, y), 1)
indices = tf.where(condition)
choosed_inputs = tf.gather_nd(batch, indices)
print("label:\n{}\ncondition:\n{}\nindices:\n{}\nchoosed_inputs:\n{}\n".format(y, condition, indices, choosed_inputs))
Выходы:
label:
[1. 0.]
condition:
[ True False False]
indices:
[[0]]
choosed_inputs:
[[1 2 3]]
label:
[0. 1.]
condition:
[False True True]
indices:
[[1]
[2]]
choosed_inputs:
[[2 3 1]
[3 1 2]]
Пример кода, который создает модель condition-based CNN
и обучает ее в пользовательском режиме обучения:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import *
from tensorflow.keras.models import *
from tensorflow.keras.utils import *
import numpy as np
img_rows, img_cols, number_of_class, batch_size = 256, 256, 2, 64
#----------- main model (Model 1) ------------------------------------
inputs = Input(shape=(img_rows, img_cols, 3))
conv_01 = Convolution2D(64, 3, 3, activation='relu', name = 'conv_01') (inputs)
conv_02 = Convolution2D(64, 3, 3, activation='relu', name = 'conv_02') (conv_01)
skip_dog = conv_02
conv_03 = Convolution2D(64, 3, 3, activation='relu', name = 'conv_03') (conv_02)
skip_cat = conv_03
conv_04 = Convolution2D(64, 3, 3, activation='relu', name = 'conv_04') (conv_03)
flatten_main_model = Flatten() (conv_04)
Output_main_model = Dense(units = number_of_class , activation = 'softmax', name = "Output_layer")(flatten_main_model)
#----------- Conditional Cat model ------------------------------------
inputs_1 = Input(shape=skip_cat.shape[1:])
conv_05 = Convolution2D(64, 3, 3, activation='relu', name = 'conv_05') (inputs_1)
flatten_cat_model = Flatten() (conv_05)
Output_cat_model = Dense(units = number_of_class , activation = 'softmax', name = "Output_layer_cat")(flatten_cat_model)
#----------- Conditional Dog model ------------------------------------
inputs_2 = Input(shape=skip_dog.shape[1:])
conv_06 = Convolution2D(64, 3, 3, activation='relu', name = 'conv_06') (inputs_2)
flatten_dog_model = Flatten() (conv_06)
Output_dog_model = Dense(units = number_of_class , activation = 'softmax', name = "Output_layer_dog")(flatten_dog_model)
#----------------------------- My discrete 3 models --------------------------------
model_01 = Model(inputs = inputs, outputs = [skip_cat, skip_dog, Output_main_model], name = 'model_main')
model_02_1 = Model(inputs = inputs_1, outputs = Output_cat_model, name = 'Conditional_cat_model')
model_02_2 = Model(inputs = inputs_2, outputs = Output_dog_model, name = 'Conditional_dog_model')
# Get one hot vectors for all the labels
y_all = [tf.one_hot(i, number_of_class, dtype=tf.float32) for i in range(number_of_class)]
sub_models_all = [model_02_1, model_02_2]
sub_models_trainable_variables = [model_01.trainable_variables[:6] + model_02_1.trainable_variables,
model_01.trainable_variables[:4] + model_02_2.trainable_variables]
cce = keras.losses.CategoricalCrossentropy()
optimizer_01 = keras.optimizers.Adam(learning_rate=1e-3, name='Adam_01')
optimizer_02 = keras.optimizers.Adam(learning_rate=2e-3, name='Adam_02')
@tf.function
def train_step(batch_imgs, labels):
with tf.GradientTape(persistent=True) as tape:
model_01_outputs = model_01(batch_imgs)
y_p_01 = model_01_outputs[-1]
loss_01 = cce(labels, y_p_01)
# Convert outputs of model_01 from float in (0, 1) to one hot vectors, no gradients flow back from here
y_p_01 = tf.one_hot(tf.math.argmax(y_p_01, axis=1), number_of_class, dtype=tf.float32)
loss_02_all = []
for i in range(number_of_class):
condition = tf.reduce_all(tf.math.equal(y_p_01, y_all[i]), 1)
indices = tf.where(condition)
choosed_inputs = tf.gather_nd(model_01_outputs[i], indices)
# Note here the inputs batch size for each sub-model is dynamic
y_p_02 = sub_models_all[i](choosed_inputs)
y_t = tf.gather_nd(labels, indices)
loss_02 = cce(y_t, y_p_02)
loss_02_all.append(loss_02)
grads_01 = tape.gradient(loss_01, model_01.trainable_variables)
optimizer_01.apply_gradients(zip(grads_01, model_01.trainable_variables))
for i in range(number_of_class):
grads_02 = tape.gradient(loss_02_all[i], sub_models_trainable_variables[i])
optimizer_02.apply_gradients(zip(grads_02, sub_models_trainable_variables[i]))
return loss_01, loss_02_all
def training():
for j in range(10):
random_imgs = np.random.rand(batch_size, img_rows, img_cols, 3)
random_labels = np.eye(number_of_class)[np.random.choice(number_of_class, batch_size)]
loss_01, loss_02_all = train_step(random_imgs, random_labels)
print("Step: {}, Loss_01: {}, Loss_02_all: {}".format(j, loss_01, loss_02_all))
Выходы что-то вроде:
Step: 0, Loss_01: 0.6966696977615356, Loss_02_1: 0.0, Loss_02_2: 0.6886894702911377
Step: 1, Loss_01: 0.6912064552307129, Loss_02_1: 0.6968430280685425, Loss_02_2: 0.6911896467208862
Step: 2, Loss_01: 0.6910352110862732, Loss_02_1: 0.698455274105072, Loss_02_2: 0.6935626864433289
Step: 3, Loss_01: 0.6955667734146118, Loss_02_1: 0.6843984127044678, Loss_02_2: 0.6953505277633667
Step: 4, Loss_01: 0.6941269636154175, Loss_02_1: 0.673763632774353, Loss_02_2: 0.6994296908378601
Step: 5, Loss_01: 0.6872361898422241, Loss_02_1: 0.6769005060195923, Loss_02_2: 0.6907837390899658
Step: 6, Loss_01: 0.6931678056716919, Loss_02_1: 0.7674703598022461, Loss_02_2: 0.6935689449310303
Step: 7, Loss_01: 0.6976977586746216, Loss_02_1: 0.7503389120101929, Loss_02_2: 0.7076178789138794
Step: 8, Loss_01: 0.6932153105735779, Loss_02_1: 0.7428234219551086, Loss_02_2: 0.6935019493103027
Step: 9, Loss_01: 0.693305253982544, Loss_02_1: 0.6476342082023621, Loss_02_2: 0.6916818618774414
Проблема с переключателем или условными операторами (например, if-then-else) как частью нейронной сети заключается в том, что условные операторы не везде дифференцируемы. Поэтому методы автоматической дифференциации не будут работать напрямую, и решить эту проблему очень сложно. Проверьте это для более подробной информации.
Ярлык заключается в том, что вы можете в конечном итоге обучить 3 отдельные модели независимо, а затем во время вывода использовать управляющий поток условных выражений для вывода из них.
#Training -
model1 = model.fit(all images, P(cat/dog))
model2 = model.fit(all images, P(cat))
model3 = model.fit(all images, P(dog))
final prediction = argmax(model2, model3)
#Inference -
if model1.predict == Cat:
model2.predict
else:
model3.predict
Но я не думаю, что вы ищете это. Я думаю, вы хотите включить условные выражения как часть самого графа вычислений.
К сожалению, насколько мне известно, у вас нет прямого способа построить условие «если-то» как часть графа вычислений. keras.switch
, который вы видите, позволяет вам работать с тензорными выходами, но не со слоями графа во время обучения. Вот почему вы увидите, что он используется как часть функций потерь, а не в графах вычислений (выдает ошибки ввода).
Однако вы можете попытаться построить что-то подобное с помощью skip connections
и soft switching
.
Соединение с пропуском — это соединение предыдущего уровня с другим уровнем, позволяющее передавать информацию последующим уровням. Это довольно распространено в очень глубоких сетях, где информация из исходных данных впоследствии теряется. Например, проверьте U-net или Resnet, которые используют пропуск соединений между слоями для передачи информации будущим слоям.
Следующим вопросом является вопрос переключения. Вы хотите переключиться между двумя возможными путями на графике. Что вы можете сделать, так это метод мягкого переключения, который я вдохновил этой статьи. Обратите внимание, что для того, чтобы switch
между двумя распределениями слов (одно из декодера и другое из ввода), авторы умножают их на p
и (1-p)
, чтобы получить кумулятивное распределение. Это программный переключатель, который позволяет модели выбирать следующее предсказанное слово либо из декодера, либо из самого ввода. (помогает, когда вы хотите, чтобы ваш чат-бот произносил слова, введенные пользователем, как часть ответа на него!)
Поняв эти 2 понятия, давайте попробуем интуитивно построить нашу архитектуру.
Сначала нам нужен граф с одним входом и несколькими выходами, так как мы обучаем 2 модели.
Наша первая модель представляет собой многоклассовую классификацию, которая предсказывает индивидуальные вероятности для Кошки и Собаки по отдельности. Это будет тренироваться с активацией softmax
и потерей categorical_crossentropy
.
Далее, давайте возьмем логит, который предсказывает вероятность Cat, и умножим на него слой свертки 3. Это можно сделать с помощью слоя Lambda
.
И аналогично, давайте возьмем вероятность Собаки и умножим ее на слой свертки 2. Это можно увидеть следующим образом:
1*(Conv3)
и 0*(Conv2)
.0*(Conv3)
и 1*(Conv2)
soft-switch
ИЛИ как forget gate
из LSTM. forget gate
— это сигмовидный (от 0 до 1) выходной сигнал, который умножает состояние ячейки, чтобы закрыть ее и позволить LSTM забыть или запомнить предыдущие временные шаги. Аналогичная концепция здесь!Эти Conv3 и Conv2 теперь могут быть дополнительно обработаны, сведены, объединены и переданы на другой плотный слой для окончательного прогноза.
Таким образом, если модель не уверена в отношении собаки или кошки, функции conv2 и conv3 участвуют в прогнозах второй модели. Вот как вы можете использовать механизм, вдохновленный skip connections
и soft switch
, чтобы добавить в свою сеть некоторое количество условного потока управления.
Проверьте мою реализацию графа вычислений ниже.
from tensorflow.keras import layers, Model, utils
import numpy as np
X = np.random.random((10,500,500,3))
y = np.random.random((10,2))
#Model
inp = layers.Input((500,500,3))
x = layers.Conv2D(6, 3, name='conv1')(inp)
x = layers.MaxPooling2D(3)(x)
c2 = layers.Conv2D(9, 3, name='conv2')(x)
c2 = layers.MaxPooling2D(3)(c2)
c3 = layers.Conv2D(12, 3, name='conv3')(c2)
c3 = layers.MaxPooling2D(3)(c3)
x = layers.Conv2D(15, 3, name='conv4')(c3)
x = layers.MaxPooling2D(3)(x)
x = layers.Flatten()(x)
out1 = layers.Dense(2, activation='softmax', name='first')(x)
c = layers.Lambda(lambda x: x[:,:1])(out1)
d = layers.Lambda(lambda x: x[:,1:])(out1)
c = layers.Multiply()([c3, c])
d = layers.Multiply()([c2, d])
c = layers.Conv2D(15, 3, name='conv5')(c)
c = layers.MaxPooling2D(3)(c)
c = layers.Flatten()(c)
d = layers.Conv2D(12, 3, name='conv6')(d)
d = layers.MaxPooling2D(3)(d)
d = layers.Conv2D(15, 3, name='conv7')(d)
d = layers.MaxPooling2D(3)(d)
d = layers.Flatten()(d)
x = layers.concatenate([c,d])
x = layers.Dense(32)(x)
out2 = layers.Dense(2, activation='softmax',name='second')(x)
model = Model(inp, [out1, out2])
model.compile(optimizer='adam', loss='categorical_crossentropy', loss_weights=[0.5, 0.5])
model.fit(X, [y, y], epochs=5)
utils.plot_model(model, show_layer_names=False, show_shapes=True)
Epoch 1/5
1/1 [==============================] - 1s 1s/step - loss: 0.6819 - first_loss: 0.7424 - second_loss: 0.6214
Epoch 2/5
1/1 [==============================] - 0s 423ms/step - loss: 0.6381 - first_loss: 0.6361 - second_loss: 0.6400
Epoch 3/5
1/1 [==============================] - 0s 442ms/step - loss: 0.6137 - first_loss: 0.6126 - second_loss: 0.6147
Epoch 4/5
1/1 [==============================] - 0s 434ms/step - loss: 0.6214 - first_loss: 0.6159 - second_loss: 0.6268
Epoch 5/5
1/1 [==============================] - 0s 427ms/step - loss: 0.6248 - first_loss: 0.6184 - second_loss: 0.6311
Проверьте этот ответ stackoverflow.com/a/52854474/14161847