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

sty0213 2024-08-26 16:01:05 阅读 60

目录

一、环境配置

二、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将案例文件下来到服务器上:

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

<code>git lfs install

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

2.2安装前端工具 

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

<code>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代码

<code># 导入所需的库

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='./')code>

# 定义模型路径

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>')code>

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()code>

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,我们尝试注释模型输出这一行试一下:

然后运行:

<code>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)增加<code>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='./')code>

# 定义模型路径

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>')code>

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"]code>

# 如果输入长度超过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使用的是一个小模型,当我们使用支持更大上下文的模型时,可以对应调整参数。

我们看看调整完的效果:

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。