未来对话:HAI创作个人专属的知识宇宙

腾讯云 高性能应用服务 HAI 应用部署,基于 ChatGLM2-6B 创作个人专属的知识宇宙

一、背景介绍:

腾讯云高性能应用服务 HAI 是为开发者量身打造的澎湃算力平台。无需复杂配置,便可享受即开即用的GPU云服务体验。在 HAI 中,根据应用智能匹配并推选出最适合的GPU算力资源,以确保您在数据科学、LLM、AI作画等高性能应用中获得最佳性价比。

HAI 服务优势

智能选型 :根据应用匹配推选GPU算力资源,实现最高性价比。同时,打通必备云服务组件,大幅简化云服务配置流程。

一键部署 :分钟级自动构建LLM、AI作画等应用环境。提供多种预装模型环境,包含如 StableDiffusion、ChatGLM2等热门模型。

可视化界面 :友好的图形界面,AI调试更为简单

场景介绍

基于HAI部署的 ChatGLM2-6B 快速进行AI对话

基于HAI部署的 ChatGLM2-6B API 快速实现开发者所需的相关API服务

基于HAI部署的 ChatGLM2-6B API 快速实现模型的微调

二、 实验介绍

本次我们使用 腾讯云高性能应用服务 HAI 体验快速搭建并使用AI模型 ChatGLM2-6B ,实现思路如下:

1、体验 高性能应用服务HAI 一键部署 ChatGLM2-6B

2、启动 ChatGLM2-6B WebUI 进行简单的对话

3、开发者体验 JupyterLabCloud Studio 进行 ChatGLM2-6B API 的配置调用

4、开发者使用 Cloud Studio 应用推荐 ChatGPT Next Web 快速开发调用 ChatGLM2-6B OpenAI API 服务,搭建自己的GPT

5、开发者使用 高性能应用服务HAI 快速部署 ChatGLM2-6B-int4 本地模型及基于 P-Tuning v2 的微调

三、效果展示

使用 ChatGLM2-6B WebUI 进行简单的对话

Cloud Studio 进行 ChatGLM2-6B API 的调用

Cloud Studio 调用 ChatGLM2-6B 流式 API 接口

Cloud Studio 创建应用 ChatGPT Next Web 并快速调用 ChatGLM2-6B OpenAI API 接口

开发者使用 高性能应用服务HAI 训练 ChatGLM2-6B 本地模型

开发者使用 高性能应用服务HAI 测试训练完成后的ChatGLM2-6B 模型

四、实操指导

1、申请高性能应用服务 HAI

① . 点击链接进入 高性能应用服务 HAI 申请体验资格

② . 等待审核通过后,进入 高性能应用服务 HAI

③ . 点击前往体验HAI,登录 高性能应用服务 HAI 控制台

③ . 点击 新建 选择 AI模型,输入实例名称

温馨提示:如果没有进阶型的算力方案(2.41元/小时),建议您购买基础型的算力方案(0.88元/小时),并在创建成功后参考实验过程中关闭 、重新开启 webui 功能的命令,以提高服务器的性能,加速您完成实验的体验!

④ . 等待创建完成 (预计等待3-8分钟,等待时间不计费)

⑤ . 创建完成,查看相关状态

⑥ . 查看配置详情

2、启动 高性能应用服务HAI 配置的 ChatGLM2-6B WebUI 进行简单的对话

① . 选择 chatglm2_gradio 进入 WebUI 页面

② . 体验与 ChatGLM2-6B 简单的对话

3、高性能应用服务HAI 快速为开发者提供 ChatGLM2-6B API 服务

① .使用 JupyterLab 启动 ChatGLM2-6B 提供的 API 服务

(1) .在 算力管理 页面,选择进入 jupyter_lab 页面

选择 终端命令

温馨提示:如果您购买使用的是 基础型算力服务器(0.88元/小时) 请您在开始实验前输入以下关闭 webui 功能的命令,提高服务器的性能,以便后续实验能快速正常进行:

apt-get update && apt-get install sudo
sudo apt-get update  
sudo apt-get install psmisc
sudo fuser -k 6889/tcp #执行这条命令将关闭 HAI提供的 chatglm2_gradio webui功能

如果需要重新开启 webui 服务执行以下命令:

 python /root/ChatGLM2-6B/web_demo.py --listen --port 6889

输入命令 用于开启 API 服务:

cd ./ChatGLM2-6B
python api.py

(2) .新增服务器端口规则

选择 编辑规则

选择 入站规则 中的添加规则

添加入站规则 (来源: 0.0.0.0/0 协议端口: TCP:8000)

② .使用 Cloud Studio 快速调用测试 ChatGLM2-6B 提供的 API 服务

(1) .打开 Cloud Studio 并创建开发空间

新建工作空间 输入 空间名称 ,选择 代码来源为空 ,开发环境 Python 即可

等待数十秒 工作空间启动完成

(2) .编写调用代码并运行测试

workspace 下右键选择 新建文件

创建 get_api.py 代码文件,并复制以下代码 用于测试请求
注意: 请确保将代码中的地址和端口更改为您的API服务器的实际地址和端口

import requests

# 定义测试数据,以及FastAPI服务器的地址和端口
server_url = "http://0.0.0.0:8000"  # 请确保将地址和端口更改为您的API服务器的实际地址和端口
test_data = {
    "prompt": "'你好,发热了怎么办?'",
    "history": [],
    "max_length": 50,
    "top_p": 0.7,
    "temperature": 0.95
}

# 发送HTTP POST请求
response = requests.post(server_url, json=test_data)

# 处理响应
if response.status_code == 200:
    result = response.json()
    print("Response:", result["response"])
    print("History:", result["history"])
    print("Status:", result["status"])
    print("Time:", result["time"])
else:
    print("Failed to get a valid response. Status code:", response.status_code)

查看 ChatGLM2-6B 实例 公网地址,并修改代码中 服务器地址 部分

修改完代码文件,可点击右上角的运行按钮进行测试

返回请求结果:

服务器端可查看记录并使用 Ctrl+C 关闭服务:

③ .使用 JupyterLab 开发 ChatGLM2-6B 流式返回 API 接口并使用 Cloud Studio 快速调用测试

​ (1) .使用 JupyterLab 编写基于 FastAPI 编写的服务器端代码并开启服务

​ 在 JupyterLab 中选择文件夹操作界面,依次打开 root 文件夹下的 ChatGLM2-6B 文件夹,并创建一个 Python File ,拷贝一下代码并保存,同时将文件名修改为 chatglm2-6b-stream-api.py ,最后开启API服务

创建Python文件


粘贴 chatglm2-6b-stream-api.py 代码

# -*-coding:utf-8-*-
'''
File Name:chatglm2-6b-stream-api.py
Author:Luofan
Time:2023/6/26 13:33
'''

import os
import sys
import json
import torch
import uvicorn
import logging
import argparse
from fastapi import FastAPI
from transformers import AutoTokenizer, AutoModel
from fastapi.middleware.cors import CORSMiddleware
from sse_starlette.sse import ServerSentEvent, EventSourceResponse


def getLogger(name, file_name, use_formatter=True):
    logger = logging.getLogger(name)
    logger.setLevel(logging.INFO)
    console_handler = logging.StreamHandler(sys.stdout)
    formatter = logging.Formatter('%(asctime)s    %(message)s')
    console_handler.setFormatter(formatter)
    console_handler.setLevel(logging.INFO)
    logger.addHandler(console_handler)
    if file_name:
        handler = logging.FileHandler(file_name, encoding='utf8')
        handler.setLevel(logging.INFO)
        if use_formatter:
            formatter = logging.Formatter('%(asctime)s - %(name)s - %(message)s')
            handler.setFormatter(formatter)
        logger.addHandler(handler)
    return logger


logger = getLogger('ChatGLM', 'chatlog.log')

MAX_HISTORY = 3


class ChatGLM():
    def __init__(self) -> None:
        logger.info("Start initialize model...")
        self.tokenizer =  AutoTokenizer.from_pretrained("THUDM/chatglm2-6b", revision="v1.0", trust_remote_code=True)
        self.model = AutoModel.from_pretrained("THUDM/chatglm2-6b", revision="v1.0", trust_remote_code=True).cuda()
        self.model.eval()
        logger.info("Model initialization finished.")

    def clear(self) -> None:
        if torch.cuda.is_available():
            with torch.cuda.device(f"cuda:{args.device}"):
                torch.cuda.empty_cache()
                torch.cuda.ipc_collect()

    def answer(self, query: str, history):
        response, history = self.model.chat(self.tokenizer, query, history=history)
        history = [list(h) for h in history]
        return response, history

    def stream(self, query, history):
        if query is None or history is None:
            yield {"query": "", "response": "", "history": [], "finished": True}
        size = 0
        response = ""
        for response, history in self.model.stream_chat(self.tokenizer, query, history):
            this_response = response[size:]
            history = [list(h) for h in history]
            size = len(response)
            yield {"delta": this_response, "response": response, "finished": False}
        logger.info("Answer - {}".format(response))
        yield {"query": query, "delta": "[EOS]", "response": response, "history": history, "finished": True}


def start_server(http_address: str, port: int, gpu_id: str):
    os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'
    os.environ['CUDA_VISIBLE_DEVICES'] = gpu_id

    bot = ChatGLM()

    app = FastAPI()
    app.add_middleware(CORSMiddleware,
                       allow_origins=["*"],
                       allow_credentials=True,
                       allow_methods=["*"],
                       allow_headers=["*"]
                       )

    @app.get("/")
    def index():
        return {'message': 'started', 'success': True}

    @app.post("/chat")
    async def answer_question(arg_dict: dict):
        result = {"query": "", "response": "", "success": False}
        try:
            text = arg_dict["query"]
            ori_history = arg_dict["history"]
            logger.info("Query - {}".format(text))
            if len(ori_history) > 0:
                logger.info("History - {}".format(ori_history))
            history = ori_history[-MAX_HISTORY:]
            history = [tuple(h) for h in history]
            response, history = bot.answer(text, history)
            logger.info("Answer - {}".format(response))
            ori_history.append((text, response))
            result = {"query": text, "response": response,
                      "history": ori_history, "success": True}
        except Exception as e:
            logger.error(f"error: {e}")
        return result

    @app.post("/stream")
    def answer_question_stream(arg_dict: dict):
        def decorate(generator):
            for item in generator:
                #yield ServerSentEvent(json.dumps(item, ensure_ascii=False), event='delta')
                yield ServerSentEvent(json.dumps(item, ensure_ascii=False))

        try:
            text = arg_dict["query"]
            ori_history = arg_dict["history"]
            logger.info("Query - {}".format(text))
            if len(ori_history) > 0:
                logger.info("History - {}".format(ori_history))
            history = ori_history[-MAX_HISTORY:]
            history = [tuple(h) for h in history]
            return EventSourceResponse(decorate(bot.stream(text, history)))
        except Exception as e:
            logger.error(f"error: {e}")
            return EventSourceResponse(decorate(bot.stream(None, None)))

    @app.get("/free_gc")
    def free_gpu_cache():
        try:
            bot.clear()
            return {"success": True}
        except Exception as e:
            logger.error(f"error: {e}")
            return {"success": False}

    logger.info("starting server...")
    uvicorn.run(app=app, host=http_address, port=port, workers=1)


if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Stream API Service for ChatGLM2-6B')
    parser.add_argument('--device', '-d', help='device,-1 means cpu, other means gpu ids', default='0')
    parser.add_argument('--host', '-H', help='host to listen', default='0.0.0.0')
    parser.add_argument('--port', '-P', help='port of this service', default=8000)
    args = parser.parse_args()
    start_server(args.host, int(args.port), args.device)

JupyterLab 中完成文件的创建并重命名 chatglm2-6b-stream-api.py 成功:

JupyterLab终端界面 中输入命令开启 chatglm2-6b-stream-api.py 服务:

python chatglm2-6b-stream-api.py

注意: 请将上一个 API服务 关闭,否则服务无法启动成功

(2) .使用 Cloud Studio 编写客户端代码

使用普通Http请求调用 /chat 接口

Cloud Studio 工作空间下继续创建 Python 代码文件 use_chatglm2-6b-stream-api.py
注意: 请将代码中的地址和端口更改为实际的服务器地址和端口

use_chatglm2-6b-stream-api.py 代码文件:

import requests
import json

# 设置服务器地址和端口
server_address = "http://0.0.0.0"  # 请将地址和端口更改为实际的服务器地址和端口
server_port = 8000

# 构造请求数据
request_data = {
    "query": "你好,发热了怎么办?",
    "history": []
}

# 发送HTTP POST请求到聊天端点
response = requests.post(f"{server_address}:{server_port}/chat", json=request_data)

# 处理响应
if response.status_code == 200:
    result = response.json()
    if result["success"]:
        print("Response:", result["response"])
        print("History:", result["history"])
    else:
        print("Failed to get a valid response.")
else:
    print("Failed to connect to the server. Status code:", response.status_code)

创建完成:

运行并查看返回结果:
在菜单栏中点击 终端 选择 新建终端,输入命令执行代码

python use_chatglm2-6b-stream-api.py

调用接口成功

服务端查看记录:

使用AioHttp调用 /stream 流式接口

Cloud Studio 工作空间下 继续创建 Python 代码文件 use_stream_chatglm2-6b-stream-api.py
注意: 请将代码中的地址和端口更改为实际的服务器地址和端口

use_stream_chatglm2-6b-stream-api.py 代码文件:

import aiohttp  
import json  
import asyncio  
  
async def main():  
    async with aiohttp.ClientSession() as session:  
        server_address = "http://0.0.0.0"  # 请将地址更改为实际的服务器地址  
        server_port = 8000  
        endpoint = f"{server_address}:{server_port}/stream"  # 流式处理端点  
  
        request_data = {  
            "query": "你好,发热怎么办?",  
            "history": []  
        }  
  
        try:  
            async with session.post(endpoint, json=request_data, timeout=None) as response:  
                if response.status == 200:  
                    async for line in response.content.iter_any():  
                        data = line.decode('utf-8')  
                        data = data.replace('event: delta\r\ndata: ','')
                        data = data.replace('\r\n','')
                        data = data.replace('data: ','')
                        try:  
                            result = json.loads(data)  
                            if result.get("finished"):  
                                    print("Response:", result["response"])  
                                    print('\r\n')
                                    print("History:", result["history"])  
                            #else:  
                            #    print("Delta:", result["delta"])  
                        except json.JSONDecodeError:  
                            print("Failed to parse JSON:")  
                else:  
                    print(f"Failed to connect to the server. Status code: {response.status}")  
        except aiohttp.ClientError as e:  
            print(f"Client error occurred: {e}")  
            await asyncio.sleep(1)  # Avoid continuous failures, wait a bit before retrying  
            print(f"Unexpected error occurred: {e}")  
            await asyncio.sleep(1)  # Avoid continuous failures, wait a bit before retrying  
  
if __name__ == '__main__':  
    asyncio.run(main())

终端 中输入命令 安装 aiohttp 模块

pip install aiohttp 

运行并查看返回结果:

终端 中输入命令执行代码

python use_stream_chatglm2-6b-stream-api.py

4、开发者使用 Cloud Studio 创建应用推荐 ChatGPT Next Web 开源项目 并快速开发调用 ChatGLM2-6B OpenAI API 服务

​ ① .使用 JupyterLab 修改 openai_api.py 代码并开启服务

​ 打开 JupyterLab 后关闭上一次开启的 API 服务
​ 使用 Ctrl+C 关闭服务

​ 打开右边文件夹下的 openai_api.py 文件

如果直接使用会在调用时报错,复制以下 openai_api.py 代码直接覆盖源文件并 Ctrl+S 保存代码

# coding=utf-8
# Implements API for ChatGLM2-6B in OpenAI's format. (https://platform.openai.com/docs/api-reference/chat)
# Usage: python openai_api.py
# Visit http://localhost:8000/docs for documents.

import time
import torch
import uvicorn
from pydantic import BaseModel, Field
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from typing import Any, Dict, List, Literal, Optional, Union
from transformers import AutoTokenizer, AutoModel
from sse_starlette.sse import ServerSentEvent, EventSourceResponse


@asynccontextmanager
async def lifespan(app: FastAPI): # collects GPU memory
    yield
    if torch.cuda.is_available():
        torch.cuda.empty_cache()
        torch.cuda.ipc_collect()


app = FastAPI(lifespan=lifespan)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

class ModelCard(BaseModel):
    id: str
    object: str = "model"
    created: int = Field(default_factory=lambda: int(time.time()))
    owned_by: str = "owner"
    root: Optional[str] = None
    parent: Optional[str] = None
    permission: Optional[list] = None


class ModelList(BaseModel):
    object: str = "list"
    data: List[ModelCard] = []


class ChatMessage(BaseModel):
    role: Literal["user", "assistant", "system"]
    content: str


class DeltaMessage(BaseModel):
    role: Optional[Literal["user", "assistant", "system"]] = None
    content: Optional[str] = None


class ChatCompletionRequest(BaseModel):
    model: str
    messages: List[ChatMessage]
    temperature: Optional[float] = None
    top_p: Optional[float] = None
    max_length: Optional[int] = None
    stream: Optional[bool] = False


class ChatCompletionResponseChoice(BaseModel):
    index: int
    message: ChatMessage
    finish_reason: Literal["stop", "length"]


class ChatCompletionResponseStreamChoice(BaseModel):
    index: int
    delta: DeltaMessage
    finish_reason: Optional[Literal["stop", "length"]]


class ChatCompletionResponse(BaseModel):
    model: str
    object: Literal["chat.completion", "chat.completion.chunk"]
    choices: List[Union[ChatCompletionResponseChoice, ChatCompletionResponseStreamChoice]]
    created: Optional[int] = Field(default_factory=lambda: int(time.time()))


@app.get("/v1/models", response_model=ModelList)
async def list_models():
    global model_args
    model_card = ModelCard(id="gpt-3.5-turbo")
    return ModelList(data=[model_card])


@app.post("/v1/chat/completions", response_model=ChatCompletionResponse)
async def create_chat_completion(request: ChatCompletionRequest):
    global model, tokenizer

    if request.messages[-1].role != "user":
        raise HTTPException(status_code=400, detail="Invalid request")
    query = request.messages[-1].content

    prev_messages = request.messages[:-1]
    if len(prev_messages) > 0 and prev_messages[0].role == "system":
        query = prev_messages.pop(0).content + query

    history = []
    if len(prev_messages) % 2 == 0:
        for i in range(0, len(prev_messages), 2):
            if prev_messages[i].role == "user" and prev_messages[i+1].role == "assistant":
                history.append([prev_messages[i].content, prev_messages[i+1].content])

    if request.stream:
        generate = predict(query, history, request.model)
        return EventSourceResponse(generate, media_type="text/event-stream")

    response, _ = model.chat(tokenizer, query, history=history)
    choice_data = ChatCompletionResponseChoice(
        index=0,
        message=ChatMessage(role="assistant", content=response),
        finish_reason="stop"
    )

    return ChatCompletionResponse(model=request.model, choices=[choice_data], object="chat.completion")


async def predict(query: str, history: List[List[str]], model_id: str):
    global model, tokenizer

    choice_data = ChatCompletionResponseStreamChoice(
        index=0,
        delta=DeltaMessage(role="assistant"),
        finish_reason=None
    )
    chunk = ChatCompletionResponse(model=model_id, choices=[choice_data], object="chat.completion.chunk")
    #yield "{}".format(chunk.json(exclude_unset=True, ensure_ascii=False))
    yield "{}".format(chunk.model_dump_json(exclude_unset=True))

    current_length = 0

    for new_response, _ in model.stream_chat(tokenizer, query, history):
        if len(new_response) == current_length:
            continue

        new_text = new_response[current_length:]
        current_length = len(new_response)

        choice_data = ChatCompletionResponseStreamChoice(
            index=0,
            delta=DeltaMessage(content=new_text),
            finish_reason=None
        )
        chunk = ChatCompletionResponse(model=model_id, choices=[choice_data], object="chat.completion.chunk")
        #yield "{}".format(chunk.json(exclude_unset=True, ensure_ascii=False))
        yield "{}".format(chunk.model_dump_json(exclude_unset=True))

    choice_data = ChatCompletionResponseStreamChoice(
        index=0,
        delta=DeltaMessage(),
        finish_reason="stop"
    )
    chunk = ChatCompletionResponse(model=model_id, choices=[choice_data], object="chat.completion.chunk")
    #yield "{}".format(chunk.json(exclude_unset=True, ensure_ascii=False))
    yield "{}".format(chunk.model_dump_json(exclude_unset=True))
    yield '[DONE]'



if __name__ == "__main__":
    tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm2-6b", revision="v1.0", trust_remote_code=True)
    model = AutoModel.from_pretrained("THUDM/chatglm2-6b", revision="v1.0", trust_remote_code=True).cuda()
    # 多显卡支持,使用下面两行代替上面一行,将num_gpus改为你实际的显卡数量
    # from utils import load_model_on_gpus
    # model = load_model_on_gpus("THUDM/chatglm2-6b", num_gpus=2)
    model.eval()

    uvicorn.run(app, host='0.0.0.0', port=8000, workers=1)

修改后的 openai_api.py 示意图:

服务端开启服务:

python openai_api.py

② .使用 Cloud Studio 快速创建 应用推荐 下的 ChatGPT Next Web 开源项目

打开 Cloud Studio 开发空间下,我们创建的项目,并停止服务。

成功后,打开 应用推荐 选择 ChatGPT Next Web 项目

选择 Fork

等待数秒后,工作空间创建完毕

③ .使用 Cloud Studio 快速配置并启动项目

Fork 完成后,选择 .env.template 文件

修改配置信息如下:

修改文件 后缀名.env

新建 终端命令

输入命令:

npm install 

依赖安装完成后,输入命令开启服务

yarn dev

点击 端口 ,可使用 浏览器或标签页 两种方式运行项目

如遇到这个报错提醒,可点击关闭,不影响体验

web浏览器测试:

Cloud Studio 标签页查看:

服务端可查看相关的请求记录:

停止 Cloud Studio 工作空间服务:

服务端停止服务:

5、高性能应用服务HAI 快速部署 ChatGLM2-6B-int4 本地模型及基于 P-Tuning v2 的微调

​ ① .使用 JupyterLab 快速部署 ChatGLM2-6B-int4 本地模型

​ 输入命令下载 ChatGLM2-6B-int4 相关源码:

仓库地址

cd ./ChatGLM2-6B
git clone https://gitee.com/dadac/chatglm2-6b-int4.git
# 输入你的gitee账号密码
cd chatglm2-6b-int4
  1. 在服务器上生成ssh密钥,在Jupyter终端中依次执行以下命令

    ssh-keygen -t ed25519 -C ""  #双引号里面填ssh key,一般填自己邮箱
    # 回车一直默认即可,会在家目录生成 ~/.ssh 文件夹,存放密钥信息
    cat ~/.ssh/id_ed25519.pub  # 查看生成的公钥
    

  2. 复制红色区域内容

  3. 打开gitee登录,并添加密钥

  4. 在Jupyter终端中依次执行以下命令

    git clone git@gitee.com:dadac/chatglm2-6b-int4.git
    # 输入yes 回车确认
    

代码下载完成:

由于访问外网下载速度很慢,这里我们使用清华大学云盘下载 chatglm2-6b-int4 的相关模型文件

注意:下载链接 存在时效问题,需要手动找一下文件真实地址,方式如下:

  1. 打开清华大学云盘
  2. 选择模型文件
    Alt text
    Alt text
  3. 打开浏览器控制台(window使用 F12 ,macOs使用command+option+i, 或者右键点击选择检查)
    Alt text
  4. 在控制台中选择网络选项卡(NetWork),筛选类型选择All,并清空当前网络请求记录
    Alt text
  5. 点击下载,在网络请求记录中找到文件下载链接并复制(防止给您的电脑产生垃圾文件,记得取消本地下载或删掉在您电脑上已下载的文件)
    Alt text
  6. 同样的方式找到 pytorch_model.bin tokenizer.model文件链接
    找到链接地址后在 JupyterLab 中输入命令:
rm pytorch_model.bin #删除旧文件
wget http://xxxxxx   # 复制的pytorch_model.bin 文件链接
rm tokenizer.model   #删除旧文件
wget http://xxxxxx   # 复制的tokenizer.model 文件链接

由于 pytorch_model.bin 模型文件较大下载大约需要13分钟左右,请您耐心等待。

完成下载后,修改代码来加载本地模型

返回到 ChatGLM2-6B 文件夹,找到 web_demo.py 文件,修改代码文件中第6、7行

tokenizer = AutoTokenizer.from_pretrained("./chatglm2-6b-int4", revision="v1.0", trust_remote_code=True)
model = AutoModel.from_pretrained("./chatglm2-6b-int4", revision="v1.0", trust_remote_code=True).half().cuda()

**如果您在实验之前已经关闭了相关服务,请忽略下面关闭 webui 功能的命令。**在web服务开启之前,为了优化服务器的性能,关闭 HAI 提供的 chatglm2_gradio webui,命令如下:

apt-get update && apt-get install sudo
sudo apt-get update  
sudo apt-get install psmisc
sudo fuser -k 6889/tcp #执行这条命令将关闭 HAI提供的 chatglm2_gradio webui功能

如果需要重新开启 webui 服务执行以下命令:

 python /root/ChatGLM2-6B/web_demo.py --listen --port 6889

关闭后输入命令可以启动web服务测试:

cd /root/ChatGLM2-6B
python web_demo.py --listen --port 8000

注意:为避免重复的配置,这里我们将端口配置为我们已经开启的 8000 端口

体验使用本地模型对话:

② .使用 JupyterLab 微调 ChatGLM2-6B 模型(基于 P-Tuning v2 ),创建个人的专属知识库

打开 JupyterLab 选择 ChatGLM2-6B 文件夹下的 ptuning 文件夹

添加训练集、验证集文件(您也可配置自己的训练集,添加的内容越多训练时间越长)

本地创建 train.json 文件

[	
	{
        "content": "你好,你是谁",
        "summary": "你好,我是腾讯云高性能应用HAI的助手小hai。"
    },
	{
        "content": "你是谁",
        "summary": "你好,我是HAI的助手小hai。"
    },
    {
        "content": "HAI是谁",
        "summary": "HAI是为开发者量身打造的澎湃算力平台。无需复杂配置,便可享受即开即用的GPU云服务体验。在HAI中,根据应用智能匹配并推选出最适合的GPU算力资源,以确保您在数据科学、LLM、AI作画等高性能应用中获得最佳性价比。"
    }
]

在目录 /ChatGLM2-6B/ptuning/ 下点击上传按钮将本地已保存的 train.json 文件上传至 ptuning 目录

同样用相同的方法 本地创建 verify.json 文件,并上传至 ptuning 目录

[	
	{
        "content": "你好,你是谁",
        "summary": "你好,我是腾讯云高性能应用HAI的助手小hai。"
    },
	{
        "content": "你是谁",
        "summary": "你好,我是HAI的助手小hai。"
    },
    {
        "content": "HAI是谁",
        "summary": "HAI是为开发者量身打造的澎湃算力平台。无需复杂配置,便可享受即开即用的GPU云服务体验。在HAI中,根据应用智能匹配并推选出最适合的GPU算力资源,以确保您在数据科学、LLM、AI作画等高性能应用中获得最佳性价比。"
    }
]

train.jsonverify.json 上传成功

选择 train_chat.sh 文件复制以下配置信息替换并保存文件

PRE_SEQ_LEN=128
LR=1e-2
NUM_GPUS=1

torchrun --standalone --nnodes=1 --nproc-per-node=$NUM_GPUS main.py \
    --do_train \
    --train_file train.json \
    --validation_file verify.json \
    --preprocessing_num_workers 10 \
    --prompt_column content \
    --response_column summary \
    --overwrite_cache \
    --model_name_or_path /root/ChatGLM2-6B/chatglm2-6b-int4 \
    --output_dir /root/ChatGLM2-6B/output/chatglm2-6b-int4−$LR \
    --overwrite_output_dir \
    --max_source_length 512 \
    --max_target_length 512 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --gradient_accumulation_steps 16 \
    --predict_with_generate \
    --max_steps 3000 \
    --logging_steps 10 \
    --save_steps 1000 \
    --learning_rate $LR \
    --pre_seq_len $PRE_SEQ_LEN \
    --quantization_bit 4
参数 描述
PRE_SEQ_LEN=128 定义了一个名为PRE_SEQ_LEN的变量,并将其设置为128。这个变量的作用在后续的代码中会用到
LR=2e-2 定义了一个名为LR的变量,并将其设置为2e-2,即0.02。这个变量表示学习率。
train_file 指定训练数据文件的路径和文件名为"train.json"。
validation_file 指定验证数据文件的路径和文件名为"verify.json"
prompt_column 指定输入数据中作为提示的列名为"content"
response_column 指定输入数据中作为响应的列名为"summary"
overwrite_cache 一个命令行参数,指示在缓存存在的情况下覆盖缓存
model_name_or_path 指定使用的模型的名称或路径为"/root/ChatGLM2-6B/chatglm2-6b-int4"
output_dir : 指定输出目录的路径和名称为"/root/ChatGLM2-6B/output/chatglm2-6b-int4−$LR "。这是训练结果和日志的保存位置。
overwrite_output_dir 一个命令行参数,指示在输出目录存在的情况下覆盖输出目录。
max_source_length 指定输入序列的最大长度为512
max_target_length 指定输出序列的最大长度为512
per_device_train_batch_size 指定每个训练设备的训练批次大小为1
per_device_eval_batch_size 指定每个评估设备的评估批次大小为1
gradient_accumulation_steps 指定梯度累积的步数为16。在每个更新步骤之前,将计算并累积一定数量的梯度。
predict_with_generate 一个命令行参数,指示在生成模型的预测时使用生成模式
max_steps 指定训练的最大步数为3000
logging_steps 指定每隔10个步骤记录一次日志
save_steps 指定每隔1000个步骤保存一次模型
learning_rate 指定学习率为之前定义的LR变量的值
pre_seq_len 指定预设序列长度为之前定义的PRE_SEQ_LEN变量的值
quantization_bit 指定量化位数为4。这个参数可能是与模型相关的特定设置。

保存相关后截图:

在正式微调之前 我们还需要安装以下依赖, 新建 终端命令窗口

输入命令安装依赖,这里我们使用 腾讯云 提供的镜像:

pip install -i https://mirrors.cloud.tencent.com/pypi/simple rouge_chinese nltk jieba datasets

安装完成之后 执行下面的命令进行训练:

cd ChatGLM2-6B/ptuning
sh train_chat.sh

模型开始训练,数据集越多耗时越长,目前测试的三条训练集、验证集大约需要1个小时左右:

训练完成:

​ ③ .使用 JupyterLab 测试微调后的模型

完成训练后 修改 ChatGLM2-6B 文件下的 web_demo.py 文件用来测试新训练的模型文件

w-760

修改 web_demo.py 文件

from transformers import AutoModel, AutoTokenizer,AutoConfig
import gradio as gr
import mdtex2html
import torch
import os
from utils import load_model_on_gpus


MODEL_PATH = "./chatglm2-6b-int4"
CHECKPOINT_PATH = "./output/chatglm2-6b-int4−1e-2/checkpoint-3000"
 
# 载入Tokenizer
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)
 
config = AutoConfig.from_pretrained(MODEL_PATH, trust_remote_code=True, pre_seq_len=128)
model = AutoModel.from_pretrained(MODEL_PATH, config=config, trust_remote_code=True).cuda()
 
prefix_state_dict = torch.load(os.path.join(CHECKPOINT_PATH, "pytorch_model.bin"))
new_prefix_state_dict = {}
 
for k, v in prefix_state_dict.items():
    if k.startswith("transformer.prefix_encoder."):
        new_prefix_state_dict[k[len("transformer.prefix_encoder."):]] = v
model.transformer.prefix_encoder.load_state_dict(new_prefix_state_dict)
 
print(f"Quantized to 4 bit")
model = model.quantize(4)
model = model.half().cuda()
model.transformer.prefix_encoder.float()
model = model.eval()

#tokenizer = AutoTokenizer.from_pretrained("./chatglm2-6b-int4", revision="v1.0", trust_remote_code=True)
#model = AutoModel.from_pretrained("./chatglm2-6b-int4", revision="v1.0", trust_remote_code=True).half().cuda()

# 多显卡支持,使用下面两行代替上面一行,将num_gpus改为你实际的显卡数量
# from utils import load_model_on_gpus
# model = load_model_on_gpus("THUDM/chatglm2-6B", num_gpus=2)
#model = model.eval()

"""Override Chatbot.postprocess"""


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, past_key_values):
    chatbot.append((parse_text(input), ""))
    for response, history, past_key_values in model.stream_chat(tokenizer, input, history, past_key_values=past_key_values,
                                                                return_past_key_values=True,
                                                                max_length=max_length, top_p=top_p,
                                                                temperature=temperature):
        chatbot[-1] = (parse_text(input), parse_text(response))

        yield chatbot, history, past_key_values


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


def reset_state():
    return [], [], None


if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("--listen", action='store_true',
                        help="launch gradio with 0.0.0.0 as server name, allowing to respond to network requests")
    parser.add_argument("--port", type=int,
                        help="launch gradio with given server port, you need root/admin rights for ports < 1024, defaults to 7860 if available",
                        default=None)
    args = parser.parse_args()
    server_name = "0.0.0.0" if args.listen else None
    server_port = args.port

    with gr.Blocks() as demo:
        gr.HTML("""<h1 align="center">ChatGLM2-6B</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, 32768, value=8192, step=1.0, label="Maximum length", interactive=True)
                top_p = gr.Slider(0, 1, value=0.8, step=0.01, label="Top P", interactive=True)
                temperature = gr.Slider(0, 1, value=0.95, step=0.01, label="Temperature", interactive=True)

        history = gr.State([])
        past_key_values = gr.State(None)

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

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

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

重新启动服务,命令:

python web_demo.py --listen --port 8000

训练前的:

训练后的:

w-760

五、销毁资源

1、清理 Cloud Studio 中创建的工作空间

2、销毁 高性能应用服务HAI 创建的 ChatGLM2 6B 服务

六、实验小结

本次实验主要是引导大家如何使用 高性能应用服务 HAI 部署 ChatGLM2-6B 运行环境进行AI对话并创作个人专属的知识宇宙,开箱即用,可以快速上手;同时,也使用了 Cloud Studio 快速开发部署简单的应用程序。最后也欢迎大家一起探索 高性能应用服务 HAI 更多的功能,为工作中赋能增效降本!

七、活动介绍

完成实验后填写调查问卷,将会额外获得10积分奖励

问卷调查地址https://jinshuju.net/f/hrU50x