OpenCV

: 실시간 이미지/영상 처리에 사용하는 오픈 소스 라이브러리 

: Python, C++, Java 와 같은 다양한 개발 환경을 지원

: Windows, Linux, Mac OS, iOS Android같은 다양한 OS를 지원하는 크로스 플랫폼

 

 

Cascade Classifier : 다단계 분류

- Haar Cascade Object Detection 모듈

     : 머신러닝 기반 Object Detection 알고리즘. OpenCV의 대표적인 API

     : 다수의 객체 이미지와 객체가 아닌 이미지를 cascade 함수로 트레이닝 시켜 객체 검출

     : 미리 정해진 방식으로 인식(xml 파일에 학습데이터 저장되어 있음)

       ↔ Machine Learning : data training 방식

     : 빠른 Object Detection 가능, 간단, 가벼움

     : 정확도가 낮아 예외 사항에 약함

 

import cv2
import numpy as np
from tkinter import *
from PIL import Image
from PIL import ImageTk
from tkinter import filedialog

#xml에 있는 얼굴인식 파일정보를 기반으로 detection
face_cascade_name = './opencv/data/haarcascades/haarcascade_frontalface_alt.xml'
eyes_cascade_name = './opencv/data/haarcascades/haarcascade_eye_tree_eyeglasses.xml'

file_name = 'soccer.jpg'
title_name = 'Haar cascade object detection'
frame_width = 500 #사이즈가 크면 인식하는게 더 많기도 함(Haar cascade의 경우)

def selectFile():
    file_name=filedialog.askopenfilename(initialdir="./", title="Select file",filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
    print('File name: ', file_name)

    read_image = cv2.imread(file_name)
    (height, width) = read_image.shape[:2]
    frameSize = int(sizeSpin.get())
    ratio = frameSize / width
    dimension = (frameSize, int(height * ratio))
    read_image = cv2.resize(read_image, dimension, interpolation = cv2.INTER_AREA)
    image = cv2.cvtColor(read_image, cv2.COLOR_BGR2RGB)
    image = Image.fromarray(image)
    imgtk = ImageTk.PhotoImage(image=image)
    detectAndDisplay(read_image)

def detectAndDisplay(frame):
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)#Gray scale로 바꿔줌
    frame_gray = cv2.equalizeHist(frame_gray) #히스토그램 이용 -> 디지털 이미지
    #-- Detect faces
    faces = face_cascade.detectMultiScale(frame_gray) #MultiScale 특정 영역.. 얼굴정보

    for(x,y,w,h) in faces:
        center = (x+w//2, y+h//2) #사각형(얼굴) 중간
        frame = cv2.rectangle(frame, (x,y), (x+w, y+h), (0,255,0), 4) #녹색 사각형(두께4)그리기
        faceROI = frame_gray[y:y+h, x:x+w] #관심영역 = 금방 선택한 얼굴
        #-- In each face, detect eyes
        eyes = eyes_cascade.detectMultiScale(faceROI)
        for (x2,y2,w2,h2) in eyes:
            eye_center = (x + x2 + w2//2, y + y2 + h2//2)
            radius = int(round((w2 + h2)*0.25)) #반지금 구하기 : 정사각형모양에 0.25곱해서(4분의 1)실수, 반올림
            frame = cv2.circle(frame, eye_center, radius, (255,0,0),4)

    #cv2.imshow("Capture - Face detection", frame)
    #버튼을 눌러 새로운 화면 보여줌
    image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    image= Image.fromarray(image)
    imgtk = ImageTk.PhotoImage(image = image)
    detection.config(image=imgtk)
    detection.image = imgtk


#main
main = Tk()
main.title(title_name)
main.geometry()

read_img = cv2.imread(file_name)
(height, width) = read_img.shape[:2]
ratio = frame_width / width
dimension = (frame_width, int(height * ratio))
read_img = cv2.resize(read_img, dimension, interpolation = cv2.INTER_AREA)

image = cv2.cvtColor(read_img, cv2.COLOR_BGR2RGB) #이걸 안하면 색이 좀 빛바랜 것처럼 나옴
image = Image.fromarray(image)
imgtk = ImageTk.PhotoImage(image = image) #Tk Pillow 위에 그리기 위한 과정

#cv2.imshow("Original Image", image)

face_cascade = cv2.CascadeClassifier()
eyes_cascade = cv2.CascadeClassifier()

#-- 1. Load the cascades
if not face_cascade.load(cv2.samples.findFile(face_cascade_name)):
    print("--(!) Error Loading face cascade")
    exit(0)
if not eyes_cascade.load(cv2.samples.findFile(eyes_cascade_name)):
    print("--(!) Error Loading eyes cascade")
    exit(0)
    

label = Label(main, text=title_name) #타이틀
label.config(font=("Courier", 18))
label.grid(row=0, column=0, columnspan=4)

sizeLabel = Label(main, text='Frame Width : ')
sizeLabel.grid(row=1, column=0)

sizeVal = IntVar(value = frame_width)
sizeSpin = Spinbox(main, textvariable = sizeVal, from_=0, to=2000, increment=100, justify=RIGHT) #부모 윈도우 안에,,,
sizeSpin.grid(row=1, column=1)

Button(main, text="File Select", height=2, command=lambda:selectFile()).grid(row=1,column=2, columnspan=2)
detection = Label(main, image=imgtk)
detection.grid(row=2, column=0, columnspan=4)
detectAndDisplay(read_img)

사진 크기에 따라 인식률이 달라지는 것을 확인할 수 있다

'Study > Deep Learning' 카테고리의 다른 글

Gradient Clipping  (0) 2021.10.20
TensorFlow 2.0을 이용한 Char-RNN 구현  (0) 2021.10.20
Embedding  (0) 2021.10.20
순환신경망(RNN)  (0) 2021.10.19
Pre-Trained CNN 모델을 이용한 Image Classification  (0) 2021.10.18

깊은 인공 신경망을 학습하다보면 계산과정에서 Vanishing gradient problem(기울기 소실)과 반대로

Gradient가 무한히 커지는 Exploding Gradient Problem(폭주)이 발생할 수 있다.

출처 http://www.cs.toronto.edu/~rgrosse/courses/csc321_2017/readings/L15%20Exploding%20and%20Vanishing%20Gradients.pdf


특히, RNN은 순환 연산 구조를 갖기 때문에 이 문제가 발생하기 더 쉽다.

 

하지만 Gradient Clipping이라는 방법론을 통해 손쉽게 해결 가능하다.

Gradient에 threshold(한계점)을 지정해 놓고 이 값을 넘어가는 현상이 발생할 시 gradient값을 threshold로 지정하는 것이다.

 

아래 코드는 1,2,3,4를 넣으면 2,4,6,8의 경향성을 학습해 y=2x의 Linear Regression을 수행하는 예제의 일부이다.

 

grad_clip = 5 # clipping을 적용할 임계치를 설정


# 최적화를 위한 function을 정의
@tf.function
def train_step(model, x, y):
  with tf.GradientTape() as tape:
    y_pred = model(x)
    loss = mse_loss(y_pred, y)
  gradients = tape.gradient(loss, model.trainable_variables) # gradients에는 개별 Layer의 노드 별 gradient 값 저장
  # Gradient Clipping을 적용
  clipped_grads = [] # 빈 리스트
  for grad in gradients: 
    clipped_grads.append(tf.clip_by_norm(grad, grad_clip)) # 임계치보다 커지면 clip
  optimizer.apply_gradients(zip(clipped_grads, model.trainable_variables)) # optimizer에 반영시 clipped된 gradient값을 반영하도록 함

'Study > Deep Learning' 카테고리의 다른 글

Object Detection by Haar cascade  (0) 2021.11.10
TensorFlow 2.0을 이용한 Char-RNN 구현  (0) 2021.10.20
Embedding  (0) 2021.10.20
순환신경망(RNN)  (0) 2021.10.19
Pre-Trained CNN 모델을 이용한 Image Classification  (0) 2021.10.18
더보기
# -*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function, unicode_literals

from absl import app
import tensorflow as tf

import numpy as np
import os
import time

# supervised learning을 위한 training data set 구성
# input 데이터와 input 데이터를 한글자씩 뒤로 민 target 데이터를 생성하는 utility 함수를 정의
def split_input_target(chunk):
  input_text = chunk[:-1]  #한글자 뒤로 밀기 전 text
  target_text = chunk[1:]  #한글자 뒤로 민 text

  return input_text, target_text 

# 학습에 필요한 설정값들을 지정
data_dir = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')  # 셰익스피어 희곡 다운로드
batch_size = 64      # Training : 64, Sampling : 1 , 한 번의 mini-batch gradient descent 실행할 때의 개수
seq_length = 100     # Training : 100, Sampling : 1 , 몇 글자를 하나의 시계열로 볼 것인지
embedding_dim = 256  # Embedding matrix에서 형변환 하는 차원수
hidden_size = 1024   # RNN 히든 레이어의 노드 개수
num_epochs = 10      # 전체 학습 반복 횟수

# 학습에 사용할 txt 파일을 읽음
text = open(data_dir, 'rb').read().decode(encoding='utf-8')
# 학습데이터에 포함된 모든 character들을 나타내는 변수인 vocab과 vocab에 id를 부여해 dict 형태로 만든 char2idx를 선언
vocab = sorted(set(text))  # 유니크한 character 개수. 단어집합 생성
vocab_size = len(vocab)
print('{} unique characters'.format(vocab_size))
char2idx = {u: i for i, u in enumerate(vocab)} # character와 인덱스(int)를 연결시켜 하나의 dictionary 형태로 만들어줌
idx2char = np.array(vocab)

# 이렇게 만든 dictionary를 이용해 모든 글자(character)를 integer 변환을 수행, numpy array로 만듬
text_as_int = np.array([char2idx[c] for c in text]) 

# split_input_target 함수를 이용해서 input 데이터와 input 데이터를 한글자씩 뒤로 민 target 데이터를 생성
# sequence 개수만큼 묶은 후, batch 사이즈로 묶음
char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int) 
sequences = char_dataset.batch(seq_length+1, drop_remainder=True) # sequence 수만큼 묶음
dataset = sequences.map(split_input_target)

# tf.data API를 이용해서 데이터를 섞고 batch 형태로 가져옴
dataset = dataset.shuffle(10000).batch(batch_size, drop_remainder=True) # 전체 데이터를 batch 단위로 묶음


# tf.keras.Model을 이용해서 RNN 모델을 정의
class RNN(tf.keras.Model):
 def __init__(self, batch_size):
   super(RNN, self).__init__()
   self.embedding_layer = tf.keras.layers.Embedding(vocab_size, embedding_dim,
                                                    batch_input_shape=[batch_size, None])
   self.hidden_layer_1 = tf.keras.layers.LSTM(hidden_size,
                                             return_sequences=True,
                                             stateful=True,
                                             recurrent_initializer='glorot_uniform')
   self.output_layer = tf.keras.layers.Dense(vocab_size)

 def call(self, x):
   embedded_input = self.embedding_layer(x)
   features = self.hidden_layer_1(embedded_input)
   logits = self.output_layer(features)

   return logits

# sparse cross-entropy 손실 함수를 정의☆
def sparse_cross_entropy_loss(labels, logits):
  return tf.reduce_mean(tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True))

# 최적화를 위한 Adam 옵티마이저를 정의
optimizer = tf.keras.optimizers.Adam()

# 최적화를 위한 function을 정의
@tf.function
def train_step(model, input, target):
  with tf.GradientTape() as tape:
    logits = model(input)
    loss = sparse_cross_entropy_loss(target, logits)
  grads = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(grads, model.trainable_variables))

  return loss # 현재 상태의 loss function 값 반환

def generate_text(model, start_string):
  num_sampling = 4000  # 생성할 글자(Character)의 개수를 지정

  # start_string을 integer 형태로 변환
  input_eval = [char2idx[s] for s in start_string] # 처음 input으로 들어온 character를 int로 변환
  input_eval = tf.expand_dims(input_eval, 0)

  # 4000개의 전체 샘플링 결과로 생성된 string을 저장할 배열을 초기화
  text_generated = []

  # 낮은 temperature 값은 더욱 정확한 텍스트를 생성
  # 높은 temperature 값은 더욱 다양한 텍스트를 생성
  temperature = 1.0

  # 여기서 batch size = 1 
  model.reset_states()
  for i in range(num_sampling):
    predictions = model(input_eval)
    # 불필요한 batch dimension을 삭제
    predictions = tf.squeeze(predictions, 0)

    # argmax 샘플링 시 결과의 다양성이 사라지기 때문에
    # argmax의 장점은 가져가고 랜덤성은 높이는 것이 필요함
    # 따라서 모델의 예측결과에 기반해서 랜덤 샘플링을 하기위해 categorical distribution을 사용
    predictions = predictions / temperature
    predicted_id = tf.random.categorical(predictions, num_samples=1)[-1,0].numpy()

    # 예측된 character를 다음 input으로 사용
    input_eval = tf.expand_dims([predicted_id], 0)
    # 샘플링 결과를 text_generated 배열에 추가
    text_generated.append(idx2char[predicted_id])

  return (start_string + ''.join(text_generated))

def main(_):
  # Recurrent Neural Networks(RNN) 모델을 선언
  RNN_model = RNN(batch_size=batch_size)

  # Sanity Check - 하나의 데이터를 뽑아 문제가 없는지 확인
  # 데이터 구조 파악을 위해서 예제로 임의의 하나의 배치 데이터 예측하고, 예측결과를 출력
  for input_example_batch, target_example_batch in dataset.take(1):
    example_batch_predictions = RNN_model(input_example_batch)
    print(example_batch_predictions.shape, "# (batch_size, sequence_length, vocab_size)")

  # 현재 모델 구조를 출력
  RNN_model.summary() 

  # checkpoint 데이터를 저장할 경로를 지정
  checkpoint_dir = './training_checkpoints' # 학습 중간중간의 파라미터 저장
  checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}")

  for epoch in range(num_epochs):
    start = time.time()

    # 매 반복마다 hidden state 초기화(최초의 hidden 값은 None)
    hidden = RNN_model.reset_states()

    for (batch_n, (input, target)) in enumerate(dataset): # 데이터 셋에 있는 것들을 batch 단위로 가져옴
      loss = train_step(RNN_model, input, target) 

      if batch_n % 100 == 0: # 100번의 gradient descent 수행할 때마다 
        template = 'Epoch {} Batch {} Loss {}'
        print(template.format(epoch+1, batch_n, loss)) # 현재 상태의 loss 값 출력

    # 5회 반복마다 파라미터 결과값을 checkpoint로 저장
    if (epoch + 1) % 5 == 0:
      RNN_model.save_weights(checkpoint_prefix.format(epoch=epoch))

    print ('Epoch {} Loss {:.4f}'.format(epoch+1, loss)) # 매 epoch 마다 loss 값 감소량 출력
    print ('Time taken for 1 epoch {} sec\n'.format(time.time() - start))

  RNN_model.save_weights(checkpoint_prefix.format(epoch=epoch))
  print("트레이닝이 끝났습니다!")

  # 실제 sampling을 위한 RNN 구조 생성
  sampling_RNN_model = RNN(batch_size=1)
  sampling_RNN_model.load_weights(tf.train.latest_checkpoint(checkpoint_dir)) # 학습해놨던 파라미터를 불러옴
  sampling_RNN_model.build(tf.TensorShape([1, None]))
  sampling_RNN_model.summary()

  # 샘플링 시작
  print("샘플링을 시작합니다!")
  print(generate_text(sampling_RNN_model, start_string=u' ')) # 공백부터 시작

if __name__ == '__main__':
  # main 함수 호출
  app.run(main)

 

 

summary 함수를 통한 결과. RNN의 구조 확인 가능
epoch 별 중간중간 loss 값 확인할 수 있고 training이 끝난 후 실제 sampling 과정을 시작한다.

셰익스피어의 희곡을 학습한 후 RNN 모델이 새로운 희곡을 작성한 결과이다.

한 눈에 봐도 그럴듯한 텍스트를 생성해낸 모습을 볼 수 있다. 

자세히 보면 단어의 구성과 흐름은 말이 안되지만 희곡의 글자 배열 등 구조적으로 그럴듯한 모습을 띈다.

 

'Study > Deep Learning' 카테고리의 다른 글

Object Detection by Haar cascade  (0) 2021.11.10
Gradient Clipping  (0) 2021.10.20
Embedding  (0) 2021.10.20
순환신경망(RNN)  (0) 2021.10.19
Pre-Trained CNN 모델을 이용한 Image Classification  (0) 2021.10.18

임베딩, Embedding

: 자연어 처리 문제를 다룰 때 널리 사용되는 기법

 

머신러닝 알고리즘에서 데이터를 표현하는 일반적인 방법은 One-hot Encoding이다.

 

하지만 이 표현법은 Sparse하다는 문제점을 가지고 있다. (sparse? 희소, 부족)

예를 들어 10,000개의 단어사전에 있는 단어 하나를 one-hot encoding으로 표현하면 10,000x1 행렬에서 1개의 행에만 1이라는 값이 들어있고, 나머지 9999개의 행에는 0이라는 의미없는 값이 들어가 있게 된다. 

 

이런 문제를 해결하기 위해서 Sparse한 데이터 표현을 Dense한 표현형태로 변환하는 기법이 필요해졌고, 이것이 Embedding이다. 

이는 원본 데이터에 Dense한 임베딩 행렬(Embedding matrix)을 곱하는 방식으로 진행된다.

임베딩 수행 예시

 

Embedding의 장점

1. sparse 한 데이터 표현을 dense한 표현 형태로 변경하여 딥러닝 모델이 학습하기 좋은 형태가 됨

2. dimension reduction 효과로 연산량 감소

3. 의미있는 embedding metrix 였다면 embedding vector가 유의미한 단어들 간의 연관성을 표현할 수 있게 됨

(벡터 형상 자체에 어느 정도의 의미 정도 포함)

 

 

Language Modeling

: 자연어 처리에서 광범위하게 사용되는 개념

단어 배열에 기반하여 다음에 어떤 단어가 오는게 적합한지 예측하는 문제

 

 

Char-RNN

: 하나의 글자 Character를 RNN의 입력값으로 받고, RNN은 다음에 올 글자를 예측하는 문제

 

이 학습을 위해 RNN의 타겟 데이터를 인풋 문장에서 한 글자씩 뒤로 민 형태로 구성하면 된다.

ex) "HELLO"라는 문장을 학습하고자 하는 경우, (인풋 데이터, 타겟 데이터) 쌍을 (H,E),(E,L),(L,L),(L,O)로 구성하면 된다.

출처 https://jfun.tistory.com/190

 

인풋 데이터는 Character에 대한 원-핫 인코딩으로 표현된다. 

Char-RNN의 출력값 형태는 학습에 사용하는 전체 문자(단어) 집합에 대한 Softmax 출력 값이다.

 

따라서 영어 문자로만 구성된 데이터셋일 경우, 전체 문자 집합은 알파벳 글자 개수인 26이 된다.

즉, Char-RNN의 출력값은 다음에 올 26개의 알파벳 문자에 대한 확신의 정도를 나타내는 26x1 크기의 행렬이 된다.

그 중에서 argmax로 가장 확률이 높은 글자를 다음에 올 글자로 확정하고 그 글자를 이용해서 또 다음에 올 글자를 예측하는 과정을 반복한다.

 

 

 

'Study > Deep Learning' 카테고리의 다른 글

Gradient Clipping  (0) 2021.10.20
TensorFlow 2.0을 이용한 Char-RNN 구현  (0) 2021.10.20
순환신경망(RNN)  (0) 2021.10.19
Pre-Trained CNN 모델을 이용한 Image Classification  (0) 2021.10.18
Fine-Tuning(Transfer Learning)  (0) 2021.10.18

순환신경망(Recurrent Neural Networks, RNN)

: 자연어 처리(Natural Language Processing) 문제에 주로 사용되는 인공신경망 구조

: 시계열 데이터를 다루기에 최적화된 인공신경망

(시계열 데이터? 시간축을 중심으로 현재 시간의 데이터가 앞, 뒤 시간의 데이터와 연관 관계를 가지고 있는 데이터. 주식 가격, 파형으로 표현되는 음성 데이터, 앞뒤 문맥을 가진 단어들의 집합으로 표현되는 자연어 데이터 등)

 

 

 

<RNN 구조>

이미지 출처&nbsp;https://en.wikipedia.org/wiki/File:Recurrent_neural_network_unfold.svg

: 기존 ANN 구조에서 이전 시간(t-1)의 은닉층의 출력값을 다음 시간(t)에 은닉층의 입력값으로 다시 집어넣는 경로가 추가된 형태

: 현재 시간 t의 결과가 다음 시간 t+1에 영향을 미치고 이는 다시 다음 t+2에 영향을 미치는 과정이 끊임없이 반복됨

 

→ 이러한 구조를 통해 얻을 수 있는 장점은 이전 상태에 대한 정보를 일종의 메모리 형태로 저장할 수 있다는 점

 

 

 

<Vanishing Gradient Problem, 경사도 사라짐 문제>

이미지 출처&nbsp;https://gruuuuu.github.io/machine-learning/lstm-doc2/

 

시간 1에서 입력받은 데이터는 시간 1에서 RNN 파라미터를 업데이트하거나 예측을 진행하는데 강한 영향력(=큰 경사도)을 끼친다. 하지만 2,3,...에서 계속 새로운 데이터가 들어옴으로써 새로 들어온 데이터의 영향력에 덮어 쓰여서, 시간 1에서 입력받은 데이터의 영향력은 조금씩 사라지다가 시간 7쯤에서 영향력이 완전히 사라지는 현상이 발생한다.

 

--> RNN의 문제점 : 장기기억력을 가지지 못한다는 점. 

현재 시간에서 가까운 시간의 데이터만 고려해서 예측을 진행하게 되고, 현재 시간보다 오래 전의 데이터를 고려해서 예측해야하는 상황에서 RNN의 성능이 감소하는 결과를 낳는다.

 

 

따라서 이런 문제를 해결하기 위해 생긴 RNN구조가 바로 LSTM(Long-Short Term Memory Networks, 장/단기 기억 네트워크)이다.

 

이미지 출처&nbsp;https://en.wikipedia.org/wiki/File:Long_Short-Term_Memory.svg

LSTM은 은닉층의 각각의 노드를 인풋 게이트 Input Gate, 포겟 게이트 Forget Gate, 아웃풋 게이트 Output Gate로 구성된 메모리 블럭 Memory Block이라는 복잡한 구조로 대체된다.

 

 

아래 그림에서 ○는 게이트가 열려있음을, ㅡ는 게이트가 닫혀있음을 뜻한다. 

시간 1에서의 인풋데이터를 받은 이후에 Input Gate를 닫아버려서 새로운 인풋을 받지 않고, Forget Gate를 열어놔서 시간 1에서의 인풋데이터를 계속해서 전달받으면, 시간 1에서의 인풋의 영향력을 계속해서 가져갈 수 있다. 마지막으로, Output Gate를 열고 닫으면서, 시간 1에서의 인풋데이터의 영향력을 반영하거나 반영하지 않을 수 있다.

 

 

 

<Gate Recurrent Unit, GRU>

: LSTM의 간략화된 버전

: LSTM의 3개의 게이트를 2개의 게이트(리셋 게이트(r)업데이트 게이트(z))로 축소

 

이는 LSTM보다 학습 속도가 빠르다고 알려져있지만, 비슷한 성능을 보인다고 평가되어진다.

데이터 양이 적을 때는 GRU, 데이터 양이 많을 때는 LSTM을 사용하는 것이 좋다.

 

GRU 구조

'Study > Deep Learning' 카테고리의 다른 글

TensorFlow 2.0을 이용한 Char-RNN 구현  (0) 2021.10.20
Embedding  (0) 2021.10.20
Pre-Trained CNN 모델을 이용한 Image Classification  (0) 2021.10.18
Fine-Tuning(Transfer Learning)  (0) 2021.10.18
TensorBoard 이용하기  (0) 2021.10.18

tf.keras.applications 모듈을 이용한 VGGNet의 Fine-Tuning을 통한 Cats vs Dogs Dataset 분류

 

kaggle에서 제공하는 Cats vs Dogs 데이터셋을 이용해 이 둘을 분류해내는 예제이다.

https://www.kaggle.com/c/dogs-vs-cats

 

Dogs vs. Cats | Kaggle

 

www.kaggle.com

 

 

아래는 Google colab으로 작성된 글이다.

먼저 필요한 라이브러리들을 import하는 과정이 필요하다.

 

그리고 나중에 이미지 사이즈를 160x160으로 고정하기 위한 전역변수 IMG_SIZE를 선언한다.

 

format_example 함수 부분

tf.cast.(image, tf.float32) : 이미지를 인풋으로 받고, float 형태로 변환

image = (image / 127.5) -1 : 이미지를 [-1, 1]로 정규화. 이미지 데이터 실수 범위를 -1에서 1 사이로 만들어 줌

그 후 이미지 사이즈 재조정하는 부분이 나온다.

 

 

Cats vs Dogs 데이터셋을 다운받고 불러오는 과정이다.

tensorflow dataset(tfds)에 이미 'cats_vs_dogs'라는 이름으로 데이터셋이 저장되어 있기 때문에 저렇게 간단히 다운로드 할 수 있다.

80%는 training data, 10%는 validation(확인) data, 나머지 10%는 test data로 사용하기 위해 3부분으로 나눠 불러온다.

 

 

label 이름들은 metadata에서 label이라는 컬럼에 있으니

이를 가져오고, 문자열로 바꿔주는 과정이 필요하다.

그리고 확인해본다.

 

 

첫 번째 나눴던 부분 raw_train에서 2개의 이미지를 불러와 확인해보는 과정이다.

plt.figure() : figure()함수를 불러냄

imshow()로 이미지를 불러오고, title()로 그에 맞는 label을 제목으로 지정한다.

그리고 show()로 화면에 출력한다.

 

여기까지가 사전 학습된 모델 데이터를 전처리하는 코드였다.


이미지 사이즈를 지정하고 

사전 학습 model을 'VGG16' 모델로 지정한다.

 

input 이미지는 160x160, 3개에서 VGG16을 거치게 되면서 5x5, 512개로 변환됨을 알 수 있다.

 

그리고 trainable = False로 지정함으로써 전체 파라미터를 frozen 상태로 정한다. (Fine-Tuning 세 번째 전략)

 

VGG16의 구조를 살펴보면 위의 형태와 같다.

두 번째 실습에선 block5_con1, block5_con2, block5_con3 부분까지 Fine-Tuning 범위로 지정할 것이다.

 

 

대략 이런 흐름?

초기 loss 값과 정확도를 측정해본다. 

 

 

history는 dict 형태로 저장되어 있는 것을 알 수 있다

 

이를 우리가 사용할 변수명으로 바꿔준다.

 

첫 번째 학습 결과를 시각화해본다.

10번의 epoch 동안의 training data와 validation data의 정확도와 손실함수 값 변화가 나타난다. 

 

 

 

현재 base_model은 19층으로 이루어져 있다.

block5_conv1층부터 학습하는 것으로 fine-tuning 하기 위해 위와 같은 과정을 거친다.

 

앞서 진행했던 것과 같이 BinaryCrossentropy를 손실함수로 정해주고

기존 learning rate가 0.0001 이었지만 이를 0.00001로 다시 지정해주면서 정확도를 높이고자 한다.

 

기존 방식으로 10번 학습한 후, 두 번째 Fine-Tuning이 적용된 방식으로 10번 학습한다. 

정확도가 매우 높아지며 손실함수 값은 작아지는 것을 확인할 수 있다.

'Study > Deep Learning' 카테고리의 다른 글

Embedding  (0) 2021.10.20
순환신경망(RNN)  (0) 2021.10.19
Fine-Tuning(Transfer Learning)  (0) 2021.10.18
TensorBoard 이용하기  (0) 2021.10.18
tf.train.CheckpointManager API  (0) 2021.10.18

Transfer Learning(전이 학습)

: 이미 학습된 Neural Networks의 파라미터를 새로운 Task에 맞게 다시 미세조정(Fine-Tuning)하는 것

 

몇몇 사전 학습 모델들은 큰 사이즈의 합성곱 신경망(CNN) 구조를 가지고 있다.

CNN은 크게 두 가지 파트로 나뉜다.

이미지 출처 https://muzukphysics.tistory.com/entry/DL-8-Convolutional-Neural-Network-CNN-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D

1. Convolutional base

: Convolutional 층과 Pooling 층이 여러겹 쌓여있는 부분. 

  이 부분의 목표는 이미지로부터 특징을 효과적으로 추출하는 것이다.

 

2. Classifier

: 주로 완전 연결 계층(모든 계층의 뉴런이 이전 층의 출력노드와 하나도 빠짐없이 모두 연결되어 있는 층)으로 이루어짐.

  이 부분의 목표는 추출된 측징을 잘 학습해서 이미지를 알맞는 카테고리로 분류하는 것이다.

 

→ Convolutional base부분에서 낮은 레벨의 계층(input에 가까운 계층)일수록 일반적인 특징을 추출할 것이고, 높은 레벨의 계층(output과 가까운 계층)과 Classifier 부분은 보다 구체적이고 특유한 특징들을 추출한다.

 


<Fine-Tuning의 세 가지 전략>

이미지 출처 https://adioshun.gitbook.io/deep-learning/transfer-learning/transfer-learning

① 전체 모델을 새로 학습

-  사전학습 모델의 구조만 사용, 나머지는 전부 새로 학습시키기

   따라서 큰 사이즈의 데이터셋이 필요

 

② Convolutional base의 일부분은 고정시킨 상태로, 나머지 계층과 classifier를 새로 학습

-   Convolutional의 낮은 레벨의 계층 혹은 높은 레벨의 계층은 각각 특징이 달랐다.

    따라서 이런 파라미터 중 어느 정도까지 재학습시킬지를 정해야 한다. 

 

ex) 데이터셋이 작고 모델의 파라미터가 많다면, 오버피팅이 될 위험이 있으므로 더 많은 계층을 건들지 않고 그대로 둠

     데이터셋이 크고 모델의 파라미터가 적다면 더 많은 계층을 학습시켜서 진행하고자 하는 프로젝트에 맞게 학습시킴

 

③ Convolutional base 전체는 고정, classifier만 새로 학습

-    컴퓨팅 연산 능력이 부족하거나 데이터셋이 너무 작을때,

     진행하고자 하는 문제가 사전학습모델이 이미 학습한 데이서셋과 매우 비슷할 때 고려할 수 있는 방법

'Study > Deep Learning' 카테고리의 다른 글

순환신경망(RNN)  (0) 2021.10.19
Pre-Trained CNN 모델을 이용한 Image Classification  (0) 2021.10.18
TensorBoard 이용하기  (0) 2021.10.18
tf.train.CheckpointManager API  (0) 2021.10.18
드롭아웃(Dropout)  (0) 2021.10.17

터미널 로그 등을 이용해서 학습 과정을 모니터링 할 경우, 한눈에 학습 과정의 문제점을 파악하기 쉽지 않다.

따라서 TensorFlow 에서는 학습과정 시각화를 위해 TensorBoard 기능을 제공한다.

 

 

 

TensorBoard는 크게 3가지의 API를 제공한다.

 

① tf.summary.scalar : scalar값 형태의 로그 저장

② tf.summary.histogram : histogram 형태의 로그 저장

③ tf.summary.image : 이미지 형태의 로그 저장

 

 

 

[텐서보드 로그 저장법]

① 인자값으로 텐서보드 로그 파일을 저장할 경로르 지정해서 File Writer 생성

summary_writer = tf.summary.create_file_writer('./tensorboard_log')

 

 

② 요약 정보를 남기고 싶은 값을 Writer scope 내에서 tf.summary.* API로 추가

with summary_writer.as_default():

      tf.summary.scalar('loss function', loss, step=optimizer.iterations)

="summary_writer를 as_default()로 열어준 다음, 현재 손실함수 값을 loss function이라는 이름으로 저장하고, 현재 iterations 횟수를 step 인자에 넣겠다"

 

--> epoch 별 손실함수 값이 파일에 저장되며, 이를 불러와 시각화 된 형태로 볼 수 있게 된다.

 

 

[텐서보드 실행방법]

터미널에서 명령어 실행

tensorboard --logdir=로그가 저장된 경로

 

그 후 웹 브라우저에서 실행

URL 창에 localhost:6006 을 입력

 


이전에 진행했던 'CNN을 이용한 MNIST 숫자 분류기 구현'에 TensorBoard 기능을 추가한 예제이다.

https://these-dayss.tistory.com/139

 

TensorFlow 2.0을 이용한 MNIST 숫자분류를 위한 CNN 구현

# -*- coding: utf-8 -*- # Convolutional Neural Networks(CNN)을 이용한 MNIST 분류기(Classifier) - Keras API를 이용한 구현 import tensorflow as tf # MNIST 데이터를 다운로드 (x_train, y_train), (x_test..

these-dayss.tistory.com

 

위의 예제에서 추가 및 수정된 부분만 따로 정리해두었다.

# 텐서보드 summary 정보들을 저장할 폴더 경로를 설정하고 FileWriter를 선언
# trainig 과정에서 발생되는 log를 저장하기 위한 것과 test 과정에서의 log를 저장하기 위한 것을 따로 지정
train_summary_writer = tf.summary.create_file_writer('./tensorboard_log/train')
test_summary_writer = tf.summary.create_file_writer('./tensorboard_log/test')

# 최적화를 위한 function을 정의
@tf.function
def train_step(model, x, y):
  with tf.GradientTape() as tape:
    y_pred, logits = model(x)
    loss = cross_entropy_loss(logits, y)
  # 매 step마다 tf.summary.scalar, tf.summary.image 텐서보드 로그를 기록
  with train_summary_writer.as_default():
    tf.summary.scalar('loss', loss, step=optimizer.iterations) # loss라는 이름으로 현재 iteration time step의 loss값 저장
    x_image = tf.reshape(x, [-1, 28, 28, 1]) # flattening된 상태 (784차원)를 다시 28x28 gray scale 이미지로 저장
    tf.summary.image('training image', x_image, max_outputs=10, step=optimizer.iterations) # training image라는 이름으로 그 시점에서 뽑힌 랜덤 배치의 mnist 트레이닝 이미지 저장. 저장 크기는 최대 10개로 지정

  gradients = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))

# 모델의 정확도를 출력하는 함수를 정의
@tf.function
def compute_accuracy(y_pred, y, summary_writer): # 정확도 측정함수 인자값에 summary_writer 추가
  correct_prediction = tf.equal(tf.argmax(y_pred,1), tf.argmax(y,1))
  accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

  with summary_writer.as_default():
    tf.summary.scalar('accuracy', accuracy, step=optimizer.iterations)

  return accuracy

# Convolutional Neural Networks(CNN) 모델을 선언
CNN_model = CNN()

# 10000 Step만큼 최적화를 수행
for i in range(10000):
  # 50개씩 MNIST 데이터를 불러옴
  batch_x, batch_y = next(train_data_iter)
  # 100 Step마다 training 데이터셋에 대한 정확도를 출력
  if i % 100 == 0: # 100번의 학습마다 정확도 측정
    train_accuracy = compute_accuracy(CNN_model(batch_x)[0], batch_y, train_summary_writer)
    print("반복(Epoch): %d, 트레이닝 데이터 정확도: %f" % (i, train_accuracy))
  # 옵티마이저를 실행해 파라미터를 한스텝 업데이트
  train_step(CNN_model, batch_x, batch_y)

# 학습이 끝나면 학습된 모델의 정확도를 출력. test 데이터셋에 대한 정확도
print("정확도(Accuracy): %f" % compute_accuracy(CNN_model(x_test)[0], y_test, test_summary_writer))

 

결과
파일 생성
각각의 데이터셋(training, test)의 Log를 저장하기 위한 파일 생성
training 이미지셋에 대한 log를 시각화
test 이미지셋에 대한 log를 시각화

실제 문제를 해결하기 위해서는 많은 횟수를 반복하여 학습하는 것이 필요하다.

따라서 중간중간 학습시킨 파라미터를 저장하고, 이를 불러와 이어서 학습을 진행하는 것이 필수적이다.

TensorFlow 2.0에선 tf.train.CheckpointManager를 제공해주어 이 과정을 따를 수 있다.

 

 

[파라미터를 저장하는 법]

① tf.train.Checkpoint 클래스의 인자값으로 저장하고자 하는 tf.keras.Model 인스턴스와 전역 반복횟수를 지정해서 선언

 

ckpt = tf.train.Checkpoint(step=tf.Variable(0), model=CNN_model)

="전역 반복횟수는 0부터 시작, tf.keras.Model 인스턴스에 있는 CNN_model을 사용하겠다"

 

 

 

② tf.train.CheckpointManager에 인자값으로 선언한 tf.train.Checkpoint 인스턴스와 중간 파라미터를 저장할 경로 설정

 

ckpt_manager = tf.train.CheckpointManager(ckpt, directory=SAVER_DIR, max_to_keep=5)

="ckpt 인스턴스를 사용할 것이며, 저장할 경로는 SAVER_DIR이고 최근 5개의 학습 결과를 저장할 것이다."

 

 

 

③ 파라미터를 저장하고자 하는 시점에 해당 시점의 전역 반복횟수를 인자값으로 선언한 tf.train.CheckpointManager의 save 메소드를 호출

 

ckpt_manager.save(checkpoint_number=ckpt.step)

="전역 반복횟수를 저장"

 

 

 

④ tf.train.Checkpoint의 전역 반복 횟수 값(ckpt.step)을 매 반복마다 1씩 증가시킴

 

ckpt.step.assign_add(1)

="전역 반복횟수 1씩 증가"

 

 

[파라미터를 불러오는 법]

① tf.train.latest_checkpoint의 인자값으로 파라미터가 저장된 폴더 경로를 지정해서 가장 최근의 체크포인트 파일의 경로(full path)를 가져옴

 

latest_ckpt = tf.train.latest_checkpoint(SAVER_DIR)

 

 

 

② 선언한 tf.train.CheckpointManager의 restore 함수의 인자값으로 불러올 체크포인트 파일의 경로를 지정해서 파라미터 값을 복원

 

ckpt.restore(latest_ckpt)

 

 


# -*- coding: utf-8 -*-
# CNN을 이용한 MNIST 분류기 - tf.train.CheckpointManager API 예제 - Keras API를 이용한 구현

import tensorflow as tf

# MNIST 데이터를 다운로드 
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# 이미지들을 float32 데이터 타입으로 변경
x_train, x_test = x_train.astype('float32'), x_test.astype('float32')
# 28*28 형태의 이미지를 784차원으로 flattening 
x_train, x_test = x_train.reshape([-1, 784]), x_test.reshape([-1, 784])
# [0, 255] 사이의 값을 [0, 1]사이의 값으로 Normalize
x_train, x_test = x_train / 255., x_test / 255.
# 레이블 데이터에 one-hot encoding을 적용
y_train, y_test = tf.one_hot(y_train, depth=10), tf.one_hot(y_test, depth=10)

# tf.data API를 이용해서 데이터를 섞고 batch 형태로 가져옴
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_data = train_data.repeat().shuffle(60000).batch(50)
train_data_iter = iter(train_data)

# tf.keras.Model을 이용해서 CNN 모델을 정의
class CNN(tf.keras.Model):
  def __init__(self):
    super(CNN, self).__init__()
    # 첫번째 Convolution Layer
    # 5x5 Kernel Size를 가진 32개의 Filter를 적용
    self.conv_layer_1 = tf.keras.layers.Conv2D(filters=32, kernel_size=5, strides=1, padding='same', activation='relu')
    self.pool_layer_1 = tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=2)

    # 두번째 Convolutional Layer
    # 5x5 Kernel Size를 가진 64개의 Filter를 적용
    self.conv_layer_2 = tf.keras.layers.Conv2D(filters=64, kernel_size=5, strides=1, padding='same', activation='relu')
    self.pool_layer_2 = tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=2)

    # Fully Connected Layer
    # 7x7 크기를 가진 64개의 activation map을 1024개의 특징들로 변환
    self.flatten_layer = tf.keras.layers.Flatten()
    self.fc_layer_1 = tf.keras.layers.Dense(1024, activation='relu')

    # Output Layer
    # 1024개의 특징들(feature)을 10개의 클래스-one-hot encoding으로 표현된 숫자 0~9-로 변환
    self.output_layer = tf.keras.layers.Dense(10, activation=None)

  def call(self, x):
    # MNIST 데이터를 3차원 형태로 reshape함. MNIST 데이터는 grayscale 이미지기 때문에 3번째차원(컬러채널)의 값은 1임
    x_image = tf.reshape(x, [-1, 28, 28, 1])
    # 28x28x1 -> 28x28x32
    h_conv1 = self.conv_layer_1(x_image)
    # 28x28x32 -> 14x14x32
    h_pool1 = self.pool_layer_1(h_conv1)
    # 14x14x32 -> 14x14x64
    h_conv2 = self.conv_layer_2(h_pool1)
    # 14x14x64 -> 7x7x64
    h_pool2 = self.pool_layer_2(h_conv2)
    # 7x7x64(3136) -> 1024
    h_pool2_flat = self.flatten_layer(h_pool2)
    h_fc1 = self.fc_layer_1(h_pool2_flat)
    # 1024 -> 10
    logits = self.output_layer(h_fc1)
    y_pred = tf.nn.softmax(logits)

    return y_pred, logits

# cross-entropy 손실 함수를 정의
@tf.function
def cross_entropy_loss(logits, y):
  return tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))

# 최적화를 위한 Adam 옵티마이저를 정의
optimizer = tf.optimizers.Adam(1e-4)

# 최적화를 위한 function을 정의
@tf.function
def train_step(model, x, y):
  with tf.GradientTape() as tape:
    y_pred, logits = model(x)
    loss = cross_entropy_loss(logits, y)
  gradients = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))

# 모델의 정확도를 출력하는 함수를 정의
@tf.function
def compute_accuracy(y_pred, y):
  correct_prediction = tf.equal(tf.argmax(y_pred,1), tf.argmax(y,1))
  accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

  return accuracy

# Convolutional Neural Networks(CNN) 모델을 선언
CNN_model = CNN()

# tf.train.CheckpointManager를 이용해서 파라미터를 저장
SAVER_DIR = "./model"
ckpt = tf.train.Checkpoint(step=tf.Variable(0), model=CNN_model)
ckpt_manager = tf.train.CheckpointManager(
      ckpt, directory=SAVER_DIR, max_to_keep=5)
latest_ckpt = tf.train.latest_checkpoint(SAVER_DIR)

# 만약 저장된 모델과 파라미터가 있으면 이를 불러오고 (Restore)
# Restored 모델을 이용해서 테스트 데이터에 대한 정확도를 출력하고 프로그램을 종료
if latest_ckpt:
  ckpt.restore(latest_ckpt)
  print("테스트 데이터 정확도 (Restored) : %f" % compute_accuracy(CNN_model(x_test)[0], y_test))
  exit()

# 10000 Step만큼 최적화를 수행
while int(ckpt.step) < (10000 + 1):
  # 50개씩 MNIST 데이터를 불러옴
  batch_x, batch_y = next(train_data_iter)
  # 100 Step마다 training 데이터셋에 대한 정확도를 출력하고 tf.train.CheckpointManager를 이용해서 파라미터를 저장
  if ckpt.step % 100 == 0:
    ckpt_manager.save(checkpoint_number=ckpt.step)
    train_accuracy = compute_accuracy(CNN_model(batch_x)[0], batch_y)
    print("반복(Epoch): %d, 트레이닝 데이터 정확도: %f" % (ckpt.step, train_accuracy))
  # 옵티마이저를 실행해 파라미터를 한스텝 업데이트
  train_step(CNN_model, batch_x, batch_y)

  ckpt.step.assign_add(1)

# 학습이 끝나면 테스트 데이터에 대한 정확도를 출력
print("정확도(Accuracy): %f" % compute_accuracy(CNN_model(x_test)[0], y_test))

 

위 코드는 앞서 진행했던 MNIST 숫자분류를 위한 CNN 모델 실습 코드와 거의 유사하며 여기에 checkpoint 기능만 추가한 것이다.

 

https://these-dayss.tistory.com/139

 

TensorFlow 2.0을 이용한 MNIST 숫자분류를 위한 CNN 구현

# -*- coding: utf-8 -*- # Convolutional Neural Networks(CNN)을 이용한 MNIST 분류기(Classifier) - Keras API를 이용한 구현 import tensorflow as tf # MNIST 데이터를 다운로드 (x_train, y_train), (x_test..

these-dayss.tistory.com

 

 

1900번까지 학습한 결과

 

실행 결과, model 폴더가 생성되며, 100번의 학습마다 파라미터 저장 파일들이 생성되었음을 알 수 있고, 

1900번 정도의 학습까지만 시키고 끝냈기 때문에 1500부터 1900까지의 학습 파라미터가 저장된 것을 확인할 수 있다.

(최근 5번의 학습결과)

 

checkpoint라는 이름의 text 파일도 생성되는데, 여기에는 반복횟수에 대한 메타정보 등이 key-value 형태로 들어가 있다.

 

 

두 번째 결과

# 만약 저장된 모델과 파라미터가 있으면 이를 불러오고 (Restore)
# Restored 모델을 이용해서 테스트 데이터에 대한 정확도를 출력하고 프로그램을 종료
if latest_ckpt:
  ckpt.restore(latest_ckpt)
  print("테스트 데이터 정확도 (Restored) : %f" % compute_accuracy(CNN_model(x_test)[0], y_test))
  exit()

실행코드 중 이 if문의 마지막 exit() 함수로 인해서 다시 실행하면 이전 학습했던 상태까지의 정확도를 측정한 후 프로그램이 종료됨을 알 수 있다.

Regularization 기법

: 오버피팅(OverFitting)을 방지할 수 있도록 만들어주는 기법을 총칭하는 말

 

오버피팅? 이전 글 참고

2021.10.14 - [Study/Deep Learning] - 머신러닝 Data 종류

 

머신러닝 Data 종류

머신러닝 모델은 크게 트레이닝 과정과 테스트 과정으로 나뉜다. ▷ 트레이닝 과정 - 대량의 데이터와 충분한 시간을 들여 모델의 최적 파라미터를 찾음 ▷ 테스트 과정 - 트레이닝 과정에서 구

these-dayss.tistory.com

 

 

Dropout이 대표적인 Regularization 기법 중 하나

= 학습 과정에서 일부 노드를 사용하지 않는 형태로 만들어서 오버피팅을 방지할 수 있도록 만들어주는 기법

 

드롭아웃 기법 / 출처 https://sacko.tistory.com/45

%를 정해서 일부 노드를 배제.

적은 노드 개수를 이용해 전체 특징을 학습시켜야 하기 때문에 모델의 압축적인 표현으로 효율성이 높아지게 된다.

 

Training Data에 대해서는 오버피팅을 방지하기 위해서  Dropout을 수행하지만 Test Data에 대해서는 Dropout을 수행하지 않는다.

 

 

 

<Tensorflow에서 제공하는 드롭아웃 API>

 

tf.nn.dropout

 

https://www.tensorflow.org/api_docs/python/tf/nn/dropout

 

tf.nn.dropout  |  TensorFlow Core v2.6.0

Computes dropout: randomly sets elements to zero to prevent overfitting.

www.tensorflow.org

 

'Study > Deep Learning' 카테고리의 다른 글

TensorBoard 이용하기  (0) 2021.10.18
tf.train.CheckpointManager API  (0) 2021.10.18
TensorFlow 2.0을 이용한 MNIST 숫자분류를 위한 CNN 구현  (0) 2021.10.17
CNN(Convolutional Neural Networks)  (0) 2021.10.17
CNN 의 등장  (0) 2021.10.16

+ Recent posts