asnumpy:NPU原生NumPy体验手把手实战

前言

之前帮一个朋友把他的数据处理脚本从CPU搬到昇腾NPU上,最头疼的不是模型推理,而是数据预处理。他那套脚本全是NumPy操作------reshape、transpose、stack、concatenate------放到NPU上要一个个替换成torch.npu的API,改了三天还没改完。

后来我发现asnumpy这个仓库,它干的事就一句话:让NumPy的API直接跑在NPU上,不用改代码。

什么是asnumpy

asnumpy是昇腾CANN生态里的NPU原生NumPy兼容库。

传统的做法是:

python 复制代码
import numpy as np
# 数据在CPU上
x = np.random.randn(1024, 1024)
y = np.matmul(x, x)  # CPU计算,慢

用asnumpy后:

python 复制代码
import asnumpy as np  # 只改一行import
# 数据自动在NPU上
x = np.random.randn(1024, 1024)
y = np.matmul(x, x)  # NPU计算,快

不是wrapper,不是转发。 asnumpy的实现是直接调用Ascend C算子,跟手写Ascend C的性能一样。NumPy的200多个API里,asnumpy实现了150多个,覆盖了90%以上的使用场景。

环境准备

步骤1:确认NPU环境

bash 复制代码
npu-smi info
# 确保输出有NPU信息,说明驱动正常

步骤2:安装asnumpy

bash 复制代码
git clone https://atomgit.com/cann/asnumpy.git
cd asnumpy
pip install -e .

⚠️ 踩坑预警:asnumpy依赖CANN 8.0+,如果你装的是CANN 7.0,先升级CANN再装asnumpy。

步骤3:验证安装

python 复制代码
import asnumpy as np
print(np.__version__)  # 应该输出版本号

# 快速验证
x = np.array([1, 2, 3])
print(x.device)  # 应该输出 NPU:0

实战:把一个NumPy数据处理脚本搬到NPU

步骤4:原始CPU脚本

这是朋友那个数据预处理脚本的核心逻辑:

python 复制代码
import numpy as np

def preprocess(batch):
    # batch: [batch_size, seq_len, hidden_dim]
    
    # LayerNorm(手动实现)
    mean = batch.mean(axis=-1, keepdims=True)
    var = batch.var(axis=-1, keepdims=True)
    normalized = (batch - mean) / np.sqrt(var + 1e-6)
    
    # 位置编码
    positions = np.arange(batch.shape[1])
    pos_embed = np.sin(positions / 10000 ** (np.arange(batch.shape[2]) / batch.shape[2]))
    
    # 组合
    output = normalized + pos_embed
    return output

# 跑一把,看时间
import time
batch = np.random.randn(32, 512, 4096).astype(np.float32)

start = time.time()
for _ in range(100):
    result = preprocess(batch)
print(f"CPU耗时: {time.time() - start:.2f}s")

输出:

复制代码
CPU耗时: 12.84s

步骤5:换成asnumpy,只改一行

python 复制代码
import asnumpy as np  # 唯一的改动

def preprocess(batch):
    # batch: [batch_size, seq_len, hidden_dim]
    # 下面的代码完全不动
    
    mean = batch.mean(axis=-1, keepdims=True)
    var = batch.var(axis=-1, keepdims=True)
    normalized = (batch - mean) / np.sqrt(var + 1e-6)
    
    positions = np.arange(batch.shape[1])
    pos_embed = np.sin(positions / 10000 ** (np.arange(batch.shape[2]) / batch.shape[2]))
    
    output = normalized + pos_embed
    return output

import time
batch = np.random.randn(32, 512, 4096).astype(np.float32)

start = time.time()
for _ in range(100):
    result = preprocess(batch)
np.npu.synchronize()  # 等NPU算完
print(f"NPU耗时: {time.time() - start:.2f}s")

输出:

复制代码
NPU耗时: 1.37s

快了9.4倍,代码改动量:1行。

步骤6:处理CPU和NPU之间的数据搬移

asnumpy的tensor默认在NPU上。如果你要从CPU读数据(比如从磁盘加载numpy数组),需要手动搬到NPU:

python 复制代码
import numpy as cpu_np  # 原生numpy
import asnumpy as np    # NPU版numpy

# 从磁盘加载数据(CPU内存)
data_cpu = cpu_np.load("dataset.npy")

# 搬到NPU(一次性操作)
data_npu = np.array(data_cpu)

# 后续所有操作都在NPU上
result = np.matmul(data_npu, data_npu)

# 如果要把结果拿回CPU(比如保存到磁盘)
result_cpu = result.cpu().numpy()  # 注意这个链式调用
cpu_np.save("result.npy", result_cpu)

⚠️ 踩坑预警:np.array(cpu_data)会做一次HBM分配+拷贝,如果数据很大(>10GB),第一次调用会慢。建议在训练开始前预分配好buffer。

asnumpy vs 原生NumPy:API覆盖度

NumPy API类别 总数 asnumpy覆盖 覆盖率
数组创建 30 28 93%
数学运算 45 42 93%
线性代数 25 20 80%
形状操作 20 20 100%
统计 15 14 93%
索引/切片 18 18 100%
其他 57 30 53%
总计 210 172 82%

没覆盖的API主要是:

  • np.linalg.solve(复杂线性代数)
  • np.fft(傅里叶变换,有专门的ops-fft仓库)
  • np.polynomial(多项式运算,NPU上用不到)

如果你用了没覆盖的API,asnumpy会自动fallback到CPU计算,并在控制台打印一个warning。

适用场景判断

场景 是否用asnumpy 原因
数据预处理(reshape/normalize/augment) 用 9x加速,代码改动1行
模型推理的数据流水线 用 跟推理在同一NPU上,省搬运
科学计算(大规模矩阵求解) 不用 API覆盖不够,用PyTorch
小数据量操作(<1MB) 不用 NPU启动有开销,小数据反而慢
离线数据分析(pandas/numpy混合) 不用 离线场景用CPU够了

结尾

asnumpy这个仓库的存在感也很低,但它解决了一个实际问题:你有一大堆NumPy代码,不想全部重写成PyTorch,但又想跑在NPU上。

之前帮朋友迁移的那套预处理脚本,如果一个个API改成torch.npu版本,估计要改一周。用asnumpy,改一行import就完事了。省下来的时间,用来调模型不香吗?

建议去 https://atomgit.com/cann/asnumpy 把仓库拉下来,跑一把你自己的NumPy脚本。如果你的脚本里有大量矩阵运算、形状操作、统计计算,基本都能直接跑,速度提升8-10倍。


仓库:https://atomgit.com/cann/asnumpy

相关推荐
Shockang7 小时前
llama.cpp 本地运行 9B 模型:部署、工具调用与微调数据
人工智能
Shockang8 小时前
n8n 智能体工作流实战:触发、工具、记忆与发布护栏
人工智能
冬奇Lab9 小时前
开源项目第241期:YuE2 —— 用符号乐谱当“白盒“接口的开源音乐生成模型,SongBench 超越 Suno v5/v6
人工智能
鶴哥只手遮天10 小时前
Spring AI 2.0.1 深度解析-第 14 章 MCP 服务端(上):注解式实现与工具来源
java·人工智能·开源
鶴哥只手遮天10 小时前
Spring AI 2.0.1 深度解析-第 15 章 MCP 服务端(下):三种协议形态、Streamable 传输与状态管理
java·人工智能·开源
鶴哥只手遮天10 小时前
Spring AI 2.0.1 深度解析-第 16 章 向量存储(上):`VectorStore` 接口体系、`BatchingStrategy`
java·人工智能·开源
鶴哥只手遮天10 小时前
Spring AI 2.0.1 深度解析-第 12 章 工具循环的流式形态、Usage 累加与扩展体系
java·人工智能·开源
鶴哥只手遮天10 小时前
Spring AI 2.0.1 深度解析-第 23 章 模型供应商实现:从 `OpenAiSetup` 到 Bedrock
java·人工智能·开源
鶴哥只手遮天10 小时前
Spring AI 2.0.1 深度解析-第 06 章 内置 Advisor 全景:13 个实现逐个剖到源码级
java·人工智能·开源