patch_intent_cls.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import os
  2. script_dir = os.path.dirname(os.path.abspath(__file__))
  3. import sys
  4. sys.path.append(script_dir)
  5. from intent_description_template import template, intent_code
  6. from remote_model import RemoteBGEModel
  7. import torch
  8. import faiss
  9. import numpy as np
  10. import re
  11. class IntentRecognizer:
  12. def __init__(self):
  13. # 加载元数据
  14. self.template_meta = {}
  15. for k, v in template.items():
  16. for desc in v:
  17. self.template_meta[desc] = k
  18. # 模板元数据
  19. self.template_meta_list = list(self.template_meta.keys())
  20. # 加载远程模型
  21. self.model = RemoteBGEModel('dev')
  22. # 模型预热
  23. print("模型预热中...")
  24. self.model.encode(["这是一段预热文字,首次推理通过预测保证后续推理的稳定性和性能。"])
  25. self.model.compute_score([("这是一段预热文字,首次推理通过预测保证后续推理的稳定性和性能。",
  26. "这是一段预热文字,首次推理通过预测保证后续推理的稳定性和性能。")])
  27. self.print_gpu()
  28. # 加载向量数据库
  29. self.database_index = None
  30. database_path = os.path.join(script_dir, "intent_index.faiss")
  31. if not os.path.exists(database_path):
  32. # embeddings = self.model.encode(self.template_meta_list)['dense_vecs'].astype(np.float32) # 选取密集向量,变为float32
  33. #faiss.normalize_L2(embeddings) # L2归一化,本地模型需要归一化
  34. # 调用远程embedding模型,one by one 地处理,远程模型通过配置参数进行归一化
  35. embeddings = [self.model.encode([temp], normalize=True)[0] for temp in self.template_meta_list]
  36. for _ in embeddings:
  37. if _ is None:
  38. raise RuntimeError('构建意图描述模板库时发生异常,embeddings不能存在None')
  39. # 要求embeddings是一个二维矩阵,类型为float32
  40. embeddings = np.array(embeddings, dtype=np.float32)
  41. # Create FAISS index
  42. dimension = embeddings[0].shape[0]
  43. self.database_index = faiss.IndexFlatIP(dimension) # 建立内积索引
  44. self.database_index.add(embeddings) # 添加索引
  45. # Save for future use
  46. faiss.write_index(self.database_index, database_path)
  47. if self.database_index is None:
  48. self.database_index = faiss.read_index(database_path)
  49. @staticmethod
  50. def print_gpu():
  51. if torch.cuda.is_available():
  52. print(f"allocated:{torch.cuda.memory_allocated()/1024**3:.2f}GB", end=' ')
  53. print(f"reserved: {torch.cuda.memory_reserved()/1024**3:.2f}GB")
  54. # 应用推理阶段
  55. def pick_out(self, query, top_k):
  56. # 使用本地模型
  57. # query_embedding = self.model.encode([query])['dense_vecs'].astype(np.float32)
  58. # 下面使用远程模型代替本地模型
  59. # 要求query_embedding是一个二维矩阵,形状为(1, 1024)
  60. query_embedding = np.array(self.model.encode([query], normalize=True), dtype=np.float32)
  61. # faiss.normalize_L2(query_embedding)
  62. distances, indices = self.database_index.search(query_embedding, top_k)
  63. group_query = [(query, self.template_meta_list[indices[0][i]]) for i in range(top_k)]
  64. # 调用远程reranker模型
  65. score = self.model.compute_score(group_query)
  66. rerank_result = sorted([(distances[0][_], indices[0][_], score[_]) for _ in range(top_k)], key=lambda x: x[2],
  67. reverse=True) # distance, indices, rerank_score
  68. score_idx = 2 # 重排序相关度
  69. meta_idx = 1 # 模板位置
  70. similarity_idx = 0 # 向量相似度
  71. print("***检索结果***:")
  72. for i in range(top_k - 1, -1, -1):
  73. print(
  74. f"***{i} 相关度:{rerank_result[i][score_idx]:.2f} "
  75. f"相似度:{rerank_result[i][similarity_idx]:.2f} "
  76. f"意图:{self.template_meta.get(self.template_meta_list[rerank_result[i][meta_idx]])} "
  77. f"{intent_code.get(self.template_meta.get(self.template_meta_list[rerank_result[i][meta_idx]])).get('alias')} "
  78. f"关联:{self.template_meta_list[rerank_result[i][meta_idx]]}"
  79. )
  80. # 从排序结果中拆解到意图的大小类编号
  81. result = [] # 意图识别结果
  82. confidence = []# 置信度
  83. for i in range(top_k):
  84. # 拿到描述词
  85. description = self.template_meta_list[rerank_result[i][meta_idx]]
  86. # 拿到自编码
  87. custom_number = self.template_meta[description]
  88. # 拿到大小类标号
  89. result.append(intent_code[custom_number])
  90. # 添加置信度
  91. confidence.append(rerank_result[i][score_idx])
  92. return confidence, result
  93. def quick_answer_q_a_v2(question, is_english=0):
  94. """快速问答分支,赋值metric"""
  95. metric = ''
  96. question += ' ' # 深拷贝
  97. if is_english == 0:
  98. pattern_res_open_qa = re.findall("(开启|打开).*?问答", question)
  99. pattern_res_close_qa = re.findall("(关闭|关掉).*?问答", question)
  100. else:
  101. question = question.strip().lower()
  102. pattern_res_open_qa = re.findall(
  103. r'\b(?:open|show me|enable)\b\W*.*?(?:quiz\W+with\W+prizes|enable\W+award\W*-\W*winning\W+q\W*&\W*a)',
  104. question)
  105. pattern_res_close_qa = re.findall(
  106. r'\b(?:close|disable)\b\W*.*?(?:quiz\W+with\W+prizes|enable\W+award\W*-\W*winning\W+q\W*&\W*a)', question)
  107. if len(pattern_res_open_qa) > 0:
  108. metric = "openQandA"
  109. if len(pattern_res_close_qa) > 0:
  110. metric = "closeQandA"
  111. return metric
  112. # 单例模式
  113. recognizer_bge = IntentRecognizer()