使用Langchain,vLLM,FastAPI构建一个自托管的Qwen-7B-Chat

使用Langchain,vLLM,FastAPI构建一个自托管的Qwen-7B-Chat

在这里插入图片描述

自从2022年底chatgpt横空出世,ai的应用层出不穷。你是否希望可以通过一些流行的框架构建一个自己的llm(Large Language Model)系统,并将LLM投入生产?那么本文或许将符合你的要求。

本教程将逐步构建出一个简单的Demo,在过程中将使用Vllm进行模型推理,Langchain构建向量数据库,使用Fastapi提供Web服务,并在DeepLn平台实现模型的部署。

如何选择实例

运行深度学习模型特别是LLM需要大量的算力,虽然可以通过一些方法来使用cpu运行llm(llama.cpp),但一般来说需要使用GPU才可以流畅并高效地运行。对于本教程来说,vLLM目前支持Qwen 7B Chat的Int4量化版本(经过测试,截止到教程发布前不支持Int8量化),该版本最小运行显存为7GB,所以可以在类似3060这样显存>=8GB的显卡上运行。如果需要使用半精度推理,那么至少需要16.5GB显存,那么运行它就需要3090这样大显存的卡了。

由于vLLM并没有对量化模型进行优化,所以在示例中使用模型的未量化版本,以获得更好的准确性和更高的吞吐量。

启动实例并配置环境

启动实例

打开DeepLn官网,如果没有注册账号,可以先注册账号,现在注册账号并绑定微信送30算力金, DeepLn旨在为算力需求着们提供高性能、易于使用、性价比极高的算力服务,坚信GPU算力不应该成为算力需求者的科研障碍。话不多说,点击"算力市场",这里以A100为示例,点击"可用"进入选择主机界面,选择可用的主机,点击"立即租用",此时出现选择gpu数量和框架等的界面,参考配置如下
在这里插入图片描述
点击**“可用"进入选择主机界面,选择可用的主机,点击"立即租用”**,此时出现选择gpu数量和框架等的界面,参考配置如下
在这里插入图片描述

点击**“立即创建”,即可来到控制台,此时状态为"创建中”**
在这里插入图片描述
在创建结束后状态变为"运行中",此时即可通过code-server或者ssh访问实例。

配置环境

使用如下命令将pip源更换为国内源,加速包的安装:

cd ~
mkdir .pip
cd .pip
touch pip.conf
echo "[global]\nindex-url=https://pypi.tuna.tsinghua.edu.cn/simple">>pip.conf

然后通过如下命令安装依赖项:

pip install langchain vllm gptcache modelscope
pip install transformers==4.32.0 accelerate tiktoken einops scipy transformers_stream_generator==0.0.4 peft deepspeed

如果使用的是Int4版本的模型,还需要额外安装如下依赖项:

pip install auto-gptq optimum

下载模型并测试离线推理

在本教程中将使用Qwen-7B-Chat,以下为模型的官方介绍:

**通义千问-7B(Qwen-7B)**是阿里云研发的通义千问大模型系列的70亿参数规模的模型。Qwen-7B是基于Transformer的大语言模型, 在超大规模的预训练数据上进行训练得到。预训练数据类型多样,覆盖广泛,包括大量网络文本、专业书籍、代码等。同时,在Qwen-7B的基础上,我们使用对齐机制打造了基于大语言模型的AI助手Qwen-7B-Chat。相较于最初开源的Qwen-7B模型,我们现已将预训练模型和Chat模型更新到效果更优的版本。Github代码库

我们先测试离线LLM推理,然后再部署模型。只要从modelscpoe或者huggingface上将模型下载到本地,就可以无限进行推理。

from vllm import LLM, SamplingParams
import time
import os
#使用modelscope,如果不设置该环境变量,将会从huggingface下载
os.environ['VLLM_USE_MODELSCOPE']='True'

上面的代码导入了需要的库,并设置了”VLLM_USE_MODELSCOPE“这个环境变量为"True",这将会从modelscope而不是huggingface下载模型。如果需要从huggingface上下载可以将这行代码注释掉。
然后就可以下载模型了,只需要执行如下代码,就会自动从modelscope/huggingface下载到本地。

#无量化,最低显存占用约16.5GB
llm = LLM(model="qwen/Qwen-7B-Chat", trust_remote_code=True)
#int4量化,最低显存占用约7GB
# llm = LLM(model="qwen/Qwen-7B-Chat-int4", trust_remote_code=True,gpu_memory_utilization=0.35)

值得注意的是如果显存不够大,需要自行调整gpu_memory_utilization参数到一个合适的值,这个值会限制模型可以使用的最大显存(当然,给模型使用的显存必须大于最低值,否则无法成功加载)。
当你下载成功后,代码的输出应该类似于这样:

Downloading: 100%|██████████| 8.21k/8.21k [00:00<00:00, 12.1MB/s]
Downloading: 100%|██████████| 50.8k/50.8k [00:00<00:00, 1.39MB/s]
Downloading: 100%|██████████| 244k/244k [00:00<00:00, 3.02MB/s]
Downloading: 100%|██████████| 135k/135k [00:00<00:00, 1.74MB/s]
Downloading: 100%|██████████| 910/910 [00:00<00:00, 3.00MB/s]
Downloading: 100%|██████████| 77.0/77.0 [00:00<00:00, 317kB/s]
Downloading: 100%|██████████| 2.29k/2.29k [00:00<00:00, 8.67MB/s]
Downloading: 100%|██████████| 1.88k/1.88k [00:00<00:00, 7.11MB/s]
Downloading: 100%|██████████| 249/249 [00:00<00:00, 1.05MB/s]
Downloading: 100%|██████████| 1.63M/1.63M [00:00<00:00, 12.8MB/s]
Downloading: 100%|██████████| 1.84M/1.84M [00:00<00:00, 12.7MB/s]
Downloading: 100%|██████████| 2.64M/2.64M [00:00<00:00, 17.7MB/s]
Downloading: 100%|██████████| 6.73k/6.73k [00:00<00:00, 1.29MB/s]
Downloading: 100%|██████████| 80.8k/80.8k [00:00<00:00, 2.01MB/s]
Downloading: 100%|██████████| 80.8k/80.8k [00:00<00:00, 2.22MB/s]
Downloading: 100%|█████████▉| 1.83G/1.83G [00:22<00:00, 87.2MB/s]
Downloading: 100%|█████████▉| 1.88G/1.88G [00:20<00:00, 99.9MB/s]
Downloading: 100%|█████████▉| 1.88G/1.88G [00:26<00:00, 77.0MB/s]
Downloading: 100%|█████████▉| 1.88G/1.88G [00:27<00:00, 74.3MB/s]
Downloading: 100%|█████████▉| 1.88G/1.88G [00:25<00:00, 79.9MB/s]
...
Downloading: 100%|██████████| 41.9k/41.9k [00:00<00:00, 1.12MB/s]
Downloading: 100%|██████████| 230k/230k [00:00<00:00, 2.84MB/s]
Downloading: 100%|██████████| 1.27M/1.27M [00:00<00:00, 11.0MB/s]
Downloading: 100%|██████████| 664k/664k [00:00<00:00, 6.90MB/s]
Downloading: 100%|██████████| 404k/404k [00:00<00:00, 5.16MB/s]

接下来测试模型的推理:

prompts = [
'''
Let's think step by step:
将大象塞到冰箱里面有几个步骤?
'''
]

sampling_params = SamplingParams(temperature=0.8,top_k=10, top_p=0.95,max_tokens=256,stop=["<|endoftext|>","<|im_end|>"])
start_time = time.time()
outputs = llm.generate(prompts, sampling_params)
end_time = time.time()
latency = end_time - start_time
print(f"Latency: {latency} seconds")
# Print the outputs.
for output in outputs:
    prompt = output.prompt
    generated_text = output.outputs[0].text
    print(f"Prompt: {prompt} \nGenerated text: \n{generated_text}")

这里有很多参数,比如temperature,top_k等,如果你想详细了解,可以查看huggingface手册

执行如上代码,输出如下:

Processed prompts: 100%|██████████| 1/1 [00:00<00:00,  3.27it/s]
Latency: 0.30954647064208984 seconds
Prompt: 
Let's think step by step:
将大象塞到冰箱里面有几个步骤?
 
Generated text: 
首先,打开冰箱门。其次,将大象塞入冰箱。最后,关闭冰箱门。

可以看到A100只用了约0.3秒就完成了推理,速度非常快,并且正确回答了问题。

使用FastAPI启动web服务并进行推理

既然我们已经部署了模型,尝试了离线推理,让我们开始使用FastAPI构建的API,它将处理请求并使用已部署的模型生成LLM响应。

以下代码将启动FastAPI Python应用程序,并在/v1/generateText API上托管LLM模型,它会在端口5001上启动API。如果之前的离线推理部分已经完成了LLM模型下载,则此处不会重新下载LLM模型。

from vllm import LLM, SamplingParams
import os
from fastapi import BackgroundTasks, FastAPI, Request
from fastapi.responses import JSONResponse, Response, StreamingResponse
from fastapi import FastAPI
from vllm import LLM, SamplingParams
import uvicorn
#使用modelscope,如果不设置该环境变量,将会从huggingface下载
os.environ['VLLM_USE_MODELSCOPE']='True'

app = FastAPI()

llm = LLM(model="qwen/Qwen-7B-Chat", trust_remote_code=True)
sampling_params = SamplingParams(temperature=0.8,top_k=10, top_p=0.95,max_tokens=256,stop=["<|endoftext|>","<|im_end|>"])

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.post("/v1/generateText")
async def generateText(request: Request) -> Response:
    request_dict = await request.json()
    prompt = request_dict.pop("prompt")
    prompt=[f'''
    {prompt}
            '''
            ]
    print(prompt)
    output = llm.generate(prompt,sampling_params)
    generated_text=output[0].outputs[0].text
    print("Generated text:", generated_text)
    # ret = {"text": str(generated_text)}
    return JSONResponse(generated_text)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=5001)

运行上面的 python 程序,它的输出应该类似:

(torch) ➜  demo python server.py
2023-12-27 07:25:02,050 - modelscope - INFO - PyTorch version 2.1.2 Found.
2023-12-27 07:25:02,050 - modelscope - INFO - Loading ast index from /home/user/.cache/modelscope/ast_indexer
2023-12-27 07:25:02,084 - modelscope - INFO - Loading done! Current index file version is 1.10.0, with md5 0618eb10d9c919cba5f8a841e5fab225 and a total number of 946 components indexed
2023-12-27 07:25:03,004 - modelscope - WARNING - Model revision not specified, use revision: v1.1.9
INFO 12-27 07:25:03 llm_engine.py:73] Initializing an LLM engine with config: model='/home/user/.cache/modelscope/hub/qwen/Qwen-7B-Chat', tokenizer='/home/user/.cache/modelscope/hub/qwen/Qwen-7B-Chat', tokenizer_mode=auto, revision=None, tokenizer_revision=None, trust_remote_code=True, dtype=torch.float16, max_seq_len=8192, download_dir='/home/user/.cache/modelscope/hub/qwen/Qwen-7B-Chat', load_format=auto, tensor_parallel_size=1, quantization=None, enforce_eager=False, seed=0)
WARNING 12-27 07:25:04 tokenizer.py:62] Using a slow tokenizer. This might cause a significant slowdown. Consider using a fast tokenizer instead.
INFO 12-27 07:25:20 llm_engine.py:223] # GPU blocks: 2427, # CPU blocks: 512
INFO 12-27 07:25:21 model_runner.py:394] Capturing the model for CUDA graphs. This may lead to unexpected consequences if the model is not static. To run the model in eager mode, set 'enforce_eager=True' or use '--enforce-eager' in the CLI.
INFO 12-27 07:25:26 model_runner.py:437] Graph capturing finished in 5 secs.
INFO:     Started server process [30399]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:5001 (Press CTRL+C to quit)

可以使用以下方法像已经启动的服务API发送请求:

import requests
import json
import time

# Define the API endpoint
url = "http://0.0.0.0:5001/v1/generateText"

headers = {"Content-Type": "application/json"}

prompt =''' 
Let's think step by step:
将大象塞到冰箱里面有几个步骤?
'''
data = {"prompt": prompt}


start_time = time.time()
# Make the POST request
response = requests.post(url, headers=headers, data=json.dumps(data))
end_time = time.time()
latency = end_time - start_time
print(f"Latency: {latency} seconds")
text=json.loads(response.text)
print("LLM response: " +text["text"] )

我的返回结果如下:

(torch) ➜  demo python client.py
Latency: 0.42313146591186523 seconds
LLM response:  1. 打开冰箱门
             2. 将大象塞进去
             3. 关闭冰箱门

网络部分略微增加了延迟(0.1s),但总体延迟依然很低。

使用langchain增加知识库

在这个示例中,我们需要使用FAISS库,可以通过下面的命令安装gpu版本:

pip install faiss-gpu

安装cpu版本的命令如下:

pip install faiss-cpu

同时为了将知识存入向量数据库,我们还需要embedding模型,使用以下命令安装embedding模型,就像之前的llm一样,这里的embedding模型也会自动下载:

from langchain.vectorstores import FAISS
from langchain.embeddings import ModelScopeEmbeddings
model_id = "damo/nlp_corom_sentence-embedding_english-base"
embeddings = ModelScopeEmbeddings(model_id=model_id)

加载完毕之后我们就可以导入知识了,这里的知识以list的形式导入:

knowledges=["DeepLN致力于提供高性价比的GPU租赁。"]
vectorstore = FAISS.from_texts(
    knowledges, embedding=embeddings
)
retriever = vectorstore.as_retriever()

调用向量数据库

retriever.invoke("GPU租用选那家?")[0].page_content

输出如下:

'哪里可以租到高性价比的显卡?DeepLN!'

现在将向量数据库添加到之前的代码中,并提示LLM注意背景知识:

from vllm import LLM, SamplingParams
import os
from fastapi import BackgroundTasks, FastAPI, Request
from fastapi.responses import JSONResponse, Response, StreamingResponse
from fastapi import FastAPI
from vllm import LLM, SamplingParams
import uvicorn
from langchain.vectorstores import FAISS
from langchain.embeddings import ModelScopeEmbeddings

# 使用modelscope,如果不设置该环境变量,将会从huggingface下载
os.environ['VLLM_USE_MODELSCOPE'] = 'True'

app = FastAPI()

llm = LLM(model="qwen/Qwen-7B-Chat", trust_remote_code=True,)
sampling_params = SamplingParams(temperature=0.8, top_k=10, top_p=0.95, max_tokens=256, stop=[
                                 "<|endoftext|>"])

embedding_model_id = "damo/nlp_corom_sentence-embedding_english-base"
embeddings = ModelScopeEmbeddings(model_id=embedding_model_id)
knowledges = ["DeepLN致力于提供高性价比的GPU租赁。"]
vectorstore = FAISS.from_texts(
    knowledges, embedding=embeddings
)
retriever = vectorstore.as_retriever()

system_ptompt="你是一个有用的机器人,会根据背景知识回答我的问题。"



@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.post("/v1/generateText")
async def generateText(request: Request) -> Response:
    request_dict = await request.json()
    prompt = request_dict.pop("prompt")
    print("prompt:",prompt)
    background = retriever.invoke(prompt)[0].page_content
    print("background:",background)

    input=prompt = [str(f'''\
    {system_ptompt+"背景知识:"+background+prompt}''')
              ]
    output = llm.generate(input, sampling_params)
    generated_text = output[0].outputs[0].text
    generated_text=generated_text.replace("<|im_start|>","").replace("<|im_end|>","")
    print("Generated text:", generated_text)
    # ret = {"text": str(generated_text)}
    return JSONResponse(generated_text)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=5001)

依然通过和之前相同的方式启动API,我们再次尝试发送HTTP请求:

import requests
import json
import time

# Define the API endpoint
url = "http://0.0.0.0:5001/v1/generateText"

headers = {"Content-Type": "application/json"}

prompt = ''' \
Let's think step by step:
哪里可以租到高性价比的GPU?
'''
data = {"prompt": prompt}


start_time = time.time()
# Make the POST request
response = requests.post(url, headers=headers, data=json.dumps(data))
end_time = time.time()
latency = end_time - start_time
print(f"Latency: {latency} seconds")
text = json.loads(response.text)
print("LLM response: " + text)

得到的输出如下:

(torch) ➜  lecture0 git:(main) ✗ python client.py
Latency: 10.097851753234863 seconds
LLM response: 't think twice, DeepLN is your best bet!

模型根据我们提供的背景知识给出了回答。

如果希望在服务器上运行的模型向外界提供服务,可以使用ssh端口转发或者反向代理将流量从80/443端口转发到5001端口,这样可以在1s内获得HTTP响应。

好的,以上就是本篇教程的全部内容了,如果你觉得这个教程对你有用,别忘了点赞并关注DeepLN官方账号,我们会在这里更新各种有用的教程和博客。
我们下次再见,祝你使用AI愉快。

  • 38
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值