inoki-giskard ZeroCommand commited on
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 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 all 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,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
- # set the default columns to show
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 datasets
3
- import os
4
- import time
5
- import subprocess
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 try_submit(m_id, d_id, config, split, local):
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
- @gr.on(triggers=[label.change for label in column_mappings],
152
- inputs=[dataset_id_input, dataset_config_input, dataset_split_input, *column_mappings])
153
- def write_column_mapping_to_config(dataset_id, dataset_config, dataset_split, *labels):
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
- def list_labels_and_features_from_dataset(ds_labels, ds_features, model_id2label):
174
- model_labels = list(model_id2label.values())
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=[model_id_input, dataset_id_input, dataset_config_input, dataset_split_input, run_local],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- column_mapping = config.get("column_mapping", dict())
 
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 mapping is None:
 
 
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;">