ejschwartz commited on
Commit
e1f9aa2
·
1 Parent(s): acd88b4
Files changed (3) hide show
  1. README.md +2 -0
  2. app.py +124 -0
  3. requirements.txt +5 -0
README.md CHANGED
@@ -9,4 +9,6 @@ app_file: app.py
9
  pinned: false
10
  ---
11
 
 
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
9
  pinned: false
10
  ---
11
 
12
+ This Space is set up for ZeroGPU via the `@spaces.GPU` decorator in `app.py`. Select ZeroGPU as the hardware in your Space settings.
13
+
14
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import math
3
+ import spaces
4
+ import torch
5
+ import gradio as gr
6
+ from transformers import AutoTokenizer, AutoModelForCausalLM
7
+
8
+ MODEL_ID = os.getenv("MODEL_ID", "Qwen/Qwen3-4B")
9
+
10
+
11
+ def load_model():
12
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
13
+ # Ensure a pad token exists for safe batching; use eos if needed
14
+ if tokenizer.pad_token is None:
15
+ tokenizer.pad_token = tokenizer.eos_token
16
+
17
+ model = AutoModelForCausalLM.from_pretrained(
18
+ MODEL_ID,
19
+ torch_dtype="auto",
20
+ device_map="auto",
21
+ trust_remote_code=True,
22
+ )
23
+ model.eval()
24
+ return tokenizer, model
25
+
26
+
27
+ TOKENIZER, MODEL = load_model()
28
+
29
+
30
+ @spaces.GPU
31
+ def compute_entropy(code: str):
32
+ if not code or not code.strip():
33
+ return "Please paste some source code.", None
34
+
35
+ with torch.no_grad():
36
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
37
+ if next(MODEL.parameters()).device != device:
38
+ MODEL.to(device)
39
+
40
+ enc = TOKENIZER(code, return_tensors="pt")
41
+ input_ids = enc["input_ids"]
42
+ attention_mask = enc.get("attention_mask")
43
+
44
+ input_ids = input_ids.to(device)
45
+ if attention_mask is not None:
46
+ attention_mask = attention_mask.to(device)
47
+
48
+ # Need at least 2 tokens to compute next-token NLL
49
+ if input_ids.shape[1] < 2:
50
+ return "Input is too short to compute token-level entropy.", None
51
+
52
+ outputs = MODEL(input_ids=input_ids, attention_mask=attention_mask)
53
+ logits = outputs.logits
54
+
55
+ # Shift for next-token prediction
56
+ shift_logits = logits[:, :-1, :]
57
+ shift_labels = input_ids[:, 1:]
58
+
59
+ log_probs = torch.log_softmax(shift_logits, dim=-1)
60
+ # Gather log prob of the true next token
61
+ true_log_probs = log_probs.gather(2, shift_labels.unsqueeze(-1)).squeeze(-1)
62
+ nll = -true_log_probs # negative log-likelihood (nats)
63
+
64
+ nll_list = nll.squeeze(0).detach().cpu().tolist()
65
+ label_ids = shift_labels.squeeze(0).detach().cpu().tolist()
66
+ tokens = TOKENIZER.convert_ids_to_tokens(label_ids)
67
+
68
+ rows = []
69
+ for tok, nll_val in zip(tokens, nll_list):
70
+ prob = math.exp(-nll_val)
71
+ rows.append([tok, float(nll_val), float(prob)])
72
+
73
+ avg_nll = sum(nll_list) / len(nll_list)
74
+ avg_bits = avg_nll / math.log(2)
75
+ summary = (
76
+ f"Tokens evaluated: {len(nll_list)}\n"
77
+ f"Average NLL (nats): {avg_nll:.4f}\n"
78
+ f"Average NLL (bits): {avg_bits:.4f}"
79
+ )
80
+
81
+ return summary, rows
82
+
83
+
84
+ def build_app():
85
+ with gr.Blocks(title="Entropy for Source Code", theme=gr.themes.Soft()) as demo:
86
+ gr.Markdown(
87
+ """
88
+ # Source Code Entropy (Qwen3-4B)
89
+
90
+ Paste code below to compute token-level negative log-likelihood (NLL).
91
+ The table shows each token's NLL and probability under the model.
92
+ """
93
+ )
94
+
95
+ code = gr.Textbox(
96
+ label="Source Code",
97
+ lines=16,
98
+ placeholder="Paste your source code here...",
99
+ )
100
+ btn = gr.Button("Compute Entropy")
101
+ summary = gr.Textbox(label="Summary", lines=4)
102
+ table = gr.Dataframe(
103
+ headers=["token", "nll_nats", "prob"],
104
+ datatype=["str", "number", "number"],
105
+ label="Token-level NLL",
106
+ )
107
+
108
+ btn.click(fn=compute_entropy, inputs=[code], outputs=[summary, table])
109
+
110
+ gr.Markdown(
111
+ """
112
+ Notes:
113
+ - NLL is computed for next-token prediction and excludes the first token.
114
+ - Large inputs may take time to process depending on hardware.
115
+ """
116
+ )
117
+
118
+ return demo
119
+
120
+
121
+ app = build_app()
122
+
123
+ if __name__ == "__main__":
124
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ transformers>=4.45.0
2
+ accelerate>=0.34.0
3
+ torch>=2.2.0
4
+ gradio>=6.5.1
5
+ spaces>=0.28.0