python #3 pgvector 유사 리뷰 검색

python #3 pgvector 유사 리뷰 검색 #

#2025-08-20


1. 목적 #

고객 리뷰 문장을 벡터로 임베딩하고 PostgreSQL의 pgvector 기능을 활용하여 비슷한 리뷰를 검색하는 기능을 구현

#

2. 코드 #

import torch
import transformers
import sentence_transformers
import sklearn
import numpy
import scipy

print(f"torch: {torch.__version__}")
print(f"transformers: {transformers.__version__}")
print(f"sentence-transformers: {sentence_transformers.__version__}")
print(f"scikit-learn: {sklearn.__version__}")
print(f"numpy: {numpy.__version__}")
print(f"scipy: {scipy.__version__}")

from dotenv import load_dotenv
import os

load_dotenv()  # 같은 폴더에 있는 .env 로드
torch: 2.2.2
transformers: 4.25.1
sentence-transformers: 2.2.2
scikit-learn: 1.3.2
numpy: 1.24.4
scipy: 1.10.1

skala conda 환경을 만들었었는데 pgvector 돌리기용으로 지피티가 추천해준 패키지 조합이 있어서 그냥 force로 저렇게 깔아줬다.

from sentence_transformers import SentenceTransformer
import numpy as np

# 1단계: 문장 임베딩
model = SentenceTransformer('paraphrase-MiniLM-L6-v2')

reviews = [
    "배송이 빠르고 제품도 좋아요.",
    "품질이 기대 이상입니다!",
    "생각보다 배송이 오래 걸렸어요.",
    "배송은 느렸지만 포장은 안전했어요.",
    "아주 만족스러운 제품입니다."
]

embeddings = model.encode(reviews)
import psycopg2
from pgvector.psycopg2 import register_vector

# 2단계: PostgreSQL 테이블 생성
conn = psycopg2.connect(
    host="localhost",          
    port=5432,                
    database="postgres",    
    user="postgres",       
    password=os.getenv("PG_PASSWORD"),  # 환경변수에서 불러옴 
)

register_vector(conn)  # 벡터 변환 활성화
cur = conn.cursor()

# DB 초기화 (기존 테이블 삭제 후 재생성)
dim = model.get_sentence_embedding_dimension()
cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
cur.execute("DROP TABLE IF EXISTS review_vectors;")  # 테이블 완전 삭제
cur.execute(f"""
    CREATE TABLE review_vectors (
        id SERIAL PRIMARY KEY,
        review TEXT,
        embedding vector({dim})
    );
""")
conn.commit()
# 3단계: 벡터 저장
for review, emb in zip(reviews, embeddings):
    cur.execute(
        "INSERT INTO review_vectors (review, embedding) VALUES (%s, %s)",
        (review, np.array(emb, dtype=np.float32)) 
    )
conn.commit()
# 4단계: 유사도 검색
query = "배송이 느렸어요"
query_vec = model.encode([query])[0].astype(np.float32)

print("\n유사도 검색 결과:")
cur.execute(
    """
    SELECT review, embedding <=> %s AS cosine_distance
    FROM review_vectors
    ORDER BY embedding <=> %s
    LIMIT 3;
    """,
    (query_vec, query_vec)
)
for review, dist in cur.fetchall():
    print(f"코사인거리: {dist:.4f} | 리뷰: {review}")
유사도 검색 결과:
코사인거리: 0.0783 | 리뷰: 배송이 빠르고 제품도 좋아요.
코사인거리: 0.0990 | 리뷰: 배송은 느렸지만 포장은 안전했어요.
코사인거리: 0.1253 | 리뷰: 생각보다 배송이 오래 걸렸어요.
# 5. 마무리
cur.close()
conn.close()

#

3. 생각 #

PostgreSQL 테이블 생성 단계에서 나는 python으로 그냥 쏴줬는데 pgadmin 왔다갔다하면서 연동 느낌을 주는게 목적인가? 싶어서 남들 코드로 확인만 해보기.

  1. pgadmin을 들어가서 postgresql에 테이블 생성
-- 리뷰 테이블 생성
CREATE TABLE review_vectors (
    id SERIAL PRIMARY KEY,
    review TEXT,
    embedding VECTOR(384) -- 384차원 임베딩 벡터
);
# 벡터 DB에 저장
conn = psycopg2.connect(
    dbname="*",
    user="*",
    password="*",
    host="localhost",
    port="5432"
)
cur = conn.cursor()

# 각 리뷰와 임베딩을 DB에 저장
for review, embedding in zip(reviews, embeddings):
    emb_list = embedding.tolist() 
    cur.execute(
        "INSERT INTO review_vectors (review, embedding) VALUES (%s, %s)",
        (review, emb_list)
    )

요게 정석인듯. python으로 review를 embedding이라는 벡터로 만들고 -> SQL 쿼리문 작성하고 -> python으로 연결해서 python으로 리뷰 임베딩을 작성하고 -> reviews, embeddings를 db에 저장.

#

내코드는?

conn = psycopg2.connect( # DB 연결 객체 conn 생성
    host="localhost",          
    port=5432,                
    database="postgres",    
    user="postgres",       
    password=os.getenv("PG_PASSWORD"),  # 환경변수에서 불러옴 
)

DB연결을 먼저하고

register_vector(conn) 
cur = conn.cursor() # cursor 객체 cur 생성 (SQL을 실행하고 결과를 가져오는 역할)

dim = model.get_sentence_embedding_dimension()
cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
cur.execute("DROP TABLE IF EXISTS review_vectors;")  # 테이블 완전 삭제
cur.execute(f"""
    CREATE TABLE review_vectors (
        id SERIAL PRIMARY KEY,
        review TEXT,
        embedding vector({dim})
    );
""")
conn.commit()

테이블 생성을 해줌.

# 3단계: 벡터 저장
for review, emb in zip(reviews, embeddings):
    cur.execute(
        "INSERT INTO review_vectors (review, embedding) VALUES (%s, %s)",
        (review, np.array(emb, dtype=np.float32)) 
    )
conn.commit()

여기는 똑같다.

#