Developer's Development
3.3.12 [LLM] 프롬프트 엔지니어링: 벡터 데이터베이스 본문
벡터 데이터베이스 (Vector Database)
- 벡터 DB의 이해
벡터 데이터베이스는 벡터 형식으로 저장된 데이터(고차원 임베딩 데이터)를 관리하고, 유사도 기반 검색 및 작업을 최적화하도록 설계된 데이터베이스이다. AI/ML, 자연어 처리(NLP), 이미지 검색, 추천 시스템에서 주로 사용된다. 일반적인 관계형 DB와 달리 유사도를 기반으로 데이터를 검색하는 최근접 이웃 검색(ANN, Approximate Nearest Neighbor) 기법을 활용한다.
👉🏻 벡터 DB의 특징
- 벡터 임베딩 저장: 데이터를 벡터로 변환해 저장
- 유사도 검색 최적화: 입력 벡터와 가장 유사한 벡터를 빠르게 검색
- 확장성: 대규모 벡터 데이터를 처리할 수 있는 구조 제공
👉🏻 장점
- 고차원 데이터를 효율적으로 관리
- 비정형 데이터(이미지, 텍스트 등) 처리에 적합
- 유사도 계산을 빠르게 수행
👉🏻 적용 분야: 추천 시스템, 콘텐츠 기반 검색, 이미지 검색, 자연어 검색 등
👀 [참고] Vector DB vs RDB
| 항목 | 관계형 DB (RDB) | 벡터 DB |
| 데이터 저장 방식 | 테이블(행-열) 기반 구조 | 고차원 벡터 형태로 저장 |
| 검색 방식 | SQL 기반의 정렬 및 필터링 | 유사도 기반 검색 (Cosine Similarity, Eucidean Distance 등) |
| 데이터 유형 | 구조화된 데이터 (정형 데이터) |
텍스트, 이미지, 오디오 등의 비정형 데이터 |
| 사용 사례 | 전통적인 기업 데이터 (고객 정보, 트랜잭션 등) |
추천 시스템, 이미지/문서 검색, AI 모델 연동 |
▶ 관계형 DB는 정확한 데이터 매칭이 강점이고, 벡터 DB는 유사도 기반 검색이 강점이다.
👉🏻 벡터 DB의 종류
(사용할 예정인 것만 정리, 빨강색은 무료로 사용 가능)
Chroma: Python 기반의 간단한 API 제공, LangChain과 통합 가능 (🐿️ 학습용으로 많이 사용)
FAISS: Facebook에서 개발한 라이브러리로, 고속 유사도 검색 지원
Pinecone: 클라우드 기반 서비스로 사용이 간편하며, 대규모 벡터 데이터 처리 가능
🐿️ 실무에선 Milvus (규모가 큰 데이터), Qdrant (규모가 크지 않은 데이터)를 많이 사용
- 벡터 DB를 활용한 검색
두 벡터 간의 유사도를 측정하여 데이터 간 관계를 파악한다.
👀 활용 사례
- 추천 시스템: 사용자와 비슷한 취향의 콘텐츠 추천
- 문서 검색: 입력 문장과 가장 관련 있는 문서 검색
👉🏻 유사도 계산 방식
Cosine Similarity: 두 벡터 간의 코사인 각도를 이용해 유사도를 측정, 값 범위 [-1, 1]
유클리드 거리: 두 벡터 간의 거리로 유사도를 측정, 값이 작을수록 유사
점수 기반 검색: 특정 기준 점수(Threshold) 이상인 결과만 반환
- 벡터 DB 활용 사례
추천 시스템: 사용자의 취향과 유사한 콘텐츠 검색
이미지 검색: 유사한 이미지 검색(ex. Google 이미지 검색)
문서 검색: 텍스트 임베딩을 기반으로 관련 문서 찾기 (→ RAG)
음성 검색: 오디오 데이터를 벡터화하여 유사한 음성 파일 검색
의료 데이터 검색: 환자 기록을 벡터로 변환하여 유사한 진단 사례 찾기
사이버 보안: 이상 징후 탐지 (벡터화된 네트워크 로그를 비교하여 비정상적인 패턴 탐지)
- Pinecone 회원가입
The vector database to build knowledgeable AI | Pinecone
Search through billions of items for similar matches to any object, in milliseconds. It’s the next generation of search, an API call away.
www.pinecone.io
실습 (Chroma DB)
- 가상환경 생성 (python 3.9 version 필요)
conda create -n vectordb_env python=3.9
conda activate vectordb_env
pip install jupyter notebook ipykernel numpy pandas datasets
python -m ipykernel install --user --name vectordb_env --display-name vectordb_env
- VS Code
!pip install chromadb
import chromadb
chroma_client = chromadb.Client() # 인스턴스 생성
collection = chroma_client.create_collection(name='my_collection') # 하나의 DB 테이블 생성
collection.add(
documents = [
'This is a document about pineapple',
'This is a document about mango',
'This is a document about strawberry'
],
ids=['id1', 'id2', 'id3']
)
results = collection.query( # 질의
query_texts = ['This is a query document about vietnam'],
n_results=3 # 결과의 갯수
)
results
# 쿼리 텍스트를 넣으면 유사도(거리) 기반으로 검색
"""
{'ids': [['id1', 'id2', 'id3']],
'embeddings': None,
'documents': [['This is a document about pineapple',
'This is a document about mango',
'This is a document about strawberry']],
'uris': None,
'included': ['metadatas', 'documents', 'distances'],
'data': None, # 넣은 커스텀데이터가 없음
'metadatas': [[None, None, None]], # 별도로 달아준 부가정보가 없음
'distances': [[1.2225853204727173, 1.2783520221710205, 1.3223111629486084]]}
"""
- SciQ dataset 활용
from datasets import load_dataset
dataset = load_dataset('sciq', split='train')
dataset = dataset.filter(lambda x: x['support'] != '') # 공백인 데이터는 빼기
dataset
"""
# 다운로드 과정 생략
Dataset({
features: ['question', 'distractor3', 'distractor1', 'distractor2', 'correct_answer', 'support'],
num_rows: 11679
})
"""
client = chromadb.Client()
collection = client.create_collection(name='sciq_support')
!pip install transformers==4.40.2 sentence-transformers==2.7.0
from sentence_transformers import SentenceTransformer
embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
supports = dataset['support'][:100]
supports_embed = embedding_model.encode(supports).tolist()
# print(len(supports_embed)) # 100
# print(len(supports_embed[0])) # 384
collection.add(
ids=[str(i) for i in range(100)],
embeddings=supports_embed,
metadatas=[{'type': 'support', 'text': text} for text in supports]
)
questions = dataset['question'][:3]
questions_embed = embedding_model.encode(questions).tolist()
results = collection.query(
query_embeddings=questions_embed,
n_results=1
)
# results
for i, q in enumerate(questions):
print("Question:", q)
print("Support:", results['metadatas'][i][0]['text'])
print()
"""
Question: What type of organism is commonly used in preparation of foods such as cheese and yogurt?
Support: Agents of Decomposition The fungus-like protist saprobes are specialized to absorb nutrients from nonliving organic matter, such as dead organisms or their wastes. For instance, many types of oomycetes grow on dead animals or algae. Saprobic protists have the essential function of returning inorganic nutrients to the soil and water. This process allows for new plant growth, which in turn generates sustenance for other organisms along the food chain. Indeed, without saprobe species, such as protists, fungi, and bacteria, life would cease to exist as all organic carbon became “tied up” in dead organisms.
Question: What phenomenon makes global winds blow northeast to southwest or the reverse in the northern hemisphere and northwest to southeast or the reverse in the southern hemisphere?
Support: Without Coriolis Effect the global winds would blow north to south or south to north. But Coriolis makes them blow northeast to southwest or the reverse in the Northern Hemisphere. The winds blow northwest to southeast or the reverse in the southern hemisphere.
Question: Changes from a less-ordered state to a more-ordered state (such as a liquid to a solid) are always what?
Support: Summary Changes of state are examples of phase changes, or phase transitions. All phase changes are accompanied by changes in the energy of a system. Changes from a more-ordered state to a less-ordered state (such as a liquid to a gas) areendothermic. Changes from a less-ordered state to a more-ordered state (such as a liquid to a solid) are always exothermic. The conversion of a solid to a liquid is called fusion (or melting). The energy required to melt 1 mol of a substance is its enthalpy of fusion (ΔHfus). The energy change required to vaporize 1 mol of a substance is the enthalpy of vaporization (ΔHvap). The direct conversion of a solid to a gas is sublimation. The amount of energy needed to sublime 1 mol of a substance is its enthalpy of sublimation (ΔHsub) and is the sum of the enthalpies of fusion and vaporization. Plots of the temperature of a substance versus heat added or versus heating time at a constant rate of heating are calledheating curves. Heating curves relate temperature changes to phase transitions. A superheated liquid, a liquid at a temperature and pressure at which it should be a gas, is not stable. A cooling curve is not exactly the reverse of the heating curve because many liquids do not freeze at the expected temperature. Instead, they form a supercooled liquid, a metastable liquid phase that exists below the normal melting point. Supercooled liquids usually crystallize on standing, or adding a seed crystal of the same or another substance can induce crystallization.
"""
- Chroma DB를 활용한 키워드 기반 검색
documents = [
"인공지능은 인간의 작업을 자동화하는 기술이다.",
"기계 학습은 데이터에서 패턴을 학습하여 예측하는 기술이다.",
"벡터 데이터베이스는 유사도를 기반으로 데이터를 검색하는 DB이다.",
"AI는 사람들이 하는 일을 대신 수행할 수 있도록 돕는 기술이다.",
"머신 러닝은 컴퓨터가 데이터를 분석하여 스스로 배우는 알고리즘이다.",
"벡터 DB는 데이터를 빠르게 검색하기 위해 유사도 기반으로 동작한다.",
"인공지능은 알고리즘을 사용하여 문제를 해결하는 시스템이다.",
"기계 학습을 통해 컴퓨터는 경험을 바탕으로 더 나은 결정을 내릴 수 있다.",
"벡터 데이터베이스는 고속 검색과 유사도 검색에 강점을 가진 데이터베이스이다.",
"AI 기술은 자동화된 시스템을 통해 사람들의 작업을 효율적으로 돕는다.",
"기계 학습 모델은 주어진 데이터를 학습하여 미래의 결과를 예측한다.",
"벡터 DB는 데이터의 유사도를 계산하여 관련성이 높은 정보를 빠르게 찾는다.",
"인공지능은 복잡한 문제를 해결하기 위해 인간의 지능을 모방하는 시스템이다.",
"기계 학습 알고리즘은 데이터를 통해 패턴을 발견하고, 이를 통해 예측을 개선한다.",
"벡터 데이터베이스는 대량의 데이터를 효율적으로 처리하고 빠른 검색을 가능하게 한다.",
"대형 언어 모델(LLM)은 자연어를 이해하고 생성할 수 있는 인공지능 모델이다.",
"트랜스포머 모델은 대형 언어 모델에서 주로 사용되는 네트워크 아키텍처이다.",
"자연어 처리(NLP)는 텍스트 데이터에서 의미를 추출하고 분석하는 기술이다.",
"파인튜닝(fine-tuning)은 사전 학습된 모델을 특정 작업에 맞게 조정하는 과정이다.",
"GPT는 대형 언어 모델 중 하나로, 텍스트 생성에 매우 뛰어난 성능을 보인다.",
"언어 모델은 문법적 의미를 이해하고, 자연스러운 문장을 생성하는데 사용된다.",
"BERT는 텍스트의 양방향 문맥을 학습하여 자연어 처리에서 뛰어난 성능을 발휘하는 모델이다.",
"자연어 생성(NLG)은 기계가 인간처럼 문장을 생성하는 기술이다.",
"LLM은 방대한 양의 텍스트 데이터를 학습하여 질문에 답하고 정보를 제공할 수 있다.",
"어텐션 메커니즘은 트랜스포머 모델에서 핵심적인 역할을 하는 기술로, 입력 문장에 대한 중요한 부분에 집중한다.",
"GPT 모델은 대화형 AI 시스템에서 자연스러운 대화 생성을 가능하게 한다."
]
client = chromadb.PersistentClient(path='./chroma_db')
collection = client.create_collection(name='ai_documents') # path의 name을 가지고 있으면 가져오고, 없으면 생성
# Embedding 모델 로드
model = SentenceTransformer('all-MiniLM-L6-v2')
# Embedding & add
for i, doc in enumerate(documents):
embed = model.encode(doc).tolist()
collection.add(
ids=[str(i)],
embeddings=[embed],
metadatas=[{'text': doc}]
)
# query
query_keyword = 'NLP'
query_embed = model.encode(query_keyword).tolist()
results = collection.query(query_embed, n_results=3)
for result in results['metadatas'][0]:
print('검색된 문서:', result['text'])
"""
검색된 문서: 자연어 처리(NLP)는 텍스트 데이터에서 의미를 추출하고 분석하는 기술이다.
검색된 문서: 자연어 생성(NLG)은 기계가 인간처럼 문장을 생성하는 기술이다.
검색된 문서: BERT는 텍스트의 양방향 문맥을 학습하여 자연어 처리에서 뛰어난 성능을 발휘하는 모델이다.
"""
- 영화 추천 시스템
import pandas as pd
df = pd.read_csv('./data/tmdb_5000_movies.csv')
# df.columns
# df.info() # 4,083 row
client = chromadb.PersistentClient(path='./chroma_db')
collection = client.get_or_create_collection(name='movies')
model = SentenceTransformer('all-MiniLM-L6-v2')
movies = [
{
"id": str(idx),
"title": row['title'],
"overview": row['overview'] if pd.notna(row['overview']) else ""
} for idx, row in df.iterrows()
]
movies
# add
# -ids, embeddings: 줄거리, metadatas: 줄거리 text, 제목
for movie in movies:
overview_embed = model.encode(movie['overview']).tolist()
collection.add(
ids=movie['id'],
embeddings=overview_embed,
metadatas=[{"title": movie['title'], "overview": movie['overview']}]
)
# query: 원하는 줄거리 입력 -> 유사도 검색
# query_text = 'A movie where heroes save the Earth'
query_text = 'Korea'
query_embed = model.encode(query_text).tolist()
results = collection.query(query_embeddings=[query_embed], n_results=3)
for result in results['metadatas'][0]:
print('제목:', result['title'])
print('줄거리:', result['overview'])
print()
"""
제목: Silmido
줄거리: On 31 January 1968, 31 North Korean commandos infiltrated South Korea in a failed mission to assassinate President Park Chung-hee. In revenge, the South Korean military assembled a team of 31 criminals on the island of Silmido to kill Kim Il-sung for a suicide mission to redeem their honor, but was cancelled, leaving them frustrated. It is loosely based on a military uprising in the 1970s.
제목: Tae Guk Gi: The Brotherhood of War
줄거리: In 1950, in South Korea, shoe-shiner Jin-tae Lee and his 18-year-old old student brother, Jin-seok Lee, form a poor but happy family with their mother, Jin-tae's fiancé Young-shin Kim, and her young sisters. Jin-tae and his mother are tough workers, who sacrifice themselves to send Jin-seok to the university. When North Korea invades the South, the family escapes to a relative's house in the country, but along their journey, Jin-seok is forced to join the army to fight in the front, and Jin-tae enlists too to protect his young brother. The commander promises Jin-tae that if he gets a medal he would release his brother, and Jin-tae becomes the braver soldier in the company. Along the bloody war between brothers, the relationship of Jin-seok with his older brother deteriorates leading to a dramatic and tragic end.
제목: Operation Chromite
줄거리: A squad of soldiers fight in the Korean War's crucial Battle of Incheon.
"""
- (논문) pdf 내용 검색
!pip install PyPDF2
import PyPDF2
client = chromadb.PersistentClient(path='./chroma_db')
# client.delete_collection('papers')
collection = client.get_or_create_collection(name='papers')
papers = [
{'id':'1', 'title':'딥러닝', 'path':'./data/deep_learning.pdf'},
{'id':'2', 'title':'자연어처리', 'path':'./data/nlp_paper.pdf'}
]
def extract_text_from_pdf(path):
with open(path, 'rb') as f:
reader = PyPDF2.PdfReader(f)
text = " ".join({page.extract_text() for page in reader.pages if page.extract_text()})
return text
for paper in papers:
text = extract_text_from_pdf(paper['path'])
embedding = model.encode(text).tolist()
collection.add(
ids=[paper['id']],
embeddings=[embedding],
metadatas=[{'title': paper['title']}],
documents=[text]
)
# collection.get()
collection.get(
include=['embeddings', 'documents', 'metadatas']
)
query_text = 'Natural Language'
query_embed = model.encode(query_text).tolist()
results = collection.query(query_embeddings=[query_embed], n_results=1)
# results
results['metadatas'][0][0]['title'] # '자연어처리'
실습 (FAISS DB)
Facebook AI Similarity Search
!pip install faiss-cpu
- 영화 추천 시스템
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
movies = [
{"id": 0, "title": "인셉션", "description": "꿈속에서 다른 사람의 생각을 조작하는 스토리"},
{"id": 1, "title": "어벤져스: 엔드게임", "description": "어벤져스가 타노스를 물리치기 위한 마지막 전투를 벌이는 이야기"},
{"id": 2, "title": "기생충", "description": "부유한 가정과 가난한 가정의 충격적인 만남을 그린 사회적 드라마"},
{"id": 3, "title": "해리 포터와 마법사의 돌", "description": "마법의 세계에 입학한 소년 해리 포터의 첫 번째 모험"},
{"id": 4, "title": "반지의 제왕: 왕의 귀환", "description": "반지를 파괴하기 위한 최후의 전투가 벌어지는 이야기"},
{"id": 5, "title": "스타워즈: 제국의 역습", "description": "제국의 추격을 피해 도망치는 반란군들의 이야기"},
{"id": 6, "title": "타이타닉", "description": "침몰하는 타이타닉 호에서의 사랑과 비극적인 운명을 그린 이야기"},
{"id": 7, "title": "쇼생크 탈출", "description": "불법적인 이유로 감옥에 갇힌 남자가 탈출을 계획하는 이야기"},
{"id": 8, "title": "인터스텔라", "description": "우주를 넘어서 인류의 생존을 위한 탐험을 그린 이야기"},
{"id": 9, "title": "블랙 팬서", "description": "와칸다 왕국의 새로운 왕이 되어가는 이야기를 그린 슈퍼히어로 영화"},
{"id": 10, "title": "어드벤처 타임", "description": "모험과 상상력으로 가득한 세계에서 주인공들이 펼치는 이야기"},
{"id": 11, "title": "혹성 탈출", "description": "인간과 유인원의 관계를 그린 고전적인 SF 영화"},
{"id": 12, "title": "죽은 시인의 사회", "description": "고등학교 교사와 학생들이 펼치는 삶의 의미를 찾는 이야기"},
{"id": 13, "title": "올드보이", "description": "13년간 감금된 남자가 복수를 위해 싸우는 미스터리 영화"},
{"id": 14, "title": "아이언맨", "description": "억만장자 천재 발명가가 슈퍼히어로로 변신하는 이야기"},
{"id": 15, "title": "포레스트 검프", "description": "특별한 능력 없이 살아온 한 남자의 삶을 그린 감동적인 이야기"},
{"id": 16, "title": "카카오", "description": "기업 카카오의 창업과 성공 스토리를 그린 영화"},
{"id": 17, "title": "디지털 포레스트", "description": "인터넷과 디지털 기술로 뒤얽힌 사회와 인간의 관계를 그린 영화"},
{"id": 18, "title": "미션 임파서블: 고스트 프로토콜", "description": "특수 요원들이 전 세계에서 벌어지는 테러를 막기 위해 고군분투하는 이야기"},
{"id": 19, "title": "어둠의 경로", "description": "밤마다 악몽을 겪는 주인공이 자신만의 진실을 찾아가는 스릴러 영화"}
]
descriptions = [movie['description'] for movie in movies]
[model.encode(description) for description in descriptions]
import numpy as np
descriptions = [movie['description'] for movie in movies]
desc_embed = np.array([model.encode(description) for description in descriptions], dtype='float32')
import faiss
dim = desc_embed.shape[1]
idx = faiss.IndexFlatL2(dim)
idx.add(desc_embed)
query_text = '탈출하는 이야기'
query_embed = np.array([model.encode(query_text)], dtype='float32')
top_n = 5
distance, indices = idx.search(query_embed, top_n)
print(distance) # [[0.8666453 0.8801925 0.9008679 0.9916291 1.0403186]]
print(indices) # [[ 0 13 4 3 18]]
for i in range(top_n):
movie_id = indices[0][i]
print(f'{i+1}번째 추천: {movies[movie_id]["title"]} (유사도 거리: {distance[0][i]:.2f})')
"""
1번째 추천: 인셉션 (유사도 거리: 0.87)
2번째 추천: 올드보이 (유사도 거리: 0.88)
3번째 추천: 반지의 제왕: 왕의 귀환 (유사도 거리: 0.90)
4번째 추천: 해리 포터와 마법사의 돌 (유사도 거리: 0.99)
5번째 추천: 미션 임파서블: 고스트 프로토콜 (유사도 거리: 1.04)
"""
- 사용자 맞춤 뉴스 추천
# 키워드를 받아서 네이버 뉴스 제목 크롤링 (상위 10건만)
import requests
from bs4 import BeautifulSoup
keyword = input('뉴스 검색 키워드 입력: ')
url = f"https://search.naver.com/search.naver?ssc=tab.news.all&where=news&sm=tab_jum&query={keyword}"
response = requests.get(url)
html = response.text
bs = BeautifulSoup(html, 'html.parser')
news_contents = bs.select('.fds-news-item-list-tab > div')
news_list = []
for idx, news_content in enumerate(news_contents):
title_tag = news_content.select_one('span.sds-comps-text-type-headline1')
title = title_tag.text
news_list.append(title)
# 임베딩 모델 로드 및 임베딩 처리
model = SentenceTransformer('all-MiniLM-L6-v2')
titles = [news.title for news in news_list]
title_embeddings = np.array(model.encode(titles), dtype='float32')
# FAISS index 생성 및 데이터 추가
dimension = title_embeddings.shape[1]
index = faiss.IndexFlatL2(dimension)
index.add(title_embeddings)
# 질의 텍스트 임베딩
query_embedding = np.array([model.encode(keyword)], dtype='float32')
# 검색
top_n = 2
distances, indices = index.search(query_embedding, top_n)
print('🤖 AI 추천 뉴스')
print(f'"{keyword}" 키워드와 가장 관련 높은 뉴스 2건입니다.')
print()
for i in range(top_n):
recommended_news_index = indices[0][i]
print(f"{titles[recommended_news_index]} (유사도 거리: {distance[0][i]:.2f})")
"""
# 롯데자이언츠 검색 시
🤖 AI 추천 뉴스
"롯데자이언츠" 키워드와 가장 관련 높은 뉴스 2건입니다.
롯데자이언츠, 5개월 연속 정상…프로야구상 '팬심 대세' 굳히기 [8월 네... (유사도 거리: 0.87)
야구, 숫자로 말하다③ - 숫자 너머 흐름 읽는다, 롯데 자이언츠 박주현... (유사도 거리: 0.88)
"""
실습 (Pinecone DB)
Starter plan 사용 (FREE plan)
!pip install pinecone
from pinecone import Pinecone, ServerlessSpec
from dotenv import load_dotenv
import os
load_dotenv()
PINECONE_API_KEY = os.getenv('PINECONE_API_KEY')
pc = Pinecone(api_key=PINECONE_API_KEY)
index_name = 'reviewtest'
pc.create_index(
name=index_name,
dimension=384,
metric='cosine',
spec=ServerlessSpec(
cloud='aws',
region='us-east-1'
)
)
reviews = [
{"id": "1", "text": "이 노트북 정말 좋아요! 속도도 빠르고 디자인도 마음에 들어요.", "sentiment": "positive"},
{"id": "2", "text": "디스플레이가 너무 어두워요. 화면이 선명하지 않아서 불편합니다.", "sentiment": "negative"},
{"id": "3", "text": "배터리가 오래가고, 가벼워서 휴대하기 좋아요.", "sentiment": "positive"},
{"id": "4", "text": "노트북이 자주 끊겨서 작업하기 불편해요. 비추천합니다.", "sentiment": "negative"},
{"id": "5", "text": "이 노트북은 성능이 뛰어나고 게임도 잘 돌아가요. 매우 만족합니다.", "sentiment": "positive"},
{"id": "6", "text": "키보드가 불편하고, 손목이 아파요. 다음에는 다른 제품을 선택할 것 같아요.", "sentiment": "negative"},
{"id": "7", "text": "가성비 최고! 가격에 비해 성능이 훌륭해요.", "sentiment": "positive"},
{"id": "8", "text": "정말 실망이에요. 화면이 자주 깜빡거려서 사용하기 힘들어요.", "sentiment": "negative"},
{"id": "9", "text": "디자인이 예쁘고, 성능도 괜찮아요. 오랜 시간 사용해도 발열이 적어요.", "sentiment": "positive"},
{"id": "10", "text": "구매하고 나서 몇 달 안 가서 고장이 나버렸어요. 수리비가 비쌉니다.", "sentiment": "negative"},
{"id": "11", "text": "어떤 작업이든 다 잘 처리되네요. 게임이나 영상 편집도 무리 없이 가능해요.", "sentiment": "positive"},
{"id": "12", "text": "가격에 비해 성능이 너무 부족해요. 작업할 때 자주 느려져서 짜증이 나요.", "sentiment": "negative"},
{"id": "13", "text": "이 노트북은 디자인도 멋지고, 속도도 빠릅니다. 정말 만족스러워요!", "sentiment": "positive"},
{"id": "14", "text": "사용 중 너무 뜨겁고, 소음도 심해요. 절대 추천하지 않습니다.", "sentiment": "negative"},
{"id": "15", "text": "경량이라 이동할 때 매우 편리하고, 배터리도 오래가요. 좋은 선택이었어요.", "sentiment": "positive"},
{"id": "16", "text": "불량품이 왔어요. 화면에 이상이 생겨서 교환 요청했지만 번거로웠어요.", "sentiment": "negative"},
{"id": "17", "text": "좋은 제품입니다! 화면이 선명하고, 멀티태스킹도 잘 되네요.", "sentiment": "positive"},
{"id": "18", "text": "구매 후 사용 중 멈춤 현상이 자주 발생하고, 해결이 안 돼요.", "sentiment": "negative"},
{"id": "19", "text": "너무 빠르고 효율적이에요! 업무용으로 딱 좋은 제품입니다.", "sentiment": "positive"},
{"id": "20", "text": "키보드가 너무 불편하고, 자주 오작동이 일어나요. 실망했습니다.", "sentiment": "negative"}
]
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
idx = pc.Index(index_name)
for review in reviews:
review_embed = model.encode(review['text']).tolist()
idx.upsert([
(
review['id'],
review_embed,
{
'text': review['text'],
'sentiment': review['sentiment']
}
)
])
- 고객 리뷰 분석 및 감성 분석
query_text = '이 노트북의 성능은 괜찮은가요?'
query_embed = model.encode(query_text).tolist()
results = idx.query(
vector=query_embed,
top_k=3,
include_metadata=True
)
results
"""
{'matches': [{'id': '13',
'metadata': {'sentiment': 'positive',
'text': '이 노트북은 디자인도 멋지고, 속도도 빠릅니다. 정말 만족스러워요!'},
'score': 0.752987862,
'values': []},
{'id': '9',
'metadata': {'sentiment': 'positive',
'text': '디자인이 예쁘고, 성능도 괜찮아요. 오랜 시간 사용해도 발열이 적어요.'},
'score': 0.725770056,
'values': []},
{'id': '14',
'metadata': {'sentiment': 'negative',
'text': '사용 중 너무 뜨겁고, 소음도 심해요. 절대 추천하지 않습니다.'},
'score': 0.7131567,
'values': []}],
'namespace': '',
'usage': {'read_units': 1}}
"""
query_text = '이 노트북의 성능은 괜찮은가요?'
query_embed = model.encode(query_text).tolist()
results = idx.query(
vector=query_embed,
top_k=5,
include_metadata=True
)
sentiment_counts = {
'positive': 0,
'negative': 0
}
for review in results['matches']:
sentiment = review['metadata']['sentiment']
sentiment_counts[sentiment] += 1
print('positive' if sentiment_counts['positive'] > sentiment_counts['negative'] else 'negative')
# positive
# query_text를 부정적인 내용으로 바꾸면, negative로 결과가 나옴
'LLM' 카테고리의 다른 글
| 3.3.14 [LLM] 프롬프트 엔지니어링 응용: RAG (1) | 2025.09.13 |
|---|---|
| 3.3.13 [LLM] 프롬프트 엔지니어링 응용: LangChain (2) | 2025.09.13 |
| 3.3.11 [LLM] Ollama (3) | 2025.09.02 |
| 3.3.10 [LLM] 실습 (OpenAI API) (4) | 2025.09.01 |
| 3.3.9 [LLM] 기초 및 응용 (10) | 2025.09.01 |