이미지 인식 대회의 2012년 winner였던 alex net의 논문을 리뷰한다.

당시 압도적인 성능을 보였던 뿐만아니라 후대의 신경망들에 큰 영향을 미쳤다

 

 

1. Architecture

 

총 8개의 층을 가지는데 5개의 conv층 + 3개의 fc층을 가지며 GPU 두개로 병렬처리한다. 

  • ReLU
  • gpu 2대 병렬처리
  • Local response norm
  • overlapping pooling
  • dropout
  • agumentation

이러한 특징을 가진다.

 

 

 

2. Dataset

 

  • train 1.2m / validation 50000 / test 15000 images 
  • 모든이미지를 256*256으로 통일 시켜 사용 (256*256*3)
  • 직사각형 이미지의 경우 짧은 면을 256으로 조정하고 가운데를 중심으로 자름

 

3. Architecture Detail

 

  3-1. ReLU

전통적으로 쓰이던 tanh나 sigmoid를 사용하지 않고 ReLU를 사용했다.

아래 그래프에서 보이듯 error가 0.25에 도달하기까지의 epoch가 많이 차이난다.

 3-2. Multiple GPUs

당시 gpu로는 메모리가 부족으로 사용했다. 몇개의 레이어에서만 서로의 값을 공유한다.

(alexnet에서는 어쩔수 없었지만 이 방법이 후대의 경량화 모델에 많은 영감을 주었다)

 

 3-3. Local Response Nomalization

 ReLU를 Saturated 부분이 없지만 그 값이 무한히 커질 가능성이 있어 너무 큰 값이 주변 값들을 무시할 수도 있다.

 

AlexNet에서는 ReLU 결과 값을 Normalization 하여 일반화 된 모델을 만들기 위해 Local Response Normalization (LRN) 을 사용했다.

 

논문에서는  k=2, n=5, alpha = 10**-4,  beta = 0.75 를 사용했다.

채널중에서 n = 5기 때문에 만약 7번째 채널을본다면

5,6,7,8,9 이렇게 앞뒤로 같은 위치의 액티배이션값을 제곱한 한것을 분모에 두어 정규화한다. 

lateral inhibition

이는 lateral inhibition(측면억제)이라는 실제 우리의 뇌에서 일어나는 증상을 본따 만든것으로 위의 그림을 보면 흰색선에 집중하지 않는다면 회색의 점이 봉니는데 이는 흰색으로 둘러싸인 사각형에서 억제를 발생시키기 때문에 흰색이 반감ㅇ되어 보이는것 입니다. LRN도 이렇게 relu로 아주 커진 값을 주변에 맞게 억제하는 방향을 원합니다.

 

 3-4. Overlapping Pooling

 

 오버래핑 풀링은 stride를 풀링 사이즈보다 작게 가져가 겹치는 부분이 생기게 하는 기법인데 이런 풀링 방법을 적용하여 과적합을 방지했다고 한다.  

 예를 들어 위 예의 경우 2번째 사각형의 5는 왼쪽 사각형의 4에비에 큰 자극임에도 불구하고 일반 풀링의 경우 5는 사라지게 된다 하지만 오버래핑풀링의 경우 5의 정보가 남게 되는 것이다.

 이는 오버피팅을 방지하는 효과를 가져왔지만 풀링 이후의 사이즈가 작아지는 정도가 일반 풀링에 비해 크긱 때문에 연산량 메모리 측면에서 좋지않아 추후의 모델에서는 잘 사용되지않는 테크닉이다

 

3-5. Drop out

 

과적합을 방지하기 위해서 drop아웃을 사용했다. 이는 학습시에만 사용되었으며 예측시에는 사용되지않느다.

 

co-adaptation

 = 신경망의 학습 중, 어느 시점에서 같은 층의 두 개 이상의 노드의 입력 및 출력 연결강도가 같아지면, 아무리 학습이 진행되어도 그 노드들은 같은 일을 수행하게 되어 불필요한 중복이 생기는 문제를 말한다. 

0.5의 드롭아웃을 줘서 co-adaptation을 막았다.

 

3-6. Agumentation

 

256으로 자른이미지를 총 10로 증강시키는 기법을 사용햇다

 

가운데 이미지 + 좌상단 이미지 + 우상단이미지 + 좌하단 + 우하단 이미지 이렇게 5개와

좌우로 반전한것의 5개를 활용해서 10개의 이미지로 증강하여 사용한다. 

 

'CV > classification' 카테고리의 다른 글

[CV] ResNet  (0) 2022.05.03
[CV] GoogLeNet  (0) 2022.05.02
[CV] VGG Net 리뷰  (0) 2022.05.02
[CV] Network in Network 리뷰  (0) 2022.05.02
[CV] ZF Net 리뷰  (0) 2022.05.01

내가 좋아하는 침착맨님 , 윈도우 배경으로 만드는 것은 전게시물에 있다 그 결과물을 바탕으로 얼굴을 인식하여 모자이크를 해주는 프로그램이다.

 

 

import dlib
import cv2
import numpy as np
from imutils import face_utils,resize
# cap = cv2.VideoCapture(0) 
cap = cv2.VideoCapture('remove.avi')
if not cap.isOpened(): 
    exit() 

detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')

fourcc = cv2.VideoWriter_fourcc(*'DIVX')
width = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS) 

# 저장 파일명, 코덱, FPS, 크기 (width, height)
out = cv2.VideoWriter('mosaic_chim.avi', fourcc, fps, (width, height))

    
while True:
    ret, frame = cap.read()
    if not ret:
        break
        
    img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces =  detector(img_gray)
    for face in faces:
        landmarks = predictor(img_gray, face)
        points = face_utils.shape_to_np(landmarks)
        rect = cv2.boundingRect(points)
        x,y,w,h = rect

#         cv2.rectangle(frame, (x,y), (x+w,y+h), (0,0,255), 1)
        
        mosaic = frame[y:y+h, x:x+w].copy()
        mosaic = cv2.resize(mosaic, dsize=None, fx=0.1, fy=0.1, interpolation=cv2.INTER_NEAREST)
        mosaic = cv2.resize(mosaic, dsize=(w,h), interpolation=cv2.INTER_NEAREST)
        frame[y:y+h, x:x+w] = mosaic
        
    out.write(frame) 
    cv2.imshow('camera', frame)
    if cv2.waitKey(1) == ord('q'): # 사용자가 q 를 입력하면 
        break
        
out.release() 
cap.release()
cv2.destroyAllWindows()

 

 

dlib은 얼굴과  라이브러리이다. python에서는 pip로 쉽게 설치할 수 있다.

dlib로 face detection, face landmark detection 등을 수행가능하다. 

 

 

shape_predictor_68_face_landmarks.data로 사람얼굴의 68가지 랜드마크를 인식하고

외접하는 사각형을 만들어 그부분의 해상도를 극히 낮추어 씌운 방식이다.

'CV > open cv' 카테고리의 다른 글

[cv] python 픽셀아트 만들기  (0) 2022.04.14

 

이미지를 받아오고 panel에 들어온 값에 따라 해상도(?)를 설정하여 픽셀의 형태로 만드는 코드

 

import cv2

file_name = 'rosicky.jpg'
image = cv2.imread(file_name)

panel = np.zeros([100,400], np.uint8)
cv2.namedWindow('panel')

def nothing(x):
    pass

cv2.createTrackbar('FX', 'panel', 1,100, nothing)
cv2.createTrackbar('FY', 'panel', 1,100, nothing)

return_name = file_name.replace('.','_pixel.')

while True:
    
    # 트랙바의 위치에 따라 변수에 값할당
    fx = cv2.getTrackbarPos('FX', 'panel') / 100
    fy = cv2.getTrackbarPos('FY', 'panel') / 100

    pixel = cv2.resize(image, dsize=None, fx=fx, fy=fy, interpolation=cv2.INTER_NEAREST)
    pixel = cv2.resize(pixel, dsize=(int(image.shape[1]*1),int(image.shape[0]*1)), interpolation=cv2.INTER_NEAREST)
    
    cv2.imshow('image', image)
    cv2.imshow('pixel', pixel)
    cv2.imshow('panel', panel)
    
    if cv2.waitKey(1) == ord('s'):
        cv2.imwrite(return_name, pixel)
    if cv2.waitKey(1) == ord('q'):
        break

cv2.waitKey(0)
cv2.destroyAllWindows()

 

pixel = cv2.resize(image, dsize=None, fx=fx, fy=fy, interpolation=cv2.INTER_NEAREST)

pixel = cv2.resize(pixel, dsize=(int(image.shape[1]*1),int(image.shape[0]*1)), interpolation=cv2.INTER_NEAREST)

 

이미지의 사이즈를 조정하는 방법 중 제일 퀄리티가 떨어지게되는 INTER_NEAREST 옵션을 사용하여 줄이고 다시 원래크기로 복원하여 해상도를 낮게 만들어 픽셀화한다.

panel에 크기를 줄이는 가중치를 받아와 실시간으로 결과가 어떻게 변하는지를 확인할 수 있도록함

 

s를 입력시 동명의 이름에 _pixel이 붙은 상태로 저장이되고 

q를 입력시 종료된다.

'CV > open cv' 카테고리의 다른 글

[cv] 얼굴인식하여 얼굴부분만 모자이크하기  (2) 2022.04.14

 

 

해외 사이트를 크롤링하려고 시도 했을때 해외에서 접근을 막은 (한국의 접근을)막은 사이트의 경우 프록시를 우회하여

접근할 필요가 있다 그 경우에 사용했던 코드이다..

 

 

무료 프록시 제공사이트에서 포트와 ip를 받아오고 (크롤링) 그것을 적용하는 request코드이다.

from bs4 import BeautifulSoup
import pandas as pd
import requests
import re

headers = {'User-Agent':'--ari/537.36'}

import random

def random_us_proxy():
    
    proxy_url = "https://www.us-proxy.org/"

    res = requests.get(proxy_url)
    soup = BeautifulSoup(res.text,'lxml')

    table = soup.find('tbody')
    rows = table.find_all('tr')
    proxy_server_list = []

    for row in rows:
        https = row.find('td', attrs = {'class':'hx'})
        if https.text == 'yes':
            ip = row.find_all('td')[0].text
            port = row.find_all('td')[1].text
            server = f"{ip}:{port}"
            proxy_server_list.append(server)

    proxy_server = random.choices(proxy_server_list)[0]
    return proxy_server

proxy_server = random_us_proxy()
proxies = {"http": 'http://' + proxy_server, 'https': 'http://' + proxy_server}

res = requests.get(url, headers = headers, proxies=proxies)
res.raise_for_status() # 오류가 생기면 셀종료
soup = BeautifulSoup(res.text, 'lxml')

 

 

셀레니움 코드이다.

from selenium.webdriver.common.proxy import Proxy, ProxyType
from selenium import webdriver


proxy_ip_port = random_us_proxy()

proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = proxy_ip_port
proxy.ssl_proxy = proxy_ip_port

capabilities = webdriver.DesiredCapabilities.CHROME
proxy.add_to_capabilities(capabilities)

# replace 'your_absolute_path' with your chrome binary absolute path
driver = webdriver.Chrome(desired_capabilities=capabilities)

뉴스나 인터넷 방송, 영화촬영에 사용하는 크로마키 배경을 제거하는 방법을 파이썬으로 구현

 

 

1. 크로마키란

 

화면 합성 등의 특수 효과를 이용하기 위해 이용하는 배경. 흔히 초록색과 파란색을 사용하여 그린 스크린, 블루 스크린이라고도 한다. 촬영 과정에서 배우가 단색 배경 앞에서 연기를 하고 후편집 과정에서 같은 색으로 찍힌 부분을 다른 배경으로 바꾸면 바꾼 배경에서 연기한 것과 같은 효과를 낼 수 있다.

원래는 'chroma key'로 두 단어이지만 한국에서는 한 단어인 것처럼 '크로마키'라고 쓴다

 

[출처] 나무위키

 

[출처] http://news.samsungdisplay.com/15102

2. 크로마키의 원리 

초록색배경을 투명화하는 것이다.

원래 크로마키 기술에서는 어떻게 적용되는지는 모르겠으나 파이썬에서는 초록색이 아닌부분을 추출하고 그 부분을 배경으로원하는 부분위에 올려다 놓는다.

 

3. 코드 

 

좌측과 중앙이미지를 결합하여 우측이미지를 만든다

 

import cv2
import numpy as np

cap = cv2.imread('chim55cap.png')

# 배경사이즈를 타겟이미지와 맞춤
window = cv2.imread('windowbg.jpg')
window = cv2.resize(window, (cap.shape[1],cap.shape[0])) # width, height 고정 크기

# 트랙바 생성
panel = np.zeros([100,400], np.uint8)
cv2.namedWindow('panel')

def nothing(x):
    pass

# 트랙바의 범위 설정
cv2.createTrackbar('L-H', 'panel', 0,179, nothing)
cv2.createTrackbar('U-H', 'panel', 179,179, nothing)

cv2.createTrackbar('L-S', 'panel', 0,255, nothing)
cv2.createTrackbar('U-S', 'panel', 255,255, nothing)

cv2.createTrackbar('L-V', 'panel', 0,255, nothing)
cv2.createTrackbar('U-V', 'panel', 255,255, nothing)

# 초록면 없애기 위해 hsv로 변환
hsv = cv2.cvtColor(cap, cv2.COLOR_BGR2HSV)
while True:
    
    # 트랙바의 위치에 따라 변수에 값할당
    l_h = cv2.getTrackbarPos('L-H', 'panel')
    u_h = cv2.getTrackbarPos('U-H', 'panel')
    l_s = cv2.getTrackbarPos('L-S', 'panel')
    u_s = cv2.getTrackbarPos('U-S', 'panel')
    l_v = cv2.getTrackbarPos('L-V', 'panel')
    u_v = cv2.getTrackbarPos('U-V', 'panel')
    
    lower_green = np.array([l_h,l_s,l_v])
    upper_green = np.array([u_h,u_s,u_v])
    
    # 범위사이에 있는 놈들을 마스크로 만들어라
    mask = cv2.inRange(hsv, lower_green, upper_green) 
    mask_inv = cv2.bitwise_not(mask) # 마스크를 거꾸로만들어라
    
    bg = cv2.bitwise_and(cap, cap, mask=mask) # lg와 ug 사이 놈들을 마스크 사이놈들만 살린다
    
    # 침착맨의 실루엣을 추출(마스킹)
    fg = cv2.bitwise_and(cap, cap, mask=mask_inv) # 위에거의 반대
    
    # 윈도우 배경에서 침착맨의 실루엣을 제거
    window_bg = cv2.bitwise_and(window, window, mask=mask) 
    
    # 두화면을 더함
    result = cv2.addWeighted(src1 = fg, src2=window_bg, alpha=1, beta=1, gamma=0)

#     cv2.imshow('bg', bg)
#     cv2.imshow('fg', fg)
#     cv2.imshow('window', window_bg)
    cv2.imshow('result', result)
    cv2.imshow('panel', panel)
    
    if cv2.waitKey(1) == ord('q'):
        break

    cv2.imwrite('result.png', result) # png 형태로 저장
    
cv2.destroyAllWindows()

실행하면 트랙바가 생기게 되는데 

트랙바의 위치 즉 마스킹을 할 범위에 따라서 타겟 이미지가 마스킹 될지 안될지 변한다 트랙바를 적절히 변경을하고 키보드의 q를 누르면 저장이되고 해당 셀이 종료되는 코드이다..

 

간단하게 원리를 알아보면 

 

\

 

이미지의 색을 hsv로 변환시켜서  설정한 hsv값의 범위안에 속하는 녀석들만 남기는 것이다.

배경은 그것의 반대로 응용하고 두개를 합치면된다.

 

 

4. 영상에서 응용

 

cap = cv2.VideoCapture('chim55.mp4')

window = cv2.imread('windowbg.jpg')
# 코덱 정의
fourcc = cv2.VideoWriter_fourcc(*'DIVX')

# 프레임 크기, FPS
width = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS) 

# 저장 파일명, 코덱, FPS, 크기 (width, height)
out = cv2.VideoWriter('remove.avi', fourcc, fps, (width, height))
window = cv2.resize(window, (width, height)) # width, height 고정 크기


while cap.isOpened():
    ret, frame = cap.read()
    
    if not ret:
        break
    
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv, lower_green, upper_green) # 사이에 있는 놈들을 마스크로 만들어라
    mask_inv = cv2.bitwise_not(mask) # 마스크를 거꾸로만들어라
    
    bg = cv2.bitwise_and(frame, frame, mask=mask) # lg와 ug 사이 놈들을 마스크 사이놈들만 살린다
    fg = cv2.bitwise_and(frame, frame, mask=mask_inv) # 위에거의 반대
    
    window_bg = cv2.bitwise_and(window, window, mask=mask) 
    
    result = cv2.addWeighted(src1 = fg, src2=window_bg, alpha=1, beta=1, gamma=0)
    
    if not ret:
        break
    
    out.write(result) # 영상 데이터만 저장 (소리 X)
    cv2.imshow('video', result)
    
        
out.release() # 자원 해제
cap.release()
cv2.destroyAllWindows()

위에서 설정한 범위에 따라서 영상을 저장한다

 

https://www.youtube.com/watch?v=6TGyI4XjlCk&t=1153s 

대부분 해당 영상에서 가져왔습니다.

 

 

컨볼루션 신경망이란 (Convolution Neural Network,  CNN) 

 

- 완전 연결 네트워크의 문제로 부터 시작

  ~ 매개변수의 폭발적인 증가

  ~ 공간 추론의 부족 (픽셀 사이 근접성 개념이 소실되는 문제)

 

- 합성곱 계층은 입력이미지가 커져도 매개변수 개수에 영향을 주지않음

 

- 어떤 이미지에도 차원 수와 상관없이 적용 가능

 

[이미지 출처]  https://medium.com/@pechyonkin/key-deep-learning-architectures-lenet-5-6fc3c59e6f4

 

 

2. 컨볼루션 연산

 

- 필터연산 

  ~ 입력데이터에 필터를 통해 연산을 진행

  ~ 필터에 대응하는 원소끼리 곱하고, 합을 구함

  ~ 연산이 완료된 결과를 feature 맵이라고 함

 

- 필터(filter)
  ~ 커널(kernel)이라고도 칭함

  ~ 필터의 사이즈는 "거의 항상 홀수"
       * 짝수이면 패딩이 비대칭이 되어버림

       * 왼쪽, 오른쪽을 다르게 주어야함

       * 중심위치가 존재, 즉 구별된 하나의 픽셀(중심 픽셀)이 존재

 ~ 필터의 학습 파라미터 개수는 입력 데이터의 크기와 상관없이 일정
 ~ 따라서, 과적합을 방지할 수 있음

 

[출처] https://towardsdatascience.com/intuitively-understanding-convolutions-for-deep-learning-1f6f42faee1

  ~ 일반적으로 합성곱 연산을 한 후 사이즈는

        (n - f + 1) * (n - f + 1)

         * n은 입력데이터의 크기, f는 필터의 크기  (정사각 일 떄)

         * 위의 경우 (5-3+1) = 3

 

- 패딩

  ~ 입력 데이터의 주변을 특정값으로 채우는 기법(주로 0으로 채움)

  ~ 전 이미지와 크기를 같이 하기위함

 

[출처] https://towardsdatascience.com/intuitively-understanding-convolutions-for-deep-learning-1f6f42faee1

  ~ TF에서 padding='valid'  시 패딩x padding='same' 시 입력값과 출력값의 크기를 같게

     (padding_size = (filter_size-1)/2, stride=1)

 

 

- 스트라이드

  ~ 필터를 적용하는 간격을 의미

  ~ 커지면 출력 데이터가 작아짐(과적합x)

     

- TF

conv = Conv2D(filters=N, kernel_size=(k,k), strides=5,
              padding='valid', activation='relu')

k*k size의 필터를 N개 적용하는 층, padding은 없고 strides 는 5

 

 

- 풀링 (Pooling)

  ~맥스풀링 (Max Pooling)

    * 가장 많이 사용

    * 일반적으로 strides=2, kernel_size=2 를 통해 특징맵을 절반으로 줄임

    * 물체의 주요한 특징을 학습할 수 있더록

  ~ 평균풀링(Avg Pooling)

     * 필터 내의 평균을 구하는과정

     * 일반적으로 strides=2, kernel_size=2 를 통해 특징맵을 절반으로 줄임

     * 과거에 많이 사용 요즘에는 거의 사용 x

 

 

- LeNet

최초의 cnn모델 위에서 배운 풀링을 활용하여 구현

 

원 모델은 activation 으로 sigmoid를 사용했으나 예제에서는 relu활용

from tensorflow.keras import Model
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from tensorflow.keras.datasets import mnist

import numpy as np


class LeNet5(Model):
  def __init__(self, num_classes):
    super(LeNet5, self).__init__()
    self.conv1 = Conv2D(6, kernel_size=(5,5), padding='same', activation='relu')
    self.conv2 = Conv2D(16, kernel_size=(5,5), activation='relu')
    self.max_pool = MaxPooling2D(pool_size=(2,2))
    self.flatten = Flatten()
    self.dense1 = Dense(120, activation='relu')
    self.dense2 = Dense(84, activation='relu')
    self.dense3 = Dense(10, activation='softmax')
  
  def call(self, input_data):
    x = self.max_pool(self.conv1(input_data))
    x = self.max_pool(self.conv2(x))
    x = self.flatten(x)
    x = self.dense3(self.dense2(self.dense1(x)))

    return x
    
model.compile(optimizer='sgd',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
  
callbacks = [tf.keras.callbacks.EarlyStopping(patience=3, monitor='val_loss'),
             tf.keras.callbacks.TensorBoard(log_dir='./logs', histogram_freq=1)]
             
             
model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          validation_data=(x_test,y_test),
          callbacks=callbacks)
          
%load_ext tensorboard         
%tensorboard --logdir logs

mnist에서의 결과

'TF' 카테고리의 다른 글

[TF] 딥러닝 학습기술  (0) 2022.04.12
[TF] 모델의 저장, callbacks  (0) 2022.04.11
[TF] 모델 컴파일 및 학습 mnist  (0) 2022.04.11
[TF] Layer, Model, 모델구성  (0) 2022.04.11

하이퍼파라미터 튜닝

 

1. 장점

- 다른  튜닝방법들이 존재하지만 상대적으로 빠르다고 할 수 있고

- 파라미터별 중요도를 직관적으로 확인할 수 있는 시각화까지 제공하여 용이하다

- 적용이 직관적이고 어렵지 않다는 장점 또한 뺴먹을수없다..

 

 

1. 모듈 임포트

import optuna
from optuna import Trial, visualization
from optuna.samplers import TPESampler

 

2. 목적값 정의

def lgbm_params_fold_start(lgb_params, X, y):
    
    skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=722)
    skf.split(X, X['Gender'])

    pred_list = []
    mae_list = []

    for fold,(train_index, val_index) in enumerate(skf.split(X,  X['Gender'])):

        print(f'***********{fold+1}th fold start***********')
        x_train, x_val, y_train, y_val = X.loc[train_index], X.loc[val_index], y.loc[train_index], y.loc[val_index]

        lgb = lightgbm.LGBMRegressor(**lgb_params, categorical_feature=[0])

        lgb.fit(x_train, y_train,
                eval_set=(x_val,y_val),
                eval_metric=NMAE, verbose=False, early_stopping_rounds=100)

        pred = lgb.predict(x_val)
        result = NMAE(pred,y_val)[1]

        mae_list.append(result)
        
    return np.mean(mae_list)

 

먼저 최적화될 모델을 정의한다. 나는 5폴드의 lgbm을 정의했고 주목해야할 점은  lgb_params를 받아 학습을 실시하고 최적화할 해를 리턴값으로 리턴하는다는점이다. 폴드를 적용하지 않는다면 objective에 바로 모델을 넣어도 무방하다.

 

def objectiveLGB(trial: Trial, X, y):
    param = {
        'boosting_type' : 'gbdt',
        "n_estimators" : 10000,
#         'fold_size':trial.suggest_int('fold_size', 4, 16),
        'max_depth':trial.suggest_int('max_depth', 4, 16),
        'random_state': 722,
        'reg_alpha': trial.suggest_loguniform('reg_alpha', 1e-8, 10.0),
        'reg_lambda': trial.suggest_loguniform('reg_lambda', 1e-8, 10.0),
        'num_leaves': trial.suggest_int('num_leaves', 8, 32),
        'colsample_bytree': trial.suggest_uniform('colsample_bytree', 0.5, 1.0),
        'subsample': trial.suggest_uniform('subsample', 0.8, 1.0),
        'subsample_freq': trial.suggest_int('subsample_freq', 1, 8),
        'min_child_samples': trial.suggest_int('min_child_samples', 16, 64),
        'learning_rate': 0.025
    }
    score = lgbm_params_fold_start(param, X, y)

    return score

이후 파라미터의 범위를 지정하는 objective함수를 정의한다. 

딕셔너리 형태로 정의하며 각 파라미터의 특성에 맞게 아래의 것으 선택하면되겠다.

 

- suggest_int : 범위 내의 정수형 값

ex) 'max_depth':trial.suggest_int('max_depth', 4, 16) 

 

- suggest_int : 범위 내의 실수형 값

ex) 'max_depth':trial.suggest_float('max_depth', 4, 16) 

 

* 위 두 방법은 log=True 메소드를 통해 로그값도 가능

 

- suggest_categorical : list 내의 데이터 선택

ex)  'colsample_bytree': trial.suggest_categorical('colsample_bytree', [0.5,0.6,0.7])

 

- suggest_uniform : 범위내의 균등분포를 값으로 선택

ex) 'colsample_bytree': trial.suggest_uniform('colsample_bytree', 0.5, 1.0)

 

- suggest_discrete_uniform : 범위내의 이산균등분포를 값으로 선택

ex) 'colsample_bytree': trial.suggest_uniform('colsample_bytree', 0.5, 1.0, 0.1)

 

- suggest_loguniform : 범위 내의 로그함수 선상의 값을 선택

ex) 'reg_lambda': trial.suggest_loguniform('reg_lambda', 1e-8, 10.0)

 

def objective(trial):
    x, y = ...
 
    classifier_name = trial.suggest_categorical("classifier", ["SVC", "RandomForest"])
    if classifier_name == "SVC":
        svc_c = trial.suggest_float("svc_c", 1e-10, 1e10, log=True)
        classifier_obj = sklearn.svm.SVC(...")
    else:
        rf_max_depth = trial.suggest_int("rf_max_depth", 2, 32, log=True)
        classifier_obj = sklearn.ensemble.RandomForestClassifier(...)
 
    accuracy = ...
    return accuracy

위 와같은 방법으로 모델 선택도 가능

 

 

3. 실행

 

study = optuna.create_study(direction='minimize',sampler=TPESampler(seed=42))
study.optimize(lambda trial : objectiveLGB(trial, X, y), n_trials=100)
print('Best trial: score {},\nparams {}'.format(study.best_trial.value,study.best_trial.params))

study 라는 객체를 생성하여 실행한다. 

 

- direction 은 목적한 objective 함수를 최소화할 것인지 최소화할것인지 입력한다 minimize, maximize

- sampler는 어떻게 값을 찾을 것인지 결정한다. 

https://optuna.readthedocs.io/en/stable/reference/samplers.html

 

객체의 optimize 메소드를 활용해 튜닝을 시작한다. 

- n_trials 는 시도횟수를 의미한다.

 

완료되면 study best_trial.value 메소드를 이용하여 목적함수의 값

best_trial.params 메소드를 이용해 파라미터들의 값을 받을 수 있다.

 

 

4. 시각화

 

optuna.visualization.plot_param_importances(study)

각 파라미터의 중요도를 나타내는 그래프

 

optuna.visualization.plot_optimization_history(study)

 

최적화 과정을 보여주는 그래프

'ML' 카테고리의 다른 글

[ML] 교차검증, cross-validation  (0) 2022.04.11
[ML] 인공신경망 학습  (0) 2022.02.17
[ ML ] 인공신경망 기본  (0) 2022.02.16
[ML] cycling encoding  (0) 2022.02.15
[ML] XGBoost - Classification  (0) 2022.01.18

과대적합과 과소적합을 해결하는 딥러닝학습기술

 

 

 

1. 모델의 크기조절

- 가장 단순한 방법

- 모델의 크기를 줄인다는 것은 학습 파라미터의 수를 줄이는 것

- 당연히 학습속도도 같이 커지거나 작아짐

- 파라미터의 수가 클 수록 과적합에 더욱민감하잼

val_loss

 

model 의 경우 총 160,305개의 파라미터

model(small)의 경우 70,007개의 파라미터

model(big)의 경우 총 1,296,769개의 파라미터

 

2. 옵티마이저

 

- 확률적 경사하강법(SGD)

  전체를 한번에 계산하지않고 확률적으로 일부 샘플을 뽑아 조금씩 나누어 학습

  데이터의 수가 적어지기대문에 한 번 처리하는 속도는 빠름

  손실함수의 최솟값에 이르기 까지 다소 위아래로 요동

  단순하지만 문제에 따라 시간이 매우 오래걸림

 

- Momentum

  운동량을 의미, 관성을 의미

  이전의 속도를 유지하려는 성향

  SGD보다 방향이 적게 변함

from tensorflow.keras.optimizers import SGD

optimizer = SGD(learning_rate=0.001, momentum=0.9)
# 모멘텀안주면 일반적인거 씀

 

 

- Nesterov

  모멘텀의 방향으로 조금 앞선곳에서 그래디언트르르 구함

  시간이 지날수록 좀 더 빠르게 최솟값도달

optimizer = SGD(lr=0.001, momentum=0.9, nesterov=True)

 

 

- AdaGrad

  가장 가파른 경사를 따라 하강하는 방법

  학습률을 자동으로 변화시킴

  학습률이 너무 감소되어 전역최솟값에 도달하기 전에 학습이 빨리 종료되기도 하기댸문에 딥러닝에서 자제

from tensorflow.keras.optimizers import Adagrad
optimizer = Adagrad(learning_rate=0.01, epsilon=None, decay=0.0)

 

- RMSProp

  adagrad를 보완하기위해 등장

  합 대신 지수의 평균값을 활용

  학습이 잘안되면 학습률을 키우고 너무 크면 다시줄임

 

from tensorflow.keras.optimizers import RMSprop

optimizer = RMSprop(lr=0.001, rho=0.9, epsilon=None, decay=0.0)

 

- Adam

  모멘텀의 최적화와 RMSProp의 아이디어를 더한것

  지난 그래디언트의 지수 감소 평균을 따르고(Momentum), 지난 그레디언트 제곱의 지수 감소된 평균(RMSProp)을 따    름

  가장많이 사용되는 방법

 

from tensorflow.keras.optimizers import Adam

optimizer = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)

 

 

- 최적화 optimizer 비교

3. 가중치 초기화

 

- 가중치 소실

  활성화함수가 선형일 때, 은닉층의 갯수가 늘어 날수록 가중치가 역전파되며 가중치 소실문제 발생

  * 0~1사이로 출력되면서 0또는 1에 가중치값이 편중됨 이는 미분값이 0에 가까워짐을 의미

  * relu 등장

- 가중치 초기화문제

  가중치의 값이 치우쳐지면 표현 가능한 신경망의 수가 적어짐

  따라서 활성화 값이 골고루 분포되는것이 중요

 

- Xavier (Glorot)

  은닉층의 노드 수가 n이면 표준편차가 1/sqrt(n) 인분포

  활성화함수가 선형인 경우 매우적합

 

- He

  표준편차가 sqrt(2/n) 인분포

  활성화함수가 비선형일떄 매우적합

 

Dense(1, kernel_initializer='he_normal')

Dense(1, kernel_initializer='glorot_normal')

 

 

4. 배치 정규화

 

- 모델에 들어가는 샘플들을 균일하게 만드는 방법

 

- 가중치의 활성화값이 적당히 퍼지게끔함

 

- 학습을 빠르게 진행시킴

 

- 일반화성능ㅇ을 늘림

 

- 과대적합방지

 

- 주로  Dense또는 Conv2D 이후, 활성화함수 이전에 놓임

 

from tensorflow.keras.layers import BatchNormalization, Dense, Activation
from tensorflow.keras.utils import plot_model

model = Sequential()

model.add(Dense(32, input_shape=(28*28,), kernel_initializer='he_normal'))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.summary()

plot_model(model)

5. 규제 regularzation 

 

- 과대적합(Overfitting, 오버피팅)을 방지하는 방법 중 하나


- 과대적합은 가중치의 매개변수 값이 커서 발생하는 경우가 많음


- 이를 방지하기 위해 큰 가중치 값에 큰 규제를 가하는 것


- 규제란 가중치의 절댓값을 가능한 작게 만드는 것으로, 가중치의 모든 원소를 0에 가깝게 하여 모든 특성이 출력에 주    는 영향을 최소한으로 만드는 것(기울기를 작게 만드는 것)을 의미


- 복잡한 네트워크 일수록 네트워크의 복잡도에 제한을 두어 가중치가 작은 값을 가지도록 함


- 적절한 규제값을 찾는 것이 중요

 

- L2 규제: 가중치의 제곱에 비례하는 비용이 추가(흔히 가중치 감쇠라고도 불림)
  가중치의 제곱합 

  손실함수값에 일정값을 더함으로써 과적합을 방지 

  lambda값이 크면 규제가 커지고 반대는 작아진다

  더 로버스트한 모델을 생성하므로 l1보다 더 많이 사용

 

from tensorflow.keras.regularizers import l1, l2, l1_l2

l2_model = Sequential([Dense(16, kernel_regularizer=l2(0.001), activation='relu', input_shape=(10000,)),
                       Dense(16, kernel_regularizer=l2(0.001), activation='relu'),
                       Dense(1, activation='sigmoid')])


- L1 규제: 가중치의 절댓값에 비례하는 비용이 추가
  가중치의 절대값합

  l2규제와 달리 어떤 가중치는 0이되는데 이는 모델이 가벼워짐을 의미

l1_model = Sequential([Dense(16, kernel_regularizer=l1(0.00001), activation='relu', input_shape=(10000,)),
                       Dense(16, kernel_regularizer=l1(0.00001), activation='relu'),
                       Dense(1, activation='sigmoid')])

 

- l1 l2 동시사용

Dense(16, kernel_regularizer=l1_l2(l1=0.00001, l2=0.001), activation='relu', input_shape=(10000,))

 

 

6. Dropout 

 

- 신경망을 위해 사용되는 규제 기법 중 가장 효과적이고 널리 사용되는 방법

 

- 학습할 때 사용하는 노드의 수를 전체 노드 중에서 일부만을 사용

- 신경망의 레이어에 드롭아웃을 적용하면 훈련하는 동안 무작위로 층의 일부 특성(노드)를 제외

 

- 테스트 단계에서는 그 어떤 노드도 드롭아웃 되지 않고, 대신 해당 레이어의 출력 노드를 드롭아웃 비율에 맞게 줄여줌

 

dropout_model = Sequential([Dense(16, activation='relu', input_shape=(10000,)),
                       Dropout(0.5),
                       Dense(16, activation='relu'),
                       Dropout(0.5),
                       Dense(1, activation='sigmoid')])

'TF' 카테고리의 다른 글

[TF] CNN 컨볼루션 신경망  (0) 2022.04.13
[TF] 모델의 저장, callbacks  (0) 2022.04.11
[TF] 모델 컴파일 및 학습 mnist  (0) 2022.04.11
[TF] Layer, Model, 모델구성  (0) 2022.04.11

 

교차 검증의 필요성

 

val

만약 위와 같이 train과 val set을 고정하고 학습한다면 학습과정에서 valset을 기준으로 학습 및 파라미터 튜닝이 진행 되기 때문에 val set에 과적합되는 경우가 생기게된다. 이를 해결하고자하는 것이 바로 교차검증이다.

 

장점

- 특정 val set에 과적합되는 것을 방지할 수 있다.

- 최대한 모든 데이터를 활용가능하다.

 

단점

- 학습의 속도가 k배로 늘어난다.

 

 

 

1. KFold 

데이터를 k 등분하고 각각 k-1개의 데이터를 trainset으로 나머지 하나의 데이터를 valset으로 활용하는 방법이이다.

 

from sklearn.model_selection import StratifiedKFold, KFold
from sklearn.metrics import mean_absolute_error

kf = KFold(n_splits=5, shuffle=True, random_state=722)

pred_list = []
mae_list = []

for fold,(train_index, val_index) in enumerate(kf.split(X)):

    print(f'***********{fold+1}th fold start***********')
    x_train, x_val, y_train, y_val = X.loc[train_index], X.loc[val_index], y.loc[train_index], y.loc[val_index]
    
    lgb = lightgbm.LGBMRegressor(boosting_type='gbdt',
        n_estimators=1000, random_state=722, learning_rate=0.05, categorical_feature=[0])

    lgb.fit(x_train, y_train,
            eval_set=(x_val,y_val),
            eval_metric='mae', verbose=False, early_stopping_rounds=100)

    pred = lgb.predict(x_val)
    result = mean_absolute_error(pred,y_val)
    
    mae_list.append(result)
    print(f'mae : {result:.4f}', end='\n\n')
    
print(f'mean mae {np.mean(mae_list):.4f}')
***********1th fold start***********
mae : 1.6950

***********2th fold start***********
mae : 1.6058

***********3th fold start***********
mae : 1.5267

***********4th fold start***********
mae : 1.5838

***********5th fold start***********
mae : 1.5302

mean mae 1.5883

kf = KFold(n_splits=5, shuffle=True, random_state=722)

- n_splits : k

- shuffle=True (섞을까?)

위와 같이 Kfold 객체를 생성하고 

for fold,(train_index, val_index) in enumerate(kf.split(X)):

split(x) 메소드로 각 인덱스를 받게 된다

 

이후 위의 코드처럼 학습을 진행하면된다.

 

2. StratifiedKFold

kfold를 하더라도 어느 fold의 트레인셋에 특정  class의 값이 편중되어있다면 해당클래스에 과적합될 것이다. 이를 방지하기 위하여 StratifiedKFold를 사용한다.

 

데이터를 k등분 한다는 점은 kfold와 같지만 StratifiedKFold는 범주형변수의 비율에 맞게 fold들을 반환한다.

 

예측하려는 변수가 범주형이라면 타겟을 fold를 나누는데에 활용하면 될 것이고

예측하려는 변수가 연속형이라면 타겟의 차이가 크게 나타나는 범주형변수나 구간화를 진행하여 활용하면 된다.

skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=722)

for fold,(train_index, val_index) in enumerate(kf.split(X)):

    print(f'***********{fold+1}th fold start***********')
    x_train, x_val, y_train, y_val = X.loc[train_index], X.loc[val_index], y.loc[train_index], y.loc[val_index]

'ML' 카테고리의 다른 글

[ML] 파라미터 튜닝 optuna - lgbm  (0) 2022.04.12
[ML] 인공신경망 학습  (0) 2022.02.17
[ ML ] 인공신경망 기본  (0) 2022.02.16
[ML] cycling encoding  (0) 2022.02.15
[ML] XGBoost - Classification  (0) 2022.01.18

 

https://www.youtube.com/watch?v=28QbrkRkHlo&t=5526s 

 

대부분 위의 영상을 참고했다.

 

 

 

모델의 저장과 복원

- save() 메소드를 이용하여 저장

 

- load_model() 을 이용해 복원

 

- Sequencial API, 함수형 API에서는 모델의 저장 및 로드가 가능하지만 서브클래싱 방식으로는 할 수 없음

 

- 서브클래싱 방식은 save_weights()와 load_weights()를 이용해 모델의 파라미터만 저장 및 로드

 

- JSON 형식
model.to_json() (저장)
tf.keras.models.model_from_json(file_path) (복원)

 

- YAML로 직렬화
model.to_yaml() (저장)
tf.keras.models.model_from_yaml(file_path) (복원)

 

model.save('mnist_model.h5')
loaded_model = models.load_model('mnist_model.h5')
loaded_model.summary()

 

 

콜백 (callbacks)

- 학습시에 callbacks 변수를 통해 훈련의 시작이나 끝에 호출할 객체 지정

- 여러개 같이 사용가능

 

from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, LearningRateScheduler, TensorBoard

 

1. ModelCheckpoint

 

-정기적으로 모델의 체크포인트를 저장하고 문제가 발생시 복구하는데에 사용

check_point_cb = ModelCheckpoint('keras_mnist_model.h5', save_best_only=True)
history = model.fit(x_train, y_train, epochs=10,
                    validation_data=(x_val, y_val),
                    callbacks=[check_point_cb])

save_best_only=True 시 최상의 모델만을 저장 (validation 의 성능을 보고 판단)

 

 

2. EarlyStopping

 

- val_set이 한동안 개선되지 않을 경우 학습을 중단할 때 사용

- 일정 patience 동안 val_set의 점수가 오르지 않으면 학습을 중단함

- 모델이 좋아지지않으면 학습이 자동으로 중단되므로 epochs를 크게 가져가도 무방하다 (과적합에 주의)

- 학습이 끝난 후 최상의 가중치를 복원한다.

check_point_cb = ModelCheckpoint('keras_mnist_model.h5', save_best_only=True)
earlystopping_cb = EarlyStopping(patience=3, monitor='val_loss',
                                 restore_best_weights=True)
history = model.fit(x_train, y_train, epochs=10,
                    validation_data=(x_val, y_val),
                    callbacks=[check_point_cb, earlystopping_cb])

 

3. LearnigRateScheduler

- 학습중에 학습률을 변경시키기 위해 사용

def scheduler(epoch, leaning_rate):
  if epoch < 10:
    return leaning_rate
  else:
    return leaning_rate * tf.math.exp(-0.1)
    
    
print(round(model.optimizer.lr.numpy(),5))
lr_scheduler_cb = LearningRateScheduler(scheduler)

history = model.fit(x_train, y_train, epochs=15,
                    callbacks=[lr_scheduler_cb], verbose=0)

print(round(model.optimizer.lr.numpy(),5))

 

0.01
0.00607

함수를 정의하여 lr을 어떻게 변화시킬지 정의해야함

 

4. Tensorboard

 

- 학습과정 모니터링하기 위함

-

log_dir = './logs'
tensor_board_cb = [TensorBoard(log_dir=log_dir, histogram_freq=1, write_graph=True, write_images=True,)]
model.fit(x_train, y_train, batch_size=32, validation_data=(x_val, y_val),
          epochs=30, callbacks=tensor_board_cb)
%load_ext tensorboard
#%tensorboard --logdir {log_dir} port 8000
%tensorboard --logdir {log_dir}

이런식으로 동적으로 확인할 수 있는  창이 뜸

'TF' 카테고리의 다른 글

[TF] CNN 컨볼루션 신경망  (0) 2022.04.13
[TF] 딥러닝 학습기술  (0) 2022.04.12
[TF] 모델 컴파일 및 학습 mnist  (0) 2022.04.11
[TF] Layer, Model, 모델구성  (0) 2022.04.11

+ Recent posts