从0开始,部署基于yangjianxin开发的流萤(Firefly)中文对话式大语言模型的http服务端

news2024/11/18 8:35:41

项目介绍:

Firefly(流萤) 是yangjianxin开发的开源的中文大语言模型项目,本文主要实现将此模型部署到http服务器上,语言实现:python,本项目为双创项目后端部分代码(本人根据firefly训练代码修改+微调的模型暂不方便开源),样例模型改用firefly1b4模型

项目环境:

1.pytorch:2.0.1+cpu

2.transformers:4.29.1

3.httpserver库

例外:requests库(如果不接其他api不需要)

模型下载:YeungNLP (YeungNLP) (huggingface.co)

下载后新建model文件夹将下载的所有文件放入文件夹,如下图所示

打开config.json,将torch_dtype的值改为int8,可以有效降低卡顿(尤其适用于cpu版本)

硬件环境:

由于是模型的使用,除了推理的时候不会很吃Cpu/Gpu,加载模型比较吃内存,目前经过测试发现实际运行,8G可以勉强运行模型,但是有大概率导致整机卡死,建议至少达到12G内存

项目开发环境:Cpu:i58400,内存:16G(此配置下运行模型再跑androidstudio+非androidstudio自带的模拟器也是搓搓有余的)


代码部分:

1.导入包:

print("导入requests库中...")
import requests
print("导入http库中...")
import http.server
print("导入json库中...")
import json
print("导入os库中...")
import os
print("导入time库中...")
import time
print("导入urllib库中...")
import urllib
import random
from urllib import parse
print("导入transformers库中...")
from transformers import BloomTokenizerFast, BloomForCausalLM
print("导包完成=====================")

2.RequestHandlerImpl类部分(httpserver) 

class RequestHandlerImpl(http.server.BaseHTTPRequestHandler):

    

    def do_GET(self):
        get_str=""
        get_cmd=self.requestline[5:self.requestline.find("HTTP/1.1")]
        self.send_response(200)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.end_headers()
        get_str=checkget(get_cmd,self.headers)
        if get_str=="":get_str= "Hello World\n"
        self.wfile.write(get_str.encode("utf-8"))
        
                         

        

    def do_POST(self):
        req_body = self.rfile.read(int(self.headers["Content-Length"])).decode()
        self.send_response(200)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.end_headers()
        get_str=checkpost(self.path,req_body)
        self.wfile.write(get_str.encode("utf-8"))

3.项目函数部分(由于是app后端有接入其他接口):

def get_answer(text): 
    print("得到新问题",text)
    input_ids = tokenizer(text, return_tensors="pt").input_ids
    input_ids = input_ids.to(device)
    outputs = model.generate(input_ids, max_new_tokens=200, do_sample=True, top_p=0.85, temperature=0.35,repetition_penalty=1.2, eos_token_id=tokenizer.eos_token_id)
    rets = tokenizer.batch_decode(outputs)
    output = rets[0].strip().replace(text, "").replace('</s>', "")
    return format(output)


def get_list(parm): #新闻类接口,可以发布
    parm=parm[1:]
    get_tx=parm.split("&")
    name="福州"
    page="0"

    for i in range(0, len(get_tx)):
        if get_tx[i][0:5]=="name=":
            name=get_tx[i][5:]
            
        if get_tx[i][0:5]=="page=":
            page=get_tx[i][5:].replace(' ', '')

    url = "https://v.api.aa1.cn/api/api-tplist/go.php/api/News/local_news?name=" +  name + "&page=" + page
    print(url)
    response = requests.get(url)
    content = response.text
    return content


def get_top(): #百度热搜接口
    url ='https://v.api.aa1.cn/api/topbaidu/index.php'
    response = requests.get(url)
    content = response.text
    return content


def get_weather(): #天气类接口(付费的)
    url ='http://apis.juhe.cn/simpleWeather/query?city=%E7%A6%8F%E5%B7%9E&key=需要自己加上'
    response = requests.get(url)
    content = response.text
    return content
  
def login(up): #登录接口
    get_tx=up.split("&")
    un=""
    pw=""
    code=0

    for i in range(0, len(get_tx)):
        if get_tx[i][0:5]=="user=":
            un=get_tx[i][5:]
            
        if get_tx[i][0:5]=="pass=":
            pw=get_tx[i][5:].replace(' ', '')
            
    print(un)
    print(pw)

    f=open('libaray/uw', encoding='gbk') #加载type字符库
    for line in f:
        get_tx=line.split(",")
        
        if un==get_tx[0] and pw==get_tx[1].replace('\n', ''):
            dic = {'code': 200, 'msg': "登录成功","token":token}
            break
        else:
            dic = {'code': 201, 'msg': "用户名或密码错误"}

    f.close()
    
    print(dic)
    return json.dumps(dic, sort_keys=True,ensure_ascii= False,indent=4, separators=(',', ':'))


def register(up):  #登录接口
    get_tx=up.split("&")
    uw=""
    pw=""
    code=0
    
    for i in range(0, len(get_tx)): #这里和登录类似,可以封装起来,目的是获取传来的用户,密码
        if get_tx[i][0:5]=="user=":
            un=get_tx[i][5:]
        if get_tx[i][0:5]=="pass=":
            pw=get_tx[i][5:].replace(' ', '')

    print(un)
    print(pw)

    #加载uw密码库,后续可以写成load函数,在加载时候开启
    f=open('libaray/uw', encoding='gbk')
    for line in f:
        get_tx=line.split(",")
        if un==get_tx[0]:
            dic = {'code': 201, 'msg':"用户已存在"}
            return json.dumps(dic, sort_keys=True,ensure_ascii= False,indent=4, separators=(',', ':'))
    f.close()
    f=open('libaray/uw','a+')
    f.write(un+","+pw+"\n")
    dic = {'code': 200, 'msg':"注册成功"}
    f.close()
    return json.dumps(dic, sort_keys=True,ensure_ascii= False,indent=4, separators=(',', ':'))
    
       
def checkpost(path,get_cmd): #查看post进来的数据
    if path=="/login":
        return login(get_cmd)

    if path=="/register":
        return register(get_cmd)
                   
def checkhead(head): #检查需要加密的接口,传进来的头
    print(token == head.get("Authorization"))
    if  token == head.get("Authorization"):
        return True
    else:
        return False


def checkget(get_cmd="",head=""): #查看get进来的数据
    if get_cmd[0:9]=="question=":
        if checkhead(head): 
            dic = {'code': 200, 'msg':get_answer(parse.unquote(get_cmd[9:])),"prompt":urllib.parse.unquote(get_cmd[9:])}
            return json.dumps(dic, sort_keys=True,ensure_ascii= False,indent=4, separators=(',', ':'))  
        else:
            dic = {'code': 401, 'msg':"没有权限"}
            return json.dumps(dic, sort_keys=True,ensure_ascii= False,indent=4, separators=(',', ':'))  
    if get_cmd[0:4]=="list": 
        if checkhead(head):
            return get_list(get_cmd[4:])
        else:
            dic = {'code': 401, 'msg':"没有权限"}
            return json.dumps(dic, sort_keys=True,ensure_ascii= False,indent=4, separators=(',', ':'))
        
    if get_cmd[0:6]=="gettop": 
        if checkhead(head):
            return get_top()
        else:
            dic = {'code': 401, 'msg':"没有权限"}
            return json.dumps(dic, sort_keys=True,ensure_ascii= False,indent=4, separators=(',', ':'))

    if get_cmd[0:7]=="weather": #免费api接口
        return get_weather()
            
    if get_cmd[0:6]=="login?": 
        gcmd=get_cmd[6:]
        return login(gcmd)

main:
 

print("加载tokenizer中")
tokenizer = BloomTokenizerFast.from_pretrained('model/')   #路径以文件夹下的model为例
print("加载model中")
model = BloomForCausalLM.from_pretrained('model/')
model.eval()
device="cpu"
model = model.to(device) #用cuda或者cpu
print("tlc机器人已启动")
token=''.join(random.sample('abcdefghijklmnopqrstuvwxyzABCDEGHIJKLMNOPQRSTWVUXYZ!@#$%&',39))
print("加密为token=" + token) #这句加入是方便测试
local_ip="10.1.136.73" #local ip为服务器ip
server_address = (local_ip, 19999) 
httpd = http.server.HTTPServer(server_address, RequestHandlerImpl)
httpd.serve_forever()

接口测试:

1.运行代码:

运行代码后,如果提示如下图所示就是没有问题的了,可以看到有一个token=xxxx的参数,这个参数是随机生成的临时token,目前设定是每次启动服务端生成一次,这里为了方便演示打印出来,实际需要登录接口来获取,后续可以注释掉

2.测试接口是否可用:

如下图所示在postman输入http://10.1.136.73:19999/question=<s>你好</s></s>

由于输出到模型的数据被格式化成<s></s>的形式,为方便客户端传递历史对话作为promat,我没有在python格式化字符串,而是在客户端里实现。

弹出401的提示是我们加入头,无法通过验证,但是可以证明http服务端可以正常跑起来


 

3.加入头继续验证:

在head加入参数名为Authorization,参数值为临时生成的token的head再次运行。发现已可行,如下图所示,发现客户端正常返回json

 code参数:为200时正常,其他不正常,prompt是传入的值,msg是出来的值。

多轮对话:

在实际测试中发现Firefly1b4的版本也是可以支持多轮对话的,但是效果的确会差些,我们只需要在外部把数据格式化成以下的形式:

<s>问题1</s></s>回答1</s></s>问题2</s></s>回答2</s></s>问题3</s></s>回答3</s></s>

以下是效果案例:

传入的promat为<s>你知道北京吗</s></s>北京市是中国的首都,位于中国北方。</s></s>那里有什么美食</s></s>烤鸭、炸酱面、豆汁、涮羊肉、豆腐脑等等。</s></s>有什么娱乐的地方</s></s>

输出为长城、故宫博物院、颐和园、天坛、圆明园等。

 



Developed by 福州机电工程职业技术学校 wh

邮箱联系方式:xiaohui032901@foxmail.com

qq联系方式:2151335401、3135144152

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/695861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分类预测 | MATLAB实现GA-GRU遗传算法优化门控循环单元的数据多输入分类预测

分类预测 | MATLAB实现GA-GRU遗传算法优化门控循环单元的数据多输入分类预测 目录 分类预测 | MATLAB实现GA-GRU遗传算法优化门控循环单元的数据多输入分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现GA-GRU遗传算法优化门控循环单元的数据多输入分类预…

elementui el-table-column表头换行,自定义表头以及排序图标的位置放置

目录 1、普通表头换行⭐️想实现以下效果 2、表头换行时调整文字和排序图标的位置⭐️想实现以下效果遇到问题 效果如下遇到问题 效果如下⭐️最终成功实现以下效果 &#x1f44d;写在最后 1、普通表头换行 https://www.jb51.net/article/228935.htm // 在需要换行的地方加入换…

一个类似AOV或者AOE的数据结构的类似排序的算法

背景: 一个东西的执行有多个入参和出参, 一个东西的出参又可以是别的东西的入参, 因此执行的依赖关系. 草图里a b c d e f为三个东西, 上面的数字是入参,下面的数字是出参 当前已知这6个东西, 和他们的入参出参 求他们的运行顺序. 要求同样执行顺序的东西可以并行执行. 代码如…

【图像处理OpenCV(C++版)】——5.4 图像平滑之中值平滑(滤波)

前言&#xff1a; &#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义&#xff0c;适用于平时学习、工作快…

IntelliJ IDEA - 一篇解决如何多模块项目提交到同一个 Git 仓库

问题描述 事情是这样的&#xff0c;最近新建了一个项目&#xff08;多模块&#xff09;&#xff0c;这个不必多说大家就知道长什么样子&#xff08;文件结构&#xff09;&#xff0c;问题是在我写完后想提交项目到 Git 仓库时&#xff0c;发现每个模块都要设置 Remote Git 地址…

Get “https://xx.xx.x.xx/v2/“: x509: certificate signed by unknown authority

问题描述 使用Windows docker客户端login docker私库&#xff0c;无法登录提示&#xff1a; Get "https://xx.xx.x.xx/v2/": x509: certificate signed by unknown authority原因分析&#xff1a; 由于harbor做了ssl&#xff0c;通常客户端连接需要配置证书 解决方…

Hbuilder打包android安装包流程

Hbuilder打包android安装包流程 第一步打开项目文件的manifest.json,在右侧填写具体项目应用信息&#xff0c;每次打包升级前版本号一定要设置的比上版本的高。 点击App图标配置&#xff0c;选择图标资源进行全量生成。 点击菜单”发行”&#xff0c;选择云打包&#xff0c…

十分钟实现 Android Camera2 相机预览

1. 前言 因为工作中要使用Android Camera2 API&#xff0c;但因为Camera2比较复杂&#xff0c;网上资料也比较乱&#xff0c;有一定入门门槛&#xff0c;所以花了几天时间系统研究了下&#xff0c;并在CSDN上记录了下&#xff0c;希望能帮助到更多的小伙伴。 2. Camera2 API …

Lenovo联想Yoga 14s 2021AMD平台ARH版(82LB)原装Win10系统镜像 恢复出厂OEM预装系统

lenovo联想笔记本电脑&#xff0c;Yoga 14s 2021AMD平台ARH版(82LB)原装出厂Windows10系统&#xff0c;原厂OEM预装自带系统镜像 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;I…

轻松掌握财务报表的二十二个知识点

财务报表&#xff0c;一是会看资产负债表。知道所在企业的家底有多厚&#xff0c;都分布在什么地方;二是会看利润表。知道企业赚了多少钱&#xff0c;是盈利还是亏损了&#xff0c;赚钱赚在哪里&#xff0c;亏钱亏到了何地;知道企业总共赚了多少钱。三是会看现金流量表。知道企…

软件测试技能,JMeter压力测试教程,Plugins Manager插件管理器(十三)

前言 前面讲了JDBC连接数据库的时候&#xff0c;需下载mysql对应的jar包&#xff0c;放到lib\ext目录下就可以使用了 jmeter 有个插件管理器Plugins Manager&#xff0c;可以方便的管理其他插件的下载和更新 一、插件管理器Plugins Manager 下载地址&#xff1a;Install ::…

6.28黄金能否守住关键支撑,今日多空如何布局

近期有哪些消息面影响黄金走势&#xff1f;今日黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周三&#xff08;6月28日&#xff09;亚市盘中&#xff0c;现货黄金窄幅震荡&#xff0c;现交投于1916美元/盎司附近&#xff0c;隔夜公布的美国经济数据强劲&#…

CSS中常用的颜色格式

本文翻译自 Color Formats in CSS&#xff0c;作者&#xff1a;Joshwcomeau。 略有删改 CSS 中的颜色格式可以采用不同的表示方式&#xff0c;包括常用的十六进制、RGB、RGBA、HSL 和 HSLA 等格式。十六进制是最常用的格式&#xff0c;使用 6 个十六进制数字来表示颜色&#xf…

netwox网络工具的使用【网络工程】(保姆级图文)

目录 1. 打开工具功能菜单查看某方面的模块使用搜索功能得到要使用的模块功能编号 查询模块的帮助信息总结 欢迎关注 『网络工程专业』 系列&#xff0c;持续更新中 欢迎关注 『网络工程专业』 系列&#xff0c;持续更新中 温馨提示&#xff1a;对虚拟机做任何设置&#xff0c;…

世界人工智能大会与ICDAR有何不同?

从事人工智能领域的小伙伴对世界人工智能大会和ICDAR应该都不会陌生&#xff0c;它们似乎都是属于研究、讨论人工智能的一种会议&#xff0c;但其实它们的区别还是挺大的&#xff0c;具体来说&#xff0c;它们主要有以下的区别&#xff1a; 一、组织性质不同 ICDAR是Internati…

《计算机系统与网络安全》 第六章 密钥管理

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

viper读取etcd热更新

概述 项目启动肯定少不了配置文件&#xff0c;一般我们会放在单独的目录&#xff0c;例如config中&#xff0c;有yaml、ini、json等等格式&#xff0c;一般用开源的读取相应问的文件映射到结构体中。 但是当一个项目秒杀频繁控制库存和限流策略等、或者其他需要频繁的变更配置…

uniapp app端常见坑

文章目录 uniapp app端常见坑页面内容出现在状态栏数据持久化问题项目初始化跳转登录页闪屏的问题 总结 uniapp app端常见坑 本文主要记录在uniapp-vite-vue3项目app端出现的常见问题 页面内容出现在状态栏 当在page.json设置 “navigationStyle”:“custom” 取消原生导航栏…

Spring进阶学习(附面试快速答法)

文章目录 1、Bean线程安全问题小总结面试快速答法 2、AOP小总结面试快速答法 3、bean的生命周期小总结面试快速答法 4、循环引用小总结面试快速答法 &#xff15;、SpringMVC的执行流程小总结面试快速答法 6、Springboot自动配置原理小总结面试快速答法 7、Spring框架常见注解面…

初步学习使用Mybatis框架

mybatis框架是一款半自动的ORM持久层框架&#xff0c;具有较高的SQL灵活性 所谓半自动的ORM持久层框架&#xff0c;是因为用mybatis进行开发&#xff0c;需要手动编写。而全自动的ORM框架&#xff0c;如hibernate&#xff0c;则不需要编写SQL语句。 对于mybatis&#xff0c;就…