# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: conda_pytorch_p36
#     language: python
#     name: conda_pytorch_p36
# ---

# # Distributed data parallel BERT model training with PyTorch and SMDataParallel
#
# SMDataParallel is a new capability in Amazon SageMaker to train deep learning models faster and cheaper. SMDataParallel is a distributed data parallel training framework for PyTorch, TensorFlow, and MXNet.
#
# This notebook example shows how to use SMDataParallel with PyTorch(version 1.6.0) on [Amazon SageMaker](https://aws.amazon.com/sagemaker/) to train a BERT model using [Amazon FSx for Lustre file-system](https://aws.amazon.com/fsx/lustre/) as data source.
#
#
# The outline of steps is as follows:
#
# 1. Stage dataset in [Amazon S3](https://aws.amazon.com/s3/). Original dataset for BERT pretraining consists of text passages from BooksCorpus (800M words) (Zhu et al. 2015) and English Wikipedia (2,500M words). Please follow original guidelines by NVidia to prepare training data in hdf5 format - 
# https://github.com/NVIDIA/DeepLearningExamples/blob/master/PyTorch/LanguageModeling/BERT/README.md#getting-the-data
# 2. Create Amazon FSx Lustre file-system and import data into the file-system from S3
# 3. Build Docker training image and push it to [Amazon ECR](https://aws.amazon.com/ecr/)
# 4. Configure data input channels for SageMaker
# 5. Configure hyper-prarameters
# 6. Define training metrics
# 7. Define training job, set distribution strategy to SMDataParallel and start training
#
# **NOTE:**  With large traning dataset, we recommend using (Amazon FSx)[https://aws.amazon.com/fsx/] as the input filesystem for the SageMaker training job. FSx file input to SageMaker significantly cuts down training start up time on SageMaker because it avoids downloading the training data each time you start the training job (as done with S3 input for SageMaker training job) and provides good data read throughput.
#
#
# **NOTE:** This example requires SageMaker Python SDK v2.X.
#

# ## Amazon SageMaker Initialization
#
# Initialize the notebook instance. Get the aws region, sagemaker execution role.
#
# The IAM role arn used to give training and hosting access to your data. See the [Amazon SageMaker Roles](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-roles.html) for how to create these. Note, if more than one role is required for notebook instances, training, and/or hosting, please replace the sagemaker.get_execution_role() with the appropriate full IAM role arn string(s). As described above, since we will be using FSx, please make sure to attach `FSx Access` permission to this IAM role.

# +
# %%time
# ! python3 -m pip install --upgrade sagemaker
import sagemaker
from sagemaker import get_execution_role
from sagemaker.estimator import Estimator
import boto3

sagemaker_session = sagemaker.Session()
bucket = sagemaker_session.default_bucket()

role = get_execution_role() # provide a pre-existing role ARN as an alternative to creating a new role
print(f'SageMaker Execution Role:{role}')

client = boto3.client('sts')
account = client.get_caller_identity()['Account']
print(f'AWS account:{account}')

session = boto3.session.Session()
region = session.region_name
print(f'AWS region:{region}')
# -

# ## Prepare SageMaker Training Images
#
# 1. SageMaker by default use the latest [Amazon Deep Learning Container Images (DLC)](https://github.com/aws/deep-learning-containers/blob/master/available_images.md) PyTorch training image. In this step, we use it as a base image and install additional dependencies required for training BERT model.
# 2. In the Github repository https://github.com/HerringForks/DeepLearningExamples.git we have made PyTorch-SMDataParallel BERT training script available for your use. This repository will be cloned in the training image for running the model training.
#
# ### Build and Push Docker Image to ECR
#
# Run the below command build the docker image and push it to ECR.

image = "<ADD NAME OF REPO>"  # Example: bert-smdataparallel-sagemaker
tag = "<ADD TAG FOR IMAGE>"   # Example: pt1.6 

# !pygmentize ./Dockerfile

# !pygmentize ./build_and_push.sh

# %%time
# ! chmod +x build_and_push.sh; bash build_and_push.sh {region} {image} {tag}

# ### Training script
#
# In the Github repository https://github.com/HerringForks/DeepLearningExamples.git we have made PyTorch-SMDataParallel BERT training script available for your use. Clone the repository.

# !rm -rf DeepLearningExamples
# !git clone https://github.com/HerringForks/DeepLearningExamples

# ## Configure hyperparameters for your training

# !pygmentize train.sh

# ## Preparing FSx Input for SageMaker
#
# 1. Download and prepare your training dataset on S3.
# 2. Follow the steps listed here to create a FSx linked with your S3 bucket with training data - https://docs.aws.amazon.com/fsx/latest/LustreGuide/create-fs-linked-data-repo.html. Make sure to add an endpoint to your VPC allowing S3 access.
# 3. Follow the steps listed here to configure your SageMaker training job to use FSx https://aws.amazon.com/blogs/machine-learning/speed-up-training-on-amazon-sagemaker-using-amazon-efs-or-amazon-fsx-for-lustre-file-systems/
#
#
# ### Important Caveats
#
# 1. You need use the same `subnet` and `vpc` and `security group` used with FSx when launching the SageMaker notebook instance. The same configurations will be used by your SageMaker training job.
# 2. Make sure you set appropriate inbound/output rules in the `security group`. Specically, opening up these ports is necessary for SageMaker to access the FSx filesystem in the training job. https://docs.aws.amazon.com/fsx/latest/LustreGuide/limit-access-security-groups.html
# 3. Make sure `SageMaker IAM Role` used to launch this SageMaker training job has access to `AmazonFSx`.

# +
from sagemaker.inputs import FileSystemInput

subnets=['<SUBNET_ID>'] # Should be same as Subnet used for FSx. Example: subnet-01aXXXX
security_group_ids=['<SECURITY_GROUP_ID>'] # Should be same as Security group used for FSx. sg-075ZZZZZZ
file_system_id= '<FSX_ID>' # FSx file system ID with your training dataset. Example: 'fs-0bYYYYYY'

file_system_directory_path= 'YOUR_MOUNT_PATH_FOR_TRAINING_DATA' # NOTE: '/fsx/' will be the root mount path. Example: '/fsx/bert/pt/phase1'
file_system_access_mode =    "ro"

file_system_type = 'FSxLustre'

train_fs = FileSystemInput(file_system_id=file_system_id,
                                    file_system_type=file_system_type,
                                    directory_path=file_system_directory_path,
                                    file_system_access_mode=file_system_access_mode)

# -

# ## SageMaker PyTorch Estimator function options
#
# In the following code block, you can update the estimator function to use a different instance type, instance count, and distrubtion strategy. You're also passing in the training script you reviewed in the previous cell.
#
# **Instance types**
#
# SMDataParallel supports model training on SageMaker with the following instance types only:
# 1. ml.p3.16xlarge
# 1. ml.p3dn.24xlarge [Recommended]
# 1. ml.p4d.24xlarge [Recommended]
#
# **Instance count**
#
# To get the best performance and the most out of SMDataParallel, you should use at least 2 instances, but you can also use 1 for testing this example.
#
# **Distribution strategy**
#
# Note that to use DDP mode, you update the the `distribution` strategy, and set it to use `smdistributed dataparallel`. 

# +
from sagemaker.pytorch import PyTorch

docker_image = f"{account}.dkr.ecr.{region}.amazonaws.com/{image}:{tag}" # YOUR_ECR_IMAGE_BUILT_WITH_ABOVE_DOCKER_FILE
instance_type = "ml.p3dn.24xlarge" # Other supported instance type: ml.p3.16xlarge, ml.p4d.24xlarge
instance_count = 2 # You can use 2, 4, 8 etc.
# This job name is used as prefix to the sagemaker training job. Makes it easy for your look for your training job in SageMaker Training job console.
job_name = 'pt-bert-smdataparallel-N%d-%s' % (instance_count, instance_type.split(".")[1]) 
print("Job name: ", job_name)

estimator = PyTorch(base_job_name=job_name,
                    source_dir=".",
                    entry_point="train.sh",
                    role=role,
                    image_uri=docker_image,
                    framework_version='1.6.0',
                    py_version='py3',
                    instance_count=instance_count,
                    instance_type=instance_type,
                    sagemaker_session=sagemaker_session,
                    subnets=subnets,
                    security_group_ids=security_group_ids,
                    debugger_hook_config=False,
                    # Training using SMDataParallel Distributed Training Framework
                    distribution={'smdistributed':{
                                        'dataparallel':{
                                                'enabled': True
                                            }
                                        }
                                 }
                   )

# +
# !pygmentize train.sh

estimator.fit(train_fs)
# -

model_data = estimator.model_data
print("Storing {} as model_data".format(model_data))
# %store model_data

StarcoderdataJupyterScriptsDedupFiltered