ELSER 파이프라인
원본 보기ELSER 파이프라인
Elastic Learned Sparse EncodeR(ELSER)는 Elastic이 학습시킨 NLP 모델로, 희소 벡터 표현을 사용해 시맨틱 검색을 수행할 수 있게 해줍니다. 시맨틱 검색은 검색어를 문자 그대로 매칭하는 대신, 검색 쿼리의 의도와 문맥적 의미를 기반으로 결과를 가져옵니다.
이 튜토리얼의 지침은 ELSER를 사용해 데이터에 시맨틱 검색을 수행하는 방법을 보여줍니다.
Elastic Stack에서 시맨틱 검색을 수행하는 가장 쉬운 방법은 semantic_text 엔드투엔드 튜토리얼을 참고하세요.
ELSER를 사용한 시맨틱 검색에서는 필드당 추출된 토큰 중 처음 512개만 고려됩니다. 자세한 내용은 이 페이지를 참고하세요.
ELSER로 시맨틱 검색을 수행하려면 클러스터에 해당 NLP 모델이 배포되어 있어야 합니다. 모델을 다운로드하고 배포하는 방법은 ELSER 문서를 참고하세요.
배포 오토스케일링이 꺼져 있는 경우, Elastic Cloud Hosted에서 ELSER 모델을 배포하고 사용하기 위한 전용 ML 노드의 최소 크기는 4GB입니다. 오토스케일링을 켜면 수요에 따라 배포가 리소스를 동적으로 조정할 수 있으므로 켜는 것을 권장합니다. 할당 수를 늘리거나 할당당 스레드 수를 늘리면 더 나은 성능을 얻을 수 있으며, 이를 위해서는 더 큰 ML 노드가 필요합니다. 오토스케일링은 필요할 때 더 큰 노드를 제공합니다. 오토스케일링이 꺼져 있다면 적절한 크기의 노드를 직접 제공해야 합니다.
먼저, 대상 인덱스(모델이 텍스트를 기반으로 생성한 토큰을 담는 인덱스)의 매핑을 만들어야 합니다. 대상 인덱스에는 ELSER 출력을 색인하기 위해 sparse_vector 또는 rank_features 필드 타입을 가진 필드가 있어야 합니다.
ELSER 출력은 sparse_vector 또는 rank_features 필드 타입의 필드로 인제스트해야 합니다. 그렇지 않으면 Elasticsearch는 토큰-가중치 쌍을 문서 내의 엄청나게 많은 필드로 해석합니다. "Limit of total fields [1000] has been exceeded while adding new fields"와 비슷한 오류가 발생한다면, ELSER 출력 필드가 제대로 매핑되지 않았고 sparse_vector나 rank_features가 아닌 다른 필드 타입을 갖고 있다는 뜻입니다.
PUT my-index
{
"mappings": {
"properties": {
"content_embedding": {
"type": "sparse_vector"
},
"content": {
"type": "text"
}
}
}
}
- 생성된 토큰을 담을 필드의 이름입니다. 다음 단계의 추론 파이프라인 구성에서 이 이름을 참조해야 합니다.
- 토큰을 담을 필드는
sparse_vector필드입니다. - 희소 벡터 표현을 생성할 원본 필드의 이름입니다. 이 예제에서 필드 이름은
content입니다. 다음 단계의 추론 파이프라인 구성에서 이 이름을 참조해야 합니다. - 이 예제에서는 text인 필드 타입입니다.
공간을 최적화하는 방법은 문서 소스에서 ELSER 토큰을 제외해 디스크 공간 절약하기와 inference 프로세서를 참고해, 파이프라인에서 인제스트되는 데이터에 대해 ELSER로 추론을 수행하세요.
PUT _ingest/pipeline/elser-v2-test
{
"processors": [
{
"inference": {
"model_id": ".elser_model_2",
"input_output": [
{
"input_field": "content",
"output_field": "content_embedding"
}
]
}
}
]
}
- 추론 과정의
input_field와 추론 결과를 담을output_field를 정의하는 구성 객체입니다.
이 단계에서는 이후 추론 인제스트 파이프라인에서 토큰을 추출할 대상이 될 데이터를 적재합니다.
MS MARCO Passage Ranking 데이터셋의 부분집합인 msmarco-passagetest2019-top1000 데이터셋을 사용합니다. 이 데이터셋은 200개의 쿼리로 구성되며, 각 쿼리에는 관련 텍스트 구절 목록이 함께 제공됩니다. 해당 데이터셋에서 중복 없는 모든 구절을 ID와 함께 추출해 tsv 파일로 정리했습니다.
msmarco-passagetest2019-top1000 데이터셋은 모델 학습에 사용되지 않았습니다. 이 튜토리얼에서는 시연 목적으로 쉽게 구할 수 있기 때문에 이 샘플 데이터셋을 사용합니다. 다른 데이터셋을 사용해 워크플로를 테스트하고 익혀도 됩니다.
파일을 다운로드한 뒤 UI의 File Uploader를 사용해 클러스터에 업로드하세요. 데이터 분석이 끝나면 Override settings를 클릭합니다. Edit field names 아래에서 첫 번째 열에 id, 두 번째 열에 content를 지정합니다. Apply를 클릭한 다음 Import를 클릭합니다. 인덱스 이름을 test-data로 지정하고 Import를 클릭합니다. 업로드가 완료되면 182,469개의 문서를 가진 test-data라는 인덱스를 확인할 수 있습니다.
ELSER를 추론 모델로 사용하는 추론 파이프라인을 통해 데이터를 리인덱싱하여 텍스트로부터 토큰을 생성합니다.
POST _reindex?wait_for_completion=false
{
"source": {
"index": "test-data",
"size": 50
},
"dest": {
"index": "my-index",
"pipeline": "elser-v2-test"
}
}
- 리인덱싱의 기본 배치 크기는 1000입니다.
size를 더 작은 값으로 줄이면 리인덱싱 과정의 업데이트가 더 빨라지므로 진행 상황을 면밀히 추적하고 오류를 조기에 발견할 수 있습니다.
이 호출은 진행 상황을 모니터링할 수 있는 태스크 ID를 반환합니다:
GET _tasks/<task_id>
Trained Models UI를 열고 ELSER 아래의 Pipelines 탭을 선택해 진행 상황을 확인할 수도 있습니다.
대용량 데이터셋의 리인덱싱은 오래 걸릴 수 있습니다. 데이터셋의 일부만 사용해 이 워크플로를 테스트할 수 있습니다. 리인덱싱 과정을 취소하고, 리인덱싱된 부분집합에 대해서만 임베딩을 생성하면 됩니다. 다음 API 요청은 리인덱싱 태스크를 취소합니다:
POST _tasks/<task_id>/_cancel
시맨틱 검색을 수행하려면 sparse_vector 쿼리를 사용하고, 쿼리 텍스트와 ELSER 모델에 연결된 추론 ID를 제공하세요. 아래 예제는 쿼리 텍스트로 "How to avoid muscle soreness after running?"를 사용하며, content_embedding 필드에는 생성된 ELSER 출력이 들어 있습니다:
GET my-index/_search
{
"query":{
"sparse_vector":{
"field": "content_embedding",
"inference_id": "my-elser-endpoint",
"query": "How to avoid muscle soreness after running?"
}
}
}
결과는 my-index 인덱스에서 쿼리 텍스트와 의미상 가장 가까운 상위 10개 문서를 관련도 순으로 정렬한 것입니다. 결과에는 관련 검색 결과 각각에 대해 추출된 토큰과 그 가중치도 포함됩니다. 토큰은 관련성을 포착하도록 학습된 연관 관계이며, 동의어가 아닙니다. 토큰이 무엇인지 자세히 알아보려면 이 페이지를 참고하세요. 소스에서 토큰을 제외할 수도 있으며, 자세한 내용은 이 섹션을 참고하세요.
"hits": {
"total": {
"value": 10000,
"relation": "gte"
},
"max_score": 26.199875,
"hits": [
{
"_index": "my-index",
"_id": "FPr9HYsBag9jXmT8lEpI",
"_score": 26.199875,
"_source": {
"content_embedding": {
"muscular": 0.2821541,
"bleeding": 0.37929374,
"foods": 1.1718726,
"delayed": 1.2112266,
"cure": 0.6848574,
"during": 0.5886185,
"fighting": 0.35022718,
"rid": 0.2752442,
"soon": 0.2967024,
"leg": 0.37649947,
"preparation": 0.32974035,
"advance": 0.09652356,
(...)
},
"id": 1713868,
"model_id": ".elser_model_2",
"content": "For example, if you go for a run, you will mostly use the muscles in your lower body. Give yourself 2 days to rest those muscles so they have a chance to heal before you exercise them again. Not giving your muscles enough time to rest can cause muscle damage, rather than muscle development."
}
},
(...)
]
}
sparse_vector를 복합 쿼리 안에서 다른 쿼리와 결합할 수 있습니다. 예를 들어 Boolean 쿼리의 filter 절을 사용하거나, sparse_vector 쿼리와 같은(또는 다른) 쿼리 텍스트로 전문 검색 쿼리를 사용할 수 있습니다. 이렇게 하면 두 쿼리의 검색 결과를 결합할 수 있습니다.
sparse_vector 쿼리의 검색 히트는 다른 Elasticsearch 쿼리보다 높은 점수를 받는 경향이 있습니다. 이런 점수는 boost 파라미터로 각 쿼리의 관련도 점수를 높이거나 낮춰 정규화할 수 있습니다. sparse_vector 쿼리는 관련성이 낮은 결과가 긴 꼬리로 이어지는 경우 재현율이 높을 수 있습니다. min_score 파라미터를 사용해 관련성이 낮은 문서를 걸러내세요.
GET my-index/_search
{
"query": {
"bool": {
"should": [
{
"sparse_vector": {
"field": "content_embedding",
"inference_id": "my-elser-endpoint",
"query": "How to avoid muscle soreness after running?",
"boost": 1
}
},
{
"query_string": {
"query": "toxins",
"boost": 4
}
}
]
}
},
"min_score": 10
}
sparse_vector쿼리와query_string쿼리 모두bool쿼리의should절에 들어 있습니다.sparse_vector쿼리의boost값은 기본값인1입니다. 즉 이 쿼리 결과의 관련도 점수는 부스팅되지 않습니다.query_string쿼리의boost값은4입니다. 이 쿼리 결과의 관련도 점수가 높아져 검색 결과에서 더 상위에 노출됩니다.- 점수가
10이상인 결과만 표시됩니다.
ELSER가 생성한 토큰은 sparse_vector 쿼리에서 사용하려면 색인되어야 합니다. 하지만 그 텀들을 문서 소스에 보관할 필요는 없습니다. source exclude 매핑을 사용해 문서 소스에서 ELSER 텀을 제거하면 디스크 공간을 절약할 수 있습니다.
리인덱싱은 문서 소스를 사용해 대상 인덱스를 채웁니다. ELSER 텀을 소스에서 제외하고 나면 리인덱싱으로 복구할 수 없습니다. 소스에서 토큰을 제외하는 것은 공간을 절약하는 최적화이며, 앞으로 리인덱싱이 필요하지 않다고 확신하는 경우에만 적용해야 합니다! 이 트레이드오프를 신중히 고려하고, ELSER 텀을 소스에서 제외하는 것이 구체적인 요구사항과 사용 사례에 부합하는지 확인하는 것이 중요합니다. _source에서 토큰을 제외했을 때 발생할 수 있는 결과를 자세히 알아보려면 _source 필드 비활성화와 _source에서 필드 포함/제외하기 섹션을 꼼꼼히 검토하세요.
content_embedding을 _source 필드에서 제외하는 매핑은 다음 API 호출로 만들 수 있습니다:
PUT my-index
{
"mappings": {
"_source": {
"excludes": [
"content_embedding"
]
},
"properties": {
"content_embedding": {
"type": "sparse_vector"
},
"content": {
"type": "text"
}
}
}
}
데이터에 따라 track_total_hits: false를 사용하면 sparse_vector 쿼리가 더 빠를 수 있습니다.
elasticsearch-labs저장소에는 Elasticsearch Python 클라이언트를 사용해 ELSER 기반 시맨틱 검색을 실행하는 대화형 예제가 있습니다.