Вопрос или проблема
У меня есть модель Keras, и я хочу сделать с ней крутые визуализации. Это сеть для распознавания объектов.
Я подумал, было бы здорово ввести пустое изображение в сеть и рассматривать его как переменную, а не как веса, а затем обучить сеть всегда выводить, например, мороженое.
Поэтому я написал следующий код:
# загрузка модели
model = load_model('model.h5')
# создание входного изображения как переменной
w = tf.Variable(tf.zeros([1,224,224,3]))
# создание потока данных с переменной на входе
pred = model.call(inputs=w)
# создание желаемого распределения выходных данных
desired = np.zeros((1000))
desired[928] = 1.0
err = tf.reduce_mean(tf.subtract(pred,desired))
lr = tf.placeholder(dtype=tf.float32, shape=None)
# создание оптимизатора, который может влиять только на первоначальную входную переменную, которую я создал
optimizer = tf.train.AdamOptimizer(learning_rate=0.0001).minimize(err, var_list=[w])
# обучение сети
for i in range(0,100):
_,cost = sess.run([optimizer,err])
print(cost)
Я думал, что код будет работать хорошо, но стоимость просто не меняется. Она остается на месте, как будто полностью не подвергается воздействию.
Попробуйте явно установить переменную как обучаемую, установив Trainable=True. Также после создания сети проверьте, находится ли переменная в списке обучаемых https://www.tensorflow.org/api_docs/python/tf/trainable_variables
Ошибка проста: когда вы вводите пустое изображение. Предположим, черное изображение, как вы сделали с tf.zeros
, результат будет полон нулей, поэтому когда вы умножите свои веса на 0, полученное скалярное произведение будет 0.
Поскольку формула выглядит как sum(weights * conv)
, а так как conv заполнено нулями, скалярное произведение будет 0.
Затем мы добавим смещение: каждое значение в результирующем изображении будет активацией смещения, и будет оставаться тем же самым значением везде. Независимо от того, какое входное значение вы дадите и какие веса будут в первом слое, его градиент будет 0 и, следовательно, не изменится. Не имеет значения, какое входное изображение вы дадите, результирующее изображение будет заполнено 0 со значениями смещения, и конечный вывод будет тем же самым. Таким образом, оно не будет ничему учиться.
Ответ или решение
Ваша задача заключается в том, чтобы использовать пустое изображение в качестве переменной и обучить нейронную сеть генерировать определенную категорию объектов (в данном случае – мороженое). Однако, как вы уже заметили, ваша модель не обучается, потому что входные данные (пустое изображение) оказываются неэффективными для передачи информации в сеть.
Основная проблема
Когда вы используете tf.zeros
для создания входного изображения, в результате вы получаете тензор, заполненный нулями. При прогонке через первую свертку нейронной сети, результат будет равен нулю, так как:
[ \text{output} = \text{sum(weights} \times \text{conv}) + \text{bias} ]
Поскольку все значения в conv
равны нулю, произведение будет равно нулю. В таком случае на выходе мы получим только смещение (bias
), что приводит к тому, что градиенты для веса будут равны нулю, и сеть не сможет обучаться.
Как можно решить проблему
Чтобы ваша модель могла учиться, вы можете использовать несколько подходов:
- Инициализация случайного изображения: Вместо того, чтобы начинать с нуля, создайте случайное изображение. Например, используйте
tf.random.uniform
для генерации случайных значений в нужной области:
w = tf.Variable(tf.random.uniform([1, 224, 224, 3]))
-
Использование предобученного изображения: Вместо пустого изображения можете воспользоваться случайным предобученным изображением, соответствующим задаче, чтобы улучшить обучение.
-
Добавление шума: Если не желаете использовать предобученные изображения, попробуйте добавить небольшой уровень шума к вашему пустому изображению, чтобы получить разнообразные градиенты.
-
Изменение функции потерь: Если у вас есть доступ к классу, который часто встречается в ваших данных, попробуйте использовать не только прямую зависимость от целевой метки, но и дополнительно учитывать примеры, имеющие неопределенную метку.
Пример исправленного кода
Вот пример вашего кода с исправлениями:
# Загрузка модели
model = load_model('model.h5')
# Создаем случайное изображение в качестве переменной
w = tf.Variable(tf.random.uniform([1, 224, 224, 3], minval=0, maxval=255))
# Создаем граф потока с переменной на входе
pred = model.call(inputs=w)
# Создаем распределение желаемых выходов
desired = np.zeros((1000))
desired[928] = 1.0
# Функция потерь
err = tf.reduce_mean(tf.subtract(pred, desired))
lr = tf.placeholder(dtype=tf.float32, shape=None)
# Создаем оптимизатор
optimizer = tf.train.AdamOptimizer(learning_rate=0.0001).minimize(err, var_list=[w])
# Обучаем сеть
for i in range(100):
_, cost = sess.run([optimizer, err])
print(cost)
Использование такого подхода должно помочь вашей модели начать обучение, поскольку входные данные теперь содержат информацию, а не просто нули. Это приведет к ненулевым градиентам и обновлению весов.