예전에 읽었던 랭체인 도서에서 RAG 구현에 대한 설명이 있었는데,
여기서는 sparse retrieval로 1차 필터링 한 후 -> dense retrieval로 최종 결과를 선정하였다.
나도 그 아이디어를 쫓아 Hybrid Retrieval을 구현해보고자 한다.
1. 문서 검색(Sparse Retrieval)
: 역색인을 이용한 검색. 단순 matching
def sparse_retrieve(query_str, size):
query = { "match": {"content": {"query": query_str}}}
return es.search(index='test', query=query, size=size, sort='_score')
2. 유사도 검색(Dense Retrieval)
: 보다 정교한(의미적 유사성 반영) 유사도 계산
함수를 2개 만들었다.
# dense_retrieve: Vector 유사도를 이용한 검색
def dense_retrieve(query_str, size):
query_embedding = get_embedding([query_str])[0] # 벡터 유사도 검색에 사용할 쿼리 임베딩 가져오기
knn = {"field": "embeddings", # KNN을 사용한 벡터 유사성 검색을 위한 매개변수 설정
"query_vector": query_embedding.tolist(),
"k": size, # k: 검색할 최근접 이웃의 개수
"num_candidates": 100} # KNN 검색에 사용할 후보 문서 개수
return es.search(index='test', knn=knn) # 지정된 인덱스에서 벡터 유사도 검색 수행
# dense_retrieve_2: sparse_retrieve 함수로 필터링된 문서들에 대해서만 KNN 검색
def dense_retrieve_2(query_str, documents, size):
query_embedding = get_embedding([query_str][0])
document_ids = [doc['_id'] for doc in documents]
# document_embeddings = [doc['_source']['embeddings'] for doc in documents]
knn_query = {
"field": "embeddings",
"query_vector": query_embedding.tolist(),
"k": size,
"num_candidates": len(documents)
}
# 미리 필터링된 문서들에 대해서만 'test' 인덱스에서 KNN 검색 수행
# KNN 검색을 위해 적절한 body 구조 생성
body = {
"_source": {"includes": ["content", "embeddings"]},
"query": {
"bool": {
"filter": [
{"terms": {"_id": document_ids}}
]
}
},
"knn": knn_query
}
# 실제로 반환된 결과의 개수를 제한하기 위해 size 파라미터를 사용
response = es.search(index='test', body=body, size=size)
return es.search(index='test', body=body)
예제로 확인해보자
# 검색엔진에 색인이 잘 되었는지 테스트하기 위한 질의
test_query = "직류와 교류 전류의 차이에 대해 알려줘"
# sparse_retrieve
search_result_retrieve = sparse_retrieve(test_query, 10)
# 결과 출력 테스트
for rst in search_result_retrieve['hits']['hits']:
print('score:', rst['_score'], 'source:', rst['_source']['content'])
score: 13.403629 source: 직류 전압을 측정하는 데에는 여러 가지 방법이 있습니다. 그 중에서도 전위차계 방법은 전압계를 사용한 직접 측정보다 더 정확한 결과를 얻을 수 있습니다. 이는 전위차계 방법이 회로에 부하를 전혀 주지 않기 때문입니다. 전압계를 사용한 직접 측정은 회로에 직접적으로 연결되어 전류를 흐르게 하기 때문에 회로에 부하를 주게 됩니다. 하지만 전위차계 방법은 회로에 부하를 주지 않고도 전압을 정확하게 측정할 수 있습니다. 이는 전압계의 내부 회로가 매우 높은 입력 임피던스를 가지고 있기 때문입니다. 따라서 전위차계 방법을 사용하면 정확한 직류 전압 측정이 가능하며, 회로에 부하를 주지 않는 장점을 가지고 있습니다.
score: 12.6978855 source: 네 개의 동일한 교류 발전기가 병렬로 작동하고 있으며, 각각의 등급은 20 MVA이고, 전압은 11 KV입니다. 이 발전기들은 초기 과도 리액턴스가 16%로 설정되어 있습니다. 이러한 설정에서 모선에서의 단락 전류 레벨은 500 MVA입니다.
score: 11.086004 source: 특정 네트워크의 모든 요소가 선형인 경우, 중첩원리가 유지되려면 여자가 AC 또는 DC이어야 한다. 선형 네트워크는 전기 회로에서 매우 중요한 개념입니다. 선형 네트워크는 모든 요소가 선형적으로 연결되어 있는 네트워크를 의미합니다. 이러한 선형 네트워크에서는 겹쳐 맞춤의 정리가 유지되어야 합니다. 겹쳐 맞춤의 정리란, 네트워크의 어떤 부분을 분리하고 다시 연결해도 전체 네트워크의 특성이 변하지 않는 것을 말합니다. 여기서 여자가 AC 또는 DC이어야 한다는 것은, 선형 네트워크에서 전원의 종류가 교류(AC) 또는 직류(DC)여야 한다는 의미입니다. 선형 네트워크에서는 전원의 종류가 중요한 역할을 합니다. AC와 DC는 전기 신호의 특성이 다르기 때문에, 선형 네트워크에서는 이러한 특성을 고려하여 전원의 종류를 선택해야 합니다. 따라서, 특정 네트워크의 모든 요소가 선형인 경우, 중첩원리가 유지되려면 여자가 AC 또는 DC이어야 한다는 결론을 얻을 수 있습니다.
score: 11.01634 source: 미국에서는 남부와 나머지 지역 사이에서 학교 인종차별 철폐에 대한 지지도 차이가 존재했습니다. 이러한 차이는 대략 40% 정도였습니다. 남부 지역에서는 학교 인종차별 철폐에 대한 지지도가 상대적으로 낮았으며, 이에 반해 나머지 지역에서는 더 높은 지지도를 보였습니다. 이러한 차이는 사회적, 경제적, 문화적인 요인들로 인해 발생했을 수 있습니다. 남부 지역은 과거에 인종차별이 심각하게 이루어진 지역으로 알려져 있으며, 이로 인해 학교 인종차별 철폐에 대한 인식이 낮을 수 있습니다. 반면에 나머지 지역은 상대적으로 인종차별이 적은 지역으로 알려져 있어 학교 인종차별 철폐에 대한 인식이 더 높을 수 있습니다. 이러한 지지도 차이는 미국 사회의 다양성과 인종관계에 대한 이해와 대화의 필요성을 강조합니다. 미국은 다양한 인종과 문화가 공존하는 나라로서, 학교 인종차별 철폐에 대한 노력이 계속되어야 합니다.
score: 10.741834 source: 2극 중권 직류기에서 도체 한 개의 저항이 2 Ω이고 도체의 총수는 100이다. 따라서, 총 저항은 50Ω입니다. 이는 도체의 저항이 병렬로 연결되어 있기 때문에 발생하는 현상입니다. 중권 직류기는 전기를 안정적으로 공급하기 위해 사용되며, 도체의 저항은 전기의 흐름을 제한하는 역할을 합니다. 따라서, 도체의 저항이 증가하면 전기의 흐름이 감소하게 되어 총 저항이 증가하게 됩니다. 이러한 원리를 이용하여 중권 직류기에서 도체의 총 저항을 계산할 수 있습니다. 따라서, 이 문제에서 주어진 조건에 따라 도체 한 개의 저항이 2 Ω이고 도체의 총수가 100이므로 총 저항은 50Ω입니다.
score: 10.037293 source: [Ru(CN)6]2-에 대한 [Ru(H2O)6]2+의 화학 이동은 0 ppm 내지 16,050 ppm이다. [Ru(H2O)6]2+, [Ru(CN)6]2- 및 [Ru(NH3)6]2+의 전자 전달 에너지는 각각 18,900, 40,000 및 25,600 cm-1이다. 화학 이동 차이가 상자성 전류의 영향을 주로 받는다고 가정하면, 우리는 [Ru(NH3)6]2+의 화학 이동을 8,090 ppm으로 계산할 수 있다.
score: 9.647073 source: 전구가 병렬로 연결된 회로에서는 각 전구의 전류는 독립적으로 흐르게 됩니다. 따라서 한 전구의 전류가 2A라고 해서 다른 전구의 전류를 정확히 알 수는 없습니다. 전구의 전류는 전압과 저항에 따라 결정되는데, 이 문제에서는 전압과 저항에 대한 정보가 주어지지 않았습니다. 따라서 대답할 만한 정보가 없습니다.
score: 9.587992 source: 힘-전류 유사성에서 대시포트에 대한 전기적 아날로그량은 컨덕턴스이다. 컨덕턴스는 전기 회로에서 전류의 흐름을 측정하는 데 사용되는 물리적인 속성이다. 컨덕턴스는 전기 저항의 역수로 정의되며, 단위는 시멘스(Siemens)이다. 컨덕턴스가 높을수록 전류의 흐름이 쉽게 일어나며, 저항이 낮은 회로일수록 컨덕턴스가 높아진다. 따라서, 대시포트에 대한 전기적 아날로그량은 컨덕턴스로 표현된다.
score: 9.489292 source: 와이어가 전류를 흐르는 와이어가 원형 루프의 형태로 굽혀져 있다면, 와이어의 각 부분 주변에는 자기장이 형성됩니다. 이 자기장은 와이어의 평면에 평행하게 분포됩니다. 와이어의 평면을 기준으로 자기장이 형성되므로, 와이어 주변의 자기장은 와이어의 평면에 평행하게 배치됩니다. 이러한 현상은 전류가 흐르는 와이어 주변에 자기장이 형성되는 일반적인 현상으로 알려져 있습니다. 와이어의 평면에 평행한 자기장은 와이어 주변에서 전류의 흐름을 제어하고, 전류의 방향을 결정하는 역할을 합니다. 따라서, 와이어의 평면에 평행한 자기장은 전류의 흐름과 관련된 중요한 요소입니다.
# desnse_retrieve
search_result_retrieve = dense_retrieve(test_query, 3)
# 결과 출력 테스트
for rst in search_result_retrieve['hits']['hits']:
print('score:', rst['_score'], 'source:', rst['_source']["content"])
score: 0.0030002617 source: 로버트슨 전좌에서 융합이 발생하는 곳은 동원체입니다. 동원체는 다양한 분야에서 융합이 일어나는 중요한 장소입니다. 융합은 서로 다른 분야나 개념이 결합하여 새로운 아이디어나 기술을 만들어내는 과정을 말합니다. 로버트슨 전좌는 이러한 융합이 발생하는 핵심적인 장소로 알려져 있습니다. 동원체에서는 과학, 기술, 예술, 경제 등 다양한 분야의 전문가들이 모여 협업하고 아이디어를 공유하여 혁신적인 결과물을 창출합니다. 이를 통해 사회와 산업의 발전에 기여하고 있습니다. 동원체는 융합의 중요성을 인식하고, 다양한 분야의 전문가들이 함께 일하는 환경을 제공하여 혁신과 창조성을 촉진하고 있습니다. 따라서 로버트슨 전좌에서 융합이 발생하는 곳은 동원체입니다.
score: 0.0029772597 source: 아칸소주를 통과하는 물새 수의 매년 변화를 알아보기 위해서는 다음과 같은 방법을 사용할 수 있습니다. 첫째로, 일 년 중 하루를 선택하여 이동하는 철새의 수를 조사합니다. 둘째로, 주 전역의 서로 다른 10개의 호수에서 새의 수를 셉니다. 이를 10년 동안 매년 같은 날 실시하여 데이터를 수집합니다. 이 방법을 통해 아칸소주를 통과하는 물새 수의 매년 변화를 정확하게 파악할 수 있습니다. 이러한 정보는 철새의 이동 패턴과 서식지 변화 등을 이해하는 데 도움이 될 것입니다.
score: 0.0029594307 source: 다윈은 생물의 적응과 변형에 대한 연구를 통해 현대 생물학의 기초를 마련했습니다. 그는 갈라파고스 제도에서 수집한 다양한 종의 동물들을 관찰하고, 이들이 특정 환경에 적응하면서 어떻게 변형을 겪는지를 연구했습니다. 만약 HMS 비글이 갈라파고스 제도를 완전히 우회했다면, 다윈은 이러한 적응과 변형에 대한 중요한 인사이트를 얻지 못했을 것입니다. 다윈의 연구는 인구의 능력을 보여주었는데, 특정 환경에 적응하면서 생물들은 변형을 겪을 수 있는 능력을 갖고 있다는 것을 밝혀냈습니다. 이는 생물의 진화와 다양성을 이해하는 데에 중요한 역할을 하였습니다. 다윈의 연구는 현대 생물학의 기초를 이루는 중요한 이론 중 하나로 여겨지고 있습니다.
잉...?? dense retrieval의 결과가 더 나쁘다...! 😱
# desnse_retrieve_2
search_result_retrieve = dense_retrieve_2(test_query, search_result_retrieve['hits']['hits'], 3)
# 결과 출력 테스트
for rst in search_result_retrieve['hits']['hits']:
print('score:', rst['_score'], 'source:', rst['_source']["content"])
score: 0.0029594307 source: 다윈은 생물의 적응과 변형에 대한 연구를 통해 현대 생물학의 기초를 마련했습니다. 그는 갈라파고스 제도에서 수집한 다양한 종의 동물들을 관찰하고, 이들이 특정 환경에 적응하면서 어떻게 변형을 겪는지를 연구했습니다. 만약 HMS 비글이 갈라파고스 제도를 완전히 우회했다면, 다윈은 이러한 적응과 변형에 대한 중요한 인사이트를 얻지 못했을 것입니다. 다윈의 연구는 인구의 능력을 보여주었는데, 특정 환경에 적응하면서 생물들은 변형을 겪을 수 있는 능력을 갖고 있다는 것을 밝혀냈습니다. 이는 생물의 진화와 다양성을 이해하는 데에 중요한 역할을 하였습니다. 다윈의 연구는 현대 생물학의 기초를 이루는 중요한 이론 중 하나로 여겨지고 있습니다.
score: 0.0028807586 source: 생물학에서 일부 생물체의 분류 방법이 변경되었습니다. 이제 생물체를 재분류하는 데에는 구조보다는 분자 수준에서의 조사가 사용됩니다. 이 새로운 방법은 생물체의 유전자나 단백질의 구조와 기능을 분석하여 그들의 진화적 관계를 밝히는 데에 큰 도움이 됩니다. 이러한 분자 수준의 조사는 생물체의 유전적 유사성을 파악하고, 서로 다른 종 간의 진화적 연결고리를 찾는 데에 중요한 역할을 합니다. 이 방법은 생물체의 분류를 더욱 정확하고 명확하게 만들어주며, 생물 다양성 연구에도 큰 기여를 하고 있습니다. 이제 구조보다는 분자 수준에서의 조사가 생물체의 재분류에 사용되고 있으며, 이는 생물학의 발전에 새로운 지평을 열어주고 있습니다.
score: 0.0028641922 source: 동물들은 종류에 상관없이 서로를 인식하는 데 어려움을 겪을 수 있습니다. 그러나 동물들은 서로를 인식하도록 돕기 위해 다양한 적응력을 발달시켰습니다. 이 중에서 가장 높은 가능성을 가진 적응력은 체취를 생성하는 능력입니다. 동물들은 체취를 통해 서로의 신원을 파악하고, 터리토리를 구분하며, 파트너를 찾는 등의 다양한 목적으로 사용합니다. 체취는 동물들에게 매우 중요한 의사소통 수단이며, 종간의 인식을 돕는 역할을 합니다. 따라서 동물들은 체취를 생성하는 능력을 통해 서로를 인식하고 소통하는 데 큰 도움을 받고 있습니다.
네???? dense_retrieve_2는 위 sparse_retrieve의 결과물 10개 중에서 KNN으로 최종 3개를 선정해야하는데,sparse_retrieve의 결과물에 없는 내용을 출력하고 있다. 위 dense_retrieve의 결과물과 비슷한걸 보니, sparse_retrieve의 결과물이 전혀 반영되지 않았다. 😣
문제의 원인
: sparse_retrieve의 출력물은 document id를 출력하지 않는데 dense_retrieve_2에서는 검색대상 문서의 document id로 body 구조를 생성하고 있다. document에 id가 없는데, 어떻게 document id를 만드는거지....? 어쨌거나 문제의 원인을 잡아냈으니 다시 함수를 수정한다. 💪
(매달 꼬박꼬박 용돈을 주고 있는 챗지피티와 클로드는 원인을 잡아내지 못했다 😡)
ElasticSearch의 KNN 쿼리와 bool 쿼리의 상호작용방식 문제
: KNN 쿼리가 bool 쿼리의 필터를 완전히 존중하지 않는 경우가 있음
-> KNN이 아닌 script_score를 사용하여 코사인유사도 계산하기로 함
def dense_retrieve_2(query_str, documents, size):
query_embedding = get_embedding([query_str])[0]
document_ids = [doc['_id'] for doc in documents]
body = {
"_source": {"includes": ["content", "embeddings"]},
"query": {
"bool": {
"must": [
{
"ids": {
"values": document_ids
}
},
{
"script_score": {
"query": {"match_all": {}},
"script": {
"source": "cosineSimilarity(params.query_vector, 'embeddings') + 1.0",
"params": {"query_vector": query_embedding.tolist()}
}
}
}
]
}
}
}
results = es.search(index='test', body=body, size=size)
# 결과를 스코어에 따라 정렬
sorted_results = sorted(results['hits']['hits'], key=lambda x: x['_score'], reverse=True)
# 상위 size개의 결과만 반환
return {'hits': {'hits': sorted_results[:size]}}
: dense_retrieve_2 함수 수정
sparse_retrieve의 결과물 10 개 중 dense_retrieve_2의 결과물 3개가 선정되어 출력되는 것을 확인.
원했던 대로 hybrid retrieval 구현 성공! 😎
'RAG' 카테고리의 다른 글
Barclays Bank와의 채팅 (1) | 2024.07.24 |
---|---|
[RAG] Information Retrieval 대회: 스코어가 안나온다... (0) | 2024.06.26 |
[RAG] AutoRAG 설명 (0) | 2024.06.24 |
[RAG] Retrieval 평가지표 (0) | 2024.06.24 |