psaiops 0.4.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of psaiops might be problematic. Click here for more details.
- psaiops/__init__.py +0 -0
- psaiops/combine/__init__.py +0 -0
- psaiops/combine/app.py +366 -0
- psaiops/common/__init__.py +0 -0
- psaiops/common/data.py +31 -0
- psaiops/common/model.py +73 -0
- psaiops/common/tokenizer.py +41 -0
- psaiops/compose/__init__.py +0 -0
- psaiops/compose/contrast/__init__.py +0 -0
- psaiops/compose/contrast/app.py +195 -0
- psaiops/compose/contrast/lib.py +143 -0
- psaiops/compose/maths/__init__.py +0 -0
- psaiops/compose/maths/app.py +323 -0
- psaiops/compose/maths/lib.py +1 -0
- psaiops/edit/__init__.py +0 -0
- psaiops/reverse/__init__.py +0 -0
- psaiops/score/__init__.py +0 -0
- psaiops/score/attention/__init__.py +0 -0
- psaiops/score/attention/app.py +303 -0
- psaiops/score/attention/lib.py +118 -0
- psaiops/score/residual/__init__.py +0 -0
- psaiops/score/residual/app.py +507 -0
- psaiops/score/residual/lib.py +187 -0
- psaiops/score/router/__init__.py +0 -0
- psaiops/score/router/app.py +282 -0
- psaiops/score/router/lib.py +59 -0
- psaiops/score/shapley/__init__.py +0 -0
- psaiops/score/shapley/app.py +158 -0
- psaiops/score/shapley/lib.py +1 -0
- psaiops/score/similarity/__init__.py +0 -0
- psaiops/score/similarity/app.py +152 -0
- psaiops/score/similarity/lib.py +1 -0
- psaiops-0.4.7.dist-info/METADATA +34 -0
- psaiops-0.4.7.dist-info/RECORD +36 -0
- psaiops-0.4.7.dist-info/WHEEL +4 -0
- psaiops-0.4.7.dist-info/licenses/.github/LICENSE.md +661 -0
|
File without changes
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
import gradio
|
|
4
|
+
import torch
|
|
5
|
+
import torch.cuda
|
|
6
|
+
import matplotlib.pyplot
|
|
7
|
+
|
|
8
|
+
import psaiops.common.model
|
|
9
|
+
import psaiops.common.tokenizer
|
|
10
|
+
import psaiops.score.router.lib
|
|
11
|
+
|
|
12
|
+
# META #########################################################################
|
|
13
|
+
|
|
14
|
+
STYLE = '''.white-text span { color: white; }'''
|
|
15
|
+
TITLE = '''Router Scoring'''
|
|
16
|
+
INTRO = '''Plot the logits of the router for a given prompt.\nUnder construction, only "openai/gpt-oss-20b" is available for now.'''
|
|
17
|
+
|
|
18
|
+
MODEL = 'openai/gpt-oss-20b'
|
|
19
|
+
|
|
20
|
+
# COLORS #######################################################################
|
|
21
|
+
|
|
22
|
+
def create_color_map() -> dict:
|
|
23
|
+
return {
|
|
24
|
+
'0': '#000000',
|
|
25
|
+
'1': '#004444',}
|
|
26
|
+
|
|
27
|
+
# INTRO ########################################################################
|
|
28
|
+
|
|
29
|
+
def create_intro_block(intro: str) -> dict:
|
|
30
|
+
__intro = gradio.Markdown(intro, line_breaks=True)
|
|
31
|
+
return {'intro_block': __intro}
|
|
32
|
+
|
|
33
|
+
# MODEL ########################################################################
|
|
34
|
+
|
|
35
|
+
def create_model_block() -> dict:
|
|
36
|
+
__model = gradio.Dropdown(label='Model', value='openai/gpt-oss-20b', choices=['openai/gpt-oss-20b'], scale=1, allow_custom_value=False, multiselect=False, interactive=True) # 'openai/gpt-oss-120b'
|
|
37
|
+
return {'model_block': __model,}
|
|
38
|
+
|
|
39
|
+
# SAMPLING #####################################################################
|
|
40
|
+
|
|
41
|
+
def create_sampling_block() -> dict:
|
|
42
|
+
__tokens = gradio.Slider(label='Tokens', value=16, minimum=1, maximum=128, step=1, scale=1, interactive=True)
|
|
43
|
+
__topk = gradio.Slider(label='Top K', value=4, minimum=1, maximum=8, step=1, scale=1, interactive=True)
|
|
44
|
+
__topp = gradio.Slider(label='Top P', value=0.9, minimum=0.0, maximum=1.0, step=0.1, scale=1, interactive=True)
|
|
45
|
+
return {
|
|
46
|
+
'tokens_block': __tokens,
|
|
47
|
+
'topk_block': __topk,
|
|
48
|
+
'topp_block': __topp,}
|
|
49
|
+
|
|
50
|
+
# INPUTS #######################################################################
|
|
51
|
+
|
|
52
|
+
def create_inputs_block() -> dict:
|
|
53
|
+
__input = gradio.Textbox(label='Prompt', value='', placeholder='A string of tokens to score.', lines=4, scale=1, show_copy_button=True, interactive=True)
|
|
54
|
+
return {'input_block': __input}
|
|
55
|
+
|
|
56
|
+
# PLOTS ########################################################################
|
|
57
|
+
|
|
58
|
+
def create_plot_block() -> dict:
|
|
59
|
+
__plot = gradio.Plot(label='Router', scale=1)
|
|
60
|
+
return {'plot_block': __plot,}
|
|
61
|
+
|
|
62
|
+
# OUTPUTS ######################################################################
|
|
63
|
+
|
|
64
|
+
def create_outputs_block() -> dict:
|
|
65
|
+
__output = gradio.HighlightedText(label='Output', value='', scale=1, interactive=False, show_legend=False, show_inline_category=False, combine_adjacent=False, color_map=create_color_map(), elem_classes='white-text')
|
|
66
|
+
return {'output_block': __output}
|
|
67
|
+
|
|
68
|
+
# SELECT #######################################################################
|
|
69
|
+
|
|
70
|
+
def create_selection_block() -> dict:
|
|
71
|
+
# __play = gradio.Button('>', variant='primary', size='lg', scale=1, interactive=True)
|
|
72
|
+
__position = gradio.Slider(label='Token', value=-1, minimum=-1, maximum=15, step=1, scale=1, interactive=True) # info='-1 to average on all tokens'
|
|
73
|
+
return {'position_block': __position,}
|
|
74
|
+
|
|
75
|
+
# ACTIONS ######################################################################
|
|
76
|
+
|
|
77
|
+
def create_actions_block() -> dict:
|
|
78
|
+
__process = gradio.Button('Process', variant='primary', size='lg', scale=1, interactive=True)
|
|
79
|
+
return {'process_block': __process,}
|
|
80
|
+
|
|
81
|
+
# STATE ########################################################################
|
|
82
|
+
|
|
83
|
+
def create_state() -> dict:
|
|
84
|
+
return {
|
|
85
|
+
'output_state': gradio.State(None),
|
|
86
|
+
'router_state': gradio.State(None),}
|
|
87
|
+
|
|
88
|
+
# LAYOUT #######################################################################
|
|
89
|
+
|
|
90
|
+
def create_layout(intro: str=INTRO) -> dict:
|
|
91
|
+
__fields = {}
|
|
92
|
+
__fields.update(create_intro_block(intro=intro))
|
|
93
|
+
with gradio.Tabs():
|
|
94
|
+
with gradio.Tab('Score Tokens') as __main_tab:
|
|
95
|
+
__fields.update({'main_tab': __main_tab})
|
|
96
|
+
with gradio.Row(equal_height=True):
|
|
97
|
+
__fields.update(create_inputs_block())
|
|
98
|
+
with gradio.Row(equal_height=True):
|
|
99
|
+
__fields.update(create_plot_block())
|
|
100
|
+
with gradio.Row(equal_height=True):
|
|
101
|
+
__fields.update(create_outputs_block())
|
|
102
|
+
with gradio.Row(equal_height=True):
|
|
103
|
+
__fields.update(create_selection_block())
|
|
104
|
+
with gradio.Row(equal_height=True):
|
|
105
|
+
__fields.update(create_actions_block())
|
|
106
|
+
with gradio.Tab('Settings') as __settings_tab:
|
|
107
|
+
__fields.update({'settings_tab': __settings_tab})
|
|
108
|
+
with gradio.Column(scale=1):
|
|
109
|
+
with gradio.Row(equal_height=True):
|
|
110
|
+
__fields.update(create_model_block())
|
|
111
|
+
with gradio.Row(equal_height=True):
|
|
112
|
+
__fields.update(create_sampling_block())
|
|
113
|
+
return __fields
|
|
114
|
+
|
|
115
|
+
# EVENTS #######################################################################
|
|
116
|
+
|
|
117
|
+
def update_position_range(
|
|
118
|
+
current_val: float,
|
|
119
|
+
token_num: float,
|
|
120
|
+
output_data: torch.Tensor,
|
|
121
|
+
) -> dict:
|
|
122
|
+
# take the generated tokens into account
|
|
123
|
+
__max = int(token_num) - 1 if (output_data is None) else int(output_data.shape[-1])
|
|
124
|
+
# keep the previous value if possible
|
|
125
|
+
__val = min(int(current_val), __max)
|
|
126
|
+
# return a gradio update dictionary
|
|
127
|
+
return gradio.update(maximum=__max, value=__val)
|
|
128
|
+
|
|
129
|
+
def update_computation_state(
|
|
130
|
+
token_num: float,
|
|
131
|
+
topk_num: float,
|
|
132
|
+
topp_num: float,
|
|
133
|
+
token_idx: float,
|
|
134
|
+
prompt_str: str,
|
|
135
|
+
device_str: str,
|
|
136
|
+
model_obj: object,
|
|
137
|
+
tokenizer_obj: object,
|
|
138
|
+
) -> tuple:
|
|
139
|
+
# sanitize the inputs
|
|
140
|
+
__token_num = max(1, min(128, int(token_num)))
|
|
141
|
+
__topk_num = max(1, min(8, int(topk_num)))
|
|
142
|
+
__topp_num = max(0.0, min(1.0, float(topp_num)))
|
|
143
|
+
__token_idx = max(-1, min(__token_num, int(token_idx)))
|
|
144
|
+
__prompt_str = prompt_str.strip()
|
|
145
|
+
__device_str = device_str if (device_str in ['cpu', 'cuda']) else 'cpu'
|
|
146
|
+
# exit if some values are missing
|
|
147
|
+
if (not __prompt_str) or (model_obj is None) or (tokenizer_obj is None):
|
|
148
|
+
return (torch.empty(0), torch.empty(0))
|
|
149
|
+
# dictionary {'input_ids': _, 'attention_mask': _}
|
|
150
|
+
__input_data = psaiops.common.tokenizer.preprocess_token_ids(
|
|
151
|
+
tokenizer_obj=tokenizer_obj,
|
|
152
|
+
prompt_str=__prompt_str,
|
|
153
|
+
device_str=__device_str)
|
|
154
|
+
# tensor (1, T)
|
|
155
|
+
__output_data = psaiops.common.model.generate_token_ids(
|
|
156
|
+
model_obj=model_obj,
|
|
157
|
+
input_ids=__input_data['input_ids'],
|
|
158
|
+
attention_mask=__input_data['attention_mask'],
|
|
159
|
+
token_num=__token_num,
|
|
160
|
+
topk_num=__topk_num,
|
|
161
|
+
topp_num=__topp_num)
|
|
162
|
+
# tensor (L, S, H, T, T)
|
|
163
|
+
__router_data = psaiops.score.router.lib.compute_router_weights(
|
|
164
|
+
model_obj=model_obj,
|
|
165
|
+
token_data=__output_data)
|
|
166
|
+
# update each component => (highlight, plot) states
|
|
167
|
+
return (
|
|
168
|
+
__output_data.cpu(),
|
|
169
|
+
__router_data.cpu(),)
|
|
170
|
+
|
|
171
|
+
def update_router_plot(
|
|
172
|
+
token_idx: float,
|
|
173
|
+
router_data: torch.Tensor,
|
|
174
|
+
) -> tuple:
|
|
175
|
+
# exit if some values are missing
|
|
176
|
+
if (router_data is None) or (len(router_data) == 0):
|
|
177
|
+
return None
|
|
178
|
+
# reduce the batch and token axes => tensor (L, E)
|
|
179
|
+
__plot_data = psaiops.score.router.lib.reduce_router_weights(
|
|
180
|
+
router_data=router_data,
|
|
181
|
+
token_idx=int(token_idx),)
|
|
182
|
+
# translate the scores into integer labels
|
|
183
|
+
__plot_data = psaiops.score.router.lib.postprocess_router_weights(
|
|
184
|
+
router_data=__plot_data,)
|
|
185
|
+
# plot the data
|
|
186
|
+
__figure, __axes = matplotlib.pyplot.subplots()
|
|
187
|
+
__axes.imshow(__plot_data.float().numpy(), vmin=0.0, vmax=1.0, cmap='viridis')
|
|
188
|
+
__figure.tight_layout()
|
|
189
|
+
# remove the figure for the pyplot register for garbage collection
|
|
190
|
+
matplotlib.pyplot.close(__figure)
|
|
191
|
+
# update each component => (highlight, plot) states
|
|
192
|
+
return __figure
|
|
193
|
+
|
|
194
|
+
def update_text_highlight(
|
|
195
|
+
token_idx: float,
|
|
196
|
+
output_data: torch.Tensor,
|
|
197
|
+
tokenizer_obj: object,
|
|
198
|
+
) -> list:
|
|
199
|
+
# exit if some values are missing
|
|
200
|
+
if (output_data is None) or (len(output_data) == 0):
|
|
201
|
+
return None
|
|
202
|
+
# detokenize the IDs
|
|
203
|
+
__token_str = psaiops.common.tokenizer.postprocess_token_ids(
|
|
204
|
+
tokenizer_obj=tokenizer_obj,
|
|
205
|
+
token_data=output_data)
|
|
206
|
+
# list of string classes
|
|
207
|
+
__token_cls = psaiops.score.router.lib.postprocess_token_cls(
|
|
208
|
+
token_idx=int(token_idx),
|
|
209
|
+
token_dim=len(__token_str))
|
|
210
|
+
# pairs of token and class
|
|
211
|
+
return list(zip(__token_str, __token_cls))
|
|
212
|
+
|
|
213
|
+
# APP ##########################################################################
|
|
214
|
+
|
|
215
|
+
def create_app(title: str=TITLE, intro: str=INTRO, style: str=STYLE, model: str=MODEL) -> gradio.Blocks:
|
|
216
|
+
__fields = {}
|
|
217
|
+
with gradio.Blocks(theme=gradio.themes.Soft(), title=title, css=style) as __app:
|
|
218
|
+
# load the model
|
|
219
|
+
__device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
220
|
+
__model = psaiops.common.model.get_model(name=model, device=__device)
|
|
221
|
+
__tokenizer = psaiops.common.tokenizer.get_tokenizer(name=model, device=__device)
|
|
222
|
+
# adapt the event handlers
|
|
223
|
+
__compute = functools.partial(update_computation_state, model_obj=__model, tokenizer_obj=__tokenizer, device_str=__device)
|
|
224
|
+
__highlight = functools.partial(update_text_highlight, tokenizer_obj=__tokenizer)
|
|
225
|
+
# create the UI
|
|
226
|
+
__fields.update(create_layout(intro=intro))
|
|
227
|
+
# init the state
|
|
228
|
+
__fields.update(create_state())
|
|
229
|
+
# update the data after clicking process
|
|
230
|
+
__fields['process_block'].click(
|
|
231
|
+
fn=__compute,
|
|
232
|
+
inputs=[__fields[__k] for __k in ['tokens_block', 'topk_block', 'topp_block', 'position_block', 'input_block']],
|
|
233
|
+
outputs=[__fields[__k] for __k in ['output_state', 'router_state']],
|
|
234
|
+
queue=False,
|
|
235
|
+
show_progress='full').then(
|
|
236
|
+
# update the range of the position slider when the output changes
|
|
237
|
+
fn=update_position_range,
|
|
238
|
+
inputs=[__fields[__k] for __k in ['position_block', 'tokens_block', 'output_state']],
|
|
239
|
+
outputs=__fields['position_block'],
|
|
240
|
+
queue=False,
|
|
241
|
+
show_progress='hidden').then(
|
|
242
|
+
# update the token highlight when the output data changes
|
|
243
|
+
fn=__highlight,
|
|
244
|
+
inputs=[__fields[__k] for __k in ['position_block', 'output_state']],
|
|
245
|
+
outputs=__fields['output_block'],
|
|
246
|
+
queue=False,
|
|
247
|
+
show_progress='full').then(
|
|
248
|
+
# update the plot when the router data changes
|
|
249
|
+
fn=update_router_plot,
|
|
250
|
+
inputs=[__fields[__k] for __k in ['position_block', 'router_state']],
|
|
251
|
+
outputs=__fields['plot_block'],
|
|
252
|
+
queue=False,
|
|
253
|
+
show_progress='full')
|
|
254
|
+
# update the range of the position slider when the settings change
|
|
255
|
+
__fields['tokens_block'].change(
|
|
256
|
+
fn=update_position_range,
|
|
257
|
+
inputs=[__fields[__k] for __k in ['position_block', 'tokens_block', 'output_state']],
|
|
258
|
+
outputs=__fields['position_block'],
|
|
259
|
+
queue=False,
|
|
260
|
+
show_progress='hidden')
|
|
261
|
+
# update the plot when the focus changes
|
|
262
|
+
__fields['position_block'].change(
|
|
263
|
+
fn=update_router_plot,
|
|
264
|
+
inputs=[__fields[__k] for __k in ['position_block', 'router_state']],
|
|
265
|
+
outputs=__fields['plot_block'],
|
|
266
|
+
queue=False,
|
|
267
|
+
show_progress='full')
|
|
268
|
+
# update the token highlight when the token focus changes
|
|
269
|
+
__fields['position_block'].change(
|
|
270
|
+
fn=__highlight,
|
|
271
|
+
inputs=[__fields[__k] for __k in ['position_block', 'output_state']],
|
|
272
|
+
outputs=__fields['output_block'],
|
|
273
|
+
queue=False,
|
|
274
|
+
show_progress='hidden')
|
|
275
|
+
# gradio application
|
|
276
|
+
return __app
|
|
277
|
+
|
|
278
|
+
# MAIN #########################################################################
|
|
279
|
+
|
|
280
|
+
if __name__ == '__main__':
|
|
281
|
+
__app = create_app()
|
|
282
|
+
__app.launch(share=True, debug=True)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
# COMPUTE ########################################################################
|
|
6
|
+
|
|
7
|
+
def compute_router_weights(
|
|
8
|
+
model_obj: object,
|
|
9
|
+
token_data: torch.Tensor,
|
|
10
|
+
) -> torch.Tensor:
|
|
11
|
+
# process the full sequence
|
|
12
|
+
with torch.no_grad():
|
|
13
|
+
__outputs = model_obj(
|
|
14
|
+
input_ids=token_data,
|
|
15
|
+
output_attentions=False,
|
|
16
|
+
output_router_logits=True,
|
|
17
|
+
return_dict=True)
|
|
18
|
+
# stack all the layer outputs L * (T, E) => (L, T, E)
|
|
19
|
+
__logits = torch.stack(__outputs.router_logits, dim=0)
|
|
20
|
+
# turn the logits into expert probabilities
|
|
21
|
+
return torch.softmax(__logits, dim=-1)
|
|
22
|
+
|
|
23
|
+
# REDUCE #######################################################################
|
|
24
|
+
|
|
25
|
+
def reduce_router_weights(
|
|
26
|
+
router_data: torch.Tensor,
|
|
27
|
+
token_idx: int, # -1 => avg over all tokens
|
|
28
|
+
) -> torch.Tensor:
|
|
29
|
+
# parse
|
|
30
|
+
__layer_dim, __token_dim, __expert_dim = tuple(router_data.shape) # L, T, E
|
|
31
|
+
__token_idx = min(token_idx, __token_dim - 1)
|
|
32
|
+
# select the relevant data along each axis
|
|
33
|
+
__token_slice = slice(0, __token_dim) if (__token_idx < 0) else slice(__token_idx, __token_idx + 1)
|
|
34
|
+
# filter the data
|
|
35
|
+
__data = router_data[slice(None), __token_slice, slice(None)]
|
|
36
|
+
# reduce all the axes but the last
|
|
37
|
+
return __data.mean(dim=1, keepdim=False)
|
|
38
|
+
|
|
39
|
+
# FORMAT #########################################################################
|
|
40
|
+
|
|
41
|
+
def postprocess_router_weights(
|
|
42
|
+
router_data: torch.Tensor, # (L, E)
|
|
43
|
+
) -> list:
|
|
44
|
+
# the averaging over tokens may have broken the scaling
|
|
45
|
+
__probs = torch.softmax(router_data, dim=-1)
|
|
46
|
+
# enforce the output range [0; 1] with 1 included
|
|
47
|
+
return __probs / __probs.amax(dim=-1, keepdim=True)
|
|
48
|
+
|
|
49
|
+
# POSTPROCESS ####################################################################
|
|
50
|
+
|
|
51
|
+
def postprocess_token_cls(
|
|
52
|
+
token_idx: int,
|
|
53
|
+
token_dim: int,
|
|
54
|
+
) -> list:
|
|
55
|
+
__token_idx = max(-1, min(token_dim, token_idx))
|
|
56
|
+
# class 1 for the focused token(s) 0 for the rest
|
|
57
|
+
__token_cls = [str(int(__i == token_idx)) for __i in range(token_dim)]
|
|
58
|
+
# average on all the tokens when the idx is negative
|
|
59
|
+
return token_dim * ['1'] if (token_idx < 0) else __token_cls
|
|
File without changes
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
import gradio
|
|
4
|
+
import torch
|
|
5
|
+
import torch.cuda
|
|
6
|
+
|
|
7
|
+
import psaiops.common.model
|
|
8
|
+
import psaiops.common.tokenizer
|
|
9
|
+
|
|
10
|
+
# META #########################################################################
|
|
11
|
+
|
|
12
|
+
STYLE = '''.white-text span { color: white; }'''
|
|
13
|
+
TITLE = '''Shapley Scoring'''
|
|
14
|
+
INTRO = '''Score each token according to their [Shapley value](https://en.wikipedia.org/wiki/Shapley_value).\nUnder construction, only "openai/gpt-oss-20b" is available for now.'''
|
|
15
|
+
|
|
16
|
+
MODEL = 'openai/gpt-oss-20b'
|
|
17
|
+
|
|
18
|
+
# COLORS #######################################################################
|
|
19
|
+
|
|
20
|
+
def create_color_map() -> dict:
|
|
21
|
+
return {
|
|
22
|
+
'-1': '#004444',
|
|
23
|
+
**{str(__i): '#{:02x}0000'.format(int(2.55 * __i)) for __i in range(101)}}
|
|
24
|
+
|
|
25
|
+
# INTRO ########################################################################
|
|
26
|
+
|
|
27
|
+
def create_intro_block(intro: str) -> dict:
|
|
28
|
+
__intro = gradio.Markdown(intro, line_breaks=True)
|
|
29
|
+
return {'intro_block': __intro}
|
|
30
|
+
|
|
31
|
+
# MODEL ########################################################################
|
|
32
|
+
|
|
33
|
+
def create_model_block() -> dict:
|
|
34
|
+
__model = gradio.Dropdown(label='Model', value='openai/gpt-oss-20b', choices=['openai/gpt-oss-20b'], scale=1, allow_custom_value=False, multiselect=False, interactive=True) # 'openai/gpt-oss-120b'
|
|
35
|
+
return {'model_block': __model,}
|
|
36
|
+
|
|
37
|
+
# SAMPLING #####################################################################
|
|
38
|
+
|
|
39
|
+
def create_sampling_block() -> dict:
|
|
40
|
+
__tokens = gradio.Slider(label='Tokens', value=16, minimum=1, maximum=128, step=1, scale=1, interactive=True)
|
|
41
|
+
__topk = gradio.Slider(label='Top K', value=4, minimum=1, maximum=8, step=1, scale=1, interactive=True)
|
|
42
|
+
__topp = gradio.Slider(label='Top P', value=0.9, minimum=0.0, maximum=1.0, step=0.1, scale=1, interactive=True)
|
|
43
|
+
return {
|
|
44
|
+
'tokens_block': __tokens,
|
|
45
|
+
'topk_block': __topk,
|
|
46
|
+
'topp_block': __topp,}
|
|
47
|
+
|
|
48
|
+
# SAMPLING #####################################################################
|
|
49
|
+
|
|
50
|
+
def create_computation_block() -> dict:
|
|
51
|
+
__count = gradio.Slider(label='Sample count', value=4, minimum=1, maximum=16, step=1, scale=1, interactive=True)
|
|
52
|
+
__min = gradio.Slider(label='Min size', value=50, minimum=0, maximum=100, step=1, scale=1, interactive=True)
|
|
53
|
+
__max = gradio.Slider(label='Max size', value=100, minimum=0, maximum=100, step=1, scale=1, interactive=True)
|
|
54
|
+
return {
|
|
55
|
+
'count_block': __count,
|
|
56
|
+
'min_block': __min,
|
|
57
|
+
'max_block': __max,}
|
|
58
|
+
|
|
59
|
+
# INPUTS #######################################################################
|
|
60
|
+
|
|
61
|
+
def create_inputs_block() -> dict:
|
|
62
|
+
__input = gradio.Textbox(label='Prompt', value='', placeholder='A string of tokens to score.', lines=4, scale=1, show_copy_button=True, interactive=True)
|
|
63
|
+
return {'input_block': __input}
|
|
64
|
+
|
|
65
|
+
# OUTPUTS ######################################################################
|
|
66
|
+
|
|
67
|
+
def create_outputs_block() -> dict:
|
|
68
|
+
__output = gradio.HighlightedText(label='Scores', value='', scale=1, interactive=False, show_legend=False, show_inline_category=False, combine_adjacent=False, color_map=create_color_map(), elem_classes='white-text')
|
|
69
|
+
return {'output_block': __output,}
|
|
70
|
+
|
|
71
|
+
# SELECT #######################################################################
|
|
72
|
+
|
|
73
|
+
def create_selection_block() -> dict:
|
|
74
|
+
__position = gradio.Slider(label='Token Position', value=-1, minimum=-1, maximum=15, step=1, scale=1, interactive=True) # info='-1 to average on all tokens'
|
|
75
|
+
__layer = gradio.Slider(label='Layer Depth', value=12, minimum=-1, maximum=23, step=1, scale=1, interactive=True) # info='-1 to average on all layers'
|
|
76
|
+
return {
|
|
77
|
+
'position_block': __position,
|
|
78
|
+
'layer_block': __layer,}
|
|
79
|
+
|
|
80
|
+
# ACTIONS ######################################################################
|
|
81
|
+
|
|
82
|
+
def create_actions_block() -> dict:
|
|
83
|
+
__process = gradio.Button('Process', variant='primary', size='lg', scale=1, interactive=True)
|
|
84
|
+
return {'process_block': __process,}
|
|
85
|
+
|
|
86
|
+
# STATE ########################################################################
|
|
87
|
+
|
|
88
|
+
def create_state() -> dict:
|
|
89
|
+
return {}
|
|
90
|
+
|
|
91
|
+
# LAYOUT #######################################################################
|
|
92
|
+
|
|
93
|
+
def create_layout(intro: str=INTRO) -> dict:
|
|
94
|
+
__fields = {}
|
|
95
|
+
__fields.update(create_intro_block(intro=intro))
|
|
96
|
+
with gradio.Tabs():
|
|
97
|
+
with gradio.Tab('Score Tokens') as __main_tab:
|
|
98
|
+
__fields.update({'main_tab': __main_tab})
|
|
99
|
+
with gradio.Row(equal_height=True):
|
|
100
|
+
__fields.update(create_inputs_block())
|
|
101
|
+
__fields.update(create_outputs_block())
|
|
102
|
+
with gradio.Row(equal_height=True):
|
|
103
|
+
__fields.update(create_selection_block())
|
|
104
|
+
with gradio.Row(equal_height=True):
|
|
105
|
+
__fields.update(create_actions_block())
|
|
106
|
+
with gradio.Tab('Settings') as __settings_tab:
|
|
107
|
+
__fields.update({'settings_tab': __settings_tab})
|
|
108
|
+
with gradio.Column(scale=1):
|
|
109
|
+
with gradio.Row(equal_height=True):
|
|
110
|
+
__fields.update(create_model_block())
|
|
111
|
+
with gradio.Row(equal_height=True):
|
|
112
|
+
__fields.update(create_sampling_block())
|
|
113
|
+
with gradio.Row(equal_height=True):
|
|
114
|
+
__fields.update(create_computation_block())
|
|
115
|
+
return __fields
|
|
116
|
+
|
|
117
|
+
# EVENTS #######################################################################
|
|
118
|
+
|
|
119
|
+
def update_layer_range(value: float, model: str) -> dict:
|
|
120
|
+
return gradio.update(maximum=35, value=min(35, int(value))) if '120b' in model else gradio.update(maximum=23, value=min(23, int(value)))
|
|
121
|
+
|
|
122
|
+
def update_position_range(value: float, tokens: float) -> dict:
|
|
123
|
+
return gradio.update(maximum=int(tokens) - 1, value=min(int(tokens) - 1, int(value)))
|
|
124
|
+
|
|
125
|
+
# APP ##########################################################################
|
|
126
|
+
|
|
127
|
+
def create_app(title: str=TITLE, intro: str=INTRO, style: str=STYLE, model: str=MODEL) -> gradio.Blocks:
|
|
128
|
+
__fields = {}
|
|
129
|
+
with gradio.Blocks(theme=gradio.themes.Soft(), title=title, css=style) as __app:
|
|
130
|
+
# load the model
|
|
131
|
+
__device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
132
|
+
# __model = psaiops.common.model.get_model(name=model, device=__device)
|
|
133
|
+
__tokenizer = psaiops.common.tokenizer.get_tokenizer(name=model, device=__device)
|
|
134
|
+
# create the UI
|
|
135
|
+
__fields.update(create_layout(intro=intro))
|
|
136
|
+
# init the state
|
|
137
|
+
__fields.update(create_state())
|
|
138
|
+
# wire the input fields
|
|
139
|
+
__fields['tokens_block'].change(
|
|
140
|
+
fn=update_position_range,
|
|
141
|
+
inputs=[__fields[__k] for __k in ['position_block', 'tokens_block']],
|
|
142
|
+
outputs=__fields['position_block'],
|
|
143
|
+
queue=False,
|
|
144
|
+
show_progress='hidden')
|
|
145
|
+
__fields['model_block'].change(
|
|
146
|
+
fn=update_layer_range,
|
|
147
|
+
inputs=[__fields[__k] for __k in ['layer_block', 'model_block']],
|
|
148
|
+
outputs=__fields['layer_block'],
|
|
149
|
+
queue=False,
|
|
150
|
+
show_progress='hidden')
|
|
151
|
+
# gradio application
|
|
152
|
+
return __app
|
|
153
|
+
|
|
154
|
+
# MAIN #########################################################################
|
|
155
|
+
|
|
156
|
+
if __name__ == '__main__':
|
|
157
|
+
__app = create_app()
|
|
158
|
+
__app.launch(share=True, debug=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import torch
|
|
File without changes
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
import gradio
|
|
4
|
+
import torch
|
|
5
|
+
import torch.cuda
|
|
6
|
+
|
|
7
|
+
import psaiops.common.model
|
|
8
|
+
import psaiops.common.tokenizer
|
|
9
|
+
|
|
10
|
+
# META #########################################################################
|
|
11
|
+
|
|
12
|
+
STYLE = '''.white-text span { color: white; }'''
|
|
13
|
+
TITLE = '''Similarity Scoring'''
|
|
14
|
+
INTRO = '''Score each token according to its similarity with a reference prompt.\nUnder construction, only "openai/gpt-oss-20b" is available for now.'''
|
|
15
|
+
|
|
16
|
+
MODEL = 'openai/gpt-oss-20b'
|
|
17
|
+
|
|
18
|
+
# COLORS #######################################################################
|
|
19
|
+
|
|
20
|
+
def create_color_map() -> dict:
|
|
21
|
+
return {
|
|
22
|
+
'-1': '#004444',
|
|
23
|
+
**{str(__i): '#{:02x}0000'.format(int(2.55 * __i)) for __i in range(101)}}
|
|
24
|
+
|
|
25
|
+
# INTRO ########################################################################
|
|
26
|
+
|
|
27
|
+
def create_intro_block(intro: str) -> dict:
|
|
28
|
+
__intro = gradio.Markdown(intro, line_breaks=True)
|
|
29
|
+
return {'intro_block': __intro}
|
|
30
|
+
|
|
31
|
+
# MODEL ########################################################################
|
|
32
|
+
|
|
33
|
+
def create_model_block() -> dict:
|
|
34
|
+
__model = gradio.Dropdown(label='Model', value='openai/gpt-oss-20b', choices=['openai/gpt-oss-20b'], scale=1, allow_custom_value=False, multiselect=False, interactive=True) # 'openai/gpt-oss-120b'
|
|
35
|
+
return {'model_block': __model,}
|
|
36
|
+
|
|
37
|
+
# SAMPLING #####################################################################
|
|
38
|
+
|
|
39
|
+
def create_sampling_block() -> dict:
|
|
40
|
+
__tokens = gradio.Slider(label='Tokens', value=16, minimum=1, maximum=128, step=1, scale=1, interactive=True)
|
|
41
|
+
__topk = gradio.Slider(label='Top K', value=4, minimum=1, maximum=8, step=1, scale=1, interactive=True)
|
|
42
|
+
__topp = gradio.Slider(label='Top P', value=0.9, minimum=0.0, maximum=1.0, step=0.1, scale=1, interactive=True)
|
|
43
|
+
return {
|
|
44
|
+
'tokens_block': __tokens,
|
|
45
|
+
'topk_block': __topk,
|
|
46
|
+
'topp_block': __topp,}
|
|
47
|
+
|
|
48
|
+
# INPUTS #######################################################################
|
|
49
|
+
|
|
50
|
+
def create_inputs_block() -> dict:
|
|
51
|
+
__reference = gradio.Textbox(label='Reference', value='', placeholder='The text used as reference for the comparison.', lines=4, scale=1, show_copy_button=True, interactive=True)
|
|
52
|
+
__prompt = gradio.Textbox(label='Prompt', value='', placeholder='A string of tokens to compare to the reference.', lines=4, scale=1, show_copy_button=True, interactive=True)
|
|
53
|
+
return {
|
|
54
|
+
'reference_input_block': __reference,
|
|
55
|
+
'prompt_input_block': __prompt,}
|
|
56
|
+
|
|
57
|
+
# OUTPUTS ######################################################################
|
|
58
|
+
|
|
59
|
+
def create_outputs_block() -> dict:
|
|
60
|
+
__reference = gradio.HighlightedText(label='', value='', scale=1, interactive=False, show_label=False, show_legend=False, show_inline_category=False, combine_adjacent=False, color_map=create_color_map(), elem_classes='white-text')
|
|
61
|
+
__prompt = gradio.HighlightedText(label='', value='', scale=1, interactive=False, show_label=False, show_legend=False, show_inline_category=False, combine_adjacent=False, color_map=create_color_map(), elem_classes='white-text')
|
|
62
|
+
return {
|
|
63
|
+
'reference_output_block': __reference,
|
|
64
|
+
'prompt_output_block': __prompt,}
|
|
65
|
+
|
|
66
|
+
# SELECT #######################################################################
|
|
67
|
+
|
|
68
|
+
def create_selection_block() -> dict:
|
|
69
|
+
__position = gradio.Slider(label='Token Position', value=-1, minimum=-1, maximum=15, step=1, scale=1, interactive=True) # info='-1 to average on all tokens'
|
|
70
|
+
__layer = gradio.Slider(label='Layer Depth', value=12, minimum=-1, maximum=23, step=1, scale=1, interactive=True) # info='-1 to average on all layers'
|
|
71
|
+
return {
|
|
72
|
+
'position_block': __position,
|
|
73
|
+
'layer_block': __layer,}
|
|
74
|
+
|
|
75
|
+
# ACTIONS ######################################################################
|
|
76
|
+
|
|
77
|
+
def create_actions_block() -> dict:
|
|
78
|
+
__process = gradio.Button('Process', variant='primary', size='lg', scale=1, interactive=True)
|
|
79
|
+
return {'process_block': __process,}
|
|
80
|
+
|
|
81
|
+
# STATE ########################################################################
|
|
82
|
+
|
|
83
|
+
def create_state() -> dict:
|
|
84
|
+
return {}
|
|
85
|
+
|
|
86
|
+
# LAYOUT #######################################################################
|
|
87
|
+
|
|
88
|
+
def create_layout(intro: str=INTRO) -> dict:
|
|
89
|
+
__fields = {}
|
|
90
|
+
__fields.update(create_intro_block(intro=intro))
|
|
91
|
+
with gradio.Tabs():
|
|
92
|
+
with gradio.Tab('Score Tokens') as __main_tab:
|
|
93
|
+
__fields.update({'main_tab': __main_tab})
|
|
94
|
+
with gradio.Row(equal_height=True):
|
|
95
|
+
__fields.update(create_inputs_block())
|
|
96
|
+
with gradio.Row(equal_height=True):
|
|
97
|
+
__fields.update(create_outputs_block())
|
|
98
|
+
with gradio.Row(equal_height=True):
|
|
99
|
+
__fields.update(create_selection_block())
|
|
100
|
+
with gradio.Row(equal_height=True):
|
|
101
|
+
__fields.update(create_actions_block())
|
|
102
|
+
with gradio.Tab('Settings') as __settings_tab:
|
|
103
|
+
__fields.update({'settings_tab': __settings_tab})
|
|
104
|
+
with gradio.Column(scale=1):
|
|
105
|
+
with gradio.Row(equal_height=True):
|
|
106
|
+
__fields.update(create_model_block())
|
|
107
|
+
with gradio.Row(equal_height=True):
|
|
108
|
+
__fields.update(create_sampling_block())
|
|
109
|
+
return __fields
|
|
110
|
+
|
|
111
|
+
# EVENTS #######################################################################
|
|
112
|
+
|
|
113
|
+
def update_layer_range(value: float, model: str) -> dict:
|
|
114
|
+
return gradio.update(maximum=35, value=min(35, int(value))) if '120b' in model else gradio.update(maximum=23, value=min(23, int(value)))
|
|
115
|
+
|
|
116
|
+
def update_position_range(value: float, tokens: float) -> dict:
|
|
117
|
+
return gradio.update(maximum=int(tokens) - 1, value=min(int(tokens) - 1, int(value)))
|
|
118
|
+
|
|
119
|
+
# APP ##########################################################################
|
|
120
|
+
|
|
121
|
+
def create_app(title: str=TITLE, intro: str=INTRO, style: str=STYLE, model: str=MODEL) -> gradio.Blocks:
|
|
122
|
+
__fields = {}
|
|
123
|
+
with gradio.Blocks(theme=gradio.themes.Soft(), title=title, css=style) as __app:
|
|
124
|
+
# load the model
|
|
125
|
+
__device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
126
|
+
# __model = psaiops.common.model.get_model(name=model, device=__device)
|
|
127
|
+
__tokenizer = psaiops.common.tokenizer.get_tokenizer(name=model, device=__device)
|
|
128
|
+
# create the UI
|
|
129
|
+
__fields.update(create_layout(intro=intro))
|
|
130
|
+
# init the state
|
|
131
|
+
__fields.update(create_state())
|
|
132
|
+
# wire the input fields
|
|
133
|
+
__fields['tokens_block'].change(
|
|
134
|
+
fn=update_position_range,
|
|
135
|
+
inputs=[__fields[__k] for __k in ['position_block', 'tokens_block']],
|
|
136
|
+
outputs=__fields['position_block'],
|
|
137
|
+
queue=False,
|
|
138
|
+
show_progress='hidden')
|
|
139
|
+
__fields['model_block'].change(
|
|
140
|
+
fn=update_layer_range,
|
|
141
|
+
inputs=[__fields[__k] for __k in ['layer_block', 'model_block']],
|
|
142
|
+
outputs=__fields['layer_block'],
|
|
143
|
+
queue=False,
|
|
144
|
+
show_progress='hidden')
|
|
145
|
+
# gradio application
|
|
146
|
+
return __app
|
|
147
|
+
|
|
148
|
+
# MAIN #########################################################################
|
|
149
|
+
|
|
150
|
+
if __name__ == '__main__':
|
|
151
|
+
__app = create_app()
|
|
152
|
+
__app.launch(share=True, debug=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import torch
|