BizFrameworks and DeepLearning

ビジネスの分かる機械学習エンジニアを目指してます

TensorFlowチュートリアル和訳(GPUの利用)

TensorFlowのチュートリアルを随時和訳していきます。 第一回目はGPUの利用編です。

Supported devices

TensorFlowはCPUとGPUの利用をサポートします。これらはstringで以下のように表現します。

  • "/cpu:0" :マシンのCPU
  • "/gpu:0" :マシンのGPU(1つめ)
  • "/gpu:1" :マシンのGPU(2つ以上持っている場合は番号でサポート)

TensorFlow内の動作でCPUとGPUの両方が実装されている場合、GPUバイスにはそのデバイスに割り当てられた操作が優先されます。例えばmatmul(内積の計算)にはCPUカーネルGPUカーネルの両方があります。デバイスcpu:0gpu:0を持つシステムの場合、このmatmulの実行ではgpu:0が自動的に選択されます。

Logging Device placement

操作やテンソルが割り当てられているデバイスを調べる際にはlog_device_placementオプションをTrueに設定してセッションを作成します。

# グラフの作成
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
# log_device_placementをTrueに設定してセッションを作成
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# セッションを開始
print(sess.run(c))

このようにすると以下のようなoutputが得られます。

Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: Tesla K40c, pci bus
id: 0000:05:00.0
b: /job:localhost/replica:0/task:0/gpu:0
a: /job:localhost/replica:0/task:0/gpu:0
MatMul: /job:localhost/replica:0/task:0/gpu:0
[[ 22.  28.]
 [ 49.  64.]]

グラフの内容やmatmulの計算がgpu:0に割り当てられていることが分かりますね。

Manual device placement

自動的に選択されたデバイスを利用せずに、特定の動作を行うデバイスを指定したい場合はtf.deviceを使用することができます。これによってコンテキスト内のすべての操作が同じデバイス割り当てを持つようになります。

# グラフの作成
with tf.device('/cpu:0'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)
# log_device_placementをTrueに設定してセッションを作成
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# セッションを開始
print(sess.run(c))

これによって通常はgpu:0に割り当てられるa,bcpu:0に割り当てられるように変更されます。

Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: Tesla K40c, pci bus
id: 0000:05:00.0
b: /job:localhost/replica:0/task:0/cpu:0
a: /job:localhost/replica:0/task:0/cpu:0
MatMul: /job:localhost/replica:0/task:0/gpu:0
[[ 22.  28.]
 [ 49.  64.]]

Allowing GPU memory growth

デフォルトでは、TensorFlowはGPUのメモリのほぼ全てをプロセスにマッピングします。これはメモリの断片化を軽減するために行なっており、これによって貴重なGPUメモリ資源を効率的に使用することができます。

ただ、場合によっては利用可能なメモリのサブセットを用意して、基本的にはサブセットのみを割り当て、プロセスによってGPUの使用量を増加させた方が良い場合もあります。TensorFlowにはこれを制御するための2つのConfigオプションがあります。

1つ目はallow_growthオプションです。これはランタイム割り当てに基づいてGPUメモリを割り当てることができます。このオプションを利用する場合、最初プロセスには微小のGPUメモリしか割り当てられず、必要に応じてGPUメモリ領域を拡張していきます。ただ、メモリの断片化を防ぐためにメモリの解放はしないことに注意してください。 このオプションをオンにするには、以下の方法でConfigProtoに設定します。

config = tf.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.Session(config=config, ...)

2つ目はper_process_gpu_memory_fractionオプションです。このオプションでは、TensorFlowが見えているGPUメモリ量から、割り当てるメモリの割合を決めることができます。 例えば、以下のようにするとTensorFlowに各GPUの合計メモリの40%を割り当てることができます。

config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.4
session = tf.Session(config=config, ...)

このオプションはTensorFlowで利用可能なGPUメモリ量を制限したい時に便利な手法です。

Using a single GPU on a multi-GPU system

システムに複数のGPUがある場合、最も低いIDを持つGPUがデフォルトで選択されます。別のGPUで実行したい場合は、環境設定で明示的に指定する必要があります。

# グラフの作成
with tf.device('/gpu:2'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)
# log_device_placementをTrueに設定してセッションを作成
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# セッションを開始
print(sess.run(c))

指定したデバイスが存在しない場合、以下のようなInvalidArgumentErrorが出ます。

InvalidArgumentError: Invalid argument: Cannot assign a device to node 'b':
Could not satisfy explicit device specification '/gpu:2'
   [[Node: b = Const[dtype=DT_FLOAT, value=Tensor<type: float shape: [3,2]
   values: 1 2 3...>, _device="/gpu:2"]()]]

指定したデバイスが存在しない場合にエラーを吐かずに自動的にデバイスを選択して操作を実行したい時は、セッションの作成時の設定オプションでallow_soft_placementTrueにします。

# グラフの作成
with tf.device('/gpu:2'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)
# allow_soft_placementとlog_device_placementをTrueにしてセッションを作成
sess = tf.Session(config=tf.ConfigProto(
      allow_soft_placement=True, log_device_placement=True))
# セッションを開始
print(sess.run(c))

Using multiple GPUs

複数のGPUでTensorFlowを実行したい場合、multi-tower形式を構築します。これにより、各Towerごとで別のGPUを利用することができます。例えば、

# グラフの作成
c = []
for d in ['/gpu:2', '/gpu:3']:
  with tf.device(d):
    a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3])
    b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2])
    c.append(tf.matmul(a, b))
with tf.device('/cpu:0'):
  sum = tf.add_n(c)
# log_device_placementをTrueにしてセッションを作成
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# セッションを開始
print(sess.run(sum))

のようにすると以下のようなoutputが得られます。

Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: Tesla K20m, pci bus
id: 0000:02:00.0
/job:localhost/replica:0/task:0/gpu:1 -> device: 1, name: Tesla K20m, pci bus
id: 0000:03:00.0
/job:localhost/replica:0/task:0/gpu:2 -> device: 2, name: Tesla K20m, pci bus
id: 0000:83:00.0
/job:localhost/replica:0/task:0/gpu:3 -> device: 3, name: Tesla K20m, pci bus
id: 0000:84:00.0
Const_3: /job:localhost/replica:0/task:0/gpu:3
Const_2: /job:localhost/replica:0/task:0/gpu:3
MatMul_1: /job:localhost/replica:0/task:0/gpu:3
Const_1: /job:localhost/replica:0/task:0/gpu:2
Const: /job:localhost/replica:0/task:0/gpu:2
MatMul: /job:localhost/replica:0/task:0/gpu:2
AddN: /job:localhost/replica:0/task:0/cpu:0
[[  44.   56.]
 [  98.  128.]]

cifar10のチュートリアルは複数GPUを使用するのに良いトレーニングです。

終わりに

TensorFlowにおけるGPUの利用についてまとめました。初回から少し地味な内容ですが、ディープラーニングGPUは不可欠なので、最初に勉強しておいて損はないはずです。 また、GPUマシンは高価なので、複数人でマシンを共有して利用しているケースも少なくないと思います。このような場合は`per_process_gpu_memory_fractionによって個々人が利用できるGPUメモリを制限するのはとても大切ですね。