ryanyen22 commited on
Commit
fddaf33
·
verified ·
1 Parent(s): 5d719ee

Add utils/model_utils.py

Browse files
Files changed (1) hide show
  1. utils/model_utils.py +133 -0
utils/model_utils.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Model loading and tokenization utilities.
3
+
4
+ Supports:
5
+ - Local loading with optional quantization (4-bit, 8-bit)
6
+ - Multiple model sizes (8B for prototyping, 70B for production)
7
+ - Consistent tokenization across scenarios
8
+ """
9
+
10
+ import torch
11
+ from typing import Optional, Dict, Any, Tuple
12
+
13
+
14
+ def load_model_and_tokenizer(
15
+ model_name: str = "meta-llama/Meta-Llama-3.1-8B-Instruct",
16
+ quantize: Optional[str] = None, # '4bit', '8bit', None
17
+ device_map: str = "auto",
18
+ attn_implementation: Optional[str] = None,
19
+ ) -> Tuple[Any, Any]:
20
+ """
21
+ Load a HuggingFace model and tokenizer.
22
+
23
+ Args:
24
+ model_name: HF model ID
25
+ quantize: '4bit', '8bit', or None for full precision
26
+ device_map: device placement strategy
27
+ attn_implementation: 'flash_attention_2', 'sdpa', or None
28
+
29
+ Returns:
30
+ (model, tokenizer) tuple
31
+ """
32
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
33
+
34
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
35
+ if tokenizer.pad_token is None:
36
+ tokenizer.pad_token = tokenizer.eos_token
37
+
38
+ kwargs: Dict[str, Any] = {
39
+ "device_map": device_map,
40
+ "torch_dtype": torch.bfloat16,
41
+ }
42
+
43
+ if quantize == "4bit":
44
+ kwargs["quantization_config"] = BitsAndBytesConfig(
45
+ load_in_4bit=True,
46
+ bnb_4bit_compute_dtype=torch.bfloat16,
47
+ bnb_4bit_use_double_quant=True,
48
+ bnb_4bit_quant_type="nf4",
49
+ )
50
+ elif quantize == "8bit":
51
+ kwargs["quantization_config"] = BitsAndBytesConfig(load_in_8bit=True)
52
+
53
+ if attn_implementation:
54
+ kwargs["attn_implementation"] = attn_implementation
55
+
56
+ model = AutoModelForCausalLM.from_pretrained(model_name, **kwargs)
57
+ model.eval()
58
+
59
+ return model, tokenizer
60
+
61
+
62
+ def get_token_ids(tokenizer, tokens: list) -> Dict[str, int]:
63
+ """
64
+ Get token IDs for a list of target tokens (aggregation functions).
65
+ Handles multi-token cases by returning the first token.
66
+ """
67
+ token_ids = {}
68
+ for token in tokens:
69
+ # Try with and without leading space
70
+ for variant in [token, f" {token}", f" {token.upper()}", token.upper()]:
71
+ ids = tokenizer.encode(variant, add_special_tokens=False)
72
+ if len(ids) >= 1:
73
+ token_ids[token] = ids[0]
74
+ break
75
+ return token_ids
76
+
77
+
78
+ def get_logit_probs(model, tokenizer, prompt: str, target_tokens: list) -> Dict[str, float]:
79
+ """
80
+ Get probability distribution over target tokens at the next-token position.
81
+
82
+ Args:
83
+ model: loaded HF model
84
+ tokenizer: corresponding tokenizer
85
+ prompt: input prompt text
86
+ target_tokens: list of target completions (e.g., ['MAX', 'AVG', 'MEDIAN'])
87
+
88
+ Returns:
89
+ Dict mapping token -> probability
90
+ """
91
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
92
+
93
+ with torch.no_grad():
94
+ outputs = model(**inputs)
95
+ logits = outputs.logits[0, -1, :] # last token position
96
+
97
+ token_ids = get_token_ids(tokenizer, target_tokens)
98
+
99
+ # Extract logits for target tokens
100
+ target_logits = torch.tensor([logits[tid].item() for tid in token_ids.values()])
101
+ probs = torch.softmax(target_logits, dim=0)
102
+
103
+ result = {}
104
+ for (token, _), prob in zip(token_ids.items(), probs):
105
+ result[token] = prob.item()
106
+
107
+ return result
108
+
109
+
110
+ def get_logit_difference(
111
+ model, tokenizer, prompt: str,
112
+ positive_token: str, negative_token: str
113
+ ) -> float:
114
+ """
115
+ Compute logit difference: logit(positive) - logit(negative).
116
+
117
+ This is the primary metric for circuit analysis:
118
+ - Positive values → model prefers positive_token
119
+ - Negative values → model prefers negative_token
120
+ """
121
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
122
+
123
+ with torch.no_grad():
124
+ outputs = model(**inputs)
125
+ logits = outputs.logits[0, -1, :]
126
+
127
+ pos_ids = get_token_ids(tokenizer, [positive_token])
128
+ neg_ids = get_token_ids(tokenizer, [negative_token])
129
+
130
+ pos_logit = logits[list(pos_ids.values())[0]]
131
+ neg_logit = logits[list(neg_ids.values())[0]]
132
+
133
+ return (pos_logit - neg_logit).item()