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使用的是一个小模型,当我们使用支持更大上下文的模型时,可以对应调整参数。
我们看看调整完的效果:
现在不会报错了,但还会有一些模型幻觉问题,可能换一个大一些的模型会有所改善,对于这个问题读者可以提出自己的想法,并在评论留言呀~
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。