def define_cnn(n_classes, input_image_shape, n_kernels, filter_scale, poolsize,
               n_hidden, dropout_p, activation="relu", l1_reg=0, l2_reg=0):
    """Shortcut to build the list of layer definitions for
    a convolutional neural network

    Defines a series of convolutional layers, followed by max-pooling layers,
    after which a multi-layer perceptron calculates the probabilities of
    membership in each class.

    Parameters
    ----------
    n_classes : int
        Number of classes to calculate probabilities for
    input_image_shape : list or tuple
        Shape of input image, (n_channels, n_pixels_x, n_pixels_y)
    n_kernels : list of ints
        Number of convolutional kernels in each convolutional layer
    filter_scale : list of ints
        Size of (square) filters in each convolutional layer.
        Must be the same length as `n_kernels`.
    poolsize : list of ints
        Size of (square) non-overlapping max-pooling kernel to be
        applied after each convolutional layer (may be zero, meaning
        no max pooling after that layer). Must be the same length
        as `n_kernels`.
    n_hidden : list of ints
        Number of units in each hidden layer
    dropout_p : float or list of floats
        Dropout fraction for input and each hidden layer. If a single float,
        this dropout fraction will be applied to every layer.
    activation : {"relu", "prelu", "sigmoid", "tanh", "abstanh", "linear"}
        Activation function to use for all layers
    l1_reg, l2_reg : float, optional
        L1 and L2 regularization strengths for all layers

    Returns
    -------
    list
        Layer definitions suitable for input to a `NNClassifier`

    Examples
    --------
    >>> layers = define_cnn(10, (28, 28), n_kernels=[32, 32], filter_scale=[4, 3],
    >>>                     poolsize=[0, 2], n_hidden=[400], dropout_p=0.2)
    >>> print(layers)
    [['InputImageLayer', {'n_images': 1, 'n_pixels': [28, 28], 'name': 'input'}],
     ['DropoutLayer', {'dropout_p': 0.2, 'name': 'DO-input'}],
     ['ConvLayer',
      {'activation': 'relu',
       'filter_shape': (4, 4),
       'n_output_maps': 32,
       'name': 'conv0'}],
     ['DropoutLayer', {'dropout_p': 0.2, 'name': 'DO-conv0'}],
     ['ConvLayer',
      {'activation': 'relu',
       'filter_shape': (3, 3),
       'n_output_maps': 32,
       'name': 'conv1'}],
     ['MaxPool2DLayer', {'name': 'maxpool1', 'pool_shape': (2, 2)}],
     ['DropoutLayer', {'dropout_p': 0.2, 'name': 'DO-conv1'}],
     ['FCLayer',
      {'activation': 'relu', 'l1': 0, 'l2': 0, 'n_units': 400, 'name': 'fc0'}],
     ['DropoutLayer', {'dropout_p': 0.2, 'name': 'DO-fc0'}],
     ['ClassificationOutputLayer', {'l1': 0, 'l2': 0, 'n_classes': 10}]]
    """
    # Assume input images are 2D. If the `input_image_shape` is 3 elements,
    # the first element is the number of images in the input. Otherwise, assume
    # that there's only one image in the input.
    if len(input_image_shape) == 3:
        pass
    elif len(input_image_shape) == 2:
        input_image_shape = [1] + list(input_image_shape)
    else:
        raise ValueError("The input image shape must be (n_channels, n_pixels_x, n_pixels_y).")

    try:
        # Make sure that `n_hidden` is a list.
        len(n_hidden)
    except TypeError:
        n_hidden = [n_hidden]
    try:
        # Make sure that `dropout_p` is a list.
        len(dropout_p)
    except TypeError:
        dropout_p = (1 + len(n_hidden) + len(n_kernels)) * [dropout_p]
    if len(dropout_p) != len(n_kernels) + len(n_hidden) + 1:
        raise ValueError("Either specify one dropout for all layers or one dropout for "
                         "each layer (inputs + hidden layers).")
    dropout_p = dropout_p[::-1]  # Pops come from the end, so reverse this list.

    # Start by putting on the input layer.
    layer_defs = [["InputImageLayer", {"name": "input", "n_images": input_image_shape[0],
                                       "n_pixels": input_image_shape[1:]}]]
    input_do = dropout_p.pop()
    if input_do:
        layer_defs.append(["DropoutLayer", {"name": "DO-input", "dropout_p": input_do}])

    # Add convolutional layers.
    for i_conv, (kernels, filter, pool) in enumerate(zip(n_kernels, filter_scale, poolsize)):
        layer_defs.append(["ConvLayer", {"name": "conv{}".format(i_conv),
                                         "n_output_maps": kernels,
                                         "filter_shape": (filter, filter),
                                         "activation": activation}])
        if pool:
            layer_defs.append(["MaxPool2DLayer", {"name": "maxpool{}".format(i_conv),
                                                  "pool_shape": (pool, pool)}])
        layer_do = dropout_p.pop()
        if layer_do:
            layer_defs.append(["DropoutLayer", {"name": "DO-conv{}".format(i_conv),
                                                "dropout_p": layer_do}])

    # Add fully-connected layers.
    for i_hidden, hidden in enumerate(n_hidden):
        layer_defs.append(["FCLayer", {"name": "fc{}".format(i_hidden),
                                       "n_units": hidden, "activation": activation,
                                       "l1": l1_reg, "l2": l2_reg}])
        layer_do = dropout_p.pop()
        if layer_do:
            layer_defs.append(["DropoutLayer", {"name": "DO-fc{}".format(i_hidden),
                                                "dropout_p": layer_do}])

    # Put on an output layer.
    layer_defs.append(["ClassificationOutputLayer", {"n_classes": n_classes, "l1": l1_reg,
                                                     "l2": l2_reg}])

    return layer_defs