File size: 6,396 Bytes
1ccdd5a
 
 
 
d5f5e05
1ccdd5a
 
d213847
1ccdd5a
 
 
 
d213847
 
 
1ccdd5a
def7fc3
 
 
2083ffe
 
dda1ebd
 
 
d213847
1ccdd5a
 
17dc250
1ccdd5a
dd184ef
607ae4f
dd184ef
607ae4f
1ccdd5a
d213847
1ccdd5a
d213847
1ccdd5a
 
 
 
 
 
 
 
 
 
292ba65
 
 
 
1ccdd5a
d213847
1ccdd5a
b392eb0
 
1ccdd5a
 
 
 
d213847
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3383b5d
d213847
 
 
 
b0fb0fe
1ccdd5a
 
d213847
1ccdd5a
 
 
 
 
 
ee8b768
 
1ccdd5a
d213847
ee8b768
 
d213847
ee8b768
d213847
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee8b768
d213847
 
 
607ae4f
 
 
 
d213847
1ccdd5a
 
ee8b768
cc56a6c
dda1ebd
 
ee8b768
 
 
 
 
1ccdd5a
 
 
 
ee8b768
 
 
cc56a6c
ee8b768
 
 
6482360
ee8b768
 
 
1ccdd5a
 
 
856701e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import sys
tabpfn_path = 'TabPFN'
sys.path.insert(0, tabpfn_path) # our submodule of the TabPFN repo (at 045c8400203ebd062346970b4f2c0ccda5a40618)
from TabPFN.scripts.transformer_prediction_interface import TabPFNClassifier
from decision_boundary import DecisionBoundaryDisplay

import numpy as np
from pathlib import Path
import pandas as pd
import torch
import gradio as gr
import openml
import os
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

default_device = "cuda:0" if torch.cuda.is_available() else "cpu:0"

classifier = TabPFNClassifier(base_path=tabpfn_path, device=default_device, N_ensemble_configurations=4)


def compute(df_table):
    headers = df_table.columns
    table = df_table.to_numpy()
    vfunc = np.vectorize(lambda s: len(str(s)))
    non_empty_row_mask = (vfunc(table).sum(1) != 0)
    table = table[non_empty_row_mask]
    empty_mask = table == ''
    empty_inds = np.where(empty_mask)
    if table.shape[0] > 1024:
        return "⚠️ **ERROR: TabPFN is not made for datasets with a trainingsize > 1024.**", None, None
    if table.shape[1] > 100:
        return "⚠️ **ERROR: TabPFN is not made for datasets with a feature size > 100.**", None, None
    if not len(empty_inds[0]):
        return "⚠️ **ERROR: Please leave at least one field blank for prediction.**", None, None
    if not np.all(empty_inds[1][0] == empty_inds[1]):
        return "⚠️ **Please only leave fields of one column blank for prediction.**", None, None
    y_column = empty_inds[1][0]
    eval_lines = empty_inds[0]

    train_table = np.delete(table, eval_lines, axis=0)
    eval_table = table[eval_lines]

    try:
        x_train = torch.tensor(np.delete(train_table, y_column, axis=1).astype(np.float32))
        x_eval = torch.tensor(np.delete(eval_table, y_column, axis=1).astype(np.float32))

        #y_train = train_table[:, y_column]
        y_train = np.array(train_table[:, y_column].tolist())


    except ValueError:
        return "⚠️ **Please only add numbers (to the inputs) or leave fields empty.**", None, None

    print(y_train)
    print(y_train[:10],y_train[-10:])
    classifier.fit(x_train, y_train)
    y_eval, p_eval = classifier.predict(x_eval, return_winning_probability=True)

    # print(file, type(file))
    out_table = pd.DataFrame(table.copy().astype(str))
    out_table.iloc[eval_lines, y_column] = [f"{y_e} (p={p_e:.2f})" for y_e, p_e in zip(y_eval, p_eval)]
    out_table = out_table.iloc[eval_lines, :]
    out_table.columns = headers
    
    # PLOTTING
    fig = plt.figure(figsize=(10,10))
    ax = fig.add_subplot(111)
    cm = plt.cm.RdBu
    cm_bright = ListedColormap(["#FF0000", "#0000FF"])
    
    # Plot the training points
    vfunc = np.vectorize(lambda x : np.where(classifier.classes_ == x)[0])
    y_train_index = vfunc(y_train)
    y_train_index = y_train_index == 0
    y_train = y_train_index
    #x_train = x_train[y_train_index <= 1]
    #y_train = y_train[y_train_index <= 1]
    #y_train_index = y_train_index[y_train_index <= 1]
    
    ax.scatter(x_train[:, 0], x_train[:, 1], c=y_train_index, cmap=cm_bright)
    
    classifier.fit(x_train[:, 0:2], y_train)
    
    DecisionBoundaryDisplay.from_estimator(
        classifier, x_train[:, 0:2], alpha=0.6, ax=ax, eps=2.0, grid_resolution=25, response_method="predict_proba"
    )
    plt.xlabel(headers[0])
    plt.ylabel(headers[1])

    return "The plot visualizes a predictor based on only two features and for two classes. The tabular results below are based on the full dataset.\nThis demo is running on a CPU only and with 4 ensemble members (32 in the paper).", out_table, fig


def upload_file(file, remove_entries=10):
    if file.name.endswith('.arff'):
        dataset = openml.datasets.OpenMLDataset('t', 'test', data_file=file.name)
        X_, _, categorical_indicator_, attribute_names_ = dataset.get_data(
            dataset_format="array"
        )
        df = pd.DataFrame(X_, columns=attribute_names_)
        headers = np.arange(len(df.columns))
        df.columns = headers
    elif file.name.endswith('.csv') or file.name.endswith('.data'):
        df = pd.read_csv(file.name, header='infer')
        headers = np.arange(len(df.columns))
        df.columns = headers
        
    df.iloc[0:remove_entries, -1] = ''
    return df


def update_table(table):
    vfunc = np.vectorize(lambda s: len(str(s)))
    non_empty_row_mask = (vfunc(table).sum(1) != 0)
    table = table[non_empty_row_mask]
    empty_mask = table == ''
    empty_inds = np.where(empty_mask)
    if not len(empty_inds[0]):
        return table
    
    y_column = empty_inds[1][0]
    eval_lines = empty_inds[0]
    
    table.iloc[eval_lines, y_column] = ''
    
    return table

gr.Markdown("""This demo allows you to experiment with the **TabPFN** model for tabular data.

            If you remove values in the target column, TabPFN will make predictions on them after clicking on the Button. The first 10 target values were already removed for this example dataset, so TabPFN will predict the first 10 classes.
            Please, provide everything but the targets as numeric values and only remove values in one column (the target column).
                """)

with gr.Blocks() as demo:
    with gr.Row():
        with gr.Column():
            inp_table = gr.DataFrame(type='pandas', value=upload_file(Path('iris.csv'), remove_entries=10)
                                     , headers=[''] * 5)
            
            inp_file = gr.File(
            label='Drop either a .csv (without header, only numeric values for all but the labels) or a .arff file.')
            
            examples = gr.Examples(examples=['iris.csv', 'balance-scale.arff'],
                           inputs=[inp_file],
                           outputs=[inp_table],
                           fn=upload_file,
                           cache_examples=True)
            
            #inp_table.change(fn=update_table, inputs=inp_table, outputs=inp_table)
    
        with gr.Column():

            btn = gr.Button("Calculate Predictions")
            out_text = gr.Markdown()
            out_plot = gr.Plot(type="Matplotlib")
            out_table = gr.DataFrame()
    
    btn.click(fn=compute, inputs=inp_table, outputs=[out_text, out_table, out_plot])

    inp_file.change(fn=upload_file, inputs=inp_file, outputs=inp_table)

demo.launch()