plasmova commited on
Commit
64f89d6
·
verified ·
1 Parent(s): 546d6ff

Add KV cache support for generation

Browse files
Files changed (1) hide show
  1. modeling_nova.py +63 -12
modeling_nova.py CHANGED
@@ -2,7 +2,7 @@ import torch
2
  import torch.nn as nn
3
  import torch.nn.functional as F
4
  from transformers import GenerationMixin, PreTrainedModel
5
- from transformers.modeling_outputs import CausalLMOutput
6
 
7
  try:
8
  from .configuration_nova import NovaConfig
@@ -45,7 +45,7 @@ class NovaBlock(nn.Module):
45
  self.up = nn.Linear(d, ff, bias=False)
46
  self.down = nn.Linear(ff, d, bias=False)
47
 
48
- def forward(self, x, cos, sin, attention_mask):
49
  batch, length, _ = x.shape
50
  h = self.n1(x)
51
  q = self.wq(h).view(batch, length, self.h, self.hd)
@@ -54,17 +54,26 @@ class NovaBlock(nn.Module):
54
  q = _rope(self.qn(q), cos, sin).to(v.dtype).transpose(1, 2)
55
  k = _rope(self.kn(k), cos, sin).to(v.dtype).transpose(1, 2)
56
  v = v.transpose(1, 2)
 
 
 
 
57
  repeat = self.h // self.kv
58
  k = k.repeat_interleave(repeat, dim=1)
59
  v = v.repeat_interleave(repeat, dim=1)
60
- causal = torch.ones(length, length, dtype=torch.bool, device=x.device).tril()
 
 
 
 
61
  mask = causal[None, None, :, :]
62
  if attention_mask is not None:
63
  mask = mask & attention_mask[:, None, None, :].to(torch.bool)
64
  out = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
65
  x = x + self.wo(out.transpose(1, 2).reshape(batch, length, -1))
66
  h = self.n2(x)
67
- return x + self.down(F.silu(self.gate(h)) * self.up(h))
 
68
 
69
 
70
  class NovaModel(PreTrainedModel):
@@ -99,25 +108,42 @@ class NovaModel(PreTrainedModel):
99
  attention_mask=None,
100
  position_ids=None,
101
  inputs_embeds=None,
 
 
102
  **kwargs,
103
  ):
104
  if inputs_embeds is None:
105
  inputs_embeds = self.emb(input_ids)
106
  batch, length, _ = inputs_embeds.shape
107
  if position_ids is None:
108
- position_ids = torch.arange(length, device=inputs_embeds.device).unsqueeze(0).expand(batch, -1)
 
 
 
 
 
 
 
 
109
  cos = self.cos[position_ids].unsqueeze(-2)
110
  sin = self.sin[position_ids].unsqueeze(-2)
111
  hidden = inputs_embeds
112
- for block in self.blocks:
113
- hidden = block(hidden, cos, sin, attention_mask)
114
- return self.norm(hidden)
 
 
 
 
 
 
115
 
116
 
117
  class NovaForCausalLM(PreTrainedModel, GenerationMixin):
118
  config_class = NovaConfig
119
  base_model_prefix = "model"
120
  _tied_weights_keys = []
 
121
 
122
  def __init__(self, config):
123
  super().__init__(config)
@@ -134,8 +160,25 @@ class NovaForCausalLM(PreTrainedModel, GenerationMixin):
134
  return None
135
 
136
  def prepare_inputs_for_generation(self, input_ids, attention_mask=None, **kwargs):
137
- # This model has no KV cache; each generation step recomputes the full prefix.
138
- return {"input_ids": input_ids, "attention_mask": attention_mask}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
  def forward(
141
  self,
@@ -144,13 +187,19 @@ class NovaForCausalLM(PreTrainedModel, GenerationMixin):
144
  position_ids=None,
145
  inputs_embeds=None,
146
  labels=None,
 
 
147
  **kwargs,
148
  ):
149
- hidden = self.model(
 
 
150
  input_ids=input_ids,
151
  attention_mask=attention_mask,
152
  position_ids=position_ids,
153
  inputs_embeds=inputs_embeds,
 
 
154
  )
155
  logits = F.linear(hidden, self.model.emb.weight)
156
  loss = None
@@ -160,5 +209,7 @@ class NovaForCausalLM(PreTrainedModel, GenerationMixin):
160
  labels[..., 1:].contiguous().view(-1),
161
  ignore_index=-100,
162
  )
163
- return CausalLMOutput(loss=loss, logits=logits)
 
 
164
 
 
2
  import torch.nn as nn
3
  import torch.nn.functional as F
4
  from transformers import GenerationMixin, PreTrainedModel
5
+ from transformers.modeling_outputs import CausalLMOutputWithPast
6
 
7
  try:
8
  from .configuration_nova import NovaConfig
 
45
  self.up = nn.Linear(d, ff, bias=False)
46
  self.down = nn.Linear(ff, d, bias=False)
47
 
48
+ def forward(self, x, cos, sin, attention_mask, past_key_value=None, use_cache=False):
49
  batch, length, _ = x.shape
50
  h = self.n1(x)
51
  q = self.wq(h).view(batch, length, self.h, self.hd)
 
54
  q = _rope(self.qn(q), cos, sin).to(v.dtype).transpose(1, 2)
55
  k = _rope(self.kn(k), cos, sin).to(v.dtype).transpose(1, 2)
56
  v = v.transpose(1, 2)
57
+ if past_key_value is not None:
58
+ k = torch.cat((past_key_value[0], k), dim=-2)
59
+ v = torch.cat((past_key_value[1], v), dim=-2)
60
+ present = (k, v) if use_cache else None
61
  repeat = self.h // self.kv
62
  k = k.repeat_interleave(repeat, dim=1)
63
  v = v.repeat_interleave(repeat, dim=1)
64
+ key_length = k.size(-2)
65
+ past_length = key_length - length
66
+ query_positions = past_length + torch.arange(length, device=x.device)
67
+ key_positions = torch.arange(key_length, device=x.device)
68
+ causal = key_positions[None, :] <= query_positions[:, None]
69
  mask = causal[None, None, :, :]
70
  if attention_mask is not None:
71
  mask = mask & attention_mask[:, None, None, :].to(torch.bool)
72
  out = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
73
  x = x + self.wo(out.transpose(1, 2).reshape(batch, length, -1))
74
  h = self.n2(x)
75
+ x = x + self.down(F.silu(self.gate(h)) * self.up(h))
76
+ return x, present
77
 
78
 
79
  class NovaModel(PreTrainedModel):
 
108
  attention_mask=None,
109
  position_ids=None,
110
  inputs_embeds=None,
111
+ past_key_values=None,
112
+ use_cache=False,
113
  **kwargs,
114
  ):
115
  if inputs_embeds is None:
116
  inputs_embeds = self.emb(input_ids)
117
  batch, length, _ = inputs_embeds.shape
118
  if position_ids is None:
119
+ past_length = past_key_values[0][0].size(-2) if past_key_values else 0
120
+ if attention_mask is not None:
121
+ position_ids = attention_mask.long().cumsum(-1) - 1
122
+ position_ids.masked_fill_(attention_mask == 0, 0)
123
+ position_ids = position_ids[:, -length:]
124
+ else:
125
+ position_ids = torch.arange(
126
+ past_length, past_length + length, device=inputs_embeds.device
127
+ ).unsqueeze(0).expand(batch, -1)
128
  cos = self.cos[position_ids].unsqueeze(-2)
129
  sin = self.sin[position_ids].unsqueeze(-2)
130
  hidden = inputs_embeds
131
+ presents = () if use_cache else None
132
+ for i, block in enumerate(self.blocks):
133
+ layer_past = past_key_values[i] if past_key_values is not None else None
134
+ hidden, present = block(
135
+ hidden, cos, sin, attention_mask, layer_past, use_cache
136
+ )
137
+ if use_cache:
138
+ presents += (present,)
139
+ return self.norm(hidden), presents
140
 
141
 
142
  class NovaForCausalLM(PreTrainedModel, GenerationMixin):
143
  config_class = NovaConfig
144
  base_model_prefix = "model"
145
  _tied_weights_keys = []
146
+ _supports_cache_class = False
147
 
148
  def __init__(self, config):
149
  super().__init__(config)
 
160
  return None
161
 
162
  def prepare_inputs_for_generation(self, input_ids, attention_mask=None, **kwargs):
163
+ past_key_values = kwargs.get("past_key_values")
164
+ if past_key_values is not None:
165
+ input_ids = input_ids[:, -1:]
166
+ if attention_mask is not None:
167
+ position_ids = attention_mask.long().cumsum(-1) - 1
168
+ position_ids.masked_fill_(attention_mask == 0, 0)
169
+ position_ids = position_ids[:, -input_ids.size(1):]
170
+ else:
171
+ past_length = past_key_values[0][0].size(-2) if past_key_values else 0
172
+ position_ids = torch.arange(
173
+ past_length, past_length + input_ids.size(1), device=input_ids.device
174
+ ).unsqueeze(0).expand(input_ids.size(0), -1)
175
+ return {
176
+ "input_ids": input_ids,
177
+ "attention_mask": attention_mask,
178
+ "position_ids": position_ids,
179
+ "past_key_values": past_key_values,
180
+ "use_cache": kwargs.get("use_cache", True),
181
+ }
182
 
183
  def forward(
184
  self,
 
187
  position_ids=None,
188
  inputs_embeds=None,
189
  labels=None,
190
+ past_key_values=None,
191
+ use_cache=None,
192
  **kwargs,
193
  ):
194
+ if use_cache is None:
195
+ use_cache = self.config.use_cache
196
+ hidden, presents = self.model(
197
  input_ids=input_ids,
198
  attention_mask=attention_mask,
199
  position_ids=position_ids,
200
  inputs_embeds=inputs_embeds,
201
+ past_key_values=past_key_values,
202
+ use_cache=use_cache,
203
  )
204
  logits = F.linear(hidden, self.model.emb.weight)
205
  loss = None
 
209
  labels[..., 1:].contiguous().view(-1),
210
  ignore_index=-100,
211
  )
212
+ return CausalLMOutputWithPast(
213
+ loss=loss, logits=logits, past_key_values=presents
214
+ )
215