ztxz16 / fastllm

纯c++的全平台llm加速库,支持python调用,chatglm-6B级模型单卡可达10000+token / s,支持glm, llama, moss基座,手机端流畅运行
Apache License 2.0
3.32k stars 339 forks source link

某次Merge更新后(约10月30号前后),所有flm模型的推理质量出现大幅度下降,怀疑有显存提前被释放了,这里以chatglm3为例 #376

Open ColorfulDick opened 11 months ago

ColorfulDick commented 11 months ago

转化脚本如下,模型和lora都可以在huggingface找到:

from transformers import AutoTokenizer, AutoModel
from peft import PeftModel

tokenizer = AutoTokenizer.from_pretrained("./chatglm3-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("./chatglm3-6b", trust_remote_code=True, device_map='auto')
model = model.half().cuda().eval()

# 加入下面这两行,将huggingface模型转换成fastllm模型
# 目前from_hf接口只能接受原始模型,或者ChatGLM的int4, int8量化模型,暂时不能转换其它量化模型
from fastllm_pytools import llm
model = llm.from_hf(model, tokenizer) # dtype支持 "float16", "int8", "int4"
model.save("./chatglm3-6b.flm")

使用transformers库运行的gradio界面代码如下:

from transformers import AutoModel, AutoTokenizer
from peft import PeftModel
import gradio as gr
import mdtex2html
"""
tokenizer = AutoTokenizer.from_pretrained("../chatglm3-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("../chatglm3-6b", trust_remote_code=True).half().cuda()
model = model.eval()
"""

"""Override Chatbot.postprocess"""

tokenizer = AutoTokenizer.from_pretrained("../chatglm3-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("../chatglm3-6b", trust_remote_code=True, device_map='auto')
model = model.half().cuda().eval()

def postprocess(self, y):
    if y is None:
        return []
    for i, (message, response) in enumerate(y):
        y[i] = (
            None if message is None else mdtex2html.convert((message)),
            None if response is None else mdtex2html.convert(response),
        )
    return y

gr.Chatbot.postprocess = postprocess

def parse_text(text):
    """copy from https://github.com/GaiZhenbiao/ChuanhuChatGPT/"""
    lines = text.split("\n")
    lines = [line for line in lines if line != ""]
    count = 0
    for i, line in enumerate(lines):
        if "```" in line:
            count += 1
            items = line.split('`')
            if count % 2 == 1:
                lines[i] = f'<pre><code class="language-{items[-1]}">'
            else:
                lines[i] = f'<br></code></pre>'
        else:
            if i > 0:
                if count % 2 == 1:
                    line = line.replace("`", "\`")
                    line = line.replace("<", "&lt;")
                    line = line.replace(">", "&gt;")
                    line = line.replace(" ", "&nbsp;")
                    line = line.replace("*", "&ast;")
                    line = line.replace("_", "&lowbar;")
                    line = line.replace("-", "&#45;")
                    line = line.replace(".", "&#46;")
                    line = line.replace("!", "&#33;")
                    line = line.replace("(", "&#40;")
                    line = line.replace(")", "&#41;")
                    line = line.replace("$", "&#36;")
                lines[i] = "<br>"+line
    text = "".join(lines)
    return text

def predict(input, chatbot, max_length, top_p, temperature, history):
    chatbot.append((parse_text(input), ""))
    for response, history in model.stream_chat(tokenizer, input, history, do_sample=False,max_length=max_length, top_p=top_p,
                                               temperature=temperature,repeat_penalty = 1.02):
        chatbot[-1] = (parse_text(input), parse_text(response))       

        yield chatbot, history

def reset_user_input():
    return gr.update(value='')

def reset_state():
    return [], []

with gr.Blocks() as demo:
    gr.HTML("""<h1 align="center">ChatGLM</h1>""")

    chatbot = gr.Chatbot()
    with gr.Row():
        with gr.Column(scale=4):
            with gr.Column(scale=12):
                user_input = gr.Textbox(show_label=False, placeholder="Input...", lines=10).style(
                    container=False)
            with gr.Column(min_width=32, scale=1):
                submitBtn = gr.Button("Submit", variant="primary")
        with gr.Column(scale=1):
            emptyBtn = gr.Button("Clear History")
            max_length = gr.Slider(0, 8192, value=4096, step=1.0, label="Maximum length", interactive=True)
            top_p = gr.Slider(0, 1, value=0.85, step=0.01, label="Top P", interactive=True)
            temperature = gr.Slider(0, 1, value=0.85, step=0.01, label="Temperature", interactive=True)

    history = gr.State([])

    submitBtn.click(predict, [user_input, chatbot, max_length, top_p, temperature, history], [chatbot, history],
                    show_progress=True)
    submitBtn.click(reset_user_input, [], [user_input])

    emptyBtn.click(reset_state, outputs=[chatbot, history], show_progress=True)

demo.queue().launch(share=False, inbrowser=True,server_name="0.0.0.0")

使用转化后的flm格式代码如下,可以看到两边的top_p等系数都是一样的:

import gradio as gr
from fastllm_pytools import llm

meta_instruction = ""
model = llm.model("./chatglm3-6b.flm")

def respond(message, chat_history,max_length, top_p, temperature):
        prompt = meta_instruction
        for i, (old_query, response) in enumerate(chat_history):
            prompt += "[Round {}]\n\n问:{}\n\n答:{}\n\n".format(i + 1, old_query, response)
        prompt += "[Round {}]\n\n问:{}\n\n答:".format(len(chat_history) + 1, message)  
        bot_message = ""
        chat_history.append([message,bot_message])
        for new_text in model.stream_response(prompt,max_length=max_length,do_sample=False,top_p=top_p,temperature=temperature,repeat_penalty = 1.02):
            bot_message += new_text
            chat_history[-1][1] = bot_message
            if len(bot_message) > 2560000:
                raise StopIteration()
            yield chat_history
        return

def reset_user_input():
    return gr.update(value='')

def reset_state():
    return [], []

with gr.Blocks() as demo:
    gr.HTML("""<h1 align="center">ChatGLM</h1>""")

    chatbot = gr.Chatbot()
    with gr.Row():
        with gr.Column(scale=4):
            with gr.Column(scale=12):
                user_input = gr.Textbox(show_label=False, placeholder="Input...", lines=10).style(
                    container=False)
            with gr.Column(min_width=32, scale=1):
                submitBtn = gr.Button("Submit", variant="primary")
        with gr.Column(scale=1):
            emptyBtn = gr.Button("Clear History")
            max_length = gr.Slider(0, 8192, value=4096, step=1.0, label="Maximum length", interactive=True)
            top_p = gr.Slider(0, 1, value=0.85, step=0.01, label="Top P", interactive=True)
            temperature = gr.Slider(0, 1, value=0.85, step=0.01, label="Temperature", interactive=True)

    history = gr.State([])

    submitBtn.click(respond, [user_input, chatbot, max_length, top_p, temperature], chatbot, queue=True,
                    show_progress=True)
    submitBtn.click(reset_user_input, [], [user_input])

    emptyBtn.click(reset_state, outputs=[chatbot, history], show_progress=True)

demo.queue().launch(share=False, inbrowser=True,server_name="0.0.0.0")

输入的prompt如下:

请将下面的文本输出为简历:“温某佳,137****8845|abc_007008@qq.com|男|生日:1993年10月,在职-月内到岗|1年以内工作经验,个人优势,毕业于软件工程专业,对数据分析及机器学习相关的内容充满学习热情。通过工作项目以及资料,结合本科专业基础,学习并,完成多个数据分析项目,包括但不限于数据采集,数据清洗。寻找机会利用并发挥我的数学、分析及技术能力解决大量数据与,模型相关的现实问题,提出有助于业务决策的建议。熟悉Python。,工作经历,广州众成大数据科技有限公司,2021年4月至今,中台部,数据挖掘,内容:1.学习使用机器学习相关开源项目,如pyHanLP(自然语言处理)、PaddlePaddle(图像处理)等。,2.入门学习深度学习相关知识,包括但不限于MultiLayerPerceptron、Transformer等理论知识,还包括Hugging,face等机器学习相关的多个工具库。,3.训练、使用深度学习完成命名实体识别NER模型,解决文本中OOV问题以及实际业务中的文本分类问题。,4.从零入门学习并掌握多项技术,成功完成多个海外网站的数据采集项目,采集程序根据业务需求定制,且具备健壮性,和可复用性,采集数据累计过600万条,详情见项目经历。,5.对获取的数据进行去重,条件筛选清洗,标准化数据切割等工作。,6.进行数据格式文档,程序注释文档,API文档等文本编写工作,7.,中科智投(广州)科技有限公司,2019年5月至2020年12月,培训师,内容:1.公司成立初期,作为产品培训师学习代理产品及参与课程开发项目中,在团队的课程开发工作中主动担任其团队领导,任务,利用自身经验于学习能力,主导团队讨论及确定开发方向,任务划分及分配,执行任务完成及督促,并主持每日,例会,为产品使用以及后期课程教学提供了一系列上百份扎实可靠的内容。,2.在公司业务推广阶段,作为具备经验的团队成员,根据合作内容,主动商讨及策划活动方案,担当活动执行的沟通、,安排、等多方面工作,与多家学校及教育机构成功举行多场产品推广活动及试课活动,获得多方人员包括学生家长的好,评。,3.在与学校的多次合作中,作为主要负责人,与番禺石基中心小学教师共同成功举办“广州市中小学人工智能第一,课“公开课活动。此后在广州市第二师范学院组织及主持多场产品培训及课程分享活动,获得多方在校教师的认可。期,间多次复盘活动并制作多份演讲文件,与公司内部及合作伙伴交流分享工作经验;里贝拉(上海)商务咨询有限公司,2017年6月至2018年12月,活动策划,内容:·在团队执行部门中担任活动策划助理,主要负责活动方案具体落地以及物资筹备。其中包括与客户以及场地提供方沟通,明确需求,寻找合适供应商,规划设计活动场地,活动现场布置,以及活动执行的前期彩排、执行期间维护、活动结束,后整理等多方面的工作。举办的成功案例有GlobusFachmärkte CANSupplierMeeting,珠海瑞吉酒店品牌开幕仪,式,Facebook Gaming Welcome Party Bangkok等等。,广州友谊集团有限公司,2016年6月至2016年12月,电商运营,内容:在网购部中主要负责网上商城的日常业务运营,包括客服沟通、商品上新等。期间亦负责商城网站后台商品数据及用户,数据的简单维护更新,网站错误上报等工作,并不时参与公司每日会议以及支援商场现场的大型活动,项目经历,文本命名实体识别全栈,2021年8月至2021年8月,内容:1.使用pandas进行数据清洗及切片处理,标准化处理,并通过字典匹配打上实体标签。,2.学习并调用pyhanlp开源项目,进行文本信息提取。,3.学习并使用bert作为embedding并接上CRF作为下游构建出命名实体识别NER模型(基于tensorflow平台),训,练,并应用到业务的招投标文本信息中;,4.使用flask的blueprint加载NER模型,并部署请求接口,使用gunicorn实现NER项目的线上部署;,业绩:1.原始数据量为9万条。,2.NER模型过拟合前的最佳精准度为0.980,对应ep0ch数为8。,3.准确识别出新文本中的字典外的新实体及对应分类,很好地解决了业务中文本分类的需求。,新加坡国外网站,数据采集及数据清洗,2021年7月至2021年8月,内容:1.首次使用httpx用作保持多个请求间会话的持续性;,2.设计了通用爬虫程序类,根据不同参数采集不同类别的数据;,3.引入tqdm模块实现采集进度可视化;,4.通过接入平台解决关键字搜索的图片验证流程;,5.通过shelve模块实现数据持久化;,6.使用Mock模块模拟函数调用,对采集程序进行功能测试debug;,7.通过parse.quote_plus对数据存储路径进行风险规避;,8.验证码图片数据清洗,图片切割,整理成后续OCR模型训练素材。业绩:1.文本数据量为130万条。,2.图片数据量为3200张。,日本国外网站数据采集及数据清洗,2021年06月2021年7月,内容:1.通过网站的jQuery源码分析,找到文档下载请求APl,完成数据采集。,2.使用开源OCR项目paddlepaddle的OCR模块,实现pdf文件中日语文字的内容识别。,业绩:文件的数据量为8千份,泰国国外网站数据采集及数据清洗,2021年5月至2021年6月,内容:1.网络请求的数据复杂,故设计为数据父类,由跟具体业务的子数据类继承;,2.业务为两个类别的广度加深度爬虫,爬虫主程序亦设计为通用父类,由两个具体业务类别的子类继承及实现;,3.子类为爬虫程序单例,其中网络及本地IO使用协程并发;,4.子类的实例化调用运行使用了进程池并发运行;,5.根据Python语言参考手册编写了定制化的日志模块,解决多进程情况下的文件存储的写入风险。,业绩:数据量为300万条。,澳大利亚国外网站数据采集及数据清洗,2021年5月至2021年5月,内容:1.首次引入使用多进程进行数据采集;,2.首次使用RedisStream作为中间件解决多进程文件存储的写入风险;,3.并根据业务需求按照功能区分将高耦合度的函数模块拆分,降低耦合度。,业绩:数据量为7.5万条。,越南国外网站数据采集及清洗,2021年4月至2021年5月,内容:1.使用Requests,BeautifulSoups4,,2.首次引入了Redis,井根据生产者-消费者模式,将厂度爬虫的结果通过RedisStrams作为中间件发布深度爬虫的任,务;,4.网络IO及本地IO模块首次使用协程并发执行;,5.首次引入装饰器模块,对网络IO进行网络ip代理及重试处理。,6.编写存储功能装饰器,装饰于网页解析函数上,对解析出的目标数据直接执行存储操作;,业绩:深度爬虫的数据量为89万条。,加拿大国外网站数据采集及清洗,2021年4月至2021年5月,内容:1.使用Requesgts请求响应,BeautifulSoup4解析页面,2.引入了pandas模块进行搜索关键字的csv文件读取载入;,3.首次使用threading模块,将queue模块的Queue作为共享队列,进行多线程共享任务队列,实现网络请求关键字的,任务分发,单一程序多线程执行。,业绩:深度爬虫的数据量为120万条。,韩国国外网站,数据采集,2021年4月至2021年4月,内容:1.从零开始学习使用Requests获取网站响应内容,,2.BeautifulSoup4解析获取目标文本。,业绩:数据量为9万条。,HousingPricesCompetition,数据分析,2021年3月至2021年3月,内容:读取cSV数据,并将数据划多组,用平均绝对差验证不同深度参数的随机森林算法,利用最优深度参数的算法预测并交叉,验证相关数据。,毕业设计,个人网站全栈开发,2016年5月至2016年6月,内容:基于Flask架构的vps网站搭建;,业绩:语:Python,架构:Flask、Django(了解),前端:Bootstrap,数据:MySQL、Json,服务器:NGINX、Gunicorn,教育经历,仲恺农业工程学院,本科,信息与计算科学,2012年至2016年”

使用transformers库进行推理的结果如下,整体较好:

姓名:温某佳
电话:137****8845
邮箱:abc_007008@qq.com
性别:男
生日:1993年10月
在职状态:在职-月内到岗
工作经验:1年以内
个人优势:毕业于软件工程专业,对数据分析及机器学习相关的内容充满学习热情。通过工作项目以及资料,结合本科专业基础,学习并完成多个数据分析项目,包括但不限于数据采集,数据清洗。寻找机会利用并发挥我的数学、分析及技术能力解决大量数据与模型相关的现实问题,提出有助于业务决策的建议。熟悉Python。
工作经历:
1. 广州众成大数据科技有限公司,2021年4月至今,中台部,数据挖掘
内容:1.学习使用机器学习相关开源项目,如pyHanLP(自然语言处理)、PaddlePaddle(图像处理)等。
2.入门学习深度学习相关知识,包括但不限于MultiLayerPerceptron、Transformer等理论知识,还包括Hugging face等机器学习相关的多个工具库。
3.训练、使用深度学习完成命名实体识别NER模型,解决文本中OOV问题以及实际业务中的文本分类问题。
4.从零入门学习并掌握多项技术,成功完成多个海外网站的数据采集项目,采集程序根据业务需求定制,且具备健壮性,和可复用性,采集数据累计过600万条,详情见项目经历。
5.对获取的数据进行去重,条件筛选清洗,标准化数据切割等工作。
6.进行数据格式文档,程序注释文档,API文档等文本编写工作
7. 中科智投(广州)科技有限公司,2019年5月至2020年12月,培训师
内容:1.公司成立初期,作为产品培训师学习代理产品及参与课程开发项目中,在团队的课程开发工作中主动担任其团队领导,任务,利用自身经验于学习能力,主导团队讨论及确定开发方向,任务划分及分配,执行任务完成及督促,并主持每日例会,为产品使用以及后期课程教学提供了一系列上百份扎实可靠的内容。
2.在公司业务推广阶段,作为具备经验的团队成员,根据合作内容,主动商讨及策划活动方案,担当活动执行的沟通、安排、等多方面工作,与多家学校及教育机构成功举行多场产品推广活动及试课活动,获得多方人员包括学生家长的好评。
3.在与学校的多次合作中,作为主要负责人,与番禺石基中心小学教师共同成功举办“广州市中小学人工智能第一课”公开课活动。此后在广州市第二师范学院组织及主持多场产品培训及课程分享活动,获得多方在校教师的认可。期间多次复盘活动并制作多份演讲文件,与公司内部及合作伙伴交流分享工作经验;
教育经历:
仲恺农业工程学院,本科,信息与计算科学,2012年至2016年
技能:Python,Flask、Django(了解),前端:Bootstrap,数据:MySQL、Json,服务器:NGINX、Gunicorn

使用flm格式进行推理的结果如下,不完整,且丢失了教育经历及技能):

姓名:温某佳 电话:137****8845 邮箱:[abc_007008@qq.com](mailto:abc_007008@qq.com) 性别:男 生日:1993年10月 在职状态:在职,最近一年到岗1年 工作经验:1年以内 个人优势:毕业于软件工程专业,对数据分析及机器学习相关的内容充满学习热情。

工作经历:

广州众成大数据科技有限公司,2021年4月至今,中台部,数据挖掘 内容:1.学习使用机器学习相关开源项目,如pyHanLP(自然语言处理)、PaddlePaddle(图像处理)等。 2.入门学习深度学习相关知识,包括但不限于MULTI_LAYER PERCENTRON、TRANSformer等理论知识,还包括Hugging face等机器学习相关的多个工具库。 3.训练、使用深度学习完成命名实体识别NER模型,解决文本中OOV问题以及实际业务中的文本分类问题。 4.使用pandas进行数据清洗及切片处理,标准化处理,并通过字典匹配打上实体标签。 5.学习并调用pyhanlp开源项目,进行文本信息提取。 6.学习并使用bert作为embedding并接上CRF作为下游构建出命名实体识别NER模型(基于tensorflow平台),训练并应用到业务的招投标文本信息中。 7.使用flaask的bnueprint加载NER模型,并部署请求接口,使用gunicorn实现NER项目的线上部署。

中科智投(广州)科技有限公司,2019年5月至2020年12月,培训师 内容:1.公司成立初期,作为产品培训师学习代理产品及参与课程开发项目中,在团队的课程开发工作中主动担任其团队领导,任务,利用自身经验于学习能力,主导团队讨论及确定开发方向,任务划分及分配,执行任务完成及督促,并主持每日例会,为产品使用以及后期课程教学提供了一系列上百份扎实可靠的内容。 2.在公司业务推广阶段,作为具备经验的团队成员,根据合作内容,主动商讨及策划活动方案,担当活动执行的沟通、安排、等多方面工作,与多家学校及教育机构成功举行多场产品推广活动及试课活动,获得多方人员包括学生家长的好评。 3.在与学校的多次合作中,作为主要负责人,与番禺石基中心小学教师共同成功举办“广州市中小学人工智能第一课”公开课活动。此后在广州市第二师范学院组织及主持多场产品培训及课程分享活动,获得多方在校教师的认可。期间多次复盘活动并制作多份演讲文件,与公司内部及合作伙伴交流分享工作经验。

里贝拉(上海)商务咨询有限公司,2017年6月至2018年12月,活动策划 内容:1.在团队执行部门中担任活动策划助理,主要负责活动方案具体落地以及物资筹备。其中包括与客户以及场地提供方沟通,明确需求,寻找合适供应商,规划设计活动场地,活动现场布置,以及活动执行的前期彩排、执行期间维护、活动结束后的整理等多方面的工作。 2.举办的成功案例有GlbusFachmärktes CANSupplierMeeting,珠海瑞吉酒店品牌开幕仪式,Facebook Gaming Welcome Party Bangkok等等。

广州友谊集团有限公司,2016年6月至2016年12月,电商运营 内容:1.在网购部中主要负责网上商城的日常业务运营,包括客服沟通、商品上新等。期间亦负责商城网站后台商品数据及用户数据的简单维护更新,网站错误上报等工作,并不时参与公司每日会议以及支援商场现场的大型活动。

期多次复盘活动并制作多份演讲文件,与公司内部及合作伙伴交流分享工作经验。

业绩:1.原始数据量为9万条。 2.NER模型过拟合前的最佳精准度为0.980,对应ep0ch数为8。 3.准确识别出新文本中的字典外的新实体及对应分类,很好地解决了业务中文本分类的需求。

其他经历:1.使用httpx用作保持多个请求间会话的持续性;设计了通用爬虫程序类,根据不同参数采集不同类别的数据;引入tqdm模块实现采集进度可视化;通过接入平台解决关键字搜索的图片验证流程;使用Mock模块模拟函数调用,对采集程序进行功能测试debug;通过parse.quote_plu
TylunasLi commented 10 months ago

未能完全复现您的输出,但目前已知这些问题可改进:

  1. 目前使用 model.save()保存模型,tokenizer中的score没有保留,提交 38c5baa5 修复了这个问题。
  2. respond 方法中的 stream_response 方法无需自己构造prompt(除非制定model.direct_query),且当前prompt格式不是chatglm3的。实测对结果影响较多。