GitHub - THUDM/ChatGLM2-6B: ChatGLM2-6B: An Open Bilingual Chat LLM | 开源双语对话语言模型
都是泪啊!硬件跟不上,又上不了云,又想搞一下,只能先弄个丐版了……先透露一下,那响应速度简直了……
OS 名称: Microsoft Windows 10 教育版
处理器: i5-4590T CPU @ 2.00GHz
系统类型: x64-based PC
OS 版本: 10.0.19042 暂缺 Build 19042
机带RAM: 12.0GB
GPU: 你猜
你可以命令行输入
光有硬件还不行,还得来点软件,咱得两手抓。现在这个行情,必须得把
人生苦短,我用Python。之前已经安装过了,版本是:Python 3.10.2。如果没有安装过,先去官网下载一下:Welcome to Python.org
多说一句:安装的时候,记得添加到
注意:这里还设置了
克隆
又火了一个站点:Hugging Face。看看这吉祥物多可爱:🤗。按理说咱应该去人家拥抱脸那里下载,都不知道网上好多教程的大神们是怎么串门的,反正我这里是不行啊,难道她没有给我发请帖,太难了。还好,又给我开了一扇窗:互链高科,不过这窗户始终是窗户啊,太慢了。这里不得不说一下人家
重点:【互链高科】下载【清华大学云盘】中没有的文件,两者合并,完美。
这里将下载的模型放到
这个就比较简单了,直接去GitHub搞就行了。你可以使用 git 克隆;也可以官网上下载zip压缩包;如果有发布版本,你也可以下载稳定的版本(这里没有啊)。
这里将下载的仓库放到
说明:这里的仓库是今天(2023-10-16)在GitHub获取的。
在代码中搜索
Model_Local_Path = "D:\llm\THUDM\chatglm2-6b-int4"
为了支持本地模型和CPU部署,需要修改一下代码。模型支持三种访问模式:命令行、API、WebUI。先用WebUI测试一下,展示效果比较好。修改
# 引入本地模型绝对路径 from modelPath import Model_Local_Path
# 原始代码 tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm2-6b", trust_remote_code=True) model = AutoModel.from_pretrained("THUDM/chatglm2-6b", trust_remote_code=True).cuda() # 用下面的两行代码替换上面的 # .float() 支持CPU tokenizer = AutoTokenizer.from_pretrained(Model_Local_Path, trust_remote_code=True) model = AutoModel.from_pretrained(Model_Local_Path, trust_remote_code=True).float()
# 创建虚拟环境 python -m venv venv # 激活虚拟环境 .\venv\scripts\activate
# 我这里设置全局的清华镜像源 pip install -r requirements.txt # 如果你不想全局设置,也可以仅本次安装时使用镜像源(如下面的截图) pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 命令行 Demo python cli_demo.py # 基于 Gradio 的网页版 demo 【注意】问答时出现bug,前端操作dom的问题 python web_demo.py # 基于 Streamlit 的网页版 demo streamlit run web_demo2.py
这里运行的是基于 Streamlit 的网页版。命令行会出现一些错误,但是并不影响运行。效果图如下:
这家伙生成的代码,很有随机性。这次回答的和上次差异很大啊!我都不好意思截图了。不过,咱也得体谅啊,毕竟这是改版啊,你说是吧。下面的截图是前几天第一次跑通时的截图:
上面基于命令行、Web UI简单的体验了一下。咱肯定不能止步于此啊,是吧!赶紧操作起来吧!
项目中已经提供了
if __name__ == '__main__': Model_Local_Path = "D:\llm\THUDM\chatglm2-6b-int4" tokenizer = AutoTokenizer.from_pretrained(Model_Local_Path, trust_remote_code=True) model = AutoModel.from_pretrained(Model_Local_Path, trust_remote_code=True).float() # 多显卡支持,使用下面三行代替上面两行,将num_gpus改为你实际的显卡数量 # model_path = "THUDM/chatglm2-6b" # tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) # model = load_model_on_gpus(model_path, num_gpus=2) model.eval() uvicorn.run(app, host='127.0.0.1', port=8899, workers=1)
好了,代码弄好了,赶紧让API跑起来吧!请您输入启动命令:
# API Demo python api.py
API跑起来之后,先用大名鼎鼎的
具体代码如下:
import traceback from langchain.prompts import PromptTemplate from langchain.llms.chatglm import ChatGLM from langchain.chains import LLMChain template = """{question}""" prompt = PromptTemplate(template=template, input_variables=["question"]) llm = ChatGLM( endpoint_url="http://127.0.0.1:8899", max_token=80000, history=[], top_p=0.9, model_kwargs={"sample_model_args": False}, ) llm_chain = LLMChain(prompt=prompt, llm=llm) try: question1 = "你的名字是什么啊" print("问:" + question1) answer1 = llm_chain.run(question1) print("答:" + str(answer1)) question2 = "你知道 xiaodu114 吗" print("问:" + question2) answer2 = llm_chain.run(question2) print("答:" + str(answer2)) except Exception as ex: print(ex.args) print("="*24+">") print(traceback.format_exc())
具体代码如下:
import traceback from langchain.llms.chatglm import ChatGLM from langchain.chains.question_answering import load_qa_chain llm = ChatGLM( endpoint_url="http://127.0.0.1:8899", max_token=80000, history=[], top_p=0.9, model_kwargs={"sample_model_args": False}, ) chain = load_qa_chain(llm, chain_type="stuff") question = "你知道 xiaodu114 吗?" try: print("问:" + question) answer = chain.run(input_documents=[], question=question) print("答:" + str(answer)) except Exception as ex: print(ex.args) print("="*24+">") print(traceback.format_exc())
上面的两个例子分别使用:LLMChain 和 load_qa_chain,在结果上有所不同,回答结果和日志信息也有些令人吃惊,下面是结果:
你可以看看这篇文章:OpenAI开发系列(十):Chat Completion Models API详解与构建本地知识库问答系统实践
这个示例使用
# API Demo(模拟 openai api) python openai_api.py
具体代码如下:
const btnOpenAiAPIEle = document.getElementById("btnOpenAiAPI"); btnOpenAiAPIEle.addEventListener("click", () => { let myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Accept", "text/event-stream"); const url = "http://127.0.0.1:8899/v1/chat/completions"; fetch(url, { method: "POST", headers: myHeaders, body: JSON.stringify({ model: "xxx", stream: true, messages: [{ role: "user", content: "你好" }] }) }).then( (response) => { if (!(response && response.ok)) { console.error("异常:" + response.status); return; } let reader = response.body.getReader(); reader.read().then(function processResult(result) { if (result.done) { console.log("Event stream ended."); return; } const decoder = new TextDecoder(); const decodedString = decoder.decode(result.value); console.log(decodedString); // 继续读取下一个事件流数据 reader.read().then(processResult); }); }, (error) => { console.error(`异常:${JSON.stringify(error)}`); } ); });
果然是不顺利啊!这异常可真长……
# 修改之前的代码。可以用下面的代码替换。这里共修改了三处 "{}".format(chunk.json(exclude_unset=True, ensure_ascii=False)) # 修改之后 "{}".format(chunk.model_dump_json(exclude_unset=True))
修改完代码之后,重新执行
采用
传送门:FastAPI
import datetime import uvicorn from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/GetDateTime") def get_server_datetime(): return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") if __name__ == '__main__': uvicorn.run(app, host='127.0.0.1', port=8899, workers=1)
启动一个服务器不要太方便哦!还贴心的带上了API文档,简直不要太爽啊!请看:
const btnGetDateTimeEle = document.getElementById("btnGetDateTime"); btnGetDateTimeEle.addEventListener("click", () => { fetch("http://127.0.0.1:8899/GetDateTime") .then( (response) => { if (response.ok) { return response.json(); } else { console.error(`异常!响应状态码:${response.status} ;响应状态信息:${response.statusText}`); } }, (error) => { console.error(`异常! ${JSON.stringify(error)}`); } ) .then((data) => { alert("服务器端时间:" + data); }); });
这里想着学习一下
你可以看看这篇文章:ChatGLM-6B是如何生成回复的?技术详解 - 知乎
import datetime import json import traceback import uvicorn from transformers import AutoTokenizer, AutoModel from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from sse_starlette.sse import EventSourceResponse app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/GetDateTime") def get_server_datetime(): return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") async def process(prompt, max_length, top_p, temperature, history): """ 发文字消息 """ # 不能是空消息 if not prompt: yield "prompt 不能为空" return try: current_length = 0 # 经测试发现,历史对话也就是 history 参数需要的格式如下: # [('中国的首都是哪里', '中国的首都是北京。'), ('他的面积是多少', '北京市的总面积大约为16,800平方公里。')] for response, history in model.stream_chat(tokenizer, prompt, history=history, max_length=max_length if max_length else 999999, top_p=top_p if top_p else 0.7, temperature=temperature if temperature else 0.95): if len(response) == current_length: continue new_text = response[current_length:] current_length = len(response) yield json.dumps({"text":str(new_text)},ensure_ascii=False) except Exception as ex: yield ex.args +"\n" + traceback.format_exc() return @app.post("/ChatStream") async def create_item(request: Request): global model, tokenizer json_post_raw = await request.json() json_post = json.dumps(json_post_raw) json_post_list = json.loads(json_post) prompt = json_post_list.get('prompt') max_length = json_post_list.get('max_length') top_p = json_post_list.get('top_p') temperature = json_post_list.get('temperature') history = json_post_list.get('history') # 先这样定一下客户端传入 history 的格式,如下: # [["中国的首都是哪里", "中国的首都是北京。"],["他的面积是多少", "北京市的总面积大约为16,800平方公里。"]] # 这里在转成 model.stream_chat 需要的格式 history = [tuple(h) for h in history] answer_text = process(prompt, max_length, top_p, temperature,history) return EventSourceResponse(answer_text, media_type="text/event-stream") if __name__ == '__main__': model_path = "D:\llm\THUDM\chatglm2-6b-int4" tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) model = AutoModel.from_pretrained(model_path, trust_remote_code=True).float() model = model.eval() uvicorn.run(app, host='127.0.0.1', port=8899, workers=1)
这里还是使用
const btnTest1Ele = document.getElementById("btnTest1"); btnTest1Ele.addEventListener("click", () => { let myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Accept", "text/event-stream"); // 陆续询问下面这几个问题: // 中国的首都是哪里 // 他的面积是多少 // 他有机场吗?几座? // 下辖哪些行政区域 const url = "http://127.0.0.1:8899/ChatStream"; fetch(url, { method: "POST", headers: myHeaders, // body: JSON.stringify({ // prompt: "中国的首都是哪里", // history: [] // }) // body: JSON.stringify({ // prompt: "他的面积是多少", // history: [["中国的首都是哪里", "中国的首都是北京。"]] // }) body: JSON.stringify({ prompt: "他有机场吗?几座?", history: [ ["中国的首都是哪里", "中国的首都是北京。"], ["他的面积是多少", "北京市的总面积大约为16,800平方公里。"] ] }) }).then( (response) => { if (!(response && response.ok)) { console.error("异常:" + response.status); return; } let answer = ""; let reader = response.body.getReader(); const decoder = new TextDecoder(); reader.read().then(function processResult(result) { if (result.done) { console.log("Event stream ended."); console.log(answer); return; } try { let decodedString = decoder.decode(result.value); console.log(decodedString); if (decodedString.startsWith("data:")) { let data = JSON.parse(decodedString.slice("data:".length).trim()); answer += data.text; } } catch (error) {} // 继续读取下一个事件流数据 reader.read().then(processResult); }); }, (error) => { console.error(`异常:${JSON.stringify(error)}`); } ); });
不行,我得截个图,让你们看看效果:
这才是硬菜。在通用大语言模型的基础上,添加些自己特色的语料,让其成为你的贴心小助手。
理想很丰满,显示很苗条。暂时没有调通,还得继续吃啊!得赶紧胖起来。
记录一下我的微调过程:
革命尚未成功,还得继续搞啊!我的微调……