1. CuPy入门:CuPy是什么、与NumPy的关系、安装配置、GPU环境检查
1.1 CuPy是什么?
简单来说,CuPy就是NumPy的GPU版本。我最初接触时,第一反应是“这不就是把数组操作搬到显卡上吗?”没错,它的核心就是让你用几乎一样的API,在GPU上做并行计算。你写过的np.array()、np.dot(),换成cp.array()、cp.dot(),就能自动跑在CUDA核心上。底层靠的是CUDA和cuDNN这些库,但CuPy帮你把调用细节全封装好了。
我在项目中遇到过几次:数据量一大,NumPy跑个聚类要等半天。换成CuPy后,同样是K-Means,训练时间从十几分钟压缩到几十秒。当然,不是所有场景都适合——小数据或频繁CPU-GPU传输时反而更慢。这个后面会细讲。
1.2 与NumPy的关系
CuPy和NumPy就像双胞胎。API长得几乎一样,但一个在CPU上跑,一个在GPU上跑。下面的表格可以帮你快速对比:
| 维度 | NumPy | CuPy |
|---|---|---|
| 运行设备 | CPU (内存) | GPU (显存) |
| 数据类型 | ndarray |
cupy.ndarray |
| 核心库 | BLAS, LAPACK 等 | CUDA, cuBLAS, cuDNN |
| 并行能力 | 多线程 (CPU核心) | 数千个CUDA核心 |
| 适用数据量 | 中小规模 (GB级以内) | 大规模 (显存GB/TB级) |
| API兼容性 | 参考基准 | 约90%接口一致 |
你想想看,如果你已经会用NumPy,上手CuPy基本就是改个import的事。但有几个细节要留意:
- 显存有限:一个
float32的1亿元素数组占用约400MB显存,别把整机内存都往里塞。 - 数据传输延迟:每次
cupy.asnumpy()都要从显存拷贝回内存,频繁操作会拖慢速度。 - 部分函数未实现:比如某些复杂的排序算法,CuPy还没覆盖全。碰到时可以用
cp.asnumpy()回退到NumPy处理。
我个人习惯:先在NumPy上验证小数据,再切到CuPy跑全量。这样既能保正逻辑正确,又能享受加速红利。
1.3 安装配置
安装CuPy并不复杂,但关键的一步是选对CUDA版本对应的包。我之前踩过一个坑:机器上装的CUDA 11.7,我直接pip install cupy,结果装了个cupy-cuda12x的版本,跑起来报错说找不到CUDA运行时。
正确做法是先查你的CUDA版本:
nvidia-smi | grep "CUDA Version"
比如看到CUDA Version: 11.7,那就装cupy-cuda11x系列:
pip install cupy-cuda11x
如果你用的是CUDA 12.x,就换:
pip install cupy-cuda12x
另外,官方还提供cupy-cuda101、cupy-cuda110等更细分的版本,但日常用最常见的版本号就好。我建议用虚拟环境安装,避免污染全局Python。比如:
python -m venv cupy_env
source cupy_env/bin/activate # Linux/Mac
cupy_env\Scripts\activate # Windows
pip install cupy-cuda11x
如果你没有NVIDIA显卡,或者CUDA版本特别老(比如CUDA 10.x),可以考虑用cupy-rocm(AMD ROCm)或者纯CPU模式跑CuPy做测试——但那就没有加速意义了。
pip show cupy-cuda11x确认版本。如果看到类似Requires: fastrlock, numpy,说明依赖正确。
1.4 GPU环境检查
装好之后,咱们得确认CuPy真的能调用到GPU。先跑个基本的硬件检查:
nvidia-smi
你会看到GPU型号、显存、温度、进程等信息。如果没输出,说明驱动没装好或者没有NVIDIA显卡——CuPy就没法用了。
然后进Python验证:
import cupy as cp
print(cp.__version__)
print(cp.cuda.runtime.getDeviceCount()) # GPU数量
print(cp.cuda.runtime.getDeviceProperties(0)['name']) # 第一块显卡名字
输出类似:
12.0.0
1
'NVIDIA GeForce RTX 3090'
如果getDeviceCount()返回0,要么驱动没装好,要么CuPy链接的CUDA版本不对。解决办法:卸载当前CuPy,重新装匹配CUDA的版本。
我还习惯跑一个简单的性能测试,看看加速效果:
import numpy as np
import cupy as cp
import time
n = 10000
a_np = np.random.randn(n, n).astype(np.float32)
b_np = np.random.randn(n, n).astype(np.float32)
a_cp = cp.asarray(a_np)
b_cp = cp.asarray(b_np)
# CPU 矩阵乘法
t0 = time.time()
c_np = np.dot(a_np, b_np)
t1 = time.time()
print(f"NumPy: {t1-t0:.4f} s")
# GPU 矩阵乘法
t0 = time.time()
c_cp = cp.dot(a_cp, b_cp)
cp.cuda.Stream.null.synchronize() # 等待GPU完成
t1 = time.time()
print(f"CuPy: {t1-t0:.4f} s")
在我机器上,NumPy大约2.1秒,CuPy只要0.08秒——快了25倍。这个差距会随矩阵增大而更明显。
cp.cuda.Stream.null.synchronize(),否则时间测量会不准确。这块儿容易出错,我建议每次测速都用同步。
好了,到此你已经掌握了CuPy的基本概念、安装方法和环境验证。其实说白了,就是让NumPy代码“坐火箭”——把任务扔给GPU的几千个核心并行处理。接下来你就可以开始用CuPy写聚类和训练脚本了。不过别急,记得先跑通上面的测试代码,确保你的环境没问题。