大模型学习之路
持续更新中…

大模型

简单讲就是参数量巨大的神经网络模型

1. 基础知识

1.1NLP基础知识

  • RNN(循环神经网络)

    • 对于语言来说顺序是十分重要的,顺序提供了一定的信息,“我爱你”和“你爱我“,就是两个不同的情况。RNN一个重视序列信息的网络,序列即前后关系。

    • HIDDEN STATE(隐状态):保存信息

    • 缺点:数据输入时间越早,在隐状态中占据的影响就越小。

    • 应用场景:机器翻译|图生文|语音识别|量化交易模型

  • seq2seq

    • 受限于网络结构,RNN只实现N to N(机器翻译任务,一个词翻译一个词)或者1 to N,N to 1,不能解决N to M的问题

    • seq2seq:有Encoder和Decoder的模型,这两部分依然是RNN网络

      Encoder提取原始信息中的意义再由Decoder将意义转换成对应的目标语言,但意义单元能够存储的信息有限,句子太长会导致精度下降,(这个问题可以通过加入attention来解决,但这样做会的计算方式太慢啦,于是就有了transfomer)

  • LSTM(长短时记忆网络)

    • RNN有一定的记忆能力,但它只能保留短期记忆,在各类任务上表现并不好。此时参考人类“有取舍”但记忆机制,出现了LSTM。
    • GATE(门控单元)
      • 遗忘门决定了小盒子要保留多少原有信息
      • 输入门决定了当前网络信息有多少信息要被保留
      • 输出门决定了多大程度的输出小盒子的星系
  • GRU(门控循环单元) LSTM的变体

    • 将输入门与遗忘门合并为更新门
      • 更新门决定丢弃哪些旧信息,更新哪些新信息
      • 重置门决定写入多少上一时刻的网络状态,用来捕捉短期记忆
    • 结构简单,计算高效,效果与LSTM不相上下
  • 经典NLP任务

    • 情感分析(sentiment-analysis):对给定的文本分析其情感极性
    • 文本生成(text-generation):根据给定的文本进行生成命名
    • 实体识别(ner):标记句子中的实体
    • 阅读理解(question-answering):给定上下文与问题,从上下文中抽取答案
    • 掩码填充(fill-mask):填充给定文本中的掩码词
    • 文本摘要(summarization):生成一段长文本的摘要
    • 机器翻译(translation):将文本翻译成另一种语言
    • 特征提取(feature-extraction):生成给定文本的张量表示
    • 对话机器人(conversioinal):根据用户输入文本,产生回应,与用户对话

1.2LLM资源以及效率估算

论文参考:LLMem: Estimating GPU Memory Usage for Fine-Tuning Pre-Trained LLMs

大模型微调和推理所需的内存空间(主要指 GPU 显存)取决于多个因素,包括模型的大小、参数量、数据集大小、训练/推理的批次大小(batch size)、精度类型(如 FP32、FP16、INT8),以及优化策略(如混合精度训练、剪枝、量化等)

  • LLM微调

大模型微调需要大量显存来存储模型的权重、梯度、中间激活值,以及优化器状态等。影响微调内存需求的关键因素包括模型大小、批次大小和优化器选择。

  • VRAM

    • 结论:全量微调在推理的4倍左右,推理基本就在modelweight的4-5倍左右(fp32)
    • 模型参数量:7b=70亿个参数,一个参数在FP32:占4个Byte(字节) FP16:占2个Byte INT8:占1个Byte。一个字节为8bit(位),1kb=1024Byte
    • 微调时一般会选择FP16,此时模型大小为7B*2=14GB
    • 选用LORA微调为例:LoRA参数 = r * 原始权重矩阵尺寸(r=4 通常会选择较小的秩以减少计算)
    • 显存的其他开销:存储激活值、优化器状态等。
  • LLM 推理的核心指标以基础知识

    • TTFT(Time To Firset Token):首个Token延迟,即用户输入到输出第一个token之间的延迟,流式输出时,TTFT是很重要的评价指标,决定用户体验

    • TPOT(Time Per Output Token):每个输出token之间的延迟(不包含首个Token)。决定整个推理过程的时间

    • Latency:延迟。从输入到输出最后一个token的延迟,Latency = (TTFT) + (TPOT) * (the number of tokens to be generated)。

      Latency 可以转换为 Tokens Per Second (TPS):TPS = (the number of tokens to be generated) / Latency。

    • Throughput:吞吐量,即每秒针对所有请求生成的 token 数。以上三个指标都针对单个请求,而吞吐量是针对所有并发请求的。

    • VRAM:推理部分的显存主要用于存储 模型参数和kv缓存

      • kv缓存是输入上下文和所有先前令牌的键/值嵌入。它们被计算一次病存储在内存中,以便在解码阶段重用
      • 显存使用影响最大批量大小和序列长度,这会大幅影响LLM推理吞吐量

    • 推理主要分为两个部分

      • 预填充(prefill):模型处理输入上下文,计算embeding,计算并存储输入序列中的词块儿的keyvalue向量,并生成第一个输入的词块儿
      • 解码(decode):根据上下文(输入和已经生成的token)生成下一个token
  • 根据业务场景计算大模型推理所需的最小GPU显存以及推理时间

    • 估计推理时间就是估计推理每个阶段的总内存和计算工作,并处以GPU的处理速率。

      s:序列长度;b:批量大小;h:隐藏纬度;L:transformer层数;N:模型参数;

      GPU FLOPs速率:对于A100,是31.2 TFLOPS

      GPU HBM速率:对于A00,是1.5TB/秒

1.3大模型的部署与加载

大模型的加载主要分为通过API调用,本地化部署后直接调用

  • 大模型框架

    ​ 大模型框架是指用于训练、推理和部署大型语言模型(LLMs)的软件工具和库。通常提供高效的计算资源管理、分布式训练、模型优化和推理加速等功能。

    • 常见的大模型框架
      • ollama
        • API简介,易于上手|支持硬件加速(GPU,TPU)|高并发性受限|高级功能支持有限
      • vLLM
        • 极高的推理速度|并发速度快|优化内存使用|支持多种分布式计算模型
      • LightLLM
        • 轻量级设计|易于拓展|高速性能
      • Local AI
        • 本地部署
      • fastllm
        • 初c++实现|可以在安卓上直接编译|平台推理速度都很快
      • DeepSpeed-MIL
        • KV缓存|连续批处理|动态SplitFuse|张量并行性|高性能cuda内核
  • 模型平台 HuggingFace|ModelScope

    • API调用

      • openai

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        from openai import OpenAI
        from dotenv import load_dotenv, find_dotenv

        # 加载.env文件获取API-key
        load_dotenv(find_dotenv())

        client = OpenAI()


        completion = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
        {"role": "system", "content": "你是一个心理咨询师"},
        {"role": "user", "content": "你好"}
        ]
        )

        print(completion.choices[0].message)
      • langchain

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        # 使用langchain进行llm的建立
        import os
        from langchain.chat_models import ChatOpenAI
        from dotenv import load_dotenv,find_dotenv

        load_dotenv(find_dotenv()) # 加载环境变量 find_dotenv() # 找到.env文件

        # 输出dotenv获取的环境变量
        # print(f"OPENAI_API_KEY: {os.environ['OPENAI_API_KEY']}")

        chat = ChatOpenAI(openai_api_key = os.environ['OPENAI_API_KEY'],
        model='gpt-3.5-turbo'
        )

        # langchain的格式如下
        from langchain.schema import SystemMessage,HumanMessage,AIMessage

        # message 可以理解成memory
        messages = [
        SystemMessage(content="You are a helpful assistant."),
        HumanMessage(content="What is the capital of France?"),
        AIMessage(content="Paris is the capital of France."),
        HumanMessage(content="What is the capital of Germany?"),
        ]

        res = chat(messages)
        print(res)
  • 部署

    ​ 本地部署是指将软件或应用程序安装在用户本地设备(例如个人计算机、服务器或其他硬件设备)上的过程。与云端部署不同,本地部署将软件的所有组件和资源直接安装在用户的设备上,使其能够在本地运行而无需依赖云服务。

    1、本地部署的基本原理

    本地部署的基本原理是将软件的代码、数据库以及其他必要的资源存储在用户的设备上。用户通过本地设备直接访问和操作软件,不需要依赖外部服务器或云服务。这种方式使得用户能够在本地环境中完全掌控软件的运行和数据存储过程。

    2、本地部署与云端部署的区别

    与云端部署相比,本地部署的最大区别在于数据存储和计算资源的位置。云端部署将数据存储在云服务器上,而本地部署则将数据储存在用户的本地设备上。这一区别对于一些安全性要求较高或对数据隐私有严格要求的应用来说尤为重要。

    • 本地部署

      • transformers

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        from transformers import AutoModelForCausalLM, AutoTokenizer

        model_name = "Qwen/Qwen2.5-7B-Instruct"

        model = AutoModelForCausalLM.from_pretrained(
        model_name,
        torch_dtype="auto",
        device_map="auto"
        )
        tokenizer = AutoTokenizer.from_pretrained(model_name)

        prompt = "Give me a short introduction to large language model."
        messages = [
        {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
        {"role": "user", "content": prompt},
        ]
        text = tokenizer.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True,
        )
        model_inputs = tokenizer([text], return_tensors="pt").to(model.device)

        generated_ids = model.generate(
        **model_inputs,
        max_new_tokens=512,
        )
        generated_ids = [
        output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
        ]

        response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
      • modelscope

        1
        2
        3
        4
        5
        6
        7
        from modelscope import AutoModelForCausalLM, AutoTokenizer, GenerationConfig
        tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-1_8B-Chat", revision='master', trust_remote_code=True)
        model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen-1_8B-Chat", revision='master', device_map="auto", trust_remote_code=True).eval()
        # 第一轮对话 1st dialogue turn
        response, history = model.chat(tokenizer, "你好", history=None)
        print(response)
        # 你好!很高兴为你提供帮助。

        上述部署方式都用到了AutoModelForCausalLM库,使用from_config和from_pretrained构建模型

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        #  加载模型但是不加载权重
        config = AutoConfig.from_pretrained('Qwen/Qwen-1_8B-Chat')#加载
        model = AutoModelForCausalLM.from_config(config)
        # 加载模型同时加载权重
        model = AutoModelForCausalLM.from_pretrained('Qwen/Qwen-1_8B-Chat',revision='master', trust_remote_code=True)
        # from_pretrained的参数解释
        """
        1. **pretrained_model_name_or_path** (str, optional):
        - 指定要加载的预训练模型的名称或路径。这可以是模型的名称(例如,'bert-base-uncased'),也可以是模型的本地路径。

        2. **cache_dir** (str, optional):
        - 指定用于缓存预训练模型配置文件的目录路径。如果设置为 `None`,将使用默认缓存目录。

        3. **force_download** (bool, optional):
        - 如果设置为 `True`,将强制重新下载模型配置,覆盖任何现有的缓存。

        4. **resume_download** (bool, optional):

        - 这是可选参数,如果设置为 True,则在下载过程中重新开始下载,即使部分文件已经存在。

        5. **proxies** (`Dict[str, str]`, *optional*)

        - proxies(可选参数):这是一个字典,用于指定代理服务器的设置。代理服务器允许您在访问互联网资源时通过中继服务器进行请求,这对于在受限网络环境中使用 Transformers 库来加载模型配置信息非常有用。

        proxies = { "http": "http://your_http_proxy_url", "https": "https://your_https_proxy_url" }

        6. **revision** (str, optional):
        - 指定要加载的模型的 Git 版本(通过提交哈希)。

        7. **return_unused_kwargs** (bool, optional, 默认值为 False):

        - 如果将此参数设置为 True,函数将返回未使用的配置参数。这对于识别和检查传递给函数的不受支持或未识别的参数很有用。

        8. trust_remote_code (`bool`, *optional*, defaults to `False`)

        trust_remote_code=True:

        默认情况下,trust_remote_code 设置为 True。这意味着当您使用 from_pretrained() 方法加载模型配置文件时,它将下载来自 Hugging Face 模型中心或其他在线资源的配置文件。这是一个方便的默认行为,因为通常这些配置文件是由官方提供的,且是可信的。
        trust_remote_code=False:

        如果您将 trust_remote_code 设置为 False,则表示您不信任从远程下载的配置文件,希望加载本地的配置文件。这对于安全性或定制性要求较高的场景可能是有用的。

        在这种情况下,您需要提供一个本地文件路径,以明确指定要加载的配置文件

        总之,trust_remote_code 参数允许您在使用 Hugging Face Transformers 库时控制是否信任从远程下载的配置文件。默认情况下,它被设置为 True,以方便加载官方提供的配置文件,但您可以将其设置为 False 并提供本地配置文件的路径,以进行更精细的控制。
        """
    • 模型文件导入方式

      • GGUF(GPT-Generated Unified Format)

        • GGUF (GPT-Generated Unified Format) 是一种文件格式,用于保存经过微调的语言模型。这种格式旨在帮助用户方便地在不同的平台和环境之间共享和导入模型。它支持多种量化格式,可以有效减少模型文件的大小。它的前身是 GGML(GPT-Generated Model Language),是专门为了机器学习而设计的 Tensor 库,目的是为了有一个单文件的格式,并且易在不同架构的 CPU 以及 GPU 上可以推理,但后续由于开发遇到了灵活性不足、相容性及难以维护的问题。
      • Safetensors

        • 是一种用于存储深度学习模型权重的文件格式,它旨在解决安全性、效率和易用性方面的问题。目前这部分功能还有待社区成员开发,目前文档资源有限。如果正在导入的模型是以下架构之一,则可以通过 Modelfile 直接导入 Ollama。当然,你也可以将 safetensors 文件转换为 gguf 文件,再进行处理,转换过程可以参考第三部分。
    • 云端部署

2. 主流大模型

LLM中的三种重要模型:基座模型(base)、聊天模型(chat)和指令模型(instruct)。

  1. 基座模型(Base Model)
    • 基座模型通常是指那些通过大量文本数据训练的基础语言模型。这些模型经过预训练后可以生成连贯的文本,并且能够完成诸如文本填充、问答、翻译等任务。基座模型通常是其他更复杂模型的基础,因为它们可以提供语言理解和生成的基础能力。
    • 基座模型的例子包括BERT、GPT系列等。
  2. 聊天模型(Chat Model)
    • 聊天模型是专门用于对话系统的语言模型,它们能够模拟人类对话,并且通常具备上下文理解能力。这类模型被设计成能够进行连续多轮对话,同时保持对话的一致性和连贯性。
    • 例如,OpenAI的ChatGPT就是一种聊天模型,它可以用来创建聊天机器人,进行自然的人机交互。
  3. 指令模型(Instruct Model)
    • 指令模型是一种特殊类型的语言模型,它被训练来理解和执行指令或任务。与聊天模型相比,指令模型更侧重于完成具体的任务,如编写代码、总结文档、提供建议等。
    • 这种模型通常会接受一个明确的任务描述或者指令作为输入,然后根据这个指令生成相应的输出。例如,InstructGPT就是一种被训练来执行特定指令的模型。

transformer,bert,gpt,v5

gpt

llama

chatglm

qwen

baichuan

3. 大模型优化

优化方法的选择

当一个大模型在一个具体的应用场景中表现没有达到我们的预期的时候我们通常会考虑两种优化策略

FT与PE,这里我们对比主流的两种方法RAG和FT

主要选型的依据是 成本用户体验问题模型幻觉问题

特点对比 RAG(检索增强生成) PT(微调)
知识更新 RAG直接更新检索知识库,保持信息更新,模型无需频繁的重新训练,适合动态数据环境 FT存储静态数据知识与数据,更新需要重新训练
外部知识 RAG擅长利用外部资源,非常适合文档或其他结构化/非结构化数据 虽然FT可以对大模型进行微调以对齐预训练学到的外部知识,但对于频繁更改数据源来说不太实用
数据处理 RAG对数据加工和处理的要求低 FT依赖高质量数据,有限的数据集可能不会产生显著性能的提升
模型风格 RAG主要关注信息检索,擅长整合外部知识,但可能无法完成定制模型的行为或写作风格 FT允许根据特定的语气或术语调整大语言模型的行为、写作风格或特定领域的知识
可解释性 RAG通常可以追溯到特定数据源的答案,从而提供更高等级的可解释性和可溯源性 FT微调就像黑匣子,并不清楚模型为什么会做出这种反应,可解释性较低
计算资源 RAG需要高效的检索策略和大型数据库相关技术。另外还需要保持外部数据源集成以及数据更新 FT需要准备和整理高质量的训练数据集,定义微调目标以及相应的计算资源
延迟和实时要求 RAG需要进行数据检索,可能会有更高延迟 经过FT的大模型无需检索即可响应,延迟较低
减少幻觉 RAG本质上不太容易产生幻觉,因为每个回答都是建立在检索到的证据上 FT可以通过讲模型基于特定领域的训练数据来减少幻觉。但当面临不熟悉的输入时,它仍然可能会产生幻觉
道德与隐私问题 RAG的道德和隐私问题来源于从外部数据库检索的文本 FT的道德和隐私问题则因为模型的训练数据存在敏感内容

3.1 FT(Fine-tuning)

3.1.1 微调模式

  • 全量微调(Full Fine-tuning:在全量微调中,所有模型参数都会被更新。这通常需要大量计算资源和存储空间,因为预训练模型可能包含数亿甚至数千亿个参数。

  • 局部微调(Partial Fine-tuning):局部微调只更新模型的一部分参数,保持其他参数不变。LoRA 通过在部分层添加低秩矩阵来表示小规模的权重调整,从而避免更新所有参数。这样可以极大减少微调时的参数数量,减少存储和计算成本。

  • 增量微调(Incremental Fine-tuning):增量微调通常指在通过新增参数的方式进行微调,新的知识存储在新的参数中。

  • PEFT(Parameter Efficient Fine Tuning) 参数高效微调

    • Prefix Tuning(前缀微调)
      • 概念:前缀微调是一种在冻结的语言模型前面插入可训练“前缀” token 的方法。这些前缀 token 在输入序列之前加入,但模型的其他参数保持不变。通过调整前缀 token 来影响模型的输出,而无需修改原模型的参数。
      • 应用:常用于语言生成任务,如对话、文本生成等。特别适合大型预训练语言模型,因其能够保持原模型的能力,仅调整小部分参数来适应新任务。
    • P-tuning
      • 概念:P-tuning 是一种通过学习一组可训练的嵌入层(embedding)向量来优化模型的输入。在每个位置插入这些可学习的向量,以此引导模型在新任务中的表现。这些嵌入可以看作是提示(prompt)的延伸。
      • 版本:
        • P-tuning v1:直接在输入序列的每个位置插入可学习的嵌入向量。
        • P-tuning v2:改进了 v1,能够应用于更大规模模型并具备更强的表达能力,同时适用于更多下游任务。
      • 应用:尤其适合自然语言理解任务,如分类、问答等。
    • LoRA(Low-Rank Adaptation)
      • 概念:LoRA 是通过引入低秩矩阵来对模型中的特定层(如注意力层)的权重进行调整。它冻结了模型的权重,只对低秩的矩阵进行训练,极大地减少了参数量,同时保持模型的表达能力。
      • 机制:LoRA 的核心思想是在现有模型的某些权重矩阵中引入一个低秩分解部分,仅训练该低秩分解的参数,从而在微调时减少大量的计算和存储需求。
      • 应用:LoRA 常用于大规模语言模型、图像生成等任务中,能在大幅减少微调参数的情况下,保持性能接近于完全微调。

模型微调

为了优化模型在某个垂域上的效果

微调工具 xtunner llama-factory

示例:

(服务器下载 速率太慢 在本地下载,然后上传到服务器的 00:51开始上传文件大小是31GB)

在服务器上使用xtunner工具,使用xxx数据微调InternLM模型

! pip3 install --upgrad pip升级pip

! pip3 install bitsan>=0.39.0

! git clone https://github.com/InternLM/xtuner.git

cd xtuner pip3 install -e '.[deepspeed]'

模型:InternLM2.5-7B-Chat

1
2
3
4
apt-get install git-lfs # Git LFS(Git Large File Storage,大文件存储)是 Git 的一个拓展
git lfs install
git clone https://www.modelscope.cn/Shanghai_AI_Laboratory/internlm2_5-7b-chat.git # 使用git clone
# 成功使用下载文件大小应该是30.95GB

在终端使用du -sh internlm2_5-7b-chat查看文件夹大小

git clone https://github.com/SmartFlowAI/EmoLLM.git 你跟我讲这个是个数据集?

加国内镜像:git clone https://gitclone.com/github.com/SmartFlowAI/EmoLLM.git 但是加了国内镜像需要注意版本问题,有时候镜像可能会更新不及时

打开EmoLLM中的dataset 我们选用multi_turn_dataset_2.json

里面是一些conversation 的QA对

在/xtuner/xtuner/configs/中找到你的模型对应的py文件

3.2 PE(PromptEngineering)

提示词工程

3.2.1 RAG


  • RAG基本步骤

    • 步骤一:构建数据索引(index)

      • 加载不同来源,不同格式文档
      • 将文档分割成块(chunk)
      • 对切块的数据进行进行向量化并存储到向量化数据库
    • 步骤二:检索-增强(Retrieval-Augmented)

      • 通过向量相似度检索与问题最相关的k个文档
      • 检索召回知识附加上下文填充至prompt
    • 步骤三:生成(generation)

      • 检索增强提升输入至LLM生成请求响应

RAG有四种变体,这些变体使得RAG能够适应不同的应用场景和需求,提升其整体性能。

  • NaiveRAG:最基本的形式,直接从检索系统中获取信息,并将其与生成模型结合,通常使用最简单的检索方式
  • AdvancedRAG:引入更复杂的检索机制生成策略,可能包括对检索结果的优化、重排序以及增强生成过程的上下文理解能力
  • ModularRAG:采用模块化设计,将检索和生成过程拆分为独立模块,允许更灵活的组合和优化,使得每个模块可以独立改进
  • GraphRAG:利用图结构表示和检索信息,通过图神经网络等方法增强信息的关联性和上下文理解,特别适合处理复杂性关系和多模态数据

框架选择:langchainllamaindexDify

关于框架的使用:框架在一定程度上简化我们的工作流程,但另一方面会限制我们的开发自由度,在个性化功能时,需要修改的东西太多啦,重开发的成本较高。

为什么我们不再使用 LangChain 来构建我们的 AI 代理

3.2.2 COT

链式思考提示:这个过程需要手工制作有效切多样化的例子,这种手动工作可能会导致次优解决方案

3.2.3 TOT

Tree of Thoughts是一种提示技术,他允许LLm思考问题,并生成一系列步骤,然后根据步骤生成答案

Agent

Agent的能力取决于LLM的意图理解,任务拆分,推理实现和能力调度能力

4.大模型的加速

4.1算法优化

算法优化时大模型加速的重要手段。例如:通过引入注意力机制(Attention Mechanism)和自注意力(self-attention)等结构,可以显著提高模型的表征能力和计算效率。

此外还有一些针对大模型的优化算法,如混合精度训练(Mixed Precision Training),梯度累计(Gradient Accmulation)

4.2硬件加速

硬件加速是另外一种重要的优化手段。随着专用加速器(TPU,GPU)的不断发展,大模型的计算速度得到了显著提升

4.3软件工程

一方面,通过优化代码实现,可以提高模型的运行效率。另一方面,通过并行计算和分布式训练,可以充分利用多核cpu和多GPU多计算资源,进一步提高模型的进行训练速度