langchain-ChatGLM源码阅读:参数设置

这篇具有很好参考价值的文章主要介绍了langchain-ChatGLM源码阅读:参数设置。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上下文关联

上下文关联相关参数:

  • 知识相关度阈值score_threshold
  • 内容条数k
  • 是否启用上下文关联chunk_conent
  • 上下文最大长度chunk_size

其主要作用是在所在文档中扩展与当前query相似度较高的知识库的内容,作为相关信息与query按照prompt规则组合后作为输入获得模型的回答。

langchain-ChatGLM源码阅读:参数设置,自然语言处理,神经网络,langchain,chatglm,知识库问答

  • 获取查询句query嵌入:faiss.py
def similarity_search_with_score(
        self, query: str, k: int = 4
    ) -> List[Tuple[Document, float]]:
        """Return docs most similar to query.

        Args:
            query: Text to look up documents similar to.
            k: Number of Documents to return. Defaults to 4.

        Returns:
            List of Documents most similar to the query and score for each
        """
        embedding = self.embedding_function(query)
        docs = self.similarity_search_with_score_by_vector(embedding, k)
        return docs
  • 上下文生成:MyFAISS.py

    def seperate_list(self, ls: List[int]) -> List[List[int]]:
        # TODO: 增加是否属于同一文档的判断
        lists = []
        ls1 = [ls[0]]
        for i in range(1, len(ls)):
            if ls[i - 1] + 1 == ls[i]:
                ls1.append(ls[i])
            else:
                lists.append(ls1)
                ls1 = [ls[i]]
        lists.append(ls1)
        return lists

    def similarity_search_with_score_by_vector(
            self, embedding: List[float], k: int = 4
    ) -> List[Document]:
        faiss = dependable_faiss_import()
        # (1,1024)
        vector = np.array([embedding], dtype=np.float32)
        # 默认FALSE
        if self._normalize_L2:
            faiss.normalize_L2(vector)
        # shape均为(1, k),这步获取与query有top-k相似度的知识库
        scores, indices = self.index.search(vector, k)
        docs = []
        id_set = set()
        store_len = len(self.index_to_docstore_id)
        rearrange_id_list = False
        # 遍历找到的k个最相似知识库的索引
        # k是第一次的筛选条件,score是第二次的筛选条件
        for j, i in enumerate(indices[0]):
            if i == -1 or 0 < self.score_threshold < scores[0][j]:
                # This happens when not enough docs are returned.
                continue
            if i in self.index_to_docstore_id:
                _id = self.index_to_docstore_id[i]
            # 执行接下来的操作
            else:
                continue
            # index→id→content
            doc = self.docstore.search(_id)
            if (not self.chunk_conent) or ("context_expand" in doc.metadata and not doc.metadata["context_expand"]):
                # 匹配出的文本如果不需要扩展上下文则执行如下代码
                if not isinstance(doc, Document):
                    raise ValueError(f"Could not find document for id {_id}, got {doc}")
                doc.metadata["score"] = int(scores[0][j])
                docs.append(doc)
                continue
            # 其实存的都是index
            id_set.add(i)
            docs_len = len(doc.page_content)
            # 跟外部变量定义的k重名了,烂
            # 一个知识库是分句后得到的一句话,i是当前知识库在总知识库中的位置,store_len是总知识库大小
            # 所以k说的是扩充上下文时最多能跨多少个知识库
            for k in range(1, max(i, store_len - i)):
                break_flag = False
                if "context_expand_method" in doc.metadata and doc.metadata["context_expand_method"] == "forward":
                    expand_range = [i + k]
                elif "context_expand_method" in doc.metadata and doc.metadata["context_expand_method"] == "backward":
                    expand_range = [i - k]
                else:
                    # i=4922, k=1 → [4923, 4921]
                    expand_range = [i + k, i - k]
                for l in expand_range:
                    # 确保扩展上下文时不会造成重复
                    if l not in id_set and 0 <= l < len(self.index_to_docstore_id):
                        _id0 = self.index_to_docstore_id[l]
                        doc0 = self.docstore.search(_id0)
                        # 如果当前字数大于250或者是知识库跨了文件,扩充上下文过程终止
                        # 这一句有些问题,有一端跨文件就终止,应该是两端同时跨才终止才对
                        if docs_len + len(doc0.page_content) > self.chunk_size or doc0.metadata["source"] != \
                                doc.metadata["source"]:
                            break_flag = True
                            break
                        elif doc0.metadata["source"] == doc.metadata["source"]:
                            docs_len += len(doc0.page_content)
                            id_set.add(l)
                            rearrange_id_list = True
                if break_flag:
                    break
        # 如果没有扩展上下文(不需要或是不能)
        if (not self.chunk_conent) or (not rearrange_id_list):
            return docs
        if len(id_set) == 0 and self.score_threshold > 0:
            return []
        id_list = sorted(list(id_set))
        # 连续必然属于同一文档,但不连续也可能在同一文档
        # 返回二级列表,第一级是连续的index列表,第二级是具体index
        id_lists = self.seperate_list(id_list)
        for id_seq in id_lists:
            for id in id_seq:
                if id == id_seq[0]:
                    _id = self.index_to_docstore_id[id]
                    # doc = self.docstore.search(_id)
                    doc = copy.deepcopy(self.docstore.search(_id))
                else:
                    _id0 = self.index_to_docstore_id[id]
                    doc0 = self.docstore.search(_id0)
                    doc.page_content += " " + doc0.page_content
            if not isinstance(doc, Document):
                raise ValueError(f"Could not find document for id {_id}, got {doc}")
            # indices为相关文件的索引
            # 因为可能会将多个连续的id合并,因此需要将同一seq内所有位于top-k的知识库的分数取最小值作为seq对应的分数
            doc_score = min([scores[0][id] for id in [indices[0].tolist().index(i) for i in id_seq if i in indices[0]]])
            doc.metadata["score"] = int(doc_score)
            docs.append(doc)
        # 注意这里docs没有按相似度排序,可以自行加个sort
        return docs

考虑到相似度计算在双向扩展上有一些问题,对算法做了一些修改:

 def similarity_search_with_score_by_vector(
            self, embedding: List[float], k: int = 4
    ) -> List[Document]:
        faiss = dependable_faiss_import()
        # (1,1024)
        vector = np.array([embedding], dtype=np.float32)
        # 默认FALSE
        if self._normalize_L2:
            faiss.normalize_L2(vector)
        # shape均为(1, k)
        scores, indices = self.index.search(vector, k)
        docs = []
        id_set = set()
        store_len = len(self.index_to_docstore_id)
        rearrange_id_list = False
        # 存储关键句
        keysentences = []
        # 遍历找到的k个最近相关文档的索引
        # top-k是第一次的筛选条件,score是第二次的筛选条件
        for j, i in enumerate(indices[0]):
            if i == -1 or 0 < self.score_threshold < scores[0][j]:
                # This happens when not enough docs are returned.
                continue
            if i in self.index_to_docstore_id:
                _id = self.index_to_docstore_id[i]
            # 执行接下来的操作
            else:
                continue
            # index→id→content
            doc = self.docstore.search(_id)
            if (not self.chunk_conent) or ("context_expand" in doc.metadata and not doc.metadata["context_expand"]):
                # 匹配出的文本如果不需要扩展上下文则执行如下代码
                if not isinstance(doc, Document):
                    raise ValueError(f"Could not find document for id {_id}, got {doc}")
                doc.metadata["score"] = int(scores[0][j])
                docs.append(doc)
                continue
            # 其实存的都是index
            id_set.add(i)
            docs_len = len(doc.page_content.strip())
            # 跟外部变量定义的k重名了,烂
            # 一个知识库是分句后得到的一句话,i是当前知识库在总知识库中的位置,store_len是总知识库大小
            # 所以k说的是扩充上下文时最多能跨多少个知识库
            for k in range(1, max(i, store_len - i)):
                single_break_flag = 0
                double_break_flag = 0
                if "context_expand_method" in doc.metadata and doc.metadata["context_expand_method"] == "forward":
                    expand_range = [i + k]
                elif "context_expand_method" in doc.metadata and doc.metadata["context_expand_method"] == "backward":
                    expand_range = [i - k]
                else:
                    # i=4922, k=1 → [4923, 4921]
                    expand_range = [i + k, i - k]
                for l in expand_range:
                    # 确保扩展上下文时不会造成重复
                    if l not in id_set and 0 <= l < len(self.index_to_docstore_id):
                        _id0 = self.index_to_docstore_id[l]
                        doc0 = self.docstore.search(_id0)
                        # 如果当前字数大于250或者是知识库跨了文件,扩充上下文过程终止
                        # 这一句有些问题,有一端跨文件就终止,应该是两端同时跨才终止才对
                        if docs_len + len(doc0.page_content.strip()) > self.chunk_size or doc0.metadata["source"] != \
                                doc.metadata["source"]:
                            single_break_flag = 1
                            if docs_len + len(doc0.page_content.strip()) > self.chunk_size:
                                double_break_flag = 2
                                break
                            else:
                                double_break_flag += 1
                        elif doc0.metadata["source"] == doc.metadata["source"]:
                            docs_len += len(doc0.page_content.strip())
                            id_set.add(l)
                            rearrange_id_list = True
                if double_break_flag == 2:
                    break
        # 如果没有扩展上下文(不需要或是不能)
        if (not self.chunk_conent) or (not rearrange_id_list):
            return docs, keysentences
        if len(id_set) == 0 and self.score_threshold > 0:
            return [], []
        id_list = sorted(list(id_set))
        # 连续必然属于同一文档,但不连续也可能在同一文档
        # 返回二级列表,第一级是连续的index列表,第二级是具体index
        id_lists = self.seperate_list(id_list)
        keyids = indices[0].tolist()
        filter_rate = 0.05
        for id_seq in id_lists:
            seqsentences = []
            for id_index, id in enumerate(id_seq):
                if id == id_seq[0]:
                    _id = self.index_to_docstore_id[id]
                    # doc = self.docstore.search(_id)
                    doc = copy.deepcopy(self.docstore.search(_id))
                    if id in keyids:
                        seqsentences.append({'key': doc.page_content.strip(), 'rate': id_index/len(id_seq)})
                        doc.page_content = "<b>"+doc.page_content.strip()+"</b>"
                else:
                    _id0 = self.index_to_docstore_id[id]
                    doc0 = self.docstore.search(_id0)
                    if id in keyids:
                        seqsentences.append({'key': doc0.page_content.strip(), 'rate': id_index/len(id_seq)})
                        doc.page_content += " <b>" + doc0.page_content.strip()+"</b>"
                    else:
                        doc.page_content += " " + doc0.page_content.strip()
            if not isinstance(doc, Document):
                raise ValueError(f"Could not find document for id {_id}, got {doc}")
            # indices为相关文件的索引
            # 因为可能会将多个连续的id合并,因此需要将同一seq内所有位于top-k的知识库的分数取最小值作为seq对应的分数
            doc_score = min([scores[0][id] for id in [indices[0].tolist().index(i) for i in id_seq if i in indices[0]]])
            doc.metadata["score"] = int(doc_score)
            docs.append(doc)
            seqsentences.sort(key=lambda data: data['rate'])
            if seqsentences[-1]['rate'] < filter_rate:
                keysentences.append(seqsentences[-1]['key'])
            else:
                keysentences += [seqsentence['key'] for seqsentence in seqsentences if seqsentence['rate'] > filter_rate]
        docs.sort(key=lambda doc: doc.metadata['score'])
        return docs, keysentences

  • prompt生成:local_doc_qa.py
def get_knowledge_based_answer(self, query, vs_path, chat_history=[], streaming: bool = STREAMING):
        related_docs_with_score = vector_store.similarity_search_with_score(query, k=self.top_k)
        torch_gc()
        if len(related_docs_with_score) > 0:
            prompt = generate_prompt(related_docs_with_score, query)
        else:
            prompt = query
        answer_result_stream_result = self.llm_model_chain(
            {"prompt": prompt, "history": chat_history, "streaming": streaming})

def generate_prompt(related_docs: List[str],
                    query: str,
                    prompt_template: str = PROMPT_TEMPLATE, ) -> str:
    context = "\n".join([doc.page_content for doc in related_docs])
    prompt = prompt_template.replace("{question}", query).replace("{context}", context)
    return prompt

对话轮数

langchain-ChatGLM源码阅读:参数设置,自然语言处理,神经网络,langchain,chatglm,知识库问答
其实就是要存多少历史记录,如果为0的话就是在执行当前对话时不考虑历史问答

  • 模型内部调用时使用,以chatglm为例:chatglm_llm.py
 def _generate_answer(self,
                         inputs: Dict[str, Any],
                         run_manager: Optional[CallbackManagerForChainRun] = None,
                         generate_with_callback: AnswerResultStream = None) -> None:
        history = inputs[self.history_key]
        streaming = inputs[self.streaming_key]
        prompt = inputs[self.prompt_key]
        print(f"__call:{prompt}")
        # Create the StoppingCriteriaList with the stopping strings
        stopping_criteria_list = transformers.StoppingCriteriaList()
        # 定义模型stopping_criteria 队列,在每次响应时将 torch.LongTensor, torch.FloatTensor同步到AnswerResult
        listenerQueue = AnswerResultQueueSentinelTokenListenerQueue()
        stopping_criteria_list.append(listenerQueue)
        if streaming:
            history += [[]]
            for inum, (stream_resp, _) in enumerate(self.checkPoint.model.stream_chat(
                    self.checkPoint.tokenizer,
                    prompt,
                    # 为0时history返回[]
                    history=history[-self.history_len:-1] if self.history_len > 0 else [],
                    max_length=self.max_token,
                    temperature=self.temperature,
                    top_p=self.top_p,
                    top_k=self.top_k,
                    stopping_criteria=stopping_criteria_list
            )):

向量匹配 top k

虽然放在了模型配置那一页,但实际上还是用来控制上下文关联里面的内容条数k的,不知道为什么写了两遍。。。
langchain-ChatGLM源码阅读:参数设置,自然语言处理,神经网络,langchain,chatglm,知识库问答

控制生成质量的参数

这些参数没有在前端显式地给出,而是写死在了模型定义里文章来源地址https://www.toymoban.com/news/detail-630544.html

  • 模型定义,以chatglm为例:chatglm_llm.py
class ChatGLMLLMChain(BaseAnswer, Chain, ABC):
    max_token: int = 10000
    temperature: float = 0.01
    # 相关度
    top_p = 0.4
    # 候选词数量
    top_k = 10
    checkPoint: LoaderCheckPoint = None
    # history = []
    history_len: int = 10

参数设置心得

  • score_threshold和k设太小会找不到问题对应的原文件,太大找到一堆不相关的
  • chunk_size设太小不能在原文件里找到问题对应的原文,太大无法有效归纳出答案
  • temperature和top_p默认值下生成的答案基本固定,但也很死板;过大的temperature导致回答的事实不稳定;过大的top_p导致回答的语言风格不稳定;调整top_k没发现结果有什么变化

到了这里,关于langchain-ChatGLM源码阅读:参数设置的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 本地部署 langchain-ChatGLM

    一种利用 ChatGLM-6B + langchain 实现的基于本地知识的 ChatGLM 应用。增加 clue-ai/ChatYuan 项目的模型 ClueAI/ChatYuan-large-v2 的支持。 本项目中 Embedding 默认选用的是 GanymedeNil/text2vec-large-chinese,LLM 默认选用的是 ChatGLM-6B。依托上述模型,本项目可实现全部使用开源模型离线私有部署。

    2024年02月06日
    浏览(44)
  • LangChain-ChatGLM在WIndows10下的部署

    1、LangChain + ChatGLM2-6B 搭建个人专属知识库中的LangChain + ChatGLM2-6B 构建知识库这一节:基本的逻辑和步骤是对的,但要根据Windows和现状做很多调整。 2、没有动过model_config.py中的“LORA_MODEL_PATH_BAICHUAN”这一项内容,却报错:对报错“LORA_MODEL_PATH_BAICHUAN”提供了重要解决思路,虽

    2024年02月13日
    浏览(36)
  • windows环境下的langchain-ChatGLM的本地部署

    首先是项目开源地址 https://github.com/imClumsyPanda/langchain-ChatGLM 下载这个项目的源码非常简单,但运行起来十分麻烦,各种环境的搭配简直是折磨人,尤其是电脑上缺少各种安装环境的,我首先先列举几个,例如conda安装python的虚拟环境,用这个比较方便,还有Anoconda的安装,

    2024年02月13日
    浏览(46)
  • 2M大小的PDF文档上传到LangChain-ChatGLM知识图谱中,大致需要的时间

    对于将2M大小的PDF文档上传到LangChain-ChatGLM知识图谱中,大致需要的时间如下: PDF到文本的提取转换:若PDF内容主要为文本,此步骤约需要1-2分钟。 提取的文本经过预处理与分析:此步骤需要对文本进行分词、命名实体识别等处理,约需要2-5分钟。 抽取文本中的结构化知识(实体、关

    2024年02月08日
    浏览(42)
  • CentOS7上部署langchain-chatglm或stable-diffusion可能遇到的Bug的解决方案

    进入你的代码目录下 下载依赖 这里可能有的朋友会有问题会出现某些包下载不了,这里建议直接使用阿里源即可,在确定你的cuda版本之后(使用nvidia-smi确定cuda版本) 命令行执行 卸载掉刚才pip安装的版本!!!!因为此处安装的版本还缺少cuda的支持,确定卸载掉之后 执行 此处X为

    2024年02月16日
    浏览(39)
  • langchain源码阅读系列(三)之Chain模块

    原文首发于博客文章langchain源码阅读 本节是langchian源码阅读系列第三篇,下面进入Chain模块👇: LLM 应用构建实践笔记 Chain链定义 链定义为对组件的一系列调用,也可以包括其他链,这种在链中将组件组合在一起的想法很简单但功能强大,极大地简化了复杂应用程序的实现并

    2024年02月13日
    浏览(43)
  • langchain源码阅读系列(五)之Callback模块

    原文首发于博客文章,详情请前往博客langchain源码阅读 本节是langchian源码阅读系列第五篇,下面进入Callback模块👇: LLM 应用构建实践笔记 回调模块允许接到LLM应用程序的各个阶段,鉴于LLM的幻觉问题,这对于日志记录、监视、流式处理和其他任务非常有用,现在也有专用的

    2024年02月13日
    浏览(36)
  • 阿里云部署 ChatGLM2-6B 与 langchain+ChatGLM

    更新系统 安装git 克隆 ChatGLM2-6B 源码 克隆 chatglm2-6b 模型 安装 ChatGLM2-6B 依赖 修改模型的路径 修改成 启动服务 启动成功后 克隆 langchain-ChatGLM 源码 git clone https://github.com/imClumsyPanda/langchain-ChatGLM.git 克隆模型 安装 langchain-ChatGLM 依赖 修改配置 修改一 修改成 修改二 修改成 修改

    2024年02月15日
    浏览(50)
  • ChatGLM-6B+LangChain实战

    目标:原始使用ChatGLM-6B可接受的文字长度有限,打算结合LangChain实现长文本生成摘要. 方法: step1:自定义一个GLM继承LangChain中的langchain.llms.base.LLM,load自己的模型. step2:使用LangChain的mapreduce的方法,对文本分块,做摘要,输出结果. 使用的机器资源:T4显卡(16G显存) 附参

    2024年02月16日
    浏览(33)
  • 【ChatGLM】基于 ChatGLM-6B + langchain 实现本地化知识库检索与智能答案生成: 中文 LangChain 项目的实现开源工作

      目录 【ChatGLM】基于 ChatGLM-6B + langchain 实现本地化知识库检索与智能答案生成: 中文 LangChain 项目的实现开源工作 1.克隆源代码:

    2024年02月11日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包