# pip install -U FlagEmbeddingfromFlagEmbeddingimportBGEM3FlagModelmodel=BGEM3FlagModel("BAAI/bge-m3",use_fp16=True)sentences=["What is BM25?","Definition of BM25"]output=model.encode(sentences,return_dense=True,return_sparse=True)# The dense embedding of the two texts above:dense_embedding=output['dense_vecs']# [[-0.02501617 -0.04525582 -0.01972856 ... -0.0099566 0.01677509# 0.01292699]# [-0.0106499 -0.04731942 -0.01477693 ... -0.0149255 0.01040097# 0.00965083]sparse_weight_id=output['lexical_weights']# You can see the weight for each token:# print(model.convert_id_to_token(sparse_weight_id))# # [{'What': 0.10430284, 'is': 0.10090457, 'BM': 0.2635918, '25': 0.3382988, '?': 0.052101523}, # {'Definition': 0.14298248, 'of': 0.076763615, 'BM': 0.2577639, '25': 0.33806682}]
通过将文本输入到像 BGE-M3 这样的稀疏嵌入模型中,我们可以轻松获得稀疏嵌入。
# pip install -U numpyimportnumpyasnp# Sparse vector embedding of textsparse_indices=[[int(token_id)fortoken_idintext]fortextinsparse_weight_id]# [[4865, 83, 90017, 2588, 32], [155455, 111, 90017, 2588]]sparse_values=[[float(text[token_id])fortoken_idintext]fortextinsparse_weight_id]# [[0.10430284, 0.10090457, 0.2635918, 0.3382988, 0.052101523], [0.14298248, 0.076763615, 0.2577639, 0.33806682]]# Sparse indexes must be ascending for each pgvecto.rsforiinrange(len(sparse_weight_id)):sparse_values[i]=[xfor_,xinsorted(zip(sparse_indices[i],sparse_values[i]))]sparse_indices[i]=sorted(sparse_indices[i])
# pip install -U psycopgimportpsycopgURL="postgresql://postgres:mysecretpassword@localhost:5432/postgres"withpsycopg.connect(URL)asconn:conn.execute("DROP TABLE IF EXISTS documents;")conn.execute(f"""
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
text TEXT NOT NULL,
dense vector(1024) NOT NULL,
sparse svector({len(model.tokenizer)}) NOT NULL);
""")
表创建完成后,我们就可以将嵌入插入表中,并为向量列创建索引。
# pip install -U pgvecto_rsimportpsycopgfrompgvecto_rs.psycopgimportregister_vectorURL="postgresql://postgres:mysecretpassword@localhost:5432/postgres"withpsycopg.connect(URL)asconn:register_vector(conn)fortext,dense,sparse_ind,sparse_valinzip(sentences,dense_embedding,sparse_indices,sparse_values):conn.execute("INSERT INTO documents (text, dense, sparse) VALUES (%s, %s, to_svector(%s, %s::real[]::int[], %s::real[]));",(text,dense,len(model.tokenizer),sparse_ind,sparse_val),)conn.execute("""
CREATE INDEX ON documents
USING vectors (sparse svector_dot_ops)
WITH (options = \"[indexing.hnsw]\");
""")conn.execute("""
CREATE INDEX ON documents
USING vectors (dense vector_l2_ops)
WITH (options = \"[indexing.hnsw]\");
""")
defembedding(sentence:str)->tuple[np.ndarray,list[list[int]],list[list[float]]]:"""
Create dense and sparse embeddings from text
"""output=model.encode(sentence,return_dense=True,return_sparse=True,return_colbert_vecs=False)dense_embedding=output['dense_vecs']sparse_weight_id=output['lexical_weights']sparse_indices=[int(token_id)fortoken_idinsparse_weight_id]sparse_values=[float(sparse_weight_id[token_id])fortoken_idinsparse_weight_id]sparse_values=[xfor_,xinsorted(zip(sparse_indices,sparse_values))]sparse_indices=sorted(sparse_indices)returndense_embedding,sparse_indices,sparse_values
dense_embedding,sparse_indices,sparse_values=embedding("The text you want to search...")withpsycopg.connect(URL)asconn:register_vector(conn)cur=conn.execute("SELECT text FROM documents ORDER BY sparse <#> to_svector(%s, %s::real[]::int[], %s::real[]) LIMIT 50;",(len(model.tokenizer),sparse_indices,sparse_values),)sparse_result=cur.fetchall()cur=conn.execute("SELECT text FROM documents ORDER BY dense <-> %s LIMIT 50;",(dense_embedding,),)dense_result=cur.fetchall()# Merge sparse and dense vector search resultsmix_text=set([r[0]forrindense_result]).union([r[0]forrinsparse_result])
fromFlagEmbeddingimportFlagRerankerreranker=FlagReranker('BAAI/bge-reranker-large',use_fp16=True)# Obtain relevance scores (Higher scores indicate greater relevance)scores=reranker.compute_score([["The text you want to search...",candidate]forcandidateinmix_text])# Rerank text using combined dense and sparse embeddingsreranked_text=[tfor_,tinsorted(zip(scores,mix_text))]