Redis와 Cohere
Redis와 Cohere (통합 가이드)
Cohere와 Redis를 통합해 텍스트 데이터에 대한 유사도 검색을 수행하는 방법을 단계별로 알아볼 거예요.
RedisVL은 Redis를 벡터 데이터베이스로 사용하기 위한 강력하고 전용적인 Python 클라이언트 라이브러리예요. 이 가이드는 위키백과 기사 데이터셋을 사용해 시맨틱 검색용 파이프라인을 구성하면서 Cohere 임베딩을 Redis와 통합하는 방법을 안내해요. 다음을 다룰 거예요.
- Redis 인덱스 설정하기
- 구절(passage)을 임베딩하고 데이터베이스에 저장하기
- 사용자 검색 쿼리를 임베딩하고 Redis 인덱스에서 검색하기
- 쿼리에 다양한 필터링 옵션 탐색하기
전체 코드 샘플을 보려면 이 노트북을 참조하세요. Redis와 Cohere 사용에 대한 자세한 내용은 이 가이드도 참고할 수 있어요.
출처: 문서
사전 요구 사항 (Prerequisites)
이 페이지의 코드 샘플은 다음을 가정해요.
- 로컬에서 docker가 실행 중이어야 함
SHELL
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
패키지 설치하기
필요한 Python 패키지를 설치하고 임포트하세요.
jsonlines: 이 예시에서는 샘플 구절이jsonl파일에 있으므로, jsonlines를 사용해 이 데이터를 환경에 로드할 거예요.redisvl: 버전0.1.0이상인지 확인하세요cohere: 버전4.45이상인지 확인하세요
패키지를 설치하려면 다음 코드를 사용하세요.
SHELL
!pip install redisvl==0.1.0
!pip install cohere==4.45
!pip install jsonlines
필요한 패키지 임포트하기
PYTHON
from redis import Redis
from redisvl.index import SearchIndex
from redisvl.schema import IndexSchema
from redisvl.utils.vectorize import CohereTextVectorizer
from redisvl.query import VectorQuery
from redisvl.query.filter import Tag, Text, Num
import jsonlines
Cohere와 Redis로 검색 파이프라인 구축하기
Schema.yaml 설정하기
Redis 인덱스를 구성하려면 yaml 파일을 지정하거나 딕셔너리를 임포트할 수 있어요. 이 튜토리얼에서는 다음 스키마를 가진 yaml 파일을 사용할 거예요. 이 저장소의 yaml 파일을 사용하거나, 로컬에 다음 구성으로 .yaml 파일을 만들어 주세요.
YAML
version: "0.1.0"
index:
name: semantic_search_demo
prefix: rvl
storage_type: hash
fields:
- name: url
type: text
- name: title
type: tag
- name: text
type: text
- name: wiki_id
type: numeric
- name: paragraph_id
type: numeric
- name: id
type: numeric
- name: views
type: numeric
- name: langs
type: numeric
- name: embedding
type: vector
attrs:
algorithm: flat
dims: 1024
distance_metric: cosine
datatype: float32
이 인덱스의 이름은 semantic_search_demo이고 storage_type: hash를 사용하며, 이는 벡터라이저를 호출할 때마다 as_buffer=True로 설정해야 한다는 뜻이에요. Hash 데이터 구조는 문자열로 직렬화되므로, 임베딩을 hash에 바이트 문자열로 저장해요.
이 가이드에서는 벡터 차원 크기가 1024인 Cohere embed-english-v3.0 model을 사용할 거예요.
Cohere 텍스트 벡터라이저 초기화하기
PYTHON
# create a vectorizer
api_key = "{Insert your cohere API Key}"
cohere_vectorizer = CohereTextVectorizer(
model="embed-english-v3.0",
api_config={"api_key": api_key},
)
임베딩 모델과 API 키를 지정해 CohereTextVectorizer를 만드세요.
Cohere가 제공하는 임베딩 모델과 각 모델의 차원에 대한 자세한 내용은 Cohere의 Embed Models를 참조하세요.
Redis 인덱스 초기화하기
PYTHON
# construct a search index from the schema - this schema is called "semantic_search_demo"
schema = IndexSchema.from_yaml("./schema.yaml")
client = Redis.from_url("redis://localhost:6379")
index = SearchIndex(schema, client)
# create the index (no data yet)
index.create(overwrite=True)
스키마를 yaml 파일에서 임포트하기 때문에 SearchIndex.from_yaml을 사용한다는 점에 유의해 주세요. SearchIndex.from_dict를 사용할 수도 있어요.
CURL
!rvl index listall
위 코드는 인덱스가 생성됐는지 확인해요. 생성됐다면 아래와 같은 출력이 보이게 돼요.
TEXT
15:39:22 [RedisVL] INFO Indices:
15:39:22 [RedisVL] INFO 1. semantic_search_demo
인덱스 내부를 확인해서 원하는 스키마와 일치하는지 확인하세요.
CURL
!rvl index info -i semantic_search_demo
다음과 같은 출력이 보이게 돼요.
Look inside the index to make sure it matches the schema you want:
╭──────────────────────┬────────────────┬────────────┬─────────────────┬────────────╮
│ Index Name │ Storage Type │ Prefixes │ Index Options │ Indexing │
├──────────────────────┼────────────────┼────────────┼─────────────────┼────────────┤
│ semantic_search_demo │ HASH │ ['rvl'] │ [] │ 0 │
╰──────────────────────┴────────────────┴────────────┴─────────────────┴────────────╯
Index Fields:
╭──────────────┬──────────────┬─────────┬────────────────┬────────────────┬────────────────┬────────────────┬────────────────┬────────────────┬─────────────────┬────────────────╮
│ Name │ Attribute │ Type │ Field Option │ Option Value │ Field Option │ Option Value │ Field Option │ Option Value │ Field Option │ Option Value │
├──────────────┼──────────────┼─────────┼────────────────┼────────────────┼────────────────┼────────────────┼────────────────┼────────────────┼─────────────────┼────────────────┤
│ url │ url │ TEXT │ WEIGHT │ 1 │ │ │ │ │ │ │
│ title │ title │ TEXT │ WEIGHT │ 1 │ │ │ │ │ │ │
│ text │ text │ TEXT │ WEIGHT │ 1 │ │ │ │ │ │ │
│ wiki_id │ wiki_id │ NUMERIC │ │ │ │ │ │ │ │ │
│ paragraph_id │ paragraph_id │ NUMERIC │ │ │ │ │ │ │ │ │
│ id │ id │ NUMERIC │ │ │ │ │ │ │ │ │
│ views │ views │ NUMERIC │ │ │ │ │ │ │ │ │
│ langs │ langs │ NUMERIC │ │ │ │ │ │ │ │ │
│ embedding │ embedding │ VECTOR │ algorithm │ FLAT │ data_type │ FLOAT32 │ dim │ 1024 │ distance_metric │ COSINE │
╰──────────────┴──────────────┴─────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴─────────────────┴────────────────╯
로컬호스트 Redis GUI도 방문할 수 있어요. Redis GUI에서 인덱스를 실시간으로 확인할 수 있어요.
문서를 로드하고 Redis로 임베딩하기
PYTHON
# read in your documents
jsonl_file_path = "data/redis_guide_data.jsonl"
corpus = []
text_to_embed = []
with jsonlines.open(jsonl_file_path, mode="r") as reader:
for line in reader:
corpus.append(line)
# we want to store the embeddings of the field called `text`
text_to_embed.append(line["text"])
# call embed_many which returns an array
# hash data structures get serialized as a string and thus we store the embeddings in hashes as a byte string (handled by numpy)
res = cohere_vectorizer.embed_many(
text_to_embed, input_type="search_document", as_buffer=True
)
위키백과에서 가져온 단락을 포함하는 데이터의 일부를 로드할 거예요. 데이터는 jsonl에 있고, 임베딩 대상인 text 필드를 얻으려면 파싱이 필요해요. 이를 위해 파일을 로드해 한 줄씩 읽으면서 corpus 객체와 text_to_embed 객체를 만들고, string 목록을 받는 co.embed_many에 text_to_embed 객체를 전달해요.
인덱스에 삽입할 데이터 준비하기
PYTHON
# contruct the data payload to be uploaded to your index
data = [
{
"url": row["url"],
"title": row["title"],
"text": row["text"],
"wiki_id": row["wiki_id"],
"paragraph_id": row["paragraph_id"],
"id": row["id"],
"views": row["views"],
"langs": row["langs"],
"embedding": v,
}
for row, v in zip(corpus, res)
]
# load the data into your index
index.load(data)
각 단락의 모든 메타데이터를 테이블에 보존하고, 인덱스에 삽입할 딕셔너리 목록을 만들고 싶어요.
이 시점에서 Redis DB는 시맨틱 검색을 할 준비가 됐어요!
Redis DB 쿼리하기
PYTHON
# use the Cohere vectorizer again to create a query embedding
query_embedding = cohere_vectorizer.embed(
"What did Microsoft release in 2015?",
input_type="search_query",
as_buffer=True,
)
query = VectorQuery(
vector=query_embedding,
vector_field_name="embedding",
return_fields=[
"url",
"wiki_id",
"paragraph_id",
"id",
"views",
"langs",
"title",
"text",
],
num_results=5,
)
results = index.query(query)
for doc in results:
print(
f"Title:{doc['title']}\nText:{doc['text']}\nDistance {doc['vector_distance']}\n\n"
)
VectorQuery 클래스를 사용해 쿼리 객체를 만들 수 있어요. 여기서 Redis가 반환할 필드와 결과 수(이 예시에서는 5로 설정)를 지정할 수 있어요.
Redis 필터
태그 필터 추가하기
PYTHON
# Initialize a tag filter
tag_filter = Tag("title") == "Microsoft Office"
# set the tag filter on our existing query
query.set_filter(tag_filter)
results = index.query(query)
for doc in results:
print(
f"Title:{doc['title']}\nText:{doc['text']}\nDistance {doc['vector_distance']}\n"
)
Redis의 특징 중 하나는 쿼리에 필터링을 즉석에서 추가할 수 있다는 점이에요. 여기서는 스키마에서 type=tag로 초기화된 title 열에 tag filter를 구성하고 있어요.
필터 표현식 사용하기
PYTHON
# define a tag match on the title, text match on the text field, and numeric filter on the views field
filter_data = (
(Tag("title") == "Elizabeth II")
& (Text("text") % "born")
& (Num("views") > 4500)
)
query_embedding = co.embed(
"When was she born?", input_type="search_query", as_buffer=True
)
# reinitialize the query with the filter expression
query = VectorQuery(
vector=query_embedding,
vector_field_name="embedding",
return_fields=[
"url",
"wiki_id",
"paragraph_id",
"id",
"views",
"langs",
"title",
"text",
],
num_results=5,
filter_expression=filter_data,
)
results = index.query(query)
print(results)
for doc in results:
print(
f"Title:{doc['title']}\nText:{doc['text']}\nDistance {doc['vector_distance']}\nView {doc['views']}"
)
Redis의 또 다른 특징은 필터 표현식이라는 필터 집합으로 쿼리를 초기화할 수 있다는 점이에요. 필터 표현식은 쿼리 시점에 임의의 필드 집합에 대해 일련의 필터를 결합할 수 있게 해 줘요.