--- title: NetsPresso_QA app_file: run_ralm_netspresso_doc.py sdk: gradio sdk_version: 3.41.2 --- # Text retrieval inference (indexing, search) ## Installation 1. 저장소 다운로드 ```bash git clone https://github.com/nota-github/np_app_text_retrieval_inference ``` 2. 모델 환경이 정의된 도커 이미지 생성 및 실행 ```bash cd np_app_semantic_search_inference docker build --cache-from notadockerhub/np_app_text_retrieval_inference:latest -t notadockerhub/np_app_text_retrieval_inference:latest -f ./Dockerfile . docker run --name {container_name} --shm-size=8g -it --gpus '"device=0"' -v {your_code_dir}:/root/np_app_text_retrieval_inference -v /{your_data_dir}:/workspace/datasets notadockerhub/np_app_text_retrieval_inference:latest ``` * retrieval시에는 gpu가 BERT 기반의 query encoding시에만 사용됩니다. 전체 시간에서는 적은 비율을 차지하므로 cpu만 사용해도 속도에서 큰 차이는 없습니다. * 원하는 문서들을 indexing하는 경우 BERT를 이용하여 일회성으로 encoding하는데, 이 경우는 gpu를 사용하면 cpu보다 많은 시간을 절약할 수 있습니다. * 현재 구현에서는 single gpu 사용만을 지원하고 있으며, multi gpu 사용을 위해서는 individual process를 만들어서 병렬로 처리해야 합니다. * 대부분의 코드는 [pyserini](https://github.com/castorini/pyserini)에 기반하고 있습니다. ## Dataset ``` datasets |-- dataset_name | |-- collection.jsonl | |-- queries.tsv | |-- qrels.txt (optional, 정량평가를 원할 경우) ``` * collection.jsonl: each line is `{"id": "PASSAGE_ID", "contents": "CONTENTS"}`. * queries.tsv: each line is `QUERY_ID\tCONTENTS`. * qrels.txt: each line is `QUERY_ID QUERY_TYPE PASSAGE_ID RELEVANCE_SCORE`. ## Recommended retriever * sparse model: BM25 * dense model * multi-lingual: mDPR, mContriever * multi-vector: colBERT * hybrid model: sparse (first-pass) + dense (reranking) * 다국어를 encode하는 baseline 모델은 `castorini/mdpr-question-nq`을 사용. * 언어별 다양한 pre-trained 모델은 [HuggingFace model hub](https://huggingface.co/models)에서 검색 해볼 수 있음. ## Sample dataset * [mrtydi-korean](https://github.com/castorini/mr.tydi) * 11개 언어를 포함한 다국어 검색을 위한 benchmark dataset * 한국어의 경우 1496126개의 passage와 421개의 test query를 제공함 * `title`과 `text`를 포함한 multi-field를 활용할 수 있음 (일반적으로는 `text`만 사용 가능) * [data hub](https://www.notion.so/notaai/Data-Hub-V1-Current-Version-45b0b0aa62084b3e985244ebb264d444?pvs=4)에서 원본 데이터 및 indexing된 결과물을 다운받을 수 있음. * @data_hub:/ssd2/np_app/Dataset_Hub/Semantic_Search/{corpus,indexes} ## Procedure ### 1. Indexing * Fast retrieval을 위해서 collection의 passage에 대한 indexing을 미리 계산함 * indexing 과정은 미리 만들둔것을 사용해도 됨 * mrtydi-korean의 경우 data hub에 존재 * [pre-built indexes for benchmark corpus/model](https://github.com/castorini/pyserini/blob/master/docs/prebuilt-indexes.md)
* dense model ``` python -m pyserini.encode \ input --corpus /path/to/dataset/collection.jsonl \ --fields text \ output --embeddings indexes/dataset_name/dense \ --to-faiss \ encoder --encoder huggingface_model_name_or_checkpoint_path \ --fields text \ --max-length $MAX_LENGTH \ --batch $BATCH_SIZE \ --fp16 ``` * huggingface_model_name_or_checkpoint_path: huggingface model hub에서 제공하는 모델 이름 또는 checkpoint path * e.g., mrtydi의 경우: `castorini/mdpr-passage-nq` 사용 (retrieval시의 query encoding: `castorini/mdpr-question-nq`) * tied(vs. split)의 경우 passage/query encoder가 같음(vs. 다름) * sparse model ``` python -m pyserini.index.lucene \ --collection JsonCollection \ --input datasets/dataset_name/collection.jsonl \ --index /path/to/indexing/sparse \ --fields text \ --generator DefaultLuceneDocumentGenerator \ --language $LANG_CODE \ --threads $NUM_THREADS \ --storePositions --storeDocvectors --storeRaw ``` * language code의 경우 ISO 639-1 방식을 따름 (e.g., en, ko, ja, zh) * multifield를 활용할 경우 collection의 "contents"의 텍스트내에 field들을 \n으로 구분하고, --fields에 field 이름들(i.e., --fields title text)을 넣어줌. * mrtydi의 경우 delimiter를 '\n\n'으로 사용함 ``` {"id": "5#1", "contents": "지미 카터\n\n지미 카터는 조지아주 섬터 카운티 플레인스 마을에서 태어났다. 조지아 공과대학교를 졸업하였다. 그 후 해군에 들어가 전함·원자력·잠수함의 승무원으로 일하였다. 1953년 미국 해군 대위로 예편하였고 이후 땅콩·면화 등을 가꿔 많은 돈을 벌었다. 그의 별명이 \"땅콩 농부\" (Peanut Farmer)로 알려졌다."} ``` * MAX_LENGTH: positional embedding의 최대 길이 (e.g., BERT: 512, DPR: 256) * 결과물 (dir: /path/to/indexing) - docid: sets of passage id - index: concatenation of (compressed) index vectors, binary file ### 2. Search * Indexing된 collection에 대하여 query에 대한 ranking 수행 #### online * with sparse indexing ``` export QUERY="최초로 전기 자동차를 개발한 기업은 어디인가?" python search_online.py --index_type sparse --index /path/to/indexing/sparse --query "$QUERY" --lang_abbr $LANG_CODE ```
결과 예시

 1 1830196#0       21.52590
{
  "id" : "1830196#0",
  "contents" : "창안 자동차(, )는 중화인민공화국의 자동차 제조 기업이다. 본사는 충칭 시에 있다. 디이 자동차, 둥펑 자동차, 상하이 자동차, 체리 자동차와 함께 중화인민공화국의 5대 자동차 제조 기업으로 여겨진다. 중화인민공화국의 자동차 제조 및 판매, 자동차 엔진 제품 제조 업체이다. 1862년 상하이 시에서 이홍장에 의해 설립되었으며 1950년대 말에 지프를 최초로 생산하면서 자동차 제조 기업이 되었다. 1996년 10월 31일 법인설립되었고 대표자는 장 바오린이다. 1984년에는 일본의 자동차 제조 기업인 스즈키와 제휴 관계를 수립했고 2001년에는 포드 모터 컴퍼니를 합병하면서 창안 포드 자동차(長安福特汽車)가 설립되었다. 2009년에는 하페이 자동차(哈飛汽車), 창허 자동차(昌河汽車)를 합병했다. 충칭 자동차 생산의 태반은 창안자동차가 담당하고 있다. 창안은 1959년 이후 차를 만들어온 국유기업으로 2차대전의 미군용 지프를 본떠 만든 군용트럭이 시발점이었다. 오늘날 라인업은 전기차 하나를 비롯한 17개 모델로 확대됐다. 7개 조립공장과 1개 엔진공장을 통해 한해 약 100만 대를 만든다. 여기에다가 창안은 포드, 푸조와 스즈키와도 합작하고 있어 한해 생산량은 300만 대에 이른다. 창안자동차는 글로벌연구개발시스템을 가동중에 있다. 현재 충칭, 베이징, 허베이, 허페이, 이탈리아 토리노, 일본 요코하마, 영국 버밍엄, 미국 디트로이트 등지에 연구개발센터를 설립하였다. 우리나라 한온시스템은 독일 폴크스바겐, 중국 창안자동차 등에 친환경차용 전동식 컴프레셔를 납품하고 있다."
}
 2 128660#8        19.02320
{
  "id" : "128660#8",
  "contents" : "1990년대에 들어선 직후 가솔린자동차에 의한 환경오염문제가 대두되었다. 1996년 제너럴 모터스(GM)사는 양산 전기차 1호로 볼 수 있는 'EV1' 전기자동차를 개발한다. 이 전기자동차는 미국 캘리포니아 지역에서 임대형식으로 보급된다. 그러나 GM사는 수요가 크지 않아 수익성이 낮다는 이유로 1년만에 전기자동차 'EV1'의 조립라인을 폐쇄한다."
}
 3 320611#0        18.99790
{
  "id" : "320611#0",
  "contents" : "기아 그랜토(Kia Granto) 또는 아시아 그랜토(Asia Granto)는 1995년에 아시아자동차가 생산한 대형 트럭이다. 기아차가 일본 히노 자동차와 기술 제휴해서 히노 프로피아의 차체로 개발한 대형 트럭이다. 기존의 AM 트럭의 후속 차종으로 개발한 트럭으로, 아시아자동차가 창사 30주년을 기념해서 개발한 트럭이다. 선택 사양으로 ABS 브레이크, 속도 제한 장치, 브레이크 라이닝 간극 자동 조정 장치, 오토 그리스, 튜브형 브레이크 파이프, 전기식 변속기 전환 장치 등을 탑재하였다. 1997년에 대한민국산 트럭 최초로 U자형 적재함을 탑재하였으며, 최고 출력 430마력의 FY(8×4) 23톤 덤프 트럭을 출시하였다. 1999년에 아시아자동차가 기아자동차에게 흡수 합병되었으며, 이후 기아자동차에서 생산하다가 2000년 8월에 배기 가스 규제를 충족시키지 못하여 후속 차종 없이 단종되면서, 기아자동차는 대형 트럭 사업을 스카니아 코리아에 양도함에 따라 대형 트럭의 시장에서 완전히 철수하였다."
}
 4 1226703#1       18.78540
{
  "id" : "1226703#1",
  "contents" : "1845년에 회사를 창립 했으며 독일의 전지형 기중기 생산하는 기업 중 가장 오래되었다. 1868년에 말이 끄는 소방차를 개발했으며 1890년에 최초로 증기 소방 차량을 생산했다. 1914년에 최초로 트럭과 특수 차량을 제작했다. 1918년에 안스바흐 자동차 공장과 뉘르베르크 자동차 공장을 합병했다. 1937년에 3축 트럭을 생산 했으며 1943년에 제2차 세계대전으로 기존 공장이 파괴되면서 새로운 공장을 건설했다. 1956년에 군사 목적을 위해 대형 트력과 장비를 개발했다. 1960년대에 최초로 기중기를 제작하기 시작 했으며 1970년대부터 1980년대까지 개발했다. 1985년에 최대 50톤 용량의 가진 전지형 기중기를 개발했다. 1990년 일본의 기중기 회사였던 타다노에 인수 되었다. 1991년에 일본 수출을 위해 전지형 기중기를 생산했다. 1995년에 회사 창립 150주년이 되었다. 2004년에 최초로 험지형 기중기를 제작한데 이어 2009년에 트럭 기중기를 제작했다. 2013년에 공장을 확장 및 이전하면서 현재에 이르고 있다."
}
 5 1045157#14      18.30410
{
  "id" : "1045157#14",
  "contents" : "2010년 3월 세계최초의 2000cc급 자동차를 위한 15Kw급 BLDC발전기 개발, 전기자동차의 주행거 리 제한 극복 세계최초의 동급 내연이륜차의 성능을 능가하는 전기스쿠터 힐리스 모델출시 및 신차발표회 EV전시장 오픈"
}
 6 128661#7        17.92510
{
  "id" : "128661#7",
  "contents" : "1991년 11월 21일 현대자동차는 한국내에서는 최초의 전기자동차를 독자개발했다고 발표했다."
}
 7 1312657#1       17.78780
{
  "id" : "1312657#1",
  "contents" : "1939년에 이탈리아 나폴리 출신인 빈센조 앙헬레스 게르바지오()와 타예레스 나폴리()에 의해 설립했다. 제2차 세계대전 당시 스페인에서 트럭을 생산하기 위해 차체 및 용접을 했으나, 이후 샤시에 특장 트럭 캡 디자인을 개발했다. 1958년에 최초로 공장이 이전되면서 버스를 생산하기 시작했다. 1960년에 세계 최초로 2층 버스를 생산했다. 1962년에 생산 공장이 재이전 되면서 팩토리아스 나폴리스 SA()에 인수되었다. 이 회사는 상용차를 생산한 업체로 주로 버스와 트럭을 생산했다. 1966년에 바헤이로스 디젤 SA()에 매각했다. 1969년에 다시 크라이슬러에 마각이 되었지만 버스 제조 부문의 경우 1971년에 벨기에의 자동차 제조 기업인 반호르에 매각되었다. 1983년에 반호르가 최대 주주가 되었고 인수 최기에 반호르의 브랜드로 차량 생산을 했지만 이후 이스파노 카로세라 SAL()로 사명이 변경되었다. 1997년에 이탈리아의 자동차 제조 기업인 피닌파라나()와 제휴를 맺고 시내버스 모델인 아비토와 고속버스 모델인 디보를 개발하기 시작했다. 2000년 9월에 모로코의 수도 카사블랑카에 공장을 설립했다. 2005년에 인도의 자동차 제조 기업인 타타자동차가 21%의 지분을 획득한데 이어 2009년에 지분 79%를 인수하면서 자회사가 되었다. 2010년에 현재의 사명으로 변경이 되었다. 2013년 9월에 타타자동차는 사라고사 공장 폐쇄를 발표했다. 매출 하락과 미래 전망이 불투명으로 폐쇄 결정을 내렸다."
}
 8 128660#63       17.71300
{
  "id" : "128660#63",
  "contents" : "후지중공업과 마츠비시 자동차는 2005년 8월에 전기자동차의 개발 계획을 발표하였다. 이 2개 회사가 거의 중지 상태였던 전기자동차의 개발을 재개하고 있다. 2008년에 들어 닛산-르노 연합이 전기자동차로 본격 참여 방침을 표명하였고, 도요타도 2010년대 초반에 전기자동차를 출시하기로 발표하는 등 전기 자동차가 활성화 조짐을 보이고 있다."
}
 9 126891#2        17.63640
{
  "id" : "126891#2",
  "contents" : "2007년, 스웨덴의 대표 자동차 메이커인 볼보는 세계 최초로 에탄올 자동차를 제작해서 자동차 경주에 참가했다. 스웨덴에서는 가솔린 자동차의 도시내 사용을 줄이고, 시민들이 자전거로 생활할 수 있게끔 유도하고 있다. 또한 볼보에서 친환경 자동차를 적극적으로 개발하게 하고, 시민들에게는 친환경 자동차 구입비에 150만 원의 보조금을 지급하며, 연료비는 가솔린의 70% 가격에 주유할 수 있게 하는 등 적극적인 탈석유 정책을 시행하고 있다."
}
10 128660#3        17.29680
{
  "id" : "128660#3",
  "contents" : "전기자동차는 디젤 엔진, 가솔린 엔진을 사용하는 오토사이클(정적사이클)방식의 자동차보다 먼저 고안 되었다. 1830년부터 1840년 사이에 영국 스코틀랜드의 사업가 앤더슨이 전기자동차의 시초라고 할 수 있는 세계 최초의 원유전기마차를 발명한다. 1835년에 네덜란드 크리스토퍼 베커는 작은 크기의 전기자동차를 만든다."
}
* with dense indexing ``` python search_online.py --index_type dense --index /path/to/indexing/dense --query "$QUERY" --encoder huggingface_model_name_or_checkpoint_path --device $DEVICE ``` * DEVICE: 'cpu' or 'cuda:$GPU_ID' * search는 현재는 single gpu만 지원됩니다. multi gpu를 사용하려면 individual process를 만들어서 병렬로 처리해야 합니다. * with hybrid (first-pass: sparse, reranking: dense) indexing ``` python search_online.py --index_type hybrid --index /path/to/indexing/sparse,/path/to/indexing/dense --query "$QUERY" --encoder huggingface_model_name_or_checkpoint_path --device $DEVICE --alpha $ALPHA_MULTIPLIED_ON_SPARSE_SCORE --normalization --lang_abbr $LANG_CODE ``` * ALPHA_MULTIPLIED_ON_SPARSE_SCORE는 (0,2)에서 line search를 하면서 최적의 값을 찾으며 0.5가 기본값입니다. #### batch * with dense indexing ``` python -m pyserini.search.faiss \ --encoder huggingface_model_name_or_checkpoint_path \ --index /path/to/indexing_dense \ --topics datasets/dataset_name/queries.tsv \ --output /path/to/runfile --batch $BATCH_SIZE --threads $NUM_THREADS \ --hits $TOPK --remove-query --device $DEVICE ``` * BATCH_SIZE, NUM_THREADS는 기본값을 64, 16으로 사용합니다. * with sparse indexing ``` python -m pyserini.search.lucene --bm25 \ --topics datasets/dataset_name/queries.tsv \ --index /path/to/indexing_sparse \ --hits $TOPK \ --language $LANG_CODE \ --output /path/to/runfile ``` * hybrid model ``` python -m pyserini.search.hybrid \ dense --index /path/to/indexing_dense \ --encoder huggingface_model_name_or_checkpoint_path \ --device $DEVICE \ sparse --index /path/to/indexing_sprase \ fusion --alpha $ALPHA_MULTIPLIED_ON_SPARSE_SCORE \ run --topics datasets/dataset_name/queries.jsonl \ --output /path/to/runfile \ --threads $NUM_THREADS \ --batch-size $BATCH_SIZE \ --hits $TOPK python -m pyserini.search.hybrid \ dense --index path/to/indexing/dense \ --encoder huggingface_model_name_or_checkpoint_path \ --device $DEVICE \ sparse --index /path/to/indexing/sprase \ fusion --alpha $ALPHA_MULTIPLIED_ON_SPARSE_SCORE \ run --topics datasets/dataset_name/queries.tsv \ --output runs/hybrid.run \ --threads $NUM_THREADS \ --batch-size $BATCH_SIZE \ --hits 1000 ``` * 결과물 (dir: /path/to/runfile) format: qid q_type pid topK score retrieval_type example: ``` 46 Q0 271267 1 2.134944 Faiss 46 Q0 63734 2 2.118700 Faiss 46 Q0 174045 3 2.110519 Faiss ... ``` ### 3. Evaluation (optional) * **qrels** 파일은 정량평가를 위한 ground truth 파일로, qid q_type pid relevance_score 형식으로 구성되어 있음. * **runfile**은 batch로 검색한 결과로, qid q_type pid topK score retrieval_type 형식으로 구성되어 있음. * 아래 스크립트는 qrels 파일과 runfile을 비교하여 nDCG@10, MRR@100, Recall@100 등의 지표를 계산함. ``` python -m pyserini.eval.trec_eval -c -mndcg_cut.10 -mrecip_rank -mrecall.100 /path/to/qrels /path/to/runfile recip_rank all 0.3628 recall_100 all 0.7158 ndcg_cut_10 all 0.3805 ```