glm-4-9b-chat模型本地推理流程

模型输入输出解出问题的探讨

下面对glm-4-9b-chat模型进行测试,使用transformers 进行推理,测试输入和输出的的流程,以及对一些参数的解释。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

device = "cuda"

tokenizer = AutoTokenizer.from_pretrained("/data/model/ZhipuAI/glm-4-9b-chat",trust_remote_code=True)

query = "你能做什么"

# 转换成词汇表编码
inputs = tokenizer.apply_chat_template([
    {"role": "system", "content": "你是一个乐于助人的助手"},\
    {"role": "user", "content": "你是谁"},
    {"role" : "assistant", "content": "我是一个人工智能助手,名为 ChatGLM,是基于清华大学 KEG 实验室和智谱 AI 公司于 2024 年共同训练的语言模型开发的。我的任务是针对用户的问题和要求提供适当的答复和支持" },
    {"role": "user", "content": query}
],
                                       add_generation_prompt=True,
                                       tokenize=True,
                                       return_tensors="pt",
                                       return_dict=True
                                       )

inputs = inputs.to(device)
model = AutoModelForCausalLM.from_pretrained(
    "/data/model/ZhipuAI/glm-4-9b-chat",
    torch_dtype=torch.bfloat16,
    low_cpu_mem_usage=True,
    trust_remote_code=True
).to(device).eval()

gen_kwargs = {"max_length": 25000, "do_sample": True, "top_k": 1}
with torch.no_grad():
    outputs = model.generate(**inputs, **gen_kwargs)
    new = outputs
    outputs = outputs[:, inputs['input_ids'].shape[1]:]
    print(tokenizer.decode(outputs[0], skip_special_tokens=True))

模型的输入处理

转换词汇表

输入的数据首先需要转换成词汇表中的编码信息。
tokenizer.apply_chat_template方法:将输入内容转换成词汇表编码

  • add_generation_prompt=True:这个参数指示分词器在格式化消息后添加一个生成提示,告诉模型在哪里开始生成文本。
  • tokenize=True:这个参数表示应该对消息内容进行分词处理,即将文本转换为模型可以理解的词汇标识符(tokens)。
  • return_tensors=“pt”:这个参数指示分词器返回PyTorch张量,这是模型期望的输入格式。
  • return_dict=True:这个参数指示分词器返回一个字典,而不是一个列表
    输入例子
[
    {
        "role": "system",
        "content": "你是一个乐于助人的助手"
    },
    {
        "role": "user",
        "content": "你是谁"
    },
    {
        "role": "assistant",
        "content": "我是一个人工智能助手,名为 ChatGLM,是基于清华大学 KEG 实验室和智谱 AI 公司于 2024 年共同训练的语言模型开发的。我的任务是针对用户的问题和要求提供适当的答复和支持"
    },
    {
        "role": "user",
        "content": "你能做什么"
    }
]

经过分词器后会对模型进行编码,转换成词汇表编码形式,同时添加了一些特殊的标记,下面是解码后的输入信息

'[gMASK] <sop> <|system|> \n你是一个乐于助人的助手 
<|user|> \n你是谁 
<|assistant|> \n我是一个人工智能助手,名为 ChatGLM,是基于清华大学 KEG 实验室和智谱 AI 公司于 2024 年共同训练的语言模型开发的。我的任务是针对用户的问题和要求提供适当的答复和支持 
<|user|> \n你能做什么 
<|assistant|>'

下面就是转换后对应的词汇表编码信息

tensor([151331, 151333, 151335,    198,  98406,  99950, 113336,  98869,  98942,
        113255, 151336,    198, 103408,  99668, 151337,    198, 122786, 104668,
        113255,   3837, 103092,  12847,   3825,     44,   3837, 117220, 108332,
           730,   9372,  41244,    252,  98752,  99071,  98327,  99126, 100789,
         15223, 100734,  98354,    220, 115937,     19, 101622,  99947, 100597,
        109331, 100484, 110230,   1773,  99198, 126744, 100293,  99833, 100000,
        121189,  99089, 108419, 113574, 111426, 151336,    198, 106779, 105520,
        151337], device='cuda:0')

可以将某个字符转换成原文,如下

#将token ID转换为token字符串
tokenizer.convert_ids_to_tokens(99668).decode('utf-8')
# 输出: '谁'

下面是分词器的结构,其中显示词汇表有151329个词汇token,这些词汇包含了特殊标记和预留的词汇token

ChatGLM4Tokenizer(name_or_path='/data/model/ZhipuAI/glm-4-9b-chat', vocab_size=151329, model_max_length=128000, is_fast=False, padding_side='left', truncation_side='right', special_tokens={'eos_token': '<|endoftext|>', 'pad_token': '<|endoftext|>', 'additional_special_tokens': ['<|endoftext|>', '[MASK]', '[gMASK]', '[sMASK]', '<sop>', '<eop>', '<|system|>', '<|user|>', '<|assistant|>', '<|observation|>', '<|begin_of_image|>', '<|end_of_image|>', '<|begin_of_video|>', '<|end_of_video|>']}, clean_up_tokenization_spaces=False),  added_tokens_decoder={
        151329: AddedToken("<|endoftext|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151330: AddedToken("[MASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151331: AddedToken("[gMASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151332: AddedToken("[sMASK]", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151333: AddedToken("<sop>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151334: AddedToken("<eop>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151335: AddedToken("<|system|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151336: AddedToken("<|user|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151337: AddedToken("<|assistant|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151338: AddedToken("<|observation|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151339: AddedToken("<|begin_of_image|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151340: AddedToken("<|end_of_image|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151341: AddedToken("<|begin_of_video|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
        151342: AddedToken("<|end_of_video|>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}

词向量过程

输入首先经过的是embedding层,它会将词汇表编码转换成向量形式。其中的每个token都转换成4096维的向量。
Embedding(151552, 4096)可以这样理解:Embedding中151552表示能处理的最大令牌,它将 151552 个可能的单词或令牌映射到 4096 维的向量

Embedding在处理的最大token数量和tokenizer在最大的词汇表是不一定相同的,tokenizer中的词汇表可能有冗余未使用的部分

下面是模型的结构,

ChatGLMForConditionalGeneration(
  (transformer): ChatGLMModel(
    (embedding): Embedding(
      (word_embeddings): Embedding(151552, 4096)
    )
    (rotary_pos_emb): RotaryEmbedding()
    (encoder): GLMTransformer(
      (layers): ModuleList(
        (0-39): 40 x GLMBlock(
          (input_layernorm): RMSNorm()
          (self_attention): SelfAttention(
            (query_key_value): Linear(in_features=4096, out_features=4608, bias=True)
            (core_attention): SdpaAttention(
              (attention_dropout): Dropout(p=0.0, inplace=False)
            )
            (dense): Linear(in_features=4096, out_features=4096, bias=False)
          )
          (post_attention_layernorm): RMSNorm()
          (mlp): MLP(
            (dense_h_to_4h): Linear(in_features=4096, out_features=27392, bias=False)
            (dense_4h_to_h): Linear(in_features=13696, out_features=4096, bias=False)
          )
        )
      )
      (final_layernorm): RMSNorm()
    )
    (output_layer): Linear(in_features=4096, out_features=151552, bias=False)
  )
)

模型输出

模型的原始输出解码后如下,其中包含了输入部分和当前模型的输出部分

[gMASK] <sop> <|system|> \n你是一个乐于助人的助手 
<|user|> \n你是谁 
<|assistant|> \n我是一个人工智能助手,名为 ChatGLM,是基于清华大学 KEG 实验室和智谱 AI 公司于 2024 年共同训练的语言模型开发的。我的任务是针对用户的问题和要求提供适当的答复和支持
<|user|> \n你能做什么 
<|assistant|> \n作为一个人工智能助手,我可以做以下几件事情:\n\n1. **信息查询**:提供各种领域的知识,包括科学、历史、文化、技术等。\n\n2. **解答问题**:帮助解答各种问题,从简单的计算到复杂的学术问题。\n\n3. **语言翻译**:提供多种语言之间的翻译服务。\n\n4. **写作辅助**:帮助撰写文章、报告、邮件等,包括语法检查、文本润色等。\n\n5. **编程帮助**:提供编程相关的建议、代码示例和调试帮助。\n\n6. **学习辅导**:辅助学习,提供学习资源、解释概念和解答学习中的难题。\n\n7. **生活建议**:提供健康、饮食、运动等方面的建议。\n\n8. **娱乐互动**:进行简单的对话、讲笑话、提供谜语等娱乐内容。\n\n9. **技术支持**:解答关于软件、硬件和互联网使用的问题。\n\n10. **日程管理**:帮助规划日程、设置提醒和闹钟。\n\n请注意,我的能力受限于我的训练数据和算法,我无法进行物理交互或处理需要人类直觉和判断的任务。 <|user|>'

输出会进行截断处理,最终展示的内容就是本轮推理的真实结果(其中的25000参数表示模型的最大输出长度)

gen_kwargs = {"max_length": 25000, "do_sample": True, "top_k": 1}
with torch.no_grad():
    outputs = model.generate(**inputs, **gen_kwargs)
    new = outputs
    # 获得本轮有效的输出内容
    outputs = outputs[:, inputs['input_ids'].shape[1]:]
    print(tokenizer.decode(outputs[0], skip_special_tokens=True))

有效的输出内容如下:

作为一个人工智能助手,我可以做以下几件事情:
1. **信息查询**:提供各种领域的知识,包括科学、历史、文化、技术等。
2. **解答问题**:帮助解答各种问题,从简单的计算到复杂的学术问题。
3. **语言翻译**:提供多种语言之间的翻译服务。
4. **写作辅助**:帮助撰写文章、报告、邮件等,包括语法检查、文本润色等。
5. **编程帮助**:提供编程相关的建议、代码示例和调试帮助。
6. **学习辅导**:辅助学习,提供学习资源、解释概念和解答学习中的难题。
7. **生活建议**:提供健康、饮食、运动等方面的建议。
8. **娱乐互动**:进行简单的对话、讲笑话、提供谜语等娱乐内容。
9. **技术支持**:解答关于软件、硬件和互联网使用的问题。
10. **日程管理**:帮助规划日程、设置提醒和闹钟。
请注意,我的能力受限于我的训练数据和算法,我无法进行物理交互或处理需要人类直觉和判断的任务。

下面是各个数据的长度,原始输出是295,本轮有效的输出231个token,输入64个token
在这里插入图片描述

最大输出长度的理解

glm4-9b模型的最大上下文长度是128k,在推理时也可以手动限制,transformers库有max_length和max_new_tokens两个参数控制

max_length表示模型的最大输出长度,假如我们设置为64,也就是和输入token一样长,会发生什么?
会报错,显示max_length应该大于输入token长度

Input length of input_ids is 64, but `max_length` is set to 64. This can lead to unexpected behavior. You should consider increasing `max_length` or, better yet, setting `max_new_tokens`.
input_ids的输入长度为64,但' max_length '被设置为64。这可能导致意想不到的行为。你应该考虑增加' max_length ',或者更好的是,设置' max_new_tokens '

如果吧max_length设置为100,由于模型输出中带上了输入,所以真实的输出应该会被截断

'[gMASK] <sop> <|system|> \n你是一个乐于助人的助手
 <|user|> \n你是谁 
 <|assistant|> \n我是一个人工智能助手,名为 ChatGLM,是基于清华大学 KEG 实验室和智谱 AI 公司于 2024 年共同训练的语言模型开发的。我的任务是针对用户的问题和要求提供适当的答复和支持 
 <|user|> \n你能做什么
 <|assistant|> \n作为一个人工智能助手,我可以做以下几件事情:\n\n1. **信息查询**:我可以帮助你查找各种信息,包括新闻、历史数据、科学知识、技术'

之前的报错提示我们设置max_new_tokens ,如果我们这样采用下面的参数

gen_kwargs = {"max_length": 100,"max_new_tokens" :1000 , "do_sample": True, "top_k": 1}

模型可以输出完整的内容

'[gMASK] <sop> <|system|> \n你是一个乐于助人的助手 <|user|> \n你是谁 <|assistant|> \n我是一个人工智能助手,名为 ChatGLM,是基于清华大学 KEG 实验室和智谱 AI 公司于 2024 年共同训练的语言模型开发的。我的任务是针对用户的问题和要求提供适当的答复和支持 <|user|> \n你能做什么 <|assistant|> \n作为一个人工智能助手,我可以做以下几件事情:\n\n1. **信息查询**:我可以帮助你查找各种信息,包括新闻、历史数据、科学知识、技术信息等。\n\n2. **语言翻译**:我可以提供多种语言之间的翻译服务。\n\n3. **写作辅助**:我可以帮助你撰写文章、报告、邮件等,包括语法纠正、内容润色等。\n\n4. **学习辅导**:我可以提供学习资源、解释复杂概念、帮助解答学习中的问题。\n\n5. **生活建议**:我可以提供一些建议,比如健康饮食、锻炼计划、时间管理等。\n\n6. **技术支持**:我可以帮助解决一些基本的技术问题,比如软件使用、设备故障等。\n\n7. **娱乐互动**:我可以参与简单的对话,提供笑话、谜语、游戏等娱乐内容。\n\n8. **数据分析**:我可以处理和分析数据,提供统计信息或趋势预测。\n\n9. **编程帮助**:我可以提供编程语言的解释、代码示例和调试建议。\n\n10. **心理咨询**:虽然我不是专业的心理咨询师,但我可以提供一些基本的情绪支持和心理健康的建议。\n\n请注意,我的能力有限,特别是在需要深度专业知识或个人化服务的情况下,我可能无法提供与人类专家同等水平的帮助。 <|user|>'

但是会有警告,提示我们两个参数都设置了,优先考虑max_new_tokens

Both `max_new_tokens` (=1000) and `max_length`(=100) seem to have been set. `max_new_tokens` will take precedence. Please refer to the documentation for more information. 

huggingface上的解释:

  • max_length (int, optional, defaults to 20) :
    生成的令牌可以具有的最大长度。对应于输入提示符的长度+ max_new_tokens。它的效果被max_new_tokens覆盖,如果也设置了
  • max_new_tokens (int, optional)
    要生成的令牌的最大数量,忽略提示中的令牌数量。

可以这样理解两个控制模型输出的参数

  • max_new_tokens 就是指定模型最大的有效输出,不计算输出中输入的部分
  • max_length 就是指定完整的输出部分的长度,输出部分的长度 = 输入部分 + 效果输出部分
  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值