ARotting's picture
Publish Interactive continuous-time recurrence comparison
eff9b8d verified
Raw
History Blame Contribute Delete
2.36 kB
from __future__ import annotations
import torch
from torch import nn
class LiquidTimeConstantRNN(nn.Module):
def __init__(self, hidden_dimensions: int = 41) -> None:
super().__init__()
self.hidden_dimensions = hidden_dimensions
self.candidate = nn.Linear(hidden_dimensions + 2, hidden_dimensions)
self.log_time_constant = nn.Parameter(torch.zeros(hidden_dimensions))
self.output = nn.Linear(hidden_dimensions, 1)
def forward(self, sequence: torch.Tensor) -> torch.Tensor:
hidden = torch.zeros(
len(sequence),
self.hidden_dimensions,
device=sequence.device,
)
time_constant = torch.nn.functional.softplus(self.log_time_constant) + 0.03
outputs = []
for step in range(sequence.shape[1]):
value_and_dt = sequence[:, step]
candidate_inputs = torch.cat(
[
value_and_dt[:, :1],
value_and_dt[:, 1:2].clamp(max=0.12),
],
dim=1,
)
candidate = torch.tanh(
self.candidate(torch.cat([candidate_inputs, hidden], dim=1))
)
delta_time = value_and_dt[:, 1:2]
decay = torch.exp(-delta_time / time_constant[None])
hidden = decay * hidden + (1 - decay) * candidate
outputs.append(self.output(hidden))
return torch.stack(outputs, dim=1)
class MatchedGRU(nn.Module):
def __init__(self, hidden_dimensions: int = 23) -> None:
super().__init__()
self.recurrent = nn.GRU(2, hidden_dimensions, batch_first=True)
self.output = nn.Linear(hidden_dimensions, 1)
def forward(self, sequence: torch.Tensor) -> torch.Tensor:
hidden, _ = self.recurrent(sequence)
return self.output(hidden)
class MatchedRNN(nn.Module):
def __init__(self, hidden_dimensions: int = 41) -> None:
super().__init__()
self.recurrent = nn.RNN(2, hidden_dimensions, batch_first=True)
self.output = nn.Linear(hidden_dimensions, 1)
def forward(self, sequence: torch.Tensor) -> torch.Tensor:
hidden, _ = self.recurrent(sequence)
return self.output(hidden)
def parameter_count(model: nn.Module) -> int:
return sum(parameter.numel() for parameter in model.parameters())