CoolFace
Apppublic

build-small-hackathon/InflectionLM

sourceHugging Facemitupdated 3mo agoView on Hugging Face
1likes
inflections_funcs.py142 linesDownload Raw Back to root
1from transformers import AutoProcessor, AutoModelForCausalLM2import torch3import torch.nn.functional as F4from torch import exp5from typing import Any, Tuple, List6 7def start_model(model_id: str = "google/gemma-4-31B-it"):8    '''9    Initializes and returns the processor and model.10    '''11    processor = AutoProcessor.from_pretrained(model_id)12    model = AutoModelForCausalLM.from_pretrained(13        model_id,14        dtype="auto",15        device_map="auto"16    )17    print(f'Model {model_id} has been installed.')18    return model, processor19 20def make_beams(model: AutoModelForCausalLM, processor: AutoProcessor, initial_prompt: str, temperature: float = 1.0) -> Tuple[Any, List[str]]:21    '''22    Generates 3 diverse responses in response to a prompt.23    '''24    messages = [25        {"role": "system", "content": "You are a helpful assistant."},26        {"role": "user", "content": initial_prompt},27    ]28 29    # Process input30    text = processor.apply_chat_template(31        messages,32        tokenize=False,33        add_generation_prompt=True,34        enable_thinking=False35    )36    inputs = processor(text=text, return_tensors="pt").to(model.device)37 38    # Generate output39    generated_dicts = model.generate(**inputs,40                                    max_new_tokens=1024,41                                    num_beams=1,          # Disable beam search for pure sampling42                                    num_return_sequences=3, # Generate 3 independent diverse samples43                                    return_dict_in_generate=True,44                                    output_scores=True,45                                    temperature=temperature if temperature > 0 else 0.1, # Ensure T > 0 for sampling46                                    top_p=0.9,             # Nucleus sampling for high-quality diversity47                                    top_k=50,              # Top-K sampling to filter noise48                                    do_sample=True)49 50    transcription = processor.batch_decode(generated_dicts.sequences, skip_special_tokens=True)51 52    print('Keys in model output -------------------')53    for key in generated_dicts:54        print(key)55    print('----------------------------------------')56 57    print('Beam scores ----------------------------')58    # sequences_scores is only present in beam search.59    # For sampling, we can approximate the overall score by averaging the transition probabilities.60    if hasattr(generated_dicts, 'sequences_scores'):61        for score in generated_dicts.sequences_scores:62            print(exp(score).item())63    else:64        print('Sampling mode: sequences_scores not available.')65    print('----------------------------------------')66 67    return generated_dicts, transcription68 69def parse_beams(transcription: List[str]) -> List[str]:70    '''71    Parses beams to extract only the response after 'model\nthought'.72    '''73    beam_text = []74    for beam in transcription:75        parts = beam.split('''model\nthought''')76        if len(parts) > 1:77            response = parts[1].strip('\n')78        else:79            response = beam80        beam_text.append(response)81 82    print('Beams have been parsed. --------------')83    return beam_text84 85def get_beam_tokens(generated_dicts: Any, processor: AutoProcessor) -> List[List[str]]:86    '''87    Decodes the generated sequences into individual tokens for each beam.88    '''89    beam_tokens = []90    # The number of generated tokens is the length of the scores list91    gen_len = len(generated_dicts.scores)92 93    for sequence in generated_dicts.sequences:94        # Calculate input length for this specific sequence95        total_len = sequence.shape[0]96        input_len = total_len - gen_len97 98        # Extract only the generated token IDs99        generated_ids = sequence[input_len:].tolist()100 101        # Convert IDs to tokens (e.g., ' Hello', ' world')102        tokens = processor.tokenizer.convert_ids_to_tokens(generated_ids)103        beam_tokens.append(tokens)104 105    return beam_tokens106 107def calculate_score_vectors(model: AutoModelForCausalLM, generated_dicts: Any) -> List[List[float]]:108    '''109    Creates a score vector for each beam containing the probability of each110    token that was chosen.111 112    Optimized to use generated_dicts.scores instead of a full model forward pass113    to prevent GPU timeouts on ZeroGPU.114    '''115    # Number of sequences generated116    num_sequences = generated_dicts.sequences.shape[0]117    # Number of generated tokens (excluding prompt)118    gen_len = len(generated_dicts.scores)119    # Total length of sequences (including prompt)120    total_len = generated_dicts.sequences.shape[1]121    # Input length (prompt length)122    input_len = total_len - gen_len123 124    # Stack the transition scores (logits) from the generation process125    # generated_dicts.scores is a tuple of length gen_len, each element (num_beams, vocab_size)126    all_logits = torch.stack(generated_dicts.scores, dim=0) # shape: (gen_len, num_sequences, vocab_size)127 128    # Convert logits to probabilities across the vocab dimension129    all_probs = F.softmax(all_logits, dim=-1) # shape: (gen_len, num_sequences, vocab_size)130 131    score_vectors = []132    for i in range(num_sequences):133        beam_probs = []134        # Extract the probability for the specific token that was chosen at each step135        for t in range(gen_len):136            token_id = generated_dicts.sequences[i, input_len + t]137            prob = all_probs[t, i, token_id].item()138            beam_probs.append(prob)139        score_vectors.append(beam_probs)140 141    return score_vectors142