Files
wehub-resource-sync 593b94c120
pytest / Unit Tests (push) Has been cancelled
pytest / Integration (integration_tests_a) (push) Has been cancelled
pytest / Integration (integration_tests_b) (push) Has been cancelled
pytest / Integration (integration_tests_c) (push) Has been cancelled
pytest / Integration (integration_tests_d) (push) Has been cancelled
pytest / Integration (integration_tests_e) (push) Has been cancelled
pytest / Integration (integration_tests_f) (push) Has been cancelled
pytest / Integration (integration_tests_g) (push) Has been cancelled
pytest / Integration (integration_tests_h) (push) Has been cancelled
pytest / Integration (integration_tests_i) (push) Has been cancelled
pytest / Integration (integration_tests_j) (push) Has been cancelled
pytest / Distributed (distributed_a) (push) Has been cancelled
pytest / Distributed (distributed_b) (push) Has been cancelled
pytest / Distributed (distributed_c) (push) Has been cancelled
pytest / Distributed (distributed_d) (push) Has been cancelled
pytest / Distributed (distributed_e) (push) Has been cancelled
pytest / Distributed (distributed_f) (push) Has been cancelled
pytest / Minimal Install (push) Has been cancelled
pytest / Event File (push) Has been cancelled
pytest (slow) / py-slow (push) Has been cancelled
Publish JSON Schema / publish-schema (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:49:20 +08:00

130 lines
5.2 KiB
Python

import logging
from collections.abc import Callable
import torch
from ludwig.constants import ENCODER_OUTPUT
from ludwig.utils.torch_utils import LudwigModule
logger = logging.getLogger(__name__)
class ParameterUpdateError(Exception):
pass
def check_module_parameters_updated(
module: LudwigModule,
module_input_args: tuple,
module_target: torch.Tensor,
loss_function: Callable | None = None,
max_steps: int = 1,
learning_rate: float = 0.001,
) -> tuple:
"""
Reports on the number of parameters in a Ludwig component and their update status.
Args:
module: (LudwigModel) model to be tested.
module_input_args: (tuple) input for model
module_target: (Tensor) target values for computing loss and parameter updates
loss_function: (None or Callable) Optional for module specific loss calculation
max_steps: (int, default=1) maximum number of steps allowed to test for parameter
updates.
learning_rate: (float, default=0.001) learning rate for the optimizer
Returns: Tuple(frozen_parameters, trainable_parameters, parameters_updated, not_updated)
frozen_parameters: count of frozen parameters
trainable_parameters: count of trainable parameters
parameters_updated: count of trainable parameters that were updated
not_updated: list of parameters that were not updated
"""
# setup
if loss_function is None:
loss_function = torch.nn.MSELoss()
# Ensure module and all inputs are on the same device
from ludwig.utils.torch_utils import get_torch_device
device = get_torch_device()
module = module.to(device)
def _move_to_device(arg):
if isinstance(arg, torch.Tensor):
return arg.to(device)
if isinstance(arg, dict):
return {k: _move_to_device(v) for k, v in arg.items()}
if isinstance(arg, (list, tuple)):
return type(arg)(_move_to_device(v) for v in arg)
return arg
module_input_args = tuple(_move_to_device(arg) for arg in module_input_args)
module_target = module_target.to(device)
optimizer = torch.optim.SGD(module.parameters(), lr=learning_rate)
module.train(True)
trainable_parameter_list = []
frozen_parameter_list = []
parameter_updated = []
parameters_not_updated = []
for step in range(max_steps):
# make pass through model
module_output = module(*module_input_args)
# check for any frozen parameters
frozen_parameter_list = []
trainable_parameter_list = []
for p in module.named_parameters():
if p[1].requires_grad:
trainable_parameter_list.append(p)
else:
frozen_parameter_list.append(p)
# check parameter updates only if there are some unfrozen parameters
if len(trainable_parameter_list) > 0:
# do update of model parameters
optimizer.zero_grad()
if isinstance(module_output, torch.Tensor):
module_target = module_target.to(device=module_output.device)
loss = loss_function(module_output, module_target)
elif isinstance(module_output, dict):
if "logits" in module_output:
module_target = module_target.to(device=module_output["logits"].device)
loss = loss_function(module_output["logits"], module_target)
elif ENCODER_OUTPUT in module_output:
module_target = module_target.to(device=module_output[ENCODER_OUTPUT].device)
loss = loss_function(module_output[ENCODER_OUTPUT], module_target)
elif "combiner_output" in module_output:
module_target = module_target.to(device=module_output["combiner_output"].device)
loss = loss_function(module_output["combiner_output"], module_target)
elif isinstance(module_output, (list, tuple)):
module_target = module_target.to(device=module_output[0].device)
loss = loss_function(module_output[0], module_target)
else:
raise ValueError(f"Unexpected output type. Module type found is {type(module_output)}")
loss.backward()
optimizer.step()
# check for parameter updates
parameter_updated = []
# create tuple for each parameter: (parameter name, update indicator True/False)
# parameter is deemed updated if the gradient is not None and the gradient has non-zero value
for p in module.named_parameters():
parameter_updated.append((p[0], (p[1].grad is not None) and (not torch.all(p[1].grad == 0))))
else:
parameter_updated = []
parameters_not_updated = []
for p in parameter_updated:
# if not updated, record parameter name
if not p[1]:
parameters_not_updated.append(p[0])
trainable_parameters = len(trainable_parameter_list)
parameters_updated = sum(p[1] for p in parameter_updated)
frozen_parameters = len(frozen_parameter_list)
return frozen_parameters, trainable_parameters, parameters_updated, parameters_not_updated