streamlit-search_demo_solr-2021-05-13-10-05-91.mp4
streamlit-search_demo_elasticsearch-2021-05-14-22-05-55.mp4
This code is described in the following Medium stories, taking one step at a time:
Neural Search with BERT and Solr (August 18,2020)
Fun with Apache Lucene and BERT Embeddings (November 15, 2020)
Speeding up BERT Search in Elasticsearch (March 15, 2021)
Ask Me Anything about Vector Search (June 20, 2021) This blog post gives the answers to the 3 most interesting questions asked during the AMA session at Berlin Buzzwords 2021. The video recording is available here: https://www.youtube.com/watch?v=blFe2yOD1WA
5 ways to increase result diversity at web-scale (Video) Implementation of result diversity algorithms on top of neural search using code in this repository. Streamlit demo
Tech stack:
- Hugging Face
- Solr / Elasticsearch / OpenSearch
- streamlit
- Python 3.8 (upgraded recently)
Code for dealing with Solr and Elasticsearch has been copied from the great (and highly recommended) https://github.com/o19s/hello-ltr project. OpenSearch client is implemented on top of this code and https://github.com/DmitryKey/search_with_machine_learning_course/blob/main/week1/opensearch.py
If you encounter issues with the above installation, consider installing full list of packages:
pip install -r requirements_freeze.txt
This sets up the stage for our further experiment with Solr.
This is by far the key ingredient of every experiment. You want to find an interesting
collection of texts, that are suitable for semantic level search. Well, maybe all texts are. I have chosen a collection of abstracts from DBPedia,
that I downloaded from here: https://wiki.dbpedia.org/dbpedia-version-2016-04 and placed into data/dbpedia
directory in bz2 format.
You don't need to extract this file onto disk: the provided code will read directly from the compressed file.
Before running preprocessing / indexing, you need to configure the vector plugin, which allows to index and query the vector data. You can find the plugin for Solr 8.x here: https://github.com/DmitryKey/solr-vector-scoring
After the plugin's jar has been added, configure it in the solrconfig.xml like so:
<queryParser name="vp" class="com.github.saaay71.solr.VectorQParserPlugin" />
Schema also requires an addition: field of type VectorField
is required in order to index vector data:
<field name="vector" type="VectorField" indexed="true" termOffsets="true" stored="true" termPositions="true" termVectors="true" multiValued="true"/>
Find ready-made schema and solrconfig here: https://github.com/DmitryKey/bert-solr-search/tree/master/solr_conf
Let's preprocess the downloaded abstracts, and index them in Solr. First, execute the following command to start Solr:
bin/solr start -m 2g
If during processing you will notice:
<...>/bert-solr-search/venv/lib/python3.7/site-packages/bert_serving/client/__init__.py:299: UserWarning: some of your sentences have more tokens than "max_seq_len=500" set on the server, as consequence you may get less-accurate or truncated embeddings.
here is what you can do:
- disable the length-check by create a new "BertClient(check_length=False)" when you do not want to display this warning
- or, start a new server with a larger "max_seq_len"
'- or, start a new server with a larger "max_seq_len"' % self.length_limit)
The index_dbpedia_abstracts_solr.py
script will output statistics:
Maximum tokens observed per abstract: 697
Flushing 100 docs
Committing changes
All done. Took: 82.46466588973999 seconds
We know how many abstracts there are:
bzcat data/dbpedia/long_abstracts_en.ttl.bz2 | wc -l
5045733
This project implements several ways to index vector data:
src/index_dbpedia_abstracts_elastic.py
vanilla Elasticsearch: usingdense_vector
data typesrc/index_dbpedia_abstracts_elastiknn.py
Elastiknn plugin: implements own data type. I usedelastiknn_dense_float_vector
src/index_dbpedia_abstracts_opendistro.py
OpenDistro for Elasticsearch: uses nmslib to build Hierarchical Navigable Small World (HNSW) graphs during indexing
Each indexer relies on ready-made Elasticsearch mapping file, that can be found in es_conf/
directory.
In order to use GSI APU solution, a user needs to produce two files: numpy 2D array with vectors of desired dimension (768 in my case) a pickle file with document ids matching the document ids of the said vectors in Elasticsearch.
After these data files get uploaded to the GSI server, the same data gets indexed in Elasticsearch. The APU powered search is performed on up to 3 Leda-G PCIe APU boards. Since I’ve run into indexing performance with bert-as-service solution, I decided to take SBERT approach from Hugging Face to prepare the numpy and pickle array files. This allowed me to index into Elasticsearch freely at any time, without waiting for days. You can use this script to do this on DBPedia data, which allows using:
EmbeddingModel.HUGGING_FACE_SENTENCE (SBERT)
To generate the numpy and pickle files, use the following script: scr/create_gsi_files.py
.
This script produces two files:
data/1000000_EmbeddingModel.HUGGING_FACE_SENTENCE_vectors.npy
data/1000000_EmbeddingModel.HUGGING_FACE_SENTENCE_vectors_docids.pkl
Both files are perfectly suitable for indexing with Solr and Elasticsearch.
To test the GSI plugin, you will need to upload these files to GSI server for loading them both to Elasticsearch and APU.
There are two streamlit demos for running BERT search for Solr and Elasticsearch. Each demo compares to BM25 based search. The following scripts assume either Elasticsearch or Solr running with the index containing field with embeddings.
To run a demo, execute the following on the command line from the project root:
# for experiments with Elasticsearch
streamlit run src/search_demo_elasticsearch.py
# for experiments with Solr
streamlit run src/search_demo_solr.py