Amazon Web Services 한국 블로그

Amazon SageMaker Debugger – 기계 학습 모델 학습 과정 디버깅 기능 출시 (서울 리전 포함)

Amazon SageMaker Debugger는 기계 학습(ML) 훈련 작업 중 발생하는 복잡한 문제를 자동으로 식별해주는 기능입니다.

ML 모델을 구축하고 학습하려면 과학과 기술(요술이라고 말하는 사람도 있음)이 모두 필요합니다. 데이터 세트를 수집하고 준비하는 것부터 다양한 알고리즘을 실험하여 최적의 학습 파라미터(공포의 하이퍼파라미터)를 찾는 것까지, ML 실무자가 고성능 모델을 제공하기까지 넘어야 할 허들은 꽤 많습니다. 그래서 AWS는 모듈식의 완전관리형 서비스인 Amazon SageMaker를 만들었습니다. 이 서비스는 ML 워크플로를 간소화하고 가속화합니다.

ML만큼 머피의 법칙이 잘 들어맞는 것도 없습니다. 잘못될 가능성이 있는 모든 것이 자주 잘못되니까요. 특히, 학습 프로세스에서 발생하는 불분명한 다수의 문제로 인해 모델이 데이터 세트에 있는 패턴을 올바르게 추출하고 학습하는 데 차질이 생깁니다. ML 라이브러러의 소프트웨어 버그를 말하는 게 아닙니다. 물론, 버그도 발생하기는 합니다. 그러나 대부분의 학습 작업이 실패하는 이유는 부적절한 파라미터 초기화, 결함이 있는 하이퍼파라미터의 조합, 자체 코드의 설계 문제 등에 있습니다.

게다가 이러한 문제가 즉시 발견되는 경우는 거의 없습니다. 차차 발전하면서 느리지만 확실한 방법으로 학습 프로세스를 망치고 결국에는 정확도가 낮은 모델을 만들어 냅니다. 현실적으로, 진짜 고수가 온다고 해도 문제를 식별하고 추적하기가 극히 어렵고 많은 시간이 소모됩니다. Amazon SageMaker Debugger는 이러한 이유로 만들어졌습니다.

좀 더 자세히 알아봅시다.

Amazon SageMaker Debugger소개
TensorFlow, Keras, Apache MXNet, PyTorch 및 XGBoost의 기존 학습 코드에서 새로운 SageMaker Debugger SDK를 사용하여 내부 모델 상태를 주기적인 간격으로 저장할 수 있습니다.

개별 모델 상태는 예상하신 대로 Amazon Simple Storage Service(S3)에 저장됩니다. 이 상태는 다음으로 구성됩니다.

  • 모델이 학습하는 파라미터(예: 신경망에 대한 가중치 및 편차)
  • 최적화 프로그램을 통해 이러한 파라미터에 적용되는 변경(기울기)
  • 최적화 파라미터 자체
  • 스칼라 값(예: 정확도 및 손실)
  • 각 계층의 출력
  • 기타 등등…

특정 값 세트(예: 시간대별로 특정 신경망 계층을 통해 흐르는 기울기의 시퀀스)는 개별적으로 저장되며 이를 텐서라고 합니다. 텐서는 컬렉션(가중치, 기울기 등)으로 구성되는데 학습 중에 저장할 항목을 결정할 수 있습니다. 그런 다음 SageMaker SDK와 예측기를 사용하여 SageMaker Debugger에서 적용할 규칙을 정의하는 추가 파라미터를 전달하면서 학습 작업을 구성합니다.

각 규칙은 학습 중인 모델의 텐서를 분석하여 바람직하지 않은 조건을 찾는 Python 코드 조각입니다. 텐서 급증/소멸(파라미터가 NaN 또는 0 값에 도달), 기울기 급증/소멸, 변경 없는 손실 등의 일반적인 문제에 대해 미리 정의된 규칙을 사용할 수 있습니다. 물론, 직접 규칙을 쓰셔도 됩니다.

Amazon SageMaker 예측기가 구성되면 학습 작업을 시작할 수 있습니다. 즉시, 구성한 각 규칙에서 디버깅 작업이 발효되고 사용 가능한 텐서를 검사하기 시작합니다. 디버깅 작업에서 문제가 발견되면 작업이 중지되고 추가 정보가 기록됩니다. 추가 자동화 단계를 트리거하려는 경우 CloudWatch Events 이벤트도 전송됩니다.

이제, 기울기 소멸로 인해 딥 러닝 작업에서 문제가 발생합니다. 잠깐의 숙고와 경험을 바탕으로 문제 영역을 찾게 됩니다. 신경망이 너무 깊은 것이 문제일까? 학습 속도가 너무 느린가? 내부 상태가 S3에 저장되었으므로 이제 SageMaker Debugger SDK를 사용하여 텐서의 시간대별 변화를 탐색하고 가설을 확인한 다음 근본 원인을 해결할 수 있습니다.

SageMaker Debugger의 작동 방식을 빠른 데모로 살펴봅시다.

Amazon SageMaker Debugger 기반 모델 디버깅
SageMaker Debugger의 핵심은 학습 중에 텐서를 캡처하는 기능입니다. 이 기능을 사용하려면 학습 코드를 약간 조정하여 저장할 텐서 컬렉션을 결정하고 저장 빈도를 선택하고 값 자체를 저장할지 감소 값(중간 값, 평균 등)을 저장할지 여부를 선택해야 합니다.

SageMaker Debugger SDK는 지원하는 각 프레임워크를 위한 단순한 API를 제공합니다. 이 API를 사용하여 이 작업을 수행할 수 있습니다. 2차원 선형 회귀 모델을 찾는 단순한 TensorFlow 스크립트를 사용하여 작동 방식을 알아봅시다. 물론, 이 Github 리포지토리에서 더 많은 예제를 찾을 수 있습니다.

초기 코드를 살펴봅시다.

import argparse
import numpy as np
import tensorflow as tf
import random

parser = argparse.ArgumentParser()
parser.add_argument('--model_dir', type=str, help="S3 path for the model")
parser.add_argument('--lr', type=float, help="Learning Rate", default=0.001)
parser.add_argument('--steps', type=int, help="Number of steps to run", default=100)
parser.add_argument('--scale', type=float, help="Scaling factor for inputs", default=1.0)

args = parser.parse_args()

with tf.name_scope('initialize'):
    # 2-dimensional input sample
    x = tf.placeholder(shape=(None, 2), dtype=tf.float32)
    # Initial weights: [10, 10]
    w = tf.Variable(initial_value=[[10.], [10.]], name='weight1')
    # True weights, i.e. the ones we're trying to learn
    w0 = [[1], [1.]]
with tf.name_scope('multiply'):
    # Compute true label
    y = tf.matmul(x, w0)
    # Compute "predicted" label
    y_hat = tf.matmul(x, w)
with tf.name_scope('loss'):
    # Compute loss
    loss = tf.reduce_mean((y_hat - y) ** 2, name="loss")

optimizer = tf.train.AdamOptimizer(args.lr)
optimizer_op = optimizer.minimize(loss)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(args.steps):
        x_ = np.random.random((10, 2)) * args.scale
        _loss, opt = sess.run([loss, optimizer_op], {x: x_})
        print (f'Step={i}, Loss={_loss}')

TensorFlow Estimator를 사용하여 이 스크립트를 학습합시다. SageMaker 로컬 모드를 사용하는데 이 모드는 실험적 코드를 빠르게 반복하기에 아주 좋습니다.

bad_hyperparameters = {'steps': 10, 'lr': 100, 'scale': 100000000000}

estimator = TensorFlow(
    role=sagemaker.get_execution_role(),
    base_job_name='debugger-simple-demo',
    train_instance_count=1,
    train_instance_type='local',
    entry_point='script-v1.py',
    framework_version='1.13.1',
    py_version='py3',
    script_mode=True,
    hyperparameters=bad_hyperparameters)

학습 로그를 보면 결과가 좋지 않습니다.

Step=0, Loss=7.883463958023267e+23
algo-1-hrvqg_1 | Step=1, Loss=9.502028841062608e+23
algo-1-hrvqg_1 | Step=2, Loss=nan
algo-1-hrvqg_1 | Step=3, Loss=nan
algo-1-hrvqg_1 | Step=4, Loss=nan
algo-1-hrvqg_1 | Step=5, Loss=nan
algo-1-hrvqg_1 | Step=6, Loss=nan
algo-1-hrvqg_1 | Step=7, Loss=nan
algo-1-hrvqg_1 | Step=8, Loss=nan
algo-1-hrvqg_1 | Step=9, Loss=nan

손실이 전혀 줄어들지 않습니다. 심지어 무한대로 올라갑니다. 텐서 문제가 급증하는 것 같은데 이 급증은 SageMaker Debugger에 기본적으로 정의된 규칙 중 하나입니다. 이제 작업을 시작합니다.

Amazon SageMaker Debugger SDK 사용
텐서를 캡처하려면 다음을 사용하여 학습 스크립트를 구성해야 합니다.

  • 텐서를 저장할 빈도를 지정하는 SaveConfig 객체
  • TensorFlow 세션에 연결되어 학습 중에 모든 것을 결합하고 필요한 텐서만 저장하는 SessionHook 객체
  • (선택 사항) 전체 텐서 대신 저장할 텐서 감소를 나열하는 ReductionConfig 객체
  • (선택 사항) 기울기를 캡처하는 최적화 프로그램 래퍼

업데이트된 코드는 다음과 같습니다. SageMaker Debugg er파라미터에 대한 명령줄 인수가 추가되었습니다.

import argparse
import numpy as np
import tensorflow as tf
import random
import smdebug.tensorflow as smd

parser = argparse.ArgumentParser()
parser.add_argument('--model_dir', type=str, help="S3 path for the model")
parser.add_argument('--lr', type=float, help="Learning Rate", default=0.001 )
parser.add_argument('--steps', type=int, help="Number of steps to run", default=100 )
parser.add_argument('--scale', type=float, help="Scaling factor for inputs", default=1.0 )
parser.add_argument('--debug_path', type=str, default='/opt/ml/output/tensors')
parser.add_argument('--debug_frequency', type=int, help="How often to save tensor data", default=10)
feature_parser = parser.add_mutually_exclusive_group(required=False)
feature_parser.add_argument('--reductions', dest='reductions', action='store_true', help="save reductions of tensors instead of saving full tensors")
feature_parser.add_argument('--no_reductions', dest='reductions', action='store_false', help="save full tensors")
args = parser.parse_args()
args = parser.parse_args()

reduc = smd.ReductionConfig(reductions=['mean'], abs_reductions=['max'], norms=['l1']) if args.reductions else None

hook = smd.SessionHook(out_dir=args.debug_path,
                       include_collections=['weights', 'gradients', 'losses'],
                       save_config=smd.SaveConfig(save_interval=args.debug_frequency),
                       reduction_config=reduc)

with tf.name_scope('initialize'):
    # 2-dimensional input sample
    x = tf.placeholder(shape=(None, 2), dtype=tf.float32)
    # Initial weights: [10, 10]
    w = tf.Variable(initial_value=[[10.], [10.]], name='weight1')
    # True weights, i.e. the ones we're trying to learn
    w0 = [[1], [1.]]
with tf.name_scope('multiply'):
    # Compute true label
    y = tf.matmul(x, w0)
    # Compute "predicted" label
    y_hat = tf.matmul(x, w)
with tf.name_scope('loss'):
    # Compute loss
    loss = tf.reduce_mean((y_hat - y) ** 2, name="loss")
    hook.add_to_collection('losses', loss)

optimizer = tf.train.AdamOptimizer(args.lr)
optimizer = hook.wrap_optimizer(optimizer)
optimizer_op = optimizer.minimize(loss)

hook.set_mode(smd.modes.TRAIN)

with tf.train.MonitoredSession(hooks=[hook]) as sess:
    for i in range(args.steps):
        x_ = np.random.random((10, 2)) * args.scale
        _loss, opt = sess.run([loss, optimizer_op], {x: x_})
        print (f'Step={i}, Loss={_loss}')

TensorFlow Estimator도 수정해야 합니다. SageMaker Debugger가 활성화된 학습 컨테이너를 사용하고 추가 파라미터를 전달하도록 수정합니다.

bad_hyperparameters = {'steps': 10, 'lr': 100, 'scale': 100000000000, 'debug_frequency': 1}

from sagemaker.debugger import Rule, rule_configs
estimator = TensorFlow(
    role=sagemaker.get_execution_role(),
    base_job_name='debugger-simple-demo',
    train_instance_count=1,
    train_instance_type='ml.c5.2xlarge',
    image_name=cpu_docker_image_name,
    entry_point='script-v2.py',
    framework_version='1.15',
    py_version='py3',
    script_mode=True,
    hyperparameters=bad_hyperparameters,
    rules = [Rule.sagemaker(rule_configs.exploding_tensor())]
)

estimator.fit()
2019-11-27 10:42:02 Starting - Starting the training job...
2019-11-27 10:42:25 Starting - Launching requested ML instances
********* Debugger Rule Status *********
*
* ExplodingTensor: InProgress 
*
****************************************

2가지 작업이 실행되고 있습니다. 실제 학습 작업과 Estimator에 정의된 규칙을 확인하는 디버깅 작업입니다. 순식간에 디버깅 작업이 실패합니다!

학습 작업을 기술하면 더 자세한 정보를 얻을 수 있습니다.

description = client.describe_training_job(TrainingJobName=job_name)
print(description['DebugRuleEvaluationStatuses'][0]['RuleConfigurationName'])
print(description['DebugRuleEvaluationStatuses'][0]['RuleEvaluationStatus'])

ExplodingTensor
IssuesFound

저장된 텐서를 살펴봅시다.

텐서 급증
학습 프로세스 중에 S3에 저장된 텐서를 간단히 가져올 수 있습니다.

s3_output_path = description["DebugConfig"]["DebugHookConfig"]["S3OutputPath"]
trial = create_trial(s3_output_path)

사용 가능한 텐서를 나열합니다.

trial.tensors()

['loss/loss:0',
'gradients/multiply/MatMul_1_grad/tuple/control_dependency_1:0',
'initialize/weight1:0']

모든 값이 numpy 어레이이므로 쉽게 반복할 수 있습니다.

tensor = 'gradients/multiply/MatMul_1_grad/tuple/control_dependency_1:0'
for s in list(trial.tensor(tensor).steps()):
    print("Value: ", trial.tensor(tensor).step(s).value)

Value:  [[1.1508383e+23] [1.0809098e+23]]
Value:  [[1.0278440e+23] [1.1347468e+23]]
Value:  [[nan] [nan]]
Value:  [[nan] [nan]]
Value:  [[nan] [nan]]
Value:  [[nan] [nan]]
Value:  [[nan] [nan]]
Value:  [[nan] [nan]]
Value:  [[nan] [nan]]
Value:  [[nan] [nan]]

텐서 이름에는 학습 코드에 정의된 TensorFlow 범위가 포함되므로 행렬 곱셈에 문제가 있다는 것을 쉽게 알 수 있습니다.

# Compute true label
y = tf.matmul(x, w0)
# Compute "predicted" label
y_hat = tf.matmul(x, w)

좀 더 자세히 보면 조정 파라미터에 의해 x 입력이 수정되어 예측기에서 100000000000으로 설정된 것을 알 수 있습니다. 학습 속도 또한 정상이 아닌 것으로 보입니다. 찾았습니다!

x_ = np.random.random((10, 2)) * args.scale

bad_hyperparameters = {'steps': 10, 'lr': 100, 'scale': 100000000000, 'debug_frequency': 1}

이미 아셨겠지만 이러한 하이퍼파라미터를 좀 더 합리적인 값으로 설정하면 학습 문제가 해결됩니다.

지금 이용 가능!
Amazon SageMaker Debugger는 학습 문제를 더 빠르게 찾고 해결하는 데 도움이 됩니다. 지금 바로 버그 사냥을 시작하십시오.

오늘부터 Amazon SageMaker가 제공되는 모든 상용 리전에서 SageMaker Debugger를 사용할 수 있습니다. 이 기능을 사용하고 Amazon SageMaker에 대한 AWS 포럼 또는 일반적인 AWS Support 연락처를 통해 피드백을 보내주시기 바랍니다.

– Julien