



Feature Extractor - 특징추출부(Conv, Pooling을 사용해서 특징만 모아놓은 데이터를 만든다.


축소 샘플링 : 이미지 크기 줄이기
→ 결과 : 효율적인 학습이 가능하다.







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)

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))
# 입력층, 특징추출부
# 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('고양이')






# 데이터 경로 지정
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)

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
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. 특성추출부의 하단에 동결 해제 안된 조금의 층 동결 해제 후 다시 학습
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
)

# 특성추출부가 분류부의 오차를 이어받아서 학습
# → 특성추출부가 기존 방식에서 조금만 변경되길 원함, 오차가 조금 있는 상태에서 진행
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
)
