데이터 기반의 의사결정을 하거나 AI 알고리즘/rule을 서비스에 추가하기 위해서, 여러 다양한 데이터를 가공하고 DB에 넣고 querying 할 필요가 있습니다. 본 포스팅에서는 이와 관련된 vector DB 사용법 부터 기본적인 SQL query 문, 시각화 방법 등을 기록합니다.
Qdrant → Collection → Shard → Segment. 이 부분은 다른 search engine 들과 비교해보면 좋을듯
Collections: points(vectors with a payload)의 집합
Points: vector와 payload(optional)로 구성
Payload: vector와 함께 저장 가능한 추가 정보
Search: 기본적으로 dot, cos, euc similarity search 제공
Index: 필터링 속도 향상을 위한 payload index와 vector search 속도 향상을 위한 vector index 존재
Vector Search with HNSW
HNSW는 벡터 검색 알고리즘 중 하나인데 qdrant의 작동을 이해하기 위해 중요합니다. NHN Forward 발표에서도 잘 설명해주고 있어 이를 참고 하였습니다.
k-Nearest Neighbor: 검색 대상 벡터와 모든 벡터와의 거리를 계산하여 가장 가까운 k개를 리턴하는 방식
Approximate Nearest Neighbor (e.g., ANNOY): Spotify에서 개발한 tree-based ANN 기법으로 정확도를 조금 줄이는 대신 검색 속도를 높임. 전체 벡터를 여러 공간으로 분할하여 tree 형태의 자료구조 만들어 검색 수행. 데이터 들어올 때 마다 자료구조 다시 만들어야 해서 데이터 추가시 빌드 비용 높음
Hierarchical Navigable Small World: Skip-list와 Navigable small world 개념에 영향을 받은 방식
아래 이미지의 오른쪽을 보면 이해가 쉬운데, 초록색 query에 대해서 빨간색 start point 부터 검색을 시작할 때, 한 번 nearest neighbor을 찾을 때 마다 깊은 layer로 내려간다고 생각하면 됨 (greedy search)
데이터 추가가 쉽고, 데이터 경향성에 따라 성능 차이 있음. 클러스링 데이터 대해 응답속도 빠르고 재현율도 높음
HNSW challenges: (1) 여전히 데이터 index 빌드 타임 소요 (2) 100% 정확도/재현율 보장하지 않음 (3) Sequetial read pattern을 가져서 병렬화 불가능. Qdrant는 이런 점들 보완하기 위해 quatization & oversampling 활용
Filterable HNSW(payload-based refinement): post-filtering, pre-filtering이 비효율적이기 때문에 in-place filtering 방식으로 구현
Taken From, Yury A. Malkov, et al.
Python qdrant client
Client initialization
from qdrant_client import QdrantClient
from qdrant_client.http.models import Distance, VectorParams, PointStruct
from qdrant_client.http.models import Filter, FieldCondition, MatchValue
client = QdrantClient("localhost", port=6333)
# run a query without filter
search_result = client.search(
collection_name="test_collection", query_vector=[0.2,0.1,0.9,0.7], limit=2)# run a query with filter
search_result = client.search(
collection_name="test_collection",
query_vector=[0.2,0.1,0.9,0.7],
query_filter=Filter(must=[FieldCondition(key="city",match=MatchValue(value="London"))]),
limit=2,)
SQL
Basics
SELECT 컬럼
FROM 테이블
WHERE 조건
GROUP BY 묶는 기준
ORDER BY 정렬 기준
LIKE 문자열 조건
LIMIT 데이터 출력수
OFFSET 앞 부터 N개 제외
...;
Join
employees 테이블과 departments 테이블에 대해 ChatGPT가 제공해준 join 예시들
employee_id
employee_name
department_id
1
John Doe
101
2
Jane Smith
102
3
Mike Brown
NULL
department_id
department_name
101
Human Resources
102
Finance
103
IT
INNER JOIN: 두 테이블에서 일치하는 값이 있는 레코드 반환
SELECT employees.employee_name, departments.department_name
FROM employees
INNERJOIN departments
ON employees.department_id = departments.department_id;
employee_name
department_name
John Doe
Human Resources
Jane Smith
Finance
LEFT JOIN (LEFT OUTER JOIN): 왼쪽 테이블의 모든 레코드와 오른쪽 테이블의 일치하는 레코드 반환
SELECT employees.employee_name, departments.department_name
FROM employees
LEFTJOIN departments
ON employees.department_id = departments.department_id;
employee_name
department_name
John Doe
Human Resources
Jane Smith
Finance
Mike Brown
NULL
RIGHT JOIN (RIGHT OUTER JOIN): 오른쪽 테이블의 모든 레코드와 왼쪽 테이블의 일치하는 레코드 반환
SELECT employees.employee_name, departments.department_name
FROM employees
RIGHTJOIN departments
ON employees.department_id = departments.department_id;
employee_name
department_name
John Doe
Human Resources
Jane Smith
Finance
NULL
IT
OUTER JOIN (FULL OUTER JOIN): 왼쪽 또는 오른쪽 테이블에 일치하는 항목이 있는 모든 레코드 반환
SELECT employees.employee_name, departments.department_name
FROM employees
FULLOUTERJOIN departments
ON employees.department_id = departments.department_id;
employee_name
department_name
John Doe
Human Resources
Jane Smith
Finance
Mike Brown
NULL
NULL
IT
UNION: 각 select 의 결과 집합(set)을 결합. UNION을 위한 각 열 개수가 모두 동일해야 하고, 데이터 타입이 유사해야 하며, 순서도 동일해야 함.
UNION은 중복 rows를 지우므로, 중복을 허용하려면 UNION ALL 사용
SELECT employee_name FROM employees WHERE department_id =101UNIONSELECT employee_name FROM employees WHERE department_id =102;
employee_name
John Doe
Jane Smith
Advanced
LIKE: '%'는 N개의 문자, '_'는 1개의 문자를 의미
GROUP BY: 같은 값을 가진 rows를 그룹지어 줌. COUNT(), MAX(), MIN(), SUM(), AVG()과 같은 집계함수와 주로 같이 사용됨
SELECT문의 모든 열이 집계함수이거나 GROUP BY 절에 존재해야함
집계함수 없이 아래 예시 같이 사용하는 경우에 중복을 제거한 unique rows를 확인해볼 수 있음
SELECT
employee_name,CASEWHEN department_id ISNULLTHEN'No Department'ELSE(SELECT department_name FROM departments WHERE department_id = employees.department_id)ENDAS department_status
FROM employees;
employee_name
department_status
John Doe
Human Resources
Jane Smith
Finance
Mike Brown
No Department
WITH: 이름을 가진 sub query를 정의한 후 사용하는 구문
WITH절을 사용했을 때의 장점이 schatz37님의 블로그에 매우 상세히 설명되어 있어 여러번 읽어보면 좋을 것 같음 (Materilaize 방식의 경우 가독성의 장점 뿐만 아니라 쿼리 성능 개선으로도 이어짐)
WITH 임시테이블1AS(SELECT...),WITH 임시테이블2AS(SELECT...)SELECT*FROM 임시테이블1, 임시테이블2
Window function: MAX, MIN, RANK 등.. 있지만 ROW_NUM 예시를 적음
WINODW_FUNCTION (ARGUMENTS) OVER ([PARTITION BY][ORDER BY][WINDOWING])
아래와 같이 작성시, 중복 데이터 중에서 제일 최근에 데이터 웨어하우스에 추가된 데이터만 골라낼 수 있음 (중복 제거)