text: '''Trains a simple convnet on the MNIST dataset.
  Gets to 99.25% test accuracy after 12 epochs
  (there is still a lot of margin for parameter tuning).
  16 seconds per epoch on a GRID K520 GPU.
  '''
  #python 2/3 compatibility
  from __future__ import print_function
  #simplified interface for building models 
  import keras
  #our handwritten character labeled dataset
  from keras.datasets import mnist
  #because our models are simple
  from keras.models import Sequential
  #dense means fully connected layers, dropout is a technique to improve convergence, flatten to reshape our matrices for feeding
  #into respective layers
  from keras.layers import Dense, Dropout, Flatten, Activation
  #for convolution (images) and pooling is a technique to help choose the most relevant features in an image
  from keras.layers import Conv2D, MaxPooling2D
  from keras import backend as K
  
  #mini batch gradient descent ftw
  batch_size = 128
  #10 difference characters
  num_classes = 10
  #very short training time
  epochs = 15 #12
  
  # input image dimensions
  #28x28 pixel images. 
  img_rows, img_cols = 28, 28
  
  # the data downloaded, shuffled and split between train and test sets
  #if only all datasets were this easy to import and format
  (x_train, y_train), (x_test, y_test) = mnist.load_data()
  
  #this assumes our data format
  #For 3D data, "channels_last" assumes (conv_dim1, conv_dim2, conv_dim3, channels) while 
  #"channels_first" assumes (channels, conv_dim1, conv_dim2, conv_dim3).
  if K.image_data_format() == 'channels_first':
      x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
      x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
      input_shape = (1, img_rows, img_cols)
  else:
      x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
      x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
      input_shape = (img_rows, img_cols, 1)
  
  #more reshaping
  x_train = x_train.astype('float32')
  x_test = x_test.astype('float32')
  x_train /= 255
  x_test /= 255
  print('x_train shape:', x_train.shape)
  print(x_train.shape[0], 'train samples')
  print(x_test.shape[0], 'test samples')
  
  # convert class vectors to binary class matrices
  y_train = keras.utils.to_categorical(y_train, num_classes)
  y_test = keras.utils.to_categorical(y_test, num_classes)
  
  #build our model
  model = Sequential()
  model.add(Conv2D(32, (3, 3), input_shape=input_shape))
  model.add(Activation('relu'))
  model.add(Conv2D(32, (3, 3)))
  model.add(Activation('relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Conv2D(32, (3, 3)))
  model.add(Activation('relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Dropout(0.25))
  model.add(Flatten())
  model.add(Dense(128))
  model.add(Activation('relu'))
  model.add(Dropout(0.5))
  model.add(Dense(num_classes))
  model.add(Activation('softmax'))
  
  model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
  
  #train that ish!
  model.fit(x_train, y_train,
            batch_size=batch_size,
            epochs=epochs,
            verbose=1,
            validation_data=(x_test, y_test))
   #how well did it do? 
  score = model.evaluate(x_test, y_test, verbose=0)
  print('Test loss:', score[0])
  print('Test accuracy:', score[1])
  
  
  #Save the model
  # serialize model to JSON
  model_json = model.to_json()
  with open("model.json", "w") as json_file:
      json_file.write(model_json)
  # serialize weights to HDF5
  model.save_weights("model.h5")
  print("Saved model to disk")