wshuai190 commited on
Commit
884ff12
·
verified ·
1 Parent(s): d92e443

Add self-contained DiffRetriever (trust_remote_code: code + config + adapter/)

Browse files
README.md ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model: Dream-org/Dream-v0-Instruct-7B
3
+ library_name: peft
4
+ tags:
5
+ - base_model:adapter:Dream-org/Dream-v0-Instruct-7B
6
+ - lora
7
+ - transformers
8
+ ---
9
+
10
+ # Model Card for Model ID
11
+
12
+ <!-- Provide a quick summary of what the model is/does. -->
13
+
14
+
15
+
16
+ ## Model Details
17
+
18
+ ### Model Description
19
+
20
+ <!-- Provide a longer summary of what this model is. -->
21
+
22
+
23
+
24
+ - **Developed by:** [More Information Needed]
25
+ - **Funded by [optional]:** [More Information Needed]
26
+ - **Shared by [optional]:** [More Information Needed]
27
+ - **Model type:** [More Information Needed]
28
+ - **Language(s) (NLP):** [More Information Needed]
29
+ - **License:** [More Information Needed]
30
+ - **Finetuned from model [optional]:** [More Information Needed]
31
+
32
+ ### Model Sources [optional]
33
+
34
+ <!-- Provide the basic links for the model. -->
35
+
36
+ - **Repository:** [More Information Needed]
37
+ - **Paper [optional]:** [More Information Needed]
38
+ - **Demo [optional]:** [More Information Needed]
39
+
40
+ ## Uses
41
+
42
+ <!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->
43
+
44
+ ### Direct Use
45
+
46
+ <!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->
47
+
48
+ [More Information Needed]
49
+
50
+ ### Downstream Use [optional]
51
+
52
+ <!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->
53
+
54
+ [More Information Needed]
55
+
56
+ ### Out-of-Scope Use
57
+
58
+ <!-- This section addresses misuse, malicious use, and uses that the model will not work well for. -->
59
+
60
+ [More Information Needed]
61
+
62
+ ## Bias, Risks, and Limitations
63
+
64
+ <!-- This section is meant to convey both technical and sociotechnical limitations. -->
65
+
66
+ [More Information Needed]
67
+
68
+ ### Recommendations
69
+
70
+ <!-- This section is meant to convey recommendations with respect to the bias, risk, and technical limitations. -->
71
+
72
+ Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.
73
+
74
+ ## How to Get Started with the Model
75
+
76
+ Use the code below to get started with the model.
77
+
78
+ [More Information Needed]
79
+
80
+ ## Training Details
81
+
82
+ ### Training Data
83
+
84
+ <!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->
85
+
86
+ [More Information Needed]
87
+
88
+ ### Training Procedure
89
+
90
+ <!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->
91
+
92
+ #### Preprocessing [optional]
93
+
94
+ [More Information Needed]
95
+
96
+
97
+ #### Training Hyperparameters
98
+
99
+ - **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->
100
+
101
+ #### Speeds, Sizes, Times [optional]
102
+
103
+ <!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->
104
+
105
+ [More Information Needed]
106
+
107
+ ## Evaluation
108
+
109
+ <!-- This section describes the evaluation protocols and provides the results. -->
110
+
111
+ ### Testing Data, Factors & Metrics
112
+
113
+ #### Testing Data
114
+
115
+ <!-- This should link to a Dataset Card if possible. -->
116
+
117
+ [More Information Needed]
118
+
119
+ #### Factors
120
+
121
+ <!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->
122
+
123
+ [More Information Needed]
124
+
125
+ #### Metrics
126
+
127
+ <!-- These are the evaluation metrics being used, ideally with a description of why. -->
128
+
129
+ [More Information Needed]
130
+
131
+ ### Results
132
+
133
+ [More Information Needed]
134
+
135
+ #### Summary
136
+
137
+
138
+
139
+ ## Model Examination [optional]
140
+
141
+ <!-- Relevant interpretability work for the model goes here -->
142
+
143
+ [More Information Needed]
144
+
145
+ ## Environmental Impact
146
+
147
+ <!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->
148
+
149
+ Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).
150
+
151
+ - **Hardware Type:** [More Information Needed]
152
+ - **Hours used:** [More Information Needed]
153
+ - **Cloud Provider:** [More Information Needed]
154
+ - **Compute Region:** [More Information Needed]
155
+ - **Carbon Emitted:** [More Information Needed]
156
+
157
+ ## Technical Specifications [optional]
158
+
159
+ ### Model Architecture and Objective
160
+
161
+ [More Information Needed]
162
+
163
+ ### Compute Infrastructure
164
+
165
+ [More Information Needed]
166
+
167
+ #### Hardware
168
+
169
+ [More Information Needed]
170
+
171
+ #### Software
172
+
173
+ [More Information Needed]
174
+
175
+ ## Citation [optional]
176
+
177
+ <!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->
178
+
179
+ **BibTeX:**
180
+
181
+ [More Information Needed]
182
+
183
+ **APA:**
184
+
185
+ [More Information Needed]
186
+
187
+ ## Glossary [optional]
188
+
189
+ <!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->
190
+
191
+ [More Information Needed]
192
+
193
+ ## More Information [optional]
194
+
195
+ [More Information Needed]
196
+
197
+ ## Model Card Authors [optional]
198
+
199
+ [More Information Needed]
200
+
201
+ ## Model Card Contact
202
+
203
+ [More Information Needed]
204
+ ### Framework versions
205
+
206
+ - PEFT 0.18.1
adapter/adapter_config.json ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alora_invocation_tokens": null,
3
+ "alpha_pattern": {},
4
+ "arrow_config": null,
5
+ "auto_mapping": null,
6
+ "base_model_name_or_path": "Dream-org/Dream-v0-Instruct-7B",
7
+ "bias": "none",
8
+ "corda_config": null,
9
+ "ensure_weight_tying": false,
10
+ "eva_config": null,
11
+ "exclude_modules": null,
12
+ "fan_in_fan_out": false,
13
+ "inference_mode": true,
14
+ "init_lora_weights": true,
15
+ "layer_replication": null,
16
+ "layers_pattern": null,
17
+ "layers_to_transform": null,
18
+ "loftq_config": {},
19
+ "lora_alpha": 64,
20
+ "lora_bias": false,
21
+ "lora_dropout": 0.05,
22
+ "megatron_config": null,
23
+ "megatron_core": "megatron.core",
24
+ "modules_to_save": null,
25
+ "peft_type": "LORA",
26
+ "peft_version": "0.18.1",
27
+ "qalora_group_size": 16,
28
+ "r": 16,
29
+ "rank_pattern": {},
30
+ "revision": null,
31
+ "target_modules": [
32
+ "v_proj",
33
+ "q_proj",
34
+ "k_proj",
35
+ "o_proj",
36
+ "up_proj",
37
+ "gate_proj",
38
+ "down_proj"
39
+ ],
40
+ "target_parameters": null,
41
+ "task_type": "FEATURE_EXTRACTION",
42
+ "trainable_token_indices": null,
43
+ "use_dora": false,
44
+ "use_qalora": false,
45
+ "use_rslora": false
46
+ }
adapter/adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2963a476388a17cf51e47c1141d00ff7c9fb8bd575459862d632cf9749becfac
3
+ size 80792880
added_tokens.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "</tool_call>": 151658,
3
+ "<tool_call>": 151657,
4
+ "<|beginoftext|>": 151665,
5
+ "<|box_end|>": 151649,
6
+ "<|box_start|>": 151648,
7
+ "<|endoftext|>": 151643,
8
+ "<|file_sep|>": 151664,
9
+ "<|fim_middle|>": 151660,
10
+ "<|fim_pad|>": 151662,
11
+ "<|fim_prefix|>": 151659,
12
+ "<|fim_suffix|>": 151661,
13
+ "<|im_end|>": 151645,
14
+ "<|im_start|>": 151644,
15
+ "<|image_pad|>": 151655,
16
+ "<|mask|>": 151666,
17
+ "<|object_ref_end|>": 151647,
18
+ "<|object_ref_start|>": 151646,
19
+ "<|quad_end|>": 151651,
20
+ "<|quad_start|>": 151650,
21
+ "<|repo_name|>": 151663,
22
+ "<|video_pad|>": 151656,
23
+ "<|vision_end|>": 151653,
24
+ "<|vision_pad|>": 151654,
25
+ "<|vision_start|>": 151652
26
+ }
backbone_adapters.py ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Backbone adapters for diffusion retriever training.
3
+
4
+ Each adapter encapsulates ALL model-specific behavior in one place:
5
+ - How to load the backbone (AutoModel vs AutoModelForCausalLM)
6
+ - PEFT/LoRA configuration (target modules, task type)
7
+ - Attention mask format (2D vs 4D)
8
+ - Hidden state extraction (forward hook on output projection)
9
+ - Mask token ID (verified from HuggingFace tokenizer configs)
10
+ - Gradient checkpointing support
11
+
12
+ The TrainableDiffusionRetriever delegates to an adapter and has ZERO
13
+ model-specific branches. Adding a new model = adding one adapter class.
14
+ """
15
+
16
+ from abc import ABC, abstractmethod
17
+ from typing import Dict, Optional, Tuple
18
+ import logging
19
+
20
+ import torch
21
+ import torch.nn as nn
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Base adapter
28
+ # ---------------------------------------------------------------------------
29
+
30
+ class BackboneAdapter(ABC):
31
+ """Abstract interface for model-specific backbone behavior."""
32
+
33
+ model_type: str # e.g. 'dream', 'llada1', 'llada2'
34
+ mask_token_id: int # verified from HuggingFace model cards
35
+ hub_model_name: str # HuggingFace model ID for fallback loading
36
+
37
+ def __init__(self):
38
+ self.flash_attn: bool = False # set by load_backbone
39
+
40
+ # -- loading --------------------------------------------------------
41
+
42
+ def load_backbone(self, source: str, device_map=None) -> nn.Module:
43
+ """Load backbone from HuggingFace model name or local directory.
44
+
45
+ Tries flash attention variants in order, falls back to eager.
46
+ Sets ``self.flash_attn`` as a side effect.
47
+ """
48
+ common_kw = dict(trust_remote_code=True, torch_dtype=torch.bfloat16)
49
+ if device_map is not None:
50
+ common_kw['device_map'] = device_map
51
+
52
+ for attn_impl in self._flash_attn_impls:
53
+ try:
54
+ bb = self._auto_class().from_pretrained(
55
+ source, attn_implementation=attn_impl, **common_kw)
56
+ self.flash_attn = True
57
+ logger.info(f"{self.model_type}: {attn_impl} enabled")
58
+ return bb
59
+ except (ValueError, ImportError):
60
+ pass
61
+
62
+ self.flash_attn = False
63
+ return self._auto_class().from_pretrained(source, **common_kw)
64
+
65
+ @staticmethod
66
+ @abstractmethod
67
+ def _auto_class():
68
+ """Return the AutoModel class to use (AutoModel or AutoModelForCausalLM)."""
69
+
70
+ _flash_attn_impls: Tuple[str, ...] = ('flash_attention_2',)
71
+
72
+ # -- PEFT / LoRA ----------------------------------------------------
73
+
74
+ @abstractmethod
75
+ def get_lora_config(self, lora_rank: int, lora_alpha: int,
76
+ lora_dropout: float = 0.0):
77
+ """Return a ``peft.LoraConfig`` appropriate for this backbone."""
78
+
79
+ # -- attention mask --------------------------------------------------
80
+
81
+ @abstractmethod
82
+ def needs_4d_mask(self) -> bool:
83
+ """Whether the backbone expects a 4D ``[B,1,L,L]`` attention mask.
84
+
85
+ If False, the backbone handles bidirectional attention internally
86
+ and expects a standard 2D ``[B,L]`` padding mask.
87
+ """
88
+
89
+ # -- hidden state extraction -----------------------------------------
90
+
91
+ def register_hidden_hook(self, backbone: nn.Module,
92
+ ref_dict: Dict[str, torch.Tensor]) -> bool:
93
+ """Register a forward hook on the output projection to capture the
94
+ last hidden state without ``output_hidden_states=True``.
95
+
96
+ Returns True if a hook was registered, False otherwise (in which
97
+ case the caller should fall back to ``output_hidden_states``).
98
+ """
99
+ return False # default: no hook, use output_hidden_states
100
+
101
+ # -- gradient checkpointing ------------------------------------------
102
+
103
+ def enable_gradient_checkpointing(self, backbone: nn.Module, **kwargs):
104
+ """Enable gradient checkpointing. Override for models that don't
105
+ support it."""
106
+ backbone.gradient_checkpointing_enable(**kwargs)
107
+ logger.info("Gradient checkpointing enabled")
108
+
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # Hook helpers (shared across adapters)
112
+ # ---------------------------------------------------------------------------
113
+
114
+ def _is_linear(mod: nn.Module) -> bool:
115
+ """Check if a module is a Linear layer (plain or LoRA-wrapped)."""
116
+ if isinstance(mod, nn.Linear):
117
+ return True
118
+ # PEFT LoRA wraps nn.Linear in peft.tuners.lora.layer.Linear which is
119
+ # NOT a subclass of nn.Linear, but has a base_layer that is.
120
+ if hasattr(mod, 'base_layer') and isinstance(mod.base_layer, nn.Linear):
121
+ return True
122
+ return False
123
+
124
+
125
+ def _hook_on_module(backbone: nn.Module, ref_dict: Dict,
126
+ target_name: str, skip_if_contains: Optional[str] = None,
127
+ adapter_name: str = '') -> bool:
128
+ """Register a forward hook on the first Linear (or LoRA-wrapped Linear)
129
+ whose leaf name matches *target_name*. Optionally skip modules whose
130
+ full path contains *skip_if_contains* (e.g. 'blocks' to skip per-layer
131
+ ff_out).
132
+ """
133
+ for name, mod in backbone.named_modules():
134
+ leaf = name.split('.')[-1]
135
+ if leaf == target_name and _is_linear(mod):
136
+ if skip_if_contains and skip_if_contains in name:
137
+ continue
138
+ mod.register_forward_hook(
139
+ lambda m, inp, out, r=ref_dict: r.update({'h': inp[0]})
140
+ )
141
+ logger.info(f"{adapter_name}: hook on '{name}'")
142
+ return True
143
+ return False
144
+
145
+
146
+ # ---------------------------------------------------------------------------
147
+ # Dream
148
+ # ---------------------------------------------------------------------------
149
+
150
+ class DreamAdapter(BackboneAdapter):
151
+ model_type = 'dream'
152
+ mask_token_id = 151666 # <|mask|> — Dream-org/Dream-v0-Instruct-7B
153
+ hub_model_name = 'Dream-org/Dream-v0-Instruct-7B'
154
+
155
+ @staticmethod
156
+ def _auto_class():
157
+ from transformers import AutoModel
158
+ return AutoModel
159
+
160
+ def get_lora_config(self, lora_rank, lora_alpha, lora_dropout=0.0):
161
+ from peft import LoraConfig, TaskType
162
+ # Dream is loaded via AutoModel (not AutoModelForCausalLM), so
163
+ # FEATURE_EXTRACTION avoids PeftModelForCausalLM which would
164
+ # require prepare_inputs_for_generation (Dream lacks this).
165
+ return LoraConfig(
166
+ r=lora_rank, lora_alpha=lora_alpha, lora_dropout=lora_dropout,
167
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
168
+ "gate_proj", "up_proj", "down_proj"],
169
+ task_type=TaskType.FEATURE_EXTRACTION,
170
+ bias="none",
171
+ )
172
+
173
+ def needs_4d_mask(self) -> bool:
174
+ # Standard HF Qwen2 attention — needs 4D to enforce bidirectional.
175
+ return True
176
+
177
+ def register_hidden_hook(self, backbone, ref_dict):
178
+ return _hook_on_module(backbone, ref_dict, 'lm_head',
179
+ adapter_name='dream')
180
+
181
+
182
+ # ---------------------------------------------------------------------------
183
+ # LLaDA v1 (GSAI-ML/LLaDA-8B-Instruct)
184
+ # ---------------------------------------------------------------------------
185
+
186
+ class LLaDA1Adapter(BackboneAdapter):
187
+ model_type = 'llada1'
188
+ mask_token_id = 126336 # <|mdm_mask|> — GSAI-ML/LLaDA-8B-Instruct
189
+ hub_model_name = 'GSAI-ML/LLaDA-8B-Instruct'
190
+ _flash_attn_impls = ('flash_attention_3', 'flash_attention_2')
191
+
192
+ @staticmethod
193
+ def _auto_class():
194
+ from transformers import AutoModelForCausalLM
195
+ return AutoModelForCausalLM
196
+
197
+ def get_lora_config(self, lora_rank, lora_alpha, lora_dropout=0.0):
198
+ from peft import LoraConfig, TaskType
199
+ # LLaDA1 custom arch uses different names than standard LLaMA:
200
+ # attn_out (not o_proj), ff_proj (not gate_proj), ff_out (not down_proj)
201
+ return LoraConfig(
202
+ r=lora_rank, lora_alpha=lora_alpha, lora_dropout=lora_dropout,
203
+ target_modules=["q_proj", "k_proj", "v_proj", "attn_out",
204
+ "ff_proj", "up_proj", "ff_out"],
205
+ task_type=TaskType.CAUSAL_LM,
206
+ bias="none",
207
+ )
208
+
209
+ def needs_4d_mask(self) -> bool:
210
+ # Custom code handles bidirectional attention internally — always 2D.
211
+ return False
212
+
213
+ def register_hidden_hook(self, backbone, ref_dict):
214
+ # LLaDA1 has ff_out per-block (blocks.X.ff_out = FFN output) AND at
215
+ # the model level (transformer.ff_out = output projection to vocab).
216
+ # We need the model-level one; skip per-block ones.
217
+ return _hook_on_module(backbone, ref_dict, 'ff_out',
218
+ skip_if_contains='blocks',
219
+ adapter_name='llada1')
220
+
221
+ def enable_gradient_checkpointing(self, backbone, **kwargs):
222
+ # LLaDA1's LLaDAModelLM doesn't support HF gradient_checkpointing_enable.
223
+ # Manually wrap each transformer block with torch checkpoint.
224
+ from torch.utils.checkpoint import checkpoint as ckpt_fn
225
+
226
+ # Find the blocks ModuleList through the PEFT wrapper
227
+ blocks = None
228
+ for name, mod in backbone.named_modules():
229
+ if name.endswith('.blocks') and isinstance(mod, nn.ModuleList):
230
+ blocks = mod
231
+ break
232
+
233
+ if blocks is None:
234
+ logger.warning("LLaDA1: couldn't find transformer blocks — "
235
+ "skipping gradient checkpointing")
236
+ return
237
+
238
+ for block in blocks:
239
+ orig_forward = block.forward
240
+
241
+ def _make_ckpt(fwd):
242
+ def _ckpt_forward(*args, **kwargs):
243
+ if not torch.is_grad_enabled():
244
+ return fwd(*args, **kwargs)
245
+ return ckpt_fn(fwd, *args, use_reentrant=False, **kwargs)
246
+ return _ckpt_forward
247
+
248
+ block.forward = _make_ckpt(orig_forward)
249
+
250
+ logger.info(f"LLaDA1: manual gradient checkpointing on {len(blocks)} blocks")
251
+
252
+
253
+ # ---------------------------------------------------------------------------
254
+ # LLaDA v1.5 (GSAI-ML/LLaDA-1.5) — same architecture as v1
255
+ # ---------------------------------------------------------------------------
256
+
257
+ class LLaDA15Adapter(LLaDA1Adapter):
258
+ model_type = 'llada15'
259
+ mask_token_id = 126336 # <|mdm_mask|> — same tokenizer as v1
260
+ hub_model_name = 'GSAI-ML/LLaDA-1.5'
261
+
262
+ def register_hidden_hook(self, backbone, ref_dict):
263
+ return _hook_on_module(backbone, ref_dict, 'ff_out',
264
+ skip_if_contains='blocks',
265
+ adapter_name='llada15')
266
+
267
+
268
+
269
+ # ---------------------------------------------------------------------------
270
+ # LLaDA v2 (inclusionAI/LLaDA2.0-mini)
271
+ # ---------------------------------------------------------------------------
272
+
273
+ class LLaDA2Adapter(BackboneAdapter):
274
+ model_type = 'llada2'
275
+ mask_token_id = 156895 # <|mask|> — inclusionAI/LLaDA2.0-mini
276
+ hub_model_name = 'inclusionAI/LLaDA2.0-mini'
277
+ _flash_attn_impls = ('flash_attention_3', 'flash_attention_2')
278
+
279
+ @staticmethod
280
+ def _auto_class():
281
+ from transformers import AutoModelForCausalLM
282
+ return AutoModelForCausalLM
283
+
284
+ def get_lora_config(self, lora_rank, lora_alpha, lora_dropout=0.0):
285
+ from peft import LoraConfig, TaskType
286
+ # LLaDA2 uses fused QKV ("query_key_value") and "dense" for attn
287
+ # output. Skip MoE expert FFN layers to avoid multiplying params.
288
+ return LoraConfig(
289
+ r=lora_rank, lora_alpha=lora_alpha, lora_dropout=lora_dropout,
290
+ target_modules=[
291
+ "query_key_value", "dense",
292
+ "mlp.shared_experts.gate_proj",
293
+ "mlp.shared_experts.up_proj",
294
+ "mlp.shared_experts.down_proj",
295
+ ],
296
+ task_type=TaskType.CAUSAL_LM,
297
+ bias="none",
298
+ )
299
+
300
+ def needs_4d_mask(self) -> bool:
301
+ # Standard HF causal model — needs 4D to override causal attention,
302
+ # unless flash attention handles masking itself.
303
+ return not self.flash_attn
304
+
305
+ def register_hidden_hook(self, backbone, ref_dict):
306
+ return _hook_on_module(backbone, ref_dict, 'lm_head',
307
+ adapter_name='llada2')
308
+
309
+
310
+ # ---------------------------------------------------------------------------
311
+ # Registry
312
+ # ---------------------------------------------------------------------------
313
+
314
+ ADAPTER_REGISTRY: Dict[str, type] = {
315
+ 'dream': DreamAdapter,
316
+ 'llada1': LLaDA1Adapter,
317
+ 'llada15': LLaDA15Adapter,
318
+ 'llada2': LLaDA2Adapter,
319
+ }
320
+
321
+
322
+ def get_adapter(model_type: str) -> BackboneAdapter:
323
+ """Create a BackboneAdapter for the given model_type."""
324
+ cls = ADAPTER_REGISTRY.get(model_type)
325
+ if cls is None:
326
+ raise ValueError(
327
+ f"Unknown model_type: {model_type!r}. "
328
+ f"Available: {sorted(ADAPTER_REGISTRY.keys())}")
329
+ return cls()
chat_template.jinja ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- if tools %}
2
+ {{- '<|im_start|>system\n' }}
3
+ {%- if messages[0]['role'] == 'system' %}
4
+ {{- messages[0]['content'] }}
5
+ {%- else %}
6
+ {{- 'You are a helpful assistant.' }}
7
+ {%- endif %}
8
+ {{- "\n\n# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
9
+ {%- for tool in tools %}
10
+ {{- "\n" }}
11
+ {{- tool | tojson }}
12
+ {%- endfor %}
13
+ {{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
14
+ {%- else %}
15
+ {%- if messages[0]['role'] == 'system' %}
16
+ {{- '<|im_start|>system\n' + messages[0]['content'] + '<|im_end|>\n' }}
17
+ {%- else %}
18
+ {{- '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n' }}
19
+ {%- endif %}
20
+ {%- endif %}
21
+ {%- for message in messages %}
22
+ {%- if (message.role == "user") or (message.role == "system" and not loop.first) or (message.role == "assistant" and not message.tool_calls) %}
23
+ {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }}
24
+ {%- elif message.role == "assistant" %}
25
+ {{- '<|im_start|>' + message.role }}
26
+ {%- if message.content %}
27
+ {{- '\n' + message.content }}
28
+ {%- endif %}
29
+ {%- for tool_call in message.tool_calls %}
30
+ {%- if tool_call.function is defined %}
31
+ {%- set tool_call = tool_call.function %}
32
+ {%- endif %}
33
+ {{- '\n<tool_call>\n{"name": "' }}
34
+ {{- tool_call.name }}
35
+ {{- '", "arguments": ' }}
36
+ {{- tool_call.arguments | tojson }}
37
+ {{- '}\n</tool_call>' }}
38
+ {%- endfor %}
39
+ {{- '<|im_end|>\n' }}
40
+ {%- elif message.role == "tool" %}
41
+ {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != "tool") %}
42
+ {{- '<|im_start|>user' }}
43
+ {%- endif %}
44
+ {{- '\n<tool_response>\n' }}
45
+ {{- message.content }}
46
+ {{- '\n</tool_response>' }}
47
+ {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
48
+ {{- '<|im_end|>\n' }}
49
+ {%- endif %}
50
+ {%- endif %}
51
+ {%- endfor %}
52
+ {%- if add_generation_prompt %}
53
+ {{- '<|im_start|>assistant\n' }}
54
+ {%- endif %}
config.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "diffretriever",
3
+ "architectures": [
4
+ "DiffRetrieverModel"
5
+ ],
6
+ "auto_map": {
7
+ "AutoConfig": "configuration_diffretriever.DiffRetrieverConfig",
8
+ "AutoModel": "modeling_diffretriever.DiffRetrieverModel"
9
+ },
10
+ "base_model": "Dream-org/Dream-v0-Instruct-7B",
11
+ "backbone_type": "dream",
12
+ "mode": "multi",
13
+ "k_q": 4,
14
+ "k_p": 16
15
+ }
configuration_diffretriever.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HuggingFace config for DiffRetriever `trust_remote_code` loading.
2
+
3
+ A thin `PretrainedConfig` so that
4
+
5
+ AutoModel.from_pretrained("ielabgroup/diffretriever-...", trust_remote_code=True)
6
+
7
+ can route to `DiffRetrieverModel` via the repo's `config.json` `auto_map`.
8
+
9
+ The real retrieval configuration (prompt token ids, K_q/K_p, temperature,
10
+ sparse weight, ...) lives in `retriever_config.json` and is read by
11
+ `TrainableDiffusionRetriever.load()`. The fields here are informational only
12
+ (they show up in the Hub config viewer) and are not required for loading.
13
+
14
+ This file is shipped *inside each model repo* — keep it dependency-light.
15
+ """
16
+ from transformers import PretrainedConfig
17
+
18
+
19
+ class DiffRetrieverConfig(PretrainedConfig):
20
+ model_type = "diffretriever"
21
+
22
+ def __init__(
23
+ self,
24
+ base_model: str | None = None,
25
+ backbone_type: str | None = None,
26
+ mode: str = "single",
27
+ k_q: int = 1,
28
+ k_p: int = 1,
29
+ **kwargs,
30
+ ):
31
+ self.base_model = base_model # e.g. "Dream-org/Dream-v0-Instruct-7B"
32
+ self.backbone_type = backbone_type # e.g. "dream" / "llada"
33
+ self.mode = mode # "single" | "multi"
34
+ self.k_q = k_q
35
+ self.k_p = k_p
36
+ super().__init__(**kwargs)
diffretriever_trainable.py ADDED
The diff for this file is too large to render. See raw diff
 
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
modeling_diffretriever.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HuggingFace `trust_remote_code` wrapper for DiffRetriever.
2
+
3
+ Lets a released checkpoint load with a single call:
4
+
5
+ from transformers import AutoModel
6
+ model = AutoModel.from_pretrained(
7
+ "ielabgroup/diffretriever-dream-7b-single", trust_remote_code=True)
8
+ model.eval()
9
+ ids, mask = model.tokenize(["a query"], is_query=True)
10
+ out = model.encode(ids, mask, is_query=True) # {'repr_hidden', ...}
11
+
12
+ It wraps `TrainableDiffusionRetriever` (shipped in the same repo) and exposes
13
+ `.tokenize()` / `.encode()` / `.backbone`. The base diffusion backbone
14
+ (Dream / LLaDA / ...) is pulled from its own Hub repo at load time; this repo
15
+ carries only the LoRA adapter + tokenizer + retriever_config.json + this code.
16
+
17
+ Shipped inside each model repo, so keep the import surface minimal.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import shutil
23
+ import tempfile
24
+
25
+ import torch # noqa: F401 (used by the wrapped retriever; keep import explicit)
26
+ from transformers import PreTrainedModel
27
+
28
+ from .configuration_diffretriever import DiffRetrieverConfig
29
+ from .diffretriever_trainable import TrainableDiffusionRetriever
30
+
31
+
32
+ class DiffRetrieverModel(PreTrainedModel):
33
+ config_class = DiffRetrieverConfig
34
+
35
+ def __init__(self, config: DiffRetrieverConfig, retriever=None):
36
+ super().__init__(config)
37
+ # Registered as a submodule so .to()/.eval()/.parameters() recurse into it.
38
+ self.retriever = retriever
39
+
40
+ # ── Loading ──────────────────────────────────────────────────────────────
41
+ @classmethod
42
+ def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
43
+ # We do NOT call super().from_pretrained: the checkpoint is a LoRA
44
+ # adapter that must be attached on top of a base backbone pulled from a
45
+ # different Hub repo, which TrainableDiffusionRetriever.load() handles.
46
+ kwargs.pop("trust_remote_code", None)
47
+ config = kwargs.pop("config", None)
48
+
49
+ path = str(pretrained_model_name_or_path)
50
+ if not os.path.isdir(path):
51
+ from huggingface_hub import snapshot_download
52
+ dl = {k: kwargs[k] for k in
53
+ ("cache_dir", "revision", "token", "local_files_only", "proxies")
54
+ if k in kwargs and kwargs[k] is not None}
55
+ path = snapshot_download(path, **dl)
56
+
57
+ # Build a temp view of the snapshot for TrainableDiffusionRetriever.load:
58
+ # - DROP config.json: load() detects a released adapter repo only when
59
+ # adapter_config.json is present AND config.json is absent; this repo
60
+ # ships a config.json for the auto_map.
61
+ # - FLATTEN the adapter/ subdir into the root: the LoRA adapter is
62
+ # stored under adapter/ (not the repo root) on purpose, so that
63
+ # transformers' PEFT auto-loader does NOT hijack AutoModel and load
64
+ # the base model directly instead of this wrapper. load() still needs
65
+ # adapter_config.json at the top level, so we link it up here.
66
+ tmp = tempfile.mkdtemp(prefix="diffretriever_")
67
+
68
+ def _link(src, dst):
69
+ if os.path.isfile(src) and not os.path.exists(dst):
70
+ os.symlink(os.path.abspath(src), dst)
71
+
72
+ try:
73
+ for fn in os.listdir(path):
74
+ if fn == "config.json":
75
+ continue
76
+ _link(os.path.join(path, fn), os.path.join(tmp, fn))
77
+ adapter_dir = os.path.join(path, "adapter")
78
+ if os.path.isdir(adapter_dir):
79
+ for fn in os.listdir(adapter_dir):
80
+ _link(os.path.join(adapter_dir, fn), os.path.join(tmp, fn))
81
+ retriever = TrainableDiffusionRetriever.load(tmp)
82
+ finally:
83
+ shutil.rmtree(tmp, ignore_errors=True)
84
+
85
+ if config is None:
86
+ config = DiffRetrieverConfig(
87
+ base_model=getattr(retriever, "model_name", None),
88
+ backbone_type=getattr(retriever, "model_type", None),
89
+ )
90
+ return cls(config, retriever=retriever)
91
+
92
+ # ── Retrieval API (delegates to the wrapped retriever) ─────────────────────
93
+ @property
94
+ def backbone(self):
95
+ return self.retriever.backbone
96
+
97
+ def tokenize(self, *args, **kwargs):
98
+ return self.retriever.tokenize(*args, **kwargs)
99
+
100
+ def encode(self, *args, **kwargs):
101
+ return self.retriever.encode(*args, **kwargs)
102
+
103
+ def forward(self, *args, **kwargs):
104
+ return self.retriever(*args, **kwargs)
retriever_config.json ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "dream",
3
+ "mask_token_id": 151666,
4
+ "hidden_size": 3584,
5
+ "max_length": 156,
6
+ "n_gen_tokens": 4,
7
+ "n_gen_q_tokens": 4,
8
+ "n_gen_p_tokens": 16,
9
+ "temperature": 0.01,
10
+ "flops_weight": 0.0,
11
+ "num_denoise_steps": 1,
12
+ "sparse_weight": 1.0,
13
+ "normalize": true,
14
+ "query_prefix_ids": [
15
+ 151644,
16
+ 8948,
17
+ 198,
18
+ 2610,
19
+ 525,
20
+ 458,
21
+ 15235,
22
+ 17847,
23
+ 429,
24
+ 646,
25
+ 3535,
26
+ 3738,
27
+ 4128,
28
+ 13,
29
+ 151645,
30
+ 198,
31
+ 151644,
32
+ 872,
33
+ 198,
34
+ 2859,
35
+ 25,
36
+ 330
37
+ ],
38
+ "query_suffix_ids": [
39
+ 3263,
40
+ 5443,
41
+ 264,
42
+ 2421,
43
+ 4244,
44
+ 311,
45
+ 4009,
46
+ 279,
47
+ 3239,
48
+ 304,
49
+ 264,
50
+ 56370,
51
+ 3383,
52
+ 13,
53
+ 7405,
54
+ 2704,
55
+ 697,
56
+ 4244,
57
+ 525,
58
+ 304,
59
+ 42047,
60
+ 13,
61
+ 151645,
62
+ 198,
63
+ 151644,
64
+ 77091,
65
+ 198,
66
+ 785,
67
+ 4244,
68
+ 525,
69
+ 25,
70
+ 330
71
+ ],
72
+ "passage_prefix_ids": [
73
+ 151644,
74
+ 8948,
75
+ 198,
76
+ 2610,
77
+ 525,
78
+ 458,
79
+ 15235,
80
+ 17847,
81
+ 429,
82
+ 646,
83
+ 3535,
84
+ 3738,
85
+ 4128,
86
+ 13,
87
+ 151645,
88
+ 198,
89
+ 151644,
90
+ 872,
91
+ 198,
92
+ 12187,
93
+ 424,
94
+ 25,
95
+ 330
96
+ ],
97
+ "passage_suffix_ids": [
98
+ 3263,
99
+ 5443,
100
+ 264,
101
+ 2421,
102
+ 4244,
103
+ 311,
104
+ 4009,
105
+ 279,
106
+ 21085,
107
+ 304,
108
+ 264,
109
+ 56370,
110
+ 3383,
111
+ 13,
112
+ 7405,
113
+ 2704,
114
+ 697,
115
+ 4244,
116
+ 525,
117
+ 304,
118
+ 42047,
119
+ 13,
120
+ 151645,
121
+ 198,
122
+ 151644,
123
+ 77091,
124
+ 198,
125
+ 785,
126
+ 4244,
127
+ 525,
128
+ 25,
129
+ 330
130
+ ],
131
+ "lora_rank": 16,
132
+ "lora_alpha": 64,
133
+ "use_eos": false,
134
+ "n_tail": 3
135
+ }
sparse_utils.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Sparse representation utilities matching original PromptReps.
3
+
4
+ Reference: https://github.com/ielab/PromptReps
5
+ The original filters sparse logits by:
6
+ 1. Word-tokenizing the text (lowercased) with NLTK
7
+ 2. Removing stopwords and punctuation
8
+ 3. Re-tokenizing each content word independently to get clean token IDs
9
+ 4. Only keeping logits for those token IDs
10
+ """
11
+
12
+ import torch
13
+ from typing import List, Set, Optional
14
+ import string
15
+ import logging
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ try:
20
+ from nltk import word_tokenize
21
+ from nltk.corpus import stopwords as _sw_corpus
22
+ STOPWORDS = set(_sw_corpus.words('english') + list(string.punctuation))
23
+ except LookupError:
24
+ import nltk
25
+ nltk.download('punkt_tab', quiet=True)
26
+ nltk.download('stopwords', quiet=True)
27
+ from nltk import word_tokenize
28
+ from nltk.corpus import stopwords as _sw_corpus
29
+ STOPWORDS = set(_sw_corpus.words('english') + list(string.punctuation))
30
+
31
+
32
+ def get_content_token_ids(texts: List[str], tokenizer) -> List[Set[int]]:
33
+ """Extract content token IDs from texts (stopwords removed, word-level tokenization).
34
+
35
+ Matches original PromptReps get_valid_tokens_values:
36
+ 1. word_tokenize(text.lower())
37
+ 2. Remove stopwords + punctuation
38
+ 3. tokenizer.encode(word) for each remaining word
39
+
40
+ Args:
41
+ texts: List of raw text strings.
42
+ tokenizer: HuggingFace tokenizer.
43
+
44
+ Returns:
45
+ List of sets of token IDs, one per text.
46
+ """
47
+ all_token_ids = []
48
+ for text in texts:
49
+ words = [w for w in word_tokenize(text.lower())
50
+ if w not in STOPWORDS]
51
+ if words:
52
+ # Batch encode all words at once (much faster than per-word encode)
53
+ batch_ids = tokenizer(words, add_special_tokens=False)['input_ids']
54
+ token_ids = set()
55
+ for ids in batch_ids:
56
+ token_ids.update(ids)
57
+ else:
58
+ token_ids = set()
59
+ all_token_ids.append(token_ids)
60
+ return all_token_ids
61
+
62
+
63
+ def filter_sparse(
64
+ sparse: torch.Tensor,
65
+ content_token_ids: List[Set[int]],
66
+ exclude_ids: Optional[List[int]] = None,
67
+ ) -> torch.Tensor:
68
+ """Filter sparse logits to only keep content tokens.
69
+
70
+ Args:
71
+ sparse: [batch, vocab] sparse logit tensor.
72
+ content_token_ids: List of sets of valid token IDs per example.
73
+ exclude_ids: Token IDs to always exclude (e.g., MASK token).
74
+
75
+ Returns:
76
+ Filtered sparse tensor (same shape, non-content entries zeroed).
77
+ """
78
+ # Build (row, col) index pairs for all content tokens across batch
79
+ rows, cols = [], []
80
+ for i in range(sparse.size(0)):
81
+ if content_token_ids[i]:
82
+ ids = list(content_token_ids[i])
83
+ rows.extend([i] * len(ids))
84
+ cols.extend(ids)
85
+ if rows:
86
+ mask = torch.zeros_like(sparse)
87
+ mask[rows, cols] = 1.0
88
+ else:
89
+ mask = torch.zeros_like(sparse)
90
+ if exclude_ids:
91
+ for eid in exclude_ids:
92
+ mask[:, eid] = 0.0
93
+ return sparse * mask
special_tokens_map.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_special_tokens": [
3
+ "<|beginoftext|>",
4
+ "<|mask|>"
5
+ ],
6
+ "bos_token": {
7
+ "content": "<|beginoftext|>",
8
+ "lstrip": false,
9
+ "normalized": false,
10
+ "rstrip": false,
11
+ "single_word": false
12
+ },
13
+ "eos_token": {
14
+ "content": "<|endoftext|>",
15
+ "lstrip": false,
16
+ "normalized": false,
17
+ "rstrip": false,
18
+ "single_word": false
19
+ },
20
+ "mask_token": {
21
+ "content": "<|mask|>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false
26
+ },
27
+ "pad_token": {
28
+ "content": "<|endoftext|>",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false
33
+ }
34
+ }
tokenization_dream.py ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2024 The Dream team, HKUNLP Group and The HuggingFace Inc. team. All rights reserved.
3
+ #
4
+ # This code is based on Qwen's implementations in this library.
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """Tokenization classes for Dream."""
17
+
18
+ import json
19
+ import os
20
+ import unicodedata
21
+ from functools import lru_cache
22
+ from typing import Optional, Tuple
23
+
24
+ import regex as re
25
+
26
+ from transformers.tokenization_utils import AddedToken, PreTrainedTokenizer
27
+ from transformers.utils import logging
28
+
29
+
30
+ logger = logging.get_logger(__name__)
31
+
32
+ VOCAB_FILES_NAMES = {
33
+ "vocab_file": "vocab.json",
34
+ "merges_file": "merges.txt",
35
+ }
36
+
37
+
38
+ MAX_MODEL_INPUT_SIZES = {"dream/dream-tokenizer": 32768}
39
+
40
+ PRETOKENIZE_REGEX = r"""(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+"""
41
+
42
+
43
+ @lru_cache()
44
+ # Copied from transformers.models.gpt2.tokenization_gpt2.bytes_to_unicode
45
+ def bytes_to_unicode():
46
+ """
47
+ Returns list of utf-8 byte and a mapping to unicode strings. We specifically avoids mapping to whitespace/control
48
+ characters the bpe code barfs on.
49
+
50
+ The reversible bpe codes work on unicode strings. This means you need a large # of unicode characters in your vocab
51
+ if you want to avoid UNKs. When you're at something like a 10B token dataset you end up needing around 5K for
52
+ decent coverage. This is a significant percentage of your normal, say, 32K bpe vocab. To avoid that, we want lookup
53
+ tables between utf-8 bytes and unicode strings.
54
+ """
55
+ bs = (
56
+ list(range(ord("!"), ord("~") + 1)) + list(range(ord("¡"), ord("¬") + 1)) + list(range(ord("®"), ord("ÿ") + 1))
57
+ )
58
+ cs = bs[:]
59
+ n = 0
60
+ for b in range(2**8):
61
+ if b not in bs:
62
+ bs.append(b)
63
+ cs.append(2**8 + n)
64
+ n += 1
65
+ cs = [chr(n) for n in cs]
66
+ return dict(zip(bs, cs))
67
+
68
+
69
+ # Copied from transformers.models.gpt2.tokenization_gpt2.get_pairs
70
+ def get_pairs(word):
71
+ """
72
+ Return set of symbol pairs in a word.
73
+
74
+ Word is represented as tuple of symbols (symbols being variable-length strings).
75
+ """
76
+ pairs = set()
77
+ prev_char = word[0]
78
+ for char in word[1:]:
79
+ pairs.add((prev_char, char))
80
+ prev_char = char
81
+ return pairs
82
+
83
+
84
+ class DreamTokenizer(PreTrainedTokenizer):
85
+ """
86
+ Construct a Dream tokenizer. Based on byte-level Byte-Pair-Encoding.
87
+
88
+ Same with GPT2Tokenizer, this tokenizer has been trained to treat spaces like parts of the tokens so a word will
89
+ be encoded differently whether it is at the beginning of the sentence (without space) or not:
90
+
91
+ ```python
92
+ >>> from transformers import AutoTokenizer
93
+
94
+ >>> tokenizer = AutoTokenizer.from_pretrained("Dream-org/Dream-v0-Base-7B", trust_remote_code=True)
95
+ >>> tokenizer("Hello world")["input_ids"]
96
+ [9707, 1879]
97
+
98
+ >>> tokenizer(" Hello world")["input_ids"]
99
+ [21927, 1879]
100
+ ```
101
+ This is expected.
102
+
103
+ You should not use GPT2Tokenizer instead, because of the different pretokenization rules.
104
+
105
+ This tokenizer inherits from [`PreTrainedTokenizer`] which contains most of the main methods. Users should refer to
106
+ this superclass for more information regarding those methods.
107
+
108
+ Args:
109
+ vocab_file (`str`):
110
+ Path to the vocabulary file.
111
+ merges_file (`str`):
112
+ Path to the merges file.
113
+ errors (`str`, *optional*, defaults to `"replace"`):
114
+ Paradigm to follow when decoding bytes to UTF-8. See
115
+ [bytes.decode](https://docs.python.org/3/library/stdtypes.html#bytes.decode) for more information.
116
+ unk_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
117
+ The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
118
+ token instead.
119
+ bos_token (`str`, *optional*):
120
+ The beginning of sequence token. Not applicable for this tokenizer.
121
+ eos_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
122
+ The end of sequence token.
123
+ pad_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
124
+ The token used for padding, for example when batching sequences of different lengths.
125
+ clean_up_tokenization_spaces (`bool`, *optional*, defaults to `False`):
126
+ Whether or not the model should cleanup the spaces that were added when splitting the input text during the
127
+ tokenization process. Not applicable to this tokenizer, since tokenization does not add spaces.
128
+ split_special_tokens (`bool`, *optional*, defaults to `False`):
129
+ Whether or not the special tokens should be split during the tokenization process. The default behavior is
130
+ to not split special tokens. This means that if `<|endoftext|>` is the `eos_token`, then `tokenizer.tokenize("<|endoftext|>") =
131
+ ['<|endoftext|>`]. Otherwise, if `split_special_tokens=True`, then `tokenizer.tokenize("<|endoftext|>")` will be give `['<',
132
+ '|', 'endo', 'ft', 'ext', '|', '>']`. This argument is only supported for `slow` tokenizers for the moment.
133
+ """
134
+
135
+ vocab_files_names = VOCAB_FILES_NAMES
136
+ model_input_names = ["input_ids", "attention_mask"]
137
+
138
+ def __init__(
139
+ self,
140
+ vocab_file,
141
+ merges_file,
142
+ errors="replace",
143
+ unk_token="<|endoftext|>",
144
+ bos_token=None,
145
+ eos_token="<|endoftext|>",
146
+ pad_token="<|endoftext|>",
147
+ clean_up_tokenization_spaces=False,
148
+ split_special_tokens=False,
149
+ **kwargs,
150
+ ):
151
+ # Dream vocab does not contain control tokens; added tokens need to be special
152
+ bos_token = (
153
+ AddedToken(bos_token, lstrip=False, rstrip=False, special=True, normalized=False)
154
+ if isinstance(bos_token, str)
155
+ else bos_token
156
+ )
157
+ eos_token = (
158
+ AddedToken(eos_token, lstrip=False, rstrip=False, special=True, normalized=False)
159
+ if isinstance(eos_token, str)
160
+ else eos_token
161
+ )
162
+ unk_token = (
163
+ AddedToken(unk_token, lstrip=False, rstrip=False, special=True, normalized=False)
164
+ if isinstance(unk_token, str)
165
+ else unk_token
166
+ )
167
+ pad_token = (
168
+ AddedToken(pad_token, lstrip=False, rstrip=False, special=True, normalized=False)
169
+ if isinstance(pad_token, str)
170
+ else pad_token
171
+ )
172
+
173
+ with open(vocab_file, encoding="utf-8") as vocab_handle:
174
+ self.encoder = json.load(vocab_handle)
175
+ self.decoder = {v: k for k, v in self.encoder.items()}
176
+ self.errors = errors # how to handle errors in decoding
177
+ self.byte_encoder = bytes_to_unicode()
178
+ self.byte_decoder = {v: k for k, v in self.byte_encoder.items()}
179
+ bpe_merges = []
180
+ with open(merges_file, encoding="utf-8") as merges_handle:
181
+ for i, line in enumerate(merges_handle):
182
+ line = line.strip()
183
+ if (i == 0 and line.startswith("#version:")) or not line:
184
+ continue
185
+ bpe_merges.append(tuple(line.split()))
186
+ self.bpe_ranks = dict(zip(bpe_merges, range(len(bpe_merges))))
187
+ # NOTE: the cache can grow without bound and will get really large for long running processes
188
+ # (esp. for texts of language that do not use space between word, e.g. Chinese); technically
189
+ # not a memory leak but appears as one.
190
+ # GPT2Tokenizer has the same problem, so let's be consistent.
191
+ self.cache = {}
192
+
193
+ self.pat = re.compile(PRETOKENIZE_REGEX)
194
+
195
+ if kwargs.get("add_prefix_space", False):
196
+ logger.warning_once(
197
+ f"{self.__class__.__name} does not support `add_prefix_space`, setting it to True has no effect."
198
+ )
199
+
200
+ super().__init__(
201
+ errors=errors,
202
+ bos_token=bos_token,
203
+ eos_token=eos_token,
204
+ pad_token=pad_token,
205
+ unk_token=unk_token,
206
+ clean_up_tokenization_spaces=clean_up_tokenization_spaces,
207
+ split_special_tokens=split_special_tokens,
208
+ **kwargs,
209
+ )
210
+
211
+ @property
212
+ def vocab_size(self) -> int:
213
+ return len(self.encoder)
214
+
215
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer.get_vocab
216
+ def get_vocab(self):
217
+ return dict(self.encoder, **self.added_tokens_encoder)
218
+
219
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer.bpe
220
+ def bpe(self, token):
221
+ if token in self.cache:
222
+ return self.cache[token]
223
+ word = tuple(token)
224
+ pairs = get_pairs(word)
225
+
226
+ if not pairs:
227
+ return token
228
+
229
+ while True:
230
+ bigram = min(pairs, key=lambda pair: self.bpe_ranks.get(pair, float("inf")))
231
+ if bigram not in self.bpe_ranks:
232
+ break
233
+ first, second = bigram
234
+ new_word = []
235
+ i = 0
236
+ while i < len(word):
237
+ try:
238
+ j = word.index(first, i)
239
+ except ValueError:
240
+ new_word.extend(word[i:])
241
+ break
242
+ else:
243
+ new_word.extend(word[i:j])
244
+ i = j
245
+
246
+ if word[i] == first and i < len(word) - 1 and word[i + 1] == second:
247
+ new_word.append(first + second)
248
+ i += 2
249
+ else:
250
+ new_word.append(word[i])
251
+ i += 1
252
+ new_word = tuple(new_word)
253
+ word = new_word
254
+ if len(word) == 1:
255
+ break
256
+ else:
257
+ pairs = get_pairs(word)
258
+ word = " ".join(word)
259
+ self.cache[token] = word
260
+ return word
261
+
262
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer._tokenize
263
+ def _tokenize(self, text):
264
+ """Tokenize a string."""
265
+ bpe_tokens = []
266
+ for token in re.findall(self.pat, text):
267
+ token = "".join(
268
+ self.byte_encoder[b] for b in token.encode("utf-8")
269
+ ) # Maps all our bytes to unicode strings, avoiding control tokens of the BPE (spaces in our case)
270
+ bpe_tokens.extend(bpe_token for bpe_token in self.bpe(token).split(" "))
271
+ return bpe_tokens
272
+
273
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer._convert_token_to_id
274
+ def _convert_token_to_id(self, token):
275
+ """Converts a token (str) in an id using the vocab."""
276
+ return self.encoder.get(token, self.encoder.get(self.unk_token))
277
+
278
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer._convert_id_to_token
279
+ def _convert_id_to_token(self, index):
280
+ """Converts an index (integer) in a token (str) using the vocab."""
281
+ return self.decoder.get(index)
282
+
283
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer.convert_tokens_to_string
284
+ def convert_tokens_to_string(self, tokens):
285
+ """Converts a sequence of tokens (string) in a single string."""
286
+ text = "".join(tokens)
287
+ text = bytearray([self.byte_decoder[c] for c in text]).decode("utf-8", errors=self.errors)
288
+ return text
289
+
290
+ def decode(
291
+ self,
292
+ token_ids,
293
+ skip_special_tokens: bool = False,
294
+ clean_up_tokenization_spaces: Optional[bool] = False,
295
+ spaces_between_special_tokens: bool = False,
296
+ **kwargs,
297
+ ) -> str:
298
+ # `spaces_between_special_tokens` defaults to True for _decode in slow tokenizers
299
+ # and cannot be configured elsewhere, but it should default to False for DreamTokenizer
300
+ return super().decode(
301
+ token_ids,
302
+ skip_special_tokens=skip_special_tokens,
303
+ clean_up_tokenization_spaces=clean_up_tokenization_spaces,
304
+ spaces_between_special_tokens=spaces_between_special_tokens,
305
+ **kwargs,
306
+ )
307
+
308
+ # Copied from transformers.models.gpt2.tokenization_gpt2.GPT2Tokenizer.save_vocabulary
309
+ def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]:
310
+ if not os.path.isdir(save_directory):
311
+ logger.error(f"Vocabulary path ({save_directory}) should be a directory")
312
+ return
313
+ vocab_file = os.path.join(
314
+ save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]
315
+ )
316
+ merge_file = os.path.join(
317
+ save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["merges_file"]
318
+ )
319
+
320
+ with open(vocab_file, "w", encoding="utf-8") as f:
321
+ f.write(json.dumps(self.encoder, indent=2, sort_keys=True, ensure_ascii=False) + "\n")
322
+
323
+ index = 0
324
+ with open(merge_file, "w", encoding="utf-8") as writer:
325
+ writer.write("#version: 0.2\n")
326
+ for bpe_tokens, token_index in sorted(self.bpe_ranks.items(), key=lambda kv: kv[1]):
327
+ if index != token_index:
328
+ logger.warning(
329
+ f"Saving vocabulary to {merge_file}: BPE merge indices are not consecutive."
330
+ " Please check that the tokenizer is not corrupted!"
331
+ )
332
+ index = token_index
333
+ writer.write(" ".join(bpe_tokens) + "\n")
334
+ index += 1
335
+
336
+ return vocab_file, merge_file
337
+
338
+ def prepare_for_tokenization(self, text, **kwargs):
339
+ text = unicodedata.normalize("NFC", text)
340
+ return (text, kwargs)
tokenizer_config.json ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": false,
3
+ "add_prefix_space": false,
4
+ "added_tokens_decoder": {
5
+ "151643": {
6
+ "content": "<|endoftext|>",
7
+ "lstrip": false,
8
+ "normalized": false,
9
+ "rstrip": false,
10
+ "single_word": false,
11
+ "special": true
12
+ },
13
+ "151644": {
14
+ "content": "<|im_start|>",
15
+ "lstrip": false,
16
+ "normalized": false,
17
+ "rstrip": false,
18
+ "single_word": false,
19
+ "special": true
20
+ },
21
+ "151645": {
22
+ "content": "<|im_end|>",
23
+ "lstrip": false,
24
+ "normalized": false,
25
+ "rstrip": false,
26
+ "single_word": false,
27
+ "special": true
28
+ },
29
+ "151646": {
30
+ "content": "<|object_ref_start|>",
31
+ "lstrip": false,
32
+ "normalized": false,
33
+ "rstrip": false,
34
+ "single_word": false,
35
+ "special": true
36
+ },
37
+ "151647": {
38
+ "content": "<|object_ref_end|>",
39
+ "lstrip": false,
40
+ "normalized": false,
41
+ "rstrip": false,
42
+ "single_word": false,
43
+ "special": true
44
+ },
45
+ "151648": {
46
+ "content": "<|box_start|>",
47
+ "lstrip": false,
48
+ "normalized": false,
49
+ "rstrip": false,
50
+ "single_word": false,
51
+ "special": true
52
+ },
53
+ "151649": {
54
+ "content": "<|box_end|>",
55
+ "lstrip": false,
56
+ "normalized": false,
57
+ "rstrip": false,
58
+ "single_word": false,
59
+ "special": true
60
+ },
61
+ "151650": {
62
+ "content": "<|quad_start|>",
63
+ "lstrip": false,
64
+ "normalized": false,
65
+ "rstrip": false,
66
+ "single_word": false,
67
+ "special": true
68
+ },
69
+ "151651": {
70
+ "content": "<|quad_end|>",
71
+ "lstrip": false,
72
+ "normalized": false,
73
+ "rstrip": false,
74
+ "single_word": false,
75
+ "special": true
76
+ },
77
+ "151652": {
78
+ "content": "<|vision_start|>",
79
+ "lstrip": false,
80
+ "normalized": false,
81
+ "rstrip": false,
82
+ "single_word": false,
83
+ "special": true
84
+ },
85
+ "151653": {
86
+ "content": "<|vision_end|>",
87
+ "lstrip": false,
88
+ "normalized": false,
89
+ "rstrip": false,
90
+ "single_word": false,
91
+ "special": true
92
+ },
93
+ "151654": {
94
+ "content": "<|vision_pad|>",
95
+ "lstrip": false,
96
+ "normalized": false,
97
+ "rstrip": false,
98
+ "single_word": false,
99
+ "special": true
100
+ },
101
+ "151655": {
102
+ "content": "<|image_pad|>",
103
+ "lstrip": false,
104
+ "normalized": false,
105
+ "rstrip": false,
106
+ "single_word": false,
107
+ "special": true
108
+ },
109
+ "151656": {
110
+ "content": "<|video_pad|>",
111
+ "lstrip": false,
112
+ "normalized": false,
113
+ "rstrip": false,
114
+ "single_word": false,
115
+ "special": true
116
+ },
117
+ "151657": {
118
+ "content": "<tool_call>",
119
+ "lstrip": false,
120
+ "normalized": false,
121
+ "rstrip": false,
122
+ "single_word": false,
123
+ "special": false
124
+ },
125
+ "151658": {
126
+ "content": "</tool_call>",
127
+ "lstrip": false,
128
+ "normalized": false,
129
+ "rstrip": false,
130
+ "single_word": false,
131
+ "special": false
132
+ },
133
+ "151659": {
134
+ "content": "<|fim_prefix|>",
135
+ "lstrip": false,
136
+ "normalized": false,
137
+ "rstrip": false,
138
+ "single_word": false,
139
+ "special": false
140
+ },
141
+ "151660": {
142
+ "content": "<|fim_middle|>",
143
+ "lstrip": false,
144
+ "normalized": false,
145
+ "rstrip": false,
146
+ "single_word": false,
147
+ "special": false
148
+ },
149
+ "151661": {
150
+ "content": "<|fim_suffix|>",
151
+ "lstrip": false,
152
+ "normalized": false,
153
+ "rstrip": false,
154
+ "single_word": false,
155
+ "special": false
156
+ },
157
+ "151662": {
158
+ "content": "<|fim_pad|>",
159
+ "lstrip": false,
160
+ "normalized": false,
161
+ "rstrip": false,
162
+ "single_word": false,
163
+ "special": false
164
+ },
165
+ "151663": {
166
+ "content": "<|repo_name|>",
167
+ "lstrip": false,
168
+ "normalized": false,
169
+ "rstrip": false,
170
+ "single_word": false,
171
+ "special": false
172
+ },
173
+ "151664": {
174
+ "content": "<|file_sep|>",
175
+ "lstrip": false,
176
+ "normalized": false,
177
+ "rstrip": false,
178
+ "single_word": false,
179
+ "special": false
180
+ },
181
+ "151665": {
182
+ "content": "<|beginoftext|>",
183
+ "lstrip": false,
184
+ "normalized": false,
185
+ "rstrip": false,
186
+ "single_word": false,
187
+ "special": true
188
+ },
189
+ "151666": {
190
+ "content": "<|mask|>",
191
+ "lstrip": false,
192
+ "normalized": false,
193
+ "rstrip": false,
194
+ "single_word": false,
195
+ "special": true
196
+ }
197
+ },
198
+ "additional_special_tokens": [
199
+ "<|beginoftext|>",
200
+ "<|mask|>"
201
+ ],
202
+ "auto_map": {
203
+ "AutoTokenizer": [
204
+ "tokenization_dream.DreamTokenizer",
205
+ null
206
+ ]
207
+ },
208
+ "bos_token": "<|beginoftext|>",
209
+ "clean_up_tokenization_spaces": false,
210
+ "eos_token": "<|endoftext|>",
211
+ "errors": "replace",
212
+ "extra_special_tokens": {},
213
+ "mask_token": "<|mask|>",
214
+ "model_max_length": 131072,
215
+ "pad_token": "<|endoftext|>",
216
+ "split_special_tokens": false,
217
+ "tokenizer_class": "DreamTokenizer",
218
+ "unk_token": null
219
+ }
vocab.json ADDED
The diff for this file is too large to render. See raw diff