Source code for rlightning.policy.utils.utils
# Copied from RLinf/RLinf (Apache-2.0):
# https://github.com/RLinf/RLinf
# Original path: rlightning/policy/utils/utils.py
# See THIRD_PARTY_NOTICES.md for details.
from typing import Optional
import torch
[docs]
def huber_loss(error: torch.Tensor, delta: float) -> torch.Tensor:
return torch.where(error.abs() < delta, 0.5 * error**2, delta * (error.abs() - 0.5 * delta))
[docs]
def preprocess_loss_inputs(
logprobs: torch.Tensor,
old_logprobs: torch.Tensor,
advantages: torch.Tensor,
logprob_type: Optional[str] = None,
single_action_dim: Optional[int] = None,
loss_mask: Optional[torch.Tensor] = None,
loss_mask_sum: Optional[torch.Tensor] = None,
values: Optional[torch.Tensor] = None,
prev_values: Optional[torch.Tensor] = None,
returns: Optional[torch.Tensor] = None,
reward_type: Optional[str] = None,
**kwargs,
) -> dict:
if reward_type == "chunk_level":
advantages = advantages.flatten()
if loss_mask is not None:
loss_mask = loss_mask.flatten()
if loss_mask_sum is not None:
loss_mask_sum = loss_mask_sum.flatten()
if values is not None:
values = values.flatten()
if prev_values is not None:
prev_values = prev_values.flatten()
if returns is not None:
returns = returns.flatten()
bsz = logprobs.shape[0]
if logprob_type == "token_level":
# logprobs, old_logprobs: [bsz, num_action_chunks, action_dim] -> [bsz, num_action_chunks, action_dim]
logprobs = logprobs.reshape(bsz, -1, single_action_dim)
old_logprobs = old_logprobs.reshape(bsz, -1, single_action_dim)
advantages = advantages.unsqueeze(-1)
if loss_mask is not None:
loss_mask = loss_mask.unsqueeze(-1)
if loss_mask_sum is not None:
loss_mask_sum = loss_mask_sum.unsqueeze(-1)
elif logprob_type == "action_level":
# logprobs, old_logprobs: [bsz, num_action_chunks, action_dim] -> [bsz, num_action_chunks]
logprobs = logprobs.reshape(bsz, -1, single_action_dim).sum(dim=-1)
old_logprobs = old_logprobs.reshape(bsz, -1, single_action_dim).sum(dim=-1)
elif logprob_type == "chunk_level":
# logprobs, old_logprobs: [bsz, num_action_chunks, action_dim] -> [bsz]
logprobs = logprobs.reshape(bsz, -1, single_action_dim).sum(dim=[1, 2])
old_logprobs = old_logprobs.reshape(bsz, -1, single_action_dim).sum(dim=[1, 2])
target_shape = logprobs.shape
advantages = expand_to_target_dim(advantages, target_shape)
loss_mask = expand_to_target_dim(loss_mask, target_shape)
loss_mask_sum = expand_to_target_dim(loss_mask_sum, target_shape)
values = expand_to_target_dim(values, target_shape)
prev_values = expand_to_target_dim(prev_values, target_shape)
returns = expand_to_target_dim(returns, target_shape)
kwargs.update(
{
"logprobs": logprobs,
"old_logprobs": old_logprobs,
"advantages": advantages,
"loss_mask": loss_mask,
"loss_mask_sum": loss_mask_sum,
"values": values,
"prev_values": prev_values,
"returns": returns,
}
)
return kwargs
[docs]
def postprocess_loss_metric(metrics_data: dict) -> dict:
for k, v in metrics_data.items():
if isinstance(v, torch.Tensor):
metrics_data[k] = v.detach().item()
elif isinstance(v, (float, int)):
metrics_data[k] = v
return metrics_data
[docs]
def expand_to_target_dim(tensor, target_shape):
if tensor is None:
return None
if tensor.shape != target_shape:
while len(tensor.shape) < len(target_shape):
tensor = tensor.unsqueeze(-1)
return tensor
[docs]
def append_to_dict(data, new_data):
for key, val in new_data.items():
if key not in data:
data[key] = []
data[key].append(val)
data[key].append(val)
[docs]
def masked_mean(values: torch.Tensor, mask: torch.Tensor, axis=None):
"""Compute mean of tensor with a masked values."""
if mask is None:
return values.mean(axis=axis)
elif (~mask).all():
return (values * mask).sum(axis=axis)
else:
return (values * mask).sum(axis=axis) / mask.sum(axis=axis)
[docs]
def masked_sum(values: torch.Tensor, mask: torch.Tensor, axis=None):
"""Compute mean of tensor with a masked values."""
return (values * mask).sum(axis=axis)
[docs]
def seq_mean_token_sum(values: torch.Tensor, mask: torch.Tensor, dim: int = -1):
seq_losses = torch.sum(values * mask, dim=-1) # token-sum
loss = torch.mean(seq_losses) # seq-mean
return loss
[docs]
def seq_mean_token_mean(values: torch.Tensor, mask: torch.Tensor, dim: int = -1):
seq_losses = torch.sum(values * mask, dim=-1) / torch.sum(mask, dim=-1) # token-mean
loss = torch.mean(seq_losses) # seq-mean
return loss
[docs]
def masked_mean_ratio(values: torch.Tensor, mask: torch.Tensor, loss_mask_ratio: torch.Tensor):
# for embodied tasks
return (values / loss_mask_ratio * mask).mean()