[Python] .grad attribute of a Tensor that is not a leaf Tensor is being accessed

Discussão em 'Python' iniciado por Stack, Novembro 30, 2024.

  1. Stack

    Stack Membro Participativo

    I am trying to implement a dictionary learning algorithm and have been struggling with the following error.

    UserWarning: The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its .grad attribute won't be populated during autograd.backward(). If you indeed want the .grad field to be populated for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor instead. See github.com/pytorch/pytorch/pull/30531 for more informations. (Triggered internally at aten/src/ATen/core/TensorBody.h:417.)

    I know this is a warning, but since I need the gradient later, not calculating the gradient ends up throwing a NoneType error at the following line in my code: P2 = -0.5 * (gradient / torch.norm(gradient, dim=0)) + P1 This is in a method to calculate the step to take:

    def get_spherical_step(self, start, gradient, step_size):
    with torch.no_grad():
    P1 = start / torch.norm(start, dim=0)
    P2 = -0.5 * (gradient / torch.norm(gradient, dim=0)) + P1
    P2 /= torch.norm(P2, dim=0)

    projection_p1_p2 = (P1 * P2).sum(dim=0, keepdim=True) * P1
    orthogonal_part = P2 - projection_p1_p2

    end = P1 * math.cos(step_size) + (orthogonal_part / torch.norm(orthogonal_part, dim=0, keepdim=True)) * math.sin(step_size)

    epsilon = 1e-7
    zero_gradient_mask = (torch.norm(gradient, dim=0) <= epsilon) | (torch.norm(orthogonal_part, dim=0) <= epsilon)
    end[:, zero_gradient_mask] = P1[:, zero_gradient_mask]

    return end


    This is the method that takes that step:

    def optimizer_step(
    self
    ,
    batch
    ,
    loss_function
    ):

    if
    self.current_probe_step == self.max_probe_steps:
    self.reset_probe()

    self.current_probe_step += 1


    with
    torch.no_grad():
    smaller_step_R = torch.linalg.lstsq(self.smaller_step_dictionary, batch).solution
    normal_step_R = torch.linalg.lstsq(self.dictionary, batch).solution
    bigger_step_R = torch.linalg.lstsq(self.bigger_step_dictionary, batch).solution

    dictionaries = [self.smaller_step_dictionary, self.dictionary, self.bigger_step_dictionary]
    step_sizes = [self.step_size / 2, self.step_size, self.step_size * 2]

    batch_losses = []

    for
    i, dictionary
    in
    enumerate(dictionaries):
    dictionary.requires_grad_(True)
    R = [smaller_step_R, normal_step_R, bigger_step_R]
    batch_loss = loss_function(batch, dictionary, R, self.neuron_locations)
    batch_loss.retain_grad()
    batch_loss.backward()
    batch_losses.append(batch_loss.item())


    with
    torch.no_grad():
    self.smaller_step_loss += batch_losses[0]
    self.normal_step_loss += batch_losses[1]
    self.bigger_step_loss += batch_losses[2]


    for
    i, dictionary
    in
    enumerate(dictionaries):
    dictionaries = self.get_spherical_step(dictionary, dictionary.grad, step_sizes)

    self.smaller_step_dictionary, self.dictionary, self.bigger_step_dictionary = dictionaries


    which is in turn called by the train_dictionary function:

    def train_dictionary(self, training_batches, validation_set, num_epochs):
    loss_function = LossFunction.LossFunction(self.penalty_type, self.lamb)
    self.step_size = 0.1
    self.dictionary.requires_grad_(True)

    for epoch in range(num_epochs):
    print(f"Starting epoch {epoch}")
    training_batches = Preprocessing.shuffle_data(training_batches)

    for batch_index, batch in enumerate(training_batches):
    batch = batch.to(self.device)
    if self.step_size < 1e-9:
    self.dictionary.requires_grad_(False)
    return

    R = self.forward(batch)
    self.optimizer_step(batch, loss_function)

    if batch_index % 1000 == 0:
    with torch.no_grad():
    loss = loss_function(batch, self.dictionary, R, self.neuron_locations)
    print(f"{batch_index}/{len(training_batches)} batches complete")
    print(f"loss = {loss}")
    print(f"current step size is: {self.step_size}")

    with torch.no_grad():
    _, acc, prec, recall = self.get_best_threshold(validation_set)

    print(f"Epoch {epoch} complete. Accuracy, precision, and recall are as follows:\n{acc}\n{prec}\n{recall}")

    self.dictionary.requires_grad_(False)

    def optimizer_step(self, batch, loss_function):
    if self.current_probe_step == self.max_probe_steps:
    self.reset_probe()

    self.current_probe_step += 1

    with torch.no_grad():
    smaller_step_R = torch.linalg.lstsq(self.smaller_step_dictionary, batch).solution
    normal_step_R = torch.linalg.lstsq(self.dictionary, batch).solution
    bigger_step_R = torch.linalg.lstsq(self.bigger_step_dictionary, batch).solution

    dictionaries = [self.smaller_step_dictionary, self.dictionary, self.bigger_step_dictionary]
    step_sizes = [self.step_size / 2, self.step_size, self.step_size * 2]

    batch_losses = []
    for i, dictionary in enumerate(dictionaries):
    dictionary.requires_grad_(True)
    R = [smaller_step_R, normal_step_R, bigger_step_R]
    batch_loss = loss_function(batch, dictionary, R, self.neuron_locations)
    batch_loss.retain_grad()
    batch_loss.backward()
    batch_losses.append(batch_loss.item())

    with torch.no_grad():
    self.smaller_step_loss += batch_losses[0]
    self.normal_step_loss += batch_losses[1]
    self.bigger_step_loss += batch_losses[2]

    for i, dictionary in enumerate(dictionaries):
    dictionaries = self.get_spherical_step(dictionary, dictionary.grad, step_sizes)

    self.smaller_step_dictionary, self.dictionary, self.bigger_step_dictionary = dictionaries


    I didn't use to have this error before, when I use a simple grid search for hyperparameter optimization. I only start to get this error when I tried using Optuna to do a Bayesian optimization. The error usually throws after I'm done with trial 0 and starts trial 1:

    for target_dimension in range(upper_bound, lower_bound - 1, -1):

    # Inner function to optimize lambda for a fixed target_dimension
    def objective(trial):
    nonlocal iteration

    penalty_coefficient = trial.suggest_float("lambda", 1e-5, 10.0, log=True)

    # Initialize model with pretrained dictionary if available
    current_model = DictionaryLearning.DictionaryModel(
    penalty_type=penalty_type,
    penalty_multiplier=penalty_coefficient,
    target_dimension=target_dimension,
    original_dimension=original_dimension,
    receptor_type=receptor_type,
    neuron_locations=locations,
    pretrained_dictionary=previous_dictionary,
    is_random_init=is_random_init
    ).to(device)

    # Train and evaluate model
    current_model.train_dictionary(training_batches, validation_set, num_epochs=15)
    cutoff, _, current_precision, current_recall = current_model.get_best_threshold(validation_set)

    trial.set_user_attr("dictionary", current_model.dictionary)
    trial.set_user_attr("model", current_model)
    trial.set_user_attr("cutoff", cutoff)

    current_stat_set = StatSet(space, penalty_coefficient, penalty_type, receptor_type, cutoff, current_model, validation_set)
    current_f1_score = (2 * current_precision * current_recall) / (current_precision + current_recall)
    sparsity_score = current_stat_set.average_utilization
    locality_score = current_stat_set.interpretable_locality

    lambdas.append(penalty_coefficient)
    f1_scores.append(current_f1_score)
    sparsity_scores.append(sparsity_score)
    locality_scores.append(locality_score)

    save_dictionary(save_path, iteration, current_model)
    iteration += 1

    # Return F1 score as the objective to maximize
    return current_f1_score

    # Run Bayesian Optimization on lambda for current target_dimension
    study = optuna.create_study(direction="maximize")
    study.optimize(objective, n_trials=20)

    # Get the best F1 score and lambda for this target dimension
    best_trial = study.best_trial
    best_f1 = best_trial.value
    best_lambda_for_dimension = best_trial.params["lambda"]

    # Check if this target_dimension meets the F1 threshold
    if best_f1 >= f1_threshold or first:
    best_target_dimension = target_dimension
    best_lambda = best_lambda_for_dimension
    best_f1_score = best_f1

    print(f"Best target_dimension: {best_target_dimension}, Best lambda: {best_lambda}, F1: {best_f1_score}")

    best_dictionary = best_trial.user_attrs["dictionary"]
    previous_dictionary = torch.clone(best_dictionary).to(device)

    model = best_trial.user_attrs["model"]
    cutoff = best_trial.user_attrs["cutoff"]

    best_stat_set = StatSet(space, best_lambda, penalty_type, receptor_type, cutoff, model, validation_set)
    best_stat_set.print_stats()
    save_dictionary(save_path, "", model)

    optimization_fig = plot_optimization_history(study)
    slice_fig = plot_slice(study)

    optimization_fig.figure.savefig("optimization_history.pdf", format="pdf")
    slice_fig.figure.savefig("slice_plot.pdf", format="pdf")

    if first:
    first = False
    else:
    break


    I looked this up on StackOverflow and included batch_loss.retain_grad() as a result, but the error is still there. In addition, why is the default only to calculate gradients for the leaf tensors? Any help would be really appreciated! Thank you.

    Continue reading...

Compartilhe esta Página