def prettify_prediction_interpretation(interpretation_info, prediction_interpretation, config):
    """
    Convert the prediction interpretation to a pretty markdown string.
    The text coloring only supports up to 6 colors, i.e. up to 6 classes of labels. If you want more, it's easy
    to add.

    Format per instance:
    * original text
    * marked text: each token is preceded by a colored @ with a color corresponding to the class of the filter
      that "chose" an ngram containing this token.
    * a table of all the prediction data:
      filter name | filter identity class | passes | ngram | activation | slots

      - filter name format: w{ngram size}.f{filter index} ex. w3.f4 = filter number
        4 among the filters processing ngrams of size 3
      - passes: whether the ngram passes the threshold of this filter
      - activation: the final activation of the filter on this ngram. note that this number does NOT equal the sum of
        the slot activations, because it includes the filter bias weight and is after the ReLU layer (non-negative)
      - slots: the slot activation vector, i.e. the token-level activations gathered from breaking the filter-ngram
        dot-product into k dot-products (for ngram of length k)
    """
    output_str = "# Prediction info"

    def mark_span(sentence, span_start, span_end, color):
        mark = f'<span style="background-color: {color}">@</span>'
        marked_ngram = []
        for token in sentence[span_start:span_end]:
            if mark in token:
                marked_ngram.append(token)
            else:
                marked_ngram.append(mark + token)
        return sentence[:span_start] + marked_ngram + sentence[span_end:]

    colors = ["#FFFF00", "#6698FF", "#E56717", "#00FF7F", "#FFA07A", "#FF8C00"]  # add more colors here
    class_to_color = {}
    for cl, color in zip(list(config["class_to_str"]), colors):
        class_to_color[cl] = color

    identity_classes = interpretation_info["threshold_info"]["identity_classes"]
    thresholds = interpretation_info["threshold_info"]["thresholds"]

    for pinfo in prediction_interpretation:
        sen = pinfo["sentence"]

        table = "filter | filter class | passes | ngram | activation | slots\n"
        table += ":-- | :-- | :-- | :-- | :-- | :--\n"
        for wix, w_size in enumerate(config["ngram_sizes"]):
            for fix in range(config["num_filters"]):
                fname = "w" + str(w_size) + ".f" + str(fix)
                ngram = " ".join(pinfo[fname]["chosen_ngram"])
                ngram_span = pinfo[fname]["chosen_ngram_span"]

                c = identity_classes[fname]

                passes = "x" if pinfo[fname]["activation"] > thresholds[wix, fix] else " "

                if passes == "x":
                    sen = mark_span(sen, ngram_span[0], ngram_span[1], class_to_color[str(c)])

                l = " | ".join(
                    [fname, config["class_to_str"][str(c)], passes, "`" + ngram + "`",
                     "{0:.2f}".format(pinfo[fname]["activation"]),
                     str(["{0:.2f}".format(i) for i in pinfo[fname]["slot_activations"]])]) + "\n"
                table += l

        header = "## Original input: \n"
        header += "``` " + " ".join(pinfo["sentence"]) + " ``` \n\n"
        header += "## Marked input: \n"
        header += "<pre>" + " ".join(sen) + "</pre> \n\n"
        header += "Gold: " + pinfo["gold_str"] + ", Prediction: " + pinfo["prediction_str"] + "\n\n"

        output_str += "\n" + header + "\n" + table

    return output_str