wenmo8 发布的文章

回顾2024,大模型日新月异,智能体百家争鸣。作为AI应用的重要组成部分,RAG也是“群雄逐鹿,诸侯并起”。年初ModularRAG持续升温、GraphRAG大放异彩,年中开源工具如火如荼、知识图谱再创新机,年末图表理解、多模态RAG又启新征程,简直“你方唱罢我登场”,奇技叠出,不胜枚举!

我在这里遴选了2024年度典型的RAG系统和论文(含AI注解、来源、摘要信息),并于文末附上RAG综述和测试基准材料,望执此一文,计一万六千言,助大家速通RAG。

全文共72篇,逐月为纲,强谓之“RAG七十二式”,以献诸位。

2024.01

(01) GraphReader【图解专家】

图解专家
:像个善于制作思维导图的导师,将冗长的文本转化为清晰的知识网络,让AI能够像沿着地图探索一样,轻松找到答案需要的各个关键点,有效克服了处理长文本时的"迷路"问题。

GraphReader是一种基于图的智能体系统,旨在通过将长文本构建成图并使用智能体自主探索该图来处理长文本。在接收到问题后,智能体首先进行逐步分析并制定合理的计划。然后,它调用一组预定义的函数来读取节点内容和邻居,促进对图进行从粗到细的探索。在整个探索过程中,智能体不断记录新的见解并反思当前情况以优化过程,直到它收集到足够的信息来生成答案。

(02) MM-RAG【多面手】

多面手
:就像一个能同时精通视觉、听觉和语言的全能选手,不仅能理解不同形式的信息,还能在它们之间自如切换和关联。通过对各种信息的综合理解,它能在推荐、助手、媒体等多个领域提供更智能、更自然的服务。

介绍了多模态机器学习的发展,包括对比学习、多模态嵌入实现的任意模态搜索、多模态检索增强生成(MM-RAG)以及如何使用向量数据库构建多模态生产系统等。同时还探讨了多模态人工智能的未来发展趋势,强调了其在推荐系统、虚拟助手、媒体和电子商务等领域的应用前景。

(03) CRAG【自我校正】

自我校正
:像个经验丰富的编辑,先用简单快速的方式筛选初步资料,再通过网络搜索扩充信息,最后通过拆解重组的方式,确保最终呈现的内容既准确又可靠。就像是给RAG装上了一个质量控制系统,让它产出的内容更值得信赖。

CRAG通过设计轻量级的检索评估器和引入大规模网络搜索,来改进检索文档的质量,并通过分解再重组算法进一步提炼检索到的信息,从而提升生成文本的准确性和可靠性。CRAG是对现有RAG技术的有益补充和改进,它通过自我校正检索结果,增强了生成文本的鲁棒性。

(04) RAPTOR【分层归纳】

分层归纳
:像个善于组织的图书管理员,将文档内容自下而上地整理成树状结构,让信息检索能在不同层级间灵活穿梭,既能看到整体概要,又能深入细节。

RAPTOR(Recursive Abstractive Processing for Tree-Organized Retrieval)引入了一种新方法,即递归嵌入、聚类和总结文本块,从下往上构建具有不同总结级别的树。在推理时,RAPTOR 模型从这棵树中检索,整合不同抽象级别的长文档中的信息。

2024.02

(05) T-RAG【私人顾问】

私人顾问
:像个熟悉组织架构的内部顾问,善于利用树状结构组织信息,在保护隐私的同时,高效且经济地提供本地化服务。

T-RAG(树状检索增强生成)结合RAG与微调的开源LLM,使用树结构来表示组织内的实体层次结构增强上下文,利用本地托管的开源模型来解决数据隐私问题,同时解决推理延迟、令牌使用成本以及区域和地理可用性问题。

2024.03

(06) RAT【思考者】

思考者
:像个善于反思的导师,不是一次性得出结论,而是先有初步想法,然后利用检索到的相关信息,不断审视和完善每一步推理过程,让思维链条更加严密可靠。

RAT(检索增强思维)在生成初始零样本思维链(CoT)后,利用与任务查询、当前和过去思维步骤相关的检索信息逐个修订每个思维步骤,RAT可显著提高各种长时生成任务上的性能。

(07) RAFT【开卷高手】

开卷高手
:像个优秀的考生,不仅会找对参考资料,还能准确引用关键内容,并清晰地解释推理过程,让答案既有据可循又合情合理。

RAFT旨在提高模型在特定领域内的“开卷”环境中回答问题的能力,通过训练模型忽略无关文档,并逐字引用相关文档中的正确序列来回答问题,结合思维链式响应,显著提升了模型的推理能力。

(08) Adaptive-RAG【因材施教】

因材施教
:面对不同难度的问题,它会智能地选择最合适的解答方式。简单问题直接回答,复杂问题则会查阅更多资料或分步骤推理,就像一个经验丰富的老师,懂得根据学生的具体问题调整教学方法。

Adaptive-RAG根据查询的复杂程度动态选择最适合的检索增强策略,从最简单到最复杂的策略中动态地为LLM选择最合适的策略。这个选择过程通过一个小语言模型分类器来实现,预测查询的复杂性并自动收集标签以优化选择过程。这种方法提供了一种平衡的策略,能够在迭代式和单步检索增强型 LLMs 以及无检索方法之间无缝适应,以应对一系列查询复杂度。

(09) HippoRAG【海马体】

海马体
:像人脑海马体一样,把新旧知识巧妙编织成网。不是简单地堆积信息,而是让每条新知识都找到最恰当的归属。

HippoRAG是一种新颖的检索框架,其灵感来源于人类长期记忆的海马体索引理论,旨在实现对新经验更深入、更高效的知识整合。HippoRAG协同编排 LLMs、知识图谱和个性化PageRank算法,以模拟新皮层和海马体在人类记忆中的不同角色。

(10) RAE【智能编辑】

智能编辑
:像个细心的新闻编辑,不仅会深入挖掘相关事实,还能通过连环推理找出容易被忽略的关键信息,同时懂得删减冗余内容,确保最终呈现的信息既准确又精炼,避免"说得天花乱坠却不靠谱"的问题。

RAE(多跳问答检索增强模型编辑框架)首先检索经过编辑的事实,然后通过上下文学习来优化语言模型。基于互信息最大化的检索方法利用大型语言模型的推理能力来识别传统基于相似性的搜索可能会错过的链式事实。此外框架包括一种修剪策略,以从检索到的事实中消除冗余信息,这提高了编辑准确性并减轻了幻觉问题。

2024.04

(11) RAGCache【仓储员】

仓储员
:像大型物流中心一样,把常用知识放在最容易取的货架上。懂得把经常用的包裹放在门口,把不常用的放在后仓,让取货效率最大化。

RAGCache是一种为RAG量身定制的新型多级动态缓存系统,它将检索到的知识的中间状态组织在知识树中,并在GPU和主机内存层次结构中进行缓存。RAGCache提出了一种考虑到LLM推理特征和RAG检索模式的替换策略。它还动态地重叠检索和推理步骤,以最小化端到端延迟。

(12) GraphRAG【社区摘要】

社区摘要
:先把小区居民的关系网理清楚,再给每个邻里圈做个简介。有人问路时,各个邻里圈提供线索,最后整合成最完整的答案。

GraphRAG分两个阶段构建基于图的文本索引:首先从源文档中推导出实体知识图,然后为所有紧密相关实体的组预生成社区摘要。给定一个问题,每个社区摘要用于生成部分响应,然后在向用户的最终响应中再次总结所有部分响应。

2024.05

(13) R4【编排大师】

编排大师
:像个排版高手,通过优化材料的顺序和呈现方式来提升输出质量,无需改动核心模型就能让内容更有条理,重点更突出。

R4 (Reinforced Retriever-Reorder-Responder)用于为检索增强型大语言模型学习文档排序,从而在大语言模型的大量参数保持冻结的情况下进一步增强其生成能力。重排序学习过程根据生成响应的质量分为两个步骤:文档顺序调整和文档表示增强。具体来说,文档顺序调整旨在基于图注意力学习将检索到的文档排序组织到开头、中间和结尾位置,以最大化响应质量的强化奖励。文档表示增强通过文档级梯度对抗学习进一步细化质量较差的响应的检索文档表示。

(14) IM-RAG【自言自语】

自言自语
:遇到问题时会在心里盘算"我需要查什么资料"、"这个信息够不够",通过不断的内心对话来完善答案,这种"独白"能力像人类专家一样,能够逐步深入思考并解决复杂问题。

IM-RAG通过学习内部独白(Inner Monologues)来连接IR系统与LLMs,从而支持多轮检索增强生成。该方法将信息检索系统与大型语言模型相整合,通过学习内心独白来支持多轮检索增强生成。在内心独白过程中,大型语言模型充当核心推理模型,它既可以通过检索器提出查询以收集更多信息,也可以基于对话上下文提供最终答案。我们还引入了一个优化器,它能对检索器的输出进行改进,有效地弥合推理器与能力各异的信息检索模块之间的差距,并促进多轮通信。整个内心独白过程通过强化学习(RL)进行优化,在此过程中还引入了一个进展跟踪器来提供中间步骤奖励,并且答案预测会通过监督微调(SFT)进一步单独优化。

(15) AntGroup-GraphRAG【百家之长】

百家之长
:汇集行业百家之长,擅用多种方式快速定位信息,既能提供精准检索,又能理解自然语言查询,让复杂的知识检索变得既经济又高效。

蚂蚁TuGraph团队基于DB-GPT构建的开源GraphRAG框架,兼容了向量、图谱、全文等多种知识库索引底座,支持低成本的知识抽取、文档结构图谱、图社区摘要与混合检索以解决QFS问答问题。另外也提供了关键词、向量和自然语言等多样化的检索能力支持。

(16) Kotaemon【乐高】

乐高
:一套现成的问答积木套装,既能直接拿来用,又能自由拆装改造。用户要用就用,开发要改就改,随心所欲不失章法。

一个开源的干净且可定制的RAG UI,用于构建和定制自己的文档问答系统。既考虑了最终用户的需求,也考虑了开发者的需求。

(17) FlashRAG【百宝箱】

百宝箱
:把各路RAG神器打包成一个工具包,让研究者像挑选积木一样,随心所欲地搭建自己的检索模型。

FlashRAG是一个高效且模块化的开源工具包,旨在帮助研究人员在统一框架内重现现有的RAG方法并开发他们自己的RAG算法。我们的工具包实现了12种先进的RAG方法,并收集和整理了32个基准数据集。

(18) GRAG【侦探】

侦探
:不满足于表面线索,深入挖掘文本之间的关联网络,像破案一样追踪每条信息背后的真相,让答案更准确。

传统RAG模型在处理复杂的图结构数据时忽视了文本之间的联系和数据库的拓扑信息,从而导致了性能瓶颈。GRAG通过强调子图结构的重要性,显著提升了检索和生成过程的性能并降低幻觉。

(19) Camel-GraphRAG【左右开弓】

左右开弓
:一只眼睛用Mistral扫描文本提取情报,另只眼睛用Neo4j编织关系网。查找时左右眼配合,既能找相似的,又能顺着线索图追踪,让搜索更全面精准。

Camel-GraphRAG依托Mistral模型提供支持,从给定的内容中提取知识并构建知识结构,然后将这些信息存储在 Neo4j图数据库中。随后采用一种混合方法,将向量检索与知识图谱检索相结合,来查询和探索所存储的知识。

(20) G-RAG【串门神器】

串门神器
:不再是单打独斗地查资料,而是给每个知识点都建立人际关系网。像个社交达人,不仅知道每个朋友的特长,还清楚谁和谁是酒肉朋友,找答案时直接顺藤摸瓜。

RAG 在处理文档与问题上下文的关系时仍存在挑战,当文档与问题的关联性不明显或仅包含部分信息时,模型可能无法有效利用这些文档。此外,如何合理推断文档之间的关联也是一个重要问题。 G-RAG实现了RAG检索器和阅读器之间基于图神经网络(GNN)的重排器。该方法结合了文档之间的连接信息和语义信息(通过抽象语义表示图),为 RAG 提供了基于上下文的排序器。

(21) LLM-Graph-Builder【搬运工】

搬运工
:给混乱的文字安个明白的家。不是简单地搬运,而是像个强迫症患者,把每个知识点都贴上标签,画上关系线,最后在Neo4j的数据库里盖起一座井井有序的知识大厦。

Neo4j开源的基于LLM提取知识图谱的生成器,可以把非结构化数据转换成Neo4j中的知识图谱。利用大模型从非结构化数据中提取节点、关系及其属性。

2024.06

(22) MRAG【八爪鱼】

八爪鱼
:不是只长一个脑袋死磕问题,而是像章鱼一样长出多个触角,每个触角负责抓取一个角度。简单说,这就是AI版的"一心多用"。

现有的 RAG 解决方案并未专注于可能需要获取内容差异显著的多个文档的查询。此类查询经常出现,但具有挑战性,因为这些文档的嵌入在嵌入空间中可能相距较远,使得难以全部检索到它们。本文介绍了多头 RAG(MRAG),这是一种新颖的方案,旨在通过一个简单而强大的想法来填补这一空白:利用 Transformer 多头注意力层的激活,而非解码器层,作为获取多方面文档的键。其驱动动机是不同的注意力头可以学习捕获不同的数据方面。利用相应的激活会产生代表数据项和查询各个层面的嵌入,从而提高复杂查询的检索准确性。

(23) PlanRAG【战略家】

战略家
:先制定完整作战计划,再根据规则和数据分析局势,最后做出最佳战术决策。

PlanRAG研究如何利用大型语言模型解决复杂数据分析决策问题的方案,通过定义决策问答(Decision QA)任务,即根据决策问题Q、业务规则R和数据库D,确定最佳决策d。PlanRAG首先生成决策计划,然后检索器生成数据分析的查询。

(24) FoRAG【作家】

作家
:先列写作大纲构思文章框架,再逐段扩充完善内容。同时还配备了一个"编辑",通过仔细的事实核查和修改建议,帮助完善每个细节,确保作品的质量。

FoRAG提出了一种新颖的大纲增强生成器,在第一阶段生成器使用大纲模板,根据用户查询和上下文草拟答案大纲,第二阶段基于生成的大纲扩展每个观点,构建最终答案。同时提出一种基于精心设计的双精细粒度RLHF框架的事实性优化方法,通过在事实性评估和奖励建模两个核心步骤中引入细粒度设计,提供了更密集的奖励信号。

(25) Multi-Meta-RAG【元筛选器】

元筛选器
:像个经验丰富的资料管理员,通过多重筛选机制,从海量信息中精准定位最相关的内容。它不只看表面,还会深入分析文档的"身份标签"(元数据),确保找到的每份资料都真正对题。

Multi-Meta-RAG使用数据库过滤和LLM提取的元数据来改进RAG从各种来源中选择与问题相关的相关文档。

2024.07

(26) RankRAG【全能选手】

全能选手
:通过一点特训就能当好"评委"和"选手"双重角色。像个天赋异禀的运动员,只需要少量指导就能在多个项目上超越专业选手,还能把看家本领都融会贯通。

RankRAG的通过指令微调单一的LLM,使其同时具备上下文排名和答案生成的双重功能。通过在训练数据中加入少量排序数据,经过指令微调的大语言模型效果出奇地好,甚至超过了现有的专家排序模型,包括在大量排序数据上专门微调的相同大语言模型。这种设计不仅简化了传统RAG系统中多模型的复杂性,还通过共享模型参数增强了上下文的相关性判断和信息的利用效率。

(27) GraphRAG-Local-UI【改装师】

改装师
:把跑车改装成适合本地道路的实用车,加装了友好的仪表盘,让人人都能轻松驾驶。

GraphRAG-Local-UI是基于Microsoft的GraphRAG的本地模型适配版本,具有丰富的交互式用户界面生态系统。

(28) ThinkRAG【小秘书】

小秘书
:把庞大的知识体系浓缩成口袋版,像个随身携带的小秘书,不用大型设备就能随时帮你查找解答。

ThinkRAG大模型检索增强生成系统,可以轻松部署在笔记本电脑上,实现本地知识库智能问答。

(29) Nano-GraphRAG【轻装上阵】

轻装上阵
:像个轻装上阵的运动员,把繁复的装备都简化了,但保留了核心能力。

Nano-GraphRAG是一个更小、更快、更简洁的 GraphRAG,同时保留了核心功能。

2024.08

(30) RAGFlow-GraphRAG【导航员】

导航员
:在问答的迷宫里开辟捷径,先画张地图把知识点都标好,重复的路标合并掉,还特地给地图瘦身,让问路的人不会绕远路。

RAGFlow借鉴了GraphRAG的实现,在文档预处理阶段,引入知识图谱构建作为可选项,服务于QFS问答场景,并引入了实体去重、Token优化等改进。

(31) Tiny-GraphRAG【入门版】

入门版
:像个初学者友好的教学版本,保留了所有基础功能,让你能完整体验从收纳文档到查询问答的全过程,是GraphRAG家族的"新手套装"。

Tiny-GraphRAG是一个简洁版本的GraphRAG实现,旨在提供一个最简单的GraphRAG系统,包含所有必要的功能。我们实现了添加文档的全部流程,以及本地查询和全局查询的功能。

(32) Medical-Graph-RAG【数字医生】

数字医生
:像个经验丰富的医学顾问,用图谱把复杂的医疗知识整理得清清楚楚,诊断建议不是凭空想象,而是有理有据,让医生和患者都能看明白每个诊断背后的依据。

MedGraphRAG 是一个框架,旨在解决在医学中应用 LLM 的挑战。它使用基于图谱的方法来提高诊断准确性、透明度并集成到临床工作流程中。该系统通过生成由可靠来源支持的响应来提高诊断准确性,解决了在大量医疗数据中维护上下文的困难。

(33) W-RAG【进化搜索】

进化搜索
:像个善于自我进化的搜索引擎,通过大模型对文章段落的评分来学习什么是好答案,逐步提升自己找到关键信息的能力。

开放域问答中的弱监督密集检索技术,利用大型语言模型的排序能力为训练密集检索器创建弱标注数据。通过评估大型语言模型基于问题和每个段落生成正确答案的概率,对通过 BM25 检索到的前 K 个段落进行重新排序。排名最高的段落随后被用作密集检索的正训练示例。

(34) RAGChecker【质检员】

质检员
:不只简单地判断答案对错,而是会深入检查整个回答过程中的每个环节,从资料查找到最终答案生成,就像一个严格的考官,既给出详细的评分报告,还会指出具体哪里需要改进。

RAGChecker 的诊断工具为 RAG 系统提供细粒度、全面、可靠的诊断报告,并为进一步提升性能,提供可操作的方向。它不仅能评估系统的整体表现,还能深入分析检索和生成两大核心模块的性能。

(35) Meta-Knowledge-RAG【学者】

学者
:像个学术界的资深研究员,不仅收集资料,还会主动思考问题,为每份文档做批注和总结,甚至预先设想可能的问题。它会把相关的知识点串联起来,形成知识网络,让查询变得更有深度和广度,就像有一个学者在帮你做研究综述。

Meta-Knowledge-RAG(MK Summary)引入了一种新颖的以数据为中心的 RAG 工作流程,将传统的 “检索-读取” 系统转变为更先进的 “准备-重写-检索-读取” 框架,以实现对知识库的更高领域专家级理解。我们的方法依赖于为每个文档生成元数据和合成的问题与答案以及为基于元数据的文档集群引入元知识摘要的新概念。所提出的创新实现了个性化的用户查询增强和跨知识库的深度信息检索。

(36) CommunityKG-RAG【社群探索】

社群探索
:像个熟悉社区关系网络的向导,善于利用知识间的关联和群组特征,在不需要特别学习的情况下,就能准确地找到相关信息,并验证其可靠性。

CommunityKG-RAG是一种新颖的零样本框架,它将知识图谱中的社区结构与RAG系统相结合,以增强事实核查过程。CommunityKG-RAG能够在无需额外训练的情况下适应新的领域和查询,它利用知识图谱中社区结构的多跳性质,显著提高信息检索的准确性和相关性。

(37) TC-RAG【记忆术士】

记忆术士
:给LLM装了个带自动清理功能的大脑。就像我们解题,会把重要步骤写在草稿纸上,做完就划掉。它不是死记硬背,该记的记住,该忘的及时清空,像个会收拾房间的学霸。

通过引入图灵完备的系统来管理状态变量,从而实现更高效、准确的知识检索。通过利用具有自适应检索、推理和规划能力的记忆堆栈系统,TC-RAG不仅确保了检索过程的受控停止,还通过Push和Pop操作减轻了错误知识的积累。

(38) RAGLAB【竞技场】

竞技场
:让各种算法可以在相同的规则下进行公平竞争和比较,就像科学实验室里的标准化测试流程,确保每个新方法都能得到客观透明的评估。

新型RAG算法之间越来越缺乏全面和公平的比较,开源工具的高级抽象导致缺乏透明度,并限制了开发新算法和评估指标的能力。RAGLAB是一个模块化、研究导向的开源库,重现6种算法并构建全面研究生态。借助RAGLAB,我们在10个基准上公平对比6种算法,助力研究人员高效评估和创新算法。

2024.09

(39) MemoRAG【过目不忘】

过目不忘
:它不只是按需查找资料,而是已经把整个知识库都深入理解并记在心里。当你问问题时,它能快速从这个"超级大脑"中调取相关记忆,给出既准确又富有见地的答案,就像一个博学多识的专家。

MemoRAG是一个创新的检索增强生成(RAG)框架,构建在一个高效的超长记忆模型之上。与主要处理具有明确信息需求查询的标准RAG不同,MemoRAG利用其记忆模型实现对整个数据库的全局理解。通过从记忆中召回特定于查询的线索,MemoRAG增强了证据检索,从而产生更准确且具有丰富上下文的响应生成。

(40) OP-RAG【注意力管理】

注意力管理
:就像看一本特别厚的书,你不可能把每个细节都记住,但懂得在关键章节做好标记的人才是高手。它不是漫无目的地看,而是像个资深读书人,边读边在重点处画下重点,需要的时候直接翻到标记页。

LLM中的极长语境会导致对相关信息的关注度降低,并导致答案质量的潜在下降。重新审视长上下文答案生成中的RAG。我们提出了一种顺序保留检索增强生成机制OP-RAG,显著提高了RAG在长上下文问答应用中的性能。

(41) AgentRE【智能抽取】

智能抽取
:像个善于观察人际关系的社会学家,不仅能记住关键信息,还会主动查证并深入思考,从而准确理解复杂的关系网络。即使面对错综复杂的关系,也能通过多角度分析,理清其中的脉络,避免望文生义。

AgentRE通过整合大型语言模型的记忆、检索和反思能力,有效应对复杂场景关系抽取中关系类型多样以及单个句子中实体之间关系模糊的挑战。AgentRE 包含三大模块,助力代理高效获取并处理信息,显著提升 RE 性能。

(42) iText2KG【建筑师】

建筑师
:像个有条理的工程师,通过分步骤提炼、提取和整合信息,逐步将零散文档转化为系统的知识网络,而且不需要事先准备详细的建筑图纸,可以根据需要灵活地扩建和完善。

iText2KG(增量式知识图谱构建)利用大型语言模型 (LLM) 从原始文档中构建知识图谱,并通过四个模块(文档提炼器、增量实体提取器、增量关系提取器和图谱集成器)实现增量式知识图谱构建,无需事先定义本体或进行大量的监督训练。

(43) GraphInsight【图谱解读】

图谱解读
:像个擅长信息图表分析的专家,知道把重要信息放在最显眼的位置,同时在需要时查阅参考资料来补充细节,并能step by step地推理复杂图表,让AI既能把握全局又不遗漏细节。

GraphInsight旨在提升LLMs对宏观和微观层面图形信息理解的新框架。GraphInsight基于两大关键策略:1)将关键图形信息置于LLMs记忆性能较强的位置;2)借鉴检索增强生成(RAG)的思想,对记忆性能较弱的区域引入轻量级外部知识库。此外,GraphInsight探索将这两种策略整合到LLM代理过程中,以应对需要多步推理的复合图任务。

(44) LA-RAG【方言通】

方言通
:像个精通各地方言的语言专家,通过细致的语音分析和上下文理解,不仅能准确识别标准普通话,还能听懂带有地方特色的口音,让AI与不同地区的人都能无障碍交流。

LA-RAG,是一种基于LLM的ASR的新型检索增强生成(RAG)范例。LA-RAG 利用细粒度标记级语音数据存储和语音到语音检索机制,通过 LLM 上下文学习 (ICL) 功能提高 ASR 准确性。

(45) SFR-RAG【精简检索】

精简检索
:像个精练的参考顾问,体积虽小但功能精准,既能理解需求又懂得寻求外部帮助,保证回答既准确又高效。

SFR-RAG是一个经过指令微调的小型语言模型,重点是基于上下文的生成和最小化幻觉。通过专注于在保持高性能的同时减少参数数量,SFR-RAG模型包含函数调用功能,使其能够与外部工具动态交互以检索高质量的上下文信息。

(46) FlexRAG【压缩专家】

压缩专家
:把长篇大论浓缩成精华摘要,而且压缩比例可以根据需要灵活调整,既不丢失关键信息,又能节省存储和处理成本。就像把一本厚书精炼成一份简明扼要的读书笔记。

FlexRAG检索到的上下文在被LLMs编码之前被压缩为紧凑的嵌入。同时这些压缩后的嵌入经过优化以提升下游RAG的性能。FlexRAG的一个关键特性是其灵活性,它能够有效支持不同的压缩比,并选择性地保留重要上下文。得益于这些技术设计,FlexRAG在显著降低运行成本的同时实现了卓越的生成质量。在各种问答数据集上进行的全面实验验证了我们的方法是RAG系统的一种具有成本效益且灵活的解决方案。

(47) CoTKR【图谱翻译】

图谱翻译
:像个耐心的老师,先理解知识的来龙去脉,再一步步讲解,不是简单复述而是深入浅出地转述。同时通过不断收集"学生"的反馈来改进自己的讲解方式,让知识传递更加清晰有效。

CoTKR(Chain-of-Thought Enhanced Knowledge Rewriting)方法交替生成推理路径和相应知识,从而克服了单步知识改写的限制。此外,为了弥合知识改写器和问答模型之间的偏好差异,我们提出了一种训练策略,即从问答反馈中对齐偏好通过利用QA模型的反馈进一步优化知识改写器。

2024.10

(48) Open-RAG【智囊团】

智囊团
:把庞大的语言模型分解成专家小组,让它们既能独立思考又能协同工作,还特别会分辨真假信息,关键时刻知道该不该查资料,像个经验丰富的智囊团。

Open-RAG通过开源大语言模型提高RAG中的推理能力,将任意密集的大语言模型转换为参数高效的稀疏专家混合(MoE)模型,该模型能够处理复杂的推理任务,包括单跳和多跳查询。OPEN-RAG独特地训练模型以应对那些看似相关但具有误导性的挑战性干扰项。

(49) TableRAG【Excel专家】

Excel专家
:不只简单地查看表格数据,而是懂得从表头和单元格两个维度去理解和检索数据,就像熟练使用数据透视表一样,能快速定位和提取所需的关键信息。

TableRAG专为表格理解设计了检索增强生成框架,通过查询扩展结合Schema和单元格检索,能够在提供信息给语言模型之前精准定位关键数据,从而实现更高效的数据编码和精确检索,大幅缩短提示长度并减少信息丢失。

(50) LightRAG【蜘蛛侠】

蜘蛛侠
:在知识的网中灵活穿梭,既能抓住知识点之间的丝,又能借网顺藤摸瓜。像个长了千里眼的图书管理员,不仅知道每本书在哪,还知道哪些书该一起看。

该框架将图结构融入文本索引和检索过程中。这一创新框架采用了一个双层检索系统,从低级和高级知识发现中增强全面的信息检索。此外,将图结构与向量表示相结合,便于高效检索相关实体及其关系,显著提高了响应时间,同时保持了上下文相关性。这一能力通过增量更新算法得到了进一步增强,该算法确保了新数据的及时整合,使系统能够在快速变化的数据环境中保持有效性和响应性。

(51) AstuteRAG【明智判官】

明智判官
:对外部信息保持警惕,不轻信检索结果,善用自身积累的知识,甄别信息真伪,像资深法官一样,权衡多方证据定论。

通过适应性地从LLMs内部知识中提取信息,结合外部检索结果,并根据信息的可靠性来最终确定答案,从而提高系统的鲁棒性和可信度。

(52) TurboRAG【速记高手】

速记高手
:提前把功课做好,把答案都记在小本本里。像个考前突击的学霸,不是临场抱佛脚,而是把常考题提前整理成错题本。需要的时候直接翻出来用,省得每次都要现场推导一遍。

TurboRAG通过离线预计算和存储文档的KV缓存来优化RAG系统的推理范式。与传统方法不同,TurboRAG在每次推理时不再计算这些KV缓存,而是检索预先计算的缓存以进行高效的预填充,从而消除了重复在线计算的需要。这种方法显著减少了计算开销,加快了响应时间,同时保持了准确性。

(53) StructRAG【收纳师】

收纳师
:把杂乱无章的信息像收纳衣柜一样分门别类地整理好。像个模仿人类思维的学霸,不是死记硬背,而是先画个思维导图。

受人类在处理知识密集型推理时将原始信息转换为结构化知识的认知理论启发,该框架引入了一种混合信息结构化机制,该机制根据手头任务的特定要求以最合适的格式构建和利用结构化知识。通过模仿类人的思维过程,提高了LLM在知识密集型推理任务上的表现。

(54) VisRAG【火眼金睛】

火眼金睛
:终于悟出文字不过是图像的一种特殊表现形式。像个开了天眼的阅读者,不再执着于逐字解析,而是直接"看"透全局。用照相机代替了OCR,懂得了"一图胜千言"的精髓。

通过构建基于视觉-语言模型 (VLM) 的RAG流程,直接将文档作为图像嵌入并检索,从而增强生成效果。相比传统文本RAG,VisRAG避免了解析过程中的信息损失,更全面地保留了原始文档的信息。实验显示,VisRAG在检索和生成阶段均超越传统RAG,端到端性能提升达25-39%。VisRAG不仅有效利用训练数据,还展现出强大的泛化能力,成为多模态文档RAG的理想选择。

(55) AGENTiGraph【知识管家】

知识管家
:像个善于对话的图书管理员,通过日常交流帮你整理和展示知识,带着一队助手随时准备解答问题、更新资料,让知识管理变得简单自然。

AGENTiGraph通过自然语言交互进行知识管理的平台。它集成了知识提取、集成和实时可视化。AGENTiGraph 采用多智能体架构来动态解释用户意图、管理任务和集成新知识,确保能够适应不断变化的用户需求和数据上下文。

(56) RuleRAG【循规蹈矩】

循规蹈矩
:用规矩来教AI做事,就像带新人入职,先给本员工手册。不是漫无目的地学,而是像个严格的老师,先把规矩和范例都讲明白,然后再让学生自己动手。做多了,这些规矩就变成了肌肉记忆,下次遇到类似问题自然知道怎么处理。

RuleRAG提出了基于语言模型的规则引导检索增强生成方法,该方法明确引入符号规则作为上下文学习(RuleRAG - ICL)的示例,以引导检索器按照规则方向检索逻辑相关的文档,并统一引导生成器在同一组规则的指导下生成有依据的答案。此外,查询和规则的组合可进一步用作有监督的微调数据,用以更新检索器和生成器(RuleRAG - FT),从而实现更好的基于规则的指令遵循能力,进而检索到更具支持性的结果并生成更可接受的答案。

(57) Class-RAG【法官】

法官
:不是靠死板的条文判案,而是通过不断扩充的判例库来研判。像个经验老到的法官,手握活页法典,随时翻阅最新案例,让判决既有温度又有尺度。

内容审核分类器对生成式 AI 的安全性至关重要。然而,安全与不安全内容间的细微差别常令人难以区分。随着技术广泛应用,持续微调模型以应对风险变得愈发困难且昂贵。为此,我们提出 Class-RAG 方法,通过动态更新检索库,实现即时风险缓解。与传统微调模型相比,Class-RAG 更具灵活性与透明度,且在分类与抗攻击方面表现更佳。研究还表明,扩大检索库能有效提升审核性能,成本低廉。

(58) Self-RAG【反思者】

反思者
:在回答问题时,不仅会查阅资料,还会不断思考和检查自己的答案是否准确完整。通过"边说边想"的方式,像一个谨慎的学者一样,确保每个观点都有可靠的依据支持。

Self-RAG通过检索和自我反思来提升语言模型的质量和准确性。框架训练一个单一的任意语言模型,该模型能按需自适应地检索文段,并使用被称为反思标记的特殊标记来对检索到的文段及其自身生成的内容进行生成和反思。生成反思标记使得语言模型在推理阶段具备可控性,使其能够根据不同的任务要求调整自身行为。

(59) SimRAG【自学成才】

自学成才
:面对专业领域时,先自己提问再自己回答,通过不断练习来提升专业知识储备,就像学生通过反复做习题来熟悉专业知识一样。

SimRAG是一种自训练方法,使LLM具备问答和问题生成的联合能力以适应特定领域。只有真正理解了知识,才能提出好的问题。这两个能力相辅相成,可以帮助模型更好地理解专业知识。首先在指令遵循、问答和搜索相关数据上对LLM进行微调。然后,它促使同一LLM从无标签语料库中生成各种与领域相关的问题,并采用额外的过滤策略来保留高质量的合成示例。通过利用这些合成示例,LLM可以提高其在特定领域RAG任务上的性能。

(60) ChunkRAG【摘抄达人】

摘抄达人
:先把长文章分成小段落,再用专业眼光挑出最相关的片段,既不遗漏重点,又不被无关内容干扰。

ChunkRAG提出LLM驱动的块过滤方法,通过在块级别评估和过滤检索到的信息来增强RAG系统的框架,其中 “块” 代表文档中较小的连贯部分。我们的方法采用语义分块将文档划分为连贯的部分,并利用基于大语言模型的相关性评分来评估每个块与用户查询的匹配程度。通过在生成阶段之前过滤掉不太相关的块,我们显著减少了幻觉并提高了事实准确性。

(61) FastGraphRAG【雷达】

雷达
:像谷歌网页排名一样,给知识点也排出个热度榜。就好比社交网络中的意见领袖,越多人关注就越容易被看见。它不是漫无目的地搜索,而是像个带着雷达的侦察兵,哪里的信号强就往哪里看。

FastGraphRAG提供了一个高效、可解释且精度高的快速图检索增强生成(FastGraphRAG)框架。它将PageRank算法应用于知识图谱的遍历过程,快速定位最相关的知识节点。通过计算节点的重要性得分,PageRank使GraphRAG能够更智能地筛选和排序知识图谱中的信息。这就像是为GraphRAG装上了一个"重要性雷达",能够在浩如烟海的数据中快速定位关键信息。

(62) AutoRAG【调音师】

调音师
:一位经验丰富的调音师,不是靠猜测调音,而是通过科学测试找到最佳音效。它会自动尝试各种RAG组合,就像调音师测试不同的音响设备搭配,最终找到最和谐的"演奏方案"。

AutoRAG框架能够自动为给定数据集识别合适的RAG模块,并探索和逼近该数据集的RAG模块的最优组合。通过系统评估不同的RAG设置来优化技术选择,该框架类似于传统机器学习中的AutoML实践,通过广泛实验来优化RAG技术的选择,提高RAG系统的效率和可扩展性。

(63) Plan×RAG【项目经理】

项目经理
:先规划后行动,把大任务分解成小任务,安排多个"专家"并行工作。每个专家负责自己的领域,最后由项目经理统筹汇总结果。这种方式不仅更快、更准,还能清楚交代每个结论的来源。

Plan×RAG是一个新颖的框架,它将现有RAG框架的 “检索 - 推理” 范式扩充为 “计划 - 检索”范式。Plan×RAG 将推理计划制定为有向无环图(DAG),将查询分解为相互关联的原子子查询。答案生成遵循 DAG 结构,通过并行检索和生成显著提高效率。虽然最先进的RAG解决方案需要大量的数据生成和语言模型(LMs)的微调,但Plan×RAG纳入了冻结的LMs作为即插即用的专家来生成高质量的答案。

(64) SubgraphRAG【定位仪】

定位仪
:不是漫无目的地大海捞针,而是精准绘制一张小型知识地图,让 AI 能快速找到答案。

SubgraphRAG扩展了基于KG的RAG框架,通过检索子图并利用LLM进行推理和答案预测。将轻量级多层感知器与并行三元组评分机制相结合,以实现高效灵活的子图检索,同时编码有向结构距离以提高检索有效性。检索到的子图大小可以灵活调整,以匹配查询需求和下游LLM的能力。这种设计在模型复杂性和推理能力之间取得了平衡,实现了可扩展且通用的检索过程。

2024.11

(65) RuRAG【炼金术士】

炼金术士
:像个炼金术士,能将海量数据提炼成清晰的逻辑规则,并用通俗易懂的语言表达出来,让AI在实际应用中更有智慧。

旨在通过将大量离线数据自动蒸馏成可解释的一阶逻辑规则,并注入大型语言模型(LLM)中,以提升其推理能力。该框架使用蒙特卡洛树搜索(MCTS)来发现逻辑规则,并将这些规则转化为自然语言,实现针对LLM下游任务的知识注入和无缝集成。该论文在公共和私有工业任务上评估了该框架的有效性,证明了其在多样化任务中增强LLM能力的潜力。

(66) RAGViz【透视眼】

透视眼
:让RAG系统变透明,看得见模型在读哪句话,像医生看X光片一样,哪里不对一目了然。

RAGViz提供了对检索文档和模型注意力的可视化,帮助用户理解生成的标记与检索文档之间的交互,可用于诊断和可视化RAG系统。

(67) AgenticRAG【智能助手】

智能助手
:不再是简单的查找复制,而是配了个能当机要秘书的助手。像个得力的行政官,不光会查资料,还知道什么时候该打电话,什么时候该开会,什么时候该请示领导。

AgenticRAG描述了基于AI智能体实现的RAG。具体来说,它将AI智能体纳入RAG流程中,以协调其组件并执行超出简单信息检索和生成的额外行动,以克服非智能体流程的局限性。

(68) HtmlRAG【排版师】

排版师
:把知识不是当作流水账来记,而是像排版杂志一样,该加粗的加粗,该标红的标红。就像一个挑剔的美编,觉得光有内容不够,还得讲究排版,这样重点才能一目了然。

HtmlRAG在RAG中使用HTML而不是纯文本作为检索知识的格式,在对外部文档中的知识进行建模时,HTML比纯文本更好,并且大多数LLM具有强大的理解HTML的能力。HtmlRAG提出了HTML清理、压缩和修剪策略,以缩短HTML同时最小化信息损失。

(69) M3DocRAG【感官达人】

感官达人
:不是只会读书,还能看图识图,听声辨位。像个综艺节目里的全能选手,图片能看懂,文字能理解,该跳跃思维时就跳跃,该专注细节时就专注,各种挑战都难不倒。

M3DocRAG是一种新颖的多模态RAG框架,能够灵活适应各种文档上下文(封闭域和开放域)、问题跳转(单跳和多跳)和证据模式(文本、图表、图形等)。M3DocRAG使用多模态检索器和MLM查找相关文档并回答问题,因此它可以有效地处理单个或多个文档,同时保留视觉信息。

(70) KAG【逻辑大师】

逻辑大师
:不光靠感觉找相似的答案,还得讲究知识间的因果关系。像个严谨的数学老师,不仅要知道答案是什么,还得解释清楚这答案是怎么一步步推导出来的。

RAG中向量相似性与知识推理的相关性之间的差距,以及对知识逻辑(如数值、时间关系、专家规则等)不敏感阻碍了专业知识服务的有效性。KAG的设计目的是充分利用知识图谱(KG)和向量检索的优势来应对上述挑战,并通过五个关键方面双向增强大型语言模型(LLM)和知识图谱来提高生成和推理性能:(1)对LLM友好的知识表示,(2)知识图谱与原始块之间的相互索引,(3)逻辑形式引导的混合推理引擎,(4)与语义推理的知识对齐,(5)KAG的模型能力增强。

(71) FILCO【筛选师】

筛选师
:像个严谨的编辑,善于从大量文本中识别并保留最有价值的信息,确保传递给AI的每段内容都精准且相关。

FILCO通过基于词法和信息论方法识别有用的上下文,以及训练上下文过滤模型,以过滤检索到的上下文,来提高提供给生成器的上下文质量。

(72) LazyGraphRAG【精算师】

精算师
:能省一步是一步,把贵的大模型用在刀刃上。就像个会过日子的主妇,不是看到超市打折就买,而是货比三家后才决定在哪里花钱最值。

一种新型的图谱增强生成增强检索(RAG)方法。这种方法显著降低了索引和查询成本,同时在回答质量上保持或超越竞争对手,使其在多种用例中具有高度的可扩展性和高效性。LazyGraphRAG推迟了对LLM的使用。在索引阶段,LazyGraphRAG仅使用轻量级的NLP技术来处理文本,将LLM的调用延迟到实际查询时。这种“懒惰”的策略避免了前期高昂的索引成本,实现了高效的资源利用。

传统GraphRAG LazyGraphRAG
索引阶段 - 使用LLM提取并描述实体和关系
- 为每个实体和关系生成摘要
- 利用LLM总结社区内容
- 生成嵌入向量
- 生成Parquet文件
- 使用NLP技术提取概念和共现关系
- 构建概念图
- 提取社区结构
- 索引阶段不使用LLM
查询阶段 - 直接使用社区摘要回答查询
- 缺乏对查询的细化和对相关信息的聚焦
- 使用LLM细化查询并生成子查询
- 根据相关性选择文本片段和社区
- 使用LLM提取和生成答案
- 更加聚集于相关内容,回答更精确
LLM调用 - 在索引阶段和查询阶段都大量使用 - 在索引阶段不使用LLM
- 仅在查询阶段调用LLM
- LLM的使用更加高效
成本效率 - 索引成本高,耗时长
- 查询性能受限于索引质量
- 索引成本仅为传统GraphRAG的0.1%
- 查询效率高,答案质量好
数据存储 - 索引数据生成 Parquet 文件,适合大规模数据的存储和处理 - 索引数据存储为轻量级格式(如 JSON、CSV),更适合快速开发和小规模数据
使用场景 - 适用于对计算资源和时间不敏感的场景
- 需要提前构建完整的知识图谱,并存储为Parquet文件,方便后续导入数据库进行复杂分析
- 适用于需要快速索引和响应的场景
- 适合一次性查询、探索性分析和流式数据处理

RAG Survey

RAG Benchmark

在开发ASP.NET Core应用程序时,全局异常处理是一个重要的概念。它允许我们集中处理应用程序中未捕获的异常,确保应用程序的稳定性和用户体验。

1. 为什么需要全局异常处理

全局异常处理的目的是为了:

  • 统一错误响应
    :确保所有错误都以统一的格式返回给客户端。
  • 避免敏感信息泄露
    :防止将内部错误细节暴露给用户。
  • 提高用户体验
    :通过友好的错误信息提升用户满意度。
  • 日志记录
    :记录错误详情,便于后续的问题追踪和修复。

2. 异常过滤器与异常中间件

在ASP.NET Core MVC中,异常过滤器(Exception Filters)和异常中间件(Middleware)都可以用来处理异常,但它们在请求处理管道中的位置和用途有所不同。

图片引用自博客园:
https://www.cnblogs.com/dotnet261010/p/13193124.html

下面分析一下两者的区别

2.1异常中间件

异常中间件是在请求处理管道中处理异常的组件。它可以捕获整个请求处理过程中抛出的异常,包括在中间件、控制器、动作方法或视图渲染过程中发生的异常。

实现方式

异常中间件通常通过实现
IMiddleware
接口或创建一个扩展方法来注册。如前文所述,可以创建一个自定义的异常处理中间件,并在
Startup.cs
中注册它。

特点

  • 全局性
    :异常中间件可以捕获整个请求处理管道中的异常。
  • 顺序性
    :需要在
    Startup.cs
    中正确配置中间件的顺序。
  • 灵活性
    :可以自定义异常处理逻辑,包括错误日志记录、错误响应格式等。

使用

创建一个异常处理中间件。这个中间件会在请求处理管道中的其他中间件抛出异常时被调用。

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;

    public ExceptionHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        var result = JsonConvert.SerializeObject(new
        {
            Success = false,
            Message = "An error occurred while processing your request.",
            Error = exception.Message
        });

        context.Response.ContentType = "application/json";
        return context.Response.WriteAsync(result);
    }
}

注册中间件,在
Startup.cs
中注册这个中间件

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 其他中间件注册...

    app.UseMiddleware<ExceptionHandlingMiddleware>();

    // 其他中间件注册...
}

2.2MVC异常过滤器

MVC异常过滤器是专门用于ASP.NET Core MVC应用程序的异常处理机制。它们允许开发者针对特定的控制器或动作方法配置异常处理逻辑。

实现方式

异常过滤器可以通过实现
IExceptionFilter

IAsyncExceptionFilter

IExceptionFilter

IAsyncExceptionFilter
接口来创建。

public class CustomExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        // 处理异常
        context.Result = new ContentResult
        {
            Content = "An error occurred.",
            StatusCode = (int)HttpStatusCode.InternalServerError
        };
        context.ExceptionHandled = true;
    }
}

注册方式

异常过滤器可以在全局、控制器或动作方法级别注册。

  • 全局注册
    :在
    Startup.cs
    中使用
    services.AddControllers(options => options.Filters.Add(new CustomExceptionFilter());
  • 控制器级别
    :通过在控制器类上使用
    [TypeFilter(typeof(CustomExceptionFilter))]
    属性。
  • 动作方法级别
    :通过在动作方法上使用
    [ExceptionFilter(typeof(CustomExceptionFilter))]
    属性。

特点

  • 针对性
    :可以针对特定的控制器或动作方法配置异常处理逻辑。
  • 集成性
    :与MVC框架紧密集成,可以直接访问
    ExceptionContext
  • 顺序性
    :可以在执行过程中的多个阶段(如
    OnException

    OnExceptionAsync
    )处理异常。

比较和选择

  • 全局性 vs. 针对性
    :异常中间件提供全局异常处理,而异常过滤器允许针对特定控制器或动作方法进行处理。
  • 处理时机
    :异常中间件可以在请求处理的任何阶段捕获异常,而异常过滤器仅在MVC的执行过程中捕获异常。
  • 使用场景
    :对于需要全局统一处理的异常,使用异常中间件;对于需要特定逻辑处理的异常,使用异常过滤器。

总结

全局异常处理是ASP.NET Core应用程序中一个重要的功能,它有助于提高应用程序的稳定性和用户体验。
欢迎关注我的公众号“
Net分享
”,技术文章第一时间推送,随缘更新 , 分享一些你可能注意不到的细节。

博客:
https://learnopencv.com/lightrag
视频:
https://www.youtube.com/watch?v=oageL-1I0GE
代码:
https://github.com/HKUDS/LightRAG
论文:
https://arxiv.org/abs/2410.05779

​ - 时间:2024.10

​ - 单位:University of Hong Kong、Beijing University of Posts and Telecommunications

RAG基本

好文:
https://zhuanlan.zhihu.com/p/675509396
数据准备阶段:数据提取——>文本分块(Chunking)——>向量化(embedding)——>数据入库
应用阶段:用户提问——>数据检索(召回)——>重排/过滤——>注入Prompt——>LLM生成答案

RAG发展

  • 传统RAG:
    (1)痛点:


    • 它们无法捕捉分散在多个文档中的碎片信息之间的相互联系,这使得概括全面的见解变得具有挑战性。
    • 有限的上下文理解源于缺乏对检索到的块的整体概述。
    • 当数据量不断增长时,就会出现可扩展性效率低下的问题,从而导致检索质量不佳。
  • GraphRAG:
    好文:GraphRAG学习小结(2)_graphrag local-CSDN博客
    论文:
    https://arxiv.org/pdf/2404.16130
    - 时间:2024.4
    - 单位:Microsoft
    (1)创新:


    • 为了构建结构化索引知识图谱KG,GraphRAG 使用 LLM 从源文档中提取实体和关系。KG有什么好处呢?
      • 而传统 RAG 主要依赖于文本块的相似匹配,如果查询问题与存储文本不完全匹配或不在同一个文本块中,就可能无法返回最相关的结果,导致生成结果不够精准。GraphRAG可以有效地连接和聚合多个相关特性,即使它们在不同的节点中表达,也能通过图谱中的关系找到最佳检索结果。因此,它能更精确地满足复杂、多维的用户查询。

      • 传统RAG的一大痛点是它虽然能够较好的回答一些事实性问题,但是在面对一些统计性、总结性、概要性的QFS问题(Query-Focused Summarization)时却无能为力,或需要采用更复杂的技术手段来解决。GraphRAG的一个主要出发点也是能够更好的回答基于高层语义理解的总结性查询问题。


    (2)痛点:


    • GraphRAG 运行速度通常非常慢,因为它需要多个 LLM API 调用,可能会达到速率限制。

    • 它的成本极高。

    • 为了将新数据合并到现有的图形索引中,我们还需要为以前的数据重建整个 KG,这是一种低效的方法。

    • 没有对重复元素执行明确的重复数据删除步骤,这会导致图形索引嘈杂。

快速总结(vs GraphRAG)

(1)
LightRAG 的知识图谱可以增量更新。
为了将新数据合并到现有的图形索引中,GraphRAG 还需要为以前的数据重建整个 KG
(2)结合了 graph indexing 和 standard embedding 方法构建知识图谱。相对于 GraphRAG 以社区遍历的方法,
LightRAG 专注于实体和关系的检索,
进而减少检索开销
(3)
Hybrid Query
双层检索策略,
结合了 local 和 global 方法检索
(4)
推理速度更快。
在检索阶段,LightRAG 需要少于 100 个token和一个 API 调用,而 GraphRAG 需要 社区数量 x 每个社区的平均令牌数量token

详细步骤

image

构建基于图的文本索引(知识图谱 KG)

(1)
R(.),提取实体和关系:
正如 GraphRAG 中看到的那样,这里
精心设计的提示词
被发送到 LLM 以获取节点和边

image

"""Example 1:

Entity_types: [person, technology, mission, organization, location]
Text:
while Alex clenched his jaw, the buzz of frustration dull against the backdrop of Taylor's authoritarian certainty. It was this competitive undercurrent that kept him alert, the sense that his and Jordan's shared commitment to discovery was an unspoken rebellion against Cruz's narrowing vision of control and order.

Then Taylor did something unexpected. They paused beside Jordan and, for a moment, observed the device with something akin to reverence. “If this tech can be understood..." Taylor said, their voice quieter, "It could change the game for us. For all of us.”

The underlying dismissal earlier seemed to falter, replaced by a glimpse of reluctant respect for the gravity of what lay in their hands. Jordan looked up, and for a fleeting heartbeat, their eyes locked with Taylor's, a wordless clash of wills softening into an uneasy truce.

It was a small transformation, barely perceptible, but one that Alex noted with an inward nod. They had all been brought here by different paths
################
Output:
("entity"{tuple_delimiter}"Alex"{tuple_delimiter}"person"{tuple_delimiter}"Alex is a character who experiences frustration and is observant of the dynamics among other characters."){record_delimiter}
("entity"{tuple_delimiter}"Taylor"{tuple_delimiter}"person"{tuple_delimiter}"Taylor is portrayed with authoritarian certainty and shows a moment of reverence towards a device, indicating a change in perspective."){record_delimiter}
("entity"{tuple_delimiter}"Jordan"{tuple_delimiter}"person"{tuple_delimiter}"Jordan shares a commitment to discovery and has a significant interaction with Taylor regarding a device."){record_delimiter}
("entity"{tuple_delimiter}"Cruz"{tuple_delimiter}"person"{tuple_delimiter}"Cruz is associated with a vision of control and order, influencing the dynamics among other characters."){record_delimiter}
("entity"{tuple_delimiter}"The Device"{tuple_delimiter}"technology"{tuple_delimiter}"The Device is central to the story, with potential game-changing implications, and is revered by Taylor."){record_delimiter}
("relationship"{tuple_delimiter}"Alex"{tuple_delimiter}"Taylor"{tuple_delimiter}"Alex is affected by Taylor's authoritarian certainty and observes changes in Taylor's attitude towards the device."{tuple_delimiter}"power dynamics, perspective shift"{tuple_delimiter}7){record_delimiter}
("relationship"{tuple_delimiter}"Alex"{tuple_delimiter}"Jordan"{tuple_delimiter}"Alex and Jordan share a commitment to discovery, which contrasts with Cruz's vision."{tuple_delimiter}"shared goals, rebellion"{tuple_delimiter}6){record_delimiter}
("relationship"{tuple_delimiter}"Taylor"{tuple_delimiter}"Jordan"{tuple_delimiter}"Taylor and Jordan interact directly regarding the device, leading to a moment of mutual respect and an uneasy truce."{tuple_delimiter}"conflict resolution, mutual respect"{tuple_delimiter}8){record_delimiter}
("relationship"{tuple_delimiter}"Jordan"{tuple_delimiter}"Cruz"{tuple_delimiter}"Jordan's commitment to discovery is in rebellion against Cruz's vision of control and order."{tuple_delimiter}"ideological conflict, rebellion"{tuple_delimiter}5){record_delimiter}
("relationship"{tuple_delimiter}"Taylor"{tuple_delimiter}"The Device"{tuple_delimiter}"Taylor shows reverence towards the device, indicating its importance and potential impact."{tuple_delimiter}"reverence, technological significance"{tuple_delimiter}9){record_delimiter}
("content_keywords"{tuple_delimiter}"power dynamics, ideological conflict, discovery, rebellion"){completion_delimiter}"""

(2)
P(.),LLM 分析键值对生成:
键 (K) 是一个单词或短语,而值 (V) 是一段总结相关内容的段落。为每个实体及每条关系生成一个文本键值对(K, V),其中 K 是一个单词或短语,便于高效检索,V 是一个文本段落,用于文本片段的总结。

LightRAG combines graph indexing and standard embedding based approach. These KV data structures offer a more precise retrieval than less accurate standard embedding only RAG or inefficient chunk traversal techniques in GraphRAG.

image

PROMPTS[
    "summarize_entity_descriptions"
] = """You are a helpful assistant responsible for generating a comprehensive summary of the data provided below.
Given one or two entities, and a list of descriptions, all related to the same entity or group of entities.
Please concatenate all of these into a single, comprehensive description. Make sure to include information collected from all the descriptions.
If the provided descriptions are contradictory, please resolve the contradictions and provide a single, coherent summary.
Make sure it is written in third person, and include the entity names so we the have full context.
Use {language} as output language.

#######
-Data-
Entities: {entity_name}
Description List: {description_list}
#######
Output:
"""

(3)
D(.),重复数据删除以优化图形操作:
通过去重函数识别并合并来自不同段落的相同实体和关系。

最后,我们得到了初始 KG 的最终优化版本:

img

双层检索机制

image

(1)对于给定的查询,LightRAG 的检索算法会提取局部查询关键词(low_level_keywords)和全局查询关键词(high_level_keywords):

image

(2)检索算法使用向量数据库来匹配
局部查询关键词与候选实体
,以及
全局查询关键词与候选关系

async def _build_global_query_context(
    keywords,
    knowledge_graph_inst: BaseGraphStorage,
    entities_vdb: BaseVectorStorage,
    relationships_vdb: BaseVectorStorage,
    text_chunks_db: BaseKVStorage[TextChunkSchema],
    query_param: QueryParam,
):
    

# 用keywords匹配top_k个最相关的relationships

edge_datas = await relationships_vdb.query(keywords, top_k=query_param.top_k)

# 根据relationships找到entities

use_entities = await _find_most_related_entities_from_relationships(
edge_datas, query_param, knowledge_graph_inst
)

# 找到对应的文本块

use_text_units = await _find_related_text_unit_from_relationships(
edge_datas, query_param, text_chunks_db, knowledge_graph_inst
)

......
from nano_vectordb import NanoVectorDB

......

self._client = NanoVectorDB(
     self.embedding_func.embedding_dim, storage_file=self._client_file_name
)

......

async def query(self, query: str, top_k=5):
    embedding = await self.embedding_func([query])
    embedding = embedding[0]
    results = self._client.query(
        query=embedding,
        top_k=top_k,
        better_than_threshold=self.cosine_better_than_threshold,
    )
    results = [
        {**dp, "id": dp["__id__"], "distance": dp["__metrics__"]} for dp in results
    ]
    return results

(3)增强高阶关联性:。LightRAG 进一步收集已检索到的实体或关系的局部子图,如实体或关系的一跳邻近节点,数学公式表示如下:

image

其中 Nv 和 Ne 分别表示检索到的节点 v 和边 e 的单跳相邻节点。

(4)检索到的信息分为三个部分:实体、关系和对应的文本块。然后将这个结构化数据输入到 LLM 中,使其能够为查询生成全面的答案:

image

评估指标

Win rates

跨越四个数据集和四个评估维度:

image

引入了一个综合评估框架,
用于根据三个关键标准将两个答案与同一问题进行比较
:Comprehensiveness、Diversity 和 Empowerment。其目的是通过为每个标准选择更好的答案来指导 LLM,然后进行整体评估。对于这三个标准中的每一个,LLM 必须确定哪些答案表现更好,并为其选择提供理由。最终,整体获胜者是根据所有三个维度的性能确定的,并附有证明决策的详细摘要:

image

Model cost

image

(1)索引阶段(Retrieval Phase):
Cmax 表示每个 API 调用允许的最大令牌数。在检索阶段,GraphRAG 生成 1,399 个社区,本实验积极使用 610 个 level-2 社区进行检索。
因为 GraphRAG 的 map-reduce 机制
,导致总内存消耗为 610,000 个 token(610 个社区 × 每个社区 1,000 个 token)。此外,GraphRAG 单独遍历每个社区的要求会导致数百个 API 调用,显著增加检索开销。相比之下,LightRAG 通过使用少于 100 个标记的关键字生成和检索来优化这个过程,只需要整个过程的单个 API 调用。
(2)更新 KG 阶段(Incremental Text Update):
Textract 表示实体和关系提取的令牌开销,Cextract 表示 extraction 所需的 API 调用数。在增量数据更新阶段,GraphRAG 必须消除其现有的社区结构以合并新的实体和关系,然后进行完全再生。GraphRAG 将需要大约 1399 个社区 × 2 级 level × 每个社区产生约 5,000 个 token 成本来重建原始和新的社区报告。相比之下,LightRAG 无缝地将新提取的实体和关系集成到现有图中,而无需完全重建。

1. 原因

在日常开发中,
Task.Delay
是一个常用的异步延迟方法。然而,
Task.Delay
的定时并不总是非常准确。例如:

  • 系统负载
    Task.Delay
    的定时精度可能会受到系统负载的影响。如果系统负载较高,CPU 和其他资源被大量占用,任务调度可能会被延迟,从而导致
    Task.Delay
    的实际延迟时间超过预期。

  • 任务调度
    Task.Delay
    是基于任务调度器的,而任务调度器的调度精度可能会受到操作系统的影响。操作系统的任务调度器会根据系统的整体负载和优先级来调度任务,这可能会导致
    Task.Delay
    的实际延迟时间不够精确。

  • 定时器精度
    Task.Delay
    使用的是系统定时器,而系统定时器的精度可能会受到硬件和操作系统的限制。不同的操作系统和硬件平台可能会有不同的定时器精度,这也会影响
    Task.Delay
    的精度。

  • 电源管理
    在某些情况下,电源管理策略(如节能模式)可能会影响任务调度和定时器的精度。例如,在节能模式下,CPU 可能会降低频率或进入休眠状态,从而影响
    Task.Delay
    的精度。

  • GC(垃圾回收
    ) 在 .NET 中,垃圾回收(GC)可能会暂停所有托管线程,从而影响
    Task.Delay
    的精度。如果在
    Task.Delay
    期间发生了垃圾回收,实际的延迟时间可能会超过预期。

以下是一个示例代码,展示了如何使用
Task.Delay

Stopwatch
来测量实际延迟时间,以便更好地理解
Task.Delay
的精度:

using System;
using System.Diagnostics;
using System.Threading.Tasks;

public class Program
{
    public static async Task Main(string[] args)
    {
        int delayMilliseconds = 1000; // 1秒

        Stopwatch stopwatch = Stopwatch.StartNew();
        await Task.Delay(delayMilliseconds);
        stopwatch.Stop();

        Console.WriteLine($"Expected delay: {delayMilliseconds} ms");
        Console.WriteLine($"Actual delay: {stopwatch.ElapsedMilliseconds} ms");
    }
}

在这个示例中,我们使用
Stopwatch
来测量
Task.Delay
的实际延迟时间。你可以运行这个示例代码,观察实际延迟时间与预期延迟时间之间的差异。

2. 更加精准的解决方案对比

为了实现更高精度的定时,我们可以使用
System.Threading.Timer

System.Diagnostics.Stopwatch
。以下是这两种方法的对比示例:

2.1. 使用
System.Threading.Timer

System.Threading.Timer
提供了更高精度的定时控制,可以避免
Task.Delay
的一些不准确性。

using System;
using System.Threading;
using System.Threading.Tasks;

public class TimerExample
{
    public async Task ExecuteWithTimer(int delayMilliseconds)
    {
        var tcs = new TaskCompletionSource<bool>();
        using (var timer = new Timer(_ => tcs.SetResult(true), null, delayMilliseconds, Timeout.Infinite))
        {
            await tcs.Task;
        }
    }
}

2.2. 使用
System.Diagnostics.Stopwatch

Stopwatch
可以用来精确测量时间间隔,并结合
Task.Delay
实现更高精度的定时控制。

using System;
using System.Diagnostics;
using System.Threading.Tasks;

public class StopwatchExample
{
    public async Task ExecuteWithStopwatch(int delayMilliseconds)
    {
        Stopwatch stopwatch = Stopwatch.StartNew();
        while (stopwatch.ElapsedMilliseconds < delayMilliseconds)
        {
            await Task.Delay(1); // 短暂延迟以避免忙等待
        }
    }
}

2.3. 使用示例

public class Program
{
    public static async Task Main(string[] args)
    {
        int delayMilliseconds = 1000; // 1秒

        // 使用 Task.Delay
        Stopwatch stopwatch1 = Stopwatch.StartNew();
        await Task.Delay(delayMilliseconds);
        stopwatch1.Stop();
        Console.WriteLine($"Task.Delay - Expected delay: {delayMilliseconds} ms, Actual delay: {stopwatch1.ElapsedMilliseconds} ms");

        // 使用 System.Threading.Timer
        var timerExample = new TimerExample();
        Stopwatch stopwatch2 = Stopwatch.StartNew();
        await timerExample.ExecuteWithTimer(delayMilliseconds);
        stopwatch2.Stop();
        Console.WriteLine($"Timer - Expected delay: {delayMilliseconds} ms, Actual delay: {stopwatch2.ElapsedMilliseconds} ms");

        // 使用 System.Diagnostics.Stopwatch
        var stopwatchExample = new StopwatchExample();
        Stopwatch stopwatch3 = Stopwatch.StartNew();
        await stopwatchExample.ExecuteWithStopwatch(delayMilliseconds);
        stopwatch3.Stop();
        Console.WriteLine($"Stopwatch - Expected delay: {delayMilliseconds} ms, Actual delay: {stopwatch3.ElapsedMilliseconds} ms");
    }
}

3. 总结

虽然
Task.Delay
在大多数情况下是足够准确的,但它确实可能受到系统负载、任务调度、定时器精度、电源管理和垃圾回收等因素的影响,导致定时不够精确。通过使用
System.Threading.Timer

System.Diagnostics.Stopwatch
,我们可以实现更高精度的定时控制。

前言

本文的内容是来自教程视频的第十五集,个人感觉,这个教程是有点虎头蛇尾,就是前面开始的教程,是非常惊人的好,但到这里,就有点水了,可以说就是把代码一铺,然后简单介绍一遍,很多细节都没有讲,所以,我在学习的时候,也是没办法从视频中直接学习,要先自学了代码后,再看的视频教程。

概念

前面已经解释了神经网络和卷积神经网络,本文要介绍残差网络。在学习之前,我们要先学习几个概念。

迁移学习 Transfer learning

Transfer learning 迁移学习,是旨在利用在一个或多个源任务(source tasks)上已经学习到的知识(例如模型参数、特征表示等),来帮助在目标任务(target task)上进行更高效的学习,使得目标任务能够在较少的标注数据、较短的训练时间等情况下,达到较好的学习效果。

残差网络

残差网络(Residual Network,简称 ResNet)是2015年提出的[深度卷积神经网络(CNN)架构],旨在解决深层神经网络训练中的退化问题。ResNet引入了一个非常重要的概念——残差学习(Residual Learning),使得网络能够训练非常深的网络,同时避免了传统深度网络中梯度消失或爆炸等问题。

ResNet-18

ResNet-18(Residual Network 18)是一个深度卷积神经网络(CNN),属于 ResNet(残差网络) 系列的一种,具有 18 层。它是由微软研究院(Microsoft Research)提出的,主要解决深层神经网络训练中的退化问题,并且在很多计算机视觉任务中表现非常出色。
ResNet-18 是 ResNet 系列中的一个较小的模型,它包含 18 层(包括卷积层、批归一化层、ReLU 激活函数等)。与更深的 ResNet 模型(如 ResNet-34、ResNet-50、ResNet-101 等)相比,ResNet-18 相对较小,适用于计算资源有限或者需要较快速训练的任务。
ResNet-18 的架构大致如下:
输入层:通常输入为大小为 (224, 224, 3) 的 RGB 图像(比如 ImageNet 数据集中的图像),也可以根据任务调整输入尺寸。
卷积层:ResNet-18 包含初始卷积层和多个残差块(Residual Block)。每个残差块中使用了所谓的“跳跃连接”,即输入会绕过某些卷积层,直接与某些层的输出相加,从而帮助缓解训练中的梯度消失和梯度爆炸问题。
残差连接(Skip Connection):通过引入 跳跃连接,每个残差块的输入都会与通过一系列卷积层后的输出相加,这样的设计能有效解决网络深度增加带来的性能退化问题。
全连接层:经过卷积和池化操作后的特征图会展平,并通过全连接层进行分类。

python和tensor基础学习

python字典推导式:

{key_expression: value_expression for item in iterable if condition}
 names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]

# 使用字典推导式将列表转为字典
age_dict = {names[i]: ages[i] for i in range(len(names))}

print(age_dict)

python的文件夹

在python中【../】表示向上一级目录。

ImageFolder

ImageFolder 会自动产生标签,所以可以配合DataLoader,作为入参使用。

#==========================transforms对象===================================================
# 在 ../pytorchTutorial/data/hymenoptera_data 下有两个子文件夹 train 和 val,每个文件夹中包含不同类别的图像文件。
# 目录结构如下:
# hymenoptera_data/
# ├── train/
# │   ├── ants/
# │   └── bees/
# └── val/
#     ├── ants/
#     └── bees/
# 在这种结构下,ImageFolder 会自动为 ants 和 bees 文件夹分配标签(例如,ants 可能对应标签 0,bees 对应标签 1)。
# 每个图像会被转换为一个 PIL (Python Imaging Library)图像对象,经过预处理后转化为 Tensor。

data_dir = '../pytorchTutorial/data/hymenoptera_data'# ../表示向上一级目录
#导入图片
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['train', 'val']}

image
上图中1=0就是这个图片的标签。

DataLoader使用ImageFolder

data_dir = '../pytorchTutorial/data/hymenoptera_data'# ../表示向上一级目录
#导入图片
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['train', 'val']}
#图片导入进dataloader  shuffle=True启用了数据洗牌
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,
                                              shuffle=True, num_workers=0)
               for x in ['train', 'val']}
#取出图片的train和val文件夹图片的数量
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
 

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 标签名
class_names = image_datasets['train'].classes
# 标签值
class_to_idx = image_datasets['train'].class_to_idx  
print('标签值',class_to_idx) # {'ants': 0, 'bees': 1}
print('标签名',class_names) #  ['ants', 'bees']

print('train-dataset_sizes',dataset_sizes['train']) #train-dataset_sizes 244
print('val-dataset_sizes',dataset_sizes['val']) #val-dataset_sizes 153

模型参数

model.state_dict()是参数模型,就是权重值和偏移量,就是前面讲到的y=wx+b的w和b。

print('查看模型的参数')
model = nn.Sequential(
    nn.Linear(2, 3),
    nn.ReLU(),
    nn.Linear(3, 1)
)

# 查看模型的参数
print(model.state_dict())

# 输出将是模型每一层的权重和偏置:
# OrderedDict([('0.weight', tensor([[ 0.3356,  0.5206], [-0.4083,  0.3680], [-0.4225, -0.5568]])), 
#             ('0.bias', tensor([-0.5880, -0.6807, -0.4809])), 
#             ('2.weight', tensor([[-0.2668,  0.5349,  0.2408]])), 
#             ('2.bias', tensor([-0.3686]))])

调整张量的列顺序

print('使用tensor调整表格顺序')
#========使用tensor调整表格顺序
# 假设 images 是一个形状为 (B, C, H, W) 的图像 Tensor
# B 是批量大小(即图像的数量)。
# C 是图像的通道数(例如,RGB 图像的通道数是 3)。
# H 和 W 分别是图像的高度和宽度。
# 创建一个假设的图像数据:6 张 3 通道(RGB),28x28 大小的图像
images = torch.randn(6, 3, 28, 28)

# 将图像们排列成网格,grid_img 就是将 6 张图片拼接后的网格图像
# torchvision.utils.make_grid() 函数生成的网格图像,它的形状是 (C, H, W)
grid_img = torchvision.utils.make_grid(images)
print("grid_img.shape",grid_img.shape) # grid_img.shape torch.Size([3, 32, 182])

# 显示这个网格图像
# PyTorch 张量默认的图像格式是 (C, H, W),也就是说:通道、图像高度和宽度。
# 这里传0 2 1是在调整grid_img的3列的顺序,让(C, H, W)调整为(H, W, C),这符合 Matplotlib 显示图像时的标准要求
plt.imshow(grid_img.permute(1, 2, 0))  # 由于 grid_img 的形状是 (C, H, W),需要将其转为 (H, W, C)
plt.show()


print('使用numpy调整表格顺序')
#========使用numpy调整表格顺序
def imshow(img):
    img = img / 2 + 0.5  # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0))) # 调整tensor列顺序
    plt.show()

imshow(torchvision.utils.make_grid(images))

torch.max使用

print('torch.max使用')
#======== torch.max

# 假设模型输出 (3个样本, 4个类别)
outputs = torch.tensor([[1.2, 2.5, 0.3, 4.1],
                        [3.1, 0.2, 1.8, 2.5],
                        [0.5, 0.1, 2.2, 1.3]])

# 找到每行的最大值及其索引
_, predicted = torch.max(outputs, 1)

print("Predicted class indices:", predicted)
# Predicted class indices: tensor([3, 0, 2])

损失函数直接使用

print('损失函数直接使用')
loss = nn.CrossEntropyLoss()
Y= torch.tensor([0]) #这y是一行一列矩阵,但值0表示类别,如0=猫,1=狗,2=兔子
#nsamples x nclasses=1x3  1行3列
Y_pred_good = torch.tensor([[2.0,1.0, 0.1]]) # 这个预测的y里,2最大,2的索引是0.所以,这个预测的y最可能是猫 
l1 = loss(Y_pred_good, Y) 
print('输出损失精度的值',l1.item()) # 输出损失精度的值

torch.sum使用

print('torch.sum使用')
preds = torch.tensor([0, 1, 2, 1])  # 模型的预测
labels = torch.tensor([0, 1, 1, 1])  # 实际标签
preds == labels.data  # 返回: tensor([True, True, False, True]) 
running_corrects = torch.sum(preds == labels.data)  # 返回: 3(表示有 3 个预测正确)
print('计算当前批次的正确预测数量',running_corrects)

残差模型训练和使用

残差模型训练和使用的代码如下。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
import copy
print('===========启动==========')
mean = np.array([0.5, 0.5, 0.5])
std = np.array([0.25, 0.25, 0.25])

data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize(mean, std)
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(mean, std)
    ]),
}

data_dir = 'data/hymenoptera_data'
#导入图片
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['train', 'val']}
#图片导入进dataloader
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,
                                             shuffle=True, num_workers=0)
              for x in ['train', 'val']}
#取出图片的train和val文件夹下图片的数量
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}


class_names = image_datasets['train'].classes

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('标签名',class_names) #class_names ['ants', 'bees']
print('train-dataset_sizes',dataset_sizes['train']) #train-dataset_sizes 244
print('val-dataset_sizes',dataset_sizes['val']) #val-dataset_sizes 153
def imshow(inp, title):
    """Imshow for Tensor."""
    inp = inp.numpy().transpose((1, 2, 0))
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    plt.imshow(inp)
    plt.title(title)
    plt.show()


# Get a batch of training data
#next等价于 dataiter = iter(dataloaders['train']) inputs, classes =dataiter.__next__()
inputs, classes = next(iter(dataloaders['train'])) 
print("inputs.shape",inputs.shape)
# Make a grid from batch
out = torchvision.utils.make_grid(inputs)

imshow(out, title=[class_names[x] for x in classes])

def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
    since = time.time()
    # model.state_dict()返回的是 模型的参数(权重和偏置) 的集合
    best_model_wts = copy.deepcopy(model.state_dict())
    best_acc = 0.0

    for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)

        # Each epoch has a training and validation phase
        for phase in ['train', 'val']:
            if phase == 'train':
                model.train()  # Set model to training mode resnet18的模型开启【训练】模式
            else:
                model.eval()   # Set model to evaluate mode resnet18的模型开启【评估】模式

            running_loss = 0.0
            running_corrects = 0

            # Iterate over data.
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                labels = labels.to(device)
                print("inputs.shape",inputs.shape) # inputs.shape torch.Size([4, 3, 224, 224])
             
                # track history if only in train 
                # set_grad_enabled是个类,不是函数,构造函数入参是ture,所以使用phase == 'train'传参
                # 用途是开启梯度计算,是通过改变全局的tensor的requires_grad属性来实现的
                with torch.set_grad_enabled(phase == 'train'):
                    # forward——前向传播
                    outputs = model(inputs)
                    print("outputs.shape",outputs.shape)
                    # 设置model的最后的全连接层是model.fc = nn.Linear(num_ftrs, 2),outputs输出的2列
                    # outputs输出的2列是 logits(原始得分),它是指经过神经网络最后一层计算后得到的值,但这些值并没有经过任何激活函数(如 Softmax 或 Sigmoid)的归一化处理。 
                    # torch.max(outputs, 1) 会在 outputs 的每一行中找到最大值的索引。然后组层新的张量 
                    _, preds = torch.max(outputs, 1)
                    print("predictions.shape", preds.shape) #predictions.shape torch.Size([4]),因为1批是4个,所以这个是个4行1列的数组,没一行代表logits(原始得分)最高的索引,即他是哪个类别
                    
                    loss = criterion(outputs, labels)

                    # backward + optimize only if in training phase
                    if phase == 'train':
                        optimizer.zero_grad()
                        loss.backward()
                        optimizer.step()
                
                # loss.item()=损失值 
                # inputs.size(0)返回inputs 的第一个维度的大小,这里inputs.size(0)=4,因为dataloaders定义的时候batch=4,所以使用next取值的时候,一组(一批)就是4个(图片)
                # running_loss=批次的总损失
                running_loss += loss.item() * inputs.size(0)
                print('计算批次的总损失',running_loss)
                # 计算当前批次的正确预测数量
                running_corrects += torch.sum(preds == labels.data)
                print('计算当前批次的正确预测数量',running_corrects)
            if phase == 'train':
                scheduler.step()
                
            # epoch_loss=训练模型中的平均损失,epoch_loss反映了模型在整个训练或验证阶段的学习效果。通常,训练过程中的损失应该逐渐减小,表明模型在逐渐学习并减少错误。
            # dataset_sizes[phase]=样本总数(train的dataset_sizes=244,val的dataset_sizes=153)
            epoch_loss = running_loss / dataset_sizes[phase]  # 平均损失
            # 准确率越高,表示模型在分类任务中的表现越好
            epoch_acc = running_corrects.double() / dataset_sizes[phase] # 平均准确率

            print('{} Loss: {:.4f} Acc: {:.4f}'.format( phase, epoch_loss, epoch_acc))

            # 当验证准确率超过历史最优时,更新权重和偏置
            if phase == 'val' and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy(model.state_dict())

        print()

    time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(
        time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:4f}'.format(best_acc))

    # load best model weights
    # 重新导入权重和偏置
    model.load_state_dict(best_model_wts)
    return model

# ==============================================================================================================================
# 完整地加载了预训练的 ResNet-18,并且 所有的参数都是可训练的。
#### Finetuning the convnet ####
# Load a pretrained model and reset final fully connected layer.

# 创建一个预训ResNet-18 模型  ImageNet 数据集 上预训练过的 ResNet-18 模型 
# ResNet-18 在 ImageNet 数据集上进行了训练,ImageNet 是一个包含 1000 个类别、约 120 万张图像的大型视觉分类数据集。(Microsoft Research 的研究团队训练的)
model = models.resnet18(pretrained=True)
#fc 是最后一个全连接层(Fully Connected Layer)。
#对于 ResNet-18 来说,fc 层的输入特征数是 512,这是因为在 ResNet-18 中,最后一个卷积层的输出经过全局平均池化(Global Average Pooling)后,输出了一个大小为 512 的特征向量。因此,in_features 是 512。

num_ftrs = model.fc.in_features 
# Here the size of each output sample is set to 2.
# Alternatively, it can be generalized to nn.Linear(num_ftrs, len(class_names)).
model.fc = nn.Linear(num_ftrs, 2)
# *** 上面几行是把resnet-18里最后一个全连接层重新定义一下,因为原来的输出列数,不是我们需要的,我们就需要蜜蜂和蚂蚁俩类别,所以,我们输出是2,输入我们不知道,所以要先取出来最后一个全连接层,然后读取他的输入列。

model = model.to(device) #移动到设备上计算,有gpu用gpu计算,没有用cpu计算

criterion = nn.CrossEntropyLoss() #交叉熵损失函数

# Observe that all parameters are being optimized
optimizer = optim.SGD(model.parameters(), lr=0.001)

# StepLR Decays the learning rate of each parameter group by gamma every step_size epochs
# Decay LR by a factor of 0.1 every 7 epochs
# Learning rate scheduling should be applied after optimizer’s update
# e.g., you should write your code this way:
# for epoch in range(100):
#     train(...)
#     validate(...)
#     scheduler.step()


# lr_scheduler.StepLR:是 PyTorch 中的一种学习率调度器,它可以在训练过程中动态调整学习率。
# optimizer:传入需要调节学习率的优化器。
# step_size=7:每训练 7 个 epoch 后,学习率将被调整。
# gamma=0.1:每次调整时,学习率将乘以 0.1,即每 7 个 epoch 后,学习率减少 10%。
# 就是在优化七次后,在将学习率乘以0.1
step_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)

# train_model是个函数,内部会读取图片训练

model = train_model(model, criterion, optimizer, step_lr_scheduler, num_epochs=25)

# ==============================================================================================================================
# 冻结卷积层通常是因为你希望利用 预训练的卷积特征(已经从大数据集上训练过),而只希望通过训练 全连接层 来适应你的特定任务。
#### ConvNet as fixed feature extractor ####
# Here, we need to freeze all the network except the final layer.
# We need to set requires_grad == False to freeze the parameters so that the gradients are not computed in backward()
model_conv = torchvision.models.resnet18(pretrained=True)
for param in model_conv.parameters():
    param.requires_grad = False

# Parameters of newly constructed modules have requires_grad=True by default
num_ftrs = model_conv.fc.in_features
model_conv.fc = nn.Linear(num_ftrs, 2)

model_conv = model_conv.to(device)

criterion = nn.CrossEntropyLoss()

# Observe that only parameters of final layer are being optimized as
# opposed to before.
optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.001, momentum=0.9)

# Decay LR by a factor of 0.1 every 7 epochs
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)

model_conv = train_model(model_conv, criterion, optimizer_conv,
                         exp_lr_scheduler, num_epochs=25)

这段代码还是比较难理解,不过如果理解了,那就基本上证明,前面的神经网络和卷积神经网络你都掌握了。


这样我们迁移学习——残差网络就学完了。


传送门:
零基础学习人工智能—Python—Pytorch学习—全集


注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!



若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18572640