yasserrmd commited on
Commit
df37cf1
·
verified ·
1 Parent(s): a88c182

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +191 -3
README.md CHANGED
@@ -3,6 +3,194 @@
3
  license: mit
4
  language: en
5
  ---
6
- # Smoothie: Diffusion Model for Paraphrasing
7
- This repository contains a diffusion model based on the "Smoothie" paper, trained on QQP.
8
- It requires `trust_remote_code=True` to load.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  license: mit
4
  language: en
5
  ---
6
+
7
+ # Smoothie: A Diffusion Model for Paraphrase Generation
8
+
9
+ [![Generic badge](https://img.shields.io/badge/Model-Custom_Smoothie-blue.svg)](https://shields.io/)
10
+ [![Generic badge](https://img.shields.io/badge/Dataset-QQP-green.svg)](https://huggingface.co/datasets/glue)
11
+ [![Generic badge](https://img.shields.io/badge/Paper-arXiv:2505.18853v1-red.svg)](https://arxiv.org/abs/2505.18853)
12
+
13
+ This repository contains a diffusion-based model for text generation, trained on the **Quora Question Pairs (QQP)** dataset for the task of **paraphrasing**. The architecture and training methodology are based on the paper *Smoothie: Smoothing Diffusion on Token Embeddings for Text Generation*.
14
+
15
+ This is a custom model and **requires `trust_remote_code=True`** to load, as the model's architecture is defined in the accompanying `modeling_smoothie.py` file.
16
+
17
+ ## Model Description
18
+
19
+ The "Smoothie" model is a non-autoregressive text generation model that uses a diffusion process. Unlike traditional models that generate text token-by-token, this model starts with pure random noise and iteratively refines it over hundreds of steps to produce a full sentence.
20
+
21
+ The key features of the architecture are:
22
+ - **Diffusion Process:** Operates in a continuous space based on the negative squared Euclidean distances between token embeddings. This allows the model to smoothly add and remove "semantic noise".
23
+ - **Backbone:** A Transformer Decoder with UNet-style skip connections, which is effective for denoising tasks.
24
+ - **Conditional Generation:** The model is conditioned on an input sentence (a question) to generate a semantically similar output sentence (a paraphrase).
25
+
26
+ This specific checkpoint was trained on the paraphrase pairs from the GLUE QQP dataset, using `bert-base-cased` as the base for its token embeddings.
27
+
28
+ ---
29
+
30
+ ## How to Use
31
+
32
+ The following is a complete, self-contained example of how to load the model and use it for inference. The `SmoothieDiffusion` class, which orchestrates the multi-step generation process, is included for convenience.
33
+
34
+ First, make sure you have the necessary libraries installed:
35
+ ```bash
36
+ pip install torch transformers accelerate huggingface_hub -q
37
+ ```
38
+
39
+ Then, you can run the following Python script:
40
+
41
+ ```python
42
+ import torch
43
+ import torch.nn as nn
44
+ from transformers import AutoTokenizer, AutoModel, BertModel
45
+ from tqdm.auto import tqdm
46
+ import math
47
+
48
+ # =============================================================================
49
+ # PART 1: THE DIFFUSION PIPELINE (INFERENCE LOGIC)
50
+ # This class is required to use the Smoothie model for generation.
51
+ # =============================================================================
52
+
53
+ def get_noise_schedule(T, s_min=1.5, s_max=200.0, d=9.0, epsilon=1e-5):
54
+ """Generates the noise schedule used during training."""
55
+ t = torch.arange(0, T + 1, dtype=torch.float32)
56
+ ratio = t / (T - t + epsilon)
57
+ arg = (1/d) * ratio
58
+ schedule = (s_max - s_min) * (2 / math.pi) * torch.atan(arg) + s_min
59
+ schedule = s_min
60
+ schedule[T] = s_max
61
+ return schedule
62
+
63
+ class SmoothieDiffusion:
64
+ """The inference pipeline for the Smoothie model."""
65
+ def __init__(self, E, schedule):
66
+ self.E = E.cuda() # The semantic map (embedding matrix)
67
+ self.V, self.D = E.shape
68
+ self.sigmas = schedule.cuda() # The blueprint (noise schedule)
69
+ self.T = len(schedule) - 1
70
+
71
+ @torch.no_grad()
72
+ def get_D0(self, target_embeddings):
73
+ """Memory-efficient calculation of the distance matrix D0."""
74
+ term1 = torch.sum(target_embeddings.pow(2), dim=-1, keepdim=True)
75
+ term2 = torch.sum(self.E.pow(2), dim=-1).unsqueeze(0).unsqueeze(0)
76
+ term3 = -2 * torch.matmul(target_embeddings, self.E.T)
77
+ return -(term1 + term2 + term3)
78
+
79
+ @torch.no_grad()
80
+ def p_sample(self, model, D_t, t, delta_gen, src_tokens=None, src_mask=None):
81
+ """A single reverse diffusion (denoising) step."""
82
+ p_t = torch.softmax(D_t, dim=-1)
83
+ weighted_avg_emb = torch.matmul(p_t, self.E)
84
+ t_tensor = torch.full((D_t.shape,), t, device=D_t.device, dtype=torch.long)
85
+
86
+ pred_E0 = model(
87
+ weighted_avg_emb=weighted_avg_emb,
88
+ t=t_tensor,
89
+ src_tokens=src_tokens,
90
+ src_mask=src_mask
91
+ )
92
+
93
+ pred_D0 = self.get_D0(pred_E0)
94
+ if t == 0:
95
+ return pred_D0
96
+
97
+ sigma_t_minus_1 = self.sigmas[t-1]
98
+ D_t_minus_1 = pred_D0 / (sigma_t_minus_1 ** 2)
99
+ if delta_gen > 0:
100
+ D_t_minus_1 += delta_gen * torch.randn_like(D_t)
101
+ return D_t_minus_1
102
+
103
+ @torch.no_grad()
104
+ def p_sample_loop(self, model, shape, delta_gen, src_tokens=None, src_mask=None):
105
+ """The full denoising loop from T to 0."""
106
+ device = self.E.device
107
+ D_t = torch.randn(shape, device=device) * delta_gen
108
+ for t in tqdm(reversed(range(0, self.T + 1)), desc="Sampling", total=self.T + 1):
109
+ D_t = self.p_sample(model, D_t, t, delta_gen, src_tokens=src_tokens, src_mask=src_mask)
110
+ return D_t
111
+
112
+ # =============================================================================
113
+ # PART 2: LOADING THE MODEL AND RUNNING INFERENCE
114
+ # =============================================================================
115
+
116
+ # --- Configuration ---
117
+ # Replace with your own username and repo name if you forked this
118
+ repo_id = "your-hf-username/smoothie-diffusion-qqp"
119
+ device = "cuda" if torch.cuda.is_available() else "cpu"
120
+
121
+ # --- Load Model and Tokenizer from the Hub ---
122
+ print(f"Loading tokenizer and model from: {repo_id}")
123
+ tokenizer = AutoTokenizer.from_pretrained(repo_id)
124
+
125
+ # `trust_remote_code=True` is essential to load the custom SmoothieModel architecture
126
+ model = AutoModel.from_pretrained(repo_id, trust_remote_code=True).to(device)
127
+ model.eval()
128
+ print("\nModel loaded successfully from the Hub!")
129
+
130
+ # --- Prepare Diffusion Components ---
131
+ print("Preparing the embedding matrix for the diffusion process...")
132
+ bert_for_embeddings = BertModel.from_pretrained("bert-base-cased")
133
+ embedding_matrix = bert_for_embeddings.embeddings.word_embeddings.weight.detach().clone().to(device)
134
+ mean = embedding_matrix.mean(0, keepdim=True)
135
+ std = embedding_matrix.std(0, keepdim=True)
136
+ embedding_matrix = (embedding_matrix - mean) / std
137
+
138
+ # Recreate the exact noise schedule and initialize the diffusion pipeline
139
+ DIFFUSION_STEPS = 200
140
+ DELTA_GEN = 0.25
141
+ noise_schedule = get_noise_schedule(T=DIFFUSION_STEPS)
142
+ diffusion_pipeline = SmoothieDiffusion(E=embedding_matrix, schedule=noise_schedule)
143
+ print("Diffusion components are ready.")
144
+
145
+ # --- Run Inference ---
146
+ source_question = "How can I become a better writer?"
147
+ print(f"\nSource Question: {source_question}")
148
+
149
+ inputs = tokenizer(
150
+ source_question,
151
+ max_length=model.config.max_seq_len,
152
+ padding="max_length",
153
+ truncation=True,
154
+ return_tensors="pt"
155
+ )
156
+ src_tokens = inputs['input_ids'].to(device)
157
+ src_mask = (src_tokens == tokenizer.pad_token_id).to(device)
158
+
159
+ generated_D0 = diffusion_pipeline.p_sample_loop(
160
+ model,
161
+ shape=(1, model.config.max_seq_len, model.config.vocab_size),
162
+ delta_gen=DELTA_GEN,
163
+ src_tokens=src_tokens,
164
+ src_mask=src_mask
165
+ )
166
+
167
+ # --- Decode and Display the Result ---
168
+ output_tokens = torch.argmax(generated_D0, dim=-1)
169
+ decoded_text = tokenizer.decode(output_tokens, skip_special_tokens=True)
170
+
171
+ print("-" * 30)
172
+ print(f"Generated Paraphrase: {decoded_text}")
173
+ print("-" * 30)
174
+
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Training Details
180
+
181
+ This model was trained from scratch using the code available in [this notebook/repository](LINK_TO_YOUR_COLAB_NOTEBOOK_OR_GITHUB_REPO).
182
+
183
+ - **Dataset:** `glue/qqp`, filtered for positive pairs (is_duplicate = 1).
184
+ - **Training Steps:** 25,000
185
+ - **Batch Size:** 16
186
+ - **Optimizer:** AdamW
187
+ - **Learning Rate:** 2e-4
188
+ - **Hardware:** Trained on a single NVIDIA T4 GPU via Google Colab.
189
+
190
+ ### Limitations and Bias
191
+
192
+ - The model's knowledge is limited to the topics present in the Quora Questions dataset. It may perform poorly on highly specialized or out-of-domain topics.
193
+ - As with any model trained on large-scale internet text, it may reflect societal biases present in the training data.
194
+ - The model is currently undertrained and may not always produce semantically perfect paraphrases. Continued training would improve its accuracy.
195
+
196
+ ```