| import torch |
| from torch import nn |
| from torch.utils.data import TensorDataset, DataLoader |
| import einops |
| from nmt_utils import * |
|
|
| torch.set_default_device(torch.device("cpu")) |
|
|
| |
| human_vocab, machine_vocab, inv_machine_vocab = load_vocab() |
|
|
| |
| Tx = 30 |
| Ty = 10 |
| n_a = 32 |
| n_s = 64 |
|
|
| class WeightedSum(nn.Module): |
| """ |
| Compute context_t by weighted sum |
| """ |
| def __init__(self, a): |
| super().__init__() |
| self.a = a |
|
|
| def forward(self, alpha): |
| return torch.sum(alpha * self.a, dim=1) |
|
|
| class AttentionBlock(nn.Module): |
| def __init__(self): |
| super().__init__() |
| self.flatten = nn.Flatten() |
|
|
| def forward(self, s_prev: torch.Tensor, a: torch.Tensor): |
| """ |
| Params: |
| s_prev: previous hidden state of LSTM, of shape (1, m, n_s) needed to reshape to (m, n_s) |
| a: Bi-LSTM's outputs, of shape (m, Tx, 2*n_a), each output is a concatenation of two direction outputs |
| |
| Return: |
| context_t, output of attention block, of shape (m, 1, 2 * n_a) |
| """ |
| Tx = a.shape[1] |
| s_prev = s_prev.reshape(-1, n_s) |
| s_prev_repeated = einops.repeat(s_prev, "m n_s -> m Tx n_s", Tx=Tx) |
| concat = torch.cat((s_prev_repeated, a), dim=2) |
|
|
| return nn.Sequential( |
| nn.Linear(in_features=n_s + 2 * n_a, out_features=10), |
| nn.Tanh(), |
| nn.Linear(in_features=10, out_features=1), |
| nn.ReLU(), |
| nn.Softmax(dim=2), |
| WeightedSum(a) |
| )(concat).reshape(-1, 1, 2*n_a) |
| |
| class NMTAttentionModel(nn.Module): |
| """ |
| Output: |
| Predicted result sequence without softmax, of shape (m, Ty, len(machine_vocab)) |
| |
| """ |
| def __init__(self, Tx: int, Ty: int): |
| super().__init__() |
| self.pre_attention_lstm = nn.LSTM(1, n_a, 1, batch_first=True, bidirectional=True) |
| self.attention_block = AttentionBlock() |
| self.post_attention_lstm = nn.LSTM(2 * n_a, n_s, 1, batch_first=True) |
| self.output_layer = nn.Linear(in_features=n_s, out_features=len(machine_vocab)) |
|
|
| def forward(self, X, example_size: int): |
| s_t = torch.zeros((1, example_size, n_s)) |
| c_t = torch.zeros((1, example_size, n_s)) |
| a, _ = self.pre_attention_lstm(X, (torch.zeros((2, example_size, n_a)), torch.zeros((2, example_size, n_a)))) |
| y_pred = torch.zeros((example_size, Ty, len(machine_vocab))) |
|
|
| for t in range(Ty): |
| context_t = self.attention_block(s_t, a) |
| _, (s_t, c_t) = self.post_attention_lstm(context_t, (s_t, c_t)) |
| output = self.output_layer(s_t) |
| y_pred[:, t] = output |
|
|
| return y_pred |
| |
| model = NMTAttentionModel(Tx, Ty) |
| model.load_state_dict(torch.load("date_recognition_attention_model_cpu.pt", weights_only=True)) |
| model.eval() |
|
|
| def test_model(model, human_date): |
| source = string_to_int(human_date, Tx, human_vocab) |
| source = torch.tensor(source, dtype=torch.float32).reshape(1, -1, 1) |
| with torch.no_grad(): |
| pred = model(source, 1) |
| prediction = pred.argmax(dim=2).reshape(Ty,) |
| output = ''.join([inv_machine_vocab[int(i)] for i in prediction]) |
| |
| return output |