Datawhale AI夏令营第四期:动手学大模型应用全栈开发task1

目录

一、环境配置

二、Demo搭建!

2.1文件下载

2.2安装前端工具 

2.3启动Demo

三、精读baseline

3.1 baseline代码

3.2解决遗留问题

速览:这边笔记将会带大家快速跑一遍baseline,再带大家精读一下baseline。

参考教程:Datawhale

一、环境配置

首先我们配置环境,这里笔者使用阿里云的PAI-DSW服务,可以申请试用,可获得 5000算力时!有效期3个月!开通方法可以参考Datawhale

去到PAI-DSW创建一个PAI实例并打开:

打开后是这个界面:

二、Demo搭建!

2.1文件下载

我们可以用git将案例文件下来到服务器上:

点击终端出入以下命令后按回车:

git lfs install
git clone https://www.modelscope.cn/datasets/Datawhale/AICamp_yuan_baseline.git

2.2安装前端工具 

在终端输入以下命令,回车运行:

pip install streamlit==1.24.0

之后等待依赖安装成功即可。

2.3启动Demo

在终端输入以下命令,回车运行:

streamlit run AICamp_yuan_baseline/Task\ 1:零基础玩转源大模型/web_demo_2b.py --server.address 127.0.0.1 --server.port 6007

这个Demo是用Streamlit搭建的前端页面,Streamlit简单易上手~可以快速搭建自己的web页面,它还有如下特点:

 实时推理

将大模型部署在后台服务器上,通过 Streamlit 提供的接口,用户可以输入数据,实时获取模型的推理结果。

 交互式调参

使用 Streamlit 的滑块、选择框等组件,允许用户动态调整大模型的参数(如温度、最大生成长度等),并实时查看输出结果。

OK,现在我们点击链接,跳转到新页面:

  

  

等待后台模型下载完成之后就可以对话啦!

我们让他帮忙写一道leetcode:

  

    可以看到生成的代码通过了测试。我们可以多尝试一下,可以给出不同的解法:

我们再让它换一种解法: 

  

 发现报错,具体原因在下一章章节,精读baseline会提到。

三、精读baseline

3.1 baseline代码

# 导入所需的库
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
import streamlit as st

# 创建一个标题和一个副标题
st.title("💬 Yuan2.0 智能编程助手")

# 源大模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('IEITYuan/Yuan2-2B-Mars-hf', cache_dir='./')

# 定义模型路径
path = './IEITYuan/Yuan2-2B-Mars-hf'

# 定义模型数据类型
torch_dtype = torch.bfloat16 # A10
# torch_dtype = torch.float16 # P100

# 定义一个函数,用于获取模型和tokenizer
@st.cache_resource
def get_model():
    print("Creat tokenizer...")
    tokenizer = AutoTokenizer.from_pretrained(path, add_eos_token=False, add_bos_token=False, eos_token='<eod>')
    tokenizer.add_tokens(['<sep>', '<pad>', '<mask>', '<predict>', '<FIM_SUFFIX>', '<FIM_PREFIX>', '<FIM_MIDDLE>','<commit_before>','<commit_msg>','<commit_after>','<jupyter_start>','<jupyter_text>','<jupyter_code>','<jupyter_output>','<empty_output>'], special_tokens=True)

    print("Creat model...")
    model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch_dtype, trust_remote_code=True).cuda()

    print("Done.")
    return tokenizer, model

# 加载model和tokenizer
tokenizer, model = get_model()

# 初次运行时,session_state中没有"messages",需要创建一个空列表
if "messages" not in st.session_state:
    st.session_state["messages"] = []

# 每次对话时,都需要遍历session_state中的所有消息,并显示在聊天界面上
for msg in st.session_state.messages:
    st.chat_message(msg["role"]).write(msg["content"])

# 如果用户在聊天输入框中输入了内容,则执行以下操作
if prompt := st.chat_input():
    # 将用户的输入添加到session_state中的messages列表中
    st.session_state.messages.append({"role": "user", "content": prompt})

    # 在聊天界面上显示用户的输入
    st.chat_message("user").write(prompt)

    # 调用模型
    prompt = "<n>".join(msg["content"] for msg in st.session_state.messages) + "<sep>" # 拼接对话历史
    inputs = tokenizer(prompt, return_tensors="pt")["input_ids"].cuda()
    outputs = model.generate(inputs, do_sample=False, max_length=1024) # 设置解码方式和最大生成长度
    output = tokenizer.decode(outputs[0])
    response = output.split("<sep>")[-1].replace("<eod>", '')

    # 将模型的输出添加到session_state中的messages列表中
    st.session_state.messages.append({"role": "assistant", "content": response})

    # 在聊天界面上显示模型的输出
    st.chat_message("assistant").write(response)

来看一下baseline,首先导入必要的库。使用modelscope库下载大语言模型Yuan2.0,并将其缓存到本地目录。

@st.cache_resource是streamlit的装饰器缓存加载的模型,以提高性能。下面我们可以注意一下这两行:

# 将用户的输入添加到session_state中的messages列表中
    st.session_state.messages.append({"role": "user", "content": prompt})
 # 将模型的输出添加到session_state中的messages列表中
    st.session_state.messages.append({"role": "assistant", "content": response})

这两行会将用户的输入和模型的输出加入到messages列表中,使得模型可以记住你之前说的话,然后把这个messages列表都作为prompt提交给模型,这个功能我们一般也称为Memory,我们尝试注释模型输出这一行试一下:

然后运行:

streamlit run AICamp_yuan_baseline/Task\ 1:零基础玩转源大模型/web_demo_2b.py --server.address 127.0.0.1 --server.port 6007

 

 可以看到我们要求它换一种解法,但是它已经不记得自己刚刚的解法了,给出了相同的结果。

3.2解决遗留问题

这个时候我们再将代码恢复,然后回到第二章的报错问题。

报错信息:ValueError: Input length of input_ids is 1033, but max_length is set to 1024. This can lead to unexpected behavior. You should consider increasing max_length or, better yet, setting max_new_tokens.

可以看到显示我们input_ids的长度超过了max_length,这是因为baseline会将每次用户的输入和模型的输出加入到messages列表中,在多轮对话中会导致messages列表越来越长,直到超过max_length,这时候模型将无法工作。为解决这个问题,笔者提出一下解决思路:

(1)增加max_length:但这可能会超出模型的处理能力,并且可能导致性能问题。

(2)对输入进行截断:不限制输入的长度,但是如果上下文长度不够了,就删除messages列表中最早的output和input,也就是根据条件删除Memory。

修改后的代码如下: 

# 导入所需的库
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
import streamlit as st

# 创建一个标题和一个副标题
st.title("💬 Yuan2.0 智能编程助手")

# 源大模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('IEITYuan/Yuan2-2B-Mars-hf', cache_dir='./')

# 定义模型路径
path = './IEITYuan/Yuan2-2B-Mars-hf'

# 定义模型数据类型
torch_dtype = torch.bfloat16 # A10
# torch_dtype = torch.float16 # P100

# 定义一个函数,用于获取模型和tokenizer
@st.cache_resource
def get_model():
    print("Creat tokenizer...")
    tokenizer = AutoTokenizer.from_pretrained(path, add_eos_token=False, add_bos_token=False, eos_token='<eod>')
    tokenizer.add_tokens(['<sep>', '<pad>', '<mask>', '<predict>', '<FIM_SUFFIX>', '<FIM_PREFIX>', '<FIM_MIDDLE>','<commit_before>','<commit_msg>','<commit_after>','<jupyter_start>','<jupyter_text>','<jupyter_code>','<jupyter_output>','<empty_output>'], special_tokens=True)

    print("Creat model...")
    model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch_dtype, trust_remote_code=True).cuda()

    print("Done.")
    return tokenizer, model

# 加载model和tokenizer
tokenizer, model = get_model()

# 初次运行时,session_state中没有"messages",需要创建一个空列表
if "messages" not in st.session_state:
    st.session_state["messages"] = []

# 每次对话时,都需要遍历session_state中的所有消息,并显示在聊天界面上
for msg in st.session_state.messages:
    st.chat_message(msg["role"]).write(msg["content"])

# 如果用户在聊天输入框中输入了内容,则执行以下操作
if prompt := st.chat_input():
    # 将用户的输入添加到session_state中的messages列表中
    st.session_state.messages.append({"role": "user", "content": prompt})

    # 在聊天界面上显示用户的输入
    st.chat_message("user").write(prompt)

    # 检查输入长度并删除最早的消息
    while True:
        # 拼接对话历史
        prompt = "<n>".join(msg["content"] for msg in st.session_state.messages) + "<sep>"
        inputs = tokenizer(prompt, return_tensors="pt", truncation=False)["input_ids"]

        # 如果输入长度超过max_length,则删除最早的消息
        if inputs.shape[1] > 1024:
            del st.session_state.messages[0:2]
        else:
            break

    inputs = inputs.cuda()
    outputs = model.generate(inputs, do_sample=False, max_length=1024) 的数量
    output = tokenizer.decode(outputs[0])
    response = output.split("<sep>")[-1].replace("<eod>", '')

    # 将模型的输出添加到session_state中的messages列表中
    st.session_state.messages.append({"role": "assistant", "content": response})

    # 在聊天界面上显示模型的输出
    st.chat_message("assistant").write(response)

在这段代码中,关键的调整是:

在这段代码中,我们添加了一个while循环,在生成之前检查输入的长度。如果输入的长度超过了最大长度(1024),则删除最早的消息,并重新检查长度,直到输入长度符合要求为止。这样可以确保每次生成的输入长度不会超过模型的最大长度,同时删除最早的消息以保留最近的对话历史。

这里结合了模型的性能来制定方案,baseline使用的是一个小模型,当我们使用支持更大上下文的模型时,可以对应调整参数。

我们看看调整完的效果:

现在不会报错了,但还会有一些模型幻觉问题,可能换一个大一些的模型会有所改善,对于这个问题读者可以提出自己的想法,并在评论留言呀~

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值