はじめに
対象者
PyCUDAをMacにインストールする手順を説明します。
以下に該当する方が対象となりえます。
- Python3.xを使用
- Chainer, Tensorflow, KerasなどのDeep Learningライブラリを使用しないでGPUで計算したい
- Mac上でPythonを使ってGPGPUプログラミングをしたい
- NVIDIA製のGPUを搭載したMac/Macbook Proを所有している
開発環境
使用したMacのスペックは以下の通り。
- Macbook Pro, 2014 Mid
- MacOS Sierra 10.12.6
- Core i7 2.8GHz/16GBメモリ
- NVIDIA GeForce GT 750M 2GB GDDR5
- Xcode 7.3.1 & Command Line Tools (Xcode 8.xはCUDA8が対応していません)
最後のXcodeについて補足ですが、Xcode8をインストール済みでもXcode7と共存させる方法がありますので後述します。
PyCUDA環境構築
Xcode7 Installation
記事執筆時ではXcode 7.3.1がXcode7では最新でした。Downloads for Apple DeveloperからSee more downloadsを選んで、検索ボックスに「Xcode 7」と入力します。Xcode 7.3.1とCommand Line Tools for Xcode 7.3.1をダウンロードします。
ダウンロードしたdmgファイルを開いてXcode 7.3.1をインストールします。続いてCommand Line Tools for Xcode7.3.1もインストールします。
続いて、Command Line Toolsのdmgを開いてをインストールします。
Xcode8がインストール済みの場合
Xcode8を既にインストールしているときは、Xcode7をXcode7.appとして/Applicationsにコピーします。Xcode7をアクティブにするには
$ sudo mv /Applications/Xcode.app /Applications/Xcode8.app
$ sudo mv /Applications/Xcode7.app /Applications/Xcode.app
$ sudo xcode-select -s /Applications/Xcode.app
逆にXcode8をアクティブにするには
$ sudo mv /Applications/Xcode.app /Applications/Xcode7.app
$ sudo mv /Applications/Xcode8.app /Applications/Xcode.app
$ sudo xcode-select -s /Applications/Xcode.app
CUDA/PyCUDAを使用するにはXcode7をアクティブにしてください。CUDA8はXcode8非対応です。
CUDA8 Installation
CUDAはNVIDIAが提供するGPGPUのためのライブラリ、コンパイルなどの統合開発環境です。要はNVIDIA製のGPUを使って並列計算するためのツール群です。PyCUDAがCUDAを必要とするためCUDA8をインストールします。
CUDA8 Installation
CUDA Toolkit Download | NVIDIA DeveloperからOS、バージョンを選んでパッケージをダウンロードします。インストーラーの指示通りインストールします。
インストール後、MacOSのシステム環境設定からCUDAを選んでInstall CUDA UpdateをクリックしてCUDAをアップデートします。
cuDNN Installation
NVIDIA cuDNN | NVIDIA Developerからcudnnをダウンロードします。メンバー登録が必要な場合があります。
ダウンロードしたcudnn-8.0-osx-x64-v6.0.tgzを解凍します。
$ tar zxvf cudnn-8.0-osx-x64-v6.0.tgz
ファイルをシステムにデプロイします。
$ sudo mv <解凍フォルダ>/lib/libcudnn* /usr/local/cuda/lib
$ sudo mv <解凍フォルダ>/include/cudnn.h /usr/local/cuda/include
DYLD_LIBRARY_PATHとPATHを設定
.bashrcに追加します。
export DYLD_LIBRARY_PATH=/usr/local/cuda/lib:/Developer/NVIDIA/CUDA-8.0/lib:$DYLD_LIBRARY_PATH
export PATH=/Developer/NVIDIA/CUDA-8.0/bin:$PATH
変更内容を反映。
$ source .bashrc
CUDAサンプルプログラム
サンプルプログラムでインストールを確認できます。ただしコンパイルに時間が結構かかりますのでスキップしても結構です。
$ /Developer/NVIDIA/CUDA-8.0/bin/cuda-install-samples-8.0.sh ~/Downloads
$ cd ~/Downloads
$ make -C 1_Utilities/deviceQuery
$ 1_Utilities/deviceQuery/deviceQuery
$ make -C 5_Simulations/smokeParticles
$ 5_Simulations/smokeParticles/smokeParticles
コンパイルエラーが出る場合は、Xcodeの切り替え、DYLD_LIBRARY_PATHとPATHの設定、システム環境設定の省エネルギーの設定(グラフィックスの自動切り替えをオフ)をチェックします。
Homebrew Installation
Homebrewのインストールがまだなら、
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”
Python3のインストールがまだなら、
$ brew install python3
Python3確認
$ python3 --version
pip Installation
pipのインストールがまだなら、
$ curl -kL https://raw.github.com/pypa/pip/master/contrib/get-pip.p
Numpy Installation
numpyのインストールがまだなら、
$ pip3 install numpy
$ pip3 install scipy
注意) Python2.xにNumpyがインストールされていても、Python3.x用にインストールする必要があります。
numpy確認
$ python3
>>> import numpy as np
PyCUDA Installation
いよいよPyCUDAのインストールです。
$ pip3 install pycuda
PyCUDA確認
$ python3 -c "import pycuda.autoinit
エラーが出なければとりあえずOKです。エラーが出る場合は、Xcode7への切り替え、DYLD_LIBRARY_PATHとPATHの設定、システム環境設定の省エネルギーの設定(グラフィックスの自動切り替えをオフ)を疑ってみてください。
続いて、scikit-cuda (skcuda)をインストールします。
$ pip3 install scikit-cuda
PyCUDAでGPGPUプログラミング
テストスクリプト test_gpu.py
import pycuda.autoinit
import pycuda.driver as cuda
from pycuda import gpuarray
from skcuda import linalg
import numpy as np
linalg.init()
a = np.random.rand(2, 1).astype(np.float32)
b = np.ones(2).astype(np.float32).reshape(1, 2)
a_gpu = gpuarray.to_gpu(a)
b_gpu = gpuarray.to_gpu(b)
c_gpu = linalg.dot(a_gpu, b_gpu)
c = c_gpu.get()
print(a)
print(b)
# 内積をCPUで計算した結果
print(np.dot(a, b))
# 内積をGPUで計算した結果
print(c)
上のスクリプトをtest_gpu.pyとして保存して実行します。
$ python3 test_gpu.py
[[ 0.85600704]
[ 0.02441464]]
[[ 1. 1.]]
[[ 0.85600704 0.85600704]
[ 0.02441464 0.02441464]]
[[ 0.85600704 0.85600704]
[ 0.02441464 0.02441464]]
テストスクリプトtest_gpu.pyを説明します。
まず、PythonでGPUを利用するための初期化処理等の準備をします。
import pycuda.autoinit
...
linalg.init()
GPUで処理する前に一旦Numpyのndarrayを作ります。データタイプはfloat32を使用します。
a = random.rand(2, 1).astype(np.float32)
b = np.ones(2).astype(np.float32).reshape(1, 2)
次にndarrayをgpuarrayに変換します。
a_gpu = gpuarray.to_gpu(a)
b_gpu = gpuarray.to_gpu(b)
この処理でCPU用のメモリからGPU用のメモリに配列データが変換・転送されます(CPU→GPU)。
Numpyのnp.dotに相当するGPU内積演算です。
c_gpu = linalg.dot(ga, gb)
結果を確認するにはgpuarray (GPU)からndarray (CPU)に戻す必要があります。
c = c_gpu.get()
print(c)
今度はGPU→CPUの流れです。
PyCUDAプログラミングの注意点
PyCUDAを利用したプログラミングでは、CPUとGPUを行き来するため、変数がndarrayかgpuarrayかを常に意識することが重要です。また、CPU/GPU間のデータ変換はパフォーマンスに大きな影響を与えるため、最小限に止めることが望ましいです。
GPUメモリが少ないMacでgpuarrayサイズを大きくすると、システムが極端にもっさりして、簡単に操作不能に陥ります。GPUメモリの少ないマシンでスクリプトを実行する際は、ウィンドウを閉じてアプリケーションを完全に終了した方が無難です。特にSafariは結構メモリを消費するので、ご注意ください。
便利な関数
gpuarrayとlinalgには下記のような便利な関数があります。ただし、x, y, zをgpuarrayとします。
- linalg.dot(x, y): 内積
- linalg.transpose(x): 転置行列
- gpuarray.max(x): 最大値
- abs(x): 絶対値
- x.shape: ndarrayのshapeと同じ
- gpuarray.if_positive(x > z, x, z): zが0ならrelu
- linalg.mdot(x, y): reshapeすることで、外積として利用できる