본문 바로가기
혼공학습단-혼공머신

[혼공머신] 6주차 Ch 7. 딥러닝을 시작합니다 (상)

by 바이오인포하는 tansansoo 2025. 8. 17.

썸네일

드디어 마지막 주차입니다! 이번 주 공부인증은 두 편으로 나눠서 업로드합니다.

6주차 (하) 링크

https://tansansooandbioinfo.tistory.com/22

5주차 우수혼공족 선정 감사합니다!!

 

이제 챕터 7 시작하겠습니다.

Ch 7. 딥러닝을 시작합니다

패션 럭키백을 판매합니다!

 

07-1 인공 신경망

한빛 마켓에서는 생선 럭키백의 성공 이후, 패션 분야로도 진출하게 되어서 이번엔 패션 럭키백을 만들어보고자 합니다.

 

패션 MNIST

판매할 패션 상품의 데이터는 아직 없지만, 한빛 마켓에서 판매할 상품과 똑같은 데이터로 딥러닝에서 유명한 MNIST 데이터셋을 사용해 보겠습니다.

MNIST 데이터셋은 손으로 직접 쓴 0~9까지의 숫자로 이루어진 대형 데이터셋입니다. 다양한 화상 처리 시스템을 트레이닝하기 위해 많이 사용됩니다. 이 데이터셋은 훈련용 6만 개의 28*28 사이즈의 이미지와 테스트용 만 개의 이미지를 포함하고 있습니다. 이번 챕터 7에서는 이 MNIST 데이터셋의 대체용으로 나온 패션 MNIST 데이터셋을 사용합니다. 패션 MNIST 데이터셋은 MNIST 데이터셋과 이미지 크기, 훈련 및 테스트 분할 구조가 동일합니다. 다만, MNIST 데이터셋이 직접 쓴 숫자로 이루어져 있지만, 패션 MNIST 데이터셋은 패션아이템으로 구성되어 있다는 차이점이 있습니다.

MNIST 테스트 데이터셋의 샘플 이미지(Wikipedia).

 

패션 MNIST 데이터셋은 케라스를 사용해서 불러올 수 있습니다. 이 케라스도 코랩에서 바로 사용할 수 있습니다.

 

keras.datasets.fashion_mnist 모듈 아래의 load_data() 함수는 훈련 데이터와 테스트 데이터를 나누어 반환합니다.

이게 어떻게 가능한 걸까요??

 

Fashion‑MNIST는 Zalando가 공개할 때부터 훈련 세트와 테스트 세트가 미리 구분되어 있으며, 케라스의 load_data()는 이 미리 정의된 두 묶음(“훈련 60,000장 / 테스트 10,000장”)을 각각 (x_train, y_train), (x_test, y_test)로 반환한다.

즉, 사용자가 분할 비율을 지정하거나 시드를 바꿔서 분할 방식을 변경할 수 있는 옵션은 제공되지 않는다.
이렇게 분할을 사전에 지정해 놓은 건 MNIST 데이터셋도 동일하다.

 

전달받은 데이터의 크기를 확인합니다.

훈련 데이터는 총 6만 장이고 이미지의 크기는 28*28 임을 알 수 있습니다.

훈련 데이터의 타깃도 6만 개의 원소가 있는 1차원 배열입니다.

 

테스트 데이터는 총 만장이고 훈련 데이터와 크기가 동일합니다. 테스트 데이터의 타깃도 만 개의 원소입니다.

 

챕터 6에서처럼 이미지를 직접 출력해서 확인해 볼 수 있습니다.

 

우리가 아는 흑백 이미지(바탕 흰색, 물체 검은색)로 표현하기 위해 cmap='gray_r'로 지정하였습니다.

 

출력한 샘플들의 타깃값을 확인해 봅시다.

패션 MNIST 타깃은 0~9까지의 숫자 레이블로 구성되어 있습니다.

즉, 처음 10개 샘플의 타깃 값들은 [앵클부츠, 티셔츠, 티셔츠, 드레스, 티셔츠, 스웨터, 스니커즈, 스웨터, 샌달, 샌달]이 됩니다.

타깃 값의 레이블과 레이블에 있는 샘플 개수를 찾아보겠습니다.

 

레이블이 0~9까지 총 10개가 있고 각 레이블마다 샘플이 6000개씩 있는 것을 확인할 수 있습니다.

 

로지스틱 회귀로 패션 아이템 분류하기

훈련 데이터가 무려 6만 개나 되기 때문에 훈련 데이터 전부를 한꺼번에 훈련시키는 것보다 샘플을 하나씩 혹은 일부만 꺼내서 훈련시키는 것이 더 나은 것 같습니다.

혼공머신 3주차 공부인 챕터 4 기억하시나요?

https://tansansooandbioinfo.tistory.com/18

챕터 4에서 배운 SGDclassifier를 복습해 봅시다

SGDclassifier는 손실 함수를 최소화하기 위해 가장 가파른 경로를 찾는 경사 하강법을 사용합니다. 특성의 스케일이 다르면, 손실 함수의 기울기에 큰 차이가 나서 일부 특성의 가중치가 커지고, 학습이 불안정해지거나 느려질 수 있다. 그래서 데이터 표준화를 해야 합니다.

SGDClassifier는 데이터 표준화가 필요하네요! 특히 패션 MNIST 데이터셋은 흑백 이미지라 픽셀은 0~255 사이의 정수값을 가집니다. 정규화를 하여 0~1 사이의 값을 가지게 합니다.

SGDclassifier를 사용하기 위해 2차원 배열인 현재 샘플을 챕터 6에서 배운 reshape() 메서드를 사용해서 1차원으로 만들어줍니다.

변환된 훈련 데이터는 샘플 6만 개로 이루어져 있고 28*28 크기의 2차원 배열이기 때문에 784개의 1차원 배열로 변환되었습니다. (28*28 = 784)

모델의 성능을 더 정확하고 일반화되게 평가하기 위해 SGDclassifier 클래스와 교차 검증으로 성능을 확인해 보겠습니다.

 

훈련 데이터를 5개로 나누어 5번의 교차검증을 하여 각각의 평가 점수를 계산한 뒤, 그 평균을 구하는 방식으로 모델 성능을 평가하였습니다.

 

약 0.819로 나왔는데 파이썬 조건문을 활용하면 에포크 횟수와 교차검증 세트를 그래프로 그려볼 수도 있지 않을까요?

그래서 한번 그려봤습니다!

정확한 그래프인지는 알 수는 없지만 확실한 것은 에포크 횟수가 많다고 해서 좋은 것은 아니라는 사실입니다.

 

확률적 경사하강법을 사용한 로지스틱 회귀 함수와 교차검증을 사용해도 모델의 성능이 만족할 만한 수준이 아니었습니다.

로지스틱 회귀 방정식

z = a×(Weight) + b×(Lenght) + c×(Diagonal) + d×(Height) + e×(Width) + f

(여기서, a, b, c, d, e는 가중치, f는 편향입니다)

패션 MNIST 데이터셋은 10개의 레이블로 이루어져 있으므로, 방정식은 총 10개가 됩니다. 그리고 가중치는 특성 개수인 784개만큼 존재하고 마지막에 절편 b까지 존재하게 됩니다. 이럴 경우, 모델 파라미터는 784*10+10=7850개가 됩니다.

이렇게 10개 클래스에 대한 선형 방정식을 모두 계산한 후에는 다중분류이므로 소프트맥스 함수를 사용하여 각 클래스에 대한 확률을 모두 얻습니다. 소프트맥스 함수 기억이 나지 않는다면 챕터 4를 복습합시다!

https://tansansooandbioinfo.tistory.com/18 <- 챕터 4

 

인공신경망

인공신경망은 생물학적 뉴런에서 영감을 받아 만들어진 인공 뉴런 모델에서 출발하였습니다.

생물학적 뉴런, Wikipedia

생물학적 뉴런은 신경세포입니다. 여러 신경세포로부터 신호(입력)를 수상 돌기를 통해 받아 세포체에서 신호를 처리하고, 일정 임계값 이상이 되면 축삭 돌기를 통해 다른 신경세포로 신호를 전달합니다. 이때 신경세포 간 연결부위인 시냅스라는 곳에서 신호의 강도를 조절하는데, 학습과 경험을 통해 강화되거나 약화되기도 합니다.

인공신경망의 인공 뉴런은 생물학적 뉴런의 입력 신호 통합 및 임계값 초과 시 신호 발화를 수학적으로 구현하였습니다.

  • 수상 돌기는 입력층(input layer)
  • 시냅스의 강도 조절은 가중치(weight) 조절
  • 신호 처리는 가중치 합산 후 활성화 함수(activation function) 적용
  • 축삭 돌기는 출력층(output layer)
  • 에 대응합니다.

그리고 중간에 위치하는 은닉층(hidden layer) 은 입력층과 출력층 사이에서 입력 신호를 가중치와 활성화 함수를 통해 복잡하게 변환, 추상화하는 역할을 합니다.

은닉층이 많아질수록 신경망은 더 깊어지고 복잡한 특징을 학습할 수 있어, 난이도 높은 패턴 인식과 분류 문제를 해결하는 데 유리합니다.

실제 뇌의 뉴런은 훨씬 복잡하고, 신경전달물질과 시냅스의 화학적/전기적 작용 등 다양한 요소에 의해 작동합니다. 본 설명은 신경세포(뉴런)의 신호 전달 과정을 이해하기 쉽게 축약하였기 때문에, 틀린 부분이 있을 수도 있습니다.

 

인공 신경망으로 모델 만들기

앞서 로지스틱 회귀에서 만든 데이터 표준화된 훈련 데이터 train_scaled와 train_target을 사용하겠습니다. 로지스틱 회귀에서는 모델을 평가할 때 성능 안정화를 위해 교차 검증을 사용했지만, 인공 신경망에서는 교차 검증 없이, 검증 세트를 따로 분리해 사용합니다.

인공 신경망에서 교차 검증 없이, 검증 세트를 분리해 사용하는 이유

1. 딥러닝 분야의 데이터셋이 충분히 커서 검증 점수가 안정적이다.
2. 교차 검증을 수행하면 훈련 시간이 오래 걸린다

 

전체 세트 60000개 중에서 20%인 12000개는 검증 세트로 분리가 되어서 훈련 세트는 남은 80%인 48000개의 샘플이 된 것을 확인할 수 있습니다.

 

먼저, 훈련 세트로 모델을 만든 후 검증 세트로 훈련한 모델을 평가해 보겠습니다.

  • 입력층 객체를 input으로 하면 파이썬 내장 함수로 오해받을 수 있기 때문에 inputs라고 씁니다.
  • shape 매개변수에는 튜플을 전달해야 하기 때문에, 꼭 (784, )와 같이 배열의 크기를 지정해야 합니다.
  • 패션 MNIST 데이터셋의 클래스는 총 10개 이기 때문에, 밀집층 매개변수에 들어갈 뉴런 개수를 10개로 지정합니다.
  • 다중 분류라 밀집층 매개변수에 들어갈 뉴런의 출력에 적용할 함수를 소프트맥스 함수로 지정했습니다.
  • 신경망 모델을 만들 때, Sequential 클래스에 입력층과 밀집층 객체를 리스트로 묶어 전달했습니다.
  • 소프트맥스 함수처럼 뉴런의 선형 방정식 계산 결과에 적용하는 함수를 활성화 함수(activation function)이라고 부릅니다.

인공 신경망으로 패션 아이템 분류하기

케라스 모델은 훈련하기 전에 손실 함수의 종류와 훈련 과정에서 계산하고 싶은 측정값을 지정해야 합니다. model 객체의 compile() 메서드에서 수행합니다.

패션 MNIST는 클래스가 10개 이므로 다중 분류 문제입니다. 그래서 크로스 엔트로피 손실 함수를 사용해야 합니다. 케라스에서는 크로스 엔트로피 손실 함수를 'sparse_categorical_crossentropy'라고 합니다. 이진 분류 문제에 사용하는 이진 크로스 손실 함수는 ‘binary_crossentropy’ 입니다.

 

챕터 4에 나와있다시피, 이진 분류 문제에 사용하는 이진 크로스 손실 함수는 양성 샘플에 1을, 음성 샘플에는 (1-타깃값)을 곱해서 양성과 음성 클래스에 대한 크로스 엔트로피 손실을 모두 계산하였습니다.

 

다중 분류일 경우에는, 각 클래스에 대한 확률이 모두 출력되기 때문에 타깃값을 해당 클래스만 1이고 나머지는 모두 0을 곱해 0인 배열로 만들어 크로스 엔트로피 손실을 계산합니다. 이걸 원-핫 인코딩(one-hot encoding)이라고 부릅니다.

 

하지만, 케라스에서는 정수로 된 타깃값을 굳이 원-핫 인코딩으로 바꾸지 않고 그대로 사용할 수 있습니다. sparse_categorical_crossentropy는 정수로 된 타깃값을 변환 없이 사용해 크로스 엔트로피 손실을 계산합니다. 만약 타깃값을 원-핫 인코딩으로 준비했자면 model 객체의 compile() 메서드에서 loss=’categorical_crossentropy’라고 지정하면 됩니다.

 

케라스는 모델 훈련마다 기본으로 에포크마다의 손실 값을 출력해 줍니다. 정확도를 함께 출력하기 위해 metrics 매개변수에 accuracy를 지정했습니다. 여러 개의 지표를 지정할 수 있는데, 하나의 지표를 지정할 때도 리스트로 전달하는 것을 잊지 마세요!

이제 모델을 훈련합니다!

 

케라스는 에포크 마다 소요 시간과 손실, 정확도를 출력해줍니다. 4 번째에 정확도가 85%를 넘어섰습니다.

이제 검증 세트에서 모델의 성능을 확인해 보겠습니다. 케라스에서는 모델의 성능을 평가할 때 evaluate() 메서드를 사용합니다.

 

evaluate() 메서드의 출력 결과는 fit() 메서드와 비슷합니다. 일반적으로, 검증 세트의 점수는 훈련 세트 점수보다 낮게 나옵니다. 예상대로 평가 결과는 훈련 세트의 점수(0.8537) 보다 조금 낮은 0.8462의 정확도로 나왔습니다.

 

기본 숙제

  • 07-1 확인문제 풀고 설명하기

확인 문제

1. 어떤 인공 신경망의 입력 특성이 100개이고 밀집층에 있는 뉴런 개수가 10개 일 때 필요한 모델 파라미터의 개수는 몇 개인가?

모델 파라미터의 개수는 (입력 특성의 개수)뉴런 개수+절편 총 개수로 계산합니다. 그래서 10010+10=1010개로 정답은 3번입니다.

 

 

2. 케라스의 Dense 클래스를 사용해 신경망의 출력층을 만들려고 합니다. 이 신경망이 이진 분류 모델이라면 activation 매개변수에 어떤 활성화 함수를 지정해야 하나요?

이진 분류이므로 시그노이드 함수를 활성화 함수로 지정해야 합니다. 그래서 정답은 2번입니다. 만약 다중 분류라면 소프트맥스 함수를 지정해야 합니다.

 

 

3. 케라스 모델에서 손실 함수와 측정 지표 등을 지정하는 메서드는 무엇인가요?

compile() 메서드입니다. 정답은 4번

 

4. 정수 레이블을 타깃으로 가지는 다중 분류 문제일 때 케라스 모델의 compile() 메서드에 지정할 손실 함수로 적절한 것은 무엇인가?

정수 레이블을 변환 없이 타깃으로 가져가기 때문에 ‘sparse’가 들어가야 하고 다중 분류 문제는 ‘categorical_crossentropy’입니다. 그래서 정답은 ‘sparse_categorical_crossentropy’인 1번입니다.

 

07-2 심층 신경망

2개의 층

케라스 API를 사용해서 패션 MNIST 데이터셋을 불러오고 데이터 표준화 및 1차원 배열로 펼치고 훈련 및 검증 세트로 나눕니다.

여기까지는 07-1절과 동일합니다. 그래서 코드와 코드 설명은 생략합니다.

 

이제, 인공신경망 모델에 층을 2개 추가해 보겠습니다.

07-1절에서 만든 신경망 모델과는 다르게, 입력층과 출력층 사이에 은닉층을 추가합니다. 은닉층이란, 신경망 모델에서 입력층과 출력층 사이의 모든 층을 뜻합니다. 은닉층에는 활성화 함수가 항상 껌딱지처럼 붙어 다닙니다.

  • 활성화 함수: 신경망 층의 선형 방정식의 계산 값에 적용하는 함수

07-1절의 신경망 모델의 출력층에 적용했던 소프트맥스 함수를 기억하시나요? 이 소프트맥스 함수도 일종의 활성화 함수입니다!

다만, 출력층의 활성화 함수에는 제한된 종류만 사용 가능합니다. 이진 분류에는 시그모이드 함수를 사용하고 다중 분류에서는 소프트맥스 함수를 사용합니다. 은닉층의 활성화 함수에는 이런 제한이 없어 비교적 자유롭습니다. 대표적으로 시그모이드 함수와 볼 렐루 함수 등을 사용합니다.

 

회귀를 위한 신경망의 출력층에서는 어떤 활성화 함수를 사용할까?

분류 문제에서는 각 클래스의 확률을 계산하기 위해서 출력층에 활성화 함수를 사용한다.

챕터 3에서 다루었던 k-최근접 이웃 회귀 모델을 복습해 보자.

회귀에서는 예측하려는 값이나 타깃이 모두 임의의 숫자이기 때문에, 정확한 숫자를 맞춘다는 것은 거의 불가능하다. 그래서 회귀 모델의 성능은 정확도 대신 결정계수 R^2로 평가한다.

즉, 회귀 문제는 활성화 함수를 적용할 필요가 없어 출력층의 선형 방정식을 그대로 적용하고 실행하기 때문에 Dense 층의 activation 매개변수에 값을 지정하지 않는다.

 

 

은닉층에 활성화 함수를 적용하는 이유

활성화 함수는 선형 연산의 한계를 넘어 비선형성을 도입함으로써, 신경망이 복잡한 함수를 근사하고 표현력을 확장할 수 있게 한다.

만약 은닉층에 활성화 함수(비선형 함수)가 없다면, 신경망은 결국 W1 * X → W2 * (...) 같은 일련의 선형 변환을 반복하게 된다. 그런데 선형 변환끼리의 조합은 결국 또 다른 선형 변환 하나로 환원될 수 있다. 즉, 아무리 은닉층을 깊게 쌓아도 단층 퍼셉트론과 같은 표현력밖에 갖지 못한다. 활성화 함수가 도입되면, 각 층이 단순 선형사상에 머무르지 않고 비선형적인 매핑을 수행할 수 있게 된다. 이는 신경망이 단순 회귀선이나 초평면 분류자가 아닌, 훨씬 복잡한 **비선형적 결정 경계(decision boundary)**를 학습할 수 있게 만든다.

정리하자면, 활성화 함수를 통해 각 층이 이전 층과 단순히 선형적으로 흡수되지 않고, 실제로 계층적 특징 변환(feature transformation)을 수행할 수 있다.

 

은닉층에 많이 사용하는 활성화 함수 중 하나는 챕터 4에서 다루었던 시그모이드 함수입니다.

시그모이드 함수는 뉴런의 출력 z값을 0~1 사이의 값으로 압축합니다.

입력층과 은닉층, 출력층을 만들어 보겠습니다.

은닉층의 뉴런 개수는 무조건 출력층의 뉴런 개수보다 많이 만들어야 합니다. 클래스 별 확률을 예측해야 하는데 출력층보다 뉴런 개수가 적으면 부족한 정보가 전달되게 됩니다.

출력층의 뉴런 개수는 클래스의 개수와 동일한 10개로 지정하고 다중 분류이므로 활성화 함수로 소프트맥스 함수를 사용하였습니다.

심층 신경망 만들기

앞에서 만든 inputs, dense1, dense2 객체를 Sequential 클래스에 추가해서 심층 신경망을 만들어 보겠습니다.

위와 같이 Sequential 클래스의 객체를 만들었습니다. 여러 개의 층을 추가하려면 위와 같이 리스트에 계속해서 필요한 층을 추가하면 됩니다. 이때 주의할 점은 리스트의 순서입니다. 리스트의 맨 앞에는 입력층이, 맨 뒤에는 반드시 출력층이 위치해야 합니다.

인공 신경망의 강력한 성능은 층을 추가하여 입력 데이터에 대한 연속적인 학습을 진행하는 능력으로부터 나옵니다.

 

케라스는 모델의 층에 대한 정보를 summary() 메서드를 통해 제공합니다.

  • 맨 첫 줄에 모델의 이름이 나옵니다.
  • 그다음으로 모델에 들어있는 층이 순서대로 나열됩니다. 이때, 입력층을 제외한 맨 처음의 은닉층부터 출력층까지 순서대로 나열되어 있습니다. 층마다 층의 이름, 클래스, 출력 크기, 모델 파라미터 개수가 출력됩니다. 층의 이름은 name 매개변수로 지정할 수 있습니다. 층의 종류는 모두 Dense로 완전 연결층을 의미합니다.
  • 첫 번째 은닉층의 출력 크기는 (None, 100)입니다. 첫 번째 차원은 샘플의 개수를 의미합니다. 아직 샘플의 개수를 정의하지 않았습니다. 이렇게 신경망 층에 입력되거나 출력되는 배열의 첫 번째 차원을 배치 차원이라 부릅니다. 그래서 배치 차원은 None입니다.
    • 두 번째 차원인 100은 출력되는 특성 개수를 의미합니다. 첫 번째 은닉층의 뉴런을 100개로 설정하였으니 출력되는 특성 개수는 100개가 됩니다. 즉, 처음의 784개의 픽셀값이 첫 번째 은닉층을 거치면서 100개의 특성으로 압축되어 100차원의 벡터로 출력된다는 의미입니다.
  • 모델 파라미터 개수는 각 층의 정보에서 맨 오른쪽, 마지막에 출력됩니다. 첫 번째 은닉층은 Dense 층이므로 입력 픽셀 784개와 100개의 조합에 대한 가중치, 뉴런마다 1개씩 존재하는 절편이 있어 총 모델 파라미터 개수는 784100+1100=78500 개가 됩니다.
  • summary() 메서드의 마지막에는 총 모델 파라미터(Total params) 개수와 훈련되는 파라미터(Trainable params) 개수, 훈련되지 않은 모델 파라미터(Non-trainable params) 개수가 출력됩니다. 이 모델에선 총 모델 파리미터와 훈련되는 파라미터 개수가 79510개로 동일하게 나왔습니다. 간혹 경사 하강법으로 훈련되지 않은 파라미터를 가진 층이 있으면, 훈련되지 않은 모델 파라미터 개수가 나타납니다.
케라스 모델에서 샘플의 개수를 정의하지 않은 이유

이 케라스 모델은 훈련 세트를 전부 한 번에 사용하지 않고 여러 번에 걸쳐 나누어서 훈련하는 미니배치 경사 하강법을 사용한다. 케라스의 기본 미니배치 크기는 32개인데, fit() 메서드에서 batch_size 매개변수로 변경할 수 있다. 샘플 개수를 고정하지 않고 어떠한 크기의 배치에도 유연하게 대응할 수 있게 샘플의 개수를 ‘None’으로 고정하지 않았다.

 

층을 추가하는 다른 방법

앞의 방법은 Dense 클래스의 객체 inputs, dense1, dense2를 만들어서 Sequential 클래스에 전달하는 방식이었습니다. 이 방식 말고도 아래와 같이 Sequential 클래스의 생성자 안에서 바로 클래스의 객체를 만드는 경우가 많습니다.

이렇게 작업하면 Dense 클래스의 객체를 따로 변수에 담지 않고 바로 Sequential 생성자에 전달하게 됩니다. 추가되는 층을 한눈에 쉽게 알아볼 수 있다는 장점이 있습니다. summary() 메서드의 출력으로 모델과 층의 이름이 잘 반영되었는지 확인할 수 있습니다.

 

  • 모델 이름: 패션 MNIST 모델로 name 매개변수로 지정한 모델 이름이 반영되었습니다.
  • 은닉층과 출력층의 지정된 이름이 반영되었습니다.

이 방법은 간편하고 쉬운 문법으로 구성되어 있지만, 두 가지 한계점이 있습니다.

  1. 아주 많은 층을 추가하려면 Sequential 생성자가 매우 길어지게 됨
  2. 조건에 따라 층을 추가할 수 없음

그래서 Sequential 클래스에서 층을 추가할 때 모델의 add() 메서드를 가장 널리 사용합니다.

 

모델의 add() 메서드를 사용한 방법 역시 Dense 클래스의 객체를 변수에 따로 담지 않고 바로 add() 메서드로 전달합니다. 또한, 한눈에 추가되는 층을 볼 수 있다는 장점이 있습니다. 이전 방법과의 비교되는 점은 바로 프로그램 실행 시 동적으로 층을 선택하여 추가할 수 있다는 점입니다.

 

왜 두 번째 방법만 동적으로 추가가 가능할까?

첫 번째 방법(생성자에 리스트로 층을 한 번에 넣는 방식)은 실행 전에 층의 구성(어떤 층이 몇 개 들어갈지 등)을 미리 전부 정해야 한다.
반면 두 번째 방법(add 사용)은 프로그램을 실행하면서 조건에 따라 층을 순차적으로 추가할 수 있다.

예를 들어 반복문이나 if 조건문 안에서 특정 상황일 때만 층을 더 넣거나, 어떤 데이터가 들어왔을 때만 층을 바꿀 수도 있다. 이렇게 하면, 층 개수·종류를 코드 실행 중에 자유롭게 바꿀 수 있다.

summary() 메서드의 결과에서 층과 모델 파라미터 개수는 당연히 동일합니다.

코드와 실행 결과는 같기 때문에 생략하겠습니다.

 

이제 모델을 훈련시켜 보겠습니다. compile() 메서드를 사용하며, 07-1절과 동일합니다. 코드는 생략하고 실행 결과와 07-1 절의 실행 결과를 비교하기 위해 같이 가져왔습니다.

07-1절 실행 결과
07-2절 실행 결과

 

위쪽의 07-1절의 간단한 신경망 모델보다 아래쪽의 신경망 모델이 성능이 더 좋습니다. 둘의 차이점은 이번 07-2절에서 배운 은닉층을 추가한 것 말고는 없기 때문에 이 추가된 층이 성능을 향상했다는 점을 잘 알 수 있습니다!

 

케라스 API는 인공 신경망에 몇 개의 층을 추가하더라도 compile() 메서드와 fit() 메서드의 사용법은 동일하다는 장점을 가지고 있습니다.

 

렐루 함수

시그노이드 함수가 거의 출력층의 활성화 함수로만 쓰이는 이유

1. 기울기 소실 문제 (Gradient Vanishing)
인공 신경망 전체 구조적인 문제로, 많은 층을 거치면서 gradient가 점점 작아지면서 사라진다. 시그모이드 함수는 출력 값을 0과 1 사이로 제한한다. 이때 입력 값이 너무 크거나 작으면 출력이 거의 0 또는 1에 가까워져서 함수의 미분값이 0에 가까워진다.
역전파 과정에서, 미분값인 기울기를 여러 층을 거치면서 곱해줘야 하는데 기울기가 점점 작아지면 아래층으로 전달되는 신호가 거의 없어지거나 사라지게 된다. 그러면 학습이 잘 되지 않는다. 특히 층이 깊은 신경망에서 이 문제가 심각해지는데, 렐루 함수의 등장 전까지 해결되지 않았다.

2. 출력값 중심이 0이 아님 (Non-zero-centered output)
시그모이드 함수의 출력은 항상 양수(0에서 1 사이)다. 신경망은 여러 층을 거치면서 가중치를 조금씩 바꿔가면서 학습한다. 근데 출력값이 모두 양수이면 층에서 계산하는 기울기가 모두 비슷한 방향으로만 움직이게 된다.
쉽게 말하자면, 길을 걸을 때 무조건 우회전만 해서 간다고 해보자. 우회전만 하는 사람은 자유롭게 길을 가는 사람보다 같은 목적지에 빨리 도착하기 어려울 것이다.
즉, 가중치 업데이트 시 모든 파라미터의 기울기가 같은 부호를 가지게 되어 최적화의 방향이 지그재그 형태로 움직이게 되면서 학습이 비효율적이고 학습 속도가 저하된다.
출처: https://happy-obok.tistory.com/55

3. 포화 영역 문제 (Saturation)
포화 현상이란, 한 뉴런 단위에서 일정한 구간 변화가 없는 현상이다. 시그모이드 함수의 입력값이 매우 크거나 작으면 함수값이 0 또는 1에 가까워진다. 그러면 미분값은 거의 0이 된다. 입력 데이터가 포화 영역에 들어가면 뉴런의 출력 변화가 거의 없어지게 되어 학습 중 뉴런이 거의 죽은 상태가 된다.
포화 영역 때문에 각 층의 기울기가 0에 가까워지고, 이것이 연속적으로 곱해지면서 신경망 전체의 학습이 멈추게 되어 기울기 소실이 일어나게 된다. 포화 현상이 기울기 소실의 원인이 되는 셈이다.

이러한 이유들 때문에 현대 신경망에서는 은닉층의 활성화 함수로 시그모이드보다는 ReLU 같은 다른 활성화 함수가 더 자주 사용된다.

 

시그모이드 함수의 한계점 때문에 이를 개선하기 위한 다른 활성화 함수로 렐루 함수가 제안되었습니다.

렐루 함수는 입력이 양수이면 그대로 입력값을 통과시키고 입력값이 음수이면 0만 출력시키는 함수입니다.

 

렐루 함수 출처: https://machinelearningmastery.com/rectified-linear-activation-function-for-deep-learning-neural-networks/

 

렐루 함수는 max(0, z)라고 쓸 수 있습니다. 렐루 함수는 z가 0보다 크면 z값을 그대로 출력하고 0보다 작으면 0을 출력합니다. 렐루 함수는 특히 이미지 처리에서 좋은 성능을 냅니다.

 

잠시, 케라스에서 제공하는 편리한 층인 Flatten 층에 대해서 알아보겠습니다.

07-1절과 이번 절(07-2)에서 사용한 패션 MNIST 데이터셋은 흑백 패션 아이템 이미지로 28*28 크기의 2차원 데이터입니다. 인공 신경망에 주입하기 위해 1차원으로 펼쳐주는 과정을 꼭 거쳐야 했지만, 케라스에서 제공하는 Flatten 층을 사용하면 이 번거로운 과정을 거칠 필요가 없습니다.

Flatten 클래스는 배치 차원을 제외하고 나머지 입력 차원을 모두 일렬로 펼쳐줍니다. 펼치기만 하고 가중치나 절편을 곱하는 학습을 하지 않기 때문에 모델의 성능에 영향을 주지도 않습니다. Flatten 클래스를 층처럼 입력층과 은닉층 사이에 추가하기 때문에 Flatten 층이라 부릅니다.

 

  • 첫 번째 층으로 Flatten 층이 나왔습니다. summary() 메서드로 출력된 층은 입력층을 제외하고 층을 순서대로 나열합니다. 그래서 입력층과 은닉층 사이에 들어가는 Flatten 층이 첫 번째로 나열되었습니다. Flatten 층은 학습하지 않기 때문에 모델 파라미터는 0개로 나옵니다. 출력 결과가 (None, 784)로 나와 두 번째 차원이 784입니다. 이 말은 출력되는 특성 개수가 784개라는 말이고 입력층에 들어간 입력값의 차원이 784개라는 말이 됩니다. Flatten 층은 학습하지 않아 차원 축소가 일어나지 않기 때문에 입력층에 들어간 차원이 그대로 나옵니다.
  • 나머진 그대로입니다.

훈련 데이터를 다시 준비해서 모델을 훈련해 보겠습니다. Flatten 층을 추가할 거라 입력값을 펼치는 reshape() 메서드를 적용하지 않습니다.

모델을 컴파일하고 훈련하는 것은 이전과 동일하므로 코드를 생략하고 출력 결과만 보겠습니다.

 

 

모델의 성능은 시그노이드 함수를 활성화 함수로 사용한 모델보다 성능이 더 좋아졌습니다.

검증 세트에서의 성능도 확인해 봅시다.

검증 세트 성능
07-1절 은닉층 추가 전 검증 세트의 성능 결과

 

1절의 은닉층을 추가하지 않았을 때보다 약 0.2% 성능이 더 향상되었습니다.

 

옵티마이저

챕터 3에서 배웠던 하이퍼파라미터 정의를 다시 복습해 보겠습니다.

하이퍼파라미터: 머신러닝 알고리즘이 학습하지 않은 파라미터로, 사람이 사전에 지정해야 함. 대표적으로 릿지와 라쏘의 규제 강도 alpha 파라미터가 있음

07-2절에서는 신경망 모델에 은닉층을 하나 추가했습니다. 신경망 모델에 넣을 적절한 은닉층의 개수는 몇 개일까요? 은닉층의 개수는 모델이 훈련을 통해 알아내는 것이 아니라 모델을 만들 때 지정해 줘야 합니다. 그래서 추가할 은닉층의 개수는 하이퍼파라미터에 속합니다.

 

07-2절까지 다룬 하이퍼파라미터에는 에포크 매개변수, 배치 사이즈 매개변수, 층의 종류, 활성화 함수, 뉴런 개수, 추가할 은닉층의 개수, 경사 하강법 알고리즘의 학습률이 있습니다. 은닉층의 종류로 Dense 즉 밀집층만 다루었지만 층의 종류는 여러 가지입니다.

 

compile() 메서드에서는 케라스의 기본 경사 하강법 알고리즘인 RMSprop를 사용했습니다. 케라스는 다양한 종류의 경사 하강법 알고리즘을 제공하는데 이들을 옵티마이저(optimizer)라고 부릅니다.

가장 기본적인 옵티마이저는 확률적 경사 하강법인 SGD입니다. SGD는 미니 배치를 사용합니다.

SGD 옵티마이저는 compile() 메서드의 optimizer 매개변수를 ‘sgd’로 지정하여 사용할 수 있습니다. SGD 클래스의 학습률의 기본값은 0.01입니다. 학습률은 learning_rate 매개변수에 원하는 값을 전달하여 변경할 수 있습니다.

 

SGD 클래스의 momentum 매개변수의 기본값은 0입니다. 만약, momentum 매개변수에 0보다 큰 값을 전달하면 마치 이전의 그레이디언트를 가속도처럼 사용하는 모멘텀 최적화를 사용하게 됩니다. 보통 0.9 이상으로 지정합니다.

 

모멘텀 최적화를 사용하게 했으면, nesterov 매개변수의 기본값인 FALSE를 TRUE로 변경하여 네스테로프 모멘텀 최적화(또는 네스테로프 가속 경사)를 사용할 수도 있습니다. 네스테로프 모멘텀은 모멘텀 최적화를 두 번 반복해서 구현합니다. 일반적으로 네스테로프 모멘텀이 SGD보단 더 나은 성능을 제공합니다.

 

모델 최적점과 학습률

모델이 학습을 하면서 점점 좋은 상태(최적점)에 가까워질수록 학습률을 낮출 수 있다. 여기서 학습률이란, ‘한 번에 얼마만큼씩 배워서 모델 파라미터를 바꿀지’를 의미한다.

처음에는 학습률이 크면 한 번에 많이 움직이기 때문에 빠르게 학습하지만, 최적점에 가까워지면 큰 걸음으로 가다 낭떠러지에 떨어질 수 있어서 학습률을 작게 줄여서 천천히 움직이는 것이 더 안전하다.

그래서 최적점에 안정적으로 도착할 확률이 높아진다.

적응적 학습률(adaptive learning rate)은 모델이 최적점에 가까이 갈수록 안정적으로 수렴하도록 학습률을 낮추도록 조정하는 방법으로, 이런 방식들은 학습률 매개변수를 튜닝하는 수고를 덜 수 있는 것이 장점이다. 쉽게 말해, ‘학습을 할 때 상황에 맞게 학습률을 자동으로 조절하는 방법’이다.

 

적응적 학습률을 사용하는 대표적인 옵티마이저로 Adagrad와 RMSprop, Adam이 있습니다. compile() 메서드의 optimizer 매개변수에 각각 ‘adagrad’와 ‘rmsprop’, ‘adam’을 지정하여 사용합니다. optimizer 매개변수의 기본값은 ‘rmsprop’입니다.

 

Adam 옵티마이저는 모멘텀 최적화와 RMSprop 장점을 접목한 것입니다. Adam 클래스도 keras.optimizer 패키지 아래에 있습니다. Adagrad와 RMSprop와 같이 learning_rate 매개변수의 기본값으로 모두 0.001을 사용합니다.

 

Adam 옵티마이저를 사용해 패션 MNIST 모델을 훈련시켜 보고 RMSprop를 사용한 모델과 성능 비교를 해보겠습니다. 모델 생성은 이전과 같은 코드를 사용하므로 생략합니다.

 

 

이 출력 결과를 보면 RMSprop 옵티마이저를 사용한 모델과 거의 같은 성능을 보여줍니다. 마지막으로 검증 세트에서의 성능도 확인해 봅시다.

 

기본 RMSprop의 성능보다 조금 더 좋은 성능을 보여줍니다.

 

추가 숙제

  • 07-2 확인문제 풀고 설명

확인 문제

1. 다음 중 모델의 add() 메서드 사용법이 올바른 것은 어떤 것인가요?

정답은 2번입니다. add() 메서드를 사용해서 모델의 층을 추가할 때에는 층의 객체(클래스)인 Dense, 층에서 사용할 뉴런 개수, Dense 층의 activation 매개변수로 사용할 활성화 함수를 전달해야 합니다. 그래서 model.add(keras.layers.Dense(10, activation=’relu’)) 이렇게 작성해야 합니다.

 

2. 크기가 300*300인 입력을 케라스 층으로 펼치려고 합니다. 다음 중 어떤 층을 사용해야 하나요?

2차원 배열인 입력값을 펼치는 것은 입력층과 은닉층 사이에 들어가는 케라스 층인 Flatten 층에서 일어나므로 정답은 2번입니다. Flatten 층은 배치 차원을 제외한 나머지 입력 차원을 일렬로 펼칩니다. Plate, Normalize라는 이름의 클래스는 존재하지 않습니다. Dense 층은 신경망에서 가장 기본적인 밀집층으로 은닉층에 속합니다. 활성화 함수가 항상 포함되어 있습니다.

 

3. 다음 중에서 이미지 분류를 위한 심층 신경망에 널리 사용되는 케라스의 활성화 함수는 무엇인가요?

정답은 3번입니다. 케라스의 활성화 함수 중 이미지 분류를 위한 심층 신경망에 널리 사용되는 것은 렐루(Relu) 함수입니다. ‘linear’는 선형 활성화 함수라고 부르며 실제로는 활성화 함수를 사용하지 않는 것을 의미합니다. 회귀 문제는 활성화 함수를 적용할 필요가 없어 회귀 문제에서의 신경망에 사용될 수 있습니다.

 

4. 다음 중 적응적 학습률을 사용하지 않는 옵티마이저는 무엇인가요?

정답은 1번입니다. 가장 기본적인 경사 하강법을 사용하는 SGD 옵티마이저만 적응적 학습률을 사용하지 않습니다. SGD 옵티마이저는 momentum 매개변수를 0보다 큰 값으로 지정하여 모멘텀 최적화를 사용할 수 있고 거기에 nesterov 매개변수를 True로 바꾸면 네스테로프 모멘텀을 사용할 수 있습니다. 나머지 Adagrad, RMSprop, Adam 모두 대표적인 적응적 학습률 옵티마이저입니다.

 

07-3절은 6주차 공부인증 (하)에서 다루겠습니다.