소프트웨어 엔지니어는 이 세상에서 흥미로운 위치를 차지하고 있습니다. 기술 스택이나 산업에 관계없이 우리는 고용주의 목표와 목표에 직접적으로 기여하는 문제를 해결하는 임무를 맡고 있습니다. 보너스로 우리는 기술을 사용하여 십자선에 부딪히는 모든 문제를 완화할 수 있습니다.
이 예에서는 Postgres의 오픈 소스 벡터 유사성 검색인 pgVector를 사용하여 기업 데이터에 존재하는 데이터 유사성을 식별하는 방법에 중점을 두고 싶었습니다.
간단한 예로, 마케팅 부서에서 출시하려는 캠페인에 대한 지원이 필요하다고 가정해 보겠습니다. 목표는 소프트웨어 산업과 긴밀하게 연계된 산업에 속한 모든 Salesforce 계정에 접근하는 것입니다.
결국 그들은 가장 유사한 상위 3개 업계의 계정에 집중하고 향후 이 도구를 사용하여 다른 업계의 유사점을 찾을 수 있기를 원합니다. 가능하다면 항상 상위 3개 산업을 반환하는 대신 원하는 수의 일치 산업을 제공하는 옵션을 원합니다.
이 사용 사례는 유사성 검색 수행을 중심으로 합니다. 이 연습을 수동으로 완료하는 것이 가능하지만 이미 여러 언어에 대해 생성된 사전 훈련된 임베딩 때문에 Wikipedia2Vec 도구가 떠오릅니다. 벡터라고도 알려진 단어 임베딩은 구문 정보와 의미 정보를 모두 포함하는 단어를 숫자로 표현한 것입니다. 단어를 벡터로 표현함으로써 어떤 단어가 의미상 다른 단어와 "더 가까운" 단어인지 수학적으로 결정할 수 있습니다.
이 예에서는 Salesforce에 구성된 각 산업에 대한 단어 벡터를 생성하는 간단한 Python 프로그램을 작성할 수도 있습니다.
pgvector
확장에는 Postgres 데이터베이스가 필요합니다. 그러나 이 예의 기업 데이터는 현재 Salesforce에 있습니다. 다행히 Heroku Connect는 Salesforce 계정을 Heroku Postgres와 동기화하여 salesforce.account
라는 테이블에 저장하는 쉬운 방법을 제공합니다. 그런 다음 관련 단어 벡터와 함께 Salesforce의 각 산업(VARCHAR 키)을 포함하는 salesforce.industries
라는 또 다른 테이블이 있습니다.
Postgres의 Salesforce 데이터 및 단어 벡터를 사용하여 Java 및 Spring Boot를 사용하여 RESTful API를 생성합니다. 이 서비스는 필요한 쿼리를 수행하고 결과를 JSON 형식으로 반환합니다.
다음과 같이 솔루션에 대한 개략적인 관점을 설명할 수 있습니다.
소스 코드는 GitLab에 있습니다. git push heroku
명령을 실행하면 Heroku에서 배포가 시작되어 마케팅 팀이 쉽게 사용할 수 있는 RESTful API가 도입됩니다.
높은 수준의 설계가 완료되면 솔루션 구축을 시작할 수 있습니다. Salesforce 로그인을 사용하여 계정 화면으로 이동하여 이 연습에 대한 데이터를 볼 수 있었습니다. 다음은 기업 데이터의 첫 번째 페이지 예입니다.
이러한 노력을 위해 마케팅팀의 요청을 해결하기 위해 Heroku를 사용할 계획이었습니다. 내 Heroku 계정에 로그인하고 새 앱 만들기 버튼을 사용하여 similarity-search-sfdc
라는 새 애플리케이션을 설정했습니다.
앱을 만든 후 리소스 탭으로 이동하여 Heroku Postgres 추가 기능을 찾았습니다. 추가 기능 검색 필드에 "Postgres"를 입력했습니다.
목록에서 Heroku Postgres를 선택한 후 Standard 0 플랜을 선택했지만 pgVector는 PostgreSQL 15 또는 베타 Essential-tier 데이터베이스를 실행하는 Standard-tier(또는 그 이상) 데이터베이스 제품에서 사용할 수 있습니다 .
추가 기능을 확인했을 때 Heroku는 DATABASE_URL
연결 문자열을 생성하고 제공했습니다. 내 앱 설정 탭의 구성 변수 섹션에서 이것을 찾았습니다. 이 정보를 사용하여 데이터베이스에 연결하고 다음과 같이 pgVector 확장을 활성화했습니다.
CREATE EXTENSION vector;
다음으로 Heroku Connect 애드온을 검색해서 찾았습니다. 저는 이것이 Salesforce의 기업 데이터에 쉽게 연결할 수 있는 방법을 제공한다는 것을 알았습니다.
이 연습에서는 무료 Demo Edition 계획이 잘 작동합니다.
이 시점에서 similarity-search-sfdc
앱의 리소스 탭은 다음과 같습니다.
내 Salesforce 계정을 Heroku Connect에 연결하기 위해 " Heroku Connect 설정 " 지침을 따랐습니다. 그런 다음 동기화를 위해 Account 개체를 선택했습니다. 완료되면 Heroku Connect와 기본 Postgres 데이터베이스에서 동일한 Salesforce 계정 데이터를 볼 수 있었습니다.
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]
단어 벡터를 저장하려면 다음 SQL 명령을 사용하여 Postgres 데이터베이스에 industries
테이블을 추가해야 했습니다.
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] ');
참고하세요. 소프트웨어 산업(소프트웨어)을 소문자로 표현하는 단어 벡터를 생성했지만 industry.name industries.name
은 대문자로 표시된 산업 이름(소프트웨어)과 일치해야 합니다.
생성된 모든 단어 벡터가 industries
테이블에 추가되면 RESTful API를 도입하는 데 중점을 둘 수 있습니다.
당면한 과제를 해결하기 위한 모든 것이 준비되어 있었기 때문에 소프트웨어 엔지니어로서의 나의 열정이 최고조에 달한 지점이었습니다.
다음으로 Spring Boot 3.2.2 및 Java(temurin) 17을 사용하여 IntelliJ IDEA에서 다음 Maven 종속성을 갖춘 similarity-search-sfdc
프로젝트를 만들었습니다.
<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 데이터베이스 테이블과 일치하는 Account 개체와 Industry (embedding) 개체 모두에 대해 단순화된 엔터티를 만들었습니다.
@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;
여기에서 JPA 저장소와 상호 작용하기 위해 AccountsService
클래스를 빌드했습니다.
@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); } } }
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 CLI를 사용하여 Heroku 플랫폼에 대한 similarity-search-sfdc
서비스를 위한 GitLab 저장소에 원격을 추가했습니다.
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 명령을 실행하여 소프트웨어 산업과 가장 가까운 상위 3개 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년에 매우 재미있었던 이벤트의 스크린샷입니다.
내 삶의 이러한 측면을 잘 아는 사람들은 내가 기본적으로 선택한 플레이어 클래스가 군인이었다고 말할 수 있습니다. 병사는 체력, 이동력, 속도, 화력의 균형이 가장 잘 잡혀 있기 때문입니다.
소프트웨어 엔지니어는 어떤 상황에도 적응할 수 있고 효율적으로 기대에 부응하는 솔루션을 제공하는 데 집중할 수 있기 때문에 현실 세계의 "군인 클래스"라고 생각합니다.
몇 년 동안 저는 모든 IT 전문가에게 적용될 수 있다고 생각하는 다음 사명 선언문에 집중해 왔습니다.
“지적 재산의 가치를 확장하는 특징/기능을 제공하는 데 시간을 집중하십시오. 다른 모든 것에 프레임워크, 제품, 서비스를 활용하세요.”
- J. 베스터
이 게시물의 예에서는 Heroku Connect를 활용하여 기업 데이터를 Postgres 데이터베이스와 동기화할 수 있었습니다. pgVector 확장 프로그램을 설치한 후 Salesforce 계정에서 각 고유 산업에 대한 단어 벡터를 만들었습니다. 마지막으로 우리는 업계가 다른 업계와 가장 가까운 Salesforce 계정을 찾는 프로세스를 단순화하는 Spring Boot 서비스를 도입했습니다.
우리는 기존 오픈 소스 기술, 작은 Spring Boot 서비스 추가 및 Heroku PaaS를 통해 이 사용 사례를 신속하게 해결했습니다. 이는 내 사명 선언문을 완전히 준수했습니다. 이러한 프레임워크, 제품, 서비스가 없다면 얼마나 많은 시간이 필요할지 상상할 수 없습니다.
관심이 있다면 GitLab에서 이 기사의 원본 소스 코드를 찾을 수 있습니다.
https://gitlab.com/johnjvester/similarity-search-sfdc
정말 좋은 하루 보내세요!