20.04 우분투 기반 NVIDIA GeForce RTX 3090에 CUDA, cuDNN Pytorch 설치


컴퓨터 스팩은 아래와 같으며, 오랜만에 직접 조립 했다.
몇일 써보니 호환이나 큰 문제 없이 돌아가므로 사양정보를 공유 한다.

컴퓨터 스팩

  • 그래픽카드: 갤럭시 GALAX 지포스 RTX 3090 SG D6X 24GB

 

Widnows10, ubuntu 20.04 멀티 부팅

UEFI BIOS 모드를 기준으로 설명. Legacy 방식은 다루지 않음.

Bootable USB 제작

MAC(OSX), uBuntu에서 dd 커맨드로 제작이 가능하다. 설치 과정중 기가바이트 Z490칩셋기준으로 예기치 않은 오류가 계속 발생하여, 가장 안정적인Rufus를 이용햇 제작함.

Widnows10 제작

스크린샷 2020-12-14 오후 4.42.43

참조: https://privatenote.tistory.com/67

ubuntu 20.04 제작

스크린샷 2020-12-14 오후 4.45.40

멀티 부팅 설정

Windows10 설치 후 ubuntu 20.04를 설치하는 것이 정신건강에 좋다. 두 경우 모두 아래의 이전 포스트에서 방법을 다루고 있음.

파티션 설정 및 오토 마운팅

이전 포스트 참조

ubuntu에서 3090 그래픽카드 드라이버 설치

아래와 같이 기본 우분투 소프트웨어 업데이트 도구를 이용해서 그래픽 드라이버 업데이트가 가능하다.
다만 Nouveau드라이버는 문제가 많이므로 독접에서 제공하는 NVIDIA 드라이버로 설치한다.

nvidia 드라이버 설치

설치가 완료되면 nvidia-settings 또는 아래와 같이 nvidia-smi 커맨드로 드라이버 버전과 설치된 상태를 알 수 있다.

스크린샷 2020-12-15 오전 7.22.23

CUDA 설치

3090은 CUDA 11.0 이상의 버전을 설치 해야한다. 20년 12월 기준 CUDA Toolkit 11.1 Update 1 Downloads이 최신 버전이라 해당 버전을 설치한다.

우분투 이므로 아래와 같이 설정해서 다운로드 받는다.

스크린샷 2020-12-15 오전 7.46.16

미리 그냥 wget으로 다 받아두고 설치하는게 오류도 없고 좋다.

wget https://developer.download.nvidia.com/compute/cuda/11.1.1/local_installers/cuda_11.1.1_455.32.00_linux.run sudo sh cuda_11.1.1_455.32.00_linux.run 

기존에 NVIDIA Driver가 있으므로 경고가 나온다. 일단 무시하고 진행

스크린샷 2020-12-15 오전 7.49.31


동의: accept 입력

스크린샷 2020-12-15 오전 7.49.46
  • CUDA 11 이상을 설치하려면 드라이버가 455버전 이상으로 맞춰야 한다.
  • 이전 소프트웨어 업데이트로 455.38버전을 설치 했으므로 쿠다에 동봉된 455.32버전 설치는 하지 않도록 체크를 해제한다.
  • 향후에 CCUDA를 업데이트할 때 항상 그래피카드 드라이버와의 호환 버전음 참조해서 선택을 하면된다.
  • 덮어씌우기 할경우 문제가 발생하므로 지금은 체크 해제한다.
스크린샷 2020-12-15 오전 7.49.56

환경변수 설정

CUDA 사용을 위해서 PATH등록을 한다.
runfile을 사용 할 떄 필요한 LD_LIBRARY_PATH도 같이 설정 한다.

vim ~/.bashrc export PATH=/usr/local/cuda-11.1/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH 

CUDA 설치 확인 및 예제 실행

환경 변수 설정이 끝나면 nvcc --version을 통해서 확인 가능함.

jemin@jemin-3090:~/NVIDIA_CUDA-11.1_Samples$ nvcc --version nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2020 NVIDIA Corporation Built on Mon_Oct_12_20:09:46_PDT_2020 Cuda compilation tools, release 11.1, V11.1.105 Build cuda_11.1.TC455_06.29190527_0 

CUDA 설치시 sample 코드도 같이 설치 했기에 $HOME/NVIDIA_CUDA-11.1_Samples디렉토리에 샘플코드가 있다.

컴파일하여 실행하면 CUDA가 정상적으로 설치된 것이다.

# 예제 코드들 /NVIDIA_CUDA-11.1_Samples$ ls 0_Simple     2_Graphics  4_Finance      6_Advanced       EULA.txt  bin 1_Utilities  3_Imaging   5_Simulations  7_CUDALibraries  Makefile  common 

이중 간단한 Simple 예제중 행렬 곱샘을 실행시켜 본다.

## 컴파일 jemin@jemin-3090:~/NVIDIA_CUDA-11.1_Samples/0_Simple/matrixMul$ make /usr/local/cuda/bin/nvcc -ccbin g++ -I../../common/inc  -m64    -gencode .. .. cp matrixMul ../../bin/x86_64/linux/release  ## 실행 jemin@jemin-3090:~/NVIDIA_CUDA-11.1_Samples/0_Simple/matrixMul$ ./matrixMul  [Matrix Multiply Using CUDA] - Starting... GPU Device 0: "Ampere" with compute capability 8.6  MatrixA(320,320), MatrixB(640,320) Computing result using CUDA Kernel... done Performance= 2235.81 GFlop/s, Time= 0.059 msec, Size= 131072000 Ops, WorkgroupSize= 1024 threads/block Checking computed result for correctness: Result = PASS  NOTE: The CUDA Samples are not meant for performancemeasurements. Results may vary when GPU Boost is enabled. 

예제들 의미 참조 (NVIDIA 공식): https://docs.nvidia.com/cuda/cuda-samples/index.html#matrix-multiplication--cuda-runtime-api-version-

cuDNN 설치

CUDA를 11.1로 설치 했으므로 이에 맞춰서 cuDNN은 8.0.5를 다운 받으며, 아래 두 가지 방법들 중 원하는 것으로 설치 한다.

스크린샷 2020-12-16 오전 12.29.18
  • cuDNN Library for Linux(x86_64)를 선택하면 tar 압축파일을 받아서 직접 복사하여 설치 하는 방식이다.

직접 복사 방법

$ sudo cp cuda/include/cudnn*.h /usr/local/cuda/include $ sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64  $ sudo chmod a+r /usr/local/cuda/include/cudnn*.h /usr/local/cuda/lib64/libcudnn* 
  • [Deb]파일 세개를 각각 받아서 debin package 설치법으로도 설치 할 수 있다.

Deb 페키지 설치 방법: 세 개 모두 설치 한다.

sudo dpkg -i libcudnn8_8.0.5.39-1+cuda11.1_amd64.deb sudo dpkg -i libcudnn8-dev_8.0.5.39-1+cuda11.1_amd64.deb sudo dpkg -i libcudnn8-samples_8.0.5.39-1+cuda11.1_amd64.deb 

설치를 확인 하기 위해서 예제코드를 실행한다.
Deb 페키지를 이용해서 설치할 경우에만 sample코드가 정상적으로 설치된다.
/usr/src/cudnn_samples_v8내부에 여러 예제들 중에서 mnistCUDNN를 실행 한다.

cd /usr/src/cudnn_samples_v8/mnistCUDNN make clean && make ./mnistCUDNN 

아래와 같이 나오면 설치가 성공한 것이다.

Resulting weights from Softmax: 0.0000000 0.0000008 0.0000000 0.0000002 0.0000000 1.0000000 0.0000154 0.0000000 0.0000012 0.0000006  Result of classification: 1 3 5  Test passed! 

cuDNN 설치법 공식문서: https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html#installlinux-tar

Pytorch 설치 및 확인

설치

Stable(1.7.1) Pytorch 버전을 설치 했다.

pip install torch==1.7.1+cu110 torchvision==0.8.2+cu110 torchaudio===0.7.2 -f https://download.pytorch.org/whl/torch_stable.html 

CUDA 버전에 맞춰서 설치 하면된다. cuda를 11.1로 설치 했지만 코드들은 정상 동작한다.
공식 문서: https://pytorch.org/get-started/locally/

실행

간단하게 아래와 같이 torch가 import되고 사용가능한 cuda가 print되면 정상 설치 된 것이다.

import torch x = torch.rand(5, 3) print(x)  import torch torch.cuda.is_available() 

Pytorch.org/tutorials에 가면 많은 예제들이 있고 그 중 cifar10 예제는 아래와 같다.
https://github.com/pytorch/tutorials/blob/master/beginner_source/blitz/cifar10_tutorial.py

아래와 같이 cuda:0까지 나오면 모두 설치가 정상이다.

python3 cifar10_tutorial.py Files already downloaded and verified Files already downloaded and verified   car horse   dog  deer [1,  2000] loss: 2.182 [1,  4000] loss: 1.810 [1,  6000] loss: 1.682 [1,  8000] loss: 1.605 [1, 10000] loss: 1.514 [1, 12000] loss: 1.516 [2,  2000] loss: 1.408 [2,  4000] loss: 1.405 [2,  6000] loss: 1.381 [2,  8000] loss: 1.346 [2, 10000] loss: 1.333 [2, 12000] loss: 1.304 Finished Training GroundTruth:    cat  ship  ship plane Predicted:    cat   car   car  ship Accuracy of the network on the 10000 test images: 53 % Accuracy of plane : 55 % Accuracy of   car : 77 % Accuracy of  bird : 35 % Accuracy of   cat : 39 % Accuracy of  deer : 44 % Accuracy of   dog : 49 % Accuracy of  frog : 63 % Accuracy of horse : 56 % Accuracy of  ship : 76 % Accuracy of truck : 40 % cuda:0 

CVPR2020 양자화 논문 실행: https://github.com/amirgholami/ZeroQ
2080ti랑 비교하여 학습과 검증시 체감 효과는 크지는 않았다. 다른 벤치마크가 필요할 것 같다.

jemin@jemin-3090:~/development/ZeroQ/classification$ python3 uniform_test.py --dataset imagenet --model resnet50 --batch_size 64 --test_batch_size 128 ****** Full precision model loaded ****** ****** Data loaded ****** ****** Zero Shot Quantization Finished ****** Testing |################################| (391/391) | ETA: 0:00:01 | top1: 0.75844 Final acc: 75.84% (37922/50000) 

문제 해결

Nouveau 드라이버 문제

기존 설치 제거 방법

드라이버 삭제

  1. apt --installed list | grep nvidia-driver
  2. -> sudo apt remove --autoremove nvidia-*
    -> sudo apt remove --autoremove nvidia-cuda-toolkit

 

TensorFlow GPU (2080ti)버전 우분투 18.04 설치 하기


본 포스트에서 사용한 그래픽카드는 기가바이트 RTX 2080ti를 사용 했다.

우분투 이미지를 생성하는 방법은 이전 블로그를 참조
우분투 18.04 USB 이미지 생성

1. 그래픽카드 드라이버 설치

2080ti 경우 ubuntu 18.04.2로 설치해도 드라이버가 설치가 되지 않는다.
직접 다운받아 설치 한다.

1_LP4iAyT_aFiW2QkE0LRcYQ

nouveau kernel문제 회피법
터미널 모드 진입

  • ctrl+alt+F1
  • lightdm 끄기
    • sudo /etc/init.d/lightdm stop
chmod +x NVIDIA-Linux-x86_64-418.56.run
sudo ./NVIDIA-Linux-x86_64-418.56.run

설치 과정중 여러가지 경고창이 나오지만 추천 선택을 기준으로 선택해서 넘어간다.

정상적으로 설치되면 nvidia-settings 커맨드를 입력하면 설정과 드라이버 관련 정보를 GUI로 볼 수 있다.

nvidia-smi를 통해서도 콘솔로 아래와 같이 내용을 확인 할 수 있다.

  • 드라이버 버전 418.56
  • CUDA 10.1 (추후 설치)
    스크린샷 2019-05-03 오전 10.59.59

2. CUDA 설치

TensorFlow의 경우 19년 4월 기준 CUDA-10.0까지만 지원하므로 legacy로 접속해서 해당 버전을 다운 받는다.

스크린샷 2019-04-05 오후 2.41.13

2.1 2080ti 드라이버와의 충돌문제 해결

쿠다를 설치할 때 기존에 그래픽카드 드라이버와 자동으로 설치된 쿠다버전과 충돌이 발생한다. 이것을 잘 해결해 주지 않으면 X-windos로 아에 접속이 안되므로 주의해야 한다.

nouveau display diver blacklist 설정

  • blacklist-nouveau.conf 파일 생성
    아래와 같이 vim이던 gedit이던 편한 것으로 사용자 계정 home directory에 생성한다.
    blacklist nouveau
    options nouveau modeset=0
    

GUI 환경에서 logout

  • logout GUI with ctrl+alt+f1
  • Back to the GUI ctrl+alt+f7

    f1~f7 각자 사용자 환경에 따라 IO가 다르므로 약간 차이가 있을 수 있다. 중간의 키값을 한 번씩 다 눌러보면 알 수 있다.

nvidia-cuda 삭제

드라이버와 함께 설치된 쿠다를 제거한다.

sudo apt-get purge nvidia-cuda*

disable X-server

  • sudo service lightdm stop (in black screen terminal)

super user modeset

  • sudo -i

2.2 CUDA 설치 시작

sudo dpkg -i cuda-repo-ubuntu1804_10.1.105-1_amd64.deb
sudo apt-key adv --fetch-keys <generated_code>
sudo apt-get update
sudo apt-get install cuda
sudo apt-get install -y cuda
..
..
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...

done.
done.

콘솔로 설치 과정 중 fail이 발생하면 그래픽 드라이버를 설치하지 않는것으로 disable시킨 상태에서 다시 시도 한다.

3. cuDNN 설치

설치한 쿠다 버전과 맞는것을 설치 한다.
스크린샷 2019-04-05 오후 3.25.57

ubuntu기준으로 cuDNN library for Linux를 다운받는다.
deb의 경우 자동 설치이긴 하지만 오류를 많이 발생 시키므로 직접 파일 복사 방식으로 설치하는 방법을 따른다.

설치방법은 계속 조금씩 변경 되므로 cuDNN library for Linux클릭 했을 때 나오는 설명을 기준으로 수행한다.

4. Anaconda 설치 (optional)

jemin@jemin:~/다운로드$ sudo ./Anaconda3-2018.12-Linux-x86_64.sh

5. TensorFlow 설치

pip install tensorflow-gpu

6. 테스트

jemin@jemin:~$ python -c "import tensorflow as tf; print(tf.__version__)"
1.13.1

간단한 MNIST CNN 모델 성능 테스트

참고자료


윈도우 GPU tensorflow 설치 및 그래픽카드별 성능 비교


한국 시간으로 2016년 11월 29일 저녁
TensorFlow v0.12.0 RC0가 업데이트 되었다.

아래 실험은 TF 1.4.0에서 테스트 한것이다.
현재는 1.6까지 나온듯 하다 (2018-03.27).

핵심 변경 사항 중 Window에서 GPU 버전의 TensorFlow를 지원한다는 부분이 있다.
이제 Docker를 쓰지 않고 CPU 버전을 설치 할 수 있는것은 물론 Ubuntu에서만 가능하던 GPU 버전도 설치가 가능하다.
설치방법과 Window GPU 버전 텐서플로의 학습 속도를 분석해 본다.

설치방법

여러 방법이 있으나 필자는 Anaconda를 이용한 방법으로 설치한다. 가장 간단한다. 추가로 딱히 Window에선 Python 다른 응용을 개발하지 않으므로 특별히 conda 또는 virtualenv를 설정하지않고 global에 직접 설치한다. 기존 시스템과의 충돌을 걱정한다면 가상환경을 만들고 설치하길 권장한다.
방법: https://www.tensorflow.org/versions/r0.12/get_started/os_setup.html#pip-installation-on-windows

1. Anaconda 4.2.0 설치

NumPy, SciPy, Pandas, Matplotlib 등의 과학연산에 필요한 Python package들을 통합했으며 Jupyter Notebook을 제공하는 환경이다.

Window TensorFlow는 Python 3.5 이상부터 동작하므로 해당 Anaconda 관리자 권한으로 설치한다.

모든 작업을 anaconda만 설치하면 환경변수등 자동으로 설정 되므로 pip명령어를 이용해서 하나하나 설치하는것 보다 훨씬 효율적이다.

다운로드: https://www.continuum.io/downloads

2. CUDA 8.0.44 -win 10 설치

Tensorflow v0.12.0 RC0는 CUDA 8.0이상을 권장하므로 이것을 설치한다.
Network버전은 설치가 오래걸리므로 local버전 1.2GB 상당을 직접 다운받아서 설치한다.

다운로드 https://developer.nvidia.com/cuda-downloads

필자의 Window환경 컴퓨터의 GPU 스팩이다.

name: GeForce GTX 745
major: 5 minor: 0 memoryClockRate (GHz) 1.0325

3. CuDNN 5.1 설치

Deep Learning을 위해서 엔비디아에서 제공하는 추가 라이브러리 꾸러미이다.
다운로드: https://developer.nvidia.com/cudnn

CUDA 8.0 cuDNN 5.1을 설치해야한다.
회원 가입을 하고 다운 받는다.

다운받으면 그냥 압축 파일이므로 압축해제후 CUDA 설치 경로에 붙여넣기 해야한다.
CUDA 설치 경로 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0

4. TensorFlow-GPU 설치

윈도우 버전은 파이썬 3.5 에서 하다.
0.12 RC0 버전 부터는 텐서플로우 패키지가 PyPI 에 업로드되어 있어 pip 명령으로 간단히 설치할 수 있게 되었다.

처음 설치

pip install tensorflow-gpu

업데이트 방법 (추후에 새로운 버전이 나왔을 때)

pip install --upgrade https://storage.googleapis.com/tensorflow/windows/gpu/tensorflow_gpu-0.12.0rc1-cp35-cp35m-win_amd64.whl

최근에 RC0에서 RC1로 업데이트 되었다. 기존 RC0사용자라면 버그가 고쳐진 새로운 버전으로 업데이트 할 때 위 명령어를 이용 한다.

설치확인
Anaconda prompt를 관리자 권한으로 실행한다.
python을 실행하고 아래의 script를 실행해 본다.

>>> import tensorflow as tf
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess = tf.Session()
>>> print(sess.run(hello))
Hello, TensorFlow!

Jupyter Notebook에서의 실행결과
Anaconda를 설치하면 자동으로 Jupyter Notebook이 설치된다.
Jupyter Notebook을 실행해서 아래의 코드를 실행해보자.
정상적으로 수행되면 설치 성공이다.


성능 테스트

간단한 MNIST 셋의 CNN을 이용한 손글씨 인식을 학습해 본다.

CNN구조는 아래와 같다.

자세한 코드는 이전 POST를 참조한다.

소스코드 위치: https://github.com/leejaymin/TensorFlowLecture/tree/master/5.CNN
해당 repo를 clone해서 그 안에 CNNforMNIST.py를 실행 한다.
실행이 끝나면 경과된 시간이 출력 된다.

CNN으로 평가한 컴퓨터 환경들은 아래와 같다.

i7, 10 GFLOPS

105분 31초

GTX 745, 793 GFLOPS

36분 30초

K520, 2448*2 GFLOPS

22분 4초

GTX 970, 3.4 TFLOPS

9분 10초

GTX 1060 3GB, 3.9TFLOPS

8분 5초

GTX 1080, 8.87 TFLOPS

5분 27초

Tesla P100, 9.3 TFlops

4분 57초

Tesla P100, 9.3 TFlops (8개)

실험 예정

RTX 2080ti, 13.4 TFLOPS

스크린샷 2019-04-09 오후 5.50.17
`4분 6초``

결론

I7-CPU (6331초)를 기준으로 비교했을 때 각각의 성능 향상은 아래와 같다.

  • GTX 745는 2.89x
  • K520은 4.78x
  • GTX 970 11.5x
  • GTX 1060 13x
  • GTX 1080 19.3x
  • P100 21.3x
  • RTX 2080ti 25.7x

Jupyter에 conda env. 추가하기


coda 환경 생성

공식 TensorFlow 사이트에 따르면 conda를 이용해서 virtual env에 설치 할 수 있지만, Google에서 maintain하는 Tensorflow가 아니므로 추천하지 않는다고 했다.

conda 환경 생성

$ conda create -n tensorflow_1.9 pip python=3.6

# enable env.
$ source activate tensorflow
(tensorflow_1.9) jemin@jemin-desktop:~$

# install TensorFlow
(tensorflow_1.9)$ pip3 install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.9.0-cp36-cp36m-linux_x86_64.whl

현재 18.08.04기준으로https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.9.0-cp36-cp36m-linux_x86_64.whl이 3.6 GPU버전 TF1.9이다.

설치된 conda 환경 확인
conda env list

remove env.
conda remove --name <new_env_name> --all

jupyter에 conda env. kernel 연결

보통은 그냥 설치하면 잘 설정되나 안되면 아래와 같이 확인해보고 재설정 한다.

jupyter kernelspec list

kernelspec list
Available kernels:
  caffe_py2_virtualenv    /home/jemin/.local/share/jupyter/kernels/caffe_py2_virtualenv
  python2                 /home/jemin/.local/share/jupyter/kernels/python2
  python3                 /home/jemin/anaconda3/envs/tensorflow_1.9/share/jupyter/kernels/python3

만약 위와 같이 잘 안되었다면, nb_conda를 설치한다.
conda install nb_conda


TensorFlow를 공용 GPU에서 사용 할 때 메모리 절약 방법


절대적 메모리 uppeor bound 설정

tf.Session생성 할 때 GPU memory allocation을 지정할 수 있다. 이것을 위해서 tf.GPUOptions config부분을 아래와 같이 활용 한다.

# Assume that you have 12GB of GPU memory and want to allocate ~4GB:
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)

sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))

위와 같이 per_process_gpu_memory_fraction=0.333으로 설정된 것은 strict하게 upper bound on the amount of GPU memory를 설정한 것이다.

탄력적으로 GPU memory 사용 방법

아래와 같이 allow_growth True로 설정하면 필요에 따라 탄력적으로 memory를 사용하게 된다.

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

TensorFlow GPU 버전 우분투 16.04에 설치 하기


본 포스트에서는 Tensorflow를 Ubuntu 16.04에 설치하는 법을 다룬다.

사용한 PC는 엔비디아 GPU 1080을 사용 한다.
정확한 컴퓨터 모델은 보스몬스터 DX3 6708KSW 이다.

TensorFlow 1.9에 맞춰서 CUDA 9.0 cuDNN 7.1로 업그레이드 설치 했다.

1. CUDA 라이브러리 설치

https://developer.nvidia.com/cuda-downloads

linux -> x86_64 -> Ubuntu -> 16.04 -> deb(network)

파일 다운로드후 아래의 명령어 수행
2018.07.31일 기준 CUDA 9.0까지 지원하지만 TF 1.9에서는 9.0을 설치하라고하니 9.0으로 다운 받는다.
9.2을 설치해보니 library loading이 잘 되지 않았다.

Network
패치를 따로 안해줘도 된다.
정확히 아래처럼 cuda-9-0으로 줘야 제대로 설치된다. 그냥 cuda하면 9.2최신 버전으로 설치 될 수도 있다.

sudo dpkg -i cuda-repo-ubuntu1604_9.0.176-1_amd64.deb

sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub

sudo apt-get update

sudo apt-get install cuda-9-0

local version
파일을 다운받아서 하는 방법이다.
patch 파일도 cuBLAS관련해서 세개나 있어서 각각 업데이트 해줘야 한다.

#본 파일
cuda-repo-ubuntu1604-9-0-local_9.0.176-1_amd64.deb
#패치 파일
cuda-repo-ubuntu1604-9-0-local-cublas-performance-update-3_1.0-1_amd64.deb
cuda-repo-ubuntu1604-9-0-local-cublas-performance-update-2_1.0-1_amd64.deb
cuda-repo-ubuntu1604-9-0-local-cublas-performance-update_1.0-1_amd64.deb

본설치

sudo dpkg -i cuda-repo-ubuntu1604-9-0-local_9.0.176-1_amd64.deb
sudo apt-key add /var/cuda-repo-<version>/7fa2af80.pub
sudo apt-get update
sudo apt-get install cuda

패치 적용

sudo dpkg -i cuda-repo-ubuntu1604-9-0-local-cublas-performance-update_1.0-1_amd64.deb
sudo apt-get update
sudo apt-get upgrade cuda-9-0

sudo dpkg -i cuda-repo-ubuntu1604-9-0-local-cublas-performance-update-2_1.0-1_amd64.deb
sudo apt-get update
sudo apt-get upgrade cuda-9-0

sudo dpkg -i cuda-repo-ubuntu1604-9-0-local-cublas-performance-update-3_1.0-1_amd64.deb
sudo apt-get update
sudo apt-get upgrade cuda-9-0

2. cuDNN 및 환경변수 설정

https://developer.nvidia.com/cudnn

cuDNN v7.1.4 (May 16, 2018) for CUDA 8.0으로 다운 받는다.

  • 6.0 Library for Linux (tar.gz) 버전

아래명령어를 실행한다.

tar xvfz cudnn-9.0-linux-x64-v7.1.tgz

sudo cp -P cuda/include/cudnn.h /usr/local/cuda/include
sudo cp -P cuda/lib64/libcudnn* /usr/local/cuda/lib64
sudo chmod a+r /usr/local/cuda/include/cudnn.h /usr/local/cuda/lib64/libcudnn*

추가적인 dependencies도 설치한다.

$ sudo apt-get install libcupti-dev

환경변수 설정

LD_LIBRARY_PATH CUDA_HOME을 설정한다.

vi ~/.bashrc

export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64"
export CUDA_HOME=/usr/local/cuda

버전확인

$ nvcc --version

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2017 NVIDIA Corporation
Built on Fri_Sep__1_21:08:03_CDT_2017
Cuda compilation tools, release 9.0, V9.0.176

pip로 바로 설치

python2의 경우

# Ubuntu/Linux 64-bit
$ sudo apt-get install python-pip python-dev

python3.5의 경우

# Ubuntu/Linux 64-bit
$ sudo apt-get install python3-pip python3-dev

Tensorflow-GPU 설치

$ pip3 install tensorflow-gpu

업그레이드 방법 (추후에 새로운 버전이 나왔 을때)

타겟 URL은 홈페이지에서 참조 한다.
https://www.tensorflow.org/get_started/os_setup

sudo pip3 install --upgrade $TF_BINARY_URL

실행 결과

Python 3.5.2 (default, Nov 17 2016, 17:05:23)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcurand.so locally
>>> hello = tf.constant("Hello, tensorFlow!")
>>> sess = tf.Session()
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
I tensorflow/core/common_runtime/gpu/gpu_device.cc:885] Found device 0 with properties:
name: GeForce GTX 1080
major: 6 minor: 1 memoryClockRate (GHz) 1.797
pciBusID 0000:01:00.0
Total memory: 7.92GiB
Free memory: 7.53GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1080, pci bus id: 0000:01:00.0)
>>> print(sess.run(hello))
b'Hello, tensorFlow!'

설치 위치
설치 경로 찾는 command

$ python3 -c 'import os; import inspect; import tensorflow; print(os.path.dirname(inspect.getfile(tensorflow)))'
/home/jemin/.local/lib/python3.5/site-packages/tensorflow

최종 버전 확인

jaynux@jaynux-desktop:~/tf_examples$ python3.5 -c 'import tensorflow as tf; print(tf.__version__)'
1.9.0

그래픽 카드별 성능차이 비교는 이전포스트를 참조한다.

CUDA 삭제

잘못 설치 했을 때 삭제 방법이다.

--auto-remove 옵션을 통해서 의존성 파일들을 모두 제거 한다.

# 간단한 방법
sudo apt-get remove --auto-remove nvidia-cuda-toolkit

# 좀 더 깨끗하게 
sudo apt-get --purge remove 'cuda*'
sudo apt-get autoremove --purge 'cuda*'

sudo rm -rf /usr/local/cuda-[your version]
sudo rm -rf /usr/local/cuda

PyCharm Pro로 TensorFlow 원격 빌드 환경설정


  • 클라이언트 환경: Windows-10, PyCharm Pro
  • 서버 환경: UbunTu 16.04 LTS, NVIDIA GTX-1080, TensorFlow 1.4

포인트는 SFTP SSH를 사용 해야 한다는 것이다. 이것을 도와주는것이 Remote Interpreter기능이다.
단, Pycharm pro 버전만 된다. comunity는 Remote interpreter를 지원하지 않는다.
하지만 jetbrain은 좋은 회사라서 대학생&대학원생이면 학교 이메일로 grant를 받을 수있다.
Grant관련해서는 이전 포스트를 참조 한다.

원격 인터프리터 설정

메뉴항목에서 File-> Settings -> project Interpreter를 선택 톱니바퀴 모양을 눌러서Add Remote..을 선택한다.

아래와 같은 창이 나오면 SSH Credentials를 선택

  • SSH Credentials 입력내용
    • Host name: IP
    • user name:계정
    • password: 비밀번호
    • python interpreter path: 자신의 python버전에 따라서 적절히 변경한다. python2.7의 경우/usr/bin/python이고 python3.5의 경우 /usr/bin/python3.5이다.
  • 환경 변수 설정
    • run -> Edit Configurations
    • Environment variables
    • CUDA_HOME추가 /usr/local/cuda
    • LD_LIBRARY_PATH 추가$LD_LIBRARY_PATH:/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64

코드 동기화 설정

메뉴에서 아래의 항목을 클릭한다.
Tools->development->Configuration

  1. Add를 눌러서 SFTP로 서버 설정을 생성

  2. Connection탭에서 IP 주소와 계정 비밀번호를 차례로 입력

  3. Mapping탭에서 Local Path Remote Path를 각각 맞게 입력

자동 파일 업로드

보통 설정 되어 있지만 혹시나 확인해 본다.
Tools > Deployment > Automatic Upload

원격 서버에서 실행 하기

  1. Run->Edit Configurations...를 눌러서 interpreter를 변경해 주자.

테스트 코드

'''
Created on Nov 17, 2015

@author: root
'''

import tensorflow as tf

a = tf.placeholder(tf.int16)
b = tf.placeholder(tf.int16)

# Define some operations
add = tf.add(a, b)
mul = tf.mul(a, b)

with tf.Session() as sess:
    print ("Addition with variables: %i" % sess.run(add, feed_dict={a:2, b:3}))
    print ("Multiplication with variables: %d" % sess.run(mul, feed_dict={a:2, b:3}))

실행 결과
아래와 같이 원격 PC에서 정상적으로 수행 되는것을 볼 수 있다.

ssh://jemin@xxx.xxx.xxx.xxx:22/usr/bin/python3 -u /home/jemin/tf_examples/
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcurand.so locally
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
I tensorflow/core/common_runtime/gpu/gpu_device.cc:885] Found device 0 with properties: 
name: GeForce GTX 1080
major: 6 minor: 1 memoryClockRate (GHz) 1.797
pciBusID 0000:01:00.0
Total memory: 7.92GiB
Free memory: 7.52GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0 
I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0:   Y 
I tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1080, pci bus id: 0000:01:00.0)
Addition with variables: 5
Multiplication with variables: 6

원격으로 이미지 파일 실행하기 (X11 forwarding)

ssh만 이용할 경우 결과가 이미지라면 실행에 에러가 발생한다.
예를들어 matplotlib를 이용해서 plot을 생성해서 show할경우 command line에서는 에러를 발생 시킨다.

이를 해결할 수 있는 방법이 X11 forwarding기능이다.
윈도우 환경에서 X11 forwarding을 사용하기 위해서는 아래의 프로그램을 설치해야 한다. 그럼 localhost server를 실행시켜주고 이 서버가 대신 이미지 결과를 받아서 보여주게 된다.

putty를 이용할 경우

pycharm을 이용할 경우

  1. 환경변수 탭에 아래와 같이 추가한다.
    Run->Edit Configurations이다.

  2. sudo vi /etc/ssh/sshd_config

  3. python consol 설정

  4. vim ~/.bashrc 설정 DISPLAY=localhost:10.0

실행 코드

import tensorflow
import matplotlib
matplotlib.use('GTKAgg')
import matplotlib.pyplot as plt
import numpy as np

print "Tensorflow Imported"
plt.plot(np.arange(100))
plt.show()

실행결과
실행 결과가 아래와 같이 나오는 것을 알 수 있다.

Troubleshooting

sftp 설정

Tools -> Developyment -> Configuration에서 SFTP를 설정 한다.

Mapping tab에서 local과 remote path가 잘 설정 되었는지 확인한다.

자동 갱신 설정

Tools->Deployment->OPtions에서Upload changed files automatically to the default server를 Always로 설정해 두면 자동으로 업로드까지 해준다.

GPU CUDA 문제

Pycharm이 .barshrc를 찾지 못하면 문제가 발생 할 수도 있다.

메뉴에서 Run>Edit Configuration>Environment Environment Variables라는 설정이 있다.
설정을 Name LD_LIBRARY_PATH로 정하고
Value 자신의CUDA 경로/lib64로 설정한다.

ConnectionRefusedError: [Errno 111] Connection refused

Run Edit Configuration에서 Path mappings안의 항목에 Show command line afterwards가 체크되어 있는 경우

[Errno 111] Connection refused 에러가 발생한다. 해결을 위해서 이것을 해제하고 실행한다.

CONDA Env환경일 경우

python interpreter path부분을 /usr/local/anaconda3/envs/<CONDA_ENV_NAME>/bin로 변경한다.
위와 같이 하면 해당 CONDA 환경의 python interpreter로 실행이 가능하다.

참고문헌

Work remotely with PyCharm, TensorFlow and SSH
http://stackoverflow.com/questions/30813370/how-can-i-enable-x-11-forwarding-in-pycharm-when-connecting-to-vagrant-or-a-rem
Using IntelliJ as Remote X Windows App


학습 모델의 재사용 (Transfer Learning)


DNN 모델을 스케치 단계서 부터 전부 다시 학습하는 것은 어려운 일이다. 정말 많은 데이터를 필요로 하기 때문에 학습이 거의 대부분 실패 한다.

여러 방법이 있을 수 있지만 여기서는 Transfer Learning을 다룬다. 잘알려진pretrainted model을 이용해서 마지막 final layer만 수정해서 본인의 목적에 맞게 다시 학습 시키는 방법이다.
혹자는 Fine tuning이라 부르기도 한다.

본인 보유한 데이터 양에 따라 어느 정도 레이어들을 수정할지가 결정된다. 보통 앞부분 layer는 추상적인 feature를 extraction하는 레이어이므로 건들지 않고 마지막 레이러들을 수정한다.

하지만 pretrained model이 현재 수행하려고 하는 작업에 전혀 관련이 없다면Transfer Learning이 의미가 퇴색 된다.
이 경우 unsupervised pre-training방법으로 RBM 또는 Autoencoder방법을 사용하거나 실제로 labeling 데이터를 왕창 모아서 학습을 해야한다.
하지만 scarth부터 random initialization 상태에서 학습하는 것은 매우 도전적인 일이므로 추천 하진 않는다.

결국 아무 때나 Deep Learning을 쓰기엔 무리가 있다.

개념설명

아래와 같이 ImageNet모델이 있다고 가정한다. 1000개를 classify할 수 있다.

이것을 가지고 고양이 Tigger, Misty, Neither 세개를 구분하는 모델을 만든다. 당연히 이런 특정 고양이 이미지는 많이 없다. 따라서 pre-trained 모델을 사용 한다.

이 때 학습할 때 마지막 softmax layer만 학습하게 된다.
이전 데이터는 freeze하게 된다. 이것은 deep learning framework에 따라 구현코드가 달라진다. 이후에는 TensorFlow를 이용해서 이 방법을 다룬다.

  • trainablePrameter =0, freeze = 1

그리고 이러한 freeze레이어의 숫자는 training data의 양에 따라서 다르게 적용할 수 있다.

Tensorflow 구현 (개념 설명)

전부다 저장하고 restore하는 코드

[...] # construct the original model

with tf.Session() as sess:
	saver.restore(sess, "./my_original_model.ckpt")
	[...] # Train it on your new task

부분적으로 복구하는 방법

init = tf.global_variables_initializer()
reuse_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope="hidden[123]")
reuse_vars_dict = dict([(var.name, var.name) for var in reuse_vars])
original_saver = tf.Saver(reuse_vars_dict) # saver to restore the original model

new_saver = tf.Saver() # saver to save the new model

with tf.Session() as sess:
	sess.run(init)
	original_saver.restore("./my_original_model.ckpt") # restore layers 1 to 3
	[...] # train the new model
	new_saver.save("./my_new_model.ckpt") # save the whole model

좀 더 상세한 내용은 이전 포스트를 참조한다.

실제코드

MNIST 데이터를 가지고 fully connected DNN의 pre-trained model을 로드해서 fine-tuning 하는 방법을 다룬다.
전체 구현코드는 Github Link에 있다.

  • 모델 학습
  • 전체 로드
  • 일부분 로드
  • 앞부분 레이어 고정후 뒷 부분만 학습
  • 고정레이어 cache후 뒷 부분만 학습 (트레이닝 속도 향상)

기본 라이브러리 로딩

import tensorflow as tf
# Common imports
import numpy as np
import os
from tensorflow.examples.tutorials.mnist import input_data

def reset_graph(seed=42):
    tf.reset_default_graph()
    tf.set_random_seed(seed)
    np.random.seed(seed)
def leaky_relu(z, name=None):
    return tf.maximum(0.01 * z, z, name=name)

mnist = input_data.read_data_sets("./")
Extracting ./train-images-idx3-ubyte.gz
Extracting ./train-labels-idx1-ubyte.gz
Extracting ./t10k-images-idx3-ubyte.gz
Extracting ./t10k-labels-idx1-ubyte.gz

모델 생성

하이퍼파라메터 설정

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 50
n_hidden3 = 50
n_hidden4 = 50
n_hidden5 = 50
n_outputs = 10

n_epochs = 20
batch_size = 50

모델 생성

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2")
    hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3")
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4")
    hidden5 = tf.layers.dense(hidden4, n_hidden5, activation=tf.nn.relu, name="hidden5")
    logits = tf.layers.dense(hidden5, n_outputs, name="outputs")

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

learning_rate = 0.01
threshold = 1.0

optimizer = tf.train.GradientDescentOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(loss)
capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var)
              for grad, var in grads_and_vars]
training_op = optimizer.apply_gradients(capped_gvs)

init = tf.global_variables_initializer()
saver = tf.train.Saver()

학습

with tf.Session() as sess:
    init.run()
    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        if epoch % 5 == 0:
            acc_train = accuracy.eval(feed_dict={X: X_batch, y: y_batch})
            acc_test = accuracy.eval(feed_dict={X: mnist.validation.images, y: mnist.validation.labels})
            print(epoch, "Batch accuracy:", acc_train, "Validation accuracy:", acc_test)

    save_path = saver.save(sess, "./my_model_final.ckpt")
0 Batch accuracy: 0.94 Validation accuracy: 0.9006
5 Batch accuracy: 1.0 Validation accuracy: 0.9642
10 Batch accuracy: 0.96 Validation accuracy: 0.9712
15 Batch accuracy: 0.98 Validation accuracy: 0.9772
20 Batch accuracy: 1.0 Validation accuracy: 0.9774
25 Batch accuracy: 1.0 Validation accuracy: 0.9786
30 Batch accuracy: 1.0 Validation accuracy: 0.9778
35 Batch accuracy: 1.0 Validation accuracy: 0.9776

모델 로딩후 재학습

여기서 부터 모델을 불러와서 다시 fine-tuning 하는 부분을 구현 한다.

reset_graph()
saver = tf.train.import_meta_graph("./my_model_final.ckpt.meta")

로딩가능한 operation이 무엇인지 확인한다.

for op in tf.get_default_graph().get_operations():
    print(op.name)
X
y
hidden1/kernel/Initializer/random_uniform/shape
hidden1/kernel/Initializer/random_uniform/min
hidden1/kernel/Initializer/random_uniform/max
[생략]
[생략]
save/RestoreV2_11/tensor_names
save/RestoreV2_11/shape_and_slices
save/RestoreV2_11
save/Assign_11
save/restore_all

파라메터가 너무 많기 때문에 TensorBoard로 확인한다.
그다음 아래와 같이 get_tensor_by_name get_operation_by_name으로 로드한다.

X = tf.get_default_graph().get_tensor_by_name("X:0")
y = tf.get_default_graph().get_tensor_by_name("y:0")

accuracy = tf.get_default_graph().get_tensor_by_name("eval/accuracy:0")

training_op = tf.get_default_graph().get_operation_by_name("GradientDescent")

다른 사람의 편의를 위해서 import operation collection으로 미리 정의해 줄수도 있다.

for op in (X, y, accuracy, training_op):
    tf.add_to_collection("my_important_ops", op)

이렇게 하면 다른 사람들이 쉽게 로딩 가능하다.

X, y, accuracy, training_op = tf.get_collection("my_important_ops")

아래와 같이 이제 본인의 데이터로 본격적으로 학습이 가능하다.

with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")
    # continue training the model...
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt

아니면 모델을 로드에서 Testing만 가능하다.

with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_new_model_final.ckpt")    
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9743
1 Test accuracy: 0.9744
2 Test accuracy: 0.9756
3 Test accuracy: 0.9743
4 Test accuracy: 0.9751
5 Test accuracy: 0.975
6 Test accuracy: 0.9741
7 Test accuracy: 0.9742
8 Test accuracy: 0.9751
9 Test accuracy: 0.9748
10 Test accuracy: 0.9744
11 Test accuracy: 0.9747
12 Test accuracy: 0.9746
13 Test accuracy: 0.9747
14 Test accuracy: 0.9746
15 Test accuracy: 0.9747
16 Test accuracy: 0.9742
17 Test accuracy: 0.9749
18 Test accuracy: 0.9746
19 Test accuracy: 0.9748
20 Test accuracy: 0.975
21 Test accuracy: 0.9746
22 Test accuracy: 0.9745
23 Test accuracy: 0.975
24 Test accuracy: 0.9744
25 Test accuracy: 0.9743
26 Test accuracy: 0.9743
27 Test accuracy: 0.9745
28 Test accuracy: 0.9746
29 Test accuracy: 0.9749
30 Test accuracy: 0.9746
31 Test accuracy: 0.9747
32 Test accuracy: 0.9747
33 Test accuracy: 0.9743
34 Test accuracy: 0.9746
35 Test accuracy: 0.9746
36 Test accuracy: 0.9749
37 Test accuracy: 0.9751
38 Test accuracy: 0.9748
39 Test accuracy: 0.9743

다른 방법은 그냥 원래 모델에 대한 code가 있다면 그것을 로딩해도 된다. 그렇게 하면import_meta_graph()를 호출하지 않아도 된다.

마지막 4번째 레어어만 수정해서 재학습하기 (not freezing the lower layers)

  • import_meta_graph()로 전체 graph를 모두 불러온다음 4번째 Layer를 무시한다.
  • 즉 3번째 레이어 까지만 재사용한다.
  • 그리고 output 레이어도 재설정한다.
  • 그리고 이것으로 optimizer를 이용해서 최적화한다.
  • 이렇게 생성된 결과를 새로운 파일에 저장한다.
reset_graph()

n_hidden4 = 20  # new layer
n_outputs = 10  # new layer

saver = tf.train.import_meta_graph("./my_model_final.ckpt.meta")

X = tf.get_default_graph().get_tensor_by_name("X:0")
y = tf.get_default_graph().get_tensor_by_name("y:0")

hidden3 = tf.get_default_graph().get_tensor_by_name("dnn/hidden4/Relu:0")

new_hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="new_hidden4")
new_logits = tf.layers.dense(new_hidden4, n_outputs, name="new_outputs")

with tf.name_scope("new_loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=new_logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("new_eval"):
    correct = tf.nn.in_top_k(new_logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

with tf.name_scope("new_train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)

init = tf.global_variables_initializer()
new_saver = tf.train.Saver()

새로운 레이어를 정의하고 loss namesapce를 다르게 정의 했기 때문에saver.restore()후에 값이 중복 되지 않는다.

with tf.Session() as sess:
    init.run()
    saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = new_saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9665
...
...
39 Test accuracy: 0.9751

마지막 4번째 레어어만 수정해서 재학습하기 (freezing the lower layers)

구현을 위해서는 2가지 방법이 존재한다.

  • tf.GraphKeys.TRAINABLE_VARIABLES, scope="outputs")을 이용한 방법
  • tf.stop_gradient를 이용한방법

tf.GraphKeys.TRAINABLE_VARIABLES 이용

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300 # reused
n_hidden2 = 50  # reused
n_hidden3 = 50  # reused
n_hidden4 = 20  # new!
n_outputs = 10  # new!

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")       # reused
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # reused
    hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # reused
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # new!
    logits = tf.layers.dense(hidden4, n_outputs, name="outputs")                         # new!

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

학습할 대상을 정규식에 의해서 scope을 정해서 불러온다.
결과는 아래와 같다.

tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
                                   scope="outputs")
[<tf.Variable 'outputs/kernel:0' shape=(20, 10) dtype=float32_ref>,
<tf.Variable 'outputs/bias:0' shape=(10,) dtype=float32_ref>]
with tf.name_scope("train"):                                         # not shown in the book
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)     # not shown
    train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
                                   scope="outputs")
    training_op = optimizer.minimize(loss, var_list=train_vars)
init = tf.global_variables_initializer()
new_saver = tf.train.Saver()

따로 새롭게 namesapce를 정의하지 않고 load할 때 정규식을 이용해서 일부분만 불러온다.
이렇게 하면 name을 변경할 필요 없다.

tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]|outputs") # regular expression
[<tf.Variable 'hidden1/kernel:0' shape=(784, 300) dtype=float32_ref>,
 <tf.Variable 'hidden1/bias:0' shape=(300,) dtype=float32_ref>,
 <tf.Variable 'hidden2/kernel:0' shape=(300, 50) dtype=float32_ref>,
 <tf.Variable 'hidden2/bias:0' shape=(50,) dtype=float32_ref>,
 <tf.Variable 'hidden3/kernel:0' shape=(50, 50) dtype=float32_ref>,
 <tf.Variable 'hidden3/bias:0' shape=(50,) dtype=float32_ref>,
 <tf.Variable 'outputs/kernel:0' shape=(20, 10) dtype=float32_ref>,
 <tf.Variable 'outputs/bias:0' shape=(10,) dtype=float32_ref>]
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]|outputs]") # regular expression
reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3

init = tf.global_variables_initializer()
saver = tf.train.Saver()

with tf.Session() as sess:
    init.run()
    restore_saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9221
...
...
39 Test accuracy: 0.9556

tf.stop_gradient 이용

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300 # reused
n_hidden2 = 50  # reused
n_hidden3 = 50  # reused
n_hidden4 = 20  # new!
n_outputs = 10  # new!

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")
with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
                              name="hidden1") # reused frozen
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,
                              name="hidden2") # reused frozen
    hidden2_stop = tf.stop_gradient(hidden2)
    hidden3 = tf.layers.dense(hidden2_stop, n_hidden3, activation=tf.nn.relu,
                              name="hidden3") # reused, not frozen
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu,
                              name="hidden4") # new!
    logits = tf.layers.dense(hidden4, n_outputs, name="outputs") # new!
with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)

위와 같이 hidden2_stop = tf.stop_gradient(hidden2)을 사용해서 중간 레이어를 만든다.
그 이후에는 트레이닝 코드는 위 방식과 정확히 일치한다.

%%time
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]") # regular expression
reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3

init = tf.global_variables_initializer()
saver = tf.train.Saver()

with tf.Session() as sess:
    init.run()
    restore_saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9654
...
...
19 Test accuracy: 0.9738
CPU times: user 23.1 s, sys: 852 ms, total: 23.9 s
Wall time: 13.5 s

Fronzen Layer를 cache해서 학습속도를 올리는 방법

Frozen 레이어는 변화하지 않기 때문에 이것은 cache해서 재사용 할 수 있다.

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300 # reused
n_hidden2 = 50  # reused
n_hidden3 = 50  # reused
n_hidden4 = 20  # new!
n_outputs = 10  # new!

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
                              name="hidden1") # reused frozen
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,
                              name="hidden2") # reused frozen & cached
    hidden2_stop = tf.stop_gradient(hidden2)
    hidden3 = tf.layers.dense(hidden2_stop, n_hidden3, activation=tf.nn.relu,
                              name="hidden3") # reused, not frozen
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu,
                              name="hidden4") # new!
    logits = tf.layers.dense(hidden4, n_outputs, name="outputs") # new!

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]") # regular expression
reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3

init = tf.global_variables_initializer()
saver = tf.train.Saver()

핵심 방법은 1번만 트레이닝해서 h2_cache를 만든다음 이것을 suffling한 index를 가지고 epoch을 돌면서 training하는 것이다.
메모리가 충분하다면 이러한 방법이 가능하다.
뒷 부분을 계산하지 않기 때문에 training속도를 증가 시킬 수 있다.13.5초에서9.63초로 속도가 빨라진 것을 알 수 있다.

%%time
import numpy as np

n_batches = mnist.train.num_examples // batch_size

with tf.Session() as sess:
    init.run()
    restore_saver.restore(sess, "./my_model_final.ckpt")
    
    h2_cache = sess.run(hidden2, feed_dict={X: mnist.train.images})
    h2_cache_test = sess.run(hidden2, feed_dict={X: mnist.test.images}) # not shown in the book

    for epoch in range(n_epochs):
        shuffled_idx = np.random.permutation(mnist.train.num_examples)
        hidden2_batches = np.array_split(h2_cache[shuffled_idx], n_batches)
        y_batches = np.array_split(mnist.train.labels[shuffled_idx], n_batches)
        for hidden2_batch, y_batch in zip(hidden2_batches, y_batches):
            sess.run(training_op, feed_dict={hidden2:hidden2_batch, y:y_batch})

        accuracy_val = accuracy.eval(feed_dict={hidden2: h2_cache_test, # not shown
                                                y: mnist.test.labels})  # not shown
        print(epoch, "Test accuracy:", accuracy_val)                    # not shown

    save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9648
...
...
19 Test accuracy: 0.9736
CPU times: user 20.7 s, sys: 428 ms, total: 21.1 s
Wall time: 9.63 s

참고문헌

Coursera deep learning ai, Andrew Ng.
Hands-On Machine Learning with Scikit-Learn and Tensorflow, Aureien Geron


TensorFlow 모델을 저장하고 불러오기 (save and restore)


해당 튜토리얼에 사용한 코드는 개인 GitHub Link에서 확인 할 수 있다.

저장 복구를 위해서는 두개의 파일이 필요하다.

a) Meta graph

Tensorflow graph를 저장 하게 된다. 즉 all variables, operations, collections 등을 저장 한다. .meta로 확장자를 가진다.

b) Checkpoint file

binary 파일로 weights, biases, gradients 등을 저장 한다.
0.11부터는 두개의 파일로 저장된다.

  • model.ckpt.data-00000-of-00001
  • model.ckpt.index

.data파일의 경우 training variable를 가지고 있다.
여전히 checkpoint파일도 보유하고 있지만 이것은 단순히 최근 상태만을 기록하고 있다.

모델 저장 방법

saver = tf.train.Saver()를 통해서 가능 하다.

import tensorflow as tf
w1 = tf.Variable(tf.random_normal(shape=[2]), name='w1')
w2 = tf.Variable(tf.random_normal(shape=[5]), name='w2')
saver = tf.train.Saver()
sess = tf.Session()
sess.run(tf.global_variables_initializer())
saver.save(sess, 'my_test_model')

# This will save following files in Tensorflow v >= 0.11
# my_test_model.data-00000-of-00001
# my_test_model.index
# my_test_model.meta
# checkpoint

만약 1000 interations 이후에 model을 저장하고 싶다면 아래와 같이 한다.

saver.save(sess, "my_test_model", global_step=1000)

이렇게 하면 아래처럼 모델뒤에 -1000이라는 숫자가 이름에 붙어서 나오게 됩니다.

my_test_model-1000.index
my_test_model-1000.meta
my_test_model-1000.data-00000-of-00001
checkpoint

모델의 구조는 같기 때문에 .meta파일의 경우 1000 interation당 매번 생성할 필요는 없다.

모델 값만 저장하고 graph는 저장하지 않는 코드는 아래와 같다.

saver.save(sess, 'my-model', global_step=step,write_meta_graph=False)

만약 최근 2시간동안 4개의 모델만 저장하고 싶다면 아래와 같이 옵션을 설정한다.

#saves a model every 2 hours and maximum 4 latest models are saved.
saver = tf.train.Saver(max_to_keep=4, keep_checkpoint_every_n_hours=2)

망약 tf.train.Saver()에 아무것도 지정하지 않았다면 모든 변수들을 저장하게 된다.
특정 variables/collections을 저장하고 싶다면 그것을 지정하면 된다.
List, dictionary자료구조도 받으니 그것을 잘 활용 한다.

import tensorflow as tf
w1 = tf.Variable(tf.random_normal(shape=[2]), name='w1')
w2 = tf.Variable(tf.random_normal(shape=[5]), name='w2')
saver = tf.train.Saver([w1,w2])
sess = tf.Session()
sess.run(tf.global_variables_initializer())
saver.save(sess, 'my_test_model',global_step=1000)

모델 읽기

두 가지 일을 해야한다.

a) 네트워크 생성

.meta파일을 생성 했으므로 이것을 불러오는 방식으로 network을 재생성 할 수 있다.
.meta파일을 불러오기 위해서는 tf.train.import() 함수를 이용한다.

saver = tf.train.import_meta_graph('my_test_model-1000.meta')

이렇게 하면 현재 그래프에 이어 붙는 형식으로 동작하므로tf.reset_default_graph()를 실행해서 default graph로 초기화 해주는 것이 안전하다.

b) 파라메터 로딩

tf.train.Saver()를 이용해서 파라메터를 로딩한다.

with tf.Session() as sess:
  new_saver = tf.train.import_meta_graph('my_test_model-1000.meta')
  new_saver.restore(sess, tf.train.latest_checkpoint('./'))
with tf.Session() as sess:    
    saver = tf.train.import_meta_graph('my-model-1000.meta')
    saver.restore(sess, tf.train.latest_checkpoint('./'))
    print(sess.run('w1:0'))
##Model has been restored. Above statement will print the saved value of w1.

저장된 모델로 실제 작업하기

이제 위해서 다룬 내용을 토대로 종합적으로 간단한 neural net.을 생성하고 이것을 저장한다음 다시 불러오는 코드를 작성해 본다.
이런한 작업은 추후에 transfer learning이나 testing만 별도로 작업하기 위해서 사용 될 수 있다.

아래의 코드는 $y=(w1+w2) \times b$ 를 구현한 내용이다.
여기서 핵심은 추후에 variable operation을 각각 불러오기 위해서 name을 반드시 주어야 한다.
나중에 본인 model을 공유할 때도 이름을 잘 정해주어야 다른 사람이 가져다가 본인들 목적에 맞춰서 fine-tuning해서 사용할 수 있다.

모델 생성 및 저장

import tensorflow as tf

# Prepare to feed input, i.e. feed_dict and placeholders
w1 = tf.placeholder(tf.float32, name="w1")
w2 = tf.placeholder(tf.float32, name="w2")
b1 = tf.Variable(2.0,dtype=tf.float32, name="bias")
feed_dict = {'w1': 4.0, 'w2': 8.0}

# Define a test operation that we will restore
w3 = w1 + w2
w4 = tf.multiply(w3, b1, name="op_to_restore")
sess = tf.Session()
sess.run(tf.global_variables_initializer())

# Create a saver object which will save all the variables
saver = tf.train.Saver()

# Run the operation by feeding input
result = sess.run(w4, {w1:feed_dict['w1'], w2:feed_dict['w2