Llama 3.1:本地部署

[1] Llama 3.1部署教程(非常详细)从零基础入门到精通,看完这一篇就够了
[2] 科学安装 Ollama
[3] Ollama在Linux系统下配置国内镜像源加速模型下载
[4] Llama 3.1 介绍与部署流程、高效微调
部署服务器:H100 80G
模型:Llama-3.1-8B-Instruct

一、本地部署模型

通过huggingface下载模型:https://huggingface.co/meta-llama/Llama-3.1-8B

  1. 创建conda虚拟环境(python版本3.10以上)
conda create -n 环境名称 python==3.11
  1. 激活环境
  2. 在虚拟环境中安装Pytorch
nvidia-smi # 查看CUDA版本
进入Pytorch官网:https://pytorch.org/get-started/previous-versions/
选择适合的Pytorch版本,选择版本接近且不大于主机CUDA所支持的最高版本即可,然后使用镜像源
如: # 使用清华镜像源
pip install torch2.6.0 torchvision0.21.0 torchaudio==2.6.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
  1. 升级pip
python -m pip install --upgrade pip
  1. 检查和更新wget和md5sum
wget --version
md5sum --version

如果没有该工具,可以通过以下指令进行安装:

apt-get install wget
apt-get install md5sum
  1. 按照transformer
pip install --upgrade transformers
pip install accelerate -i https://pypi.tuna.tsinghua.edu.cn/simple

7.测试:

import transformers import torch model_id ="meta-llama/Meta-Llama-3.1-8B-Instruct"# 修改地址 pipeline = transformers.pipeline("text-generation", model=model_id, model_kwargs={"torch_dtype": torch.bfloat16}, device_map="auto",) messages =[{"role":"system","content":"You are a pirate chatbot who always responds in pirate speak!"},{"role":"user","content":"Who are you?"},] outputs = pipeline( messages, max_new_tokens=256,)print(outputs[0]["generated_text"][-1])
在这里插入图片描述

二、本地部署长期运行的API服务

  1. 安装所需包
pip install fastapi uvicorn pydantic -i https://pypi.tuna.tsinghua.edu.cn/simple
  1. 创建API服务文件
# api_server.pyimport torch from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List, Optional import uvicorn import logging # 配置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__)# 模型路径 MODEL_PATH ="/.../.../.../Llama-3.1-8B-Instruct"# 修改为自己的模型路径# 定义请求/响应模型classChatMessage(BaseModel): role:str content:strclassChatRequest(BaseModel): messages: List[ChatMessage] max_tokens: Optional[int]=200 temperature: Optional[float]=0.7 top_p: Optional[float]=0.9classChatResponse(BaseModel): response:str usage:dict# 初始化FastAPI应用 app = FastAPI(title="Llama 3.1 API", version="1.0")# 全局模型变量 model =None tokenizer =None pipe [email protected]_event("startup")asyncdefstartup_event():"""启动时加载模型"""global model, tokenizer, pipe logger.info("正在加载模型...")try:# 加载tokenizer和model tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH) model = AutoModelForCausalLM.from_pretrained( MODEL_PATH, torch_dtype=torch.float16, device_map="auto", low_cpu_mem_usage=True)# 创建pipeline pipe = pipeline("text-generation", model=model, tokenizer=tokenizer, device=0if torch.cuda.is_available()else-1) logger.info(f"模型加载完成!设备: {model.device}") logger.info(f"CUDA可用: {torch.cuda.is_available()}")except Exception as e: logger.error(f"模型加载失败: {e}")[email protected]("/")asyncdefroot():"""根端点,返回服务状态"""return{"service":"Llama 3.1 API","status":"running","model":"Llama-3.1-8B-Instruct","device":str(model.device)if model else"未加载"}@app.get("/health")asyncdefhealth_check():"""健康检查"""return{"status":"healthy"}@app.post("/chat/completions", response_model=ChatResponse)asyncdefchat_completions(request: ChatRequest):"""聊天补全接口(类似OpenAI API格式)"""try:# 使用聊天模板格式化 text = tokenizer.apply_chat_template([msg.dict()for msg in request.messages], tokenize=False, add_generation_prompt=True)# 生成回复 outputs = pipe( text, max_new_tokens=request.max_tokens, temperature=request.temperature, top_p=request.top_p, do_sample=True, pad_token_id=tokenizer.eos_token_id ) response_text = outputs[0]['generated_text']# 提取助手回复if"assistant"in response_text: response_text = response_text.split("assistant")[-1].strip()else:# 移除输入文本 response_text = response_text.replace(text,"").strip()# 计算token使用量 input_tokens =len(tokenizer.encode(text)) output_tokens =len(tokenizer.encode(response_text))return ChatResponse( response=response_text, usage={"prompt_tokens": input_tokens,"completion_tokens": output_tokens,"total_tokens": input_tokens + output_tokens })except Exception as e: logger.error(f"生成失败: {e}")raise HTTPException(status_code=500, detail=str(e))@app.post("/generate")asyncdefgenerate_text(prompt:str, max_tokens:int=100):"""简单的文本生成接口"""try: outputs = pipe( prompt, max_new_tokens=max_tokens, temperature=0.7, do_sample=True)return{"text": outputs[0]['generated_text'],"prompt": prompt }except Exception as e:raise HTTPException(status_code=500, detail=str(e))if __name__ =="__main__":# 启动服务器 uvicorn.run( app, host="0.0.0.0",# 允许外部访问 port=8000, log_level="info")
  1. 启动API服务
前台运行(查看日志)
python api_server.py
  1. 测试API
# 测试端点 curl http://localhost:8000/ # 健康检查 curl http://localhost:8000/health # 使用聊天接口 curl -X POST "http://localhost:8000/chat/completions" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "system", "content": "你是一个有用的AI助手"}, {"role": "user", "content": "中国的首都是哪里?"} ], "max_tokens": 100 }' # 简单生成接口 curl -X POST "http://localhost:8000/generate?prompt=Hello&max_tokens=50" 
在这里插入图片描述

三、跨服务器部署与使用的API服务

1. 服务器端

H100服务器端(部署API)

# h100_server.pyimport torch from transformers import AutoModelForCausalLM, AutoTokenizer from fastapi import FastAPI, HTTPException from pydantic import BaseModel, field_validator, model_validator, ConfigDict from typing import List, Optional import uvicorn import logging import time import os from contextlib import asynccontextmanager # 日志配置 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__)# 模型路径 MODEL_PATH ="/dddd/ddddd/LLMs/Llama-3.1-8B-Instruct"# 全局模型变量 model =None tokenizer =None@asynccontextmanagerasyncdeflifespan(app: FastAPI):# 启动时加载模型global model, tokenizer logger.info("正在 H100 上加载 Llama-3.1-8B-Instruct 模型...")try: tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)if tokenizer.pad_token isNone: tokenizer.pad_token = tokenizer.eos_token model = AutoModelForCausalLM.from_pretrained( MODEL_PATH, torch_dtype=torch.float16, device_map="auto", low_cpu_mem_usage=True) logger.info(f"✅ 模型加载成功!设备: {model.device}")yieldexcept Exception as e: logger.error(f"❌ 模型加载失败: {e}")raise RuntimeError("模型初始化失败")from e finally:# 清理资源if model isnotNone:del model torch.cuda.empty_cache() app = FastAPI( title="Llama 3.1 Instruct API", description="OpenAI-compatible API for Llama-3.1-8B-Instruct on H100", version="1.0", lifespan=lifespan )# ======================# Pydantic Models# ======================classChatMessage(BaseModel): role:str content:strclassChatCompletionRequest(BaseModel): model_config = ConfigDict(extra="ignore") messages: Optional[List[ChatMessage]]=None prompt: Optional[str]=None model:str="llama-3.1-8b" max_tokens:int=200 temperature:float=0.7 top_p:float=0.9 stream:bool=False@model_validator(mode='after')defvalidate_input(self):ifnot self.messages andnot self.prompt:raise ValueError("必须提供 'messages' 或 'prompt'")if self.prompt andnot self.messages:# 兼容旧版:将 prompt 转为 messages self.messages =[ChatMessage(role="user", content=self.prompt)]return self @field_validator('max_tokens')@classmethoddefvalidate_max_tokens(cls, v):if v <1or v >2048:raise ValueError("max_tokens 必须在 1~2048 之间")return v classChatCompletionResponseChoice(BaseModel): index:int=0 message: ChatMessage finish_reason:str="stop"classChatCompletionResponse(BaseModel):id:strobject:str="chat.completion" created:int model:str choices: List[ChatCompletionResponseChoice] usage:dict# ======================# Routes# [email protected]("/")asyncdefroot():return{"service":"Llama 3.1 Instruct API","status":"running","device":str(model.device)if model else"uninitialized"}@app.get("/health")asyncdefhealth_check():return{"status":"healthy","model_loaded": model isnotNone}@app.post("/v1/chat/completions")asyncdefchat_completions(request: ChatCompletionRequest):if model isNoneor tokenizer isNone:raise HTTPException(status_code=503, detail="模型尚未加载完成")if request.stream:raise HTTPException(status_code=400, detail="流式输出暂不支持")try:# 准备输入if request.messages isNone:raise ValueError("messages 不能为空")# 确保 messages 是字典列表格式 messages_dict =[msg.model_dump()for msg in request.messages]# 使用 apply_chat_template encoding = tokenizer.apply_chat_template( messages_dict, add_generation_prompt=True, return_tensors="pt")# 提取 input_ids input_ids = encoding.input_ids.to(model.device) input_length = input_ids.shape[1]# 生成 start_time = time.time()with torch.no_grad(): outputs = model.generate( input_ids=input_ids, max_new_tokens=request.max_tokens, temperature=request.temperature, top_p=request.top_p, do_sample=True, pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id, use_cache=True) gen_time = time.time()- start_time # 解码新生成部分 new_tokens = outputs[0][input_length:] response_text = tokenizer.decode(new_tokens, skip_special_tokens=True).strip()# 构造响应 response = ChatCompletionResponse(id=f"cmpl-{int(time.time())}", created=int(time.time()), model=request.model, choices=[ ChatCompletionResponseChoice( message=ChatMessage(role="assistant", content=response_text))], usage={"prompt_tokens": input_length,"completion_tokens":len(new_tokens),"total_tokens": input_length +len(new_tokens)}) logger.info(f"✅ 生成完成 | 输入: {len(request.messages)} 条消息 | 输出: {len(response_text)} 字符 | 耗时: {gen_time:.2f}s")return response except Exception as e: logger.error(f"❌ 生成错误: {e}", exc_info=True)raise HTTPException(status_code=500, detail=str(e))if __name__ =="__main__": port =int(os.getenv("PORT",8000)) uvicorn.run( app, host="0.0.0.0", port=port, log_level="info", workers=1)

H100启动API服务

# 前台运行(查看日志) python h100_server.py 

服务器本地测试

# quick_test.pyfrom transformers import AutoTokenizer MODEL_PATH ="/ddd/ddddd/LLMs/Llama-3.1-8B-Instruct" tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH) messages =[{"role":"system","content":"你是一个 helpful AI 助手。"},{"role":"user","content":"你好,请介绍一下你自己。"}]print("测试修复后的逻辑...") encoding = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")print(f"encoding 类型: {type(encoding)}")print(f"encoding.input_ids 类型: {type(encoding.input_ids)}")print(f"encoding.input_ids.shape: {encoding.input_ids.shape}")print("✅ 测试通过!")
(llama_3_1) root@f2osdcap97m-0:/ddd/qddyj/LLMs# python test.py
测试修复后的逻辑…
encoding 类型: <class ‘transformers.tokenization_utils_base.BatchEncoding’>
encoding.input_ids 类型: <class ‘torch.Tensor’>
encoding.input_ids.shape: torch.Size([1, 51])
✅ 测试通过!

2. 客户端

部署环境

conda create -n llama3 python=3.11 conda activate llama3 

(1) 在一个终端建立 SSH 隧道(端口转发)

ssh -L 8000:localhost:8000 [email protected] -p 30304 # 115.11.11.111远程服务器的ip

测试是否连接服务器

netstat -tuln | grep 8000

有输出则为连接成功

tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN tcp6 0 0 ::1:8000 :::* LISTEN 

(2) 在另一个终端运行 Python 脚本

python ask_llm.py
# client_llama3.pyfrom openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1",# 这里保持 /v1 api_key="not-needed") response = client.chat.completions.create( model="llama-3.1-8b", messages=[{"role":"system","content":"你是一个 helpful AI 助手。"},{"role":"user","content":"你好,请介绍一下你自己。"}], max_tokens=150)print(response.choices[0].message.content)

回答如下:

(llama3) rd-111s@rd-111s-Z790-PG-ITX-TB4:~/document/ddd/LLM$ python client_llama3.py 你好!我是 LLaMA,一个人工智能助手。我的名字来源于“Large Language Model Application”,我是一种语言模型,能理解和生成人类语言。 我能够回答各种问题,提供信息,帮助你完成任务,甚至可以进行创作和对话。我的知识范围广泛,涵盖各个领域,包括但不限于科技、历史、文化、娱乐等。 我是一种学习型的模型,能不断学习和改进。通过与用户的互动,我能够提高我的理解力和生成能力,提供更好的服务。 我希望能成为你的一个有用的助手,帮助你解决问题,找到答案,甚至带来一些乐趣 

新的问题 1:服务器端口被占用或者客户端无法通过ssh连接到服务器

解决方法1: 服务器端换个端口port

if __name__ == "__main__": # 启动服务器 uvicorn.run( app, host="0.0.0.0", # 允许外部访问 port=8000, log_level="info" ) 

解决方法2: 停止所有相关进程

# 检查8000端口 (llama_3_1) root@f2osdcap97m-0:/ddd/dd/LLMs# netstat -tulpn | grep :8000 tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN 1845141/ssh 杀掉进程 > kill -9 1845141 

更换建立终端方式:
打开一个新的终端窗口,执行

# 在本地机器的新终端中执行 ssh -4 -N -L 8000:localhost:8000 [email protected] -p 30304 # 即在原来的 ssh [email protected] -p 30304的shh后面加一个-4 -N -L 8000:localhost:8000 

然后输入密码后可能没有输出的内容,不用关这个终端,另外打开一个进行测试

(llama3) rd-111s@rd-111s-Z790-PG-ITX-TB4:~/document/ddd/code/SGRL (v2)$ netstat -tuln | grep 8000 tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN (llama3) rd-111s@rd-111s-Z790-PG-ITX-TB4:~/document/ddd/code/SGRL (v2)$ 

连接成功

新的问题2 :ssh连接不稳定

解决方法:创建一个脚本,断了重连
使用方法:

# 设置环境变量exportSSH_TUNNEL_PASSWORD="your_password"# 运行脚本 ./ssh_tunnel.sh 

脚本内容如下:

#!/bin/bash# SSH隧道参数SSH_HOST="[email protected]"# 用户名@服务器IP地址SSH_PORT="111111"# SSH服务的端口号LOCAL_PORT="8000"# 本地监听的端口REMOTE_HOST="localhost"# 这里的localhost指的是远程服务器本身REMOTE_PORT="8000"# 远程服务器上目标服务的端口# 从环境变量读取密码SSH_PASSWORD="${SSH_TUNNEL_PASSWORD}"# 检查密码是否设置if[-z"$SSH_PASSWORD"];thenecho"错误:请设置SSH_TUNNEL_PASSWORD环境变量"echo"使用: export SSH_TUNNEL_PASSWORD='your_password'"exit1fi# 日志文件LOG_FILE="ssh_tunnel.log"# 自己设置路径PID_FILE="ssh_tunnel.pid"# 自己设置路径# 函数:检查端口是否在监听check_port(){ifcommand-vnc&> /dev/null;thennc-z localhost $LOCAL_PORTelifcommand-v telnet &> /dev/null;thenecho""| telnet localhost $LOCAL_PORT2>&1|grep-q"Connected"else ss -tln|grep-q":$LOCAL_PORT"fi}# 函数:启动SSH隧道start_tunnel(){echo"$(date): 启动SSH隧道...">>"$LOG_FILE"if!command-v sshpass &> /dev/null;thenecho"$(date): 错误:未安装sshpass">>"$LOG_FILE"return1fiifcommand-v autossh &> /dev/null;then sshpass -p"$SSH_PASSWORD" autossh -M0-4-N-L${LOCAL_PORT}:${REMOTE_HOST}:${REMOTE_PORT}${SSH_HOST}-p${SSH_PORT}\-o"ServerAliveInterval=30"\-o"ServerAliveCountMax=3"\-o"ExitOnForwardFailure=yes"\-o"StrictHostKeyChecking=no"&echo$!>"$PID_FILE"else sshpass -p"$SSH_PASSWORD"ssh-4-N-L${LOCAL_PORT}:${REMOTE_HOST}:${REMOTE_PORT}${SSH_HOST}-p${SSH_PORT}\-o"ServerAliveInterval=30"\-o"ServerAliveCountMax=3"\-o"ExitOnForwardFailure=yes"\-o"StrictHostKeyChecking=no"&echo$!>"$PID_FILE"fisleep3}# 函数:停止SSH隧道stop_tunnel(){if[-f"$PID_FILE"];thenPID=$(cat"$PID_FILE")ifkill-0$PID2>/dev/null;thenecho"$(date): 停止现有SSH隧道 (PID: $PID)">>"$LOG_FILE"kill$PIDsleep2firm-f"$PID_FILE"fi# 确保所有ssh进程都被清理pkill-f"ssh.*-L ${LOCAL_PORT}:${REMOTE_HOST}:${REMOTE_PORT}"2>/dev/null }# 清理函数cleanup(){echo"$(date): 接收到退出信号,清理进程...">>"$LOG_FILE" stop_tunnel exit0}# 设置信号处理trap cleanup SIGINT SIGTERM # 主循环echo"$(date): SSH隧道监控脚本启动">>"$LOG_FILE"echo"$(date): 目标: ${SSH_HOST}:${SSH_PORT}">>"$LOG_FILE"echo"$(date): 本地转发: ${LOCAL_PORT} -> ${REMOTE_HOST}:${REMOTE_PORT}">>"$LOG_FILE"whiletrue;do# 检查隧道是否运行if! check_port;thenecho"$(date): 隧道未运行或端口不可用,重新启动...">>"$LOG_FILE" stop_tunnel start_tunnel fi# 每隔10秒检查一次sleep10done

Read more

PX4无人机|MID360使用FAST_LIO,实现自主飞行及定点——PX4无人机配置流程(六)

PX4无人机|MID360使用FAST_LIO,实现自主飞行及定点——PX4无人机配置流程(六)

PX4固件版本为1.15.4 qgc地面站版本为4.4.5 飞控,使用微空科技MicoAir743V2 机载电脑:12代i5,ubuntu20.04 安装位置:mid360的接口对应飞机的后方 推荐阅读px4+vio实现无人机室内定位_px4+室内视觉定位-ZEEKLOG博客 和飞控连接机载电脑相关,有用 代码参考: PX4|基于FAST-LIO mid360的无人机室内自主定位及定点悬停_fastlio mid360-ZEEKLOG博客 使用视觉或动作捕捉系统进行位置估计 | PX4 指南(主) --- Using Vision or Motion Capture Systems for Position Estimation | PX4 Guide (main) 一.px4飞控设置 建议看官方文档:Using Vision or Motion

数字频率计设计在FPGA上的优化策略

FPGA上的数字频率计设计:从原理到实战的系统优化 你有没有遇到过这样的场景?手头有个信号发生器,输出一个未知频率的方波,想快速测出它的频率。用万用表?不行,普通万用表不支持高频测量。拿示波器看周期?可以,但操作繁琐、响应慢。这时候,一台高精度、响应快的 数字频率计 就显得尤为重要。 而在现代电子系统中,基于FPGA实现的数字频率计,正逐渐取代传统单片机或专用IC方案,成为高性能测量设备的核心选择。为什么?因为FPGA不仅具备并行处理能力,还能灵活重构逻辑结构,尤其适合对实时性、精度和动态范围都有严苛要求的应用。 但问题来了: 有了FPGA,是不是随便写个计数器就能搞定频率测量? 答案是否定的。 很多初学者在FPGA上做频率计时,常犯几个致命错误:直接把待测信号当钟用、跨时钟域数据没同步、低频误差大得离谱……结果要么烧板子,要么测不准,甚至系统频繁崩溃。 本文将带你深入剖析 数字频率计在FPGA平台上的完整设计链条 ,从基础原理出发,聚焦实际工程中的关键瓶颈,提出一套可落地的优化策略。我们不堆术语,只讲“人话”;不罗列理论,只谈你在调试时真正会踩的坑和对应的解法。 闸门

飞书机器人实战:5分钟搞定图片消息发送(含常见报错解决方案)

飞书机器人实战:5分钟搞定图片消息发送(含常见报错解决方案) 你是否遇到过这样的场景:服务器监控系统捕捉到一个异常峰值,你希望它能自动将一张清晰的图表截图,直接推送到团队的飞书群里,而不是一封冰冷的邮件;或者,你的自动化日报系统生成了精美的数据可视化图片,你希望它能无缝地出现在每日的晨会通知中。对于许多开发者和运维工程师来说,将图片消息集成到自动化流程中,是一个能极大提升信息传达效率和体验的“刚需”。 飞书机器人提供了强大的消息推送能力,但初次接触其图片消息发送功能时,你可能会发现它比预想的要“曲折”一些——它不像发送文本那样直接丢一个图片链接就行,而是需要经过一个“上传-获取密钥-发送”的流程。这个过程里,权限配置、tenant_access_token获取、图片上传格式、image_key的使用,每一步都可能藏着一个小坑。别担心,这篇文章就是为你准备的“避坑指南”。我们将抛开官方文档那略显冰冷的步骤罗列,从一个实战者的角度,带你用大约5分钟的时间,彻底打通从零到一发送飞书图片消息的全链路,并重点剖析那些你可能马上就会遇到的报错及其根因解决方案。我们的目标是:让你看完就能用,用了

【大模型应用篇】用 OpenClaw + 飞书打造 7x24 小时服务器运维机器人

【大模型应用篇】用 OpenClaw + 飞书打造 7x24 小时服务器运维机器人

前言 本文基于OpenClaw,也是最近超火的可在本地运行的AI Agent网关,记录从零搭建通过飞书对话管理服务器运维机器人的全过程。该机器人支持随时随地通过飞书查看服务器状态、检索日志、管理进程,其核心机制在于:由OpenClaw将聊天平台(飞书等)的消息路由至大模型,模型调用本地工具(如Shell、文件系统、浏览器)执行相应任务,最终将结果自动返回至飞书会话中,实现自动化运维交互。 架构概览 飞书 App (WebSocket 长连接)         ↕ OpenClaw Gateway (服务器上 systemd 常驻)         ↕ AI 模型 (DeepSeek v3.2/GLM 4.7)         ↕ 服务器 Shell (受白名单限制的命令执行) 核心组件: * OpenClaw Gateway:Agent 网关,管理会话、工具调用、渠道连接 * 飞书插件:通过