gabrielchua commited on
Commit
176cc29
1 Parent(s): a573698

Update to lionguard-v1

Browse files
Files changed (1) hide show
  1. app.py +64 -120
app.py CHANGED
@@ -1,82 +1,48 @@
1
- """
2
- app.py
3
- """
4
-
5
  import json
6
- from typing import Any, Dict, List, Optional
7
 
8
  import gradio as gr
9
  import numpy as np
10
  import pandas as pd
11
- import spaces
12
  import torch
13
  from huggingface_hub import hf_hub_download
14
- from sklearn.linear_model import RidgeClassifier
15
  from transformers import AutoModel, AutoTokenizer
 
16
 
17
- # Define the list of available Lionguard models
18
- Lionguard_models: List[str] = [
19
- "dsaidgovsg/Lionguard-binary-v1.0",
20
- "dsaidgovsg/Lionguard-harassment-v1.0",
21
- "dsaidgovsg/Lionguard-hateful-v1.0",
22
- "dsaidgovsg/Lionguard-public_harm-v1.0",
23
- "dsaidgovsg/Lionguard-self_harm-v1.0",
24
- "dsaidgovsg/Lionguard-sexual-v1.0",
25
- "dsaidgovsg/Lionguard-toxic-v1.0",
26
- "dsaidgovsg/Lionguard-violent-v1.0",
27
- ]
28
 
29
- def load_config(model_repo: str) -> Dict[str, Any]:
30
  """
31
- Load the configuration for a given model repository.
32
-
33
- Args:
34
- model_repo (str): The model repository name.
35
-
36
  Returns:
37
  Dict[str, Any]: The configuration dictionary.
38
  """
39
- config_path = hf_hub_download(repo_id=model_repo, filename="config.json")
40
  with open(config_path, 'r') as f:
41
  return json.load(f)
42
 
43
- def load_all_configs() -> Dict[str, Dict[str, Any]]:
44
- """
45
- Load configurations for all Lionguard models.
46
-
47
- Returns:
48
- Dict[str, Dict[str, Any]]: A dictionary of model configurations.
49
- """
50
- model_configs = {}
51
- for model_repo in Lionguard_models:
52
- model_configs[model_repo] = load_config(model_repo)
53
- print("All model configurations loaded.")
54
- return model_configs
55
-
56
- @spaces.GPU
57
  def get_embeddings(device: str, data: List[str], config: Dict[str, Any]) -> np.ndarray:
58
  """
59
  Generate embeddings for the input data using the specified model configuration.
60
-
61
  Args:
62
  device (str): The device to use for computations.
63
  data (List[str]): The input text data.
64
  config (Dict[str, Any]): The model configuration.
65
-
66
  Returns:
67
  np.ndarray: The generated embeddings.
68
  """
69
- tokenizer = AutoTokenizer.from_pretrained(config['tokenizer'])
70
- model = AutoModel.from_pretrained(config['embedding_model'])
71
  model.eval()
72
  model.to(device)
73
 
74
- batch_size = config['batch_size']
75
  num_batches = int(np.ceil(len(data)/batch_size))
76
  output = []
77
  for i in range(num_batches):
78
  sentences = data[i*batch_size:(i+1)*batch_size]
79
- encoded_input = tokenizer(sentences, max_length=config['max_length'], padding=True, truncation=True, return_tensors='pt')
80
  encoded_input.to(device)
81
  with torch.no_grad():
82
  model_output = model(**encoded_input)
@@ -85,107 +51,85 @@ def get_embeddings(device: str, data: List[str], config: Dict[str, Any]) -> np.n
85
  output.extend(sentence_embeddings.cpu().numpy())
86
  return np.array(output)
87
 
88
- def set_model_atttributes(model: RidgeClassifier, attributes: Dict[str, Any]) -> RidgeClassifier:
89
  """
90
- Set the attributes for the Ridge Classifier model.
91
-
92
- Args:
93
- model (RidgeClassifier): The Ridge Classifier model.
94
- attributes (Dict[str, Any]): The attributes to set.
95
-
96
- Returns:
97
- RidgeClassifier: The updated Ridge Classifier model.
98
- """
99
- model.coef_ = np.array(attributes['coef_'])
100
- model.intercept_ = np.array(attributes['intercept_'])
101
- model.n_features_in_ = np.array(attributes['n_features_in_'])
102
- return model
103
-
104
- def convert_decision_to_proba(d: np.ndarray) -> np.ndarray:
105
- """
106
- Convert decision function values to probabilities.
107
-
108
  Args:
109
- d (np.ndarray): The decision function values.
110
-
111
  Returns:
112
- np.ndarray: The converted probabilities.
113
  """
114
- d = np.c_[-d, d]
115
- probs = np.exp(d) / np.sum(np.exp(d), axis=1, keepdims=True)
116
- return probs
117
-
118
- def predict_all(text: str, model_configs: Dict[str, Dict[str, Any]]) -> Optional[pd.DataFrame]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  """
120
- Predict probabilities for all Lionguard models given an input text.
121
-
122
  Args:
123
  text (str): The input text to predict on.
124
- model_configs (Dict[str, Dict[str, Any]]): The configurations for all models.
125
-
126
  Returns:
127
- pd.DataFrame: A DataFrame containing prediction probabilities for each category.
128
  """
129
  if not text.strip():
130
  return None
131
 
132
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
133
-
134
- first_model = next(iter(model_configs))
135
- config = model_configs[first_model]
136
- embeddings = get_embeddings(device, [text], config)
137
- embeddings_df = pd.DataFrame(embeddings)
138
-
139
- results = []
140
- for model_repo in Lionguard_models:
141
- if model_repo not in model_configs:
142
- print(f"Configuration for {model_repo} not found. Skipping...")
143
- continue
144
-
145
- config = model_configs[model_repo]
146
-
147
- model_fp = hf_hub_download(repo_id=model_repo, filename=config['model_name'])
148
- with open(model_fp, 'r') as json_file:
149
- model_params = json.load(json_file)
150
 
151
- model = RidgeClassifier()
152
- model_attributes = model_params["attributes"]
153
- model_params.pop("attributes", None)
154
- model.set_params(**model_params)
155
- model = set_model_atttributes(model, model_attributes)
156
-
157
- preds = convert_decision_to_proba(model.decision_function(embeddings_df))[:,1]
158
-
159
- model_name = model_repo.split('/')[-1].split('-')[1]
160
- results.append({"Category": model_name, "Probability": float(preds[0])})
161
 
162
- result_df = pd.DataFrame(results).sort_values("Probability", ascending=False)
163
-
164
- if result_df.shape[0] > 0:
165
- return result_df
166
- else:
167
- return None
168
-
169
- def create_interface(model_configs: Dict[str, Dict[str, Any]]) -> gr.Interface:
170
  """
171
  Create the Gradio interface for the Lionguard demo.
172
-
173
  Args:
174
- model_configs (Dict[str, Dict[str, Any]]): The configurations for all models.
175
-
176
  Returns:
177
  gr.Interface: The Gradio interface object.
178
  """
179
  return gr.Interface(
180
- fn=lambda text: predict_all(text, model_configs),
181
  inputs=gr.Textbox(lines=3, placeholder="Enter text here..."),
182
- outputs=gr.DataFrame(label="Prediction Probabilities"),
183
  title="🦁 Lionguard Demo",
184
  description="Lionguard is a Singapore-contextualized moderation classifier that can serve against unsafe LLM outputs.",
185
  allow_flagging="never"
186
  )
187
 
188
  if __name__ == "__main__":
189
- model_configs = load_all_configs()
190
- iface = create_interface(model_configs)
191
  iface.launch()
 
 
 
 
 
1
  import json
2
+ from typing import List, Dict, Any
3
 
4
  import gradio as gr
5
  import numpy as np
6
  import pandas as pd
 
7
  import torch
8
  from huggingface_hub import hf_hub_download
 
9
  from transformers import AutoModel, AutoTokenizer
10
+ import onnxruntime as rt
11
 
12
+ # Define the Lionguard model repository
13
+ REPO_PATH = "govtech/lionguard-v1"
 
 
 
 
 
 
 
 
 
14
 
15
+ def load_config() -> Dict[str, Any]:
16
  """
17
+ Load the configuration for the Lionguard model.
 
 
 
 
18
  Returns:
19
  Dict[str, Any]: The configuration dictionary.
20
  """
21
+ config_path = hf_hub_download(repo_id=REPO_PATH, filename="config.json")
22
  with open(config_path, 'r') as f:
23
  return json.load(f)
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  def get_embeddings(device: str, data: List[str], config: Dict[str, Any]) -> np.ndarray:
26
  """
27
  Generate embeddings for the input data using the specified model configuration.
 
28
  Args:
29
  device (str): The device to use for computations.
30
  data (List[str]): The input text data.
31
  config (Dict[str, Any]): The model configuration.
 
32
  Returns:
33
  np.ndarray: The generated embeddings.
34
  """
35
+ tokenizer = AutoTokenizer.from_pretrained(config['embedding']['tokenizer'])
36
+ model = AutoModel.from_pretrained(config['embedding']['model'])
37
  model.eval()
38
  model.to(device)
39
 
40
+ batch_size = config['embedding']['batch_size']
41
  num_batches = int(np.ceil(len(data)/batch_size))
42
  output = []
43
  for i in range(num_batches):
44
  sentences = data[i*batch_size:(i+1)*batch_size]
45
+ encoded_input = tokenizer(sentences, max_length=config['embedding']['max_length'], padding=True, truncation=True, return_tensors='pt')
46
  encoded_input.to(device)
47
  with torch.no_grad():
48
  model_output = model(**encoded_input)
 
51
  output.extend(sentence_embeddings.cpu().numpy())
52
  return np.array(output)
53
 
54
+ def predict(text: str, config: Dict[str, Any]) -> Dict[str, Dict[str, Any]]:
55
  """
56
+ Predict probabilities for all Lionguard categories given an input text.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  Args:
58
+ text (str): The input text to predict on.
59
+ config (Dict[str, Any]): The model configuration.
60
  Returns:
61
+ Dict[str, Dict[str, Any]]: A dictionary containing prediction results for each category.
62
  """
63
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
64
+ embeddings = get_embeddings(device, [text], config)
65
+ X_input = np.array(embeddings, dtype=np.float32)
66
+
67
+ results = {}
68
+ for category, details in config['classifier'].items():
69
+ local_model_fp = hf_hub_download(repo_id=REPO_PATH, filename=details['model_fp'])
70
+ session = rt.InferenceSession(local_model_fp)
71
+ input_name = session.get_inputs()[0].name
72
+ outputs = session.run(None, {input_name: X_input})
73
+
74
+ if details['calibrated']:
75
+ scores = [output[1] for output in outputs[1]]
76
+ else:
77
+ scores = outputs[1].flatten()
78
+
79
+ results[category] = {
80
+ 'score': float(scores[0]),
81
+ 'predictions': {
82
+ 'high_recall': 1 if scores[0] >= details['threshold']['high_recall'] else 0,
83
+ 'balanced': 1 if scores[0] >= details['threshold']['balanced'] else 0,
84
+ 'high_precision': 1 if scores[0] >= details['threshold']['high_precision'] else 0
85
+ }
86
+ }
87
+
88
+ return results
89
+
90
+ def predict_and_format(text: str, config: Dict[str, Any]) -> pd.DataFrame:
91
  """
92
+ Predict and format the results for display in the Gradio interface.
 
93
  Args:
94
  text (str): The input text to predict on.
95
+ config (Dict[str, Any]): The model configuration.
 
96
  Returns:
97
+ pd.DataFrame: A DataFrame containing prediction results for each category.
98
  """
99
  if not text.strip():
100
  return None
101
 
102
+ results = predict(text, config)
103
+ formatted_results = []
104
+ for category, result in results.items():
105
+ formatted_results.append({
106
+ "Category": category,
107
+ "Score": f"{result['score']:.3f}",
108
+ "High Recall": result['predictions']['high_recall'],
109
+ "Balanced": result['predictions']['balanced'],
110
+ "High Precision": result['predictions']['high_precision']
111
+ })
 
 
 
 
 
 
 
 
112
 
113
+ return pd.DataFrame(formatted_results).sort_values("Score", ascending=False)
 
 
 
 
 
 
 
 
 
114
 
115
+ def create_interface(config: Dict[str, Any]) -> gr.Interface:
 
 
 
 
 
 
 
116
  """
117
  Create the Gradio interface for the Lionguard demo.
 
118
  Args:
119
+ config (Dict[str, Any]): The model configuration.
 
120
  Returns:
121
  gr.Interface: The Gradio interface object.
122
  """
123
  return gr.Interface(
124
+ fn=lambda text: predict_and_format(text, config),
125
  inputs=gr.Textbox(lines=3, placeholder="Enter text here..."),
126
+ outputs=gr.DataFrame(label="Prediction Results"),
127
  title="🦁 Lionguard Demo",
128
  description="Lionguard is a Singapore-contextualized moderation classifier that can serve against unsafe LLM outputs.",
129
  allow_flagging="never"
130
  )
131
 
132
  if __name__ == "__main__":
133
+ config = load_config()
134
+ iface = create_interface(config)
135
  iface.launch()