A gated recurrent unit (GRU) is a recurrent neural-network layer for sequence data. Use it when predictions depend on recent history, such as forecasting future positions from body-sensor trajectories. Its hidden state carries information between timesteps; gates control what that state keeps or discards. GRUs address the vanishing-gradient problem associated with ordinary RNNs. section 6.2.2 section 6.5.1
A GRU updates its state using two gates:
- The update gate controls how much past information remains in the hidden state. section 6.5.1
- The reset gate controls how much past information is forgotten. A candidate activation supplies a possible new state; the resulting hidden state is carried forward. section 6.5.1
A PyTorch sequence model
For pose estimation, `PoseEstimationGRU` creates `nn.GRU(input_size, hidden_size, num_layers, batch_first=True)` followed by a linear output layer. Its forward pass initializes a zero hidden state on the input’s device, runs the sequence through the GRU, and applies the output layer to the last ten timesteps. PyTorch handles the GRU layer’s internal implementation. section 6.5.1
A graph-aware decoder
A neural relational inference decoder uses a custom GRU to forecast future pose frames. It forms messages from sender and receiver hidden states, weights them by encoder-produced edge-type probabilities, and uses the messages alongside node inputs to update hidden states. This adds relational information that a sensor-only recurrent baseline does not use; it also requires a custom cell rather than simply dropping `nn.GRU` into the decoder. section 6.4.2 section 6.2.2
Listing 6.18 Custom GRU network
class CustomGRU(nn.Module):
def __init__(self,input_size, n_hid,num_vars=31):
super(CustomGRU, self).__init__()
self.num_vars = num_vars
self.hidden_r = nn.Linear
(n_hid, n_hid, bias=False) #1
self.hidden_i = nn.Linear\
(n_hid, n_hid, bias=False)
self.hidden_h = nn.Linear\
(n_hid, n_hid, bias=False)
self.input_r = nn.Linear\
(input_size, n_hid, bias=True) #2
self.input_i = nn.Linear(\
input_size, n_hid, bias=True)
self.input_n = nn.Linear\
(input_size, n_hid, bias=True)
def forward(self, inputs, agg_msgs, hidden):
inp_r = self.input_r(inputs)\
.view(inputs.size(0), self.num_vars, -1)
inp_i = self.input_i(inputs)\
.view(inputs.size(0), self.num_vars, -1)
inp_n = self.input_n(inputs)\
.view(inputs.size(0), self.num_vars, -1)
r = torch.sigmoid(inp_r + \
self.hidden_r(agg_msgs)) #3
i = torch.sigmoid(inp_i + \
self.hidden_i(agg_msgs)) #4
n = torch.tanh(inp_n + \
r*self.hidden_h(agg_msgs)) #5
hidden = (1 - i)*n + i*hidden #6
return hidden
The custom cell separates transformations of node inputs and hidden information so aggregated edge messages can participate in the recurrent update. section 6.4.2






