1、引言
在这个信息爆炸的时代,无论是军事爱好者、科研工作者,还是户外探险者,他们都需要快速准确地获取特定装备的信息。装备名称检索推荐系统正是为了应对这一挑战而生。它像一位经验丰富的向导,引领用户穿越复杂的装备海洋,找到最适合自己需求的那件“神器”。本项目采用faiss+uvicorn+fastapi多并发模式。
2、设计思路
2.1、验证输入的装备名称
系统将首先检查用户输入的词汇是否直接匹配我们的装备名称数据库。如果找到匹配项,系统将立即提供相关信息并返回结果。
2.2、检查输入与装备别称的对应关系
若直接名称匹配未果,系统将继续检索用户输入是否与任何装备的别称或俗称相符。一旦确认输入与装备别称相匹配,相应的装备信息将被检索并展示给用户。
2.3、探索向量数据库
如果前两步均未发现匹配项,系统将采用先进的向量搜索技术,深入我们的向量数据库进行更广泛的搜索。这一步骤旨在识别与用户输入在语义上相似的装备,从而提供更为全面和深入的检索结果。本文采用faiss深度学习向量库。
3、测试数据
F-16战斗机 战隼 蝰蛇 毒蛇
A-10攻击机 雷电II 疣猪
F-22猛禽战斗机 猛禽
F-35战斗机 闪电II
米格-29战斗机 支点
苏-27战斗机 侧卫
B-52轰炸机 同温层堡垒
B-2轰炸机 幽灵
C-130运输机 大力神
SR-71侦察机 黑鸟
U-2侦察机 龙女
AH-64武装直升机 阿帕奇
CH-47运输直升机 奇努克
V-22倾转旋翼机 鱼鹰
F-4战斗机 鬼怪II
F-15战斗机 鹰
F/A-18战斗机 大黄蜂
图-95轰炸机 熊
图-160轰炸机 黑色杰克
歼-20战斗机 威龙 炎齿
A-10攻击机 雷电II 疣猪
F-22猛禽战斗机 猛禽
F-35战斗机 闪电II
米格-29战斗机 支点
苏-27战斗机 侧卫
B-52轰炸机 同温层堡垒
B-2轰炸机 幽灵
C-130运输机 大力神
SR-71侦察机 黑鸟
U-2侦察机 龙女
AH-64武装直升机 阿帕奇
CH-47运输直升机 奇努克
V-22倾转旋翼机 鱼鹰
F-4战斗机 鬼怪II
F-15战斗机 鹰
F/A-18战斗机 大黄蜂
图-95轰炸机 熊
图-160轰炸机 黑色杰克
歼-20战斗机 威龙 炎齿
F-16轰炸机 飞龙 雷电II
4.、设置配置
主要代码如下
#!/usr/bin/env python
# -*- coding: utf-8 -*-import argparseclass Args:@staticmethoddef parse():parser = argparse.ArgumentParser()return parser@staticmethoddef initialize(parser):parser.add_argument('--config_dir', default='./config.yaml',help='config')parser.add_argument('--model_dir', default='./sentence_transformers/uer_sbert-base-chinese-nli/', help='mdoel dir for uer')parser.add_argument('--all_weapon_data_dir', default='./test_data.xlsx',help='all weapon data dir')parser.add_argument('--weapon_data_dir', default='./weapon_data.xlsx',help='Weapons and equipment include nicknames and addresses')
......return parserdef get_parser(self):parser = self.parse()parser = self.initialize(parser)return parser.parse_args()
5、生成武器yaml文件方便检索
import yaml
import pandas as pd
from Project_parameters import Args
args = Args().get_parser()class ReadConfig:def __init__(self):passdef read_yaml(self):with open(args.config_dir, "rb") as f:dict = yaml.load(stream=f.read(), Loader=yaml.FullLoader)return dictdef write_yaml(self):with open(args.config_dir, encoding='utf-8', mode='w') as f:df = pd.read_excel(args.weapon_data_dir)for weapon_values in df.values:if weapon_values[1].__len__() == 1:data = [{weapon_values[0]: weapon_values[1]}]else:data = [{weapon_values[0]: weapon_values[1].split("、")}]yaml.dump(data, stream=f, allow_unicode=True)
结果如下
- F-16战斗机:- 战隼- 蝰蛇- 毒蛇
- A-10攻击机:- 雷电II- 疣猪
- F-22猛禽战斗机:- 猛禽
- F-35战斗机:- 闪电II
- 米格-29战斗机:- 支点
- 苏-27战斗机:- 侧卫
- B-52轰炸机:- 同温层堡垒
- B-2轰炸机:- 幽灵
- C-130运输机:- 大力神
- SR-71侦察机:- 黑鸟
- U-2侦察机:- 龙女
- AH-64武装直升机:- 阿帕奇
- CH-47运输直升机:- 奇努克
- V-22倾转旋翼机:- 鱼鹰
- F-4战斗机:- 鬼怪II
- F-15战斗机: 鹰
- F/A-18战斗机:- 大黄蜂
- 图-95轰炸机: 熊
- 图-160轰炸机:- 黑色杰克
- 歼-20战斗机:- 威龙- 炎齿
- F-16轰炸机:- 飞龙- 雷电II
6、检索
#!/usr/bin/env python
# -*- coding: utf-8 -*-import pandas as pd
import time, re
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer
from Read_Config import ReadConfig
from Project_parameters import Args
from logs import Loggingsloggings = Loggings()
args = Args().get_parser()class weapon_and_weapon_alias_search:def __init__(self):pass......return weapon_resclass faiss_vector_text_search:def __init__(self):self.model = SentenceTransformer(args.model_dir)loggings.info(f"model loading completed")def Text_steering_vector(self):df = pd.read_excel(args.all_weapon_data_dir)time.sleep(3)sentences = df['sentence'].tolist()
......def text_search(self, text, top):df = pd.read_excel(args.all_weapon_data_dir)sentences = df['sentence'].tolist()sentence_embeddings = self.model.encode(sentences)dimension = sentence_embeddings.shape[1]index = faiss.IndexFlatL2(dimension)faiss_array = np.load(args.out_pth)index.add(faiss_array)search = self.model.encode([text])query_emb = np.array(search)similars, indices = index.search(query_emb, top)final_res = []print("process finished!")loggings.info(f"top{top}——序号列表: {indices}")loggings.info(f"top{top}——相似度列表: {similars} ")loggings.info(f"top{top}——相似文本索引:")for idx in indices[0]:with open(args.out_index_pth, encoding='utf-8') as f:for i, line in enumerate(f):if i == idx:loggings.info(line.replace("\n", ""))pattern = re.compile(r'text:\s*(.*)')final_res.append(pattern.findall(line))print("----------------------------------------------------------------------------------------------")return final_resclass final_search:def __init__(self):rc = ReadConfig() # 调用Readconfig类的rc.write_yaml()self.config = rc.read_yaml()loggings.info(f"Configuration loading completed")def final_weapon_search(self, text, top):weapon_res = weapon_and_weapon_alias_search().weapon_search(text, self.config)if weapon_res.__len__() == 0:weapon_alias_res = weapon_and_weapon_alias_search().weapon_alias_search(text, self.config)if weapon_alias_res.__len__() == 0:final_res = faiss_vector_text_search().text_search(text, top)return final_reselse:return weapon_alias_reselse:return weapon_resif __name__ == '__main__':args.config_dir = './config.yaml'args.model_dir = './sentence_transformers/uer_sbert-base-chinese-nli/'args.weapon_data_dir = './weapon_data.xlsx'args.all_weapon_data_dir = './test_data.xlsx'args.out_pth = './models/faiss.npy'args.out_index_pth = './models/faiss_index.txt'args.top = 3# faiss_vector_text_search().Text_steering_vector()# i = '阿帕奇'# j = '鹰击'# k = 'F-16'# final_res = final_search().final_weapon_search(i, args.top)# print(final_res)
7、api服务
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
from fastapi import FastAPI, Response
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
import traceback
from final_search import *
app = FastAPI()@app.post("/weapon")
async def get_geo(request_data: dict) -> Response:return_result = {"code": 200, "message": "success", "data": None}try:texts = request_data["text"]top=request_data["top"]loggings.info("ner 入参 " + str({"texts": texts, "top": top}))final_res = final_search().final_weapon_search(texts,top)return_result['data'] = final_resexcept Exception as e:loggings.error(str(e) + "@@@" + traceback.format_exc())return_result["code"] = 400return_result["message"] = str(e)# Use jsonable_encoder to convert the Python object to JSON-compatible datajson_content = jsonable_encoder(return_result)# Pass the JSON-compatible data to JSONResponsereturn JSONResponse(content=json_content, media_type="application/json; charset=utf-8")if __name__ == "__main__":import uvicornip = socket.gethostbyname(socket.getfqdn(socket.gethostname()))uvicorn.run(app='api:app', host= ip, port=6410, workers=4)
8、结果
8.1、基于武器装备名称的检索结果如下:
8.2、基于武器装备别称的检索结果如下:
8.3、基于faiss的检索结果如下:
总结:此项目只作为初步的探讨demo,还有很多的上升空间。例如数据的读入、存储、Embedding模型选择等等。