dhuynh95 commited on
Commit
a6d925a
·
verified ·
1 Parent(s): 3750afe

Upload 2 files

Browse files
app.py ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ from transformers import AutoModelForCausalLM, AutoTokenizer
4
+ from huggingface_hub import hf_hub_download
5
+ import numpy as np
6
+ import torch
7
+ import pickle
8
+ import numpy as np
9
+ import pandas as pd
10
+ from sklearn.linear_model import LogisticRegression
11
+
12
+ torch.set_grad_enabled(False) # avoid blowing up mem
13
+
14
+ DEFAULT_EXAMPLE = text = "I really wished I could give this movie a higher rating. The plot was interesting, but the acting was terrible. The special effects were great, but the pacing was off. The movie was too long, but the ending was satisfying."
15
+
16
+ params = {
17
+ "model_name" : "google/gemma-2-9b-it",
18
+ "width" : "16k",
19
+ "layer" : 31,
20
+ "l0" : 76,
21
+ "sae_repo_id": "google/gemma-scope-9b-it-res",
22
+ "filename" : "layer_31/width_16k/average_l0_76/params.npz"
23
+ }
24
+
25
+ model_name = params["model_name"]
26
+ width = params["width"]
27
+ layer = params["layer"]
28
+ l0 = params["l0"]
29
+ sae_repo_id = params["sae_repo_id"]
30
+ filename = params["filename"]
31
+
32
+ C = 0.01
33
+
34
+ model = AutoModelForCausalLM.from_pretrained(
35
+ model_name,
36
+ device_map='auto',
37
+ torch_dtype=torch.bfloat16,
38
+ )
39
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
40
+
41
+ path_to_params = hf_hub_download(
42
+ repo_id=sae_repo_id,
43
+ filename=filename,
44
+ force_download=False,
45
+ )
46
+
47
+ params = np.load(path_to_params)
48
+ pt_params = {k: torch.from_numpy(v).cuda() for k, v in params.items()}
49
+
50
+ clf_name = f"linear_classifier_C_{C}_ "+ model_name + "_" + filename.split(".npz")[0]
51
+ clf_name = clf_name.replace(os.sep, "_")
52
+
53
+ with open(f"{clf_name}.pkl", 'rb') as model_file:
54
+ clf: LogisticRegression = pickle.load(model_file)
55
+
56
+ import torch.nn as nn
57
+ class JumpReLUSAE(nn.Module):
58
+ def __init__(self, d_model, d_sae):
59
+ # Note that we initialise these to zeros because we're loading in pre-trained weights.
60
+ # If you want to train your own SAEs then we recommend using blah
61
+ super().__init__()
62
+ self.W_enc = nn.Parameter(torch.zeros(d_model, d_sae))
63
+ self.W_dec = nn.Parameter(torch.zeros(d_sae, d_model))
64
+ self.threshold = nn.Parameter(torch.zeros(d_sae))
65
+ self.b_enc = nn.Parameter(torch.zeros(d_sae))
66
+ self.b_dec = nn.Parameter(torch.zeros(d_model))
67
+
68
+ def encode(self, input_acts):
69
+ pre_acts = input_acts @ self.W_enc + self.b_enc
70
+ mask = (pre_acts > self.threshold)
71
+ acts = mask * torch.nn.functional.relu(pre_acts)
72
+ return acts
73
+
74
+ def decode(self, acts):
75
+ return acts @ self.W_dec + self.b_dec
76
+
77
+ def forward(self, acts):
78
+ acts = self.encode(acts)
79
+ recon = self.decode(acts)
80
+ return recon
81
+
82
+ sae = JumpReLUSAE(params['W_enc'].shape[0], params['W_enc'].shape[1])
83
+ sae.load_state_dict(pt_params)
84
+ sae.to(dtype=torch.bfloat16).cuda()
85
+
86
+ @torch.no_grad()
87
+ def gather_residual_activations(model, target_layer, inputs):
88
+ target_act = None
89
+ def gather_target_act_hook(mod, inputs, outputs):
90
+ nonlocal target_act # make sure we can modify the target_act from the outer scope
91
+ target_act = outputs[0]
92
+ return outputs
93
+ handle = model.model.layers[target_layer].register_forward_hook(gather_target_act_hook)
94
+ _ = model.forward(inputs)
95
+ handle.remove()
96
+ return target_act
97
+
98
+ import requests
99
+
100
+ def get_feature_descriptions(feature):
101
+ layer_name = f"{layer}-gemmascope-res-{width}"
102
+ model_name_neuronpedia = model_name.split("/")[1]
103
+
104
+ url = f"https://www.neuronpedia.org/api/feature/{model_name_neuronpedia}/{layer_name}/{feature}"
105
+
106
+ response = requests.get(url)
107
+ output = response.json()["explanations"][0]["description"]
108
+ return output
109
+
110
+ def embed_content(url):
111
+ html_content = f"""
112
+ <div style="width:100%; height:500px; overflow:hidden;">
113
+ <iframe src="{url}" width="100%" height="100%" frameborder="0"></iframe>
114
+ </div>
115
+ """
116
+ return html_content
117
+
118
+ def dummy_function(*args):
119
+ # This is a placeholder function. Replace with your actual logic.
120
+ return "Scores will be displayed here"
121
+
122
+ examples = [
123
+ "Despite moments of promise, this film ultimately falls short of its potential. The premise intrigues, offering a fresh take on a familiar genre, but the execution stumbles in crucial areas",
124
+ ]
125
+
126
+ topk = 5
127
+
128
+ def get_features(text):
129
+
130
+ inputs = tokenizer.encode(text, return_tensors="pt", add_special_tokens=True).to("cuda")
131
+
132
+ target_act = gather_residual_activations(model, layer, inputs)
133
+ sae_act = sae.encode(target_act)
134
+ sae_act_aggregated = ((sae_act[:,:,:] > 0).sum(1) > 0).cpu().numpy()
135
+
136
+ X = pd.DataFrame(sae_act_aggregated)
137
+
138
+ feature_contributions = X.iloc[0].astype(float).values * clf.coef_[0]
139
+
140
+ contrib_df = pd.DataFrame({
141
+ 'feature': range(len(feature_contributions)),
142
+ 'contribution': feature_contributions
143
+ })
144
+
145
+ contrib_df = contrib_df.loc[contrib_df['contribution'].abs() > 0]
146
+
147
+ # Sort by absolute contribution and get top N
148
+ contrib_df = contrib_df.reindex(contrib_df['contribution'].abs().sort_values(ascending=False).index)
149
+
150
+ contrib_df = contrib_df.head(topk)
151
+ descriptions = []
152
+ for feature in contrib_df["feature"]:
153
+ description = get_feature_descriptions(feature)
154
+ print(description)
155
+ descriptions.append(description)
156
+ contrib_df["description"] = descriptions
157
+
158
+ import plotly.graph_objs as go
159
+
160
+ fig = go.Figure(go.Bar(
161
+ x=contrib_df['contribution'],
162
+ y=contrib_df['description'],
163
+ orientation='h' # Horizontal bar chart
164
+ ))
165
+
166
+ fig.update_layout(
167
+ title='Feature contribution',
168
+ xaxis_title='Contribution',
169
+ yaxis_title='Features',
170
+ height=500,
171
+ margin=dict(l=200) # Increase left margin to accommodate longer feature names
172
+ )
173
+ fig.update_yaxes(autorange="reversed")
174
+
175
+ probability = clf.predict_proba(X)[0]
176
+ classes = {
177
+ "Positive": probability[1],
178
+ "Negative": probability[0]
179
+ }
180
+
181
+ choices = [(description, feature) for description, feature in zip(contrib_df["description"], contrib_df["feature"])]
182
+ dropdown = gr.Dropdown(choices=choices,
183
+ value=choices[0][1],
184
+ interactive=True, label="Features")
185
+
186
+ return classes, fig, dropdown
187
+
188
+ def get_highlighted_text(text, feature):
189
+
190
+ inputs = tokenizer.encode(text, return_tensors="pt", add_special_tokens=True).to("cuda")
191
+
192
+ target_act = gather_residual_activations(model, layer, inputs)
193
+ sae_act = sae.encode(target_act)
194
+
195
+ activated_tokens = sae_act[0:,:,feature]
196
+ max_activation = activated_tokens.max().item()
197
+ activated_tokens /= max_activation
198
+
199
+ activated_tokens = activated_tokens.cpu().detach().float().numpy()
200
+
201
+ output = []
202
+
203
+ for i, token_id in enumerate(inputs[0, :]):
204
+ token = tokenizer.decode(token_id)
205
+ output.append((token, activated_tokens[0, i]))
206
+
207
+ return output
208
+
209
+ def get_feature_iframe(feature):
210
+ layer_name = f"{layer}-gemmascope-res-{width}"
211
+ model_name_neuronpedia = model_name.split("/")[1]
212
+ url = f"https://neuronpedia.org/{model_name_neuronpedia}/{layer_name}/{feature}?embed=true"
213
+
214
+ html_content = embed_content(url)
215
+ html = gr.HTML(html_content)
216
+ return html
217
+
218
+ with gr.Blocks() as demo:
219
+ with gr.Row():
220
+ with gr.Column(scale=4):
221
+ input_text = gr.Textbox(label="Input", show_label=False, value=DEFAULT_EXAMPLE)
222
+ gr.Examples(
223
+ examples=examples,
224
+ inputs=input_text,
225
+ )
226
+ with gr.Column(scale=1):
227
+ run_button = gr.Button("Run")
228
+
229
+ with gr.Row():
230
+ label = gr.Label(label="Scores")
231
+
232
+ with gr.Row():
233
+ with gr.Column(scale=1):
234
+ plot = gr.Plot(label="Plot")
235
+ dropdown = gr.Dropdown(choices=["Option 1"], label="Features")
236
+
237
+ with gr.Column(scale=1):
238
+ highlighted_text = gr.HighlightedText(
239
+ label="Activating Tokens",
240
+ combine_adjacent=True,
241
+ show_legend=True,
242
+ color_map={"+": "red", "-": "green"})
243
+
244
+ with gr.Row():
245
+ html = gr.HTML()
246
+
247
+ # Connect the components
248
+ run_button.click(
249
+ fn=get_features,
250
+ inputs=[input_text],
251
+ outputs=[label, plot, dropdown],
252
+ ).then(
253
+ fn=get_highlighted_text,
254
+ inputs=[input_text, dropdown],
255
+ outputs=[highlighted_text]
256
+ ).then(
257
+ fn=get_feature_iframe,
258
+ inputs=[dropdown],
259
+ outputs=[html]
260
+ )
261
+
262
+ dropdown.change(
263
+ fn=get_highlighted_text,
264
+ inputs=[input_text, dropdown],
265
+ outputs=[highlighted_text]
266
+ ).then(
267
+ fn=get_feature_iframe,
268
+ inputs=[dropdown],
269
+ outputs=[html]
270
+ )
271
+
272
+ demo.launch(share=True)
linear_classifier_C_0.01_ google_gemma-2-9b-it_layer_31_width_16k_average_l0_76_params.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:facb7f240c7957bd7c2ebf5d4f472e100eefe20c2a498e0fbc35e25754bf9283
3
+ size 131805