大模型基础(六):ChatGLM原理介绍

1 ChatGLM简介

ChatGLM是由中国智谱AI(Zhipu AI)与清华大学知识工程实验室(KEG)联合开发的一系列开源双语(中英文)对话大语言模型。该系列模型基于GLM(General Language Model)架构,针对对话场景进行了针对性改进,使其专注于高效推理和自然对话能力,适用于多种实际场景,如智能客服、内容生成、知识问答等,是国内目前的主流大模型之一。

1.1 核心技术特点

  1. GLM架构
    • 基础框架为自回归填空(Autoregressive Blank Infilling),结合了自编码(BERT)和自回归(GPT)的优势:通过随机遮盖输入文本的片段(称为“空白”),利用双向上下文预测被遮盖内容,兼具理解与生成能力。
    • 支持长文本生成和复杂逻辑推理。

  2. 高效推理
    • 采用量化技术(如INT4低精度量化)和模型压缩,显著降低硬件资源需求(例如ChatGLM-6B可在消费级显卡上运行)。

  3. 对齐优化
    • 通过监督微调(SFT)和人类反馈强化学习(RLHF)对齐人类偏好,生成内容更安全、合理。

1.2 主要应用场景

• 智能对话:多轮对话、情感陪伴、个性化交互。
• 知识服务:百科全书式问答、法律/医疗/科技领域专业咨询。
• 内容创作:文案生成、摘要提取、代码编写。
• 企业服务:客服自动化、文档分析、数据分析。

1.3 版本演进

  1. ChatGLM-6B(2023年3月)
    • 首个开源版本,62亿参数,支持中英双语,可在单张消费级显卡(如RTX 3060)部署。
    • 特点:轻量化、低资源需求。

  2. ChatGLM2-6B(2023年6月)
    • 上下文窗口从2K扩展到8K tokens,推理速度提升42%。
    • 引入FlashAttention优化,支持更高效的并行计算。

  3. ChatGLM3系列(2023年10月)
    • 新增工具调用(Function Call)和代码解释器(Code Interpreter)功能,支持复杂任务执行。
    • 推出ChatGLM3-6B(开源)和更大规模的闭源版本(如32B、130B)。

  4. ChatGLM4(2024年1月)
    • 最新一代模型,支持更长上下文(128K tokens)、多模态输入(图像/语音)和更强的逻辑推理能力(需以官方信息为准)。
    • 增强了智能体(Agent)和Retrieval(检索)功能。
    • 更快推理速度,更多并发

1.4 总结

ChatGLM系列以高效、实用、安全为核心目标,通过GLM架构的创新和持续迭代,在双语对话场景中展现了强大的竞争力。其开源策略也推动了开发者社区的广泛参与,成为中文大模型领域的重要标杆之一。

本文只介绍原理,不会去复现。

2 GLM的自回归填空机制

要搞明白ChatGLM的原理,那么必须先知道GLM(General Language Model)及其自回归填空机制。

2.1 GLM模型的设计思路

在GLM出来之前,NLP领域主流的预训练模型可以分为三类:

  • 自回归模型,代表是GPT,本质上是一个从左到右的语言模型,常用于无条件生成任务(unconditional generation)。
  • 自编码模型,代表是BERT,通过某个降噪目标(如消除序列中的掩码)训练的语言编码器,这种模型擅长自然语言理解任务(natural language understanding tasks),常被用来生成句子的上下文表示(即对文本提取特征)。
  • 编码器-解码器模型, 代表是T5、BART,拥有完整的Transformer结构,包含一个编码器和一个解码器,常用于有条件的生成任务 (conditional generation),例如翻译、文本摘要、生成回答等。

当时这三类模型各有优缺点,没有一种框架能够在所有的自然语言处理任务中都表现出色。清华大学的研究人员试图设计出一种模型,将上述三种模型统一起来,于是诞生了GLM。

三种结构最大的区别是注意力:GPT的注意力是单向的,所以无法利用到下文的信息;BERT的注意力是双向的,可以同时感知上文和下文,因此在自然语言理解任务上表现很好,但是不适合生成任务。T5的编码器中的注意力是双向,解码器中的注意力是单向的,因此可同时应用于自然语言理解任务和生成任务。但T5为了达到和RoBERTa和DeBERTa相似的性能,往往需要更多的参数量。

要让GLM中同时兼容上述三种结构,只需要让GLM同时存在单向注意力和双向注意力即可。在原本的Transformer模型中,这两种注意力机制是通过修改 attention_mask 实现的。当 attention_mask 是全1矩阵的时候,这时注意力是双向的;当 attention_mask 是三角矩阵的时候(如下图),注意力就是单向。

因此,我们可以在只使用Transformer编码器的情况下,自定义 attention_mask 来兼容三种模型结构,你可以认为它是一个带 Mask 机制的 BERT。

2.2 自回归填充任务

假设原始的文本序列为 x1, x2, x3, x4, x5, x6,采样的两个文本片段为 x3 和 x5、x6,那么掩码后的文本序列为 x1, x2, [M], x4, [M](以下简称Part A)。接下来,我们要根据第一个 [M] 解码出 x3,根据第二个 [M] 依次解码出 x5、x6 ,也就是说,我们的任务是从 [M] 处解码出变长的序列(被称为Part B)。

接下来我们用把遮盖掉的 tokens 拼接起来,为了能将这些变长的 token 序列区别开来,可以在每一个被遮挡的序列前用 [S] 标记,因此,Part B 可以整理成:[S], x3, [S], x5, x6。

随后把 Part A 和 Part B 拼接起来,得到:x1, x2, [M], x4, [M], [S], x3, [S], x5, x6。

在GLM中,位置向量有两个,一者用来记录Part A中的相对顺序,二者用来记录被掩的文本片段内部(简称为Part B)中的相对顺序。例如,[S], x5, x6 的 position1 都是5,因为它们对应的 [M] 在Part A中的位置为5,它们的 position 2 则是 1, 2, 3,这是它们在片段内部的顺序。Part A中的词,其position 2都是0,而 Part B 的词,它们则从1开始计数。

此外,还需要通过自定义 attention mask 来达到以下目的:

  • Part A中的词彼此可见(下图(d)中蓝色框中的区域)
  • Part B中的词单向可见(图(d)淡黄色的区域)
  • Part B可见Part A(图(d)中红色框中的区域)
  • 其余不可见(图(d)中灰色的区域)

需要说明的是,Part B包含所有被掩码的文本片段,但是文本片段的相对顺序是随机打乱的,这似乎非常奇怪,但是实验结果表明这样做非常有必要。我个人认为,通过打乱 Part B,可以通过 position 1 和 position 2 控制哪一部分内容先生成,进而提高模型的泛化性。无论怎么排序,都是可以通过 position 1 与 position 2 恢复成原文。
在这里插入图片描述
在图©中的右半边,它其实就是错位预测,比如根据输入的 [S] 和位置编码(position 1、position 2)去预测 x5,然后用 x5 和输入的位置编码,去预测 x6,再用 x6 和输入的位置编码去预测片段的截止符 [E]。两个位置编码的使用,并不会限制生成的文本片段的长度,因为生成的时候,你是一个个词喂进去的,即你预测得到 [E] 之后,你要预测下一个单词的时候,position 2 才会重置为1,同时 position 1 的值需要根据你根据要生成的位置来提供。

从图(d)中可以看到,Part A是在做自注意力机制,模型在这些token上面发挥的是编码器的功能,而Part B则做自注意力+交叉注意力,模型在这些token上面发挥的是解码器的功能,但这两部分其实用的是同一个模型,只是用的 attention mask 不一样。

2.3 GLM的简易实现

我们先来实现一下如何根据 Part A 和 Part B 生成 attention mask:

import torch
def create_mask(part_a_lens, part_b_lens, max_len=512):
    """
    创建自回归填空的注意力掩码
    part_a_lens: Part A 长度,句子的数量是 batch_size
    part_b_lens: Part B 长度
    max_len: 输入的最大句子长度
    """
    batch_size = len(part_a_lens)
    mask = torch.zeros(batch_size, max_len, max_len, dtype=torch.bool)
    
    for batch_i in range(batch_size):
        part_a_len = part_a_lens[batch_i]
        part_b_len = part_b_lens[batch_i]

        # 允许所有位置关注 Part A 部分
        mask[batch_i, :, :part_a_len] = True

        # 生成一个与 Part B 相同的下三角矩阵
        ones = torch.ones(part_b_len, part_b_len)
        part_b_tril = torch.tril(ones, diagonal=0).type(torch.bool)

        # 将 Part B 部分换成下三角矩阵
        start = part_a_len
        end = part_a_len + part_b_len
        mask[batch_i, start: end, start: end] = part_b_tril

        # 允许填充部分关注所有位置(不包括自己)
        mask[batch_i, end:, :] = True
        mask[batch_i, :, end:] = False

    return mask

if __name__ == "__main__":
    part_a_lens = [6, 5]
    part_b_lens = [5, 5]
    mask = create_mask(part_a_lens, part_b_lens, max_len=12)
    print(mask.type(torch.int8))

输出:

tensor([[[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]],

        [[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]]], dtype=torch.int8)

剩下是模型

import torch
import torch.nn as nn

class GLMPositionalEncoding(nn.Module):
    def __init__(self, d_model=512, max_len=512, d_pos1=64, d_pos2=64):
        super().__init__()
        # position1 位置编码
        self.pos1_embed = nn.Embedding(max_len, d_pos1)
        # position2 位置编码
        self.pos2_embed = nn.Embedding(max_len, d_pos2)
        # 将拼接后的位置编码投影到词向量维度
        self.pos_projection = nn.Linear(d_pos1 + d_pos2, d_model)

    def forward(self, pos1, pos2):
        # pos1: [batch_size, seq_len]
        # pos2: [batch_size, seq_len]
        # 获取两种位置编码
        pos1_embed = self.pos1_embed(pos1)  # [batch, seq, d_pos_source]
        pos2_embed = self.pos2_embed(pos2)  # [batch, seq, d_pos_target]
        # 拼接并投影到d_model维度
        combined = torch.cat([pos1_embed, pos2_embed], dim=-1)  # [batch, seq, d_pos_source + d_pos_target]
        projected = self.pos_projection(combined)  # [batch, seq, d_model]
        return projected


class SimpleGLM(nn.Module):
    def __init__(self, vocab_size=10000, d_model=512, nhead=8, num_layers=6):
        super().__init__()
        self.d_model = d_model
        # 词向量层
        self.token_embed = nn.Embedding(vocab_size, d_model)
        # 二维位置编码层
        self.pos_encoder = GLMPositionalEncoding(d_model=d_model, max_len=12)
        # 简化Transformer编码层(实际GLM使用自定义结构)
        encoder_layer = nn.TransformerEncoderLayer(d_model=d_model, nhead=nhead, batch_first=True)  # 添加 batch_first=True
        self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
        # 线性层,用于输出词概率
        self.fc = nn.Linear(d_model, vocab_size)

    def forward(self, input_ids, pos1, pos2, mask):
        # 输入处理
        token_embed = self.token_embed(input_ids)  # [batch, seq, d_model]
        pos_embed = self.pos_encoder(pos1, pos2)  # [batch, seq, d_model]
        # 融合词向量与位置编码
        embeddings = token_embed + pos_embed  # [batch, seq, d_model]
        # Transformer处理
        hidden_state = self.transformer(embeddings, mask)  # [batch, seq, d_model]
        # 获取logits
        output = self.fc(hidden_state)
        return output

# 示例使用
if __name__ == "__main__":
    # 参数设置
    vocab_size = 10000
    batch_size = 2
    seq_len = 3
    d_model = 512
    max_len = 12

    # 模型初始化
    model = SimpleGLM(vocab_size=vocab_size, d_model=d_model)

    # 构造虚拟输入(假设输入为两个样本,一个长为11,另一个长为10)
    part_a_lens = [6, 5]
    part_b_lens = [5, 5]
    mask = create_mask(part_a_lens, part_b_lens, max_len)
    input_ids = [[101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 101],
                 [101, 102, 103, 104, 105, 106, 107, 108, 109, 110]]
    position1 = [[0, 1, 2, 3, 4, 5, 5, 5, 5, 3, 3],  # Part A:0, 1, 2, 3, 4, 5,PartB:5, 5, 5, 3, 3
                 [0, 1, 2, 3, 4, 2, 2, 4, 4, 4]]  # Part A:0, 1, 2, 3, 4,PartB:2, 2, 4, 4, 4
    position2 = [[0, 0, 0, 0, 0, 0, 1, 2, 3, 1, 2],
                 [0, 0, 0, 0, 0, 1, 2, 1, 2, 3]]

    # 填充或者截断
    for batch_i in range(batch_size):
        if len(input_ids[batch_i]) > max_len:
            input_ids[batch_i] = input_ids[batch_i][:max_len]
            position1[batch_i] = position1[batch_i][:max_len]
            position2[batch_i] = position2[batch_i][:max_len]
        elif len(input_ids[batch_i]) < max_len:
            pad_len = max_len - len(input_ids[batch_i])
            input_ids[batch_i].extend([9999] * pad_len)  # 用9999填充
            position1[batch_i].extend([max_len - 1] * pad_len)  # 用max_len-1填充
            position2[batch_i].extend([max_len - 1] * pad_len)  # 用max_len-1填充

    # print(input_ids)

    # 两个样本的mask不同,若打包成一个batch,则无法通过 nn.TransformerEncoderLayer
    for batch_i in range(batch_size):
        # 转Torch.Tensor
        token_ids = torch.tensor(input_ids[batch_i]).unsqueeze(0)
        pos1 = torch.tensor(position1[batch_i]).unsqueeze(0)
        pos2 = torch.tensor(position2[batch_i]).unsqueeze(0)

        # 前向传播
        outputs = model(token_ids, pos1, pos2, mask[batch_i])
        print("Output shape:", outputs.shape) 

输出:

Output shape: torch.Size([1, 12, 10000])
Output shape: torch.Size([1, 12, 10000])

原版的GLM如何让 batch 打包的 mask 通过nn.TransformerEncoderLayer模块,我没去看代码,因此也不知道是怎么操作的,不过已经不重要了,因为本文目的是理解大致过程,具体怎么做的不重要,我们不是复现。

2.4 多任务训练

从上面的代码来看,其实GLM做的事情,仍然是完形填空,只不过填的不仅仅是单个词了,还有可能是句子。如果我们遮盖掉的文本片段较长,那么模型就会有较强的文本生成能力。

作者使用了两个预训练目标来优化GLM,两个目标交替进行,以便同时增强文本理解与文本生成能力:

  • 文档级别:从文档中随机采样一个文本片段进行掩码,片段的长度为文档长度的50%-100%。(遮盖掉的多,增强的是文本生成能力)
  • 句子级别:从文档中随机掩码若干文本片段,每个文本片段必须为完整的句子,被掩码的词数量为整个文档长度的15%。(遮盖掉的少,增强的是语言理解能力)

2.5 下游任务微调

对于文本分类任务,则遵循 PET。具体来说,给定一个标记的示例 (x, y) ,将输入文本 x 转换为单个 mask token 的填空问题。例如,情感分类任务可以表述为 “{SENTENCE}。这真的是 [M]”,候选标签 y 也映射到填空的答案,称为v(x) 。在情感分类中,标签 “positive” 和“negative” 分别映射到单词 “good” 和 “bad”。

对于文本生成任务,给定的上下文构成了输入的 Part A,末尾附加了一个 [M] 符号,模型自回归地生成 Part B 的文本。可以直接应用预训练的 GLM 进行无条件的生成,或者在下游的条件生成任务上对其进行微调。

2.6 与BERT、GPT的对比

机制GLM自回归填空BERT(自编码)GPT(自回归)
任务类型遮盖部分文本并按顺序生成随机遮盖并独立预测从左到右生成全文
注意力方向双向(允许利用全部已知上下文)完全双向单向(仅左侧上下文)
生成能力支持长文本生成仅支持短文本补全支持长文本生成
训练效率单阶段训练,统一理解和生成需额外微调生成任务仅支持生成任务

2.7 局限性

• 计算复杂度:生成时需要多次迭代预测多个空白,推理速度略慢于纯自回归模型。
• 遮盖策略依赖:性能受遮盖比例和长度的影响,需根据任务调整超参数。

3 GLM与ChatGLM的区别

GLM与ChatGLM是同一系列的自然语言处理模型,但它们在设计目标、应用场景和技术细节上存在差异。以下是两者的主要关系与区别:

3.1 基础架构的继承

• GLM:是一个通用的预训练语言模型,由清华大学团队提出,采用自回归空白填充(Autoregressive Blank Infilling)的训练目标。它结合了自回归(如GPT)和自编码(如BERT)模型的优势,支持多种下游任务(文本生成、分类、问答等)。
• ChatGLM:是基于GLM架构优化而来的对话专用模型,继承了GLM的核心技术(如双向注意力机制),但针对对话场景进行了针对性改进,例如调整模型结构、训练数据和生成策略。

3.2 训练目标与数据的差异

• GLM:
• 训练目标:通过随机遮盖文本片段(Blank)并让模型按顺序填充,同时学习上下文理解和生成能力。
• 数据:使用通用大规模文本(如书籍、网页等),覆盖广泛领域的知识。

• ChatGLM:
• 训练目标:在GLM的基础上增加对话相关的优化目标,例如多轮对话一致性、情感理解和指令跟随能力。
• 数据:引入高质量对话数据(如人工标注的对话、客服记录),并可能通过强化学习(RLHF)对齐人类偏好。

3.3 模型结构与技术改进

• ChatGLM的改进点:
• 对话历史处理:优化对长对话上下文的建模,例如通过位置编码扩展或记忆机制增强多轮对话连贯性。
• 生成策略:采用更严格的重复抑制和话题引导技术,减少无效回复。
• 效率优化:可能通过模型量化或动态批处理提升推理速度,适应实时对话需求。

3.4 应用场景

• GLM:适合通用NLP任务(文本生成、摘要、翻译等),例如作为基座模型适配不同领域。
• ChatGLM:专注于对话场景,如智能客服、社交聊天机器人、个性化助手等,强调交互流畅性和意图理解。

3.5 总结

• 关系:ChatGLM是GLM在对话领域的垂直扩展,两者共享底层架构,但ChatGLM通过场景化适配提升了对话效果。
• 选择建议:若需通用文本处理,选择GLM;若需人机交互,优先使用ChatGLM。

03-11
<think>好的,我现在需要处理用户关于ChatGLM的查询。首先,用户要求了解ChatGLM介绍、特点及使用方法,并且根据系统级指令,我需要确保回答结构清晰,符合格式要求。用户还提供了三个引用,我需要合理引用这些内容。 首先,我要确认用户的问题范围。用户提到了“介绍、特点及使用方法”,这需要我分三个部分来回答。根据引用[3],ChatGLM是智谱AI开源的对话模型,基于GLM架构,支持中英双语,针对中文优化。同时,引用[2]提到它结合了GPT和BERT的结构,具备良好的双语处理能力。引用[1]则提到了GLM-4的安装使用和工具调用能力,可能相关但需注意ChatGLM可能有不同版本。 接下来,我需要整理介绍部分。应该包括开发背景、技术架构、应用场景等。引用[3]提到它基于GLM架构,结合了预训练模型的发展,所以需要强调其技术基础和发展历程。 然后是特点部分。引用[2]和[3]提到了双语能力、知识库、高效推理、开源等。需要分点列出,确保每个特点有引用支持。例如,双语能力来自引用[2],知识库来自引用[3]等。 使用方法部分,用户可能需要安装步骤和调用示例。引用[1]提到了安装方法,但需要确认是否适用于ChatGLM而非GLM-4。可能需要调整,比如通过Hugging Face或GitHub获取模型,使用transformers库加载。同时提供代码示例,但要注意用户提供的示例是Python的快速排序,可能需要类似的代码结构,比如加载模型的代码片段。 另外,系统指令要求生成相关问题,我需要根据内容提出相关问题,比如技术架构、优化方法、应用场景等。确保问题与内容相关,并引用合适的来源。 最后,检查格式:行内公式用$...$,独立公式用$$...$$,但当前内容可能不涉及数学公式,所以可能不需要。但如果有,比如模型架构中的参数,可能需要使用。同时,确保引用标识正确,如[^1]、[^2]、[^3]在相应位置。 现在需要组织语言,确保每个部分清晰,引用正确。例如,在介绍部分的开头,说明ChatGLM由智谱AI开发,基于GLM架构,参考引用[3]。技术架构部分引用[2],说明结合了GPT和BERT。特点部分分点,每个点对应引用。使用方法部分提供安装命令和代码示例,引用[1]和[3]。 可能需要注意避免直接复制引用内容,而是进行整合,确保自然流畅。例如,在特点部分,将双语处理、知识库丰富、高效推理、开源等分项说明,并标注引用来源。同时,使用方法部分要具体,比如安装步骤可能包括pip install transformers,加载模型使用from_pretrained方法,并给出示例代码块。 最后,生成相关问题,比如技术架构的细节、优化方法、应用场景等,确保每个问题都能在引用中找到相关支持,或引导用户进一步探索。 现在,将这些思考整理成结构化的回答,分介绍、特点、使用方法,然后相关问题,确保每个部分引用正确,符合格式要求。</think>### ChatGLM介绍、特点及使用方法 #### 一、ChatGLM简介 ChatGLM是由智谱AI推出的开源对话语言模型系列,基于$GLM$(General Language Model)架构构建。其设计参考了GPT的生成能力和BERT的双向理解特点,融合了预训练语言模型的技术优势。该模型针对中文场景优化,支持中英双语混合输入,广泛应用于智能客服、知识问答、文本生成等领域。 #### 二、ChatGLM核心特点 1. **中英双语能力** 可流畅处理中英文混合输入,理解复杂语义并生成连贯回答[^2]。 2. **知识库丰富性** 通过大规模预训练数据覆盖多领域知识,包括科学、技术、文化等。 3. **高效推理性能** 采用稀疏注意力机制等技术优化计算效率,适合实时交互场景。 4. **开源与可定制** 提供不同规模的模型版本(如ChatGLM-6B),支持开发者微调以适应特定任务。 #### 三、使用方法示例 1. **安装依赖** 使用Hugging Face的`transformers`库加载模型: ```bash pip install transformers ``` 2. **调用模型** ```python from transformers import AutoTokenizer, AutoModel tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True) model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).half().cuda() response, history = model.chat(tokenizer, "如何解释量子纠缠?", history=[]) print(response) ``` 该代码将加载ChatGLM-6B模型并生成对问题的回答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值