def generate_model_spec(model_name,
                        n_conv_filters,
                        n_output_hus=[64, 64],
                        residual=False,
                        clip_grads=False,
                        optimizer='adam',
                        activation='relu',
                        batch_norm=True,
                        batch_size=32,
                        early_stopping_patience=15,
                        lr_plateau_patience=5,
                        init_lr=0.001,
                        bulk_chunk_size=None,
                        max_bulk_chunk_size=None,
                        partial_weight=1e-4,
                        uniform_frac=1,
                        generation=7,
                        output_bn=True,
                        loss_weight='prior'):
    """ Generates a json specifying the model architecture and training hyperparams

    Args:
      model_name: used to save, should be unique - str
      n_conv_filters: number of filters to use in CNN layers - int or list of len 8
      n_output_hus: number of hus in the output layer - list
      residual: (optional) add residual connections
      receptive_field_size: implicit size of model receptive field - [t, x, y, z]
        all dimensions must be odd for centering purposes
        x,y dimensions must be 5x z dimension
      clip_grads: (optional) clip gradients before applying update
      optimizer: name of optimizer to use - str
      activation: activation to be used for all hidden layers - keras.layers.Activation or str
      batch_norm: whether to use batch normalization - bool
      uniform_frac: fraction of bulk samples to be drawn uniformly spatially

    Returns:
      model_spec: dict specifying model
    """
    assert isinstance(n_output_hus, list)

    if isinstance(n_conv_filters, int):
        n_conv_filters = [n_conv_filters] * 8
    else:
        assert isinstance(n_conv_filters, list)
        assert len(n_conv_filters) == 8

    assert bulk_chunk_size is not None
    if max_bulk_chunk_size is None:
        print("No max_bulk_chunk_size specified, using {}".format(bulk_chunk_size))
        max_bulk_chunk_size = bulk_chunk_size

    # NOTE: for now, clip_grad, batch_norm are double listed
    # eventually it will only exist in train_spec
    model_config = {
        'optimizer': optimizer,
        'clip_grad': clip_grads,
        'name': model_name,
        'residual': residual,
        'n_conv_filters': n_conv_filters,
        'n_output_hus': n_output_hus,
        'activation': activation,
        'batch_norm': batch_norm,
        'output_bn': output_bn,
        'generation': generation,
        'data_spec': {
            'uniform_frac': uniform_frac,
            'include_augment': True,
            'batch_size': batch_size,
            'bulk_chunk_size' : bulk_chunk_size,
            'max_bulk_chunk_size': max_bulk_chunk_size,
            'loss_weighter': loss_weight
        },
        'train_spec': {
            'early_stopping_patience': early_stopping_patience,
            'lr_plateau_patience': lr_plateau_patience,
            'learning_rate': init_lr,
            'clip_grads':  clip_grads,
            'partial_weight': partial_weight
        }
    }
    return model_config