Введение

Если вы хотите получить больше информации о своей нейронной сети изображений, как и я, вот 2 хороших способа сделать это. Оба метода подходят к этой проблеме с идеей значимости изображения.

заметность

Оксфордское определение:Выдающееся положение, способность быть особенно заметным или важным.

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

Заметность через окклюзию

Впервые представленный в Zeiler and Fergus, «Visualizing and Understanding Convolutional Networks», ECCV 2014, этот метод привел к идее перекрытия части изображения, чтобы определить, насколько «важным» был этот сегмент для предсказания наша модель.

Мы можем использовать Numpy с таким подходом:

  1. Добавление карты окклюзии в исходное изображение.
  2. Расчет количества ошибок, которые добавляются в прогноз.
  3. Приписывание ошибки закрытым пикселям.

def generateOcclusion(new_img, attrib):
  """ Adds a gray occlusion patch to an image, 
  given the size and coordinates of the occlusion.
    Args:
      new_img: Numpy array of size (NxNx3)
      attrib: tuple, with (size_of_patch, x coordinate, y coordinate)
    Returns tuple, with occluded image, and a map of occluded pixels
  """
  n = attrib[0]
  y = attrib[1]
  x = attrib[2]
  img_count = np.zeros((224,224))   # Generate an array of zeros in the size of image
  img_count[y:y+n,x:x+n] = 1        # Save which pixels were occluded
  new_img[:, y:y+n,x:x+n, :] = 0.5 # Generate the occlusion in gray

  return new_img, img_count


def schedule(size):
  """
    Args:
      size (int): Length of original image.
    Returns List of (size, pos_y, pos_x)
  """

  occ_size = 6  # Occlusion size
  wrate = 10

  diminishing_N = []
  for i in range(1):
    n = occ_size - i*wrate
    if n > 0:
      diminishing_N.append(n)

  schedule = []
  for N in diminishing_N:
    steps = size // N
    for step_y in range(steps):
      for step_x in range(steps):
        schedule.append((N, 0 + step_y*N, 0 + step_x*N))

  return schedule


def updateSaliency(saliency_err, img_ones, err, output_size):
  """
    Args:
      saliency_err: array with the previous errors with shape (NxN)
      img_ones: array with occluded pixels
      err: Amount of error with occlusion
      output_size: N.
    Returns: updated saliency_err
  """
  occluded = np.array([img_ones]*output_size)
  error = err.reshape(output_size,1,1)
  new_error = occluded * error
  saliency_err +=new_error
  return saliency_err



def saliencyViaOcclusion(og_img, model):

  output_size = model.output.shape[1]
  og_img = np.array([og_img])
  y_hat = model.predict(og_img) # (224, 224, 3)
  saliency_err = np.zeros((output_size, 224,224))
  saliency_cnt = np.zeros((output_size, 224,224))

  list_occ_img = []
  for attrib in schedule(224):
    new_img = og_img.copy()
    occ_img, img_count = generateOcclusion(new_img, attrib)
    y2_hat = model.predict(occ_img)
    err = np.absolute(y_hat - y2_hat)
    saliency_cnt += np.array([img_count]*output_size)
    saliency_err = updateSaliency(saliency_err, img_count, err, output_size)

  saliency_map = saliency_err / saliency_cnt # calculate average if maps overlap
  return saliency_map

Заметность через обратное распространение

Впервые представленный в работе Simonyan, Vedaldi, and Zisserman, «Deep Inside Convolutional Networks: Visualizing Image Classification Models and Saliency Maps», ICLR Workshop 2014, этот подход представил метод, который будет генерировать карту изображения, которая максимизирует выходной балл.

Мы можем использовать Tensorflow GradientTape():

  1. Получение функции потерь модели и подготовка наших данных.
  2. Прогнозирование с помощью модели и расчет потерь, а также использование автоматического дифференцирования.
  3. Вычислите градиент потерь по отношению к исходному изображению.

from keras import backend as k

def saliencyViaBackprop(model, loss, y_true, img_example):

  loss_fn = tf.keras.losses.get(loss)
  img_example = np.array([img_example])
  # keep track of our gradients
  x_tensor = tf.convert_to_tensor(img_example, dtype=tf.float32)
  with tf.GradientTape() as tape:
    tape.watch(x_tensor)
    # make a prediction using the model and then calculate the
    # loss
    pred = model(x_tensor)
    loss = loss_fn(y_true, pred)
   # calculate the gradients using our tape and then update the
   # model weights
  evaluated_gradients = tape.gradient(loss, x_tensor)

  res = tf.abs(evaluated_gradients)
  return evaluated_gradients

В заключение

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

Кроме того, если вы не используете Tensorflow, я показал вам способ получить результаты только с помощью numpy. Заметность с помощью обратного распространения также можно выполнить с помощью других фреймворков, таких как PyTorch с autograd(), просто выполнив те же действия.