【Datawhale AI夏令营第四期】 浪潮源大模型应用开发方向笔记 Task02 精读BaseLine代码

【Datawhale AI夏令营第四期】 浪潮源大模型应用开发方向笔记 Task02 学习BaseLine代码

Task02学习链接: https://linklearner.com/activity/14/11/30
学习期间听了开营仪式,在老师的介绍下才发现跑完BaseLine以后学习手册还有后半部分的知识,然后闻着BOSS直聘关键词的味找了几个我自己感兴趣的点:
在这里插入图片描述
在这里插入图片描述情况1和3我都遇到过:比如遇到一个新名词,大模型不知道,要是问题指定了日期(非必须条件),模型发现自己知识库过时了,就开始疯狂道歉:《对不起我的学习内容截至于blabla》。
要是是个一般常识性问题,模型意识不到自己不知道,就可能开始瞎编,比如《生蚝煮熟以后是熟蚝吗?》
在这里插入图片描述
大模型应用开发:Gradio,Streamlit
这俩词在BOSS直聘上也会露面。我猜很可能是本次夏令营我需要花功夫的地方。
在这里插入图片描述
Gradio官方文档:https://www.gradio.app/guides/quickstart
Streamlit官方文档:https://docs.streamlit.io/develop/api-reference
Streamlit给我的感觉就是个新概念的前端,而且看他的展示应该很多基础功能,比如按钮、调色板啥的都有实现。
在这里插入图片描述
然后我就在思考这是不是可以举一反三——streamlit提供了这么多现有的UI实现,其他的前端库也许也有,比如Unity的UGUI,滑动条按钮下拉框一应俱全,当时本科实习就做的Unity前端开发,让我们项目需求写个调色板觉得老难了,换到这也许就很简单了。

PS:我还真的去搜了Unity使用UGUI该怎么制作调色板,嘻嘻。看起来数学公式还挺复杂,不过只要这个需求有前人做过就不是问题,就怕让我创新发明。
《【Unity】关于调色板的简单制作》:
https://blog.csdn.net/weixin_44058587/article/details/139996080
全栈卷王专用传送门
在这里插入图片描述
这思路我一下子没看懂,但我大受震撼:
在这里插入图片描述
目前看来,我感觉Streamlit从功能划分、官方文档、使用方式上都对新手似乎要友善一些。示例的BaseLine也是用到的这个库。
在这里插入图片描述
在这里插入图片描述
服务端这块我之前也没啥了解,就存个官方教程吧:
在这里插入图片描述
官方很贴心地放出了BaseLine流程设计的细节,那我就不客气地拿下了。相比于我之前Task01笔记中展示的自己用DeepSense调用api’简单制作的一个对话助手,这个BaseLine有我没有想到该怎么实现的【交互历史拼接】功能。
在这里插入图片描述
我觉得对于以前有了解但缺乏实际开发经验的小伙伴来说,可以对着代码敲一下这个BaseLine,感受一下现成代码的效果找感觉。毕竟这个教程文档写得这么详细,不能浪漫白嫖的机会。在这里插入图片描述
在这里插入图片描述
这后半部分的过程还真有点抽象,成功吸引了我的注意。
在这里插入图片描述
另外,官方还过分善良地给了我们玩其他模型的机会:
IEITYuan/Yuan2-2B-Mars-hf:https://modelscope.cn/models/IEITYuan/Yuan2-2B-Mars-hf
IEITYuan/Yuan2-2B-July-hf:
https://modelscope.cn/models/IEITYuan/Yuan2-2B-July-hf
IEITYuan/Yuan2-2B-Janus-hf:
https://modelscope.cn/models/IEITYuan/Yuan2-2B-Janus-hf
IEITYuan/Yuan2-2B-Februa-hf-GGUF:
https://www.modelscope.cn/models/IEITYuan/Yuan2-2B-Februa-hf/files
IEITYuan/Yuan2.0-51B-hf:
https://modelscope.cn/models/IEITYuan/Yuan2.0-51B-hf
IEITYuan/Yuan2.0-102B-hf:
https://modelscope.cn/models/IEITYuan/Yuan2.0-102B-hf
在这里插入图片描述
在这里插入图片描述
在学习复刻BaseLine的时候,我意外发现JupyterNotebook要实现一些简单的UI其实比我想的方便(感谢Kimi!)
在这里插入图片描述

import ipywidgets as widgets
from IPython.display import display

text_input = widgets.Text(
    description='输入文本:',
    value='你好,世界!'
)

button = widgets.Button(
    description='提交'
)

def on_button_clicked(b):
    print("你输入的文本是:", text_input.value)

button.on_click(on_button_clicked)

display(text_input, button)

只需要打开一个jupyter notebook装好环境,粘贴进去运行,就发现绘制出了提交按钮和文本输入框:(Unity前端UGUI学习者狂喜)
在这里插入图片描述
我们稍微魔改一下玩玩,就知道各部分变量代表什么了:
在这里插入图片描述
美中不足之处是button.on_click(on_button_clicked)这个函数看起来并没有发挥它该有的打印作用,不过这不是重点,我们还是把注意力放到BaseLine上面来。
我因为方便分布读代码,把之前BaseLine原本的.py文件换成了jupyter notebook更实用的ipynb文件,当然我也很担心之前这种运行.py传参的方式该怎么迁移到.ipynb文件。
在这里插入图片描述
目前看来是这个方法感觉最靠谱:把原本跟着命令的参数硬写进代码赋值:
在这里插入图片描述
但其实,在写了第一行需要涉及打开网页看效果的代码以后,jupyter就很贴心地把该用的命令告诉我们了:
在这里插入图片描述
Warning: to view this Streamlit app on a browser, run it with the following
command:

streamlit run /usr/local/lib/python3.10/site-packages/ipykernel_launcher.py [ARGUMENTS]

DeltaGenerator()
然后切到终端粘贴上面的命令提示,就会跳出预览效果的链接:
在这里插入图片描述
然而很不幸,这两个链接我的电脑都六亲不认拒绝加载……拉到本地的话又要从零开始安装环境……心累……
最后还是只能换成.py文件跑。

报错:Streamlit requires raw Python (.py) files, but the provided file has no extension

然后我发现老遇到这个奇怪的问题:我把BaseLine的代码复制一份换个名字BaseLine web_demo_2b运行(目录位置都没改,什么都没动),就报错Error: Streamlit requires raw Python (.py) files, but the provided file has no extension.
For more information, please see https://docs.streamlit.io
最后发现,这个问题的原因是,我随手取的文件名BaseLine web_demo_2b里面有个空格……
所以最好以后文件命名都用英文+下划线的形式。

报错:6006端口占用

我把空格去掉以后,他又报了个端口占用的Bug。
出现原因大概率是之前的那个原有的代码也设置的占这个端口,但不知道为什么浏览器交互页面关了,端口还占着(大概率是把运行中【没有用Ctrl+C终止的】这个终端界面黑窗口【直接点叉关了】的原因
在这里插入图片描述
解决方案也很简单——重启
按Ctrl+C停止这个终端,如果没效果的话就把这个实例机器重新启动。
在这里插入图片描述
在这里插入图片描述
当然比较高级的玩法还有通过命令啥的解决端口占用问题,这个我查了一下,看有人说不建议啥的,那还是重启吧,免得我操心。
https://www.cnblogs.com/renrsh/p/14490680.html
传送门
在这里插入图片描述
而且,BaseLine教程里面给的这个命令一定要在最外层的文件夹打开终端执行!!!不然就会报莫名其妙的找不到文件之类的错误。
在这里插入图片描述
在这里插入图片描述
BaseLine里面这个数据类型虽然只有两行,名堂还挺多:
在这里插入图片描述
在这里插入图片描述

分词器

BaseLine的下面这个函数从预训练模型的路径加载分词器和模型,返回创建的分词器和模型对象。

#定义一个函数,用于获取模型和tokenizer  定义了一个名为 get_model 的函数,它使用 Streamlit 的 @st.cache_resource 装饰器来缓存资源。
@st.cache_resource    # Streamlit 的一个装饰器,用于缓存函数的结果。当函数再次被调用时,如果输入相同,Streamlit 将使用缓存的结果而不是重新执行函数。这可以显著提高性能,尤其适用于计算成本高昂的函数。
def get_model():
    print("Creat Tokenizer...")
    tokenizer = AutoTokenizer.from_pretrained(path, add_eos_token = False, add_bos_token = False, eos_token='<eod>') 
    #使用 AutoTokenizer.from_pretrained 方法从预训练模型的路径加载分词器。path 变量应该包含模型的路径。add_eos_token 和 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)  #向分词器中添加一系列特殊的标记,这些标记可能用于特定的任务或模型架构。special_tokens=True 表示这些标记将被视为特殊的,并在分词器的词汇表中注册。
    
    print("Creat Model...")
    model = AutoModelForCausalLM.from_pretrained(path, torch_dtype = torch_dtype,trust_remote_code=True).cuda()
    #使用 AutoModelForCausalLM.from_pretrained 方法从预训练模型的路径加载模型。torch_dtype 是之前定义的数据类型,用于设置模型的数值精度。trust_remote_code=True 允许加载远程代码,这在某些情况下是必要的,但也可能带来安全风险。.cuda() 将模型移动到GPU上运行。
    
    print("Done")
    return tokenizer, model  #函数返回创建的分词器和模型对象

所以分词器到底能干啥呢?
在这里插入图片描述
在这里插入图片描述
概括一下,大概有:转换文本到词汇表对应整数条目,添加句子处理标记,填充裁剪文本为固定长度,转换输入输出格式,编码解码文本和数字。,总之就是负责把用户的输入翻译成模型满意的样子,帮助模型理解,也帮模型说人话。
在这里插入图片描述

st.session_state 消息历史存储

这个st.session_state又是啥呢? session_state 是一个字典,用于存储跨多个页面刷新或重运行的会话状态。这两步相当于在这个字典里面新建了一个键值对,键就是messages,值就是消息列表(暂时为空)。
在这里插入图片描述
这样就可以保存和模型的历史聊天记录了吧。(是我之前从来没有想到和了解的东西)
在这里插入图片描述
如果希望每次点开页面模型都说一句话欢迎使用,就可以像下面这位大佬这样写:在创建消息列表的时候不要创建空的,直接就塞一句AI的默认欢迎语进去
https://blog.csdn.net/AAI666666/article/details/139126705
传送门
大佬还做了输入工号姓名等个人信息、清空聊天记录、知情同意勾选框等功能,值得学习!在这里插入图片描述在这里插入图片描述

st.chat_message 分角色显示消息

下面这个chat_message又是啥玩意啊?
在这里插入图片描述
我猜测拿这个role的用处是方便画左右聊天框?
在这里插入图片描述
在这里插入图片描述
Kimi的知识库不知道这个chat_message是什么,没关系,换上通义千问。
在这里插入图片描述
看起来就是遍历所有保存的消息,区别是人还是AI说的,然后区分显示在界面上。
所以如果像上面那位大佬一样,创建messages列表的时候就要显示一句固定的欢迎语句的话,这行代码就会发生作用——哪怕用户啥也不说就走了(没有进入后面的if语句),AI也会很礼貌地输出固定欢迎语打招呼
在这里插入图片描述
我自己改代码试了一下,果然跟我预想的效果一样,让AI先发制人。
在这里插入图片描述
我个人认为可以把st.session_state.messages.append({“role”: “user”, “content”: prompt})和st.chat_message(“user”).write(prompt)看作一组语句,专门负责记录+显示

if用户开始对话 来回将用户和模型说的话加入消息列表并显示(中门对狙开始)

在这里插入图片描述
在这里插入图片描述
概括一下:先用将所有历史消息记录连起来,通过编码从人话变成万能的数学ID序列,然后转换为张量准备甩锅给GPU运算,生成指定最大长度的回答(超了不行少了没事),把回答从数字ID序列翻译回带着各种标记的人话,删掉人看不懂的,等分隔符,还原成人话本该有的样子。
在这里插入图片描述在这里插入图片描述
我按自己的理解画了个不规范但好懂的草图:
在这里插入图片描述

海象运算符 :

在这里插入图片描述简而言之,这段代码的意思是:“如果用户通过 st.chat_input 输入了任何内容,并将其赋值给 prompt,且 prompt 不为空,则执行 if 语句块内的代码。”在这里插入图片描述
在这里插入图片描述

为什么一个if可以做到一直和用户对话?

我之前拿DeepSense写对话助手试水的时候,在外面套了一层while,让模型不停地判断有没有指令输入,没有的话就按原来的该咋就咋。
但这个BaseLine代码核心对话生成、消息记录、效果显示部分只有一个if语句,为什么模型实际上可以一直响应人的要求呢?
一句话,这就是这个Streamlit库的特殊机制。
在这里插入图片描述
所以,看着每次只弹出最新一条互动消息,实际上消息列表的所有历史记录全部刷新了一遍。(感觉这是前端老诈骗套路障眼法了?会不会也是因为这个,某些AI是允许修改历史输入的?)
在这里插入图片描述
在这里插入图片描述

一些自己发现容易踩雷的地方:

1.不要使用’''来标记大段长注释:
在这里插入图片描述
否则你就会发现他们不受欢迎地出现在你的AI助手运行界面。
在这里插入图片描述
2.千万小心不要误触全角输入,特别是英文的,就算是空格也不行!否则会因为奇怪的BUG“SyntaxError: invalid non-printable character U+3000”抓狂。
3.我感觉这个模型好像不太聪明的样子……?
在这里插入图片描述
附上学习手册介绍的开发路径:
在这里插入图片描述
在这里插入图片描述

写好注释的BaseLine代码我会发在个人CSDN资源库,欢迎有需要的朋友们自取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值