Skip to main content

MXNet Basic Classification (AWS SageMaker)

Hands-On Lab

 

Length

01:00:00

Difficulty

Intermediate

MXNet is a heavy hitter in the world of machine learning and artificial intelligence. In this activity, we will use MXNet along with Gluon to create an artificial neural network that performs a basic image classification task. In this lab, we use our own data set and get one step closer to the dream of an automated Lego brick sorting machine. The files used in this lab can be found on GitHub.

What are Hands-On Labs?

Hands-On Labs are scenario-based learning environments where learners can practice without consequences. Don't compromise a system or waste money on expensive downloads. Practice real-world skills without the real-world risk, no assembly required.

MXNet Basic Classification (AWS SageMaker)

Introduction

MXNet is a heavy hitter in the world of machine learning and artificial intelligence. In this activity, we will use MXNet along with Gluon to create an artificial neural network that performs a basic image classification task. In this lab, we use our own data set and get one step closer to the dream of an automated Lego brick sorting machine. The files used in this lab can be found on GitHub.

Logging In

Log in to AWS with the credentials provided on the hands-on lab page. In the Find Services box, look for SageMaker. Once we're in the SageMaker dashboard, click Notebook instances in the left-hand menu. There should be one sitting there. If there isn't one, double-check to make sure you're in the N. Virginia region (using the dropdown in the upper-right of the dashboard). When it shows up in the list of notebooks, click it and we'll land in the notebook server. To open up the notebook we'll be working with, click Open Jupyter over toward the right.

Navigate to the Jupyter Notebook

Once you're redirected to Jupyter, click to open the listed MXNet-Basic-Classification.ipynb notebook. It may take a few minutes to fully spin up — once it's ready, you should see a Lego image pop up.

Run the Libraries

  1. With The Libraries code block selected, click Run at the top.
  2. Click the ctx code block and click Run at the top.

Load the Data Ready for Training

  1. Click the next code block and then click Run to load the data by opening the files and using pickle to load the data into the running environment:

    train_fh = open('lego-simple-mx-train', 'rb')
    test_fh = open('lego-simple-mx-test', 'rb')
    
    train_data = pickle.load(train_fh)
    test_data = pickle.load(test_fh)
  2. To see the data, click Insert > Insert Cell Below and enter:

    train_data

    We'll see the training data contains both the image data and the label data.

  3. Click the next code block and then click Run to define some human-readable class_names for the image data:

    class_names = ['2x3 Brick', '2x2 Brick', '1x3 Brick', '2x1 Brick', '1x1 Brick', '2x2 Macaroni', '2x2 Curved End', 'Cog 16 Tooth', '1x2 Handles', '1x2 Grill']
  4. Click the next code block and then click Run to convert the data to MXNet tensors ahead of training:

    transformer = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize(0.13, 0.31)])
    
    train_data = train_data.transform_first(transformer)
    test_data = test_data.transform_first(transformer)
  5. Click the next code block and then click Run to look at just the first image in our data:

    train_image_no = 0
    
    images_data, label_data = train_data[train_image_no]
    plt.figure()
    plt.imshow(images_data.reshape((48,48)).asnumpy())
    plt.colorbar()
    plt.xlabel(class_names[label_data])
    plt.show()

    The first image in our training set should pop up beneath the code once it's run.

    If you'd like to see a different image, change the O in train_image_no to a different number (e.g., 12) to see another image instead.

  6. Click the next code block and then click Run to look at the first 20 images in our data set. Do they look right?

    plt.figure(figsize=(15,15))
    for i in range(20):
        images_data, label_data = train_data[i]
        plt.subplot(5,5,i+1)
        plt.xticks([])
        plt.yticks([])
        plt.imshow(images_data.reshape((48,48)).asnumpy(), cmap=plt.cm.binary)
        plt.xlabel(class_names[label_data])
    plt.show()

Create an MXNet Model

  1. Click the next code block and then click Run to define an artificial neural network using the Gluon for MXNet:

    net = nn.HybridSequential(prefix='MLP_')
    with net.name_scope():
        net.add(
            nn.Flatten(),
            nn.Dense(128, activation='relu'),
            nn.Dense(64, activation='relu'),
            nn.Dense(10, activation=None)
        )
  2. Click the next code block and then click Run to create a data loader to manage the feeding of data into our model during training:

    batch_size = 34
    train_loader = mx.gluon.data.DataLoader(train_data, shuffle=True, batch_size=batch_size)
  3. Click the next code block and then click Run to initialize the model (note we pass in a variable holding the processor type here):

    ctx = mx.gpu(0) if mx.context.num_gpus() > 0 else mx.cpu(0)
    net.initialize(mx.init.Xavier(), ctx=ctx)
  4. Gluon provides a trainer object to maintain the state of the training. Click the next code block and then click Run to create it here and use it in the training process:

    trainer = gluon.Trainer(
        params=net.collect_params(),
        optimizer='sgd',
        optimizer_params={'learning_rate': 0.04},
    )

Train the MXNet Model

  1. Click the next code block and then click Run to define the metric to use while we train and the loss function to use. Gluon provides a softmax loss function, so we just use that:

    metric = mx.metric.Accuracy()
    loss_function = gluon.loss.SoftmaxCrossEntropyLoss()
  2. Now, we train. Click the next code block and then click Run to execute this inline code:

    num_epochs = 10
    history = []
    
    for epoch in range(num_epochs):
        for inputs, labels in train_loader:
            # Possibly copy inputs and labels to the GPU
            inputs = inputs.as_in_context(ctx)
            labels = labels.as_in_context(ctx)
    
            # Forward pass
            with autograd.record():
                outputs = net(inputs)
                loss = loss_function(outputs, labels)
    
            # Backpropagation
            loss.backward()
            metric.update(labels, outputs)
    
            # Update
            trainer.step(batch_size=inputs.shape[0])
    
        # Print the evaluation metric and reset it for the next epoch
        name, acc = metric.get()
        history.insert(epoch,acc)
        print('.', end='')
        metric.reset()
    
    print('[Done]')
  3. During the training loop, we collected accuracy data in each epoch. Click the next code block and then click Run to graph this data to get a sense of how the training went:

    plt.figure(figsize=(7, 4))
    plt.plot(history)
    plt.title('Model accuracy')
    plt.ylabel('Accuracy')
    plt.xlabel('Epoch')

    We should see a Model accuracy graph.

Evaluate and Test the MXNet Model

  1. Click the next code block and then click Run to use the Gluon data loader again — this time with the test data:

    test_loader = mx.gluon.data.DataLoader(test_data, shuffle=False, batch_size=batch_size)
  2. Click the next code block and then click Run to measure the accuracy:

    metric = mx.metric.Accuracy()
    for inputs, labels in test_loader:
        # Possibly copy inputs and labels to the GPU
        inputs = inputs.as_in_context(ctx)
        labels = labels.as_in_context(ctx)
        metric.update(labels, net(inputs))
    print('Validation: {} = {}'.format(*metric.get()))

    We should see an accuracy score.

  3. Click the next code block and then click Run to define a couple of functions to display the results:

    # Function to display the image:
    def plot_image(predictions_array, true_label, img):
      plt.xticks([])
      plt.yticks([])
      plt.imshow(img.reshape((48,48)).asnumpy(), cmap=plt.cm.binary)
      predicted_label = np.argmax(predictions_array)
      if predicted_label == true_label:
        color = 'green'
      else:
        color = 'red'
      # Print a label with 'predicted class', 'probability %', 'actual class'
      plt.xlabel("{} [{:2.0f}] ({})".format(class_names[predicted_label],
                                    np.max(predictions_array),
                                    class_names[true_label]),
                                    color=color)
    
    # Function to display the prediction results in a graph:
    def plot_value_array(predictions_array, true_label):
      plt.xticks(range(10))
      plt.yticks([])
      plot = plt.bar(range(10), predictions_array, color="#777777")
      predicted_label = np.argmax(predictions_array)
      plot[predicted_label].set_color('red')
      plot[true_label].set_color('green')

Single Prediction

  1. Click the next code block and then click Run to choose one of the images from our test set:

    prediction_image_number = 25
  2. Click the next code block and then click Run to make a prediction:

    prediction_image, prediction_label = test_data[prediction_image_number]
    predictions_single = net(prediction_image)
    predictions_single

    The output may look like an error message, but it isn't. It's each of the values coming out of the output layer of our artificial neural network. Whichever one is the biggest is the answer.

  3. Click the next code block and then click Run to display those results with our function defined earlier:

    plot_value_array(predictions_single[0].asnumpy(), prediction_label)
    plt.xticks(range(10), class_names, rotation=45)
    plt.show()

    A bar graph will then display.

  4. And which block should we have found? In other words, did we get it right? Click the next code block and then click Run:

    plot_image(predictions_single[0].asnumpy(), prediction_label, prediction_image)

    We should then see a Lego brick.

    Feel free to re-run the single prediction commands with a different prediction_image_number.

  5. Click the next code block and then click Run to get prediction values for all the test images we have. This time we don't use a data loader — we just iterate through the raw test image data.

    predictions = []
    test_labels = []
    
    for i in test_data:
        pred_image, pred_label = i
        p = net(pred_image)
        predictions.append(p)
        test_labels.append(pred_label)
  6. Finally, let's use our helper functions to summarize the first 16 images in our test data. How did we do? Click the next code block and then click Run:

    num_rows = 8
    num_cols = 2
    num_images = num_rows*num_cols
    plt.figure(figsize=(15, 16))
    for i in range(num_images):
      plt.subplot(num_rows, 2*num_cols, 2*i+1)
      plot_image(predictions[i].asnumpy(), test_data[i][1], test_data[i][0])
      plt.subplot(num_rows, 2*num_cols, 2*i+2)
      plot_value_array(predictions[i][0].asnumpy(), test_data[i][1])
    plt.show()

Conclusion

Congratulations on successfully completing this (follow-along) hands-on lab!