usmannawaz commited on
Commit
44e3dc5
·
verified ·
1 Parent(s): 92e9293

Update model.py

Browse files
Files changed (1) hide show
  1. model.py +260 -71
model.py CHANGED
@@ -1,4 +1,5 @@
1
  from typing import List
 
2
  import torch
3
  import torch.nn as nn
4
 
@@ -12,47 +13,99 @@ class Vocab:
12
  "<eos>": 2,
13
  "<unk>": 3,
14
  }
15
- self.char2idx = char2idx
 
 
 
 
 
16
  if idx2char is None:
17
- self.idx2char = {i: c for c, i in self.char2idx.items()}
 
 
 
18
  else:
19
- self.idx2char = {int(k): v for k, v in idx2char.items()}
 
 
 
 
 
 
20
 
21
- def encode(self, s: str) -> List[int]:
22
- unk = self.char2idx["<unk>"]
23
- return [self.char2idx.get(ch, unk) for ch in s]
 
24
 
25
  def decode(self, ids: List[int]) -> str:
26
- out = []
27
  eos_id = self.char2idx["<eos>"]
28
- for i in ids:
29
- if i == eos_id:
 
 
 
30
  break
31
- if i > eos_id:
32
- out.append(self.idx2char.get(int(i), ""))
33
- return "".join(out)
 
 
 
 
34
 
35
 
36
  class LemmaModel(nn.Module):
37
  def __init__(
38
  self,
39
- vocab_size: int,
40
- char_emb_dim: int = 96,
41
- hidden_size: int = 128,
42
- drop_prob: float = 0.30,
43
- num_heads: int = 16,
44
- max_gen_len: int = 30,
45
  ):
46
  super().__init__()
 
47
  self.max_gen_len = max_gen_len
48
- self.emb = nn.Embedding(vocab_size, char_emb_dim, padding_idx=0)
 
 
 
 
 
 
49
  self.dropout_enc = nn.Dropout(drop_prob)
50
  self.dropout_dec = nn.Dropout(drop_prob)
51
  self.dropout_att = nn.Dropout(drop_prob)
52
- self.enc1 = nn.LSTM(char_emb_dim, hidden_size, bidirectional=True, batch_first=True)
53
- self.enc2 = nn.LSTM(hidden_size * 2, hidden_size, bidirectional=True, batch_first=True)
54
- self.attn = nn.MultiheadAttention(hidden_size * 2, num_heads, batch_first=True)
55
- self.dec = nn.LSTM(char_emb_dim + hidden_size * 4, hidden_size * 2, batch_first=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  self.dec_cross_attn = nn.MultiheadAttention(
57
  embed_dim=hidden_size * 2,
58
  num_heads=num_heads,
@@ -60,74 +113,210 @@ class LemmaModel(nn.Module):
60
  vdim=hidden_size * 4,
61
  batch_first=True,
62
  )
63
- self.out = nn.Linear(hidden_size * 2, vocab_size, bias=True)
 
 
 
 
 
64
 
65
  def encode(self, src, src_lens):
66
- emb = self.emb(src)
 
67
  packed1 = nn.utils.rnn.pack_padded_sequence(
68
- emb, src_lens.cpu(), batch_first=True, enforce_sorted=False
 
 
 
 
 
 
 
 
 
 
 
 
69
  )
70
- enc1_o, _ = self.enc1(packed1)
71
- enc1_o, _ = nn.utils.rnn.pad_packed_sequence(enc1_o, batch_first=True)
72
- enc1_o = self.dropout_enc(enc1_o)
73
  packed2 = nn.utils.rnn.pack_padded_sequence(
74
- enc1_o, src_lens.cpu(), batch_first=True, enforce_sorted=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  )
76
- enc2_o, _ = self.enc2(packed2)
77
- enc2_o, _ = nn.utils.rnn.pad_packed_sequence(enc2_o, batch_first=True)
78
- enc2_o = self.dropout_enc(enc2_o)
79
- attn_o, _ = self.attn(enc1_o, enc2_o, enc2_o)
80
- attn_o = self.dropout_att(attn_o)
81
- return torch.cat([enc2_o, attn_o], dim=-1)
82
 
83
  def forward(self, src, src_lens, tgt):
84
- encoder_combined = self.encode(src, src_lens)
85
- dt = self.emb(tgt[:, :-1])
86
- target_len = dt.size(1)
 
 
 
 
 
87
  if encoder_combined.size(1) >= target_len:
88
- comb_trim = encoder_combined[:, :target_len, :]
 
 
89
  else:
90
- pad = encoder_combined.new_zeros(
91
  encoder_combined.size(0),
92
  target_len - encoder_combined.size(1),
93
  encoder_combined.size(2),
94
  )
95
- comb_trim = torch.cat([encoder_combined, pad], dim=1)
96
- dec_inp = torch.cat([dt, comb_trim], dim=-1)
97
- dec_o, _ = self.dec(dec_inp)
98
- dec_o = self.dropout_dec(dec_o)
99
- cross_out, _ = self.dec_cross_attn(dec_o, encoder_combined, encoder_combined)
100
- cross_out = self.dropout_att(cross_out)
101
- return self.out(cross_out)
102
-
103
- def generate(self, src, src_lens, vocab, max_len=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  self.eval()
 
105
  if max_len is None:
106
  max_len = self.max_gen_len
 
107
  batch_size = src.size(0)
108
- with torch.no_grad():
109
- encoder_combined = self.encode(src, src_lens)
 
 
 
 
 
 
110
  source_len = encoder_combined.size(1)
111
- cur = torch.full(
112
- (batch_size, 1), vocab.char2idx["<sos>"], device=src.device, dtype=torch.long
 
 
 
 
113
  )
 
114
  hidden = None
115
- hyps = [[] for _ in range(batch_size)]
 
 
 
 
 
 
 
 
 
 
116
  for step in range(max_len):
117
- emb_t = self.emb(cur).squeeze(1)
118
- if source_len == 0:
119
- comb_t = encoder_combined[:, 0, :]
120
- else:
121
- comb_t = encoder_combined[:, min(step, source_len - 1), :]
122
- dec_inp_t = torch.cat([emb_t, comb_t], dim=-1).unsqueeze(1)
123
- dec_o, hidden = self.dec(dec_inp_t, hidden)
124
- dec_o = self.dropout_dec(dec_o)
125
- cross_out, _ = self.dec_cross_attn(dec_o, encoder_combined, encoder_combined)
126
- cross_out = self.dropout_att(cross_out)
127
- logits = self.out(cross_out)
128
- cur = logits.argmax(-1)
129
- for i in range(batch_size):
130
- hyps[i].append(int(cur[i, 0].item()))
131
- if all(int(cur[i, 0].item()) == vocab.char2idx["<eos>"] for i in range(batch_size)):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  break
133
- return [vocab.decode(h) for h in hyps]
 
 
 
 
 
 
 
 
 
 
 
1
  from typing import List
2
+
3
  import torch
4
  import torch.nn as nn
5
 
 
13
  "<eos>": 2,
14
  "<unk>": 3,
15
  }
16
+
17
+ self.char2idx = {
18
+ char: int(index)
19
+ for char, index in char2idx.items()
20
+ }
21
+
22
  if idx2char is None:
23
+ self.idx2char = {
24
+ index: char
25
+ for char, index in self.char2idx.items()
26
+ }
27
  else:
28
+ self.idx2char = {
29
+ int(index): char
30
+ for index, char in idx2char.items()
31
+ }
32
+
33
+ def encode(self, text: str) -> List[int]:
34
+ unk_id = self.char2idx["<unk>"]
35
 
36
+ return [
37
+ self.char2idx.get(character, unk_id)
38
+ for character in text
39
+ ]
40
 
41
  def decode(self, ids: List[int]) -> str:
42
+ characters = []
43
  eos_id = self.char2idx["<eos>"]
44
+
45
+ for index in ids:
46
+ index = int(index)
47
+
48
+ if index == eos_id:
49
  break
50
+
51
+ if index > eos_id:
52
+ characters.append(
53
+ self.idx2char.get(index, "")
54
+ )
55
+
56
+ return "".join(characters)
57
 
58
 
59
  class LemmaModel(nn.Module):
60
  def __init__(
61
  self,
62
+ vocab_size,
63
+ char_emb_dim=96,
64
+ hidden_size=128,
65
+ drop_prob=0.30,
66
+ num_heads=16,
67
+ max_gen_len=30,
68
  ):
69
  super().__init__()
70
+
71
  self.max_gen_len = max_gen_len
72
+
73
+ self.emb = nn.Embedding(
74
+ vocab_size,
75
+ char_emb_dim,
76
+ padding_idx=0,
77
+ )
78
+
79
  self.dropout_enc = nn.Dropout(drop_prob)
80
  self.dropout_dec = nn.Dropout(drop_prob)
81
  self.dropout_att = nn.Dropout(drop_prob)
82
+
83
+ self.enc1 = nn.LSTM(
84
+ char_emb_dim,
85
+ hidden_size,
86
+ bidirectional=True,
87
+ batch_first=True,
88
+ )
89
+
90
+ self.enc2 = nn.LSTM(
91
+ hidden_size * 2,
92
+ hidden_size,
93
+ bidirectional=True,
94
+ batch_first=True,
95
+ )
96
+
97
+ self.attn = nn.MultiheadAttention(
98
+ hidden_size * 2,
99
+ num_heads,
100
+ batch_first=True,
101
+ )
102
+
103
+ self.dec = nn.LSTM(
104
+ char_emb_dim + hidden_size * 4,
105
+ hidden_size * 2,
106
+ batch_first=True,
107
+ )
108
+
109
  self.dec_cross_attn = nn.MultiheadAttention(
110
  embed_dim=hidden_size * 2,
111
  num_heads=num_heads,
 
113
  vdim=hidden_size * 4,
114
  batch_first=True,
115
  )
116
+
117
+ self.out = nn.Linear(
118
+ hidden_size * 2,
119
+ vocab_size,
120
+ bias=True,
121
+ )
122
 
123
  def encode(self, src, src_lens):
124
+ embedded = self.emb(src)
125
+
126
  packed1 = nn.utils.rnn.pack_padded_sequence(
127
+ embedded,
128
+ src_lens.cpu(),
129
+ batch_first=True,
130
+ enforce_sorted=False,
131
+ )
132
+
133
+ enc1_output, _ = self.enc1(packed1)
134
+
135
+ enc1_output, _ = (
136
+ nn.utils.rnn.pad_packed_sequence(
137
+ enc1_output,
138
+ batch_first=True,
139
+ )
140
  )
141
+
142
+ enc1_output = self.dropout_enc(enc1_output)
143
+
144
  packed2 = nn.utils.rnn.pack_padded_sequence(
145
+ enc1_output,
146
+ src_lens.cpu(),
147
+ batch_first=True,
148
+ enforce_sorted=False,
149
+ )
150
+
151
+ enc2_output, _ = self.enc2(packed2)
152
+
153
+ enc2_output, _ = (
154
+ nn.utils.rnn.pad_packed_sequence(
155
+ enc2_output,
156
+ batch_first=True,
157
+ )
158
+ )
159
+
160
+ enc2_output = self.dropout_enc(enc2_output)
161
+
162
+ attention_output, _ = self.attn(
163
+ enc1_output,
164
+ enc2_output,
165
+ enc2_output,
166
+ )
167
+
168
+ attention_output = self.dropout_att(
169
+ attention_output
170
+ )
171
+
172
+ return torch.cat(
173
+ [enc2_output, attention_output],
174
+ dim=-1,
175
  )
 
 
 
 
 
 
176
 
177
  def forward(self, src, src_lens, tgt):
178
+ encoder_combined = self.encode(
179
+ src,
180
+ src_lens,
181
+ )
182
+
183
+ decoder_target = self.emb(tgt[:, :-1])
184
+ target_len = decoder_target.size(1)
185
+
186
  if encoder_combined.size(1) >= target_len:
187
+ combined_trimmed = encoder_combined[
188
+ :, :target_len, :
189
+ ]
190
  else:
191
+ padding = encoder_combined.new_zeros(
192
  encoder_combined.size(0),
193
  target_len - encoder_combined.size(1),
194
  encoder_combined.size(2),
195
  )
196
+
197
+ combined_trimmed = torch.cat(
198
+ [encoder_combined, padding],
199
+ dim=1,
200
+ )
201
+
202
+ decoder_input = torch.cat(
203
+ [decoder_target, combined_trimmed],
204
+ dim=-1,
205
+ )
206
+
207
+ decoder_output, _ = self.dec(decoder_input)
208
+ decoder_output = self.dropout_dec(decoder_output)
209
+
210
+ cross_output, _ = self.dec_cross_attn(
211
+ decoder_output,
212
+ encoder_combined,
213
+ encoder_combined,
214
+ )
215
+
216
+ cross_output = self.dropout_att(cross_output)
217
+
218
+ return self.out(cross_output)
219
+
220
+ def generate(
221
+ self,
222
+ src,
223
+ src_lens,
224
+ vocab,
225
+ max_len=None,
226
+ ):
227
  self.eval()
228
+
229
  if max_len is None:
230
  max_len = self.max_gen_len
231
+
232
  batch_size = src.size(0)
233
+ eos_id = vocab.char2idx["<eos>"]
234
+
235
+ with torch.inference_mode():
236
+ encoder_combined = self.encode(
237
+ src,
238
+ src_lens,
239
+ )
240
+
241
  source_len = encoder_combined.size(1)
242
+
243
+ current = torch.full(
244
+ (batch_size, 1),
245
+ vocab.char2idx["<sos>"],
246
+ device=src.device,
247
+ dtype=torch.long,
248
  )
249
+
250
  hidden = None
251
+ hypotheses = [
252
+ []
253
+ for _ in range(batch_size)
254
+ ]
255
+
256
+ finished = torch.zeros(
257
+ batch_size,
258
+ dtype=torch.bool,
259
+ device=src.device,
260
+ )
261
+
262
  for step in range(max_len):
263
+ embedded = self.emb(current).squeeze(1)
264
+
265
+ combined_step = encoder_combined[
266
+ :,
267
+ min(step, source_len - 1),
268
+ :,
269
+ ]
270
+
271
+ decoder_input = torch.cat(
272
+ [embedded, combined_step],
273
+ dim=-1,
274
+ ).unsqueeze(1)
275
+
276
+ decoder_output, hidden = self.dec(
277
+ decoder_input,
278
+ hidden,
279
+ )
280
+
281
+ decoder_output = self.dropout_dec(
282
+ decoder_output
283
+ )
284
+
285
+ cross_output, _ = self.dec_cross_attn(
286
+ decoder_output,
287
+ encoder_combined,
288
+ encoder_combined,
289
+ )
290
+
291
+ cross_output = self.dropout_att(
292
+ cross_output
293
+ )
294
+
295
+ logits = self.out(cross_output)
296
+ next_ids = logits.argmax(dim=-1)
297
+ current = next_ids
298
+
299
+ for index in range(batch_size):
300
+ if not finished[index]:
301
+ token_id = int(
302
+ next_ids[index, 0].item()
303
+ )
304
+
305
+ hypotheses[index].append(token_id)
306
+
307
+ if token_id == eos_id:
308
+ finished[index] = True
309
+
310
+ if finished.all():
311
  break
312
+
313
+ current = torch.where(
314
+ finished.unsqueeze(1),
315
+ torch.full_like(current, eos_id),
316
+ current,
317
+ )
318
+
319
+ return [
320
+ vocab.decode(hypothesis)
321
+ for hypothesis in hypotheses
322
+ ]