streaming-deep-rl 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {streaming_deep_rl-0.3.0 → streaming_deep_rl-0.3.2}/PKG-INFO +1 -1
- {streaming_deep_rl-0.3.0 → streaming_deep_rl-0.3.2}/pyproject.toml +1 -1
- {streaming_deep_rl-0.3.0 → streaming_deep_rl-0.3.2}/streaming_deep_rl/streaming_deep_rl.py +114 -49
- {streaming_deep_rl-0.3.0 → streaming_deep_rl-0.3.2}/.gitignore +0 -0
- {streaming_deep_rl-0.3.0 → streaming_deep_rl-0.3.2}/LICENSE +0 -0
- {streaming_deep_rl-0.3.0 → streaming_deep_rl-0.3.2}/README.md +0 -0
- {streaming_deep_rl-0.3.0 → streaming_deep_rl-0.3.2}/streaming_deep_rl/__init__.py +0 -0
- {streaming_deep_rl-0.3.0 → streaming_deep_rl-0.3.2}/streaming_deep_rl/buffer_dict.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: streaming-deep-rl
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Streaming Deep Reinforcement Learning
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/streaming-deep-rl/
|
|
6
6
|
Project-URL: Repository, https://codeberg.org/lucidrains/streaming-deep-rl
|
|
@@ -5,8 +5,9 @@ from typing import NamedTuple, Literal
|
|
|
5
5
|
|
|
6
6
|
import torch
|
|
7
7
|
import torch.nn.functional as F
|
|
8
|
-
from torch import nn, tensor, is_tensor, cat,
|
|
8
|
+
from torch import nn, tensor, is_tensor, cat, Tensor
|
|
9
9
|
from torch.nn import Module, Linear, Sequential
|
|
10
|
+
from torch.nn.utils import parameters_to_vector
|
|
10
11
|
|
|
11
12
|
import einx
|
|
12
13
|
from einops import rearrange, reduce
|
|
@@ -14,7 +15,7 @@ from einops.layers.torch import Rearrange
|
|
|
14
15
|
|
|
15
16
|
from discrete_continuous_embed_readout import Readout, Embed
|
|
16
17
|
|
|
17
|
-
from torch_einops_utils import tree_map_tensor,
|
|
18
|
+
from torch_einops_utils import tree_map_tensor, masked_mean
|
|
18
19
|
|
|
19
20
|
from ema_pytorch import EMA
|
|
20
21
|
|
|
@@ -59,6 +60,10 @@ def l2norm(t):
|
|
|
59
60
|
|
|
60
61
|
# return types
|
|
61
62
|
|
|
63
|
+
class SSLLosses(NamedTuple):
|
|
64
|
+
total: Tensor
|
|
65
|
+
recon: Tensor
|
|
66
|
+
|
|
62
67
|
class UpdateMetrics(NamedTuple):
|
|
63
68
|
td_error: float
|
|
64
69
|
value_pred: float
|
|
@@ -68,6 +73,7 @@ class UpdateMetrics(NamedTuple):
|
|
|
68
73
|
critic_trace_norm: float
|
|
69
74
|
actor_scale: float
|
|
70
75
|
critic_scale: float
|
|
76
|
+
chunked_td_lambda: float
|
|
71
77
|
|
|
72
78
|
# initialization
|
|
73
79
|
|
|
@@ -102,23 +108,35 @@ def sparse_init_(
|
|
|
102
108
|
# orthogonal projection for SPR in streaming setting
|
|
103
109
|
# https://arxiv.org/abs/2602.09396
|
|
104
110
|
|
|
105
|
-
def
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
def orthog_project_tensors(
|
|
112
|
+
grads: list[Tensor],
|
|
113
|
+
moms: list[Tensor]
|
|
114
|
+
):
|
|
115
|
+
if len(grads) == 0:
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
flat_grad = parameters_to_vector(grads)
|
|
119
|
+
flat_mom = parameters_to_vector(moms)
|
|
108
120
|
|
|
109
|
-
dtype =
|
|
121
|
+
dtype = flat_grad.dtype
|
|
110
122
|
|
|
111
|
-
if
|
|
112
|
-
|
|
123
|
+
if flat_grad.device.type != 'mps':
|
|
124
|
+
flat_grad, flat_mom = flat_grad.double(), flat_mom.double()
|
|
113
125
|
|
|
114
|
-
unit = l2norm(
|
|
126
|
+
unit = l2norm(flat_mom)
|
|
127
|
+
orthog = flat_grad - (flat_grad * unit).sum(dim = -1, keepdim = True) * unit
|
|
115
128
|
|
|
116
|
-
|
|
117
|
-
orthog = x - parallel
|
|
129
|
+
orthog = orthog.to(dtype)
|
|
118
130
|
|
|
119
|
-
|
|
131
|
+
orthog_grads = []
|
|
132
|
+
offset = 0
|
|
120
133
|
|
|
121
|
-
|
|
134
|
+
for g in grads:
|
|
135
|
+
numel = g.numel()
|
|
136
|
+
orthog_grads.append(orthog[offset:offset + numel].view_as(g))
|
|
137
|
+
offset += numel
|
|
138
|
+
|
|
139
|
+
return orthog_grads
|
|
122
140
|
|
|
123
141
|
# Simplicial Embeddings
|
|
124
142
|
# Lavoie et al - https://arxiv.org/abs/2204.00616
|
|
@@ -127,6 +145,8 @@ class SEM(Module):
|
|
|
127
145
|
def __init__(
|
|
128
146
|
self,
|
|
129
147
|
dim,
|
|
148
|
+
dim_in = None,
|
|
149
|
+
project_in = None,
|
|
130
150
|
temperature = 0.1,
|
|
131
151
|
dim_simplex = 8,
|
|
132
152
|
pre_layernorm = False
|
|
@@ -134,6 +154,11 @@ class SEM(Module):
|
|
|
134
154
|
super().__init__()
|
|
135
155
|
assert divisible_by(dim, dim_simplex), f'{dim} must be divisible by {dim_simplex}'
|
|
136
156
|
|
|
157
|
+
dim_in = default(dim_in, dim)
|
|
158
|
+
project_in = default(project_in, dim_in != dim)
|
|
159
|
+
|
|
160
|
+
self.embedder = Linear(dim_in, dim, bias = False) if project_in else nn.Identity()
|
|
161
|
+
|
|
137
162
|
self.dim = dim
|
|
138
163
|
self.dim_simplex = dim_simplex
|
|
139
164
|
self.temperature = temperature
|
|
@@ -144,6 +169,7 @@ class SEM(Module):
|
|
|
144
169
|
self,
|
|
145
170
|
t
|
|
146
171
|
):
|
|
172
|
+
t = self.embedder(t)
|
|
147
173
|
t = self.norm(t)
|
|
148
174
|
t = rearrange(t, '... (l v) -> ... l v', v = self.dim_simplex)
|
|
149
175
|
t = (t / self.temperature).softmax(dim = -1)
|
|
@@ -161,7 +187,7 @@ class SelfPredictRepr(Module):
|
|
|
161
187
|
target_embed_from_ema = True, # whether the target embed is from an EMA, or from the model itself (traditional vs sigreg from lejepa)
|
|
162
188
|
sigreg_weight = 0.,
|
|
163
189
|
dim_hidden_expand_factor = 4,
|
|
164
|
-
use_sem =
|
|
190
|
+
use_sem = True,
|
|
165
191
|
sem_dim_simplex = 8,
|
|
166
192
|
sem_temperature = 0.1
|
|
167
193
|
):
|
|
@@ -171,22 +197,26 @@ class SelfPredictRepr(Module):
|
|
|
171
197
|
dim_predict = default(dim_predict, dim_embed)
|
|
172
198
|
dim_mlp_hidden = int(dim_embed * dim_hidden_expand_factor)
|
|
173
199
|
|
|
174
|
-
|
|
200
|
+
# when SEM is active, projects dim_embed -> dim_predict before the projector
|
|
201
|
+
|
|
202
|
+
dim_projector_in = dim_predict if use_sem else dim_embed
|
|
203
|
+
|
|
204
|
+
self.to_action_embed = Embed(dim_projector_in, num_discrete = num_discrete_actions, num_continuous = num_continuous_actions)
|
|
175
205
|
|
|
176
206
|
self.target_embed_from_ema = target_embed_from_ema
|
|
177
207
|
|
|
178
208
|
self.to_projection = nn.Sequential(
|
|
179
|
-
nn.RMSNorm(
|
|
180
|
-
MLP(
|
|
209
|
+
nn.RMSNorm(dim_projector_in),
|
|
210
|
+
MLP(dim_projector_in, dim_mlp_hidden, dim_predict)
|
|
181
211
|
)
|
|
182
212
|
|
|
183
|
-
self.to_prediction = MLP(dim_predict +
|
|
213
|
+
self.to_prediction = MLP(dim_predict + dim_projector_in, dim_mlp_hidden, dim_predict)
|
|
184
214
|
|
|
185
215
|
self.apply_sigreg = sigreg_weight > 0.
|
|
186
216
|
self.sigreg_weight = sigreg_weight
|
|
187
217
|
|
|
188
218
|
self.use_sem = use_sem
|
|
189
|
-
self.sem = SEM(dim_predict, temperature = sem_temperature, dim_simplex = sem_dim_simplex) if use_sem else nn.Identity()
|
|
219
|
+
self.sem = SEM(dim_predict, dim_in = dim_embed, project_in = True, temperature = sem_temperature, dim_simplex = sem_dim_simplex) if use_sem else nn.Identity()
|
|
190
220
|
|
|
191
221
|
def forward(
|
|
192
222
|
self,
|
|
@@ -195,6 +225,10 @@ class SelfPredictRepr(Module):
|
|
|
195
225
|
next_state_embed,
|
|
196
226
|
ema_to_projection = None
|
|
197
227
|
):
|
|
228
|
+
if self.use_sem:
|
|
229
|
+
state_embed = self.sem(state_embed)
|
|
230
|
+
next_state_embed = self.sem(next_state_embed)
|
|
231
|
+
|
|
198
232
|
projected = self.to_projection(state_embed)
|
|
199
233
|
|
|
200
234
|
# maybe no
|
|
@@ -207,22 +241,20 @@ class SelfPredictRepr(Module):
|
|
|
207
241
|
|
|
208
242
|
next_projected = decorator(target_to_projection)(next_state_embed)
|
|
209
243
|
|
|
210
|
-
if self.use_sem:
|
|
211
|
-
projected = self.sem(projected)
|
|
212
|
-
next_projected = self.sem(next_projected)
|
|
213
|
-
|
|
214
244
|
action_embed = self.to_action_embed(actions)
|
|
215
245
|
|
|
216
246
|
predicted = self.to_prediction(cat((projected, action_embed), dim = -1))
|
|
217
247
|
|
|
218
248
|
# cosine sim loss
|
|
219
249
|
|
|
220
|
-
|
|
250
|
+
recon_loss = F.mse_loss(l2norm(predicted), l2norm(next_projected))
|
|
251
|
+
|
|
252
|
+
total_loss = recon_loss
|
|
221
253
|
|
|
222
|
-
if
|
|
223
|
-
|
|
254
|
+
if self.apply_sigreg:
|
|
255
|
+
total_loss = total_loss + self.sigreg_weight * sigreg(projected)
|
|
224
256
|
|
|
225
|
-
return
|
|
257
|
+
return SSLLosses(total = total_loss, recon = recon_loss)
|
|
226
258
|
|
|
227
259
|
# Dominik Schmidt https://arxiv.org/abs/2312.10812
|
|
228
260
|
|
|
@@ -313,7 +345,7 @@ class LAPO(Module):
|
|
|
313
345
|
|
|
314
346
|
# lapo actually does a decoder back to state in pixel space, although probably not crucial given SPR
|
|
315
347
|
|
|
316
|
-
return recon_loss + pred_action_loss
|
|
348
|
+
return SSLLosses(total = recon_loss + pred_action_loss, recon = recon_loss)
|
|
317
349
|
|
|
318
350
|
# sigreg from lejepa
|
|
319
351
|
|
|
@@ -550,7 +582,8 @@ class StreamingACLambda(Module):
|
|
|
550
582
|
use_hl_gauss = False,
|
|
551
583
|
hl_gauss_val_min = -3.,
|
|
552
584
|
hl_gauss_val_max = 3.,
|
|
553
|
-
hl_gauss_num_bins = 64
|
|
585
|
+
hl_gauss_num_bins = 64,
|
|
586
|
+
use_chunked_td = False
|
|
554
587
|
):
|
|
555
588
|
super().__init__()
|
|
556
589
|
|
|
@@ -559,6 +592,11 @@ class StreamingACLambda(Module):
|
|
|
559
592
|
assert not (use_minto and not use_critic_ema), 'minto can only be used with critic ema'
|
|
560
593
|
self.use_minto = use_minto
|
|
561
594
|
|
|
595
|
+
# chunked td
|
|
596
|
+
|
|
597
|
+
assert not (use_chunked_td and ssl_type is None), 'chunked td requires a predictive model (ssl_type)'
|
|
598
|
+
self.use_chunked_td = use_chunked_td
|
|
599
|
+
|
|
562
600
|
# delightful pg
|
|
563
601
|
|
|
564
602
|
self.use_delightful_pg = use_delightful_pg
|
|
@@ -846,7 +884,8 @@ class StreamingACLambda(Module):
|
|
|
846
884
|
|
|
847
885
|
# actor with entropy regularization
|
|
848
886
|
|
|
849
|
-
|
|
887
|
+
actor_embed = self.actor(state)
|
|
888
|
+
action_logits = self.readout(actor_embed)
|
|
850
889
|
log_prob = self.readout.log_prob(action_logits, action).mean()
|
|
851
890
|
entropy = self.readout.entropy(action_logits).mean()
|
|
852
891
|
|
|
@@ -884,12 +923,12 @@ class StreamingACLambda(Module):
|
|
|
884
923
|
|
|
885
924
|
# loss
|
|
886
925
|
|
|
887
|
-
|
|
926
|
+
ssl_losses = spr_module(embed, action, next_embed, ema_to_projection = ema_to_projection)
|
|
888
927
|
|
|
889
|
-
# gradients
|
|
928
|
+
# gradients (use total loss for backprop)
|
|
890
929
|
|
|
891
930
|
spr_params = list(spr_module.parameters())
|
|
892
|
-
spr_grads_tup = torch.autograd.grad(
|
|
931
|
+
spr_grads_tup = torch.autograd.grad(ssl_losses.total, network_params + spr_params, retain_graph = True, allow_unused = True)
|
|
893
932
|
spr_grads_tup = tuple(default(g, torch.zeros_like(p)) for g, p in zip(spr_grads_tup, network_params + spr_params))
|
|
894
933
|
|
|
895
934
|
spr_grad = {name: grad for (name, _), grad in zip(network_with_readout.named_parameters(), spr_grads_tup[:len(network_params)])}
|
|
@@ -898,35 +937,41 @@ class StreamingACLambda(Module):
|
|
|
898
937
|
# orth¹: project SPR encoder grads orthogonal to their own EMA momentum
|
|
899
938
|
# this decorrelates the highly correlated streaming data
|
|
900
939
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
940
|
+
enc_names = [n for n in spr_grad.keys() if n.startswith('0.')]
|
|
941
|
+
enc_grads = [spr_grad[n] for n in enc_names]
|
|
942
|
+
enc_moms = [spr_momentum[n] for n in enc_names]
|
|
943
|
+
|
|
944
|
+
enc_grads_proj = orthog_project_tensors(enc_grads, enc_moms)
|
|
904
945
|
|
|
905
|
-
|
|
946
|
+
aux_grads = spr_only_grad
|
|
947
|
+
aux_moms = list(spr_aux_momentum.values())
|
|
948
|
+
aux_grads_proj = orthog_project_tensors(aux_grads, aux_moms)
|
|
906
949
|
|
|
907
950
|
# momentum update
|
|
908
951
|
|
|
909
|
-
for grad, mom in zip(
|
|
952
|
+
for grad, mom in zip([*enc_grads, *aux_grads], [*enc_moms, *aux_moms]):
|
|
910
953
|
mom.mul_(self.spr_orth_beta).add_(grad, alpha = 1. - self.spr_orth_beta)
|
|
911
954
|
|
|
912
|
-
for name, proj_grad in zip(
|
|
955
|
+
for name, proj_grad in zip(enc_names, enc_grads_proj):
|
|
913
956
|
spr_grad[name] = proj_grad
|
|
914
957
|
|
|
915
|
-
spr_only_grad =
|
|
958
|
+
spr_only_grad = aux_grads_proj
|
|
916
959
|
|
|
917
960
|
# update SPR auxiliary networks via SGD with dedicated spr_lr
|
|
918
961
|
|
|
919
962
|
for param, grad in zip(spr_params, spr_only_grad):
|
|
920
963
|
param.data.add_(grad, alpha = -self.spr_lr)
|
|
921
964
|
|
|
922
|
-
return
|
|
965
|
+
# return recon-only loss for chunked-td (not contaminated by sigreg / action loss)
|
|
966
|
+
|
|
967
|
+
return spr_grad, ssl_losses.recon.detach()
|
|
923
968
|
|
|
924
969
|
# get the SPR loss
|
|
925
970
|
|
|
926
971
|
actor_spr_grad = None
|
|
972
|
+
actor_spr_loss = None
|
|
927
973
|
if exists(self.ssl_type):
|
|
928
|
-
|
|
929
|
-
actor_spr_grad = get_spr_grad(
|
|
974
|
+
actor_spr_grad, actor_spr_loss = get_spr_grad(
|
|
930
975
|
actor_embed,
|
|
931
976
|
self.actor,
|
|
932
977
|
self.actor_with_readout,
|
|
@@ -941,8 +986,9 @@ class StreamingACLambda(Module):
|
|
|
941
986
|
# critic SPR loss
|
|
942
987
|
|
|
943
988
|
critic_spr_grad = None
|
|
989
|
+
critic_spr_loss = None
|
|
944
990
|
if exists(self.ssl_type):
|
|
945
|
-
critic_spr_grad = get_spr_grad(
|
|
991
|
+
critic_spr_grad, critic_spr_loss = get_spr_grad(
|
|
946
992
|
critic_embed,
|
|
947
993
|
self.critic,
|
|
948
994
|
self.critic_full,
|
|
@@ -970,6 +1016,12 @@ class StreamingACLambda(Module):
|
|
|
970
1016
|
|
|
971
1017
|
decay = self.eligibility_trace_decay * self.discount_factor
|
|
972
1018
|
|
|
1019
|
+
chunked_td_lambda = 1.
|
|
1020
|
+
|
|
1021
|
+
if self.use_chunked_td and exists(critic_spr_loss):
|
|
1022
|
+
chunked_td_lambda = max(0., 1. - critic_spr_loss.item() / 2.)
|
|
1023
|
+
decay = decay * chunked_td_lambda
|
|
1024
|
+
|
|
973
1025
|
for name, trace in self.actor_trace.items():
|
|
974
1026
|
trace.mul_(decay).add_(actor_grad[name] * gate)
|
|
975
1027
|
|
|
@@ -1018,12 +1070,24 @@ class StreamingACLambda(Module):
|
|
|
1018
1070
|
|
|
1019
1071
|
global_scale = (kappa * td_error_factor * trace_sum).reciprocal().clamp(max = 1.)
|
|
1020
1072
|
|
|
1073
|
+
rl_updates = {name: adapted_grads[name] * lr for name, _ in params.named_parameters()}
|
|
1074
|
+
|
|
1075
|
+
if exists(spr_grads):
|
|
1076
|
+
enc_names = [n for n in spr_grads.keys() if n.startswith('0.')]
|
|
1077
|
+
enc_spr_updates = [-spr_grads[n] * self.spr_lr_param_update for n in enc_names]
|
|
1078
|
+
enc_rl_updates = [rl_updates[n] for n in enc_names]
|
|
1079
|
+
|
|
1080
|
+
enc_spr_updates_proj = orthog_project_tensors(enc_spr_updates, enc_rl_updates)
|
|
1081
|
+
|
|
1082
|
+
spr_updates_proj = {n: g for n, g in zip(enc_names, enc_spr_updates_proj)}
|
|
1083
|
+
else:
|
|
1084
|
+
spr_updates_proj = {}
|
|
1085
|
+
|
|
1021
1086
|
for name, param in params.named_parameters():
|
|
1022
|
-
update =
|
|
1087
|
+
update = rl_updates[name]
|
|
1023
1088
|
|
|
1024
|
-
if
|
|
1025
|
-
|
|
1026
|
-
update = update + orthog_project(spr_update, update)
|
|
1089
|
+
if name in spr_updates_proj:
|
|
1090
|
+
update = update + spr_updates_proj[name]
|
|
1027
1091
|
|
|
1028
1092
|
update = update * global_scale
|
|
1029
1093
|
|
|
@@ -1109,7 +1173,8 @@ class StreamingACLambda(Module):
|
|
|
1109
1173
|
actor_trace_norm = actor_trace_norm.item(),
|
|
1110
1174
|
critic_trace_norm = critic_trace_norm.item(),
|
|
1111
1175
|
actor_scale = actor_scale.item(),
|
|
1112
|
-
critic_scale = critic_scale.item()
|
|
1176
|
+
critic_scale = critic_scale.item(),
|
|
1177
|
+
chunked_td_lambda = chunked_td_lambda
|
|
1113
1178
|
)
|
|
1114
1179
|
|
|
1115
1180
|
@torch.no_grad()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|