‘Deep Learning with Python’ 세미나 4, 3장. Getting started with neural networks (1/2)

이번 장은 실습해야 할 것들이 있기 때문에 한 번의 세미나로 다루면 집중도가 떨어질 수 있습니다. 3.5절 부터는 다음 세미나에서 진행하도록 하겠습니다.

 

각 절의 책 내용을 먼저 읽고, 아래 정리된 내용을 다시 한 번 읽습니다.

3.1 Anatomy of a neural network

그림 3.1을 보고 신경망을 구성하는 요소들과 각각의 역할에 대해서 다시 한 번 정리해 보세요.

3.1.1 Layers: the building blocks of deep learning

The fundamental data structure in neural networks is the layer.

대부분의 딥러닝 책들은 신경망의 기본 데이터 구조로 노드를 강조합니다. 저자는 노드에 대한 언급 없이 층(layer)을 기본 데이터 구조로 언급합니다. 딥러닝에서의 신경망의 핵심을 매우 잘 짚은 것입니다. 층이 기본 데이터 구조이기 때문에 노드는 층의 unit 개수를 설정하는 속성으로 다뤄집니다.

Some layers are stateless, but more frequently layers have a state: the layer’s weights, one or several tensors learned with stochastic gradient descent, which together contain the network’s knowledge.

딥러닝은 가중치 값으로 프로그래밍을 작성하는 것이라고 할 수 있습니다.

Different layers are appropriate for different tensor formats and different types of data processing.

층들은 서로 다른 텐서 포맷(2D 텐서, 3D 텐서 등)과 데이터 처리 방식(densely connected layer, recurrent layer, convolution layer 등)을 가지고, 신경망의 목표를 달성합니다.

Building deep-learning models in Keras is done by clipping together compatible layers to form useful data-transformation pipelines.
The notion of layer compatibility here refers specifically to the fact that every layer will only accept input tensors of a certain shape and will return output tensors of a certain shape.

케라스에서 딥러닝 모델은 층들을 쌓아서(연결해서) 만듭니다. 층은 입력과 출력을 갖는데, 연결되는 층들을 서로 입력과 출력 텐서 shape을 맞혀줘야 합니다.

3.1.2 Models: networks of layers

A deep-learning model is a directed, acyclic graph of layers. The most common instance is a linear stack of layers, mapping a single input to a single output.

딥러닝 모델은 층들을 연결하는 것으로 방향성이 있는(순서가 있는) 순환하지 않는 그래프로 작성됩니다. 가장일반적인 예는 단일 입력을 단일 출력에 매핑하는 층들을 순차적으로 쌓는 것입니다.

자주 등장하는 네트워크 구조는 그런 것들이 있다는 정도만 알고 넘어갑니다.

The topology of a network defines a hypothesis space. we defined machine learning as “searching for useful representations of some input data, within a predefined space of possibilities, using guidance from a feedback signal.”

어떤 특성을 갖는 층을 어떤 방식으로 연결할 것인지를 결정한다는 것(모델을 만드는 것)은 “가설 공간(hypothesis space)”을 정하는 것입니다.

가설을 잘 못 세운 실험이 제대로 되지 않는 것처럼, 가설 공간을 잘 못 정의한 모델로는 원하는 결과를 낼 수 없습니다. 가설 공간을 잘 정하는 것이 중요합니다. 사실 가설 공간을 정하는 것은 논문을 작성하는 연구자들의 몫인 경우가 대부분입니다. 일반적인 적용 시에는 이미 잘 정의된 가설 공간(모델)을 선택해서 잘 활용하면 됩니다.

3.1.3 Loss functions and optimizers: keys to configuring the learning process
  • Loss function (objective function)—The quantity that will be minimized during training. It represents a measure of success for the task at hand.
  • Optimizer—Determines how the network will be updated based on the loss function. It implements a specific variant of stochastic gradient descent (SGD).

기존 소프트웨어 개발방식에 비유하면, 손실 함수를 테스트로, 손실 점수를 테스트 결과로, 최적화 함수를 프로그래밍 작성으로 생각할 수 있습니다.

소프트웨어 개발자가 어떤 알고리즘을 선택하느냐에 따라 프로그래밍 성능이 달라지듯, 어떤 손실 함수와 어떤 최적화 함수를 선택하느냐는 아주 중요합니다. 적용하는 입장에서 다행인 것은 이들 또한 연구자들의 몫이고, 이미 잘 정의된 방법이 있다는 것입니다.

3.2 Introduction to Keras

케라스는 딥러닝 엔진이 아닙니다. 다양한 딥러닝 엔진들 위에 구축된 모델 수준에서 딥러닝을 할 수 있도록 하는 라이브러리 입니다. 배우기 쉽고 적용하기 쉽습니다. 딥러닝 입문이나 쉽고 빠른 적용을 원할 때 적당합니다.

직접적인 텐서 조작과 같은 좀 더 세부적인 제어가 필요한 경우에는 텐서플로와 같은 딥러닝 엔진을 직접 사용하고, 딥러닝에 대한 깊은 이해를 원한다면 파이썬으로 직접 딥러닝 엔진을 구현해 보기를 추천합니다.

3.2.1 Keras, TensorFlow, Theano, and CNTK

텐서플로, 씨아노, CNTK는 케라스가 지원하는 대표적인 딥러닝 엔진입니다.

3.2.2 Developing with Keras: a quick overview

The typical Keras workflow looks just like that example:

다음 절차 정도는 기억해 둡니다.

  1. Define your training data: input tensors and target tensors.
  2. Define a network of layers (or model ) that maps your inputs to your targets.
    • models.Sequential 클래스를 사용해서 모델 구조 생성, 함수형 API를 사용하는 방법도 있음
    • layers에 정의된 클래스들을 사용해서 레이어 생성하고, 위에서 생성한 모델의 add 함수로 추가
    • 레이어 생성 시에 unit 개수와 활성화 함수를 정의, 첫 번째 레이어인 경우 입력
  3. Configure the learning process by choosing a loss function, an optimizer, and some metrics to monitor.
    • model.compile 메소드 사용
  4. Iterate on your training data by calling the fit() method of your model.
    • model.fit 메소드 사용

There are two ways to define a model: using the Sequential class (only for linear stacks of layers, which is the most common network architecture by far) or the functional API (for directed acyclic graphs of layers, which lets you build completely arbitrary architectures).

모델을 정의하는 두 가지 방법을 기억해 둡니다.

3.3 Setting up a deep-learning workstation

부록을 참조해서 개발환경을 설정합니다.

 

3.4 Classifying movie reviews: a binary classification example

데이터를 훈련셋과 테스트셋으로 구분합니다. 그 이유는 이전 장에서도 설명했지만 오버피팅을 막기 위해서 입니다. 아래 설명을 주의 깊게 읽어 보세요.

Because you should never test a machinelearning model on the same data that you used to train it! Just because a model performs well on its training data doesn’t mean it will perform well on data it has never seen; and what you care about is your model’s performance on new data (because you already know the labels of your training data—obviously you don’t need your model to predict those). For instance, it’s possible that your model could end up merely memorizing a mapping between your training samples and their targets, which would be useless for the task of predicting targets for data the model has never seen before.

3.4.1 The IMDB dataset

IMDB를 여러분이 직접 수집해서 가공(전처리)해서 딥러닝 모델을 구성해서 훈련시켜 사용해야 한다고 해 봅시다.

영화평만을 가지고 영화평이 긍정인지 부정인지 맞출 수 있는 것이 목표입니다. 먼저 영화평 50000개를 수집합니다. 긍정 부정을 각각 25000개씩 수집합니다. 어떻게 이 데이터를 가공해줘야 딥러닝에서 쉽게 사용할 수 있을까요?

하나의 영화평은 여러 단어들로 구성됩니다. 딥러닝 연산은 수에 대한 것으로 영화평에 사용되는 단어를 수치화해야 합니다. 좋은 방법은 단어사전을 만들고 영화평의 단어를 단어사전의 인덱스로 변환하는 것입니다. 영화평은 일련의 인덱스 리스트로 변환됩니다. 긍정인지 부정인지도 긍정은 1, 부정은 0으로 수치화할 수 있습니다.

데이터셋을 구성할 때 훈련셋과 테스트셋을 구분해서 제공해주면 사용하기 좋습니다. 긍정 부정을 각각 절반씩 포함하는 훈련셋 25000개, 테스트셋 25000개로 나누어서 제공하도록 합니다.

조금만 곰곰히 생각해보면 단어 인덱스가 중요한게 아니라 어떤 단어가 사용되었는지가 중요함을 알 수 있습니다. 단어 인덱스를 직접 다루게 되면 단어사전 뒷 부분에 있는 단어의 인덱스가 커지기 때문에 뒷 부분에 있는 단어들의 중요도가 더 커지게 됩니다. 

어떤 단어가 사용되었느냐에 초점을 맞추면, 어떤 단어가 사용된 경우는 1로 그렇지 않은 경우는 0으로 작성할 수 있습니다. 이렇게 하는 방법을 원핫인코딩이라고 합니다. 자주 사용하지 않는 단어들을 무시하고 10000개의 단어만 다룬다고 하면, 모든 요소가 0인 shape이 (10000,)인 벡터를 생성하고 영화평 인덱스 리스트의 단어 인덱스에 해당하는 벡터의 위치에 1을 작성해 주면 됩니다.

이제 딥러닝 모델의 입력 텐서를 작성하겠습니다. 샘플 수가 25000개이니 shape이 (25000, 10000)인 2차원 텐서(행렬)을 만들고, 위의 내용을 적용하면 됩니다. 코드 3.2의 vectorize_sequences 함수는 이것을 일반화 한 것입니다.

넘파이의 강력함은 ‘results[i, sequence] = 1.’ 코드에서 볼 수 있습니다. sequence 리스트를 배열의 인덱스로 사용할 수 있다는 점입니다. sequence로 작성된 단어 인덱스에 따라 그 위치를 한 번에 결정해서 1 값을 설정할 수 있습니다.

레이블도 넘파이의 asarray 함수를 사용해서 1차원 텐서(벡터)로 변환합니다.

3.4.3 Building your network

대부분의 경우 딥러닝 모델을 작성하기 위해서는 가장 먼저 입력층과 출력층을 결정해야 합니다. 입력층과 출력층은 입력과 출력이 결정되어 있기 때문에 다른 결정들에 비해 조금은 수월합니다. 케라스에서는 별도의 입력층이나 출력층을 두지 않습니다. 입력층은 입력 데이터의 형태를 그대로 반영한 것이기 때문에 은닉층에서 이 정보를 함께 작성해서 처리합니다. 출력층은 특별히 설정이 필요 없는 출력층에 연결된 결과에 해당합니다.

케라스에서 입력층은 층을 생성할 때 input_shape으로 설정됩니다. input_shape은 하나의 데이터의 형태를 나타내는 것으로 IMDB에서는 (10000 ,)이 됩니다.

출력에 대해서도 조금 깊이 생각해보면, 긍정인지 부정인지 100% 확정적으로 맞추지는 못할 것임을 알 수 있습니다. ‘긍정일 확률이 높다’로 표현될 것으로, 출력층은 확률적 결과를 출력해야 할 것입니다. 확률을 표현하는 하나의 값이 될 것이기 때문에 units은 한 개만 있으면 되고, 확률로 표현할 수 있는 함수가 있어야 합니다. 이렇게 확률적인 결과를 내야할 때 사용하는 함수 중 하나가 sigmoid입니다.

model.add(layers.Dense(1, activation=’sigmoid’))

각각의 층은 다음 층으로 데이터를 전달해야 합니다. 각각의 층이 다음 층에 데이터를 전달할 때는 표현력을 좀 더 풍부하게 하기 위해 데이터를 변환합니다. 층의 출력을 위한 데이터 변환 함수를 활성화 함수라고 생각하시면 됩니다. 대부분의 활성화 함수는 비선형 함수입니다. 레이어의 가중치 연산은 텐서의 덧셈과 곱셈과 텐서곱으로 선형성을 갖습니다. 여기에 비선형성을 부여해서 표현을 좀 더 풍부하게 하는 역할을 활성화 함수가 하는 것입니다. IMDB 예제에서는 첫 번째 층의 활성화 함수로 relu를 사용합니다.

model.add(layers.Dense(16, activation=’relu’, input_shape=(10000,)))

층의 중요한 속성으로 노드의 개수를 나타내는 units가 있습니다. 신경망의 기본은 연결되는 층에 속하는 노드들을 완전 연결하는 것입니다. 케라스에서 layers.Dense 클래스는 완전 연결되는 층을 나타냅니다. 3개의 노드를 갖는 층이 4개의 노드를 갖는 층과 연결된다면 3 x 4의 연결이 만들어 집니다. 케라스에서는 층의 units 속성으로 노드 개수를 설정합니다.  IMDB 예제에서는 첫 번째 층의 units로 16을 설정하고 있습니다.

model.add(layers.Dense(16, activation=’relu’, input_shape=(10000,)))

이 연결에 설정되는 값이 가중치입니다. 노드의 개수는 가중치 개수를 결정하는 중요한 요소입니다. 가중치로 프로그래밍을 하니 가중치가 많다는 것은 그 만큼 풍부한 표현이 가능하다는 것입니다. 물론 가중치가 많다고 무조건 좋은 것은 아닙니다. 문제에 맞는 수준의 가중치 개수가 있는 것이 중요합니다. 가중치가 너무 많은 경우 훈련 시 불필요한 풍부한 표현을 학습하게 됨으로 실제에는 맞지 않는 경우가 생기기 때문입니다.

딥러닝 모델을 작성할 때 두 번째 해야 할 결정은 중간층을 몇 개 둘 것인지, 이들 중간층은 어떤 특성을 가져야 하는지를 정하는 것입니다. IMDB 예제에서는 완전연결이 가능한 16개 units를 갖고 relu로 활성화 함수를 사용하는 하나의 중간층을 두고 있습니다.

model.add(layers.Dense(16, activation=’relu’))

코드 3.3을 보면서 위의 내용들이 어떻게 코딩되는지 살펴봅니다. 위에서 설명한 내용들을 기억하면서 직접 코딩을 해 봅니다.

다음 해야 할 일은 손실 함수와 최적화 함수를 결정하는 일 입니다. 이것 또한 어려운 일은 아닙니다. 대부분 어떤 문제에 어떤 것을 사용해야 하는지가 패턴화 되어 있기 때문입니다. 물론 패턴화 되지 않은 새로운 문제를 만난다면 어려운 일이 되겠지요.

이진 문제이고 분류 문제인 경우에는 손실 함수로 binary_crossentropy를 선택합니다. IMDB 예에서는 최적화 함수로 rmsprop을 사용하고 있습니다. 최적화 함수와 rmsprop에 대해 좀 더 자세한 내용을 알고자 하는 분들은 참고자료 [1]을 읽어보시길 권합니다.

다음으로 결정해야 할 것은 측정 지표를 결정하는 것입니다. IMDB에서는 정확도를 측정 지표로 선택하고 있습니다.

케라스에서 손실 함수와 최적화 함수와 측정 지표는 모델 클래스의 compile 메소드를 사용해서 설정합니다.

model.compile(optimizer=’rmsprop’, loss=’binary_crossentropy’, metrics=[‘accuracy’])

3.4.4 Validating your approach

훈련을 제대로 하기 위해서는 검증셋이 있어야 합니다. 검증셋을 테스트셋 하고는 다릅니다. 검증셋은 훈련에 사용하는 거고, 테스트셋은 훈련이 끝나고 훈련이 제대로 된 지를 평가하기 위해 사용하는 것입니다.

IMDB 예에서는 10000개의 검증 셋을 훈련 셋에서 분리했습니다.

이제 훈련을 어떻게 할 것인지를 정해야 합니다. 한 번에 몇 개씩 묶어서 훈련을 할 것인지(batch_size), 전체 훈련 셋에 대해 몇 번의 반복을 할 것인지(epochs)를 정해야 합니다. batch_size와 epochs를 정하는 것은 훈련 과정을 모니터링 하면서 조정할 수 있습니다. batch_size는 일반적으로 128, 256, 512와 같이 2n의 크기로 결정합니다. IMDB 예에서는 batch_size로 512를, epochs를 20으로 설정하고 있습니다.

케라스에서 훈련과 관련된 설정은 모델의 fit 메소드를 사용합니다. fit 메소드는 History 객체를 반환합니다. 이 객체는 훈련 동안 발생한 모든 정보를 딕션너리인 history 속성 값으로 갖고 있습니다.

history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512, validation_data=(x_val, y_val))

코드 3.9와 코드 3.10을 작성해 봅니다. 그림 3.7과 3.8에서 볼 수 있듯이 훈련셋은 손실 점수가 줄면서 정확도가 높아지고 있지만 검증셋은 몇 번의 반복 후 부터는 손실 점수가 커지고 정확도는 떨어짐을 볼 수 있습니다. 손실 점수가 커지는 부분 부터는 반복을 더 할 수록 계속 과적합 해지고 있기 때문에 더 이상 훈련할 필요가 없습니다.

훈련이 끝났으면 테스트셋으로 평가해봅니다. 케라스는 모델 클래스의 evaluate 메소드를 사용해 평가합니다.

results = model.evaluate(x_test, y_test)

테스트 결과가 만족스러우면 이제 실전에서 사용해 봅시다. 케라스는 모델 클래스의 predict 메소드를 사용합니다. 아래 코드는 x_test를 사용하고 있지만, 실전에서는 실시간으로 수집되는 것에 대해서 예측하고자 할 것이다.

model.predict(x_test)

About the Author
(주)뉴테크프라임 대표 김현남입니다. 저에 대해 좀 더 알기를 원하시는 분은 아래 링크를 참조하세요. http://www.umlcert.com/kimhn/

Leave a Reply

*