gabrielchua commited on
Commit
073508d
1 Parent(s): 3df2176
Files changed (2) hide show
  1. app.py +191 -0
  2. requirements.txt +8 -0
app.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)
83
+ sentence_embeddings = model_output[0][:, 0]
84
+ sentence_embeddings = torch.nn.functional.normalize(sentence_embeddings, p=2, dim=1)
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()
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio==4.22.0
2
+ huggingface-hub==0.20.2
3
+ numpy==1.24.4
4
+ pandas==2.2.1
5
+ torch==2.1.2
6
+ scikit-learn==1.3.0
7
+ spaces==0.28.3
8
+ transformers==4.37.2