paint-brush
Verwenden von pgvector zum Auffinden von Ähnlichkeiten in Unternehmensdatenvon@johnjvester
805 Lesungen
805 Lesungen

Verwenden von pgvector zum Auffinden von Ähnlichkeiten in Unternehmensdaten

von John Vester14m2024/03/21
Read on Terminal Reader

Zu lang; Lesen

Nutzen Sie die Leistungsfähigkeit von Wortvektoren und Ähnlichkeitssuchen, um Datenkorrelationen mit Unternehmensdaten zu ermitteln und so eine Marketingkampagne zu unterstützen.
featured image - Verwenden von pgvector zum Auffinden von Ähnlichkeiten in Unternehmensdaten
John Vester HackerNoon profile picture
0-item
1-item


Softwareentwickler nehmen einen spannenden Platz in dieser Welt ein. Unabhängig vom Tech-Stack oder der Branche haben wir die Aufgabe, Probleme zu lösen, die direkt zu den Zielen und Absichten unserer Arbeitgeber beitragen. Als Bonus können wir Technologie nutzen, um alle Herausforderungen zu bewältigen, die in unser Fadenkreuz geraten.


In diesem Beispiel wollte ich mich darauf konzentrieren, wie pgvector – eine Open-Source-Vektorähnlichkeitssuche für Postgres – verwendet werden kann, um in Unternehmensdaten vorhandene Datenähnlichkeiten zu identifizieren.

Ein einfacher Anwendungsfall

Nehmen wir als einfaches Beispiel an, dass die Marketingabteilung Unterstützung für eine Kampagne benötigt, die sie starten möchte. Das Ziel besteht darin, alle Salesforce- Konten zu erreichen, die in Branchen angesiedelt sind, die eng mit der Softwarebranche verbunden sind.


Am Ende möchten sie sich auf Konten in den drei ähnlichsten Branchen konzentrieren und dieses Tool in Zukunft nutzen, um Ähnlichkeiten für andere Branchen zu finden. Wenn möglich, möchten sie die Möglichkeit haben, die gewünschte Anzahl passender Branchen bereitzustellen, anstatt immer die ersten drei zurückzugeben.

Hochwertiges Design

Bei diesem Anwendungsfall geht es um die Durchführung einer Ähnlichkeitssuche. Obwohl es möglich ist, diese Übung manuell durchzuführen, fällt mir aufgrund der vorab trainierten Einbettungen, die bereits für mehrere Sprachen erstellt wurden, das Tool Wikipedia2Vec ein. Worteinbettungen – auch Vektoren genannt – sind numerische Darstellungen von Wörtern, die sowohl deren syntaktische als auch semantische Informationen enthalten. Durch die Darstellung von Wörtern als Vektoren können wir mathematisch bestimmen, welche Wörter anderen semantisch „näher“ sind.


In unserem Beispiel hätten wir auch ein einfaches Python-Programm schreiben können, um Wortvektoren für jede in Salesforce konfigurierte Branche zu erstellen.


Die pgvector Erweiterung erfordert eine Postgres-Datenbank. Allerdings befinden sich die Unternehmensdaten für unser Beispiel derzeit in Salesforce. Glücklicherweise bietet Heroku Connect eine einfache Möglichkeit, die Salesforce-Konten mit Heroku Postgres zu synchronisieren und sie in einer Tabelle namens salesforce.account zu speichern. Dann haben wir eine weitere Tabelle namens salesforce.industries , die jede Branche in Salesforce (als VARCHAR-Schlüssel) zusammen mit dem zugehörigen Wortvektor enthält.


Mit den Salesforce-Daten und Wortvektoren in Postgres erstellen wir eine RESTful-API mit Java und Spring Boot. Dieser Dienst führt die erforderliche Abfrage durch und gibt die Ergebnisse im JSON-Format zurück.


Wir können die allgemeine Ansicht der Lösung wie folgt veranschaulichen:


Der Quellcode befindet sich in GitLab. Die Ausgabe eines git push heroku -Befehls löst eine Bereitstellung in Heroku aus und führt eine RESTful-API ein, die das Marketingteam problemlos nutzen kann.

Die Lösung entwickeln

Sobald das High-Level-Design vorliegt, können wir mit der Entwicklung einer Lösung beginnen. Mit meinem Salesforce-Login konnte ich zum Bildschirm „Konten“ navigieren, um die Daten für diese Übung anzuzeigen. Hier ist ein Beispiel für die erste Seite mit Unternehmensdaten:


Erstellen Sie eine Heroku-App

Zu diesem Zweck wollte ich Heroku verwenden, um die Anfrage des Marketingteams zu lösen. Ich habe mich bei meinem Heroku-Konto angemeldet und über die Schaltfläche „Neue App erstellen“ eine neue Anwendung namens similarity-search-sfdc erstellt:


Nachdem ich die App erstellt hatte, navigierte ich zur Registerkarte „Ressourcen“ , um das Heroku-Postgres-Add-on zu finden. Ich habe „Postgres“ in das Add-On-Suchfeld eingegeben.


Nachdem ich Heroku Postgres aus der Liste ausgewählt hatte, entschied ich mich für den Plan Standard 0 , aber pgvector ist für Datenbankangebote der Standardstufe (oder höher) verfügbar, auf denen PostgreSQL 15 oder die Beta-Datenbank der Essential-Stufe ausgeführt wird .


Als ich das Add-on bestätigte, generierte Heroku eine DATABASE_URL Verbindungszeichenfolge und stellte sie bereit. Ich habe dies im Abschnitt „Config Vars“ der Registerkarte „Einstellungen“ meiner App gefunden. Ich habe diese Informationen verwendet, um eine Verbindung zu meiner Datenbank herzustellen und die pgvector-Erweiterung wie folgt zu aktivieren:


 CREATE EXTENSION vector;


Als nächstes habe ich nach dem Heroku Connect- Add-on gesucht und es gefunden. Ich wusste, dass mir dies eine einfache Möglichkeit bieten würde, eine Verbindung zu den Unternehmensdaten in Salesforce herzustellen.


Für diese Übung funktioniert der kostenlose Demo-Edition -Plan einwandfrei.


Zu diesem Zeitpunkt sah die Registerkarte „Ressourcen“ für die App similarity-search-sfdc folgendermaßen aus:


Ich habe die Anweisungen zum Einrichten von Heroku Connect befolgt, um mein Salesforce-Konto mit Heroku Connect zu verknüpfen. Dann habe ich das Kontoobjekt für die Synchronisierung ausgewählt. Nach Abschluss konnte ich dieselben Salesforce-Kontodaten in Heroku Connect und in der zugrunde liegenden Postgres-Datenbank sehen.


Aus SQL-Sicht führte meine Vorgehensweise zur Erstellung einer salesforce.account Tabelle mit dem folgenden Design:


 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 );


Vektoren generieren

Damit die Ähnlichkeitssuche wie erwartet funktionierte, musste ich Wortvektoren für jede Salesforce-Kontobranche generieren:


  • Bekleidung
  • Bankwesen
  • Biotechnologie
  • Konstruktion
  • Ausbildung
  • Elektronik
  • Maschinenbau
  • Unterhaltung
  • Essen & Getränke
  • Finanzen
  • Regierung
  • Gesundheitspflege
  • Gastfreundschaft
  • Versicherung
  • Medien
  • Nicht gewinnorientiert
  • Andere
  • Erholung
  • Einzelhandel
  • Versand
  • Technologie
  • Telekommunikation
  • Transport
  • Dienstprogramme


Da der primäre Anwendungsfall die Notwendigkeit anzeigte, Ähnlichkeiten für die Softwarebranche zu finden, müssten wir auch für diese Branche einen Wortvektor generieren.


Um die Dinge für diese Übung einfach zu halten, habe ich diese Aufgabe manuell mit Python 3.9 und einer Datei namens embed.py ausgeführt, die so aussieht:


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


Bitte beachten Sie, dass die Methode get_word_vector() eine Darstellung der Branche in Kleinbuchstaben erwartet.


Beim Ausführen von python embed.py wurde der folgende Wortvektor für das software generiert:


 [-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]


Erstellen Sie eine Tabelle für Branchen

Um die Wortvektoren zu speichern, mussten wir mit dem folgenden SQL-Befehl eine industries zur Postgres-Datenbank hinzufügen:


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


Nachdem die industries erstellt wurde, fügen wir jeden der generierten Wortvektoren ein. Wir tun dies mit SQL-Anweisungen, die den folgenden ähneln:


 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] ');


Bitte beachten Sie: Während wir einen Wortvektor mit der kleingeschriebenen Darstellung der Softwarebranche (Software) erstellt haben, muss die Spalte industries.name mit dem großgeschriebenen Branchennamen (Software) übereinstimmen.


Sobald alle generierten Wortvektoren zur industries hinzugefügt wurden, können wir unseren Fokus auf die Einführung einer RESTful-API verlagern.

Führen Sie einen Spring Boot-Dienst ein

Dies war der Punkt, an dem meine Leidenschaft als Softwareentwickler auf Hochtouren kam, weil ich alles hatte, um die anstehende Herausforderung zu lösen.


Als Nächstes habe ich mit Spring Boot 3.2.2 und Java (Temurin) 17 das similarity-search-sfdc Projekt in IntelliJ IDEA mit den folgenden Maven-Abhängigkeiten erstellt:


 <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>


Ich habe vereinfachte Entitäten sowohl für das Kontoobjekt als auch für das Branchenobjekt (Einbettung) erstellt, die mit den zuvor erstellten Postgres-Datenbanktabellen übereinstimmten.


 @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; }


Mithilfe der JpaRepository-Schnittstelle habe ich die folgenden Erweiterungen hinzugefügt, um einen einfachen Zugriff auf die Postgres-Tabellen zu ermöglichen:


 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> { }


Beachten Sie, dass in der Methode findSimilaritiesForIndustry() die ganze schwere Arbeit zur Lösung dieses Anwendungsfalls stattfindet. Die Methode akzeptiert die folgenden Parameter:


  • industry : die Branche, für die Ähnlichkeiten gefunden werden sollen
  • limit : die maximale Anzahl von Branchenähnlichkeiten, nach denen bei der Abfrage von Konten gesucht werden soll


Beachten Sie den euklidischen Distanzoperator (<->) in unserer obigen Abfrage. Dies ist der integrierte Operator der Erweiterung zum Durchführen der Ähnlichkeitssuche.


Mit dem ursprünglichen Anwendungsfall „Software“ und einer Beschränkung auf die drei nächstgelegenen Branchen würde die ausgeführte Abfrage wie folgt aussehen:


 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;


Von dort aus habe ich die AccountsService Klasse erstellt, um mit den JPA-Repositorys zu interagieren:


 @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"); } } }


Zuletzt ließ ich die AccountsController Klasse einen RESTful-Einstiegspunkt bereitstellen und eine Verbindung zum AccountsService herstellen:


 @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); } } }


Bereitstellung für Heroku

Nachdem der Spring Boot-Dienst bereit war, habe ich dem Projekt die folgende Procfile hinzugefügt, um Heroku mehr über unseren Dienst zu informieren:


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


Aus Sicherheitsgründen habe ich die Datei system.properties hinzugefügt, um anzugeben, welche Versionen von Java und Maven erwartet werden:


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


Mit der Heroku-CLI habe ich meinem GitLab-Repository einen Remote-Dienst für den similarity-search-sfdc -Dienst zur Heroku-Plattform hinzugefügt:


 heroku git:remote -a similarity-search-sfdc


Ich habe außerdem den Buildpack-Typ für den Dienst similarity-search-sfdc über den folgenden Befehl festgelegt:


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


Abschließend habe ich den Dienst similarity-search-sfdc mit dem folgenden Befehl für Heroku bereitgestellt:


 git push heroku


Nun wurde die Registerkarte „Ressourcen“ für die App similarity-search-sfdc wie folgt angezeigt:


Ähnlichkeitssuche in Aktion

Während die RESTful-API ausgeführt wurde, habe ich den folgenden cURL-Befehl ausgegeben, um die drei wichtigsten Salesforce-Branchen (und zugehörigen Konten) zu finden, die der Softwarebranche am nächsten kommen:


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


Die RESTful-API gibt den 200 OK HTTP zusammen mit der folgenden Nutzlast zurück:


 [ { "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" } ]


Daher sind die Branchen Technologie , Medien und Elektronik in diesem Beispiel die Branchen, die der Softwarebranche am nächsten kommen.


Jetzt verfügt die Marketingabteilung über eine Liste von Kunden, an die sie sich für ihre nächste Kampagne wenden kann.

Abschluss

Vor Jahren habe ich mehr Zeit mit dem Multiplayer-Videospiel Team Fortress 2 verbracht, als ich zugeben möchte. Hier ist ein Screenshot von einer Veranstaltung im Jahr 2012, die viel Spaß gemacht hat:


Diejenigen, die mit diesem Aspekt meines Lebens vertraut sind, könnten Ihnen sagen, dass meine bevorzugte Spielerklasse der Soldat war. Dies liegt daran, dass der Soldat über das beste Gleichgewicht zwischen Gesundheit, Bewegung, Geschwindigkeit und Feuerkraft verfügt.


Ich bin der Meinung, dass Softwareentwickler die „Soldatenklasse“ der realen Welt sind, weil wir uns an jede Situation anpassen und uns darauf konzentrieren können, Lösungen bereitzustellen, die die Erwartungen auf effiziente Weise erfüllen.


Seit einigen Jahren konzentriere ich mich auf das folgende Leitbild, das meiner Meinung nach auf jeden IT-Experten anwendbar ist:


„Konzentrieren Sie Ihre Zeit auf die Bereitstellung von Features/Funktionalitäten, die den Wert Ihres geistigen Eigentums steigern. Nutzen Sie Frameworks, Produkte und Services für alles andere.“

- J. Vester


Im Beispiel für diesen Beitrag konnten wir Heroku Connect nutzen, um Unternehmensdaten mit einer Postgres-Datenbank zu synchronisieren. Nach der Installation der pgvector-Erweiterung haben wir aus diesen Salesforce-Konten Wortvektoren für jede einzelne Branche erstellt. Schließlich haben wir einen Spring Boot-Dienst eingeführt, der das Auffinden von Salesforce-Konten vereinfacht, deren Branche einer anderen Branche am nächsten kommt.


Wir haben diesen Anwendungsfall schnell mit vorhandenen Open-Source-Technologien, der Hinzufügung eines winzigen Spring Boot-Dienstes und dem Heroku PaaS gelöst – ganz im Einklang mit meinem Leitbild. Ich kann mir nicht vorstellen, wie viel Zeit ohne diese Frameworks, Produkte und Dienstleistungen erforderlich wäre.


Bei Interesse finden Sie den Original-Quellcode für diesen Artikel auf GitLab:


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


Ich wünsche Ihnen einen wirklich tollen Tag!