关于AI_Qwen的几点记录

最近在折腾 AI 模型的实际落地,说实话,国内能用的模型不算多,选来选去还是看上了通义千问(Qwen)。最后选择 Qwen 的原因很简单:

  1. 代码开源:可以直接在 GitHub 上看到模型权重和实现细节,心里有底
  2. 部署友好:提供了多种部署方式,从本地推理到云服务都有
  3. 文档实际:除了学术paper,还有实用的代码示例和教程
  4. 社区活跃:遇到问题能搜到解决方案,github issues 回复也快

当然,这不是说 Qwen 没有问题,但至少这些问题是可以解决的,不是那种"就这样吧,忍忍"的感觉。

背景:为什么选择 Qwen

说实话,选择 Qwen 不是一开始就决定好的。最开始想的是用 GPT,但众所周知的原因,国内环境访问不稳定,而且企业级应用的合规性问题比较麻烦。然后试了试其他国产模型,要么是 API 不稳定,要么是文档写得太"学术",要么就是部署起来太复杂。

最后选择 Qwen 的原因很简单:

  1. 代码开源:可以直接在 GitHub 上看到模型权重和实现细节,心里有底
  2. 部署友好:提供了多种部署方式,从本地推理到云服务都有
  3. 文档实际:除了学术paper,还有实用的代码示例和教程
  4. 社区活跃:遇到问题能搜到解决方案,github issues 回复也快

当然,这不是说 Qwen 没有问题,但至少这些问题是可以解决的,不是那种"就这样吧,忍忍"的感觉。

需求:我要解决什么问题

具体场景是这样的:我们需要一个能理解中文技术文档的 AI 助手,主要功能包括:

  1. 代码审查:自动检查代码质量,给出中文建议
  2. 技术问答:基于公司的技术文档回答开发问题
  3. 代码生成:生成符合公司代码风格的代码片段
  4. 需求理解:将产品经理的"中文需求"转化为技术方案

核心痛点是:要懂中文、要懂代码、要可控、要快

前两个好说,大部分大模型都能做到。但"可控"和"快"就难了。可控意味着模型输出的结果要符合我们的规范,不能"天马行空";快意味着不能每次都要等几十秒才能返回结果。

实现:从零开始的部署过程

1. 模型选择

Qwen 提供了多个版本,选择哪个呢?

graph TD A[Qwen模型选择] --> B{使用场景} B -->|对话/问答| C[Qwen-Chat] B -->|代码相关| D[Qwen-Coder] B -->|通用理解| E[Qwen-Base] C --> F{资源限制} D --> F E --> F F -->|高GPU内存| G[7B或14B] F -->|中等GPU内存| H[2B或4B] F -->|CPU环境| I[Int8/Int4量化]

我的硬件资源有限(单张 RTX 4090),所以选择了 Qwen-Chat-7B 的 Int8 量化版本,平衡了性能和资源消耗。

2. 环境准备

这步很关键,坑也不少。先贴个正确配置:

# Python 环境
conda create -n qwen python=3.10
conda activate qwen

# PyTorch(CUDA 11.8)
pip install torch==2.0.1+cu118 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# Transformers 版本
pip install transformers==4.32.0
pip install accelerate==0.24.0
pip install bitsandbytes==0.41.0

# 其他依赖
pip install protobuf==3.20.0
pip install sentencepiece

踩坑记录1:一开始直接用了 pip install transformers,结果版本太新,和 Qwen 的兼容性有问题。后来查了 Qwen 的 GitHub,发现明确要求了 transformers 版本。

踩坑记录2:bitsandbytes 在某些 Linux 发行版上编译会失败,后来用了预编译的 wheel 文件才解决。

3. 模型下载

从 HuggingFace 下载模型:

from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "Qwen/Qwen-7B-Chat-Int8"

tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    device_map="auto",
    trust_remote_code=True
)

踩坑记录3:国内访问 HuggingFace 很慢,后来用了镜像站:

export HF_ENDPOINT=https://hf-mirror.com

4. 推理代码实现

基础推理代码:

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

def chat_with_qwen(prompt, model, tokenizer):
    response, history = model.chat(tokenizer, prompt, history=None)
    return response

# 使用示例
model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen-7B-Chat-Int8",
    device_map="auto",
    trust_remote_code=True
).eval()

tokenizer = AutoTokenizer.from_pretrained(
    "Qwen/Qwen-7B-Chat-Int8",
    trust_remote_code=True
)

response = chat_with_qwen("Python中如何用装饰器实现单例模式?", model, tokenizer)
print(response)

5. 批量推理优化

对于批量处理的需求,我做了几个优化:

from typing import List

def batch_chat(prompts: List[str], model, tokenizer, batch_size=4):
    """批量处理聊天请求"""
    results = []
    for i in range(0, len(prompts), batch_size):
        batch = prompts[i:i+batch_size]
        with torch.no_grad():
            batch_responses = []
            for prompt in batch:
                response, _ = model.chat(tokenizer, prompt, history=None)
                batch_responses.append(response)
        results.extend(batch_responses)
    return results

# 使用示例
questions = [
    "什么是闭包?",
    "解释一下 Python 的 GIL",
    "如何优化数据库查询?",
    "微服务和单体架构的区别?"
]

answers = batch_chat(questions, model, tokenizer)

踩坑总结:遇到的问题和解决方案

问题1:显存不够

一开始用 Qwen-14B,结果 RTX 4090 的 24G 显存直接爆满。解决方案:

# 使用梯度检查点
model.gradient_checkpointing_enable()

# 混合精度推理
from torch.cuda.amp import autocast

with autocast():
    response = model.chat(tokenizer, prompt, history=None)

最终还是降级到了 7B + Int8 量化,显存占用从 20G 降到了 8G 左右。

问题2:推理速度慢

单个请求还好,但并发请求就慢了。做了几个优化:

  1. 使用 FasterTransformer:部署时用了 FasterTransformer 加速,推理速度提升了 2-3 倍
  2. 增加缓存层:对常见问题做了结果缓存
  3. 异步处理:用 FastAPI 做异步接口,支持并发请求
from fastapi import FastAPI
from fastapi.concurrency import run_in_threadpool
import asyncio

app = FastAPI()

@app.post("/chat")
async def chat_endpoint(prompt: str):
    response = await run_in_threadpool(
        chat_with_qwen, prompt, model, tokenizer
    )
    return {"response": response}

问题3:输出质量不稳定

有时候回答很好,有时候很离谱。几番调试后发现:

  1. Temperature 太高:从 0.9 降到 0.7,输出更稳定
  2. Prompt 格式:统一了 Prompt 的格式,给模型明确的角色定位
  3. 上下文管理:控制历史对话的长度,避免"注意力发散"
def chat_with_qwen(prompt, model, tokenizer):
    system_prompt = "你是一个专业的中文技术助手,请用清晰、准确的语言回答技术问题。"
    full_prompt = f"{system_prompt}\n用户:{prompt}\n助手:"

    with torch.no_grad():
        response = model.chat(
            tokenizer,
            full_prompt,
            history=None,
            temperature=0.7,  # 降低随机性
            top_p=0.8         # 控制采样范围
        )
    return response

问题4:特定领域知识不足

比如问公司特定的代码规范,Qwen 就不知道了。解决方案:

  1. Fine-tuning:用公司技术文档做了微调(成本较高,效果一般)
  2. RAG:检索增强生成,先把相关文档找出来,再让模型基于文档回答(性价比最高)
  3. Prompt Engineering:在 Prompt 中直接提供相关代码片段

最后选择了 RAG,简单有效:

from sentence_transformers import SentenceTransformer
import faiss
import numpy as np

class RAGQwen:
    def __init__(self, model, tokenizer):
        self.model = model
        self.tokenizer = tokenizer
        self.embedder = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
        self.index = None
        self.documents = []

    def add_documents(self, docs):
        """添加文档到知识库"""
        self.documents.extend(docs)
        embeddings = self.embedder.encode(docs)
        if self.index is None:
            self.index = faiss.IndexFlatL2(embeddings.shape[1])
        self.index.add(embeddings.astype('float32'))

    def search(self, query, k=3):
        """检索相关文档"""
        query_embedding = self.embedder.encode([query])
        distances, indices = self.index.search(query_embedding.astype('float32'), k)
        return [self.documents[i] for i in indices[0]]

    def chat_with_rag(self, query):
        """结合检索结果进行聊天"""
        relevant_docs = self.search(query)
        context = "\n".join(relevant_docs)
        prompt = f"基于以下信息回答问题:\n{context}\n\n问题:{query}"

        response, _ = self.model.chat(self.tokenizer, prompt, history=None)
        return response

结果:实际的部署效果

经过几个月的折腾,现在的效果还算满意:

性能指标

指标数值
推理延迟(平均)1.2 秒/请求
并发支持10 QPS
显存占用8GB
回答质量(人工评估)85% 满意度

实际使用场景

  1. 代码审查:每天处理约 50 个 Pull Request 的自动审查
  2. 技术问答:处理约 200 个开发问题,其中 70% 能直接回答
  3. 代码生成:生成简单的样板代码,准确率约 80%
  4. 需求转化:将需求文档转化为 API 接口定义,节省约 30% 时间

成本对比

本地部署和 API 调用的差异不只在账单,延迟和可控性同样影响日常运维。

本地 Qwen 部署与 OpenAI API 在成本、延迟和可控性上的相对对比

成本与延迟的下降是量化收益,可控性翻倍则是选本地部署的核心动机。

相比直接调用 OpenAI API:

  • 成本:降低约 60%(主要是 GPU 资源 vs API 调用)
  • 延迟:降低约 40%(本地推理)
  • 可控性:提升 100%(完全控制模型行为)

当然,也有不好的地方:

  • 准确率略低于 GPT-4
  • 需要维护基础设施
  • 需要定期更新模型

结语

折腾 Qwen 的过程,其实就是一个大模型落地的缩影。不是选了最好的模型就能解决问题,而是要根据实际需求、资源限制、成本预算来做权衡。

Qwen 不是最强的模型,但对于我们的场景来说,它是最合适的。能部署、能控制、能定制、能用得起。这也是技术选型的核心:不选最强的,选最适合的

如果你也在考虑大模型落地,建议从小做起,先解决一个具体的、明确的问题,再逐步扩展。不要一开始就想做"全能 AI 助手",那个目标太大了,容易迷失方向。

最后,大模型技术还在快速发展,今天的"最佳实践"明天可能就过时了。但解决问题的思路不会变:先搞清楚要解决什么,再考虑怎么解决,最后才是用什么工具解决。

希望这篇文章能给你一些参考,少踩一些坑。


相关资源

版权声明: 本文首发于 指尖魔法屋-关于AI_Qwen的几点记录https://blog.thinkmoon.cn/post/406-ai-qwen-tongyi-practical-practice/) 转载或引用必须申明原指尖魔法屋来源及源地址!