tensorflow 사용시 유용한 팁 몇 가지 (주로 GPU 관련)
텐서 플로 사용 시 유용한 몇 가지 팁을 정리한다.
텐서 플로 2.5.0 기준
GPU는 RTX 3060 12GB를 사용하고 있다. i7-10700k, 16GB RAM, 우분투 리눅스 20.04
1. 텐서 플로의 정보 출력 억제하기
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
이 코드를 tensorflow를 import 하기 전에 적어놓으면 텐서 플로의 귀찮은 정보 출력을 억제한다.
이 값은 0에서 3 사이의 값을 가질 수 있고, 정보 출력 정도를 조절한다.
예) 깔끔한 출력
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
import tensorflow as tf
a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = tf.matmul(a, b)
print(c)
출력:
tf.Tensor(
[[22. 28.]
[49. 64.]], shape=(2, 2), dtype=float32)
예) 지저분한 출력
import tensorflow as tf
a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = tf.matmul(a, b)
print(c)
출력:
..........
2021-08-2 08:02:52.464185: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-2 08:02:52.464398: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 3614 MB memory) -> physical GPU (device: 0, name: NVIDIA GeForce RTX 3060, pci bus id: 0000:01:00.0, compute capability: 8.6)
2021-08-2 08:02:52.494098: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-08-2 08:02:52.924695: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-08-12 08:02:52.924730: I tensorflow/stream_executor/cuda/cuda_blas.cc:1838] TensorFloat-32 will be used for the matrix multiplication. This will only be logged once.
tf.Tensor(
[[22. 28.]
[49. 64.]], shape=(2, 2), dtype=float32)
주의) GPU 메모리를 사용할 경우, 위의 코드 실행 시, GPU 메모리에 여유가 없어 할당을 못하면 에러가 난다.
$ nvidia-smi -l
위 명령으로 GPU 메모리 사용 현황을 수시로 확인하자. GPU를 사용하는 python 코드를 실행 중인지 아닌지, 혹은 ipython이나 python내부에서 명령행으로 GPU 활용 코드를 사용하는 것은 아닌지, GPU를 사용하는 프로세서와 그 용량이 nvidia-smi 모니터링 화면에 잡힌다. spyder나 visual code 내부에서 ipython을 사용해도 ipython을 단독으로 사용하는 것과 별반 다를 바 없다.
2. GPU 장치 지정
GPU 장치를 지정하거나, CPU 사용을 강제할 수 있다.
Cuda 지원 NVIDIA 그래픽카드를 하나 설치하면 대부분 GPU0으로 잡힌다.
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
GPU 0번을 CUDA에서 사용하도록 강제하는 코드다. 이 코드 다음에 tensorflow를 import 해야 한다.
그래픽 카드가 하나 더 달려 있어서 GPU 1번이 있다면 그것의 사용을 지정할 수도 있겠다.
CPU를 강제 사용하고, GPU를 사용하지 않겠다면 이 값을 "-1"로 하면 된다.
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
가끔 실행 속도가 느려지면 GPU가 사용 중인지 아닌지 의심스러울 때가 있다. 이럴 때 이 옵션을 사용하면 CPU와 GPU 사용 중 어느 쪽인지 확신할 수 있다. 대개 GPU를 사용하는 편이 사용하지 않을 때보다 5~50배 가량 빨랐다.
3. GPU 메모리 용량 사용 제한
CUDA 사용 코드를 실행하면 그 프로세스가 사용가능한 전체 비디오 메모리를 차지하기 때문에, 동시에 CUDA 사용 코드를 또 하나 실행한다면 곧바로 에러가 뜬다. 분명히 실행에 이상없는 정상적인 CUDA 사용 코드인데 알수 없는 이유로 시작과 동시에 중단된다면, 대개 다른 CUDA 활용 프로세스가 비디오 램을 다 차지하고 있어서 여유가 없기 때문에 그럴 것이다. 이럴 때는 해당 코드 시작 부분에 GPU 메모리 사용 용량을 지정해주면 된다.
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
import tensorflow as tf
import time
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus: # gpu가 있다면, 용량 한도를 5GB로 설정
tf.config.experimental.set_virtual_device_configuration(gpus[0],
[tf.config.experimental.VirtualDeviceConfiguration(memory_limit=5*1024)])
a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = tf.matmul(a, b)
print(c)
time.sleep(10)
위 코드를 실행하고, 실행 도중에, 'nvidia-smi -l' 명령으로 보면,
제일 아래쪽 프로세스 python3 이 타입 C(G는 그래픽용, C는 컴퓨테이션 즉 연산용을 의미)로 5705Mb를 사용하고 있음을 알 수 있다. 필자는 RTX 3060 12GB 모델이라 아직 5~6GB 의 여유가 있다. 이 프로세스가 돌고 있는 동안에, 다른 쿠다 사용 프로세스를 돌리면서 별다른 비디오 램 사용 제한을 하지 않는다면, 나머지 비디오 램을 모두(5~6GB) 차지하게 된다.
위 코드에서 gpus는 사용가능한 gpu들의 정보를 담는 리스트 형이고, GPU가 1개인 필자의 경우는, [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
이렇게 되고, GPU 없는 시스템이라면,
[]
빈 리스트가 된다.
그래서 GPU가 없을 경우,
if gpus: 코드를 건너뛰게 되어 GPU 없는 시스템에서도 위의 코드가 에러없이 실행된다.
4. CUDA 활용 코드가 이유 없이 에러 날 경우
잘 실행되는 GPU 활용 텐서 플로 코드인데 이유 없이 에러가 난다면, GPU에서 할당 가능한 메모리가 없어서 그럴 경우가 종종 있다. 이때에는 앞서 언급했듯이 GPU 메모리 용량 제한을 프로세스에게 걸어 효율적으로 비디오 램을 사용할 필요가 있다.
제일 아래의 프로세스 번호 201177번의 python3 프로세스가 10GB 비디오램을 사용하고 있다. 이럴 경우, 그래픽에 할당된 용량과 예비 용량을 제외하면 여유 램이 없기 때문에, GPU 램 할당을 요구하는 텐서 플로 코드 실행시 에러가 나서 중단이 되는 것이다.
5. 텐서 플로 연산이 어떤 장치에서 수행되는지 알고 싶은 경우
import tensorflow as tf
tf.debugging.set_log_device_placement(True)
이 코드를 사용해서 테스트해보면,
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
#os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
import tensorflow as tf
tf.debugging.set_log_device_placement(True)
a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = tf.matmul(a, b)
print(c)
출력:
Executing op MatMul in device /job:localhost/replica:0/task:0/device:GPU:0
tf.Tensor(
[[22. 28.]
[49. 64.]], shape=(2, 2), dtype=float32)
GPU:0에서 해당 연산이 수행됨을 알려준다.
CPU에서 돌려보면,
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
import tensorflow as tf
tf.debugging.set_log_device_placement(True)
a = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = tf.matmul(a, b)
print(c)
출력:
Executing op MatMul in device /job:localhost/replica:0/task:0/device:CPU:0
tf.Tensor(
[[22. 28.]
[49. 64.]], shape=(2, 2), dtype=float32)
CPU:0에서 수행됨을 알려준다.
6. tensorflow의 GradientTape()이 유난히 느릴 경우
model.fit() 이라는 편리한 자동 방식으로 훈련시킬 때는 GPU 활용 시, 속도가 아주 빠르다가, GradientTape()이라는 수동 방식으로 코드를 짰을 경우, 속도가 무척 느려질 경우가 있다. 이런 경우, GradientTape()의 문제라기보다, GradinetTape()을 포함하는 에폭(EPOCH) 수행 도중에 어떤 느려터진 수작업 코드가 버티고 있을 가능성이 크다. 가령 원핫 인코딩을 텐서 플로에 맡기지 않고 수작업으로 짰다던지, 어떤 연산 코드를 지루한 for문을 사용해 함수화해서 그 함수를 에폭 과정에서 불러온다던지 하는 경우가 있을 것이다. 시간이 유난히 많이 걸리는 코드 블록을 찾아내야 하는데 이게 헷갈릴 수가 있다. 가급적 tensorflow나 넘파이, C/C++ 코드를 효율적으로 사용하는 게 좋겠고, 느려터진 파이썬 코딩 방식으로 for문까지 동원해 짠 함수를 에폭 내에서 호출하는 것은 피하는 것이 좋겠다. 개인이 짠 onehot 인코딩 함수와 tensorflow의 one_hot 함수를 비교했을 때 텐서 플로가 50배 빠른 경우도 있었다(GPU 사용시).
필자가 겪었던 문제인데 아래 경우를 참고하기 바란다.
https://madrabbit7.tistory.com/78
Tensorflow 2.5 GradientTape()에서 속도가 느려지는데...
자연어 처리(NLP) 분야에서 채팅 모델을 개발하고 있는데, 아직은 공부하는 단계다. 며칠 전에 기존에 문제 많던 GTX 1060 3GB를 처분하고, RTX 3060 12GB를 구입했는데, 딥러닝의 텐서 플로 가속에서 만
madrabbit7.tistory.com