Объединение двух функций потерь в Keras в последовательной модели с выходом ndarray - PullRequest
0 голосов
/ 09 ноября 2019

Я тренирую модель CNN в Керасе (обнаружение объектов на изображении и LiDAR ( Kaggle Lyft Competition )). В качестве выхода у меня есть 34-канальный гирд. Таким образом, выходной размер составляет: ДЛИНА x ШИРИНА X 34. Первые 10 каналов предназначены для различных категорий объектов (в идеале как один горячий вектор), а остальные 24 канала являются координатами ограничивающего прямоугольника в 3D.

Для первых 10 каналовЯ хочу использовать: keras.losses.categorical_crossentropy, а для остальных 24: keras.losses.mean_squared_error

Кроме того, поскольку количество объектов резко отличается, чтобы избежать смещения, я взвешиваю каждый класс. Поскольку вывод ndarray, мне пришлось написать пользовательскую функцию потерь (для взвешивания класса). Код в основном из: Пользовательская функция потерь для U-сети в кератах с использованием весов классов: `class_weight` не поддерживается для 3+ размерных целей

def weightedLoss(weightsList):

def lossFunc(true, pred):

    axis = -1 #if channels last 
    #axis=  1 #if channels first


    #argmax returns the index of the element with the greatest value
    #done in the class axis, it returns the class index    
    classSelectors = K.argmax(true, axis=axis) 

    #considering weights are ordered by class, for each class
    #true(1) if the class index is equal to the weight index   
    one64 = np.ones(1, dtype=np.int64)  #Needed to avod int32 and int64 error
    classSelectors = [K.equal(one64[0]*i, classSelectors) for i in range(len(weightsList))]

    #casting boolean to float for calculations  
    #each tensor in the list contains 1 where ground true class is equal to its index 
    #if you sum all these, you will get a tensor full of ones. 
    classSelectors = [K.cast(x, K.floatx()) for x in classSelectors]

    #for each of the selections above, multiply their respective weight
    weights = [sel * w for sel,w in zip(classSelectors, weightsList)] 

    #sums all the selections
    #result is a tensor with the respective weight for each element in predictions
    weightMultiplier = weights[0]
    for i in range(1, len(weights)):
        weightMultiplier = weightMultiplier + weights[i]

    op_chan_loss = keras.losses.categorical_crossentropy
    op_box_loss = keras.losses.mean_squared_error
    #make sure your originalLossFunc only collapses the class axis
    #you need the other axes intact to multiply the weights tensor
    print(type(true), type(pred))
    loss = op_chan_loss(true, pred) 
    loss = loss * weightMultiplier

    return loss
return lossFunc

Однако я не уверен, какобъединить две функции потерь вместе в этой пользовательской функции потерь. Пожалуйста, помогите.

1 Ответ

0 голосов
/ 10 ноября 2019

Используйте два выхода. Ваша модель должна быть моделью функционального API:

#basic example of the initial part of your model
inputs = Input(input_shape)
intermediate_output = Conv2D(...)(inputs)
intermediate_output = Conv2D(...)(intermediate_output)

В какой-то момент в вашей модели вы разделите две ветви. Может быть, последний слой или немного раньше (поскольку природа двух выходов различна, может быть (требуется тестирование, конечно), для каждого выхода требуется один или два слоя для лучшей адаптации.

output1 = SomeLayer(...)(intermediate_output)
....
output1 = Dense(10, activation='softmax', name='cat_out')(output1) 


output2 = SomeLayer(...)(intermediate_output)
....
output2 = SomeLayer(24, name='bound_out')(output2) #maybe choose an activation

Создание моделис двумя выходами:

model = Model(inputs, [output1, output2])

Теперь это позволяет вам иметь разные функции потерь для каждого выхода и разные метрики:

model.compile(loss = [weightedLoss(weights), 'mse'], 
              metrics = {'cat_out':[metrics1], 'bound_out':[metrics2], 
              optimizer = ...)

Поезд с двумя выходами:

model.fit(x_train, [y_train[:,:,:,:10], y_train[:,:,:,-24:]], ...)

Я не проверял вашу функцию потерь, но видел, что в ней есть циклы (обычно это плохо для тензорных операций).

Я предлагаю, чтобы ваш список весов был тензором:

weightsTensor = K.constant(listOfWeights)

И когда вы выбираете свои веса, попробуйте использовать tf.gather или tf.gather_nd. Я не тратил время на размышления о том, что является правильной функцией и как ее использовать, но как только вы это сделаетеэто, вы просто умножаете свои результаты на веса:

#this may need change and might be a little troublesome to understand
selectedWeights = tf.gather_nd(weightsTensor, classSelectors, ....) 

loss = keras.losses.categorical_crossentropy(true, pred)
loss = selectedWeights * loss
...