两个模型整合问题解决

任务:查找新模型整合没有回答的原因

可能存在问题:

(1)搜索返回格式错误

(2)相似度阈值太高MIN_SIMILARITY = 0.75)---->应该不可能,因为输入一样的句子,没有结果,仅点击标签,也没有结果

(3)数据库为空(没有问答对)-------->应该也没有问题

(4)模型向量生成问题

排除后:

问题1:返回格式错误(最可能)

前端需要 original_question,但我们的返回可能是 question

问题2:标签筛选逻辑问题

纯标签搜索时,相似度计算可能为0,达不到阈值。

问题3:搜索方法逻辑错误

需要逐行检查代码逻辑。

(1)先修改一下large和rerank的路径,修改为绝对路径+修改返回格式(好像之前已经修改过了)

复制代码
# 路径
MODEL_NAME = "D:\\下载\\戴雄斌_中华心法问答系统源代码\\bge-large-zh-v1.5"
RERANKER_NAME = "D:\\下载\\戴雄斌_中华心法问答系统源代码\\bge-reranker-large"
# 放回格式
        return [{
            **self.qa_pairs[res["index"]],  # 关键修改:包含所有字段,包括 original_question
            "similarity": round(float(res['similarity']), 4)
        } for res in final_results]

等待测试结果---------->不行

(2)移除重排序,修改阈值为0,放回改为10

复制代码
    # TOP_K = 1000
    TOP_K = 10
    RERANK_TOP_K = 15
    FINAL_TOP_K = 5
    # MIN_SIMILARITY = 0.75
    MIN_SIMILARITY = 0.0
    # DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
    DEVICE = "cpu"  # 强制使用CPU

def search(self, query: str, selected_tags: Dict = None) -> List[Dict]:
    if not self.qa_pairs: 
        return []
    
    cleaned_query = clean_text(query)
    
    # 如果没有搜索词,也没有选择任何标签,则直接返回空列表
    if not cleaned_query and not (selected_tags and any(selected_tags.values())):
        return []

    if cleaned_query:
        query_emb = self._get_embedding(cleaned_query)
        similarities = np.dot(self.question_vectors, query_emb)
    else:
        # 如果没有搜索词(纯标签搜索),则所有问题的基础相似度视为0
        similarities = np.zeros(len(self.qa_pairs))

    expert_scores = db.session.query(SimilarityScore.question_id, func.avg(SimilarityScore.score)).filter(SimilarityScore.query_text == query).group_by(SimilarityScore.question_id).all()
    expert_scores_map = dict(expert_scores)

    results_with_sim = []
    for i, qa_pair in enumerate(self.qa_pairs):
        final_score = (similarities[i] + expert_scores_map.get(qa_pair['db_id'], similarities[i])) / 2
        
        # --- 标签筛选逻辑(与BERT版本一致)---
        passes_filter = True
        if selected_tags:
            # 筛选一级标签
            if selected_tags.get('level1') and qa_pair['tags']['level1'] not in selected_tags['level1']:
                passes_filter = False
            
            # 筛选二级标签 (只要有一个匹配即可)
            if passes_filter and selected_tags.get('level2') and not any(t in qa_pair['tags']['level2'] for t in selected_tags['level2']):
                passes_filter = False

            # 筛选三级标签/关键词 (只要有一个匹配即可)
            if passes_filter and selected_tags.get('keywords') and not any(t in qa_pair['tags']['keywords'] for t in selected_tags['keywords']):
                passes_filter = False

        if passes_filter:
            results_with_sim.append({"similarity": final_score, "index": i})
    
    # 如果有搜索词,按相似度排序;如果只按标签搜,则按默认顺序
    if cleaned_query:
        sorted_results = sorted(results_with_sim, key=lambda x: x['similarity'], reverse=True)
    else:
        sorted_results = results_with_sim
    
    # 移除阈值筛选和重排序逻辑,先保证能返回结果
    final_results = sorted_results[:self.config.TOP_K]
    
    # 关键:返回格式与BERT版本完全一致
    return [{
        **self.qa_pairs[res["index"]],
        "similarity": round(float(res['similarity']), 4)
    } for res in final_results]

可以!!!

------>不是返回格式的问题

有可能是阈值,有可能是重排序太复杂了。

(3)再次测试

  1. 可以去除关键词权重。

2.阈值先还是0,

3.需要重排序,

  1. 返回格式保持正确的格式,

5.用bert的索引返回

6.top_k=1000

复制代码
def search(self, query: str, selected_tags: Dict = None) -> List[Dict]:
    if not self.qa_pairs: 
        return []
    
    cleaned_query = clean_text(query)
    
    # 如果没有搜索词,也没有选择任何标签,则直接返回空列表
    if not cleaned_query and not (selected_tags and any(selected_tags.values())):
        return []

    if cleaned_query:
        query_emb = self._get_embedding(cleaned_query)
        similarities = np.dot(self.question_vectors, query_emb)
        
        # 可选:关键词权重增强(暂时注释掉)
        # query_keywords = set(jieba.lcut(cleaned_query))
        # keyword_weights = np.array([
        #     len(set(jieba.lcut(q["cleaned_question"])) & query_keywords)
        #     / max(len(query_keywords), 1)
        #     for q in self.qa_pairs
        # ])
        # final_similarities = 0.95 * similarities + 0.05 * keyword_weights
        final_similarities = similarities  # 暂时只用向量相似度
        
    else:
        # 如果没有搜索词(纯标签搜索),给基础相似度
        final_similarities = np.ones(len(self.qa_pairs)) * 0.5

    # 专家评分
    expert_scores = db.session.query(SimilarityScore.question_id, func.avg(SimilarityScore.score))\
        .filter(SimilarityScore.query_text == query)\
        .group_by(SimilarityScore.question_id).all()
    expert_scores_map = dict(expert_scores)

    results_with_sim = []
    for i, qa_pair in enumerate(self.qa_pairs):
        base_score = final_similarities[i]
        expert_score = expert_scores_map.get(qa_pair['db_id'], base_score)
        final_score = (base_score + expert_score) / 2
        
        # 标签筛选逻辑
        passes_filter = True
        if selected_tags:
            # 一级标签筛选
            if selected_tags.get('level1'):
                if not isinstance(selected_tags['level1'], list):
                    selected_tags['level1'] = [selected_tags['level1']]
                if qa_pair['tags']['level1'] not in selected_tags['level1']:
                    passes_filter = False
            
            # 二级标签筛选
            if passes_filter and selected_tags.get('level2'):
                if not isinstance(selected_tags['level2'], list):
                    selected_tags['level2'] = [selected_tags['level2']]
                if not any(t in qa_pair['tags']['level2'] for t in selected_tags['level2']):
                    passes_filter = False
            
            # 关键词筛选
            if passes_filter and selected_tags.get('keywords'):
                if not isinstance(selected_tags['keywords'], list):
                    selected_tags['keywords'] = [selected_tags['keywords']]
                if not any(t in qa_pair['tags']['keywords'] for t in selected_tags['keywords']):
                    passes_filter = False
        
        # 阈值设为0,先保证所有结果都能返回
        if passes_filter:  # 移除阈值检查:and final_score >= self.config.MIN_SIMILARITY
            results_with_sim.append({
                "similarity": final_score, 
                "index": i
            })
    
    # 排序
    if cleaned_query:
        sorted_results = sorted(results_with_sim, key=lambda x: x['similarity'], reverse=True)
    else:
        sorted_results = results_with_sim
    
    # 应用重排序(如果有查询词)
    if len(sorted_results) > 0 and cleaned_query:
        # 准备重排序的候选结果
        candidates = []
        for res in sorted_results[:self.config.RERANK_TOP_K]:
            qa_data = self.qa_pairs[res["index"]]
            candidates.append({
                "similarity": res["similarity"],
                "index": res["index"],
                "original_question": qa_data["original_question"],
                "answer": qa_data["answer"],
                "tags": qa_data["tags"],
                "db_id": qa_data["db_id"]
            })
        
        # 执行重排序
        reranked_results = self._rerank(query, candidates)
        
        # 转换回索引格式
        final_indices = []
        for reranked in reranked_results[:self.config.FINAL_TOP_K]:
            # 找到对应的原始索引
            for res in sorted_results:
                if res["index"] == reranked.get("index"):
                    final_indices.append(res)
                    break
        final_results = final_indices
    else:
        final_results = sorted_results[:self.config.FINAL_TOP_K]
    
    # 索引返回模式(与BERT版本一致)
    return [{
        **self.qa_pairs[res["index"]],  # 关键:使用索引获取完整数据
        "similarity": round(float(res['similarity']), 4)
    } for res in final_results]

------>一开始可以搜出结果,但是立马又没有结果了。

接着搜索,会显示没有结果,后台也卡住了,会一条一条慢慢蹦出来,然后又有结果了。

然后后台开始报错:

复制代码
127.0.0.1 - - [01/Dec/2025 22:05:12] "POST /search HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 22:05:13] "POST /search HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 22:06:38] "POST /search HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 22:06:41] "POST /search HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 22:06:56] "POST /search HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 22:06:57] "POST /search HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 22:06:59] "POST /search HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 22:07:04] "POST /search HTTP/1.1" 200 -
127.0.0.1 - - [01/Dec/2025 22:07:08] "POST /search HTTP/1.1" 500 -
Traceback (most recent call last):
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\flask\app.py", line 1536, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\flask\app.py", line 1514, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\flask\app.py", line 1511, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\flask\app.py", line 919, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\flask\app.py", line 917, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\flask\app.py", line 902, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\Mind-Dharma Q&A System\xinfa_QA.py", line 715, in search
    results = system.search(data.get('question', ''), data.get('tags', {}))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\Mind-Dharma Q&A System\xinfa_QA.py", line 507, in search
    reranked_results = self._rerank(query, candidates)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\Mind-Dharma Q&A System\xinfa_QA.py", line 194, in _rerank
    raw_scores = self.reranker.compute_score(pairs)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\FlagEmbedding\abc\inference\AbsReranker.py", line 218, in compute_score
    return self.compute_score_single_gpu(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\torch\utils\_contextlib.py", line 116, in decorate_context
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\FlagEmbedding\inference\reranker\encoder_only\base.py", line 138, in compute_score_single_gpu
    passages_inputs_batch = self.tokenizer(
                            ^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\transformers\tokenization_utils_base.py", line 2855, in __call__
    encodings = self._call_one(text=text, text_pair=text_pair, **all_kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\transformers\tokenization_utils_base.py", line 2943, in _call_one
    return self.batch_encode_plus(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\transformers\tokenization_utils_base.py", line 3144, in batch_encode_plus
    return self._batch_encode_plus(
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\transformers\tokenization_utils_fast.py", line 541, in _batch_encode_plus
    self.set_truncation_and_padding(
  File "D:\下载\戴雄斌_中华心法问答系统源代码\venv\Lib\site-packages\transformers\tokenization_utils_fast.py", line 494, in set_truncation_and_padding
    self._tokenizer.enable_truncation(**target)
RuntimeError: Already borrowed

reranker模型内部错误RuntimeError: Already borrowed

这是多线程/多请求并发问题:reranker tokenizer在同一时间被多个请求调用,导致冲突。

问题分析:

前端快速连续搜索 → 多个请求同时到达

reranker tokenizer不是线程安全的Already borrowed错误

导致500内部服务器错误

过一会没输入了又可以运行了。

解决方案:

(1)先禁用rerank

(2)给rerank上锁(线程安全)

(3)在前端JavaScript中,添加防抖(debounce),避免快速连续搜索。

因为可以运行,所以暂且不修改。

(1)这一版本直接去掉了关键词(没什么用,最开始测试的时候就发现了,但是为了保持原来的逻辑,所以加上了),现在可以直接去掉。

(2)阈值修改为0!尽管修改为0,但是下面例子还是只有5条输出。(包括之前在匹配的时候,有相似度阈值检查,现在直接去掉了。)------>但是好像是rerank控制的就返回5条?------->没错!rerank控制返回5条!那为什么要设置阈值呢?因为没有到5条的时候,可以不返回5条吗?但是感觉有点没必要。因为一共就返回5条,感觉再减少一点没啥必要。----->所以先设置阈值=0。而且设置阈值之后,感觉仅进行标签搜索时,答案会被过滤掉。

(3)还有一个就是返回结果的格式和返回形式。保持与之前bert一样。(返回格式。输入数据的准备方式是和bert一样的索引)

(4)纯标签搜索时,给0.5分。原本是0分。----->这个可以考虑改回去。

测试上面这个例子,从输入到稳定输出结果,至少5分钟。

模型启动,至少30分钟。

存在速度很慢问题!

记得删掉之前添加的测试路由!

相关推荐
思成不止于此2 小时前
【MySQL 零基础入门】DQL 核心语法(三):学生表排序查询与分页查询篇
数据库·笔记·学习·mysql
wgego2 小时前
Polar靶场web 随写笔记
笔记·web
cuckooman2 小时前
obsidian如何删除已不再使用的属性?
笔记·obsidian
中屹指纹浏览器2 小时前
指纹浏览器与代理 IP 的跨协议栈协同优化技术
服务器·网络·经验分享·笔记·媒体
墨^O^2 小时前
软件测试开发知识笔记
笔记·学习·测试工具·单元测试·测试用例·压力测试·ab测试
中屹指纹浏览器2 小时前
2025 高并发 IP 指纹优化:基于腾讯云边缘计算的抗检测实现
服务器·网络·经验分享·笔记·媒体
代码游侠3 小时前
应用——文件I/O操作代码
linux·运维·c语言·笔记·学习·算法
sealaugh323 小时前
AI(学习笔记第十五课)从langchain的v0.3到v1.0
人工智能·笔记·学习
不会代码的小猴3 小时前
C++的第十三天笔记
c++·笔记·算法