text: '''Example of how to use sklearn wrapper
  
  Builds simple CNN models on MNIST and uses sklearn's GridSearchCV to find best model
  '''
  
  from __future__ import print_function
  
  import keras
  from keras.datasets import mnist
  from keras.models import Sequential
  from keras.layers import Dense, Dropout, Activation, Flatten
  from keras.layers import Conv2D, MaxPooling2D
  from keras.wrappers.scikit_learn import KerasClassifier
  from keras import backend as K
  from sklearn.grid_search import GridSearchCV
  
  
  num_classes = 10
  
  # input image dimensions
  img_rows, img_cols = 28, 28
  
  # load training data and do basic data normalization
  (x_train, y_train), (x_test, y_test) = mnist.load_data()
  
  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)
  
  x_train = x_train.astype('float32')
  x_test = x_test.astype('float32')
  x_train /= 255
  x_test /= 255
  
  # 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)
  
  
  def make_model(dense_layer_sizes, filters, kernel_size, pool_size):
      '''Creates model comprised of 2 convolutional layers followed by dense layers
  
      dense_layer_sizes: List of layer sizes.
          This list has one number for each layer
      filters: Number of convolutional filters in each convolutional layer
      kernel_size: Convolutional kernel size
      pool_size: Size of pooling area for max pooling
      '''
  
      model = Sequential()
      model.add(Conv2D(filters, kernel_size,
                       padding='valid',
                       input_shape=input_shape))
      model.add(Activation('relu'))
      model.add(Conv2D(filters, kernel_size))
      model.add(Activation('relu'))
      model.add(MaxPooling2D(pool_size=pool_size))
      model.add(Dropout(0.25))
  
      model.add(Flatten())
      for layer_size in dense_layer_sizes:
          model.add(Dense(layer_size))
      model.add(Activation('relu'))
      model.add(Dropout(0.5))
      model.add(Dense(num_classes))
      model.add(Activation('softmax'))
  
      model.compile(loss='categorical_crossentropy',
                    optimizer='adadelta',
                    metrics=['accuracy'])
  
      return model
  
  dense_size_candidates = [[32], [64], [32, 32], [64, 64]]
  my_classifier = KerasClassifier(make_model, batch_size=32)
  validator = GridSearchCV(my_classifier,
                           param_grid={'dense_layer_sizes': dense_size_candidates,
                                       # epochs is avail for tuning even when not
                                       # an argument to model building function
                                       'epochs': [3, 6],
                                       'filters': [8],
                                       'kernel_size': [3],
                                       'pool_size': [2]},
                           scoring='neg_log_loss',
                           n_jobs=1)
  validator.fit(x_train, y_train)
  
  print('The parameters of the best model are: ')
  print(validator.best_params_)
  
  # validator.best_estimator_ returns sklearn-wrapped version of best model.
  # validator.best_estimator_.model returns the (unwrapped) keras model
  best_model = validator.best_estimator_.model
  metric_names = best_model.metrics_names
  metric_values = best_model.evaluate(x_test, y_test)
  for metric, value in zip(metric_names, metric_values):
      print(metric, ': ', value)
  