Spaces:
Running
Running
Commit
•
8f809e2
1
Parent(s):
be473e6
GSK-2434 Add component to show logs (#17)
Browse files- update log area (f04482da801bfe6bf8c4f1fa92fab5677aa14158)
- fix pipe io bug (e631fcc66670d557b9e8f885528a392cd01188fd)
- clean code (748c85beba7853d456e65a347bb5eb03bafbe7cc)
- remove pipe| (b0a573f944ae019c75a4cde2876fa17b10b41add)
- fix run job logs (64f50dd45d73b4615e83790e7bf6c1e9c28a346f)
- add every for logs textbox (aaa034c2aae0d0d16ce112cced026c3d0fe04104)
- refresh log files not working (89d01cfcb69c644aa39afcbbcbb88d27c337ef9d)
- show log with textbox value (f227810f647cd8c0c849bd0338f289422971772f)
- fix log refresh (7f4008b268536b4f60fc0b9b57ad9fe5331b786a)
Co-authored-by: zcy <ZeroCommand@users.noreply.huggingface.co>
- app.py +22 -11
- app_leaderboard.py +3 -4
- app_text_classification.py +46 -181
- fetch_utils.py +0 -1
- io_utils.py +59 -2
- run_jobs.py +29 -0
- text_classification_ui_helpers.py +181 -0
- tmp/pipe +0 -0
- wordings.py +1 -1
app.py
CHANGED
@@ -1,17 +1,28 @@
|
|
1 |
|
2 |
-
# Start apps
|
3 |
-
# from pathlib import Path
|
4 |
-
|
5 |
import gradio as gr
|
6 |
-
|
7 |
from app_text_classification import get_demo as get_demo_text_classification
|
8 |
from app_leaderboard import get_demo as get_demo_leaderboard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
-
with gr.Blocks(theme=gr.themes.Soft(primary_hue="green")) as demo:
|
11 |
-
with gr.Tab("Text Classification"):
|
12 |
-
get_demo_text_classification()
|
13 |
-
with gr.Tab("Leaderboard"):
|
14 |
-
get_demo_leaderboard()
|
15 |
|
16 |
-
demo.queue(max_size=100)
|
17 |
-
demo.launch(share=False)
|
|
|
1 |
|
|
|
|
|
|
|
2 |
import gradio as gr
|
3 |
+
import atexit
|
4 |
from app_text_classification import get_demo as get_demo_text_classification
|
5 |
from app_leaderboard import get_demo as get_demo_leaderboard
|
6 |
+
from run_jobs import start_process_run_job, stop_thread
|
7 |
+
import threading
|
8 |
+
|
9 |
+
if threading.current_thread() is not threading.main_thread():
|
10 |
+
t = threading.current_thread()
|
11 |
+
try:
|
12 |
+
with gr.Blocks(theme=gr.themes.Soft(primary_hue="green")) as demo:
|
13 |
+
with gr.Tab("Text Classification"):
|
14 |
+
get_demo_text_classification(demo)
|
15 |
+
with gr.Tab("Leaderboard"):
|
16 |
+
get_demo_leaderboard()
|
17 |
+
|
18 |
+
start_process_run_job()
|
19 |
+
|
20 |
+
demo.queue(max_size=100)
|
21 |
+
demo.launch(share=False)
|
22 |
+
atexit.register(stop_thread)
|
23 |
+
|
24 |
+
except Exception:
|
25 |
+
print("stop background thread")
|
26 |
+
stop_thread()
|
27 |
|
|
|
|
|
|
|
|
|
|
|
28 |
|
|
|
|
app_leaderboard.py
CHANGED
@@ -32,7 +32,7 @@ def get_dataset_ids(ds):
|
|
32 |
return dataset_ids
|
33 |
|
34 |
def get_types(ds):
|
35 |
-
# set
|
36 |
types = [str(t) for t in ds.dtypes.to_list()]
|
37 |
types = [t.replace('object', 'markdown') for t in types]
|
38 |
types = [t.replace('float64', 'number') for t in types]
|
@@ -61,10 +61,9 @@ def get_demo():
|
|
61 |
|
62 |
column_names = records.columns.tolist()
|
63 |
default_columns = ['model_id', 'dataset_id', 'total_issues', 'report_link']
|
64 |
-
|
65 |
-
default_df = records[default_columns]
|
66 |
types = get_types(default_df)
|
67 |
-
display_df = get_display_df(default_df)
|
68 |
|
69 |
with gr.Row():
|
70 |
task_select = gr.Dropdown(label='Task', choices=['text_classification', 'tabular'], value='text_classification', interactive=True)
|
|
|
32 |
return dataset_ids
|
33 |
|
34 |
def get_types(ds):
|
35 |
+
# set types for each column
|
36 |
types = [str(t) for t in ds.dtypes.to_list()]
|
37 |
types = [t.replace('object', 'markdown') for t in types]
|
38 |
types = [t.replace('float64', 'number') for t in types]
|
|
|
61 |
|
62 |
column_names = records.columns.tolist()
|
63 |
default_columns = ['model_id', 'dataset_id', 'total_issues', 'report_link']
|
64 |
+
default_df = records[default_columns] # extract columns selected
|
|
|
65 |
types = get_types(default_df)
|
66 |
+
display_df = get_display_df(default_df) # the styled dataframe to display
|
67 |
|
68 |
with gr.Row():
|
69 |
task_select = gr.Dropdown(label='Task', choices=['text_classification', 'tabular'], value='text_classification', interactive=True)
|
app_text_classification.py
CHANGED
@@ -1,22 +1,8 @@
|
|
1 |
import gradio as gr
|
2 |
-
import
|
3 |
-
import
|
4 |
-
import
|
5 |
-
import
|
6 |
-
import logging
|
7 |
-
import collections
|
8 |
-
|
9 |
-
import json
|
10 |
-
|
11 |
-
from transformers.pipelines import TextClassificationPipeline
|
12 |
-
|
13 |
-
from text_classification import get_labels_and_features_from_dataset, check_model, get_example_prediction
|
14 |
-
from io_utils import read_scanners, write_scanners, read_inference_type, read_column_mapping, write_column_mapping, write_inference_type
|
15 |
-
from wordings import INTRODUCTION_MD, CONFIRM_MAPPING_DETAILS_MD, CONFIRM_MAPPING_DETAILS_FAIL_RAW
|
16 |
-
|
17 |
-
HF_REPO_ID = 'HF_REPO_ID'
|
18 |
-
HF_SPACE_ID = 'SPACE_ID'
|
19 |
-
HF_WRITE_TOKEN = 'HF_WRITE_TOKEN'
|
20 |
|
21 |
MAX_LABELS = 20
|
22 |
MAX_FEATURES = 20
|
@@ -25,76 +11,7 @@ EXAMPLE_MODEL_ID = 'cardiffnlp/twitter-roberta-base-sentiment-latest'
|
|
25 |
EXAMPLE_DATA_ID = 'tweet_eval'
|
26 |
CONFIG_PATH='./config.yaml'
|
27 |
|
28 |
-
def
|
29 |
-
all_mappings = read_column_mapping(CONFIG_PATH)
|
30 |
-
|
31 |
-
if "labels" not in all_mappings.keys():
|
32 |
-
gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
33 |
-
return gr.update(interactive=True)
|
34 |
-
label_mapping = all_mappings["labels"]
|
35 |
-
|
36 |
-
if "features" not in all_mappings.keys():
|
37 |
-
gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
38 |
-
return gr.update(interactive=True)
|
39 |
-
feature_mapping = all_mappings["features"]
|
40 |
-
|
41 |
-
# TODO: Set column mapping for some dataset such as `amazon_polarity`
|
42 |
-
if local:
|
43 |
-
command = [
|
44 |
-
"python",
|
45 |
-
"cli.py",
|
46 |
-
"--loader", "huggingface",
|
47 |
-
"--model", m_id,
|
48 |
-
"--dataset", d_id,
|
49 |
-
"--dataset_config", config,
|
50 |
-
"--dataset_split", split,
|
51 |
-
"--hf_token", os.environ.get(HF_WRITE_TOKEN),
|
52 |
-
"--discussion_repo", os.environ.get(HF_REPO_ID) or os.environ.get(HF_SPACE_ID),
|
53 |
-
"--output_format", "markdown",
|
54 |
-
"--output_portal", "huggingface",
|
55 |
-
"--feature_mapping", json.dumps(feature_mapping),
|
56 |
-
"--label_mapping", json.dumps(label_mapping),
|
57 |
-
"--scan_config", "../config.yaml",
|
58 |
-
]
|
59 |
-
|
60 |
-
eval_str = f"[{m_id}]<{d_id}({config}, {split} set)>"
|
61 |
-
start = time.time()
|
62 |
-
logging.info(f"Start local evaluation on {eval_str}")
|
63 |
-
|
64 |
-
evaluator = subprocess.Popen(
|
65 |
-
command,
|
66 |
-
cwd=os.path.join(os.path.dirname(os.path.realpath(__file__)), "cicd"),
|
67 |
-
stderr=subprocess.STDOUT,
|
68 |
-
)
|
69 |
-
result = evaluator.wait()
|
70 |
-
|
71 |
-
logging.info(f"Finished local evaluation exit code {result} on {eval_str}: {time.time() - start:.2f}s")
|
72 |
-
|
73 |
-
gr.Info(f"Finished local evaluation exit code {result} on {eval_str}: {time.time() - start:.2f}s")
|
74 |
-
else:
|
75 |
-
gr.Info("TODO: Submit task to an endpoint")
|
76 |
-
|
77 |
-
return gr.update(interactive=True) # Submit button
|
78 |
-
|
79 |
-
|
80 |
-
def check_dataset_and_get_config(dataset_id):
|
81 |
-
try:
|
82 |
-
configs = datasets.get_dataset_config_names(dataset_id)
|
83 |
-
return gr.Dropdown(configs, value=configs[0], visible=True)
|
84 |
-
except Exception:
|
85 |
-
# Dataset may not exist
|
86 |
-
pass
|
87 |
-
|
88 |
-
def check_dataset_and_get_split(dataset_id, dataset_config):
|
89 |
-
try:
|
90 |
-
splits = list(datasets.load_dataset(dataset_id, dataset_config).keys())
|
91 |
-
return gr.Dropdown(splits, value=splits[0], visible=True)
|
92 |
-
except Exception:
|
93 |
-
# Dataset may not exist
|
94 |
-
# gr.Warning(f"Failed to load dataset {dataset_id} with config {dataset_config}: {e}")
|
95 |
-
pass
|
96 |
-
|
97 |
-
def get_demo():
|
98 |
with gr.Row():
|
99 |
gr.Markdown(INTRODUCTION_MD)
|
100 |
with gr.Row():
|
@@ -137,6 +54,9 @@ def get_demo():
|
|
137 |
|
138 |
with gr.Accordion(label='Scanner Advance Config (optional)', open=False):
|
139 |
selected = read_scanners('./config.yaml')
|
|
|
|
|
|
|
140 |
scan_config = selected + ['data_leakage']
|
141 |
scanners = gr.CheckboxGroup(choices=scan_config, value=selected, label='Scan Settings', visible=True)
|
142 |
|
@@ -147,102 +67,21 @@ def get_demo():
|
|
147 |
interactive=True,
|
148 |
size="lg",
|
149 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
ds_labels, ds_features = get_labels_and_features_from_dataset(dataset_id, dataset_config, dataset_split)
|
155 |
-
if labels is None:
|
156 |
-
return
|
157 |
-
labels = [*labels]
|
158 |
-
all_mappings = read_column_mapping(CONFIG_PATH)
|
159 |
-
|
160 |
-
if "labels" not in all_mappings.keys():
|
161 |
-
all_mappings["labels"] = dict()
|
162 |
-
for i, label in enumerate(labels[:MAX_LABELS]):
|
163 |
-
if label:
|
164 |
-
all_mappings["labels"][label] = ds_labels[i]
|
165 |
-
|
166 |
-
if "features" not in all_mappings.keys():
|
167 |
-
all_mappings["features"] = dict()
|
168 |
-
for i, feat in enumerate(labels[MAX_LABELS:(MAX_LABELS + MAX_FEATURES)]):
|
169 |
-
if feat:
|
170 |
-
all_mappings["features"][feat] = ds_features[i]
|
171 |
-
write_column_mapping(all_mappings)
|
172 |
|
173 |
-
|
174 |
-
|
175 |
-
lables = [gr.Dropdown(label=f"{label}", choices=model_labels, value=model_id2label[i], interactive=True, visible=True) for i, label in enumerate(ds_labels[:MAX_LABELS])]
|
176 |
-
lables += [gr.Dropdown(visible=False) for _ in range(MAX_LABELS - len(lables))]
|
177 |
-
# TODO: Substitute 'text' with more features for zero-shot
|
178 |
-
features = [gr.Dropdown(label=f"{feature}", choices=ds_features, value=ds_features[0], interactive=True, visible=True) for feature in ['text']]
|
179 |
-
features += [gr.Dropdown(visible=False) for _ in range(MAX_FEATURES - len(features))]
|
180 |
-
return lables + features
|
181 |
-
|
182 |
-
@gr.on(triggers=[model_id_input.change, dataset_config_input.change])
|
183 |
-
def clear_column_mapping_config():
|
184 |
-
write_column_mapping(None)
|
185 |
-
|
186 |
-
@gr.on(triggers=[model_id_input.change, dataset_config_input.change, dataset_split_input.change],
|
187 |
inputs=[model_id_input, dataset_id_input, dataset_config_input, dataset_split_input],
|
188 |
outputs=[example_input, example_prediction, column_mapping_accordion, *column_mappings])
|
189 |
-
def check_model_and_show_prediction(model_id, dataset_id, dataset_config, dataset_split):
|
190 |
-
ppl = check_model(model_id)
|
191 |
-
if ppl is None or not isinstance(ppl, TextClassificationPipeline):
|
192 |
-
gr.Warning("Please check your model.")
|
193 |
-
return (
|
194 |
-
gr.update(visible=False),
|
195 |
-
gr.update(visible=False),
|
196 |
-
*[gr.update(visible=False) for _ in range(MAX_LABELS + MAX_FEATURES)]
|
197 |
-
)
|
198 |
-
|
199 |
-
dropdown_placement = [gr.Dropdown(visible=False) for _ in range(MAX_LABELS + MAX_FEATURES)]
|
200 |
-
|
201 |
-
if ppl is None: # pipeline not found
|
202 |
-
gr.Warning("Model not found")
|
203 |
-
return (
|
204 |
-
gr.update(visible=False),
|
205 |
-
gr.update(visible=False),
|
206 |
-
gr.update(visible=False, open=False),
|
207 |
-
*dropdown_placement
|
208 |
-
)
|
209 |
-
model_id2label = ppl.model.config.id2label
|
210 |
-
ds_labels, ds_features = get_labels_and_features_from_dataset(dataset_id, dataset_config, dataset_split)
|
211 |
-
|
212 |
-
# when dataset does not have labels or features
|
213 |
-
if not isinstance(ds_labels, list) or not isinstance(ds_features, list):
|
214 |
-
gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
215 |
-
return (
|
216 |
-
gr.update(visible=False),
|
217 |
-
gr.update(visible=False),
|
218 |
-
gr.update(visible=False, open=False),
|
219 |
-
*dropdown_placement
|
220 |
-
)
|
221 |
-
|
222 |
-
column_mappings = list_labels_and_features_from_dataset(
|
223 |
-
ds_labels,
|
224 |
-
ds_features,
|
225 |
-
model_id2label,
|
226 |
-
)
|
227 |
-
|
228 |
-
# when labels or features are not aligned
|
229 |
-
# show manually column mapping
|
230 |
-
if collections.Counter(model_id2label.items()) != collections.Counter(ds_labels) or ds_features[0] != 'text':
|
231 |
-
gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
232 |
-
return (
|
233 |
-
gr.update(visible=False),
|
234 |
-
gr.update(visible=False),
|
235 |
-
gr.update(visible=True, open=True),
|
236 |
-
*column_mappings
|
237 |
-
)
|
238 |
-
|
239 |
-
prediction_input, prediction_output = get_example_prediction(ppl, dataset_id, dataset_config, dataset_split)
|
240 |
-
return (
|
241 |
-
gr.update(value=prediction_input, visible=True),
|
242 |
-
gr.update(value=prediction_output, visible=True),
|
243 |
-
gr.update(visible=True, open=False),
|
244 |
-
*column_mappings
|
245 |
-
)
|
246 |
|
247 |
dataset_id_input.blur(check_dataset_and_get_config, dataset_id_input, dataset_config_input)
|
248 |
|
@@ -266,5 +105,31 @@ def get_demo():
|
|
266 |
run_btn.click,
|
267 |
],
|
268 |
fn=try_submit,
|
269 |
-
inputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
270 |
outputs=[run_btn])
|
|
|
1 |
import gradio as gr
|
2 |
+
import uuid
|
3 |
+
from io_utils import read_scanners, write_scanners, read_inference_type, write_inference_type, get_logs_file
|
4 |
+
from wordings import INTRODUCTION_MD, CONFIRM_MAPPING_DETAILS_MD
|
5 |
+
from text_classification_ui_helpers import try_submit, check_dataset_and_get_config, check_dataset_and_get_split, check_model_and_show_prediction, write_column_mapping_to_config, get_logs_file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
7 |
MAX_LABELS = 20
|
8 |
MAX_FEATURES = 20
|
|
|
11 |
EXAMPLE_DATA_ID = 'tweet_eval'
|
12 |
CONFIG_PATH='./config.yaml'
|
13 |
|
14 |
+
def get_demo(demo):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
with gr.Row():
|
16 |
gr.Markdown(INTRODUCTION_MD)
|
17 |
with gr.Row():
|
|
|
54 |
|
55 |
with gr.Accordion(label='Scanner Advance Config (optional)', open=False):
|
56 |
selected = read_scanners('./config.yaml')
|
57 |
+
# currently we remove data_leakage from the default scanners
|
58 |
+
# Reason: data_leakage barely raises any issues and takes too many requests
|
59 |
+
# when using inference API, causing rate limit error
|
60 |
scan_config = selected + ['data_leakage']
|
61 |
scanners = gr.CheckboxGroup(choices=scan_config, value=selected, label='Scan Settings', visible=True)
|
62 |
|
|
|
67 |
interactive=True,
|
68 |
size="lg",
|
69 |
)
|
70 |
+
|
71 |
+
with gr.Row():
|
72 |
+
uid = uuid.uuid4()
|
73 |
+
uid_label = gr.Textbox(label="Evaluation ID:", value=uid, visible=False, interactive=False)
|
74 |
+
logs = gr.Textbox(label="Giskard Bot Evaluation Log:", visible=False)
|
75 |
+
demo.load(get_logs_file, uid_label, logs, every=0.5)
|
76 |
|
77 |
+
gr.on(triggers=[label.change for label in column_mappings],
|
78 |
+
fn=write_column_mapping_to_config,
|
79 |
+
inputs=[dataset_id_input, dataset_config_input, dataset_split_input, *column_mappings])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
|
81 |
+
gr.on(triggers=[model_id_input.change, dataset_config_input.change, dataset_split_input.change],
|
82 |
+
fn=check_model_and_show_prediction,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
inputs=[model_id_input, dataset_id_input, dataset_config_input, dataset_split_input],
|
84 |
outputs=[example_input, example_prediction, column_mapping_accordion, *column_mappings])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
86 |
dataset_id_input.blur(check_dataset_and_get_config, dataset_id_input, dataset_config_input)
|
87 |
|
|
|
105 |
run_btn.click,
|
106 |
],
|
107 |
fn=try_submit,
|
108 |
+
inputs=[
|
109 |
+
model_id_input,
|
110 |
+
dataset_id_input,
|
111 |
+
dataset_config_input,
|
112 |
+
dataset_split_input,
|
113 |
+
run_local,
|
114 |
+
uid_label],
|
115 |
+
outputs=[run_btn, logs])
|
116 |
+
|
117 |
+
def enable_run_btn():
|
118 |
+
return (gr.update(interactive=True))
|
119 |
+
gr.on(
|
120 |
+
triggers=[
|
121 |
+
model_id_input.change,
|
122 |
+
dataset_config_input.change,
|
123 |
+
dataset_split_input.change,
|
124 |
+
run_inference.change,
|
125 |
+
run_local.change,
|
126 |
+
scanners.change],
|
127 |
+
fn=enable_run_btn,
|
128 |
+
inputs=None,
|
129 |
+
outputs=[run_btn])
|
130 |
+
|
131 |
+
gr.on(
|
132 |
+
triggers=[label.change for label in column_mappings],
|
133 |
+
fn=enable_run_btn,
|
134 |
+
inputs=None,
|
135 |
outputs=[run_btn])
|
fetch_utils.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
import huggingface_hub
|
2 |
import datasets
|
3 |
import logging
|
4 |
|
|
|
|
|
1 |
import datasets
|
2 |
import logging
|
3 |
|
io_utils.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1 |
import yaml
|
|
|
|
|
2 |
|
3 |
YAML_PATH = "./config.yaml"
|
|
|
4 |
|
5 |
class Dumper(yaml.Dumper):
|
6 |
def increase_indent(self, flow=False, *args, **kwargs):
|
@@ -49,14 +52,17 @@ def read_column_mapping(path):
|
|
49 |
column_mapping = {}
|
50 |
with open(path, "r") as f:
|
51 |
config = yaml.load(f, Loader=yaml.FullLoader)
|
52 |
-
|
|
|
53 |
return column_mapping
|
54 |
|
55 |
# write column mapping to yaml file
|
56 |
def write_column_mapping(mapping):
|
57 |
with open(YAML_PATH, "r") as f:
|
58 |
config = yaml.load(f, Loader=yaml.FullLoader)
|
59 |
-
if
|
|
|
|
|
60 |
del config["column_mapping"]
|
61 |
else:
|
62 |
config["column_mapping"] = mapping
|
@@ -71,3 +77,54 @@ def convert_column_mapping_to_json(df, label=""):
|
|
71 |
for _, row in df.iterrows():
|
72 |
column_mapping[label].append(row.tolist())
|
73 |
return column_mapping
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import yaml
|
2 |
+
import subprocess
|
3 |
+
import os
|
4 |
|
5 |
YAML_PATH = "./config.yaml"
|
6 |
+
PIPE_PATH = "./tmp/pipe"
|
7 |
|
8 |
class Dumper(yaml.Dumper):
|
9 |
def increase_indent(self, flow=False, *args, **kwargs):
|
|
|
52 |
column_mapping = {}
|
53 |
with open(path, "r") as f:
|
54 |
config = yaml.load(f, Loader=yaml.FullLoader)
|
55 |
+
if config:
|
56 |
+
column_mapping = config.get("column_mapping", dict())
|
57 |
return column_mapping
|
58 |
|
59 |
# write column mapping to yaml file
|
60 |
def write_column_mapping(mapping):
|
61 |
with open(YAML_PATH, "r") as f:
|
62 |
config = yaml.load(f, Loader=yaml.FullLoader)
|
63 |
+
if config is None:
|
64 |
+
return
|
65 |
+
if mapping is None and "column_mapping" in config.keys():
|
66 |
del config["column_mapping"]
|
67 |
else:
|
68 |
config["column_mapping"] = mapping
|
|
|
77 |
for _, row in df.iterrows():
|
78 |
column_mapping[label].append(row.tolist())
|
79 |
return column_mapping
|
80 |
+
|
81 |
+
def get_logs_file(uid):
|
82 |
+
try:
|
83 |
+
file = open(f"./tmp/{uid}_log", "r")
|
84 |
+
return file.read()
|
85 |
+
except Exception:
|
86 |
+
return "Log file does not exist"
|
87 |
+
|
88 |
+
def write_log_to_user_file(id, log):
|
89 |
+
with open(f"./tmp/{id}_log", "a") as f:
|
90 |
+
f.write(log)
|
91 |
+
|
92 |
+
def save_job_to_pipe(id, job, lock):
|
93 |
+
if not os.path.exists('./tmp'):
|
94 |
+
os.makedirs('./tmp')
|
95 |
+
job = [str(i) for i in job]
|
96 |
+
job = ",".join(job)
|
97 |
+
print(job)
|
98 |
+
with lock:
|
99 |
+
with open(PIPE_PATH, "a") as f:
|
100 |
+
# write each element in job
|
101 |
+
f.write(f'{id}@{job}\n')
|
102 |
+
|
103 |
+
def pop_job_from_pipe():
|
104 |
+
if not os.path.exists(PIPE_PATH):
|
105 |
+
return
|
106 |
+
with open(PIPE_PATH, "r") as f:
|
107 |
+
job = f.readline().strip()
|
108 |
+
remaining = f.readlines()
|
109 |
+
f.close()
|
110 |
+
print(job, remaining, ">>>>")
|
111 |
+
with open(PIPE_PATH, "w") as f:
|
112 |
+
f.write("\n".join(remaining))
|
113 |
+
f.close()
|
114 |
+
if len(job) == 0:
|
115 |
+
return
|
116 |
+
job_info = job.split('\n')[0].split("@")
|
117 |
+
if len(job_info) != 2:
|
118 |
+
raise ValueError("Invalid job info: ", job_info)
|
119 |
+
|
120 |
+
write_log_to_user_file(job_info[0], f"Running job {job_info}")
|
121 |
+
command = job_info[1].split(",")
|
122 |
+
|
123 |
+
write_log_to_user_file(job_info[0], f"Running command {command}")
|
124 |
+
log_file = open(f"./tmp/{job_info[0]}_log", "a")
|
125 |
+
subprocess.Popen(
|
126 |
+
command,
|
127 |
+
cwd=os.path.join(os.path.dirname(os.path.realpath(__file__)), "cicd"),
|
128 |
+
stdout=log_file,
|
129 |
+
stderr=log_file,
|
130 |
+
)
|
run_jobs.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from io_utils import pop_job_from_pipe
|
2 |
+
import time
|
3 |
+
import threading
|
4 |
+
|
5 |
+
def start_process_run_job():
|
6 |
+
try:
|
7 |
+
print("Running jobs in thread")
|
8 |
+
global thread
|
9 |
+
thread = threading.Thread(target=run_job)
|
10 |
+
thread.daemon = True
|
11 |
+
thread.do_run = True
|
12 |
+
thread.start()
|
13 |
+
|
14 |
+
except Exception as e:
|
15 |
+
print("Failed to start thread: ", e)
|
16 |
+
def stop_thread():
|
17 |
+
print("Stop thread")
|
18 |
+
thread.do_run = False
|
19 |
+
|
20 |
+
def run_job():
|
21 |
+
while True:
|
22 |
+
print(thread.do_run)
|
23 |
+
try:
|
24 |
+
pop_job_from_pipe()
|
25 |
+
time.sleep(10)
|
26 |
+
except KeyboardInterrupt:
|
27 |
+
print("KeyboardInterrupt stop background thread")
|
28 |
+
stop_thread()
|
29 |
+
break
|
text_classification_ui_helpers.py
ADDED
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from wordings import CONFIRM_MAPPING_DETAILS_FAIL_RAW
|
3 |
+
import json
|
4 |
+
import os
|
5 |
+
import logging
|
6 |
+
import threading
|
7 |
+
from io_utils import read_column_mapping, write_column_mapping, save_job_to_pipe, write_log_to_user_file
|
8 |
+
import datasets
|
9 |
+
import collections
|
10 |
+
from text_classification import get_labels_and_features_from_dataset, check_model, get_example_prediction
|
11 |
+
from transformers.pipelines import TextClassificationPipeline
|
12 |
+
|
13 |
+
MAX_LABELS = 20
|
14 |
+
MAX_FEATURES = 20
|
15 |
+
|
16 |
+
HF_REPO_ID = 'HF_REPO_ID'
|
17 |
+
HF_SPACE_ID = 'SPACE_ID'
|
18 |
+
HF_WRITE_TOKEN = 'HF_WRITE_TOKEN'
|
19 |
+
CONFIG_PATH = "./config.yaml"
|
20 |
+
|
21 |
+
def check_dataset_and_get_config(dataset_id):
|
22 |
+
try:
|
23 |
+
write_column_mapping(None)
|
24 |
+
configs = datasets.get_dataset_config_names(dataset_id)
|
25 |
+
return gr.Dropdown(configs, value=configs[0], visible=True)
|
26 |
+
except Exception:
|
27 |
+
# Dataset may not exist
|
28 |
+
pass
|
29 |
+
|
30 |
+
def check_dataset_and_get_split(dataset_id, dataset_config):
|
31 |
+
try:
|
32 |
+
splits = list(datasets.load_dataset(dataset_id, dataset_config).keys())
|
33 |
+
return gr.Dropdown(splits, value=splits[0], visible=True)
|
34 |
+
except Exception:
|
35 |
+
# Dataset may not exist
|
36 |
+
# gr.Warning(f"Failed to load dataset {dataset_id} with config {dataset_config}: {e}")
|
37 |
+
pass
|
38 |
+
|
39 |
+
def write_column_mapping_to_config(dataset_id, dataset_config, dataset_split, *labels):
|
40 |
+
ds_labels, ds_features = get_labels_and_features_from_dataset(dataset_id, dataset_config, dataset_split)
|
41 |
+
if labels is None:
|
42 |
+
return
|
43 |
+
labels = [*labels]
|
44 |
+
all_mappings = read_column_mapping(CONFIG_PATH)
|
45 |
+
|
46 |
+
if all_mappings is None:
|
47 |
+
all_mappings = dict()
|
48 |
+
|
49 |
+
if "labels" not in all_mappings.keys():
|
50 |
+
all_mappings["labels"] = dict()
|
51 |
+
for i, label in enumerate(labels[:MAX_LABELS]):
|
52 |
+
if label:
|
53 |
+
all_mappings["labels"][label] = ds_labels[i]
|
54 |
+
|
55 |
+
if "features" not in all_mappings.keys():
|
56 |
+
all_mappings["features"] = dict()
|
57 |
+
for i, feat in enumerate(labels[MAX_LABELS:(MAX_LABELS + MAX_FEATURES)]):
|
58 |
+
if feat:
|
59 |
+
all_mappings["features"][feat] = ds_features[i]
|
60 |
+
write_column_mapping(all_mappings)
|
61 |
+
|
62 |
+
def list_labels_and_features_from_dataset(ds_labels, ds_features, model_id2label):
|
63 |
+
model_labels = list(model_id2label.values())
|
64 |
+
len_model_labels = len(model_labels)
|
65 |
+
print(model_labels, model_id2label, 3%len_model_labels)
|
66 |
+
lables = [gr.Dropdown(label=f"{label}", choices=model_labels, value=model_id2label[i%len_model_labels], interactive=True, visible=True) for i, label in enumerate(ds_labels[:MAX_LABELS])]
|
67 |
+
lables += [gr.Dropdown(visible=False) for _ in range(MAX_LABELS - len(lables))]
|
68 |
+
# TODO: Substitute 'text' with more features for zero-shot
|
69 |
+
features = [gr.Dropdown(label=f"{feature}", choices=ds_features, value=ds_features[0], interactive=True, visible=True) for feature in ['text']]
|
70 |
+
features += [gr.Dropdown(visible=False) for _ in range(MAX_FEATURES - len(features))]
|
71 |
+
return lables + features
|
72 |
+
|
73 |
+
def check_model_and_show_prediction(model_id, dataset_id, dataset_config, dataset_split):
|
74 |
+
ppl = check_model(model_id)
|
75 |
+
if ppl is None or not isinstance(ppl, TextClassificationPipeline):
|
76 |
+
gr.Warning("Please check your model.")
|
77 |
+
return (
|
78 |
+
gr.update(visible=False),
|
79 |
+
gr.update(visible=False),
|
80 |
+
*[gr.update(visible=False) for _ in range(MAX_LABELS + MAX_FEATURES)]
|
81 |
+
)
|
82 |
+
|
83 |
+
dropdown_placement = [gr.Dropdown(visible=False) for _ in range(MAX_LABELS + MAX_FEATURES)]
|
84 |
+
|
85 |
+
if ppl is None: # pipeline not found
|
86 |
+
gr.Warning("Model not found")
|
87 |
+
return (
|
88 |
+
gr.update(visible=False),
|
89 |
+
gr.update(visible=False),
|
90 |
+
gr.update(visible=False, open=False),
|
91 |
+
*dropdown_placement
|
92 |
+
)
|
93 |
+
model_id2label = ppl.model.config.id2label
|
94 |
+
ds_labels, ds_features = get_labels_and_features_from_dataset(dataset_id, dataset_config, dataset_split)
|
95 |
+
|
96 |
+
# when dataset does not have labels or features
|
97 |
+
if not isinstance(ds_labels, list) or not isinstance(ds_features, list):
|
98 |
+
# gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
99 |
+
return (
|
100 |
+
gr.update(visible=False),
|
101 |
+
gr.update(visible=False),
|
102 |
+
gr.update(visible=False, open=False),
|
103 |
+
*dropdown_placement
|
104 |
+
)
|
105 |
+
|
106 |
+
column_mappings = list_labels_and_features_from_dataset(
|
107 |
+
ds_labels,
|
108 |
+
ds_features,
|
109 |
+
model_id2label,
|
110 |
+
)
|
111 |
+
|
112 |
+
# when labels or features are not aligned
|
113 |
+
# show manually column mapping
|
114 |
+
if collections.Counter(model_id2label.values()) != collections.Counter(ds_labels) or ds_features[0] != 'text':
|
115 |
+
gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
116 |
+
return (
|
117 |
+
gr.update(visible=False),
|
118 |
+
gr.update(visible=False),
|
119 |
+
gr.update(visible=True, open=True),
|
120 |
+
*column_mappings
|
121 |
+
)
|
122 |
+
|
123 |
+
prediction_input, prediction_output = get_example_prediction(ppl, dataset_id, dataset_config, dataset_split)
|
124 |
+
return (
|
125 |
+
gr.update(value=prediction_input, visible=True),
|
126 |
+
gr.update(value=prediction_output, visible=True),
|
127 |
+
gr.update(visible=True, open=False),
|
128 |
+
*column_mappings
|
129 |
+
)
|
130 |
+
|
131 |
+
def try_submit(m_id, d_id, config, split, local, uid):
|
132 |
+
all_mappings = read_column_mapping(CONFIG_PATH)
|
133 |
+
|
134 |
+
if all_mappings is None:
|
135 |
+
gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
136 |
+
return (gr.update(interactive=True), gr.update(visible=False))
|
137 |
+
|
138 |
+
if "labels" not in all_mappings.keys():
|
139 |
+
gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
140 |
+
return (gr.update(interactive=True), gr.update(visible=False))
|
141 |
+
label_mapping = all_mappings["labels"]
|
142 |
+
|
143 |
+
if "features" not in all_mappings.keys():
|
144 |
+
gr.Warning(CONFIRM_MAPPING_DETAILS_FAIL_RAW)
|
145 |
+
return (gr.update(interactive=True), gr.update(visible=False))
|
146 |
+
feature_mapping = all_mappings["features"]
|
147 |
+
|
148 |
+
# TODO: Set column mapping for some dataset such as `amazon_polarity`
|
149 |
+
if local:
|
150 |
+
command = [
|
151 |
+
"python",
|
152 |
+
"cli.py",
|
153 |
+
"--loader", "huggingface",
|
154 |
+
"--model", m_id,
|
155 |
+
"--dataset", d_id,
|
156 |
+
"--dataset_config", config,
|
157 |
+
"--dataset_split", split,
|
158 |
+
"--hf_token", os.environ.get(HF_WRITE_TOKEN),
|
159 |
+
"--discussion_repo", os.environ.get(HF_REPO_ID) or os.environ.get(HF_SPACE_ID),
|
160 |
+
"--output_format", "markdown",
|
161 |
+
"--output_portal", "huggingface",
|
162 |
+
"--feature_mapping", json.dumps(feature_mapping),
|
163 |
+
"--label_mapping", json.dumps(label_mapping),
|
164 |
+
"--scan_config", "../config.yaml",
|
165 |
+
]
|
166 |
+
|
167 |
+
eval_str = f"[{m_id}]<{d_id}({config}, {split} set)>"
|
168 |
+
logging.info(f"Start local evaluation on {eval_str}")
|
169 |
+
save_job_to_pipe(uid, command, threading.Lock())
|
170 |
+
write_log_to_user_file(uid, f"Start local evaluation on {eval_str}. Please wait for your job to start...\n")
|
171 |
+
gr.Info(f"Start local evaluation on {eval_str}")
|
172 |
+
|
173 |
+
return (
|
174 |
+
gr.update(interactive=False),
|
175 |
+
gr.update(lines=5, visible=True, interactive=False))
|
176 |
+
|
177 |
+
else:
|
178 |
+
gr.Info("TODO: Submit task to an endpoint")
|
179 |
+
|
180 |
+
return (gr.update(interactive=True), # Submit button
|
181 |
+
gr.update(visible=False))
|
tmp/pipe
ADDED
File without changes
|
wordings.py
CHANGED
@@ -8,7 +8,7 @@ CONFIRM_MAPPING_DETAILS_MD = '''
|
|
8 |
<h1 style="text-align: center;">
|
9 |
Confirm Pre-processing Details
|
10 |
</h1>
|
11 |
-
Please confirm the pre-processing details below. If you are not sure, please double check your model and dataset.
|
12 |
'''
|
13 |
CONFIRM_MAPPING_DETAILS_FAIL_MD = '''
|
14 |
<h1 style="text-align: center;">
|
|
|
8 |
<h1 style="text-align: center;">
|
9 |
Confirm Pre-processing Details
|
10 |
</h1>
|
11 |
+
Please confirm the pre-processing details below. Align the column names of your model in the <b>dropdown</b> menu to your dataset's. If you are not sure, please double check your model and dataset.
|
12 |
'''
|
13 |
CONFIRM_MAPPING_DETAILS_FAIL_MD = '''
|
14 |
<h1 style="text-align: center;">
|