딥러닝 모델을 훈련할 때 사용하는 GPU 장치가 무엇인지, GPU Driver version, CUDA version 등의 정보를 확인하고 싶을 때, 또 모델 훈련 중에 GPU의 총 메모리 중에서 현재 활용되고 있는 메모리는 어느정도 인지, 총 사용 가능한 전력 사용량 중에서 현재 사용하고 있는 전력량은 어느정도 인지 등을 모니터링 하고 싶을 때 nvidia-smi 유틸리티를 사용합니다. 

 

NVIDIA 시스템 관리 인터페이스(nvidia-smi, NVIDIA System Management Interface)는 NVIDIA GPU 장치의 관리 및 모니터링을 지원하기 위한 NVML (NVIDIA Management Library)을 기반으로 하는 command line utility입니다.

 

nvidia-smi, NVIDIA System Management Interface

 


이 유틸리티를 사용하면 관리자가 GPU 장치 상태를 쿼리할 수 있으며 적절한 권한으로 관리자가 GPU 장치 상태를 수정할 수 있습니다. TeslaTM, GRIDTM, QuadroTM 및 Titan X 제품을 대상으로 하지만 다른 NVIDIA GPU에서도 제한된 지원이 가능합니다.

NVIDIA-smi는 Linux의 NVIDIA GPU 디스플레이 드라이버와 64비트 Windows Server 2008 R2 및 Windows 7과 함께 제공됩니다. Nvidia-smi는 쿼리 정보를 XML 또는 사람이 읽을 수 있는 일반 텍스트로 표준 출력이나 파일에 보고할 수 있습니다. 

 

 

command line 창에서  $ nvidia-smi -q   (또는  $ nvidia-smi -query ) 명령어를 실행하면 아래와 같이 NVIDIA GUP 장치에 대한 정보와 현재 사용 중인 GPU 상태에 대한 모니터링 정보를 한꺼번에 알 수 있습니다. 

(Jupyter Notebook cell에서는 느낌표를 먼저 써주고,   ! nvidia-smi -q  를 입력해주면 됩니다.)

 

(Jupyter Notebook 에서 실행 시)
! nvidia-smi -q

(출력)

==============NVSMI LOG==============

Timestamp                                 : Sun Jan  9 12:55:56 2022
Driver Version                            : 460.32.03
CUDA Version                              : 11.2

Attached GPUs                             : 1
GPU 00000000:00:04.0
    Product Name                          : Tesla K80
    Product Brand                         : Tesla
    Product Architecture                  : Kepler
    Display Mode                          : Disabled
    Display Active                        : Disabled
    Persistence Mode                      : Disabled
    MIG Mode
        Current                           : N/A
        Pending                           : N/A
    Accounting Mode                       : Disabled
    Accounting Mode Buffer Size           : 4000
    Driver Model
        Current                           : N/A
        Pending                           : N/A
    Serial Number                         : 0320617086266
    GPU UUID                              : GPU-ed69a555-6186-feff-25f8-615b2ac9859f
    Minor Number                          : 0
    VBIOS Version                         : 80.21.25.00.02
    MultiGPU Board                        : No
    Board ID                              : 0x4
    GPU Part Number                       : 900-22080-6300-001
    Module ID                             : Uninitialized
    Inforom Version
        Image Version                     : 2080.0200.00.04
        OEM Object                        : 1.1
        ECC Object                        : 3.0
        Power Management Object           : N/A
    GPU Operation Mode
        Current                           : N/A
        Pending                           : N/A
    GSP Firmware Version                  : Uninitialized
    GPU Virtualization Mode
        Virtualization Mode               : Pass-Through
        Host VGPU Mode                    : N/A
    IBMNPU
        Relaxed Ordering Mode             : N/A
    PCI
        Bus                               : 0x00
        Device                            : 0x04
        Domain                            : 0x0000
        Device Id                         : 0x102D10DE
        Bus Id                            : 00000000:00:04.0
        Sub System Id                     : 0x106C10DE
        GPU Link Info
            PCIe Generation
                Max                       : 3
                Current                   : 1
            Link Width
                Max                       : 16x
                Current                   : 16x
        Bridge Chip
            Type                          : N/A
            Firmware                      : N/A
        Replays Since Reset               : 0
        Replay Number Rollovers           : 0
        Tx Throughput                     : N/A
        Rx Throughput                     : N/A
    Fan Speed                             : N/A
    Performance State                     : P8
    Clocks Throttle Reasons
        Idle                              : Active
        Applications Clocks Setting       : Not Active
        SW Power Cap                      : Not Active
        HW Slowdown                       : Not Active
            HW Thermal Slowdown           : N/A
            HW Power Brake Slowdown       : N/A
        Sync Boost                        : Not Active
        SW Thermal Slowdown               : Not Active
        Display Clock Setting             : Not Active
    FB Memory Usage
        Total                             : 11441 MiB
        Used                              : 0 MiB
        Free                              : 11441 MiB
    BAR1 Memory Usage
        Total                             : 16384 MiB
        Used                              : 2 MiB
        Free                              : 16382 MiB
    Compute Mode                          : Default
    Utilization
        Gpu                               : 0 %
        Memory                            : 0 %
        Encoder                           : 0 %
        Decoder                           : 0 %
    Encoder Stats
        Active Sessions                   : 0
        Average FPS                       : 0
        Average Latency                   : 0
    FBC Stats
        Active Sessions                   : 0
        Average FPS                       : 0
        Average Latency                   : 0
    Ecc Mode
        Current                           : Enabled
        Pending                           : Enabled
    ECC Errors
        Volatile
            Single Bit            
                Device Memory             : 0
                Register File             : 0
                L1 Cache                  : 0
                L2 Cache                  : 0
                Texture Memory            : 0
                Texture Shared            : N/A
                CBU                       : N/A
                Total                     : 0
            Double Bit            
                Device Memory             : 0
                Register File             : 0
                L1 Cache                  : 0
                L2 Cache                  : 0
                Texture Memory            : 0
                Texture Shared            : N/A
                CBU                       : N/A
                Total                     : 0
        Aggregate
            Single Bit            
                Device Memory             : 2
                Register File             : 0
                L1 Cache                  : 0
                L2 Cache                  : 0
                Texture Memory            : 0
                Texture Shared            : N/A
                CBU                       : N/A
                Total                     : 2
            Double Bit            
                Device Memory             : 4
                Register File             : 0
                L1 Cache                  : 0
                L2 Cache                  : 0
                Texture Memory            : 0
                Texture Shared            : N/A
                CBU                       : N/A
                Total                     : 4
    Retired Pages
        Single Bit ECC                    : 0
        Double Bit ECC                    : 2
        Pending Page Blacklist            : No
    Remapped Rows                         : N/A
    Temperature
        GPU Current Temp                  : 52 C
        GPU Shutdown Temp                 : 93 C
        GPU Slowdown Temp                 : 88 C
        GPU Max Operating Temp            : N/A
        GPU Target Temperature            : N/A
        Memory Current Temp               : N/A
        Memory Max Operating Temp         : N/A
    Power Readings
        Power Management                  : Supported
        Power Draw                        : 31.51 W
        Power Limit                       : 149.00 W
        Default Power Limit               : 149.00 W
        Enforced Power Limit              : 149.00 W
        Min Power Limit                   : 100.00 W
        Max Power Limit                   : 175.00 W
    Clocks
        Graphics                          : 324 MHz
        SM                                : 324 MHz
        Memory                            : 324 MHz
        Video                             : 405 MHz
    Applications Clocks
        Graphics                          : 562 MHz
        Memory                            : 2505 MHz
    Default Applications Clocks
        Graphics                          : 562 MHz
        Memory                            : 2505 MHz
    Max Clocks
        Graphics                          : 875 MHz
        SM                                : 875 MHz
        Memory                            : 2505 MHz
        Video                             : 540 MHz
    Max Customer Boost Clocks
        Graphics                          : N/A
    Clock Policy
        Auto Boost                        : On
        Auto Boost Default                : On
    Voltage
        Graphics                          : Uninitialized
    Processes                             : None

 

 

 

위의 실행 결과에 NVIDIA GPU 의 장치 및 실행 모니터링의 모든 정보가 출력이 되다보니 양이 너무나 많습니다. 만약 간단하게 시스템에 연결된 NVIDIA GPU 의 장치 리스트의 이름 (Product Name)과 UUID 정보만 알고 싶으면 command line 에서  $ nvidia-smi -L  (혹은  $ nvidia-smi --list-gups ) 를 실행시켜주면 됩니다. 

 

(Jupyter Notebook에서 실행 시)
! nvidia-smi -L

(출력)

GPU 0: Tesla K80 (UUID: GPU-ed69a555-6186-feff-25f8-615b2ac9859f)

 

 

만약 시스템에 연결된 NVIDIA GPU 의 메모리의 상태에 대해서 알고 싶으면 command line 창에서  $ nvidia-smi -q -d memory  를 입력해주면 됩니다. 

 

(Jupyter Notebook에서 실행 시)
! nvidia-smi -q -d memory

(출력)

[Out]
==============NVSMI LOG==============

Timestamp                                 : Sun Jan  9 12:59:09 2022
Driver Version                            : 460.32.03
CUDA Version                              : 11.2

Attached GPUs                             : 1
GPU 00000000:00:04.0
    FB Memory Usage
        Total                             : 11441 MiB
        Used                              : 0 MiB
        Free                              : 11441 MiB
    BAR1 Memory Usage
        Total                             : 16384 MiB
        Used                              : 2 MiB
        Free                              : 16382 MiB

 

 

 

[ Reference ]

(1) NVIDIA System Management Interface (NVIDIA-smi)
    : https://developer.nvidia.com/nvidia-system-management-interface

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 TensorFlow Keras의 손실함수 중에서 다중분류 문제(multiclass classification problem) 에 대한 딥러닝 모델을 훈련할 때 사용하는 손실함수에 대해서 소개하겠습니다. 

 

(1) 손실함수란 무엇인가? 

(2) 문제 유형별 Keras의 손실함수는 무엇이 있나? 

(3) 교차 엔트로피(Cross Entropy) 란 무엇인가?

(4) sparse_categorical_crossentropy() vs. categorical_crossentropy() 비교

 

 

 

(1) 손실함수란 무엇인가? 

 

 기계학습(Machine Learning), 딥러닝(Deep Learning) 은 손실함수(Loss Function)을 통해서 학습합니다. 손실함수는 다른 이름으로 비용함수(Cost Function)라고도 합니다. 

 

 손실함수는 특정 알고리즘이 주어진 데이터를 얼마나 잘 모델링하는지를 측정하는 방법입니다. 만약 학습모델을 사용해 예측한 값(predicted value)이 참 값(true value, actual value)과 차이가 많이 날 경우 손실함수의 값은 매우 커지게 되며, 손실함수 값이 작을 수록 알고리즘이 주어진 데이터를 잘 모델링했다고 평가할 수 있습니다. 

 

 기계학습, 딥러닝에서는 손실함수의 손실값(loss value)을 점점 작게 해서 최소의 손실 값을 찾아가는 최적화 함수(optimization function)을 사용해서 데이터로 부터 모델을 학습하게 됩니다. 최적화 함수에 대해서는 별도의 포스팅을 통해서 나중에 소개하도록 하겠습니다. 

 

 

 

(2) 문제 유형별 Keras의 손실함수는 무엇이 있나? 

 

 모든 문제에 공통으로 적용할 수 있는 만능의 손실함수는 없습니다. 각 문제 유형(예: 회귀, 분류)과 데이터 형태(y label 이 정수, onehot encoded), 미분 계산의 용이성, 이상치(outliers)의 포함 정도 등에 따라서 사용하는 손실함수는 달라집니다. 

 아래의 표는 TensorFlow Keras의 문제 유형(problem types) 별로 마지막층의 활성화 함수(last layer's activation function)와 손실함수/클래스를 표로 정리한 것입니다.

 

이번 포스팅에서 다루고자 하는 TensorFlow Keras의  sparse_categorical_crossentropy()와 categorical_crossentropy() 손실함수는 다중분류 문제(multiclass classification problem)에 사용하는 손실함수입니다. 

 

 

[ TensorFlow Keras의 손실 함수, 손실 클래스 (Loss Functions, Loss Classes) ]

TensorFlow Keras Loss Functions, Loss Classes

 

 

 

(3) 교차 엔트로피(Cross Entropy) 란 무엇인가?

 

교차 엔트로피 개념은 정보이론(information theroy)에서 나왔습니다. 교차 엔트로피(Cross Entropy)는 동일한 근간의 사건의 집합(over the same underlying events set)에서 뽑은 두 개의 확률 분포 p와 q에서 만약 집합에 사용된 코딩 체계가 실제 확률분포 p보다 추정 확률 분포 q에 최적화되어 있는 경우 집합으로 부터 뽑힌 사건을 식별하는데 필요한 평균 비트 수를 측정합니다.

 

위의 정의는 위키피디아(https://en.wikipedia.org/wiki/Cross_entropy) 에서 인용한 것인데요, 무슨 말인지 잘 이해하기 힘들지요? 아래의 유튜브 링크는 "Hands-on Machine Learning with Scikitlearn, Keras & TensorFlow" 책의 저자인 Aurelien Geron 님께서 "Entropy, Cross Entropy, KL Divergence"에 대해서 소개해주고 있는 영상인데요, 이걸 참고하시면 이해하는데 훨씬 쉬울거예요. 

 

 

이 정의는 다시 확률분포 q로 부터의 p의 차이인 Kullback-Leibler Divergence  Dkl(p||q) 로 공식화(be formulated)될 수 있습니다. 이산형 확률분포와 연속형 확률분포일 경우별로 교차엔트로피를 구하는 수식은 아래의 내용을 참고하세요. 

 

 

교차 엔트로피는 기계학습과 최적화에서 손실 함수를 정의하는 데 사용될 수 있습니다. 참 확률 p(i)는 정답 레이블이고, 주어진 분포 q(i)는 현재 모델의 예측 값입니다. 언어 모델을 예로 들면, 훈련 데이터셋을 기반으로 언어모델을 생성한 다음, 교차 엔트로피를 테스트 세트에서 측정하여 모델이 테스트 데이터를 예측하는 데 얼마나 정확한지 평가합니다. 이 언어모델 예에서 p는 모든 말뭉치에서 단어의 실제 분포이고, q는 모델에 의해 예측된 단어의 분포입니다. 

 

y(i) 를 실제의 참 값(actual true value), y_hat(i)를 모델이 예측한 값(predicted value)라고 했을 때, 분류 문제의 교차 엔트로피는 아래와 같은 수식으로 표현할 수 있습니다. (이진분류의 경우 y(i)가 0 또는 1 의 값을 가짐). 아래 수식을 보면 알 수 있듯이, 이진분류문제의 교차 엔트로피 손실함수는 실제 값이 0이면 아래 수식의 왼쪽 부분이 없어지고, 실제 값이 1이면 아래 수식의 오른쪽이 없어집니다. 따라서 만약 이진분류 모델이 실제 값을 틀리게 예측하고 또 예측확률값이 높을 수록 교차 엔트로피 손실함수 값이 더 커지게끔 설계되어 있습니다. (틀렸으면 벌을 주는데, 더 확신을 가지고 틀렸으면 더 큰 벌을 주는 개념). 

Cross Entropy Loss for Classification Problem

 

 

 

(4) sparse_categorical_crossentropy() vs. categorical_crossentropy() 비교

 

(4-1) 다중분류 문제(multiclass classification problem) 에 사용하는 함수 중에서 y label 의 형태에 따라서, 

 - tf.keras.losses.sparse_categorical_crossentropy(): y label 이 정수 (integer) 

 - tf.keras.losses.categorical_crossentropy(): y label 이 one-hot encoded 

를 선택해서 사용하면 됩니다.

아래의 화면캡쳐한 코드 예시를 보면 y label의 형태가 어떻게 다른지 금방 알 수 있을 거예요. 

 

(4-2) 또 하나 차이점이 있다면, 문제의 유형(problem types)에 따라서, 

 - tf.keras.losses.sparse_categorical_crossentropy(): multiclass single-label classification 만 가능

 - tf.keras.losses.categorical_crossentropy(): multiclass single-label classification, multiclass multilabel classification 둘다 사용 가능

하다는 차이점이 있습니다.  

multiclass single-label classification 문제는 다중 클래스 중에서 관측치는 단 한개의 클래스에만 속하는 문제(예: MNIST의 이미지 숫자 분류)를 말하며, multiclass multilabel classification 문제는 관측치가 여러개의 클래스에 속할 수 있는 문제(예: 음악 장류 분류, 영화 장르 분류 등)를 말합니다. 

 

Keras: sparse_categorical_crossentropy vs. categorical crossentropy

 

 

 

[ Reference ]

* TensorFlow 손실함수 (Loss Functions/ Classes)
   : https://www.tensorflow.org/api_docs/python/tf/keras/losses

* Keras 손실함수 (Loss Functions/ Classes): https://keras.io/api/losses/

* Cross Entropy: https://en.wikipedia.org/wiki/Cross_entropy

* Keras sparse_categorical_crossentropy()
   : https://www.tensorflow.org/api_docs/python/tf/keras/metrics/sparse_categorical_crossentropy

* Keras categorical_crossentropy()
   : https://www.tensorflow.org/api_docs/python/tf/keras/metrics/categorical_crossentropy

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

데이터 전처리를 하다보면 필요에 맞게 데이터 형태, 구조를 변경하는 일이 빈번하게 생깁니다. 이번 포스팅에서는 텐서의 형태를 변환, 재구조화, 전치하는 방법을 소개하겠습니다. 

이미 numpy (https://rfriend.tistory.com/345 , https://rfriend.tistory.com/289)에 익숙한 분이라면, 사용법이 비슷하기 때문에 그리 어렵지 않게 금방 이해할 수 있을 거예요. 

 

(1) tf.reshape(): 텐서의 형태 변환 

(2) tf.transpose(): 텐서의 행과 열 전치

 

 

TensorFlow reshape, transpose

 

 

(1) tf.reshape(): 텐서의 형태 변환

 

먼저, 0~11까지 12개의 원소를 가지는 벡터(vector) 로 텐서를 만들어보겠습니다. 

 

t1 = tf.constant(range(12))

print(t1)
# tf.Tensor([ 0  1  2  3  4  5  6  7  8  9 10 11], 
#.          shape=(12,), dtype=int32)


tf.rank(t1) # rank 1
# <tf.Tensor: shape=(), dtype=int32, numpy=1>

 

 

 

위의 벡터 (12,) 형태를 가지는 텐서 t1을, 행렬 (3, 4) 형태를 가지는 텐서로 tf.reshape(tensor, shape, name=None) 메소드를 사용해서 변환(reshape)해보겠습니다. 

 

## reshaping a shape of Tensor from (12,) to (3, 4)
t2 = tf.reshape(t1, [3, 4]) 

print(t2)
# tf.Tensor(
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]], shape=(3, 4), dtype=int32)


tf.rank(t2) # rank 2
# <tf.Tensor: shape=(), dtype=int32, numpy=2>

 

 

 

tf.reshape(tensor, shape) 의 shape 란에 원하는 형태를 [ ] 안에 콤마로 구분해서 넣어주면 됩니다. 이번에는 rank 3 을 가지는 (2, 2, 3) 형태의 텐서로 형태를 변환해보겠습니다. 

 

## reshaping a Tensor with a shape of (12,) to (2,2,3)
t3 = tf.reshape(t1, [2, 2, 3])

print(t3)
# tf.Tensor(
# [[[ 0  1  2]
#   [ 3  4  5]]
#  [[ 6  7  8]
#   [ 9 10 11]]], shape=(2, 2, 3), dtype=int32)


tf.rank(t3) # rank 3
# <tf.Tensor: shape=(), dtype=int32, numpy=3>

 

 

tf.reshape(tensor, shape) 의 shape 에 '-1' 이 들어있으면, 숫자가 기입된 부분의 축을 변환하고 난 후에, "남은 축의 형태는 원래 텐서의 총 크기와 같도록 알아서 추정"해준다는 뜻입니다. 

 

아래 예에서는 원래 텐서 t1 이 (12,) 형태였으며, tf.reshape(t1, [-1, 3]) 으로 재구조화시켰더니 (4, 3) 형태의 텐서로 변환되었네요. 

 

## If one component of shape is the special value -1, 
## the size of that dimension is computed 
## so that the total size remains constant.
t4 = tf.reshape(t1, [-1, 3])


print(t4)
# tf.Tensor(
# [[ 0  1  2]
#  [ 3  4  5]
#  [ 6  7  8]
#  [ 9 10 11]], shape=(4, 3), dtype=int32)


tf.rank(t4) # rank 2
# <tf.Tensor: shape=(), dtype=int32, numpy=2>

 

 

특히, tf.reshape(tensor, [-1]) 처럼 shape 에 [-1] 은 1차원의 벡터인 1-D 텐서로 변환을 한다는 뜻입니다. 

아래 예에서 텐서 t4 는 (4, 3) 형태를 가지는 행렬이었는데요, tf.reshape(t4, [-1]) 해주었더니 (12,) 의 형태를 가지는 1차원의 벡터로 변환이 되었습니다. 

 

## In particular, a shape of [-1] flattens into 1-D. 
## At most one component of shape can be -1.
t5 = tf.reshape(t4, [-1])

print(t5)
# tf.Tensor([ 0  1  2  3  4  5  6  7  8  9 10 11], 
#           shape=(12,), dtype=int32)


tf.rank(t5) # rank 1
# <tf.Tensor: shape=(), dtype=int32, numpy=1>

 

 

 

tf.reshape(tensor, []) 의 [ ] 는 텐서를 1개의 원소를 가지는 스칼라(scalar) 로 변환합니다. 

 

## tf.reshape(t, []) reshapes a tensor t with one element to a scalar.
t6 = tf.constant([5])

print(t6) # rank 1, vector
# tf.Tensor([5], shape=(1,), dtype=int32)


t7 = tf.reshape(t6, [])
print(t7) # rank 0, scalar
# tf.Tensor(5, shape=(), dtype=int32)


tf.rank(t7) # rank 0
# <tf.Tensor: shape=(), dtype=int32, numpy=0>

 

 

 

(2) tf.transpose(): 텐서의 행과 열 전치

 

tf.transpose(tensor)는 텐서의 행과 열의 위치를 전치(transpose) 시켜 줍니다. 

아래의 예에서는 shape=(3,4) 의 텐서를 shape=(4,3)의 텐서로 행과 열을 전치시켜주었습니다. 행과 열이 전치를 시키기 전과 후에 어떻게 바뀌었는지 살펴보면 금방 이해하실 수 있을 거예요. 

 

print(t2)
# tf.Tensor(
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]], shape=(3, 4), dtype=int32)


t8 = tf.transpose(t2)

print(t8)
# tf.Tensor(
# [[ 0  4  8]
#  [ 1  5  9]
#  [ 2  6 10]
#  [ 3  7 11]], shape=(4, 3), dtype=int32)

 

[ Reference ]

- tf.reshape(): https://www.tensorflow.org/api_docs/python/tf/reshape

- tf.transpose(): https://www.tensorflow.org/api_docs/python/tf/transpose

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

딥러닝 모델을 훈련하고 예측하는데 사용하는 TensorFlow 프레임웍의 이름은 Tensor + Flow (텐서를 흘려보냄) 에서 유래했습니다. 텐서(Tensor)는 텐서플로의 가장 기본이 되는 자료 구조인 만큼 만드는 방법과 속성정보 조회하는 방법을 알아둘 필요가 있겠습니다. 

 

이번 포스팅에서는 

 

(1) 텐서의 정의

(2) 텐서 만들기

     - (2-1) tf.constant([mat1, mat2, mat3])

     - (2-2) tf.stack([mat1, mat2, mat3])

     - (2-3) tf.convert_to_tensor([mat1, mat2, mat3])

(3) 텐서의 속성정보 조회하기

     - (3-1) Tensor.dtype : 텐서 데이터 유형

     - (3-2) Tensor.shape : 텐서 형태

     - (3-3) Tensor.rank : 텐서 차수(rank), 텐서를 구성하는 벡터의 개수, 랭크

     - (3-4) Tensor.device : 텐서 생성 디바이스 (CPU, GPU)

 

하는 방법에 대해서 소개하겠습니다. 

 

 

 

(1) 텐서의 정의

 

 TensorFlow의 자료 구조에는 0차원의 스칼라(scalar), 1차원의 벡터(vector), 2차원의 행렬(matrix), 3차원 이상의 텐서(tensor) 가 있습니다. 텐서플로에서 텐서는 벡터와 행렬, 그리고 3차원 이상의 고차원의 배열을 모두 아우르는 일반화된 데이터 구조를 말합니다.

 텐서플로는 파이썬 객체를 텐서(Tensor)로 변환해서 행렬 연산을 수행합니다. 텐서플로는 먼저 tf.Tensor 객체의 그래프(graph) 를 생성하는 것에서 시작하여, 각 텐서가 어떻게 계산되는지를 구체화하고, 이 그래프의 부분들을 실행하여 원하는 최종 결과를 도출합니다. 

 텐서는 데이터 유형(a data type, 예: float32, int32, string) 과 데이터 형태 (shape) 의 속성을 가집니다. 

 

TensorFlow data types: scalar, vector, matrix, tensor

 

 

(2) 텐서 만들기

 

그럼, (2, 3) 의 shape 을 가지는 3개의 배열을 가지고, 3차원인 (3, 2, 3) 형태의 텐서를 만들어보겠습니다. 

 

     - (2-1) tf.constant([mat1, mat2, mat3])

     - (2-2) tf.stack([mat1, mat2, mat3])

     - (2-3) tf.convert_to_tensor([mat1, mat2, mat3])

 

import tensorflow as tf
tf.__version__
#2.7.0


## input matrix (list of list)
mat1 = [[1, 2, 3], [4, 5,  6]]
mat2 = [[7, 8, 9], [10, 11, 12]]
mat3 = [[13, 14, 15], [16, 17, 18]]

 

 

(2-1) tf.constant([mat1, mat2, mat3])

 

## A tf.Tensor represents a multidimensional array of elements.

## (1) tf.constant(): Creates a constant tensor from a tensor-like object.
tensor1 = tf.constant([mat1, mat2, mat3])

tensor1
# <tf.Tensor: shape=(3, 2, 3), dtype=int32, numpy=
# array([[[ 1,  2,  3],
#         [ 4,  5,  6]],

#        [[ 7,  8,  9],
#         [10, 11, 12]],

#        [[13, 14, 15],
#         [16, 17, 18]]], dtype=int32)>

 

 

 

(2-2) tf.stack([mat1, mat2, mat3])

 

## tf.stack(): Stacks a list of rank-R tensors into one rank-(R+1) tensor.
tensor2 = tf.stack([mat1, mat2, mat3])

 

 

 

(2-3) tf.convert_to_tensor([mat1, mat2, mat3])

 

텐서를 만들 때 데이터 유형(dtype), 텐서 이름(name)의 속성정보를 설정할 수 있습니다. 

 

## tf.convert_to_tensor(): Converts the given value to a Tensor.
tensor3 = tf.convert_to_tensor([mat1, mat2, mat3])

# tensor3 = tf.convert_to_tensor([mat1, mat2, mat3], 
#                                dtype=tf.int64, 
#                                name='my_first_tensor')

 

 

 

(3) 텐서의 속성정보 조회하기

 

     - (3-1) Tensor.dtype : 텐서 데이터 유형

     - (3-2) Tensor.shape : 텐서 형태

     - (3-3) Tensor.rank : 텐서 차수(rank), 텐서를 구성하는 벡터의 개수, 랭크

     - (3-4) Tensor.device : 텐서 생성 디바이스 (CPU, GPU)

 

# A tf.Tensor has the following properties:
# - a single data type (float32, int32, or string, for example)
# - a shape

## The DType of elements in this tensor.
tensor3.dtype
# tf.int64


## Returns a tf.TensorShape that represents the shape of this tensor.
tensor3.shape
# TensorShape([3, 2, 3])


## rank of this tensor.
tf.rank(tensor3)
# <tf.Tensor: shape=(), dtype=int32, numpy=3>


## The name of the device on which this tensor will be produced, or None.
tensor3.device
# /job:localhost/replica:0/task:0/device:GPU:0

 

 

[ Referece ]

- tf.Tensor(): https://www.tensorflow.org/api_docs/python/tf/Tensor

- tf.constant(): https://www.tensorflow.org/api_docs/python/tf/constant

- tf.stack(): https://www.tensorflow.org/api_docs/python/tf/stack

- tf.convert_to_tensor(): https://www.tensorflow.org/api_docs/python/tf/convert_to_tensor

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python Tensorflow와 numpy 모듈을 사용하여 

  (1) 행렬 원소 간 곱 (matrix element-wise product)

  (2) 행렬 곱 (matrix multiplication) 

하는 방법과 연산 방법을 소개하겠습니다. 

얼핏보면 둘이 비슷해보이지만, 전혀 다른 연산으로서 주의가 필요합니다. 

 

 

tensorflow, numpy, element-wise product, matrix multiplication

 

 

 

먼저, TensorFlow와 numpy  모듈을 불러오고, 예제로 사용할 2차원 행렬(matrix)을 만들어보겠습니다. 

 

import numpy as np
import tensorflow as tf
print(tf.__version__)
#2.7.0


## matrix
x = tf.constant([[1, 2], [3, 4]])

print(x)
# tf.Tensor(
# [[1 2]
#  [3 4]], shape=(2, 2), dtype=int32)


y = tf.constant([[0, 10], [20, 30]])

print(y)
# tf.Tensor(
# [[ 0 10]
#  [20 30]], shape=(2, 2), dtype=int32)

 

 

 

(1) 행렬 원소 간 곱 (matrix element-wise product)

 

## [TensorFlow] matrix element-wise product
tf.math.multiply(x, y)

# <tf.Tensor: shape=(2, 2), dtype=int32, numpy=
# array([[  0,  20],
#        [ 60, 120]], dtype=int32)>

 

## [numpy] matrix element-wise produce
np.array(x) * np.array(y)

# array([[  0,  20],
#        [ 60, 120]], dtype=int32)

 

 

 

(2) 행렬 곱 (matrix multiplication) 

 

## matrix multiplication using tf.matmul()
tf.matmul(x, y)

# <tf.Tensor: shape=(2, 2), dtype=int32, numpy=
# array([[ 40,  70],
#        [ 80, 150]], dtype=int32)>

 

## [numpy] matrix multiplication using np.dot()
np.dot(x, y)

# array([[ 40,  70],
#        [ 80, 150]], dtype=int32)



## [numpy] matrix multiplication using np.matmul()
np.matmul(x, y)

# array([[ 40,  70],
#        [ 80, 150]], dtype=int32)

 

 

 

참고로, TensorFlow 의 텐서를 numpy의 array 로 변환하려면 numpy() 메소드를 사용하면 됩니다. 

 

## casting tensor to numpy's array
tf.matmul(x, y).numpy()

# array([[ 40,  70],
#        [ 80, 150]], dtype=int32)

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

자동미분(Automatic differentiation, Algorithmic differentiation, computational differentiation, auto-differentiation, 또는 줄여서 간단히 auto-diff) 은 컴퓨터 프로그램에 의해서 구체화된 함수의 미분을 산술적으로 계산할 때 사용하는 기술의 집합을 말합니다. 컴퓨터 프로그램에서 구체화한 함수는 아무리 복잡해보이더라도 기본적인 산술 연산 (덧셈, 뺄셈, 곱셉, 나눗셈 등)과 기본적인 함수 (지수, 로그, 싸인, 코싸인 등) 의 연속적인 실행으로 이루어진다는 아이디어를 기반으로 합니다. 복잡한 함수도 연쇄 법칙(Chain Rule)을 이용함으로써 합성함수를 구성하는 각 기본함의 미분의 곱으로 나타내고 반복적으로 계산함으로써 자동으로 복잡한 함수를 정확하고 효율적으로 미분할 수 있습니다.   


자동미분(Automatic Differentiation)은 딥러닝에서 오차 역전파 알고리즘을 사용해서 모델을 학습할 때 유용하게 사용합니다. TensorFlow는 Gradient Tapes 를 이용하면 즉시 실행 모드(eager execution mode)에서 쉽게 오차 역전파를 수행할 수 있습니다. 


이번 포스팅에서는 


1. 즉시실행모드에서 자동미분을 하기 위해 Gradient tapes 이 필요한 이유

2. TensorFlow에서 Gradient tapes를 이용해 자동미분하는 방법

3. 시그모이드 함수 자동미분 시각화

4. 테이프가 볼 것을 조정하는 방법 (Controlling what the tape watches)

5. Python 조건절 분기문을 이용한 테이프 기록 흐름 조정 (Control flow)


에 대해서 소개하겠습니다. 


이번 포스팅은 TensorFlow 튜토리얼의 내용을 번역하고 코드를 대부분 사용하였습니다. 


먼저 numpy, tensorflow, matplotlib 모듈을 importing 하겠습니다. 



import numpy as np

import matplotlib.pyplot as plt

import tensorflow as tf


print(tf.__version__)

2.4.0-dev20200913




  1. 즉시 실행 모드에서 자동미분을 하기 위해 Gradient tapes 이 필요한 이유


예를 들어서  y = a * x 라는 방정식에서 a 와 y 가 상수(constant)이고 x 가 변수(Varialbe) 일 때, 이 방정식을 오차역전파법으로 풀어서 변수 x 를 구하고자 한다고 합시다. 그러면 간단한 손실함수인 loss = abs(a * x - y) 를 최소로 하는 x 를 구하면 됩니다. 


아래 예의 경우 8.0 = 2.0 * x 방정식 함수로 부터 변수 x 의 값을 구하고자 합니다. x 를 10.0 에서 시작해서 abs(a * x - y) 손실함수 값을 최소로 하는 x 를 구하려면 '손실함수에 대한 x의 미분 (the gradient of the loss with respect to x)를 구해서 x 값을 미분값만큼 빼서 갱신해주어야 합니다. 


그런데 아래의 TensorFlow 2.x 버전에서의 Python 즉시실행모드(eager mode)에서 손실(Loss) 을 "바로 즉시 계산"(eager execution)해보려서 출력 결과를 보면 numpy=12.0 인 Tensor 상수입니다. 여기서 자동미분을 하려고 하면 문제가 생기는데요, 왜냐하면 자동미분을 하기 위해 필요한 함수와 계산 식의 연산 과정과 입력 값에 대한 정보가 즉시실행모드에서는 없기 때문입니다. (입력 값과 연산 과정은 저장 안하고 즉시실행해서 결과만 출력했음).  



a, y = tf.constant(2.0), tf.constant(8.0)

x = tf.Variable(10.0) # In practice, we start with a random value

loss = tf.math.abs(a * x - y)


loss

[Out] <tf.Tensor: shape=(), dtype=float32, numpy=12.0>

 




  2. TensorFlow에서 Gradient tapes를 이용해 자동미분하는 방법


이 문제를 해결하기 위해 TensorFlow 는 중간 연산 과정(함수, 연산)을 테이프(tape)에 차곡차곡 기록해주는 Gradient tapes 를 제공합니다. 


with tf.GradientTape() as tape: 로 저장할 tape을 지정해주면, 이후의 GradientTape() 문맥 아래에서의 TensorFlow의 연관 연산 코드는 tape에 저장이 됩니다. 이렇게 tape에 저장된 연산 과정 (함수, 연산식) 을 가져다가 TensorFlow는 dx = tape.gradient(loss, x) 로 후진 모드 자동 미분 (Reverse mode automatic differentiation) 방법으로 손실에 대한 x의 미분을 계산합니다. 이렇게 계산한 손실에 대한 x의 미분을 역전파(backpropagation)하여 x의 값을 갱신(update)하는 작업을 반복하므로써 변수 x의 답을 찾아가는 학습을 진행합니다. 


위의 (1)번에서 소개한 8.0 = 2.0 * x 의 방정식에서는 변수 x = 4.0 이 답인데요, TensorFlow 의 GradientTape() 과 tape.gradient() 를 사용해서 오차 역전파 방법으로 갱신하여 문제를 풀어보겠습니다. 처음에 x = 10.0에서 시작형 4회 반복하니 x = 4.0 으로 답을 잘 찾아갔군요.  



# UDF for training

def train_func():

    with tf.GradientTape() as tape:

        loss = tf.math.abs(a * x - y)

    

    # calculate gradient

    dx =  tape.gradient(loss, x)

    print('x = {}, dx = {:.2f}'.format(x.numpy(), dx))

    

    # update x <- x - dx

    x.assign(x - dx)



# Run train_func() UDF repeately

for i in range(4):

    train_func()


[Out]
x = 10.0, dx = 2.00
x = 8.0, dx = 2.00
x = 6.0, dx = 2.00
x = 4.0, dx = 0.00

 




라는 함수에서 (Target) y에 대한 (Source) x의 미분 (derivative of target y with respect to source x) 을 TensorFlow의 GradientTape.gradient() 메소드를 사용해서 계산해보겠습니다. (우리는 이미 고등학교 때 배웠던 미분에 따라 라는 것을 알고 있습니다. x = 4.0 이므로 dy/dx 를 풀면 2 * 4.0 = 8.0 이겠군요.)


GradientTape.gradient(target, sources)



x = tf.Variable(4.0)


with tf.GradientTape() as tape:

    y = x ** 2

    

    

# dy = 2x * dx

dy_dx = tape.gradient(y, x)

dy_dx.numpy()

[Out] 8.0

 




위의 간단한 예에서는 스칼라(Scalar) 를 사용하였다면, tf.GradientTape() 은 어떤 형태의 텐서에 대해서도 사용할 수 있습니다. GradientTape.gradient(target, sources) 에서 sources 에는 여러개 변수를 리스트나 사전형(Dictionary) 형태로 입력해줄 수 있습니다.  



# tf.GradientTape works as easily on any tensor. 

w = tf.Variable(tf.random.normal((4, 2)), name='w')

b = tf.Variable(tf.zeros(2, dtype=tf.float32), name='b')

x = [[1.0, 2.0, 3.0, 4.0]]


with tf.GradientTape(persistent=True) as tape:

    y = x @ w + b

    loss = tf.reduce_mean(y ** 2)

    

# To get the gradienet of y w.r.t both variables, w and b

[dl_dw, dl_db] = tape.gradient(loss, [w, b])



print('loss:', loss)

print('w:', w)

print('b:', b)

[Out]
loss: tf.Tensor(74.95752, shape=(), dtype=float32)
w: <tf.Variable 'w:0' shape=(4, 2) dtype=float32, numpy=
array([[-0.05742593, -0.06279723],
       [-0.7892129 ,  1.8175325 ],
       [ 3.1122675 ,  0.12920259],
       [ 0.8164586 ,  0.3712036 ]], dtype=float32)>
b: <tf.Variable 'b:0' shape=(2,) dtype=float32, numpy=array([0., 0.], dtype=float32)>

 



Target에 대한 Source의 미분 결과는 Source의 형상(shape)을 따릅니다. 가령, 위의 예에서 손실함수(Loss)에 대한 가중치(w)의 미분 (derivative of loss with respect to weight) 의 결과는 Source에 해당하는 가중치(w)의 형상을 따라서 (4, 2) 가 됩니다. 



print('shape of w:', w.shape)

print('shape of dl_dw:', dl_dw.shape)

[Out]
shape of w: (4, 2)
shape of dl_dw: (4, 2)

 




  3. 시그모이드 함수의 자동미분 시각화 (Auto-diff plot of Sigmoid function)


딥러닝에서 활성화 함수 중의 하나로 사용되는 S 곡선 모양의 시그모이드 함수 (Sigmoid function) 은 아래와 같습니다. 


이 시그모이드 함수를 x 에 대해서 미분하면 아래와 같습니다. 


시그모이드 함수와 시그모이드 함수의 미분 값을 x 가 -10.0에서 10.0 사이의 200개의 관측치에 대해 TensorFlow의 tf.GradientTape() 와 GradientTae=pe.gradient(Sigmoid_func, x) 로 계산해서 하나의 그래프에 겹쳐서 시각화를 해보겠습니다. 



x = tf.linspace(-10.0, 10.0, 200+1)


with tf.GradientTape() as tape:

    tape.watch(x)

    y = tf.nn.sigmoid(x)


# For an element-wise calculation, 

# the gradient of the sum gives the derivative of each element

# with respect to its input-element, since each element is independent.

dy_dx = tape.gradient(y, x)



# plot of y, dy/dx

plt.plot(x, y, 'r-', label='y')

plt.plot(x, dy_dx, 'b--', label='dy/dx')

plt.legend()

_ = plt.xlabel('x')


plt.show()






  4. 테이프가 "볼(저장)" 것을 조정하는 방법 (Colltrolling whtat the tape "watches")


TensorFlow 가 오차역전파 알고리즘으로 학습(training by backpropagation algorithm)할 때 사용하는 자동미분은 '학습 가능한 변수 (trainable tf.Variable)'를 대상으로 합니다. 


따라서 만약에 (1) 변수가 아닌 상수 ('tf.Tensor') 라면 TensorFlow가 안 보는 (not watched), 즉 기록하지 않는 (not recorded) 것이 기본 설정이므로 자동 미분을 할 수 없으며, (2) 변수(tf.Variable) 더라도 '학습 가능하지 않으면 (not trainable)' 자동 미분을 할 수 없습니다



# A trainable variable

x0 = tf.Variable(3.0, name='x0')


# Not trainable

x1 = tf.Variable(3.0, name='x1', trainable=False)


# Not a Variable: A variable + tensor returns a tensor.

x2 = tf.Variable(2.0, name='x2') + 1.0


# Not a variable

x3 = tf.constant(3.0, name='x3')


with tf.GradientTape() as tape:

    y = (x0**2) + (x1**2) + (x2**2)


# Only x0 is a trainable tf.Variable, --> can calculate gradient

grad = tape.gradient(y, [x0, x1, x2, x3]) 



for g in grad:

    print(g)


[Out] 

tf.Tensor(6.0, shape=(), dtype=float32) # <-- dy_dx0, trainable variable None # <-- dy_dx1, not trainable None # <-- dy_dx2, not a variable None # <-- dy_dx3, not a variable

 



y 에 대한 tf.Tensor 인 x의 미분을 구하기 위해서는 GradientTape.watch(x) 를 호출해서 tf.Tensor 인 x를 테이프에 기록해주면 됩니다. 



x = tf.constant(3.0)

with tf.GradientTape() as tape:

    tape.watch(x) # watch 'x' and then record it onto tape

    y = x**2


# dy = 2x * dx

dy_dx = tape.gradient(y, x)

print(dy_dx.numpy())

[Out] 6.0

 



TensorFlow의 테이프에 기록되는 '학습 가능한 변수'는 GradientTape.watched_variables() 메소드로 확인할 수 있습니다. 



[var.name for var in tape.watched_variables()]

[Out] ['Variable:0']

 



반대로, 모든 변수(tf.Variable)를 테이프에 기록하는 TensorFlow의 기본설정을 비활성화(disable)하려면 watch_accessed_variables=False 으로 매개변수를 설정해주면 됩니다. 

아래의 예에서는 x0, x1 의 두 개의 tf.Variable 모두를 watch_accessed_variables=False 로 비활성화해놓고, GradientTape.watch(x1) 으로 x1 변수만 테이프에 기록해서 자동 미분을 해보겠습니다. 



x0 = tf.Variable(0.0)

x1 = tf.Variable(10.0)


with tf.GradientTape(watch_accessed_variables=False) as tape:

    tape.watch(x1)

    y0 = tf.math.sin(x0)

    y1 = tf.nn.softplus(x1)

    y = y0 + y1

    ys = tf.reduce_sum(y)



# dy = 2x * dx

grad = tape.gradient(ys, {'x0': x0, 'x1': x1})


print('dy/dx0:', grad['x0'])

print('dy/dx1:', grad['x1'].numpy())

[Out]

dy/dx0: None dy/dx1: 0.9999546

 




  5. Python 조건절을 사용한 테이프 기록 흐름을 조정

     (Control flow using Python condition statements)


Python 의 조건절을 이용한 분기문을 이용하면 TensorFlow 의 tf.GradientTape() 맥락 아래에서 테이프에 기록하는 연산 과정의 흐름을 조건절에 해당하는 것만 취사선택해서 기록할 수 있습니다. 


아래 예에서는 if x > 0.0 일 때는 result = v0, 그렇지 않을 때는(else) result = v1 ** 2 로 result 의 연산을 다르게 해서 테이프에 기록하고, GradientTape.gradient() 로 자동미분 할 때는 앞서 if else 조건절에서 True에 해당되어서 실제 연산이 일어났던 연산에 미분을 연결해서 계산하라는 코드입니다. 


아래 예에서는 x = tf.constant(1.0) 으로서 if x > 0.0 조건절을 만족(True)하므로 result = v0 연산이 실행되고 뒤에 tape.gradient()에서 자동미분이 되었으며, else 조건절 아래의 연산은 실행되지 않았으므로 뒤에 tape.gradient()의 자동미분은 해당되지 않았습니다 (dv1: None)



x = tf.constant(1.0)


v0 = tf.Variable(2.0)

v1 = tf.Variable(2.0)


with tf.GradientTape(persistent=True) as tape:

    tape.watch(x)


    # Python flow control

    if x > 0.0:

        result = v0

    else:

        result = v1**2 


dv0, dv1 = tape.gradient(result, [v0, v1])


print('dv0:', dv0)

print('dv1:', dv1)

[Out] 
dv0: tf.Tensor(1.0, shape=(), dtype=float32)
dv1: None

 



[ Reference ]

* Automatic Differentiation: https://en.wikipedia.org/wiki/Automatic_differentiation

* TensorFlow Gradient tapes and Automatic differentiation
   : https://www.tensorflow.org/guide/autodiff?hl=en


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. 



728x90
반응형
Posted by Rfriend
,

TensorFlow에서 그래프(Graphs)는 tf.Operation 객체의 집합을 포함하고 있는 데이터 구조로서, 연산의 단위와 텐서 객체, 연산간에 흐르는 데이터 단위를 나타냅니다. ("Graphs are data structures that contain a set of tf.Operation objects, which represent units of computation; and tf.Tensor objects, which represent the units of data that flow between operations.") 

* source: https://www.tensorflow.org/guide/intro_to_graphs


TensorFlow 1.x 버전에서의 지연 실행(Lazy execution) 모드 에서는 연산 그래프 (Computational Graph)를 생성하고, 연산을 수행하기 이전에 그래프로 부터 텐서를 어떤 연산을 어떤 순서로 최적화해서 수행할지를 결정한 다음에야 연산을 게으르게(Lazy) 수행을 합니다. TensorFlow 1.x 버전에서는 Session 을 열어서 그래프를 명시적으로 작동(explicitly run)시켜줘야 합니다. 


그래프 최적화(Graph Optimization) 는 만약 동일한 연산이 여기저기서 반복되는 경우 해당 연산 결과를 캐쉬(cache)로 저장해서 사용함으로써 동일 연산이 반복적으로 일어나지 않도록 한다거나, 복잡한 연산의 경우 다수의 장비에서 병렬처리(parallel on multiple devices)를 하여 연산을 빠르게 수행할 수 있도록 하여 성능을 최적화해줍니다. 


TensorFlow 2.x 버전부터는 즉시 실행(Eager execution) 모드로 바뀌었다고 지난 포스팅에서 소개하였는데요, 이는 코드가 즉시 수행된다는 뜻으로서, 즉 "연산 그래프를 그리고 그래프 최적화를 한 후에 코드를 지연 수행"하는 것이 아니라는 뜻입니다. 


그런데 만약 복잡한 모델의 성능을 최적화할 필요가 있다거나, 다른 장비로 모델을 내보내야한다거나 할 때(가령, Python interpreter가 없는 모바일 애플리케이션, 임베디드 장비, 백엔드 서버 등), 파이썬의 즉시 실행 코드를 TensorFlow Graph 로 변환해야 할 필요가 생깁니다. 


이때 TensorFlow 2.x 에서 사용할 수 있는 것이 바로 AutoGraph 모듈과 tf.function 데코레이터(tf.function Python decorator) 입니다. TensorFlow 1.x 버전때처럼 Placeholder를 정의하거나 Session을 열고 작동시켜야하는 번거로움이 TensorFlow 2.x 에서는 없으므로 무척 편리합니다. 




TensorFlow 2.3.0 버전을 사용해서 간단한 예를 들어보겠습니다. 



import tensorflow as tf


print(tf.__version__)

[Out] 2.3.0

 



a, b, c 세개의 객체를 input으로 받아서 곱셈과 덧셈 연산을 해서 얻은 d, e 객체를 반환하는 간단한 Python 사용자 정의 함수 (User defined function) 를 만들어서 실행시켜 보겠습니다.  



# python UDF

def calc(a, b, c):

    d = a * b + c

    e = a * b * c

    return d, e

 

# run python UDF

d, e = calc(2, 3, 4)


print('d:', d)

print('e:', e)

[Out]

d: 10 e: 24




위에서 만든 Python code 를 TensorFlow 2.x 버전에서 텐서를 input으로 받아서 그래프 최적화 (Graph Optimization) 하여 연산을 수행할 수 있도록 @tf.function 과 AutoGraph 모듈을 사용하여 자동으로 그래프로 변환하여 보겠습니다. Python code 함수를 정의하기 전에 @tf.function 데코레이터를 써주면 됩니다. 



@tf.function

def calc(a, b, c):

    d = a * b + c

    e = a * b * c

    return d, e

 

 


혹시 Python 함수가 AutoGraph 에서 그래프로 어떻게 변환되었는지 궁금하다면 아래와 같이 tf.autograph.to_code() 함수를 사용해서 확인해볼 수 있습니다. (좀 복잡하군요. ^^; 이걸 알아서 자동으로 해주니 참 좋은 세상입니다! Many thanks to TensorFlow and Google!)



# If you're curious you can inspect the code autograph generates.

print(tf.autograph.to_code(calc.python_function))


def tf__calc(a, b, c):
    with ag__.FunctionScope('calc', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()
        d = ((ag__.ld(a) * ag__.ld(b)) + ag__.ld(c))
        e = ((ag__.ld(a) * ag__.ld(b)) * ag__.ld(c))
        try:
            do_return = True
            retval_ = (ag__.ld(d), ag__.ld(e))
        except:
            do_return = False
            raise
        return fscope.ret(retval_, do_return)

 




TensorFlow 1.x 버전에서는 Session을 열고 명시적으로 그래프 연산을 동작을 시켜야 하는 반면에, TensorFlow 2.x 버전에서는 Session 없이 바로 그래프 연산을 수행할 수 있습니다. 


Session 없이 바로 위에서 정의하고 그래프로 변환했던 calc() 함수에 a, b, c 상수 텐서를 입력해서 d, e 텐서를 결과로 반환해보겠습니다. 



# input constant tensor

a = tf.constant(2.0, name="a")

b = tf.constant(3.0, name="b")

c = tf.constant(4.0, name="c")


# run AutoGraph function without opening Session()

d, e = calc(a, b, c)


print('d:', d)

print('e:', e)

[Out] 
d: tf.Tensor(10.0, shape=(), dtype=float32)
e: tf.Tensor(24.0, shape=(), dtype=float32)


# convert tensor into numpy array format

(d.numpy(), e.numpy())

[Out] (10.0, 24.0)

 


[ Reference ] 

* Better performance with tf.function: https://www.tensorflow.org/guide/function

TensorFlow 2.0: tf.function and AutoGraph: https://towardsdatascience.com/tensorflow-2-0-tf-function-and-autograph-af2b974cf4f7


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 TensorFlow 2.x 버전의 Keras를 사용하여 MNIST 손글씨를 분류하는 간단한 DNN (Deep Neural Network) 모델을 만드는 방법(DNN (https://rfriend.tistory.com/553)을 소개하였습니다. 


이번 포스팅에서는 TensorFlow 에서 모델을 개발하는 과정과 결과를 시각화하여 모니터링할 수 있도록 해주는 웹 기반의 시각화 툴인 TensorBoard 사용 방법을 소개하겠습니다. 


아래 이미지는 https://keras.io 페이지의 Keras 소개 페이지에 나오는 내용인데요, Keras로 쉽고 빠르게 딥러닝 모델을 만들어서 실험하고, 여러개의 모델 실험 결과를 TensorBoard 를 사용해서 시각화하여 확인하면서 "생각의 속도로 반복(Iterate at the speed of though)" 하라고 하네요! 



TensorBoard 를 사용하면 다수의 모델 각 각 별로 쉽고 빠르게 


  • TensorFlow, Keras 로 만든 모델 구조의 그래프(Graphs)를 시각화

     --> 모델의 그래프가 의도한 설계와 일치하는지 확인

     --> op-level 그래프를 통해 TensorFlow가 프로그램을 어떻게 이해하고 있는지 확인

  • TensorFlow, Keras 로 만든 모델의 학습 과정/ 결과 (Epoch 별 정확도, 손실값)를 그래프로 시각화
  • 활성화와 경사를 히스토그램으로 시각화
  • 3D로 Embedding 탐색


을 할 수 있습니다. 



TensorBoard 사용 예를 들기 위하여 

(1) Keras로 Fashion MNIST 데이터를 분류하는 Dense 모델 구축

(2) Keras TensorBoard callback 을 포함하여 모델 학습 (Train the model with TensorBoard callback)

(3) TensorBoard 를 실행하여 모델 학습 과정/결과 시각화


의 순서대로 소개를 하겠습니다. 



  (1) Keras로 Fashion MNIST 데이터를 분류하는 Dense 모델 구축


먼저 TensorFlow와 TensorBoard 를 importing 하고 버전을 확인해보겠습니다. TensorFlow 2.3.0 버전이므로 Keras를 따로 importing 할 필요는 없습니다. 



import tensorflow as tf

tf.__version__

[Out] '2.3.0'

 

import tensorboard

tensorboard.__version__

[Out] '2.3.0'




이번 포스팅의 목적은 TensorBoard 사용법을 소개하는 것이므로 Fashion MNIST의 Training set 만을 로딩해보겠습니다. (Test set 은 사용 안 함.)

 


(train_images, train_labels), _ = tf.keras.datasets.fashion_mnist.load_data()

train_images = train_images / 255.0 # normailization (0 ~ 1)


print('shape of train images:', train_images.shape)

print('shape of train labels:', train_labels.shape)

[Out]
shape of train images: (60000, 28, 28)
shape of train labels: (60000,)

 



모델 코드는 https://www.tensorflow.org/tensorboard/graphs 페이지에서 소개하는 DNN 매우 간단한 Keras 코드를 그대로 사용하였습니다. 1개의 Dense 은닉층, Dropout 층, 그리고 10개의 classes별 확률을 반환하는 Output 층을 순서대로 쌓아서(sequential stack of layers) 딥러닝 모델을 구축하였습니다. 


그리고 최적화 알고리즘, 손실함수, 평가지표를 설정해서 모델을 컴파일 해주었습니다. 



# Define the model

model = tf.keras.models.Sequential([

    tf.keras.layers.Flatten(input_shape=(28, 28)),

    tf.keras.layers.Dense(32, activation='relu'),

    tf.keras.layers.Dropout(0.2),

    tf.keras.layers.Dense(10, activation='softmax')

])


# Compile the model

model.compile(

    optimizer='adam',

    loss='sparse_categorical_crossentropy',

    metrics=['accuracy'])




여기까지는 지난번 포스팅에서 소개했던 Keras로 모델 구축하고 컴파일하는 방법과 동일하구요, 이제 (2)번 부터 TensorBoard를 이용하기 위해서 추가로 수고를 해줘야 하는 부분입니다. 



  (2) Keras TensorBoard callback을 포함하여 모델 학습 

      (Train the model with Keras TensorBoard callback)


(2-1) 먼저, Keras TensorBoard callback 을 정의해줍니다. 

"./logs/"의 하위경로에 모델을 훈련시키는 시점의 "년월일-시간분초(%Y%m%d-%H%M%S)" 를 이름으로 가지는 하위 폴더를 추가하여 Log를 저장해줄 경로(log_dir)를 지정해주겠습니다. (모델을 여러개 구축하여 실험할 경우 버전 구분/ 관리 용도)



# Define the Keras TensorBoard callback.

from datetime import datetime


logdir="logs/" + datetime.now().strftime("%Y%m%d-%H%M%S")

tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=logdir)




현재 작업하는 경로는 os 라이브러리의 os.getcwd() 메소드로 확인할 수 있습니다. 저의 경우는 Keras TensorBoard callback 의 로그 저장 경로가 "/Users/ihongdon/Documents/TensorFlow/logs/20200822-181251" 가 되겠군요. 



import os

os.getcwd()

[Out] '/Users/ihongdon/Documents/TensorFlow'


logdir

[Out] 'logs/20200822-181251'

 



(2-2) 다음으로, 위의 (2-1)에서 정의한 Keras TensorBoard callback 을 지정(아래의 빨간색 부분)하여 모델을 훈련(training, fitting) 시킵니다. 



# Train the model.

model.fit(

    train_images,

    train_labels, 

    batch_size=64,

    epochs=5, 

    callbacks=[tensorboard_callback])


Epoch 1/5
  1/938 [..............................] - ETA: 0s - loss: 2.3300 - accuracy: 0.0781WARNING:tensorflow:From /Users/ihongdon/anaconda3/envs/py3.8_tf2.3/lib/python3.8/site-packages/tensorflow/python/ops/summary_ops_v2.py:1277: stop (from tensorflow.python.eager.profiler) is deprecated and will be removed after 2020-07-01.
Instructions for updating:
use `tf.profiler.experimental.stop` instead.
938/938 [==============================] - 1s 690us/step - loss: 0.6749 - accuracy: 0.7662
Epoch 2/5
938/938 [==============================] - 1s 698us/step - loss: 0.4813 - accuracy: 0.8287
Epoch 3/5
938/938 [==============================] - 1s 796us/step - loss: 0.4399 - accuracy: 0.8425
Epoch 4/5
938/938 [==============================] - 1s 766us/step - loss: 0.4184 - accuracy: 0.8493
Epoch 5/5
938/938 [==============================] - 1s 684us/step - loss: 0.4044 - accuracy: 0.8543
Out[6]:
<tensorflow.python.keras.callbacks.History at 0x7fca47797ca0>

 




  (3) TensorBoard 를 실행하여 모델 학습 과정/결과 시각화 (TensorFlow Visualization)


이제 TensorBoard 를 실행시켜서 위의 (2)번에서 학습하고 있는 (혹은 학습이 끝난) 모델을 시각화하여 눈으로 직접 그래프와 손실값/정확도를 확인을 해보겠습니다. 


(3-1) 명령 프롬프트(commamd prompt) 창을 열어주세요.

(위의 (1)번과 (2)번은 Jupyter Notebook에서 실행, 여기 (3)번은 명령 프롬프트 창에서 실행)


(3-2) 그리고 Anaconda 가상환경들 중에서 하나를 선택해서 활성화시켜주세요. 


 * Mac OS 의 경우       --> % source activate [virtual_env_name]

 * Windows OS 의 경우 --> % activate [virtual_env_name]

 



(3-3) 명령 프롬프트 창에서 가상환경에 들어왔으면, 앞의 (2-1)에서 정의했던 TensorBoard 의 로그 경로를 지정해서 TensorBoard를 실행시킵니다. 


% tensorboard --logdir= [TensorBoard Log 저장 경로]


아래의 명령 프롬프트 화면캡쳐한 이미지의 제일 마지막 줄을 보면 "http://localhost:6006/" 의 웹 주소에서 TensorBoard 를 볼 수 있다고 안내가 나옵니다. (Localhost의 6006번 포트) 


만약 6006 포트 말고 다른 포트를 사용하고 싶으면 --port=[port 번호] 식으로 명령 프롬프트 창에서 옵션 명령어를 추가해주면 됩니다. 


% tensorboard --logdir= [TensorBoard Log 저장 경로] --port=9000





TensorBoard 를 종료하려면 명령 프롬프트 창에서 'CTRL+C' 을 눌러주면 됩니다. 



*********************************************************************************************************

 

(3-4) 이제  Chrome이나 Firefox 같은 웹 브라우저를 열고 "http://localhost:6006/" url로 TensorBoard 에 접속해보세요. 


짜잔~ TensorBoard 가 떴습니다~! 이예~!!! ^_____^


"SCALARS"와 "GRAPHS"의 두 개의 메뉴가 있는데요, 먼저 "SCARLARS" 메뉴에 들어가서 왼쪽 하단의 모델 이름 ("20200822-181251/train") 을 선택해주면 각 epoch별로 정확도(accuracy)와 손실값(loss value, 이 예에서는 categorical cross-entropy)을 선 그래프로 해서 시각화하여 볼 수 있습니다. 

(이 예는 데이터도 작고, 5 epochs 밖에 안되다 보니 벌써 다 끝나있네요.)




다음으로 TensorBoard의 상단에 있는 "GRAPHS" 메뉴를 클릭해서 (1)번에서 구축한 Dense 모델의 그래프를 확인해보겠습니다. 





그래프 노드의 '+' 를 클릭하면 해당 층(Layer)의 세부 그래프를 확대(Zoom In)하여 살펴볼 수 있습니다. 


가령, 아래의 TensorBoard 화면캡쳐처럼 위의 상위 수준의 그래프에서  'dense' 노드의 '+'를 클릭하여 세부적인 연산 그래프를 확인해보겠습니다. 1차원으로 flatten 된 input 데이터와 weight 간의 행렬곱(MatMul) 후에, 편향을 더해주고(BiasAdd), ReLU 활성화함수를 적용하는 순서로 TensorFlow가 이해를 하고 있군요. 


펼쳐진 세부 그래프 블럭을 더블 클릭해주면 다시 원래의 노드로 Zoom Out 됩니다. 




* Reference: https://www.tensorflow.org/tensorboard/graphs


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 TensorFlow 1.x 버전이 지연(게으른) 실행 (Lazy Execution) 인데 반해 TensorFlow 2.x 버전은 즉시 실행 (Eager Execution) 으로 바뀌었다는 점에 대해서 TenforFlow의 대표적인 자료구조인 상수, 변수, Placeholder 별로 간단한 예를 들어서 비교 설명해 보았습니다. 


이번 포스팅에서는 TensorFlow 2.x 버전에서 크게 바뀐 것 중에서 "고수준 API (High-Level API) 로서 Keras API를 채택"하였다는 것에 대해서 소개하겠습니다. 


TensorFlow 2.x 는 텐서 연산은 C++로 고속으로 수행하며, 저수준 API에는 Python wrapper 의 TensorFlow가 있고, 고수준 API로는 Keras 를 사용하는 아키텍처입니다. Keras도 당연히 CPU 뿐만 아니라 GPU 를 사용한 병렬처리 학습이 가능하며, 다양한 애플리케이션/디바이스에 배포도 가능합니다. 



Keras는 Google 에서 딥러닝 연구를 하고 있는 Francois Chollet 이 만든 Deep Learning Library 인데요, TensorFlow 2.x 버전에 고수준 API 로 포함이 되었습니다. (고마워요 Google!!!)


TensorFlow 1.x 버전을 사용할 때는 TensorFlow 설치와 별도로 Native Keras를 따로 설치하고 또 Keras를 별도로 import 해서 사용해야 했습니다. 


하지만 TensorFlow 2.x 버전 부터는 TensorFlow 만 설치하면 되며, Keras는 별도로 설치하지 않아도 됩니다. 또한 Keras API를 사용할 때도 TensorFlow를 Importing 한 후에 tf.keras.models 와 같이 tf.keras 로 시작해서 뒤에 Keras의 메소드를 써주면 됩니다. (아래의 MNIST 분류 예시에서 자세히 소개)


 

TensorFlow 1.x 

TensorFlow 2.x 

 설치 (Install)

 $ pip install tensorflow==1.15

 $ pip install keras

 $ pip install tensorflow==2.3.0 

 (Keras는 별도 설치 필요 없음)

 Importing

 import tensorflow as tf

from keras import layers

 import tensorflow as tf

 from tf.keras import layers




위의 Kreas 로고 이미지는 https://keras.io 정식 홈페이지의 메인 화면에 있는 것인데요, "Simple. Flexible. Powerful." 가 Keras의 모토이며, Keras를 아래처럼 소개하고 있습니다. 


"Keras는 TensorFlow 기계학습 플랫폼 위에서 실행되는 Python 기반의 딥러닝 API 입니다. Keras는 빠른 실험을 가능하게 해주는데 초점을 두고 개발되었습니다. 아이디어에서부터 결과를 얻기까지 가능한 빠르게 할 수 있다는 것은 훌륭한 연구를 하는데 있어 매우 중요합니다" 

* Keras reference: https://keras.io/about/


백문이 불여일견이라고, Keras를 사용하여 MNIST 손글씨 0~9 숫자를 분류하는 매우 간단한 DNN (Deep Neural Network) 모델을 만들어보겠습니다.


DNN Classifier 모델 개발 및 적용 예시는 (1) 데이터 준비, (2) 모델 구축, (3) 모델 컴파일, (4) 모델 학습, (5) 모델 평가, (6) 예측의 절차에 따라서 진행하겠습니다. 




[ MNIST 데이터 손글씨 숫자 분류 DNN 모델 ]



  (1) 데이터 준비 (Preparing the data)


먼저 TensorFlow 를 importing 하고 버전(TensorFlow 2.3.0)을 확인해보겠습니다. 

 


import tensorflow as tf

import numpy as np


print(tf.__version__)

2.3.0




Keras 에는 MNIST 데이터셋이 내장되어 있어서 tf.keras.datasets.mnist.load_data() 메소드로 로딩을 할 수 있습니다. Training set에 6만개, Test set에 1만개의 손글씨 숫자 데이터와 정답지 라벨 데이터가 있습니다. 



(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()


print('shape of x_train:', x_train.shape)

print('shape of y_train:', y_train.shape)

print('shape of x_test:', x_test.shape)

print('shape of y_test:', y_test.shape)

shape of x_train: (60000, 28, 28)
shape of y_train: (60000,)
shape of x_test: (10000, 28, 28)
shape of y_test: (10000,)




 MNIST 데이터셋이 어떻게 생겼는지 확인해보기 위해서 Training set의 0번째 이미지 데이터 배열(28 by 28 array)과 정답지 라벨 ('5'), 그리고 matplotlib 으로 시각화를 해보겠습니다. 



x_train[0]

[Out] array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 18, 18, 18, 126, 136, 175, 26, 166, 255, 247, 127, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 30, 36, 94, 154, 170, 253, 253, 253, 253, 253, 225, 172, 253, 242, 195, 64, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 49, 238, 253, 253, 253, 253, 253, 253, 253, 253, 251, 93, 82, 82, 56, 39, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 18, 219, 253, 253, 253, 253, 253, 198, 182, 247, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 80, 156, 107, 253, 253, 205, 11, 0, 43, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 1, 154, 253, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 253, 190, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 190, 253, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 241, 225, 160, 108, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 240, 253, 253, 119, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 186, 253, 253, 150, 27, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 93, 252, 253, 187, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 253, 249, 64, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 130, 183, 253, 253, 207, 2, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 148, 229, 253, 253, 253, 250, 182, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 114, 221, 253, 253, 253, 253, 201, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 23, 66, 213, 253, 253, 253, 253, 198, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 18, 171, 219, 253, 253, 253, 253, 195, 80, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 55, 172, 226, 253, 253, 253, 253, 244, 133, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 136, 253, 253, 253, 212, 135, 132, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)



y_train[0]

[Out] 5


import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = (5, 5)

plt.imshow(x_train[0])

plt.show()




Deep Neural Network 모델의 input으로 넣기 위해 (28 by 28) 2차원 배열 (2D array) 이미지를 (28 * 28 = 784) 의 옆으로 길게 펼친 데이터 형태로 변형(Reshape) 하겠습니다(Flatten() 메소드를 사용해도 됨). 그리고 0~255 사이의 값을 0~1 사이의 실수 값으로 변환하겠습니다.  



# reshape and normalization

x_train = x_train.reshape((60000, 28 * 28)) / 255.0

x_test = x_test.reshape((10000, 28 * 28)) / 255.0





  (2) 모델 구축 (Building the model)


이제 본격적으로 Keras를 사용해보겠습니다. Keras의 핵심 데이터 구조로 모델(Models)층(Layers)있습니다. 모델을 구축할 때는 (a) 순차적으로 층을 쌓아가는 Sequential model 과, (b) 복잡한 구조의 모델을 만들 때 사용하는 Keras functional API 의 두 가지 방법이 있습니다. 


이미지 데이터 분류는 CNN (Convolutional Neural Network) 을 사용하면 효과적인데요, 이번 포스팅에서는 간단하게 Sequential model 을 사용하여 위의 (1)에서 전처리한 Input 이미지 데이터를 받아서, 1개의 완전히 연결된 은닉층 (Fully Connected Hidden Layer) 을 쌓고, 10개의 classes 별 확률을 반환하는 FC(Fully Connected) Output Layer 를 쌓아서 만든 DNN(Deep Neural Network) 모델을 만들어보겠습니다.  (Functional API 는 PyTorch 와 유사한데요, 나중에 별도의 포스팅에서 소개하도록 하겠습니다.)


add() 메소드를 사용하면 마치 레고 블록을 쌓듯이 차곡차곡 순서대로 원하는 층을 쌓아서 딥러닝 모델을 설계할 수 있습니다. (Dense, CNN, RNN, Embedding 등 모두 가능). Dense() 층의 units 매개변수에는 층별 노드 개수를 지정해주며, 활성화 함수는 activation 매개변수에서 지정해줍니다. 은닉층에서는 'Relu' 활성화함수를, Output 층에는 10개 classes에 대한 확률을 반환하므로 'softmax' 활성화함수를 사용하였습니다. 



# Sequential model

model = tf.keras.models.Sequential()


# Stacking layers

model.add(tf.keras.layers.Dense(units=128, activation='relu', input_shape=(28*28,)))

model.add(tf.keras.layers.Dense(units=10, activation='softmax'))




이렇게 층을 쌓아서 만든 DNN (Deep Neural Network) 모델이 어떻게 생겼는지 summary() 함수로 출력해보고, tf.keras.utils.plot_model() 메소드로 시각화해서 확인해보겠습니다. 



model.summary()

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 128)               100480    
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 101,770
Trainable params: 101,770
Non-trainable params: 0
_____________________________________


# keras model visualization

tf.keras.utils.plot_model(model, 

                          to_file='model_plot.png', 

                          show_shapes=True)


* 참고로 Keras의 tf.keras.utils.plot_model() 메소드를 사용하려면 사전에 pydot, pygraphviz 라이브러리 설치, Graphviz 설치가 필요합니다. 




  (3) 모델 컴파일 (Compiling the model)


위의 (2)번에서 딥러닝 모델을 구축하였다면, 이제 기계가 이 모델을 이해할 수 있고 학습 절차를 설정할 수 있도록 컴파일(Compile) 해줍니다. 


compile() 메소드에는 딥러닝 학습 시 사용하는 (a) 오차 역전파 경사하강법 시 최적화 알고리즘(optimizer), (b) 손실함수(loss function), 그리고 (c) 성과평가 지표(metrics) 를 설정해줍니다. 



model.compile(optimizer='sgd', 

              loss='sparse_categorical_crossentropy', 

              metrics=['accuracy']

)

 

* 참고로, input data의 다수 클래스에 대한 정답 레이블(ground-truth labels)을 바로 사용하여 손실함수를 계산할 때는 loss='sparse_categorical_crossentropy' 을 사용하며, One-hot encoding 형태로 정답 레이블이 되어있다면 loss='categorical_crossentropy' 를 사용합니다. 



Keras 는 위에서 처럼 optimizer='sgd' 처럼 기본 설정값을 사용해서 간단하게 코딩할수도 있으며, 만약 사용자가 좀더 유연하고 강력하게 설정값을 조정하고 싶다면 아래의 예시처럼 하위클래스(subclassing)을 사용해서 세부 옵션을 지정해줄 수도 있습니다. 



model.compile(

    optimizer=keras.optimizers.SGD(learning_rate=0.01, momentum=0.9, nesterov=True)

    loss=keras.losses.sparse_categorical_crossentropy

)

 




  (4) 모델 학습 (Training the model)


데이터 준비와 모델 구축, 그리고 모델 컴파일이 끝났으므로 이제 6만개의 훈련 데이터셋(Training set) 중에서 80% (4.8만개 데이터) 는 모델 학습(fitting)에, 20%(1.2만개 데이터)는 검증(validation)용으로 사용하여 DNN 모델을 학습해보겠습니다. (참고로, 1만개의 Test set은 모델 학습에서는 사용하지 않으며, 최종 모델에 대해서 제일 마지막에 모델 성능 평가를 위해서만 사용합니다.)


epochs = 5 는 전체 데이터를 5번 반복(iteration)해서 사용해서 학습한다는 의미입니다. 

verbose = 1 은 모델 학습 진행상황 막대를 출력하라는 의미입니다. ('1' 이 default이므로 생략 가능)

(0 = silent, 1 = progress bar, 2 = one line per epoch)

validation_split=0.2 는 검증용 데이터셋을 별도로 만들어놓지 않았을 때 학습용 데이터셋에서 지정한 비율(예: 20%) 만큼을 분할하여 (hyperparameter tuning을 위한) 검증용 데이터셋으로 이용하라는 의미입니다. 

batch_size=32 는 한번에 데이터 32개씩 batch로 가져다가 학습에 사용하라는 의미입니다. 


이번 포스팅은 Keras의 기본 사용법에 대한 개략적인 소개이므로 너무 자세하게는 안들어가겠구요, callbacks는 나중에 별도의 포스팅에서 자세히 설명하겠습니다. 


학습이 진행될수록 (즉, epochs 이 증가할 수록) 검증용 데이터셋에 대한 손실 값(loss value)은 낮아지고 정확도(accuracy)는 올라가고 있네요. 



model.fit(x_train, y_train, 

          epochs=5, 

          verbose=1, 

          validation_split=0.2)


Epoch 1/5
1500/1500 [==============================] - 1s 922us/step - loss: 0.7291 - accuracy: 0.8146 - val_loss: 0.3795 - val_accuracy: 0.8989
Epoch 2/5
1500/1500 [==============================] - 1s 811us/step - loss: 0.3605 - accuracy: 0.9007 - val_loss: 0.3081 - val_accuracy: 0.9145
Epoch 3/5
1500/1500 [==============================] - 1s 785us/step - loss: 0.3061 - accuracy: 0.9140 - val_loss: 0.2763 - val_accuracy: 0.9215
Epoch 4/5
1500/1500 [==============================] - 1s 772us/step - loss: 0.2749 - accuracy: 0.9228 - val_loss: 0.2519 - val_accuracy: 0.9290
Epoch 5/5
1500/1500 [==============================] - 1s 827us/step - loss: 0.2525 - accuracy: 0.9289 - val_loss: 0.2355 - val_accuracy: 0.9339
Out[14]:
<tensorflow.python.keras.callbacks.History at 0x7fc7920a1760>

 




  (5) 모델 평가 (Evaluating the model)


evaluate() 메소드를 사용하여 모델의 손실 값(Loss value) 과 컴파일 단계에서 추가로 설정해준 성능지표 값 (Metrics values) 을 Test set 에 대하여 평가할 수 있습니다. (다시 한번 강조하자면, Test set은 위의 (4)번 학습 단계에서 절대로 사용되어서는 안됩니다!)


Test set에 대한 cross entropy 손실값은 0.234, 정확도(accuracy)는 93.68% 가 나왔네요. (CNN 모델을 이용하고 hyperparameter tuning을 하면 99%까지 정확도를 올릴 수 있습니다.)



model.evaluate(x_test, y_test)

313/313 [==============================] - 0s 801us/step - loss: 0.2342 - accuracy: 0.9368
Out[15]:
[0.23418554663658142, 0.9368000030517578]





  (6) 예측 (Prediction for new data) 


위에서 학습한 DNN 모델을 사용해서 MNIST 이미지 데이터에 대해서 predict() 메소드를 사용하여 예측을 해보겠습니다. (별도의 새로운 데이터가 없으므로 test set 데이터를 사용함)


예측 결과의 0번째 데이터를 살펴보니 '7' 이라고 모델이 예측을 했군요. 



preds = model.predict(x_test, batch_size=128)


# returns an array of probability per classes

preds[0]

array([8.6389220e-05, 3.9117887e-07, 5.6002376e-04, 2.2136024e-03,
       2.7992455e-06, 1.4370076e-04, 5.6979918e-08, 9.9641860e-01,
       2.2985958e-05, 5.5149395e-04], dtype=float32)


# position of max probability

np.argmax(preds[0])

[Out] 7




실제 정답 레이블(ground-truth labels)과 비교해보니 '7' 이 정답이 맞네요. 



y_test[0]

[Out] 7


plt.imshow(x_test[0].reshape(28, 28))

plt.show()




  (7) 모델 저장, 불러오기 (Saving and Loading the model)


딥러닝 모델의 성과 평가 결과 기준치를 충족하여 현장 적용이 가능한 경우라면 모델의 요소와 학습된 가중치 정보를 파일 형태로 저장하고, 배포, 로딩해서 (재)활용할 수 있습니다. 


-  HDF5 파일로 전체 모델/가중치 저장하기


# Save the entire model to a HDF5 file.

model.save('mnist_dnn_model.h5') 




- 저장된 'mnist_dnn_model.h5' 모델/가중치 파일을 'new_model' 이름으로 불러오기


# Recreate the exact same model, including its weights and the optimizer

new_model = tf.keras.models.load_model('mnist_dnn_model.h5')


new_model.summary()

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 128)               100480    
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 101,770
Trainable params: 101,770
Non-trainable params: 0
_________________________________________________________________




TensorFlow 2.x 에서부터 High-level API 로 탑재되어 있는 Keras API 에 대한 간단한 MNIST 분류 DNN 모델 개발과 예측 소개를 마치겠습니다. 


* About Kerashttps://keras.io/about/

* Keras API Reference: https://keras.io/api/

* Save and Load model: https://www.tensorflow.org/tutorials/keras/save_and_load


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


728x90
반응형
Posted by Rfriend
,

2019년도 9월 말에 TensorFlow 2.0 버전이 공개되었으며. 이전의 TensorFlow 1.x 대비 (1) API Cleanup, (2) Eager execution, (3) No more globals, (4) Functions, not sessions 와 같은 변화가 있었습니다. (https://www.tensorflow.org/guide/effective_tf2)


이번 포스팅에서는 TensorFlow 1.x 에서의 지연 실행모드(Lazy execution), Global namespace, Session 호출 대비, TensorFlow 2.x 에서  Global namespace 나 Session 호출 없이, 즉시 실행모드(Eager execution)에 대해서 간단한 예제를 들어서 비교 설명해보겠습니다. 

(Keras API 와의 통합은 다음번 포스팅에서 소개하겠습니다.)


아래 비교 예제를 살펴보면 TensorFlow 2.x 에서의 코드가 한결 깔끔해져서 가독성도 좋아지고, 디버깅하기에도 편리해졌음을 알 수 있을 겁니다.  Python 프로그리밍에 이미 익숙한 사용자라면 TensorFlow 2.x 버전이 사용하기에 무척 친숙하게 느껴질 것입니다. 


아래에는 TensorFlow 1.12.0 버전과 TensorFlow 2.3.0 버전을 사용하여 TensorFlow 의 핵심 데이터 구조인 상수(constant), 변수(Variable), Placeholder 로 나누어서 TensorFlow 버전 간 비교해보겠습니다. 


  (1) 상수 (constant) 생성 및 연산 실행


 TensorFlow 1.x: 지연 실행 (lazy execution)

TensorFlow 2.x: 즉시 실행 (eager execution)


import tensorflow as tf


tf.__version__

'1.12.0'


a = tf.constant([0, 1, 2])

b = tf.constant([10, 20, 30])


# lazy execution

c = tf.add(a, b)

print(c)

Tensor("Add:0", shape=(3,), 
dtype=int32)



with tf.Session() as sess:

    print(sess.run(c))


[10 21 32]


import tensorflow as tf


tf.__version__

'2.3.0'


a = tf.constant([0, 1, 2])

b = tf.constant([10, 20, 30])


# eager execution

c = tf.add(a, b) # or c = a + b

print(c)

tf.Tensor([10 21 32], 
shape=(3,), dtype=int32)


TensorFlow 1.x 버전에서는 지연 실행 모드 (lazy execution) 가 기본 설정이므로 위의 더하기(add) 예제에서처럼 tf.constant() 로 두 개의 상수 배열을 만들고, tf.add(a, b)를 하면 바로 실행되는 것이 아니며, 계산 그래프와 Session을 생성하고 명시적으로 tf.Session().run() 을 호출하고 나서야 연산이 실행됩니다. 


반면에 TensorFlow 2.x 버전에서는 즉시 실행 모드 (eager execution) 가 기본 설정이므로 a, b 의 두 상수 배열을 생성한 후 tf.add(a, b) 를 하면 Session().run() 없이 바로 더하기 연산이 실행되어 그 결과를 반환합니다. 


TensorFlow 2.x 에서는 아래의 예처럼 numpy() 메소드를 사용해서 텐서를 numpy array 로 변환하여 텐서의 값에 접근할 수 있습니다. (참고로, 반대로 tf.convert_to_tensor(arr) 메소드를 사용하면 numpy array를 텐서 값으로 변환할 수 있습니다.)



# TensorFlow 2.x

# accessing the value of a Tensor

c_arr = c.numpy()

print(c_arr)

[10 21 32]

 




  (2) 변수 (Variable) 생성 및 연산 실행


TensorFlow 1.x

: lazy execution, session, global namespaces

TensorFlow 2.x

: eager execution, no session, no globals


import tensorflow as tf


tf.__version__

'1.12.0'


w = tf.Variable([[1.], [2.]])

x = tf.constant([[3., 4.]])


print(w)

<tf.Variable 'Variable:0' shape=(2, 1) dtype=float32_ref>


print(x)
Tensor("Const_2:0", shape=(1, 2), 
dtype=float32)


# lazy execution

w_x_matmul = tf.matmul(w, x)

print(w_x_matmul)

Tensor("MatMul:0", shape=(2, 2), 
dtype=float32)


with tf.Session() as sess:
    # initialization of variable
    sess.run(tf.global_variables_initializer())
    
    # runn tf.Session
    print(sess.run(w_x_matmul))


[[3. 4.]
 [6. 8.]]


import tensorflow as tf


tf.__version__

'2.3.0'


w = tf.Variable([[1.], [2.]])

x = tf.constant([[3., 4.]]) 


print(w)

<tf.Variable 'Variable:0' shape=(2, 1) dtype=float32, numpy=
array([[1.],
       [2.]], dtype=float32)>



print(x)

tf.Tensor([[3. 4.]], shape=(1, 2), 
dtype=float32)



# eager execution

# no need for tf.global_variables_initializer()

w_x_matmul = tf.matmul(w, x)

print(w_x_matmul)

tf.Tensor(
[[3. 4.]
 [6. 8.]], shape=(2, 2), 
dtype=float32)


tf.Variable() 로 생성한 w 변수(Variable)와 tf.constant()로 생성한 상수로 tf.matmul() 메소드로 행렬곱 연산을 수행하여 보았습니다. 


TensorFlow 1.x 버전에서는 지연 연산 (lazy execution) 모드이므로 tf.matmul(w, x) 에서는 아무런 연산이 일어나지 않으며, Session을 열고 session.run()으로 명시적으로 호출할 때에야 비로서 행렬곱 연산이 실행됩니다. 더불어서, 변수를 Session 에서 실행시키기 전에 tf.global_variables_initializer() 를 먼저 호출해서 변수를 초기화(initialization) 해주어야 합니다. 


반면에 TensorFlow 2.x 버전에서는 tf.Variable(), tf.constant() 로 값을 할당해주면 바로 실행이 되어 변수와 상수가 생성이 됩니다. 그리고 tf.matmul() 메소드로 행렬곱 연산을 즉시 수행하여 결과값을 반환받습니다. (이때 tf.global_variables_initializer() 를 호출할 필요는 없습니다.) TenforFlow 1.x 대비 TensorFlow 2.x 가 코드가 한결 깔끔해졌습니다. 




  (3) tf.placeholder(...), feed_dict


Placeholder 는 TensorFlow에서 그래프를 실행할 때 사용자가 데이터를 주입할 수 있는 통로입니다. TensforFlow 1.x 버전에서는 아래처럼 Session을 실행할 때 feed_dict 매개변수를 사용하여 feed_dict = {placeholder: input} 처럼 사전형으로 값을 입력할 수 있습니다. 

반면에 TensorFlow 2.x 버전에서는 Session() 실행없이 바로 함수(function)에 입력값(input)을 넣어서 즉시 실행할 수 있습니다. 역시 코드가 한결 깔끔해졌습니다. 


# TensorFlow 1.x

outputs = session.run(f(placeholder), feed_dict={placeholder: input})


# TensorFlow 2.x

outputs = f(input)



TensorFlow 1.x:

Session, Placeholder & feed_dict 

 TensorFlow 2.x:

function


import tensorflow as tf


tf.__version__

'1.12.0'


# placeholder

a = tf.placeholder(tf.int32)

b = tf.placeholder(tf.int32)


# defining a summation function

def tf_sum(a, b):

    return a + b


# lazy execution

a_b_sum = tf_sum(a, b)


print(a_b_sum)

Tensor("add_1:0", dtype=int32)



with tf.Session() as sess:

    print(sess.run(a_b_sum, 

                  feed_dict = {a: [0, 1, 2], 

                               b: [10, 20, 30]}))


[10 21 32]



import tensorflow as tf


tf.__version__

'2.3.0'


a = tf.constant([0, 1, 2])

b = tf.constant([10, 20, 30])


# define a summation fuction

def tf_sum(a, b):

    return a + b


# eager execution

a_b_sum = tf_sum(a, b)


print(a_b_sum)

tf.Tensor([10 21 32], shape=(3,), 
dtype=int32)



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. 



728x90
반응형
Posted by Rfriend
,