QWen和DeepSeek从基础概念到实践应用指南

人工智能 潘老师 1个月前 (03-20) 51 ℃ (0) 扫码查看

最近我发现自己被AI“拿捏”了,以前遇到问题第一反应是上搜索引擎,现在直接找AI询问。不得不说,大型语言模型(LLMs)彻底颠覆了人工智能领域,那些曾经只有人类能完成的自然语言处理任务,现在AI也能轻松应对。前段时间公司组织去微软参观,感受了人家的前沿技术,我深刻意识到,咱们得跟上这波AI潮流。就算不搞AI模型开发,也得对其中的概念和原理有所了解。今天就跟大家分享一下我在探索QWen和DeepSeek过程中的收获。

一、AMD在AI领域的布局

之前我买了块AMD显卡,本想着闲暇时玩AAA游戏,欣赏游戏里的精美风景。一次逛显卡驱动官网时,意外发现AMD也进军AI领域了,还推出了和NVIDIA类似的开发工具,像AMD HIP SDK和AMD ROCm。

AMD HIP SDK(Heterogeneous-computing Interface for Portability)是一套跨平台的GPU编程工具,能帮开发者编写可在AMD和NVIDIA GPU上运行的高性能计算代码。它和CUDA编程模型类似,能让开发者更轻松地开发GPU加速代码。

AMD ROCm(Radeon Open Compute)则是一个开源的GPU计算平台,专门用于高性能计算和机器学习。它包含运行时环境、工具链以及各种库,可以在AMD GPU上高效完成高性能计算任务。这里要注意,HIP是GPU编程工具,而ROCm是GPU计算平台,ROCm包含了HIP SDK。

刚接触这些概念时,我满脑子疑问。比如用PyTorch + ROCm开发时,需不需要把HIP引入工程?而且官方提供的HIP只有Windows版本,ROCm却只能在Linux环境下使用,这可把我搞懵了,用PyTorch开发不就受限了吗?看看官方的架构图,里面的各种框架、库、工具让人眼花缭乱。

二、解决开发与部署难题

官网没找到答案,我就求助AI。原来,PyTorch并不直接依赖HIP,它通过ROCm提供的底层运行时和加速库(像MIOpen、rocBLAS等)来实现GPU加速。Windows上的HIP SDK主要用于开发其他GPU应用,比如科学计算或图形处理,不是用来运行PyTorch的。

解决了开发问题,新的疑问又冒出来了:开发完成后,模型怎么运行?在Linux上用ROCm开发的模型,Windows没有ROCm环境,该怎么部署?用ROCm开发的程序能不能用NVIDIA显卡加速呢?

后来了解到,AI模型分为训练和运行两个阶段。训练阶段,可以在Linux上借助ROCm和PyTorch开发并训练深度学习模型,训练完成后,把PyTorch模型保存为通用格式,比如.pt或.onnx文件。运行阶段,在Windows系统上重新配置PyTorch环境,加载保存的模型进行推理。如果不需要GPU加速,直接用PyTorch CPU后端就行;要是需要GPU加速,这里以onnx文件为例详细说说。

2.1 认识ONNX

ONNX是一种开放的神经网络交换格式,由微软和Facebook(现在叫Meta)联合推出,主要是为了解决深度学习模型跨平台和跨框架的兼容性问题。ONNX模型很强大,能在Windows、Linux、macOS等多个操作系统上运行,而且对硬件的支持也非常广泛,无论是CPU、NVIDIA GPU(基于CUDA)、AMD GPU(基于ROCm或DirectML),还是像谷歌TPU这样的特定AI加速器,它都能适配。

DirectML是微软基于DirectX 12构建的通用GPU加速推理API,游戏玩家对DirectX肯定不陌生,很多AAA游戏的GPU加速选项里都有它的身影。借助DirectML,AMD、NVIDIA和Intel的GPU都能实现加速。

2.2 在Linux上安装ONNX Runtime

在Linux系统上安装ONNX Runtime,有不同的选择:

# 安装支持CPU的ONNX Runtime:
pip install onnxruntime

# 安装支持ROCm的ONNX Runtime:
pip install onnxruntime-rocm

# 安装支持CUDA的ONNX Runtime:
pip install onnxruntime-gpu

具体安装哪种,要根据自己手头的硬件来决定。

2.3 在Windows上安装ONNX Runtime

Windows上安装ONNX Runtime也有多种方式:

# 安装通用的ONNX Runtime:
pip install onnxruntime

# DirectML是微软推出的跨厂商GPU加速API,支持AMD和NVIDIA GPU
pip install onnxruntime-directml

# Windows可直接安装支持CUDA的ONNX Runtime:
pip install onnxruntime-gpu

要是只有AMD的GPU,在Windows上只能通过DirectML对PyTorch训练的模型进行加速。另外,在macOS上,ONNX Runtime目前仅支持CPU推理,还不支持GPU加速。

这里要注意,ONNX模型的opset版本(操作集版本)必须和目标推理引擎兼容,常见的选择是opset 11及更新版本(ONNX Runtime通常支持opset版本11 – 17)。如果目标ONNX Runtime不支持模型的opset版本,模型可能就无法加载。

2.4 ONNX Runtime的执行提供者

ONNX Runtime是一个跨平台的推理引擎,为了适配不同的硬件平台,它提供了多种执行提供者,每个执行提供者对应特定的硬件和加速方案:

  • CPUExecutionProvider:适用于所有平台,是默认的执行提供者,不使用硬件加速,没有GPU时可以用它。
  • CUDAExecutionProvider:用于NVIDIA GPU,借助NVIDIA的CUDA和cuDNN加速推理,NVIDIA GPU用户可以选择它。
  • ROCMExecutionProvider:在Linux环境下,为AMD GPU加速推理提供支持,Linux系统中使用AMD GPU的用户可以用这个。
  • DmlExecutionProvider:基于DirectML,在Windows平台上提供跨硬件加速,AMD、NVIDIA和Intel的GPU都能支持,Windows用户可以考虑。
  • OpenVINOExecutionProvider:利用Intel的OpenVINO工具包加速推理,适用于Intel CPU和VPU(比如Movidius)。
  • TensorrtExecutionProvider:专门针对NVIDIA GPU优化,在Linux系统下,使用NVIDIA TensorRT提供更高效的推理性能。
  • QNNExecutionProvider:借助Qualcomm的Hexagon DSP加速推理,适用于移动设备上的Qualcomm芯片。
  • CoreMLExecutionProvider:在macOS和iOS系统上,使用Apple的CoreML框架加速推理。

三、不同硬件平台下的模型运行

假设有开发者小明,他有一块NVIDIA GPU;还有开发者小红,她有一块AMD GPU。小明用NVIDIA GPU并通过CUDA加速训练了一个模型,然后把模型导出为ONNX格式的文件。

ONNX格式的优势在于它跨框架、跨硬件平台,所以不管是小明的NVIDIA GPU,还是小红的AMD GPU,都能加载和运行这个ONNX文件。而且ONNX与硬件无关,小明用CUDA训练模型的细节不会影响最终模型的运行。

小明可以直接用NVIDIA GPU和ONNX Runtime + CUDAExecutionProvider来运行他的ONNX模型,通过下面的代码执行推理:

pip install onnxruntime-gpu
import onnxruntime as ort

# 使用NVIDIA CUDA加速
ort_session = ort.InferenceSession("model.onnx", providers=["CUDAExecutionProvider"])

小红的电脑是Windows系统,配备了AMD显卡。她想运行小明导出的ONNX模型,虽然没有NVIDIA GPU和CUDA环境,但借助DirectML提供者,也能利用AMD GPU加速运行模型:

pip install onnxruntime-directml
import onnxruntime as ort

# 使用DirectML提供者(适用于Windows平台)
ort_session = ort.InferenceSession("model.onnx", providers=["DmlExecutionProvider"])

# 验证当前正在使用的执行提供者
print("当前使用的执行提供者:", ort_session.get_providers())

四、其它概念

4.1 GGUF

GGUF是GGML(Georgi Gerganov Machine Learning)系列框架的一种新模型格式,主要用于优化大语言模型(LLM)的存储和推理。它专为CPU和轻量化GPU推理场景设计,非常适合资源受限的设备。

像PyTorch或TensorFlow等深度学习框架,都支持将模型导出或转换为GGUF和ONNX格式。不过,目前GGUF格式主要由llama.cpp及相关工具链支持,不能直接在深度学习框架中生成,需要借助特定工具。llama.cpp提供了工具,可以把大语言模型(比如LLaMA、Qwen等)从主流框架(如PyTorch)转换为GGUF格式。

下面对比一下GGUF和ONNX的运行环境:

  • 格式设计目标:GGUF注重轻量化推理,适合低资源设备;ONNX追求通用性,支持多任务、多框架、多硬件。
  • 操作系统支持:GGUF支持Linux、macOS、Windows(WSL)及安卓;ONNX支持全平台,像Linux、Windows、macOS等都没问题。
  • 硬件支持:GGUF主要依赖CPU,初步支持GPU;ONNX支持CPU、GPU、TPU、FPGA等,能实现复杂的硬件加速。
  • 工具链依赖:GGUF依赖llama.cpp这个轻量化工具链;ONNX依赖ONNX Runtime、TensorRT、OpenVINO等。
  • 运行优化需求:GGUF追求低资源消耗,适合边缘设备;ONNX侧重于高性能优化,依赖硬件加速。
  • 是否硬件无关:两者理论上都与硬件无关,但GGUF取决于llama.cpp的实现,ONNX取决于ONNX Runtime的实现。

llama.cpp支持DirectML,能调用DirectML的GPU加速能力,实现GGUF格式的推理加速。目前,DirectML支持所有支持DirectX 12的AMD GPU(比如RX 6000系列、RX 7000系列显卡)。不过,llama.cpp目前还不直接支持ROCm,它支持OpenCL后端(可用于NVIDIA GPU和AMD GPU)和Vulkan后端(支持AMD和NVIDIA GPU),但尚未直接支持NVIDIA的CUDA后端。

4.2 Q(Quantization,量化)

在模型领域,“q”通常代表量化(Quantized),用于描述模型的量化权重或激活值。量化就是把浮点数(比如FP32或FP16)转换为整数(比如INT8)或低比特精度的表示形式,这样做的目的是减少模型的存储和计算成本,同时尽量保证模型的精度。比如,q6和q5可能表示量化为6位和5位精度的权重或数据。

以使用Qwen2.5 – Coder – 32B – Instruct – GGUF模型为例,假设计算机内存为32GB,GPU显存为24GB,该怎么选择量化方案呢?

  • q6_k(6 – bit量化):6 – bit量化后,每个参数占用约0.75字节。模型占用显存 ≈ 32 × 10⁹ × 0.75 / (1024³) = 22.39GB。考虑到总显存需求(包括临时激活值和上下文长度),可能接近或略超过24GB,所以可能需要优化上下文长度设置。
  • q5_k_m(5 – bit量化):5 – bit量化后,每个参数占用约0.625字节。模型占用显存 ≈ 32 × 10⁹ × 0.625 / (1024³) = 18.66GB。总显存需求通常在20GB左右,在24GB的GPU显存内运行会比较轻松。

综合来看,更推荐q5_k_m这种量化方案。在一些高负载场景(比如较长上下文或多批量推理)中,q6_k可能会超出显存限制。

4.3 开源AI库

4.3.1 DeepSeek

以DeepSeek的deepseek – r1 – distill – llama – 8b模型为例,来看看在Android上用Rust写一个FFI的Demo,并调用C++方法,再通过rust – jni把结果返回给Java层的具体步骤:

  1. 安装依赖项:安装一些必要的依赖项,对于rust – jni,在Android上可能需要特定配置。如果还没安装cargo.toml,可以先安装:
cargo install --dev

安装rustfmt确保代码格式正确:

cargo add rustfmt --all-features

安装rust – jni:

cargo add https://github.com/Bytedance/rust-jni
  1. 创建项目结构:创建新的Rust项目,添加必要的配置文件。在Cargo.toml中添加以下内容:
[package]
name = "rust_jni_demo"
version = "0.1.0"
edition = "2021"

[dependencies]
rust-jni = "latest"
jni_utils = "0.3"
android_support = "0.4"

[[path]]
name = "src/main.rs"
  1. 编写Rust代码:在src/main.rs中编写简单的FFI Demo:
use jni::JNIEnv;
use jni::sys::System;

#[derive(Debug)]
struct RustStruct {}

impl RustStruct {
    fn new() -> Result<Self, Box<dyn std::error::Error>> {
        println!("创建了一个Rust结构体");
        Ok(RustStruct {})
    }
}

#[jni::main]
fn main(
    mut env: JNIEnv,
    _class: *const u8,
) -> i32 {
    // 初始化Android环境
    System::initialize(env);

    let result = RustStruct::new();
    println!("Rust调用成功,结果={}", result);

    // 返回结果给Java层
    return result.map(|_| 0).try_into().unwrap_or(0);
}
  1. 编写Java层的代码:创建MainActivity类,在其中使用rust – jni调用Rust方法:
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.id.main);

        try {
            // 调用Rust层的方法
            String result = callRustMethod();
            Toast.makeText(this, "从Rust返回的结果是:" + result, Toast.LENGTH_LONG).show();
        } catch (JNIError e) {
            Toast.makeText(this, "JNI错误:" + e.toString(), Toast.LENGTH_LONG).show();
        }
    }

    // 使用rust-jni注册一个方法
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        callRustMethod();
        return true;
    }

    // 使用rust-jni创建一个函数,调用Rust层的功能
    private static final String METHOD_NAME = "callRustFunction";
    private static final int METHOD_ID = JNIEnv.getStaticMethodID(
            getClass().getDeclaredClass(),
            METHOD_NAME,
            new Class<?>[] {}
    );

    private String callRustMethod() {
        try (JNIEnv env = new Env()) {
            return env.call-static<java.lang.String>(METHOD_ID);
        } catch (JNIError e) {
            throw new RuntimeException(e);
        }
    }

    // 为了让rust-jni识别这个类,添加注解
    @RustJavaClass(name = "com.example.MainActivity", is_jni=False)
    public class Activity extends AppCompatActivity {
        // ...
    }
  1. 安装和构建C++库(可选):如果需要调用C++函数,可以编写一个简单的C++库,并编译成.so文件。比如:
// src/cpp/hello.cpp
#include <string>

using namespace std;

extern "C" {
    string sayHello() {
        return "Hello from C++";
    }
}

然后在Cargo.toml中添加构建步骤:

[project]
name = "rust_jni_demo"

[dependencies]
...

// 添加一个构建阶段,编译C++库,并生成.so文件
[target.mk]
all : $(objdir)/$(target)/libmyjnilib.so

$(objdir)/$(target)/libmyjnilib.so: src/cpp/hello.o
    g++ -o $@ -shared src/cpp/hello.o
  1. 编译和运行:在命令行编译:
cargo build
  1. 注意事项
    • 线程安全:确保JNI操作在主线程执行,对于长时间运行的任务,可以考虑在子线程处理,并通过JNI发送回调。
    • 错误处理:rust – jni提供了丰富的错误处理机制,比如JNIEnv::try来管理资源。
    • 性能优化:如果频繁调用JNI方法,可能需要优化Rust代码,提高效率。
  2. 验证和调试:在Rust中用println!输出日志信息,验证调用是否成功;在Android中使用Log utility输出日志,比如Log.d(“TAG”, “消息”),方便在Android Studio中查看。

4.3.2 QWen

同样在Android平台上,用Rust通过FFI调用C++方法,并通过rust – jni将结果返回给Java层,以QWen – Coder – 2.5 – 32B – q5_k_m模型为例,具体步骤如下:

  1. 编写C++代码:编写一个简单的C++库,包含要被Rust调用的方法。例如:
// src/native-lib.cpp
extern "C" {
    int add(int a, int b) {
        return a + b;
    }
}
  1. 构建C++库
    • Android.mk文件中配置编译规则,假设你的项目结构是在src目录下有native - lib.cpp文件。
    LOCAL_PATH := $(call my - dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := native - lib
    LOCAL_SRC_FILES := native - lib.cpp
    include $(BUILD_SHARED_LIBRARY)
    
    • 运行ndk - build命令(确保你已正确配置Android NDK环境变量),生成libnative - lib.so库文件。
  2. 配置Rust项目
    • 创建一个新的Rust项目,在Cargo.toml文件中添加依赖:
    [package]
    name = "qwen_rust_jni_demo"
    version = "0.1.0"
    edition = "2021"
    [dependencies]
    rust - jni = "latest"
    
  3. 编写Rust代码调用C++库
    • src/main.rs文件中编写如下代码:
    extern "C" {
        fn add(a: i32, b: i32) -> i32;
    }
    use jni::JNIEnv;
    use jni::sys::jint;
    #[no_mangle]
    pub extern "system" fn Java_com_example_qwen_1rust_1jni_1demo_MainActivity_callCppFunction(
        env: JNIEnv,
        _class: jni::sys::jclass,
        a: jint,
        b: jint,
    ) -> jint {
        unsafe {
            add(a, b)
        }
    }
    
    • 这里定义了一个Rust函数Java_com_example_qwen_1rust_1jni_1demo_MainActivity_callCppFunction,它会被Java层调用,在函数内部通过extern "C"块声明的add函数调用C++库中的add方法。
  4. 编写Java代码调用Rust方法
    • MainActivity.java文件中编写如下代码:
    package com.example.qwen_rust_jni_demo;
    import android.os.Bundle;
    import android.widget.Toast;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {
        static {
            System.loadLibrary("native - lib");
        }
        public native int callCppFunction(int a, int b);
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            int result = callCppFunction(3, 5);
            Toast.makeText(this, "调用C++函数结果: " + result, Toast.LENGTH_SHORT).show();
        }
    }
    
    • 在Java代码中,通过System.loadLibrary("native - lib")加载之前编译生成的C++库,然后定义了一个native方法callCppFunction,该方法会调用Rust中实现的与C++交互的函数。
  5. 编译和运行
    • 在Rust项目目录下运行cargo build --target aarch64 - linux - android(根据你的Android设备架构选择正确的目标平台,这里以aarch64为例),生成对应的动态库文件。
    • 将生成的动态库文件(如target/aarch64 - linux - android/debug/libqwen_rust_jni_demo.so)复制到Android项目的jniLibs/armeabi - v8a目录下(同样根据架构选择正确的目录)。
    • 运行Android项目,验证是否能正确调用C++函数并获取结果。

通过以上步骤,在Android平台上实现了利用Rust通过FFI调用C++方法,并通过rust – jni将结果返回给Java层,以QWen相关模型在Android平台上的使用场景为例,展示了跨语言调用的实践过程。在实际应用中,对于QWen模型,可能需要根据其具体的API和使用方式,在C++或Rust层进行更复杂的模型加载、推理等操作,然后将结果返回给Java层供应用使用。同时,在处理QWen模型相关的量化等特性时,也需要在不同语言层协同工作,确保模型在Android设备上能够高效且准确地运行。例如,在加载量化后的QWen模型时,可能需要在C++层编写特定的加载函数,通过Rust的FFI调用该函数,再将加载结果传递给Java层,以便在Android应用中进行推理等操作。

以上就是关于QWen和DeepSeek在不同方面的详细介绍与实践,希望能帮助大家更好地理解和应用这些技术。在实际开发中,根据具体的需求和硬件环境,灵活选择合适的工具和方法,以实现高效的AI应用开发。如果在实践过程中遇到问题,欢迎在评论区留言讨论。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/ai/16050.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】