Как извлечь вектор объектов из одного изображения в Pytorch?

#python #computer-vision #pytorch #feature-extraction

#python #компьютерное зрение #pytorch #извлечение функции #функция-извлечение

Вопрос:

Я пытаюсь больше понять о моделях компьютерного зрения, и я пытаюсь немного изучить, как они работают. В попытке лучше понять, как интерпретировать векторы объектов, я пытаюсь использовать Pytorch для извлечения вектора объектов. Ниже приведен мой код, который я собрал по кусочкам из разных мест.

 import torch
import torch.nn as nn
import torchvision.models as models
import torchvision.transforms as transforms
from torch.autograd import Variable
from PIL import Image



img=Image.open("Documents/01235.png")

# Load the pretrained model
model = models.resnet18(pretrained=True)

# Use the model object to select the desired layer
layer = model._modules.get('avgpool')

# Set model to evaluation mode
model.eval()

transforms = torchvision.transforms.Compose([
        torchvision.transforms.Resize(256),
        torchvision.transforms.CenterCrop(224),
        torchvision.transforms.ToTensor(),
        torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
    ])
    
def get_vector(image_name):
    # Load the image with Pillow library
    img = Image.open("Documents/Documents/Driven Data Competitions/Hateful Memes Identification/data/01235.png")
    # Create a PyTorch Variable with the transformed image
    t_img = transforms(img)
    # Create a vector of zeros that will hold our feature vector
    # The 'avgpool' layer has an output size of 512
    my_embedding = torch.zeros(512)
    # Define a function that will copy the output of a layer
    def copy_data(m, i, o):
        my_embedding.copy_(o.data)
    # Attach that function to our selected layer
    h = layer.register_forward_hook(copy_data)
    # Run the model on our transformed image
    model(t_img)
    # Detach our copy function from the layer
    h.remove()
    # Return the feature vector
    return my_embedding

pic_vector = get_vector(img)
  

Когда я делаю это, я получаю следующую ошибку:

 RuntimeError: Expected 4-dimensional input for 4-dimensional weight [64, 3, 7, 7], but got 3-dimensional input of size [3, 224, 224] instead
  

Я уверен, что это элементарная ошибка, но, похоже, я не могу понять, как это исправить. У меня сложилось впечатление, что преобразование «totensor» сделает мои данные 4-d, но, похоже, это либо работает неправильно, либо я неправильно понимаю. Ценю любую помощь или ресурсы, которые я могу использовать, чтобы узнать больше об этом!

Ответ №1:

Все значения по умолчанию nn.Modules в pytorch ожидают дополнительного пакетного измерения. Если входными данными для модуля являются shape (B, …), то на выходе также будет (B, …) (хотя последующие размеры могут меняться в зависимости от слоя). Такое поведение позволяет эффективно выполнять вывод на пакетах B входных данных одновременно. Чтобы привести свой код в соответствие, вы можете просто unsqueeze добавить дополнительное унитарное измерение в начало t_img тензора перед отправкой его в вашу модель, чтобы сделать его тензором (1, …). Вам также потребуется flatten вывод layer перед его сохранением, если вы хотите скопировать его в свой одномерный my_embedding тензор.

Пара других вещей:

  • Вы должны сделать вывод в torch.no_grad() контексте, чтобы избежать вычисления градиентов, поскольку они вам не понадобятся (обратите внимание, что model.eval() это просто изменяет поведение определенных слоев, таких как выпадение и пакетная нормализация, это не отключает построение графика вычислений, но torch.no_grad() делает).

  • Я предполагаю, что это просто проблема с копированием и вставкой, но transforms это имя импортированного модуля, а также глобальная переменная.

  • o.data просто возвращает копию o . В старом Variable интерфейсе (около PyTorch 0.3.1 и более ранних версий) это было необходимо, но Variable интерфейс был устаревшим еще в PyTorch 0.4.0 и больше не делает ничего полезного; теперь его использование просто создает путаницу. К сожалению, многие учебные пособия все еще пишутся с использованием этого старого и ненужного интерфейса.

Обновленный код выглядит следующим образом:

 import torch
import torchvision
import torchvision.models as models
from PIL import Image

img = Image.open("Documents/01235.png")

# Load the pretrained model
model = models.resnet18(pretrained=True)

# Use the model object to select the desired layer
layer = model._modules.get('avgpool')

# Set model to evaluation mode
model.eval()

transforms = torchvision.transforms.Compose([
    torchvision.transforms.Resize(256),
    torchvision.transforms.CenterCrop(224),
    torchvision.transforms.ToTensor(),
    torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])


def get_vector(image):
    # Create a PyTorch tensor with the transformed image
    t_img = transforms(image)
    # Create a vector of zeros that will hold our feature vector
    # The 'avgpool' layer has an output size of 512
    my_embedding = torch.zeros(512)

    # Define a function that will copy the output of a layer
    def copy_data(m, i, o):
        my_embedding.copy_(o.flatten())                 # <-- flatten

    # Attach that function to our selected layer
    h = layer.register_forward_hook(copy_data)
    # Run the model on our transformed image
    with torch.no_grad():                               # <-- no_grad context
        model(t_img.unsqueeze(0))                       # <-- unsqueeze
    # Detach our copy function from the layer
    h.remove()
    # Return the feature vector
    return my_embedding


pic_vector = get_vector(img)
  

Комментарии:

1. Большое спасибо за отличное объяснение. В качестве продолжения мне интересно, как наилучшим образом интерпретировать вектор объектов. Я думаю об этом так: каждое значение в этом векторе является представлением некоторой части информации о изображении. Есть ли какой-либо способ приблизительно узнать, что описывает каждое из 512 значений из документации resnet? Кажется, я ничего не могу найти. Как следствие этого, если я выполнил эту же процедуру на двух разных изображениях, соответствует ли 1-е значение в каждом результирующем pic_vector одному и тому же объекту? Если это слишком много для продолжения, я понимаю, спасибо

2. Это звучит немного абстрактно, но короткий ответ — «нет». Функция представляет собой абстрактное представление входного изображения в 512-мерном пространстве. Основная характеристика пространства объектов заключается в том, что если вы сравниваете объекты из изображений одних и тех же типов объектов, они должны быть рядом друг с другом, а разные типы объектов будут далеко друг от друга. Эта характеристика является результатом цели обучения сети. Кроме того, под «поблизости» мы обычно подразумеваем, что сходство по косинусу близко к 1, а под «далеко» мы подразумеваем, что сходство по косинусу не близко к 1.

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

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

5. Это скорее интерпретация, чем жесткое правило. В целевой функции нет ничего, что явно требовало бы, чтобы такие представления были изучены, но исследования и эксперименты показали, что это разумная интерпретация того, что происходит.

Ответ №2:

model(t_img) Вместо этого

Здесь просто делай—

 model(t_img[None])
  

Это добавит дополнительное измерение, следовательно, изображение будет иметь форму [1,3,224,224] и оно будет работать.