<jupyter_start><jupyter_text>Data PreparationData gathering, storing, denoising, alignment, curating and querying is THE most complicated aspect of predictive maintenance. Fault reports can for example be buried in pdf scans, fault types being misdiagnosed, fault only discovered weeks after they actually occured, or reported in a different timezone without the timezone information. These are a few of a thousands of pitfalls that mayawait you when trying to collate such real-world dataset at scale. In this scenario we are using a hypothetical dataset where the data is cleaned and aligned. BackgroundNASA’s Prognostic Center of Excellence established a repository with datasets to be used for benchmarking prognostics and predictive maintenance related algorithms. Among these datasets involves data from a turbofan engine simulation model C-MAPPS (or Commercial Modular Aero Propulsion System Simulation). The references section contains details about the over 100 publications using this dataset. C-MAPPS is a tool used to generate health, control and engine parameters from a simulated turbofan engine. A custom code wrapper was used to inject synthetic faults and continuous degradation trends into a time series of sensor data. Some high level characteristics of this dataset are as follows:The data obtained is from a high fidelity simulation of a turbofan engine, but closely models the sensor values of an actual engine. Synthetic noise was added to the dataset to replicate real-world scenarios. The effects of faults are masked due to operational conditions, which is a common trait of most real world systems.<jupyter_code>source_data_location = 's3://sagemaker-solutions-us-west-2/Predictive-maintenance-using-machine-learning/data'

#local data folder
data_folder = 'data'

!aws s3 cp --recursive $source_data_location $data_folder


with zipfile.ZipFile(os.path.join(data_folder, 'CMAPSSData.zip'), "r") as zip_ref:
    zip_ref.extractall(data_folder)<jupyter_output><empty_output><jupyter_text>Process  DataThere are 4 different training dataset, corresponding to 4 different engines, and we will train our models on each individually. But first we must preprocess the dataset. Since the example dataset we're using is small we can run the preprocessing code located in `preprocess.py` on this notebook instance. The preprocessing script computes the RUL (Remaining Useful Life) column for each engine data in the training dataset. We also apply min-max scaling to normalize our sensor readings to between 0 and 1 for each column. We read and preprocess the test data as well. It consists of sensor readings and the actual RUL. See the code in `preprocess.py` for more. To adapt this to your own dataset, you would need to implement your custom preprocessing for your dataset. We plot a few columns from the training data for visualization purposes.<jupyter_code>train_df, test_df, columns = preprocess.preprocess_data(data_folder)

o = train_df[0][columns[2:10]][train_df[0]['id'] == 3].plot(subplots=True, sharex=True, figsize=(20,10), title="Train: 8 sensors of Engine 1 before failure")<jupyter_output><empty_output><jupyter_text>SageMaker MXNet EstimatorNow we will go over the steps needed to define the MXNet model and train with SageMaker.  Upload processed data to S3 for trainingWe have to upload the processed data to a location in S3 so that the SageMaker training instance can access the data from that location. We will also, at the same time, upload the test data to the S3 bucket so that we can use that as an input to the trained model for scheduled inference.<jupyter_code>from sagemaker.s3 import S3Uploader

bucket = config.solution_bucket
prefix = config.s3_prefix

s3_data_prefix = "s3://{}/{}/{}".format(bucket, prefix, 'data')

# Make local folder for processed data
! mkdir -p processed-data

# Upload processed test data for inference
for i in range(len(test_df)):
    local_test_file = 'processed-data/test-{}.csv'.format(i)
    test_df[i].to_csv(local_test_file)
    S3Uploader.upload(local_test_file, s3_data_prefix)

# Upload processed data for training
for i in range(len(train_df)):
    local_train_file = 'processed-data/train-{}.csv'.format(i)
    train_df[i].to_csv(local_train_file)
    S3Uploader.upload(local_train_file, s3_data_prefix)


print('uploaded training data location: {}'.format(s3_data_prefix))<jupyter_output><empty_output><jupyter_text>Set model output location<jupyter_code>output_location = 's3://{}/{}/output'.format(bucket, prefix)
print('training artifacts will be uploaded to: {}'.format(output_location))<jupyter_output><empty_output><jupyter_text>MXNet Model Training scriptTraining MXNet models using MXNet Estimators is a two-step process. First, you prepare your training script, then second, you run this on SageMaker via an MXNet Estimator. The training script we have prepared for the model is located in the `sagemaker_predictive_maintenance_entry_point` folder. The training script contains functions to create the model for training and for inference. We also have functions to convert our dataframes into a Gluon Dataset so that it can be efficiently prefetched, transformed into numerical features used by the network and padded so that we can learn from multiple samples in batches.For more information on how to setup a training script for SageMaker using the MXNet estimator see: https://sagemaker.readthedocs.io/en/stable/using_mxnet.htmlpreparing-the-mxnet-training-script<jupyter_code>!pygmentize sagemaker_predictive_maintenance/sagemaker_predictive_maintenance_entry_point/sagemaker_predictive_maintenance_entry_point.py<jupyter_output><empty_output><jupyter_text>Train MXNet EstimatorNow, we can start the SageMaker training job by creating an MXNet estimator. We pass in the required arguments such as the `entry_point`, `role`, `train_instance_type`, and `train_instance_count` into the MXNet Estimator constructor.Then we start the training script by calling `fit` on the MXNet Estimator. `fit` takes both required and optional arguments. The required argument here is the S3 location of the training data passed in as a dictionary. We are also adding an optional argument for the job name. This is important because when the training job is complete and SageMaker needs to create a SageMaker Model for real-time inference or batch transformation.<jupyter_code>from sagemaker.mxnet import MXNet
from time import strftime, gmtime

training_job_name = "{}-{}".format(config.model_name, strftime("%Y-%m-%d-%H-%M-%S", gmtime()))
train_instance_type = 'ml.p3.2xlarge'

m = MXNet(entry_point='sagemaker_predictive_maintenance_entry_point.py',
          source_dir='sagemaker_predictive_maintenance/sagemaker_predictive_maintenance_entry_point',
          py_version='py3',
          role=role, 
          train_instance_count=1, 
          train_instance_type=train_instance_type,
          output_path=output_location,
          hyperparameters={'num-datasets' : len(train_df),
                           'num-gpus': 1,
                           'epochs': 200,
                           'optimizer': 'adam',
                           'batch-size':1,
                           'log-interval': 100},
         input_mode='File',
         train_max_run=7200,
         framework_version='1.6.0')

m.fit({'train': s3_data_prefix}, job_name=training_job_name)<jupyter_output><empty_output><jupyter_text>Create Transformer ModelWe can now call the `transformer` function to create a SageMaker Model from the estimator. The SageMaker Model will have the same name as the base name for the training job that just completed. We check first that a previous model doesn't exist with the same name. If it does, we delete the stale model and update the model name to refer to the model artifacts from the newly completed training job. This will ensure that SageMaker stores a reference to the current trained model which can be used for predictions later on.<jupyter_code>utils.delete_model_if_exists(config.model_name)
batch_output = 's3://{}/{}/{}'.format(bucket, prefix, 'batch-inference')
transformer = m.transformer(instance_count=1, instance_type='ml.m4.xlarge', output_path=batch_output,
                            model_name=config.model_name)<jupyter_output><empty_output><jupyter_text>Transform test data using the transformer modelUsing the `transformer` SageMaker Model, we can run a [SageMaker Batch Transform](https://docs.aws.amazon.com/sagemaker/latest/dg/batch-transform.html) job to get some predictions on test dataset for the model. Here we have a function that takes some test data in S3 and copies it to a new location where it's used as the input to the `transform` function of the Batch Transformer.<jupyter_code>s3_test_key = "{}/data/test-0.csv".format(prefix)
s3_transform_input = "{}/batch-transform-input".format(prefix)

job_name, input_key = utils.get_transform_input(bucket, config.solution_prefix, s3_test_key, s3_transform_input)
transformer.transform(input_key, wait=True)<jupyter_output><empty_output><jupyter_text>View model prediction resultsOnce the transform job terminates, we can see the models predictions for the fractional remaining useful life left for the sensor readings in the `data/test-0.csv`. The predictions are a fraction of `MAX_RUL` which is `130.0`, therefore the Remaining Useful Life predictions can be obtained by multiplying the output with 130<jupyter_code>utils.get_transform_output(bucket, prefix, job_name)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.06723574665402045, 'fraction_numerical': 0.007798503530403626, 'mean_word_length': 5.083333333333333, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 2, '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': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '202328', 'license': 'Apache-2.0', 'n_tokens_mistral': 2667, 'n_tokens_neox': 2546, 'n_words': 1047, 'path': 'source/notebooks/sagemaker_predictive_maintenance.ipynb', 'repo_name': 'gavbdheiver/aws-predictive-maintenance-using-machine-learning-master', 'size': '9489'}