Amazon Web Services 한국 블로그

Gluon 상세 소개 – 유연한 딥러닝을 위한 사용하기 쉬운 프로그램 인터페이스

AWS와 Microsoft는 선택한 딥 러닝 프레임워크에 상관 없이 모든 개발자를 지원할 수 있도록 기계 학습 기술의 속도, 유연성 및 액세스 가능성 개선에 초점을 둔 Gluon이라는 새로운 표준 인터페이스를 발표했습니다. 이 협업을 통해 거둔 첫 번째 성과는 Apache MXNet의 오픈 소스 라이브러리인 새로운 Gluon 인터페이스로, 초보 개발자라도 딥 러닝 모델의 프로토타입을 제작하고, 딥 러닝 모델을 빌드하고 학습시킬 수 있습니다. 이 인터페이스를 사용하면 학습에 많은 시간을 들이지 않고도 딥 러닝 모델을 매우 간편하게 제작할 수 있습니다.

다음은 4가지 주요 장점과 이 장점을 잘 보여 주는 코드 샘플입니다.

1. 이해하기 쉬운 단순한 코드

Gluon에서 간결하고 명확한 코드를 사용하여 신경망을 정의할 수 있습니다. 미리 정의된 레이어, 최적화 도구, 이니셜라이저를 포함한 플러그 앤 플레이 신경망의 구성 요소 전체 세트를 가져올 수 있습니다. 이에 따라 복잡한 구현 세부 내역이 추상화됩니다. 다음 예제에서는 단 몇 줄의 코드로 단순한 신경망의 정의 방법을 보여줍니다.

# 첫 단계는 모델 초기화
net = gluon.nn.Sequential()
# 그런 다음 모델 아키텍처 정의
with net.name_scope():
    net.add(gluon.nn.Dense(128, activation="relu")) # 첫 번째 계층 - 노드 128개
    net.add(gluon.nn.Dense(64, activation="relu")) # 두 번째 계층 – 노드 64개
    net.add(gluon.nn.Dense(num_outputs)) # 출력 계층

다음 다이어그램은 신경망 구조를 보여 줍니다.

자세한 내용은 이 자습서를 참조하여 Gluon 신경망 구성 요소로 Multilayer Perceptron(MLP)이라고 하는 간단한 신경망을 빌드하는 방법을 알아보십시오. 고급 사용 사례에서도 처음부터 신경망의 일부를 쉽게 작성할 수 있습니다. Gluon을 통해 신경망에서 미리 정의된 사용자 지정 구성 요소를 혼합하여 적용할 수 있습니다.

2. 유연한 구조

신경망 모델 학습은 컴퓨팅 집약적이고, 경우에 따라서는 수일 혹은 수주까지도 소요될 수 있습니다. 많은 딥 러닝 프레임워크는 이 모델을 명확하게 정의하고 학습 알고리즘과 분리하여 이 시간을 단축해 줍니다. 이 엄격한 접근 방식은 상당한 복잡성을 수반하여 디버깅을 어렵게 만듭니다.

하지만 Gluon 접근 방식은 다릅니다. 학습 알고리즘과 신경망 모델을 통합하여 성능을 저하시키지 않으면서 개발 프로세스에 유연성을 더해 줍니다. 이 접근 방식의 중심에는 Gluon trainer 메서드가 있고, 모델을 학습시킬 때 이 메서드를 사용합니다. 이 trainer 메서드는 MXNet autograd 라이브러리에 따라 달라지고, 미분값(기울기, gradient)을 자동으로 계산하는 데 사용됩니다. 미분값(derivate)은 변수의 변경률을 측정하는 수학 계산입니다. 미분값은 학습 알고리즘에 필요한 입력입니다. 이 autograd 라이브러리는 이 수학 계산을 효율적으로 구현하면서도 Gluon이 부여해 주는 유연성을 지원하는 데 필수적입니다. 이제 단순한 중첩 for 루프로 구성된 학습 알고리즘을 정의하려면 autogradtrainer를 통합하면 됩니다.

epochs = 10

for e in range(epochs):
    for i, batch in enumerate(train_data):
        data = batch.data[0]
        label = batch.label[0]
        with autograd.record(): # 파생물 기록 시작
            output = net(data) # 순방향 반복
            loss = softmax_cross_entropy(output, label)
            loss.backward()
        trainer.step(data.shape[0])

이처럼 유연한 구조 덕분에 코드가 직관적이고 디버깅이 용이하며, 더 수준 높은 모델을 개발할 수 있습니다. 친숙한 기본 Python 언어 구문( for 루프 또는 if 문 등)을 신경망 내에서나 알고리즘 일부로 사용할 수 있습니다. 모델과 알고리즘을 통합하여 모델의 모든 코드를 실행하므로, 버그를 유발하는 특정 코드 줄을 보다 쉽게 확인할 수 있습니다.

3. 동적 그래프

특정 시나리오에서 신경망 모델은 학습 과정 중에 그 모양과 크기를 변경해야 하는 경우도 있습니다. 특히 신경망으로 공급되는 데이터 입력이 가변적일 때 그러한 변경이 필수적이며, 이는 입력된 각 문장의 길이가 각기 다른 자연 언어 처리(NLP)에서는 일반적입니다. Gluon을 사용하면 신경망 정의가 동적이므로, 원하는 어느 구조든 실행 중에 Python의 기본 제어 흐름을 사용하여 빌드할 수 있습니다.

예를 들어, 이와 같은 동적 신경망 구조는 2015년 NLP의 주요 성과인 Kai Sheng Tai, Richard Socher, Chris Manning의 트리 구조화된 Long Short-Term Memory(LSTM) 모델을 빌드하기가 더 쉽습니다.  트리 LSTM은 한 쌍의 문장이 그 의미가 동일한지 여부를 식별하는 등의 작업에 사용되는 매우 효과적인 모델입니다. 기본적으로 두 문장의 의미가 동일한 다음 예를 들겠습니다.

  • Michael threw the football in front of the player. – “Michael이 던진 이 공은 목표에 도달하지 못했습니다.”
  • The ball was thrown short of the target by Michael. – “Michael이 던진 공은 목표에 닿지 못했습니다.”

문장을 알려진 시퀀스 학습 모델인 RNN에 입력으로 집어 넣고 분류할 수도 있습니다. 하지만 사전 지식이 있는 언어에서 종종 문제가 생긴다는 것이 트리 LSTM의 기본적인 아이디어입니다. 예를 들어 문장에 문법 구조가 나와 있고, 문장에서 이 구조를 추출할 수 있는 강력한 도구가 있다고 합시다. 단어들을 연결하여 아래 그림과 같이 문장의 문법 트리 구조를 모방한 트리 구조 신경망을 만들 수 있습니다.

(Stanford 자연 언어 처리(NLP) 그룹)

이렇게 하려면 각 예제마다 다른 신경망 구조를 실행 중에 빌드해야 합니다. 기존의 프레임워크로는 어렵지만, Gluon은 문제 없이 이런 작업을 처리할 수 있습니다. 아래 코드에서 모델 학습을 반복하는 루프를 만들고 autogradtrainer 이점을 역시 활용하는 법을 알 수 있습니다. 이와 같이, 모델에서 문장의 트리 구조를 차례로 순회하면서 학습할 수 있습니다.

def forward(self, F, inputs, tree):
	children_outputs = [self.forward(F, inputs, child)
    for child in tree.children]
  #각 입력 문장의 구문을 기반으로 신경망 빌드
  #모델 정의와 학습 과정 중의 구조
  …

4.  높은 성능 지원

Gluon이 지원하는 유연성 덕분에 신경망 모델로 프로토타입을 쉽게 제작하고 실험을 진행할 수 있습니다. 그런 다음, 속도가 유연성보다 더 중요하게 되면(모든 학습 데이터를 피드할 준비가 된 경우 등) Gluon 인터페이스를 통해 신경망 모델을 쉽게 캐시에 저장하여 고성능을 확보하고 메모리 공간을 절약할 수 있습니다. 이때 프로토타입 제작을 완료하고 대규모 데이터세트에서 이 프로토타입을 테스트할 준비가 된 후 신경망을 설정하고 나면 약간의 조정만이 필요합니다. 신경망 계층을 쌓아 올리는 데 Sequential (앞의 그림 참조)을 사용하지 말고 HybridSequential을 사용해야 합니다. 그 기능은 Sequential과 동일하지만 최적화된 기본 엔진 호출을 지원하므로 모델 아키텍처의 일부 또는 전체를 나타낼 수 있습니다.

net = nn.HybridSequential()
with net.name_scope():
    net.add(nn.Dense(128, activation="relu")) # 첫 번째 계층 - 노드 128개
    net.add(nn.Dense(64, activation="relu")) # 두 번째 계층 – 노드 64개
    net.add(nn.Dense(10)) # 출력 계층

그 다음, HybridSequential를 컴파일하고 최적화하기 위해 hybridize 메서드를 호출할 수 있습니다.

net.hybridize()

이제 모델을 학습시킬 때에도 기본 MXNet 인터페이스를 사용할 때와 거의 동일한 고성능을 활용하고 메모리 사용량을 줄일 수 있습니다.

5. Gluon 시작하기

Gluon을 사용하려면 MXNet 설치 과정 가이드에 나오는 간단한 단계에 따라 실행하거나 클라우드에서 Deep Learning Amazon 머신 이미지(AMI)를 실행하면 됩니다. 그런 다음, 앞서 다룬 여러 구성 요소를 사용하는 방법을 단계별로 실행하여 Multilayer Perceptron(MLP)이라고 하는 간단한 2계층 신경망을 빌드하고 학습합니다. Python 버전 3.3 이상에서 Jupyter 노트북을 사용하여 이 예제를 실행하는 것이 좋습니다.

먼저, MXNet를 가져오고 gluon 라이브러리뿐 아니라 다른 필수 라이브러리 autogradndarray를 붙잡습니다.

import mxnet as mx
from mxnet import gluon, autograd, ndarray

그런 다음 데이터를 가져오고 이 데이터에서 어느 정도 전처리를 실행합니다. 많이 사용되는 MNIST 데이터세트를 가져오며, 여기에는 손으로 쓴 숫자가 나오는 대규모 이미지 모음과 이 이미지에 적합한 레이블이 포함되어 있습니다. 물론, 사진을 어레이에 맞게 재구성하여 쉽게 처리하고 어레이를 MXNet 기본 NDArray 객체로 변환할 수도 있습니다.

# 기본 MXNet utils 함수를 사용하여 MNIST 가져오기
data = mx.test_utils.get_mnist()

# 학습 데이터 설정 및 사진 재구성
train_data = data['train_data'].reshape((-1, 784))
train_label = data['train_label']

# 테스트 데이터 설정 및 사진 재구성
test_data = data['test_data'].reshape((-1, 784))
test_label = data['test_label']

# 데이터를 NDArrays로 변환
train_data_mx = mx.nd.array(train_data)
train_label_mx = mx.nd.array(train_label)
test_data_mx = mx.nd.array(test_data)
test_label_mx = mx.nd.array(test_label)

그 다음, 반복자를 만들어 학습 데이터를 보유합니다. 반복자는 큰 데이터 세트를 순회할 때 유용한 객체 클래스입니다. 이 작업을 진행하기 전에 먼저 배치 크기를 설정하여 각 학습 알고리즘을 반복하는 동안 신경망이 처리할 데이터 분량을 정의합니다. 이 경우에는 32입니다.

batch_size = 32
train_data = mx.io.NDArrayIter(train_data_mx, train_label_mx, batch_size,  
                                 shuffle=True)           

이제 바로 실제 신경망을 정의할 수 있습니다. 계층 2개를 만들려고 합니다. 첫 번째 계층에는 노드 128개가 있고, 두 번째 계층에는 노드 64개가 있습니다. 이 두 계층 모두 Rectified Linear Unit(ReLU)이라는 활성화 함수를 포함합니다. 활성화 함수는 모델 입력과 출력 간의 비선형 관계를 표현하기 위해 중요합니다. 또한, 노드 개수가 가능한 총 출력 개수와 일치하도록 출력 계층을 설정해야 합니다. MNIST의 경우에 사진은 10개(즉, 0~9)뿐인 숫자를 나타내기 때문에 가능한 출력이 10개뿐입니다.

# 첫 단계는 모델 초기화
net = gluon.nn.Sequential()
# 그런 다음 모델 아키텍처 정의
with net.name_scope():
    net.add(gluon.nn.Dense(128, activation="relu")) # 첫 번째 계층 - 노드 128개
    net.add(gluon.nn.Dense(64, activation="relu")) # 두 번째 계층 – 노드 64개
    net.add(gluon.nn.Dense(10)) # 출력 계층

모델 학습 절차를 시작하기 전에 모델의 파라미터를 초기화하고 손실 및 모델 옵티마이저 함수를 설정해야 합니다.

# 표준편차가 0.05인 정규 분포에서 모델의 파라미터 전체에 대해 
# 임의 값으로 시작
net.collect_params().initialize(mx.init.Normal(sigma=0.05))

# softmax cross entropy loss 함수를 사용하여 # 모델이 정답을 얼마나 잘 예측할 수 있는지 평가하도록 선택
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()

# SGD(Stochastic Gradient Descent) 학습 알고리즘을 사용하고 
# 학습 속도 하이퍼파라미터를 .1로 설정하도록 선택
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': .1})

이제 모델 학습 알고리즘을 정의할 시간입니다. 반복할 때마다 다음 4단계를 거칩니다. (1) 데이터 한 배치를 입력 받고 (2) 신경망 모델의 출력과 실제 값의 차이를 계산하고 (3) autograd를 사용하여 손실 함수로부터 모델 파라미터의 미분값을 계산하고 (4) trainer 메서드를 사용하여 손실 함수가 감소하는 방향으로 파라미터를 최적화합니다. 전체 Epoch 개수를 10으로 설정했습니다. 즉, 전체 학습 데이터 세트를 10회 반복합니다.

epochs = 10

for e in range(epochs):
    for i, batch in enumerate(train_data):
        data = batch.data[0]
        label = batch.label[0]
        with autograd.record(): # 파생물 기록 시작
            output = net(data) # 순방향 반복
            loss = softmax_cross_entropy(output, label)
            loss.backward()
        trainer.step(data.shape[0])

이제 학습된 신경망 모델이 준비되었으므로, 확보한 테스트 데이터를 사용하여 이 모델의 정확도를 확인해 보겠습니다. 예측한 값과 실제 값을 비교하여 정확도를 계산할 수 있습니다.

acc = mx.metric.Accuracy()# 정확성 지표 초기화
output = net(test_data_mx) # 신경망을 통해 테스트 데이터 실행
predictions = ndarray.argmax(output, axis=1) # 테스트 데이터 예측
acc.update(preds=predictions, labels=test_label_mx) # 정확성 계산
print(acc)

Gluon 인터페이스와 딥 러닝에 대해 자세히 알아 보려면, 종합 자습서 모음을 참조하면 됩니다. 이 자습서에서는 딥 러닝 소개부터 첨단 신경망 모델의 구현 방법에 이르기까지 자세한 내용을 다룹니다.

작성자 소개
Vikram Madan은 AWS Deep Learning 담당 선임 제품 관리자입니다. 특히 오픈 소스 Apache MXNet 엔진에 초점을 두고 딥 러닝 엔진을 보다 쉽게 사용하도록 해주는 제품 연구에 주력하고 있습니다. 여가 시간에는 장거리 달리기와 다큐멘터리 시청을 즐기곤 합니다.

이 글은 Introducing Gluon — An Easy-to-Use Programming Interface for Flexible Deep Learning의 한국어 번역으로, 강지양 AWS 솔루션즈 아키텍트께서 감수하였습니다.