Как получить выходные данные модели до softmax, не меняя архитектуру модели?

У меня есть обученная sequentialkeras модель.

Последний слой — плотный слой с функцией активации softmax:

model = keras.models.Sequential()
model.add(...)
model.add(...)
model.add(...)
model.add(keras.layers.Dense(50, activation='softmax'))

Как я могу получить выходные данные модели до softmax, не меняя архитектуру модели? У меня есть обученная модель, которую я не могу изменить или обучить.

Я пробовал с:

probs = model.predict(X_train)
logits = probs - np.log(np.sum(np.exp(probs), axis=-1, keepdims=True))

Но кажется, что если я запускаю softmax на logtis, результаты будут отличаться от результатов проб.

def softmax(x):
    e_x = np.exp(x - np.max(x))  
    return e_x / e_x.sum(axis=1, keepdims=True)

probabilities = softmax(logits)
Udacity Nanodegree Capstone Project: Классификатор пород собак
Udacity Nanodegree Capstone Project: Классификатор пород собак
Вы можете ознакомиться со скриптами проекта и данными на github .
0
0
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете вернуть softmax обратно в logits следующими способами.

# options 1
# ref. https://stackoverflow.com/a/64668809/9215780
def inv_softmax(x, C):
    logits = np.log(x) + C
    return logits

# options 2
def inv_softmax(x):
    logits = np.log(x / (1 - x))
    return logits 

Вот фиктивный код для их проверки.

num_class=50
inputs = Input(shape=(5,))
x = Dense(128, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
x = Dense(32, activation='relu')(x)
outputs = Dense(num_class, activation='softmax')(x)
model = Model(inputs=inputs, outputs=outputs)

a = tf.ones(shape=(2, 5))
y_pred_prob = model(a)
y_pred_prob.shape # TensorShape([2, 50])

# following option 1
logits = inv_softmax(y_pred_prob, num_class)
logits.shape # (2, 50)

y_pred_prob_reproduce = tf.nn.softmax(logits)
y_pred_prob_reproduce.shape # TensorShape([2, 50])
y_pred_prob[0]
<tf.Tensor: shape=(50,), dtype=float32, numpy=
array([0.02209216, 0.01324271, 0.01495313, 0.0182846 , 0.02364523,
       0.01963637, 0.02066819, 0.02271825, 0.02229412, 0.01854686,
       0.01951347, 0.02007069, 0.02835885, 0.01483266, 0.02553979,
       0.01616779, 0.01538332, 0.01937215, 0.01792852, 0.01752241,
       0.02167817, 0.01575256, 0.0232809 , 0.0204947 , 0.01880379,
       0.01848676, 0.0199989 , 0.02911243, 0.02096296, 0.02170451,
       0.02149592, 0.02127673, 0.01858926, 0.02001583, 0.01901014,
       0.01976348, 0.01502533, 0.01940756, 0.01502022, 0.02546986,
       0.02210576, 0.01966349, 0.01942356, 0.02224619, 0.02430816,
       0.0187437 , 0.01451708, 0.02327427, 0.01841178, 0.02118474],
      dtype=float32)>

y_pred_prob_reproduce[0]
<tf.Tensor: shape=(50,), dtype=float32, numpy=
array([0.02209212, 0.01324273, 0.01495312, 0.01828457, 0.02364521,
       0.0196364 , 0.02066822, 0.02271822, 0.02229412, 0.01854688,
       0.01951349, 0.02007072, 0.02835883, 0.01483268, 0.02553976,
       0.01616781, 0.0153833 , 0.01937212, 0.01792852, 0.01752243,
       0.02167813, 0.01575255, 0.02328094, 0.02049471, 0.01880377,
       0.01848677, 0.01999888, 0.02911241, 0.02096297, 0.02170452,
       0.02149589, 0.02127676, 0.01858924, 0.02001582, 0.01901012,
       0.01976348, 0.01502534, 0.01940755, 0.01502024, 0.02546991,
       0.02210577, 0.01966346, 0.01942355, 0.0222462 , 0.02430819,
       0.01874369, 0.01451708, 0.02327428, 0.01841175, 0.02118476],
      dtype=float32)>


tolerance = 1e-6
is_equal = np.allclose(
    y_pred_prob, y_pred_prob_reproduce, atol=tolerance
) # OK
Ответ принят как подходящий

На самом деле нет необходимости инвертировать логиты.

Вы можете просто создать ту же архитектуру модели в новом экземпляре keras.models.Model, без активации softmax на последнем слое, загрузить веса исходной модели в новую модель (используя model.load_weights), и тогда у вас будет модель без softmax в конец, где можно делать прогнозы.

model = keras.models.Sequential()
model.add(...)
model.add(...)
model.add(...)
model.add(keras.layers.Dense(50, activation='linear'))

model.load_weights('model.h5')

# Now predicts logits.
model.predict(some_input)

Это звучит слишком сложно. Также добавьте код для демонстрации рабочего процесса.

Innat 02.05.2024 09:31

@Innat На самом деле он короче вашего ответа, поэтому совсем не сложен.

Dr. Snoopy 02.05.2024 10:05

На самом деле мой ответ короче вашего, если считать только метод inv_softmax. Кроме того, я добавил полный рабочий код и доказательство. Ваш ответ хорош, но слишком сложен в том смысле, что мне пришлось переписать определение модели, а также может возникнуть проблема с принятием формата сохраненной модели. :)

Innat 02.05.2024 14:30

@Innat Любой ответ лучше, если вы выбираете части, концептуально мой ответ фактически отвечает на вопрос, инвертирование softmax не всегда дает вам одни и те же исходные логиты, происходит некоторая потеря информации.

Dr. Snoopy 02.05.2024 16:32

Согласен, что часть информации будет утеряна. Итак, если точное совпадение очень желательно, то это путь. Но ОП уже пробовал обратный метод, и я предположил, что он/она хорошо об этом знает. В любом случае, спасибо.

Innat 02.05.2024 21:06

Другие вопросы по теме