вектор поиска что это
Поиск элемента в std :: vector
Я хочу проверить, существует ли элемент в векторе или нет, чтобы я мог разобраться с каждым случаем.
Я наткнулся на эту формулу:
Я не понимаю, зачем нам нужен vector.end () в конце, не достаточно ли найти (vector.begin (), vector.end (), item), чтобы найти элемент?
Решение
вернет Итератор, поэтому, сравнивая его с
вы на самом деле проверяете, существует ли такой итератор с этим элементом.
Другие решения
Я не понимаю, зачем нам нужен vector.end () в конце, не так ли?
найти (vector.begin (), vector.end (), item) достаточно, чтобы найти элемент?
Результат find при невозможности найти совпадение — итератор, указывающий на конец предоставленного диапазона, и нет ничего особенно особенного в конечных итераторах, которые позволяют сравнивать их, как логическое состояние.
find был разработан, чтобы быть алгоритмом общего назначения, который работает с широким спектром контейнеров (включая даже те, которые потенциально находятся вне стандартной библиотеки). Он также предназначен просто для того, чтобы возвращать, был ли найден элемент или нет — он возвращает итератор, указывающий на элемент, если он найден. В результате, он не возвращает ноль или что-то в этом роде, если не может найти элемент. Возвращает итераторы.
Если вы действительно много делаете такого рода вещи (и я рекомендую делать что-то подобное, если вы делаете это, поскольку вы вводите что-то постороннее в ежедневный код), вы можете сделать что-то вроде этого:
Лингвистическая онтология WordNet
8.2. WordNet: применение в информационном поиске
Векторная модель информационного поиска с вектором по синсетам WordNet
Целью экспериментов была попытка выполнить поиск документов на основе не отдельных слов, а значений WordNet. Для каждого документа сначала выполняется процедура разрешения многозначности существительных, которая выбирает единственное значение и в результате которой каждому тексту ставится в соответствие вектор синсетов WordNet. После того как вектор создан, с ним могут выполняться такие же операции, как и с пословными векторами.
Эффективность использования векторов синсетов сравнивалась с эффективностью информационного поиска на основе стандартной модели, использующей вектора слов. В стандартном прогоне и документы, и запросы представляются как вектора лемм всех значимых слов. В концептуальных прогонах и документы, и запросы представляются как вектора, состоящие из трех подвекторов:
Второй и третий подвектора представляют собой альтернативные представления документа, поскольку одни и те же слова этого документа порождают отдельные элементы каждого вектора.
Для каждого запроса стандартный прогон векторной модели сравнивался со следующими комбинациями перечисленных выше подвекторов (цифры соответствуют весу, который дается 1-му, 2-му и 3-му подвектору, соответственно):
Для экспериментов было использовано 5 разных коллекций документов (компьютерная область, медицинская область, газетные статьи и др.), и для каждой коллекции было выполнено более 30 различных запросов.
Оценки эффективности информационного поиска на основе показателя средней точности показали серьезное ухудшение эффективности для векторов, включающих синсеты (от 6,2% до 42,3%).
Эксперименты по расширению запросов на основе отношений WordNet
Другая группа экспериментов по использованию WordNet в информационном поиске исследовала возможность расширения запросов синонимами или другими словами, связанными со словами запроса отношениями, которые описаны в WordNet. В таких экспериментах нет необходимости выбора единственного значения слова, что в случае ошибки привело бы к серьезному ухудшению результатов поиска.
Для экспериментов были использованы следующие соображения.
Во-вторых, чтобы смоделировать разрешение многозначности, запрос расширяется только теми словами, которые оказались в окрестностях расширения по крайней мере двух слов запроса.
Те слова, которые встретились по крайней мере в двух таких списках, добавляются к исходному запросу.
Авторы подчеркивают, что идея аппроксимации разрешения многозначности путем поиска повторов в списках расширения оказалась неэффективной в виду того, что чаще всего это метод приводил к добавлению в запрос очень общих слов, таких как «система».
Для того чтобы исключить из рассмотрения эффект лексической многозначности и исследовать возможности WordNet по расширению поискового запроса, были выполнены эксперименты с ручным выбором значения многозначных слов в запросе.
Исследовались четыре варианта векторов:
Тестирование проходило на двух типах вопросов: более длинной и более короткой версии. При поиске по полному запросу ни одной из комбинаций не удалось улучшить результаты поиска более чем на 2 процента. Короткие вопросы состояли из небольшого списка синсетов, например,
Выводы авторов эксперимента заключаются в том, что для успешного применения WordNet в информационном поиске необходимо значительно улучшить эффективность автоматического разрешения лексической многозначности, между тем парадигматических отношений в WordNet недостаточно для решения этой задачи.
Проект Meaning
Проект Meaning является продолжением проекта EuroWordNet. Авторы проекта мотивируют необходимость продолжения работ тем, что десятки человеко-лет были затрачены для создания ворднетов для разных языков, но этих усилий недостаточно, чтобы обеспечить качество многоязычных приложений компьютерной обработки текстов.
Прогресс в этой области связан с решением двух промежуточных задач: автоматическое разрешение лексической многозначности и масштабное обогащение лексических баз знаний.
Проблема, однако, заключается в том, что существуют взаимозависимые факторы:
В проекте планируется выполнить три последовательных цикла масштабного разрешения лексической многозначности и извлечения знаний для пяти европейских языков, включая баскский, испанский, итальянский, голландский и английский языки. Накопленные знания должны храниться в Многоязычном Центральном Репозитории.
Эксперименты по семантическому индексированию в рамках проекта Meaning
В рамках европейского проекта Meaning голландская компания Irion Technologies разработала технологию концептуального индексирования TwentyOne, комбинирующую лингвистический и статистический подходы. Авторы разработки считают, что неудачи с применением WordNet в информационно-поисковых приложениях связаны с трудностями встраивания такого рода лингвистических ресурсов в приложения, а также с проблемами оптимального использования содержащейся в ворднетах информации.
Основой технологии является статистическая машина поиска, базирующаяся на стандартной векторной модели и обеспечивающая быстрый поиск документов.
Лингвистические технологии используются для улучшения результатов, выданных статистической машиной, в двух направлениях:
Фраза представляет собой именную группу (noun phrase). Каждая фраза ассоциируется с отдельными словами, определенной комбинацией слов, а также комбинацией частей слов.
Система TwentyOne использует совокупность факторов для сравнения запроса с фразами текста:
Суть технологии в том, что сначала выдаются документы, которые имеют наибольшее совпадение по концептам фраз с запросом. Среди документов, имеющих одинаковое количество сопоставленных понятий между собственными фразами и запросом, первыми выдаются наиболее схожие по конкретному набору слов.
В проводимых экспериментах для сравнения были построены четыре индекса:
Нужно, однако, отметить, что описываемые результаты на основе пословного индекса значительно ниже, чем результаты других пословных систем на основе этой же коллекции. Иными словами, произведенные улучшения получаются по сравнению с заниженным недостаточно эффективным уровнем работы системы на основе пословного индекса, и значительные улучшения могли бы быть осуществлены еще в рамках такого индекса.
Найти что-то в векторе в C++?
Вектор C ++ не имеет функции-члена поиска. Однако в библиотеке алгоритмов есть функции find () разных типов, которые можно использовать для поиска чего-либо в векторе C ++. В библиотеке алгоритмов есть четыре группы функций find (), которые можно классифицировать как «Найти», «Найти конец», «Найти сначала» и «Найти по соседству».
Чтобы использовать библиотеки векторов и алгоритмов, программа на C ++ должна начинаться с:
using namespace std ;
В этом руководстве приведены основы поиска значения в векторе C ++. Весь код в этом руководстве находится в функции main (), если не указано иное. Если вектор состоит из строк, используйте строковый класс; и не используйте «const char *». В этом случае также должен быть включен строковый класс, например:
InputIterator find (сначала InputIterator, последним InputIterator, const T & value);
Следующий код использует эту функцию, чтобы узнать, входит ли цветок «Василек» в векторный список цветов:
using namespace std ;
if ( it == vtr. end ( ) )
cout «Flower was not found!» endl ;
else
cout «Flower found at index: « it — vtr. begin ( ) endl ;
Функция find () просматривает список векторов с самого начала. Если он не видит искомого значения, он достигнет конца вектора. Конец вектора — это официально vtr.end (), который находится сразу за последним элементом. Если он не видит искомого значения, он вернет итератор, указывающий на vtr.end ().
Значение, которое он ищет, может находиться в разных местах одного и того же вектора. Когда он видит первое из искомых значений, он останавливается на нем и возвращает итератор, указывающий на это значение.
Каждое значение в векторе имеет индекс. Первое значение имеет индекс 0, соответствующий vtr.begin (). Второе значение имеет индекс 1, соответствующий vtr.begin () + 1. Третье значение имеет индекс 2, соответствующий vtr.begin () + 2. Четвертое значение имеет индекс 3, соответствующий vtr.begin () + 3. ; и так далее. Итак, индекс первого найденного значения определяется как:
Чувствительность к регистру
Поиск в векторе чувствителен к регистру. Если бы значение, которое нужно было найти, было «CORNFLOWER» для указанной выше программы, оно не было бы найдено и была бы возвращена vtr.end ().
Range Within Limits
Диапазон не обязательно должен составлять весь вектор. Для приведенной выше программы диапазон мог быть от индекса 1 до индекса 4. То есть от «vtr.begin () + 1» до «vtr.end () — 4». «Vtr.end () — 4» получается вычитанием из обратного с учетом того, что vtr.end () находится сразу за самым последним элементом.
Когда весь список векторов является диапазоном, проверка того, является ли итератор возврата vtr.end (), показывает, было ли значение найдено или нет. Если итератором возврата является vtr.end (), это означает, что значение не было найдено. Теперь, когда диапазон меньше, если итератор возврата является последним элементом выбранного диапазона, это означает, что значение либо не было найдено, либо это последнее значение диапазона.
Примечание. Поиск останавливается на последнем элементе выбранного (меньшего) диапазона, если значение не было найдено в этом диапазоне или если найденное значение является последним элементом выбранного диапазона. Если найденное значение было этим последним элементом, будет возвращен итератор, указывающий на него. Если значение было найдено раньше, поиск остановится на этом элементе перед последним элементом выбранного диапазона. Итератор этого элемента будет возвращен.
Следующий код иллюстрирует эту схему:
using namespace std ;
if ( it == vtr. end ( ) )
cout «Flower was not found!» endl ;
else if ( it — vtr. begin ( ) == 4 ) < //last element in chosen range
if ( * it == string ( «Cornflower» ) )
cout «Flower found at index: « it — vtr. begin ( ) endl ;
else
cout «Flower was not found in range!» endl ;
>
else <
cout «Flower found at index: « it — vtr. begin ( ) endl ;
>
Теперь «Василек» имеет индекс 5, а «Kingcup» — индекс 4. Последний элемент в небольшом диапазоне, выбранном для поиска, — «Kingcup». Итак, соответствующее условие теста — «it — vtr.begin () == 4». Обратите внимание, что выражения «vtr.end () — 4» и «it — vtr.begin () == 4», каждое из которых имеет 4, являются просто совпадением.
Чтобы «Василек» был в малом диапазоне поиска, соответствующее условие проверки должно быть «it — vtr.begin () == 5». Следующий код иллюстрирует это:
using namespace std ;
if ( it == vtr. end ( ) )
cout «Flower was not found!» endl ;
else if ( it — vtr. begin ( ) == 5 ) <
if ( * it == string ( «Cornflower» ) )
cout «Flower found at index: « it — vtr. begin ( ) endl ;
else
cout «Flower was not found in range!» endl ;
>
else <
cout «Flower found at index: « it — vtr. begin ( ) endl ;
>
More Than One Occurrence
В следующей программе «Василек» встречается более чем в одном месте. Чтобы найти все индексы вхождений, используйте цикл while для продолжения поиска после предыдущего вхождения до конца (vtr.end ()) вектора. Программа:
using namespace std ;
Flower found at index : 5
Flower found at index : 7
Finding Integer
Вектор может состоять из целых чисел. Первое целочисленное значение можно найти с помощью функции find () (из библиотеки алгоритмов). Следующая программа иллюстрирует это:
using namespace std ;
if ( it == vtr. end ( ) )
cout «Number was not found!» endl ;
else
cout «Number found at index: « it — vtr. begin ( ) endl ;
Predicate
InputIterator find_if (сначала InputIterator, затем — InputIterator, предикат предиката);
Здесь используется функция find_if (), а не просто find (). Pred — это имя функции, которая дает критерии поиска. Этот третий аргумент принимает только имя функции, без аргументов и без скобок. Если функция-предикат принимает аргумент, тогда в определении функции указываются параметры для аргументов. Следующая программа иллюстрирует это, ища первое четное число в списке векторов:
using namespace std ;
bool fn ( int n ) <
if ( ( n % 2 ) == )
return true ;
else
return false ;
>
if ( it == vtr. end ( ) )
cout «Number was not found!» endl ;
else
cout «Number found at index: « it — vtr. begin ( ) endl ;
Обратите внимание, что был выполнен поиск по всему вектору с диапазоном «vtr.begin (), vtr.end ()».
Имя функции-предиката здесь — fn. Требуется один аргумент — целое число. Когда функция find_if () начинает сканирование вектора с первого элемента, она вызывает функцию предиката с каждым числом в векторе в качестве аргумента. Сканирование останавливается, когда он достигает первого элемента в векторе, для которого предикат возвращает истину.
Заключение
Функция find () в библиотеке алгоритмов существует в четырех категориях: «Найти», «Найти конец», «Найти сначала» и «Найти по соседству». Только категория «Найти» была объяснена выше, и в значительной степени. Приведенное выше объяснение является основой для всех функций find () в библиотеке алгоритмов. Функции Find () имеют дело с итераторами напрямую и косвенно с индексами. Программист должен знать, как преобразовать итератор в индексную и общую арифметику итератора, как показано выше.
Векторы в C++: для начинающих
Всем привет! До этого дня мы использовали чистые массивы. Чистые — это значит простые массивы, не имеющие у себя в багаже различных функций. В этом уроке мы пройдем нечистые массивы — векторы.
Быстрый переход по статье:
Что такое вектор (vector)
Вектор — это структура данных, которая уже является моделью динамического массива.
Давайте вспомним о том, что для создания динамического массива (вручную) нам нужно пользоваться конструктором new и вдобавок указателями. Но в случае с векторами всего этого делать не нужно.
Вообще, по стандарту пользоваться динамическим массивом через конструктор new — не есть правильно. Так как в компьютере могут происходить различные утечки памяти.
Как создать вектор (vector) в C++
Кстати, сейчас и в будущем мы будем использовать именно шаблон вектора. Например, очередь или стек, не созданные с помощью массива или вектора, тоже являются шаблонными.
Далее, чтобы объявить вектор, нужно пользоваться конструкцией ниже:
В примере выше мы создали вектор строк.
Кстати, заполнить вектор можно еще при инициализации (другие способы мы пройдем позже — в методах вектора). Делается это также просто, как и в массивах. Вот так:
После имени вектора ставим знак равенства и скобки, в которых через пробел указываем значение элементов.
Такой способ инициализации можно использовать только в C++!
Второй способ обратиться к ячейке
Но в C++ есть еще один способ это сделать благодаря функции — at(). В скобках мы должны указать индекс той ячейки, к которой нужно обратиться.
Вот как она работает на практике:
Давайте запустим эту программу:
Как указать количество ячеек для вектора
Указывать размер вектора можно по-разному. Можно это сделать еще при его инициализации, а можно хоть в самом конце программы. Вот, например, способ указать длину вектора на старте:
Так в круглых скобках () после имени вектора указываем первоначальную длину. А вот второй способ:
Вы можете задать логичный вопрос:»А в чем разница?». Давайте создадим два вектора и по-разному укажем их количество ячеек.
Как видим, в первом случае мы вывели три нуля, а во втором: 17, 0, 0.
Все потому, что при использовании первого способа все ячейки автоматически заполнились нулями.
При объявлении чего-либо (массива, вектора, переменной и т.д) мы выделяем определенное количество ячеек памяти, в которых уже хранится ненужный для ПК мусор. В нашем случае этим мусором являются числа.
Поэтому, когда мы вывели второй вектор, в нем уже находились какие-то рандомные числа — 17, 0, 0. Обычно они намного больше. Можете кстати попробовать создать переменную и вывести ее значение.
Нужно помнить! При использовании второго способа есть некоторый плюс — по времени. Так как для первого способа компилятор тратит время, чтобы заполнить все ячейки нулями.
Как сравнить два вектора
Если в середине программы нам понадобиться сравнить два массива, мы, конечно, используем цикл for и поочередно проверим все элементы.
Вектор снова на шаг впереди! Чтобы нам сравнить два вектора, потребуется применить всего лишь оператор ветвления if.
Как построить свою систему поиска похожих изображений
Представлюсь
Всем привет! Меня зовут Влад Виноградов, я руководитель отдела компьютерного зрения в компании EORA.AI. Мы занимаемся глубоким обучением уже более трех лет и за это время реализовали множество проектов для российских и международных клиентов в которые входила исследовательская часть и обучение моделей. В последнее время мы фокусируемся на решении задач поиска похожих изображений и на текущий момент создали системы поиска по логотипам, чертежам, мебели, одежде и другим товарам.
Данная статья содержит справочную информацию по зарекомендованным методам, применяемым в задаче Image Retireval. Прочитав статью, вы сможете построить систему поиска похожих изображений под вашу задачу с нуля (не включая процесс разработки production решения).
О задаче
Сейчас все активнее применяется подход «Поиск по фото», в частности, в e-commerce сервисах (AliExpress, Wildberries и др.). «Поиск по ключевому слову» (с пониманием контента изображений) уже давно осел в поисковых движках Google, Яндекс и пр., но вот до маркетплейсов и прочих частных поисковых систем еще не дошел. Думаю, с момента появления нашумевшего в кругах компьютерного зрения CLIP: Connecting Text and Images ускорится глобализация и этого подхода.
Поскольку наша команда специализируется на нейронных сетях в компьютерном зрении, в этой статье я сосредоточусь только на подходе «Поиск по фото».
Базовые компоненты сервиса
Шаг 2. Индексирование базы изображений. Индексирование представляет из себя прогон обученной модели на всех изображениях и запись эмбеддингов в специальный индекс для быстрого поиска.
Шаг 3. Поиск. По загруженному пользователем изображению делается прогон модели, получение эмбеддинга и сравнение данного эмбеддинга с остальными в базе. Результатом поиска является отсортированная по релевантности выдача.
Нейросети и Metric Learning
Функции ошибок
Contrastive Loss
Нейросеть штрафуется за отдаленность друг от друга эмбеддингов изображений p и q, если эти изображения на самом деле похожи. Аналогично, возникает штраф за близость эмбеддингов, изображения которых на самом деле непохожи друг на друга. При этом в последнем случае мы ставим границу m (например, 0.5), преодолев которую, мы считаем, что нейросеть справилась с задачей «разъединения» непохожих изображений.
Triplet Loss
Здесь мы нацелены на минимизацию расстояния от якоря до позитива и максимизацию расстояния от якоря до негатива. Впервые Triplet Loss был представлен в статье FaceNet от Google по распознаванию лиц и долгое время был state-of-the-art решением.
N-tupled Loss
Angular Additive Margin (ArcFace)
Основная мысль в том, чтобы добавить в обычную кросс-энтропию отступ m, который распределяет эмбеддинги изображений одного класса в районе центроиды этого класса так, чтобы все они были отделены от кластеров эмбеддингов других классов хотя бы на угол m.
Кажется, что это идеальная функция ошибки, особенно, когда посмотришь на бэнчмарк MegaFace. Но нужно иметь в виду, что она будет работать только при наличии классификационной разметки. Если у вас такой нет, придется работать с парными лоссами.
Здесь я визуально показываю, какие функции ошибок лучше всего применять при наличии одноклассовой и многоклассовой разметки (из последней можно вывести парную разметку путем подсчета доли пересечения между multilabel векторами примеров).
Пулинги
Вернемся к архитектуре нейросети и рассмотрим парочку pooling слоев, применяемых в задачах Image Retrieval
Итоговый дескриптор учитывает локальные особенности изображения при различных масштабах, тем самым создающий богатое признаковое описание. Этот дескриптор сам может являться эмбеддингом, поэтому его можно сразу отправить в функцию ошибки.
Ранжирование
Индексы
Самые популярные: отечественная NMSLIB, Spotify Annoy, Facebook Faiss, Google Scann. Также, если хочется взять индексирование с REST API «из коробки», можно рассмотреть приложение Jina.
Переранжирование
Исследователи в области Information Retrieval давно поняли, что упорядоченная поисковая выдача может быть улучшена неким способом переупорядочивания элементов после получения исходной выдачи.
Одним из таких методов является Query Expansion. Идея состоит в том, чтобы использовать top-k ближайших элементов для генерации нового эмбеддинга. В самом простом случае можно взять усредненный вектор, как показано на картинке выше. Также можно взвесить эмбеддинги, например, по отдаленности в выдаче или косинусному расстоянию от запроса. Подобные улучшения описаны в едином фреймворке в статье Attention-Based Query Expansion Learning. По желанию можно применить Query Expansion рекурсивно.
k-reciprocal
Валидация
Мы подошли к части проверки качества поиска похожих. В этой задаче есть много тонкостей, которые новичками могут быть не замечены в первое время работы над Image Retrieval проектом.
Метрики
precision@k
Показывает долю релевантных среди top-k ответов.
показывает, насколько система избирательна в построении top-k
очень чувствительна к числу релевантных для данного запроса, что не позволяет объективно оценить качество поиска, где для разных запросов имеется разное число релевантных примеров
достичь значение 1 возможно только, если число релевантных >= k для всех запросов
R-precision
То же самое, что precision@k, где k устанавливается равным числу релевантных к данному запросу
исчезает чувствительность к числу k в precision@k, метрика становится стабильной
приходится знать общее число релевантных к запросу (может быть проблемой, если не все релевантные размечены)
recall@k
Показывает, какая доля релевантных была найдена в top-k
отвечает на вопрос, найдены ли релевантные в принципе среди top-k
стабильна и хорошо усредняется по запросам
mAP (mean Average Precision)
Показывает насколько плотно мы заполняем топ выдачи релевантными примерами. Можно на это посмотреть как на объем информации, полученной пользователем поискового движка, который прочитал наименьшее число страниц. Соответственно, чем больше объем информации к числу прочитанных страниц, тем выше метрика.
объективная стабильная оценка качества поиска
является одно-численным представлением precision-recall кривой, которая сама по себе богата информацией для анализа
приходится знать общее число релевантных к запросу (может быть проблемой, если не все релевантные размечены)
Подробнее про метрики в Information Retrieval, в том числе посмотреть вывод mAP, можно почитать здесь.
nDCG (Normalized Discounted Gain)
Данная метрика показывает, насколько корректно упорядочены элементы в top-k между собой. Плюсы и минусы этой метрики не будем рассматривать, поскольку в нашем списке это единственная метрика, учитывающая порядок элементов. Тем не менее, есть исследования, показывающие, что при необходимости учитывать порядок данная метрика является достаточно стабильной и может подойти в большинстве случаев.
Усреднение
Также важно отметить варианты усреднения метрик по запросам. Рассмотрим два варианта:
macro: для каждого запроса считается метрика, усредняем по всем запросам
+: нет значительных колебаний при разном числе релевантных к данному запросу
-: все запросы рассматриваются как равноправные, даже если на какие-то больше релевантных, чем на другие
micro: число размеченных релевантных и отдельно успешно найденных релевантных суммируется по всем запросам, затем участвует в дроби соответствующей метрики
+: запросы оцениваются с учетом числа размеченных релевантных для каждого из них
-: метрика может стать сильно низкой / сильно высокой, если для какого-то запроса было очень много размеченных релевантных и система неуспешно / успешно вывела их в топ
Схемы валидации
Предлагаю рассмотреть два варианта валидации.
Валидация на множестве запросов и выбранных к ним релевантных
На вход: изображения-запросы и изображения, релевантные к ним. Имеется разметка в виде списка релевантных для данного запроса.
Для подсчета метрик можно посчитать матрицу релевантности каждый с каждым и, на основе бинарной информации о релевантности элементов посчитать метрики.
Валидация на полной базе
Для подсчета метрик можно пройтись по всем запросам, посчитать дистанции до всех элементов, включая релевантные и отправить в функцию вычисления метрики.
Пример реализованного проекта
Примеры работы нашей системы
Заключение
На этом все. Это был обзорный материал. Надеюсь, те, кто строит или собирается строить системы поиска похожих изображений, извлекли какую-то пользу. И наоборот, если считаете, что я где-то не прав, скажите в комментариях, буду рад обратной связи.