LLM大模型: RAG两大核心利器 — embedding和reranker模型微调fine-tune

前言

要想RAG好,embedding和reranker必须给力!目前市面上流行的embedding和reranker使用的都是通用预料训练,并未针对安全这个细分领域定制,所以有必要使用安全领域的预料微调一下!目前所有的预料中,获取成本比较低、并且不需要专门投入人力标注的有两种:

  • 网上各种安全论坛的博客、各大热门产品的漏洞说明等
  • 用户的点赞反馈数据(chatGPT、copilot等都有该功能)

对于本人而言,用户的点赞反馈数据更容易获取,所以这里使用这类数据,借鉴RLHF-DPO的思路对embedding和reranker模型做微调!训练样本的数据格式如下:

{
  "query": "如何使用IDA Pro反汇编一个二进制文件?",
  "positive": \[
    "使用IDA Pro反汇编一个二进制文件的方法如下:\\n1. 打开IDA Pro并选择“新建”。\\n2. 选择适当的文件格式加载你的二进制文件。\\n3. IDA Pro会自动分析二进制文件并提供反汇编视图。\\n4. 你可以浏览反汇编的代码,以了解二进制文件的功能。\\n5. 使用IDA Pro的交互功能重命名函数、添加注释,以便更容易分析。"
  \],
  "negative": \[
    "使用IDA Pro进行文件反汇编的方法:\\n1. 打开IDA Pro并选择“新建项目”。\\n2. 加载任何类型的文件,IDA Pro会自动将其转换为源代码。\\n3. 你可以直接运行反汇编代码,并通过调试器查看执行结果。\\n4. 如果文件有加密,可以在IDA Pro中直接解密。\\n5. 最后,生成一个全新的二进制文件。",
    "使用IDA Pro进行简单的文件修改:\\n1. 打开IDA Pro并载入文件。\\n2. 选择修改的部分并进行编辑。\\n3. 保存修改后的文件。\\n4. 测试修改后的文件是否工作正常。\\n5. 完成所有修改后,生成新的文件。"
  \]
}

query是真实的用户咨询,LLM会提供两个答案,用户点赞选择的答案标记为positive,没有被选中的标记为negative!

1、先看embedding。 训练样本的格式是[query、pos、neg],微调的终极目的是让LLM的回答和query匹配,基于这个思路,设计出了Contrastive Learning,也叫Triplet Loss:先把三段文本求embedding,然后让query+pos的相似度最大,query+neg的相似度最小,loss的设计如下:

q、p、n分别是三段text的embedding,d 是距离度量(例如欧氏距离或余弦相似度),α 是一个超参数,称为边际(margin)。这个loss函数意义直观,容易理解!具体怎么落地实现了?既然要计算相似度,那就干脆先把query和pos、neg的相似度事先全部先算好,放在矩阵里,便于后续取用。矩阵的每列都是用户每次反馈的数据。矩阵的第一列是query和pos的相似度,其他列是query和neg的相似度,如下:

sim\_matrix = \[\[sim(q1, p1), sim(q1, n11), sim(q, n12), ...\]  
         \[sim(q2, p2), sim(q2, n21), sim(q, n22), ...\]\]

因为第一列是query和pos的相似度,那么第一列的数值应该尽量大,其他列的数值应该尽量小,这不正好可以使用crossEntropy么?labels向量 = [1,0,0,0…],经过crossEntropy相乘后,loss只剩query—pos的相似度啦!具体落地实现的方式稍微有些变通:

(1)以M3E微调为例,微调实现的代码在这里:https://github.com/wangyuxinwhy/uniem/blob/main/uniem/criteria.py#L62,核心的loss方法如下:

class TripletInBatchNegSoftmaxContrastLoss(ContrastLoss):
    def \_\_init\_\_(self, temperature: float = 0.05, add\_swap\_loss: bool = False):
        super().\_\_init\_\_(temperature)
        self.add\_swap\_loss \= add\_swap\_loss
        if self.add\_swap\_loss:
            self.\_pair\_contrast\_softmax\_loss \= PairInBatchNegSoftmaxContrastLoss(temperature)
        else:
            self.\_pair\_contrast\_softmax\_loss \= None

    def forward(
        self,
        text\_embeddings: torch.Tensor,
        text\_pos\_embeddings: torch.Tensor,
        text\_neg\_embeddings: torch.Tensor,
    ) \-> torch.Tensor:
        # 计算正样本相似度向量
        sim\_pos\_vector = torch.cosine\_similarity(text\_embeddings, text\_pos\_embeddings, dim=-1)
        # 计算负样本相似度矩阵
        sim\_neg\_matrix = torch.cosine\_similarity(
            text\_embeddings.unsqueeze(1),
            text\_neg\_embeddings.unsqueeze(0),
            dim\=-1,
        )
        # 将正样本相似度和负样本相似度拼接成一个矩阵
        sim\_matrix = torch.cat(\[sim\_pos\_vector.unsqueeze(1), sim\_neg\_matrix\], dim=1)
        # 温度缩放
        sim\_matrix = sim\_matrix / self.temperature
        # 生成标签,目的是让loss选择第一列的数值
        labels = torch.zeros(sim\_matrix.size(0), dtype=torch.long, device=sim\_matrix.device)
        # 计算交叉熵损失
        loss = torch.nn.CrossEntropyLoss()(sim\_matrix, labels)
        # 如果有附加交换损失,则加上
        if self.\_pair\_contrast\_softmax\_loss:
            loss += self.\_pair\_contrast\_softmax\_loss(text\_pos\_embeddings, text\_embeddings)
        return loss

uniem封装后,使用也很简单,几行代码就搞定了:

from datasets import load\_dataset

from uniem.finetuner import FineTuner

dataset \= load\_dataset('/data/security\_zh', 'STS-B')
# 指定训练的模型为 m3e-small
finetuner = FineTuner.from\_pretrained('moka-ai/m3e-large', dataset=dataset)
finetuner.run(epochs\=1)
  M3E微调后的效果好不好,测评的方式有多种:
  • 模型本身的指标:https://github.com/wangyuxinwhy/uniem/tree/main/mteb-zh 用文本分类、聚类、retrieve、rerank等方式
  • RAG的指标:https://www.cnblogs.com/theseventhson/p/18261594 context recall、context Precision
  • 用户实际使用评价,核心还是triplet的点赞数据是不是够多

(2)同理,beg的baai_general_embedding微调的方法详见:https://github.com/FlagOpen/FlagEmbedding/blob/master/examples/finetune/README.md ;数据集格式如下,都是一样的:

{"query": str, "pos": List\[str\], "neg":List\[str\]}

重写getitem函数,

 def \_\_getitem\_\_(self, item) -> Tuple\[str, List\[str\]\]:
        query \= self.dataset\[item\]\['query'\]
        if self.args.query\_instruction\_for\_retrieval is not None:
            query \= self.args.query\_instruction\_for\_retrieval + query

        passages \= \[\]

        assert isinstance(self.dataset\[item\]\['pos'\], list)
        pos \= random.choice(self.dataset\[item\]\['pos'\])
        passages.append(pos)

        if len(self.dataset\[item\]\['neg'\]) < self.args.train\_group\_size - 1:
            num \= math.ceil((self.args.train\_group\_size - 1) / len(self.dataset\[item\]\['neg'\]))
            negs \= random.sample(self.dataset\[item\]\['neg'\] \* num, self.args.train\_group\_size - 1)
        else:
            negs \= random.sample(self.dataset\[item\]\['neg'\], self.args.train\_group\_size - 1)
        passages.extend(negs)

        if self.args.passage\_instruction\_for\_retrieval is not None:
            passages \= \[self.args.passage\_instruction\_for\_retrieval+p for p in passages\]
        return query, passages

把原本的数据换个格式:

(
    "query:如何使用IDA Pro反汇编一个二进制文件?",
    \[
        "passage:使用IDA Pro反汇编一个二进制文件的方法如下:\\n1. 打开IDA Pro并选择“新建”。\\n2. 选择适当的文件格式加载你的二进制文件。\\n3. IDA Pro会自动分析二进制文件并提供反汇编视图。\\n4. 你可以浏览反汇编的代码,以了解二进制文件的功能。\\n5. 使用IDA Pro的交互功能重命名函数、添加注释,以便更容易分析。",
        "passage:使用IDA Pro进行文件反汇编的方法:\\n1. 打开IDA Pro并选择“新建项目”。\\n2. 加载任何类型的文件,IDA Pro会自动将其转换为源代码。\\n3. 你可以直接运行反汇编代码,并通过调试器查看执行结果。\\n4. 如果文件有加密,可以在IDA Pro中直接解密。\\n5. 最后,生成一个全新的二进制文件。",
        "passage:IDA Pro是一款功能强大的反汇编工具,用户可以通过它轻松分析二进制文件。"
    \]
)

微调核心过程:

def encode(self, features):
        if features is None:
            return None
        psg\_out \= self.model(\*\*features, return\_dict=True)
        p\_reps \= self.sentence\_embedding(psg\_out.last\_hidden\_state, features\['attention\_mask'\])
        if self.normlized:
            p\_reps \= torch.nn.functional.normalize(p\_reps, dim=-1)
        return p\_reps.contiguous()

    def compute\_similarity(self, q\_reps, p\_reps):
        if len(p\_reps.size()) == 2:
            return torch.matmul(q\_reps, p\_reps.transpose(0, 1))
        return torch.matmul(q\_reps, p\_reps.transpose(-2, -1))#矩阵相乘,本质还是内积

    def forward(self, query: Dict\[str, Tensor\] = None, passage: Dict\[str, Tensor\] = None, teacher\_score: Tensor = None):
        q\_reps \= self.encode(query)
        p\_reps \= self.encode(passage)

        if self.training:
            if self.negatives\_cross\_device and self.use\_inbatch\_neg:
                q\_reps \= self.\_dist\_gather\_tensor(q\_reps)
                p\_reps \= self.\_dist\_gather\_tensor(p\_reps)

            group\_size \= p\_reps.size(0) // q\_reps.size(0)
            if self.use\_inbatch\_neg:
                scores \= self.compute\_similarity(q\_reps, p\_reps) / self.temperature # B B\*G
                scores = scores.view(q\_reps.size(0), -1)

                target \= torch.arange(scores.size(0), device=scores.device, dtype=torch.long)
                target \= target \* group\_size
                loss \= self.compute\_loss(scores, target)
            else:
                scores \= self.compute\_similarity(q\_reps\[:, None, :,\], p\_reps.view(q\_reps.size(0), group\_size, -1)).squeeze(1) / self.temperature # B G
                scores \= scores.view(q\_reps.size(0), -1)
                target \= torch.zeros(scores.size(0), device=scores.device, dtype=torch.long)
                loss \= self.compute\_loss(scores, target)

        else:
            scores \= self.compute\_similarity(q\_reps, p\_reps)
            loss \= None
        return EncoderOutput(
            loss\=loss,
            scores\=scores,
            q\_reps\=q\_reps,
            p\_reps\=p\_reps,
        )

    def compute\_loss(self, scores, target):
        return self.cross\_entropy(scores, target)

    def \_dist\_gather\_tensor(self, t: Optional\[torch.Tensor\]):
        if t is None:
            return None
        t \= t.contiguous()

        all\_tensors \= \[torch.empty\_like(t) for \_ in range(self.world\_size)\]
        dist.all\_gather(all\_tensors, t)

        all\_tensors\[self.process\_rank\] \= t
        all\_tensors \= torch.cat(all\_tensors, dim=0)

        return all\_tensors

模型用的还是双塔结构 BiEncoderModel,**先用矩阵相乘的形式得到query和passage中每条text的相似度,然后构造target向量,通过crossEntropy选择passage中的pos回答,这个落地实现的核心思路和M3E完全一样啊!**微调的接口已经封装好了,直接调用:

torchrun \\
\> -m FlagEmbedding.baai\_general\_embedding.finetune.run \\
\> --output\_dir /root/huggingface/bge\_finetune \\
\> --model\_name\_or\_path /root/huggingface/bge-large-zh-v1.5 \\
\> --train\_data /root/huggingface/data/user\_feedback \\
\> --learning\_rate 1e-5 \\
\> --num\_train\_epochs 5 \\
\> --dataloader\_drop\_last True \\
\> --normlized True \\
\> --temperature 0.02 \\
\> --query\_max\_len 64 \\
\> --passage\_max\_len 256 \\
\> --train\_group\_size 2 \\
\> --negatives\_cross\_device \\
\> --logging\_steps 10 \\
\> --save\_steps 1000

运行完毕:

微调数据量有限的情况下,epoch越多,loss越小!

epoche=30,loss降至0.999

epoche=60,loss降至0.499;

微调完后测评的脚本也是现成的:https://github.com/FlagOpen/FlagEmbedding/blob/master/FlagEmbedding/baai_general_embedding/finetune/eval_msmarco.py 核心思路是对query做encode,然后查找100个最接近的answer,然后计算Recall和MRR;可以直接执行命令:

python -m FlagEmbedding.baai\_general\_embedding.finetune.eval\_msmarco \\
\--encoder /root/huggingface/bge-large-zh-v1.5 \\
\--fp16 \\
\--add\_instruction \\
\--k 100 \\
\--corpus\_data /root/huggingface/data/sec\_corpus.json \\
\--query\_data /root/huggingface/data/sec\_query.json

corpus_data包含了想要检索的内容:

{"content": "为什么要对数据加读锁了而不是互斥锁了?在互斥机制中,读者和写者都需要独立独占互斥量以独占共享资源;而在读写锁机制下,允许同时有多个读者读访问共享资源,只有写者才需要独占资源。相比互斥机制,读写机制由于允许多个读者同时读访问共享资源,进一步提高了多线程的并发度"}
{"content": "从R4+C的地方取4字节数据存入R0,然后把R0存到栈上;接着把R0+4,这里ida已经识别出了是rwlock读写锁,然后就是调用pthread\_rwlock\_rdlock获取读写锁的读锁!这就很关键了"}
{"content": "从ida的trace记录看,前面所有的指令都没有读取栈上保存的url+http头的数据,所以前面肯定还没来得及生成那4个加密字段;**从这里开始用读写锁,结合上面的分析大胆猜测:接下来要开始生成加密字段了**!"}
{"content": "第三个参数我是用frida hook得到的,换了个环境地址肯定也变了,所以这里直接”抄袭“拿过来用肯定报错,这种反调试的方法实在是秒啊!动态调试暂时卡壳"}
{"content": " 之前通过hook registerNative发现:metasec\_ml中的0x1094c被ms.bd.c.h.a方法注册成了native函数,这是metasec\_ml唯一的native函数,肯定很重要,就从这里下手呗!这个函数有5个参数,分别都是啥了?"}

query_data包含了问题和正确答案,如下:

{"query": "frida是什么?", "positive": \["Frida是一款基于python + javascript 的hook框架,适用于android/ios/linux/win/osx等平台。Frida的动态代码执行功能,主要是在它的核心引擎Gum中用C语言来实现的", "只要兼容V8引擎就能正常使用frida"\]}
{"query": "怎么使用IDA?", "positive": \["1、安装IDA   2、用IDA打开二进制文件,可以使用F5将汇编反编译成C语言伪代码   3、可以直接调试伪代码了解二进制代码逻辑"\]}
{"query": "怎么脱壳?", "positive": \["对于一代、二代壳,可以直接使用frida dexdump从内存把正常的dex代码dump到磁盘"\]}

从测评的原理来看,和https://www.cnblogs.com/theseventhson/p/18261594 这里面对整个RAG评测是一样的,所以直接采取RAG的评测方法!

2、reranker微调,这里以beg的reranker为例:https://github.com/FlagOpen/FlagEmbedding/blob/master/examples/reranker/README.md ;训练样本的格式和embedding是一样的,但是也要先对训练样本的格式做转换:

def \_\_getitem\_\_(self, item) -> List\[BatchEncoding\]:
    # 获取当前数据项的 query 和正样本
    query = self.dataset\[item\]\['query'\]
    pos \= random.choice(self.dataset\[item\]\['pos'\])

    # 如果负样本数量不足,则重复采样
    if len(self.dataset\[item\]\['neg'\]) < self.args.train\_group\_size - 1:
        num \= math.ceil((self.args.train\_group\_size - 1) / len(self.dataset\[item\]\['neg'\]))
        negs \= random.sample(self.dataset\[item\]\['neg'\] \* num, self.args.train\_group\_size - 1)
    else:
        # 随机选择 train\_group\_size - 1 个负样本
        negs = random.sample(self.dataset\[item\]\['neg'\], self.args.train\_group\_size - 1)

    # 初始化批次数据列表
    batch\_data = \[\]
    
    # 添加正样本
    batch\_data.append(self.create\_one\_example(query, pos))
    
    # 添加负样本
    for neg in negs:
        batch\_data.append(self.create\_one\_example(query, neg))

    return batch\_data  # 返回正负样本组合的批次数据

batch_data前面是pos样本,后面接着neg样本,每个batch_data的格式如下:

batch\_data = \[
    BatchEncoding({
        'input\_ids': \[101, ...\],       # pos 编码后的 token ID
        'attention\_mask': \[1, 1, ...\]  # 注意力掩码
    }),
    BatchEncoding({
        'input\_ids': \[101, ...\],       # neg 编码后的 token ID
        'attention\_mask': \[1, 1, ...\]  # 注意力掩码
    }),
    BatchEncoding({
        'input\_ids': \[101, ...\],       # neg 编码后的 token ID
        'attention\_mask': \[1, 1, ...\]  # 注意力掩码
    })
    .......
\]

底层本质还是个分类模型,使用的是SequenceClassifierOutput

class CrossEncoder(nn.Module):
    def \_\_init\_\_(self, hf\_model: PreTrainedModel, model\_args: ModelArguments, data\_args: DataArguments,
                 train\_args: TrainingArguments):
        super().\_\_init\_\_()
        self.hf\_model \= hf\_model
        self.model\_args \= model\_args
        self.train\_args \= train\_args
        self.data\_args \= data\_args

        self.config \= self.hf\_model.config
        self.cross\_entropy \= nn.CrossEntropyLoss(reduction='mean')

        self.register\_buffer(
            'target\_label',
            torch.zeros(self.train\_args.per\_device\_train\_batch\_size, dtype\=torch.long)
        )

    def gradient\_checkpointing\_enable(self, \*\*kwargs):
        self.hf\_model.gradient\_checkpointing\_enable(\*\*kwargs)

    def forward(self, batch):
        #选择分类模型
        ranker\_out: SequenceClassifierOutput = self.hf\_model(\*\*batch, return\_dict=True)
        logits \= ranker\_out.logits

        if self.training:
            scores \= logits.view(
                self.train\_args.per\_device\_train\_batch\_size,
                self.data\_args.train\_group\_size
            )
            #通过target\_label选择pos列用于计算loss的分母
            loss = self.cross\_entropy(scores, self.target\_label)

            return SequenceClassifierOutput(
                loss\=loss,#输入loss反向传播更新参数
                \*\*ranker\_out,
            )
        else:
            return ranker\_out

    @classmethod
    def from\_pretrained(
            cls, model\_args: ModelArguments, data\_args: DataArguments, train\_args: TrainingArguments,
            \*args, \*\*kwargs
    ):
        hf\_model \= AutoModelForSequenceClassification.from\_pretrained(\*args, \*\*kwargs)
        reranker \= cls(hf\_model, model\_args, data\_args, train\_args)
        return reranker

    def save\_pretrained(self, output\_dir: str):
        state\_dict \= self.hf\_model.state\_dict()
        state\_dict \= type(state\_dict)(
            {k: v.clone().cpu()
             for k,
             v in state\_dict.items()})
        self.hf\_model.save\_pretrained(output\_dir, state\_dict\=state\_dict)

最后的最后

感谢你们的阅读和喜欢,作为一位在一线互联网行业奋斗多年的老兵,我深知在这个瞬息万变的技术领域中,持续学习和进步的重要性。

为了帮助更多热爱技术、渴望成长的朋友,我特别整理了一份涵盖大模型领域的宝贵资料集。

这些资料不仅是我多年积累的心血结晶,也是我在行业一线实战经验的总结。

这些学习资料不仅深入浅出,而且非常实用,让大家系统而高效地掌握AI大模型的各个知识点。如果你愿意花时间沉下心来学习,相信它们一定能为你提供实质性的帮助。

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费

大模型知识脑图

为了成为更好的 AI大模型 开发者,这里为大家提供了总的路线图。它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

经典书籍阅读

阅读AI大模型经典书籍可以帮助读者提高技术水平,开拓视野,掌握核心技术,提高解决问题的能力,同时也可以借鉴他人的经验。对于想要深入学习AI大模型开发的读者来说,阅读经典书籍是非常有必要的。

在这里插入图片描述

实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

面试资料

我们学习AI大模型必然是想找到高薪的工作,下面这些面试题都是总结当前最新、最热、最高频的面试题,并且每道题都有详细的答案,面试前刷完这套面试题资料,小小offer,不在话下

在这里插入图片描述

640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

在这里插入图片描述

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费

<think>好的,我需要帮助用户在AutoDL平台上使用RAG调整DeepSeek模型。首先,我需要理解用户的问题。RAG是检索增强生成,结合了信息检索语言模型,用于生成更准确的输出。用户想在AutoDL平台上调整DeepSeek模型,可能是指微调或应用RAG到该模型上。 首先,我应该回顾RAG的基本原理。RAG通常包括一个检索器一个生成器。检索器从外部知识库中获取相关文档,生成器则基于检索到的内容输入生成响应。调整DeepSeek模型可能涉及将它与检索系统结合,或者对现有RAG框架进行微调。 接下来,考虑AutoDL平台的特点。AutoDL可能提供预装的环境框架,用户需要配置环境依赖。用户可能需要安装必要的库,比如Hugging Face的Transformers、FAISS用于向量检索,或者LangChain等工具。 然后,步骤可能包括: 1. 准备环境:安装Python、PyTorch、相关库。 2. 准备数据:收集或整理需要的外部知识库,处理成适合检索的格式,如分块、向量化。 3. 配置检索系统:使用FAISS或相似库建立索引。 4. 加载DeepSeek模型:通过Hugging Face的接口加载预训练模型。 5. 集成RAG:将检索器生成器结合,可能需要修改模型的输入,使其能够处理检索到的上下文。 6. 微调模型:如果需要进行端到端的微调,需准备训练数据,定义损失函数,调整参数。 7. 部署应用:在AutoDL上部署调整后的模型,可能涉及API服务或Web应用。 还需要考虑AutoDL的具体操作,比如如何创建实例、上传数据、安装依赖、运行训练脚本等。可能用户需要指导如何编写代码,调用DeepSeek模型,整合检索模块。例如,使用Hugging Face的Pipeline,或者自定义模型类。 另外,引用相关的资料。根据用户提供的引用,DB-GPT支持多种模型,包括DeepSeek,可能可以利用DB-GPT的框架来简化流程。引用[^3]提到DB-GPT是AI原生数据应用开发框架,支持自动化微调,这可能对用户有帮助。用户可能需要利用DB-GPT的RAG功能,或者参考其文档来实现。 可能的挑战包括环境配置问题、数据处理的复杂性、模型与检索系统的有效整合。需要给出具体的代码示例,比如如何加载模型,建立检索器,以及如何将者结合。同时,要注意AutoDL平台的资源限制,如GPU内存,可能需要调整批次大小或模型量化。 最后,生成相关问题,帮助用户深入理解,比如如何优化检索效率、处理数据预处理、评估RAG性能等。</think>以下是在AutoDL平台上使用RAG调整DeepSeek模型的分步指南: --- ### **步骤1:准备AutoDL环境** 1. **创建实例** - 在AutoDL控制台选择GPU实例(建议显存≥24GB),预装PyTorch 2.0+、CUDA 11.7+环境。 - 推荐镜像:`PyTorch 2.0.0 + Python 3.9(ubuntu22.04)` 2. **安装依赖库** ```bash pip install transformers faiss-cpu langchain datasets sentence-transformers # 若需GPU加速检索,安装faiss-gpu替代faiss-cpu ``` --- ### **步骤2:构建RAG流程** #### **2.1 准备知识库** - 将外部文档(如PDF/TXT)分块并向量化: ```python from langchain.text_splitter import RecursiveCharacterTextSplitter from sentence_transformers import SentenceTransformer text_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=50) documents = text_splitter.split_text(your_text_data) encoder = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2") embeddings = encoder.encode(documents) ``` #### **2.2 建立检索索引** - 使用FAISS存储向量: ```python import faiss index = faiss.IndexFlatIP(embeddings.shape[1]) index.add(embeddings) ``` --- ### **步骤3:集成DeepSeek模型** #### **3.1 加载预训练模型** ```python from transformers import AutoTokenizer, AutoModelForCausalLM model_name = "deepseek-ai/deepseek-llm-7b-base" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype="auto") ``` #### **3.2 实现RAG生成逻辑** ```python def rag_generate(query, top_k=3): # 检索相关文本 query_embedding = encoder.encode([query]) scores, indices = index.search(query_embedding, top_k) contexts = [documents[i] for i in indices[0]] # 组合检索内容与问题 input_text = f"问题:{query}\n参考内容:{' '.join(contexts)}\n回答:" # 生成回答 inputs = tokenizer(input_text, return_tensors="pt").to(model.device) outputs = model.generate(**inputs, max_new_tokens=200) return tokenizer.decode(outputs[0], skip_special_tokens=True) ``` --- ### **步骤4:微调优化(可选)** 若需端到端微调RAG系统: 1. 准备QA格式数据集,格式为`{"question": ..., "context": ..., "answer": ...}` 2. 定义联合训练目标(检索损失 + 生成损失)[^2] 3. 使用DB-GPT框架自动化微调流程(引用[^1][^3]): ```bash # 克隆DB-GPT仓库 git clone https://github.com/eosphoros-ai/DB-GPT # 配置deepseek.yaml定义模型路径与数据 python pilot/scripts/rag_finetune.py --config deepseek.yaml ``` --- ### **步骤5:部署API服务** 使用FastAPI暴露接口: ```python from fastapi import FastAPI app = FastAPI() @app.post("/ask") def ask(query: str): return {"answer": rag_generate(query)} ``` 启动服务: ```bash uvicorn app:app --host 0.0.0.0 --port 8000 ``` --- ### **关键注意事项** 1. **显存管理** - 若显存不足,启用模型量化: ```python model = AutoModelForCausalLM.from_pretrained(..., device_map="auto", load_in_4bit=True) ``` 2. **检索优化** - 使用HNSW算法加速FAISS检索: ```python index = faiss.IndexHNSWFlat(embeddings.shape[1], 32) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值