大语言模型本地部署之转录文本总结

一台搭载 Intel Core i7-10700 处理器的服务器具备 8 核心(4 插槽×2 核心)的并行计算能力,基础主频 2.90 GHz,可通过睿频提升至更高频率,配合 64 MiB L3 缓存和每核心 256 KiB L1 及 2 MiB L2 缓存,为多线程应用和虚拟化环境提供稳定高效的执行性能. 最大30G内存

Qwen-7B系列模型

Qwen-7B 系列模型包含基础预训练模型 Qwen-7B 及其多种微调衍生版本,覆盖对话、编程和数学等领域.

基础模型

Qwen-7B 是参数规模约 70 亿的 Transformer 语言模型,配置为 32 层、4096 维度、32 个注意力头,支持 8K token 上下文长度,在超大规模多语种和专业领域数据上完成预训练.

对话微调

Qwen-7B-Chat 通过监督微调(SFT)和强化学习(RLHF)对齐优化,具备先进的对话生成能力和工具调用能力,适用于聊天机器人和智能助手场景.

专业衍生

  • Code-Qwen:基于 Qwen-7B 在大规模代码语料上进一步训练,专注代码生成与理解任务.

  • Code-Qwen-Chat:在 Code-Qwen 基础上微调对话功能,优化编程辅助与交互体验.

  • Math-Qwen-Chat:针对数学推理和解题场景微调,增强多步推理和逻辑链构建能力.

量化与部署

Qwen-7B-Chat 提供 Int4 和 AWQ 4bit 量化版本,显著降低内存与显存需求,支持在 CPU 及低端 GPU 环境中高效推理.

Qwen-7B-Chat

Qwen-7B-Chat 最低需 4 核 CPU 加 16 GB 内存,建议 32 GB;GPU 推理时,4-bit 量化版需 ≥6 GB 显存,FP16/BF16 模式需 ≥16 GB 显存;软件依赖 Python 3.8+、PyTorch 1.12+、CUDA 11.4+.

基本硬件配置

  • CPU 与内存:4 核 CPU(如 i5-6500)+ 16 GB 内存,推荐 32 GB 以支持复杂查询.

  • GPU 显存:4-bit 量化模型显存占用约 5.93 GB--6.06 GB,需 ≥6 GB;FP16/BF16 模式显存占用约 14.92 GB,需 ≥16 GB.

软件依赖

  • Python 版本:3.8 及以上.

  • PyTorch 版本:1.12 及以上,推荐 2.0 及以上.

  • CUDA 版本:11.4 及以上(GPU 用户及 flash-attention 加速用户)

DeepSeek-R1 7B

以下是 DeepSeek-R1 7B 参数系列模型的主要变体及特点:

模型版本 参数量 主要特性 适用场景
deepseek-llm-7b-base 7B 原始基线模型,未做指令对齐;优秀的生成与知识检索能力 非对话生成、长文本写作、知识检索
deepseek-llm-7b-chat 7B 基于 base 版本进行监督微调(SFT)和 RLHF 对话对齐 对话机器人、交互式摘要与问答
DeepSeek-R1-Distill-Qwen-7B 7B 蒸馏自 DeepSeek-R1,平衡推理质量与资源消耗 复杂推理、会议纪要结构化总结
DeepSeek-R1-Distill-Llama-7B* 7B 蒸馏自 DeepSeek-R1,使用 Llama3 架构 代码生成、算法推理、混合任务场景

使用DeepSeek-R1-Distill-Qwen-7B(70亿参数)在不同精度下的显存与内存需求如下:

  • FP16 模式:约 14 GB GPU 显存,约 28 GB 系统内存。

  • INT8 量化:约 7 GB GPU 显存,约 14 GB 系统内存。

  • INT4 量化:约 3.5 GB GPU 显存,约 7 GB 系统内存。

推荐部署时硬件配置:

  • CPU:6--8 核处理器(如 6 核/8 线程或以上)。

  • 系统内存:≥ 32 GB RAM,以避免 OOM;量化模式下可适当降低至 16 GB(INT8)或 8 GB(INT4)。

  • GPU:FP16 模式需 ≥ 24 GB 显存;INT8/INT4 量化可在 ≥ 8 GB 显存的卡上运行。

  • 磁盘:≥ 100 GB 可用空间,用于存放模型权重(约 29 GB)及日志、缓存.

测试发现回复很慢,大概10分钟。

最终方案Qwen2-1.5B-Instruct

由于追求一些实时性,以及纯CPU硬件限制。因此选择1.5B推理,防止推理太慢。

Qwen2-1.5B-Instruct 源自 1.54B 参数的 Qwen2.5 基础模型,进一步在丰富的指令跟随数据集上进行微调,以便在摘要、问答、代码生成及数学推理等任务中提供高质量响应. 其架构包含 RoPE 位置编码、SwiGLU 激活、RMSNorm、Attention QKV 偏置与组查询注意力,层数 28、查询头 12/键值头 2,并支持高达 32K Token 的长上下文输入. 该模型在多项指令基准测试中展现卓越性能,输出连贯且符合事实,并提供 INT8/INT4 量化版本,以满足 GPU 与 CPU 上的高效部署需求.

并发执行程序示例

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Qwen2.5-1.5B 模型封装模块
基于参考程序 localtest.py 实现的生产级模型封装
提供流式和阻塞式推理接口,支持 WebSocket 集成
"""

import os
import time
import logging
import threading
from typing import Generator, Optional, Dict, Any
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

# 导入流式输出相关
try:
    from transformers import TextStreamer, TextIteratorStreamer
    STREAMING_AVAILABLE = True
except ImportError:
    STREAMING_AVAILABLE = False

logger = logging.getLogger(__name__)


class Qwen25Model:
    """
    Qwen2.5-1.5B 模型封装类
    
    提供生产级的模型加载、推理和资源管理功能
    支持流式和阻塞式推理,针对 CPU 进行优化
    """
    
    def __init__(self, 
                 model_path: str = "/home/jbj/openai/modle/txtModle/Qwen2.5-1.5B-Instruct",
                 temperature: float = 0.7,
                 max_new_tokens: int = 256,
                 top_p: float = 0.8,
                 top_k: int = 50,
                 repetition_penalty: float = 1.1,
                 device: str = "auto",
                 cpu_threads: Optional[int] = None):
        """
        初始化 Qwen2.5-1.5B 模型
        
        Args:
            model_path: 模型路径,默认为本地模型路径
            temperature: 控制生成随机性的温度参数
            max_new_tokens: 最大生成令牌数
            top_p: 核采样参数
            top_k: Top-K 采样参数
            repetition_penalty: 重复惩罚参数
            device: 设备类型,"auto" 自动选择
            cpu_threads: CPU 线程数,None 为自动检测
        """
        self.model_path = model_path
        self.temperature = temperature
        self.max_new_tokens = max_new_tokens
        self.top_p = top_p
        self.top_k = top_k
        self.repetition_penalty = repetition_penalty
        self.device = device
        
        # 模型和分词器
        self.model = None
        self.tokenizer = None
        
        # 线程安全锁
        self._lock = threading.RLock()
        self._is_loaded = False
        
        # 设置 CPU 优化
        self._setup_cpu_optimization(cpu_threads)
        
        # 初始化模型
        self._load_model()
        
        logger.info(f"Qwen25Model 初始化完成,模型路径: {model_path}")
    
    def _setup_cpu_optimization(self, cpu_threads: Optional[int] = None):
        """设置 CPU 优化配置"""
        if cpu_threads is None:
            cpu_threads = max(1, os.cpu_count() - 2)  # 保留2个核心给系统
        
        logger.info(f"配置 CPU 优化: {cpu_threads} 线程")
        
        # 设置环境变量
        os.environ["OMP_NUM_THREADS"] = str(cpu_threads)
        os.environ["MKL_NUM_THREADS"] = str(cpu_threads)
        os.environ["OPENBLAS_NUM_THREADS"] = str(cpu_threads)
        os.environ["VECLIB_MAXIMUM_THREADS"] = str(cpu_threads)
        os.environ["NUMEXPR_NUM_THREADS"] = str(cpu_threads)
        
        # 设置 PyTorch 并行线程
        torch.set_num_threads(cpu_threads)
        torch.set_num_interop_threads(cpu_threads)
    
    def _load_model(self):
        """加载模型和分词器"""
        try:
            logger.info("开始加载 Qwen2.5-1.5B 模型...")
            
            # 检查模型路径
            if not Path(self.model_path).exists():
                raise FileNotFoundError(f"模型路径不存在: {self.model_path}")
            
            # 加载分词器
            self.tokenizer = AutoTokenizer.from_pretrained(
                self.model_path, 
                use_fast=True,
                trust_remote_code=True
            )
            
            # 加载模型 - CPU 优化配置
            self.model = AutoModelForCausalLM.from_pretrained(
                self.model_path,
                torch_dtype=torch.float32,      # CPU 上使用 float32 更快
                device_map="auto",               # 自动设备映射
                low_cpu_mem_usage=True,         # 减少 CPU 内存使用
                use_cache=True,                 # 启用 KV 缓存
                trust_remote_code=True
            )
            
            # 设置为评估模式
            self.model.eval()
            
            # 尝试启用 JIT 优化
            try:
                self.model = torch.jit.optimize_for_inference(self.model)
                logger.info("✅ 已启用 JIT 优化")
            except Exception as e:
                logger.warning(f"⚠️ JIT 优化不可用: {e}")
            
            self._is_loaded = True
            logger.info("✅ 模型加载完成")
            
        except Exception as e:
            logger.error(f"❌ 模型加载失败: {e}")
            raise RuntimeError(f"模型加载失败: {e}")
    
    def _prepare_input(self, 提示: str) -> Dict[str, torch.Tensor]:
        """准备模型输入"""
        messages = [{"role": "user", "content": 提示}]
        text = self.tokenizer.apply_chat_template(
            messages, 
            tokenize=False, 
            add_generation_prompt=True
        )
        return self.tokenizer([text], return_tensors="pt").to(self.model.device)
    
    def _get_generation_config(self) -> Dict[str, Any]:
        """获取生成配置"""
        return {
            "max_new_tokens": self.max_new_tokens,
            "do_sample": True,
            "top_p": self.top_p,
            "top_k": self.top_k,
            "temperature": self.temperature,
            "repetition_penalty": self.repetition_penalty,
            "use_cache": True,
            "pad_token_id": self.tokenizer.eos_token_id
        }
    
    def generate_blocking(self, prompts: str) -> str:
        """
        返回完整生成结果的阻塞式推理
        
        Args:
            prompts: 用于摘要处理的输入文本
            
        Returns:
            str: 生成的完整摘要文本
        """
        if not self._is_loaded:
            raise RuntimeError("模型未正确加载")
        
        with self._lock:
            try:
                # 准备输入
                model_inputs = self._prepare_input(prompts)
                generation_config = self._get_generation_config()
                
                # 生成
                with torch.no_grad():
                    outputs = self.model.generate(**model_inputs, **generation_config)
                
                # 解码结果
                gen_ids = outputs[0][len(model_inputs.input_ids[0]):]
                result = self.tokenizer.decode(gen_ids, skip_special_tokens=True)
                
                logger.debug(f"生成摘要完成,长度: {len(result)} 字符")
                return result
                
            except Exception as e:
                logger.error(f"生成摘要失败: {e}")
                raise RuntimeError(f"生成摘要失败: {e}")
            
    def generate_stream(self, prompts: str) -> Generator[str, None, None]:
        """
        使用生成器进行流式推理以处理结果
        每次生成的输出都应返回完整的累积文本(而非单个的标记)
        
        Args:
            提示: 用于摘要的输入文本
            
        Yields:
            str: 每一步生成的累积文本内容
        """
        if not self._is_loaded:
            raise RuntimeError("模型未正确加载")
        
        if not STREAMING_AVAILABLE:
            # 如果不支持流式输出,回退到普通方式
            logger.warning("流式输出不可用,回退到阻塞式推理")
            yield self.generate_blocking(prompts)
            return
        
        with self._lock:
            try:
                # 准备输入
                model_inputs = self._prepare_input(prompts)
                generation_config = self._get_generation_config()
                
                # 创建流式输出器
                streamer = TextIteratorStreamer(
                    self.tokenizer,
                    skip_prompt=True,           # 跳过输入prompt
                    skip_special_tokens=True,   # 跳过特殊token
                    clean_up_tokenization_spaces=True
                )
                
                # 添加流式输出器到生成配置
                generation_config["streamer"] = streamer
                
                # 在单独线程中运行生成,避免阻塞
                def generate_in_thread():
                    with torch.no_grad():
                        self.model.generate(**model_inputs, **generation_config)
                
                thread = threading.Thread(target=generate_in_thread)
                thread.start()
                
                # 流式输出累积文本
                partial_text = ""
                for new_text in streamer:
                    partial_text += new_text
                    yield new_text
                
                thread.join()  # 等待生成完成
                
                logger.debug(f"流式生成完成,最终长度: {len(partial_text)} 字符")
                
            except Exception as e:
                logger.error(f"流式生成失败: {e}")
                raise RuntimeError(f"流式生成失败: {e}")
    
    def is_loaded(self) -> bool:
        """检查模型是否已加载"""
        return self._is_loaded
    
    def get_model_info(self) -> Dict[str, Any]:
        """获取模型信息"""
        return {
            "model_path": self.model_path,
            "is_loaded": self._is_loaded,
            "streaming_available": STREAMING_AVAILABLE,
            "device": str(self.model.device) if self.model else None,
            "config": {
                "temperature": self.temperature,
                "max_new_tokens": self.max_new_tokens,
                "top_p": self.top_p,
                "top_k": self.top_k,
                "repetition_penalty": self.repetition_penalty
            }
        }
    
    def close(self):
        """清理资源"""
        with self._lock:
            if self.model is not None:
                del self.model
                self.model = None
            if self.tokenizer is not None:
                del self.tokenizer
                self.tokenizer = None
            self._is_loaded = False
            
            # 清理 GPU 缓存(如果使用)
            if torch.cuda.is_available():
                torch.cuda.empty_cache()
                
            logger.info("模型资源已清理")
    
    def __del__(self):
        """析构函数,确保资源清理"""
        try:
            self.close()
        except Exception:
            pass  # 忽略析构时的错误


# # 便利函数
# def create_qwen25_model(**kwargs) -> Qwen25Model:
#     """
#     创建 Qwen25Model 实例的便利函数
    
#     Args:
#         **kwargs: 传递给 Qwen25Model 构造函数的参数
        
#     Returns:
#         Qwen25Model: 初始化完成的模型实例
#     """
#     return Qwen25Model(**kwargs)


if __name__ == "__main__":
    # 简单测试
    logging.basicConfig(level=logging.INFO)
    
    try:
        ##单例初始化
        model = Qwen25Model()
        test_prompt = """
对以下会议记录进行总结:
国足的这场耻辱性的大败已经成为了这两天中国体育界最大的话题。我们来看一下前国脚樊志毅对此的点评,一项直性子的范大将军直言道,国足这样输球,
直怕是连面子都不要了。你说职能部门,职能部门一一届、一届、一届花了多少个足球协会主席了?改过不了,换汤不换药啊,人家卡马球也有理由说的,
我带的是什么队啊?我带的西班牙队啊,你一批批的是什么人啊,你叫我带中国足球,现在什么水平?就这么几个人,你赵鹏什么都在踢,足会他能踢吗?
踢不了,就这个能力知道吗?这下下去我要输液来了,你泰国队输完输越来在送缅甸,现在没人输了。另一方面来说,中国队是备战二零一八世界杯。
最早我觉得你我已经说了,你像这样的比赛本身就没有打好基础,你能跟我保证在一七年或者一六年这样关键的比赛他能能赢啊,务实一点,我确认什么?
我把自己战术打法足球的这个理念先搞懂,小高带的蛮好的,你把他去换了干什么?你告诉我在合肥输个一比五,你都要告诉我怎么解释呢?他脸脸都不要了。
请根据以下模板,填写会议纪要内容:
会议主题:【请填写会议主题】
会议时间:【请填写会议日期,例如:2025年9月18日】
会议结论:【请用一句话概括本次会议最重要的决定或结论】
行动项:
@[负责人姓名]:【请详细描述任务内容】,截止日期:【请填写截止日期,例如:2025年9月22日】
@[负责人姓名]:【请详细描述任务内容】,截止日期:【请填写截止日期,例如:2025年9月25日】
(可根据需要添加更多行动项)
"""
        
        ##使用方法1 测试阻塞式推理
        print("=== 阻塞式推理测试 ===")
        result = model.generate_blocking(test_prompt)
        print(f"结果: {result}")
        
        # ##测试流式推理
        # print("\n=== 流式推理测试 ===")
        # ##=== 流式推理测试 ===
        # # 当前长度: 64
        # # for partial in model.generate_stream_length(test_prompt):
        # #     print(f"当前长度: {len(partial)}", end="\r")
        # # print(f"\n最终结果: {partial}")

        ##使用方法3
        # print("\n=== 流式推理测试 ===")
        # accumulated=""
        for partial in model.generate_stream(test_prompt):
            accumulated += partial
            print(partial) 
        print(f"\n最终结果: {accumulated}")
    except Exception as e:
        print(f"测试失败: {e}")
    finally:
        if 'model' in locals():
            model.close()

存在的问题:

本地部署大模型,ai输出过程中终止了,如何避免这种情况?在有限的tokens内,保证回答是一个完整的?

使用流式输出解决。

结果

结果: ### 会议主题:国足国际赛事表现分析与反思会议

会议时间:2023年6月14日

会议地点:北京

会议结论:

通过樊志毅等前国脚的点评,会议认为国足近期的表现严重不足,特别是与泰国和缅甸的比赛,暴露出了球队整体实力及训练态度的问题。

行动项:

  • 樊志毅\]:组织一次针对国家队的内部教练员培训,重点讲解如何提升队伍的整体技战术水平,并制定短期和长期的改进计划,预计于2023年7月1日前完成。

(注:上述行动项为示例,实际执行时应根据具体情况调整)

学习社区

https://github.com/0voice

相关推荐
AI新兵5 小时前
AI大事记13:GPT 与 BERT 的范式之争(上)
人工智能·gpt·bert
文火冰糖的硅基工坊5 小时前
[人工智能-大模型-43]:模型层技术 - 强化学学习:学习的目标、收敛条件、评估依据、应用到的模型、应用场景 - 通俗易懂。
人工智能·学习
Fibocom广和通5 小时前
禾赛科技与广和通战略合作,联合推出机器人解决方案加速具身智能商业化落地
人工智能
飞哥数智坊5 小时前
Claude Skills 自定义实战:提炼会议纪要并推送企业微信
人工智能·claude·chatglm (智谱)
golang学习记6 小时前
性能飙升4倍,苹果刚发布的M5给人看呆了
人工智能·后端
golang学习记6 小时前
快手推出AI编程IDE:自主编程时代已来!
人工智能
皮皮学姐分享-ppx6 小时前
上市公司CEO IT背景数据(2007-2024)
大数据·人工智能·经验分享·科技·区块链
亚马逊云开发者6 小时前
利用 CloudWatch AIOps 实现智能化根因分析与故障排查
人工智能