scientific-agent-skills 中的 GPU 代码转换模式:NumPy/pandas/scikit-learn 到 CuPy/cuDF/cuML 的完整迁移实战指南
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
本文是 scientific-agent-skills 仓库中 optimize-for-gpu 技能(skill)的核心文档《Code Transformation Patterns》的深度展开。当 AI Agent 面对"CPU 版 NumPy/SciPy、pandas、scikit-learn、NetworkX、scikit-image、Faiss 或
scipy.sparse.linalg代码运行缓慢,希望利用 NVIDIA GPU 加速"这一类请求时,本文提供了 12 组经过验证的"Before/After"转换模式,覆盖数组计算、DataFrame 处理、自定义内核、图分析、机器学习、物理仿真、文件 IO、可视化、图像处理、地理空间、向量检索与稀疏特征值求解。读完本文,你将掌握每一类工作负载的优先迁移路径、零代码加速开关、可复制的转换代码,以及保证数值语义一致与正确计时的工程方法。
转换前的三条铁律:契约、基线、合适性
optimize-for-gpu技能将 GPU 加速定位为"证据驱动的优化,而非自动重写"。在动手替换任何 import 之前,请先完成 SKILL.md 中定义的三件事:
- 定义契约与基线:捕获代表性输入、期望输出和可接受的数值容差;测量当前端到端耗时(包含输入、传输、计算、输出全链路);在改代码之前先用 CPU profiler 定位真正的瓶颈——是计算、内存带宽、分配、传输、同步还是存储。
- 判断合适性:只有当热点路径存在大量独立可并行的工作、运行频率足以摊薄初始化与传输开销、且工作集能放进显存(并为临时变量留出余量)时,GPU 才值得投入。小负载、强顺序、以不受支持操作为主、或需要频繁主机-设备往返的负载,应保留 CPU 路径。不要用固定的行数阈值当作"该不该上 GPU"的证据,必须用用户真实形状与真实硬件去实测。
- 选择最小的合适层次:优先使用有维护的库实现,其次才是自定义内核。技能给出的推荐顺序是:先尝试加速器/后端模式(
cudf.pandas、cuml.accel、nx-cugraph),覆盖不足时再迁移到原生 GPU API,只有 profiling 证明某个操作没有现成库实现时,才写自定义 kernel。
每种 CPU 库对应的首选 GPU 路径,可汇总为 decision_framework.md 中的选择表:
| 现有工作负载 | 首选路径 | 用途 |
|---|---|---|
| NumPy / SciPy | CuPy | 数组、稀疏矩阵、线性代数、FFT、信号处理 |
| pandas | cudf.pandas,然后cuDF | 先用加速器模式;原生 API 获得更多控制 |
| scikit-learn | cuml.accel,然后cuML | 先用加速器模式;必要时用原生估计器 |
| NetworkX | nx-cugraph,然后cuGraph | 先用后端分发;大规模用原生图 API |
| scikit-image | cuCIM | GPU 图像处理与全切片影像 |
| Faiss / Annoy / k-NN | cuVS | 精确与近似向量检索 |
| 原始或远程文件 IO | KvikIO | GPU 缓冲区与 GPUDirect Storage |
| 自定义数组内核 | Numba-CUDA-MLIR(新项目)/Numba-CUDA(存量代码) | 显式 SIMT 内核与共享内存 |
| 空间/可微内核 | Warp | 几何、仿真内核、机器人、自动微分 |
| 高层物理仿真 | Newton | 取代已移除warp.sim模块的维护引擎 |
| 底层 RAPIDS 原语 | RAFT(pylibraft) | 稀疏特征值求解器、资源、多 GPU 构件 |
注意:不要仅仅为了使用上述某个库,就把代码从 PyTorch、JAX、TensorFlow 等其他 GPU 原生框架中搬出来;应先在原框架内消除 CPU 往返,利用框架自带的编译器、profiler、混合精度与批处理能力。
环境准备:按 CUDA 版本安装对应库
所有转换代码都依赖正确的安装。本仓库的 installation.md 记录了与 RAPIDS 26.06(2026 年 6 月)对齐的安装约定:需要 Python >= 3.11、CUDA 12.x 或 13.x,每个受维护的 RAPIDS 包都提供-cu12与-cu13两种 wheel 变体,示例统一使用-cu12,CUDA 13 系统请替换为-cu13。独立示例中推荐使用uv add与该仓库约定一致;若用户项目已有其他包管理器,则遵循原项目。
# CuPy(选择正确的 CUDA 版本;CuPy 14+ 仅支持 CUDA 12/13) uv add "cupy-cuda12x==14.1.*" # CUDA 12.x uv add "cupy-cuda13x==14.1.*" # CUDA 13.x # Numba-CUDA 兼容路径(维护模式;自动安装 numba) uv add "numba-cuda[cu12]==0.30.*" # CUDA 13 用 [cu13] # Warp(仿真、空间计算、可微编程) uv add "warp-lang==1.15.*" # 内置 CUDA 12 运行时 # cuDF(RAPIDS DataFrame) uv add --extra-index-url=https://pypi.nvidia.com "cudf-cu12==26.6.*" # cuML(RAPIDS 机器学习) uv add --extra-index-url=https://pypi.nvidia.com "cuml-cu12==26.6.*" # cuGraph(RAPIDS 图分析)—— NVIDIA 索引仍然必需 uv add --extra-index-url=https://pypi.nvidia.com "cugraph-cu12==26.6.*" uv add --extra-index-url=https://pypi.nvidia.com "nx-cugraph-cu12==26.6.*" # KvikIO(高性能 GPU 文件 IO) uv add --extra-index-url=https://pypi.nvidia.com "kvikio-cu12==26.6.*" # cuCIM(RAPIDS 图像处理) uv add --extra-index-url=https://pypi.nvidia.com "cucim-cu12==26.6.*" # cuVS(RAPIDS 向量检索) uv add --extra-index-url=https://pypi.nvidia.com "cuvs-cu12==26.6.*" # cuSpatial(地理空间)—— 已归档:冻结在 25.04,仅限独立环境 uv add --extra-index-url=https://pypi.nvidia.com "cuspatial-cu12==25.4.*" # RAFT(底层 GPU 原语) uv add --extra-index-url=https://pypi.nvidia.com "pylibraft-cu12==26.6.*" uv add --extra-index-url=https://pypi.nvidia.com "raft-dask-cu12==26.6.*" # 多 GPU 可选安装后可参考 installation.md 中给出的验证片段逐个确认环境:CuPy 检查cp.cuda.runtime.getDeviceCount() >= 1;Numba 检查cuda.is_available();Warp 用wp.init();KvikIO 用kvikio.cufile_driver.get("is_gds_available")确认 GPUDirect Storage 是否就绪。
模式一:NumPy → CuPy——通常只需改 import
CuPy 是 NumPy/SciPy 兼容的 GPU 数组库,底层封装了 NVIDIA 优化库(cuBLAS、cuFFT、cuSOLVER、cuSPARSE、cuRAND),因此大多数标准数组运算开箱即已调优。多数 NumPy 代码只需把import numpy as np换成import cupy as cp:
# Before (CPU) import numpy as np a = np.random.rand(10_000_000) b = np.fft.fft(a) c = np.sort(b.real) # After (GPU) —— 常常只是改 import import cupy as cp a = cp.random.rand(10_000_000) b = cp.fft.fft(a) c = cp.sort(b.real)从源码层面看,cupy.md 详细说明了cupy.ndarray镜像numpy.ndarray的全部常用属性(shape、dtype、ndim、size、strides、T),并额外提供device属性标明数组所在 GPU。关键行为差异需要牢记:
- 两种数组不可隐式转换。
cupy.ndarray与numpy.ndarray之间的每一次转换都伴随一次主机-设备数据传输:cp.asarray(numpy_array)(已在当前设备上时零拷贝)与cp.array(numpy_array)(总是拷贝);回传用cp.asnumpy()或.get()。 - 约简返回 0 维数组而非标量:
cp.sum(a)返回 0 维cupy.ndarray而非 Python float,这是为避免隐式 GPU-CPU 同步;需要标量时用.item()。 - 越界索引静默回绕:NumPy 抛
IndexError,CuPy 不回绕不报错。 - 重复索引赋值结果未定义:
a[[0, 0]] = [1, 2]在 NumPy 存最后一个值,在 CuPy 是未定义(GPU 竞争)。 - 随机数支持
dtype参数(float32/float64),NumPy 总是返回 float64;不需要双精度时用dtype=cp.float32。
CuPy 的函数覆盖了 NumPy 主体与 SciPy 的大块内容:cupy.linalg(cuBLAS/cuSOLVER 驱动)、cupy.fft(cuFFT 驱动)、cupyx.scipy.sparse(CSR/CSC/COO,cuSPARSE 驱动,v14 起支持 64 位维度的超大稀疏矩阵)、cupyx.scipy.signal与cupyx.scipy.special。
零拷贝互操作:CuPy 数组实现了__cuda_array_interface__与 DLPack,可与 Numba、PyTorch、cuDF 直接互传——torch.as_tensor(cupy_array, device='cuda')、cp.asarray(torch_tensor)都是零拷贝。还可以写 CPU/GPU 无关代码:cp.get_array_module(x)会根据输入返回 numpy 或 cupy。
模式二:pandas → cuDF——零代码与原生 API 两条路
cuDF 提供 pandas 风格 API,全部在 GPU 上完成加载、连接、聚合、过滤与变换,构建于 Apache Arrow 列式内存格式之上。转换有两种方式:
# Before (CPU) import pandas as pd df = pd.read_parquet("large_data.parquet") result = df.groupby("category")["value"].mean() # After (GPU) —— 改 import import cudf df = cudf.read_parquet("large_data.parquet") result = df.groupby("category")["value"].mean() # 或零代码:python -m cudf.pandas your_script.pycudf.pandas加速器模式是本技能推荐的第一优先级。它把import pandas替换为同时包装 cuDF 与 pandas 的代理模块:每个操作先在 GPU(cuDF)上尝试,失败则自动回退 CPU(pandas)。激活方式有三种,详见 cudf.md:
# Jupyter/IPython(必须在任何 pandas import 之前) %load_ext cudf.pandas import pandas as pd # 从此 GPU 加速 # 命令行 # python -m cudf.pandas your_script.py # python -m cudf.pandas --profile your_script.py # 带 profiling # 编程方式 import cudf.pandas cudf.pandas.install() import pandas as pd # 从此 GPU 加速关键前提:如果会话中已 import 过 pandas,必须重启内核/进程。默认使用托管内存(managed memory),可以处理大于显存的数据集;还可用%%cudf.pandas.profile查看每个 cell 的 GPU/CPU 操作占比,用proxy_df.as_gpu_object()/as_cpu_object()取底层对象(注意取出后自动回退即失效)。若需强制 CPU-only,设CUDF_PANDAS_FALLBACK_MODE=1。
原生 cuDF API 与 pandas 的差异(迁移时必须处理):
- 结果顺序默认不确定(groupby、join 等):需要
sort=True或全局cudf.set_option("mode.pandas_compatible", True);cuDF 的 groupby 默认sort=False,与 pandas 相反。 - 全部类型可空,缺失值是
<NA>而非 NaN,使用独立 null mask;整数列含缺失值时保持整型(不做 float 强转)。 - 不支持迭代(
for val in series),objectdtype 只存字符串,不允许任意 Python 对象。 .apply()走 Numba JIT,UDF 内只支持数值类型与受限字符串操作;含中间字符串时需set_malloc_heap_size()分配堆空间。- 浮点结果可能因 GPU 并行操作顺序不同而有微小差异——必须用基于容差的比较。
迁移模式参考:cudf.md 给出 6 种常见迁移模式,包括"零成本cudf.pandas"、直接改 import、用向量化替换iterrows()、GPU 处理 + CPU 边界(只在绘图/导出时to_pandas())、以及"cuDF 不支持的行级数学交给 CuPy"(df[["x","y","z"]].to_cupy()→cp.linalg.norm)。IO 上优先 Parquet 而非 CSV(GPU 读取更快、支持谓词下推、压缩好);文件格式矩阵中 Avro/Text 为 GPU 加速只读,HDF5/Feather 回退 pandas。大规模数据用 Dask-cuDF(dask_cudf.read_parquet("path/")+.compute()/.persist())。
模式三:自定义 Python 循环 → Numba CUDA 内核
当代码中存在无法映射为数组运算的逐元素自定义逻辑时,把 Python 循环改写为 CUDA 内核。示例是把含math.sin/math.exp的串行循环改写为 SIMT 内核:
# Before (CPU) —— 慢速 Python 循环 def process(data, out): for i in range(len(data)): out[i] = math.sin(data[i]) * math.exp(-data[i]) # After (GPU) —— Numba 内核 from numba import cuda import math @cuda.jit def process(data, out): i = cuda.grid(1) if i < data.size: out[i] = math.sin(data[i]) * math.exp(-data[i]) d_data = cuda.to_device(data) d_out = cuda.device_array(d_data.shape, dtype=d_data.dtype) threads = 256 blocks = (len(data) + threads - 1) // threads processblocks, threads out = d_out.copy_to_host()结合 numba.md 的源码级说明,写内核时要注意:
- 层次模型:Grid(块集合)→ Block(线程组,最多 1024 线程/块,可共享片上内存并同步)→ Thread。
cuda.grid(ndim)取线程的全局索引;永远做边界检查(if i < array.size),否则越界线程静默破坏内存。 - 内核不能返回值,只能写入作为参数传入的输出数组。
- 内核启动是异步的:CPU 端读取结果前必须
cuda.synchronize()。 - 线程定位内建函数:
cuda.threadIdx、cuda.blockIdx、cuda.blockDim、cuda.gridDim、cuda.grid()、cuda.gridsize();用grid-stride 循环(for i in range(cuda.grid(1), data.shape[0], cuda.gridsize(1)))将网格尺寸与问题尺寸解耦。 - 数据复用用共享内存(
cuda.shared.array,静态或动态)+cuda.syncthreads()屏障;块内树形约简、Hillis-Steele 扫描、带 halo 的 stencil 模式在 reference 中均有完整实现。 - 原子操作:
cuda.atomic.add/sub/max/min/nanmax/nanmin/and_/or_/xor/exch/cas,全部返回旧值;add仅支持 int32/float32/float64。 - 设备函数用
@cuda.jit(device=True),可返回值;fastmath=True在不需要 IEEE-754 严格性时启用 FMA 与快速近似。 - 性能与调试:
@vectorize/@guvectorize可把标量函数广播成 GPU ufunc;块大小 1D 取 128–256、2D 取 (16,16)/(32,32);NUMBA_ENABLE_CUDASIM=1可在 CPU 上模拟调试内核。
工具链现状:Numba-CUDA 已进入维护模式,NVIDIA 新特性开发转向Numba-CUDA-MLIR。对既有numba.cuda代码、兼容性工作或 MLIR 尚未覆盖的特性,继续用numba-cuda;新内核项目请先评估 MLIR 及其迁移指南。不要在没有 profiling 排除 CuPy 等调优库之前贸然投资自定义内核。
模式四:NetworkX → cuGraph——原生 API 与零代码后端
图分析(PageRank、介数中心性、社区发现、最短路径)在大网络上应迁移到 cuGraph:
# Before (CPU) import networkx as nx G = nx.read_edgelist("edges.csv", delimiter=",", nodetype=int) pr = nx.pagerank(G) bc = nx.betweenness_centrality(G) # After (GPU) —— 直接 cuGraph API import cugraph import cudf edges = cudf.read_csv("edges.csv", names=["src", "dst"], dtype=["int32", "int32"]) G = cugraph.Graph() G.from_cudf_edgelist(edges, source="src", destination="dst") pr = cugraph.pagerank(G) bc = cugraph.betweenness_centrality(G) # 或零代码:NX_CUGRAPH_AUTOCONFIG=True python your_script.py决策框架(decision_framework.md)建议:先启用nx-cugraph后端并检查回退行为,当后端覆盖不足或有实测性能理由时,再迁移到带 cuDF 边列表的原生 cuGraph API;端到端基准必须包含图构建与主机-设备转换成本。cuGraph 的典型适用规模是 1 万条边以上的网络;PageRank、介数中心性、Louvain/Leiden 社区检测、BFS/SSSP、连通分量、链接预测与 GNN 采样均在其覆盖范围。
模式五:scikit-learn → cuML——先 cuml.accel 再原生估计器
机器学习训练、推理、预处理与超参搜索均可通过 cuML 加速。转换只需改 import(注意train_test_split也要换):
# Before (CPU) from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) scaler = StandardScaler() X_train = scaler.fit_transform(X_train) model = RandomForestClassifier(n_estimators=100) model.fit(X_train, y_train) # After (GPU) —— 改 import from cuml.ensemble import RandomForestClassifier from cuml.preprocessing import StandardScaler from cuml.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) scaler = StandardScaler() X_train = scaler.fit_transform(X_train) model = RandomForestClassifier(n_estimators=100) model.fit(X_train, y_train) # 或零代码:python -m cuml.accel your_script.pycuml.accel加速器模式在兼容性允许时优先尝试(零代码 sklearn 加速);迁移到原生 cuML API 的时机是:估计器不受支持、需要显式输出控制、或有实测性能理由。决策框架特别强调:不要依赖宣传中的加速倍数区间,要对用户的具体估计器、维度与端到端管线做基准。cuML 覆盖分类、回归、聚类、降维、预处理管线、模型推理,以及 UMAP、t-SNE、HDBSCAN、KNN 等大数据集场景;也支持直接接收 cuDF DataFrame。
模式六:物理仿真循环 → Warp 内核
Warp 专为空间计算设计——物理仿真、几何处理、机器人学与可微编程。它把@wp.kernel修饰的 Python 函数 JIT 编译为 CUDA,内置vec3、quat、transform等空间类型以及 Mesh、Volume、HashGrid、BVH 等空间原语。把粒子积分循环改写为 Warp 内核:
# Before (CPU) —— 对粒子做慢速 Python 循环 import numpy as np def integrate(positions, velocities, forces, dt): for i in range(len(positions)): velocities[i] += forces[i] * dt positions[i] += velocities[i] * dt # After (GPU) —— Warp 内核,JIT 编译到 CUDA import warp as wp @wp.kernel def integrate(positions: wp.array(dtype=wp.vec3), velocities: wp.array(dtype=wp.vec3), forces: wp.array(dtype=wp.vec3), dt: float): tid = wp.tid() velocities[tid] = velocities[tid] + forces[tid] * dt positions[tid] = positions[tid] + velocities[tid] * dt wp.launch(integrate, dim=num_particles, inputs=[positions, velocities, forces, 0.01], device="cuda")结合 warp.md 的关键要点:
- 内核参数必须有类型注解(Warp 从注解推断类型);
wp.tid()只在内核中可用,@wp.func用户函数需显式传线程索引。 - 2D/3D 启动:
wp.launch(kernel, dim=(nx, ny), ...),内核内i, j = wp.tid()。 - 可微性:Warp 自动生成前向与反向(伴随)内核;参与梯度的数组须
requires_grad=True,用wp.Tape()记录并tape.backward(loss),可与 PyTorch autograd / JAX JIT 集成。注意:in-place 数组修改不可微,需写独立输出数组。 - CUDA Graph 捕获:反复启动同一内核序列时,用
wp.ScopedCapture()捕获后wp.capture_launch(capture.graph)近乎零 CPU 开销回放——非常适合仿真主循环。 - 空间原语生命周期:Mesh / HashGrid / Volume / BVH 的 Python 对象必须在其
.id被内核使用期间保持存活,GC 过早回收会导致未定义行为。 - tile 编程:
wp.launch_tiled+wp.tile_load/matmul/store/sum提供类 Triton 的块级协作操作(共享内存 + Tensor Core),并支持wp.tile_bvh_query_aabb等空间查询。 - 高层物理引擎请用Newton(
warp.sim模块在 Warp 1.10 已移除);Warp 本身用于自定义内核与领域原语。
模式七:文件 IO → KvikIO——绕过 CPU 的 GPUDirect Storage
当 IO 是存储与 GPU 之间瓶颈时,用 KvikIO(cuFile 的 Python 绑定)让数据从 NVMe 或远程存储直接流入显存:
# Before —— CPU 中转(磁盘 → CPU → GPU) import numpy as np import cupy as cp data = np.fromfile("data.bin", dtype=np.float32) gpu_data = cp.asarray(data) # 额外经过一次 CPU 内存 # After —— 直达 GPU(磁盘 → GPU,经 GDS) import cupy as cp import kvikio gpu_data = cp.empty(1_000_000, dtype=cp.float32) with kvikio.CuFile("data.bin", "r") as f: f.read(gpu_data) # 用 GPUDirect Storage 绕过 CPU 内存 # 从 S3 直接读到 GPU with kvikio.RemoteFile.open_s3_url("s3://bucket/data.bin") as f: buf = cp.empty(f.nbytes() // 4, dtype=cp.float32) f.read(buf)kvikio.md 补充了完整 API:CuFile的阻塞方法read/write,非阻塞并行方法pread/pwrite(返回IOFuture,可重叠 IO 与计算),以及设备专用raw_read/raw_write系列;文件模式"r"/"w"/"a"/"+"。GDS 不可用时自动透明回退 POSIX IO。注意适用范围:
- 何时用:大二进制数据直读显存、显存数组直写磁盘、S3/HTTP/WebHDFS 直读 GPU、Zarr 数组 GPU 后端(GDSStore),或需要 IO 与计算流水线重叠。
- 何时不用:数据小于 1 MB(内核启动与 GDS 开销占主导);读取结构化格式(CSV/Parquet/JSON)应用 cuDF 自带的优化 reader;只需主机内存就用标准 Python IO。
- 安装后可用
kvikio.cufile_driver.get("is_gds_available")确认 GDS 是否配置。
模式八:GPU 数据准备 + 维护中的仪表盘栈(替代已停运的 cuxfilter)
cuxfilter 在 RAPIDS 26.06 发布了最终版本后停运。不要用 cuxfilter 启动新应用。正确的现代做法是:在 cuDF 中完成大批量变换与聚合,然后在显式可视化边界上只传输紧凑的展示数据:
# Before —— 静态 matplotlib/seaborn 图,无交互 import pandas as pd import matplotlib.pyplot as plt df = pd.read_parquet("large_dataset.parquet") fig, axes = plt.subplots(1, 2) df.plot.scatter(x="feature1", y="feature2", ax=axes[0]) df["category"].value_counts().plot.bar(ax=axes[1]) plt.show() # After —— GPU 数据准备 + 维护中的仪表盘栈 import cudf import hvplot.pandas # 在 pandas 对象上注册 .hvplot import panel as pn gpu_df = cudf.read_parquet("large_dataset.parquet") gpu_summary = ( gpu_df.groupby("category", as_index=False) .agg({"value_col": "mean"}) ) display_summary = gpu_summary.to_pandas() # 只传输约简后的结果 dashboard = pn.Column( "# Interactive Explorer", display_summary.hvplot.bar(x="category", y="value_col"), ) dashboard.servable()对于需要在大数据点上做联动选择(linked selections)的场景,使用 HoloViews/hvPlot + Datashader + Panel。实践原则:过滤/聚合回调尽量留在 GPU 上执行,文档记录每一次到 pandas 的转换;只有维护既有 26.06 应用时才阅读 cuxfilter 参考文档。
模式九:scikit-image → cuCIM——改 import 加一次 cp.asarray
图像处理(滤波、形态学、分割、特征检测)用 cuCIM 加速。cucim.skimage镜像 scikit-image API(200+ 个 GPU 加速函数),全部函数直接操作 CuPy 数组(零拷贝、全程在 GPU):
# Before (CPU) from skimage.filters import gaussian, sobel, threshold_otsu from skimage.morphology import binary_opening, disk from skimage.measure import label, regionprops_table import numpy as np blurred = gaussian(image, sigma=3) binary = blurred > threshold_otsu(blurred) cleaned = binary_opening(binary, footprint=disk(3)) labels = label(cleaned) props = regionprops_table(labels, image, properties=['area', 'centroid']) # After (GPU) —— 改 import,用 cp.asarray 包裹输入 from cucim.skimage.filters import gaussian, sobel, threshold_otsu from cucim.skimage.morphology import binary_opening, disk from cucim.skimage.measure import label, regionprops_table import cupy as cp image_gpu = cp.asarray(image) # 只传输一次 blurred = gaussian(image_gpu, sigma=3) binary = blurred > threshold_otsu(blurred) cleaned = binary_opening(binary, footprint=disk(3)) labels = label(cleaned) props = regionprops_table(labels, image_gpu, properties=['area', 'centroid'])适用场景:Gaussian/Sobel/Frangi 等滤波、形态学、阈值、连通分量标记、region properties、颜色空间转换、图像配准、去噪,以及数字病理中的全切片影像(WSI)读取与预处理管线;图像在 512×512 及以上的管道收益明显。cuCIM 还提供高性能 WSI reader(CuImage)。可与 CuPy 链式处理(cuCIM 原生操作 CuPy 数组),也可经 DLPack 零拷贝直通 PyTorch。
模式十:GeoPandas 点面包含 → cuSpatial(仅限 legacy 25.04)
cuSpatial 已归档,与当前 RAPIDS 包不兼容。仅在独立、固定到 25.04 的隔离环境中使用。point_in_polygon返回布尔成员矩阵,不是geopandas.sjoin的直接替代品:
# Before (CPU) import geopandas as gpd import numpy as np from shapely.geometry import Point points = gpd.GeoSeries([Point(x, y) for x, y in coords], crs="EPSG:4326") polygons = gpd.read_file("regions.geojson").geometry.iloc[:31] membership_cpu = np.column_stack( [points.within(polygon).to_numpy() for polygon in polygons] ) # After (GPU, legacy) —— 相同的逐点-逐多边形成员语义 import cuspatial points_gpu = cuspatial.from_geopandas(points) polygons_gpu = cuspatial.from_geopandas(polygons) membership_gpu = cuspatial.point_in_polygon(points_gpu, polygons_gpu)cuSpatial 的官方定位(decision_framework.md):仓库自 2025 年 7 月只读,最终版本 25.04,固定cudf-cu12==25.4.*因而与当前 RAPIDS 发行版冲突;没有官方继任者。新工作不要把它当作现行 GeoPandas 替代品——正确做法是把几何运算留在 GeoPandas/Shapely(CPU),用 cuDF 加速工作流中兼容的表格阶段。
模式十一:Faiss 精确检索 → cuVS 精确检索——先对齐算法语义再谈基准
向量检索迁移的纪律:先匹配算法语义,再做基准测试。用 cuVS 的 brute force 匹配 FaissIndexFlatL2的精确基线;只有当可接受近似结果时才用 CAGRA,并对照精确真值报告 recall@k——绝不把精确 CPU 算法与近似 GPU 算法当作等价物比较:
# Before (CPU) —— Faiss import faiss import numpy as np rng = np.random.default_rng(42) embeddings = rng.random((1_000_000, 128), dtype=np.float32) queries = rng.random((1_000, 128), dtype=np.float32) index = faiss.IndexFlatL2(128) index.add(embeddings) distances, neighbors = index.search(queries, k=10) # After (GPU) —— cuVS 精确暴力检索 import cupy as cp from cuvs.neighbors import brute_force embeddings_gpu = cp.asarray(embeddings) queries_gpu = cp.asarray(queries) index_gpu = brute_force.build(embeddings_gpu, metric="sqeuclidean") distances_gpu, neighbors_gpu = brute_force.search(index_gpu, queries_gpu, k=10)cuvs.md 的索引选择指南:CAGRA 是首选 ANN 候选(图结构、GPU 原生、最快);IVF-Flat 适合不做向量压缩的高召回 ANN;IVF-PQ 适合放不进显存的大数据集(压缩存储);Brute Force 适合小数据集或作真值;HNSW 用于由 GPU 构建索引、CPU 侧服务。度量支持sqeuclidean、inner_product、cosine等。不要用 RAFT 做向量检索——k-NN/IVFPQ/CAGRA 已全部迁往 cuVS。独立基准应分别测量 build、search、transfer 与序列化成本。
模式十二:scipy.sparse.linalg → RAFT——GPU 稀疏特征值求解
对大型稀疏对称矩阵的特征值问题(谱方法、图划分、大规模稀疏哈密顿量、结构振动模态),用 RAFT 的 GPU Lanczos 求解器替换scipy.sparse.linalg.eigsh:
# Before (CPU) import numpy as np from scipy.sparse import random as sparse_random from scipy.sparse.linalg import eigsh A = sparse_random(10000, 10000, density=0.01, format="csr", dtype=np.float32) A = A + A.T # 构造对称矩阵 eigenvalues, eigenvectors = eigsh(A, k=10, which="LM") # After (GPU) —— RAFT 稀疏特征值求解器 import cupy as cp import cupyx.scipy.sparse as sp_gpu from pylibraft.sparse.linalg import eigsh as gpu_eigsh A_gpu = sp_gpu.csr_matrix(A) # 传输到 GPU eigenvalues, eigenvectors = gpu_eigsh(A_gpu, k=10, which="LM")raft.md 给出了求解器的完整参数契约,迁移时应逐项核对:
| 参数 | 说明 |
|---|---|
A | 稀疏对称 CSR 矩阵(cupyx.scipy.sparse.csr_matrix) |
k | 要计算的特征值个数(默认 6),必须1 <= k < n |
which | 'LM'最大模(默认)、'LA'最大代数、'SA'最小代数、'SM'最小模 |
v0 | 起始向量(可选,默认随机) |
ncv | Lanczos 向量数,须k + 1 < ncv < n |
maxiter | 最大迭代次数 |
tol | 收敛容差(0 = 机器精度) |
seed | 随机种子(可复现性) |
handle | 可选的DeviceResources句柄 |
RAF T 的关键工程点:
DeviceResources是 CUDA 资源句柄(管理 stream、stream 池、cuBLAS/cuSOLVER 库句柄),创建一次并在多次调用间复用;可传入 CuPy stream(DeviceResources(stream=cupy_stream.ptr))。- RAF T 调用默认异步——必须在读取 CPU 侧结果前
handle.sync();不传handle时 RAF T 内部临时分配资源并在返回前同步(方便但重复调用更慢)。 - 错误稀疏格式:
eigsh只接受cupyx.scipy.sparse.csr_matrix,COO/CSC 需先转换;仅限实对称/Hermitian 矩阵;dtype 必须显式(float32/float64),不要依赖隐式转换。 - 互操作:
device_ndarray实现__cuda_array_interface__,CuPy、PyTorch、cuDF 数组可直接传入;输出类型可用pylibraft.config.set_output_as("cupy")/"torch"全局配置。 - RAF T 还提供 R-MAT 随机图生成(
pylibraft.random.rmat,适合图算法基准)与raft-dask的多节点多 GPU 通信(Comms类管理 NCCL/UCX)。 - 性能建议:复用
DeviceResources、批量同步、优先 float32、预分配输出、数据全程留在 GPU。
贯穿所有模式的工程原则
1. 保持连贯的 GPU 数据路径。输入只传输一次,中间结果保持设备驻留;复用分配、优先out=或 in-place 形式;批量小操作、融合逐元素工作以消除中间数组;只有 profiling 证明传输重叠带来收益时才使用 pinned host memory 与非默认 stream;契约允许时才选 float32/混合精度。
2. 先验证语义,再谈速度。在小而确定的 fixture 与代表性数据上对比 CPU/GPU 输出;浮点结果用显式容差;测试边界情况、NaN、排序与 dtype;对近似最近邻索引报告 recall@k;检查加速器警告与日志中的 CPU 回退。
3. 正确测量 GPU 代码。GPU 工作是异步的,CPU 定时器测量的是入队时间而非执行时间。预热上下文创建与 JIT 编译后,用 CUDA 事件或库感知计时器:
from cupyx.profiler import benchmark print(benchmark(gpu_function, (arg1, arg2), n_warmup=10, n_repeat=100))Notebook 中用%gpu_timeit,端到端时间线用 Nsight Systems(nsys),内核分析用 Nsight Compute(ncu)。同时报告同步内核时间与真实端到端延迟,生产环境需要承担传输与转换成本时把它们计入。
4. 保留、修订或拒绝移植。只有通过正确性检查并在代表性数据上改善了用户关心的指标时,才保留 GPU 路径;否则要解释限制因素到底是问题规模、传输、不支持回退、显存压力、启动粒度还是算法本身。需要可移植性时提供 CPU 回退;测试数值正确性(GPU 浮点可能因操作顺序与 CPU 略有不同);数据大于显存时考虑分块或 RAPIDS Dask 多 GPU。不同 GPU 库之间通过 CUDA Array Interface / DLPack 零拷贝互操作——组合使用(如 cuDF + cuML、cuGraph + cuML、cuCIM + PyTorch、KvikIO + CuPy、RAFT + CuPy)时,确认 device、dtype、连续性、所有权与 stream 语义,不要假设任何转换都是免费的。
完整的分库决策(何时是错误选择、如何组合)见 decision_framework.md,逐库安装与验证见 installation.md,各库的深入 API 参考(CuPy 内核融合与内存池、cuDF 字符串与 UDF、Warp tile 与可微编程、RAFT 设备内存管理等)可从 SKILL.md 的 Reference Files 索引进入。把这 12 组转换模式与"最小合适层次 + 证据驱动基准"的方法论结合,AI Agent 即可在保持数值契约的前提下,安全地把 CPU 科学计算工作负载迁上 NVIDIA GPU。
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考