text: import gzip
  import os
  import struct
  import numpy as np
  from time import perf_counter
  
  try:
      from urllib.request import urlretrieve
  except ImportError:
      from urllib import urlretrieve
  
  import cntk
  from cntk.layers import Convolution, MaxPooling, Dropout, Dense
  from cntk.initializer import gaussian
  
  
  '''
  -----------------------------------------------------
  Functions to load or download MNIST images and unpack
  into training and testing sets.
  -----------------------------------------------------
  '''
  # loading data from local path if possible. Otherwise download from online sources.
  def load_or_download_mnist_files(filename, num_samples, local_data_dir):
  
      if (local_data_dir):
          local_path = os.path.join(local_data_dir, filename)
      else:
          local_path = os.path.join(os.getcwd(), filename)
  
      if os.path.exists(local_path):
          gzfname = local_path
      else:
          local_data_dir = os.path.dirname(local_path)
          if not os.path.exists(local_data_dir):
              os.makedirs(local_data_dir)
          filename = "http://yann.lecun.com/exdb/mnist/" + filename
          print ("Downloading from" + filename, end=" ")
          gzfname, h = urlretrieve(filename, local_path)
          print ("[Done]")
  
      return gzfname
  
  def get_mnist_data(filename, num_samples, local_data_dir):
  
      gzfname = load_or_download_mnist_files(filename, num_samples, local_data_dir)
  
      with gzip.open(gzfname) as gz:
          n = struct.unpack('I', gz.read(4))
          # Read magic number.
          if n[0] != 0x3080000:
              raise Exception('Invalid file: unexpected magic number.')
          # Read number of entries.
          n = struct.unpack('>I', gz.read(4))[0]
          if n != num_samples:
              raise Exception('Invalid file: expected {0} entries.'.format(num_samples))
          crow = struct.unpack('>I', gz.read(4))[0]
          ccol = struct.unpack('>I', gz.read(4))[0]
          if crow != 28 or ccol != 28:
              raise Exception('Invalid file: expected 28 rows/cols per image.')
          # Read data.
          res = np.fromstring(gz.read(num_samples * crow * ccol), dtype = np.uint8)
  
          return res.reshape((num_samples, crow * ccol))
  
  def get_mnist_labels(filename, num_samples, local_data_dir):
  
      gzfname = load_or_download_mnist_files(filename, num_samples, local_data_dir)
  
      with gzip.open(gzfname) as gz:
          n = struct.unpack('I', gz.read(4))
          # Read magic number.
          if n[0] != 0x1080000:
              raise Exception('Invalid file: unexpected magic number.')
          # Read number of entries.
          n = struct.unpack('>I', gz.read(4))
          if n[0] != num_samples:
              raise Exception('Invalid file: expected {0} rows.'.format(num_samples))
          # Read labels.
          res = np.fromstring(gz.read(num_samples), dtype = np.uint8)
  
          return res.reshape((num_samples, 1))
  
  def load_mnist_data(data_filename, labels_filename, number_samples, local_data_dir=None):
      data = get_mnist_data(data_filename, number_samples, local_data_dir)
      labels = get_mnist_labels(labels_filename, number_samples, local_data_dir)
      return np.hstack((data, labels))
  
  # Save the data files into a format compatible with CNTK text reader
  def save_as_txt(filename, ndarray):
      dir = os.path.dirname(filename)
  
      if not os.path.exists(dir):
          os.makedirs(dir)
  
      if not os.path.isfile(filename):
          print("Saving to ", filename, end=" ")
          with open(filename, 'w') as f:
              labels = list(map(' '.join, np.eye(10, dtype=np.uint).astype(str)))
              for row in ndarray:
                  row_str = row.astype(str)
                  label_str = labels[row[-1]]
                  feature_str = ' '.join(row_str[:-1])
                  f.write('|labels {} |features {}\n'.format(label_str, feature_str))
      else:
          print("File already exists", filename)
  
  
  '''
  --------------------------------------------------
  Retrieve and process the training and testing data
  --------------------------------------------------
  '''
  # Ensure we always get the same amount of randomness
  np.random.seed(0)
  
  # Define the data dimensions
  image_shape = (1, 28, 28)
  input_dim = int(np.prod(image_shape, dtype=int))
  output_dim = 10
  
  num_train_samples = 60000
  num_test_samples = 10000
  
  # The local path where the training and test data might be found or will be downloaded to.
  training_data_path = os.path.join(os.getcwd(), "MNIST_data", "Train-28x28_cntk_text.txt")
  testing_data_path = os.path.join(os.getcwd(), "MNIST_data", "Test-28x28_cntk_text.txt")
  
  # Download the data if they don't already exist
  if not os.path.exists(training_data_path):
  
      url_train_image = "train-images-idx3-ubyte.gz"
      url_train_labels = "train-labels-idx1-ubyte.gz"
  
      print("Loading training data")
      saved_data_dir = os.path.join(os.getcwd(), "MNIST_data")
      train = load_mnist_data(url_train_image, url_train_labels, num_train_samples, local_data_dir=saved_data_dir)
  
      print ("Writing training data text file...")
      save_as_txt(training_data_path, train)
      print("[Done]")
  
  if not os.path.exists(testing_data_path):
  
      url_test_image = "t10k-images-idx3-ubyte.gz"
      url_test_labels = "t10k-labels-idx1-ubyte.gz"
  
      print("Loading testing data")
      saved_data_dir = os.path.join(os.getcwd(), "MNIST_data")
      test = load_mnist_data(url_test_image, url_test_labels, num_test_samples, saved_data_dir)
  
      print ("Writing testing data text file...")
      save_as_txt(testing_data_path, test)
      print("[Done]")
  
  feature_stream_name = 'features'
  labels_stream_name = 'labels'
  
  # Convert to CNTK MinibatchSource
  train_minibatch_source = cntk.text_format_minibatch_source(training_data_path, [
      cntk.StreamConfiguration(feature_stream_name, input_dim),
      cntk.StreamConfiguration(labels_stream_name, output_dim)])
  training_features = train_minibatch_source[feature_stream_name]
  training_labels = train_minibatch_source[labels_stream_name]
  
  print("Training data from file %s successfully read." % training_data_path)
  
  test_minibatch_source = cntk.text_format_minibatch_source(testing_data_path, [
      cntk.StreamConfiguration(feature_stream_name, input_dim),
      cntk.StreamConfiguration(labels_stream_name, output_dim)])
  test_features = test_minibatch_source[feature_stream_name]
  test_labels = test_minibatch_source[labels_stream_name]
  
  print("Test data from file %s successfully read." % testing_data_path)
  
  
  '''
  ---------------------------------------------
  Constructing the Convolutional Neural Network
  ---------------------------------------------
  '''
  def create_convolutional_neural_network(input_vars, out_dims, dropout_prob=0.0):
  
      convolutional_layer_1 = Convolution((5, 5), 32, strides=1, activation=cntk.ops.relu, pad=True, init=gaussian(), init_bias=0.1)(input_vars)
      pooling_layer_1 = MaxPooling((2, 2), strides=(2, 2), pad=True)(convolutional_layer_1)
  
      convolutional_layer_2 = Convolution((5, 5), 64, strides=1, activation=cntk.ops.relu, pad=True, init=gaussian(), init_bias=0.1)(pooling_layer_1)
      pooling_layer_2 = MaxPooling((2, 2), strides=(2, 2), pad=True)(convolutional_layer_2)
  
      convolutional_layer_3 = Convolution((5, 5), 128, strides=1, activation=cntk.ops.relu, pad=True, init=gaussian(), init_bias=0.1)(pooling_layer_2)
      pooling_layer_3 = MaxPooling((2, 2), strides=(2, 2), pad=True)(convolutional_layer_3)
  
      fully_connected_layer = Dense(1024, activation=cntk.ops.relu, init=gaussian(), init_bias=0.1)(pooling_layer_3)
      dropout_layer = Dropout(dropout_prob)(fully_connected_layer)
      output_layer = Dense(out_dims, activation=None, init=gaussian(), init_bias=0.1)(dropout_layer)
  
      return output_layer
  
  # Define the input to the neural network
  input_vars = cntk.ops.input_variable(image_shape, np.float32)
  
  # Create the convolutional neural network
  output = create_convolutional_neural_network(input_vars, output_dim, dropout_prob=0.5)
  
  
  '''
  ----------------------
  Setting up the trainer
  ----------------------
  '''
  # Define the label as the other input parameter of the trainer
  labels = cntk.ops.input_variable(output_dim, np.float32)
  
  # Initialize the parameters for the trainer
  train_minibatch_size = 50
  learning_rate = cntk.learner.training_parameter_schedule(1e-4, cntk.UnitType.sample)
  momentum = cntk.learner.momentum_as_time_constant_schedule(0.99)
  
  # Define the loss function
  loss = cntk.ops.cross_entropy_with_softmax(output, labels)
  
  # Define the function that calculates classification error
  label_error = cntk.ops.classification_error(output, labels)
  
  # Instantiate the trainer object to drive the model training
  learner = cntk.adam_sgd(output.parameters, learning_rate, momentum)
  trainer = cntk.Trainer(output, loss, label_error, [learner])
  
  
  '''
  -----------------------------------------
  Training the Convolutional Neural Network
  -----------------------------------------
  '''
  num_training_epoch = 20
  training_progress_output_freq = 100
  training_start_time = perf_counter()
  
  for epoch in range(num_training_epoch):
  
      sample_count = 0
      num_minibatch = 0
  
      # loop over minibatches in the epoch
      while sample_count < num_train_samples:
  
          minibatch = train_minibatch_source.next_minibatch(min(train_minibatch_size, num_train_samples - sample_count))
  
          # Specify the mapping of input variables in the model to actual minibatch data to be trained with
          data = {input_vars: minibatch[training_features],
                  labels: minibatch[training_labels]}
          trainer.train_minibatch(data)
  
          sample_count += data[labels].num_samples
          num_minibatch += 1
  
          # Print the training progress data
          if num_minibatch % training_progress_output_freq == 0:
              training_loss = cntk.get_train_loss(trainer)
              eval_error = cntk.get_train_eval_criterion(trainer)
              t = perf_counter() - training_start_time
              print("(%d s) Epoch %d  |  # of Samples: %6d  |  Loss: %.6f  |  Error: %.6f" % (t, epoch, sample_count, training_loss, eval_error))
  
  t_minute = (perf_counter() - training_start_time) / 60
  print("Training Completed in %f minutes." % t_minute , end="\n\n")
  
  
  '''
  -------------------
  Classification Test
  --------------------
  '''
  test_minibatch_size = 1000
  
  sample_count = 0
  test_results = []
  
  while sample_count < num_test_samples:
  
      minibatch = test_minibatch_source.next_minibatch(min(test_minibatch_size, num_test_samples - sample_count))
  
      # Specify the mapping of input variables in the model to actual minibatch data to be tested with
      data = {input_vars: minibatch[test_features],
              labels: minibatch[test_labels]}
      eval_error = trainer.test_minibatch(data)
      test_results.append(eval_error)
  
      sample_count += data[labels].num_samples
  
  # Printing the average of evaluation errors of all test minibatches
  print("Average errors of all test minibatches: %.3f%%" % (float(np.mean(test_results, dtype=float))*100))
  