5.6. GPU (Grafik İşleme Birimi)
Open the notebook in Colab
Open the notebook in Colab
Open the notebook in Colab
Open the notebook in SageMaker Studio Lab

Section 1.5 için de, son yirmi yılda hesaplamanın hızlı büyümesini tartıştık. Özetle, GPU performansı 2000 yılından bu yana her on yılda bir 1000 kat artmıştır. Bu büyük fırsatlar sunarken aynı zamanda bu tür bir performansın sağlanması için önemli bir gereksinim olduğunu da göstermektedir.

Bu bölümde, araştırmanız için bu hesaplamalı performanstan nasıl yararlanılacağını tartışmaya başlıyoruz. Öncelikle tek GPU’ları kullanarak ve daha sonra, birden çok GPU ve birden çok sunucuyu (birden çok GPU ile) nasıl kullanacağınızı tartışacağız.

Özellikle, hesaplamalar için tek bir NVIDIA GPU’nun nasıl kullanılacağını tartışacağız. İlk olarak kurulu en az bir NVIDIA GPU’nuz olduğundan emin olun. Ardından, NVIDIA sürücüsünü ve CUDA’yı indirin ve uygun yere kurmak için istemleri takip edin. Bu hazırlıklar tamamlandıktan sonra, nvidia-smi, komutu grafik kartı bilgilerini görüntülemek için kullanılabilir.

!nvidia-smi
Mon Oct 17 22:06:22 2022
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.106.00   Driver Version: 460.106.00   CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  Off  | 00000000:00:1B.0 Off |                    0 |
| N/A   37C    P0    49W / 300W |   1760MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  Tesla V100-SXM2...  Off  | 00000000:00:1C.0 Off |                    0 |
| N/A   37C    P0    39W / 300W |      3MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   2  Tesla V100-SXM2...  Off  | 00000000:00:1D.0 Off |                    0 |
| N/A   42C    P0   185W / 300W |   5472MiB / 16160MiB |      8%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   3  Tesla V100-SXM2...  Off  | 00000000:00:1E.0 Off |                    0 |
| N/A   40C    P0   202W / 300W |   5408MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    2   N/A  N/A     42922      C   ...l-tr-release-0/bin/python     5469MiB |
|    3   N/A  N/A     42922      C   ...l-tr-release-0/bin/python     5405MiB |
+-----------------------------------------------------------------------------+

Bir MXNet tensörünün bir NumPy ndarray’i ile neredeyse aynı göründüğünü fark etmiş olabilirsiniz.

Ancak birkaç önemli farklılık var. MXNet’i NumPy’den ayıran temel özelliklerden biri, çeşitli donanım aygıtlarına desteğidir.

MXNet’te her dizilimin (array) bir bağlamı vardır. Şimdiye kadar, varsayılan olarak, tüm değişkenler ve ilişkili hesaplamalar CPU’ya atanmıştır. Tipik olarak, diğer bağlamlar çeşitli GPU’lar olabilir. İşleri birden çok sunucuya dağıttığımızda işler arap saçına dönebilir. Dizilimleri bağlamlara akıllıca atayarak, cihazlar arasında veri aktarımında harcanan zamanı en aza indirebiliriz. Örneğin, GPU’lu bir sunucuda sinir ağlarını eğitirken, genellikle modelin parametrelerinin GPU’da kalmasını tercih ederiz.

Ardından, MXNet’in GPU sürümünün kurulu olduğunu onaylamamız gerekiyor. MXNet’in bir CPU sürümü zaten kuruluysa, önce onu kaldırmamız gerekir. Örneğin, pip uninstall mxnet komutunu kullanın, ardından CUDA sürümünüze göre ilgili MXNet sürümünü kurun. CUDA 10.0’un kurulu olduğunu varsayarsak, CUDA 10.0’u destekleyen MXNet sürümünü pip install mxnet-cu100 aracılığıyla kurabilirsiniz.

!nvidia-smi
Mon Oct 17 22:48:03 2022
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.106.00   Driver Version: 460.106.00   CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  Off  | 00000000:00:1B.0 Off |                    0 |
| N/A   39C    P0    50W / 300W |   1760MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  Tesla V100-SXM2...  Off  | 00000000:00:1C.0 Off |                    0 |
| N/A   38C    P0    50W / 300W |   1542MiB / 16160MiB |     30%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   2  Tesla V100-SXM2...  Off  | 00000000:00:1D.0 Off |                    0 |
| N/A   38C    P0    43W / 300W |      3MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   3  Tesla V100-SXM2...  Off  | 00000000:00:1E.0 Off |                    0 |
| N/A   36C    P0    53W / 300W |   1432MiB / 16160MiB |     22%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    1   N/A  N/A     32127      C   ...l-tr-release-0/bin/python     1539MiB |
|    3   N/A  N/A     32127      C   ...l-tr-release-0/bin/python     1429MiB |
+-----------------------------------------------------------------------------+

PyTorch’ta her dizilimin bir aygıtı vardır, biz onu genellikle bağlam olarak adlandırırız. Şimdiye kadar, varsayılan olarak, tüm değişkenler ve ilişkili hesaplama CPU’ya atanmıştır. Tipik olarak, diğer bağlamlar çeşitli GPU’lar olabilir. İşleri birden çok sunucuya dağıttığımızda işler arap saçına dönebilir. Dizileri bağlamlara akıllıca atayarak, cihazlar arasında veri aktarımında harcanan zamanı en aza indirebiliriz. Örneğin, GPU’lu bir sunucuda sinir ağlarını eğitirken, genellikle modelin parametrelerinin GPU’da kalmasını tercih ederiz.

Ardından, PyTorch’un GPU sürümünün kurulu olduğunu onaylamamız gerekiyor. PyTorch’un bir CPU sürümü zaten kuruluysa, önce onu kaldırmamız gerekir. Örneğin, pip uninstall torch komutunu kullanın, ardından CUDA sürümünüze göre ilgili PyTorch sürümünü kurun. CUDA 10.0’un kurulu olduğunu varsayarsak, CUDA 10.0’u destekleyen PyTorch sürümünü pip install torch-cu100 aracılığıyla kurabilirsiniz.

!nvidia-smi
Mon Oct 17 23:20:50 2022
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.106.00   Driver Version: 460.106.00   CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  Off  | 00000000:00:1B.0 Off |                    0 |
| N/A   36C    P0    49W / 300W |   1760MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  Tesla V100-SXM2...  Off  | 00000000:00:1C.0 Off |                    0 |
| N/A   36C    P0    35W / 300W |      3MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   2  Tesla V100-SXM2...  Off  | 00000000:00:1D.0 Off |                    0 |
| N/A   34C    P0    36W / 300W |      3MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   3  Tesla V100-SXM2...  Off  | 00000000:00:1E.0 Off |                    0 |
| N/A   32C    P0    37W / 300W |      3MiB / 16160MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

Bu bölümdeki programları çalıştırmak için en az iki GPU’ya ihtiyacınız var. Bunun çoğu masaüstü bilgisayar için abartılı olabileceğini, ancak bulutta (ör. AWS EC2 çoklu-GPU bulut sunucularını kullanarak) kolayca kullanılabileceğini unutmayın. Hemen hemen diğer bütün bölümler birden fazla GPU gerektirmez. Bunun burdaki kullanım nedeni, basitçe verilerin farklı cihazlar arasında nasıl aktığını göstermektir.

5.6.1. Hesaplama Cihazları

Depolama ve hesaplama için CPU ve GPU gibi cihazları belirtebiliriz. Varsayılan olarak, ana bellekte tensörler oluşturulur ve ardından bunu hesaplamak için CPU’yu kullanır.

MXNet’te CPU ve GPU, cpu() ve gpu() ile gösterilebilir. cpu() (veya parantez içindeki herhangi bir tam sayı), tüm fiziksel CPU’lar ve bellek anlamına gelir. Bu, MXNet’in hesaplamalarının tüm CPU çekirdeklerini kullanmaya çalışacağı anlamına gelir. Ancak, gpu() yalnızca bir kartı ve ona denk gelen belleği temsil eder. Birden fazla GPU varsa, \(i.\) GPU’yu (\(i\) 0’dan başlar) temsil etmek için gpu(i)’yu kullanırız. Ayrıca, gpu(0) ve gpu() eşdeğerdir.

from mxnet import np, npx
from mxnet.gluon import nn

npx.set_np()

npx.cpu(), npx.gpu(), npx.gpu(1)
(cpu(0), gpu(0), gpu(1))

PyTorch’ta CPU ve GPU, torch.device('cpu') ve torch.device('cuda') ile gösterilebilir. cpu aygıtının tüm fiziksel CPU’lar ve bellek anlamına geldiğine dikkat edilmelidir. Bu, PyTorch’un hesaplamalarının tüm CPU çekirdeklerini kullanmaya çalışacağı anlamına gelir. Bununla birlikte, bir gpu cihazı yalnızca bir kartı ve ona denk gelen belleği temsil eder. Birden çok GPU varsa, \(i.\) GPU (\(i\) 0’dan başlar) temsil etmek için torch.device(f'cuda:{i}')yi kullanırız. Ayrıca gpu:0 ve gpu eşdeğerdir.

import torch
from torch import nn

torch.device('cpu'), torch.device('cuda'), torch.device('cuda:1')
(device(type='cpu'), device(type='cuda'), device(type='cuda', index=1))
import tensorflow as tf

tf.device('/CPU:0'), tf.device('/GPU:0'), tf.device('/GPU:1')
(<tensorflow.python.eager.context._EagerDeviceContext at 0x7f2f4c078600>,
 <tensorflow.python.eager.context._EagerDeviceContext at 0x7f2f4ba25980>,
 <tensorflow.python.eager.context._EagerDeviceContext at 0x7f2f4ba25500>)

Mevcut GPU adetini sorgulayabiliriz.

npx.num_gpus()
2
torch.cuda.device_count()
2
len(tf.config.experimental.list_physical_devices('GPU'))
2

Şimdi, istenen GPU’lar var olmasa bile kodları çalıştırmamıza izin veren iki kullanışlı işlev tanımlıyoruz.

def try_gpu(i=0):  #@save
    """Varsa gpu(i) döndürün, aksi takdirde cpu() döndürün."""
    return npx.gpu(i) if npx.num_gpus() >= i + 1 else npx.cpu()

def try_all_gpus():  #@save
    """Mevcut tüm GPU'ları veya GPU yoksa [cpu()] döndürün."""
    devices = [npx.gpu(i) for i in range(npx.num_gpus())]
    return devices if devices else [npx.cpu()]

try_gpu(), try_gpu(10), try_all_gpus()
(gpu(0), cpu(0), [gpu(0), gpu(1)])
def try_gpu(i=0):  #@save
    """Varsa gpu(i) döndürün, aksi takdirde cpu() döndürün."""
    if torch.cuda.device_count() >= i + 1:
        return torch.device(f'cuda:{i}')
    return torch.device('cpu')

def try_all_gpus():  #@save
    """Mevcut tüm GPU'ları veya GPU yoksa [cpu()] döndürün."""
    devices = [torch.device(f'cuda:{i}')
             for i in range(torch.cuda.device_count())]
    return devices if devices else [torch.device('cpu')]

try_gpu(), try_gpu(10), try_all_gpus()
(device(type='cuda', index=0),
 device(type='cpu'),
 [device(type='cuda', index=0), device(type='cuda', index=1)])
def try_gpu(i=0):  #@save
    """Varsa gpu(i) döndürün, aksi takdirde cpu() döndürün."""
    if len(tf.config.experimental.list_physical_devices('GPU')) >= i + 1:
        return tf.device(f'/GPU:{i}')
    return tf.device('/CPU:0')

def try_all_gpus():  #@save
    """Mevcut tüm GPU'ları veya GPU yoksa [cpu()] döndürün."""
    num_gpus = len(tf.config.experimental.list_physical_devices('GPU'))
    devices = [tf.device(f'/GPU:{i}') for i in range(num_gpus)]
    return devices if devices else [tf.device('/CPU:0')]

try_gpu(), try_gpu(10), try_all_gpus()
(<tensorflow.python.eager.context._EagerDeviceContext at 0x7f2f4006ac40>,
 <tensorflow.python.eager.context._EagerDeviceContext at 0x7f2f4006afc0>,
 [<tensorflow.python.eager.context._EagerDeviceContext at 0x7f2f4006e040>,
  <tensorflow.python.eager.context._EagerDeviceContext at 0x7f2f4006e0c0>])

5.6.2. Tensörler and GPUlar

Varsayılan olarak, CPU’da tensörler oluşturulur. Tensörün bulunduğu cihazı sorgulayabiliriz.

x = np.array([1, 2, 3])
x.ctx
cpu(0)
x = torch.tensor([1, 2, 3])
x.device
device(type='cpu')
x = tf.constant([1, 2, 3])
x.device
'/job:localhost/replica:0/task:0/device:GPU:0'

Birden çok terimle çalışmak istediğimizde, aynı bağlamda olmaları gerektiğine dikkat etmemiz önemlidir. Örneğin, iki tensörü toplarsak, her iki argümanın da aynı cihazda olduğundan emin olmamız gerekir—aksi takdirde çerçeve, sonucu nerede saklayacağını ve hatta hesaplamayı nerede gerçekleştireceğine nasıl karar vereceğini bilemez.

5.6.2.1. GPU’da Depolama

GPU’da bir tensör depolamanın birkaç yolu vardır. Örneğin, bir tensör oluştururken bir depolama cihazı belirleyebiliriz. Sonra, ilk gpu’da tensör değişkeni X’i oluşturuyoruz. Bir GPU’da oluşturulan tensör yalnızca o GPU’nun belleğini harcar. GPU bellek kullanımını görüntülemek için nvidia-smi komutunu kullanabiliriz. Genel olarak, GPU bellek sınırını aşan veriler oluşturmadığımızdan emin olmamız gerekir.

X = np.ones((2, 3), ctx=try_gpu())
X
array([[1., 1., 1.],
       [1., 1., 1.]], ctx=gpu(0))
X = torch.ones(2, 3, device=try_gpu())
X
tensor([[1., 1., 1.],
        [1., 1., 1.]], device='cuda:0')
with try_gpu():
    X = tf.ones((2, 3))
X
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[1., 1., 1.],
       [1., 1., 1.]], dtype=float32)>

En az iki GPU’ya sahip olduğunuzu varsayarak, aşağıdaki kod ikinci GPU’da keyfi bir tensor oluşturacaktır.

Y = np.random.uniform(size=(2, 3), ctx=try_gpu(1))
Y
array([[0.67478997, 0.07540122, 0.9956977 ],
       [0.09488854, 0.415456  , 0.11231736]], ctx=gpu(1))
Y = torch.rand(2, 3, device=try_gpu(1))
Y
tensor([[0.5575, 0.9225, 0.4448],
        [0.2045, 0.3821, 0.9360]], device='cuda:1')
with try_gpu(1):
    Y = tf.random.uniform((2, 3))
Y
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[0.60182166, 0.2831906 , 0.12088335],
       [0.911641  , 0.5682545 , 0.8583329 ]], dtype=float32)>

5.6.2.2. Kopyalama

X + Y’yi hesaplamak istiyorsak, bu işlemi nerede gerçekleştireceğimize karar vermemiz gerekir. Örneğin Fig. 5.6.1 içinde gösterildiği gibi, X’i ikinci GPU’ya aktarabilir ve işlemi orada gerçekleştirebiliriz. Sadece X ve Y’yi toplamayın, çünkü bu bir istisnayla sonuçlanacaktır. Koşma zamanı motoru ne yapacağını bilemez: Veriyi aynı cihazda bulamaz ve başarısız olur. Çünkü Y ikinci GPU’da bulunur, ikisini toplamadan önce X’i taşımamız gerekir.

../_images/copyto.svg

Fig. 5.6.1 İşlemi yapmadan önce verileri aynı cihaza kopyalayın.

Z = X.copyto(try_gpu(1))
print(X)
print(Z)
[[1. 1. 1.]
 [1. 1. 1.]] @gpu(0)
[[1. 1. 1.]
 [1. 1. 1.]] @gpu(1)
Z = X.cuda(1)
print(X)
print(Z)
tensor([[1., 1., 1.],
        [1., 1., 1.]], device='cuda:0')
tensor([[1., 1., 1.],
        [1., 1., 1.]], device='cuda:1')
with try_gpu(1):
    Z = X
print(X)
print(Z)
tf.Tensor(
[[1. 1. 1.]
 [1. 1. 1.]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[1. 1. 1.]
 [1. 1. 1.]], shape=(2, 3), dtype=float32)

Artık veriler aynı GPU’da olduğuna göre (hem Z hem de Y), onları toplayabiliriz.

Y + Z
array([[1.6747899, 1.0754012, 1.9956977],
       [1.0948886, 1.415456 , 1.1123173]], ctx=gpu(1))

Z değişkeninizin halihazırda ikinci GPU’nuzda olduğunu hayal edin. Gene de Z.copyto(gpu(1)) çağırırsak ne olur? Değişken istenen cihazda zaten bulunsa bile, yeni bir kopya oluşturacak ve bellek tahsis edecektir. Kodumuzun çalıştığı ortama bağlı olarak, aynı cihazda iki değişkenin zaten var olduğu zamanlar vardır. Dolayısıyla, değişkenler şu anda farklı cihazlarda bulunuyorsa yalnızca bir kopya yapmak isteriz. Bu durumlarda, as_in_ctx’i çağırabiliriz. Değişken zaten belirtilen cihazda bulunuyorsa, bu işlem-yok (no-op) demektir. Özellikle bir kopya yapmak istemediğiniz sürece, as_in_ctx tercih edilen yöntemdir.

Z.as_in_ctx(try_gpu(1)) is Z
True
Y + Z
tensor([[1.5575, 1.9225, 1.4448],
        [1.2045, 1.3821, 1.9360]], device='cuda:1')

Z değişkeninizin halihazırda ikinci GPU’nuzda var olduğunu hayal edin. Gene de Z.cuda(1) diye çağırırsak ne olur? Kopyalamak ve yeni bellek ayırmak yerine Z’yi döndürür.

Z.cuda(1) is Z
True
Y + Z
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[1.6018217, 1.2831906, 1.1208833],
       [1.911641 , 1.5682545, 1.8583329]], dtype=float32)>

Z değişkeninizin halihazırda ikinci GPU’nuzda ver olduğunu hayal edin. Aynı cihaz kapsamı altında gene de Z2 = Z’yi çağırırsak ne olur? Kopyalamak ve yeni bellek ayırmak yerine Z’yi döndürür.

with try_gpu(1):
    Z2 = Z
Z2 is Z
True

5.6.2.3. Ek Notlar

İnsanlar hızlı olmalarını beklediklerinden makine öğrenmesi için GPU’ları kullanıyorlar. Ancak değişkenlerin cihazlar arasında aktarılması yavaştır. Bu yüzden, yapmanıza izin vermeden önce yavaş bir şey yapmak istediğinizden %100 emin olmanızı istiyoruz. Derin öğrenme çerçevesi kopyayı çökmeden otomatik olarak yaptıysa, yavaş çalışan bir kod yazdığınızı fark etmeyebilirsiniz.

Ayrıca, cihazlar (CPU, GPU’lar ve diğer makineler) arasında veri aktarımı, hesaplamadan çok daha yavaş bir şeydir. Ayrıca, daha fazla işlem ile ilerlemeden önce verilerin gönderilmesini (veya daha doğrusu alınmasını) beklememiz gerektiğinden bu paralelleştirmeyi çok daha zor hale getirir. Bu nedenle kopyalama işlemlerine büyük özen gösterilmelidir. Genel bir kural olarak, birçok küçük işlem, tek bir büyük işlemden çok daha kötüdür. Dahası, bir seferde birkaç işlem, koda serpiştirilmiş birçok tek işlemden çok daha iyidir, ne yaptığınızı biliyorsanız o ayrı. Bu durumda, bir aygıtın bir şey yapmadan önce bir diğerini beklemesi gerektiğinde bu tür işlemler onu engelleyebilir. Bu biraz, kahvenizi telefonla ön sipariş vermek ve siz istediğinizde hazır olduğunu öğrenmek yerine sırada bekleyerek sipariş etmek gibidir.

Son olarak, tensörleri yazdırdığımızda veya tensörleri NumPy formatına dönüştürdüğümüzde, veri ana bellekte değilse, çerçeve onu önce ana belleğe kopyalayacak ve bu da ek iletim yüküne neden olacaktır. Daha da kötüsü, şimdi Python’un her şeyi tamamlanmasını beklemesine neden olan o korkunç global yorumlayıcı kilidine tabidir.

5.6.3. Sinir Ağları ve GPUlar

Benzer şekilde, bir sinir ağı modeli cihazları belirtebilir. Aşağıdaki kod, model parametrelerini GPU’ya yerleştirir.

net = nn.Sequential()
net.add(nn.Dense(1))
net.initialize(ctx=try_gpu())
net = nn.Sequential(nn.Linear(3, 1))
net = net.to(device=try_gpu())
strategy = tf.distribute.MirroredStrategy()
with strategy.scope():
    net = tf.keras.models.Sequential([
        tf.keras.layers.Dense(1)])
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:GPU:0', '/job:localhost/replica:0/task:0/device:GPU:1')

Aşağıdaki bölümlerde GPU’larda modellerin nasıl çalıştırılacağına dair daha birçok örnek göreceğiz, çünkü onlar hesaplama açısından biraz daha yoğun hale gelecekler.

Girdi, GPU’da bir tensör olduğunda, model sonucu aynı GPU’da hesaplayacaktır.

net(X)
array([[0.04995865],
       [0.04995865]], ctx=gpu(0))
net(X)
tensor([[0.7531],
        [0.7531]], device='cuda:0', grad_fn=<AddmmBackward0>)
net(X)
<tf.Tensor: shape=(2, 1), dtype=float32, numpy=
array([[-0.05073321],
       [-0.05073321]], dtype=float32)>

Model parametrelerinin aynı GPU’da depolandığını doğrulayalım.

net[0].weight.data().ctx
gpu(0)
net[0].weight.data.device
device(type='cuda', index=0)
net.layers[0].weights[0].device, net.layers[0].weights[1].device
('/job:localhost/replica:0/task:0/device:GPU:0',
 '/job:localhost/replica:0/task:0/device:GPU:0')

Kısacası tüm veriler ve parametreler aynı cihazda olduğu sürece modelleri verimli bir şekilde öğrenebiliriz. Sonraki bölümlerde bu tür birkaç örnek göreceğiz.

5.6.4. Özet

  • Depolama ve hesaplama için CPU veya GPU gibi cihazları belirleyebiliriz. Varsayılan olarak, veriler ana bellekte oluşturulur ve ardından hesaplamalar için CPU kullanılır.

  • Derin öğrenme çerçevesi, hesaplama için tüm girdi verilerinin aynı cihazda olmasını gerektirir, ister CPU ister aynı GPU olsun.

  • Verileri dikkatsizce taşıyarak önemli bir performans kaybına uğrayabilirsiniz. Tipik bir hata şudur: GPU’daki her minigrup için kaybı hesaplamak ve bunu komut satırında kullanıcıya geri bildirmek (veya bir NumPy ndarray’de kaydetmek), bu tüm GPU’ları durduran global yorumlayıcı kilidini tetikleyecektir. GPU içinde kayıt tutmak için bellek ayırmak ve yalnızca daha büyük kayıtları taşımak çok daha iyidir.

5.6.5. Alıştırmalar

  1. Büyük matrislerin çarpımı gibi daha büyük bir hesaplama görevi deneyiniz ve CPU ile GPU arasındaki hız farkını görünüz. Az miktarda hesaplama içeren bir göreve ne olur?

  2. GPU’daki model parametrelerini nasıl okuyup yazmalıyız?

  3. \(100 \times 100\)’lük 1000 matris-matris çarpımını hesaplamak için gereken süreyi ölçünüz ve her seferde bir sonucun çıktı matrisinin Frobenius normunu günlüğe kaydetmeye karşılık GPU’da günlük tutma ve yalnızca son sonucu aktarmayı kıyaslayınız.

  4. İki GPU’da iki matris-matris çarpımını aynı anda gerçekleştirme ile tek bir GPU’da sıralı gerçekleştirmenin ne kadar zaman aldığını ölçerek karşılaştırınız. İpucu: Neredeyse doğrusal bir ölçekleme görmelisiniz.