From 53e1f7836f6c1c240274ecc0fbfe85e6a86089d0 Mon Sep 17 00:00:00 2001 From: tAnGjIa520 <1157507000@qq.com> Date: Tue, 2 Dec 2025 04:47:46 +0800 Subject: [PATCH 1/4] gumbel search --- lzero/mcts/ctree/ctree_muzero_v2/__init__.py | 0 .../mcts/ctree/ctree_muzero_v2/lib/cnode.cpp | 1175 +++++++++++++++++ lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.h | 118 ++ lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pxd | 82 ++ lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pyx | 368 ++++++ .../ctree/ctree_muzero_v2/test_batch_traverse | Bin 0 -> 268848 bytes .../mcts/ctree/ctree_muzero_v2/test_cnode_sh | Bin 0 -> 267328 bytes lzero/mcts/tree_search/__init__.py | 2 +- lzero/mcts/tree_search/mcts_ctree.py | 222 +++- lzero/policy/unizero.py | 823 ++++++++++-- setup_ctree_muzero_v2.py | 80 ++ 11 files changed, 2790 insertions(+), 80 deletions(-) create mode 100644 lzero/mcts/ctree/ctree_muzero_v2/__init__.py create mode 100644 lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.cpp create mode 100644 lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.h create mode 100644 lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pxd create mode 100644 lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pyx create mode 100755 lzero/mcts/ctree/ctree_muzero_v2/test_batch_traverse create mode 100755 lzero/mcts/ctree/ctree_muzero_v2/test_cnode_sh create mode 100644 setup_ctree_muzero_v2.py diff --git a/lzero/mcts/ctree/ctree_muzero_v2/__init__.py b/lzero/mcts/ctree/ctree_muzero_v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.cpp b/lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.cpp new file mode 100644 index 000000000..2f2c0e375 --- /dev/null +++ b/lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.cpp @@ -0,0 +1,1175 @@ +// C++11 + +#include +#include "cnode.h" +#include +#include +#include +#include + +#ifdef _WIN32 +#include "..\..\common_lib\utils.cpp" +#else +#include "../../common_lib/utils.cpp" +#endif + + +namespace tree +{ + + CSearchResults::CSearchResults() + { + /* + Overview: + Initialization of CSearchResults, the default result number is set to 0. + */ + this->num = 0; + } + + CSearchResults::CSearchResults(int num) + { + /* + Overview: + Initialization of CSearchResults with result number. + */ + this->num = num; + for (int i = 0; i < num; ++i) + { + this->search_paths.push_back(std::vector()); + } + } + + CSearchResults::~CSearchResults() {} + + //********************************************************* + + CNode::CNode() + { + /* + Overview: + Initialization of CNode. + */ + this->prior = 0; + this->legal_actions = legal_actions; + + this->visit_count = 0; + this->value_sum = 0; + this->best_action = -1; + this->to_play = 0; + this->reward = 0.0; + + // ===== Sequential Halving 初始化 ===== + this->selected_children_idx = std::vector(); + } + + CNode::CNode(float prior, std::vector &legal_actions) + { + /* + Overview: + Initialization of CNode with prior value and legal actions. + Arguments: + - prior: the prior value of this node. + - legal_actions: a vector of legal actions of this node. + */ + this->prior = prior; + this->legal_actions = legal_actions; + + this->visit_count = 0; + this->value_sum = 0; + this->best_action = -1; + this->to_play = 0; + this->current_latent_state_index = -1; + this->batch_index = -1; + + // ===== Sequential Halving 初始化 ===== + this->selected_children_idx = std::vector(); + } + + CNode::~CNode() {} + + void CNode::expand(int to_play, int current_latent_state_index, int batch_index, float reward, const std::vector &policy_logits) + { + /* + Overview: + Expand the child nodes of the current node. + Arguments: + - to_play: which player to play the game in the current node. + - current_latent_state_index: The index of latent state of the leaf node in the search path of the current node. + - batch_index: The index of latent state of the leaf node in the search path of the current node. + - reward: the reward of the current node. + - policy_logits: the logit of the child nodes. + */ + this->to_play = to_play; + this->current_latent_state_index = current_latent_state_index; + this->batch_index = batch_index; + this->reward = reward; + + int action_num = policy_logits.size(); + if (this->legal_actions.size() == 0) + { + for (int i = 0; i < action_num; ++i) + { + this->legal_actions.push_back(i); + } + } + float temp_policy; + float policy_sum = 0.0; + + #ifdef _WIN32 + // 创建动态数组 + float* policy = new float[action_num]; + #else + float policy[action_num]; + #endif + + float policy_max = FLOAT_MIN; + for (auto a : this->legal_actions) + { + if (policy_max < policy_logits[a]) + { + policy_max = policy_logits[a]; + } + } + + for (auto a : this->legal_actions) + { + temp_policy = exp(policy_logits[a] - policy_max); + policy_sum += temp_policy; + policy[a] = temp_policy; + } + + float prior; + for (auto a : this->legal_actions) + { + prior = policy[a] / policy_sum; + std::vector tmp_empty; + this->children[a] = CNode(prior, tmp_empty); // only for muzero/efficient zero, not support alphazero + } + + #ifdef _WIN32 + // 释放数组内存 + delete[] policy; + #else + #endif + } + + void CNode::add_exploration_noise(float exploration_fraction, const std::vector &noises) + { + /* + Overview: + Add a noise to the prior of the child nodes. + Arguments: + - exploration_fraction: the fraction to add noise. + - noises: the vector of noises added to each child node. + */ + float noise, prior; + for (int i = 0; i < this->legal_actions.size(); ++i) + { + noise = noises[i]; + CNode *child = this->get_child(this->legal_actions[i]); + + prior = child->prior; + child->prior = prior * (1 - exploration_fraction) + noise * exploration_fraction; + } + } + + float CNode::compute_mean_q(int isRoot, float parent_q, float discount_factor) + { + /* + Overview: + Compute the mean q value of the current node. + Arguments: + - isRoot: whether the current node is a root node. + - parent_q: the q value of the parent node. + - discount_factor: the discount_factor of reward. + */ + float total_unsigned_q = 0.0; + int total_visits = 0; + for (auto a : this->legal_actions) + { + CNode *child = this->get_child(a); + if (child->visit_count > 0) + { + float true_reward = child->reward; + float qsa = true_reward + discount_factor * child->value(); + total_unsigned_q += qsa; + total_visits += 1; + } + } + + float mean_q = 0.0; + if (isRoot && total_visits > 0) + { + mean_q = (total_unsigned_q) / (total_visits); + } + else + { + mean_q = (parent_q + total_unsigned_q) / (total_visits + 1); + } + return mean_q; + } + + void CNode::print_out() + { + return; + } + + int CNode::expanded() + { + /* + Overview: + Return whether the current node is expanded. + */ + return this->children.size() > 0; + } + + float CNode::value() + { + /* + Overview: + Return the real value of the current tree. + */ + float true_value = 0.0; + if (this->visit_count == 0) + { + return true_value; + } + else + { + true_value = this->value_sum / this->visit_count; + return true_value; + } + } + + std::vector CNode::get_trajectory() + { + /* + Overview: + Find the current best trajectory starts from the current node. + Returns: + - traj: a vector of node index, which is the current best trajectory from this node. + */ + std::vector traj; + + CNode *node = this; + int best_action = node->best_action; + while (best_action >= 0) + { + traj.push_back(best_action); + + node = node->get_child(best_action); + best_action = node->best_action; + } + return traj; + } + + std::vector CNode::get_children_distribution() + { + /* + Overview: + Get the distribution of child nodes in the format of visit_count. + Returns: + - distribution: a vector of distribution of child nodes in the format of visit count (i.e. [1,3,0,2,5]). + */ + std::vector distribution; + if (this->expanded()) + { + for (auto a : this->legal_actions) + { + CNode *child = this->get_child(a); + distribution.push_back(child->visit_count); + } + } + return distribution; + } + + CNode *CNode::get_child(int action) + { + /* + Overview: + Get the child node corresponding to the input action. + Arguments: + - action: the action to get child. + */ + return &(this->children[action]); + } + + int CNode::select_root_child_sh(const std::vector &gumble_noise) + { + /* + Overview: + Select a child action for root node using Sequential Halving. + Select the least visited action among the current candidates. + Arguments: + - gumble_noise: Gumbel noise array + Returns: + - The action index to select (least visited among candidates) + */ + int selected_action = -1; + int min_visit_count = INT_MAX; + + // If candidates not yet initialized, initialize all legal actions as candidates + if (this->selected_children_idx.empty()) { + for (int action : this->legal_actions) { + this->selected_children_idx.push_back(action); + } + } + + // Select the least visited action among candidates (equal visit strategy) + for (int action : this->selected_children_idx) { + CNode *child = this->get_child(action); + if (child != nullptr && child->visit_count < min_visit_count) { + min_visit_count = child->visit_count; + selected_action = action; + } + } + + // If no candidate found, select first legal action + if (selected_action == -1 && !this->legal_actions.empty()) { + selected_action = this->legal_actions[0]; + } + + return selected_action; + } + + void CNode::update_selected_actions(const std::vector &gumble_noise, tools::CMinMaxStats &min_max_stats, int new_num_top_actions) + { + /* + Overview: + Prune candidate actions in Sequential Halving: keep only top-k actions based on Gumbel-augmented Q-values. + Arguments: + - gumble_noise: Gumbel noise array + - min_max_stats: min-max statistics for value normalization + - new_num_top_actions: number of actions to keep in next phase + */ + if (this->selected_children_idx.empty()) { + return; + } + + // Calculate Gumbel-augmented scores for each candidate + std::vector> scored_actions; // (score, action) + + for (int action : this->selected_children_idx) { + CNode *child = this->get_child(action); + if (child == nullptr) continue; + + // Calculate score = gumbel[action] + prior[action] + normalized_q[action] + float q_value = child->value(); + float normalized_q = min_max_stats.normalize(q_value); + float gumbel_score = gumble_noise[action] + child->prior + normalized_q; + + scored_actions.push_back({gumbel_score, action}); + } + + // Sort by score in descending order and keep top-k + std::sort(scored_actions.begin(), scored_actions.end(), + [](const std::pair &a, const std::pair &b) { + return a.first > b.first; + }); + + // Update selected actions to keep only top-k + this->selected_children_idx.clear(); + for (int i = 0; i < std::min((int)scored_actions.size(), new_num_top_actions); ++i) { + this->selected_children_idx.push_back(scored_actions[i].second); + } + } + + //********************************************************* + + CRoots::CRoots() + { + /* + Overview: + The initialization of CRoots. + */ + this->root_num = 0; + } + + CRoots::CRoots(int root_num, std::vector > &legal_actions_list) + { + /* + Overview: + The initialization of CRoots with root num and legal action lists. + Arguments: + - root_num: the number of the current root. + - legal_action_list: the vector of the legal action of this root. + */ + this->root_num = root_num; + this->legal_actions_list = legal_actions_list; + + for (int i = 0; i < root_num; ++i) + { + this->roots.push_back(CNode(0, this->legal_actions_list[i])); + } + } + + CRoots::~CRoots() {} + + void CRoots::prepare(float root_noise_weight, const std::vector > &noises, const std::vector &rewards, const std::vector > &policies, std::vector &to_play_batch) + { + /* + Overview: + Expand the roots and add noises. + Arguments: + - root_noise_weight: the exploration fraction of roots + - noises: the vector of noise add to the roots. + - rewards: the vector of rewards of each root. + - policies: the vector of policy logits of each root. + - to_play_batch: the vector of the player side of each root. + */ + for (int i = 0; i < this->root_num; ++i) + { + this->roots[i].expand(to_play_batch[i], 0, i, rewards[i], policies[i]); + this->roots[i].add_exploration_noise(root_noise_weight, noises[i]); + + this->roots[i].visit_count += 1; + } + } + + void CRoots::prepare_no_noise(const std::vector &rewards, const std::vector > &policies, std::vector &to_play_batch) + { + /* + Overview: + Expand the roots without noise. + Arguments: + - rewards: the vector of rewards of each root. + - policies: the vector of policy logits of each root. + - to_play_batch: the vector of the player side of each root. + */ + for (int i = 0; i < this->root_num; ++i) + { + this->roots[i].expand(to_play_batch[i], 0, i, rewards[i], policies[i]); + + this->roots[i].visit_count += 1; + } + } + + void CRoots::clear() + { + /* + Overview: + Clear the roots vector. + */ + this->roots.clear(); + } + + std::vector > CRoots::get_trajectories() + { + /* + Overview: + Find the current best trajectory starts from each root. + Returns: + - traj: a vector of node index, which is the current best trajectory from each root. + */ + std::vector > trajs; + trajs.reserve(this->root_num); + + for (int i = 0; i < this->root_num; ++i) + { + trajs.push_back(this->roots[i].get_trajectory()); + } + return trajs; + } + + std::vector > CRoots::get_distributions() + { + /* + Overview: + Get the children distribution of each root. + Returns: + - distribution: a vector of distribution of child nodes in the format of visit count (i.e. [1,3,0,2,5]). + */ + std::vector > distributions; + distributions.reserve(this->root_num); + + for (int i = 0; i < this->root_num; ++i) + { + distributions.push_back(this->roots[i].get_children_distribution()); + } + return distributions; + } + + std::vector CRoots::get_values() + { + /* + Overview: + Return the real value of each root. + */ + std::vector values; + for (int i = 0; i < this->root_num; ++i) + { + values.push_back(this->roots[i].value()); + } + return values; + } + + // ===== Sequential Halving 初始化 ===== + void CRoots::init_sequential_halving(int num_sims, int num_top_acts) + { + /* + Overview: + Initialize Sequential Halving parameters for the roots. + Arguments: + - num_sims: total number of simulations (e.g., 50) + - num_top_acts: initial number of top actions to consider (e.g., 16) + + Design: + Precompute the transition point for each phase using the official SH formula: + visit_num_for_next_phase = max(floor(n / (log2(m) * current_m)), 1) * current_m + where n = num_simulations, m = num_top_actions + */ + this->use_sequential_halving = 1; + this->num_simulations = num_sims; + this->num_top_actions = num_top_acts; + this->current_phase = 0; + this->current_num_top_actions = num_top_acts; + this->used_visit_num = 0; + + // Calculate log2(num_top_acts) - number of phases + int log2_m = 0; + int temp = num_top_acts; + while (temp > 1) { + temp /= 2; + log2_m++; + } + if (log2_m == 0) log2_m = 1; // Ensure at least 1 phase + + // Precompute visit count for next phase transition + // Formula: max(floor(n / (log2(m) * current_m)), 1) * current_m + this->visit_num_for_next_phase = std::max( + (int)std::floor((float)num_sims / (log2_m * this->current_num_top_actions)), + 1 + ) * this->current_num_top_actions; + + // Initialize stored Gumbel noise with default value 0.0 + // Will be updated by batch_traverse when actual Gumbel noise is passed + this->stored_gumble_noise.clear(); + for (int i = 0; i < this->root_num; ++i) { + // Assume legal_actions_list[i].size() is the number of actions for this root + std::vector zero_noise(this->legal_actions_list[i].size(), 0.0); + this->stored_gumble_noise.push_back(zero_noise); + } + } + + int CRoots::ready_for_next_sh_phase() + { + /* + Overview: + Check if ready to transition to the next Sequential Halving phase. + Returns: + - 1 if ready to transition, 0 otherwise + + Logic: + - Check if used_visit_num has reached visit_num_for_next_phase + - Also ensure current_num_top_actions > 1 (can still be halved) + */ + if (this->use_sequential_halving == 0) return 0; + + if (this->used_visit_num >= this->visit_num_for_next_phase && + this->current_num_top_actions > 1) { + return 1; + } + return 0; + } + + void CRoots::apply_next_sh_phase(tools::CMinMaxStatsList *min_max_stats_lst) + { + /* + Overview: + Apply the next Sequential Halving phase: reduce candidate actions and update visit counts. + Arguments: + - min_max_stats_lst: the min-max statistics list for normalization + + Steps: + 1. Halve the number of top actions: current_num_top_actions /= 2 + 2. Move to next phase: current_phase++ + 3. Reset used_visit_num for next phase counting + 4. Recalculate visit_num_for_next_phase with new current_num_top_actions + 5. Call update_selected_actions for each root to prune candidates + */ + if (this->use_sequential_halving == 0) return; + + printf("\n[SH阶段转换] 进入新阶段\n"); + printf(" 转换前: 阶段=%d 候选数=%d\n", this->current_phase, this->current_num_top_actions); + fflush(stdout); + + // Step 1: Halve the number of top actions + this->current_num_top_actions = std::max(this->current_num_top_actions / 2, 1); + this->current_phase++; + + // Step 2: Reset used visit count for next phase + this->used_visit_num = 0; + + // Step 3: Recalculate visit count for next phase transition + if (this->current_num_top_actions > 1) { + int log2_m = 0; + int temp = this->num_top_actions; + while (temp > 1) { + temp /= 2; + log2_m++; + } + if (log2_m == 0) log2_m = 1; + + this->visit_num_for_next_phase = std::max( + (int)std::floor((float)this->num_simulations / (log2_m * this->current_num_top_actions)), + 1 + ) * this->current_num_top_actions; + } + + // Step 4: Update selected actions for each root based on current phase + for (int i = 0; i < this->root_num; ++i) { + if (this->stored_gumble_noise.size() > (size_t)i) { + this->roots[i].update_selected_actions( + this->stored_gumble_noise[i], + min_max_stats_lst->stats_lst[i], + this->current_num_top_actions + ); + } + } + + printf(" 转换后: 阶段=%d 候选数=%d 下次转换点=%d\n", + this->current_phase, this->current_num_top_actions, this->visit_num_for_next_phase); + fflush(stdout); + } + + void CRoots::set_used_visit_num(int num) + { + /* + Overview: + Set the used visit count for Sequential Halving phase transition checking. + Arguments: + - num: the number of visits to set + */ + this->used_visit_num = num; + } + + void cbackpropagate(std::vector &search_path, tools::CMinMaxStats &min_max_stats, int to_play, float value, float discount_factor) + { + /* + Overview: + Update the value sum and visit count of nodes along the search path. + Arguments: + - search_path: a vector of nodes on the search path. + - min_max_stats: a tool used to min-max normalize the q value. + - to_play: which player to play the game in the current node. + - value: the value to propagate along the search path. + - discount_factor: the discount factor of reward. + */ + assert(to_play == -1 || to_play == 1 || to_play == 2); + if (to_play == -1) + { + // for play-with-bot-mode + float bootstrap_value = value; + int path_len = search_path.size(); + for (int i = path_len - 1; i >= 0; --i) + { + CNode *node = search_path[i]; + node->value_sum += bootstrap_value; + node->visit_count += 1; + + float true_reward = node->reward; + + min_max_stats.update(true_reward + discount_factor * node->value()); + + bootstrap_value = true_reward + discount_factor * bootstrap_value; + } + } + else + { + // for self-play-mode + float bootstrap_value = value; + int path_len = search_path.size(); + for (int i = path_len - 1; i >= 0; --i) + { + CNode *node = search_path[i]; + if (node->to_play == to_play) + node->value_sum += bootstrap_value; + else + node->value_sum += -bootstrap_value; + node->visit_count += 1; + + // NOTE: in self-play-mode, value_prefix is not calculated according to the perspective of current player of node, + // but treated as 1 player, just for obtaining the true reward in the perspective of current player of node. + // float true_reward = node->value_prefix - parent_value_prefix; + float true_reward = node->reward; + + // TODO(pu): why in muzero-general is - node.value + min_max_stats.update(true_reward + discount_factor * -node->value()); + + if (node->to_play == to_play) + bootstrap_value = -true_reward + discount_factor * bootstrap_value; + else + bootstrap_value = true_reward + discount_factor * bootstrap_value; + } + } + } + + void cbatch_backpropagate(int current_latent_state_index, float discount_factor, const std::vector &rewards, const std::vector &values, const std::vector > &policies, tools::CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, std::vector &to_play_batch) + { + /* + Overview: + Expand the nodes along the search path and update the infos. + Arguments: + - current_latent_state_index: The index of latent state of the leaf node in the search path. + - discount_factor: the discount factor of reward. + - rewards: the rewards of nodes along the search path. + - values: the values to propagate along the search path. + - policies: the policy logits of nodes along the search path. + - min_max_stats: a tool used to min-max normalize the q value. + - results: the search results. + - to_play_batch: the batch of which player is playing on this node. + */ + for (int i = 0; i < results.num; ++i) + { + results.nodes[i]->expand(to_play_batch[i], current_latent_state_index, i, rewards[i], policies[i]); + cbackpropagate(results.search_paths[i], min_max_stats_lst->stats_lst[i], to_play_batch[i], values[i], discount_factor); + } + } + + void cbatch_backpropagate_with_reuse(int current_latent_state_index, float discount_factor, const std::vector &value_prefixs, const std::vector &values, const std::vector > &policies, tools::CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, std::vector &to_play_batch, std::vector &no_inference_lst, std::vector &reuse_lst, std::vector &reuse_value_lst) + { + /* + Overview: + Expand the nodes along the search path and update the infos. Details are similar to cbatch_backpropagate, but with reuse value. + Please refer to https://arxiv.org/abs/2404.16364 for more details. + Arguments: + - current_latent_state_index: The index of latent state of the leaf node in the search path. + - discount_factor: the discount factor of reward. + - value_prefixs: the value prefixs of nodes along the search path. + - values: the values to propagate along the search path. + - policies: the policy logits of nodes along the search path. + - min_max_stats: a tool used to min-max normalize the q value. + - results: the search results. + - to_play_batch: the batch of which player is playing on this node. + - no_inference_lst: the list of the nodes which does not need to expand. + - reuse_lst: the list of the nodes which should use reuse-value to backpropagate. + - reuse_value_lst: the list of the reuse-value. + */ + int count_a = 0; + int count_b = 0; + int count_c = 0; + float value_propagate = 0; + for (int i = 0; i < results.num; ++i) + { + if (i == no_inference_lst[count_a]) + { + count_a = count_a + 1; + value_propagate = reuse_value_lst[i]; + } + else + { + results.nodes[i]->expand(to_play_batch[i], current_latent_state_index, count_b, value_prefixs[count_b], policies[count_b]); + if (i == reuse_lst[count_c]) + { + value_propagate = reuse_value_lst[i]; + count_c = count_c + 1; + } + else + { + value_propagate = values[count_b]; + } + count_b = count_b + 1; + } + + cbackpropagate(results.search_paths[i], min_max_stats_lst->stats_lst[i], to_play_batch[i], value_propagate, discount_factor); + } + } + + int cselect_child(CNode *root, tools::CMinMaxStats &min_max_stats, int pb_c_base, float pb_c_init, float discount_factor, float mean_q, int players) + { + /* + Overview: + Select the child node of the roots according to ucb scores. + Arguments: + - root: the roots to select the child node. + - min_max_stats: a tool used to min-max normalize the score. + - pb_c_base: constants c2 in muzero. + - pb_c_init: constants c1 in muzero. + - disount_factor: the discount factor of reward. + - mean_q: the mean q value of the parent node. + - players: the number of players. + Returns: + - action: the action to select. + */ + float max_score = FLOAT_MIN; + const float epsilon = 0.000001; + std::vector max_index_lst; + for (auto a : root->legal_actions) + { + + CNode *child = root->get_child(a); + float temp_score = cucb_score(child, min_max_stats, mean_q, root->visit_count - 1, pb_c_base, pb_c_init, discount_factor, players); + + if (max_score < temp_score) + { + max_score = temp_score; + + max_index_lst.clear(); + max_index_lst.push_back(a); + } + else if (temp_score >= max_score - epsilon) + { + max_index_lst.push_back(a); + } + } + + int action = 0; + if (max_index_lst.size() > 0) + { + int rand_index = rand() % max_index_lst.size(); + action = max_index_lst[rand_index]; + } + return action; + } + + int cselect_root_child(CNode *root, tools::CMinMaxStats &min_max_stats, int pb_c_base, float pb_c_init, float discount_factor, float mean_q, int players, int true_action, float reuse_value) + { + /* + Overview: + Select the child node of the roots according to ucb scores. + Arguments: + - root: the roots to select the child node. + - min_max_stats: a tool used to min-max normalize the score. + - pb_c_base: constants c2 in muzero. + - pb_c_init: constants c1 in muzero. + - discount_factor: the discount factor of reward. + - mean_q: the mean q value of the parent node. + - players: the number of players. + - true_action: the action chosen in the trajectory. + - reuse_value: the value obtained from the search of the next state in the trajectory. + Returns: + - action: the action to select. + */ + float max_score = FLOAT_MIN; + const float epsilon = 0.000001; + std::vector max_index_lst; + for (auto a : root->legal_actions) + { + + CNode *child = root->get_child(a); + float temp_score = 0.0; + if (a == true_action) + { + temp_score = carm_score(child, min_max_stats, mean_q, reuse_value, root->visit_count - 1, pb_c_base, pb_c_init, discount_factor, players); + } + else + { + temp_score = cucb_score(child, min_max_stats, mean_q, root->visit_count - 1, pb_c_base, pb_c_init, discount_factor, players); + } + + if (max_score < temp_score) + { + max_score = temp_score; + + max_index_lst.clear(); + max_index_lst.push_back(a); + } + else if (temp_score >= max_score - epsilon) + { + max_index_lst.push_back(a); + } + } + + int action = 0; + if (max_index_lst.size() > 0) + { + int rand_index = rand() % max_index_lst.size(); + action = max_index_lst[rand_index]; + } + return action; + } + + float cucb_score(CNode *child, tools::CMinMaxStats &min_max_stats, float parent_mean_q, float total_children_visit_counts, float pb_c_base, float pb_c_init, float discount_factor, int players) + { + /* + Overview: + Compute the ucb score of the child. + Arguments: + - child: the child node to compute ucb score. + - min_max_stats: a tool used to min-max normalize the score. + - mean_q: the mean q value of the parent node. + - total_children_visit_counts: the total visit counts of the child nodes of the parent node. + - pb_c_base: constants c2 in muzero. + - pb_c_init: constants c1 in muzero. + - disount_factor: the discount factor of reward. + - players: the number of players. + Returns: + - ucb_value: the ucb score of the child. + */ + float pb_c = 0.0, prior_score = 0.0, value_score = 0.0; + pb_c = log((total_children_visit_counts + pb_c_base + 1) / pb_c_base) + pb_c_init; + pb_c *= (sqrt(total_children_visit_counts) / (child->visit_count + 1)); + + prior_score = pb_c * child->prior; + if (child->visit_count == 0) + { + value_score = parent_mean_q; + } + else + { + float true_reward = child->reward; + if (players == 1) + value_score = true_reward + discount_factor * child->value(); + else if (players == 2) + value_score = true_reward + discount_factor * (-child->value()); + } + + value_score = min_max_stats.normalize(value_score); + + if (value_score < 0) + value_score = 0; + if (value_score > 1) + value_score = 1; + + float ucb_value = prior_score + value_score; + return ucb_value; + } + + + float carm_score(CNode *child, tools::CMinMaxStats &min_max_stats, float parent_mean_q, float reuse_value, float total_children_visit_counts, float pb_c_base, float pb_c_init, float discount_factor, int players) + { + /* + Overview: + Compute the ucb score of the child. + Arguments: + - child: the child node to compute ucb score. + - min_max_stats: a tool used to min-max normalize the score. + - mean_q: the mean q value of the parent node. + - total_children_visit_counts: the total visit counts of the child nodes of the parent node. + - pb_c_base: constants c2 in muzero. + - pb_c_init: constants c1 in muzero. + - disount_factor: the discount factor of reward. + - players: the number of players. + Returns: + - ucb_value: the ucb score of the child. + */ + float pb_c = 0.0, prior_score = 0.0, value_score = 0.0; + pb_c = log((total_children_visit_counts + pb_c_base + 1) / pb_c_base) + pb_c_init; + pb_c *= (sqrt(total_children_visit_counts) / (child->visit_count + 1)); + + prior_score = pb_c * child->prior; + if (child->visit_count == 0) + { + value_score = parent_mean_q; + } + else + { + float true_reward = child->reward; + if (players == 1) + value_score = true_reward + discount_factor * reuse_value; + else if (players == 2) + value_score = true_reward + discount_factor * (-reuse_value); + } + + value_score = min_max_stats.normalize(value_score); + + if (value_score < 0) + value_score = 0; + if (value_score > 1) + value_score = 1; + float ucb_value = 0.0; + if (child->visit_count == 0) + { + ucb_value = prior_score + value_score; + } + else + { + ucb_value = value_score; + } + return ucb_value; + } + + void cbatch_traverse(CRoots *roots, int pb_c_base, float pb_c_init, float discount_factor, tools::CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, std::vector &virtual_to_play_batch) + { + /* + Overview: + Search node path from the roots. + Arguments: + - roots: the roots that search from. + - pb_c_base: constants c2 in muzero. + - pb_c_init: constants c1 in muzero. + - disount_factor: the discount factor of reward. + - min_max_stats: a tool used to min-max normalize the score. + - results: the search results. + - virtual_to_play_batch: the batch of which player is playing on this node. + */ + // set seed + get_time_and_set_rand_seed(); + + int last_action = -1; + float parent_q = 0.0; + results.search_lens = std::vector(); + + int players = 0; + int largest_element = *max_element(virtual_to_play_batch.begin(), virtual_to_play_batch.end()); // 0 or 2 + if (largest_element == -1) + players = 1; + else + players = 2; + + for (int i = 0; i < results.num; ++i) + { + CNode *node = &(roots->roots[i]); + int is_root = 1; + int search_len = 0; + results.search_paths[i].push_back(node); + + while (node->expanded()) + { + float mean_q = node->compute_mean_q(is_root, parent_q, discount_factor); + parent_q = mean_q; + + int action; + if (is_root == 1 && roots->use_sequential_halving) { + action = node->select_root_child_sh(roots->stored_gumble_noise[i]); + // 调试日志:显示根节点选择 + printf("[SH选择] 环境%d 当前阶段=%d 候选数=%d 选择动作=%d (访问次数=%d)\n", + i, roots->current_phase, roots->current_num_top_actions, + action, node->get_child(action)->visit_count); + fflush(stdout); + } else { + action = cselect_child(node, min_max_stats_lst->stats_lst[i], pb_c_base, pb_c_init, discount_factor, mean_q, players); + // 调试日志:显示深节点选择 + if (is_root == 0 && search_len < 3) { // 只显示前3层 + printf("[UCB选择] 环境%d 深度=%d 选择动作=%d (访问次数=%d 价值=%.4f)\n", + i, search_len, action, node->get_child(action)->visit_count, + node->get_child(action)->value()); + fflush(stdout); + } + } + + is_root = 0; + if (players > 1) + { + assert(virtual_to_play_batch[i] == 1 || virtual_to_play_batch[i] == 2); + if (virtual_to_play_batch[i] == 1) + virtual_to_play_batch[i] = 2; + else + virtual_to_play_batch[i] = 1; + } + + node->best_action = action; + // next + node = node->get_child(action); + last_action = action; + results.search_paths[i].push_back(node); + search_len += 1; + } + + CNode *parent = results.search_paths[i][results.search_paths[i].size() - 2]; + + results.latent_state_index_in_search_path.push_back(parent->current_latent_state_index); + results.latent_state_index_in_batch.push_back(parent->batch_index); + + results.last_actions.push_back(last_action); + results.search_lens.push_back(search_len); + results.nodes.push_back(node); + results.virtual_to_play_batchs.push_back(virtual_to_play_batch[i]); + } + } + + + void cbatch_traverse_with_reuse(CRoots *roots, int pb_c_base, float pb_c_init, float discount_factor, tools::CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, std::vector &virtual_to_play_batch, std::vector &true_action, std::vector &reuse_value) + { + /* + Overview: + Search node path from the roots. Details are similar to cbatch_traverse, but with reuse value. + Please refer to https://arxiv.org/abs/2404.16364 for more details. + Arguments: + - roots: the roots that search from. + - pb_c_base: constants c2 in muzero. + - pb_c_init: constants c1 in muzero. + - disount_factor: the discount factor of reward. + - min_max_stats: a tool used to min-max normalize the score. + - results: the search results. + - virtual_to_play_batch: the batch of which player is playing on this node. + - true_action: the action chosen in the trajectory. + - reuse_value: the value obtained from the search of the next state in the trajectory. + */ + // set seed + get_time_and_set_rand_seed(); + + int last_action = -1; + float parent_q = 0.0; + results.search_lens = std::vector(); + + int players = 0; + int largest_element = *max_element(virtual_to_play_batch.begin(), virtual_to_play_batch.end()); // 0 or 2 + if (largest_element == -1) + players = 1; + else + players = 2; + + for (int i = 0; i < results.num; ++i) + { + CNode *node = &(roots->roots[i]); + int is_root = 1; + int search_len = 0; + results.search_paths[i].push_back(node); + + while (node->expanded()) + { + float mean_q = node->compute_mean_q(is_root, parent_q, discount_factor); + parent_q = mean_q; + + int action = 0; + if (is_root) + { + action = cselect_root_child(node, min_max_stats_lst->stats_lst[i], pb_c_base, pb_c_init, discount_factor, mean_q, players, true_action[i], reuse_value[i]); + } + else + { + action = cselect_child(node, min_max_stats_lst->stats_lst[i], pb_c_base, pb_c_init, discount_factor, mean_q, players); + } + + if (players > 1) + { + assert(virtual_to_play_batch[i] == 1 || virtual_to_play_batch[i] == 2); + if (virtual_to_play_batch[i] == 1) + virtual_to_play_batch[i] = 2; + else + virtual_to_play_batch[i] = 1; + } + + node->best_action = action; + // next + node = node->get_child(action); + last_action = action; + results.search_paths[i].push_back(node); + search_len += 1; + + if(is_root && action == true_action[i]) + { + break; + } + + is_root = 0; + + } + + if (node->expanded()) + { + results.latent_state_index_in_search_path.push_back(-1); + results.latent_state_index_in_batch.push_back(i); + + results.last_actions.push_back(last_action); + results.search_lens.push_back(search_len); + results.nodes.push_back(node); + results.virtual_to_play_batchs.push_back(virtual_to_play_batch[i]); + } + else + { + CNode *parent = results.search_paths[i][results.search_paths[i].size() - 2]; + + results.latent_state_index_in_search_path.push_back(parent->current_latent_state_index); + results.latent_state_index_in_batch.push_back(parent->batch_index); + + results.last_actions.push_back(last_action); + results.search_lens.push_back(search_len); + results.nodes.push_back(node); + results.virtual_to_play_batchs.push_back(virtual_to_play_batch[i]); + } + } + } + +} \ No newline at end of file diff --git a/lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.h b/lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.h new file mode 100644 index 000000000..f62a660cc --- /dev/null +++ b/lzero/mcts/ctree/ctree_muzero_v2/lib/cnode.h @@ -0,0 +1,118 @@ +// C++11 + +#ifndef CNODE_H +#define CNODE_H + +#include "./../common_lib/cminimax.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const int DEBUG_MODE = 0; + +namespace tree { + + class CNode { + public: + int visit_count, to_play, current_latent_state_index, batch_index, best_action; + float reward, prior, value_sum; + std::vector children_index; + std::map children; + + std::vector legal_actions; + + // ===== Sequential Halving 根节点特有 ===== + std::vector selected_children_idx; // 当前阶段的候选动作列表 + + CNode(); + CNode(float prior, std::vector &legal_actions); + ~CNode(); + + void expand(int to_play, int current_latent_state_index, int batch_index, float reward, const std::vector &policy_logits); + void add_exploration_noise(float exploration_fraction, const std::vector &noises); + float compute_mean_q(int isRoot, float parent_q, float discount_factor); + void print_out(); + + int expanded(); + + float value(); + + std::vector get_trajectory(); + std::vector get_children_distribution(); + CNode* get_child(int action); + + // ===== Sequential Halving 函数 ===== + int select_root_child_sh(const std::vector &gumble_noise); + void update_selected_actions(const std::vector &gumble_noise, tools::CMinMaxStats &min_max_stats, int new_num_top_actions); + }; + + class CRoots{ + public: + int root_num; + std::vector roots; + std::vector > legal_actions_list; + + // ===== Sequential Halving 全局状态 ===== + int use_sequential_halving; // 是否启用 SH + int num_simulations; // 总 simulation 次数 + int num_top_actions; // 初始候选数 + int current_phase; // 当前阶段 + int current_num_top_actions; // 当前阶段的候选数 + int used_visit_num; // 已使用的 sim 计数 + int visit_num_for_next_phase; // 下一阶段转换点 + std::vector > stored_gumble_noise; // 每根一份 Gumbel 噪声 + + CRoots(); + CRoots(int root_num, std::vector > &legal_actions_list); + ~CRoots(); + + void prepare(float root_noise_weight, const std::vector > &noises, const std::vector &rewards, const std::vector > &policies, std::vector &to_play_batch); + void prepare_no_noise(const std::vector &rewards, const std::vector > &policies, std::vector &to_play_batch); + void clear(); + std::vector > get_trajectories(); + std::vector > get_distributions(); + std::vector get_values(); + + // ===== Sequential Halving 函数 ===== + void init_sequential_halving(int num_sims, int num_top_acts); + int ready_for_next_sh_phase(); + void apply_next_sh_phase(tools::CMinMaxStatsList *min_max_stats_lst); + void set_used_visit_num(int num); + + }; + + class CSearchResults{ + public: + int num; + std::vector latent_state_index_in_search_path, latent_state_index_in_batch, last_actions, search_lens; + std::vector virtual_to_play_batchs; + std::vector nodes; + std::vector > search_paths; + + CSearchResults(); + CSearchResults(int num); + ~CSearchResults(); + + }; + + + //********************************************************* + void update_tree_q(CNode* root, tools::CMinMaxStats &min_max_stats, float discount_factor, int players); + void cbackpropagate(std::vector &search_path, tools::CMinMaxStats &min_max_stats, int to_play, float value, float discount_factor); + void cbatch_backpropagate(int current_latent_state_index, float discount_factor, const std::vector &rewards, const std::vector &values, const std::vector > &policies, tools::CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, std::vector &to_play_batch); + void cbatch_backpropagate_with_reuse(int current_latent_state_index, float discount_factor, const std::vector &value_prefixs, const std::vector &values, const std::vector > &policies, tools::CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, std::vector &to_play_batch, std::vector &no_inference_lst, std::vector &reuse_lst, std::vector &reuse_value_lst); + int cselect_child(CNode* root, tools::CMinMaxStats &min_max_stats, int pb_c_base, float pb_c_init, float discount_factor, float mean_q, int players); + int cselect_root_child(CNode *root, tools::CMinMaxStats &min_max_stats, int pb_c_base, float pb_c_init, float discount_factor, float mean_q, int players, int true_action, float reuse_value); + float cucb_score(CNode *child, tools::CMinMaxStats &min_max_stats, float parent_mean_q, float total_children_visit_counts, float pb_c_base, float pb_c_init, float discount_factor, int players); + float carm_score(CNode *child, tools::CMinMaxStats &min_max_stats, float parent_mean_q, float reuse_value, float total_children_visit_counts, float pb_c_base, float pb_c_init, float discount_factor, int players); + void cbatch_traverse(CRoots *roots, int pb_c_base, float pb_c_init, float discount_factor, tools::CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, std::vector &virtual_to_play_batch); + void cbatch_traverse_with_reuse(CRoots *roots, int pb_c_base, float pb_c_init, float discount_factor, tools::CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, std::vector &virtual_to_play_batch, std::vector &true_action, std::vector &reuse_value); +} + +#endif \ No newline at end of file diff --git a/lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pxd b/lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pxd new file mode 100644 index 000000000..95a52b7d8 --- /dev/null +++ b/lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pxd @@ -0,0 +1,82 @@ +# distutils:language=c++ +# cython:language_level=3 +from libcpp.vector cimport vector + + +cdef extern from "../common_lib/cminimax.cpp": + pass + + +cdef extern from "../common_lib/cminimax.h" namespace "tools": + cdef cppclass CMinMaxStats: + CMinMaxStats() except + + float maximum, minimum, value_delta_max + + void set_delta(float value_delta_max) + void update(float value) + void clear() + float normalize(float value) + + cdef cppclass CMinMaxStatsList: + CMinMaxStatsList() except + + CMinMaxStatsList(int num) except + + int num + vector[CMinMaxStats] stats_lst + + void set_delta(float value_delta_max) + +cdef extern from "lib/cnode.cpp": + pass + + +cdef extern from "lib/cnode.h" namespace "tree": + cdef cppclass CNode: + CNode() except + + CNode(float prior, vector[int] &legal_actions) except + + int visit_count, to_play, current_latent_state_index, batch_index, best_action + float value_prefixs, prior, value_sum, parent_value_prefix + + void expand(int to_play, int current_latent_state_index, int batch_index, float value_prefixs, vector[float] policy_logits) + void add_exploration_noise(float exploration_fraction, vector[float] noises) + float compute_mean_q(int isRoot, float parent_q, float discount_factor) + + int expanded() + float value() + vector[int] get_trajectory() + vector[int] get_children_distribution() + CNode* get_child(int action) + + cdef cppclass CRoots: + CRoots() except + + CRoots(int root_num, vector[vector[int]] legal_actions_list) except + + int root_num + vector[CNode] roots + + void prepare(float root_noise_weight, const vector[vector[float]] &noises, const vector[float] &value_prefixs, const vector[vector[float]] &policies, vector[int] to_play_batch) + void prepare_no_noise(const vector[float] &value_prefixs, const vector[vector[float]] &policies, vector[int] to_play_batch) + void clear() + vector[vector[int]] get_trajectories() + vector[vector[int]] get_distributions() + vector[float] get_values() + + # Sequential Halving methods (only declare methods, not members, to avoid memory layout issues) + void init_sequential_halving(int num_sims, int num_top_acts) + int ready_for_next_sh_phase() + void apply_next_sh_phase(CMinMaxStatsList *min_max_stats_lst) + void set_used_visit_num(int num) + + cdef cppclass CSearchResults: + CSearchResults() except + + CSearchResults(int num) except + + int num + vector[int] latent_state_index_in_search_path, latent_state_index_in_batch, last_actions, search_lens + vector[int] virtual_to_play_batchs + vector[CNode*] nodes + + cdef void cbackpropagate(vector[CNode*] &search_path, CMinMaxStats &min_max_stats, int to_play, float value, float discount_factor) + void cbatch_backpropagate(int current_latent_state_index, float discount_factor, vector[float] value_prefixs, vector[float] values, vector[vector[float]] policies, + CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, vector[int] &to_play_batch) + void cbatch_backpropagate_with_reuse(int current_latent_state_index, float discount_factor, vector[float] value_prefixs, vector[float] values, vector[vector[float]] policies, + CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, vector[int] &to_play_batch, vector[int] &no_inference_lst, vector[int] &reuse_lst, vector[float] &reuse_value_lst) + void cbatch_traverse(CRoots *roots, int pb_c_base, float pb_c_init, float discount_factor, CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, vector[int] &virtual_to_play_batch) + void cbatch_traverse_with_reuse(CRoots *roots, int pb_c_base, float pb_c_init, float discount_factor, CMinMaxStatsList *min_max_stats_lst, CSearchResults &results, vector[int] &virtual_to_play_batch, vector[int] &true_action, vector[float] &reuse_value) diff --git a/lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pyx b/lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pyx new file mode 100644 index 000000000..73c8b6047 --- /dev/null +++ b/lzero/mcts/ctree/ctree_muzero_v2/mz_tree.pyx @@ -0,0 +1,368 @@ +# distutils: language=c++ +# cython:language_level=3 +from libcpp.vector cimport vector + +cdef class MinMaxStatsList: + """ + 最小最大统计列表类 + 用于批量管理多个最小最大统计对象,用于 Q 值的归一化 + """ + cdef CMinMaxStatsList *cmin_max_stats_lst # 指向 C++ 最小最大统计列表的指针 + + def __cinit__(self, int num): + """ + 初始化最小最大统计列表 + + 参数: + num: 统计对象的数量(对应批量大小) + """ + self.cmin_max_stats_lst = new CMinMaxStatsList(num) + + def set_delta(self, float value_delta_max): + """ + 设置价值增量的最大值(用于初始化 min-max 范围) + + 参数: + value_delta_max: 价值变化的最大值 + """ + self.cmin_max_stats_lst[0].set_delta(value_delta_max) + + def __dealloc__(self): + """ + 析构函数,释放 C++ 对象占用的内存 + """ + del self.cmin_max_stats_lst + +cdef class ResultsWrapper: + """ + 搜索结果包装类 + 用于包装和管理 C++ 搜索结果对象,包含搜索路径、叶节点等信息 + """ + cdef CSearchResults cresults # C++ 搜索结果对象 + + def __cinit__(self, int num): + """ + 初始化搜索结果包装器 + + 参数: + num: 搜索数量(批量大小) + """ + self.cresults = CSearchResults(num) + + def get_search_len(self): + """ + 获取搜索路径的长度列表 + + 返回: + 搜索长度列表,即每条搜索路径的深度 + """ + return self.cresults.search_lens + +cdef class Roots: + """ + 根节点集合类 + 用于批量管理多个 MCTS 搜索树的根节点,是 MuZero 并行搜索的核心数据结构 + """ + cdef int root_num # 根节点数量,对应批量大小 + cdef CRoots *roots # 指向 C++ 根节点集合的指针 + + def __cinit__(self, int root_num, vector[vector[int]] legal_actions_list): + """ + 初始化根节点集合 + + 参数: + root_num: 根节点数量(对应批量大小) + legal_actions_list: 每个根节点的合法动作列表 + """ + self.root_num = root_num + self.roots = new CRoots(root_num, legal_actions_list) + + def prepare(self, float root_noise_weight, list noises, list value_prefix_pool, list policy_logits_pool, + vector[int] & to_play_batch): + """ + 准备根节点(带探索噪声版本,用于训练) + + 扩展根节点并添加 Dirichlet 噪声以增强探索,模拟强化学习训练过程 + + 参数: + root_noise_weight: 根节点噪声权重,控制探索程度(0.25 是常用值) + noises: Dirichlet 噪声列表,每个批次元素一个噪声向量 + value_prefix_pool: 价值前缀池(即时奖励 r,MuZero 中的 value_prefix) + policy_logits_pool: 策略 logits 池(神经网络输出的动作概率原始值) + to_play_batch: 当前玩家批次(-1 表示单人游戏,1/2 表示双人游戏的玩家编号) + """ + self.roots[0].prepare(root_noise_weight, noises, value_prefix_pool, policy_logits_pool, to_play_batch) + + def prepare_no_noise(self, list value_prefix_pool, list policy_logits_pool, vector[int] & to_play_batch): + """ + 准备根节点(无噪声版本,用于评估) + + 扩展根节点但不添加探索噪声,用于模型评估和测试 + + 参数: + value_prefix_pool: 价值前缀池 + policy_logits_pool: 策略 logits 池 + to_play_batch: 当前玩家批次 + """ + self.roots[0].prepare_no_noise(value_prefix_pool, policy_logits_pool, to_play_batch) + + def get_trajectories(self): + """ + 获取从每个根节点开始的最佳轨迹 + + 返回从根节点到叶节点的最佳动作序列(基于访问次数的贪心路径) + + 返回: + 轨迹列表,每个轨迹是一个动作序列,例如 [[0, 1, 2], [1, 0, 1], ...] + """ + return self.roots[0].get_trajectories() + + def get_distributions(self): + """ + 获取每个根节点的子节点访问次数分布 + + 该分布用于生成 MCTS 改进的策略目标,是强化学习中的重要训练目标 + + 返回: + 访问次数分布列表,例如 [[1,3,0,2,5], [2,1,4,0,3], ...] + 其中每个内部列表代表一个根节点的子节点访问次数 + """ + return self.roots[0].get_distributions() + + def get_values(self): + """ + 获取每个根节点的平均价值 + + 价值是通过 value_sum / visit_count 计算得出的 + + 返回: + 价值列表,每个值为该根节点在搜索中估计的累积价值 + """ + return self.roots[0].get_values() + + def clear(self): + """ + 清空根节点集合,释放子树 + + 在开始新的搜索前调用,释放上一次搜索的树结构 + """ + self.roots[0].clear() + + def __dealloc__(self): + """ + 析构函数,释放 C++ 对象占用的内存 + """ + del self.roots + + @property + def num(self): + """ + 获取根节点数量属性 + + 返回: + 根节点数量(等同于批量大小) + """ + return self.root_num + + def init_sequential_halving(self, int num_sims, int num_top_acts): + """ + 初始化 Sequential Halving 参数 + + 参数: + num_sims: 总模拟次数 + num_top_acts: 初始候选动作数 + """ + self.roots[0].init_sequential_halving(num_sims, num_top_acts) + + def ready_for_next_sh_phase(self): + """ + 检查是否准备好进行下一个 Sequential Halving 阶段 + + 返回: + 1 如果准备好,0 否则 + """ + return self.roots[0].ready_for_next_sh_phase() + + def apply_next_sh_phase(self, MinMaxStatsList min_max_stats_lst): + """ + 应用下一个 Sequential Halving 阶段 + + 参数: + min_max_stats_lst: 最小最大统计列表,用于更新动作评分 + """ + self.roots[0].apply_next_sh_phase(min_max_stats_lst.cmin_max_stats_lst) + + def set_used_visit_num(self, int num): + """ + 设置已使用的访问次数(用于 Sequential Halving 阶段转换检查) + + 参数: + num: 已使用的访问次数 + """ + self.roots[0].set_used_visit_num(num) + +cdef class Node: + """ + 单个搜索树节点的 Python 包装类 + 对应 MuZero MCTS 中搜索树的一个节点 + """ + cdef CNode cnode # C++ 节点对象 + + def __cinit__(self): + """ + 默认构造函数 + """ + pass + + def __cinit__(self, float prior, vector[int] & legal_actions): + """ + 带参数的构造函数 + + 参数: + prior: 先验概率 + legal_actions: 合法动作列表 + """ + pass + + def expand(self, int to_play, int current_latent_state_index, int batch_index, float value_prefix, + list policy_logits): + """ + 扩展节点,创建所有合法动作的子节点 + + 在叶节点处调用,基于神经网络的预测扩展子节点, + 并根据策略 logits 初始化每个子节点的先验概率 + + 参数: + to_play: 当前玩家(-1 单人游戏,1/2 双人游戏的玩家编号) + current_latent_state_index: 当前隐状态在搜索路径中的索引 + batch_index: 当前隐状态在批次中的索引 + value_prefix: 价值前缀(即时奖励 r,MuZero 中的概念) + policy_logits: 策略 logits(神经网络输出的原始动作概率值) + """ + cdef vector[float] cpolicy = policy_logits + self.cnode.expand(to_play, current_latent_state_index, batch_index, value_prefix, cpolicy) + +def batch_backpropagate(int current_latent_state_index, float discount_factor, list value_prefixs, list values, list policies, + MinMaxStatsList min_max_stats_lst, ResultsWrapper results, list to_play_batch): + """ + 批量反向传播(标准版本) + + 在叶节点获得神经网络的预测后,沿搜索路径从叶节点向根节点反向传播价值 + 更新路径中所有节点的价值和访问次数,这是 MCTS 的关键步骤 + + 参数: + current_latent_state_index: 当前隐状态索引 + discount_factor: 折扣因子 γ,用于计算累积奖励(通常 0.99) + value_prefixs: 价值前缀列表(即时奖励,通过展开获得) + values: 价值列表(神经网络预测的叶节点价值,V(s)) + policies: 策略列表(神经网络预测的动作概率分布,π(a|s)) + min_max_stats_lst: 最小最大统计列表,用于归一化 Q 值到 [0,1] 范围 + results: 搜索结果包装器,包含搜索路径和叶节点信息 + to_play_batch: 玩家批次(-1 单人,1/2 双人游戏) + """ + cdef int i + cdef vector[float] cvalue_prefixs = value_prefixs + cdef vector[float] cvalues = values + cdef vector[vector[float]] cpolicies = policies + + cbatch_backpropagate(current_latent_state_index, discount_factor, cvalue_prefixs, cvalues, cpolicies, + min_max_stats_lst.cmin_max_stats_lst, results.cresults, to_play_batch) + +def batch_backpropagate_with_reuse(int current_latent_state_index, float discount_factor, list value_prefixs, list values, list policies, + MinMaxStatsList min_max_stats_lst, ResultsWrapper results, list to_play_batch, list no_inference_lst, list reuse_lst, list reuse_value_lst): + """ + 批量反向传播(带价值重用版本) + + 优化版本的反向传播,通过重用之前搜索的价值来减少神经网络推理次数 + 实现了 ARM (Action Reuse Method) 算法,详见论文: https://arxiv.org/abs/2404.16364 + + 这个函数处理三类节点: + 1. 无需推理的节点(no_inference_lst):直接使用重用价值 + 2. 需要使用重用价值的节点(reuse_lst):先通过网络推理获得策略,再用重用价值进行反向传播 + 3. 其他节点:正常的网络推理和反向传播 + + 参数: + current_latent_state_index: 当前隐状态索引 + discount_factor: 折扣因子 γ + value_prefixs: 价值前缀列表 + values: 价值列表 + policies: 策略列表 + min_max_stats_lst: 最小最大统计列表 + results: 搜索结果包装器 + to_play_batch: 玩家批次 + no_inference_lst: 无需推理的节点索引列表(完全跳过网络推理) + reuse_lst: 需要使用重用价值的节点索引列表(进行推理但用重用价值反向传播) + reuse_value_lst: 重用价值列表(来自上一步的搜索结果) + """ + cdef int i + cdef vector[float] cvalue_prefixs = value_prefixs + cdef vector[float] cvalues = values + cdef vector[vector[float]] cpolicies = policies + cdef vector[float] creuse_value_lst = reuse_value_lst + + cbatch_backpropagate_with_reuse(current_latent_state_index, discount_factor, cvalue_prefixs, cvalues, cpolicies, + min_max_stats_lst.cmin_max_stats_lst, results.cresults, to_play_batch, no_inference_lst, reuse_lst, creuse_value_lst) + +def batch_traverse(Roots roots, int pb_c_base, float pb_c_init, float discount_factor, MinMaxStatsList min_max_stats_lst, + ResultsWrapper results, list virtual_to_play_batch): + """ + 批量遍历搜索树(标准 MCTS 选择阶段) + + 从根节点开始,使用 UCB(Upper Confidence Bound)公式选择最佳动作, + 不断向下遍历搜索树,直到到达未扩展的叶节点 + + 这是 MCTS 的核心步骤之一,平衡探索与利用: + - 利用:选择价值高的节点 + - 探索:选择未充分探索的节点(访问次数少) + + 参数: + roots: 根节点集合 + pb_c_base: UCB 公式中的常数 c2,控制探索强度的基数(通常 19652) + pb_c_init: UCB 公式中的常数 c1,初始探索系数(通常 1.25) + discount_factor: 折扣因子 γ + min_max_stats_lst: 最小最大统计列表,用于归一化价值 + results: 搜索结果包装器,用于存储搜索路径和叶节点信息 + virtual_to_play_batch: 虚拟玩家批次(用于双人游戏模拟对手) + + 返回: + latent_state_index_in_search_path: 叶节点父节点的隐状态在搜索路径中的索引 + latent_state_index_in_batch: 叶节点父节点的隐状态在批次中的索引 + last_actions: 到达叶节点的最后一个动作 + virtual_to_play_batchs: 叶节点处的玩家信息 + """ + cbatch_traverse(roots.roots, pb_c_base, pb_c_init, discount_factor, min_max_stats_lst.cmin_max_stats_lst, results.cresults, + virtual_to_play_batch) + + return results.cresults.latent_state_index_in_search_path, results.cresults.latent_state_index_in_batch, results.cresults.last_actions, results.cresults.virtual_to_play_batchs + +def batch_traverse_with_reuse(Roots roots, int pb_c_base, float pb_c_init, float discount_factor, MinMaxStatsList min_max_stats_lst, + ResultsWrapper results, list virtual_to_play_batch, list true_action, list reuse_value): + """ + 批量遍历搜索树(带价值重用的优化版本) + + 在根节点处使用 ARM(Action Reuse Method)评分函数,将重用价值融入到选择过程中 + 这使得搜索更倾向于探索真实轨迹的方向,从而在减少计算的同时保持搜索质量 + + 与 batch_traverse 的关键区别: + - 在根节点选择动作时,对于真实轨迹中选择的动作使用 ARM 评分 + - ARM 评分用重用价值代替网络预测的价值,更准确地反映该分支的价值 + - 其他节点仍使用标准 UCB 评分 + + 参数: + roots: 根节点集合 + pb_c_base: UCB 公式中的常数 c2 + pb_c_init: UCB 公式中的常数 c1 + discount_factor: 折扣因子 γ + min_max_stats_lst: 最小最大统计列表 + results: 搜索结果包装器 + virtual_to_play_batch: 虚拟玩家批次 + true_action: 真实轨迹中选择的动作(来自上一步搜索) + reuse_value: 重用价值列表(来自上一步搜索中该动作的结果) + + 返回: + 与 batch_traverse 相同 + """ + cbatch_traverse_with_reuse(roots.roots, pb_c_base, pb_c_init, discount_factor, min_max_stats_lst.cmin_max_stats_lst, results.cresults, + virtual_to_play_batch, true_action, reuse_value) + + return results.cresults.latent_state_index_in_search_path, results.cresults.latent_state_index_in_batch, results.cresults.last_actions, results.cresults.virtual_to_play_batchs diff --git a/lzero/mcts/ctree/ctree_muzero_v2/test_batch_traverse b/lzero/mcts/ctree/ctree_muzero_v2/test_batch_traverse new file mode 100755 index 0000000000000000000000000000000000000000..c773039bf50779e5e1af669b370035c787025264 GIT binary patch literal 268848 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 literal 0 HcmV?d00001 diff --git a/lzero/mcts/ctree/ctree_muzero_v2/test_cnode_sh b/lzero/mcts/ctree/ctree_muzero_v2/test_cnode_sh new file mode 100755 index 0000000000000000000000000000000000000000..4a0675ce58e14f9766095bc87f43b5012a67e10a GIT binary patch literal 267328 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 literal 0 HcmV?d00001 diff --git a/lzero/mcts/tree_search/__init__.py b/lzero/mcts/tree_search/__init__.py index 514daf813..753e55e6b 100644 --- a/lzero/mcts/tree_search/__init__.py +++ b/lzero/mcts/tree_search/__init__.py @@ -1,4 +1,4 @@ -from .mcts_ctree import MuZeroMCTSCtree, EfficientZeroMCTSCtree, GumbelMuZeroMCTSCtree, UniZeroMCTSCtree, MuZeroRNNFullObsMCTSCtree +from .mcts_ctree import MuZeroMCTSCtree, EfficientZeroMCTSCtree, GumbelMuZeroMCTSCtree, UniZeroMCTSCtree,UniZeroMCTSCtree_v2, MuZeroRNNFullObsMCTSCtree from .mcts_ctree_sampled import SampledEfficientZeroMCTSCtree, SampledMuZeroMCTSCtree, SampledUniZeroMCTSCtree from .mcts_ctree_stochastic import StochasticMuZeroMCTSCtree from .mcts_ptree import MuZeroMCTSPtree, EfficientZeroMCTSPtree diff --git a/lzero/mcts/tree_search/mcts_ctree.py b/lzero/mcts/tree_search/mcts_ctree.py index 4e238a6b3..b3d6d1503 100644 --- a/lzero/mcts/tree_search/mcts_ctree.py +++ b/lzero/mcts/tree_search/mcts_ctree.py @@ -7,7 +7,7 @@ from lzero.mcts.ctree.ctree_efficientzero import ez_tree as tree_efficientzero from lzero.mcts.ctree.ctree_gumbel_muzero import gmz_tree as tree_gumbel_muzero -from lzero.mcts.ctree.ctree_muzero import mz_tree as tree_muzero +from lzero.mcts.ctree.ctree_muzero_v2 import mz_tree as tree_muzero from lzero.policy import DiscreteSupport, InverseScalarTransform, to_detach_cpu_numpy if TYPE_CHECKING: @@ -189,6 +189,226 @@ def search( return first_action_latent_map +class UniZeroMCTSCtree_v2(object): + """ + Overview: + MCTSCtree for UniZero with Gumbel MuZero. The core ``batch_traverse`` and ``batch_backpropagate`` function is implemented in C++. + + Interfaces: + __init__, roots, search + """ + + config = dict( + # (float) The maximum change in value allowed during the backup step of the search tree update. + value_delta_max=0.01, + # (int) The number of top actions to consider in Gumbel MuZero Sequential Halving + max_num_considered_actions=16, + # (float) Gumbel MuZero exploration constant related to visit counts + c_visit=50.0, + # (float) Gumbel MuZero exploration scaling constant + c_scale=1.0, + # (int) The total number of simulations in MCTS + num_simulations=50, + # (int) The base constant used in the PUCT formula for balancing exploration and exploitation during tree search. + pb_c_base=19652, + # (float) The initialization constant used in the PUCT formula for balancing exploration and exploitation during tree search. + pb_c_init=1.25, + env_type='not_board_games', + ) + + @classmethod + def default_config(cls: type) -> EasyDict: + cfg = EasyDict(copy.deepcopy(cls.config)) + cfg.cfg_type = cls.__name__ + 'Dict' + return cfg + + def __init__(self, cfg: EasyDict = None) -> None: + """ + Overview: + Use the default configuration mechanism. If a user passes in a cfg with a key that matches an existing key + in the default configuration, the user-provided value will override the default configuration. Otherwise, + the default configuration will be used. + """ + default_config = self.default_config() + default_config.update(cfg) + self._cfg = default_config + self.value_support = DiscreteSupport(*self._cfg.model.value_support_range, self._cfg.device) + self.reward_support = DiscreteSupport(*self._cfg.model.reward_support_range, self._cfg.device) + self.value_inverse_scalar_transform_handle = InverseScalarTransform(self.value_support, self._cfg.model.categorical_distribution) + self.reward_inverse_scalar_transform_handle = InverseScalarTransform(self.reward_support, self._cfg.model.categorical_distribution) + + @classmethod + def roots(cls: int, active_collect_env_num: int, legal_actions: List[Any]) -> "mz_ctree": + """ + Overview: + The initialization of CRoots with root num and legal action lists. + Arguments: + - root_num (:obj:`int`): the number of the current root. + - legal_action_list (:obj:`list`): the vector of the legal action of this root. + """ + from lzero.mcts.ctree.ctree_muzero_v2 import mz_tree as ctree + return ctree.Roots(active_collect_env_num, legal_actions) + + #@profile + def search( + self, roots: Any, model: torch.nn.Module, latent_state_roots: List[Any], to_play_batch: Union[int, + List[Any]], timestep: Union[int, List[Any]]=None, task_id=None + ) -> None: + """ + Overview: + Perform Monte Carlo Tree Search (MCTS) for a batch of root nodes in parallel. + This method utilizes the C++ implementation of the tree search for efficiency. + + Arguments: + - roots (:obj:`Any`): A batch of expanded root nodes. + - model (:obj:`torch.nn.Module`): The neural network model used for inference. + - latent_state_roots (:obj:`List[Any]`): The hidden states of the root nodes. + - to_play_batch (:obj:`Union[int, List[Any]]`): The list of players in self-play mode. + - timestep (:obj:`Union[int, List[Any]]`): The step index of the environment in one episode. + """ + with torch.no_grad(): + model.eval() + + # preparation some constant + batch_size = roots.num + + # Store the latent state of each possible action at the MCTS root for each environment. + first_action_latent_map = {env_id: {} for env_id in range(batch_size)} # {env_id: {action: latent_state}} + + # Generate Gumbel noise for this search (sampled once at root, used throughout) + import numpy as np + legal_actions_list = self._legal_actions if hasattr(self, '_legal_actions') else [list(range(self._cfg.model.action_space_size)) for _ in range(batch_size)] + gumbels = [] + for i in range(batch_size): + num_actions = len(legal_actions_list[i]) if isinstance(legal_actions_list[i], list) else self._cfg.model.action_space_size + # Gumbel(0,1) = -log(-log(Uniform(0,1))) + gumbel_noise = -np.log(-np.log(np.random.uniform(0, 1, num_actions))) + gumbels.append(gumbel_noise.tolist()) + + c_visit, c_scale, discount_factor = self._cfg.c_visit, self._cfg.c_scale, self._cfg.discount_factor + pb_c_base, pb_c_init = self._cfg.pb_c_base, self._cfg.pb_c_init + # the data storage of latent states: storing the latent state of all the nodes in the search. + latent_state_batch_in_search_path = [latent_state_roots] + + # minimax value storage + min_max_stats_lst = tree_muzero.MinMaxStatsList(batch_size) # batch_size already defined above + min_max_stats_lst.set_delta(self._cfg.value_delta_max) + + # ===== Sequential Halving 初始化 ===== + roots.init_sequential_halving(self._cfg.num_simulations, self._cfg.max_num_considered_actions) + + state_action_history = [] + for simulation_index in range(self._cfg.num_simulations): + # In each simulation, we expanded a new node, so in one search, we have ``num_simulations`` num of nodes at most. + latent_states = [] + + # prepare a result wrapper to transport results between python and c++ parts + results = tree_muzero.ResultsWrapper(num=batch_size) + + # latent_state_index_in_search_path: the first index of leaf node states in latent_state_batch_in_search_path, i.e. is current_latent_state_index in one the search. + # latent_state_index_in_batch: the second index of leaf node states in latent_state_batch_in_search_path, i.e. the index in the batch, whose maximum is ``batch_size``. + # e.g. the latent state of the leaf node in (x, y) is latent_state_batch_in_search_path[x, y], where x is current_latent_state_index, y is batch_index. + # The index of value prefix hidden state of the leaf node are in the same manner. + """ + MCTS stage 1: Selection + Each simulation starts from the internal root state s0, and finishes when the simulation reaches a leaf node s_l. + """ + if self._cfg.env_type == 'not_board_games': + latent_state_index_in_search_path, latent_state_index_in_batch, last_actions, virtual_to_play_batch = tree_muzero.batch_traverse( + roots, pb_c_base, pb_c_init, discount_factor, min_max_stats_lst, results, + to_play_batch + ) + else: + latent_state_index_in_search_path, latent_state_index_in_batch, last_actions, virtual_to_play_batch = tree_muzero.batch_traverse( + roots, pb_c_base, pb_c_init, discount_factor, min_max_stats_lst, results, + copy.deepcopy(to_play_batch) + ) + + # obtain the latent state for leaf node + for ix, iy in zip(latent_state_index_in_search_path, latent_state_index_in_batch): + latent_states.append(latent_state_batch_in_search_path[ix][iy]) + + try: + latent_states = torch.from_numpy(np.asarray(latent_states)).to(self._cfg.device) + except Exception as e: + print("="*20) + print(e) + print("roots:", roots, "latent_state_roots:", latent_state_roots) + print ("latent_state_roots.shape:", latent_state_roots.shape) + + + # TODO: .long() is only for discrete action + last_actions = torch.from_numpy(np.asarray(last_actions)).to(self._cfg.device).long() + + # Update state_action_history after each simulation + state_action_history.append((latent_states.detach().cpu().numpy(), last_actions)) + + """ + MCTS stage 2: Expansion + At the final time-step l of the simulation, the next_latent_state and reward/value_prefix are computed by the dynamics function. + Then we calculate the policy_logits and value for the leaf node (next_latent_state) by the prediction function. (aka. evaluation) + MCTS stage 3: Backup + At the end of the simulation, the statistics along the trajectory are updated. + """ + # search_depth is used for rope in UniZero + search_depth = results.get_search_len() + # print(f'simulation_index:{simulation_index}, search_depth:{search_depth}, latent_state_index_in_search_path:{latent_state_index_in_search_path}') + if timestep is None: + # for UniZero + if task_id is not None: + # multi task setting + network_output = model.recurrent_inference(state_action_history, simulation_index, search_depth, task_id=task_id) + else: + # single task setting + network_output = model.recurrent_inference(state_action_history, simulation_index, search_depth) + else: + # for UniZero + if task_id is not None: + # multi task setting + # network_output = model.recurrent_inference(state_action_history, simulation_index, search_depth, timestep, task_id=task_id) + network_output = model.recurrent_inference(state_action_history, simulation_index, search_depth, task_id=task_id) + else: + # single task setting + network_output = model.recurrent_inference(state_action_history, simulation_index, search_depth, timestep) + + network_output.latent_state = to_detach_cpu_numpy(network_output.latent_state) + network_output.policy_logits = to_detach_cpu_numpy(network_output.policy_logits) + network_output.value = to_detach_cpu_numpy(self.value_inverse_scalar_transform_handle(network_output.value)) + network_output.reward = to_detach_cpu_numpy(self.reward_inverse_scalar_transform_handle(network_output.reward)) + + for env_id in range(batch_size): + depth = search_depth[env_id] + action = last_actions[env_id].item() + if depth == 1 and action not in first_action_latent_map[env_id]: + first_action_latent_map[env_id][action] = network_output.latent_state[env_id] + else: + continue + + latent_state_batch_in_search_path.append(network_output.latent_state) + # tolist() is to be compatible with cpp datatype. + reward_batch = network_output.reward.reshape(-1).tolist() + value_batch = network_output.value.reshape(-1).tolist() + policy_logits_batch = network_output.policy_logits.tolist() + + # In ``batch_backpropagate()``, we first expand the leaf node using ``the policy_logits`` and + # ``reward`` predicted by the model, then perform backpropagation along the search path to update the + # statistics. + + # NOTE: simulation_index + 1 is very important, which is the depth of the current leaf node. + current_latent_state_index = simulation_index + 1 + tree_muzero.batch_backpropagate( + current_latent_state_index, discount_factor, reward_batch, value_batch, policy_logits_batch, + min_max_stats_lst, results, virtual_to_play_batch + ) + + # ===== Sequential Halving 自动转换阶段 ===== + roots.set_used_visit_num(simulation_index + 1) + if roots.ready_for_next_sh_phase(): + roots.apply_next_sh_phase(min_max_stats_lst) + + return first_action_latent_map + + class MuZeroMCTSCtree(object): """ Overview: diff --git a/lzero/policy/unizero.py b/lzero/policy/unizero.py index e42e9acf4..bb6ffb05f 100644 --- a/lzero/policy/unizero.py +++ b/lzero/policy/unizero.py @@ -9,7 +9,9 @@ from ding.utils import POLICY_REGISTRY from lzero.entry.utils import initialize_zeros_batch, initialize_pad_batch -from lzero.mcts import UniZeroMCTSCtree as MCTSCtree + +from lzero.mcts import UniZeroMCTSCtree_v2 as MCTSCtree + from lzero.model import ImageTransforms from lzero.policy import scalar_transform, InverseScalarTransform, phi_transform, \ DiscreteSupport, to_torch_float_tensor, mz_network_output_unpack, select_action, prepare_obs, \ @@ -17,7 +19,76 @@ from lzero.policy.muzero import MuZeroPolicy from .utils import configure_optimizers_nanogpt +from torch.nn.utils.convert_parameters import parameters_to_vector, vector_to_parameters +import torch.nn.functional as F + +def scale_module_weights_vectorized(module: torch.nn.Module, scale_factor: float): + """ + 使用向量化操作高效地缩放一个模块的所有权重。 + """ + if not (0.0 < scale_factor < 1.0): + return # 如果缩放因子无效,则不执行任何操作 + + # 1. 将模块的所有参数展平成一个单一向量 + params_vec = parameters_to_vector(module.parameters()) + + # 2. 在这个向量上执行一次乘法操作 + params_vec.data.mul_(scale_factor) + + # 3. 将缩放后的向量复制回模块的各个参数 + vector_to_parameters(params_vec, module.parameters()) + + +def configure_optimizer_unizero(model, learning_rate, weight_decay, device_type, betas): + """ + 为UniZero模型配置带有差异化学习率的优化器。 + """ + # 1. 定义需要特殊处理的参数 + param_dict = {pn: p for pn, p in model.named_parameters() if p.requires_grad} + + # 2. 将参数分为三组:Transformer主干、Tokenizer、Heads + transformer_params = {pn: p for pn, p in param_dict.items() if 'transformer' in pn} + tokenizer_params = {pn: p for pn, p in param_dict.items() if 'tokenizer' in pn} + + # Heads的参数是那些既不属于transformer也不属于tokenizer的 + head_params = { + pn: p for pn, p in param_dict.items() + if 'transformer' not in pn and 'tokenizer' not in pn + } + + # 3. 为每组设置不同的优化器参数(特别是学习率) + # 这里我们仍然使用AdamW,但学习率设置更合理 + optim_groups = [ + { + 'params': list(tokenizer_params.values()), + 'lr': learning_rate, # Tokenizer使用基础学习率,例如 1e-4 + # 'lr': learning_rate * 0.1, # 为encoder设置一个较小的学习率,例如 1e-5 + 'weight_decay': weight_decay * 5.0 # <-- 为Encoder设置5倍的权重衰减!这是一个强力正则化 + # 'weight_decay': weight_decay # <-- 为Encoder设置5倍的权重衰减!这是一个强力正则化 + }, + { + 'params': list(transformer_params.values()), + 'lr': learning_rate, # 1e-4 + # 'lr': learning_rate * 0.2, # 为Transformer主干设置一个较小的学习率,例如 1e-5 + 'weight_decay': weight_decay + # 'weight_decay': weight_decay * 5.0 + }, + { + 'params': list(head_params.values()), + 'lr': learning_rate, # Heads也使用基础学习率率,例如 1e-4 + 'weight_decay': 0.0 # 通常Heads的权重不做衰减 + # 'weight_decay': weight_decay + + } + ] + print("--- Optimizer Groups ---") + print(f"Transformer LR: {learning_rate}") + print(f"Tokenizer/Heads LR: {learning_rate}") + + optimizer = torch.optim.AdamW(optim_groups, betas=betas) + return optimizer + @POLICY_REGISTRY.register('unizero') class UniZeroPolicy(MuZeroPolicy): """ @@ -81,8 +152,8 @@ class UniZeroPolicy(MuZeroPolicy): device='cpu', # (bool) Whether to analyze simulation normalization. analysis_sim_norm=False, - # (bool) Whether to analyze dormant ratio. - analysis_dormant_ratio=False, + # (bool) Whether to analyze dormant ratio, average_weight_magnitude of net, effective_rank of latent. + analysis_dormant_ratio_weight_rank=False, # (int) The shape of the action space. action_space_size=6, # (int) The size of the group, related to simulation normalization. @@ -139,6 +210,7 @@ class UniZeroPolicy(MuZeroPolicy): rope_theta=10000, # (int) The maximum sequence length for position encoding. max_seq_len=8192, + lora_r= 0, # Controls where to compute reconstruction loss: 'after_backbone', 'before_backbone', or None. # - after_backbone: The reconstruction loss is computed after the encoded representation passes through the backbone. # - before_backbone: The reconstruction loss is computed directly on the encoded representation, without the backbone. @@ -146,6 +218,23 @@ class UniZeroPolicy(MuZeroPolicy): ), ), # ****** common ****** + # (bool) 是否启用自适应策略熵权重 (alpha) + use_adaptive_entropy_weight=True, + # (float) 自适应alpha优化器的学习率 + adaptive_entropy_alpha_lr=1e-4, + # ==================== START: Encoder-Clip Annealing Config ==================== + # (bool) 是否启用 encoder-clip 值的退火。 + use_encoder_clip_annealing=True, + # (str) 退火类型。可选 'linear' 或 'cosine'。 + encoder_clip_anneal_type='cosine', + # (float) 退火的起始 clip 值 (训练初期,较宽松)。 + encoder_clip_start_value=30.0, + # (float) 退火的结束 clip 值 (训练后期,较严格)。 + encoder_clip_end_value=10.0, + # (int) 完成从起始值到结束值的退火所需的训练迭代步数。 + encoder_clip_anneal_steps=100000, # 例如,在200k次迭代后达到最终值 + # ===================== END: Encoder-Clip Annealing Config ===================== + # (bool) whether to use rnd model. use_rnd_model=False, # (bool) Whether to use multi-gpu training. @@ -178,7 +267,7 @@ class UniZeroPolicy(MuZeroPolicy): # (bool) Whether to use the pure policy to collect data. collect_with_pure_policy=False, # (int) The evaluation frequency. - eval_freq=int(2e3), + eval_freq=int(5e3), # (str) The sample type. Options are ['episode', 'transition']. sample_type='transition', # ****** observation ****** @@ -211,6 +300,10 @@ class UniZeroPolicy(MuZeroPolicy): optim_type='AdamW', # (float) Learning rate for training policy network. Initial lr for manually decay schedule. learning_rate=0.0001, + # ==================== [新增] 范数监控频率 ==================== + # 每隔多少个训练迭代步数,监控一次模型参数的范数。设置为0则禁用。 + monitor_norm_freq=5000, + # ============================================================ # (int) Frequency of hard target network update. target_update_freq=100, # (int) Frequency of soft target network update. @@ -227,8 +320,12 @@ class UniZeroPolicy(MuZeroPolicy): n_episode=8, # (int) The number of num_segments in each collecting stage when use muzero_segment_collector. num_segments=8, - # (int) the number of simulations in MCTS. + # # (int) the number of simulations in MCTS for renalyze. num_simulations=50, + # (int) The number of simulations in MCTS for the collect phase. + collect_num_simulations=25, + # (int) The number of simulations in MCTS for the eval phase. + eval_num_simulations=50, # (float) Discount factor (gamma) for returns. discount_factor=0.997, # (int) The number of steps for calculating target q_value. @@ -313,24 +410,142 @@ def default_model(self) -> Tuple[str, List[str]]: """ return 'UniZeroModel', ['lzero.model.unizero_model'] + + # ==================== [新增] 模型范数监控函数 ==================== + def _monitor_model_norms(self) -> Dict[str, float]: + """ + Overview: + 计算并返回模型关键组件(Encoder, Transformer, Heads)的参数矩阵范数。 + 此函数应在 torch.no_grad() 环境下调用,以提高效率。 + Returns: + - norm_metrics (:obj:`Dict[str, float]`): 包含所有范数指标的字典,用于日志记录。 + """ + world_model = self._learn_model.world_model + norm_metrics = {} + + # 定义要监控的模块组 + module_groups = { + 'encoder': world_model.tokenizer.encoder, + 'transformer': world_model.transformer, + 'head_value': world_model.head_value, + 'head_reward': world_model.head_rewards, + 'head_policy': world_model.head_policy, + } + + for group_name, group_module in module_groups.items(): + total_norm_sq = 0.0 + for param_name, param in group_module.named_parameters(): + if param.requires_grad: + # 计算单层参数的L2范数 + param_norm = param.data.norm(2).item() + # 替换点号,使其在TensorBoard中正确显示为层级 + log_name = f'norm/{group_name}/{param_name.replace(".", "/")}' + norm_metrics[log_name] = param_norm + total_norm_sq += param_norm ** 2 + + # 计算整个模块的总范数 + total_group_norm = np.sqrt(total_norm_sq) + norm_metrics[f'norm/{group_name}/_total_norm'] = total_group_norm + + return norm_metrics + + def _monitor_gradient_norms(self) -> Dict[str, float]: + """ + Overview: + 计算并返回模型关键组件的梯度范数。 + 此函数应在梯度计算完成后、参数更新之前调用。 + Returns: + - grad_metrics (:obj:`Dict[str, float]`): 包含所有梯度范数指标的字典,用于日志记录。 + """ + world_model = self._learn_model.world_model + grad_metrics = {} + + # 定义要监控的模块组 + module_groups = { + 'encoder': world_model.tokenizer.encoder, + 'transformer': world_model.transformer, + 'head_value': world_model.head_value, + 'head_reward': world_model.head_rewards, + 'head_policy': world_model.head_policy, + } + + for group_name, group_module in module_groups.items(): + total_grad_norm_sq = 0.0 + num_params_with_grad = 0 + + for param_name, param in group_module.named_parameters(): + if param.requires_grad and param.grad is not None: + # 计算单层参数的梯度L2范数 + grad_norm = param.grad.data.norm(2).item() + # 替换点号,使其在TensorBoard中正确显示为层级 + log_name = f'grad/{group_name}/{param_name.replace(".", "/")}' + grad_metrics[log_name] = grad_norm + total_grad_norm_sq += grad_norm ** 2 + num_params_with_grad += 1 + + # 计算整个模块的总梯度范数 + if num_params_with_grad > 0: + total_group_grad_norm = np.sqrt(total_grad_norm_sq) + grad_metrics[f'grad/{group_name}/_total_norm'] = total_group_grad_norm + else: + grad_metrics[f'grad/{group_name}/_total_norm'] = 0.0 + + return grad_metrics + # ================================================================= + def _init_learn(self) -> None: """ Overview: Learn mode init method. Called by ``self.__init__``. Initialize the learn model, optimizer and MCTS utils. """ - # NOTE: nanoGPT optimizer - self._optimizer_world_model = configure_optimizers_nanogpt( - model=self._model.world_model, - learning_rate=self._cfg.learning_rate, - weight_decay=self._cfg.weight_decay, - device_type=self._cfg.device, - betas=(0.9, 0.95), - ) + if self._cfg.optim_type == 'SGD': + # --- 改为SGD优化器 --- + self._optimizer_world_model = torch.optim.SGD( + self._model.world_model.parameters(), + lr=self._cfg.learning_rate, # 初始学习率,在配置中设为 0.2 + momentum=self._cfg.momentum, # 在配置中设为 0.9 + weight_decay=self._cfg.weight_decay # 在配置中设为 1e-4 + ) + elif self._cfg.optim_type == 'AdamW': + # NOTE: nanoGPT optimizer + self._optimizer_world_model = configure_optimizers_nanogpt( + model=self._model.world_model, + learning_rate=self._cfg.learning_rate, + weight_decay=self._cfg.weight_decay, + device_type=self._cfg.device, + betas=(0.9, 0.95), + ) + elif self._cfg.optim_type == 'AdamW_mix_lr_wdecay': + self._optimizer_world_model = configure_optimizer_unizero( + model=self._model.world_model, + learning_rate=self._cfg.learning_rate, # 使用一个合理的AdamW基础学习率 + weight_decay=self._cfg.weight_decay, + device_type=self._cfg.device, + betas=(0.9, 0.95), + ) if self._cfg.cos_lr_scheduler: from torch.optim.lr_scheduler import CosineAnnealingLR # TODO: check the total training steps - self.lr_scheduler = CosineAnnealingLR(self._optimizer_world_model, 1e5, eta_min=0, last_epoch=-1) + # self.lr_scheduler = CosineAnnealingLR(self._optimizer_world_model, 1e5, eta_min=0, last_epoch=-1) + total_iters = self._cfg.get('total_iterations', 500000) # 500k iter + # final_lr = self._cfg.get('final_learning_rate', 0.0) + final_lr = self._cfg.get('final_learning_rate', 1e-6) + + self.lr_scheduler = CosineAnnealingLR( + self._optimizer_world_model, + T_max=total_iters, + eta_min=final_lr + ) + print(f"CosineAnnealingLR enabled: T_max={total_iters}, eta_min={final_lr}") + + + if self._cfg.piecewise_decay_lr_scheduler: + from torch.optim.lr_scheduler import LambdaLR + max_step = self._cfg.threshold_training_steps_for_final_lr + # NOTE: the 1, 0.1, 0.01 is the decay rate, not the lr. + lr_lambda = lambda step: 1 if step < max_step * 0.5 else (0.1 if step < max_step else 0.01) # noqa + self.lr_scheduler = LambdaLR(self._optimizer_world_model, lr_lambda=lr_lambda) # use model_wrapper for specialized demands of different modes self._target_model = copy.deepcopy(self._model) @@ -363,12 +578,8 @@ def _init_learn(self) -> None: self.grad_norm_before = 0. self.grad_norm_after = 0. - if self._cfg.model.model_type == 'conv': - self.pad_token_id = -1 - else: - encoder_tokenizer = getattr(self._model.tokenizer.encoder, 'tokenizer', None) - self.pad_token_id = encoder_tokenizer.pad_token_id if encoder_tokenizer is not None else 0 - + encoder_tokenizer = getattr(self._model.tokenizer.encoder, 'tokenizer', None) + self.pad_token_id = encoder_tokenizer.pad_token_id if encoder_tokenizer is not None else 0 if self._cfg.use_wandb: # TODO: add the model to wandb @@ -376,6 +587,63 @@ def _init_learn(self) -> None: self.accumulation_steps = self._cfg.accumulation_steps + # ==================== START: 目标熵正则化初始化 ==================== + # 从配置中读取是否启用自适应alpha,并提供一个默认值 + self.use_adaptive_entropy_weight = self._cfg.get('use_adaptive_entropy_weight', True) + + # 在 _init_learn 中增加配置 + self.target_entropy_start_ratio = self._cfg.get('target_entropy_start_ratio', 0.98) + self.target_entropy_end_ratio = self._cfg.get('target_entropy_end_ratio', 0.7) + self.target_entropy_decay_steps = self._cfg.get('target_entropy_decay_steps', 200000) # 例如,在200k步内完成退火 2M envsteps + + if self.use_adaptive_entropy_weight: + # 1. 设置目标熵。对于离散动作空间,一个常见的启发式设置是动作空间维度的负对数乘以一个系数。 + # 这个系数(例如0.98)可以作为一个超参数。 + action_space_size = self._cfg.model.action_space_size + self.target_entropy = -np.log(1.0 / action_space_size) * 0.98 + + # 2. 初始化一个可学习的 log_alpha 参数。 + # 初始化为0,意味着初始的 alpha = exp(0) = 1.0。 + self.log_alpha = torch.nn.Parameter(torch.zeros(1, device=self._cfg.device), requires_grad=True) + + # 3. 为 log_alpha 创建一个专属的优化器。 + # 使用与主优化器不同的、较小的学习率(例如1e-4)通常更稳定。 + alpha_lr = self._cfg.get('adaptive_entropy_alpha_lr', 1e-4) + self.alpha_optimizer = torch.optim.Adam([self.log_alpha], lr=alpha_lr) + + print("="*20) + print(">>> 目标熵正则化 (自适应Alpha) 已启用 <<<") + print(f" 目标熵 (Target Entropy): {self.target_entropy:.4f}") + print(f" Alpha 优化器学习率: {alpha_lr:.2e}") + print("="*20) + # ===================== END: 目标熵正则化初始化 ===================== + + # ==================== START: 初始化 Encoder-Clip Annealing 参数 ==================== + self.use_encoder_clip_annealing = self._cfg.get('use_encoder_clip_annealing', False) + self.latent_norm_clip_threshold = self._cfg.get('latent_norm_clip_threshold', 20.0) # TODO + if self.use_encoder_clip_annealing: + self.encoder_clip_anneal_type = self._cfg.get('encoder_clip_anneal_type', 'cosine') + self.encoder_clip_start = self._cfg.get('encoder_clip_start_value', 30.0) + self.encoder_clip_end = self._cfg.get('encoder_clip_end_value', 10.0) + self.encoder_clip_anneal_steps = self._cfg.get('encoder_clip_anneal_steps', 200000) + + print("="*20) + print(">>> Encoder-Clip 退火已启用 <<<") + print(f" 类型: {self.encoder_clip_anneal_type}") + print(f" 范围: {self.encoder_clip_start} -> {self.encoder_clip_end}") + print(f" 步数: {self.encoder_clip_anneal_steps}") + print("="*20) + else: + # 如果不启用退火,则使用固定的 clip 阈值 + self.latent_norm_clip_threshold = self._cfg.get('latent_norm_clip_threshold', 20.0) + # ===================== END: 初始化 Encoder-Clip Annealing 参数 ===================== + + # --- NEW: Policy Label Smoothing Parameters --- + self.policy_ls_eps_start = self._cfg.get('policy_ls_eps_start', 0.05) # TODO policy_label_smoothing_eps_start 越大的action space需要越大的eps + self.policy_ls_eps_end = self._cfg.get('policy_label_smoothing_eps_end ', 0.01) # TODO policy_label_smoothing_eps_start + self.policy_ls_eps_decay_steps = self._cfg.get('policy_ls_eps_decay_steps ', 50000) # TODO 50k + print(f"self.policy_ls_eps_start:{self.policy_ls_eps_start}") + # @profile def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, int]]: """ @@ -397,6 +665,13 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in obs_batch_ori, action_batch, target_action_batch, mask_batch, indices, weights, make_time, timestep_batch = current_batch target_reward, target_value, target_policy = target_batch + # --- NEW: Calculate current epsilon for policy --- + if self.policy_ls_eps_start > 0: + progress = min(1.0, train_iter / self.policy_ls_eps_decay_steps) + current_policy_label_eps = self.policy_ls_eps_start * (1 - progress) + self.policy_ls_eps_end * progress + else: + current_policy_label_eps = 0.0 + # Prepare observations based on frame stack number if self._cfg.model.frame_stack_num > 1: obs_batch, obs_target_batch = prepare_obs_stack_for_unizero(obs_batch_ori, self._cfg) @@ -425,8 +700,11 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in transformed_target_value = scalar_transform(target_value) # Convert to categorical distributions - target_reward_categorical = phi_transform(self.reward_support, transformed_target_reward) - target_value_categorical = phi_transform(self.value_support, transformed_target_value) + # target_reward_categorical = phi_transform(self.reward_support, transformed_target_reward) + # target_value_categorical = phi_transform(self.value_support, transformed_target_value) + + target_reward_categorical = phi_transform(self.reward_support, transformed_target_reward, label_smoothing_eps= self._cfg.label_smoothing_eps) + target_value_categorical = phi_transform(self.value_support, transformed_target_value, label_smoothing_eps=self._cfg.label_smoothing_eps) # Prepare batch for GPT model batch_for_gpt = {} @@ -449,6 +727,8 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in batch_for_gpt['target_value'] = target_value_categorical[:, :-1] batch_for_gpt['target_policy'] = target_policy[:, :-1] + batch_for_gpt['scalar_target_value'] = target_value + # Extract valid target policy data and compute entropy valid_target_policy = batch_for_gpt['target_policy'][batch_for_gpt['mask_padding']] target_policy_entropy = -torch.sum(valid_target_policy * torch.log(valid_target_policy + 1e-9), dim=-1) @@ -456,13 +736,83 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in # Update world model losses = self._learn_model.world_model.compute_loss( - batch_for_gpt, self._target_model.world_model.tokenizer, self.value_inverse_scalar_transform_handle + batch_for_gpt, self._target_model.world_model.tokenizer, self.value_inverse_scalar_transform_handle, global_step=train_iter, current_policy_label_eps=current_policy_label_eps, ) # NOTE : compute_loss third argument is now a dead argument. If this changes, it could need adaptation between value_inverse and reward_inverse. - weighted_total_loss = losses.loss_total + # ==================== [修改] 集成范数监控逻辑 ==================== + norm_log_dict = {} + # 检查是否达到监控频率 + if self._cfg.monitor_norm_freq > 0 and (train_iter == 0 or (train_iter % self._cfg.monitor_norm_freq == 0)): + with torch.no_grad(): + # 1. 监控模型参数范数 + param_norm_metrics = self._monitor_model_norms() + norm_log_dict.update(param_norm_metrics) + + # 2. 监控中间张量 x (Transformer的输出) + intermediate_x = losses.intermediate_losses.get('intermediate_tensor_x') + if intermediate_x is not None: + # x 的形状为 (B, T, E) + # 计算每个 token 的 L2 范数 + token_norms = intermediate_x.norm(p=2, dim=-1) + + # 记录这些范数的统计数据 + norm_log_dict['norm/x_token/mean'] = token_norms.mean().item() + norm_log_dict['norm/x_token/std'] = token_norms.std().item() + norm_log_dict['norm/x_token/max'] = token_norms.max().item() + norm_log_dict['norm/x_token/min'] = token_norms.min().item() + + # 3. 监控 logits 的详细统计 (Value, Policy, Reward) + logits_value = losses.intermediate_losses.get('logits_value') + if logits_value is not None: + norm_log_dict['logits/value/mean'] = logits_value.mean().item() + norm_log_dict['logits/value/std'] = logits_value.std().item() + norm_log_dict['logits/value/max'] = logits_value.max().item() + norm_log_dict['logits/value/min'] = logits_value.min().item() + norm_log_dict['logits/value/abs_max'] = logits_value.abs().max().item() + + logits_policy = losses.intermediate_losses.get('logits_policy') + if logits_policy is not None: + norm_log_dict['logits/policy/mean'] = logits_policy.mean().item() + norm_log_dict['logits/policy/std'] = logits_policy.std().item() + norm_log_dict['logits/policy/max'] = logits_policy.max().item() + norm_log_dict['logits/policy/min'] = logits_policy.min().item() + norm_log_dict['logits/policy/abs_max'] = logits_policy.abs().max().item() + + logits_reward = losses.intermediate_losses.get('logits_reward') + if logits_reward is not None: + norm_log_dict['logits/reward/mean'] = logits_reward.mean().item() + norm_log_dict['logits/reward/std'] = logits_reward.std().item() + norm_log_dict['logits/reward/max'] = logits_reward.max().item() + norm_log_dict['logits/reward/min'] = logits_reward.min().item() + norm_log_dict['logits/reward/abs_max'] = logits_reward.abs().max().item() + + # 4. 监控 obs_embeddings (Encoder输出) 的统计 + obs_embeddings = losses.intermediate_losses.get('obs_embeddings') + if obs_embeddings is not None: + # 计算每个 embedding 的 L2 范数 + emb_norms = obs_embeddings.norm(p=2, dim=-1) + norm_log_dict['embeddings/obs/norm_mean'] = emb_norms.mean().item() + norm_log_dict['embeddings/obs/norm_std'] = emb_norms.std().item() + norm_log_dict['embeddings/obs/norm_max'] = emb_norms.max().item() + norm_log_dict['embeddings/obs/norm_min'] = emb_norms.min().item() + # ================================================================= + + # ==================== START MODIFICATION 2 ==================== + # Extract the calculated value_priority from the returned losses. + value_priority_tensor = losses.intermediate_losses['value_priority'] + # Convert to numpy array for the replay buffer, adding a small epsilon. + value_priority_np = value_priority_tensor.detach().cpu().numpy() + 1e-6 + # ===================== END MODIFICATION 2 ===================== + + # weighted_total_loss = losses.loss_total + # TODO: + weighted_total_loss = (weights * losses.loss_total).mean() + for loss_name, loss_value in losses.intermediate_losses.items(): self.intermediate_losses[f"{loss_name}"] = loss_value + # 从 losses 对象中提取策略熵 + obs_loss = self.intermediate_losses['loss_obs'] reward_loss = self.intermediate_losses['loss_rewards'] policy_loss = self.intermediate_losses['loss_policy'] @@ -475,9 +825,26 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in middle_step_losses = self.intermediate_losses['middle_step_losses'] last_step_losses = self.intermediate_losses['last_step_losses'] dormant_ratio_encoder = self.intermediate_losses['dormant_ratio_encoder'] - dormant_ratio_world_model = self.intermediate_losses['dormant_ratio_world_model'] + dormant_ratio_transformer = self.intermediate_losses['dormant_ratio_transformer'] + dormant_ratio_head = self.intermediate_losses['dormant_ratio_head'] + avg_weight_mag_encoder = self.intermediate_losses['avg_weight_mag_encoder'] + avg_weight_mag_transformer = self.intermediate_losses['avg_weight_mag_transformer'] + avg_weight_mag_head = self.intermediate_losses['avg_weight_mag_head'] + e_rank_last_linear = self.intermediate_losses['e_rank_last_linear'] + e_rank_sim_norm = self.intermediate_losses['e_rank_sim_norm'] latent_state_l2_norms = self.intermediate_losses['latent_state_l2_norms'] + latent_action_l2_norms = self.intermediate_losses['latent_action_l2_norms'] + logits_value_mean=self.intermediate_losses['logits_value_mean'] + logits_value_max=self.intermediate_losses['logits_value_max'] + logits_value_min=self.intermediate_losses['logits_value_min'] + logits_policy_mean=self.intermediate_losses['logits_policy_mean'] + logits_policy_max=self.intermediate_losses['logits_policy_max'] + logits_policy_min=self.intermediate_losses['logits_policy_min'] + temperature_value=self.intermediate_losses['temperature_value'] + temperature_reward=self.intermediate_losses['temperature_reward'] + temperature_policy=self.intermediate_losses['temperature_policy'] + assert not torch.isnan(losses.loss_total).any(), "Loss contains NaN values" assert not torch.isinf(losses.loss_total).any(), "Loss contains Inf values" @@ -486,19 +853,107 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in if (train_iter % self.accumulation_steps) == 0: self._optimizer_world_model.zero_grad() + + # ==================== START: 目标熵正则化更新逻辑 ==================== + alpha_loss = None + current_alpha = self._cfg.model.world_model_cfg.policy_entropy_weight # 默认使用固定值 + if self.use_adaptive_entropy_weight: + # --- 动态计算目标熵 (这部分逻辑是正确的,予以保留) --- + progress = min(1.0, train_iter / self.target_entropy_decay_steps) + current_ratio = self.target_entropy_start_ratio * (1 - progress) + self.target_entropy_end_ratio * progress + action_space_size = self._cfg.model.action_space_size + # 注意:我们将 target_entropy 定义为正数,更符合直觉 + current_target_entropy = -np.log(1.0 / action_space_size) * current_ratio + + # --- 计算 alpha_loss (已修正符号) --- + # 这是核心修正点:去掉了最前面的负号 + # detach() 仍然是关键,确保 alpha_loss 的梯度只流向 log_alpha + alpha_loss = (self.log_alpha * (policy_entropy.detach() - current_target_entropy)).mean() + + # # --- 更新 log_alpha --- + self.alpha_optimizer.zero_grad() + alpha_loss.backward() + self.alpha_optimizer.step() + # --- [优化建议] 增加 log_alpha 裁剪作为安全措施 --- + with torch.no_grad(): + # 将 alpha 限制在例如 [1e-4, 10.0] 的范围内 + self.log_alpha.clamp_(np.log(1e-4), np.log(10.0)) + + # --- 使用当前更新后的 alpha (截断梯度流) --- + current_alpha = self.log_alpha.exp().detach() + + # 重新计算加权的策略损失和总损失 + # 注意:这里的 policy_entropy 已经是一个batch的平均值 + weighted_policy_loss = orig_policy_loss - current_alpha * policy_entropy + # 重新构建总损失 (不使用 losses.loss_total) + # 确保这里的权重与 LossWithIntermediateLosses 类中的计算方式一致 + self.obs_loss_weight = 10 + self.value_loss_weight = 0.5 + self.reward_loss_weight = 1. + self.policy_loss_weight = 1. + self.ends_loss_weight = 0. + total_loss = ( + self.reward_loss_weight * reward_loss + + self.value_loss_weight * value_loss + + self.policy_loss_weight * weighted_policy_loss + + self.obs_loss_weight * obs_loss # 假设 ssl_loss_weight 是 obs_loss 的权重 + # ... 如果还有其他损失项,也加进来 ... + ) + weighted_total_loss = (weights * total_loss).mean() + # ===================== END: 目标熵正则化更新逻辑 ===================== + # Scale the loss by the number of accumulation steps weighted_total_loss = weighted_total_loss / self.accumulation_steps weighted_total_loss.backward() + # ----------------------------------------------------------------- + # 仍然在 torch.no_grad() 环境下执行 + # ================================================================= + with torch.no_grad(): + # 1. Encoder-Clip + # ==================== START: 动态计算当前 Clip 阈值 ==================== + current_clip_value = self.latent_norm_clip_threshold # 默认使用固定值 + if self.use_encoder_clip_annealing: + progress = min(1.0, train_iter / self.encoder_clip_anneal_steps) + + if self.encoder_clip_anneal_type == 'cosine': + # 余弦调度: 从1平滑过渡到0 + cosine_progress = 0.5 * (1.0 + np.cos(np.pi * progress)) + current_clip_value = self.encoder_clip_end + \ + (self.encoder_clip_start - self.encoder_clip_end) * cosine_progress + else: # 默认为线性调度 + current_clip_value = self.encoder_clip_start * (1 - progress) + \ + self.encoder_clip_end * progress + # ===================== END: 动态计算当前 Clip 阈值 ===================== + + # 1. Encoder-Clip (使用动态计算出的 current_clip_value) + if current_clip_value > 0 and 'obs_embeddings' in losses.intermediate_losses: + obs_embeddings = losses.intermediate_losses['obs_embeddings'] + if obs_embeddings is not None: + max_latent_norm = obs_embeddings.norm(p=2, dim=-1).max() + if max_latent_norm > current_clip_value: + scale_factor = current_clip_value / max_latent_norm.item() + # 不再频繁打印,或者可以改为每隔N步打印一次 + if train_iter % 1000 == 0: + print(f"[Encoder-Clip Annealing] Iter {train_iter}: Max latent norm {max_latent_norm.item():.2f} > {current_clip_value:.2f}. Scaling by {scale_factor:.4f}.") + scale_module_weights_vectorized(self._model.world_model.tokenizer.encoder, scale_factor) + # Check if the current iteration completes an accumulation cycle if (train_iter + 1) % self.accumulation_steps == 0: + # ==================== [新增] 监控梯度范数 ==================== + # 在梯度裁剪之前监控梯度范数,用于诊断梯度爆炸/消失问题 + if self._cfg.monitor_norm_freq > 0 and (train_iter == 0 or (train_iter % self._cfg.monitor_norm_freq == 0)): + grad_norm_metrics = self._monitor_gradient_norms() + norm_log_dict.update(grad_norm_metrics) + # ================================================================= + # Analyze gradient norms if simulation normalization analysis is enabled if self._cfg.analysis_sim_norm: # Clear previous analysis results to prevent memory overflow del self.l2_norm_before, self.l2_norm_after, self.grad_norm_before, self.grad_norm_after self.l2_norm_before, self.l2_norm_after, self.grad_norm_before, self.grad_norm_after = self._learn_model.encoder_hook.analyze() self._target_model.encoder_hook.clear_data() - + # Clip gradients to prevent exploding gradients total_grad_norm_before_clip_wm = torch.nn.utils.clip_grad_norm_( self._learn_model.world_model.parameters(), self._cfg.grad_clip_value @@ -565,21 +1020,61 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in 'target_policy_entropy': average_target_policy_entropy.item(), 'reward_loss': reward_loss.item(), 'value_loss': value_loss.item(), - # 'value_priority_orig': np.zeros(self._cfg.batch_size), # TODO + # Add value_priority to the log dictionary. + 'value_priority': value_priority_np.mean().item(), + 'value_priority_orig': value_priority_np, 'target_reward': target_reward.mean().item(), 'target_value': target_value.mean().item(), 'transformed_target_reward': transformed_target_reward.mean().item(), 'transformed_target_value': transformed_target_value.mean().item(), 'total_grad_norm_before_clip_wm': total_grad_norm_before_clip_wm.item(), - 'analysis/dormant_ratio_encoder': dormant_ratio_encoder.item(), - 'analysis/dormant_ratio_world_model': dormant_ratio_world_model.item(), + 'analysis/dormant_ratio_encoder': dormant_ratio_encoder, + 'analysis/dormant_ratio_transformer': dormant_ratio_transformer, + 'analysis/dormant_ratio_head': dormant_ratio_head, + + 'analysis/avg_weight_mag_encoder': avg_weight_mag_encoder, + 'analysis/avg_weight_mag_transformer': avg_weight_mag_transformer, + 'analysis/avg_weight_mag_head': avg_weight_mag_head, + 'analysis/e_rank_last_linear': e_rank_last_linear, + 'analysis/e_rank_sim_norm': e_rank_sim_norm, + 'analysis/latent_state_l2_norms': latent_state_l2_norms.item(), + 'analysis/latent_action_l2_norms': latent_action_l2_norms, 'analysis/l2_norm_before': self.l2_norm_before, 'analysis/l2_norm_after': self.l2_norm_after, 'analysis/grad_norm_before': self.grad_norm_before, 'analysis/grad_norm_after': self.grad_norm_after, + "logits_value_mean":logits_value_mean, + "logits_value_max":logits_value_max, + "logits_value_min":logits_value_min, + "logits_policy_mean":logits_policy_mean, + "logits_policy_max":logits_policy_max, + "logits_policy_min":logits_policy_min, + + "temperature_value":temperature_value, + "temperature_reward":temperature_reward, + "temperature_policy":temperature_policy, + + "current_policy_label_eps":current_policy_label_eps, } - + + # ==================== [修改] 将范数监控结果合并到日志中 ==================== + if norm_log_dict: + return_log_dict.update(norm_log_dict) + # ======================================================================= + + # ==================== START: 添加新日志项 ==================== + if self.use_adaptive_entropy_weight: + return_log_dict['adaptive_alpha'] = current_alpha.item() + return_log_dict['adaptive_target_entropy_ratio'] = current_ratio + return_log_dict['alpha_loss'] = alpha_loss.item() + # ==================== START: 添加新日志项 ==================== + + # ==================== START: 添加新日志项 ==================== + if self.use_encoder_clip_annealing: + return_log_dict['current_encoder_clip_value'] = current_clip_value + # ===================== END: 添加新日志项 ===================== + if self._cfg.use_wandb: wandb.log({'learner_step/' + k: v for k, v in return_log_dict.items()}, step=self.env_step) wandb.log({"learner_iter_vs_env_step": self.train_iter}, step=self.env_step) @@ -601,11 +1096,13 @@ def _init_collect(self) -> None: Collect mode init method. Called by ``self.__init__``. Initialize the collect model and MCTS utils. """ self._collect_model = self._model - + # 为 collect MCTS 创建一个配置副本,并设置特定的模拟次数 + mcts_collect_cfg = copy.deepcopy(self._cfg) + mcts_collect_cfg.num_simulations = self._cfg.collect_num_simulations if self._cfg.mcts_ctree: - self._mcts_collect = MCTSCtree(self._cfg) + self._mcts_collect = MCTSCtree(mcts_collect_cfg) else: - self._mcts_collect = MCTSPtree(self._cfg) + self._mcts_collect = MCTSPtree(mcts_collect_cfg) self._collect_mcts_temperature = 1. self._collect_epsilon = 0.0 self.collector_env_num = self._cfg.collector_env_num @@ -626,8 +1123,9 @@ def _forward_collect( temperature: float = 1, to_play: List = [-1], epsilon: float = 0.25, - ready_env_id: np.ndarray = None, - timestep: List = [0] + ready_env_id: np.array = None, + timestep: List = [0], + task_id: int = None, ) -> Dict: """ Overview: @@ -640,6 +1138,7 @@ def _forward_collect( - to_play (:obj:`int`): The player to play. - ready_env_id (:obj:`list`): The id of the env that is ready to collect. - timestep (:obj:`list`): The step index of the env in one episode. + - task_id (:obj:`int`): The task id. Default is None, which means UniZero is in the single-task mode. Shape: - data (:obj:`torch.Tensor`): - For Atari, :math:`(N, C*S, H, W)`, where N is the number of collect_env, C is the number of channels, \ @@ -745,13 +1244,25 @@ def _forward_collect( self.last_batch_obs = data self.last_batch_action = batch_action - # ========= TODO: for muzero_segment_collector now ========= + # ========= TODO: This logic is a temporary workaround specific to the muzero_segment_collector. ========= if active_collect_env_num < self.collector_env_num: - print('==========collect_forward============') - print(f'len(self.last_batch_obs) < self.collector_env_num, {active_collect_env_num}<{self.collector_env_num}') + # When an environment finishes an episode ('done'), the length of `self.last_batch_obs` passed back + # becomes smaller than the total number of collector environments. + # Handling this dynamic batch size is complex, as the transformer's KV cache retrieval + # requires a stable environment ID for correct indexing. A mismatch would cause retrieval errors. + # + # Therefore, as a simpler solution, we reset the collection state for ALL environments. + # By resetting `self.last_batch_action` to -1 for all `self.collector_env_num` environments, + # we force the transformer to start its context from scratch, avoiding incorrect cache lookups. + print('========== collect_forward ============') + print(f'An environment has finished. Active envs: {active_collect_env_num} < Total envs: {self.collector_env_num}. Resetting all.') + self._reset_collect(reset_init_data=True) + + # If the sampling type is 'episode', it's unexpected for the number of active environments to drop, + # as this suggests an inconsistent state or a potential issue in the collection logic. if getattr(self._cfg, 'sample_type', '') == 'episode': - print('BUG: sample_type is episode, but len(self.last_batch_obs) < self.collector_env_num') + print('WARNING: Inconsistent state detected. `sample_type` is "episode", but the number of active environments has changed.') return output @@ -761,10 +1272,16 @@ def _init_eval(self) -> None: Evaluate mode init method. Called by ``self.__init__``. Initialize the eval model and MCTS utils. """ self._eval_model = self._model + + # 为 eval MCTS 创建一个配置副本,并设置特定的模拟次数 + mcts_eval_cfg = copy.deepcopy(self._cfg) + mcts_eval_cfg.num_simulations = self._cfg.eval_num_simulations + if self._cfg.mcts_ctree: - self._mcts_eval = MCTSCtree(self._cfg) + self._mcts_eval = MCTSCtree(mcts_eval_cfg) else: - self._mcts_eval = MCTSPtree(self._cfg) + self._mcts_eval = MCTSPtree(mcts_eval_cfg) + self.evaluator_env_num = self._cfg.evaluator_env_num if self._cfg.model.model_type == 'conv': @@ -776,8 +1293,8 @@ def _init_eval(self) -> None: ).to(self._cfg.device) self.last_batch_action = [-1 for i in range(self.collector_env_num)] - def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: List = [-1], - ready_env_id: np.array = None, timestep: List = [0]) -> Dict: + def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: int = -1, + ready_env_id: np.array = None, timestep: List = [0], task_id: int = None,) -> Dict: """ Overview: The forward function for evaluating the current policy in eval mode. Use model to execute MCTS search. @@ -788,6 +1305,7 @@ def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: List = [ - to_play (:obj:`int`): The player to play. - ready_env_id (:obj:`list`): The id of the env that is ready to eval. - timestep (:obj:`list`): The step index of the env in one episode. + - task_id (:obj:`int`): The task id. Default is None, which means UniZero is in the single-task mode. Shape: - data (:obj:`torch.Tensor`): - For Atari, :math:`(N, C*S, H, W)`, where N is the number of eval_env, C is the number of channels, \ @@ -808,7 +1326,7 @@ def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: List = [ ready_env_id = np.arange(active_eval_env_num) output = {i: None for i in ready_env_id} with torch.no_grad(): - network_output = self._eval_model.initial_inference(self.last_batch_obs, self.last_batch_action, data, timestep) + network_output = self._eval_model.initial_inference(self.last_batch_obs_eval, self.last_batch_action, data, timestep) latent_state_roots, reward_roots, pred_values, policy_logits = mz_network_output_unpack(network_output) # if not in training, obtain the scalars of the value/reward @@ -868,12 +1386,12 @@ def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: List = [ } batch_action.append(action) - self.last_batch_obs = data + self.last_batch_obs_eval = data self.last_batch_action = batch_action return output - def _reset_collect(self, env_id: int = None, current_steps: int = None, reset_init_data: bool = True) -> None: + def _reset_collect(self, env_id: int = None, current_steps: int = None, reset_init_data: bool = True, task_id: int = None) -> None: """ Overview: This method resets the collection process for a specific environment. It clears caches and memory @@ -894,15 +1412,31 @@ def _reset_collect(self, env_id: int = None, current_steps: int = None, reset_in ) self.last_batch_action = [-1 for _ in range(self._cfg.collector_env_num)] - # Return immediately if env_id is None or a list - if env_id is None or isinstance(env_id, list): - return + # We must handle both single int and list of ints for env_id. + if env_id is not None: + if isinstance(env_id, int): + env_ids_to_reset = [env_id] + else: # Assumes it's a list + env_ids_to_reset = env_id + + # The key condition: `current_steps` is None only on the end-of-episode reset call from the collector. + if current_steps is None: + world_model = self._collect_model.world_model + for eid in env_ids_to_reset: + # Clear the specific environment's initial inference cache. + if eid < len(world_model.past_kv_cache_init_infer_envs): + world_model.past_kv_cache_init_infer_envs[eid].clear() + + print(f'>>> [Collector] Cleared KV cache for env_id: {eid} at episode end.') + + # ======== TODO: 20251015 ======== # Determine the clear interval based on the environment's sample type - clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else 200 + # clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else 200 + clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else self._cfg.game_segment_length # Clear caches if the current steps are a multiple of the clear interval - if current_steps % clear_interval == 0: + if current_steps is not None and current_steps % clear_interval == 0: print(f'clear_interval: {clear_interval}') # Clear various caches in the collect model's world model @@ -915,10 +1449,9 @@ def _reset_collect(self, env_id: int = None, current_steps: int = None, reset_in # Free up GPU memory torch.cuda.empty_cache() - print('collector: collect_model clear()') - print(f'eps_steps_lst[{env_id}]: {current_steps}') + print(f'eps_steps_lst[{env_id}]: {current_steps}, collector: collect_model clear()') - def _reset_eval(self, env_id: int = None, current_steps: int = None, reset_init_data: bool = True) -> None: + def _reset_eval(self, env_id: int = None, current_steps: int = None, reset_init_data: bool = True, task_id: int = None) -> None: """ Overview: This method resets the evaluation process for a specific environment. It clears caches and memory @@ -931,23 +1464,61 @@ def _reset_eval(self, env_id: int = None, current_steps: int = None, reset_init_ - reset_init_data (:obj:`bool`, optional): Whether to reset the initial data. If True, the initial data will be reset. """ if reset_init_data: - self.last_batch_obs = initialize_pad_batch( - self._cfg.model.observation_shape, - self._cfg.evaluator_env_num, - self._cfg.device, - pad_token_id=self.pad_token_id - ) + if task_id is not None: + self.last_batch_obs_eval = initialize_zeros_batch( + self._cfg.model.observation_shape_list[task_id], + self._cfg.evaluator_env_num, + self._cfg.device, + pad_token_id=self.pad_token_id + ) + print(f'unizero.py task_id:{task_id} after _reset_eval: last_batch_obs_eval:', self.last_batch_obs_eval.shape) + + else: + self.last_batch_obs_eval = initialize_pad_batch( # TODO + self._cfg.model.observation_shape, + self._cfg.evaluator_env_num, + self._cfg.device, + pad_token_id=self.pad_token_id + ) + print(f'unizero.py task_id:{task_id} after _reset_eval: last_batch_obs_eval:', self.last_batch_obs_eval.shape) + self.last_batch_action = [-1 for _ in range(self._cfg.evaluator_env_num)] - # Return immediately if env_id is None or a list - if env_id is None or isinstance(env_id, list): - return + # --- BEGIN ROBUST FIX --- + # This logic handles the crucial end-of-episode cache clearing for evaluation. + # The evaluator calls `_policy.reset([env_id])` when an episode is done. + if env_id is not None: + if isinstance(env_id, int): + env_ids_to_reset = [env_id] + else: # Assumes it's a list + env_ids_to_reset = env_id - # Determine the clear interval based on the environment's sample type - clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else 200 + # The key condition: `current_steps` is None only on the end-of-episode reset call from the evaluator. + if current_steps is None: + world_model = self._eval_model.world_model + for eid in env_ids_to_reset: + # Clear the specific environment's initial inference cache. + if eid < len(world_model.past_kv_cache_init_infer_envs): + world_model.past_kv_cache_init_infer_envs[eid].clear() + + print(f'>>> [Evaluator] Cleared KV cache for env_id: {eid} at episode end.') + + # The recurrent cache is global. + world_model.past_kv_cache_recurrent_infer.clear() + + if hasattr(world_model, 'keys_values_wm_list'): + world_model.keys_values_wm_list.clear() + torch.cuda.empty_cache() + return + # --- END ROBUST FIX --- + + # ======== TODO: 20251015 ======== + # Determine the clear interval based on the environment's sample type + # clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else 200 + clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else self._cfg.game_segment_length # Clear caches if the current steps are a multiple of the clear interval - if current_steps % clear_interval == 0: + if current_steps is not None and current_steps % clear_interval == 0: print(f'clear_interval: {clear_interval}') # Clear various caches in the eval model's world model @@ -969,56 +1540,142 @@ def _monitor_vars_learn(self) -> List[str]: Register the variables to be monitored in learn mode. The registered variables will be logged in tensorboard according to the return value ``_forward_learn``. """ - return [ + base_vars = [ + # ==================== Analysis Metrics ==================== 'analysis/dormant_ratio_encoder', - 'analysis/dormant_ratio_world_model', + 'analysis/dormant_ratio_transformer', + 'analysis/dormant_ratio_head', + 'analysis/avg_weight_mag_encoder', + 'analysis/avg_weight_mag_transformer', + 'analysis/avg_weight_mag_head', + 'analysis/e_rank_last_linear', + 'analysis/e_rank_sim_norm', 'analysis/latent_state_l2_norms', + 'analysis/latent_action_l2_norms', 'analysis/l2_norm_before', 'analysis/l2_norm_after', 'analysis/grad_norm_before', 'analysis/grad_norm_after', + # ==================== Step-wise Loss Analysis ==================== 'analysis/first_step_loss_value', 'analysis/first_step_loss_policy', 'analysis/first_step_loss_rewards', 'analysis/first_step_loss_obs', - 'analysis/middle_step_loss_value', 'analysis/middle_step_loss_policy', 'analysis/middle_step_loss_rewards', 'analysis/middle_step_loss_obs', - 'analysis/last_step_loss_value', 'analysis/last_step_loss_policy', 'analysis/last_step_loss_rewards', 'analysis/last_step_loss_obs', + # ==================== System Metrics ==================== 'Current_GPU', 'Max_GPU', 'collect_epsilon', 'collect_mcts_temperature', 'cur_lr_world_model', - 'cur_lr_tokenizer', + # ==================== Core Losses ==================== 'weighted_total_loss', 'obs_loss', 'policy_loss', 'orig_policy_loss', 'policy_entropy', 'latent_recon_loss', + 'perceptual_loss', 'target_policy_entropy', 'reward_loss', 'value_loss', - 'consistency_loss', 'value_priority', 'target_reward', 'target_value', + 'transformed_target_reward', + 'transformed_target_value', + + # ==================== Gradient Norms ==================== 'total_grad_norm_before_clip_wm', - # tokenizer - 'commitment_loss', - 'reconstruction_loss', - 'perceptual_loss', + + # ==================== Logits Statistics ==================== + 'logits_value_mean', + 'logits_value_max', + 'logits_value_min', + 'logits_policy_mean', + 'logits_policy_max', + 'logits_policy_min', + + # ==================== Temperature Parameters ==================== + 'temperature_value', + 'temperature_reward', + 'temperature_policy', + + # ==================== Training Configuration ==================== + 'current_policy_label_eps', + 'adaptive_alpha', + 'adaptive_target_entropy_ratio', + 'alpha_loss', + 'current_encoder_clip_value', + ] + + # ==================== [新增] 范数和中间张量监控变量 ==================== + norm_vars = [ + # 模块总范数 (参数范数) + 'norm/encoder/_total_norm', + 'norm/transformer/_total_norm', + 'norm/head_value/_total_norm', + 'norm/head_reward/_total_norm', + 'norm/head_policy/_total_norm', + + # 模块总范数 (梯度范数) + 'grad/encoder/_total_norm', + 'grad/transformer/_total_norm', + 'grad/head_value/_total_norm', + 'grad/head_reward/_total_norm', + 'grad/head_policy/_total_norm', + + # 中间张量 x (Transformer输出) 的统计信息 + 'norm/x_token/mean', + 'norm/x_token/std', + 'norm/x_token/max', + 'norm/x_token/min', + + # Logits 的详细统计 (Value) + 'logits/value/mean', + 'logits/value/std', + 'logits/value/max', + 'logits/value/min', + 'logits/value/abs_max', + + # Logits 的详细统计 (Policy) + 'logits/policy/mean', + 'logits/policy/std', + 'logits/policy/max', + 'logits/policy/min', + 'logits/policy/abs_max', + + # Logits 的详细统计 (Reward) + 'logits/reward/mean', + 'logits/reward/std', + 'logits/reward/max', + 'logits/reward/min', + 'logits/reward/abs_max', + + # Embeddings 的统计信息 + 'embeddings/obs/norm_mean', + 'embeddings/obs/norm_std', + 'embeddings/obs/norm_max', + 'embeddings/obs/norm_min', ] + # 注意:我们不把每一层的范数都加到这里,因为数量太多会导致日志混乱。 + # 在实践中,如果通过总范数发现问题,可以临时在TensorBoard中搜索特定层的范数, + # 或者在本地打印 `norm_log_dict` 来进行详细分析。 + # wandb等工具可以更好地处理大量的动态指标。 + # ======================================================================== + + return base_vars + norm_vars + def _state_dict_learn(self) -> Dict[str, Any]: """ @@ -1027,11 +1684,16 @@ def _state_dict_learn(self) -> Dict[str, Any]: Returns: - state_dict (:obj:`Dict[str, Any]`): The dict of current policy learn state, for saving and restoring. """ - return { + state_dict = { 'model': self._learn_model.state_dict(), 'target_model': self._target_model.state_dict(), 'optimizer_world_model': self._optimizer_world_model.state_dict(), } + # ==================== START: 保存Alpha优化器状态 ==================== + if self.use_adaptive_entropy_weight: + state_dict['alpha_optimizer'] = self.alpha_optimizer.state_dict() + # ===================== END: 保存Alpha优化器状态 ===================== + return state_dict def _load_state_dict_learn(self, state_dict: Dict[str, Any]) -> None: """ @@ -1042,7 +1704,12 @@ def _load_state_dict_learn(self, state_dict: Dict[str, Any]) -> None: """ self._learn_model.load_state_dict(state_dict['model']) self._target_model.load_state_dict(state_dict['target_model']) - self._optimizer_world_model.load_state_dict(state_dict['optimizer_world_model']) + # self._optimizer_world_model.load_state_dict(state_dict['optimizer_world_model']) + + # ==================== START: 加载Alpha优化器状态 ==================== + # if self.use_adaptive_entropy_weight and 'alpha_optimizer' in state_dict: + # self.alpha_optimizer.load_state_dict(state_dict['alpha_optimizer']) + # ===================== END: 加载Alpha优化器状态 ===================== def recompute_pos_emb_diff_and_clear_cache(self) -> None: """ diff --git a/setup_ctree_muzero_v2.py b/setup_ctree_muzero_v2.py new file mode 100644 index 000000000..3b6ccab1e --- /dev/null +++ b/setup_ctree_muzero_v2.py @@ -0,0 +1,80 @@ +""" +Setup script for building ctree_muzero_v2 Cython extension only. +This is a minimal setup focused on compiling the mz_tree.pyx module. +""" +import os +import sys +from distutils.core import setup + +import numpy as np +from Cython.Build import cythonize +from setuptools import Extension +from distutils.sysconfig import get_python_inc + +# Get the directory of this script +here = os.path.abspath(os.path.dirname(__file__)) + +# Configure Python and include directories +python_version = f"{sys.version_info.major}.{sys.version_info.minor}" +python_include_dir = get_python_inc() +include_dirs = [np.get_include(), python_include_dir] + +# Add macOS homebrew path if available +if sys.platform == 'darwin': + homebrew_python_path = f'/usr/local/opt/python@{python_version}/Frameworks/Python.framework/Versions/{python_version}/include/python{python_version}' + if os.path.exists(homebrew_python_path): + include_dirs.append(homebrew_python_path) + +print(f"Python version: {python_version}") +print(f"Include directories: {include_dirs}") + +# Set C++11 compile parameters according to the operating system +if sys.platform == 'win32': + # Use the VS compiler on Windows platform + extra_compile_args = ["/std:c++11"] + extra_link_args = ["/std:c++11"] +else: + # Linux/macOS Platform + extra_compile_args = ["-std=c++11"] + extra_link_args = ["-std=c++11"] + +# Path to ctree_muzero_v2 directory +ctree_muzero_v2_path = os.path.join(here, 'lzero', 'mcts', 'ctree', 'ctree_muzero_v2') + +# Create extension for mz_tree +mz_tree_pyx = os.path.join(ctree_muzero_v2_path, 'mz_tree.pyx') + +if not os.path.exists(mz_tree_pyx): + raise FileNotFoundError(f"Cannot find {mz_tree_pyx}") + +# Build the extension module name: lzero.mcts.ctree.ctree_muzero_v2.mz_tree +ext_module = Extension( + 'lzero.mcts.ctree.ctree_muzero_v2.mz_tree', + [mz_tree_pyx], + include_dirs=include_dirs, + language="c++", + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, +) + +# Check if LINETRACE environment variable is set +_LINETRACE = bool(os.environ.get('LINETRACE', None)) + +setup( + name='lzero-ctree-muzero-v2', + version='0.2.0', + description='Cython extension for MuZero v2 MCTS tree.', + author='opendilab', + author_email='opendilab@pjlab.org.cn', + url='https://github.com/opendilab/LightZero', + license='Apache License, Version 2.0', + py_modules=['lzero.mcts.ctree.ctree_muzero_v2'], + python_requires=">=3.7", + ext_modules=cythonize( + [ext_module], + language_level=3, + compiler_directives=dict( + linetrace=_LINETRACE, + ), + ), +) From 75bdff35c451081c93129d8642cab6a3ab4f5a4c Mon Sep 17 00:00:00 2001 From: tAnGjIa520 <1157507000@qq.com> Date: Tue, 2 Dec 2025 12:10:50 +0800 Subject: [PATCH 2/4] add Unizero MCTS v2 --- lzero/policy/unizero.py | 841 +++++----------------------------------- 1 file changed, 91 insertions(+), 750 deletions(-) diff --git a/lzero/policy/unizero.py b/lzero/policy/unizero.py index bb6ffb05f..251afc84e 100644 --- a/lzero/policy/unizero.py +++ b/lzero/policy/unizero.py @@ -9,9 +9,7 @@ from ding.utils import POLICY_REGISTRY from lzero.entry.utils import initialize_zeros_batch, initialize_pad_batch - from lzero.mcts import UniZeroMCTSCtree_v2 as MCTSCtree - from lzero.model import ImageTransforms from lzero.policy import scalar_transform, InverseScalarTransform, phi_transform, \ DiscreteSupport, to_torch_float_tensor, mz_network_output_unpack, select_action, prepare_obs, \ @@ -19,76 +17,7 @@ from lzero.policy.muzero import MuZeroPolicy from .utils import configure_optimizers_nanogpt -from torch.nn.utils.convert_parameters import parameters_to_vector, vector_to_parameters -import torch.nn.functional as F - -def scale_module_weights_vectorized(module: torch.nn.Module, scale_factor: float): - """ - 使用向量化操作高效地缩放一个模块的所有权重。 - """ - if not (0.0 < scale_factor < 1.0): - return # 如果缩放因子无效,则不执行任何操作 - - # 1. 将模块的所有参数展平成一个单一向量 - params_vec = parameters_to_vector(module.parameters()) - - # 2. 在这个向量上执行一次乘法操作 - params_vec.data.mul_(scale_factor) - - # 3. 将缩放后的向量复制回模块的各个参数 - vector_to_parameters(params_vec, module.parameters()) - - -def configure_optimizer_unizero(model, learning_rate, weight_decay, device_type, betas): - """ - 为UniZero模型配置带有差异化学习率的优化器。 - """ - # 1. 定义需要特殊处理的参数 - param_dict = {pn: p for pn, p in model.named_parameters() if p.requires_grad} - - # 2. 将参数分为三组:Transformer主干、Tokenizer、Heads - transformer_params = {pn: p for pn, p in param_dict.items() if 'transformer' in pn} - tokenizer_params = {pn: p for pn, p in param_dict.items() if 'tokenizer' in pn} - - # Heads的参数是那些既不属于transformer也不属于tokenizer的 - head_params = { - pn: p for pn, p in param_dict.items() - if 'transformer' not in pn and 'tokenizer' not in pn - } - - # 3. 为每组设置不同的优化器参数(特别是学习率) - # 这里我们仍然使用AdamW,但学习率设置更合理 - optim_groups = [ - { - 'params': list(tokenizer_params.values()), - 'lr': learning_rate, # Tokenizer使用基础学习率,例如 1e-4 - # 'lr': learning_rate * 0.1, # 为encoder设置一个较小的学习率,例如 1e-5 - 'weight_decay': weight_decay * 5.0 # <-- 为Encoder设置5倍的权重衰减!这是一个强力正则化 - # 'weight_decay': weight_decay # <-- 为Encoder设置5倍的权重衰减!这是一个强力正则化 - }, - { - 'params': list(transformer_params.values()), - 'lr': learning_rate, # 1e-4 - # 'lr': learning_rate * 0.2, # 为Transformer主干设置一个较小的学习率,例如 1e-5 - 'weight_decay': weight_decay - # 'weight_decay': weight_decay * 5.0 - }, - { - 'params': list(head_params.values()), - 'lr': learning_rate, # Heads也使用基础学习率率,例如 1e-4 - 'weight_decay': 0.0 # 通常Heads的权重不做衰减 - # 'weight_decay': weight_decay - - } - ] - - print("--- Optimizer Groups ---") - print(f"Transformer LR: {learning_rate}") - print(f"Tokenizer/Heads LR: {learning_rate}") - optimizer = torch.optim.AdamW(optim_groups, betas=betas) - return optimizer - @POLICY_REGISTRY.register('unizero') class UniZeroPolicy(MuZeroPolicy): """ @@ -152,8 +81,8 @@ class UniZeroPolicy(MuZeroPolicy): device='cpu', # (bool) Whether to analyze simulation normalization. analysis_sim_norm=False, - # (bool) Whether to analyze dormant ratio, average_weight_magnitude of net, effective_rank of latent. - analysis_dormant_ratio_weight_rank=False, + # (bool) Whether to analyze dormant ratio. + analysis_dormant_ratio=False, # (int) The shape of the action space. action_space_size=6, # (int) The size of the group, related to simulation normalization. @@ -210,7 +139,6 @@ class UniZeroPolicy(MuZeroPolicy): rope_theta=10000, # (int) The maximum sequence length for position encoding. max_seq_len=8192, - lora_r= 0, # Controls where to compute reconstruction loss: 'after_backbone', 'before_backbone', or None. # - after_backbone: The reconstruction loss is computed after the encoded representation passes through the backbone. # - before_backbone: The reconstruction loss is computed directly on the encoded representation, without the backbone. @@ -218,23 +146,6 @@ class UniZeroPolicy(MuZeroPolicy): ), ), # ****** common ****** - # (bool) 是否启用自适应策略熵权重 (alpha) - use_adaptive_entropy_weight=True, - # (float) 自适应alpha优化器的学习率 - adaptive_entropy_alpha_lr=1e-4, - # ==================== START: Encoder-Clip Annealing Config ==================== - # (bool) 是否启用 encoder-clip 值的退火。 - use_encoder_clip_annealing=True, - # (str) 退火类型。可选 'linear' 或 'cosine'。 - encoder_clip_anneal_type='cosine', - # (float) 退火的起始 clip 值 (训练初期,较宽松)。 - encoder_clip_start_value=30.0, - # (float) 退火的结束 clip 值 (训练后期,较严格)。 - encoder_clip_end_value=10.0, - # (int) 完成从起始值到结束值的退火所需的训练迭代步数。 - encoder_clip_anneal_steps=100000, # 例如,在200k次迭代后达到最终值 - # ===================== END: Encoder-Clip Annealing Config ===================== - # (bool) whether to use rnd model. use_rnd_model=False, # (bool) Whether to use multi-gpu training. @@ -267,7 +178,7 @@ class UniZeroPolicy(MuZeroPolicy): # (bool) Whether to use the pure policy to collect data. collect_with_pure_policy=False, # (int) The evaluation frequency. - eval_freq=int(5e3), + eval_freq=int(2e3), # (str) The sample type. Options are ['episode', 'transition']. sample_type='transition', # ****** observation ****** @@ -300,10 +211,6 @@ class UniZeroPolicy(MuZeroPolicy): optim_type='AdamW', # (float) Learning rate for training policy network. Initial lr for manually decay schedule. learning_rate=0.0001, - # ==================== [新增] 范数监控频率 ==================== - # 每隔多少个训练迭代步数,监控一次模型参数的范数。设置为0则禁用。 - monitor_norm_freq=5000, - # ============================================================ # (int) Frequency of hard target network update. target_update_freq=100, # (int) Frequency of soft target network update. @@ -320,12 +227,16 @@ class UniZeroPolicy(MuZeroPolicy): n_episode=8, # (int) The number of num_segments in each collecting stage when use muzero_segment_collector. num_segments=8, - # # (int) the number of simulations in MCTS for renalyze. + # (int) the number of simulations in MCTS. num_simulations=50, - # (int) The number of simulations in MCTS for the collect phase. - collect_num_simulations=25, - # (int) The number of simulations in MCTS for the eval phase. - eval_num_simulations=50, + # (float) The maximum change in value allowed during the backup step of the search tree update. + value_delta_max=0.01, + # (int) The number of top actions to consider in Gumbel MuZero Sequential Halving + max_num_considered_actions=16, + # (float) Gumbel MuZero exploration constant related to visit counts + c_visit=50.0, + # (float) Gumbel MuZero exploration scaling constant + c_scale=1.0, # (float) Discount factor (gamma) for returns. discount_factor=0.997, # (int) The number of steps for calculating target q_value. @@ -410,142 +321,24 @@ def default_model(self) -> Tuple[str, List[str]]: """ return 'UniZeroModel', ['lzero.model.unizero_model'] - - # ==================== [新增] 模型范数监控函数 ==================== - def _monitor_model_norms(self) -> Dict[str, float]: - """ - Overview: - 计算并返回模型关键组件(Encoder, Transformer, Heads)的参数矩阵范数。 - 此函数应在 torch.no_grad() 环境下调用,以提高效率。 - Returns: - - norm_metrics (:obj:`Dict[str, float]`): 包含所有范数指标的字典,用于日志记录。 - """ - world_model = self._learn_model.world_model - norm_metrics = {} - - # 定义要监控的模块组 - module_groups = { - 'encoder': world_model.tokenizer.encoder, - 'transformer': world_model.transformer, - 'head_value': world_model.head_value, - 'head_reward': world_model.head_rewards, - 'head_policy': world_model.head_policy, - } - - for group_name, group_module in module_groups.items(): - total_norm_sq = 0.0 - for param_name, param in group_module.named_parameters(): - if param.requires_grad: - # 计算单层参数的L2范数 - param_norm = param.data.norm(2).item() - # 替换点号,使其在TensorBoard中正确显示为层级 - log_name = f'norm/{group_name}/{param_name.replace(".", "/")}' - norm_metrics[log_name] = param_norm - total_norm_sq += param_norm ** 2 - - # 计算整个模块的总范数 - total_group_norm = np.sqrt(total_norm_sq) - norm_metrics[f'norm/{group_name}/_total_norm'] = total_group_norm - - return norm_metrics - - def _monitor_gradient_norms(self) -> Dict[str, float]: - """ - Overview: - 计算并返回模型关键组件的梯度范数。 - 此函数应在梯度计算完成后、参数更新之前调用。 - Returns: - - grad_metrics (:obj:`Dict[str, float]`): 包含所有梯度范数指标的字典,用于日志记录。 - """ - world_model = self._learn_model.world_model - grad_metrics = {} - - # 定义要监控的模块组 - module_groups = { - 'encoder': world_model.tokenizer.encoder, - 'transformer': world_model.transformer, - 'head_value': world_model.head_value, - 'head_reward': world_model.head_rewards, - 'head_policy': world_model.head_policy, - } - - for group_name, group_module in module_groups.items(): - total_grad_norm_sq = 0.0 - num_params_with_grad = 0 - - for param_name, param in group_module.named_parameters(): - if param.requires_grad and param.grad is not None: - # 计算单层参数的梯度L2范数 - grad_norm = param.grad.data.norm(2).item() - # 替换点号,使其在TensorBoard中正确显示为层级 - log_name = f'grad/{group_name}/{param_name.replace(".", "/")}' - grad_metrics[log_name] = grad_norm - total_grad_norm_sq += grad_norm ** 2 - num_params_with_grad += 1 - - # 计算整个模块的总梯度范数 - if num_params_with_grad > 0: - total_group_grad_norm = np.sqrt(total_grad_norm_sq) - grad_metrics[f'grad/{group_name}/_total_norm'] = total_group_grad_norm - else: - grad_metrics[f'grad/{group_name}/_total_norm'] = 0.0 - - return grad_metrics - # ================================================================= - def _init_learn(self) -> None: """ Overview: Learn mode init method. Called by ``self.__init__``. Initialize the learn model, optimizer and MCTS utils. """ - if self._cfg.optim_type == 'SGD': - # --- 改为SGD优化器 --- - self._optimizer_world_model = torch.optim.SGD( - self._model.world_model.parameters(), - lr=self._cfg.learning_rate, # 初始学习率,在配置中设为 0.2 - momentum=self._cfg.momentum, # 在配置中设为 0.9 - weight_decay=self._cfg.weight_decay # 在配置中设为 1e-4 - ) - elif self._cfg.optim_type == 'AdamW': - # NOTE: nanoGPT optimizer - self._optimizer_world_model = configure_optimizers_nanogpt( - model=self._model.world_model, - learning_rate=self._cfg.learning_rate, - weight_decay=self._cfg.weight_decay, - device_type=self._cfg.device, - betas=(0.9, 0.95), - ) - elif self._cfg.optim_type == 'AdamW_mix_lr_wdecay': - self._optimizer_world_model = configure_optimizer_unizero( - model=self._model.world_model, - learning_rate=self._cfg.learning_rate, # 使用一个合理的AdamW基础学习率 - weight_decay=self._cfg.weight_decay, - device_type=self._cfg.device, - betas=(0.9, 0.95), - ) + # NOTE: nanoGPT optimizer + self._optimizer_world_model = configure_optimizers_nanogpt( + model=self._model.world_model, + learning_rate=self._cfg.learning_rate, + weight_decay=self._cfg.weight_decay, + device_type=self._cfg.device, + betas=(0.9, 0.95), + ) if self._cfg.cos_lr_scheduler: from torch.optim.lr_scheduler import CosineAnnealingLR # TODO: check the total training steps - # self.lr_scheduler = CosineAnnealingLR(self._optimizer_world_model, 1e5, eta_min=0, last_epoch=-1) - total_iters = self._cfg.get('total_iterations', 500000) # 500k iter - # final_lr = self._cfg.get('final_learning_rate', 0.0) - final_lr = self._cfg.get('final_learning_rate', 1e-6) - - self.lr_scheduler = CosineAnnealingLR( - self._optimizer_world_model, - T_max=total_iters, - eta_min=final_lr - ) - print(f"CosineAnnealingLR enabled: T_max={total_iters}, eta_min={final_lr}") - - - if self._cfg.piecewise_decay_lr_scheduler: - from torch.optim.lr_scheduler import LambdaLR - max_step = self._cfg.threshold_training_steps_for_final_lr - # NOTE: the 1, 0.1, 0.01 is the decay rate, not the lr. - lr_lambda = lambda step: 1 if step < max_step * 0.5 else (0.1 if step < max_step else 0.01) # noqa - self.lr_scheduler = LambdaLR(self._optimizer_world_model, lr_lambda=lr_lambda) + self.lr_scheduler = CosineAnnealingLR(self._optimizer_world_model, 1e5, eta_min=0, last_epoch=-1) # use model_wrapper for specialized demands of different modes self._target_model = copy.deepcopy(self._model) @@ -578,8 +371,12 @@ def _init_learn(self) -> None: self.grad_norm_before = 0. self.grad_norm_after = 0. - encoder_tokenizer = getattr(self._model.tokenizer.encoder, 'tokenizer', None) - self.pad_token_id = encoder_tokenizer.pad_token_id if encoder_tokenizer is not None else 0 + if self._cfg.model.model_type == 'conv': + self.pad_token_id = -1 + else: + encoder_tokenizer = getattr(self._model.tokenizer.encoder, 'tokenizer', None) + self.pad_token_id = encoder_tokenizer.pad_token_id if encoder_tokenizer is not None else 0 + if self._cfg.use_wandb: # TODO: add the model to wandb @@ -587,63 +384,6 @@ def _init_learn(self) -> None: self.accumulation_steps = self._cfg.accumulation_steps - # ==================== START: 目标熵正则化初始化 ==================== - # 从配置中读取是否启用自适应alpha,并提供一个默认值 - self.use_adaptive_entropy_weight = self._cfg.get('use_adaptive_entropy_weight', True) - - # 在 _init_learn 中增加配置 - self.target_entropy_start_ratio = self._cfg.get('target_entropy_start_ratio', 0.98) - self.target_entropy_end_ratio = self._cfg.get('target_entropy_end_ratio', 0.7) - self.target_entropy_decay_steps = self._cfg.get('target_entropy_decay_steps', 200000) # 例如,在200k步内完成退火 2M envsteps - - if self.use_adaptive_entropy_weight: - # 1. 设置目标熵。对于离散动作空间,一个常见的启发式设置是动作空间维度的负对数乘以一个系数。 - # 这个系数(例如0.98)可以作为一个超参数。 - action_space_size = self._cfg.model.action_space_size - self.target_entropy = -np.log(1.0 / action_space_size) * 0.98 - - # 2. 初始化一个可学习的 log_alpha 参数。 - # 初始化为0,意味着初始的 alpha = exp(0) = 1.0。 - self.log_alpha = torch.nn.Parameter(torch.zeros(1, device=self._cfg.device), requires_grad=True) - - # 3. 为 log_alpha 创建一个专属的优化器。 - # 使用与主优化器不同的、较小的学习率(例如1e-4)通常更稳定。 - alpha_lr = self._cfg.get('adaptive_entropy_alpha_lr', 1e-4) - self.alpha_optimizer = torch.optim.Adam([self.log_alpha], lr=alpha_lr) - - print("="*20) - print(">>> 目标熵正则化 (自适应Alpha) 已启用 <<<") - print(f" 目标熵 (Target Entropy): {self.target_entropy:.4f}") - print(f" Alpha 优化器学习率: {alpha_lr:.2e}") - print("="*20) - # ===================== END: 目标熵正则化初始化 ===================== - - # ==================== START: 初始化 Encoder-Clip Annealing 参数 ==================== - self.use_encoder_clip_annealing = self._cfg.get('use_encoder_clip_annealing', False) - self.latent_norm_clip_threshold = self._cfg.get('latent_norm_clip_threshold', 20.0) # TODO - if self.use_encoder_clip_annealing: - self.encoder_clip_anneal_type = self._cfg.get('encoder_clip_anneal_type', 'cosine') - self.encoder_clip_start = self._cfg.get('encoder_clip_start_value', 30.0) - self.encoder_clip_end = self._cfg.get('encoder_clip_end_value', 10.0) - self.encoder_clip_anneal_steps = self._cfg.get('encoder_clip_anneal_steps', 200000) - - print("="*20) - print(">>> Encoder-Clip 退火已启用 <<<") - print(f" 类型: {self.encoder_clip_anneal_type}") - print(f" 范围: {self.encoder_clip_start} -> {self.encoder_clip_end}") - print(f" 步数: {self.encoder_clip_anneal_steps}") - print("="*20) - else: - # 如果不启用退火,则使用固定的 clip 阈值 - self.latent_norm_clip_threshold = self._cfg.get('latent_norm_clip_threshold', 20.0) - # ===================== END: 初始化 Encoder-Clip Annealing 参数 ===================== - - # --- NEW: Policy Label Smoothing Parameters --- - self.policy_ls_eps_start = self._cfg.get('policy_ls_eps_start', 0.05) # TODO policy_label_smoothing_eps_start 越大的action space需要越大的eps - self.policy_ls_eps_end = self._cfg.get('policy_label_smoothing_eps_end ', 0.01) # TODO policy_label_smoothing_eps_start - self.policy_ls_eps_decay_steps = self._cfg.get('policy_ls_eps_decay_steps ', 50000) # TODO 50k - print(f"self.policy_ls_eps_start:{self.policy_ls_eps_start}") - # @profile def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, int]]: """ @@ -665,13 +405,6 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in obs_batch_ori, action_batch, target_action_batch, mask_batch, indices, weights, make_time, timestep_batch = current_batch target_reward, target_value, target_policy = target_batch - # --- NEW: Calculate current epsilon for policy --- - if self.policy_ls_eps_start > 0: - progress = min(1.0, train_iter / self.policy_ls_eps_decay_steps) - current_policy_label_eps = self.policy_ls_eps_start * (1 - progress) + self.policy_ls_eps_end * progress - else: - current_policy_label_eps = 0.0 - # Prepare observations based on frame stack number if self._cfg.model.frame_stack_num > 1: obs_batch, obs_target_batch = prepare_obs_stack_for_unizero(obs_batch_ori, self._cfg) @@ -700,11 +433,8 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in transformed_target_value = scalar_transform(target_value) # Convert to categorical distributions - # target_reward_categorical = phi_transform(self.reward_support, transformed_target_reward) - # target_value_categorical = phi_transform(self.value_support, transformed_target_value) - - target_reward_categorical = phi_transform(self.reward_support, transformed_target_reward, label_smoothing_eps= self._cfg.label_smoothing_eps) - target_value_categorical = phi_transform(self.value_support, transformed_target_value, label_smoothing_eps=self._cfg.label_smoothing_eps) + target_reward_categorical = phi_transform(self.reward_support, transformed_target_reward) + target_value_categorical = phi_transform(self.value_support, transformed_target_value) # Prepare batch for GPT model batch_for_gpt = {} @@ -727,8 +457,6 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in batch_for_gpt['target_value'] = target_value_categorical[:, :-1] batch_for_gpt['target_policy'] = target_policy[:, :-1] - batch_for_gpt['scalar_target_value'] = target_value - # Extract valid target policy data and compute entropy valid_target_policy = batch_for_gpt['target_policy'][batch_for_gpt['mask_padding']] target_policy_entropy = -torch.sum(valid_target_policy * torch.log(valid_target_policy + 1e-9), dim=-1) @@ -736,83 +464,13 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in # Update world model losses = self._learn_model.world_model.compute_loss( - batch_for_gpt, self._target_model.world_model.tokenizer, self.value_inverse_scalar_transform_handle, global_step=train_iter, current_policy_label_eps=current_policy_label_eps, + batch_for_gpt, self._target_model.world_model.tokenizer, self.value_inverse_scalar_transform_handle ) # NOTE : compute_loss third argument is now a dead argument. If this changes, it could need adaptation between value_inverse and reward_inverse. - # ==================== [修改] 集成范数监控逻辑 ==================== - norm_log_dict = {} - # 检查是否达到监控频率 - if self._cfg.monitor_norm_freq > 0 and (train_iter == 0 or (train_iter % self._cfg.monitor_norm_freq == 0)): - with torch.no_grad(): - # 1. 监控模型参数范数 - param_norm_metrics = self._monitor_model_norms() - norm_log_dict.update(param_norm_metrics) - - # 2. 监控中间张量 x (Transformer的输出) - intermediate_x = losses.intermediate_losses.get('intermediate_tensor_x') - if intermediate_x is not None: - # x 的形状为 (B, T, E) - # 计算每个 token 的 L2 范数 - token_norms = intermediate_x.norm(p=2, dim=-1) - - # 记录这些范数的统计数据 - norm_log_dict['norm/x_token/mean'] = token_norms.mean().item() - norm_log_dict['norm/x_token/std'] = token_norms.std().item() - norm_log_dict['norm/x_token/max'] = token_norms.max().item() - norm_log_dict['norm/x_token/min'] = token_norms.min().item() - - # 3. 监控 logits 的详细统计 (Value, Policy, Reward) - logits_value = losses.intermediate_losses.get('logits_value') - if logits_value is not None: - norm_log_dict['logits/value/mean'] = logits_value.mean().item() - norm_log_dict['logits/value/std'] = logits_value.std().item() - norm_log_dict['logits/value/max'] = logits_value.max().item() - norm_log_dict['logits/value/min'] = logits_value.min().item() - norm_log_dict['logits/value/abs_max'] = logits_value.abs().max().item() - - logits_policy = losses.intermediate_losses.get('logits_policy') - if logits_policy is not None: - norm_log_dict['logits/policy/mean'] = logits_policy.mean().item() - norm_log_dict['logits/policy/std'] = logits_policy.std().item() - norm_log_dict['logits/policy/max'] = logits_policy.max().item() - norm_log_dict['logits/policy/min'] = logits_policy.min().item() - norm_log_dict['logits/policy/abs_max'] = logits_policy.abs().max().item() - - logits_reward = losses.intermediate_losses.get('logits_reward') - if logits_reward is not None: - norm_log_dict['logits/reward/mean'] = logits_reward.mean().item() - norm_log_dict['logits/reward/std'] = logits_reward.std().item() - norm_log_dict['logits/reward/max'] = logits_reward.max().item() - norm_log_dict['logits/reward/min'] = logits_reward.min().item() - norm_log_dict['logits/reward/abs_max'] = logits_reward.abs().max().item() - - # 4. 监控 obs_embeddings (Encoder输出) 的统计 - obs_embeddings = losses.intermediate_losses.get('obs_embeddings') - if obs_embeddings is not None: - # 计算每个 embedding 的 L2 范数 - emb_norms = obs_embeddings.norm(p=2, dim=-1) - norm_log_dict['embeddings/obs/norm_mean'] = emb_norms.mean().item() - norm_log_dict['embeddings/obs/norm_std'] = emb_norms.std().item() - norm_log_dict['embeddings/obs/norm_max'] = emb_norms.max().item() - norm_log_dict['embeddings/obs/norm_min'] = emb_norms.min().item() - # ================================================================= - - # ==================== START MODIFICATION 2 ==================== - # Extract the calculated value_priority from the returned losses. - value_priority_tensor = losses.intermediate_losses['value_priority'] - # Convert to numpy array for the replay buffer, adding a small epsilon. - value_priority_np = value_priority_tensor.detach().cpu().numpy() + 1e-6 - # ===================== END MODIFICATION 2 ===================== - - # weighted_total_loss = losses.loss_total - # TODO: - weighted_total_loss = (weights * losses.loss_total).mean() - + weighted_total_loss = losses.loss_total for loss_name, loss_value in losses.intermediate_losses.items(): self.intermediate_losses[f"{loss_name}"] = loss_value - # 从 losses 对象中提取策略熵 - obs_loss = self.intermediate_losses['loss_obs'] reward_loss = self.intermediate_losses['loss_rewards'] policy_loss = self.intermediate_losses['loss_policy'] @@ -825,26 +483,9 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in middle_step_losses = self.intermediate_losses['middle_step_losses'] last_step_losses = self.intermediate_losses['last_step_losses'] dormant_ratio_encoder = self.intermediate_losses['dormant_ratio_encoder'] - dormant_ratio_transformer = self.intermediate_losses['dormant_ratio_transformer'] - dormant_ratio_head = self.intermediate_losses['dormant_ratio_head'] - avg_weight_mag_encoder = self.intermediate_losses['avg_weight_mag_encoder'] - avg_weight_mag_transformer = self.intermediate_losses['avg_weight_mag_transformer'] - avg_weight_mag_head = self.intermediate_losses['avg_weight_mag_head'] - e_rank_last_linear = self.intermediate_losses['e_rank_last_linear'] - e_rank_sim_norm = self.intermediate_losses['e_rank_sim_norm'] + dormant_ratio_world_model = self.intermediate_losses['dormant_ratio_world_model'] latent_state_l2_norms = self.intermediate_losses['latent_state_l2_norms'] - latent_action_l2_norms = self.intermediate_losses['latent_action_l2_norms'] - logits_value_mean=self.intermediate_losses['logits_value_mean'] - logits_value_max=self.intermediate_losses['logits_value_max'] - logits_value_min=self.intermediate_losses['logits_value_min'] - logits_policy_mean=self.intermediate_losses['logits_policy_mean'] - logits_policy_max=self.intermediate_losses['logits_policy_max'] - logits_policy_min=self.intermediate_losses['logits_policy_min'] - temperature_value=self.intermediate_losses['temperature_value'] - temperature_reward=self.intermediate_losses['temperature_reward'] - temperature_policy=self.intermediate_losses['temperature_policy'] - assert not torch.isnan(losses.loss_total).any(), "Loss contains NaN values" assert not torch.isinf(losses.loss_total).any(), "Loss contains Inf values" @@ -853,107 +494,19 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in if (train_iter % self.accumulation_steps) == 0: self._optimizer_world_model.zero_grad() - - # ==================== START: 目标熵正则化更新逻辑 ==================== - alpha_loss = None - current_alpha = self._cfg.model.world_model_cfg.policy_entropy_weight # 默认使用固定值 - if self.use_adaptive_entropy_weight: - # --- 动态计算目标熵 (这部分逻辑是正确的,予以保留) --- - progress = min(1.0, train_iter / self.target_entropy_decay_steps) - current_ratio = self.target_entropy_start_ratio * (1 - progress) + self.target_entropy_end_ratio * progress - action_space_size = self._cfg.model.action_space_size - # 注意:我们将 target_entropy 定义为正数,更符合直觉 - current_target_entropy = -np.log(1.0 / action_space_size) * current_ratio - - # --- 计算 alpha_loss (已修正符号) --- - # 这是核心修正点:去掉了最前面的负号 - # detach() 仍然是关键,确保 alpha_loss 的梯度只流向 log_alpha - alpha_loss = (self.log_alpha * (policy_entropy.detach() - current_target_entropy)).mean() - - # # --- 更新 log_alpha --- - self.alpha_optimizer.zero_grad() - alpha_loss.backward() - self.alpha_optimizer.step() - # --- [优化建议] 增加 log_alpha 裁剪作为安全措施 --- - with torch.no_grad(): - # 将 alpha 限制在例如 [1e-4, 10.0] 的范围内 - self.log_alpha.clamp_(np.log(1e-4), np.log(10.0)) - - # --- 使用当前更新后的 alpha (截断梯度流) --- - current_alpha = self.log_alpha.exp().detach() - - # 重新计算加权的策略损失和总损失 - # 注意:这里的 policy_entropy 已经是一个batch的平均值 - weighted_policy_loss = orig_policy_loss - current_alpha * policy_entropy - # 重新构建总损失 (不使用 losses.loss_total) - # 确保这里的权重与 LossWithIntermediateLosses 类中的计算方式一致 - self.obs_loss_weight = 10 - self.value_loss_weight = 0.5 - self.reward_loss_weight = 1. - self.policy_loss_weight = 1. - self.ends_loss_weight = 0. - total_loss = ( - self.reward_loss_weight * reward_loss + - self.value_loss_weight * value_loss + - self.policy_loss_weight * weighted_policy_loss + - self.obs_loss_weight * obs_loss # 假设 ssl_loss_weight 是 obs_loss 的权重 - # ... 如果还有其他损失项,也加进来 ... - ) - weighted_total_loss = (weights * total_loss).mean() - # ===================== END: 目标熵正则化更新逻辑 ===================== - # Scale the loss by the number of accumulation steps weighted_total_loss = weighted_total_loss / self.accumulation_steps weighted_total_loss.backward() - # ----------------------------------------------------------------- - # 仍然在 torch.no_grad() 环境下执行 - # ================================================================= - with torch.no_grad(): - # 1. Encoder-Clip - # ==================== START: 动态计算当前 Clip 阈值 ==================== - current_clip_value = self.latent_norm_clip_threshold # 默认使用固定值 - if self.use_encoder_clip_annealing: - progress = min(1.0, train_iter / self.encoder_clip_anneal_steps) - - if self.encoder_clip_anneal_type == 'cosine': - # 余弦调度: 从1平滑过渡到0 - cosine_progress = 0.5 * (1.0 + np.cos(np.pi * progress)) - current_clip_value = self.encoder_clip_end + \ - (self.encoder_clip_start - self.encoder_clip_end) * cosine_progress - else: # 默认为线性调度 - current_clip_value = self.encoder_clip_start * (1 - progress) + \ - self.encoder_clip_end * progress - # ===================== END: 动态计算当前 Clip 阈值 ===================== - - # 1. Encoder-Clip (使用动态计算出的 current_clip_value) - if current_clip_value > 0 and 'obs_embeddings' in losses.intermediate_losses: - obs_embeddings = losses.intermediate_losses['obs_embeddings'] - if obs_embeddings is not None: - max_latent_norm = obs_embeddings.norm(p=2, dim=-1).max() - if max_latent_norm > current_clip_value: - scale_factor = current_clip_value / max_latent_norm.item() - # 不再频繁打印,或者可以改为每隔N步打印一次 - if train_iter % 1000 == 0: - print(f"[Encoder-Clip Annealing] Iter {train_iter}: Max latent norm {max_latent_norm.item():.2f} > {current_clip_value:.2f}. Scaling by {scale_factor:.4f}.") - scale_module_weights_vectorized(self._model.world_model.tokenizer.encoder, scale_factor) - # Check if the current iteration completes an accumulation cycle if (train_iter + 1) % self.accumulation_steps == 0: - # ==================== [新增] 监控梯度范数 ==================== - # 在梯度裁剪之前监控梯度范数,用于诊断梯度爆炸/消失问题 - if self._cfg.monitor_norm_freq > 0 and (train_iter == 0 or (train_iter % self._cfg.monitor_norm_freq == 0)): - grad_norm_metrics = self._monitor_gradient_norms() - norm_log_dict.update(grad_norm_metrics) - # ================================================================= - # Analyze gradient norms if simulation normalization analysis is enabled if self._cfg.analysis_sim_norm: # Clear previous analysis results to prevent memory overflow del self.l2_norm_before, self.l2_norm_after, self.grad_norm_before, self.grad_norm_after self.l2_norm_before, self.l2_norm_after, self.grad_norm_before, self.grad_norm_after = self._learn_model.encoder_hook.analyze() self._target_model.encoder_hook.clear_data() - + # Clip gradients to prevent exploding gradients total_grad_norm_before_clip_wm = torch.nn.utils.clip_grad_norm_( self._learn_model.world_model.parameters(), self._cfg.grad_clip_value @@ -1020,61 +573,21 @@ def _forward_learn(self, data: Tuple[torch.Tensor]) -> Dict[str, Union[float, in 'target_policy_entropy': average_target_policy_entropy.item(), 'reward_loss': reward_loss.item(), 'value_loss': value_loss.item(), - # Add value_priority to the log dictionary. - 'value_priority': value_priority_np.mean().item(), - 'value_priority_orig': value_priority_np, + # 'value_priority_orig': np.zeros(self._cfg.batch_size), # TODO 'target_reward': target_reward.mean().item(), 'target_value': target_value.mean().item(), 'transformed_target_reward': transformed_target_reward.mean().item(), 'transformed_target_value': transformed_target_value.mean().item(), 'total_grad_norm_before_clip_wm': total_grad_norm_before_clip_wm.item(), - 'analysis/dormant_ratio_encoder': dormant_ratio_encoder, - 'analysis/dormant_ratio_transformer': dormant_ratio_transformer, - 'analysis/dormant_ratio_head': dormant_ratio_head, - - 'analysis/avg_weight_mag_encoder': avg_weight_mag_encoder, - 'analysis/avg_weight_mag_transformer': avg_weight_mag_transformer, - 'analysis/avg_weight_mag_head': avg_weight_mag_head, - 'analysis/e_rank_last_linear': e_rank_last_linear, - 'analysis/e_rank_sim_norm': e_rank_sim_norm, - + 'analysis/dormant_ratio_encoder': dormant_ratio_encoder.item(), + 'analysis/dormant_ratio_world_model': dormant_ratio_world_model.item(), 'analysis/latent_state_l2_norms': latent_state_l2_norms.item(), - 'analysis/latent_action_l2_norms': latent_action_l2_norms, 'analysis/l2_norm_before': self.l2_norm_before, 'analysis/l2_norm_after': self.l2_norm_after, 'analysis/grad_norm_before': self.grad_norm_before, 'analysis/grad_norm_after': self.grad_norm_after, - "logits_value_mean":logits_value_mean, - "logits_value_max":logits_value_max, - "logits_value_min":logits_value_min, - "logits_policy_mean":logits_policy_mean, - "logits_policy_max":logits_policy_max, - "logits_policy_min":logits_policy_min, - - "temperature_value":temperature_value, - "temperature_reward":temperature_reward, - "temperature_policy":temperature_policy, - - "current_policy_label_eps":current_policy_label_eps, } - - # ==================== [修改] 将范数监控结果合并到日志中 ==================== - if norm_log_dict: - return_log_dict.update(norm_log_dict) - # ======================================================================= - - # ==================== START: 添加新日志项 ==================== - if self.use_adaptive_entropy_weight: - return_log_dict['adaptive_alpha'] = current_alpha.item() - return_log_dict['adaptive_target_entropy_ratio'] = current_ratio - return_log_dict['alpha_loss'] = alpha_loss.item() - # ==================== START: 添加新日志项 ==================== - - # ==================== START: 添加新日志项 ==================== - if self.use_encoder_clip_annealing: - return_log_dict['current_encoder_clip_value'] = current_clip_value - # ===================== END: 添加新日志项 ===================== - + if self._cfg.use_wandb: wandb.log({'learner_step/' + k: v for k, v in return_log_dict.items()}, step=self.env_step) wandb.log({"learner_iter_vs_env_step": self.train_iter}, step=self.env_step) @@ -1096,13 +609,11 @@ def _init_collect(self) -> None: Collect mode init method. Called by ``self.__init__``. Initialize the collect model and MCTS utils. """ self._collect_model = self._model - # 为 collect MCTS 创建一个配置副本,并设置特定的模拟次数 - mcts_collect_cfg = copy.deepcopy(self._cfg) - mcts_collect_cfg.num_simulations = self._cfg.collect_num_simulations + if self._cfg.mcts_ctree: - self._mcts_collect = MCTSCtree(mcts_collect_cfg) + self._mcts_collect = MCTSCtree(self._cfg) else: - self._mcts_collect = MCTSPtree(mcts_collect_cfg) + self._mcts_collect = MCTSPtree(self._cfg) self._collect_mcts_temperature = 1. self._collect_epsilon = 0.0 self.collector_env_num = self._cfg.collector_env_num @@ -1123,9 +634,8 @@ def _forward_collect( temperature: float = 1, to_play: List = [-1], epsilon: float = 0.25, - ready_env_id: np.array = None, - timestep: List = [0], - task_id: int = None, + ready_env_id: np.ndarray = None, + timestep: List = [0] ) -> Dict: """ Overview: @@ -1138,7 +648,6 @@ def _forward_collect( - to_play (:obj:`int`): The player to play. - ready_env_id (:obj:`list`): The id of the env that is ready to collect. - timestep (:obj:`list`): The step index of the env in one episode. - - task_id (:obj:`int`): The task id. Default is None, which means UniZero is in the single-task mode. Shape: - data (:obj:`torch.Tensor`): - For Atari, :math:`(N, C*S, H, W)`, where N is the number of collect_env, C is the number of channels, \ @@ -1185,8 +694,8 @@ def _forward_collect( roots.prepare(self._cfg.root_noise_weight, noises, reward_roots, policy_logits, to_play) - next_latent_state_with_env = self._mcts_collect.search(roots, self._collect_model, latent_state_roots, to_play, timestep) - + first_action_latent_map = self._mcts_collect.search(roots, self._collect_model, latent_state_roots, to_play, timestep) + # list of list, shape: ``{list: batch_size} -> {list: action_space_size}`` roots_visit_count_distributions = roots.get_distributions() roots_values = roots.get_values() # shape: {list: batch_size} @@ -1214,8 +723,8 @@ def _forward_collect( # NOTE: Convert the ``action_index_in_legal_action_set`` to the corresponding ``action`` in the entire action set. action = np.where(action_mask[i] == 1.0)[0][action_index_in_legal_action_set] - next_latent_state = next_latent_state_with_env[i][action] - + next_latent_state = first_action_latent_map[i][action] + if self._cfg.model.world_model_cfg.obs_type == 'text' and self._cfg.model.world_model_cfg.decode_loss_mode is not None and self._cfg.model.world_model_cfg.decode_loss_mode.lower() != 'none': # Output the plain text content decoded by the decoder from the next latent state predicted_next = self._collect_model.tokenizer.decode_to_plain_text(embeddings=next_latent_state, max_length=256) @@ -1244,25 +753,13 @@ def _forward_collect( self.last_batch_obs = data self.last_batch_action = batch_action - # ========= TODO: This logic is a temporary workaround specific to the muzero_segment_collector. ========= + # ========= TODO: for muzero_segment_collector now ========= if active_collect_env_num < self.collector_env_num: - # When an environment finishes an episode ('done'), the length of `self.last_batch_obs` passed back - # becomes smaller than the total number of collector environments. - # Handling this dynamic batch size is complex, as the transformer's KV cache retrieval - # requires a stable environment ID for correct indexing. A mismatch would cause retrieval errors. - # - # Therefore, as a simpler solution, we reset the collection state for ALL environments. - # By resetting `self.last_batch_action` to -1 for all `self.collector_env_num` environments, - # we force the transformer to start its context from scratch, avoiding incorrect cache lookups. - print('========== collect_forward ============') - print(f'An environment has finished. Active envs: {active_collect_env_num} < Total envs: {self.collector_env_num}. Resetting all.') - + print('==========collect_forward============') + print(f'len(self.last_batch_obs) < self.collector_env_num, {active_collect_env_num}<{self.collector_env_num}') self._reset_collect(reset_init_data=True) - - # If the sampling type is 'episode', it's unexpected for the number of active environments to drop, - # as this suggests an inconsistent state or a potential issue in the collection logic. if getattr(self._cfg, 'sample_type', '') == 'episode': - print('WARNING: Inconsistent state detected. `sample_type` is "episode", but the number of active environments has changed.') + print('BUG: sample_type is episode, but len(self.last_batch_obs) < self.collector_env_num') return output @@ -1272,16 +769,10 @@ def _init_eval(self) -> None: Evaluate mode init method. Called by ``self.__init__``. Initialize the eval model and MCTS utils. """ self._eval_model = self._model - - # 为 eval MCTS 创建一个配置副本,并设置特定的模拟次数 - mcts_eval_cfg = copy.deepcopy(self._cfg) - mcts_eval_cfg.num_simulations = self._cfg.eval_num_simulations - if self._cfg.mcts_ctree: - self._mcts_eval = MCTSCtree(mcts_eval_cfg) + self._mcts_eval = MCTSCtree(self._cfg) else: - self._mcts_eval = MCTSPtree(mcts_eval_cfg) - + self._mcts_eval = MCTSPtree(self._cfg) self.evaluator_env_num = self._cfg.evaluator_env_num if self._cfg.model.model_type == 'conv': @@ -1293,8 +784,8 @@ def _init_eval(self) -> None: ).to(self._cfg.device) self.last_batch_action = [-1 for i in range(self.collector_env_num)] - def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: int = -1, - ready_env_id: np.array = None, timestep: List = [0], task_id: int = None,) -> Dict: + def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: List = [-1], + ready_env_id: np.array = None, timestep: List = [0]) -> Dict: """ Overview: The forward function for evaluating the current policy in eval mode. Use model to execute MCTS search. @@ -1305,7 +796,6 @@ def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: int = -1 - to_play (:obj:`int`): The player to play. - ready_env_id (:obj:`list`): The id of the env that is ready to eval. - timestep (:obj:`list`): The step index of the env in one episode. - - task_id (:obj:`int`): The task id. Default is None, which means UniZero is in the single-task mode. Shape: - data (:obj:`torch.Tensor`): - For Atari, :math:`(N, C*S, H, W)`, where N is the number of eval_env, C is the number of channels, \ @@ -1326,7 +816,7 @@ def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: int = -1 ready_env_id = np.arange(active_eval_env_num) output = {i: None for i in ready_env_id} with torch.no_grad(): - network_output = self._eval_model.initial_inference(self.last_batch_obs_eval, self.last_batch_action, data, timestep) + network_output = self._eval_model.initial_inference(self.last_batch_obs, self.last_batch_action, data, timestep) latent_state_roots, reward_roots, pred_values, policy_logits = mz_network_output_unpack(network_output) # if not in training, obtain the scalars of the value/reward @@ -1342,7 +832,7 @@ def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: int = -1 # python mcts_tree roots = MCTSPtree.roots(active_eval_env_num, legal_actions) roots.prepare_no_noise(reward_roots, policy_logits, to_play) - next_latent_state_with_env = self._mcts_eval.search(roots, self._eval_model, latent_state_roots, to_play, timestep) + first_action_latent_map = self._mcts_eval.search(roots, self._eval_model, latent_state_roots, to_play, timestep) # list of list, shape: ``{list: batch_size} -> {list: action_space_size}`` roots_visit_count_distributions = roots.get_distributions() @@ -1366,7 +856,7 @@ def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: int = -1 action = np.where(action_mask[i] == 1.0)[0][action_index_in_legal_action_set] # Predict the next latent state based on the selected action and policy - next_latent_state = next_latent_state_with_env[i][action] + next_latent_state = first_action_latent_map[i][action] if self._cfg.model.world_model_cfg.obs_type == 'text' and self._cfg.model.world_model_cfg.decode_loss_mode is not None and self._cfg.model.world_model_cfg.decode_loss_mode.lower() != 'none': # Output the plain text content decoded by the decoder from the next latent state @@ -1386,12 +876,12 @@ def _forward_eval(self, data: torch.Tensor, action_mask: list, to_play: int = -1 } batch_action.append(action) - self.last_batch_obs_eval = data + self.last_batch_obs = data self.last_batch_action = batch_action return output - def _reset_collect(self, env_id: int = None, current_steps: int = None, reset_init_data: bool = True, task_id: int = None) -> None: + def _reset_collect(self, env_id: int = None, current_steps: int = None, reset_init_data: bool = True) -> None: """ Overview: This method resets the collection process for a specific environment. It clears caches and memory @@ -1412,31 +902,15 @@ def _reset_collect(self, env_id: int = None, current_steps: int = None, reset_in ) self.last_batch_action = [-1 for _ in range(self._cfg.collector_env_num)] + # Return immediately if env_id is None or a list + if env_id is None or isinstance(env_id, list): + return - # We must handle both single int and list of ints for env_id. - if env_id is not None: - if isinstance(env_id, int): - env_ids_to_reset = [env_id] - else: # Assumes it's a list - env_ids_to_reset = env_id - - # The key condition: `current_steps` is None only on the end-of-episode reset call from the collector. - if current_steps is None: - world_model = self._collect_model.world_model - for eid in env_ids_to_reset: - # Clear the specific environment's initial inference cache. - if eid < len(world_model.past_kv_cache_init_infer_envs): - world_model.past_kv_cache_init_infer_envs[eid].clear() - - print(f'>>> [Collector] Cleared KV cache for env_id: {eid} at episode end.') - - # ======== TODO: 20251015 ======== # Determine the clear interval based on the environment's sample type - # clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else 200 - clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else self._cfg.game_segment_length + clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else 200 # Clear caches if the current steps are a multiple of the clear interval - if current_steps is not None and current_steps % clear_interval == 0: + if current_steps % clear_interval == 0: print(f'clear_interval: {clear_interval}') # Clear various caches in the collect model's world model @@ -1449,9 +923,10 @@ def _reset_collect(self, env_id: int = None, current_steps: int = None, reset_in # Free up GPU memory torch.cuda.empty_cache() - print(f'eps_steps_lst[{env_id}]: {current_steps}, collector: collect_model clear()') + print('collector: collect_model clear()') + print(f'eps_steps_lst[{env_id}]: {current_steps}') - def _reset_eval(self, env_id: int = None, current_steps: int = None, reset_init_data: bool = True, task_id: int = None) -> None: + def _reset_eval(self, env_id: int = None, current_steps: int = None, reset_init_data: bool = True) -> None: """ Overview: This method resets the evaluation process for a specific environment. It clears caches and memory @@ -1464,61 +939,23 @@ def _reset_eval(self, env_id: int = None, current_steps: int = None, reset_init_ - reset_init_data (:obj:`bool`, optional): Whether to reset the initial data. If True, the initial data will be reset. """ if reset_init_data: - if task_id is not None: - self.last_batch_obs_eval = initialize_zeros_batch( - self._cfg.model.observation_shape_list[task_id], - self._cfg.evaluator_env_num, - self._cfg.device, - pad_token_id=self.pad_token_id - ) - print(f'unizero.py task_id:{task_id} after _reset_eval: last_batch_obs_eval:', self.last_batch_obs_eval.shape) - - else: - self.last_batch_obs_eval = initialize_pad_batch( # TODO - self._cfg.model.observation_shape, - self._cfg.evaluator_env_num, - self._cfg.device, - pad_token_id=self.pad_token_id - ) - print(f'unizero.py task_id:{task_id} after _reset_eval: last_batch_obs_eval:', self.last_batch_obs_eval.shape) - + self.last_batch_obs = initialize_pad_batch( + self._cfg.model.observation_shape, + self._cfg.evaluator_env_num, + self._cfg.device, + pad_token_id=self.pad_token_id + ) self.last_batch_action = [-1 for _ in range(self._cfg.evaluator_env_num)] - # --- BEGIN ROBUST FIX --- - # This logic handles the crucial end-of-episode cache clearing for evaluation. - # The evaluator calls `_policy.reset([env_id])` when an episode is done. - if env_id is not None: - if isinstance(env_id, int): - env_ids_to_reset = [env_id] - else: # Assumes it's a list - env_ids_to_reset = env_id - - # The key condition: `current_steps` is None only on the end-of-episode reset call from the evaluator. - if current_steps is None: - world_model = self._eval_model.world_model - for eid in env_ids_to_reset: - # Clear the specific environment's initial inference cache. - if eid < len(world_model.past_kv_cache_init_infer_envs): - world_model.past_kv_cache_init_infer_envs[eid].clear() - - print(f'>>> [Evaluator] Cleared KV cache for env_id: {eid} at episode end.') - - # The recurrent cache is global. - world_model.past_kv_cache_recurrent_infer.clear() - - if hasattr(world_model, 'keys_values_wm_list'): - world_model.keys_values_wm_list.clear() + # Return immediately if env_id is None or a list + if env_id is None or isinstance(env_id, list): + return - torch.cuda.empty_cache() - return - # --- END ROBUST FIX --- - - # ======== TODO: 20251015 ======== # Determine the clear interval based on the environment's sample type - # clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else 200 - clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else self._cfg.game_segment_length + clear_interval = 2000 if getattr(self._cfg, 'sample_type', '') == 'episode' else 200 + # Clear caches if the current steps are a multiple of the clear interval - if current_steps is not None and current_steps % clear_interval == 0: + if current_steps % clear_interval == 0: print(f'clear_interval: {clear_interval}') # Clear various caches in the eval model's world model @@ -1540,142 +977,56 @@ def _monitor_vars_learn(self) -> List[str]: Register the variables to be monitored in learn mode. The registered variables will be logged in tensorboard according to the return value ``_forward_learn``. """ - base_vars = [ - # ==================== Analysis Metrics ==================== + return [ 'analysis/dormant_ratio_encoder', - 'analysis/dormant_ratio_transformer', - 'analysis/dormant_ratio_head', - 'analysis/avg_weight_mag_encoder', - 'analysis/avg_weight_mag_transformer', - 'analysis/avg_weight_mag_head', - 'analysis/e_rank_last_linear', - 'analysis/e_rank_sim_norm', + 'analysis/dormant_ratio_world_model', 'analysis/latent_state_l2_norms', - 'analysis/latent_action_l2_norms', 'analysis/l2_norm_before', 'analysis/l2_norm_after', 'analysis/grad_norm_before', 'analysis/grad_norm_after', - # ==================== Step-wise Loss Analysis ==================== 'analysis/first_step_loss_value', 'analysis/first_step_loss_policy', 'analysis/first_step_loss_rewards', 'analysis/first_step_loss_obs', + 'analysis/middle_step_loss_value', 'analysis/middle_step_loss_policy', 'analysis/middle_step_loss_rewards', 'analysis/middle_step_loss_obs', + 'analysis/last_step_loss_value', 'analysis/last_step_loss_policy', 'analysis/last_step_loss_rewards', 'analysis/last_step_loss_obs', - # ==================== System Metrics ==================== 'Current_GPU', 'Max_GPU', 'collect_epsilon', 'collect_mcts_temperature', 'cur_lr_world_model', + 'cur_lr_tokenizer', - # ==================== Core Losses ==================== 'weighted_total_loss', 'obs_loss', 'policy_loss', 'orig_policy_loss', 'policy_entropy', 'latent_recon_loss', - 'perceptual_loss', 'target_policy_entropy', 'reward_loss', 'value_loss', + 'consistency_loss', 'value_priority', 'target_reward', 'target_value', - 'transformed_target_reward', - 'transformed_target_value', - - # ==================== Gradient Norms ==================== 'total_grad_norm_before_clip_wm', - - # ==================== Logits Statistics ==================== - 'logits_value_mean', - 'logits_value_max', - 'logits_value_min', - 'logits_policy_mean', - 'logits_policy_max', - 'logits_policy_min', - - # ==================== Temperature Parameters ==================== - 'temperature_value', - 'temperature_reward', - 'temperature_policy', - - # ==================== Training Configuration ==================== - 'current_policy_label_eps', - 'adaptive_alpha', - 'adaptive_target_entropy_ratio', - 'alpha_loss', - 'current_encoder_clip_value', - ] - - # ==================== [新增] 范数和中间张量监控变量 ==================== - norm_vars = [ - # 模块总范数 (参数范数) - 'norm/encoder/_total_norm', - 'norm/transformer/_total_norm', - 'norm/head_value/_total_norm', - 'norm/head_reward/_total_norm', - 'norm/head_policy/_total_norm', - - # 模块总范数 (梯度范数) - 'grad/encoder/_total_norm', - 'grad/transformer/_total_norm', - 'grad/head_value/_total_norm', - 'grad/head_reward/_total_norm', - 'grad/head_policy/_total_norm', - - # 中间张量 x (Transformer输出) 的统计信息 - 'norm/x_token/mean', - 'norm/x_token/std', - 'norm/x_token/max', - 'norm/x_token/min', - - # Logits 的详细统计 (Value) - 'logits/value/mean', - 'logits/value/std', - 'logits/value/max', - 'logits/value/min', - 'logits/value/abs_max', - - # Logits 的详细统计 (Policy) - 'logits/policy/mean', - 'logits/policy/std', - 'logits/policy/max', - 'logits/policy/min', - 'logits/policy/abs_max', - - # Logits 的详细统计 (Reward) - 'logits/reward/mean', - 'logits/reward/std', - 'logits/reward/max', - 'logits/reward/min', - 'logits/reward/abs_max', - - # Embeddings 的统计信息 - 'embeddings/obs/norm_mean', - 'embeddings/obs/norm_std', - 'embeddings/obs/norm_max', - 'embeddings/obs/norm_min', + # tokenizer + 'commitment_loss', + 'reconstruction_loss', + 'perceptual_loss', ] - # 注意:我们不把每一层的范数都加到这里,因为数量太多会导致日志混乱。 - # 在实践中,如果通过总范数发现问题,可以临时在TensorBoard中搜索特定层的范数, - # 或者在本地打印 `norm_log_dict` 来进行详细分析。 - # wandb等工具可以更好地处理大量的动态指标。 - # ======================================================================== - - return base_vars + norm_vars - def _state_dict_learn(self) -> Dict[str, Any]: """ @@ -1684,16 +1035,11 @@ def _state_dict_learn(self) -> Dict[str, Any]: Returns: - state_dict (:obj:`Dict[str, Any]`): The dict of current policy learn state, for saving and restoring. """ - state_dict = { + return { 'model': self._learn_model.state_dict(), 'target_model': self._target_model.state_dict(), 'optimizer_world_model': self._optimizer_world_model.state_dict(), } - # ==================== START: 保存Alpha优化器状态 ==================== - if self.use_adaptive_entropy_weight: - state_dict['alpha_optimizer'] = self.alpha_optimizer.state_dict() - # ===================== END: 保存Alpha优化器状态 ===================== - return state_dict def _load_state_dict_learn(self, state_dict: Dict[str, Any]) -> None: """ @@ -1704,12 +1050,7 @@ def _load_state_dict_learn(self, state_dict: Dict[str, Any]) -> None: """ self._learn_model.load_state_dict(state_dict['model']) self._target_model.load_state_dict(state_dict['target_model']) - # self._optimizer_world_model.load_state_dict(state_dict['optimizer_world_model']) - - # ==================== START: 加载Alpha优化器状态 ==================== - # if self.use_adaptive_entropy_weight and 'alpha_optimizer' in state_dict: - # self.alpha_optimizer.load_state_dict(state_dict['alpha_optimizer']) - # ===================== END: 加载Alpha优化器状态 ===================== + self._optimizer_world_model.load_state_dict(state_dict['optimizer_world_model']) def recompute_pos_emb_diff_and_clear_cache(self) -> None: """ From eacd0e59ae6b9b495b9e7b61ef6872dac8fe2ebd Mon Sep 17 00:00:00 2001 From: Mr_TJ <59445465+tAnGjIa520@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:24:17 +0800 Subject: [PATCH 3/4] Delete lzero/mcts/ctree/ctree_muzero_v2/test_batch_traverse --- .../ctree/ctree_muzero_v2/test_batch_traverse | Bin 268848 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 lzero/mcts/ctree/ctree_muzero_v2/test_batch_traverse diff --git a/lzero/mcts/ctree/ctree_muzero_v2/test_batch_traverse b/lzero/mcts/ctree/ctree_muzero_v2/test_batch_traverse deleted file mode 100755 index c773039bf50779e5e1af669b370035c787025264..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 268848 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 From 6fc2cf586de675481f2947934b1b1cd7419abbef Mon Sep 17 00:00:00 2001 From: Mr_TJ <59445465+tAnGjIa520@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:24:29 +0800 Subject: [PATCH 4/4] Delete lzero/mcts/ctree/ctree_muzero_v2/test_cnode_sh --- lzero/mcts/ctree/ctree_muzero_v2/test_cnode_sh | Bin 267328 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 lzero/mcts/ctree/ctree_muzero_v2/test_cnode_sh diff --git a/lzero/mcts/ctree/ctree_muzero_v2/test_cnode_sh b/lzero/mcts/ctree/ctree_muzero_v2/test_cnode_sh deleted file mode 100755 index 4a0675ce58e14f9766095bc87f43b5012a67e10a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 267328 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