昇思学习营第七期·昇腾开发板 学习打卡_第三次

第三次打卡:

模型推理:

在开始前,我先通俗的理解一下模型推理是什么意思。我是这样理解的,就类似于小明(用户) 问了 小刚(AI模型) 一个问题:“从咱们学校去医院我需要坐哪一趟公交车啊?需要在哪一站转车?”

把他类比成大模型的工作原理就如下:

  1. 输入问题: 小明的问题就是给模型的输入对应的内容(Input)。
  2. 理解意图: 小刚(模型)首先要理解小明的问题。他需要知道“学校”和“医院”具体指哪里(相当于模型理解词语含义和上下文),以及小明想要的是详细的公交路线和转乘信息(相当于模型理解用户的意图)。
  3. 知识检索与推理: 小刚(模型)回忆起他脑子里存储的整个城市的公交线路图、站点信息和实时交通规则(这相当于模型在庞大的训练数据中学到的“知识库”或“世界知识”)。
  • 检索经过学校的公交线路。
  • 检索经过医院的公交线路。
  • 他发现没有一条线路能直达,需要进行转车
  • 他开始在脑海中规划路线:比较不同线路组合,考虑哪一站是最优的换乘点(比如距离最短、换乘最方便、线路最可靠),计算大概需要多少时间(这整个过程就是推理(Inference)!模型在运用学到的知识、逻辑和规则,一步步推导出解决方案)。

标准的大语言模型(LLM)基础的推理(inference)过程可分为四大阶段:输入处理预填充(Prefill)解码(Decode)和输出后处理(Post-processing)

详细看一下decode的原理和特点:

Decode阶段类似于"写作"过程。就像作家一次只能写一个词,并且每个新词都基于之前已写的内容,模型在Decode阶段也是一个词一个词地生成回答。

Decode阶段的主要特点包括:

  1. 逐词生成:模型一次只生成一个词
  2. 顺序执行:每个新词依赖于之前生成的所有词
  3. 记忆依赖:需要记住之前的所有内容才能决定下一个词
  4. 耗时与回答长度相关:回答越长,总生成时间越长

这就像一位作家在写小说,每写一个词都需要考虑已经写过的内容,确保故事连贯和有意义。

知道这些后,我们再去看一下推理:

首先是模型的加载:

运行后,如下:

此时,我们就可以在输入框中进行对话了:

可以看到它回答出来了。

接下来,我们来看一下每条语句推理的速度:

由于需要交互式生成,还需要进行

export INFERENCE_TIME_RECORD=True

所以我把notebook内容转换成了python内容:

import os
import platform
import mindspore
from mindnlp.transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
from mindnlp.peft import PeftModel
from threading import Thread

# === 环境设置 ===
# mindspore.set_context(pynative_synchronize=True)  # 若调试错误时可开启

# === 加载 tokenizer 和模型 ===
tokenizer = AutoTokenizer.from_pretrained(
    "MindSpore-Lab/DeepSeek-R1-Distill-Qwen-1.5B-FP16", mirror="modelers"
)

# 设置 pad_token
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token

model = AutoModelForCausalLM.from_pretrained(
    "MindSpore-Lab/DeepSeek-R1-Distill-Qwen-1.5B-FP16", mirror="modelers"
)

# 若需要加载 LoRA/Adapter 权重,请取消注释以下行:
# model = PeftModel.from_pretrained(model, "./output/adapter_model_path")

# === Prompt 设定 ===
system_prompt = "你是一个智能聊天机器人,以最简单的方式回答用户问题"

# === 构建输入 ===
def build_input_from_chat_history(chat_history, msg: str):
    messages = [{'role': 'system', 'content': system_prompt}]
    for info in chat_history:
        messages.append({'role': info['role'], 'content': info['content']})
    messages.append({'role': 'user', 'content': msg})
    return messages

# === 推理逻辑(异步流式输出) ===
def inference(message, history):
    messages = build_input_from_chat_history(history, message)
    input_ids = tokenizer.apply_chat_template(
        messages,
        add_generation_prompt=True,
        return_tensors="ms",
        tokenize=True
    )

    streamer = TextIteratorStreamer(
        tokenizer,
        timeout=300,
        skip_prompt=True,
        skip_special_tokens=True
    )

    generate_kwargs = dict(
        input_ids=input_ids,
        streamer=streamer,
        max_new_tokens=1024,
        use_cache=True,
    )

    t = Thread(target=model.generate, kwargs=generate_kwargs)
    t.start()

    partial_message = ""
    for new_token in streamer:
        partial_message += new_token
        print(new_token, end="", flush=True)

    messages.append({'role': 'assistant', 'content': partial_message})
    return messages[1:]  # 不返回 system prompt

# === 主对话循环 ===
def main():
    os_name = platform.system()
    clear_command = 'cls' if os_name == 'Windows' else 'clear'
    welcome_prompt = '欢迎使用 DeepSeek-R1-Distill-Qwen-1.5B 模型,输入内容即可进行对话,clear 清空对话历史,stop 终止程序'
    print(welcome_prompt)

    history = []

    while True:
        query = input("\n用户:").strip()
        if query == "stop":
            break
        if query == "clear":
            os.system(clear_command)
            print(welcome_prompt)
            history = []
            continue
        print("\nDeepSeek-R1-Distill-Qwen-1.5B:", end="")
        history = inference(query, history)
        print("")

# === 程序入口 ===
if __name__ == "__main__":
    main()

运行后效果如下:

接着,让我们进行性能优化后再来查看一下每条语句推理的速度:

首先,我们来进行一下O2级别的jit优化,也就是我们所说的图算融合

mindspore.set_context(
    enable_graph_kernel=True,
    mode=mindspore.GRAPH_MODE,
    jit_config={
        "jit_level": "O2",
    },
)

然后加载一下原模型。速度是非常的快,可以看到可以跑到32.4MB/S

最后,进行每一步的时间测试。

可以看到,在第一步的时候耗时18.3s,然后后面的每一步都是0.12s左右了。为什么会这样呢?这是因为在这个优化算法下,我们第一步需要先进行一下编译,也就是我们所说的冷启动,比较耗时,然后后面时间就比较快了。

最后,总结一下:

模式 动态图(普通模型) 静态图(JIT模型)
计算图构建 运行时即时构建 运行前提前编译
灵活性 高,支持动态控制 较低,不支持复杂动态变化
调试难度 低,代码直观易调试 高,调试相对复杂
推理效率 较低
适用场景 研发、调试、模型探索 生产环境、实时推理
部署稳定性 较差 较好