File size: 2,161 Bytes
f17a465
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""
Action-conditioned world model with reconstruction.

The model learns:
    observation_t -> latent_t
    latent_t + action_t -> latent_t+1
    latent_t+1 -> predicted observation_t+1
"""

import torch
from torch import nn


class ActionConditionedWorldModel(nn.Module):
    def __init__(
        self,
        obs_dim: int,
        action_dim: int,
        latent_dim: int = 32,
        hidden_dim: int = 128,
    ):
        super().__init__()

        self.encoder = nn.Sequential(
            nn.Linear(obs_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, latent_dim),
        )

        self.action_encoder = nn.Sequential(
            nn.Linear(action_dim, 32),
            nn.ReLU(),
        )

        self.dynamics = nn.Sequential(
            nn.Linear(latent_dim + 32, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, latent_dim),
        )

        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, obs_dim),
        )

    def step(
        self,
        latent: torch.Tensor,
        action: torch.Tensor,
    ) -> torch.Tensor:
        action_features = self.action_encoder(action)
        x = torch.cat([latent, action_features], dim=-1)
        delta = self.dynamics(x)
        return latent + delta

    def forward(
        self,
        obs: torch.Tensor,
        action: torch.Tensor,
    ):
        latent = self.encoder(obs)
        next_latent = self.step(latent, action)
        next_obs_pred = self.decoder(next_latent)
        return next_obs_pred, next_latent


if __name__ == "__main__":
    torch.manual_seed(0)

    model = ActionConditionedWorldModel(
        obs_dim=12,
        action_dim=3,
        latent_dim=24,
    )

    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

    obs_t = torch.randn(32, 12)
    action_t = torch.randn(32, 3)
    obs_t1 = torch.randn(32, 12)

    pred_obs_t1, _ = model(obs_t, action_t)
    loss = nn.functional.mse_loss(pred_obs_t1, obs_t1)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    print("training loss:", float(loss))