分类: 人工智能

  • 用postgreSQL插件实现自己的RAG知识库

    在医疗AI项目中遇到要LLM基于自己的知识库来输出结果,综合考虑,决定采用PostgreSQL实现自己的RAG库。

    关于技术选型

    主要是觉得AI能力落地应用、或者企业内部时,大部分企业应该做好的是系统的整合,而不是对AI本身的调优。更关注企业特有的数据,更重要的是数据的独特性,增强模型的容错性,而不是过于关注数据的清洗和数据的数量。举个例子,在医疗论文搜索中,对标题、摘要、正文和引用,可以用不同的方式进行查询和使用;或者对特定内容,比如公司介绍、产品介绍、治疗方案介绍,有不同的查询方式等等。

    先说为什么要选这样的技术方案:

    1,能用RAG的,就坚决不用微调。RAG的成本远低于微调,而且保留足够的灵活度,便于后续替换和升级。目前各大LLM模型的上下文窗口越来越长,效果越来越好,价格也越来越低,绝大部分情况下,已经没有必要去自己微调、部署;

    2,项目不大,对postgreSQL又比较熟悉,研究了一下pg的全文搜索和pgvector两个插件,发现够用,而且本来在项目中已经有了一个pg数据库,就直接拿来用,不用额外成本。

    关于全文搜索

    再说说为什么在使用语义搜索的同时,还要考虑全文搜索的支持,我想主要是几个原因:一个是项目实现阶段,可以使用全文搜索对语义搜索的结果进行校验;一个是实际应用种,可以使用全文搜索对语义搜索出来的内容,调整权重,实现一些特殊的需求。

    既然用了全文搜索,那么就有分词的问题,项目里使用的是jieba分词,直接用pg的插件支持就可以,还能够自定义字典,非常好用。可以直接在SQL语句中完成分词:

    SELECT  to_tsvector('jiebacfg', %s);

    要注意的一点是,项目中文本可能会有英文,也会有中文,遇到英文时,要用英文的分词,写成这样

    SELECT  to_tsvector('english', %s);

    举个表结构的字段,article_part_ts就是to_tsvector存储字段,同时建立一个索引:

    CREATE TABLE public.fy_article_part_index (
    	fy_article_part_id serial4 NOT NULL,
    	fy_article_info_id int4 NOT NULL,
    	article_part_text text NOT NULL,
    	article_part_ts tsvector NOT NULL,
    	article_part_em public.vector NOT NULL,
    	CONSTRAINT fy_article_part_index_pk PRIMARY KEY (fy_article_part_id)
    );
    
    CREATE INDEX fy_article_part_ts_index ON public.fy_article_part_index USING rum (article_part_ts);

    搜索时,同样需要对待搜索内容进行分词,然后对分词内容进行组合(与、或都可以,效果差别很大,在不同场景下适用),再对搜索结果相关性排序,取前N个结果就可以,比如:

    SELECT fy_article_info_id, article_part_ts <=> to_tsquery('jiebacfg', %s) AS rank
    
    FROM  fy_article_part_index
    WHERE article_part_ts @@ to_tsquery('jiebacfg', %s)
    ORDER BY rank
    LIMIT 100

    关于语义搜索

    其实前面的创建表的SQL中,已经包含了语义搜索用的字段,也就是article_part_em字段,格式是public.vector。

    这里先说embedding,他是用一个高维向量来表示一个对象的内涵意义,常见的word2vec,或者openai提供的’text-embedding-ada-002’等,都是embedding。项目里,我直接使用了阿里提供的embedding服务接口(不要自己去训练,完全没必要),不过要注意,一般大家用的embedding都是1536个维度的,但是阿里不知道为什么,把最新的模型改成了1024维度,为了保留项目的兼容性,我使用了它上一个版本,也就是1536维度的版本。记得对存储embedding的字段加上索引:

    CREATE INDEX fy_article_part_em_index ON public.fy_article_part_index USING hnsw (article_part_em vector_l2_ops) WITH (m='16', ef_construction='64');

    查询的时候,也先调用接口对待查询内容进行embedding,然后执行SQL:

    SELECT fy_article_info_id, article_part_em <#> %s::vector AS rank
    FROM  fy_article_part_index
    ORDER BY rank
    LIMIT 100

    这里要注意,我们知道两个矢量之间的距离有好几种计算方法,常见的包括:欧式距离、曼哈顿距离、余弦距离、内积等等,在项目应用中,略微有些差别,且计算开销也不一样。我这里用的操作符<#>计算的是内积,表示两个向量的相似度。

    关于文本切片

    无论是从数据存储、搜索还是收API服务的接口限制上来说,我们都要对文本内容进行切片。

    切片可以固定长度切,也可以根据语句来切,切片的长度太短会造成语义搜索效果变差,长度太长,又会造成性能问题,所以要结合项目进行调优。

    我采取的策略是先进行分句,然后把长度限制内的语句,组合成一个切片。

    对于英文内容,项目里选择的分句方法是Python的NLTK库:

    from nltk.tokenize import sent_tokenize
    
    sentences = sent_tokenize(article_en)

    对于中文,没有找到现成的分句方法,就自己实现了一个,考虑了一些特殊情况

    def split_chinese_sentences(text):
        sentences = []
        current_sentence = []
        i = 0
        n = len(text)
        
        # 状态跟踪
        in_quote = False  # 是否在引号中
        quote_chars = {'“': '”', '‘': '’'}  # 对应的引号匹配
        
        while i < n:
            char = text[i]
            
            # 处理引号状态
            if char in quote_chars:
                # 遇到开引号,进入引号状态
                current_sentence.append(char)
                expected_end_quote = quote_chars[char]
                i += 1
                # 寻找对应的闭引号
                while i < n and text[i] != expected_end_quote:
                    current_sentence.append(text[i])
                    i += 1
                if i < n:
                    current_sentence.append(text[i])  # 添加闭引号
                    i += 1
                continue
                    
            # 处理省略号(六连点)
            if i <= n-5 and text[i:i+6] == '......':
                current_sentence.append('......')
                if not in_quote:
                    sentences.append(''.join(current_sentence).strip())
                    current_sentence = []
                i += 6
                continue
                
            # 处理中文省略号(三连顿号)
            if i <= n-2 and text[i] == '⋯' and text[i+1] == '⋯' and text[i+2] == '⋯':
                current_sentence.append('⋯⋯⋯')
                if not in_quote:
                    sentences.append(''.join(current_sentence).strip())
                    current_sentence = []
                i += 3
                continue
                
            # 处理普通结束符
            if char in {'。', '!', '?', '…'}:
                current_sentence.append(char)
                if not in_quote:
                    sentences.append(''.join(current_sentence).strip())
                    current_sentence = []
                i += 1
                continue
                
            # 默认情况:积累字符
            current_sentence.append(char)
            i += 1
    
        # 处理最后未完结的句子
        if current_sentence:
            sentences.append(''.join(current_sentence).strip())
    
        return [s for s in sentences if s]

    权重调优

    接下来,把两种方法搜索出来的内容,通过设定一个最小的rank数值,过滤掉无用的内容,再把两份内容结合起来,调整权重,最终找到最适合的几篇内容。

    在文章切片以后,我选择的是通过切片内容,找到原文全部内容,整体喂给LLM。毕竟现在LLM的接口,入参的token数量大的已经到了1M,完全可以支持几万字,成本可以接受的情况下,为什么不充分利用呢?

    PROMPT工程

    最后一步就是调用LLM接口啦,这里主要是对prompt进行调优,特别是告诉LLM他的角色,用户的历史会话记录,以及刚才搜索出来的相关文档内容,设置合适的温度值,以及对输出内容的要求。这里有两个注意的地方,一个是我发现平台提供的LLM接口,即使使用同一个版本,也要定期检查它的输出内容,他一定几率会变得不可控,要及时调整prompt的写法;另一个是有些接口在设置角色、书写prompt时,英文的效果比中文好。

    希望我的记录,对大家有所帮助。

  • 好多小猫

    其实是测试一下Stable Diffusion XL 支持的style,生成了好多好多,各式各样的小猫,废话不说,直接上图:

    3D Model

    Anime

    Cinematic

    Analog Film

    Comic Book

    Craft Clay

    Digital Art

    Enhance

    Fantasy Art

    Isometric

    Line Art

    Low Poly

    Neonpunk

    Origami

    Photographic

    Pixel Art

    Texture

  • 如何搭建一个基于AI大模型的应用-《Generative AI with Large Language Models》课程摘要

    吴恩达在Coursera上又推出了一个新课程《Generative AI with Large Language Models》,学完以后发现非常不错,有系统性,又有实操性,所以把内容摘要在这里。

    先来个大框架图,看一下搭建一个基于大模型的应用,怎么一步一步来做:

    主要分为下面四个大步骤:确定场景、选择合适的预训练模型、基于应用场景对模型调优、最后模型部署和应用集成

    1,确定场景:

    目前AI应用场景主要包括:内容生成(文本、图片、音视频)、翻译、文本摘要和提取等等

    2,选择合适的模型:

    LLM大体上分成三类:

    Encoder Only Models 又叫 Autoencodind Models,能利用上下文来生成缺失的内容,适合用于文本的情感分类、实体提取和单词分类。这类模型的代表就是BERT

    Encoder Only Models 又叫 Autoregressive Models,它仅根据上文来生成后续内容,适合用于内容生成,这类模型的代表就是OpenAI的GPT、谷歌的BARD和Meta的LLaMA

    Encoder Decoder Models 又叫 Sequence-to-Sequence Modles,它不直接对应上下文的词语,而是使用向量来预测内容,适合语言翻译、内容提取和问答,这类模型的代表包括T5和BART

    关于transformers的更详细介绍,这里就不详细展开了。可以直接参考:Attention Is All You Need

    根据要完成的任务,选择适合的大模型,还要注意模型的参数和精度,参数越多、精度越高,模型一般能力越强,但是成本成本也高,这里就需要平衡。

    3,模型调优:

    接下来是怎么把通用模型做优化,使得在特定场景下能够有更好的表现

    3.1 Prompt engineering:

    第一类方法不改变模型,也不需要训练,仅仅通过prompt里给模型一些提示,来改进模型的输出,所以也叫也叫In-context learning,根据是否在prompt中给输入输出举例,和举例的数量,分为下面几种:

    zero shot:通过prompt本身的一些结构,让模型输出更好的结果,比如:

    one shot:通过在prompt中给个例子,来让模型输出更好的结果,比如:

    few shot:类似的,在prompt中给出多个例子(比如5-6个),来让模型输出更好的结果:

    注意:当模型较小时,prompt工程的效果不会特别好

    3.2 Prompt tuning and fine-tuning

    第二类方法,用专门的数据对预训练好的通用大模型进行调优,一般针对某个任务,专门提供一些样本,来对模型进行训练,针对单个任务进行训练,就是通过标注好的数据集对模型进行进一步调优

    但是这样做会遇到一个问题,就是Catastrophic forgetting,也就是模型会忘记以前的训练结果,针对其他任务,输出结果会变差,有两种方法来避免:

    一种是使用多任务进行调优,叫Multi-task instruction fine-tuning

    就是用多种任务的数据,对模型进行训练,增强模型在不同情况下的表现。

    另一种方法就是Parameter efficient fine-tuning (PEFT),通过冻结大部分参数,只训练小部分参数来进行:

    或者冻结整个模型的参数,然后在原有模型外增加额外参数来训练:

    来对模型进行调优。

    这里常见的算法就是Low-Rank Adaptation of Large Language Models (LoRA),原理如下:

    几种方法的效果对比如下:

    可以看到,当模型数量足够大时,几种方法的效果预期差距很小。

    3.3 Reinforcement learning from human feedback (RLHF)

    大模型的结果,经常会碰到输出的内容毫无意义,或者有攻击性、偏见等等情况,这时候就要再用人工标注的数据,进行一次强化学习。

    首先要准备一批人工标注的数据,一般情况下,这样的数据量可能不够,不适合直接用于训练大模型,那就要用这批数据训练一个奖励模型,然后再用这个奖励模型,来对大模型进行训练:

    这里常用的强化学习算法就叫:Proximal Policy Optimization Algorithms (PPO),这里就不详细展开了。但是,我们会遇到一个问题,模型会过分关注奖励,导致结果不合理,这种情况叫reward hacking。要避免reward hacking,可以计算原始模型和强化学习以后模型输出的结果,把差异特别大的结果,进行额外惩罚。

    同样,我们可以用前面类似PEFT的方法,来替代直接更新所有模型参数,如下所示:

    这里引出一个特别重要的概念,就是AI大模型的监管,如何让AI大模型是对人类有帮助而且无害的,可以参考:Constitutional AI: Harmlessness from AI Feedback

    3.4 模型评价

    针对应用场景,应该有自己的模型评价指标,课程里介绍了几种常用的,包括ROUGE、BLEU等,和常用的Benchmark:SuperGLUEHELM

    4. 模型部署:

    4.1 模型压缩

    符合要求的模型,在正式部署前,可以做模型压缩,一般有三条路线:

    Distillation是用大模型来训练一个相对较小的模型

    Quantization是降低大模型参数的精度,比如把16bit的浮点数降为8bit的整数

    Pruning是去掉大模型中等于0或者非常接近0的参数

    下面是几种对模型进行优化的方法对比:

    4.2 模型与外部数据的连接

    大模型一般基于公开数据、一定时间以前数据进行训练的,为了让大模型能够使用应用自身的数据、最新的数据,我们要用Retrieval augmented generation (RAG)技术和Agent技术。

    RAG是通过检索外部数据、并向量化,然后传给大模型,来让大模型能够使用特定的数据

    Agent就是通过调用API来使用其他应用的数据或者服务:

    课程中举了Program-aided language (PAL) models和

    4.3 基于大模型的应用架构

    最后来看一下,一个基于大模型的应用的整体架构:

    最底层是基础架构,包括训练和模型调优;上面一般是应用自己的特定数据源、模型本身和用户反馈数据;再上面是大模型工具集合,这个往往是开发工程量最大的部分;再上面就是应用自身的服务,比如网站、App、API等等。

  • 为什么通用大模型是一个会深刻改变世界的技术?

    最近在研究大模型的应用,昨天和几个业内大佬聊天,已经越来越让人感觉到通用大模型会深刻改变我们的工作方式。

    近几年技术热潮是一波又一波,从大数据到云计算、从区块链到隐私计算、从VR到元宇宙,往往是热过一波以后,就迅速退潮。分析下来几个原因:大数据和云计算,有能力参与的企业非常有限(数据成本、存储成本、计算成本),区块链和隐私计算的应用场景太局限,XR则从技术成熟度到应用场景都受限。

    但是这一波通用大模型完全不同,几乎所有应用场景都可以用大模型来重新实现一遍,有点类似于智能手机和移动互联网的诞生。比如办公软件,传统功能大多做到了尽头,上了大模型以后,空间一下子变大了很多,从内容生成到智能摘要;比如数据分析,现在可以直接用大模型进行特征分析甚至输出报告、比如搜索,从单个或者多个关键字的搜索,变成了提出问题,直接得到答案,比如AIGC,图片和音视频内容的生产成本将大幅降低……这样的例子太多了。

    大模型在国内,大公司可以通过训练基础模型,提供平台来获利,中小公司可以通过使用模型在各自擅长的领域内大幅提升产品服务能力来获利。大模型的不断使用,会产生更多的数据、更好的模型,是一个能够自身不断进化的过程。这一波技术能带来的红利,至少能持续20年!

    今天用百度新发布的文心一言也生成了关于大模型的一篇内容,如下:

    文心一言4.0版本

    输入:帮我生成一篇博客文章,主要内容是说,大模型的出现,深刻改变了很多行业。大模型能大大提高人类的工作效率,而且很多企业已经开始用大模型来替代人工,很多产品已经开始用大模型来优化功能

    输出:

    标题:大模型的出现:深刻改变行业格局与提升工作效率

    随着人工智能技术的飞速发展,大模型已经成为了当下最热门的话题。它们的出现正在深刻地改变许多行业,为企业带来了前所未有的机遇和挑战。本文将探讨大模型在各行业中的应用,以及它们如何大幅提高人类工作效率,从而推动企业用大模型替代人工,优化产品功能。

    一、大模型在各行业中的应用

    大模型凭借其强大的处理能力和卓越的性能,在各行各业中取得了显著的应用成果。以下是几个典型例子:

    1. 智能客服:大模型能够充分理解用户的提问,为用户提供准确、快速的解答,提高客户满意度。
    2. 广告推荐:通过分析用户的兴趣和行为,大模型可以精确地为用户推荐相关广告,提高广告点击率和转化率。
    3. 舆情监测:大模型可以实时分析网络上的舆论趋势,帮助企业及时发现并应对危机公关。

    二、大模型的优势和不足

    大模型具有以下优势:

    1. 处理能力强:大模型能够处理海量的数据,从中挖掘出有价值的信息。
    2. 性能卓越:在许多任务中,大模型的表现已经超越了人类。

    然而,大模型也存在一些不足:

    1. 训练成本高:大模型的训练需要大量的计算资源和时间,成本较高。
    2. 处理能力有限:尽管大模型处理能力很强,但在某些特定任务中,仍可能无法满足实时性要求。

    三、大模型在各行各业的具体应用案例

    1. 智能家居:通过大模型分析用户的家居使用习惯,实现智能设备自动化控制,提升家居生活的便捷性和舒适度。
    2. 医疗诊断:大模型可以帮助医生分析医学影像数据,提高诊断准确性和效率。
    3. 金融分析:大模型可以应用于风险评估、投资策略等领域,为金融机构提供有力的决策支持。

    四、总结与展望

    大模型的出现无疑为各行各业带来了巨大的变革,它们不仅大幅提高了人类的工作效率,还在很大程度上改变了我们的生活方式。随着技术的不断进步,我们有理由相信,大模型将在未来持续发挥重要作用,为企业和社会带来更多的价值。同时,我们也应关注到大模型带来的挑战,如训练成本、处理能力等方面的限制,以及在使用过程中如何确保安全性和隐私性等问题。通过不断研究和创新,我们将能够克服这些挑战,让大模型更好地服务于人类社会,推动各行业的持续发展。

  • 大模型AI距离人类有多远

    自从chatGPT火爆兴起,到Midjourney图片生成,AI又一次站到了风口浪尖,而且来势汹汹,大有颠覆行业之势。那么到底大模型AI距离人类,差距在哪些地方?

    要说差距,先看大模型AI到底能做到些什么?大模型,无论是chatGPT还是谷歌的Bard,原理都是用足够多的参数(数千亿,上万亿),和足够多的数据(号称人类全部知识)去训练模型。所谓AI模型,就是去模拟人类的输入-输出,参数量越大,模拟得越准确。

    再来看人类认知能力是怎样的。从哲学的“认识论”上来说,人类认识能力,无外乎先天知识和经验知识。先天知识是与生俱来的认知能力,而经验知识是在客观环境中,通过先天知识获得的反馈。

    先天知识对应到GPT就是预训练模型,只是这个预训练模型特别牛逼,是生命出现在地球上以来,大概35亿年的训练过程,加上好几次非常特别的参数变化:大洪水、大火山、陨石撞地球等等。训练的结果就是DNA,有效性息量大概也就3.2G(当然也可能我们对遗传物质的认知不够),而GPT3的预训练模型大小已经有几百G。其存储效率以及对应的能耗差别巨大。

    经验知识,更接近用预训练模型针对特殊场景的训练,人类这个过程就是从出生到发育完成的过程,经历十多年,并终身不断更新。不同的生活背景、教育背景、把每个人都训练成了不一样的模型。

    然而人类作为一个种群,还有跟现在大模型AI不一样的地方,就是种群内大量的模型,进行自然选择。OpenAI再厉害,也就训练出来了一个模型(或者几个),但是人类同时训练着几十亿个模型,历史上曾经出现过上千亿的人类,也就是训练了上千亿的模型,并通过自然选择,传承了最合适的一部分。单体模型和群体模型,有着根本性的区别,也就是社会性。人类作为一个独立个体,失去了社会性,只剩下动物性,其认知能力可能比现在的大模型AI更弱。我觉得这也是目前大模型AI的能力天花板。

    人类社会性有哪些体现?简单说,包括:道德、阶级、自觉、利他、服从、依赖等等,这些确实是目前AI与人类最大的区别。

    所以,AI目前的发展道路,指向的还是工具、更高级的工具,更智能的工具,而非人类的替代。就像工业化进程,并没有让人类被机器替代,反而让人类的生产能力获得空前的提升,物资空前的丰富。(本文配图来自百度文心一言)

    AI真正要变成人类,先解决模型的能效问题,再通过大量的模型之间的合作与竞争,建立社会性,才有可能。