AI Vector Search #1 - 기본 기능
AI Vector Search #2 - HNSW 벡터 인덱스
AI Vector Search #3 - IVF 벡터 인덱스
Oracle 23ai에 AI Vector Search 기능이 추가되었습니다. AI Vector Search는 AI 워크로드를 위해 설계되었으며 키워드가 아닌 의미론에 기반하여 데이터를 조회합니다. 이 글에서는 AI Vector Search의 기본 기능에 대해 간단히 살펴보겠습니다. ML을 사용한 벡터 생성과 관련된 내용은 Oracle AI Vector Search User's Guide를 참고하세요.
테스트 버전은 아래와 같습니다.
-- 1
SELECT version_full FROM product_component_version;
VERSION_FULL
------------
23.4.0.24.05
1 row selected.
테스트를 위해 아래와 같이 테이블을 생성하겠습니다. 생성한 벡터 값은 단순한 3x3 배열입니다. JSON_ARRAY 함수는 배열 형식의 JSON 값을 생성하고, TO_VECTOR 함수는 문자 값을 벡터 값으로 변환합니다. 참고로 TO_VECTOR 함수를 생략해도 암시적 데이터 변환(JSON -> VARCHAR2 -> VECTOR)에 의해 테이블이 정상적으로 생성됩니다.
-- 2
DROP TABLE t1 PURGE;
CREATE TABLE t1 (c1, c2, c3, CONSTRAINT t1_pk PRIMARY KEY (c1, c2)) AS
SELECT a.c1, b.c1, TO_VECTOR (JSON_ARRAY (a.c1, b.c1))
FROM (SELECT ROWNUM AS c1 FROM XMLTABLE ('1 to 3')) a
, (SELECT ROWNUM AS c1 FROM XMLTABLE ('1 to 3')) b;
벡터 테이터 타입은 Oracle 23ai에 추가된 새로운 기본 데이터 타입으로 데이터를 LOB 타입으로 저장하는 것으로 보입니다.
-- 3-1
SELECT data_type, data_length, vector_info
FROM user_tab_columns
WHERE table_name = 'T1'
AND column_name = 'C3';
DATA_TYPE DATA_LENGTH VECTOR_INFO
--------- ----------- -----------
VECTOR 8200 VECTOR(*,*)
1 row selected.
-- 3-2
SELECT segment_name, index_name, format, value_based, max_inline
FROM user_lobs
WHERE table_name = 'T1';
SEGMENT_NAME INDEX_NAME FORMAT VALUE_BASED MAX_INLINE
------------------------- ------------------------ -------------- ----------- ----------
SYS_LOB0000089887C00003$$ SYS_IL0000089887C00003$$ ENDIAN NEUTRAL YES 8000
1 row selected.
아래 쿼리는 VECTOR_DISTANCE 함수로 벡터 간의 거리를 계산합니다. VECTOR_DISTANCE 함수는 세 번째 매개변수에 다양한 메트릭을 지정할 수 있습니다. VECTOR_DISTANCE 함수 외에도 다수의 Vector 함수를 사용할 수 있습니다.
-- 4
SELECT c1, c2, c3
, VECTOR_DISTANCE (c3, '[1,1]', COSINE ) AS cosine -- COSINE_DISTANCE
, VECTOR_DISTANCE (c3, '[1,1]', DOT ) AS dot -- INNER_PRODUCT
, VECTOR_DISTANCE (c3, '[1,1]', EUCLIDEAN ) AS euclidean -- L2_DISTANCE
, VECTOR_DISTANCE (c3, '[1,1]', EUCLIDEAN_SQUARED) AS euclidean_squared -- L2_SQUARED
, VECTOR_DISTANCE (c3, '[1,1]', HAMMING ) AS hamming
, VECTOR_DISTANCE (c3, '[1,1]', MANHATTAN ) AS manhattan -- L1_DISTANCE
FROM t1;
C1 C2 C3 COSINE DOT EUCLIDEAN EUCLIDEAN_SQUARED HAMMING MANHATTAN
-- -- ------------------- ---------- --------- ---------- ----------------- -------- ---------
1 1 [1.0E+000,1.0E+000] -1.19E-007 -2.0E+000 0 0 0 0
1 2 [1.0E+000,2.0E+000] 5.132E-002 -3.0E+000 1.0E+000 1.0E+000 1.0E+000 1.0E+000
1 3 [1.0E+000,3.0E+000] 1.056E-001 -4.0E+000 2.0E+000 4.0E+000 1.0E+000 2.0E+000
2 1 [2.0E+000,1.0E+000] 5.132E-002 -3.0E+000 1.0E+000 1.0E+000 1.0E+000 1.0E+000
2 2 [2.0E+000,2.0E+000] -1.19E-007 -4.0E+000 1.414E+000 2.0E+000 2.0E+000 2.0E+000
2 3 [2.0E+000,3.0E+000] 1.942E-002 -5.0E+000 2.236E+000 5.0E+000 2.0E+000 3.0E+000
3 1 [3.0E+000,1.0E+000] 1.056E-001 -4.0E+000 2.0E+000 4.0E+000 1.0E+000 2.0E+000
3 2 [3.0E+000,2.0E+000] 1.942E-002 -5.0E+000 2.236E+000 5.0E+000 2.0E+000 3.0E+000
3 3 [3.0E+000,3.0E+000] -1.19E-007 -6.0E+000 2.828E+000 8.0E+000 2.0E+000 4.0E+000
9 rows selected.
아래 쿼리의 함수는 VECTOR_DISTANCE 함수의 축약 함수 (shorthand function)입니다.
-- 5
SELECT c1, c2, c3
, COSINE_DISTANCE (c3, '[1,1]') AS cosine
, INNER_PRODUCT (c3, '[1,1]') * -1 AS dot
, L2_DISTANCE (c3, '[1,1]') AS euclidean
, L1_DISTANCE (c3, '[1,1]') AS manhattan
FROM t1;
C1 C2 C3 COSINE DOT EUCLIDEAN MANHATTAN
-- -- ------------------- ---------- --------- ---------- ---------
1 1 [1.0E+000,1.0E+000] -1.19E-007 -2.0E+000 0 0
1 2 [1.0E+000,2.0E+000] 5.132E-002 -3.0E+000 1.0E+000 1.0E+000
1 3 [1.0E+000,3.0E+000] 1.056E-001 -4.0E+000 2.0E+000 2.0E+000
2 1 [2.0E+000,1.0E+000] 5.132E-002 -3.0E+000 1.0E+000 1.0E+000
2 2 [2.0E+000,2.0E+000] -1.19E-007 -4.0E+000 1.414E+000 2.0E+000
2 3 [2.0E+000,3.0E+000] 1.942E-002 -5.0E+000 2.236E+000 3.0E+000
3 1 [3.0E+000,1.0E+000] 1.056E-001 -4.0E+000 2.0E+000 2.0E+000
3 2 [3.0E+000,2.0E+000] 1.942E-002 -5.0E+000 2.236E+000 3.0E+000
3 3 [3.0E+000,3.0E+000] -1.19E-007 -6.0E+000 2.828E+000 4.0E+000
9 rows selected.
아래 쿼리는 축약 연산자(shorthand operator)로 벡터 간의 거리를 계산합니다.
-- 6
SELECT c1, c2, c3
, c3 <=> '[1,1]' AS cosine
, c3 <#> '[1,1]' AS dot
, c3 <-> '[1,1]' AS euclidean
FROM t1;
C1 C2 C3 COSINE DOT EUCLIDEAN
-- -- ------------------- ---------- --------- ----------
1 1 [1.0E+000,1.0E+000] -1.19E-007 -2.0E+000 0
1 2 [1.0E+000,2.0E+000] 5.132E-002 -3.0E+000 1.0E+000
1 3 [1.0E+000,3.0E+000] 1.056E-001 -4.0E+000 2.0E+000
2 1 [2.0E+000,1.0E+000] 5.132E-002 -3.0E+000 1.0E+000
2 2 [2.0E+000,2.0E+000] -1.19E-007 -4.0E+000 1.414E+000
2 3 [2.0E+000,3.0E+000] 1.942E-002 -5.0E+000 2.236E+000
3 1 [3.0E+000,1.0E+000] 1.056E-001 -4.0E+000 2.0E+000
3 2 [3.0E+000,2.0E+000] 1.942E-002 -5.0E+000 2.236E+000
3 3 [3.0E+000,3.0E+000] -1.19E-007 -6.0E+000 2.828E+000
9 rows selected.
VECTOR_DISTANCE 함수로 데이터를 정렬하는 경우 ROW LIMITING 절의 FETCH 절에 EXACT 또는 APPROX 키워드를 기술할 수 있습니다. 기본값은 EXACT이며, APPROX는 벡터 인덱스를 통한 Approximate Similarity Search에 사용합니다.
FETCH [ EXACT | APPROX | APPROXIMATE ] { FIRST | NEXT }
아래 쿼리는 [1,1] 벡터에 가까운 5개의 벡터를 조회합니다. Column Projection Information 항목에서 VECTOR 함수에 의해 문자값이 벡터값으로 변환되는 것을 확인할 수 있습니다. VECTOR 함수는 TO_VECTOR 함수의 synonym입니다.
-- 7
SELECT c1, c2, c3, VECTOR_DISTANCE (c3, '[1,1]', EUCLIDEAN) AS euclidean
FROM t1
WHERE (c1, c2) != (1, 1)
ORDER BY VECTOR_DISTANCE (c3, '[1,1]', EUCLIDEAN)
FETCH EXACT FIRST 5 ROWS ONLY;
C1 C2 C3 EUCLIDEAN
-- -- ------------------- ----------
1 2 [1.0E+000,2.0E+000] 1.0E+000
2 1 [2.0E+000,1.0E+000] 1.0E+000
2 2 [2.0E+000,2.0E+000] 1.414E+000
1 3 [1.0E+000,3.0E+000] 2.0E+000
3 1 [3.0E+000,1.0E+000] 2.0E+000
5 rows selected.
--------------------------------------------------------------------
| Id | Operation | Name | Starts | A-Rows | Buffers |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5 | 3 |
|* 1 | COUNT STOPKEY | | 1 | 5 | 3 |
| 2 | VIEW | | 1 | 5 | 3 |
|* 3 | SORT ORDER BY STOPKEY| | 1 | 5 | 3 |
|* 4 | TABLE ACCESS FULL | T1 | 1 | 8 | 3 |
--------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=5)
3 - filter(ROWNUM<=5)
4 - filter(("C2"<>1 OR "C1"<>1))
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "from$_subquery$_002"."C1"[NUMBER,22], "from$_subquery$_002"."C2"[NUMBER,22], "from$_subquery$_002"."C3"[VECTOR,8200],
"from$_subquery$_002"."EUCLIDEAN"[BINARY_DOUBLE,8]
2 - "from$_subquery$_002"."C1"[NUMBER,22], "from$_subquery$_002"."C2"[NUMBER,22], "from$_subquery$_002"."C3"[VECTOR,8200],
"from$_subquery$_002"."EUCLIDEAN"[BINARY_DOUBLE,8]
3 - (#keys=1) VECTOR_DISTANCE("C3" /*+ LOB_BY_VALUE */ , VECTOR('[1,1]', *, * /*+ USEBLOBPCW_QVCGMD */ ), EUCLIDEAN)[8],
"C1"[NUMBER,22], "C2"[NUMBER,22], "C3" /*+ LOB_BY_VALUE */ [VECTOR,8200]
4 - "C1"[NUMBER,22], "C2"[NUMBER,22], "C3" /*+ LOB_BY_VALUE */ [VECTOR,8200], VECTOR_DISTANCE("C3" /*+ LOB_BY_VALUE */ , VECTOR('[1,1]',
*, * /*+ USEBLOBPCW_QVCGMD */ ), EUCLIDEAN)[8]