Deep Learning - #5 CNN 정리 / 실습

임다이·2024년 1월 10일

Deep Learning

목록 보기
6/11

CNN(Convolutional Neural Network)

  • MLP 이미지 분석
    • MLP 신경망을 이미지 처리에 사용한다면 이미지의 위치에 민감하게 동작하며 위치에 종속적인 결과를 얻게 됨(모든 픽셀을 연산하기 때문)
    • MLP는 아래 세 개의 7은 패턴이 다르다고 판단
    • MLP로 이러한 숫자 인식을 하려면 숫자의 크기와 위치를 비슷하게 맞춰야 함


  • 특징들을 추출해서 비교
    -특징을 추출하고 특징 기반으로 학습하는 모델
    -데이터의 일부분을 사용해서 학습

  • 고양이의 눈으로 보는 사물의 형태에 따라 뇌의 특정영역(뉴런)만 활성화 된다는 결과를 기반으로 제안

  • CNN 구조
    -MLP와 다른점
    추가적으로 사용되는 층 : Convolution(특징찾기), Pooling(특징이 아닌 부분 삭제)층 추가 사용
    Feature Extractor - 특징추출부(Conv, Pooling을 사용해서 특징만 모아놓은 데이터를 만든다.
    Classifier - 분류부(특징을 보고 물체 판단 / Dense층 사용)

  • 이미지를 가까운 거리에서 본다면
  • 조금 더 떨어져서 보면
  • 더 떨어져서 보면

축소 샘플링 : 이미지 크기 줄이기

  • 특징값을 모아놓은 것
  • 효율적인 학습을 위해서 데이터의 크기를 줄이는게 이득이다.
  • 데이터의 의미는 거의 간직이 된다.

→ 결과 : 효율적인 학습이 가능하다.


  • 패딩(Padding)
  • 스트라이드(Stride)
  • 풀링(Pooling)

신경망 성능 개선

  • 과대적합 피하는 방법 - dropout
    드롭아웃은 학습을 하는 동안에만 적용되고 학습이 종료된 후 예측을 하는 단계에서는 모든 유닛을 사용하여 예측한다.

  • dropout 장단점

    • 장점
      학습하는 퍼셉트론을 매번 변경하면서 학습하기 때문에 과대적합 예방

    • 단점
      단기적으로는 정확도가 더 낮다.



  • 과대적합 피하는 방법 - 데이터 확장

    • 과대적합이 일어나는 이유 중 하나는 훈련데이터가 부족하기 때문이다.
    • 훈련 데이터가 충분히 많다면 과대적합을 줄일 수 있다.
    • 데이터 확장이라 훈련 데이터를 다양하게 변형하여 변형된 새로운 훈련 데이터처럼 사용함으로써 마치 훈련 데이터 수가 늘어난 효과를 얻는 것이다.


  • 기타 성능 개선

    • 가중치 초깃값 설정(Xavier 초깃값, He 초깃값)
      앞 계층의 노드를 이용해 표준편차가 1/루트n인 정규분포로 초기화 하는 방법

    • 배치정규화(Batch Normalization)
      -활성화 함수 앞 또는 뒤에서 평균 0, 분산 1로 정규화 하는 방법
      -각 층에서 값들이 적당히 분포되도록 조정하는 것


개고양이 분류하기


  • 개 고양이 데이터 불러오기
# 데이터 경로 지정
data_dir = '/content/drive/MyDrive/dogs_vs_cats_small'
train_dir = data_dir + '/train'
val_dir = data_dir + '/validation'
  • 하나의 변수에 이미지 파일 전부 다 합치기
# 이미지 크기 동일하게 만들어주기(150, 150)
# 라벨링
# 픽셀값 변경 0~255(정수) → 0~1(실수)
# 1. 값의 크기를 줄이기 → 계산량 감소
# 2. 분산 줄이기 → 원활한 계산 가능
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 픽셀값 변경 기능
generator = ImageDataGenerator(rescale=1./255)

# 하나의 변수에 이미지 파일 전부 다 합치기
# 이미지 크기 동일하게 만들어주기(150, 150)
# 라벨링
train_generator = generator.flow_from_directory(
    directory = train_dir, # train 이미지 경로
    target_size = (150, 150), # 변환 할 이미지 경로
    batch_size = 100, # 한 번에 변환 할 이미지 갯수
    class_mode = 'binary' # 라벨링(이진), 다중분류 = categorical
    # 폴더의 알파벳 순서대로 라벨링 cats(0), dogs(1), 폴더 안에 있는 파일들에 적용
    
)

val_generator = generator.flow_from_directory(
    directory = val_dir, # train 이미지 경로
    target_size = (150, 150), # 변환 할 이미지 경로
    batch_size = 100, # 한 번에 변환 할 이미지 갯수
    class_mode = 'binary' # 라벨링(이진), 다중분류 = categorical
    # 폴더의 알파벳 순서대로 라벨링 cats(0), dogs(1), 폴더 안에 있는 파일들에 적용
    
)

  • 라벨링 결과 확인
print(train_generator .class_indices)


  • CNN 모델 만들기
from tensorflow.keras import Sequential # 딥러닝 모델의 토대
from tensorflow.keras.layers import Dense, Conv2D, MaxPool2D, Flatten
# Dense : 분류부에서 특징을 기반으로 사물 인식하는 역할
# Conv2D : 특징추출부에서 특징을 찾는 역할
# MaxPool2D : 특징추출부에서 특징이 아닌 부분을 삭제하는 역할
# Flatten : 특징추출부와 분류부를 이어주는 역할, 데이터를 1차원으로 펼쳐준다.

# InputLayer 사용
# model.add(InputLayer(input_size = 10))
# model.add(Dense(units = 32, activation = 'relu'))

# InputLayer 미사용
# model.add(Dense(units = 32, activation = 'relu', input_dim = 10))
  • CNN 모델 생성

# 입력층, 특징추출부
# InputLayer의 역할을 담은 Conv층으로 시작
model1.add(Conv2D(filters = 32, # 찾을 특징의 갯수
                  kernel_size = (3, 3), # 특징의 크기
                  input_shape = (150, 150, 3), # 입력 데이터의 모양(RGB), 입력층 역할
                  activation = 'relu', # 활성화함수
                  padding = 'same', # 가장자리에 0을 먼저 채우고 kernel를 계산 → 크기가 줄어들지 않음
                                    # 기본값 : valid → 크기 줄어들게 두는 것
                  strides = (2, 2))) # 행과 열 단위로 몇 픽셀씩 건너뛰면서 계산할건지

model1.add(MaxPool2D( # 특징이 아닌 부분 삭제
    pool_size = (2, 2) # 기준 크기에서 1개의 값만 가져오기
))

model1.add(Conv2D(filters = 16, # 찾을 특징의 갯수
                  kernel_size = (3, 3), # 특징의 크기
                  activation = 'relu')) # 활성화함수

model1.add(MaxPool2D( # 특징이 아닌 부분 삭제
    pool_size = (2, 2) # 기준 크기에서 1개의 값만 가져오기
))

model1.add(Conv2D(filters = 16, # 찾을 특징의 갯수
                  kernel_size = (3, 3), # 특징의 크기
                  activation = 'relu')) # 활성화함수

model1.add(MaxPool2D( # 특징이 아닌 부분 삭제
    pool_size = (2, 2) # 기준 크기에서 1개의 값만 가져오기
))

# 특징 추출부 끝
model1.add(Flatten()) # 데이터를 1차원으로 만들어주기, Dense는 1차원만 학습 가능

# 분류부 시작
# 많은 층이 필요하지 않다. → 특징값을 이미 모아놓았기 때문
model1.add(Dense(units = 32, activation = 'relu'))
model1.add(Dense(units = 16, activation = 'relu'))

# 출력층
# 이진분류
model1.add(Dense(units = 1, activation = 'sigmoid'))
  • 확인
model1.summary()


  • 이진분류
model1.compile(
    loss = 'binary_crossentropy',
    optimizer = 'adam',
    metrics = ['accuracy']
)

model1.fit(
    train_generator,
    epochs = 20,
    validation_data = val_generator
)


# 개 vs 고양이
# 인공지능 모델은 입력한 사진을 개 or 고양이로 구분
# 입력 데이터 : 사람의 사진 → 인공지능 모델이 구분하는 얼굴상을 알 수 있다.

# 확인하고싶은 사진 가져오기
  • 결과 확인
img = '/content/drive/MyDrive/Colab Notebooks/DeepLearning/data/dog.jpg'
import PIL.Image as pimg
import cv2

# 이미지 불러오기
pre_img = cv2.imread(img,cv2.IMREAD_COLOR)

# 색상 채널 변경(RGB → BGR), 파이썬에서는 BGR 순서로 데이터를 읽음
pre_img = cv2.cvtColor(pre_img, cv2.COLOR_BGR2RGB)

# 이미지 크기 변경
pre_img = cv2.resize(pre_img,(150,150))

# 이미지 차원 변경(데이터 수, 세로, 가로, RBG채널)
pre_img = pre_img.reshape((1,150,150,3))

result = model1.predict(pre_img)

if result > 0.5 :
  print('개')
else :
  print('고양이')


실습2


  • 개 고양이 데이터 불러오기
# 데이터 경로 지정
data_dir = '/content/drive/MyDrive/dogs_vs_cats_small'
train_dir = data_dir + '/train'
val_dir = data_dir + '/validation'
from google.colab import drive
drive.mount('/content/drive')

  • 하나의 변수에 이미지 파일 전부 다 합치기
# 이미지 크기 동일하게 만들어주기(150, 150)
# 라벨링
# 픽셀값 변경 0~255(정수) → 0~1(실수)
# 1. 값의 크기를 줄이기 → 계산량 감소
# 2. 분산 줄이기 → 원활한 계산 가능
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 픽셀값 변경 기능
generator = ImageDataGenerator(rescale=1./255)

# 하나의 변수에 이미지 파일 전부 다 합치기
# 이미지 크기 동일하게 만들어주기(150, 150)
# 라벨링
train_generator = generator.flow_from_directory(
    directory = train_dir, # train 이미지 경로
    target_size = (150, 150), # 변환 할 이미지 경로
    batch_size = 100, # 한 번에 변환 할 이미지 갯수
    class_mode = 'binary' # 라벨링(이진), 다중분류 = categorical
    # 폴더의 알파벳 순서대로 라벨링 cats(0), dogs(1), 폴더 안에 있는 파일들에 적용

)

val_generator = generator.flow_from_directory(
    directory = val_dir, # train 이미지 경로
    target_size = (150, 150), # 변환 할 이미지 경로
    batch_size = 100, # 한 번에 변환 할 이미지 갯수
    class_mode = 'binary' # 라벨링(이진), 다중분류 = categorical
    # 폴더의 알파벳 순서대로 라벨링 cats(0), dogs(1), 폴더 안에 있는 파일들에 적용

)

  • 라벨링 결과 확인
print(train_generator .class_indices)

  • CNN 모델 만들기
from tensorflow.keras import Sequential # 딥러닝 모델의 토대
from tensorflow.keras.layers import Dense, Conv2D, MaxPool2D, Flatten, Dropout
# Dense : 분류부에서 특징을 기반으로 사물 인식하는 역할
# Conv2D : 특징추출부에서 특징을 찾는 역할
# MaxPool2D : 특징추출부에서 특징이 아닌 부분을 삭제하는 역할
# Flatten : 특징추출부와 분류부를 이어주는 역할, 데이터를 1차원으로 펼쳐준다.

# 10epochs 이후로 과대적합이 진행
# 개선시킬 방안
# 1. 모델 층 늘리기 → CNN모델이 규칙을 더 복잡하게 찾을 것이다.
# 1.1 다양한 층 사용하기 → 시도
# 1.2 층 갯수 늘리기 → X
# 2. 이미지 데이터 수집하기 → train 총 2천장(개 1000, 고양이 1000) → X
# 3. 이미지 확장시키기 → 가지고 있는 이미지를 여러가지로 부풀린다. → 0
# 4. 만들어져 있는 모델 가져오기 → 전이학습 → 0
  • CNN 모델 생성
model2 = Sequential()

# 입력층, 특징추출부
# InputLayer의 역할을 담은 Conv층으로 시작
model2.add(Conv2D(filters = 32, # 찾을 특징의 갯수
                  kernel_size = (3, 3), # 특징의 크기
                  input_shape = (150, 150, 3), # 입력 데이터의 모양(RGB), 입력층 역할
                  activation = 'relu', # 활성화함수
                  padding = 'same', # 가장자리에 0을 먼저 채우고 kernel를 계산 → 크기가 줄어들지 않음
                                    # 기본값 : valid → 크기 줄어들게 두는 것
                  strides = (2, 2))) # 행과 열 단위로 몇 픽셀씩 건너뛰면서 계산할건지

model2.add(MaxPool2D( # 특징이 아닌 부분 삭제
    pool_size = (2, 2) # 기준 크기에서 1개의 값만 가져오기
))

model2.add(Dropout(0.3))

model2.add(Conv2D(filters = 16, # 찾을 특징의 갯수
                  kernel_size = (3, 3), # 특징의 크기
                  activation = 'relu')) # 활성화함수

model2.add(MaxPool2D( # 특징이 아닌 부분 삭제
    pool_size = (2, 2) # 기준 크기에서 1개의 값만 가져오기
))

model2.add(Conv2D(filters = 16, # 찾을 특징의 갯수
                  kernel_size = (3, 3), # 특징의 크기
                  activation = 'relu')) # 활성화함수

model2.add(MaxPool2D( # 특징이 아닌 부분 삭제
    pool_size = (2, 2) # 기준 크기에서 1개의 값만 가져오기
))

# 특징 추출부 끝
model2.add(Flatten()) # 데이터를 1차원으로 만들어주기, Dense는 1차원만 학습 가능

# 분류부 시작
# 많은 층이 필요하지 않다. → 특징값을 이미 모아놓았기 때문
model2.add(Dense(units = 32, activation = 'relu'))
model2.add(Dropout(0.3))
model2.add(Dense(units = 16, activation = 'relu'))

# 출력층
# 이진분류
model2.add(Dense(units = 1, activation = 'sigmoid'))

model2.compile(
    loss = 'binary_crossentropy',
    optimizer = 'adam',
    metrics = ['accuracy']
)

model2.fit(
    train_generator,
    epochs = 20,
    validation_data = val_generator
)


  • 데이터 확장하기
# 데이터 확장하기
aug_generator = ImageDataGenerator(
    rescale = 1./255, # 픽셀값 변경
    rotation_range = 20, # 시계 방향 회전 범위
    width_shift_range = 0.1, # 수평이동 범위
    height_shift_range = 0.1, # 수직이동 범위
    shear_range = 0.1, # 반시계 방향 회전
    zoom_range = 0.1, # 확대/축소 범위
    horizontal_flip = True, # 수평 뒤집기
    fill_mode = 'nearest' # 가까운 값으로 채움
)

# 확장 데이터로 학습 시 주의사항
# 1. 확장하지 않은 데이터로 우선 학습
# 2. 결과가 좋지 않았을 때, 확장 데이터로 추가 학습

train_aug_generator = aug_generator.flow_from_directory(
    train_dir,
    target_size = (150, 150),
    batch_size = 100,
    class_mode = 'binary'
)

model2.fit(
    train_aug_generator,
    epochs = 20,
    validation_data = val_generator
)
# 딥러닝 모델 학습 시, 가장 처음 epochs가 가장 오래걸림
# 딥러닝 모델은 추가 학습이 가능 → 전 결과를 이어받아서 학습


  • 전이학습
# 다른 사람이 만들어 놓은 모델 가져와서 사용하기
# + 내가 진행하는 데이터와 유사한 데이터로 학습한 모델 → 사자 / 호랑이 모델
# CNN = 특징추출부 + 분류부, 특징추출부만 가져와서 사용, 분류부는 직접 학습
from tensorflow.keras.applications import VGG16
conv_base = VGG16(
    weights = 'imagenet',
    include_top = False, # 분류부 사용 할 것인가?
    input_shape = (150, 150, 3)
)

  • 확인하기
conv_base.summary()


# 현재 상황은 전체 파라미터(가중치)가 학습 가능한 상황
# 학습을 진행하면 기존에 가지고있던 1000개의 사물에서 특징을 추출하는 방법이 덮어씌워진다.
# 학습이 불가능하도록 변경
# 동결 = 가중치가 갱신되는 것을 막는 것
conv_base.trainable = False

  • 전이학습 모델 설계
# 특징추출 방식
# 특징 추출부만 사용해서 학습하기 때문에 빠른 사용이 가능
# 특징 추출이 잘 안된다면 성능이 저하
model3 = Sequential()

# 특징추출부 가져오기
model3.add(conv_base) # conv 13개층, pool 5개층 추가

model3.add(Flatten())

model3.add(Dense(units = 64, activation = 'relu'))

model3.add(Dense(units = 1, activation = 'sigmoid'))
model3.summary()


model3.compile(
    loss = 'binary_crossentropy',
    optimizer = 'adam',
    metrics = ['accuracy']
)

model3.fit(
    train_generator,
    epochs = 20,
    validation_data = val_generator
)


  • 결과 확인
img = '/content/drive/MyDrive/Colab Notebooks/DeepLearning/data/dog.jpg'
import PIL.Image as pimg
import cv2

# 이미지 불러오기
pre_img = cv2.imread(img,cv2.IMREAD_COLOR)

# 색상 채널 변경(RGB → BGR), 파이썬에서는 BGR 순서로 데이터를 읽음
pre_img = cv2.cvtColor(pre_img, cv2.COLOR_BGR2RGB)

# 이미지 크기 변경
pre_img = cv2.resize(pre_img,(150,150))

# 이미지 차원 변경(데이터 수, 세로, 가로, RBG채널)
pre_img = pre_img.reshape((1,150,150,3))

result = model1.predict(pre_img)

if result > 0.5 :
  print('개')
else :
  print('고양이')


  • 전이학습 - 미세조정방식
# 특성 추출부를 조금씩 수정
# 한 번에 많이 수정하게 되면 기존의 방식을 잃어버릴 수 있다.
# 조금씩 여러 번 수정을해야 기존의 방식과 새로운 특징추출방법이 잘 어울어짐
# 특징추출방식에 비해서 더 좋은 성능을 낼 수 있다.
# 수동 조절이 많이 필요하다.
from tensorflow.keras.applications import VGG16

conv_base = VGG16(
    weights = 'imagenet',
    include_top = False,
    input_shape = (150, 150, 3)
)

  • 동결 진행할건데, 일단은 전체 다 동결
# 1. 특성추출부 전체 동결 후 분류기 학습 → 오차가 어느정도 줄어드는걸 확인
# 2. 특성추출부의 하단의 조금의 층 동결 해제 후 다시 학습 → 하단은 새로운 데이터에 맞게 규칙 수정
# 3. 특성추출부의 하단에 동결 해제 안된 조금의 층 동결 해제 후 다시 학습
  • 0
for layer in conv_base.layers:
  layer.trainable = False
model4 = Sequential()

model4.add(conv_base) 

model4.add(Flatten())

model4.add(Dense(units = 64, activation = 'relu'))

model4.add(Dense(units = 1, activation = 'sigmoid'))
model4.compile(
    loss = 'binary_crossentropy',
    optimizer = 'adam',
    metrics = ['accuracy']
)
model4.fit(
    train_generator,
    epochs = 20,
    validation_data = val_generator
)


  • 분류부의 오차가 줄어든 시점에서 다음 단계 진행
# 특성추출부가 분류부의 오차를 이어받아서 학습
# → 특성추출부가 기존 방식에서 조금만 변경되길 원함, 오차가 조금 있는 상태에서 진행
  • block5_conv1부터 동결 해제
set_trainable = False
for layer in conv_base.layers :
  if layer.name == 'block5_conv1' :
    set_trainable = True
  
  if set_trainable : 
    layer.trainable = True
  else :
    layer.trainable = False

  • 다시 한번 더 학습
model4.fit(
    train_generator,
    epochs = 20,
    validation_data = val_generator
)


profile
노는게 제일 좋아~!

0개의 댓글