|
|
import ast
|
|
|
import pandas as pd
|
|
|
import numpy as np
|
|
|
from langchain_huggingface import HuggingFaceEmbeddings
|
|
|
|
|
|
model_name = "BAAI/bge-small-zh-v1.5"
|
|
|
model_kwargs = {'device': 'cpu'}
|
|
|
encode_kwargs = {'normalize_embeddings': True} # set True to compute cosine similarity
|
|
|
|
|
|
|
|
|
|
|
|
bge_hf_embedding = HuggingFaceEmbeddings(
|
|
|
model_name=model_name,
|
|
|
model_kwargs=model_kwargs,
|
|
|
encode_kwargs=encode_kwargs
|
|
|
)
|
|
|
|
|
|
# 【2】该函数就是把数据变为向量的函数
|
|
|
def text_2_embedding(text):
|
|
|
resp = bge_hf_embedding.embed_documents(
|
|
|
[text]
|
|
|
)
|
|
|
return resp[0]
|
|
|
|
|
|
# 【1】读取原始文件中的美食评论数据,通过调用Embedding模型,得到对应的向量,并保持到新文件中
|
|
|
def embedding_2_file(source_file, output_file):
|
|
|
"""读取原始的美食评论数据,通过调用Embedding模型,得到向量,并保持到新文件中"""
|
|
|
# 步骤:1、准备数据,并读取,从第index_col个字段读取
|
|
|
# pandas 是 Python 中用于数据处理和分析的核心工具,专门解决表格类数据的各类操作需求。
|
|
|
# pandas 支持读取几乎所有常见的结构化数据格式(Excel、CSV、SQL、JSON 等),并能将处理后的数据便捷地保存为这些格式,解决了数据导入导出的基础问题。
|
|
|
df = pd.read_csv(source_file, index_col=0)
|
|
|
# 读取后提取所需要的字段
|
|
|
df = df[['Time', 'ProductId', 'UserId', 'Score', 'Summary', 'Text']]
|
|
|
|
|
|
print(df.head(2)) # 打印前两行看看
|
|
|
|
|
|
# 步骤2: 清洗数据 和 合并数据
|
|
|
df = df.dropna() # 如果有空数据,就删掉
|
|
|
# 把评论的摘要 和 内容字段 合并成 一个字段(方便后续处理),放入新字段text_content
|
|
|
df['text_content'] = 'Summary: ' + df.Summary.str.strip() + "; Text: " + df.Text.str.strip()
|
|
|
print(df.head(2)) # 打印前两行看看 ,发现确实增加一个字段text_content
|
|
|
|
|
|
# 步骤3: 对text_content向量化,存到一个新的文件中,向量单独存入embedding字段
|
|
|
df['embedding'] = df.text_content.apply(lambda x: text_2_embedding(x))
|
|
|
# 保存到新文件
|
|
|
df.to_csv(output_file)
|
|
|
|
|
|
|
|
|
def cosine_distance(a, b):
|
|
|
"""计算余弦距离"""
|
|
|
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
|
|
|
|
|
|
# 【3】input:用户输入的问题 ,embedding_file 要检索的文件, top_n 按照相似性最多检索3个
|
|
|
def search_text(input, embedding_file, top_n=3):
|
|
|
"""
|
|
|
根据用户输入的问题,进行语义检索,返回最相似的前top_n个结果
|
|
|
:param input:
|
|
|
:param top_n:
|
|
|
:return:
|
|
|
"""
|
|
|
# 读取新文件
|
|
|
df_data = pd.read_csv(embedding_file)
|
|
|
# 取文件中df_data['embedding']字段,该字段在文件中是以字符串存储的
|
|
|
# 所以你从文件读取出来这个字段也是字符串类型的啊
|
|
|
# 要把这个字符串变成向量进行后续的数学计算,保持到新字段embedding_vector
|
|
|
# 目前这个新字段是在内存中的,没有在文件里
|
|
|
df_data['embedding_vector'] = df_data['embedding'].apply(ast.literal_eval)
|
|
|
|
|
|
|
|
|
# 把输入问题转化为向量
|
|
|
input_vector = text_2_embedding(input)
|
|
|
|
|
|
|
|
|
# 内存中embedding_vector字段和input_vector进行相似度比较
|
|
|
# 按照余弦相似度比较,余弦相似度单独封装到函数 cosine_distance中
|
|
|
# 产生一个新字段 similarity
|
|
|
df_data['similarity'] = df_data.embedding_vector.apply(lambda x: cosine_distance(x, input_vector))
|
|
|
|
|
|
res = (
|
|
|
# 对similarity字段排序,降序排
|
|
|
df_data.sort_values('similarity', ascending=False)
|
|
|
# 返回topn,此案例是:返回3个
|
|
|
.head(top_n)
|
|
|
# 把text_content字段的Summary: 替换为"",把; Text: 替换为""
|
|
|
.text_content.str.replace('Summary: ', "") # text_content是字段名
|
|
|
.str.replace('; Text: ', ';')
|
|
|
)
|
|
|
|
|
|
# 把topn的结果打印,每打印一行画个虚线
|
|
|
for r in res:
|
|
|
print(r)
|
|
|
print('-' * 30)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
# 可以先单独测试函数embedding_2_file('../datas/fine_food_reviews_1k.csv', '../datas/output_embedding.csv')
|
|
|
search_text('delicious beans', './output_embedding.csv') |