CoolFace
Modelpublic

servantofares/Stable-DiffCoder-8B-Base

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes8downloads
modeling_stable_diffcoder.py275 linesDownload Raw Back to root
1# Copyright (c) 2026 ByteDance Ltd. and/or its affiliates2# SPDX-License-Identifier: MIT3 4import numpy as np5import torch6from torch import nn7import torch.nn.functional as F8from transformers import AutoTokenizer, AutoModel, AutoModelForCausalLM, DynamicCache9from transformers.models.llama.modeling_llama import LlamaForCausalLM10from transformers.generation.utils import GenerationConfig11 12 13class StableDiffcoderForCausalLM(LlamaForCausalLM):14    def _get_num_transfer_tokens(self, mask_map, steps):15        # Only bs == 1 is supported for now16        mask_num = mask_map.sum().long().item()17 18        base = mask_num // steps19        remainder = mask_num % steps20 21        num_transfer_tokens = torch.full(22            (steps,), fill_value=base, device=mask_map.device, dtype=torch.long23        )24 25        num_transfer_tokens[:remainder] += 126 27        return num_transfer_tokens28 29    def _make_block_causal_mask(30        self, seq_len, block_size=2, device=None, dtype=torch.bfloat1631    ):32        num_blocks = (seq_len + block_size - 1) // block_size33        block_mask = torch.tril(34            torch.ones((num_blocks, num_blocks), dtype=torch.bool, device=device)35        )36        local_block = torch.ones(37            (block_size, block_size), dtype=torch.bool, device=device38        )39        mask = block_mask.kron(local_block)[:seq_len, :seq_len]40 41        attention_mask = mask.float()42        attention_mask.masked_fill_(~mask, -torch.inf)43        attention_mask = attention_mask.unsqueeze(0).unsqueeze(0).to(dtype)44        return attention_mask45 46    def _get_transfer_index(47        self,48        logits,49        temperature,50        remasking,51        mask_index,52        x,53        num_transfer_token,54        threshold=None,55        shift=False,56    ):57        def add_gumbel_noise(logits, temperature):58            if temperature == 0:59                return logits60            logits = logits.to(torch.float64)61            noise = torch.rand_like(logits, dtype=torch.float64)62            gumbel_noise = (-torch.log(noise)) ** temperature63            return logits.exp() / gumbel_noise64 65        logits_with_noise = add_gumbel_noise(logits, temperature=temperature)66        x0 = torch.argmax(logits_with_noise, dim=-1)  # b, l67        if shift:68            x0 = torch.cat([x[:, :1], x0[:, :-1]], dim=-1)69            pad = torch.zeros_like(logits[:, :1])70            logits = torch.cat([pad, logits[:, :-1]], dim=1)71        if remasking == "low_confidence":72            p = F.softmax(logits.to(torch.float64), dim=-1)73            x0_p = torch.squeeze(74                torch.gather(p, dim=-1, index=torch.unsqueeze(x0, -1)), -175            )  # b, l76        elif remasking == "random":77            x0_p = torch.rand((x0.shape[0], x0.shape[1]), device=x0.device)78        else:79            raise NotImplementedError(remasking)80 81        x0 = torch.where(mask_index, x0, x)82        confidence = torch.where(mask_index, x0_p, -np.inf)83 84        transfer_map = torch.zeros_like(x0, dtype=torch.bool, device=x0.device)85        if threshold is not None:86            num_transfer_token = mask_index.sum(dim=1, keepdim=True)87        _, select_index = torch.topk(confidence[0], k=num_transfer_token)88        transfer_map[0, select_index] = True89        if threshold is not None:90            for k in range(1, num_transfer_token):91                if confidence[0, select_index[k]] < threshold:92                    transfer_map[0, select_index[k]] = False93        return x0, transfer_map94 95    @torch.no_grad()96    def generate_block(97        self,98        input_ids: torch.LongTensor,99        steps=128,100        gen_length=128,101        block_length=4,102        temperature=0.0,103        remasking="low_confidence",104        tokenizer=None,105        mask_id=5,106        threshold=0.95,107        shift=False,108        eos_id=None,109    ):110        x = torch.cat(111            [112                input_ids,113                torch.full(114                    (input_ids.shape[0], gen_length),115                    mask_id,116                    dtype=torch.long,117                    device=input_ids.device,118                ),119            ],120            dim=1,121        )122 123        assert gen_length % block_length == 0, (124            "gen_length must be divisible by block_length"125        )126        gen_blocks = gen_length // block_length127 128        assert steps % gen_blocks == 0, (129            "steps must be divisible by the number of generation blocks"130        )131        steps = steps // gen_blocks132 133        assert x.shape[0] == 1, (134            "Only batch size of 1 is supported for block-wise generation currently."135        )136 137        prompt_length = input_ids.shape[1]138        gen_block_list = [block_length for _ in range(gen_blocks)]139 140        # Fix 3: Only handle residual blocks if the prompt length is NOT cleanly divisible141        remainder = prompt_length % block_length142        if remainder != 0:143            res_block = block_length - remainder144            gen_block_list = [res_block] + gen_block_list145            gen_block_list[-1] = block_length - res_block146            gen_blocks += 1147        cum_block = [sum(gen_block_list[: i + 1]) for i in range(len(gen_block_list))]148 149        block_diffusion_attention_mask = self._make_block_causal_mask(150            prompt_length + gen_length,151            block_length,152            self.device,153            dtype=torch.bfloat16,154        )155 156        past_key_values = DynamicCache()157 158        nfe = 0159        final_flag = False160        prefill_length = prompt_length // block_length * block_length161 162        if prefill_length > 0:163            cur_attn_mask = block_diffusion_attention_mask[164                ..., :prefill_length, :prefill_length165            ]166            # Fix 1: Explicitly pass cache_position for newer transformers prefill167            # actually not necessary since transformers will automatically generate it for prefilling168            # if unspecified, but the official `generate` method does pass it,169            # so we follow that for consistency and to avoid potential issues in future transformers updates170            cache_pos = torch.arange(prefill_length, device=x.device)171            self(172                x[:, :prefill_length],173                past_key_values=past_key_values,174                attention_mask=cur_attn_mask,175                use_cache=True,176                cache_position=cache_pos,177            )178 179        for block_id, block_size in enumerate(gen_block_list):180            block_start = (181                prompt_length + cum_block[block_id - 1]182                if block_id > 0183                else prefill_length184            )185            block_end = prompt_length + cum_block[block_id]186 187            block_mask_map = x[:, block_start:block_end] == mask_id188            num_transfer_tokens = self._get_num_transfer_tokens(block_mask_map, steps)189 190            replace_position = torch.zeros_like(x, dtype=torch.bool)191            replace_position[:, block_start:block_end] = True192 193            for token_count in num_transfer_tokens:194                if token_count > 0:195                    nfe += 1196                    mask_map = x[:, block_start:block_end] == mask_id197                    attention_mask = block_diffusion_attention_mask[198                        ..., block_start:block_end, :block_end199                    ]200                    output = self(201                        x[:, block_start:block_end],202                        attention_mask=attention_mask,203                        past_key_values=past_key_values,204                        use_cache=True,205                        cache_position=replace_position.nonzero(as_tuple=True)[1],206                    )207                    logits = output.logits208 209                    past_key_values.crop(block_start)210 211                    x0, transfer_map = self._get_transfer_index(212                        logits,213                        temperature,214                        remasking,215                        mask_map,216                        x[:, block_start:block_end],217                        token_count.item() if threshold is None else None,218                        threshold,219                        shift=shift,220                    )221                    x[:, block_start:block_end][transfer_map] = x0[transfer_map]222 223                if (x[:, block_start:block_end] == mask_id).sum() == 0:224 225                   # Fix 2: Calculate where the generated tokens ACTUALLY start in this block226                    gen_start = max(block_start, prompt_length)227 228                    if (229                        eos_id is not None230                        and gen_start < block_end231                        and (x[:, gen_start:block_end] == eos_id).sum() > 0232                    ):233                        final_flag = True234                        x = x[:, :block_end]235                        eos_pos = (x[:, gen_start:block_end] == eos_id).nonzero(as_tuple=True)[1][0].item() + gen_start236                        x[0, eos_pos:] = eos_id237                        break238 239                    nfe += 1240                    self(241                        x[:, block_start:block_end],242                        attention_mask=block_diffusion_attention_mask[243                            ..., block_start:block_end, :block_end244                        ],245                        past_key_values=past_key_values,246                        use_cache=True,247                        cache_position=replace_position.nonzero(as_tuple=True)[1],248                    )249                    break250 251            if final_flag:252                break253 254        return x, nfe255 256    @torch.no_grad()257    def generate(258        self,259        input_ids=None,260        generation_config: GenerationConfig = None,261        **kwargs,262    ):263        if input_ids is None:264            raise ValueError("input_ids must be provided")265 266        if generation_config is None:267            generation_config = self.generation_config268 269        output_ids, nfe = self.generate_block(270            input_ids=input_ids,271            **kwargs,272        )273 274        return output_ids275