Django - как сделать сложную математическую аннотацию (k ближайших соседей)

У меня есть эта модель:

class Image(models.Model):
    title = models.CharField(max_length=200)
    image = models.ImageField(upload_to='img/')
    signature = models.TextField(null = True)

Подпись представляет собой простой одномерный вектор, закодированный в json. Чтобы сделать мой запрос, я должен декодировать каждую сигнатуру объекта в nparray и сделать скалярное произведение между сигнатурой каждого объекта и заданным вектором, а затем аннотировать как поле с плавающей запятой (с именем «оценка») рядом с каждым необработанным. Наконец, я должен заказать от макс до мин.

Я пробовал это в view.py

def image_sorted(request):
    query_signature = extract_feat(settings.MEDIA_ROOT + "/cache" + "/003_ant_image_0003.jpg") # a NParray object

    image_list = Image.objects.annotate(score=np.dot(
        JSONVectConverter.json_to_vect(F('signature')), query_signature.T
    ).astype(float)).order_by('score') #JSONVectConverter is a class of mine
    return render(request, 'images/sorted.html', {'image_sorted': image_list})

конечно не работает. Я думаю, что оператор "F()" выходит за рамки...

Если вам интересно, я пишу веб-приложение для поиска изображений для своей университетской диссертации.

Спасибо.

РЕДАКТИРОВАТЬ: я нашел это, это совершенно та же проблема (вместо этого он использует postgres MySQL)

EDIT2: я только сейчас вспомнил, какое решение я принял последним! Сначала я извлекаю каждый вектор из БД и храню его в ОЗУ, затем делаю несколько простых вычислений, чтобы найти K-ближайших соседей. Затем я извлекаю из БД соответствующее изображение, используя его индекс (первичный ключ). Поэтому я отделяю эту задачу от Django ORM. Вот код (из Rest API)

def query_over_db(query_signature, page):

    query_signature = np.array(query_signature)

    t0 = time.time()

    descriptor_matrix = cache.get('descriptor_matrix')
    id_vector = cache.get('id_vector')

    if not descriptor_matrix:
        id_vector = []
        descriptor_matrix = []
        images_dict = Image.objects.all().values('id', 'signature')
        for image in images_dict:
            s = image['signature']
            descriptor = np.array(s)
            descriptor_matrix.append(descriptor)
            id_vector.append(image['id'])

        cache.set('id_vector', id_vector)
        cache.set('descriptor_matrix', descriptor_matrix)

    t1 = time.time()
    print("time to pull out the descriptors : " + str(t1 - t0))
    t1 = time.time()
    #result = np.abs(np.dot(descriptor_matrix, query_signature.T))

    #result = np.sum((descriptor_matrix - query_signature)**2, axis=1)

    result = ne.evaluate('sum((descriptor_matrix - query_signature)**2, axis=1)')

    t2 = time.time()
    print("time to calculate similarity: " + str(t2 - t1))

    perm = np.argsort(result)[(page - 1) * 30:page * 30]
    print(perm.shape)
    print(len(id_vector))

    perm_id = np.array(id_vector)[perm]
    print(len(perm_id))

    print("printing sort")
    print(np.sort(result)[0])

    t4 = time.time()

    print("time to order the result: " + str(t4 - t2))

    qs = Image.objects.defer('signature').filter(id__in=perm_id.tolist())

    qs_new = []
    for i in range(len(perm_id)):
        qs_new.append(qs.get(id=perm_id[i]))

    t3 = time.time()
    print("time to get the results from the DB : " + str(t3 - t2))
    print("total time : " + str(t3 - t0))
    print(result[perm])
    return qs_new

person Federico Vaccaro    schedule 21.01.2018    source источник
comment
Добро пожаловать в Stack Overflow! Вы можете узнать Как задать хороший вопрос и создать Минимальный, полный и проверяемый пример. Так нам легче вам помочь.   -  person Stephen Rauch    schedule 21.01.2018


Ответы (2)


Я не приблизился к тому, чтобы попробовать что-то такое сложное, однако я решил аналогичную проблему здесь: Объединение Django F, Value и dict для аннотирования набора запросов

Я не пробовал это, но вы можете попробовать:

    from django.db.models import Case, When, FloatField
    query_signature = extract_feat(settings.MEDIA_ROOT + "/cache" + "/003_ant_image_0003.jpg") # a NParray object

    value_dict = {}
    for image in Image.objects.all():
        value_dict[image.signature] = np.dot(
            JSONVectConverter.json_to_vect(image.signature),
                query_signature.T
            ).astype(float) 
    whens = [
        When(signature=k, then=v) for k, v in value_dict.items()
    ]
    qs = Image.objects.all().annotate(
        score=Case(
            *whens,
            default=0,
            output_field=FloatField()
        )
    ).order_by('score')

Надеюсь, поможет

person Omar    schedule 22.01.2018
comment
Привет, к сожалению, это не работает: выдает ту же ошибку, что и раньше: имя «F» не определено - person Federico Vaccaro; 22.01.2018
comment
Я попытался что-то изменить (выполнить точечный продукт на уровне Python), и интерпретатор может продолжить работу, но он застрял в середине, говоря, что мое имя «Когда» не определено. Я должен признать, что я не знаю этого синтаксиса. - person Federico Vaccaro; 22.01.2018
comment
@FedericoVaccaro Я внес некоторые исправления и добавил обязательный импорт для случаев и случаев — вы также можете прочитать о них здесь docs.djangoproject.com/en/2.0/ref/models/ - person Omar; 22.01.2018

Итак, это окончательный рабочий код:

def image_sorted(request):
    query_signature = extract_feat(settings.MEDIA_ROOT + "/cache" + "/001_accordion_image_0001.jpg")  # a NParray object
    #query_signature = extract_feat(settings.MEDIA_ROOT + "/cache" + "/003_ant_image_0003.jpg")  # a NParray object


    value_dict = {}
    for image in Image.objects.all():
        S = image.signature
        value_dict[image.signature] = np.dot(
            JSONVectConverter.json_to_vect(S),
            query_signature.T
        ).astype(float)
    whens = [
        When(signature=k, then=v) for k, v in value_dict.items()
    ]
    qs = Image.objects.all().annotate(
        score=Case(
            *whens,
            default=0,
            output_field=FloatField()
        )
    ).order_by('-score')

    for image in qs:
        print(image.score)

    return render(request, 'images/sorted.html', {'image_sorted': qs})

Спасибо Омару за помощь! Конечно, я все еще здесь, если есть более тонкие решения.

person Federico Vaccaro    schedule 22.01.2018