Проецируйте булеву маску из регионов максимального пула обратно на входной тензор.

Вопрос или проблема

Я работаю над реализацией пользовательского слоя макс-пулинга в TensorFlow и нуждаюсь в помощи проекции булевой маски на входной тензор, чтобы указать позиции, которые способствовали максимальным значениям в каждом окне пулинга.

Я хочу избежать использования циклов и ищу эффективное векторизованное решение, которое полностью использует параллельные вычислительные возможности GPU.

Входные данные

Вот упрощенная версия моей текущей настройки:

  1. Форма входного тензора: (размер_пакета, высота_входа, ширина_входа, n_каналов)
  2. Размер окна пулинга: (высота_пулинга, ширина_пулинга)
  3. Шаги: (высота_шага, ширина_шага)

Я использую tf.image.extract_patches, чтобы получить все области пулинга из входных данных за один шаг, затем изменяю форму, чтобы выделить каждое окно пулинга. Затем я вычисляю максимум для каждого окна, чтобы получить выходной тензор и создать булевую маску, указывающую позиции максимальных значений.

Вот мой код:

import tensorflow as tf

# Входные размеры
batch_size = 3
input_height = 4
input_width = 4
n_channels = 3
pool_height = 2
pool_width = 2
stride_height = 2
stride_width = 2

# Входной тензор
x = tf.random.normal((batch_size, input_height, input_width, n_channels)) # Форма: (размер_пакета, высота_входа, ширина_входа, n_каналов)

print("Входные данные:")
print(x[0, :, :, 0])

# Извлечение патчей для всех областей пулинга за один шаг
patches = tf.image.extract_patches(
    images = x,
    sizes = [1, pool_height, pool_width, 1],
    strides = [1, stride_height, stride_width, 1],
    rates = [1, 1, 1, 1],
    padding = "VALID"
)  # Форма: (размер_пакета, высота_выхода, ширина_выхода, высота_пулинга * ширина_пулинга * n_каналов)

# Изменение формы патчей для выделения каждого канала и окна пулинга
_, output_height, output_width, _ = patches.shape
patches = tf.reshape(patches, [batch_size, output_height, output_width, pool_height, pool_width, n_channels])  # Форма: (размер_пакета, высота_выхода, ширина_выхода, высота_пулинга, ширина_пулинга, n_каналов)

# Вычисление максимума по окну пулинга
output = tf.reduce_max(patches, axis=(3, 4))  # Форма: (размер_пакета, высота_выхода, ширина_выхода, n_каналов)

print("\nВыходные данные:")
print(output[0, :, :, 0])

# Создание маски для позиций максимумов
max_values = tf.expand_dims(tf.expand_dims(output, axis=3), axis=4)  # Форма: (размер_пакета, высота_выхода, ширина_выхода, 1, 1, n_каналов)
mask = tf.equal(patches, max_values)  # Форма: (размер_пакета, высота_выхода, ширина_выхода, высота_пулинга, ширина_пулинга, n_каналов)

print("\nМаска:")
print(mask[0, :, : ,:, :, 0])

# Тензор, который я хочу получить
cache = tf.reshape(mask, tf.shape(x)) # Форма: (размер_пакета, высота_входа, ширина_входа, n_каналов)

print("\nКэш:")
print(cache[0, :, :, 0])

Выходные данные

Выходные данные, которые я получаю, таковы (для простоты я вывел только первый канал первого элемента пакета):

Входные данные:
tf.Tensor(
[[ 1.8739315  -1.1739935   0.21598558 -1.0357039 ]
 [ 0.5625862   1.6689821   0.6279541  -0.6442553 ]
 [-1.4488437   0.4760101  -0.34631395  0.25045732]
 [ 0.8405037  -1.8099107   1.0800457   0.06632996]], shape=(4, 4), dtype=float32)

Выходные данные:
tf.Tensor(
[[1.8739315 0.6279541]
 [0.8405037 1.0800457]], shape=(2, 2), dtype=float32)

Маска:
tf.Tensor(
[[[[ True False]
   [False False]]

  [[False False]
   [ True False]]]

 [[[False False]
   [ True False]]

  [[False False]
   [ True False]]]], shape=(2, 2, 2, 2), dtype=bool)

Кэш:
tf.Tensor(
[[ True False False False]
 [False False  True False]
 [False False  True False]
 [False False  True False]], shape=(4, 4), dtype=bool)

Как видите, даже если маска кажется корректной, тензор кэша имеет некоторые неверные значения.

Ответ или решение

Проецирование булевой маски из областей макс-пулинга обратно на входной тензор

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

Пошаговое решение

  1. Извлечение патчей: Вы правильно используете tf.image.extract_patches(), чтобы извлечь области, которые будут подвергаться макс-пулингу. Это позволяет обрабатывать входной тензор векториально.

  2. Расчет максимумов: Создание выходного тензора с помощью tf.reduce_max() также выполнено корректно. Этот шаг необходим для получения значений, которые будут представлены в результирующем тензоре.

  3. Создание маски: Маска, которая вы создаете с помощью tf.equal(), позволяет отследить позицию максимумов в пулл-функции, однако последующий reshape может быть неправильно обработан.

Обработка маски

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

Определение корректного reshaping

Для того чтобы маска правильно отображалась на исходный тензор, необходимо следующее:

  • После создания маски с помощью tf.equal(), ее нужно ребразить так, чтобы соответствовать размерности входного тензора. Маска будет содержать True на позициях максимальных значений и False в остальных, и ее размер должен быть (batch_size, input_height, input_width, n_channels).

Единственное, что нужно сделать, это ввести дополнительное представление VR с использованием tf.reshape().

Вот улучшенная версия вашего кода с корректной проекцией маски:

import tensorflow as tf

# Входные размеры
batch_size = 3
input_height = 4
input_width = 4
n_channels = 3
pool_height = 2
pool_width = 2
stride_height = 2
stride_width = 2

# Входной тензор
x = tf.random.normal((batch_size, input_height, input_width, n_channels))

# Извлечение патчей для всех областей пулинга
patches = tf.image.extract_patches(
    images=x,
    sizes=[1, pool_height, pool_width, 1],
    strides=[1, stride_height, stride_width, 1],
    rates=[1, 1, 1, 1],
    padding="VALID"
)

# Изменяем форму патчей для выделения каждого канала и окон
_, output_height, output_width, _ = patches.shape
patches = tf.reshape(patches, [batch_size, output_height, output_width, pool_height, pool_width, n_channels])

# Вычисляем максимум для окна пулинга
output = tf.reduce_max(patches, axis=(3, 4))

# Создание замены для максимумов
max_values = tf.expand_dims(tf.expand_dims(output, axis=3), axis=4)  

# Маска для позиций максимумов
mask = tf.equal(patches, max_values)

# Правильная проекция маски на входной тензор
mask_output_shape = tf.shape(x)
projected_mask = tf.reshape(tf.cast(mask, tf.float32), [batch_size, output_height, output_width, pool_height, pool_width, n_channels])
cache = tf.reduce_sum(projected_mask, axis=[3, 4]) > 0  # Получение окончательной булевой маски

# Печать информации
print("Вход:")
print(x[0, :, :, 0])

print("\nВыход:")
print(output[0, :, :, 0])

print("\nМаска:")
print(mask[0, :, :, :, :, 0])

print("\nКэш:")
print(cache[0, :, :, 0])

Заключение

Данное решение позволяет вам избежать использования циклов и эффективно обрабатывает данные с использованием возможностей параллельной обработки на GPU. Проверка и корректировка reshaping обеспечит точность в отображении результатов маски на исходные данные. Проводя подобные оптимизации, вы можете значительно улучшить производительность вашего кода и точность ваших расчётов, что особенно важно в задачах глубокого обучения и компьютационной визуализации.

Оцените материал
Добавить комментарий

Капча загружается...