관리 메뉴

클라이언트/ 서버/ 엔지니어 "게임 개발자"를 향한 매일의 공부일지

딥러닝을 시작합니다 2 - 인공 신경망 2 : 인공 신경망으로 모델 만들기 본문

인공지능/딥러닝

딥러닝을 시작합니다 2 - 인공 신경망 2 : 인공 신경망으로 모델 만들기

huenuri 2024. 10. 25. 06:52

어제까지 해서 SQL 공부를 모두 마치고 오늘부터는 무슨 공부를 할지 고민이 많이 되었다. 새벽에 지난번에 공부했던 머신러닝을 조금 복습하고 이제 오늘부터는 딥러닝 공부를 다시 시작해 보기로 했다. 머신러닝은 무척 많은 시간을 투자해서 공부를 열심히 했지만, 딥러닝은 그렇지 못했다. 내가 딥러닝을 공부하고 싶었던 것도 사실은 컴퓨터 비전 관련 공부를 하고 싶어서였다.

책도 구입해 놓았지만 책이 너무 어려워서 과연 할 수 있을까 하는 마음이 들기도 한다. 하지만 컴퓨터 비전을 공부하게 된다면 딥러닝은 가볍게 건너뛰고 가야 하기에 고민이 많이 되었다. 아직 공부할 게 많아서 자바스크립트도 스프링도 11월까지는 끝내야 하기 때문이다. 그래도 내가 하고 싶은 공부를 위해서 느리더라도 일단 시작해 보기로 했다.

 

강의도 처음에는 잘 듣지 않고 내용만 캡처한 후에 책을 보면서 학습 내용을 정리하는 식으로 공부했었다. 하지만 얼마 전에 강의를 들어보니 정말 잘 가르치고 책에서는 알 수 없었던 여러 가지를 배울 수 있었다. 어차피 12월까지는 웹 개발 공부를 할 목표를 세웠으니 차근차근 공부하다 보면 분명 내가 목표한 과목들을 전부 공부할 수 있으리라 믿는다.

그럼 이어서 인공 신경망 공부를 시작해보겠다.


 

 

 

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

앞서 로지스틱 회귀에서 만든 훈련 데이터 train_scaled와 train_target을 사용하겠다. 로지스틱 회귀에서는 교차 검증을 사용해 모델을 평가했지만, 인공 신경망에서는 교차 검증을 잘 사용하지 않고 검증 세트를 별도로 덜어내어 사용한다.

 

그렇게 하는 이유는 

  1. 딥러닝 분야의 데이터셋은 충분히 크기 때문에 검증 점수가 안정적이고
  2. 교차 검증을 수행하기에는 훈련 시간이 너무 오래 걸리기 때문이다.

패션 MNIST 데이터셋이 그만큼 크지는 않지만 관례에 따라 검증 세트를 나누어보겠다. 사이킷런의 train_test_split() 함수를 사용한다.

 

 

 

 

60,000개 중에서 12,000개가 검증 세트로 분리되었다. 먼저 훈련 세트로 모델을 만들고 그다음 검증 세트로 훈련한 모델을 평가해 보겠다. 그에 앞서 인공 신경망 그림의 오른쪽에 놓인 층을 만들어보겠다. 이 층은 다음 그림처럼 10개의 패션 아이템을 분류하기 위해 10개의 뉴런으로 구성된다.

 

케라스의 레이어(keras.layers) 패키지 안에는 다양한 층이 준비되어 있다. 가장 기본이 되는 층은 밀집층이다. 왜 밀집이라고 부를까? 다음 그림에서 왼쪽에 있는 784개의 픽셀과 오른쪽에 있는 10개의 뉴런이 모두 연결된 선을 생각해 보자. 정말 빽빽하다.

이런 층을 양쪽의 뉴런이 모두 연결하고 있기 때문에 완전 연결층이라고도 부른다. 이제 케라스의 Dense 클래스를 사용해 밀집층을 만들어보겠다. 필요한 매개변수는 뉴런 개수, 뉴런의 출력에 적용할 함수, 입력의 크기이다.

 

 

첫 번째 매개변수로 뉴런 개수를 10개로 지정한다. 10개의 패션 아이템을 분류하기 때문이다. 10개의 뉴런에서 출력되는 값을 확률로 바꾸기 위해서는 소프트맥스 함수를 사용한다. 

케라스 층에서는 activation 개개변수에 이 함수를 지정한다. 만약 2개의 클래스를 분류하는 이진 분류라면 시그모이드 함수를 사용하기 위해 activation='sigmoid'와 같이 설정한다.

 

세 번째 매개변수는 입력값의 크기이다. 10개의 뉴런이 각각 몇 개의 입력을 받는지 튜플로 지정한다. 여기서는 784개의 픽셀값을 받는다. 이제 이 밀집층을 가진 신경말 모델을 만들어야 한다.

 

 

책에는 대괄호를 넣지 않고 소괄호로 dense 매개변수를 넣어주었다. 하지만 이렇게 하면 TypeError가 발생했다. 앞에서 만든 밀집층의 객체 dense를 전달했다. 여기서 만든 model 객체가 바로 신경망 모델이다. 다음 그림은 지금까지 만든 신경망이다.

 

 

이 그림에서 가중치는 표시하지 않았다. 절편의 경우 아예 선도 그리지 않는 경우가 많지만, 절편은 뉴런마다 더해진다. 소프트맥스와 같이 뉴런의 선형 방정식 계산 결과에 적용되는 함수를 활성화 함수라고 부른다. 여기서는 이 값을 a로 표시하겠다. 


 

 

 

 

 

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

지금까지 사용했던 사이킷런에 비해 케라스에서 모델을 만드는 방식은 조금 다르다. 케라스 모델은 훈련하기 전에 설정 단계가 있다. 이런 설정을 model 객체의 compile() 메서드에서 수행한다. 꼭 지정해야 할 것은 소실 함수의 종류이다. 그다음 훈련 과정에서 계산하고 싶은 측정값을 지정한다.

 

 

이 역시 책에 있는 코드를 사용하면 오류가 뜬다. 오류의  이유는 앞서 발생한 것과 같은 이유 같다. 어쨌든 다중 분류에서는 크로스 엔트로피 손실 함수를 사용한다. 케라스는 이 두 손실함수를 다음과 같이 나눈다.

  • 이진 분류 : loss = 'binary_crossentropy'
  • 다중 분류 : loss = 'categorical_crossentropy'

sparse라는 이름은 왜 붙었을까? 이진 크로스 엔트로피 손실을 위해 -log(예측 확률)에 타깃값(정답)을 곱했다. 

 

이진 분류에서는 출력층의 뉴런이 하나이다. 이 뉴런이 출력하는 확률값 a(시그모이드 함수의 출력값)를 사용해 양성 클래스와 음성 클래스에 대한 크로스 엔트로피를 계산한다. 

이진 분류의 출력 뉴런은 오직 양성 클래스에 대한 확률(a)만 출력하기 때문에 음성  클래스에 대한 확률은 간단히 1-a로 구할 수 있다. 역시 이진 분류의 타깃값은 양성 샘플일 경우에는 1, 음성 샘플일 경우에는 0으로 되어 있다. 0을 곱하면 어떤 계산이든지 모두 0이 되기 때문에 특별히 음성 샘플일 경우 1로 바꾸어 계산한다. 이렇게 하면 하나의 뉴런만으로 양성과 음성 클래스에 대한 크로스 엔트로피 손실을 모두 계산할 수 있다.

 

패션 MNIST 데이터셋과 같이 다중 분류일 경우는 다음과 같이 계산한다.

 

출력층은 10개의 뉴런이 있고 10개의 클래스에 대한 확률을 출력한다. 첫 번째 뉴런은 티셔츠일 확률이고 두 번째 뉴런은 바지일 확률을 출력한다. 이진 분류와 달리 각 클래스에 대한 확률이 모두 출력되기 때문에 타깃에 해당하는 확률만 남겨 놓기 위해서 나머지 확률에는 모두 0을 곱한다.

 

예를 들어 샘플이 티셔츠일 경우 첫 번째 뉴런의 활성화 함수 출력인 a1에 크로스 엔트로피 손실 함수를 적용하고 나머지 활성화 함수 출력 a2~a10까지는 모두 '으로 만든다.

길이가 같은 넘파이 배열의 곱심은 원소별 곱셈으로 수행된다. 즉, 배열에서 동일한 위치의 원소끼리 곱셈이 된다. 

 

결국 신경망은 티셔츠 샘플에서 손실을 낮추려면 첫 번째 뉴런의 활성화 출력 a1의 값을 가능한 1에 가깝게 만들어야 한다. 바로 이것이 크로스 엔트로피 손실 함수가 신경망에 원하는 바이다. 만약 샘플이 바지일 경우는 다음과 같다.

 

 

두 번째 뉴런의 활성화 출력인 a2만 남기려면 두 번째 원소만 1이고 나머지는 모두 '으로 타깃값을 준비하면 된다. ㅏ지 샘플을 정확하게 분류하려면 신경망이 a2의 출력을 가능한 한 높여야 한다. 이와 같이 타깃값을 해당 클래스만 1이고 나머지는 모두 0인 배열로 만드는 것을 원-핫 인코딩이라고 부른다.

 

따라서 다중 분류에서 크로스 엔트로피 손실 함수를 사용하려면 0, 1, 2와 같이 정수로 된 타깃값을 원-핫 인코딩으로 변환해야 한다.

 

패션 MNIST 데이터의 타깃값은 다음과 같다.

 

텐서플로에서는 정수로 된 타깃값을 원-핫 인코딩으로 바꾸지 않고 그냥 사용할 수 있다. 정수로된 타깃값을 사용해 크로스 엔트로피 손실을 계산하는 것이 'sparse_categorical_crossentropy'이다. 타깃값은 원-핫 인코딩으로 준비했다면 compile9) 메서드에 손실 함수를 loss='categorical_crossentropy'로 지정한다.

케라스는 모델이 훈련할 때 기본으로 에포크마다 손실 값을 출력해 준다. 손실이 줄어드는 것을 보고 훈련이 잘 되었다는 것을 알 수 있지만 정확도를 함께 출력하면 좋다. 이를 위해 metrics 매개변수에 정확도 지표를 의미하는 'accuracy'를 지정했다.

 

이제 모델을 훈련해 보겠다. 처음 두 매개변수에 입력과 타깃을 지정한다. 그다음 반복할 에포크 횟수를 epochs 매개변수로 지정한다.

 

케라스는 친절하게도 에포크마다 걸린 시간과 손실, 정확도를 출력해 준다. 5번 반복에 정확도가 78%를 넘었다. 확실히 이전 모델보다 나아졌다. 

 

 

앞서 따로 떼어 놓은 검증 세트에서 모델의 성능을 확인해 보겠다. 케라스에서 모델의 성능을 평가하는 메서드는  evalute() 메서드이다.

 

예상대로 평가 결과는 훈련 세트의 점수보다 조금 낮은 81%의 정확도를 보인다. 

 

 


 

 

 

 

단원 마무리하기

 

 

 

 

 

 

 

 

1번은 밀집층에 있는 10개의 뉴런이 100개의 입력과 모두 연결되기 때문에 1000개의 가중치가 있다. 뉴런마다 1개의 절편이 있으므로 모델 파라미터는 이 10개를 합한 1010이 된다.

2번은 이진 분류일 경우 출력층의 뉴런이 1개이고 선형 방정식의 결과를 확률로 바꾸기 위해 'sigmoid' 함수를 사용한다. binary라는 활성화 함수는 없다. 


 

 

학습을 마치고

 

이번 시간에는 28 x 28 크기의 흑백 이미지로 저장된 패션 아이템의 데이터셋인 패션 MNIST 데이터셋을 사용했다. 먼저 로지스틱 손실 함수를 사용한 SGDClassifier 모델을 만들어 교차 검증 점수를 확인했다.

그다음 가장 인기 있는 딥러닝 라이브러리인 텐서플로와 라이브러리 텐서플로와 케라스 API를 소개하고 케라스를 사용해 간단한 인공 신경망 모델을 만들어 패션 아이템을 분류해 보았다. 

 

인공신경망 모델을 만들면서 이전 장에서 배웠던 로지스틱 손실 함수와 크로스 엔트로피 손실 함수를 다시 되새겨 보았다. 그리고 신경망에서 이런 손실 함수를 어떻게 계산하는지 그림을 통해 배웠다. 

정말 어려운 내용이었지만 그래도 한 단원을 다 마칠 수 있어서 정말 기뻤다. 앞의 부분은 10월 4일의 학습을 마지막으로 오늘 오랜만에 공부해 보니 3주나 지났다. 처음에는 집중도 잘 되지 않고 무슨 말인지도 도통 알아들을 수 없었지만 이제 다시 회복이 되었다.