페이지

2022년 8월 6일 토요일

STEP 1 상자로서의 변수

 첫 번째 단계에서는 DeZero의 구성 요소인 '변수'를 만듭니다. 변수는 DeZero에서 가장 중요한 개념입니다. 이번 단계에서는 변수가 어떤 기능을 하는지 이해하고, 그 기능에 맞게 코드로 구현합니다.



제1고지 미분 자동 계산

 미분은 다양한 과학 기술 분야에 사용됩니다. 특히 립러닝을 포한한 머신러닝의 여러 분야에서 중추적인 역할을 합니다. 딥러닝 프레임워크는 말하자면 미분을 계산하기 위한 도구입니다. 그래서 이 책의 주제도 자연스럽게 '미분'과 이어집니다. 즉, 컴퓨터를 사용하여 미분을 계산하는 일이 주요 주제입니다.

지금부터 시작되는 제1고지는 총 10간계로 구성됩니다. 이 고지에서는 미분을 자동으로 계산하는 틀을 만듭니다. '미분을 자동으로 계산한다'라는 말은 미분을 (사람이 아니라)컴퓨터가 계산한다는 뜻입니다. 정확히 말하면, 어떤 계산(함수)을 코드로 구현하면 그 계산의 미분을 컴퓨터가 자동으로 계산해주는 시스템을 가리킵니다.

이번 고지에서는 미분을 자동으로 계산하기 위해 '변수'와 '함수'를 표현하는 두 클래스 Variable과 Function을 만듭니다. 놀랍게도 이 두 클래스만으로 미분 자동 계산의 기반이 완성됩니다. 제1고지가 끝날 무렵에는 간단한 계산(함수)의 미분은 자동으로 계산할 수 있게 됩니다. 그럼 DeZero의 첫 번째 단계로 발을 내디뎌보죠.

2022년 8월 3일 수요일

5.3.1 특성 추출

 특성 추출은 사전에 학습된 네트워크의 표현을 사용하여 새로운 샘플에서 흥미로운 특성을 ㅂ뽀아내는 것입니다. 이런 특성을 사용하여 새로운 분류기를 처음부터 훈련합니다.

앞서 보았듯이 컨브넷은 이미지 분류를 위해 두 부분으로 구성됩니다. 먼저 연속된 합성곱과 풀링 층으로 시작해서 완전 연결 분류기로 끝납니다. 첫 번째 부분을 모델의 합성곱 기반 층(convolutional base)이라고 부르겠습니다. 컨브넷의 경우 특성 추출은 사전에 훈련된 네트워크의 합성곱 기반층을 선택하여 새로운 데이터를 통과시키고, 그 출력으로 새로운 분류기를 훈련합니다.


왜 합성곱 층만 재사용할까요? 완전 연결 분류기도 재사용할 수 있을까요? 일반적으로 권장히지 않습니다. 합성곱 층에 의해 삭습된 표현이 더 일반적이어서 재사용이 가능하기 때문입니다. 컨브넷의 특성 맵은 사진에 대한 일반적인 콘셉트의 존재 여부를 기록한 맵입니다. 주어진 컴퓨터 비전 문제에 상관없이 유용하게 사용할 수 있습니다. 하지만 뷴류기에서 학습한 표현은 모델이 훈련된 클래스 집합에 특화되어 있습니다. 분류기는 전체 사진에 어떤 클래스가 존재할 확률에 관한 정보만 담고 있습니다. 더군다나 완전 연결 층에서 찾은 표현은 더 이상 입력 이미지에 있는 객체의 위치 정보를 가지고 있지 않습니다. 완전 연결 층들은 공간 개념을 제거하지만 합성곱의 특성맵은 객체 위치를 고려합니다. 객체 위치가 중요한 문제라면 완전 연결 층에서 만든 특성은 크게 쓸모가 없습니다.
특성 합성곱 층에서 추출한 표현의 일반성(그리고 재사용성) 수준은 모델에 있는 층의 깊이에 달려 있습니다. 모델의 하위 층은(에지, 색깔, 질감 등) 지역적이고 매우 일반적인 특성 맵을 추출합니다. 모델의 하위 층은(에지, 색깔, 질감 등) 지역적이고 매우 일반적인 특성 맵을 추출합니다. 반면에 상위 층은('강아지 눈'이나 '고양이 귀'처럼) 좀 더 추상적인 개념을 추출합니다. 새로운 데이터셋이 원본 모델이 훈련한 데이터셋과 많이 다르다면 전체 합성곱 기반 층을 사용하는 것보다는 모델의 하위 층 몇 개만 특성 추출에 사용하는 것이 좋습니다.

ImageNet의 클래스 집합에는 여러 종류의 강아지와 고양이를 포함하고 있습니다. 이런 경우 원본 모델의 완전 연결 층에 있는 정보를 재사용하는 것이 도움이 될 것 같습니다. 하지만 새로운 문제의 클래스가 원본 모델의 클래스 집합과 겹치지 않는 좀 더 일반적인 경우를 다루기 위해서 여기서는 완전 연결 층을 사용하지 않겠습니다. ImageNet 데이터셋에 훈련된 VGG16네트워크의 합성곱 기반 층을 사용하여 강아지와 고양이 이미지에서 유요한 특성을 추출해 보겠습니다. 그런 다음 이 특성으로 강아지 vs. 고양이 분류기를 훈련합니다.
VGG16모델은 케라스에 패키지로 포함되어 있습니다. keras.applications 모듈에서 임포트할 수 있습니다. keras.application 모듈에서 사용 가능한 이미지 분류 모델은 다음과 같습니다.
(모두 ImageNet 데이터셋에서 훈련되었습니다).
1) Xception
2) Inception V3
3) REsNet50
4) VGG16
5) VGG19
6) MobileNet

VGG16모델을 만들어 보죠.

from keras.applications.vgg16 import VGG16

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

VGG16함수에 3개의 매개변수를 전달합니다.
1) weights는 모델을 초기화할 가중치 체크포인트(checkpoint)를 지정합니다.
2) include_top은





5.3 사전 훈련된 컨브넷 사용하기

 작은 이미지 데이터셋에 딥러닝을 적용하는 일반적이고 매우 효과적인 방법은 사전 훈련된 네트워크를 사용하는 것입니다. 사전 훈련된 네트워크(pretrained network)는 일반적으로 대규모 이미지 분류 문제를 위해 대량의 데이터셋에서 미리 훈련되어 정장된 네트워크입니다. 원본 데이터셋이 충분히 크고 일반적이라면 사전 훈련된 네트워크에 의해 학습된 특성의 계층 구조는 실제 세상에 대한 일반적인 모델로 효율적인 역할을 할 수 있습니다. 새로운 문제가 원래 작업과 완전히 다른 클래스에 대한 것이라도 이런 특성은 많은 컴퓨터 비전 문제에 유용합니다. 예를 들어(대부분 동물이나 생활 용품으로 이루어진)Image Net 데이터셋에 네트워크를 훈련합니다. 그다음 이 네트워크를 이미지에서 가구 아이템을 식별하는 것 같은 다른 용도로 사용할 수 있습니다. 학습된 특성을 다른 문제에 적용할 수 있는 이런 유연성은 이전의 많은 얕은 학습 방법과 비교했을 때 딥러닝의 핵심 장점입니다. 이런 방식으로 작은 데이터셋을 가진 문제에도 딥러닝이 효율적으로 작동할 수 있습니다.

여기에서는 (1,400만 개의 레이블된 이미지와 1,000개의 클래스로 이루어진)ImageNet데이터셋에서 훈련된 대규모 컨브넷을 사용해 보겠ㅆ브니다. ImageNet 데이터셋은 다양한 종의 강아지와 고양이를 비록하여 많은 동물들을 포함하고 있습니다. 그래서 강아지 vs. 고양이 분류 문제에 좋은 성능을 낼 것 같습니다.

캐런 시몬연(Karen Simonyan)과 앤드류 지서먼(Andrew Zisseman)이 2014년에 개발한 VGG16구조를 사용하겠습니다. VGG16은 간단하고 ImageNet 데이터셋에 널리 사용되는 컨브넷 구조입니다. VGG16은 조금 오래되었고 최고 수준의 성능에는 못 미치며 최근의 다른 모델보다는 조금 무겁습니다. 하지만 이 모델의 구조가 이전에 보았던 것과 비슷해서 새로운 개념을 도입하지 않고 이해하기 쉽기 때문에 선택했습니다. 아마 VGG가 처음 보는 모델 애칭일지 모르겠습니다. 이런 이름에는 VGG, ResNet, Inception, Inception-ResNet, Xception등이 있습니다. 컴퓨터 비전을 위해 딥러닝을 계속 공부하다 보면 이런 이름을 자주 만나게 될 것입니다.

사전 훈련된 네트워크를 사용하는 두 가지 방법이 있습니다. 특성 추출(feature extraction)과 미세 조정(fine tunning)입니다. 이 두 가지를 모두 다루어 보겠습니다. 

5.2.5 데이터 증식 사용하기

 과대적합은 학습할 샘플이 너무 적어 새로운 데이터에 일반화할 수 있는 모델을 훈련시킬 수 없기 때문에 발생합니다. 무한히 많은 데이터가 주어지면 데이터 분포의 모든 가능한 측면을 모델이 학습할 수 있을 것입니다. 데이터 증식은 기존 훈련 샘플로부터 더 많은 훈련 데이터를 생성하는 방법입니다. 이 방법은 그럴듯한 이미지를 생성하도록 여러 가지 랜덤한 변환을 적용하여 샘플을 늘립니다. 훈련할 때 모델이 정확히 같은 데이터를 두 번 만나지 않도록 하는 것이 목표입니다. 모델이 데이터의 여러 측면을 학습하면 일반화에 도움이 될 것입니다.

케라스에서는 ImageDataGenerator가 읽은 이미지에 여러 종류의 랜덤 변환을 적용하도록 설정할 수 있습니다. 예를 먼저 만들어 보죠.


datagen = ImageDataGenerator(
    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) rotation_range는 랜덤하게 사진을 회전시킬 각도 범위입니다(0-180사이).
2) width_shift_range와 height_shift_range는 사진을 수평과 수직으로 랜덤하게 평행 이동
시킬 범위입니다(전체 너비와 높이에 대한 비율).
3) zoom_range는 랜덤하게 사진을 확대할 범위입니다.
4) horizontal_flip은 랜덤하게 이미지를 수평으로 뒤집습니다. 수평 대칭을 가정할 수 있을때
사용합니다(옐르 들어 풍경/인물 사진).
5) fill_mode는 회전이나 가로/세로 이동으로 인해 새롭게 생성해야 할 픽셀을 채울 전략입니다.
증식된 이미지 샘픙르 살펴보죠

# 이미지 전처리 유틸리티 모듈
from keras.preprocessing import image

fnames = sorted([os.path.join(train_cats_dir, fname) for 
                 fname in os.listdir(train_cats_dir)])

# 증식할 이미지 선택합니다
img_path = fnames[3]

# 이미지를 읽고 크기를 변경합니다
img = image.load_img(img_path, target_size=(150150))

# (150, 150, 3) 크기의 넘파이 배열로 변환합니다
x = image.img_to_array(img)

# (1, 150, 150, 3) 크기로 변환합니다
x = x.reshape((1,) + x.shape)

# flow() 메서드는 랜덤하게 변환된 이미지의 배치를 생성합니다.
# 무한 반복되기 때문에 어느 지점에서 중지해야 합니다!
i = 0
for batch in datagen.flow(x, batch_size=1):
    plt.figure(i)
    imgplot = plt.imshow(image.array_to_img(batch[0]))
    i += 1
    if i % 4 == 0:
        break

plt.show()

데이터 증식을 사용하여 새로운 네트워클르 훈련시킬 때 네트워크에 같은 입력 데이터가 두 번
중입되지 않습니다. 하지만 적은 수의 원본 이미지에서 만들어졌기 때문에 여전히 입력 데이터들
사이에 상호 연관성이 큽니다. 즉 새로운 정보를 만들어 낼 수 없고 단지 기존 정보의 재조합만
가능 합니다. 그렇기 때문에 완전히 과대적합을 제거하기에 충분하지 않을 수 있습니다. 과대
적합을 더 억제하기 위해 완전 연결 분류기 직전에 Dropout층을 추가하겠습니다.

model = models.Sequential()
model.add(layers.Conv2D(32, (33), activation='relu',
                        input_shape=(1501503)))
model.add(layers.MaxPooling2D((22)))
model.add(layers.Conv2D(64, (33), activation='relu'))
model.add(layers.MaxPooling2D((22)))
model.add(layers.Conv2D(128, (33), activation='relu'))
model.add(layers.MaxPooling2D((22)))
model.add(layers.Conv2D(128, (33), activation='relu'))
model.add(layers.MaxPooling2D((22)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(learning_rate=1e-4),
              metrics=['acc'])

데이터 증식과 드롭아웃을 사용하여 이 네트워크를 훈련시켜 봅시다.

train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,)

# 검증 데이터는 증식되어서는 안 됩니다!
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # 타깃 디렉터리
        train_dir,
        # 모든 이미지를 150 × 150 크기로 바꿉니다
        target_size=(150150),
        batch_size=32,
        # binary_crossentropy 손실을 사용하기 때문에 이진 레이블을 만들어야 합니다
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150150),
        batch_size=32,
        class_mode='binary')

history = model.fit(
      train_generator,
      steps_per_epoch=100,
      epochs=100,
      validation_data=validation_generator,
      validation_steps=50)

5.4절에서 이 모델을 사용하기 위해 저장합니다.

model.save('cats_and_dogs_small_2.h5')

결과 그래프를 다시 그려 봅시다. 그림 5-12와 그림 5-13을 참고하세요. 데이터 증식과 드롭아웃
덕택에 더 이상 과대적합되지 않습니다. 훈련 곡선이 검증 곡선에 가깝게 따라가고 있습니다.
검증 데이터에서 82% 정확도를 달성했습니다. 규제하지 않은 모델과 비교했을 때 15%정도
향상되었습니다

다른 규제 기법을 더 사용하고 네트워크의 파라미터를 튜닝하면(합성곱 층의 필터 수나 네트워크
층의 수 등) 86%나 87%정도까지 더 높은 정확도를 얻을 수 있습니다. 하지만 데이터가 적기
때문에 컨브넷을 처음부터 훈련해서 더 높은 정확도를 달성하기는 어렵습니다. 이런 상황에서
정확도를 높이기 위한 다음 단계는 사전 훈련된 모델을 사용하는 것입니다. 다음 두 절에서
이에 대해 집중적으로 살펴 보겠습니다.

2022년 8월 1일 월요일

5.2.4 데이터 전처리

 데이터는 네트워크에 주입되기 전에 부동 소수타입의 텐서로 적절하게 전처리 되어 있어야 합니다. 지금은 데이터가 JPEG파일로 되어 있으므로 네트워크에 주입하려면 대략 다음 과정을 따릅니다.

1) 사진 파일을 읽습니다.

2) JPEG콘텐츠를 RGB픽셀 값으로 디코딩합니다.

3) 그다음 부동 소스 타입의 텐서로 변환합니다.

4) 픽셀 값(0에서 255 사이)의 스케일을 [0, 1]사이로 조정합니다(신경망은 작은 입력 값을 선호합니다).

좀 복잡하게 보일 수 있지만 다행히 케라스는 이런 단계를 자동으로 처리하는 유틸리티가 있습니다. 또 케라스에는 keras.preprocessing.image에 이미지 처리를 위한 헬퍼 도구들도 있습니다. 특히 ImageDataGenerator 클래스는 디스크에 있는 이미지 파일을 전처리된 배치 텐서로 바동으로 바꾸어 주는 파이썬 제너레이터(generator)를 만들어 줍니다. 이 캘래스를 사요해 보겠습니다.


from keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
              train_dir,
              target_size=(150150),
              batch_size=20,
              class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
          validation_dir,
          target_size=(150150),
          batch_size=20,
          class_mode='binary'
)

파이썬 제너레이터 이해하기
파이썬 제너레이터(generator)는 반복자(iterator)처럼 작동하는 객체로 for ... in 연산자에
사용할 수 있습니다. 제너레이터는 yield 연산자를 사용하여 만듭니다.
다음 정수를 반환하는 제너레이터의 예입니다.

def generator():
    i = 0
    while True:
      i += 1
      yield i

for item in generator():
  print(item)
  if item > 4:
    break



다음과 같이 출력됩니다.
1 2 3 4 5

이 제너레이터의 출력 하나를 살펴보죠. 이 출력은 150 * 150 RGB 이미지의 배치
((20, 150, 150,3) 크기)와 이진 레이블의 배치((20,) 크기)입니다. 각 배치에는 20개의 샘플
(배치 크기)이 있습니다. 제너레이터는 이 배치를 무한정 만들어 냅니다. 타깃 폴더에 있는
이미지를 끝없이 반복합니다. 따라서 반복 루프 안 어디에선가 break문을 사용해야 합니다.

for data_batch, labels_batch in train_generator;
  print('배치 데이터 크리:', data_batch.shape)
  print('배치 레이블 크기:', labels_batch.shape)
  break


배치 데이터 크기: (20, 150, 150, 3)
배치 레이블 크기: (20,)

제너레이터를 사용한 데이터에 모델을 훈련시켜 보겠습니다. fit_generator 메서드는 fit 메서드
와 동일하되 데이터 제너레이터를 사용할 수 있습니다. 이 메서드는 첫 번째 매배변수로 입력과
타깃의 배치를 끝없이 반환하는 파이썬 제너레이터를 기대합니다. 데이터가 끝없이 생성되기
때문에 케라스 모델에 하나의 에포크를 정의하기 위해 제너레이터로부터 얼마나 많은 샘플을 뽑을것인지
알려주어야 합니다. steps_per_epoch 매개변수에서 이를 설정합니다. 제너레이터로부터
steps_per_epoch개의 배치만큼 뽑은 후, 즉 steps_per_epoch횟수만큼 경사 하강법 단곌르 실행
한 후에 훈련 프로세스는 다음 에포크로 넘어갑니다. 여기서는 20개의 샘플이 하나의 배치이므로
2,000개의 샘플을 모두 처리할 때까지 100개의 배치를 뽑을 것입니다.
fit_generator를 사용할 때 fit 메서드와 마찬가지로 validation_Data 매개변수를 전달할 수 있
습니다. 이 매개변수에는 데이터 제너레이터도 가능하지만 넘파이 배열의 튜플도 가능합니다.
validation_data로 제너레이터를 전달하면 검증 데이터의 배치를 끝없이 반환합니다. 따라서 검증
데이터 제너레이터에서 얼마나 많은 배치를 추출하여 평가할지 validation_steps매개변수에
지정해야 합니다.
history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=50
)
훈련이 끝나면 항상 모델을 저장하는 것이 좋은 습관입니다.
model.save('cat_and_dogs_smll_1.h5')
훈련 데이터와 검증 데이터에 대한 모델의 손실과 정확도를 그래프로 나타내보겠습니다.

import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1len(acc) +1)

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b' label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

이 그래프는 과대적합의 특성을 보여 줍니다. 훈련 정확도가 시간이 지남에 따라 선형적으로 증가
해서 거의 100%에 도달합니다. 반면에 검증 정확도는 70-72%에서 멈추었습니다. 검증 손실은
다섯 번의 에포크만에 최솟값에 다다른 이후 더 이상 진전되지 않았습니다. 반면에 훈련 손실은
거의 0에 도달할 때까지 선형적으로 계속 감소합니다.
비교적 훈련 샘플의 수(2,000개)가 적기 때문에 과대적합이 가장 중요한 문제입니다. 드롭아웃이나
가중치 감소(L2 규제)처럼 과대적합을 감소시킬 수 있는 여러 가지 기법들을 배웠씁니다. 여기
에서는 검퓨터 비전에 특화되어 있어서 딥러닝으로 이미지를 다룰 때 매우 일반적으로 사용되는
새로운 방법인 데이터 증식을 시도해 보겠습니다.


2022년 7월 31일 일요일

5.2.3 네트워크 구성하기

 이전 예제에서 MNIST를 위해 간단한 컨브넷을 만들었습니다. 이제 컨브넷에 친숙해졌을 것입니다. 여기서 사용할 구조도 일반적으로 동일합니다. Conv2D(relu 활성화 함수 사용)와  MaxPooling2D층을 번갈아 쌓은 컨브넷을 만들겠습니다.

이전보다 이미지가 크고 복잡한 문제이기 때문에 네트워크를 좀 더 크게 만들겠습니다. Conv2D + MaxPooling2D 단계를 하나 더 추가합니다. 이렇게 하면 네트워크의 용량을 늘리고 Flatten층의 크기가 너무 커지지 않도록 특성 맵의 크기를 줄일 수 있습니다. 150 * 150 크기(임의로 선택한 것입니다)의 입력으로 시작해서  Flatten 층 이전에 7 * 7 크기의 특성 맵으로 줄어듭니다.

특성 맵의 깊이는 네트워크에서 점진적으로 증가하지만(32에서 128까지). 특성 맵의 크기는 감소합니다.(150 * 150 에서 7 * 7까지). 이는 거의 모든 컨브넷에서 볼 수 있는 전형적인 패턴입니다.

이진 분류 문제이크로 네트워크는 하나의 유닛(크기가 1인 Dense 층)과 sigmoid활성화 함수로 끝납니다. 이 유닛은 한 클래스에 대한 확률을 인코딩할 것입니다.


from keras import layers
from keras import models

model = models.Sequential()
model.add(layers.Conv2D(32, (33), activation='relu',input_shape=(1501503)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64, (3,3), activation='relu',))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128, (33), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128, (33), activation='relu'))
model.add(layers.MaxPooling2D((22)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

층들을 거치면서 특성 맵의 차원이 어떻게 변하는지 살펴보겠습니다.
>>>model.summary()
Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_4 (Conv2D) (None, 148, 148, 32) 896 max_pooling2d_4 (MaxPooling (None, 74, 74, 32) 0 2D) conv2d_5 (Conv2D) (None, 72, 72, 64) 18496 max_pooling2d_5 (MaxPooling (None, 36, 36, 64) 0 2D) conv2d_6 (Conv2D) (None, 34, 34, 128) 73856 max_pooling2d_6 (MaxPooling (None, 17, 17, 128) 0 2D) conv2d_7 (Conv2D) (None, 15, 15, 128) 147584 max_pooling2d_7 (MaxPooling (None, 7, 7, 128) 0 2D) flatten_1 (Flatten) (None, 6272) 0 dense_1 (Dense) (None, 512) 3211776 dense_2 (Dense) (None, 1) 513 ================================================================= Total params: 3,453,121 Trainable params: 3,453,121 Non-trainable params: 0 _________________________________________________________________

컴파일 단계에서 이전과 같이 RMSprop 옵티마이저를 선택하겠습니다. 네트워의 마지막의 하나의
시그모이드 유닛이기 때문에 이진 크로스엔트로피(binary crossentropy)를 손실로 사용합니다
(표 4-1에서 다양한 경우에 사용할 수 있는 손실 함수 목록을 볼수 있습니다).

from tensorflow.keras import optimizers

model.compile(loss='binary_crossentropy'
              optimizer=optimizers.RMSprop(learning_rate=1e-4),
              metrics=['acc'])