TensorFlow, GPU ve Çoklu İşleme Konularında İpuçları ve Tüyolar
Bulut servislerini kullanmadığınız takdirde bugün bir GPU kartına 10 binlerce dolar ödemeniz gerekmekte. Örneğin bugünlerde Tesla P100 serisi bir işlemci 7 bin dolarken, V100 serisinde fiyat 10 bin dolara çıkmakta. Komik olmakla birlikte grafik işlemci kartları kullananların en çok şikayet ettikleri konular da bellek boyutlarının azlığı. Yüzlerce gigabyte’lık flash diskleri bir kaç dolara sipariş edebiliyorken, grafik kartlar çoğunlukla 16 GB, lüks olanları ise 32 GB’lık belleğe sahiptir. Bu yazıda Keras ve TensorFlow ile gerçekleştireceğiniz makine öğrenmesi projelerinizde GPU ve çoklu işleme (multiprocessing) konularında bazı tüyolar paylaşmaya çalışacağım.
Tek bir GPU’nun rezerve edilmesi
TensorFlow’un GPU için olan versiyonunu indirdiğinizi varsayalım.
1 2 3 4 5 |
pip install tensorflow-gpu |
TensorFlow aç gözlü bir yaklaşımla tüm GPU’ların tüm belleğini ihtiyacı olmasa bile kendisine tahsis etmektedir. Bu da aynı sunucu üzerinde birden fazla kişi ya da proje çalışıyorsa diğer paydaşlar bellek yetersizliği sebebiyle hata almasına neden olmaktadır.
Makinenizdeki GPU’ları listelemek için nvidia-smi komutunu kullanabilirsiniz. Komut GPU’ları listelediği gibi işlemci ve bellek kullanımlarının yüzdelerini de göstermektedir. Komutun başına watch ekleyerek 2 sn’de bir güncellenmesini sağlayarak sistemi izleyebilirsiniz.
1 2 3 4 5 |
watch nvidia-smi |
Çoklu GPU’lardan sadece birinin tahsis edilmesi için GPU indeksini belirtmeniz yeterli olacaktır.
1 2 3 4 5 6 |
import os os.environ["CUDA_VISIBLE_DEVICES"]="0" |
Birden fazlasını tahsis etmek isterseniz virgül ile indeksleri belirtebilirsiniz.
1 2 3 4 5 6 |
import os os.environ["CUDA_VISIBLE_DEVICES"]="0,1" |
Alternatif olarak, modellerin dizaynı sırasında da kullanmasını istediğiniz GPU’yu belirtebilirsiniz. Bu kullanım size farklı modellerin farklı GPU’lar üzerinde birbirinden bağımsız olarak koşmasını sağlaycaktır.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
with tf.device('/gpu:0'): content_model = vgg19.VGG19(input_tensor=base_image , weights='imagenet', include_top=False) style_model = vgg19.VGG19(input_tensor=style_reference_image , weights='imagenet', include_top=False) with tf.device('/gpu:1'): generated_model = vgg19.VGG19(input_tensor=combination_image , weights='imagenet', include_top=False) |
Merkezi İşlemci Birimi (CPU)
Hiç GPU kullanmadan doğrudan CPU core’ları üzerinde çalışmak istiyorsanız da indeks değerini boş atamanız yeterli olacaktır.
1 2 3 4 5 6 |
import os os.environ["CUDA_VISIBLE_DEVICES"]="" |
Kullanılacak CPU core’larını limitlemek isterseniz device_count olarak atayabilirsiniz. Bu kullanımda GPU kullanılmak istenilmemiş, 5 cpu core kullanılmıştır. Aynı şekilde GPU adetlerini de belirtebilirsiniz.
1 2 3 4 5 6 7 |
config = tf.ConfigProto( device_count = {'GPU': 0 , 'CPU': 5} ) sess = tf.Session(config=config) keras.backend.set_session(sess) |
GPU belleği çok değerlidir!
Tek bir GPU’yu kendinize tahsis etseniz bile TensorFlow o GPU’ya ait tüm belleği tahsis edecektir. nvidi-smi komutu ile 200 MB’lık bir veri seti ile çalışıyorsanız da 16 GB’ın kullanımda olduğunu görürsünüz. Oysa ki aynı GPU’yu başka bir görev ile birlikte ortak kullanabilirdik. TensorFlow oturumunda belleğin büyümesine izin verirseniz (allow_growth) GPU belleğinin sadece ihtiyacınız kadarı size tahsis edilecektir.
1 2 3 4 5 6 7 8 |
config = tf.ConfigProto() config.gpu_options.allow_growth = True session = tf.Session(config=config) keras.backend.set_session(session) |
Çoklu işleme
Modeliniz özellikle eğitim safhası uzun sürmektedir. Eğitim ya da tahmin işlemlerini seri uygulamak yerine python’un çoklu işleme kütüphanesi ile paralel hale getirebiliriz. Bu şekilde zaman kaybını minimuma indirebiliriz.
TensorFlow’un belirtmediğiniz takdirde tüm belleği tahsis ettiğini biliyoruz. Dolayısıyla multiprocessing içerisinde TensorFlow’u doğrudan çağırırsak bellek yetersiz hatası alacağız.
1 2 3 4 5 6 7 |
from device: CUDA_ERROR_OUT_OF_MEMORY E tensorflow/core/common_runtime/direct_session.cc:154] Internal: CUDA runtime implicit initialization on GPU:0 failed. Status: out of memory |
allow_growth parametersine başta doğru değerini atarsak ise yine hata almaktayız.
1 2 3 4 5 |
E tensorflow/core/grappler/clusters/utils.cc:81] Failed to get device properties, error code: 3 |
Çözümü ise her bir çoklu işleme işi içerisinde allow_growth değerini tekrar tekrar doğru atamaktan geçiyor.
Örnek bir uygulama
Çoklu işleme havuzuna 10 eleman beslerken, çoklu işleme kütüphanesi aynı anda 10 elemanı işleyecektir. my_tuple değişkenler grubunun boyutu ile havuz boyutunun eşit olmasına özen gösterin. Aksi takdirde örneğin çoklu değişkenleriniz 100 elemandan oluşurken bunu havuz boyutunu 10 olarak atayarak 10’ar 10’ar işlemek isterseniz bir hata almazken oturumunuzun asılı kaldığını görebiliyorsunuz.
Dikkat etmeniz gereken çoklu işleme kütüphanesi 10 paralelde train fonksiyonunu çağırırken bu fonksiyon içerisinde allow_growth değerini doğru olarak atadık.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
import pandas as pd import multiprocessing from multiprocessing import Pool def train(index, df): import tensorflow as tf import keras from keras.models import Sequential #------------------------------ #this block enables GPU enabled multiprocessing core_config = tf.ConfigProto() core_config.gpu_options.allow_growth = True session = tf.Session(config=core_config) keras.backend.set_session(session) #------------------------------ #prepare input and output values df = df.drop(columns=['index']) data = df.drop(columns=['target']).values target = df['target'] #------------------------------ model = Sequential() model.add(Dense(5 #num of hidden units , input_shape=(data.shape[1],))) #num of features in input layer model.add(Activation('sigmoid')) model.add(Dense(1))#number of nodes in output layer model.add(Activation('sigmoid')) model.compile(loss='mse', optimizer=keras.optimizers.Adam()) #------------------------------ model.fit(data, target, epochs = 5000, verbose = 1) model.save("model_for_%s.hdf5" % index) #------------------------------ #finally, close sessions session.close() keras.backend.clear_session() return 0 #----------------------------- #main program df = pd.read_csv("dataset.csv") my_tuple = [(i, df[df['index'] == i]) for i in range(0, 10)] with Pool(10) as pool: pool.starmap(train, my_tuple) |
Böylelikle işleme hızımızı 10 kat hızlandırmış olduk.
GPU destekli çoklu işleme görevinin kodunu GitHub‘ta da bulabilirsiniz.
Tüm GPU’ları kullanabilmek
Çoklu GPU’ya sahip olmak sizi bir kaç kat daha hızlı yapmayacaktır. Bu GPU’ların birbirine ana kart üzerinde nvlink ile bağlanmış olması önemli husus öncelikle. Framework’un gradyanların aynı GPU’da olmasını zorunlu tutması da elleriniz kollarınızı bağlayan bir husus. Uber mühendislerinin geliştirdiği horovod, GPU’ları sanal olarak birleştirip tek bir GPU gibi hareket etmesini sağlamakta.
Bu yazıda GPU, çoklu işleme (multiprocessing), TensorFlow ve Keras ile kişisel denemelerden edindiğim bazı tüyo ve ipuçlarını paylaşmaya çalıştım.
Bu yazı Tips and Tricks for GPU and Multiprocessing in TensorFlow yazısından Türkçe’ye çevrilmiştir.