Image

Разбираю свой фотоархив

TL;DR: Сделал поиск по личному архиву фотографий с применением трех нейросетей, векторного расширения к PostgreSQL и Django. Красивое.

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

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

Разумеется, нельзя просто так взять облачный ChatGPT и натравить на архив, чтобы он посмотрел на всё это богатство и разложил по полочкам и коробочкам. Во-первых, это будет дорого, во-вторых — страшно дорого, а в третьих будет долго и непонятно, что нужно сделать, чтобы прийти к нужному результату.

То есть надо рассматривать локальные модели.

Сначала я рассматривал такой вариант — прошерстить архив мультимодальной моделью, чтобы она создала описания для каждой фотографии. Полученные описания, вместе с распарсенным EXIF, сложить в полнотекстовый поиск — что-то на основе Apache Lucene, то есть Elasticsearch или Solr. Скорее второй, потому что я с ним имел дело когда-то. И уже над этим делать интерфейс поиска.

b8344f958a20e4ffbabca3a7fd306d6f

Перебрал несколько моделей, но не нашел такой, чтобы сделала внятное описание на русском языке и, одновременно, чтобы я бы ее смог запустить локально. Причем те многоязычные модели, что качественно описывают фотографию на английском, на русском творят несусветную дичь.

Фото счастливой семьи. Взято из открытых источников. Но что, по мнению ИИ, здесь изображено?
Фото счастливой семьи. Взято из открытых источников. Но что, по мнению ИИ, здесь изображено?

Ответы llava-hf/llava-v1.6-mistral-7b-hf

«Describe the image»

The image is a color photograph featuring three individuals, likely a family, outdoors. In the foreground, there is a woman with blonde hair, wearing a light pink top and blue jeans, smiling broadly and looking towards the camera. She appears to be holding a young girl with her left arm wrapped around her. The girl has light brown hair, is wearing a red top, and is also smiling.

Behind the woman and the girl, there is a man with short dark hair, wearing a light blue t-shirt and khaki shorts. He is also smiling and looking towards the camera. His right arm is wrapped around the woman’s waist, and his left arm is wrapped around the girl’s shoulders, suggesting a close and affectionate relationship among the three.

The background is blurred, but it appears to be a natural setting with greenery, indicating they might be in a park or a garden. The lighting suggests it is a sunny day. There are no visible texts or logos in the image. The overall mood of the photograph is cheerful and familial.

«three individuals»?

«Что изображено на картинке? Отвечай на русском языке»

На картинке изображено четыре люди, которые se смеют и сближением с камерой. Они se смеют и сближением с камерой, показывая себя с улыбкой и радостью. Наиболее вероятно, что это семейство, т.е. мама и три дети, поскольку они se сближением с камерой и смеются.

Мама и три дети, ага.

Ответы Qwen/Qwen2-VL-7B-Instruct

«Describe the image»

The image shows a family of four enjoying a moment together outdoors. The family consists of two adults and two children. The adults are smiling and appear to be having a fun time, with one adult giving the other a piggyback ride. The children are also smiling and seem to be enjoying the moment. The background suggests they are in a park or a similar outdoor setting with trees and greenery.

«Что изображено на картинке?»

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

А иногда модель на твердом русском языке убеждала, что на этом фото только два человека.

Ок, умный в гору не пойдет, настоящие герои всегда ищут другое решение. Если есть модель, которая умеет делать описания на английском языке, не будем пытаться переучивать её на русский — долго, дорого, непонятно. Лучше наймем ей переводчика, то есть возьмем модель, обученную делать только одну функцию — перевод с английского на русский, зато делает это отлично достаточно хорошо.

3612987d936fb80d340d3b4329fcd151

Примерно определился с моделями:

  • Описания делает llava-hf/llava-v1.6-mistral-7b-hf

  • Переводом занимается Helsinki-NLP/opus-mt-en-ru

Где-то в этот момент Сбер выкатил на HF модель русскоязычного эмбеддинга ai-sage/Giga-Embeddings-instruct, об этом были статьи на Хабре. И тут Штирлица осенило — а на куа фа городить полнотекстовый поиск, если можно описания превратить в векторы, сложить в векторную же базу и искать уже в ней?

1155bd1155a8c0882edffdb3c21929f8

Сказано — сделано. То есть попробовано. Только взгляд на модель Сбера несколько расстроил — файлов на 13 гигов. Влезет ли на видеокарту, в которой 16 гигов? На HF нашлась еще одна моделька эмбеддинга, angelitasr/jina-embeddings-v3_eeid, её файлы занимают около 1,1 гига.

Впоследствии оказалось, что я зря опасался, Giga-Embeddings-instruct занимает на GPU примерно 7,5 гиг, а вторая — 2,5. то есть влезет и та, и другая, и обе вместе. Но модель от Сбера создает вектор на 2048 элементов, а вторая — на 1024. Учитывая заявленную русскоязычность Giga-Embeddings-instruct, выбор очевиден.

С выбором векторной базы вопрос не стоял — разумеется, PostgreSQL с расширением pgvector. Не потому, что постгри весь такой крутой и сильный, а потому что дополнительных приключений на свой филей не хотелось. Раз финтех на нём работает, то и я как-нибудь разберусь.

e969a35883d3295aafa5c89ebcc252fc

Когда я запускал модели для пробы, то брал примеры скриптов прямо из их Readme. Эти же скрипты, чуть обмазав их http.server и изобразив некое подобие OpenAI API, теперь запускают на сервере модели. Да, то есть нет, я не использую ни vllm, ни llama.cpp, ни ollama, мой велосипед, где хочу, там и запускаю.

198118890e725b285d8a038a3de824d0

Итого у нас здесь банкет на две персоны — сервер, который не знает, что за фотки ему подсовывают, и клиент, который ходит по папкам, собирает фотографии, переводит их в векторы и складывает в базу.

«Но и это ещё не всё!», как говорят в рекламе. Теперь нужен какой-то интерфейс к этой, несомненно, очень нужной и важной штуке, который позволит, собственно, искать фото.

Мне нужен доступ в домашней сети, поэтому нужен веб-сервер, в который будет достаточно просто встроить Python-код поиска в базе. Следовательно, надо брать Django, хоть я с ним и не знаком от слова «вообще».

03294b27b7d06f1188c4d1cce326b182

Нашел в Сети самый простой пример показа галереи картинок, переделал под свои нужды — одно простое поле ввода, ниже открываются найденные картинки. Как тебе такое, Google Photo?

086fe0953e7ca856e36dbf9a85ebcf5c

Строка поиска отправляется на ту же самую (и это важно) модель эмбеддинга Giga-Embeddings-instruct, которой создавались вектора по картинкам. По полученному от модели вектору делаем поиск в векторной базе, фильтруя дистанцию между векторами какой-то наспех выбранной константой, сортируем по дистанции, и ограничиваем выборку, скажем, 50-ю записями.

Далее немного кода в Django

# models.py from django.db import models from pgvector.django import VectorField import base64 import os class Image(models.Model): image_path = models.CharField() # путь к оригинальному фото на диске embedding = VectorField(dimensions=2048) # вектор для описания фото description = models.CharField() # Сгенерированное описание thumb = models.ImageField() # Миниатюра, PNG def __str__(self): return os.path.basename(self.image_path) def thumb_url(self) -> str: «»» Формирует строку в base64 миниатюры фотографии. Если миниатюры нет, возвращает url на заглушку. Полученную строку можно использовать в <img src=»url»> :return: строка url «»» if self.thumb: return ‘data:image/png;base64, ‘ + base64.b64encode(bytes(self.thumb)).decode(‘utf-8’) return ‘static/noimage.png’ class Meta: db_table = ‘description_vectors’ managed = False # views.py from django.shortcuts import render from .models import Image from django.http import HttpResponse, HttpRequest from .extern import ExternModel from pgvector.django import L2Distance def image_list(request: HttpRequest): «»» По строке поиска из параметра ‘q’ получает вектор от модели-эмбеддера. По этому вектору ищет в БД подходящие записи и рендерит страницу. :param request: GET-запрос с query-параметром ‘q’ :return: HTML-страница «»» model = ExternModel(«ai-sage/Giga-Embeddings-instruct») # подключаем модель эмбеддинга prev_sample = «» # для заполнения строки поиска sample = request.GET.get(«q») if sample: vector = model.embedding(sample) # получили вектор images = Image.objects.using(«cloud_db»).alias( # подключлись к БД distance=L2Distance(’embedding’, vector) # берем дистанцию от вектора ).filter( distance__lt=0.93 # и ограничиваем ее неким числом, выбранным по вкусу ).order_by( L2Distance(’embedding’, vector) # сортируем, чтобы самые похожие были в начале )[:50] # оставляем только 50 записей prev_sample = sample else: images = [] return render(request, «search.html», {«images»: images, «sample»: prev_sample}) # extern.py import requests class ExternModel: «»» Обращение к внешней модели «»» def __init__(self, model): «»» :param model: имя модели, сконфигурированное на сервере «»» super().__init__() self.server = server_url # адрес сервера, где крутится модель self.checkpoint = model def embedding(self, text) -> tuple or list: «»» Обращается к модели и получает веектор :param text: строка :return: вектор эмбеддинга «»» req = { «model»: self.checkpoint, «text»: text } response = requests.post(self.server + ‘/embeddings’, json=req) r = response.json() answer = r.get(‘text’, {}).get(’embedding’, []) return answer search.html<!DOCTYPE html> <html lang=»en»> <head> <meta charset=»UTF-8″> <title>Поиск фото</title> <style> /* Add CSS styles for flex container and items */ .image-list { display: flex; flex-wrap: wrap; /* Allow items to wrap to the next row if necessary */ justify-content: space-between; /* Space items evenly along the main axis */ list-style: none; /* Remove list styles */ padding: 0; } .image-item { flex: 1; /* Grow to fill available space evenly */ max-width: 30%; /* Limit item width to avoid overcrowding */ margin: 10px; /* Add spacing between items */ border: 1px solid #ccc; /* Add a border for visual separation */ padding: 10px; text-align: center; } </style> </head> <body> <div> <form method=»get»> <label> Поиск фото <input type=»text» name=»q» style=»width: 80%» value=»{{ sample }}»/> </label> <button type=»submit»>поиск</button> </form> </div> {% load static %} <div> <ul class=»image-list»> {% for image in images %} <li class=»image-item»> <a href=»{% static image.image_path %}» target=»_blank»> <img src=»{{ image.thumb_url }}» alt=»{{ image.image_path }}» title=»{{ image.description }}» height=»200″/> </a> {# <p>{{ image.image_path }}</p>#} </li> {% endfor %} </ul> </div> </body> </html>

И вот что вышло.

Поиск по слову &quot;горнолыжники&quot;. Сгенерированные и переведенные описания здесь специально не скрыты, чтобы видеть качество поиска.
Поиск по слову «горнолыжники». Сгенерированные и переведенные описания здесь специально не скрыты, чтобы видеть качество поиска.

Еще примеры

Поиск по фразе &quot;птицы или звери&quot;
Поиск по фразе «птицы или звери»
&quot;туман в горах&quot;
«туман в горах»
Без трудностей перевода никуда. Поиск &quot;чайника&quot; приводит к чайкам.
Без трудностей перевода никуда. Поиск «чайника» приводит к чайкам.
Но &quot;пизанская башня&quot; - это всё же Пизанская башня
Но «пизанская башня» — это всё же Пизанская башня

Красота, однако!

По времени такой расклад:

  • Генерация описания на английском: примерно от 9 до 20 секунд

  • Перевод описания на русский язык: около двух секунд

  • Получение вектора по тексту: менее секунды

  • Поиск фото:

    • первый раз, пока модель разворачивается в GPU — около 10 секунд

    • при уже загруженной модели — примерно 2 секунды

Что бы еще такого сделать? Есть мысли:

  1. Найти получше модели генерации описания и перевода. Лучше всего, конечно, использовать одну русскоязычную модель генерации описания, если такая найдется.

  2. Сделать предварительную классификацию фото, хотя бы по EXIF, вынести ее на Web-интерфейс, чтобы искать, например, только среди прошлогодних фотографий, а не во всём архиве.

  3. Прикрутить распознавание знакомых лиц, и тоже вынести их в классификатор.

Источник: habr.com

✅ Найденные теги: новости, Разбираю

ОСТАВЬТЕ СВОЙ КОММЕНТАРИЙ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

Фото сгенерированных лиц: исследование показывает, что люди не могут отличить настоящие лица от сгенерированных
Нейросети построили капитализм за трое суток: 100 агентов Claude заперли…
Скетч: цифровой осьминог и виртуальный мир внутри компьютера с человечком.
Сцена с жестами пальцами, где один жест символизирует "VPN", а другой "KHP".
‼️Paramount купила Warner Bros. Discovery — сумма сделки составила безумные…
Скриншот репозитория GitHub "Claude Scientific Skills" AI для научных исследований.
Структура эффективного запроса Claude с элементами задачи, контекста и референса.
Эскиз и готовая веб-страница платформы для AI-дизайна в современном темном режиме.
ideipro logotyp
Image Not Found
Звёздное небо с галактиками и туманностями, космос, Вселенная, астрофотография.

Система оповещения обсерватории Рубина отправила 800 000 сигналов в первую ночь наблюдений.

Астрономы будут получать оповещения о небесных явлениях в течение нескольких минут после их обнаружения. Теренс О'Брайен, редактор раздела «Выходные». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной…

Мар 2, 2026
Женщина с длинными тёмными волосами в синем свете, нейтральный фон.

Расследование в отношении 61-фунтовой машины, которая «пожирает» пластик и выплевывает кирпичи.

Обзор компактного пресса для мягкого пластика Clear Drop — и что будет дальше. Шон Холлистер, старший редактор Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Мар 2, 2026
Черный углеродное волокно с текстурой плетения, отражающий свет.

Материал будущего: как работает «бессмертный» композит

Учёные из Университета штата Северная Каролина представили композит нового поколения, способный самостоятельно восстанавливаться после серьёзных повреждений.  Речь идёт о модифицированном армированном волокном полимере (FRP), который не просто сохраняет прочность при малом весе, но и способен «залечивать» внутренние…

Мар 2, 2026
Круглый экран с изображением замка и горы, рядом электронная плата.

Круглый дисплей Waveshare для креативных проектов

Круглый 7-дюймовый сенсорный дисплей от Waveshare создан для разработчиков и дизайнеров, которым нужен нестандартный экран.  Это IPS-панель с разрешением 1 080×1 080 пикселей, поддержкой 10-точечного ёмкостного сенсора, оптической склейкой и защитным закалённым стеклом, выполненная в круглом форм-факторе.…

Мар 2, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых