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']})
print(result)
# Prints 24 which is sum of (w1+w2)*b1

# Now, save the graph
saver.save(sess, './my_test_model', global_step=1000)

실행결과는 24이다.

모델 불러오기와서 새로운 입력값으로 처리

모델을 복구하고 feed_dict을 다르게 입력하는 코드이다.

import tensorflow as tf

sess=tf.Session()    
#First let's load meta graph and restore weights
saver = tf.train.import_meta_graph('my_test_model-1000.meta')
saver.restore(sess,tf.train.latest_checkpoint('./'))


# Now, let's access and create placeholders variables and
# create feed-dict to feed new data

graph = tf.get_default_graph()
w1 = graph.get_tensor_by_name("w1:0")
w2 = graph.get_tensor_by_name("w2:0")
feed_dict ={w1:13.0,w2:17.0}

#Now, access the op that you want to run. 
op_to_restore = graph.get_tensor_by_name("op_to_restore:0")

print (sess.run(op_to_restore,feed_dict))
#This will print 60 which is calculated 
#using new values of w1 and w2 and saved value of b1. 

실행결과는 60이다.

위 예제는 Tensor를 이용해서 간단하게 구현하다보니 모두get_tensor_by_name()으로 가능하지만 실제로 operation과 placeholder는 각각 다르게 load해야 한다.

  • placeholder 불러오기

    • graph.get_tensor_by_name()
  • operation 불러오기

    • graph.get_operation_by_name()

로딩 가능한 현재 graph에서의 operation의 종류이다.

for op in tf.get_default_graph().get_operations():
    print(op.name)

모델을 불러오고 operation과 layer 추가

import tensorflow as tf

sess=tf.Session()    
#First let's load meta graph and restore weights
saver = tf.train.import_meta_graph('my_test_model-1000.meta')
saver.restore(sess,tf.train.latest_checkpoint('./'))


# Now, let's access and create placeholders variables and
# create feed-dict to feed new data

graph = tf.get_default_graph()
w1 = graph.get_tensor_by_name("w1:0")
w2 = graph.get_tensor_by_name("w2:0")
feed_dict ={w1:13.0,w2:17.0}

#Now, access the op that you want to run. 
op_to_restore = graph.get_tensor_by_name("op_to_restore:0")

#Add more to the current graph
add_on_op = tf.multiply(op_to_restore,2)

print (sess.run(add_on_op,feed_dict))
#This will print 120.

실행결과 120이다.

참고자료

http://cv-tricks.com/tensorflow-tutorial/save-restore-tensorflow-models-quick-complete-tutorial/


Batch 크기의 결정 방법


보통 vectorization방법으로 gradient descent알고리즘의 효율을 높이게 된다.

하지만 input 데이터가 너무 크다면 그 방법을 사용할 수 없다.
메모리 문제도 발생하고 한 번 interation (epoch)당 시간이 너무 오래 걸리기 때문이다.

Batch-gradient descent mini-bach gradient descent의 cost 그래프의 차이는 아래와 같다.

Choosing your mini-batch size

  • Mini-batch 크기가 전체 트레이닝 셋 데이터 사이즈인 m과 같다면 이것은 Batch gradient descent방법이다.

    • (+) 상대적으로 아래의 contour그래프를 보면 알 수 있듯이 적은 noise Large step으로 글로벌 미니멈에 수렴한다.
    • (+) vectorization 효율이 좋다.
    • (-) interation당 속도가 느리다.
    • (-) 메모리 여유 공간에 따라서 실행 불가능 할 수도 있다.
  • Mini-batch 크기가 1이라면,Stochastic gradient descent라고 부른다.

    • (+) 적은 메모리로 동작 가능하다. noise한 부분은 learning rate을 작게하면 완화 할 수 있다.
    • (-) vectorization 효율이 없다. training data를 1개만 사용하기 때문이다.
  • Mini-batch크기를 너무 작게도 크게도 하지 않는다.

    • (+) vectorization을 효과 본다.
    • (+) interation당 긴 시간없이 progress를 만들 수 있다.

최종적 가이드라인

  • 데이터가 별로 없다면 batch gradient descent를 쓴다.
    • ex 2,000 정도
  • mini-batch를 선택
    • 64, 128, 256, 512 사이즈로 선택한다. 메모리 사이즈에 맞춰서
  • CPU/GPU memory 사이즈에 맞춰서 조절 한다.

Take-away message
SGD GD가 같은 글로벌 cost 최소점에 도달 할 수 있다는 것은 증명이 되어 있지만, neural netwrok은 convex가 아니기 때문에 batch 크기의 설정 방법에 따라 수렴하는 조건이 다를 수 있다.

Batch size는 일반적으로 메모리가 감당할 수 있는 정도까지 최대한 크게 잡는게 좋을것 같다.

참고자료

Coursera DeepLearning ai, Andrew Ng


딥러닝 입력 데이터 정규화 (Normalizing inputs)


학습을 수행하기 전에 값의 범위를 normalization 하는 것은 중요하다. 그 이유는 아래와 같다.

입력 변수가 MLP에서와 같이 선형 적으로 결합된다면 적어도 이론 상으로는 입력을 표준화하는 것이 거의 필요하지 않습니다.
그 이유는 해당 weight bais를 변경하여 입력 벡터를 재조정하면 이전과 완전히 똑같은 결과를 남길 수 있기 때문입니다.
그러나 입력을 Standardization하면 학습을 더 빨리하고 지역 최적의 상태에 빠지게 될 가능성을 줄이는 다양한 실용적인 이유가 있습니다.
또한, 표준화 된 입력을 통해 Gradient Descent  Bayesian estimation을 보다 편리하게 수행 할 수 있습니다.

Normalization

수식 : (요소값 - 최소값) / (최대값 - 최소값)
설명 : 전체 구간을 0~1사이의 값으로 맞춰 준다.

Standardization

수식 : (요소값 - 평균) / 표준편차
설명 : 평균은 0 표준편차는

$$\mu = \frac{1}{m} \sum_{i=1}^{m}{x^{(i)}}$$
$$x := x -\mu$$
$$\sigma^{2}=\frac{1}{m}\sum_{i=1}^{m}{(x^{(i)}-\mu)^{2}}$$

직관적 이해

아래와 같이 Unnormalized된 상태에서는 Learning Rate을 매우 작게 설정해야 정상적을 학습이 된다.
이유는 cost 그래프가 elongated하기 때문이다. 따라서 elongated contour의 모습을 가진다.
아래와 같이 Input의 Range가 서로 다르다면 Gradient Descent Algorithm을 적용하는것이 매우 까다로워지는 상황이 발생 한다.

하지만 normalization을 적용하면 좀 더 spherical contour를 가지게 된다.
이렇게 하면 좀 더 Gradient Descent Algorithm으로 쉽게 그리고 빠르게 최적화 지점을 찾게 된다.

구현 코드

아래와 같은 코드를 가질 때
x가 1.0
y가 0.0
이렇게 1.0~0 사이의 값으로 입력과 출력이 정규화가 잘되면 학습이 잘 이뤄 진다.
Learning rate을 적절히 값을 잡아도 학습이 잘된다.

import tensorflow as tf
x = tf.constant(1.0, name='input')
w = tf.Variable(0.8, name='weight')
y = tf.mul(w, x, name='output')
y_ = tf.constant(0.0, name='correct_value')
loss = tf.pow(y - y_, 2, name='loss')
train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)

for value in [x, w, y, y_, loss]:
    tf.scalar_summary(value.op.name, value)

summaries = tf.merge_all_summaries()

sess = tf.Session()
summary_writer = tf.train.SummaryWriter('log_simple_stats', sess.graph)

sess.run(tf.initialize_all_variables())
for i in range(100):
    if i % 10 ==0:
        print("epoch {}, output: {}".format(i, sess.run(y)))
    summary_writer.add_summary(sess.run(summaries), i)
    sess.run(train_step)
epoch 80, output: 0.01321229338645935
epoch 90, output: 0.007910688407719135

하지만 x의 범위르 10배만 넓혀서 10으로 하면 학습이 실패 한다.
이 때는 Learning rate을 더 작게 주어야 한다.

import tensorflow as tf
x = tf.constant(10.0, name='input')
w = tf.Variable(0.8, name='weight')
y = tf.mul(w, x, name='output')
y_ = tf.constant(0.0, name='correct_value')
loss = tf.pow(y - y_, 2, name='loss')
train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)

for value in [x, w, y, y_, loss]:
    tf.scalar_summary(value.op.name, value)

summaries = tf.merge_all_summaries()

sess = tf.Session()
summary_writer = tf.train.SummaryWriter('log_simple_stats', sess.graph)

sess.run(tf.initialize_all_variables())
for i in range(100):
    if i % 10 ==0:
        print("epoch {}, output: {}".format(i, sess.run(y)))
    summary_writer.add_summary(sess.run(summaries), i)
    sess.run(train_step)
epoch 70, output: nan

통상의 Learning Rate의 문제와 비슷하게 발생 한다.

참고 사이트

http://stackoverflow.com/questions/4674623/why-do-we-have-to-normalize-the-input-for-an-artificial-neural-network


rpy2와 Pandas를 활용한 R object 변환


Rdata파일을 읽어와서 Robject를 다루는 방법을 기술한다.
필요한 package들은 아래와 같다.

from rpy2.robjects import r
from rpy2.robjects import pandas2ri
import pandas as pd
pandas2ri.activate()

위와 같이 robjects를 다루는 r pandas r을 연결해주는 pandas2ri 두 개의 package가 필요하다.

로딩 방법

# Object를 생성 한다.

r.load("./Rdata/num10FclassDf10PassTrain.Rdata")

실행 결과

R object with classes: ('character',) mapped to:
<StrVector - Python:0x7fc114f787c8 / R:0x2cdfbd8>
['num10FclassDf10PassTrain']

데이터 출력

r['num10FclassDf10PassTrain']

실행결과

R object with classes: ('list',) mapped to:
<ListVector - Python:0x7fc115095bc8 / R:0x265d9f0>
[Matrix, Matrix, Matrix, ..., Matrix, Matrix, Matrix]
  acquaintanceSeula: <class 'rpy2.robjects.vectors.Matrix'>
  R object with classes: ('matrix',) mapped to:
<Matrix - Python:0x7fc1150877c8 / R:0x4072ca0>
[       1,        1,        1, ...,        1,        1,        1]
  ikhee: <class 'rpy2.robjects.vectors.Matrix'>
  R object with classes: ('matrix',) mapped to:
<Matrix - Python:0x7fc1150b7308 / R:0x407a9d0>
[       1,        2,        1, ...,        1,        1,        2]
  Jemin: <class 'rpy2.robjects.vectors.Matrix'>
  R object with classes: ('matrix',) mapped to:
<Matrix - Python:0x7fc1153085c8 / R:0x40843b0>
[       2,        1,        1, ...,        1,        2,        2]
  ...
  acquaintanceSeula: <class 'rpy2.robjects.vectors.Matrix'>
  R object with classes: ('matrix',) mapped to:
<Matrix - Python:0x7fc11530c048 / R:0x432ead0>
[       1,        2,        2, ...,        2,        2,        2]
  ikhee: <class 'rpy2.robjects.vectors.Matrix'>
  R object with classes: ('matrix',) mapped to:
<Matrix - Python:0x7fc114efff08 / R:0x434e400>
[       1,        1,        1, ...,        2,        2,        2]
  Jemin: <class 'rpy2.robjects.vectors.Matrix'>
  R object with classes: ('matrix',) mapped to:
<Matrix - Python:0x7fc114eff888 / R:0x4354790>
[       1,        1,        3, ...,        1,        1,        2]

ri2py로 데이터형 변환

pandas2ri.ri2py(r['num10FclassDf10PassTrain'][0])

데이터 프레임으로 변경하는 방법

df1 = pd.DataFrame(pandas2ri.ri2py(r['num10FclassDf10PassTrain'][0]), columns=['AppName', "Title", "Hours", "Days", "RecentPhoneUsage", "Proximity", "Priority", "Activity", "PhoneStatus", "SeenTime", "class"])
df1.head()

참고문헌

https://pandas.pydata.org/pandas-docs/stable/r_interface.html


Confusion Matrix in python


Tensorflow를 이용한 prediction 결과를 평가하기 위해서 Confusion Matrix을 이용한다.
단순 Accuracy에 대해서는 어느정도 문제가 발생하기 때문이다.
아래의 두 package를 각각 이용할 수도 있다.

scikit

scikit package를 이용한 방법이 존재한다.

특정 값만 표시해 줄 수도 있고 Confusion Matrix를 표현할 수도 있다.

# Import packages
from sklearn.metrics import classification_report
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score

# Data
y_true = [1, 1, 0, 1, 0]
y_pred = [1, 1, 1, 0, 0]
target_names = ['false', 'true']
print(classification_report(y_true, y_pred, target_names=target_names))
precision_score(y_true, y_pred, average='binary',pos_label=1)
recall_score(y_true, y_pred, average='binary',pos_label=1)

실행 결과

	      precision    recall  f1-score   support

false       0.50      0.50      0.50         2
true       0.67      0.67      0.67         3

avg / total       0.60      0.60      0.60         5

precision: 0.66666666666666663
recall: 0.66666666666666663

Pandas

import pandas as pd

y_true = pd.Series([2, 0, 2, 2, 0, 1, 1, 2, 2, 0, 1, 2])
y_pred = pd.Series([0, 0, 2, 1, 0, 2, 1, 0, 2, 0, 2, 2])

pd.crosstab(y_true, y_pred, rownames=['True'], colnames=['Predicted'], margins=True)

Result

Predicted  0  1  2  All
True                   
0          3  0  0    3
1          0  1  2    3
2          2  1  3    6
All        5  2  5   12


Tensorflow Object Detection API (SSD, Faster-R-CNN)


2017.6.15에 Google에서 Tensorflow로 구현된Object Detection코드를 공개 했다. 말은 API라고 적혀 있지만 그냥 구현 코드이다. 지원하는 모델은 아래와 같다.

  • Single Shot Multibox Detector (SSD) with MobileNet
  • SSD with Inception V2
  • Region-Based Fully Convolutional Networks (R-FCN) with ResNet 101
  • Faster R-CNN with Resnet 101
  • Faster RCNN with Inception Resnet v2

설치 및 테스팅

일단 TF관련 예제를 구글이 만들어 놓은곳은 모두 modelsgithub에 있다.

git clone https://github.com/tensorflow/models.git

클론한 다음 object detection으로 들어간 다음Jupyter Notebook으로object_detection_tutorial.ipynb을 실행 한다.

코드를 정상적으로 실행 하기 위해선 아래의 작업을 수행해 줘야 한다.

dependencies

  • Protobuf 2.6
  • Pillow 1.0
  • lxml
  • tf Slim (which is included in the "tensorflow/models" checkout)
  • Jupyter notebook
  • Matplotlib
  • Tensorflow

Ubuntu 16.04 기준

sudo apt-get install protobuf-compiler python-pil python-lxml
sudo pip install jupyter
sudo pip install matplotlib

sudo pip install pillow
sudo pip install lxml
sudo pip install jupyter
sudo pip install matplotlib

Protobuf 컴파일

clone한 models에서 실행 한다.

# From tensorflow/models/
protoc object_detection/protos/*.proto --python_out=.

실행하면 아래와 같이 python코드들이 생성된다.

jemin@jemin-desktop:~/tf_examples/models/object_detection/protos$ ls
BUILD                         losses_pb2.py
__init__.py                   matcher.proto
__pycache__                   matcher_pb2.py
anchor_generator.proto        mean_stddev_box_coder.proto
anchor_generator_pb2.py       mean_stddev_box_coder_pb2.py
argmax_matcher.proto          model.proto
argmax_matcher_pb2.py         model_pb2.py
bipartite_matcher.proto       optimizer.proto
bipartite_matcher_pb2.py      optimizer_pb2.py
box_coder.proto               pipeline.proto
box_coder_pb2.py              pipeline_pb2.py
box_predictor.proto           post_processing.proto
box_predictor_pb2.py          post_processing_pb2.py
eval.proto                    preprocessor.proto
eval_pb2.py                   preprocessor_pb2.py
faster_rcnn.proto             region_similarity_calculator.proto
faster_rcnn_box_coder.proto   region_similarity_calculator_pb2.py
faster_rcnn_box_coder_pb2.py  square_box_coder.proto
faster_rcnn_pb2.py            square_box_coder_pb2.py
grid_anchor_generator.proto   ssd.proto
grid_anchor_generator_pb2.py  ssd_anchor_generator.proto
hyperparams.proto             ssd_anchor_generator_pb2.py
hyperparams_pb2.py            ssd_pb2.py
image_resizer.proto           string_int_label_map.proto
image_resizer_pb2.py          string_int_label_map_pb2.py
input_reader.proto            train.proto
input_reader_pb2.py           train_pb2.py
losses.proto

Add Libraries to PYTHONPATH

slim 디렉터리를 append시키기 위함이다.

# From tensorflow/models/
export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim

Testing the Installation
최종적으로 설치가 정상적으로 되었는지 아래의 명령어로 확인한다.

python object_detection/builders/model_builder_test.py

.......
----------------------------------------------------------------------
Ran 7 tests in 0.013s

OK

기타

  • Tensorflow 1.2.1 버전
  • Jupyter 5.4.0
  • Python 3.5.2

Troubleshooting

  • Load a Tensorflow model into memory 부분에서 에러가 발생하면 python3으로 커널을 변경하면 해결 된다.

실행 결과

COCO 이미지 두개를 불러와서 바운딩 박스를 치면 아래와 같다.

with detection_graph.as_default():
  with tf.Session(graph=detection_graph) as sess:
    for image_path in TEST_IMAGE_PATHS:
      image = Image.open(image_path)
      # the array based representation of the image will be used later in order to prepare the
      # result image with boxes and labels on it.
      image_np = load_image_into_numpy_array(image)
      # Expand dimensions since the model expects images to have shape: [1, None, None, 3]
      image_np_expanded = np.expand_dims(image_np, axis=0)
      image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
      # Each box represents a part of the image where a particular object was detected.
      boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
      # Each score represent how level of confidence for each of the objects.
      # Score is shown on the result image, together with the class label.
      scores = detection_graph.get_tensor_by_name('detection_scores:0')
      classes = detection_graph.get_tensor_by_name('detection_classes:0')
      num_detections = detection_graph.get_tensor_by_name('num_detections:0')
      # Actual detection.
      (boxes, scores, classes, num_detections) = sess.run(
          [boxes, scores, classes, num_detections],
          feed_dict={image_tensor: image_np_expanded})
      # Visualization of the results of a detection.
      vis_util.visualize_boxes_and_labels_on_image_array(
          image_np,
          np.squeeze(boxes),
          np.squeeze(classes).astype(np.int32),
          np.squeeze(scores),
          category_index,
          use_normalized_coordinates=True,
          line_thickness=8)
      plt.figure(figsize=IMAGE_SIZE)
      plt.imshow(image_np)


Convolutional Neural Network for CIFAR-10


CIFAR-10은 RGB 32x32짜리 이미지이다.
이미지 카테고리는 아래와 같다. 10개여서 CIFAR-10인것이다.

airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck.

공식 사이트는 다음과 같다.
CIFAR-10 dataset

큰 이미지로 바로 테스트 하기 어렵기 때문에 일단 작은 이미지로 modeling을하고 테스팅을 해본다.

Higlights of the Tutorial

핵심 기술은 모두 AlexNet paper에 나온 것들이다.

  • convolution
  • rectified linear activations
  • max pooling
  • local response normalization

Model Architecture

기본적으로 Alex Krizhevsky의 모델을 따르며 위에 계층만 약간 튜닝한 형태를 가진다.

몇시간 동안 트레이닝을 했을 때의 최대 정확도는 86%이다.

코드의 구성

  • cifar10_input.py Reads the native CIFAR-10 binary file format.
  • cifar10.py Builds the CIFAR-10 model.
  • cifar10_train.py Trains a CIFAR-10 model on a CPU or GPU.
  • cifar10_multi_gpu_train.py Trains a CIFAR-10 model on multiple GPUs.
  • cifar10_eval.py Evaluates the predictive performance of a CIFAR-10 model.

Model Training

Prediction을 위해서 n-way classification을 수행 한다.
multinomial logistic regression 또는 softmax regression으로 알려져 있다.

  • batch size는 128로 수행
  • learning rate $10^5$

Launching and Training the Model

아래의 명령어로 실행 한다.

python3 cifar10_train.py

맨 처음에는 이미지 데이터가 없기 때문에 다운로드 부터 시작한다. 데이터 셋은 약 160MB이다.

모델링은 약 몇시간 걸린다.

레퍼런스 성능은 아래와 같다.

System        | Step Time (sec/batch)  |     Accuracy
------------------------------------------------------------------
1 Tesla K20m  | 0.35-0.60              | ~86% at 60K steps  (5 hours)
1 Tesla K40m  | 0.25-0.35              | ~86% at 100K steps (4 hours)

default가 1000k steps니 왠만하면 값을 주고 training하는것이 좋다.
GTX 1080 기준으로 1 step당 0.05인것 같다.

2017-03-03 17:26:35.188576: step 353720, loss = 0.16 (2436.1 examples/sec; 0.053 sec/batch)
2017-03-03 17:26:35.725523: step 353730, loss = 0.14 (2291.4 examples/sec; 0.056 sec/batch)
2017-03-03 17:26:36.253775: step 353740, loss = 0.18 (2391.7 examples/sec; 0.054 sec/batch)
2017-03-03 17:26:36.781440: step 353750, loss = 0.15 (2413.0 examples/sec; 0.053 sec/batch)
2017-03-03 17:26:37.313428: step 353760, loss = 0.15 (2395.6 examples/sec; 0.053 sec/batch)

결국 128 batch size로 1 step당 0.05초 정도 소요된다.

Evaluating a Model

Testing을 위해서 10,000장의 의미지를 사용 한다.

python cifar10_eval.py

레퍼런스 기준으로 precision 1 = 0.860이라고 한다.

jemin@jemin-desktop:~/tf_examples/models/tutorials/image/cifar10$ python3 cifar10_eval.py
2017-05-18 14:15:00.822222: I tensorflow/core/common_runtime/gpu/gpu_device.cc:887] Found device 0 with pro                           perties:
name: GeForce GTX 1080
major: 6 minor: 1 memoryClockRate (GHz) 1.797
pciBusID 0000:01:00.0
Total memory: 7.92GiB
Free memory: 7.66GiB
2017-05-18 14:15:00.822247: I tensorflow/core/common_runtime/gpu/gpu_device.cc:908] DMA: 0
2017-05-18 14:15:00.822253: I tensorflow/core/common_runtime/gpu/gpu_device.cc:918] 0:   Y
2017-05-18 14:15:00.822258: I tensorflow/core/common_runtime/gpu/gpu_device.cc:977] Creating TensorFlow dev                           ice (/gpu:0) -> (device: 0, name: GeForce GTX 1080, pci bus id: 0000:01:00.0)
2017-05-18 14:15:02.951141: precision @ 1 = 0.866

참고 문헌

Tensorflow tutorial, convolutional neural networks
CNN
Convolutional Neural Networks


Hands on TensorBoard TensorFlow Dev Summit-2017


그냥 TensorFlow를 이용해서 Graph를 생성하게 되면 복잡한 모양을 나타낸다.

아래 예제에서 사용할 전체 코드는 구글 개발자가 작성한 코드를 동작하도록 약간 수정한 버전이 저의 아래 github에 있습니다.
https://github.com/leejaymin/TensorFlowLecture/blob/master/7.TensorBoard/mnist.py

아래와 같이 사용할경우 scoping이 없어서 복잡한 tensorBoard를 생성한다.

def conv_layer(input, size_in, size_out, name="conv"):
    w = tf.Variable(tf.truncated_normal([5, 5, size_in, size_out], stddev=0.1))
    b = tf.Variable(tf.constant(0.1, shape=[size_out]))
    conv = tf.nn.conv2d(input, w, strides=[1, 1, 1, 1], padding="SAME")
    act = tf.nn.relu(conv + b)
    tf.summary.histogram("weights", w)
    tf.summary.histogram("biases", b)
    tf.summary.histogram("activations", act)
    return tf.nn.max_pool(act, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")

Cleaning the Graph

  • Node Names
  • Name Scopes

Scope과 name을 설정한 코드는 아래와 같다.

def conv_layer(input, size_in, size_out, name="conv"):
  with tf.name_scope(name):
    w = tf.Variable(tf.truncated_normal([5, 5, size_in, size_out], stddev=0.1), name="W")
    b = tf.Variable(tf.constant(0.1, shape=[size_out]), name="B")
    conv = tf.nn.conv2d(input, w, strides=[1, 1, 1, 1], padding="SAME")
    act = tf.nn.relu(conv + b)
    tf.summary.histogram("weights", w)
    tf.summary.histogram("biases", b)
    tf.summary.histogram("activations", act)
    return tf.nn.max_pool(act, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")

Variable과 Placeholder에도 Name을 넣어 줘야 한다.

x = tf.placeholder(tf.float32, shape=[None, 784], name="x")
x_image = tf.reshape(x, [-1, 28, 28, 1])
tf.summary.image('input', x_image, 3)
y = tf.placeholder(tf.float32, shape=[None, 10], name="labels")

각각의 연산 step에도 scoping을 작성해 주어야 한다.

  with tf.name_scope("train"):
    train_step = tf.train.AdamOptimizer(learning_rate).minimize(xent)

  with tf.name_scope("accuracy"):
    correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    tf.summary.scalar("accuracy", accuracy)

그다음 TensorBoard를 생성하면 좀 더 깔끔하게 그룹핑 되어서 그려지는것을 볼 수 있다.

Hyperparameter Search

  • What about different learning rates ?
  • What about different model architectures ?

TensorBoard를 통해서 각각의 모델을 비교할 수 있다.
서로 다른 hyperparameter를 assign하고 이것을 비교해서 나타낼 수 있다.

아래와 같이 조건을 주고 여러번 실행 하면 된다.

def main():
  # You can try adding some more learning rates
  for learning_rate in [1E-4]:

    # Include "False" as a value to try different model architectures
    for use_two_fc in [True]:
      for use_two_conv in [True]:
        # Construct a hyperparameter string for each one (example: "lr_1E-3,fc=2,conv=2)
        hparam = make_hparam_string(learning_rate, use_two_fc, use_two_conv)
        print('Starting run for %s' % hparam)

	    # Actually run with the new settings
        mnist_model(learning_rate, use_two_fc, use_two_conv, hparam)

Embedding Visualizer

high dimensional data를 3D-mentional data로 projection 시키는 것을 말한다.

Embedding과 관련된 코드는 아래와 같다.

  embedding = tf.Variable(tf.zeros([1024, embedding_size]), name="test_embedding")
  assignment = embedding.assign(embedding_input)

  config = tf.contrib.tensorboard.plugins.projector.ProjectorConfig()
  embedding_config = config.embeddings.add()
  embedding_config.tensor_name = embedding.name
  embedding_config.sprite.image_path = LOGDIR + 'sprite_1024.png'
  embedding_config.metadata_path = LOGDIR + 'labels_1024.tsv'

  # Specify the width and height of a single thumbnail.
  embedding_config.sprite.single_image_dim.extend([28, 28])
  tf.contrib.tensorboard.plugins.projector.visualize_embeddings(writer, config)

각각을 file writer를 이용해서 기록하게 된다.

    if i % 500 == 0:
      sess.run(assignment, feed_dict={x: mnist.test.images[:1024], y: mnist.test.labels[:1024]})
      saver.save(sess, os.path.join(LOGDIR, "model.ckpt"), i)

500번 실행 될 때 마다 saver를 이용해서 기록을 하게 된다.
model checkpoint는 모든 variable들과 임베딩 variable들을 포함하는 정보들을 가지고 있으며 이것을 아래의 경로에 저장하게 된다.
tensorbard --logdir /tmp/mnist_tutorial

아래 그림은 임베딩 후에 PCA를 수행한 것이다.

컬러를 넣어보면 아래와 같다. PCA결과 749이 유사한 것을 알 수 있다.
Top 3 컴포넌트만 표현한 것이다.

T-SNE를 이용해서 분석하면 local similarity를 판단할 수 있다.

이러한 유사도가 높은 이미지들은 생성한 classifier가 잘못된 분류를 할 확률도 높아지게 된다.
Embedding visualization은 image에서도 유용하지만 vocabulary에 대해서도 유용하다.
아래는 smart-reply에 대한 embedding 결과이다.

Future for TensorBoard

  • TensorFlow Debugger Integration
  • Plugins
  • domain specific visualization
  • Org-scale TensorBoard

참고자료


Transitioning to TensorFlow 1.0


TensorFlow 1.0 부터는 모든 backwards compatible을 지원하지 않는다.
깔끔한 1.N lifesycle을 위해서 이러한 작업을 수행 한다.

upgrade

$ sudo pip3 install --upgrade TF_BINARY_URL # Python 3.N

https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0-cp35-cp35m-linux_x86_64.whl

Collecting tensorflow-gpu==1.0.0 from https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0-cp35-cp35m-linux_x86_64.whl
  Downloading https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0-cp35-cp35m-linux_x86_64.whl (95.0MB)
    100% |████████████████████████████████| 95.0MB 25kB/s
Requirement already up-to-date: six>=1.10.0 in ./.local/lib/python3.5/site-packages (from tensorflow-gpu==1.0.0)
Requirement already up-to-date: numpy>=1.11.0 in ./.local/lib/python3.5/site-packages (from tensorflow-gpu==1.0.0)
Requirement already up-to-date: wheel>=0.26 in ./.local/lib/python3.5/site-packages (from tensorflow-gpu==1.0.0)
Collecting protobuf>=3.1.0 (from tensorflow-gpu==1.0.0)
  Downloading protobuf-3.2.0-cp35-cp35m-manylinux1_x86_64.whl (5.6MB)
    100% |████████████████████████████████| 5.6MB 392kB/s
Collecting setuptools (from protobuf>=3.1.0->tensorflow-gpu==1.0.0)
  Downloading setuptools-34.3.0-py2.py3-none-any.whl (389kB)
    100% |████████████████████████████████| 399kB 2.4MB/s
Collecting appdirs>=1.4.0 (from setuptools->protobuf>=3.1.0->tensorflow-gpu==1.0.0)
  Downloading appdirs-1.4.2-py2.py3-none-any.whl
Collecting packaging>=16.8 (from setuptools->protobuf>=3.1.0->tensorflow-gpu==1.0.0)
  Downloading packaging-16.8-py2.py3-none-any.whl
Requirement already up-to-date: pyparsing in ./.local/lib/python3.5/site-packages (from packaging>=16.8->setuptools->protobuf>=3.1.0->tensorflow-gpu==1.0.0)
Installing collected packages: appdirs, packaging, setuptools, protobuf, tensorflow-gpu
  Found existing installation: setuptools 32.3.1
    Uninstalling setuptools-32.3.1:
      Successfully uninstalled setuptools-32.3.1
  Found existing installation: protobuf 3.1.0.post1
    Uninstalling protobuf-3.1.0.post1:
      Successfully uninstalled protobuf-3.1.0.post1
  Found existing installation: tensorflow-gpu 0.12.1
    Uninstalling tensorflow-gpu-0.12.1:
      Successfully uninstalled tensorflow-gpu-0.12.1
Successfully installed appdirs-1.4.2 packaging-16.8 protobuf-3.2.0 setuptools-34.3.0 tensorflow-gpu-1.0.0

버전 확인

jemin@jemin-desktop:~$ python3
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:135] successfully opened CUDA library libcublas.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcudnn.so.5 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcufft.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcurand.so.8.0 locally
>>> tf.__version__
'1.0.0'

Code-Upgrade

아래와 같이 Google에서 제공하는 python코드를 이용해서 업그레이드 할 수 있다.

다운로드: link

To convert a single 0,n TensorFlow source file to 1.0, enter a command of the following format:

python tf_upgrade.py --infile InputFile --outfile OutputFile

출력 결과로 report.txt도 나오게 된다. 이것은 상세한 결과와 추가적으로 수작업 해야할 부분을 알려 준다.

디렉터리에 있는 모든것을 업데이트 하기위해서는 아래의 명령어를 사용 한다.

python tf_upgrade.py --intree InputDir --outtree OutputDir

실행하면 아래와 같이 정상적으로 잘 동작하는 것을 알 수 있다.

jemin@jemin-desktop:~/ESLAB_ML/jemin$ python ~/tensorflow_source/tensorflow/tensorflow/tools/compatibility/tf_upgrade.py --intree ./TensorFlowLecture --outtree ./TensorFlowLecture_1.0
TensorFlow 1.0 Upgrade Script
-----------------------------
Converted 33 files

Detected 0 errors that require attention
--------------------------------------------------------------------------------

Make sure to read the detailed log 'report.txt'


Tensor Board


Serializing the data

summary data는 Tensor Flow실행 중에 생성된 데이터를 담고 있다.

아래는 일반적인 Tensor Board의 lifecycle을 담고 있다.

  1. TensorFlow Graph를 생성 한다. 그것은 어떻게 summary 데이터를 수집할지에 대한 정보를 담고 있다.

예를 들어서 scalar_summary를 이용해서 loss function의 진행상황을 기록할 수 있다.
추가로 gradient weight를 하고 싶다면histogram_summary를 사용 한다.

  1. summary들을 초기화 해주어야 한다.
    그것을 일일이 하나하나 하는것은 tedious한 방법이니tf.summary.merge_all을 실행 시키면single operation으로 모든 summary data를 초기화 하게 된다.

  2. merged summary op를 실행 한다.
    그리고 probuf 데이터는 결국 tf.train.SummaryWriter에 의해서 object로 기록된다.

사용방법 포인트

  • tf.global_variables_initializer().run() 전에merged = tf.summary.merge_all()을 해도 된다.tf.summary.FileWriter도 실행 해도 된다.

0.012 패치에 따른 API deplicated

init = tf.initialize_all_variables()

  • tf.global_variables_initializer().run()

tf.histogram_summary

  • tf.summary.histogram
  • tf.summary.scalar

merged = tf.merge_all_summaries()

  • merged = tf.summary.merge_all()

writer = tf.train.SummaryWriter("./logs/xor_logs", sess.graph_def)

  • train_writer = tf.summary.FileWriter(FLAGS.log_dir + '/train', sess.graph)

summary = sess.run(merged, feed_dict={X:x_data, Y:y_data})
좋은 코드 패턴 google

def feed_dict(train):
"""Make a TensorFlow feed_dict: maps data onto Tensor placeholders."""
if train or FLAGS.fake_data:
  xs, ys = mnist.train.next_batch(100, fake_data=FLAGS.fake_data)
  k = FLAGS.dropout
else:
  xs, ys = mnist.test.images, mnist.test.labels
  k = 1.0
return {x: xs, y_: ys, keep_prob: k}

summary, _ = sess.run([merged, train_step],
                      feed_dict=feed_dict(True),
                      options=run_options,
                      run_metadata=run_metadata)

Troubleshooting

kenrel panic

tensorboard를 쓰기위해서 잘못된 naming을 사용하면 발생

InvalidArguementError

Jupyter Notebook의 오류인것 같다.

관련사이트: tensorflow/tensorflow#225

그냥 python3 name.py 로 실행하거나 kernel을 껏다가 다시키면 1번은 동작한다. 똑같은 코드라도 어딘 되고 안되는게 tensorboard가 아무래도 불안정 한것 같다.

5 steps of using tensorboard

From TF graph, decide which node you want to annotate

  • with tf.name_scope("test") as scope:
  • tf.histogram_summary("weights", W), tf.scalar_summary(“accuracy", accuracy)

모두다 병합해서 하나로 보여준다.
Merge all summaries

  • merged = tf.merge_all_summaries()

Create writer

  • writer = tf.train.SummaryWriter("/tmp/mnist_logs", sess.graph_def)

실행 시킨 결과를 보여준다.
Run summary merge and add_summary

  • summary = sess.run(merged, …); writer.add_summary(summary);

Launch Tensorboard

  • tensorboard --logdir=/tmp/mnist_logs

Name variables

X = tf.placeholder(tf.float32, name='X-input')
Y = tf.placeholder(tf.float32, name='Y-input')

W1 = tf.Variable(tf.random_uniform([2, 2], -1.0, 1.0), name='Weight1')
W2 = tf.Variable(tf.random_uniform([2, 1], -1.0, 1.0), name='Weight2')

b1 = tf.Variable(tf.zeros([2]), name="Bias1")
b2 = tf.Variable(tf.zeros([1]), name="Bias2")

Add scope for between graph hierarchy

# Our hypothesis
with tf.name_scope("layer2") as scope:
    L2 = tf.sigmoid(tf.matmul(X, W1) + b1)

with tf.name_scope("layer3") as scope:
    hypothesis = tf.sigmoid(tf.matmul(L2, W2) + b2)

# Cost function
with tf.name_scope("cost") as scope:
    cost = -tf.reduce_mean(Y*tf.log(hypothesis) + (1-Y)*tf.log(1-hypothesis))
    cost_summ = tf.scalar_summary("cost", cost)

# Minimize
with tf.name_scope("train") as scope:
    a = tf.Variable(0.1) # Learning rate, alpha
    optimizer = tf.train.GradientDescentOptimizer(a)
    train = optimizer.minimize(cost)

Add histogram

# Add histogram
w1_hist = tf.histogram_summary("weights1", W1)
w2_hist = tf.histogram_summary("weights2", W2)

b1_hist = tf.histogram_summary("biases1", b1)
b2_hist = tf.histogram_summary("biases2", b2)

y_hist = tf.histogram_summary("y", Y)

Add scalar variables

# Cost function
with tf.name_scope("cost") as scope:
    cost = -tf.reduce_mean(Y*tf.log(hypothesis) + (1-Y)*tf.log(1-hypothesis))
    cost_summ = tf.scalar_summary("cost", cost)

merge summaries and create writer after creating session

# Launch the graph,
with tf.Session() as sess:
    # tensorboard --logdir=./logs/xor_logs
    merged = tf.merge_all_summaries()
    writer = tf.train.SummaryWriter("./logs/xor_logs", sess.graph_def)

매번 기록하면 너무 log데이터가 많으니 아래처럼 기술해 준다.

    # Fit the line.
    for step in xrange(20000):
        sess.run(train, feed_dict={X:x_data, Y:y_data})
        if step % 200 == 0:
            summary = sess.run(merged, feed_dict={X:x_data, Y:y_data})
            writer.add_summary(summary, step)
            print step, sess.run(cost, feed_dict={X:x_data, Y:y_data}), sess.run(W1), sess.run(W2)

실행 예제

아래의 실행 예제는 이전에 작성한 NN with XOR를 가지고 만든것이다.

전체코드

import tensorflow as tf
import numpy as np

xy = np.loadtxt('XORtrain.txt', unpack=True)


x_data = np.transpose(xy[0:-1])
y_data = np.reshape(xy[-1], (4, 1))

X = tf.placeholder(tf.float32, name='X-input')
Y = tf.placeholder(tf.float32, name='Y-input')

W1 = tf.Variable(tf.random_uniform([2, 2], -1.0, 1.0), name='Weight1')
W2 = tf.Variable(tf.random_uniform([2, 1], -1.0, 1.0), name='Weight2')

b1 = tf.Variable(tf.zeros([2]), name="Bias1")
b2 = tf.Variable(tf.zeros([1]), name="Bias2")


# Our hypothesis
with tf.name_scope("layer2") as scope:
    L2 = tf.sigmoid(tf.matmul(X, W1) + b1)

with tf.name_scope("layer3") as scope:
    hypothesis = tf.sigmoid(tf.matmul(L2, W2) + b2)

# Cost function
with tf.name_scope("cost") as scope:
    cost = -tf.reduce_mean(Y*tf.log(hypothesis) + (1-Y)*tf.log(1-hypothesis))
    cost_summ = tf.scalar_summary("cost", cost)

# Minimize
with tf.name_scope("train") as scope:
    a = tf.Variable(0.1) # Learning rate, alpha
    optimizer = tf.train.GradientDescentOptimizer(a)
    train = optimizer.minimize(cost)

# Add histogram
w1_hist = tf.histogram_summary("weights1", W1)
w2_hist = tf.histogram_summary("weights2", W2)

b1_hist = tf.histogram_summary("biases1", b1)
b2_hist = tf.histogram_summary("biases2", b2)

y_hist = tf.histogram_summary("y", Y)


# Before starting, initialize the variables. We will `run` this first.
init = tf.initialize_all_variables()


# Launch the graph,
with tf.Session() as sess:
    # tensorboard --logdir=./logs/xor_logs
    merged = tf.merge_all_summaries()
    writer = tf.train.SummaryWriter("./logs/xor_logs", sess.graph_def)

    sess.run(init)
    # Fit the line.
    for step in xrange(20000):
        sess.run(train, feed_dict={X:x_data, Y:y_data})
        if step % 200 == 0:
            summary = sess.run(merged, feed_dict={X:x_data, Y:y_data})
            writer.add_summary(summary, step)
            print step, sess.run(cost, feed_dict={X:x_data, Y:y_data}), sess.run(W1), sess.run(W2)

    # Test model
    correct_prediction = tf.equal(tf.floor(hypothesis+0.5), Y)
    # Calculate accuracy
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    print sess.run([hypothesis, tf.floor(hypothesis+0.5), correct_prediction, accuracy], feed_dict={X:x_data, Y:y_data})
    print "Accuracy:", accuracy.eval({X:x_data, Y:y_data})

실행방법

  1. chrome 브라우저를 사용한다.
  2. 전체 경로를 설정해야한다.

아래와 같이 log file이 저장된 full path를 입력해주어야 한다.

tensorboard --logdir=/root/PycharmProjects/TensorFlowTest/logs/xor_logs/

정상적으로 실행이 됬다면 아래와 같은 메시지가 나온다.

Starting TensorBoard on port 6006
(You can navigate to http://localhost:6006)

그리고 웹브라우저 크롬을 이용해서 접속하면 아래와 같은 화면을 볼 수 있다.

위에 보면 메뉴바에
EVENTSIMAGESGRAPHHISTOGRAMS이렇게 4개의 메뉴가 있는것을 알 수 있다.
각각 우리가 설정한대로 데이터를 보여주게된다.

cost 값이 interation에 따라서 감소하는 것을 시각화 해서 알 수 있다.

생성한 Neural networks를 graph로 확인 할 수 있다.

참고자료


Early Stopping 및 Index Shuffling


그런데 실제로 Deep Learning을 학습할 때에는 우리가 정한 횟수만큼의 iteration을 무조건 반복하는 것이 아니라, 적당히 학습이 완료되었다고 생각되면 학습을 중단하는 Early Stopping을 해야한다. 이것을 하지 않고 무조건 정해진 iteration을 하게되면, 모델이 주어진 데이터에만 과도하게 학습하여, 보지않은 데이터에 대한 일반화 성능이 떨어지는 overfitting이 일어나게 된다. 따라서 Early Stopping을 통해 이러한 일이 일어나기 전에 학습을 중단해야 한다.

또한 위의 MNIST 데이터는 이미 구현된 함수를 통해 미리 순서가 뒤섞이고, one-hot coding이 된 데이터를 필요한 개수만큼 가져올 수 있었다. 그러나 실제로 자신의 데이터를 학습시키기 위해서는 이러한 과정도 파이썬의 numpy를 이용해 구현해 주어야한다.

그래서 이번 예제에서는 아주 간단하면서 유명한 Iris 데이터를 이용해 위의 구현을 실습해보도록 한다.

Iris data : 50개*3종의 iris 꽃에서 4종류의 feature를 추출한 데이터

https://en.wikipedia.org/wiki/Iris_flower_data_set

%matplotlib inline
import tensorflow as tf
from tensorflow.contrib.learn.python.learn.datasets.base import load_iris
import numpy as np
tf.reset_default_graph()

def MLP_iris():
    # load the iris data.
    iris = load_iris()

    np.random.seed(0)
    random_index = np.random.permutation(150)

    iris_data = iris.data[random_index]
    iris_target = iris.target[random_index]
    iris_target_onehot = np.zeros((150, 3))
    iris_target_onehot[np.arange(150), iris_target] = 1

    accuracy_list = []
    
    # build computation graph
    x = tf.placeholder("float", shape=[None, 4], name = 'x')
    y_target = tf.placeholder("float", shape=[None, 3], name = 'y_target')

    W1 = tf.Variable(tf.zeros([4, 128]), name = 'W1')
    b1 = tf.Variable(tf.zeros([128]), name = 'b1')
    h1 = tf.sigmoid(tf.matmul(x, W1) + b1, name = 'h1')

    W2 = tf.Variable(tf.zeros([128, 3]), name = 'W2')
    b2 = tf.Variable(tf.zeros([3]), name = 'b2')
    y = tf.nn.softmax(tf.matmul(h1, W2) + b2, name = 'y')

    cross_entropy = -tf.reduce_sum(y_target*tf.log(y), name = 'cross_entropy')

    train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_target, 1))

    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) 

    sess = tf.Session(config=tf.ConfigProto(gpu_options=tf.GPUOptions(allow_growth=True)))
    sess.run(tf.initialize_all_variables())

    for i in range(500):
        sess.run(train_step, feed_dict={x: iris_data[0:100], y_target: iris_target_onehot[0:100]})

        train_accuracy = sess.run(accuracy, feed_dict={x: iris_data[0:100], y_target: iris_target_onehot[0:100]})
        validation_accuracy = sess.run(accuracy, feed_dict={x: iris_data[100:], y_target: iris_target_onehot[100:]})
        print ("step %d, training accuracy: %.3f / validation accuracy: %.3f" %(i, train_accuracy, validation_accuracy))

        accuracy_list.append(validation_accuracy)
        
        if i >= 50:
            if validation_accuracy - np.mean(accuracy_list[int(round(len(accuracy_list)/2)):]) <= 0.01 :
                break
            
    sess.close()

MLP_iris()

핵심 코드는 아래와 같다.
1,2 번 째의 validation accuracy 값은 제거하고 너무 큰 값이기 때문이
그 이후부터의 값을 누적 시켜서 평균을 계산하게 된다.
그 다음 해당 값이 현재의 validation accuracy와의 차이를 구해서 그 차이가 0.01보다 큰지 작은지를 반단해서
break 여부를 결정하게 된다.

if i >= 50:
	if validation_accuracy - np.mean(accuracy_list[int(round(len(accuracy_list)/2)):]) <= 0.01 :
	    break

실행 결과를 보면 어느정도 동작하다가 멈추는 것을알 수 있다.

step 166, training accuracy: 0.970 / validation accuracy: 0.960
step 167, training accuracy: 0.970 / validation accuracy: 0.960
step 168, training accuracy: 0.970 / validation accuracy: 0.960
step 169, training accuracy: 0.970 / validation accuracy: 0.960
step 170, training accuracy: 0.970 / validation accuracy: 0.960
step 171, training accuracy: 0.970 / validation accuracy: 0.960


Convolutional Neural Network (CNN) 구현


Introduction

고양이 실험에서 시작 되었다.
고양이에게 어떤 그림을 보여줬더니, 동시에 뉴런이 모두 시작되지 않았다.
각각의 부분에 대해서 다르게 동작 하였다.

이러한 개념을 이용해서 필터를 생성하여 각각의 부분을 추출하는 방식을 이용 한다.

CNN을 Image가 아닌 input data에 대해서 적용 할 때는 CNN이 왜 잘 동작하는지에 대한 직관적인 이해가 필요하다.
만약 적용하려는 데이터가 여전히 이것을 만족한다면 구지 Image가 아니어도 CNN은 우수한 성능을 보장한다.

convnet 적용하기 위한 데이터 특징

CNN의 핵심은 Weight Sharing이라고 한다. 왜냐하면 보통 Stride=1을 주기 때문이 다수의 필터들이 한칸씩만 옆으로 이동하면서 적용 되기 때문에 많은 weight들이 서로 공유하고 있는 형태가 된다. 따라서 사진 처럼 인접한 픽셀들이 서로 관련성이 매우 높아야 한다.

즉, convnet은 지역적으로 weight값들이 공유 된다는 가정하게 수행 되게 된다.
만약 이미지가 아니라면 최소한 근처 데이터 끼리 상관 관계가 높아야 한다.
예를 들면, Audio데이터 같은 time series라고 하면 이전의 사건이 이후의 사건과 관계가 높으므로 convnet을 적용하기 좋다.

만약 data.frame데이터의 각 컬럼이 서로 연관성이 없고 scaling이 심지어 서로 다르다면 convnet을 적용할 의미는 없다.

Implementation

TensorFlow를 이용해서 간단한 CNN을 구현해 보자.
이전에 MLP에서 사용한 MNIST set을 그대로 사용한다.

구현하고자 하는 모델을 표현하면 아래와 같다.

conv를 적용하기 위해선 아래의 함수를 이용한다.

tf.nn.conv2d(X, w, strides=[1, 1, 1, 1], padding='SAME')

위와 같을 때 SAME의 의미는 원래 Image와 같은 크기의 Activiation Map을 만들 겠다는 의미이다.

파라메터는 순서대로 아래와 같다.

  • input: [batch, in_height, in_width, in_channels] 형식. 28x28x1 형식의 손글씨 이미지.
  • filter: [filter_height, filter_width, in_channels, out_channels] 형식. 3, 3, 1, 32의 w.
  • strides: 크기 4인 1차원 리스트. [0], [3]은 반드시 1. 일반적으로 [1], [2]는 같은 값 사용.
  • padding: 'SAME' 또는 'VALID'. 패딩을 추가하는 공식의 차이. SAME은 출력 크기를 입력과 같게 유지.

28x28x1의 이미지 이다.
필터는 32 filters로 (3x3x1)이다. 필터의 사이즈는 조절이 가능하다.

필터를 만든다는 것은 weight을 생성 한다는 것이다.
초기가값은 정규분포에 따르는 랜덤 값을 주게되고
학습이 되어야 하는 필터이므로 Variable로 선언한다.

w=tf.Variable(tf.random_normal([3,3,1,32], stddev=0.01))

이제 각각의 필터들을 이동하면서 적용을 해주어야 하는데 이게 귀찮기 때문에 TensorFlow에서는 이것을 지원 한다.

tf.nn.cov2d(X,W)함수로 이것을 지원 한다.
X는 이미지가 되고 w는 필터가 된다.
strides=[1,1,1,1], padding='SAME으로 합치게 된다.
strides = [1, stride, stride, 1]

아래와 같이 결국 activation map은 32개 층을 이루며 size는 same 이기 때문에 28x28의 크기를 가지게 된다.

그리고 Convolution과 Relu를 같이 주고 싶다면 아래 코드로 간단하게 적용이 가능하다.

l1a = tf.nn.relu(tf.nn.conv2d(X, w, strides=[1, 1, 1, 1], padding='SAME'))

풀링도 아래와 같이 간단히 구현이 가능하다.
c1은 컨볼루셔널 결과를 받는 것이다.

결과는 2by2로 2,2를 하기 때문에 반 줄어든
14,14,32가 된다.
SAME 이라도 stride 2라서 14기 때문이다.

print l1a를 하면 shape을 얻을 수 있다. 오류를 막기 위해서 직접 출력해 보는 것도 좋은 방법이다.

Tensor reshape

tf.reshape(tensor, shape, name=None)

위 함수를 이용해서 주어진 tensor를 다른 모양으로 변경해서 반환 하게 된다.
-1을 쓰게 되면 평평한 1-D 모양을 생성 한다.
최종적인 the number of elements는 결국 같아야 한다.

동작 예제는 아래와 같다.

# tensor 't' is [1, 2, 3, 4, 5, 6, 7, 8, 9]
# tensor 't' has shape [9]
reshape(t, [3, 3]) ==> [[1, 2, 3],
                        [4, 5, 6],
                        [7, 8, 9]]

# tensor 't' is [[[1, 1], [2, 2]],
#                [[3, 3], [4, 4]]]
# tensor 't' has shape [2, 2, 2]
reshape(t, [2, 4]) ==> [[1, 1, 2, 2],
                        [3, 3, 4, 4]]

# tensor 't' is [[[1, 1, 1],
#                 [2, 2, 2]],
#                [[3, 3, 3],
#                 [4, 4, 4]],
#                [[5, 5, 5],
#                 [6, 6, 6]]]
# tensor 't' has shape [3, 2, 3]
# pass '[-1]' to flatten 't'
reshape(t, [-1]) ==> [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6]

# -1 can also be used to infer the shape

# -1 is inferred to be 9:
reshape(t, [2, -1]) ==> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
                         [4, 4, 4, 5, 5, 5, 6, 6, 6]]
# -1 is inferred to be 2:
reshape(t, [-1, 9]) ==> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
                         [4, 4, 4, 5, 5, 5, 6, 6, 6]]
# -1 is inferred to be 3:
reshape(t, [ 2, -1, 3]) ==> [[[1, 1, 1],
                              [2, 2, 2],
                              [3, 3, 3]],
                             [[4, 4, 4],
                              [5, 5, 5],
                              [6, 6, 6]]]

# tensor 't' is [7]
# shape `[]` reshapes to a scalar
reshape(t, []) ==> 7

코드

import tensorflow as tf
import numpy as np
import input_data
import time

batch_size = 128
test_size = 256

def init_weights(shape):
    return tf.Variable(tf.random_normal(shape, stddev=0.01))

# Filter weight vectors: w, w2, w3, w4, w_0
def model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden):
    l1a = tf.nn.relu(tf.nn.conv2d(X, w,                       # l1a shape=(?, 28, 28, 32)
                        strides=[1, 1, 1, 1], padding='SAME'))
    l1 = tf.nn.max_pool(l1a, ksize=[1, 2, 2, 1],              # l1 shape=(?, 14, 14, 32)
                        strides=[1, 2, 2, 1], padding='SAME')
    l1 = tf.nn.dropout(l1, p_keep_conv)

    l2a = tf.nn.relu(tf.nn.conv2d(l1, w2,                     # l2a shape=(?, 14, 14, 64)
                        strides=[1, 1, 1, 1], padding='SAME'))
    l2 = tf.nn.max_pool(l2a, ksize=[1, 2, 2, 1],              # l2 shape=(?, 7, 7, 64)
                        strides=[1, 2, 2, 1], padding='SAME')
    l2 = tf.nn.dropout(l2, p_keep_conv)

    l3a = tf.nn.relu(tf.nn.conv2d(l2, w3,                     # l3a shape=(?, 7, 7, 128)
                        strides=[1, 1, 1, 1], padding='SAME'))
    l3 = tf.nn.max_pool(l3a, ksize=[1, 2, 2, 1],              # l3 shape=(?, 4, 4, 128)
                        strides=[1, 2, 2, 1], padding='SAME')
    l3 = tf.reshape(l3, [-1, w4.get_shape().as_list()[0]])    # reshape to (?, 2048)
    l3 = tf.nn.dropout(l3, p_keep_conv)

    l4 = tf.nn.relu(tf.matmul(l3, w4))
    l4 = tf.nn.dropout(l4, p_keep_hidden)

    pyx = tf.matmul(l4, w_o)
    return pyx

# Read data
mnist = input_data.read_data_sets("MNIST_DATA/", one_hot=True)
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels

# trx.reshape( n-inputs, image size, image size, depth )
 # this variable is input in model()
trX = trX.reshape(-1, 28, 28, 1)  # 28x28x1 input img
teX = teX.reshape(-1, 28, 28, 1)  # 28x28x1 input img

X = tf.placeholder("float", [None, 28, 28, 1])
Y = tf.placeholder("float", [None, 10])

w = init_weights([3, 3, 1, 32])       # 3x3x1 conv, 32 outputs
w2 = init_weights([3, 3, 32, 64])     # 3x3x32 conv, 64 outputs
w3 = init_weights([3, 3, 64, 128])    # 3x3x32 conv, 128 outputs
w4 = init_weights([128 * 4 * 4, 625]) # FC 128 * 4 * 4 inputs, 625 outputs
w_o = init_weights([625, 10])         # FC 625 inputs, 10 outputs (labels)

p_keep_conv = tf.placeholder("float")
p_keep_hidden = tf.placeholder("float")
py_x = model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden)

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(py_x, Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
predict_op = tf.argmax(py_x, 1)

# Launch the graph in a session
with tf.Session() as sess:
    # you need to initialize all variables
    start_time = time.time()
    tf.initialize_all_variables().run()

    for i in range(100):
        training_batch = zip(range(0, len(trX), batch_size),
                             range(batch_size, len(trX)+1, batch_size))
        for start, end in training_batch:
            sess.run(train_op, feed_dict={X: trX[start:end], Y: trY[start:end],
                                          p_keep_conv: 0.8, p_keep_hidden: 0.5})

        test_indices = np.arange(len(teX)) # Get A Test Batch
        np.random.shuffle(test_indices)
        test_indices = test_indices[0:test_size]

        print(i, np.mean(np.argmax(teY[test_indices], axis=1) ==
                         sess.run(predict_op, feed_dict={X: teX[test_indices],
                                                         Y: teY[test_indices],
                                                         p_keep_conv: 1.0,
                                                         p_keep_hidden: 1.0})))

    print("time elapsed: {:.2f}s".format(time.time() - start_time))

출력

/root/tensorflow/bin/python /root/DataScience/TensorFlowLecture/5.CNN/CNNforMNIST.py
Extracting MNIST_DATA/train-images-idx3-ubyte.gz
/usr/lib/python2.7/gzip.py:268: VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
  chunk = self.extrabuf[offset: offset + size]
/root/DataScience/TensorFlowLecture/5.CNN/input_data.py:47: VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
  data = data.reshape(num_images, rows, cols, 1)
Extracting MNIST_DATA/train-labels-idx1-ubyte.gz
Extracting MNIST_DATA/t10k-images-idx3-ubyte.gz
Extracting MNIST_DATA/t10k-labels-idx1-ubyte.gz
(0, 0.95703125)
(1, 0.96875)
(2, 0.98828125)
(3, 0.984375)
(4, 0.984375)
(5, 0.9765625)
(6, 0.99609375)
(7, 0.98046875)
(8, 1.0)
(9, 0.9921875)
(10, 0.99609375)
(11, 0.99609375)

원래 100번 Loop를 수행 해야하나 CPU로는 너무 느려서 그냥 중간에 포기했다.
대충 몇번만 Epoch해도 정확도가 1에 근접하는 놀라운 성능을 보여준다.

작성된 완전한 코드는 아래에 있습니다.
https://github.com/leejaymin/TensorFlowLecture/blob/master/5.CNN/CNNforMNIST.ipynb

참고 사이트

모두를위한 딥러닝
CNN을 Text Classification에 적용하기


'AI > TensorFlow, PyTorch, Keras, Scikit' 카테고리의 다른 글

Tensor Board  (0) 2017.02.08
Early Stopping 및 Index Shuffling  (0) 2017.01.03
Convolutional Neural Network (CNN) 이론  (1) 2016.12.31
AWS의 GPU를 이용한 TensorFlow  (1) 2016.12.31
rpy2 Windows 10에 설치 하기  (0) 2016.12.13

Convolutional Neural Network (CNN) 이론


Convolutional Neural Network (CNN)은 결국 CONVReLUPOOLDropu-out을 번갈아서 적용하는 것을 말한다.
마지막에는 Fully connected network으로 구성 된다.
Pooling은 sampling과 resizing을 의미한다.

Start with an image (width x hight x depth)으로 구성된다.
결국, 32 x 32 x 3 image 이다.
3이라고 하면 red, green, blue를 나타낸다.
gray scale일 경우 1의 depth를 나타낸다.

결국은 위와 같이 filter를 상징하는 weight를 찾는 것이 Convolutional Neural Network(CNN)의 목적이라고 할 수 있다.

필터를 이미지에 적용 (conv + ReLU)

필터를 이미지에 적용하고 Stride에 따라서 나오는 이미지의 크기가 달라지게 된다.

필터의 사이즈가 너무 작으면 Weight를 구하는것이 의미가 없어 진다.

아래의 공식대로 필터가 구해진다.
출력 크기 = 1 + (입력 크기 - 필터 크기) / stride 크기

핵심은 구하려고하는 값이 2.33과 같이 나머지가 존재해서는 안된다.
정수로 딱 떨어져야 이 방법이 적용 가능하다.


실전에서는 위와 같이 Filter를 적용할 때 마다 원본 image의 크기가 작아지는 문제가 발생 한다.
stride크기에 상관 없이 최소한 (필터 크기 -1)만큼 줄어들 수 밖에 없다.
따라서 padding을 처리하는 것이 중요 하다.

Max Pooling

Pooling의 다른 말은 Sampling 또는 Resizing이다.
Pooling을 거치면 크기가 작아지게 된다.
결국 이전에 filter를 통해서 생성 했던 activation map이 크기는 1/2로 작아지고 channel의 두께는 바뀌지 않게 되는 것이다.

MAX POOLING

Pooling 기법 중에서 가장 많이 사용하는 Max pooling이 있다.
여러 개의 값 중에서 가장 큰 값을 꺼내서 모아 놓은것을 말한다.

이전의 Convolutional Layer에서는 Filter를 곱해서 새로운 출력 결과를 만들어 낸다.
이와 다르게 polling에서는 단순하게 존재하는 값 중에서 하나를 선택하게 된다.
Max pooling은 여기서 가장 큰 값을 선택하는 것을 말한다.

참고문헌

http://pythonkim.tistory.com/52


AWS의 GPU를 이용한 TensorFlow


최근 실행해본 CNN 정도만해도 이제 CPU TensorFlow로는 학습이 느린감이 있다.

이전에 학생 크래딧 받기를 참조해서 아마존으로 부터 $100를 받아서 AWS EC2를 사용해 보자.
이전포스트

계정을 생성하고 EC2를 생성해보자.
아래와 같은 GPU Instance 모델들이 존재 한다.

필자는 아래의 모델중 g2.8xlarge를 선택 했다.

Instance를 생성 할 때 김성훈 교수님께서 이미 EC2를 공유해 주셨기 때문에 해당 Instance를 카피해서 생성한다.
AMI라고해서 Amazon Machine Instance를 검색해서 찾을 수 있다.
아래의 이름으로 검색하면 된다.

  • ami-9e39dcf3 (N. Virginia)
  • ami-38f60658 (oregon)

그리고 생성 하려고하면 아마 limit fail이 발생할 것이다.
고객센터가서 사용 목적을 적어서 이메일 보내면 30분내로 instance 갯수 허용이 늘어나고
생성할 수 있게된다.

아래와 같이 정상적으로 생성 할 수 있다.

인증키로 로그인 하기

putty로 접속을 하려면 key를 생성해야 합니다.
http://www.chiark.greenend.org.uk/~sgtatham/putty
PUtty key generator를 실행해야하므로 모든 관련 파일이 있는 putty.zip을 다운 받아야 합니다.

PUTTYGEN을 실행 합니다.
Conversions-> Import key를 선택합니다.
EC2를 생성할 때 사용 했던 key를 이용해서 접속 key를 생성해야 합니다.

키를 로드하고 save private key 버튼을 눌러서 저장합니다.

EC2 키의 확장자는 pem이고 PUTTYGEN에 의해서 생성한 키의 확장자는 ppk입니다.

Amazon EC2 관리자로가서 Public IP 주소를 확인 합니다.
Putty를 실행해서 IP 주소를 입력하고
오른쪽 창에서 SSH-> Auth로 이동합니다.
Browse버튼을 눌러서 생성한 Key를 선택합니다.
open버튼을 눌러서 접속 합니다.

각 EC2 Instance의 운영체제에 따라서 기본 사용자명이 다릅니다.

  • Amazon Linux: ec2-user
  • Red Hat Enterprise Linux: ec2-user
  • SuSE Linux: root
  • Ubuntu Linux: ubuntu

필자는 Ubuntu이므로 아이디는 ubuntu 이다.

아래와 같이 정상적으로 로그인 되는것을 알 수 있습니다.

MNIST set을 가지고 CNN모델을 트레이닝 하는것을
AWS GPU와 i7-cpu에서 트레이닝 했을 때를 녹화한 것이다.

동영상은 아래의 유투브에서 볼 수 있다.
Youtube: https://youtu.be/-kQ4fm2fqcM

대략 5배~7배 정도 차이인것 같다.
K520 GPU가 4대 있는 EC2인데 생각보다 차이는 크지 않다.

최종 결과는 아래와 같다.

EC2

MNIST로 CNN 모델을 트레이닝 하는데 총 22분 4.040초 걸렸다.

i7 CPU

결론적으로 4.8배 차이이다.


rpy2 Windows 10에 설치 하기

정식으로 rpy2에서 Window를 지원하지 않아서 생각 보다는 까다로운 것 같다.

설치한 환경은 아래와 같다.

  • Window 10 1주년 버전
  • Anaconda 4.x
  • python 3.5.x
  • R.3.3.2

pip instal rpy2를 실행 할경우 여러 오류가 발생 한다.

환경 변수 미 설정에 따른 오류

해당 오류는 init.py에서 R_HOME 환경변수를 참조 하기 때문이다. 기타 R을 쓰기위한 환경 변수를 모두 설정 해줘야 한다.

모두 시스템 변수로 설정 했다.

  • R_HOME: C:\Program Files\R\R-3.3.2
  • R_USER: 사용자 계정을 적는다.
  • Path: 맨뒤에 C:\Program Files\R\R-3.3.2\bin추가한다.

*.sh 스크립트 실행 불가 오류

자동 다운로드가 안되서 직접 .whl을 받아서 설치 한다.
사이트: http://www.lfd.uci.edu/~gohlke/pythonlibs/#rpy2

Python3의 환경 이므로
rpy2‑2.8.4‑cp36‑cp36m‑win_amd64.whl을 다운 받았다.

"not a supported wheel on this platform". 오류

아래 명령어를 실행 하면 위와 같은 오류가 발생한다.
파일 명을 변경해줘야한다.
Python 3.5 부터 파일 이름가지고 구분 하는것 같다.

pip install rpy2‑2.8.4‑cp36‑cp36m‑win_amd64.whl

파일명을 rpy2-2.8.4-py3-none-win_amd64.whl로 변경 한다.
즉 py3-none이 cp36-cp36m을 대체한 것이다.

실행 테스트


Python에서 rpy2를 이용한 ROjbect 불러오기


rpy2 설치하기 (ubuntu)

R 구 버전 삭제
ubuntu 14.04는 기본적으로 3.0.1 버전을 설치한다.
rpy2는 guswo 3.2 버전 이상부터 동작하므로, 기본 보전인 2013년도 R을 설치하면 진행이 안된다.

우선 현재 구 버전 R이 설치되었다면 아래 방법으로 깨긋이 삭제 한다.

R을 실행해서 관련 package들이 어디에 설치 되어 있는지 확인 한다.

.libPaths() 

그리고 아래의 명령어로 삭제한다.

sudo apt-get remove r-base-core
sudo apt-get remove r-base
sudo apt-get autoremove

확인한 package 경로에 들어가서 남아있는 파일들이 없는지 확인 한다.

R 최신 버전 설치
Youtube 동영상: https://www.youtube.com/watch?v=Nxl7HDUyw0I
원본 방법: https://launchpad.net/~marutter/+archive/ubuntu/rrutter

필자는 동영상 처럼 Update방식으로 할 경우 충돌이나서 이전 버전을 완전히 지우고 새로 설치 하는 방법으로 진행 했다.

# Add PPA:
sudo add-apt-repository ppa:marutter/rrutter 

# Update ubuntu for upgrade:
sudo apt-get update

# install
sudo apt-get install r-base

rpy2 설치 하기
간단히 pip 명령어로 설치 할 수 있다.

pip install rpy2

아래와 같은 에러가 발생하면 R version이 구버전 이거나 중요 시스템 package들이 설치 안되서이다.
rpy2 error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

해결 방법은 아래의 package들을 모두 설치하는 것이다.

sudo apt-get install build-essential autoconf libtool pkg-config python-opengl python-imaging python-pyrex python-pyside.qtopengl idle-python2.7 qt4-dev-tools qt4-designer libqtgui4 libqtcore4 libqt4-xml libqt4-test libqt4-script libqt4-network libqt4-dbus python-qt4 python-qt4-gl libgle3 python-dev

통상 python-dev와 build-essential을 설치하지 않아서 발생 한다고 한다.
참고사이트: http://stackoverflow.com/questions/26053982/error-setup-script-exited-with-error-command-x86-64-linux-gnu-gcc-failed-wit

rpy2로 Rdata 불러오기

import rpy2.robjects as robjects
import pandas.rpy.common as com
import pandas as pd

# check the current working directory 
os.getcwd()

## load .RData and converts to pd.DataFrame
robj = robjects.r.load("seenPhoneStatus10FeaDfList.Rdata")

robjects.r('lapply(seenPhoneStatus10FeaDfList,dim)')

실행 결과

R object with classes: ('list',) mapped to:
<ListVector - Python:0x7f7b64129d40 / R:0x2e68950>
[IntVe..., IntVe..., IntVe..., ..., IntVe..., IntVe..., IntVe...]
  X: <class 'rpy2.robjects.vectors.IntVector'>
  R object with classes: ('integer',) mapped to:
<IntVector - Python:0x7f7b65bf67e8 / R:0x3f4ae18>
[    1038,       10]


TensorFlow 기본 개념 (2)


연산은 Graph로 이뤄 진다.
Graph는 Session Context에 들어가서 처리 된다.
Data는 Tensor로 표현된다.
상태 정보는 Variables로 관리 된다.

Session

반드시 그래프의 실행은 Session안에서 이뤄져야 한다.
Session은 Graph ops를 CPUsGPUs와 같은 장치에서 실행 시킨다.
그리고 실행 결과로 ndarray object를 반환 한다.

Simple Example

Building the graph

import tensorflow as tf

# Create a Constant op that produces a 1x2 matrix.  The op is
# added as a node to the default graph.
#
# The value returned by the constructor represents the output
# of the Constant op.
matrix1 = tf.constant([[3., 3.]])

# Create another Constant that produces a 2x1 matrix.
matrix2 = tf.constant([[2.],[2.]])

# Create a Matmul op that takes 'matrix1' and 'matrix2' as inputs.
# The returned value, 'product', represents the result of the matrix
# multiplication.
product = tf.matmul(matrix1, matrix2)

위 코드는 Graph를 생성하는 코드이다.
현재 Default Graph는 3개의 Node로 구성된다.
두 개의 constant() 그리고 하나의 matmul() op 함수이다.

  • Matrix1: 1x2
  • Matrix2: 2x1

하지만, 지금은 정의만 한것이다. 앞선 기본개념에서도 언급 했듯이 TensorFlow는 정의 부분과 실행 부분이 나눠진다.
실제 seesion에서 해당 Graph를 실행해야 비로소 연산이 실행 된다.

Launching the graph in a session

# Launch the default graph.
sess = tf.Session()


# To run the matmul op we call the session 'run()' method, passing 'product'
# which represents the output of the matmul op.  This indicates to the call
# that we want to get the output of the matmul op back.
#
# All inputs needed by the op are run automatically by the session.  They
# typically are run in parallel.
#
# The call 'run(product)' thus causes the execution of three ops in the
# graph: the two constants and matmul.
#
# The output of the op is returned in 'result' as a numpy `ndarray` object.
result = sess.run(product)
print(result)
# ==> [[ 12.]]

# Close the Session when we're done.
sess.close()

위 코드는 Graph를 Launch하는 코드이다. 이를 위해서 Session object를 생성 한다.
어떠한 Argument도 없이 session을 생성하면 default graph로 실행 된다.

최종적으로 matrix연산을 수행한 후에 ndarry object 형태로 실수 12.이 반환 된다.

Session은 반드시 close시켜서 resource해제 시켜줘야 한다.

With block
with를 이용하면 자동으로 with block이 끝나는 지점에서 리소스를 반환하게 된다.

with tf.Session() as sess:
  result = sess.run([product])
  print(result)

TensorFlow의 구현은 Graph정의를 실행가능한 operation으로 변환하는 것을 말한다. 
그리고 이러한 operation은 현재 가능한 컴퓨팅 리소스 (CPU, GPU card)등에 할당 된다.
이 작업은 추상화되어 프로그래머가 명시적으로 해줘야할 것은 없다.

장치가 여러개일 경우

with tf.Session() as sess:
  with tf.device("/gpu:1"):
    matrix1 = tf.constant([[3., 3.]])
    matrix2 = tf.constant([[2.],[2.]])
    product = tf.matmul(matrix1, matrix2)
    ...

위와 같이 직접 하드웨어를 선택해서 처리 한다.

"/cpu:0": The CPU of your machine.
"/gpu:0": The GPU of your machine, if you have one.
"/gpu:1": The second GPU of your machine, etc.

Interactive Usage

# Enter an interactive TensorFlow Session.
import tensorflow as tf
sess = tf.InteractiveSession()

x = tf.Variable([1.0, 2.0])
a = tf.constant([3.0, 3.0])

# Initialize 'x' using the run() method of its initializer op.
x.initializer.run()

# Add an op to subtract 'a' from 'x'.  Run it and print the result
sub = tf.sub(x, a)
print(sub.eval())
# ==> [-2. -1.]

# Close the Session when we're done.
sess.close()

Tensor

Tensor는 n-dimensional array 도는 list를 말한다.

Variables

import tensorflow as tf

# Create a Variable, that will be initialized to the scalar value 0.
state = tf.Variable(0, name="counter")

# Create an Op to add one to `state`.

one = tf.constant(1)
new_value = tf.add(state, one)
update = tf.assign(state, new_value)

# Variables must be initialized by running an `init` Op after having
# launched the graph.  We first have to add the `init` Op to the graph.
init_op = tf.initialize_all_variables()

# Launch the graph and run the ops.
with tf.Session() as sess:
  # Run the 'init' op
  sess.run(init_op)
  # Print the initial value of 'state'
  print(sess.run(state))
  # Run the op that updates 'state' and print 'state'.
  for _ in range(3):
    sess.run(update)
    print(sess.run(state))

# output:

# 0
# 1
# 2
# 3

Graph의 실행 상태를 저장하는 것이 Variables의 특징이다.
위 예제에서의 Variables는 간단한 counter의 역할을 하게 된다.

  • assign() operation은 add()와 같은 graph상의 하나의 operation이다. 당연히 session.run()을 하기 전까지는 실행 되지 않는다.
  • Neural network에서 tensor에 weight값으 저장할 때 Variables를 사용하게 된다.

Fetches

input1 = tf.constant([3.0])
input2 = tf.constant([2.0])
input3 = tf.constant([5.0])
intermed = tf.add(input2, input3)
mul = tf.mul(input1, intermed)

with tf.Session() as sess:
  result = sess.run([mul, intermed])
  print(result)

# output:
# [array([ 21.], dtype=float32), array([ 7.], dtype=float32)]

Graph의 실행은 결국 run을 이용해서 수행하게 된다.

Feeds

input1 = tf.placeholder(tf.float32)
input2 = tf.placeholder(tf.float32)
output = tf.mul(input1, input2)

with tf.Session() as sess:
  print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))

# output:
# [array([ 14.], dtype=float32)]

이전 까지 예제들은 Constants와 Variables에 저장된 값을 가지고 graph를 연산하는 것을 배웠다.

동적으로 값을 전달하는 방법을 알아보자.
run()을 할 때 Argument로 전달할 수 있는 매커니즘을 말한다.

placeholder라는 이름으로 선언하면 run()을 할 때 값을 정해주는 기능으로 동작하게 된다. 
주의할 것은 정의를 하지 않으면 error를 발생 시킨다는 점이다. 반드시 초기화를 해줘야 한다.

작성코드

본 내용에 작성된 코드들은 Github를 통해서 공유하겠습니다.
주소: https://github.com/leejaymin/TensorFlowLecture/tree/master/0.Basic


MNIST 데이터 셋을 이용한 손글씨 인식 Deep Nerual Network 구현


Deep Nerual net에 여러 기술을 적용해서 정확도를 점점 향상시켜보는 내용이다.

MNIST Database overview

머신러닝 최고 Guru중 한명인 Yann LeCun 뉴욕대 교수가 제공하는 데이터 셋이다.
교수 겸임과 함께 Facebook과 함께 일하고 있다.

숫자 0~9까지의 손글씨 이미지의 집합이다.

학습데이터 60,000개, 테스트데이터 10,000개로 구성 되어 있다.
이전과 다르게 모든 모델이 검증은 이 테스트 데이터로 수행하게 된다.
재치환 에러가 아니라, unseen data에 대한 평가이기 때문에 진짜 평가라고 할 수 있다.

이 손글씨는 size-normalized 되어 있고 centered 되어 있다.
사이즈는 28x28의 크기를 가진다. 이미지의 값은 0 또는 1이다 (흑,백)

결국 이 데이터 셋은 패턴인식이나 기계학습 기술을 적용하기 위해 사용할 수 있는 최적의 이미지 셋이다. 이미 preprocessing이나 formatting이 모두 완료 되었기 때문이다.

4개의 파일을 아래의 주소에서도 다운받을 수 있고, 아래의 input_data.py를 통해서도 자동으로 다운 받을 수 있다.
a Link of the MNIST Database of handwritten digits, Yann LeCun

train-images-idx3-ubyte.gz:  training set images (9912422 bytes) 
train-labels-idx1-ubyte.gz:  training set labels (28881 bytes) 
t10k-images-idx3-ubyte.gz:   test set images (1648877 bytes) 
t10k-labels-idx1-ubyte.gz:   test set labels (4542 bytes)

편리한 이미지 제어를 위해서 TensorFlow자체에서 제공하는 script인 input_data.py를 사용한다.
다운로드
기능은 크게 두 가지이다.

  • dataset downloading
  • Loading the entire dataset into numpy array

Softmax Logistic regression for MNIST

단일 layer의 logistic regression에다가 softmax를 붙여서 0~9 사이의 숫자로 classifier 해주는 code 이다.
이런 단순한 방법의 경우 정확도가 얼마나 나오는지를 확인해보자.

import tensorflow as tf
import input_data

learning_rate = 0.01
training_epochs = 25
batch_size = 100
display_step = 1

mnist = input_data.read_data_sets("./MNIST_DATA", one_hot=True)

# tensorflow graph input
X = tf.placeholder('float', [None, 784]) # mnist data image of shape 28 * 28 = 784
Y = tf.placeholder('float', [None, 10]) # 0-9 digits recognition = > 10 classes

# set model weights
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

# Our hypothesis
activation = tf.add(tf.matmul(X, W),b)  # Softmax

# Cost function: cross entropy
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(activation, Y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)  # Gradient Descen

# Before starting, initialize the variables. We will `run` this first.
init = tf.initialize_all_variables()

# Launch the graph,
with tf.Session() as sess:
    sess.run(init)

    # Training cycle
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(mnist.train.num_examples/batch_size)

        # Fit the line.
        for step in xrange(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            # Fit training using batch data

            sess.run(optimizer, feed_dict={X: batch_xs, Y: batch_ys})

            # Compute average loss
            avg_cost += sess.run(cost, feed_dict={X: batch_xs, Y: batch_ys})/total_batch
        # Display logs per epoch step
        if epoch % display_step == 0:
            print "Epoch:", '%04d' %(epoch+1), "cost=", "{:.9f}".format(avg_cost)

    print "Optimization Finished!"

    # Test model
    correct_prediction = tf.equal(tf.argmax(activation, 1), tf.argmax(Y, 1))
    # Calculate accuracy
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    print ("Accuracy:", accuracy.eval({X: mnist.test.images, Y: mnist.test.labels}))

동작을 할떄 수행 과정이 너무 많기 때문에 batch단위로 나눠서 보여주게 된다.

총 트레이닝에 사용되는 example은 55,000개이다.
batch size는 100이다. 즉 한번에 550개를 이용해서 training을 수행 하게 된다.

위와 같이 수행하면 아래와 같은 결과를 얻는다.
테스트 데이터 1만개에 대해서 91% 정확도로 숫자를 판별 할 수 있었다. 훌륭한 결과이다.
이제 이것을 계속 개선 시켜 나간다.

Epoch: 0001 cost= 0.358261517
Epoch: 0002 cost= 0.281931180
Epoch: 0003 cost= 0.271845694
Epoch: 0004 cost= 0.265042298
Epoch: 0005 cost= 0.261528213
Epoch: 0006 cost= 0.260637467
Epoch: 0007 cost= 0.259087178
Epoch: 0008 cost= 0.255747356
Epoch: 0009 cost= 0.256775191
Epoch: 0010 cost= 0.254862096
Epoch: 0011 cost= 0.253365451
Epoch: 0012 cost= 0.250304825
Epoch: 0013 cost= 0.250392489
Epoch: 0014 cost= 0.248266828
Epoch: 0015 cost= 0.252892739
Epoch: 0016 cost= 0.245914599
Epoch: 0017 cost= 0.246812203
Epoch: 0018 cost= 0.248447235
Epoch: 0019 cost= 0.245472498
Epoch: 0020 cost= 0.243310648
Epoch: 0021 cost= 0.244416240
Epoch: 0022 cost= 0.244889498
Epoch: 0023 cost= 0.242864834
Epoch: 0024 cost= 0.242400869
Epoch: 0025 cost= 0.244274715
Optimization Finished!
('Accuracy:', 0.91829997)

Deep Neural Nets (DNN) for MNIST

Hidden Layer를 하나 추가해서 multiple nerual network을 구성해보자.
학습을 위해서 activation function으로 ReLU를 사용 한다.

weight 초기는 아래와 같이 random_normal을 이용해서 랜덤으로 설정해 준다.
정규 분포에 의해서 0~1사이의 값으로 만들어 낸다.

W1 = tf.Variable(tf.random_normal([784, 256]))
W2 = tf.Variable(tf.random_normal([256, 256]))
W3 = tf.Variable(tf.random_normal([256, 10]))

B1 = tf.Variable(tf.random_normal([256]))
B2 = tf.Variable(tf.random_normal([256]))
B3 = tf.Variable(tf.random_normal([10]))

그냥 무작정 0으로 wegiht을 모두 초기화한 다음 수행하면 cost 값이 converge 되지 않는다.

import tensorflow as tf
import input_data


learning_rate = 0.01
training_epochs = 15
batch_size = 100
display_step = 1

mnist = input_data.read_data_sets("./MNIST_DATA", one_hot=True)
# tensorflow graph input
X = tf.placeholder('float', [None, 784]) # mnist data image of shape 28 * 28 = 784
Y = tf.placeholder('float', [None, 10]) # 0-9 digits recognition = > 10 classes

# set model weights
W1 = tf.Variable(tf.random_normal([784, 256]))
W2 = tf.Variable(tf.random_normal([256, 256]))
W3 = tf.Variable(tf.random_normal([256, 10]))

B1 = tf.Variable(tf.random_normal([256]))
B2 = tf.Variable(tf.random_normal([256]))
B3 = tf.Variable(tf.random_normal([10]))

# Construct model
L1 = tf.nn.relu(tf.add(tf.matmul(X,W1),B1))
L2 = tf.nn.relu(tf.add(tf.matmul(L1,W2),B2)) # Hidden layer with RELU activation
hypothesis = tf.add(tf.matmul(L2, W3), B3) # No need to use softmax here

# Define loss and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(hypothesis, Y)) # Softmax loss
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # Adam Optimizer

# Initializing the variables
init = tf.initialize_all_variables()

# Launch the graph,
with tf.Session() as sess:
    sess.run(init)

    # Training cycle
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(mnist.train.num_examples/batch_size)

        # Fit the line.
        for step in xrange(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)

            # Fit training using batch data

            sess.run(optimizer, feed_dict={X: batch_xs, Y: batch_ys})

            # Compute average loss
            avg_cost += sess.run(cost, feed_dict={X: batch_xs, Y: batch_ys})/total_batch
        # Display logs per epoch step
        if epoch % display_step == 0:
            print "Epoch:", '%04d' %(epoch+1), "cost=", "{:.9f}".format(avg_cost)

    print "Optimization Finished!"

    # Test model
    correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(Y, 1))
    # Calculate accuracy
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    print ("Accuracy:", accuracy.eval({X: mnist.test.images, Y: mnist.test.labels}))

activation function을 ReLU로 변경하고 1개의 hidden layer만 추가 했는데도 정확도가 많이 좋아졌다.
심지어 epoch를 더 적게 수행해서 트레닝 데이터 55,000개에 대해서 모두 bach processing을 다 하지도 않았다.
하지만, initial wegiht을 zero로 주거나 잘못 줄경우 아에 학습이 안될 수도 있는 불안 요소가 있고
초기에 cost값이 너무 크게 시작한다는 문제점이 있다. 자칫 layer가 다수가 된다면 정상적으로 동작 안할 수도 있다.

/root/tensorflow/bin/python /root/DataScience/TensorFlowLecture/DNNforMNIST.py
Extracting ./MNIST_DATA/train-images-idx3-ubyte.gz
/usr/lib/python2.7/gzip.py:268: VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
Extracting ./MNIST_DATA/train-labels-idx1-ubyte.gz
  chunk = self.extrabuf[offset: offset + size]
Extracting ./MNIST_DATA/t10k-images-idx3-ubyte.gz
/root/DataScience/TensorFlowLecture/input_data.py:47: VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
  data = data.reshape(num_images, rows, cols, 1)
Extracting ./MNIST_DATA/t10k-labels-idx1-ubyte.gz
Epoch: 0001 cost= 36.855221693
Epoch: 0002 cost= 6.358338172
Epoch: 0003 cost= 2.991123097
Epoch: 0004 cost= 1.751982349
Epoch: 0005 cost= 1.301591293
Epoch: 0006 cost= 1.072350917
Epoch: 0007 cost= 0.896756601
Epoch: 0008 cost= 0.748790441
Epoch: 0009 cost= 0.573228549
Epoch: 0010 cost= 0.474619466
Epoch: 0011 cost= 0.429405935
Epoch: 0012 cost= 0.421186241
Epoch: 0013 cost= 0.325840454
Epoch: 0014 cost= 0.297165287
Epoch: 0015 cost= 0.260923379
Optimization Finished!
('Accuracy:', 0.95840001)

Process finished with exit code 0

Weight initialization

지금 까지는 아래의 코드와 같이
초기 weight vector들을 그냥 0으로 초기화 했었다.
또는 그냥 정규분포를 이용해서 0~1사이의 값으로 대충 초기화 했었다.
이렇게하면 계층구조가 복잡해질 경우 학습이 이뤄지지 않는 문제가 있다.

W = tf.Variable(tf.zeros([784, 10]))
tf.random_normal([784, 256])

이렇게 무식하게 하지말고 weight 값을 좀 더 올바르게 주고 시작하자는 의미 이다.

Hinton 교수가 2006년에 발표한 논문 A Fast Learning Algorithm for Deep Belief Nets에서 제안한Restricted Boatman Machine (RBM)방법에 기초 한다.

해당 방법은 인접한 두개의 layer 들에 대해서만 pre-training step을 거쳐서 wegiht 값을 정하는 것을 말한다.

그렇게 한다고 쳐도 RBM 방식은 약간 복잡하다고 할 수 있다.

좋은 소식은 아래와 같이 더 쉽지만 비슷한 효과를 얻는 pre-training 방법들이 많이 고안 되었다.

  • Xavier initialization: X. Glorot and Y. Bengio,“Understanding the difficulty of training deep feedforward neural networks,” in International conference on artificial intelligence and statistics, 2010.
  • He’s initialization: K. He, X. Zhang, S. Ren, and J. Sun,“Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification,” 2015.

Xavier initialization은 URL에서 코드를 알 수 있다.

import tensorflow as tf
import input_data

def xavier_initializer(n_inputs, n_outputs, uniform = True):
    if uniform:
        init_range = tf.sqrt(6.0/ (n_inputs + n_outputs))
        return tf.random_uniform_initializer(-init_range, init_range)

    else:
        stddev = tf.sqrt(3.0 / (n_inputs + n_outputs))
        return tf.truncated_normal_initializer(stddev=stddev)

learning_rate = 0.01
training_epochs = 15
batch_size = 100
display_step = 1

mnist = input_data.read_data_sets("./MNIST_DATA", one_hot=True)
# tensorflow graph input
X = tf.placeholder('float', [None, 784]) # mnist data image of shape 28 * 28 = 784
Y = tf.placeholder('float', [None, 10]) # 0-9 digits recognition = > 10 classes

# set model weights
W1 = tf.get_variable("W1", shape=[784, 256], initializer=xavier_initializer(784, 256))
W2 = tf.get_variable("W2", shape=[256, 256], initializer=xavier_initializer(256, 256))
W3 = tf.get_variable("W3", shape=[256, 10], initializer=xavier_initializer(256, 10))


B1 = tf.Variable(tf.zeros([256]))
B2 = tf.Variable(tf.zeros([256]))
B3 = tf.Variable(tf.zeros([10]))

# Construct model
L1 = tf.nn.relu(tf.add(tf.matmul(X,W1),B1))
L2 = tf.nn.relu(tf.add(tf.matmul(L1,W2),B2)) # Hidden layer with RELU activation
hypothesis = tf.add(tf.matmul(L2, W3), B3) # No need to use softmax here

# Define loss and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(hypothesis, Y)) # Softmax loss
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # Adam Optimizer

# Initializing the variables
init = tf.initialize_all_variables()

# Launch the graph,
with tf.Session() as sess:
    sess.run(init)

    # Training cycle
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(mnist.train.num_examples/batch_size)

        # Fit the line.
        for step in xrange(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)

            # Fit training using batch data

            sess.run(optimizer, feed_dict={X: batch_xs, Y: batch_ys})

            # Compute average loss
            avg_cost += sess.run(cost, feed_dict={X: batch_xs, Y: batch_ys})/total_batch
        # Display logs per epoch step
        if epoch % display_step == 0:
            print "Epoch:", '%04d' %(epoch+1), "cost=", "{:.9f}".format(avg_cost)

    print "Optimization Finished!"

    # Test model
    correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(Y, 1))
    # Calculate accuracy
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    print ("Accuracy:", accuracy.eval({X: mnist.test.images, Y: mnist.test.labels}))

정확도 면에서는 크게 차이없지만, cost function이 converge하는 속도가 다르다.
처음 시작 할 때 cost 값이 0.2수준으로 이전과는 엄청난 차이가 있다.

/root/tensorflow/bin/python /root/DataScience/TensorFlowLecture/DNNforMNIST.py
Extracting ./MNIST_DATA/train-images-idx3-ubyte.gz
/usr/lib/python2.7/gzip.py:268: VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
  chunk = self.extrabuf[offset: offset + size]
/root/DataScience/TensorFlowLecture/input_data.py:47: VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
  data = data.reshape(num_images, rows, cols, 1)
Extracting ./MNIST_DATA/train-labels-idx1-ubyte.gz
Extracting ./MNIST_DATA/t10k-images-idx3-ubyte.gz
Extracting ./MNIST_DATA/t10k-labels-idx1-ubyte.gz
Epoch: 0001 cost= 0.237908776
Epoch: 0002 cost= 0.094579589
Epoch: 0003 cost= 0.076395853
Epoch: 0004 cost= 0.065301783
Epoch: 0005 cost= 0.061461856
Epoch: 0006 cost= 0.055610619
Epoch: 0007 cost= 0.055899355
Epoch: 0008 cost= 0.048690692
Epoch: 0009 cost= 0.044984061
Epoch: 0010 cost= 0.042311433
Epoch: 0011 cost= 0.046229366
Epoch: 0012 cost= 0.041400560
Epoch: 0013 cost= 0.037953107
Epoch: 0014 cost= 0.035833549
Epoch: 0015 cost= 0.034003958
Optimization Finished!
('Accuracy:', 0.96890002)

Process finished with exit code 0

Dropout을 이용한 5 layer nerual network

Layer 수를 증가 시킨다.
그리고 overffiting을 막기 위해서 training을 할 때 dropout을 적용해서 training을 수행 한다.

dropout은 모든 레이어의 perceptron을 연결 시키지않고 임의로 지정된 비율만큼 단절 시킨 상태에서 weight값을 계산하는 기법을 말한다. 이를 통해서 overffting을 어느정도 막을 수 있다.

그리고 prediction을 수행 할 때는 다시 dropout 비율을 1로 해서 모든 perceptron을 연결한 상태에서 모델예측을 수행 한다.

import tensorflow as tf
import input_data

def xaver_init(n_inputs, n_outputs, uniform = True):
    if uniform:
        init_range = tf.sqrt(6.0/ (n_inputs + n_outputs))
        return tf.random_uniform_initializer(-init_range, init_range)

    else:
        stddev = tf.sqrt(3.0 / (n_inputs + n_outputs))
        return tf.truncated_normal_initializer(stddev=stddev)

learning_rate = 0.001
training_epochs = 25
batch_size = 100
display_step = 1

mnist = input_data.read_data_sets("./MNIST_DATA", one_hot=True)
# tensorflow graph input
X = tf.placeholder('float', [None, 784]) # mnist data image of shape 28 * 28 = 784
Y = tf.placeholder('float', [None, 10]) # 0-9 digits recognition = > 10 classes

# set dropout rate
dropout_rate = tf.placeholder("float")

# set model weights
W1 = tf.get_variable("W1", shape=[784, 256], initializer=xaver_init(784, 256))
W2 = tf.get_variable("W2", shape=[256, 256], initializer=xaver_init(256, 256))
W3 = tf.get_variable("W3", shape=[256, 256], initializer=xaver_init(256, 256))
W4 = tf.get_variable("W4", shape=[256, 256], initializer=xaver_init(256, 256))
W5 = tf.get_variable("W5", shape=[256, 10], initializer=xaver_init(256, 10))

B1 = tf.Variable(tf.random_normal([256]))
B2 = tf.Variable(tf.random_normal([256]))
B3 = tf.Variable(tf.random_normal([256]))
B4 = tf.Variable(tf.random_normal([256]))
B5 = tf.Variable(tf.random_normal([10]))

# Construct model
_L1 = tf.nn.relu(tf.add(tf.matmul(X,W1),B1))
L1 = tf.nn.dropout(_L1, dropout_rate)
_L2 = tf.nn.relu(tf.add(tf.matmul(L1, W2),B2)) # Hidden layer with ReLU activation
L2 = tf.nn.dropout(_L2, dropout_rate)
_L3 = tf.nn.relu(tf.add(tf.matmul(L2, W3),B3)) # Hidden layer with ReLU activation
L3 = tf.nn.dropout(_L3, dropout_rate)
_L4 = tf.nn.relu(tf.add(tf.matmul(L3, W4),B4)) # Hidden layer with ReLU activation
L4 = tf.nn.dropout(_L4, dropout_rate)

hypothesis = tf.add(tf.matmul(L4, W5), B5) # No need to use softmax here

# Define loss and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(hypothesis, Y)) # Softmax loss
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # Adam Optimizer

# Initializing the variables
init = tf.initialize_all_variables()

# Launch the graph,
with tf.Session() as sess:
    sess.run(init)

    # Training cycle
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(mnist.train.num_examples/batch_size)

        # Fit the line.
        for step in xrange(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)

            # Fit training using batch data
            # set up 0.7 for training time
            sess.run(optimizer, feed_dict={X: batch_xs, Y: batch_ys, dropout_rate: 0.7})

            # Compute average loss
            avg_cost += sess.run(cost, feed_dict={X: batch_xs, Y: batch_ys, dropout_rate: 0.7})/total_batch
        # Display logs per epoch step
        if epoch % display_step == 0:
            print "Epoch:", '%04d' %(epoch+1), "cost=", "{:.9f}".format(avg_cost)

    print "Optimization Finished!"

    # Test model
    correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(Y, 1))
    # Calculate accuracy
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    print ("Accuracy:", accuracy.eval({X: mnist.test.images, Y: mnist.test.labels, dropout_rate: 1}))

위코드에서 아래의 코드가 dropout을 나타낸다.
prediction을 할떄는 dropout_rate = 1로 설정한 다음 수행 한다.
learning_rate = 0.001으로 설정한것이 이전 코드와 차이점이다.
0.01로 할경우 오히려 정확도가 떨어진다.

L1 = tf.nn.dropout(_L1, dropout_rate)

최종 결과는 아래와 같다.
testing set 10000개에 대해서 98%의 정확도로 prediction을 하고 있다.
매우 훌륭한 결과를 얻었다.

/root/tensorflow/bin/python /root/DataScience/TensorFlowLecture/DNN_DropoutForMNIST.py
Extracting ./MNIST_DATA/train-images-idx3-ubyte.gz
/usr/lib/python2.7/gzip.py:268: VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
  chunk = self.extrabuf[offset: offset + size]
/root/DataScience/TensorFlowLecture/input_data.py:47: VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
  data = data.reshape(num_images, rows, cols, 1)
Extracting ./MNIST_DATA/train-labels-idx1-ubyte.gz
Extracting ./MNIST_DATA/t10k-images-idx3-ubyte.gz
Extracting ./MNIST_DATA/t10k-labels-idx1-ubyte.gz
Epoch: 0001 cost= 0.541453579
Epoch: 0002 cost= 0.206996800
Epoch: 0003 cost= 0.157958631
Epoch: 0004 cost= 0.131259738
Epoch: 0005 cost= 0.115274848
Epoch: 0006 cost= 0.098876665
Epoch: 0007 cost= 0.089464739
Epoch: 0008 cost= 0.088287840
Epoch: 0009 cost= 0.078404236
Epoch: 0010 cost= 0.072852779
Epoch: 0011 cost= 0.066189782
Epoch: 0012 cost= 0.066654595
Epoch: 0013 cost= 0.060475496
Epoch: 0014 cost= 0.057644885
Epoch: 0015 cost= 0.054782469
Epoch: 0016 cost= 0.052261842
Epoch: 0017 cost= 0.053377932
Epoch: 0018 cost= 0.049687267
Epoch: 0019 cost= 0.050434303
Epoch: 0020 cost= 0.049436003
Epoch: 0021 cost= 0.044065983
Epoch: 0022 cost= 0.043885315
Epoch: 0023 cost= 0.044500848
Epoch: 0024 cost= 0.044421383
Epoch: 0025 cost= 0.040629214
Optimization Finished!
('Accuracy:', 0.98079997)

Process finished with exit code 0

구현 코드는 GitHub에 올려 두었다.

참고자료

모두를 위한 머신 러닝 by 홍콩과기대 김성훈 교수


TensorFlow 기본 개념 (1)


머신러닝의 정의란 Andrew Ng교수님의 말을 인용하면 아래와 같다.

A computer program is said to learn from experience E with respect to some class of taks T and performance tasks in T, as measured by P, improves with experience E, Tom M. Mitchell

Tensor는 뭔가?

많은 데이터를 효과적으로 처리하는 자료구조이다.
다차원 array, list라고 생각하면 된다.
이래서 NumPy를 확장한것이 Tensor인 것이다.

# What is tensor?
tensor1 = 7          # 0-dimensional
tensor2 = [7]        # 1-dimensional
tensor3 = [[1,2,3],  # 2-dimensional
           [4,5,6]]  ...
           ...

Flow

Flow란 결국 Graph이다.
즉 모든 계산을 쉽게 하기 위해서 각각의 연산을 잘게 쪼개고 이것을 Graph로 연결 한 것이다.
미분 Chain Rule 같은것을 생각해보면 왜 연산이 간단해 지는지 알 수 있다.

Graph, Node, Edge

선언부와 실행부가 다르다

TensorFlow코드 상에서 a=1을 선언해도 추후에 이것이 반영되는 것이지 지금 당장 변수 a가 1로 assgin된것은 아니다.
이러한 부분이 기본적인 python 프로그래밍과는 다른점이다. 
이러한 동작 매커니즘을 생각해보면 결국 앞으로 어떻게 동작 할 것이다라는 계획을 Graph로 표현한것이 TensorFlow가 되는 것이다.

  • Operation: 동작을 정의한 것
  • Node: Operation 정의를 포함한 것
  • Edge: Node와 Node를 연결한 것

이러한 구조에서 Tensor데이터 구조가 이동하면서 Operation에 의해서 연산을 하는 방식이다.

import tensotflow as tf

위처럼 TensorFlow를 일단 Import 하면 내부적으로 default_graph_stack에 Default Graph가 생긴다.

tf.get_default_graph()명령어로 접근 가능
이 graph에 저장된 operation을 확인해 보면 []비어 있는 것을 알 수 있다.

상수를 하나 선언 하면 아래처럼 주소가 기록된다.
이러한 의미는 operation이 리스트 형태로 들어가 있는 것이다.

import tensorflow as tf
graph = tf.get_default_graph()
graph.get_operations()
input = tf.constant(1.0)
operations = graph.get_operations()
operations
[<tensorflow.python.framework.ops.Operation at 0x7f94695fbd90>,
 <tensorflow.python.framework.ops.Operation at 0x7f944788a990>]

operation을 감싸고 있는 node를 출력해보자.

>>> operations[0].node_def
## name: "Const"
## op: "Const"
## attr {
##   key: "dtype"
##   value {
##     type: DT_FLOAT
##   }
## }
## attr {
##   key: "value"
##   value {
##     tensor {
##       dtype: DT_FLOAT
##       tensor_shape {
##       }
##       float_val: 1.0
##     }
##   }
## }

TensorFlow는 내부적으로 protocol buffer를 이용한다.
어떤 google style의 JSON이라고 생각하면 쉽다. 위에 출력도 JSON 스럽기 때문이다.

왜 tensorflow는 이처럼 고유한 특징의 구조나 타입들을 가지는 것일까?

Numpy는 기본적으로 matrix을 연산을 위해서 C++로 개발 되었다. 하지만 여전히 많은 overhead를 발생 시킨다.
완전히 Python 영역 밖에서 동작 시킴으로 성능을 끌어 올린다.
이러한 방법은 Theano또는 Torch에서 수행하는 방식이다.

그냥 input을 출력해 보자

In [27]: input
Out[27]: <tf.Tensor 'Const_9:0' shape=() dtype=float32>

32비트 float tensor를 확인 할 수 있다. no dimension 이며 그냥 싱글 숫자이다.

이제 실제로 input값을 session으로 실행하자.
default에 의해서 default graph에서 값을 꺼내오는 방식이다.

>>> sess = tf.Session()
>>> sess.run(input_value)
## 1.0

초 간단 TensorFlow neuron

Hadley Wickham said Names have objects rather than the reverse이다.
표현 하면 아래의 그림과 같다.

여기서 말하는 초간단 neron은 non-identity activation function도 없고 bias도 없는 것을 말한다.

constant는 상수를 의미하고, variable은 계속 변화하는 값을 의미 한다.

weight = tf.Variable(0.8)
#지원하는 operation 이름 확인
for op in graph.get_operations(): print op.name

x에 w를 곱해서 y라는 출력값을 만들어냄
이때 참값은 0이다.

입력값은 1로 고정 시킨다면 당연히 참값 y_ = 0과 일치시키는 w는 0일것이다.

단순한 방정식이다.

$$ 1 \times w = 0 $$

위와 같을 때는 $w$는 0이 최적화된 값일 것이다.

하지만 이러한 해를 wieght값 w를 조정하면서 전체 cost를 줄이는 방법으로 차근 차근 찾아보자.

w의 시작값을 0.8로 설정 한다.
이때의 cost는 $1 \times 0.8 = 0.8$ 이다.
이 cost를 계산하는 것이 최소제곱법이 있다.

$$ cost = minimum(\hat { y }- y)^{2} $$
이것을 최소화로 만들어주는 방법은 y를 결정하는 함수 $f(x) = wx$ 입니다. 결국 w를 조정해서 해를 찾을 수 있게 됩니다.

우선, cost함수를 w에 대해서 편미분하면 chain rule에 의해서 아래와 같이 나옵니다.

$$ \frac{\partial cost}{\partial w} = 2 \times x$$

그리고 이러한 과정에 쓰이는 데이터를 트레이닝 데이터라고 하고 과정을 학습(learning)이라고 합니다.
TensorFlow에서느 사실 이러한 미분을 자동으로 해주게 됩니다. 구지 손으로 저렇게 해서 식으로 적을 필요 없습니다.

그리고 이러한 미분값을 최적화 시키기위해서 조금씩 값을 update하는 방법을 사용하게 됩니다. 
방법의 이름은 gradient descent algorihtm입니다. 알고리즘을 표현하면 아래와 같습니다.

$$ w = w - \alpha \frac{\partial cost}{\partial w} $$

위 알고리즘은 간단하게 TensorFlow에서는 아래와 같은 함수하나만 이용하면 딥니다.

optim = tf.train.GradientDescentOptimizer(learning_rate = 0.025)

learing rate을 0.025를 적용해서 손으로 1단계를 게산해보면 아래와 같습니다.
$1.6 \times 0.025 = 0.04$가 됩니다.

아래 코드는 이러한 과정을 적용한 전체 코드 입니다.

import tensorflow as tf
x = tf.constant(1.0, name='input')
w = tf.Variable(0.8, name='weight')
y = tf.mul(w, x, name='output')
y_ = tf.constant(0.0, name='correct_value')
loss = tf.pow(y - y_, 2, name='loss')

train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)

for value in [x, w, y, y_, loss]:
    tf.scalar_summary(value.op.name, value)

summaries = tf.merge_all_summaries()

sess = tf.Session()
summary_writer = tf.train.SummaryWriter('log_simple_stats', sess.graph)

sess.run(tf.initialize_all_variables())
for i in range(100):
    if i % 10 ==0:
        print("epoch {}, output: {}".format(i, sess.run(y)))
    summary_writer.add_summary(sess.run(summaries), i)
    sess.run(train_step)

출력결과

epochs 0, output: 0.800000011921
epochs 10, output: 0.478989481926
epochs 20, output: 0.286788702011
epochs 30, output: 0.171710968018
epochs 40, output: 0.10280970484
epochs 50, output: 0.0615559667349
epochs 60, output: 0.0368558317423
epochs 70, output: 0.0220669470727
epochs 80, output: 0.0132122989744
epochs 90, output: 0.00791069027036

output은 weight 값 w를 의미하며 정답인 0에 거의 근접한것을 알 수 있다.

해당 결과를 Tensor Board를 통해서 시각적으로 확인 할 수도 있다.
프로젝트 디렉터리에서 log_simple_stats에 저장한다고 했으니 그곳에서 아래의 명령어를 수행 한다.

tensorboard --logdir=./log_simple_stats

그다음 크롬을 이용해서 http://192.168.188.129:6006/#graphs아래 주소로 접속 한다.
IP Address는 각자 맞춰서 한다. 
그냥 local에서 작업한다면 localhost:6006/#graphs라고 쓰면 된다.

작성코드

본 내용에 작성된 코드들은 Github를 통해서 공유하겠습니다.
주소: https://github.com/leejaymin/TensorFlowLecture/tree/master/0.Basic

참고자료

https://www.oreilly.com/learning/hello-tensorflow
https://jihobak.github.io/2016-06-26-deeplearning-ninja001/


TensorFlow 버전 업데이트 (Version Update)


현재 버전 확인

virtualenv 사용자는 활성화 시킴

source ./tensorflow/bin/activate

Tensor flow version 확인

(tensorflow)root@jemin-virtual-machine:~/tensorflow# pip show tensorflow
---
Name: tensorflow
Version: 0.5.0
Location: /root/tensorflow/lib/python2.7/site-packages
Requires: numpy, six

필자의 경우 설치를 예전에해서 r0.5버전이다.

업데이트 (r0.5 -> r0.9)

당연히 python에서 pacakge를 쉽게 설치하게 도와주는 pip유틸이 설치되어 있어야 한다.

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

그 다음 Ubuntu 6bit이며 Python 2.7버전을 쓰는 사람은 아래의 r0.9 tensorflow를 다운 받자.
2016.6.15일 기준으로 필자는 r0.5에서 r0.9로 업데이트 했다.

# Ubuntu/Linux 64-bit, CPU only, Python 2.7
export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.9.0rc0-cp27-none-linux_x86_64.whl

# Python 2
(tensorflow)$ sudo pip install --upgrade $TF_BINARY_URL

실행하면 아래와 같이 설치가 된다.

    changing mode of /root/tensorflow/bin/f2py to 755
  Found existing installation: wheel 0.24.0
    Not uninstalling wheel at /usr/lib/python2.7/dist-packages, outside environment /root/tensorflow
  Found existing installation: setuptools 2.2
    Uninstalling setuptools:
      Successfully uninstalled setuptools
Successfully installed tensorflow numpy protobuf wheel setuptools
Cleaning up...

버전을 확인하면 r0.9로 변경된것을 알 수 있다.

(tensorflow)root@jemin-virtual-machine:~/tensorflow# pip show tensorflow
---
Name: tensorflow
Version: 0.9.0rc0
Location: /root/tensorflow/lib/python2.7/site-packages
Requires: numpy, protobuf, wheel, six

업데이트 (r0.9 -> r0.12)

Update 전

(tensorflow)root@jemin-virtual-machine:~# pip show tensorflow
---
Name: tensorflow
Version: 0.9.0rc0
Location: /root/tensorflow/lib/python2.7/site-packages
Requires: numpy, protobuf, wheel, six

자신에게 맞는 최신 TensorFlow 링크를 추가한다.

# Ubuntu/Linux 64-bit, CPU only, Python 2.7
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.0rc0-cp27-none-linux_x86_64.whl

# Ubuntu/Linux 64-bit, GPU enabled, Python 2.7
# Requires CUDA toolkit 8.0 and CuDNN v5. For other versions, see "Installing from sources" below.
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-0.12.0rc0-cp27-none-linux_x86_64.whl

# Mac OS X, CPU only, Python 2.7:
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.12.0rc0-py2-none-any.whl

# Mac OS X, GPU enabled, Python 2.7:
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/gpu/tensorflow_gpu-0.12.0rc0-py2-none-any.whl

# Ubuntu/Linux 64-bit, CPU only, Python 3.4
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.0rc0-cp34-cp34m-linux_x86_64.whl

# Ubuntu/Linux 64-bit, GPU enabled, Python 3.4
# Requires CUDA toolkit 8.0 and CuDNN v5. For other versions, see "Installing from sources" below.
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-0.12.0rc0-cp34-cp34m-linux_x86_64.whl

# Ubuntu/Linux 64-bit, CPU only, Python 3.5
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.0rc0-cp35-cp35m-linux_x86_64.whl

# Ubuntu/Linux 64-bit, GPU enabled, Python 3.5
# Requires CUDA toolkit 8.0 and CuDNN v5. For other versions, see "Installing from sources" below.
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-0.12.0rc0-cp35-cp35m-linux_x86_64.whl

# Mac OS X, CPU only, Python 3.4 or 3.5:
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.12.0rc0-py3-none-any.whl

# Mac OS X, GPU enabled, Python 3.4 or 3.5:
$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/gpu/tensorflow_gpu-0.12.0rc0-py3-none-any.whl

업그레이드

필자는 Ubuntu 64bit, python 2.7, CPU 버전을 쓰므로 아래 링크를 추가 했다.

# Ubuntu/Linux 64-bit, CPU only, Python 2.7
export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.0rc0-cp27-none-linux_x86_64.whl

pip install --upgrade $TF_BINARY_URL

성공 메시지

Successfully installed tensorflow protobuf mock numpy setuptools funcsigs pbr
Cleaning up..

버전업데이트 확인
무슨 문제인건지 pip show로 이전처럼 버전 확인이 잘 안된다. 계속 예전버전으로 표시된다.
직접 파이썬에서 Tensorflow를 실행해서 확인하자.

(tensorflow)root@jemin-virtual-machine:~# python -c 'import tensorflow as tf; print(tf.__version__)'

0.12.0-rc0


Naive Baeys vs Neural network


Naive Bayes와 Deep Neural Networks에서 어느 알고리즘이 더 우수한지 평가한다.

한 사용자의 응답성을 나타내는 데이터이다.
1276개의 응답성 지표를 가지고 분석 했다.
1276

응답 비율은 아래와 같다.
(FALSE): 922
(TRUE): 354

해당 데이터를 70%는 트레이닝 30%는 테스팅으로 분할 한다.
임의로 분할 하기 때문에 모든 요일과 시간이 적절히 섞인다.

요청이 있어서 데이터의 일부분을 Google Drive로 공유합니다.
단순히 형태만 참고 하시면 됩니다.
앞의 8개는 입력 Feature 이고 마지막 class가 출력값 입니다.
응답성은 True와 False값을 가지므로 Binary classification문제가 됩니다.

Naive Bayes Classifier in R##

데이터 구조

> head(training)
1        1    74          18               4                  2         2        1                 1
2        2    10          18               4                  2         2        3                 2
4        1    56          19               4                  2         2        1                 1
6        1    84          19               4                  1         1        1                 4
8        1    39          19               4                  1         2        1                 4
9        1    56          19               4                  1         2        1                 4
library(caret)
set.seed(12358)
inTrain <- createDataPartition(y=factorClassList[['ikhee']], p=0.70, list =FALSE)
training <- data.frame(dfList[['ikhee']][inTrain,])
testing <-  data.frame(dfList[['ikhee']][-inTrain,])

classTraining <- factorClassList[['ikhee']][inTrain]
classtesting <-  factorClassList[['ikhee']][-inTrain]

sms_model1 <- train(training,classTraining, method="nb", trControl = ctrl, tuneGrid = 
                        data.frame(.fL=c(0,0,1,1,10,10), .usekernel=c(FALSE,TRUE,FALSE,TRUE,FALSE,TRUE)))
sms_model1

sms_predict1 <- predict(sms_model1, testing)
cm1 <- confusionMatrix(sms_predict1, classtesting, positive="TRUE")
cm1

결과

> cm1
Confusion Matrix and Statistics

          Reference
Prediction FALSE TRUE
     FALSE   273  102
     TRUE      3    4
                                          
               Accuracy : 0.7251          
                 95% CI : (0.6774, 0.7693)
    No Information Rate : 0.7225          
    P-Value [Acc > NIR] : 0.4806          
                                          
                  Kappa : 0.0377          
 Mcnemar's Test P-Value : <2e-16          
                                          
            Sensitivity : 0.03774         
            Specificity : 0.98913         
         Pos Pred Value : 0.57143         
         Neg Pred Value : 0.72800         
             Prevalence : 0.27749         
         Detection Rate : 0.01047         
   Detection Prevalence : 0.01832         
      Balanced Accuracy : 0.51343         
                                          
       'Positive' Class : TRUE  

실제값

> table(classtesting)
classtesting
FALSE  TRUE 
  276   106 

Deep Neural Networks with TensorFlow in Python##

three layers neural networks. Activation function is Sigmoid (logistic)

데이터구조
Neural network의 경우 학습을 위해서
모든 Feature와 Class를 0~1 사이로 normalization 해야 cost function이 convergence 된다.
그렇지 않으면 발산 한다.

> head(ikheeTrainingDf_norm)
1   0.0000 0.56153846   0.7391304             0.5                  1         1        0               0.0  1
2   0.0625 0.06923077   0.7391304             0.5                  1         1        1               0.2  1
3   0.0000 0.42307692   0.7826087             0.5                  1         1        0               0.0  1
4   0.0000 0.63846154   0.7826087             0.5                  0         0        0               0.6  1
5   0.0000 0.29230769   0.7826087             0.5                  0         1        0               0.6  0
6   0.0000 0.42307692   0.7826087             0.5                  0         1        0               0.6  1

이러한 데이터를 txt로 export해서
python으로 다시 처리한다.

import tensorflow as tf
import numpy as np
from sklearn.metrics import precision_score, confusion_matrix
from sklearn.metrics import classification_report
import pandas as pd

# three layers neural networks. Activation function is Sigmoid (logistic)

xyTraining = np.loadtxt('ikheeTrainingNorm.txt', unpack=True)
xyTesting = np.loadtxt('ikheeTestingNorm.txt', unpack=True)

x_data_training = np.transpose(xyTraining[0:-1])
y_data_training = np.reshape(xyTraining[-1], (len(x_data_training), 1))

x_data_testing = np.transpose(xyTesting[0:-1])
y_data_testing = np.reshape(xyTesting[-1], (len(x_data_testing), 1))

X = tf.placeholder(tf.float32, name='X-input')
Y = tf.placeholder(tf.float32, name='Y-input')

W1 = tf.Variable(tf.random_uniform([8, 16], -1.0, 1.0), name='Weight1')
W2 = tf.Variable(tf.random_uniform([16, 8], -1.0, 1.0), name='Weight2')
W3 = tf.Variable(tf.random_uniform([8, 1], -1.0, 1.0), name='Weight3')

b1 = tf.Variable(tf.zeros([16]), name="Bias1")
b2 = tf.Variable(tf.zeros([8]), name="Bias2")
b3 = tf.Variable(tf.zeros([1]), name="Bias3")


# Our hypothesis
with tf.name_scope("layer2") as scope:
    L2 = tf.sigmoid(tf.matmul(X, W1) + b1)

with tf.name_scope("layer3") as scope:
    L3 = tf.sigmoid(tf.matmul(L2, W2) + b2)

with tf.name_scope("layer4") as scope:
    hypothesis = tf.sigmoid(tf.matmul(L3, W3) + b3)

# Cost function
with tf.name_scope("cost") as scope:
    cost = -tf.reduce_mean(Y*tf.log(hypothesis) + (1-Y)*tf.log(1-hypothesis))
    cost_summ = tf.scalar_summary("cost", cost)

# Minimize
with tf.name_scope("train") as scope:
    a = tf.Variable(0.01) # Learning rate, alpha
    optimizer = tf.train.GradientDescentOptimizer(a)
    train = optimizer.minimize(cost)

# Add histogram
w1_hist = tf.histogram_summary("weights1", W1)
w2_hist = tf.histogram_summary("weights2", W2)

b1_hist = tf.histogram_summary("biases1", b1)
b2_hist = tf.histogram_summary("biases2", b2)

y_hist = tf.histogram_summary("y", Y)


# Before starting, initialize the variables.
# We will `run` this first.
init = tf.initialize_all_variables()


# Launch the graph,
with tf.Session() as sess:
    # tensorboard --logdir=./logs/xor_logs
    merged = tf.merge_all_summaries()
    writer = tf.train.SummaryWriter("./logs/xor_logs", sess.graph_def)

    sess.run(init)
    # Fit the line.
    for step in xrange(2000):
        sess.run(train, feed_dict={X:x_data_training, Y:y_data_training})
        if step % 200 == 0:
            summary = sess.run(merged, feed_dict={X:x_data_training, Y:y_data_training})
            writer.add_summary(summary, step)
            #print step, sess.run(cost, feed_dict={X:x_data, Y:y_data}), sess.run(W1), sess.run(W2)
            print step, sess.run(cost, feed_dict={X:x_data_training, Y:y_data_training})

    # Test model
    correct_prediction = tf.equal(tf.floor(hypothesis+0.5), Y)
    # Calculate accuracy
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

    y_data_pred = sess.run(tf.floor(hypothesis + 0.5),
                           feed_dict={X: x_data_testing, Y: y_data_testing})

    print sess.run([hypothesis, tf.floor(hypothesis+0.5), correct_prediction, accuracy], feed_dict={X:x_data_testing, Y:y_data_testing})
    print "Accuracy:", accuracy.eval({X:x_data_testing, Y:y_data_testing})

#   print confusion_matrix(y_data_testing[:,0], y_data_pred[:,0], labels=[0, 1])
    pd_y_true = pd.Series(y_data_testing[:, 0])
    pd_x_pred = pd.Series(y_data_pred[:, 0])
    print pd.crosstab(pd_y_true, pd_x_pred, rownames=['True'], colnames=['Predicted'], margins=True)

    target_names = ['false', 'true']
    print(classification_report(y_data_testing[:, 0], y_data_pred[:, 0], target_names=target_names))
    print 'Precision', precision_score(y_data_testing[:, 0], y_data_pred[:, 0], average='binary',pos_label=1)

결과

# Iteration & Cost
0 0.594439
200 0.59129
400 0.591194
600 0.591135
800 0.591078
1000 0.59102
1200 0.590963
1400 0.590907
1600 0.590852
1800 0.590796

Accuracy: 0.722513

# Confusion Matrix
Predicted  0.0  All
0.0        276  276
1.0        106  106
All        382  382


# Precision & Recall
             precision    recall  f1-score   support
      false       0.72      1.00      0.84       276
       true       0.00      0.00      0.00       106

avg / total       0.52      0.72      0.61       382

11 layers neural networks.
Activation functions are LeRu and Sigmoid (logistic)

코드

import tensorflow as tf
import numpy as np
from sklearn.metrics import precision_score, confusion_matrix
from sklearn.metrics import classification_report
import pandas as pd

# three layers neural networks. Activation function is Sigmoid (logistic)

xyTraining = np.loadtxt('ikheeTrainingNorm.txt', unpack=True)
xyTesting = np.loadtxt('ikheeTestingNorm.txt', unpack=True)

x_data_training = np.transpose(xyTraining[0:-1])
y_data_training = np.reshape(xyTraining[-1], (len(x_data_training), 1))

x_data_testing = np.transpose(xyTesting[0:-1])
y_data_testing = np.reshape(xyTesting[-1], (len(x_data_testing), 1))

X = tf.placeholder(tf.float32, name='X-input')
Y = tf.placeholder(tf.float32, name='Y-input')

W1 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight1')
# 9 hidden layers
W2 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight2')
W3 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight3')
W4 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight4')
W5 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight5')
W6 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight6')
W7 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight7')
W8 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight8')
W9 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight9')
W10 = tf.Variable(tf.random_uniform([8, 8], -1.0, 1.0), name='Weight10')

W11 = tf.Variable(tf.random_uniform([8, 1], -1.0, 1.0), name='Weight11')

b1 = tf.Variable(tf.zeros([8]), name="Bias1")
b2 = tf.Variable(tf.zeros([8]), name="Bias2")
b3 = tf.Variable(tf.zeros([8]), name="Bias3")
b4 = tf.Variable(tf.zeros([8]), name="Bias4")
b5 = tf.Variable(tf.zeros([8]), name="Bias5")
b6 = tf.Variable(tf.zeros([8]), name="Bias6")
b7 = tf.Variable(tf.zeros([8]), name="Bias7")
b8 = tf.Variable(tf.zeros([8]), name="Bias8")
b9 = tf.Variable(tf.zeros([8]), name="Bias9")
b10 = tf.Variable(tf.zeros([8]), name="Bias10")

b11 = tf.Variable(tf.zeros([1]), name="Bias11")


# Our hypothesis
with tf.name_scope("layer1") as scope:
    L1 = tf.nn.relu(tf.matmul(X, W1) + b1)
with tf.name_scope("layer2") as scope:
    L2 = tf.nn.relu(tf.matmul(L1, W2) + b2)
with tf.name_scope("layer3") as scope:
    L3 = tf.nn.relu(tf.matmul(L2, W3) + b3)
with tf.name_scope("layer4") as scope:
    L4 = tf.nn.relu(tf.matmul(L3, W4) + b4)
with tf.name_scope("layer5") as scope:
    L5 = tf.nn.relu(tf.matmul(L4, W5) + b5)
with tf.name_scope("layer6") as scope:
    L6 = tf.nn.relu(tf.matmul(L5, W6) + b6)
with tf.name_scope("layer7") as scope:
    L7 = tf.nn.relu(tf.matmul(L6, W7) + b7)
with tf.name_scope("layer8") as scope:
    L8 = tf.nn.relu(tf.matmul(L7, W8) + b8)
with tf.name_scope("layer9") as scope:
    L9 = tf.nn.relu(tf.matmul(L8, W9) + b9)
with tf.name_scope("layer10") as scope:
    L10 = tf.nn.relu(tf.matmul(L9, W10) + b10)
with tf.name_scope("last") as scope:
    hypothesis = tf.sigmoid(tf.matmul(L10, W11) + b11)


# Cost function
with tf.name_scope("cost") as scope:
    cost = -tf.reduce_mean(Y*tf.log(hypothesis) + (1-Y)*tf.log(1-hypothesis))
    cost_summ = tf.scalar_summary("cost", cost)

# Minimize
with tf.name_scope("train") as scope:
    a = tf.Variable(0.001) # Learning rate, alpha
    optimizer = tf.train.GradientDescentOptimizer(a)
    train = optimizer.minimize(cost)



# Before starting, initialize the variables.
# We will `run` this first.
init = tf.initialize_all_variables()


# Launch the graph,
with tf.Session() as sess:
    # tensorboard --logdir=./logs/xor_logs
    merged = tf.merge_all_summaries()
    writer = tf.train.SummaryWriter("./logs/xor_logs", sess.graph_def)

    sess.run(init)
    # Fit the line.
    for step in xrange(50000):
        sess.run(train, feed_dict={X:x_data_training, Y:y_data_training})
        if step % 2000 == 0:
            summary = sess.run(merged, feed_dict={X:x_data_training, Y:y_data_training})
            writer.add_summary(summary, step)
            #print step, sess.run(cost, feed_dict={X:x_data, Y:y_data}), sess.run(W1), sess.run(W2)
            print step, sess.run(cost, feed_dict={X:x_data_training, Y:y_data_training})

    # Test model
    correct_prediction = tf.equal(tf.floor(hypothesis+0.5), Y)
    # Calculate accuracy
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

    y_data_pred = sess.run(tf.floor(hypothesis + 0.5),
                           feed_dict={X: x_data_testing, Y: y_data_testing})

    print sess.run([hypothesis, tf.floor(hypothesis+0.5), correct_prediction, accuracy], feed_dict={X:x_data_testing, Y:y_data_testing})
    print "Accuracy:", accuracy.eval({X:x_data_testing, Y:y_data_testing})

#   print confusion_matrix(y_data_testing[:,0], y_data_pred[:,0], labels=[0, 1])
    pd_y_true = pd.Series(y_data_testing[:, 0])
    pd_x_pred = pd.Series(y_data_pred[:, 0])
    print pd.crosstab(pd_y_true, pd_x_pred, rownames=['True'], colnames=['Predicted'], margins=True)

    target_names = ['false', 'true']
    print(classification_report(y_data_testing[:, 0], y_data_pred[:, 0], target_names=target_names))
    print 'Precision', precision_score(y_data_testing[:, 0], y_data_pred[:, 0], average='binary',pos_label=1)

결과

/root/tensorflow/bin/python /root/PycharmProjects/TensorFlowTest/PASSwithDNNLeRu9Hidden.py
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 8
I tensorflow/core/common_runtime/local_session.cc:45] Local session inter op parallelism threads: 8
0 0.770699
2000 0.589376
4000 0.580235
6000 0.578699
8000 0.577574
10000 0.576372
12000 0.575388
14000 0.574309
16000 0.572363
18000 0.570983
20000 0.569931
22000 0.568943
24000 0.567569
26000 0.565458
28000 0.564114
30000 0.562682
32000 0.561554
34000 0.56046
36000 0.559264
38000 0.558028
40000 0.556391
42000 0.555027
44000 0.553637
46000 0.55207
48000 0.550296


Accuracy: 0.727749
Predicted  0.0  1.0  All
True                    
0.0        276    0  276
1.0        104    2  106
All        380    2  382
             precision    recall  f1-score   support

      false       0.73      1.00      0.84       276
       true       1.00      0.02      0.04       106

avg / total       0.80      0.73      0.62       382

Precision 1.0

Conclusion

데이터 자체의 Label즉 truefalse가 부정확하기 때문에

Garbage in Garbage out의 명제대로 그다지 차이가 없다.
하지만 precision 정확도가 Deep Neural Network의 경우 매우 우수하기 때문에
False Prediction이 치명적인 시스템에서는 유효하다고 볼 수있다.

좀더 Deep Neural Network을
DroupoutAda Optimizer초기 Weight설정 등을 통해서 향상 시킬 수 있을것 같다.


'AI > TensorFlow, PyTorch, Keras, Scikit' 카테고리의 다른 글

TensorFlow 기본 개념 (1)  (0) 2016.07.01
TensorFlow 버전 업데이트 (Version Update)  (4) 2016.06.15
Softmax Function  (0) 2016.04.19
Neural Networks in XOR problem  (0) 2016.04.18
Logistic Regression  (0) 2016.04.17

Softmax Function


Logistic regression이란 아래의 식 처럼 0~1사이의 값을 출력해 준다.

$$ Z=H(X), g(z)$$
$$ g(z) = \frac{1}{1+e^{-z}}$$

이것을 그림으로 간소화 시켜보면 아래와 같다.

결국, Logistic Regression이라고 하는 것은 아래와 같은 두개의 데이터 분포에서
하나의 선을 그어서 데이터를 분할 하는 것을 말한다.

Activation Function으로 Sigmoid를 사용한다면 Logistic Regression과 Perceptron은 같은 개념 같기도 하다.

Multinomial Classification

여러개의 class로 확장을 해보자.

데이터 셋은 아래와 같다.

x1(hours)x2(attendance)y(grade)
105A
95A
32B
24B
111C

A, B, C 세 개가 있을 때

  • A or B, C
  • B or A, C
  • C or A, C 이렇게 세개의 Classifier가 있으면 가능 하다. 

그리고 이것을 각각 Matrix 연산을 하게 된다.

계산이 복잡하므로 이것을 합치면 아래와 같다.

그리고 세개의 값 중에서 가장 큰 값을 선택해서 그것으로 class label을 prediction 해도 괜찮다.

결국 20, 10, 0.1 이런식으로 나올 것이다.
하지만 이것은 우리가 원하는 것이 아니다.

0~1의 값을 가지며, 모든 class를 합치면 1이 되는 그러한 형태로 만들고 싶다.

그래서 사용하는것이 아래와 같은 Softmax함수이다.
그러면 0~1값과 모두 확률 값이므로 더하면 1이된다.

마지막으로 이중에서 가장 큰 확률을 가지는것만 1로 설정하고 나머지들은 모두 0으로 하기 위해서 아래와 같이
one hot encoding방식을 이용하게 된다.

이렇게해서 hypothesis는 완성이 되었다.

Cost Function 생성
예측값과 실제값 간의 차이를 나타내는 함수를 이제 설계 한다.
이것을 최소화 함으로써 학습을 완성하게 된다.

아래의 그림과 같이 예측값 $\hat{y}$과 실제값 $L=y$에대한 모습을 나타낸다.
그리고 이것의 차이를 나타내는 cost function을 cross entropy로 설정하게 된다.
즉 이전에 logistic regression의 cost function과 같은 형태인 것이다.

로그를 한것과 element wise multiplicatoin을 수행하는 형태이다.

수식으로 좀 더 자세히 표현하면 아래와 같다.

$$-\sum_{i}{L_{i}\log(S_{i})} = -\sum_{i}{L_{i} \log{(\hat{y}_{i})}}$$

여기서 곱은 내적 즉 element wise multiplication 이다.

결국 이것은 Logistic regression에서 다루었던 cost function
$$ C(H(x),y) = -y\log{(H(x))}+(1-y)\log{(1-H(x))}$$
과 같은 형태를 가진다.

각각을 예제에 따라서 계산해보면 아래와 같이 일치하면 cost가 0이고
불일치 하면 cost가 무한대로 계산 되는 것을 알 수 있다.

그리고 이것을 일반화 하면 아래와 같이 나오고 똑같이 미분해서 gradient descent알고리즘을 수행 시키면 된다.

Multinomial Classification 구현

구현 코드

import tensorflow as tf
import numpy as np

xy = np.loadtxt('softmaxTrain.txt', unpack=True, dtype='float')

x_data = np.transpose(xy[0:3])
y_data = np.transpose(xy[3:])
# tensorflow graph input
X = tf.placeholder('float', [None, 3]) # x1, x2, and 1 (for bias)
Y = tf.placeholder('float', [None, 3]) # A, B, C = > three classes
# set model weights
W = tf.Variable(tf.random_uniform([3,3],-1.0, 1.0))

# Our hypothesis
hypothesis = tf.nn.softmax(tf.matmul(X, W)) # softmax

# Cost function: cross entropy
cost = tf.reduce_mean(-tf.reduce_sum(Y*tf.log(hypothesis), reduction_indices=1))

# Minimize
a = tf.Variable(0.2) # Learning rate, alpha
optimizer = tf.train.GradientDescentOptimizer(a)
train = optimizer.minimize(cost)

# Before starting, initialize the variables. We will `run` this first.
init = tf.initialize_all_variables()

# Launch the graph,
with tf.Session() as sess:
    sess.run(init)

    # Fit the line.
    for step in xrange(10000):
        sess.run(train, feed_dict={X:x_data, Y:y_data})
        if step % 200 == 0:
            print step, sess.run(cost, feed_dict={X:x_data, Y:y_data}), sess.run(W)

    # Test model
    correct_prediction = tf.equal(tf.floor(hypothesis+0.5), Y)
    # Calculate accuracy (re-substitution error)
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    print sess.run([hypothesis, tf.floor(hypothesis+0.5), correct_prediction, accuracy], feed_dict={X:x_data, Y:y_data})
    print "Accuracy:", accuracy.eval({X:x_data, Y:y_data})

    # Test & one-hot encoding
    a = sess.run(hypothesis, feed_dict={X:[[1, 11, 7]]})
    print a, sess.run(tf.arg_max(a,1))

    b = sess.run(hypothesis, feed_dict={X: [[1, 3, 4]]})
    print b, sess.run(tf.arg_max(b, 1))

    c = sess.run(hypothesis, feed_dict={X: [[1, 1, 0]]})
    print b, sess.run(tf.arg_max(c, 1))

    all = sess.run(hypothesis, feed_dict={X:[[1, 11, 7], [1, 3, 4], [1, 1, 0]]})
    print all, sess.run(tf.arg_max(all, 1))

재치환 에러 (re-substitution)은 0이다. 결국 정확도는 1이 나온다.

두 번째는 샘플을 주고 각각의 argmax를 실행하는 것이다.
참값은 모르기 때문에 그냥 생성된 모델이 저렇게 예측한다는 것을 보기 위함이다.

실행결과

9800 0.0927773 [[-21.29355621   2.04889441  21.23555946]
 [  0.84294367   0.75854331   0.5262934 ]
 [  4.56429529   0.49110752  -3.74019885]]

[array([[  1.98164840e-15,   4.48229486e-07,   9.99999523e-01],
       [  1.17479602e-11,   4.03313388e-05,   9.99959707e-01],
       [  1.81849638e-04,   1.70287594e-01,   8.29530597e-01],
       [  6.83906451e-02,   8.93268943e-01,   3.83404866e-02],
       [  8.11137408e-02,   8.94745708e-01,   2.41405368e-02],
       [  5.18718772e-02,   8.72947454e-01,   7.51806125e-02],
       [  8.34491730e-01,   1.65429384e-01,   7.89094338e-05],
       [  9.97000158e-01,   2.99978442e-03,   1.59018363e-08]], dtype=float32), array([[ 0.,  0.,  1.],
       [ 0.,  0.,  1.],
       [ 0.,  0.,  1.],
       [ 0.,  1.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  1.,  0.],
       [ 1.,  0.,  0.],
       [ 1.,  0.,  0.]], dtype=float32), array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]], dtype=bool), 1.0]

재치환 에러는 0이고

Accuracy: 1.0
[[  9.97858584e-01   2.14142259e-03   4.48543203e-09]] [0]
[[  1.81849638e-04   1.70287609e-01   8.29530597e-01]] [2]
[[  1.81849638e-04   1.70287609e-01   8.29530597e-01]] [2]
[[  9.97858584e-01   2.14142259e-03   4.48543203e-09]
 [  1.81849638e-04   1.70287594e-01   8.29530597e-01]
 [  3.34250399e-19   4.98129182e-09   1.00000000e+00]] [0 2 2]

각각에 대한 출력값은 0,2,2 이다.
즉 학점으로 보면 A,C,C 인것이다.

출처

모두를 위한 머신러닝/딥러닝 강의, 김성훈 교수 (홍콩과기대)
http://hunkim.github.io/ml/


+ Recent posts