본문 바로가기

spartacodingclub/개발보고서

가장 쉽게 배우는 머신러닝 3주차

https://spartacodingclub.kr/

 

 

*강의 내용*

 

딥러닝Deep Learning이란?

현실에서의 문제들은 대부분 선형이 아니라 비선형 구조를 띄고 있다.

예를 들어 딥러닝의 태동을 불러온 XOR 문제를 확인해보면, OR와 AND는 전부 선형으로 표현이 가능하다.

반면에 XOR은 표를 표현할 수 있는 선형 구조가 존재하지 않는다.

이런 문제에 대해 사람들은 Perceptron을 여러개 붙인 Multilayer Perceptrons (MLP)라는 개념을 도입해 문제를 풀려고 했지만, 상당수의 사람들이 당시 기술로는 불가능하다고 주장해 이때 딥러닝(MLP)의 발전은 후퇴하게 된다.

그러던 중 1974년에 발표된 폴Paul Werbos이라는 사람의 박사 논문이 발표되었고, 폴은 이와 같이 주장했다. MLP가 만들어낸 출력이 정답값과 다를 경우 W와 b(1주차에서 다루었던 것들)를 조절해야 한다. 즉, 출력에서 오차Error를 발견해 뒤에서 앞으로 조절해야 하는 방법이 필요하다.

당시에 앞으로만 전진하던 방식을 사용하기만 했었는데, 폴의 논문을 시작점으로 에러에 대해 피드백을 주기 위해 되돌아가는 방식이 도입되게 된 것이다.

이러한 MLP를 이용해 XOR문제를 풀 수 있게 되었고, 이를 역전파 알고리즘의 발견이라 한다.

 

Deep Neural Networks 구성 방법은?

딥러닝에서 네트워크의 구조는 크게 세 가지로 나누어진다.

입력층Input layer은 네트워크의 입력 부분으로 x값을 가리킨다.

출력층Output layer은 네트워크의 출력 부분으로 y값을 가리킨다.

은닉층Hidden layers은 입력층과 출력층을 제외한 중간층을 가리킨다.

딥러닝 네트워크MLP는 여러 층을 쌓아 구성하는데, 이때 대체로 은닉층의 중간 부분을 넓게 만든다. 예를 들어 노트의 개수가 점점 늘어나다가 줄어드는 방식으로 구성한다.

 

네트워크의 너비Width와 깊이Depth란?

너비Width는 은닉층의 개수를 그대로 두고 은닉층의 노드 개수를 늘리는 방식이다.

깊이Depth는 은닉층의 노드 개수를 그대로 두고 은닉층의 개수를 늘리는 방식이다.

 

배치batch와 이터레이션iteration이란?

수천만, 수억 개의 데이터셋을 가지고 있다고 가정했을 때, 이 데이터셋을 한꺼번에 메모리에 올려 학습시키면 엄청난 용량을 가진 메모리가 필요하고, 그 메모리를 사기 위해서는 엄청난 비용이 들게 될 것이다.

따라서 데이터셋을 작은 단위로 쪼개 학습시키는데, 이때 쪼개는 단위를 배치batch라고 부르고, 쪼개는 것을 반복하는 과정을 이터레이션Iteration이라고 부른다.

 

에폭Epoch이란?

보통의 머신러닝은 똑같은 데이터셋을 반복 학습한다. 예를 들어 100번 반복 학습을 한다고 하면, 100 epochs를 반복한다고 말한다. 배치를 몇 개로 나눴냐에 상관없이 전체 데이터셋은 한 번 돌 때 한 epoch가 끝난다.

 

활성화 함수Activation function란?

MLP의 연결 구조는 전기 신호의 크기가 특정 임계치를 넘어야 다음 뉴런으로 신호가 전달되는 뉴런의 신호전달 체계를 흉내내는 함수로 되어있다. 이때 신호를 받기 위해 활성화되는 다음 뉴런의 행동을 따라하기 위해 도입된 것이 활성화 함수이다.

활성화 함수는 비선형 함수여야 한다. 비선형 함수의 대표적인 예는 시그모이드 함수이다.

그 외에도 이와 같은 다양한 활성화 함수가 있는데, 이 중에서도 보편적으로 많이 사용되는 함수는 ReLU이다.

다른 활성화 함수에 비해 학습이 빠르고, 연산 비용이 적고, 구현이 간단하기 때문이다.

>> 실무에서는 여러 활성화 함수를 교체해 가며 최종적으로 정확도를 높이는 작업을 동반한다. 이러한 과정을 모델 튜닝이라고 부른다.

 

과적합Overfitting, 과소적합Underfitting이란?

딥러닝 모델을 설계하고 튜닝하며 학습시키다 보면 가끔씩 Training loss는 점점 작아지는데 Validation loss가 높아지는 현상이 나타나게 된다.

이러한 현상을 과적합 현상이라고 한다. 풀어야 하는 문제의 난이도에 비해 모델의 복잡도Complexity가 클 경우 가장 많이 발생하는 현상이다.

반대로 풀어야 하는 문제의 난이도에 비해 모델의 복잡도가 낮으면 문제를 제대로 풀지 못하게 되는데, 이러한 현상을 과소적합이라고 한다.

 

데이터 증강 기법Data augmentation이란?

과적합을 해결할 수 있는 가장 좋은 방법은 데이터의 개수를 늘리는 방법이다.

부족한 데이터를 보충하기 위해 데이터 증강 기법이라는 꼼수 아닌 꼼수를 사용할 때가 많다. 이미지 처리 분야의 딥러닝에서 주로 사용되는 기법으로, 아래와 같은 방식으로 데이터의 개수를 늘리는 것을 의미한다.

 

드랍아웃Dropout이란?

과적합을 해결할 수 있는 간단한 방법으로 드랍아웃이 있다. 단어 의미 그대로 노드들이 이어진 선을 빼 없애버리는 것이다.

노드가 지나칠 정도로 많을 때, 일부만 사용해도 충분히 결과를 낼 수 있다 판단한다면 적당한 수의 노드만 선출하는 것이 훨씬 더 균형 잡힌 결과를 낼 수 있다.

앙상블Ensemble이란?

여러 개의 딥러닝 모델을 만들어 각각 학습시킨 뒤, 각각의 모델에서 나온 출력을 기반으로 다수결 투표를 하거나, 평균값을 구하는 방법도 있고, 마지막에 결정하는 레이어를 사용하는 등 다양한 방법으로 응용할 수 있다.

 

Learning rate decay란?

Local minimum(1주차)에 빠르게 도달하기 위해 사용하는 방식이다.

첫 부분에서는 큰 폭으로 learning rate를 설정하고, 뒤로 갈수록 그 값을 줄여서 조금씩 움직여 효율적으로 Local minimum을 찾아갈 수 있게 하는 것이다.

그림에서는 왼쪽에 있는 것이 Learning rate decay이고, 오른쪽에 있는 것이 속도 변화 없이 일정한 속도로 움직이는 모양새를 담고 있다. 왼쪽은 세 번 만에 Local minimum에 다다랐고, 오른쪽은 네 번 만에 Local minimum에 다다를 수 있었다.

 

 

 

*실습*

 

손글씨 숫자 데이터셋을 가지고 와 이미지 학습을 시키는 실습

(https://colab.research.google.com/drive/1ReNtn2jtYsf81iFpNGO3ti2pnYn3-6UH?usp=sharing)

 

import os
os.environ['KAGGLE_USERNAME'] = 'username' # username
os.environ['KAGGLE_KEY'] = 'key' # key

!kaggle datasets download -d oddrationale/mnist-in-csv
!unzip mnist-in-csv.zip

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.optimizers import Adam, SGD
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import OneHotEncoder

train_df = pd.read_csv('mnist_train.csv')
test_df = pd.read_csv('mnist_test.csv')

plt.figure(figsize=(16, 10)) #그래프 만들기
sns.countplot(train_df['label']) #라벨을 x축으로 한 카운트 그래프
plt.show() #화면에 보여주기

 

위와 같은 출력 정보를 확인할 수 있다.

 

train_df = train_df.astype(np.float32)
x_train = train_df.drop(columns=['label'], axis=1).values #.values = np array로 바꾸는 과정
y_train = train_df[['label']].values

test_df = test_df.astype(np.float32)
x_test = test_df.drop(columns=['label'], axis=1).values
y_test = test_df[['label']].values

print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)

 

 

위와 같은 형태의 데이터들이라는 것을 확인할 수 있다.

 

index = 1
plt.title(str(y_train[index]))
plt.imshow(x_train[index].reshape((28, 28)), cmap='gray') #이미지 데이터를 픽셀 단위로 reshape
plt.show() #첫 번째 이미지 데이터를 확인하는 방식

encoder = OneHotEncoder()
y_train = encoder.fit_transform(y_train).toarray()
y_test = encoder.fit_transform(y_test).toarray()
# 이 과정을 거치면 ex. 4 > 0,0,0,0,1 의 형태로 바뀌게 된다 (원핫인코딩)

print(y_train.shape)
# 이 코드를 이용해 모양을 확인해야 출력층의 크기를 정할 수 있다.

 

위의 코드를 실행하면 이 두 가지 결과를 확인할 수 있는데, 이때 그림은 데이터를 확인하면서 출력된 것이고, 우리가 중요하게 봐야 하는 것은 밑에 있는 값이다. 콤마(,) 뒤에 따라오는 숫자를 기억하고 있어야 한다.

 

x_train = x_train / 255. #픽셀이기 때문에 255로 나눠서 0~1사이의 값으로 일반화한다
x_test = x_test / 255.

input = Input(shape=(784,))
hidden = Dense(1024, activation='relu')(input)
hidden = Dense(512, activation='relu')(hidden)
hidden = Dense(256, activation='relu')(hidden)
output = Dense(10, activation='softmax')(hidden)
#은닉층을 추가해 네트워크를 구성해준다.
#이때 출력층output에 들어가는 숫자는 이전에 확인했던 크기와 맞춰줘야 한다.

model = Model(inputs=input, outputs=output)

model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.001), metrics=['acc'])
#모델을 맞추고

model.summary()
#네트워크를 표로 보여준다.

 

 

history = model.fit(
    x_train,
    y_train,
    validation_data=(x_test, y_test), # 검증 데이터를 넣어주면 한 epoch이 끝날때마다 자동으로 검증
    epochs=20
  )
  #반복 학습을 시켜준다.
  #개인적으로 실습을 하게 된다면 코랩에서 런타임 > 런타임 유형 변경에서 꼭 GPU를 체크해야한다.
  #그렇지 않으면 이 과정에서 엄청난 시간을 빼앗기게 된다.
plt.figure(figsize=(16, 10))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])

plt.figure(figsize=(16, 10))
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])

 

위의 코드를 입력하면 loss와 검증 loss, 정확도acc와 검증 acc의 그래프를 각각 확인할 수 있다.