在本文中,我們使用預(yù)訓(xùn)練的 BERT 模型和 Elasticsearch 來構(gòu)建搜索引擎。 Elasticsearch 最近發(fā)布了帶有向量場(chǎng)的文本相似性(text similarity search with vector field)搜索。 另一方面,你可以使用 BERT 將文本轉(zhuǎn)換為固定長(zhǎng)度的向量。 因此,一旦我們將文檔通過 BERT 轉(zhuǎn)換為向量并存儲(chǔ)到 Elasticsearch 中,我們就可以使用 Elasticsearch 和 BERT 搜索相似的文檔。
這篇文章通過以下架構(gòu)實(shí)現(xiàn)了一個(gè)帶有 Elasticsearch 和 BERT 的搜索引擎。 在這里,我們使用 Docker 將整個(gè)系統(tǒng)分為三個(gè)部分:應(yīng)用程序、BERT 和 Elasticsearch。 目的是使擴(kuò)展每個(gè)服務(wù)更容易。
?整個(gè)系統(tǒng)是在 docker-compose.yamlin 中編寫的,位于以下 GitHub 存儲(chǔ)庫(kù)中。 請(qǐng)查看存儲(chǔ)庫(kù):GitHub - liu-xiao-guo/bertsearch: Elasticsearch with BERT for advanced document search.
$ pwd
/Users/liuxg/python/bertsearch
$ tree -L 3
.
├── LICENSE
├── README.md
├── bertserving
│?? ├── Dockerfile
│?? └── entrypoint.sh
├── docker-compose.yaml
├── docs
│?? ├── architecture.png
│?? ├── diagram.key
│?? └── example.png
├── example
│?? ├── __init__.py
│?? ├── create_documents.py
│?? ├── create_index.py
│?? ├── example.csv
│?? ├── index.json
│?? ├── index_documents.py
│?? └── requirements.txt
└── web
├── Dockerfile
├── app.py
├── requirements.txt
└── templates
└── index.html
請(qǐng)注意:在本文的展示中,我使用 TensorFlow 來進(jìn)行展示。更對(duì)關(guān)于 Pytorch 的展示,請(qǐng)參閱我之前的文章 “Elastic:開發(fā)者上手指南” 中的 “NLP - 自然語言處理” 部分。另外,由于 TensorFlow 里的指令限制,該展示不支持 Apple chipset 的電腦。你需要在 Intel 運(yùn)行的機(jī)器上運(yùn)行。
這篇文章的計(jì)劃是:
- 下載預(yù)訓(xùn)練的 BERT 模型
- 設(shè)置環(huán)境變量
- 啟動(dòng) Docker 容器
- 創(chuàng)建 Elasticsearch 索引
- 創(chuàng)建文件
- 索引文件
安裝
你需要安裝好自己的 Docker 環(huán)境。你需要安裝自己的 Python。需要在版本 3.0 及以上。另外,為了能夠讓項(xiàng)目里的 Python 能夠正常運(yùn)行,你需要按照如下的命令來安裝如下的庫(kù):
pip install bert_serving_server
pip install bert_serving_client
你需要確保這里安裝的庫(kù)和 docker-compose.yml 里的所定義的庫(kù)的版本是一致的。
web/requirements.txt
bert-serving-client==1.10.0
elasticsearch==8.6.1
Flask==2.2.2
bertserving/Dockerfile
FROM tensorflow/tensorflow:1.12.0-py3
RUN pip install -U pip
RUN pip install --no-cache-dir bert-serving-server==1.10.0
COPY ./ /app
COPY ./entrypoint.sh /app
WORKDIR /app
ENTRYPOINT ["/app/entrypoint.sh"]
CMD []
example/requirements.txt
bert-serving-client==1.10.0
elasticsearch==7.0.4
pandas==0.25.1
如上所示,我們選擇的 bert-serving-client 及 bert-serving-server 的版本都是 1.10.0。
在本展示中,我將使用最新的 Elastic Stack 8.6.1 來進(jìn)行展示,但是在 Elasticsearch 的配置中,我不使用安全配置。
docker-compose.yml
version: '3.9'
services:
web:
build: ./web
ports:
- "5100:5100"
environment:
- INDEX_NAME
depends_on:
- elasticsearch
- bertserving
networks:
- elastic
elasticsearch:
container_name: elasticsearch
image: elasticsearch:8.6.1
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms1g -Xmx1g
- xpack.security.enabled=false
volumes:
- es_data:/usr/share/elasticsearch/data
ports:
- target: 9200
published: 9200
networks:
- elastic
kibana:
container_name: kibana
image: kibana:8.6.1
ports:
- target: 5601
published: 5601
depends_on:
- elasticsearch
networks:
- elastic
bertserving:
container_name: bertserving
build: ./bertserving
ports:
- "5555:5555"
- "5556:5556"
environment:
- PATH_MODEL=${PATH_MODEL}
volumes:
- "${PATH_MODEL}:/model"
networks:
- elastic
volumes:
es_data:
driver: local
networks:
elastic:
name: elastic
driver: bridge
下載 pre-trained BERT 模型
首先,下載預(yù)訓(xùn)練的 BERT 模型。 以下命令是下載英文模型的示例:
wget https://storage.googleapis.com/bert_models/2018_10_18/cased_L-12_H-768_A-12.zip
unzip cased_L-12_H-768_A-12.zip
$ pwd
/Users/liuxg/python/bertsearch
$ wget https://storage.googleapis.com/bert_models/2018_10_18/cased_L-12_H-768_A-12.zip
--2023-02-27 09:40:16-- https://storage.googleapis.com/bert_models/2018_10_18/cased_L-12_H-768_A-12.zip
Resolving storage.googleapis.com (storage.googleapis.com)... 142.251.36.16
Connecting to storage.googleapis.com (storage.googleapis.com)|142.251.36.16|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 404261442 (386M) [application/zip]
Saving to: ‘cased_L-12_H-768_A-12.zip’
cased_L-12_H-768_A- 100%[===================>] 385.53M 16.0MB/s in 24s
2023-02-27 09:40:41 (16.0 MB/s) - ‘cased_L-12_H-768_A-12.zip’ saved [404261442/404261442]
$ unzip cased_L-12_H-768_A-12.zip
Archive: cased_L-12_H-768_A-12.zip
creating: cased_L-12_H-768_A-12/
inflating: cased_L-12_H-768_A-12/bert_model.ckpt.meta
inflating: cased_L-12_H-768_A-12/bert_model.ckpt.data-00000-of-00001
inflating: cased_L-12_H-768_A-12/vocab.txt
inflating: cased_L-12_H-768_A-12/bert_model.ckpt.index
inflating: cased_L-12_H-768_A-12/bert_config.json
$ ls
LICENSE cased_L-12_H-768_A-12 docs
README.md cased_L-12_H-768_A-12.zip example
bertserving docker-compose.yaml web
從上面,我們可以看出來在當(dāng)前的目錄里創(chuàng)建了一個(gè)叫做?cased_L-12_H-768_A-12 的子目錄。它將在下面的 bertserving 容器中被使用到。
設(shè)置環(huán)境變量
你需要將預(yù)訓(xùn)練的 BERT 模型和 Elasticsearch 的索引名稱設(shè)置為環(huán)境變量。 這些變量在 Docker 容器中使用。 下面是一個(gè)將 jobsearch 指定為索引名稱并將 ./cased_L-12_H-768_A-12 指定為模型路徑的示例:?
export PATH_MODEL=./cased_L-12_H-768_A-12
export INDEX_NAME=jobsearch
啟動(dòng) docker
現(xiàn)在,讓我們使用 Docker compose 啟動(dòng) Docker 容器。 這里要啟動(dòng)三個(gè)容器:應(yīng)用程序容器、BERT 容器和 Elasticsearch 容器。我們按照如下的命令來啟動(dòng)所有的容器:
docker-compose up
一旦成功啟動(dòng)完畢后,我們可以用在 http://localhost:9200 來訪問 Elasticsearch,并在地址 http://localhost:5601 訪問 Kibana。
$ curl http://localhost:9200
{
"name" : "d996f0e69e91",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "KiXF66HWSw2RSEXTiHKP2Q",
"version" : {
"number" : "8.6.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "180c9830da956993e59e2cd70eb32b5e383ea42c",
"build_date" : "2023-01-24T21:35:11.506992272Z",
"build_snapshot" : false,
"lucene_version" : "9.4.2",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
我們可以通過如下的命令來查看所有的真正運(yùn)行的容器:
docker ps
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a043a2b1cabc bertsearch_web "python app.py" 3 minutes ago Up 3 minutes 0.0.0.0:5100->5100/tcp bertsearch_web_1
2c1e20206eff bertsearch_bertserving "/app/entrypoint.sh" 3 minutes ago Up 3 minutes 6006/tcp, 0.0.0.0:5555-5556->5555-5556/tcp, 8888/tcp bertserving
7eb3a9422c50 kibana:8.6.1 "/bin/tini -- /usr/l…" 16 minutes ago Up 16 minutes 0.0.0.0:5601->5601/tcp kibana
d996f0e69e91 elasticsearch:8.6.1 "/bin/tini -- /usr/l…" 16 minutes ago Up 16 minutes 0.0.0.0:9200->9200/tcp, 9300/tcp elasticsearch
從上面,我們可以看出來有四個(gè)正在運(yùn)行的容器。請(qǐng)注意,我建議你為 Docker 分配更多內(nèi)存(超過 8GB)。 因?yàn)?BERT 容器需要大內(nèi)存。
我們的 bertserving 服務(wù)運(yùn)行于 http://localhost:5555,而 web 服務(wù)運(yùn)行于 http://localhost:5100。我們可以在 docker-compose.yml 里進(jìn)行查看。
創(chuàng)建 Elasticsearch 索引
你可以使用創(chuàng)建索引 API 將新索引添加到 Elasticsearch 集群。 創(chuàng)建索引時(shí),你可以指定以下內(nèi)容:
- 索引的設(shè)置
- 索引中字段的映射
- 索引別名
例如,如果要?jiǎng)?chuàng)建包含 title、text 和 text_vector 字段的 jobsearch 索引,可以通過以下命令創(chuàng)建索引:
python example/create_index.py --index_file=example/index.json --index_name=jobsearch
上面書命令在 Elasticsearch 中創(chuàng)建了具有如下配置的一個(gè)叫做 jobsearch 的索引:
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 1
},
"mappings": {
"dynamic": "true",
"_source": {
"enabled": "true"
},
"properties": {
"title": {
"type": "text"
},
"text": {
"type": "text"
},
"text_vector": {
"type": "dense_vector",
"dims": 768
}
}
}
}
我們可以通過如下的命令來查看:
GET jobsearch
注意:text_vector 的 dims 值必須與預(yù)訓(xùn)練的 BERT 模型的 dims 相匹配。
創(chuàng)建文檔
創(chuàng)建索引后,你就可以為一些文檔編制索引了。 這里的重點(diǎn)是使用 BERT 將你的文檔轉(zhuǎn)換為向量。 結(jié)果向量存儲(chǔ)在 text_vector 字段中。 讓我們將你的數(shù)據(jù)轉(zhuǎn)換為 JSON 文檔。在本示例中,我們是用了一個(gè)簡(jiǎn)單的 example.csv 文件:
example/example.csv
"Title","Description"
"Saleswoman","a woman whose job is to sell a product or service in a given territory, in a store, or by telephone"
"Software Developer","Hire Expert Software Engineers and Developers With Crowdbotics"
"Chief Financial Officer","a senior executive responsible for managing the financial actions of a company. "
"General Manager","esponsible for improving efficiency and increasing departmental profits while managing the company’s overall operations."
"Network Administrator","installing, monitoring, troubleshooting, and upgrading network infrastructure, including both hardware and software components"
如上所示,我們的 csv 文件中,含有兩個(gè)字段:Title 及 Description。我們將把 Description 這個(gè)部分向量化,以方便我們下面的搜索。
python example/create_documents.py --data=example/example.csv --index_name=jobsearch
完成腳本后,你可以得到如下的 JSON 文檔:
$ pwd
/Users/liuxg/python/bertsearch
$ ls
LICENSE cased_L-12_H-768_A-12 docs
README.md cased_L-12_H-768_A-12.zip example
bertserving docker-compose.yaml web
$ python example/create_documents.py --data=example/example.csv --index_name=jobsearch
$ ls
LICENSE cased_L-12_H-768_A-12.zip example
README.md docker-compose.yaml web
bertserving docs
cased_L-12_H-768_A-12 documents.jsonl
從上面的輸出中,我們可以看出來,運(yùn)行命令后,當(dāng)前目錄下多了一個(gè)文件??documents.jsonl。它的文件格式如下:
上述格式顯然是易于我們使用 bulk 命令來進(jìn)行批寫入的格式。?
寫入文檔到 Elasticsearch
將數(shù)據(jù)轉(zhuǎn)換為 JSON 后,你可以將 JSON 文檔添加到指定索引并使其可搜索。
python example/index_documents.py
執(zhí)行完上面的命令后,我們可以在 Kibana 中進(jìn)行查看:
GET jobsearch/_search
打開瀏覽器
轉(zhuǎn)到 http://localhost:5100。 下面是一些搜索的例子。
從上面的搜索結(jié)果中,我們可以看出來,盡管我們輸入的詞并不完全匹配文字中的描述,但是它還是給了我們最想要的最為相關(guān)的結(jié)果。這些結(jié)果是按照相關(guān)性進(jìn)行排列顯示的。
上面的搜索,其實(shí)在 web 里是使用了如下的搜索命令:
GET jobsearch/_search
{
"_source": ["text", "title"],
"query": {
"script_score": {
"script": {
"source": "cosineSimilarity(params.query_vector, 'text_vector')",
"params": {
"query_vector": [
0.480839341878891,
-0.3990676701068878,
-0.1494527906179428,
-0.6091867685317993,
-0.014144758693873882,
-0.053846489638090134,
0.727445125579834,
-0.009675377979874611,
-0.29119399189949036,
0.14104360342025757,
0.2982104420661926,
0.5848511457443237,
...
]
}
}
}
}
文章來源:http://www.zghlxwxcb.cn/news/detail-729042.html
特別值得指出的是:在最新的 Elasticsearch 發(fā)布版中,我們可以使用 knn 搜索。具體例子可以參考文章 “Elasticsearch:運(yùn)用 Python 實(shí)現(xiàn)在 Elasticsearch 上的向量搜索”。你可以嘗試修改 web 里的搜索部分來完成這個(gè)練習(xí)。這里就不再展示了。文章來源地址http://www.zghlxwxcb.cn/news/detail-729042.html
到了這里,關(guān)于Elasticsearch:使用 Elasticsearch 和 BERT 構(gòu)建搜索引擎 - TensorFlow的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!