paint-brush
Использование pgvector для поиска сходств в корпоративных данныхк@johnjvester
805 чтения
805 чтения

Использование pgvector для поиска сходств в корпоративных данных

к John Vester14m2024/03/21
Read on Terminal Reader

Слишком долго; Читать

Используйте возможности векторов слов и поиска по сходству, чтобы обнаружить корреляции данных с корпоративными данными и помочь в маркетинговой кампании.
featured image - Использование pgvector для поиска сходств в корпоративных данных
John Vester HackerNoon profile picture
0-item
1-item


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


В этом примере я хотел сосредоточиться на том, как pgvector — программа поиска сходства векторов с открытым исходным кодом для Postgres — может использоваться для выявления сходства данных, существующих в корпоративных данных.

Простой вариант использования

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


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

Высокоуровневый дизайн

Этот вариант использования сосредоточен на выполнении поиска по сходству. Хотя это упражнение можно выполнить вручную, на ум приходит инструмент Wikipedia2Vec из-за предварительно обученных вложений, которые уже созданы для нескольких языков. Вложения слов, также известные как векторы, представляют собой числовые представления слов, которые содержат как их синтаксическую, так и семантическую информацию. Представляя слова в виде векторов, мы можем математически определить, какие слова семантически «ближе» к другим.


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


Расширению pgvector требуется база данных Postgres. Однако корпоративные данные для нашего примера в настоящее время находятся в Salesforce. К счастью, Heroku Connect предоставляет простой способ синхронизации учетных записей Salesforce с Heroku Postgres, сохраняя их в таблице под названием salesforce.account . Затем у нас будет еще одна таблица под названием salesforce.industries , которая содержит каждую отрасль в Salesforce (в виде ключа VARCHAR) вместе со связанным с ней вектором слов.


Используя данные Salesforce и векторы слов в Postgres, мы создадим RESTful API с использованием Java и Spring Boot. Этот сервис выполнит необходимый запрос и вернет результаты в формате JSON.


Мы можем проиллюстрировать общий вид решения следующим образом:


Исходный код будет находиться в GitLab. Выполнение команды git push heroku инициирует развертывание в Heroku, предоставляя RESTful API, который маркетинговая команда может легко использовать.

Создание решения

Имея общий проект, мы можем приступить к созданию решения. Используя свой логин в Salesforce, я смог перейти на экран «Учетные записи» и просмотреть данные для этого упражнения. Вот пример первой страницы данных предприятия:


Создайте приложение Heroku

Для этой цели я планировал использовать Heroku для решения запроса маркетинговой команды. Я вошел в свою учетную запись Heroku и использовал кнопку «Создать новое приложение» , чтобы создать новое приложение под названием similarity-search-sfdc :


После создания приложения я перешел на вкладку «Ресурсы» и нашел надстройку Heroku Postgres. Я набрал «Postgres» в поле поиска дополнений.


Выбрав Heroku Postgres из списка, я выбрал план Standard 0 , но pgvector доступен в предложениях баз данных уровня Standard (или выше), работающих под управлением PostgreSQL 15 или бета-версии базы данных Essential-уровня .


Когда я подтвердил дополнение, Heroku сгенерировал и предоставил строку подключения DATABASE_URL . Я нашел это в разделе Config Vars на вкладке Settings моего приложения. Я использовал эту информацию для подключения к своей базе данных и включения расширения pgvector следующим образом:


 CREATE EXTENSION vector;


Затем я поискал и нашел дополнение Heroku Connect . Я знал, что это даст мне простой способ подключения к корпоративным данным в Salesforce.


Для этого упражнения бесплатный план Demo Edition отлично подойдет.


На этом этапе вкладка «Ресурсы» приложения similarity-search-sfdc выглядела следующим образом:


Я следовал инструкциям « Настройка Heroku Connect », чтобы связать свою учетную запись Salesforce с Heroku Connect. Затем я выбрал объект «Учетная запись» для синхронизации. После завершения я смог увидеть одни и те же данные учетной записи Salesforce в Heroku Connect и в базовой базе данных Postgres.


С точки зрения SQL то, что я сделал, привело к созданию таблицы salesforce.account следующего вида:


 create table salesforce.account ( createddate timestamp, isdeleted boolean, name varchar(255), systemmodstamp timestamp, accountnumber varchar(40), industry varchar(255), sfid varchar(18), id serial primary key, _hc_lastop varchar(32), _hc_err text );


Генерировать векторы

Чтобы поиск по сходству работал должным образом, мне нужно было сгенерировать векторы слов для каждой отрасли учетной записи Salesforce:


  • Одежда
  • Банковское дело
  • Биотехнология
  • Строительство
  • Образование
  • Электроника
  • Инженерное дело
  • Развлечение
  • Еда и напитки
  • Финансы
  • Правительство
  • Здравоохранение
  • Гостеприимство
  • Страхование
  • СМИ
  • Не для прибыли
  • Другой
  • Отдых
  • Розничная торговля
  • Перевозки
  • Технологии
  • Телекоммуникации
  • Транспорт
  • Утилиты


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


Чтобы упростить это упражнение, я выполнил эту задачу вручную, используя Python 3.9 и файл embed.py , который выглядит следующим образом:


 from wikipedia2vec import Wikipedia2Vec wiki2vec = Wikipedia2Vec.load('enwiki_20180420_100d.pkl') print(wiki2vec.get_word_vector('software').tolist())


Обратите внимание: метод get_word_vector() предполагает представление отрасли в нижнем регистре.


Запуск python embed.py сгенерировал следующий вектор слов для software слова:


 [-0.40402618050575256, 0.5711150765419006, -0.7885153293609619, -0.15960034728050232, -0.5692323446273804, 0.005377458408474922, -0.1315757781267166, -0.16840921342372894, 0.6626015305519104, -0.26056772470474243, 0.3681095242500305, -0.453583300113678, 0.004738557618111372, -0.4111144244670868, -0.1817493587732315, -0.9268549680709839, 0.07973367720842361, -0.17835664749145508, -0.2949991524219513, -0.5533796548843384, 0.04348105192184448, -0.028855713084340096, -0.13867013156414032, -0.6649054884910583, 0.03129105269908905, -0.24817068874835968, 0.05968991294503212, -0.24743635952472687, 0.20582349598407745, 0.6240783929824829, 0.3214546740055084, -0.14210252463817596, 0.3178422152996063, 0.7693028450012207, 0.2426985204219818, -0.6515568494796753, -0.2868216037750244, 0.3189859390258789, 0.5168254971504211, 0.11008890718221664, 0.3537853956222534, -0.713259220123291, -0.4132286608219147, -0.026366405189037323, 0.003034653142094612, -0.5275223851203918, -0.018167126923799515, 0.23878540098667145, -0.6077089905738831, 0.5368344187736511, -0.1210874393582344, 0.26415619254112244, -0.3066694438457489, 0.1471938043832779, 0.04954215884208679, 0.2045321762561798, 0.1391817331314087, 0.5286830067634583, 0.5764685273170471, 0.1882934868335724, -0.30167853832244873, -0.2122340053319931, -0.45651525259017944, -0.016777794808149338, 0.45624101161956787, -0.0438646525144577, -0.992512047290802, -0.3771328926086426, 0.04916151612997055, -0.5830298066139221, -0.01255014631897211, 0.21600870788097382, -0.18419665098190308, 0.1754663586616516, -0.1499166339635849, -0.1916201263666153, -0.22884036600589752, 0.17280352115631104, 0.25274306535720825, 0.3511175513267517, -0.20270302891731262, -0.6383468508720398, 0.43260180950164795, -0.21136239171028137, -0.05920517444610596, 0.7145522832870483, 0.7626600861549377, -0.5473887920379639, 0.4523043632507324, -0.1723199188709259, -0.10209759324789047, -0.5577948093414307, -0.10156919807195663, 0.31126976013183594, 0.3604489266872406, -0.13295558094978333, 0.2473849356174469, 0.278846800327301, -0.28618067502975464, 0.00527254119515419]


Создать таблицу для отраслей

Чтобы сохранить векторы слов, нам нужно было добавить таблицу industries в базу данных Postgres, используя следующую команду SQL:


 create table salesforce.industries ( name varchar not null constraint industries_pk primary key, embeddings vector(100) not null );


После создания таблицы industries мы вставим каждый из сгенерированных векторов слов. Мы делаем это с помощью операторов SQL, подобных следующим:


 INSERT INTO salesforce.industries (name, embeddings) VALUES ('Software','[-0.40402618050575256, 0.5711150765419006, -0.7885153293609619, -0.15960034728050232, -0.5692323446273804, 0.005377458408474922, -0.1315757781267166, -0.16840921342372894, 0.6626015305519104, -0.26056772470474243, 0.3681095242500305, -0.453583300113678, 0.004738557618111372, -0.4111144244670868, -0.1817493587732315, -0.9268549680709839, 0.07973367720842361, -0.17835664749145508, -0.2949991524219513, -0.5533796548843384, 0.04348105192184448, -0.028855713084340096, -0.13867013156414032, -0.6649054884910583, 0.03129105269908905, -0.24817068874835968, 0.05968991294503212, -0.24743635952472687, 0.20582349598407745, 0.6240783929824829, 0.3214546740055084, -0.14210252463817596, 0.3178422152996063, 0.7693028450012207, 0.2426985204219818, -0.6515568494796753, -0.2868216037750244, 0.3189859390258789, 0.5168254971504211, 0.11008890718221664, 0.3537853956222534, -0.713259220123291, -0.4132286608219147, -0.026366405189037323, 0.003034653142094612, -0.5275223851203918, -0.018167126923799515, 0.23878540098667145, -0.6077089905738831, 0.5368344187736511, -0.1210874393582344, 0.26415619254112244, -0.3066694438457489, 0.1471938043832779, 0.04954215884208679, 0.2045321762561798, 0.1391817331314087, 0.5286830067634583, 0.5764685273170471, 0.1882934868335724, -0.30167853832244873, -0.2122340053319931, -0.45651525259017944, -0.016777794808149338, 0.45624101161956787, -0.0438646525144577, -0.992512047290802, -0.3771328926086426, 0.04916151612997055, -0.5830298066139221, -0.01255014631897211, 0.21600870788097382, -0.18419665098190308, 0.1754663586616516, -0.1499166339635849, -0.1916201263666153, -0.22884036600589752, 0.17280352115631104, 0.25274306535720825, 0.3511175513267517, -0.20270302891731262, -0.6383468508720398, 0.43260180950164795, -0.21136239171028137, -0.05920517444610596, 0.7145522832870483, 0.7626600861549377, -0.5473887920379639, 0.4523043632507324, -0.1723199188709259, -0.10209759324789047, -0.5577948093414307, -0.10156919807195663, 0.31126976013183594, 0.3604489266872406, -0.13295558094978333, 0.2473849356174469, 0.278846800327301, -0.28618067502975464, 0.00527254119515419] ');


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


Как только все сгенерированные векторы слов будут добавлены в таблицу industries , мы можем переключить внимание на внедрение RESTful API.

Внедрение службы Spring Boot

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


Затем, используя Spring Boot 3.2.2 и Java (temurin) 17, я создал проект similarity-search-sfdc в IntelliJ IDEA со следующими зависимостями Maven:


 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.pgvector</groupId> <artifactId>pgvector</artifactId> <version>0.1.4</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>


Я создал упрощенные сущности как для объекта «Учетная запись », так и для объекта « Промышленность » (встраивание), которые соответствовали таблицам базы данных Postgres, созданным ранее.


 @AllArgsConstructor @NoArgsConstructor @Data @Entity @Table(name = "account", schema = "salesforce") public class Account { @Id @Column(name = "sfid") private String id; private String name; private String industry; } @AllArgsConstructor @NoArgsConstructor @Data @Entity @Table(name = "industries", schema = "salesforce") public class Industry { @Id private String name; }


Используя интерфейс JpaRepository, я добавил следующие расширения, обеспечивающие легкий доступ к таблицам Postgres:


 public interface AccountsRepository extends JpaRepository<Account, String> { @Query(nativeQuery = true, value = "SELECT sfid, name, industry " + "FROM salesforce.account " + "WHERE industry IN (SELECT name " + " FROM salesforce.industries " + " WHERE name != :industry " + " ORDER BY embeddings <-> (SELECT embeddings FROM salesforce.industries WHERE name = :industry) " + " LIMIT :limit)" + "ORDER BY name") Set<Account> findSimilaritiesForIndustry(String industry, int limit); } public interface IndustriesRepository extends JpaRepository<Industry, String> { }


Обратите внимание, что вся тяжелая работа по решению этого варианта использования будет выполняться в методе findSimilaritiesForIndustry() . Метод будет принимать следующие параметры:


  • industry : отрасль, в которой нужно найти сходство
  • limit : максимальное количество отраслевых сходств для поиска при запросе учетных записей.


Обратите внимание на оператор евклидова расстояния (<->) в нашем запросе выше. Это встроенный в расширение оператор поиска по сходству.


С исходным вариантом использования в отрасли «Программное обеспечение» и ограничением, установленным для трех ближайших отраслей, выполняемый запрос будет выглядеть следующим образом:


 SELECT sfid, name, industry FROM salesforce.account WHERE industry IN (SELECT name FROM salesforce.industries WHERE name != 'Software' ORDER BY embeddings <-> (SELECT embeddings FROM salesforce.industries WHERE name = 'Software') LIMIT 3) ORDER BY name;


На основе этого я создал класс AccountsService для взаимодействия с репозиториями JPA:


 @RequiredArgsConstructor @Service public class AccountsService { private final AccountsRepository accountsRepository; private final IndustriesRepository industriesRepository; public Set<Account> getAccountsBySimilarIndustry(String industry, int limit) throws Exception { List<Industry> industries = industriesRepository.findAll(); if (industries .stream() .map(Industry::getName) .anyMatch(industry::equals)) { return accountsRepository .findSimilaritiesForIndustry(industry, limit); } else { throw new Exception( "Could not locate '" + industry + "' industry"); } } }


Наконец, я попросил класс AccountsController предоставить точку входа RESTful и подключиться к AccountsService :


 @RequiredArgsConstructor @RestController @RequestMapping(value = "/accounts") public class AccountsController { private final AccountsService accountsService; @GetMapping(value = "/similarities") public ResponseEntity<Set<Account>> getAccountsBySimilarIndustry(@RequestParam String industry, @RequestParam int limit) { try { return new ResponseEntity<>( accountsService .getAccountsBySimilarIndustry(industry, limit), HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } }


Развертывание в Heroku

Когда сервис Spring Boot был готов, я добавил в проект следующий Procfile , позволяющий Heroku узнать больше о нашем сервисе:


 web: java $JAVA_OPTS -Dserver.port=$PORT -jar target/*.jar


На всякий случай я добавил файл system.properties , чтобы указать, какие версии Java и Maven ожидаются:


 java.runtime.version=17 maven.version=3.9.5


Используя интерфейс командной строки Heroku, я добавил в свой репозиторий GitLab удаленный сервис для службы similarity-search-sfdc на платформе Heroku:


 heroku git:remote -a similarity-search-sfdc


Я также установил тип пакета сборки для службы similarity-search-sfdc с помощью следующей команды:


 heroku buildpacks:set https://github.com/heroku/heroku-buildpack-java


Наконец, я развернул сервис similarity-search-sfdc в Heroku, используя следующую команду:


 git push heroku


Теперь вкладка «Ресурсы» для приложения similarity-search-sfdc появилась, как показано ниже:


Поиск по сходству в действии

При работающем RESTful API я ввел следующую команду cURL, чтобы найти три ведущие отрасли Salesforce (и связанные с ними учетные записи), наиболее близкие к индустрии программного обеспечения :


 curl --location 'https://HEROKU-APP-ROOT-URL/accounts/similarities?industry=Software&limit=3'


RESTful API возвращает статус ответа 200 OK HTTP вместе со следующей полезной нагрузкой:


 [ { "id": "001Kd00001bsP80IAE", "name": "CleanSlate Technology Group", "industry": "Technology" }, { "id": "001Kd00001bsPBFIA2", "name": "CMG Worldwide", "industry": "Media" }, { "id": "001Kd00001bsP8AIAU", "name": "Dev Spotlight", "industry": "Technology" }, { "id": "001Kd00001bsP8hIAE", "name": "Egghead", "industry": "Electronics" }, { "id": "001Kd00001bsP85IAE", "name": "Marqeta", "industry": "Technology" } ]


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


Теперь у отдела маркетинга есть список аккаунтов, с которыми они могут связаться для проведения следующей кампании.

Заключение

Несколько лет назад я провел больше времени, чем мне хотелось бы признать, играя в многопользовательскую видеоигру Team Fortress 2 . Вот скриншот с очень веселого мероприятия 2012 года:


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


Я чувствую, что инженеры-программисты — это «солдатский класс» реального мира, потому что мы можем адаптироваться к любой ситуации и сосредоточиться на предоставлении решений, которые эффективно отвечают ожиданиям.


Вот уже несколько лет я сосредоточен на следующей формулировке миссии, которая, по моему мнению, может быть применима к любому ИТ-специалисту:


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

- Дж. Вестер


В примере для этой статьи мы смогли использовать Heroku Connect для синхронизации корпоративных данных с базой данных Postgres. После установки расширения pgvector мы создали векторы слов для каждой уникальной отрасли из этих учетных записей Salesforce. Наконец, мы представили службу Spring Boot, которая упростила процесс поиска учетных записей Salesforce, отрасль которых наиболее близка к другой отрасли.


Мы быстро решили этот вариант использования с помощью существующих технологий с открытым исходным кодом, добавления крошечного сервиса Spring Boot и Heroku PaaS – полностью следуя моей формулировке миссии. Я не могу себе представить, сколько времени потребовалось бы без этих фреймворков, продуктов и услуг.


Если вам интересно, вы можете найти исходный код этой статьи на GitLab:


https://gitlab.com/johnjvester/similarity-search-sfdc


Хорошего дня!