<jupyter_start><jupyter_text>How to identify low GPU utilization due to small batch size***Note: 본 노트북 코드는 [영문 노트북](https://github.com/aws/amazon-sagemaker-examples/blob/master/sagemaker-debugger/tensorflow_profiling/low_batch_size.ipynb)을 한국어화(중간중간 역주 추가와 documentation 보완)하면서 보완하면서, Debugger 예제 코드를 추가하였습니다.***이 노트북에서는 Amazon SageMaker Debugger의 프로파일링 기능을 사용하여 미니배치의 작은 크기로 인한 GPU 리소스의 활용도가 낮은 경우를 식별하는 방법을 보여줍니다. ResNet50 모델 및 CIFAR-10 데이터셋에서 TensorFlow를 사용하여 이를 시연합니다.이 예제의 훈련 스크립트는  [demo/train_tf_bottleneck.py](./demo/train_tf_bottleneck.py) 입니다.  Install SageMaker and smdebug2020년 12월에 출시된 새로운 디버거 프로파일링 기능을 사용하려면, 최신 버전의 SageMaker 및 SMDebug SDK가 설치되어 있는지 확인하세요. 다음 코드 셀을 사용하여 라이브러리를 업데이트하고 Jupyter 커널을 다시 시작하여 업데이트를 적용합니다.<jupyter_code>import sys
import IPython
import boto3    
import sagemaker    
install_needed = False  # should only be True once
if install_needed:
    print("installing deps and restarting kernel")
    !{sys.executable} -m pip install -U sagemaker smdebug
    IPython.Application.instance().kernel.do_shutdown(True)

bucket = sagemaker.Session().default_bucket()<jupyter_output><empty_output><jupyter_text>1. Prepare training dataset--- Tensorflow Datasets package우선 노트북 커널을 Tensorflow 2.x로 설정합니다.이 실험에는 CIFAR-10 데이터셋을 사용합니다. CIFAR-10 데이터셋을 다운로드하고 TFRecord 형식으로 변환하려면 `demo/generate_cifar10_tfrecords`를 실행하고 tfrecord 파일을 S3 버킷에 업로드하세요.<jupyter_code>!python demo/generate_cifar10_tfrecords.py --data-dir=./data
s3_bucket = sagemaker.Session().default_bucket()

dataset_prefix = "data/cifar10-tfrecords"
desired_s3_uri = f"s3://{s3_bucket}/{dataset_prefix}"

dataset_location = sagemaker.s3.S3Uploader.upload(local_path="data", desired_s3_uri=desired_s3_uri)
print(f"Dataset uploaded to {dataset_location}")<jupyter_output><empty_output><jupyter_text>2. Create a Training Job with Profiling Enabled---표준 [SageMaker Estimator API for Tensorflow](https://sagemaker.readthedocs.io/en/stable/frameworks/tensorflow/sagemaker.tensorflow.htmltensorflow-estimator)를 사용하여 훈련 작업을 생성합니다. 프로파일링을 사용하려면 `ProfilerConfig` 객체를 만들고 `TensorFlow` estimator `profiler_config` 파라메터에 전달합니다. 본 예제는 프로파일링 간격을 500밀리초(0.5초)로 설정했습니다.  Set a profiler configuration<jupyter_code>from sagemaker.debugger import ProfilerConfig, FrameworkProfile

profiler_config = ProfilerConfig(
    system_monitor_interval_millis=500,
    framework_profile_params=FrameworkProfile(
        local_path="/opt/ml/output/profiler/", start_step=5, num_steps=2
    ),
)<jupyter_output><empty_output><jupyter_text>Define hyperparameters훈련 스크립트 [train_tf_bottleneck.py](./demo/train_tf_bottleneck) 여러 파라메터들을 허용합니다. 여기서는 테스트 목적으로 훈련 시간을 단축하기 위해 `batch_size`를 64로 설정하고 Epoch을 3으로 설정합니다.<jupyter_code>batch_size = 64

hyperparameters = {
    "epoch": 2,
    "batch_size": batch_size,
}<jupyter_output><empty_output><jupyter_text>Get the image URI이 노트북을 실행하는 리전에 따라 도커 이미지가 달라집니다.<jupyter_code>session = boto3.session.Session()
region = session.region_name

image_uri = f"763104351884.dkr.ecr.{region}.amazonaws.com/tensorflow-training:2.4.1-gpu-py37-cu110-ubuntu18.04"<jupyter_output><empty_output><jupyter_text>Define SageMaker Tensorflow Estimator프로파일링을 활성화하려면 디버거 프로파일링 구성 (`profiler_config`), 디버거 규칙 목록 (`rules`) 및 이미지 URI (`image_uri)`를 estimator에 전달해야 합니다. 디버거는 SageMaker estimator가 훈련 작업을 요청하는 동안 모니터링 및 프로파일링을 활성화합니다.<jupyter_code>from sagemaker.tensorflow import TensorFlow

job_name = f"lowbatchsize-{batch_size}"
instance_count = 1
instance_type = "ml.p2.xlarge"
entry_script = "train_tf_bottleneck.py"

estimator = TensorFlow(
    role=sagemaker.get_execution_role(),
    image_uri=image_uri,
    base_job_name=job_name,
    instance_type=instance_type,
    instance_count=instance_count,
    entry_point=entry_script,
    source_dir="demo",
    profiler_config=profiler_config,
    script_mode=True,
    hyperparameters=hyperparameters,
    input_mode="Pipe",
)<jupyter_output><empty_output><jupyter_text>Start training job`wait=False` argument를 포함한 `estimator.fit()`은 백그라운드에서 훈련 작업을 시작합니다. 대시보드 또는 분석 노트북 실행을 계속할 수 있습니다.<jupyter_code>remote_inputs = {"train": dataset_location + "/train"}

estimator.fit(remote_inputs, wait=False)
training_job_name = estimator.latest_training_job.name
print("Training Job Name:  {}".format(training_job_name))<jupyter_output><empty_output><jupyter_text>AWS 콘솔 화면에서 `Training jobs`를 확인해 보세요. 아래 코드 셀에서 자동으로 생성되는 링크를 클릭하셔도 됩니다.<jupyter_code>from IPython.core.display import display, HTML

display(
    HTML(
        '<b>Review <a target="blank" href="https://console.aws.amazon.com/sagemaker/home?region={}#/jobs/{}">Training Job</a> After About 5 Minutes</b>'.format(
            region, training_job_name
        )
    )
)

display(
    HTML(
        '<b>Review <a target="blank" href="https://console.aws.amazon.com/cloudwatch/home?region={}#logStream:group=/aws/sagemaker/TrainingJobs;prefix={};streamFilter=typeLogStreamPrefix">CloudWatch Logs</a> After About 5 Minutes</b>'.format(
            region, training_job_name
        )
    )
)

display(
    HTML(
        '<b>Review <a target="blank" href="https://s3.console.aws.amazon.com/s3/buckets/{}/{}/?region={}&tab=overview">S3 Output Data</a> After The Training Job Has Completed</b>'.format(
            bucket, training_job_name, region
        )
    )
)<jupyter_output><empty_output><jupyter_text>3. [SageMaker Studio Only] Monitor the system resource utilization using SageMaker Studio---SageMaker Studio는 시스템 및 프레임워크 성능 메트릭의 분석 리포트와 plot을 찾을 수 있는 Sagemaker Debugger용 시각화 도구를 제공합니다.SageMaker Studio에서 이 정보에 액세스하려면, 왼쪽의 마지막 아이콘을 클릭하여 `SageMaker Components and registries` 를 열고 `Experiments and trials` 을 선택합니다. 훈련 작업 목록이 표시됩니다. 해당 job을 마우스 오른쪽 버튼으로 클릭하면 팝업 메뉴가 표시되고, `Open Debugger for insights` 를 클릭하면 아래와 같이 SageMaker 디버거에 대한 새 탭이 열립니다.![Debugger-in-Studio](./images/Debugger-in-Studio.png)`Overview`와 `Nodes`의 두 가지 탭이 있습니다. `Overview`는 빠른 검토를 위한 프로파일링 요약을 제공하고, `Nodes`는 모든 노드에 대한 자세한 utilization 정보를 제공합니다.`Nodes`에서 확인할 수 있는 GPU 및 시스템 utilization 히스토리는 GPU의 사용률이 낮음을 나타냅니다. 아래 그림의 경우, GPU 사용률은 60%이고 GPU 메모리 사용률은 20%였습니다.![low-GPU-utilization](./images/low-GPU-utilization.png)이 경우에 취해야 할 첫번째 조치는 배치 크기를 늘려 더 많은 샘플들을 GPU에 푸시하는 것입니다. 이 예시에서는 하이퍼파라미터 값을 변경하여 배치 크기를 늘리고 훈련 작업을 다시 실행할 수 있습니다. 예를 들어`batch_size`를 64에서 1024로 변경합니다.```pythonhyperparameters = {'epoch': 20,                    'batch_size': 1024                  }```배치 크기가 1024인 시스템 리소스 사용률은 다음 그림과 같이 GPU 리소스를 완전히 사용함을 보여줍니다.![low-GPU-utilization-fixed](./images/low-GPU-utilization-fixed.png)  4. Analyze Profiling Data---<jupyter_code>training_job_name = estimator.latest_training_job.name
print(f"Training jobname: {training_job_name}")
print(f"Region: {region}")
from smdebug.profiler.analysis.notebook_utils.training_job import TrainingJob

tj = TrainingJob(training_job_name, region)
tj.wait_for_sys_profiling_data_to_be_available()<jupyter_output><empty_output><jupyter_text>`ProfilerReport()` 규칙은 기본 규칙 요약 및 다음 단계 권장 사항이 포함된 html 리포트 `profiler-report.html`을 생성합니다. 이 리포트는 S3 버킷에서 찾을 수 있습니다.<jupyter_code>rule_output_path = estimator.output_path + estimator.latest_training_job.job_name + "/rule-output"
print(f"You will find the profiler report in {rule_output_path}")<jupyter_output><empty_output><jupyter_text>아래 코드 셀을 실행시키면 프로파일링 리포트를 로컬 환경으로 다운로드하고 리포트를 다이렉트로 확인할 수 있는 링크를 생성합니다. 링크를 새 탭이나 새 창으로 연 다음 프로파일링 보고서를 확인하세요디버거 프로파일링 보고서를 다운로드하고 여는 방법에 대한 자세한 내용은 SageMaker 개발자 가이드의 [SageMaker Debugger Profiling Report](https://docs.aws.amazon.com/sagemaker/latest/dg/debugger-profiling-report.html)를 참조하세요.**[주의] 만약 JupyterLab에서 실행하는 경우 화면 좌측 상단의 Trust HTML을 클릭해야 html 리포트가 정상적으로 출력됩니다!**<jupyter_code>def get_profile_report_folder(bucket, prefix, maxkeys=1):
    s3 = boto3.client("s3")
    results = s3.list_objects_v2(Bucket=bucket, Prefix=prefix, MaxKeys=maxkeys)
    filename = results['Contents'][0]['Key']
    profile_report_folder = [s for s in filename.split('/') if "ProfilerReport" in s][0]
    return profile_report_folder
output_dir = './output'
profile_output = output_dir+'/ProfilerReport'
profile_report_folder = get_profile_report_folder(s3_bucket, estimator.latest_training_job.job_name + "/rule-output")

!rm -rf $output_dir

import json, os
from IPython.core.display import display, HTML

if not os.path.exists(output_dir):
    os.makedirs(output_dir)
    
if not os.path.exists(profile_output):
    os.makedirs(profile_output)    
    
!aws s3 ls {rule_output_path}/{profile_report_folder}/profiler-output/
!aws s3 cp {rule_output_path}/{profile_report_folder}/profiler-output/ {output_dir}/ProfilerReport/ --recursive    

display(HTML('<b>ProfilerReport : <a href="{}profiler-report.html">Profiler Report</a></b>'.format(output_dir+"/ProfilerReport/")))
training_job_name = estimator.latest_training_job.job_name
region = sagemaker.Session().boto_region_name

tj = TrainingJob(estimator.latest_training_job.job_name, sagemaker.Session().boto_region_name)

# Retrieve a description of the training job description and the S3 bucket URI where the metric data are saved
tj.describe_training_job()
tj.get_config_and_profiler_s3_output_path()<jupyter_output><empty_output><jupyter_text>Plotting MetricsSageMaker Debugger Profiler는 시스템 지표와 프레임워크 지표를 쉽게 확인할 수 있는 5가지 클래스들을 제공하고 있습니다. - MetricsHistogram- StepTimelineChart- StepHistogram- TimelineCharts- Heatmap `select_dimensions`와 `select_events` argument는 optional 파라메터로, 이를 지정하여 필요한 지표만 시각화할 수 있습니다. 예를 들어, `select_dimensions = ["GPU"]`를 지정하면 plot 메서드는 "GPU" 키워드를 포함하는 지표를 필터링합니다. 유사하게 `select_events = ["total"]`을 지정하면 "total" 이벤트 태그를 포함하는 지표를 필터링합니다.자세한 내용은 https://docs.aws.amazon.com/sagemaker/latest/dg/debugger-access-data-profiling-default-plot.html 를 참조하세요.<jupyter_code># Wait for the data to be available
tj.wait_for_sys_profiling_data_to_be_available()
tj.wait_for_framework_profiling_data_to_be_available()

# Get the metrics reader
system_metrics_reader = tj.get_systems_metrics_reader()
framework_metrics_reader = tj.get_framework_metrics_reader()

# Refresh the event file list
system_metrics_reader.refresh_event_file_list()
framework_metrics_reader.refresh_event_file_list()
from smdebug.profiler.analysis.notebook_utils.metrics_histogram import MetricsHistogram
from smdebug.profiler.analysis.notebook_utils.step_timeline_chart import StepTimelineChart
from smdebug.profiler.analysis.notebook_utils.step_histogram import StepHistogram
from smdebug.profiler.analysis.notebook_utils.timeline_charts import TimelineCharts
from smdebug.profiler.analysis.notebook_utils.heatmap import Heatmap
from smdebug.profiler.analysis.notebook_utils.training_job import TrainingJob<jupyter_output><empty_output><jupyter_text>System metrics histogram<jupyter_code>metrics_histogram = MetricsHistogram(system_metrics_reader)
metrics_histogram.plot(
    starttime=0, 
    endtime=system_metrics_reader.get_timestamp_of_latest_available_file(), 
    select_dimensions=["CPU", "GPU", "I/O"],
    select_events=["total"]
)<jupyter_output><empty_output><jupyter_text>System and framework metrics heatmap<jupyter_code>view_heatmap = Heatmap(
    system_metrics_reader,
    framework_metrics_reader,
    select_dimensions=["CPU", "GPU", "I/O"],
    select_events=["total"],
    plot_height=200
)<jupyter_output><empty_output><jupyter_text>Time line charts<jupyter_code>from smdebug.profiler.analysis.notebook_utils.timeline_charts import TimelineCharts

view_timeline_charts = TimelineCharts(
    system_metrics_reader, 
    framework_metrics_reader,
    select_dimensions=["CPU", "GPU", "I/O"], # optional
    select_events=["total"]                  # optional 
)<jupyter_output><empty_output><jupyter_text>Step histogram<jupyter_code>from smdebug.profiler.analysis.notebook_utils.step_histogram import StepHistogram

step_histogram = StepHistogram(framework_metrics_reader)
step_histogram.plot(
    starttime=step_histogram.last_timestamp - 5 * 1000 * 1000, 
    endtime=step_histogram.last_timestamp, 
    show_workers=True
)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09944425732569889, 'fraction_numerical': 0.011030650050522062, 'mean_word_length': 5.759817871371657, 'pattern_counts': {'":': 3, '<': 81, '<?xml version=': 0, '>': 81, 'https://': 7, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '634346', 'license': 'MIT', 'n_tokens_mistral': 5369, 'n_tokens_neox': 6066, 'n_words': 923, 'path': '1.profiling/low_batch_size.ipynb', 'repo_name': 'daekeun-ml/sagemaker-distributed-training-tf2', 'size': '11876'}