Thanks for reaching out! There is some amount of CPUs that Ray will always use, since some of the background processes (Raylet, GCS, monitors) always run on CPUs. This useage however is pretty small and is needed to coordinate the execution of the program.
Now concerning your actual program (the task or actors): If a task has num_gpus > 0 it means that Ray will set CUDA_VISIBLE_DEVICES, which will instruct the deep learning library used in the task (if any) to use GPU. If there is lots of pure python code in the task or the task is not using a deep learning library (or otherwise GPU enabled library), it will still use CPUs. You can find more information about this in GPU Support — Ray v2.0.0.dev0.
The num_cpus=0 flag to ray.init will make sure Ray will not schedule CPU tasks (for example tasks have @ray.remote(num_cpu=1) by default). Tasks with num_cpu=0 will be scheduled, but those tasks may still use some CPU even if you set @ray.remote(num_gpu=1) if it is not offloading the computation to CUDA.
I hope this makes things clearer!