next(self.parameters()).new_zeros()是什么

本文详细解析了PyTorch中next(self.parameters())及其相关函数的使用,包括如何利用这些函数进行模型参数的初始化,如创建零张量,并介绍了其在神经网络训练中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Q:next(self.parameters())是什么?

Q:next(self.parameters()).new_zeros()是什么?

Q:next(self.parameters()).data是什么?

Q:next(self.parameters()).data.new(batch_size, self.nhid).zero_()是什么?

A:

1. 使用:

某类的某一函数:
    def init_hidden(self, batch_size, hidden_size):
        weight = next(self.parameters()) 
        h0 = weight.new_zeros(batch_size, hidden_size)
        return h0
或
    def init_hidden(self, batch_size):
        weight = next(self.parameters()).data
        h0 = weight.new(batch_size, self.nhid).zero_()
        return h0
4.0版本不需要Variable

2. 解释:

next:通过调用它的next()方法从迭代器中检索下一项。

next(self.parameters()):这里,它返回该类的第一个参数。

next(self.parameters()).new_zeros(4,100):按照自己的意愿创建一个变量,但是您需要在这种情况下指定数据类型。通过new,我们构造了一个数据类型相同的新张量(作为第一个参数)。

 

 

 

参考链接:https://discuss.pytorch.org/t/what-does-next-self-parameters-data-mean/1458

评估这个代码的正确性def prepare_data(self): dataset = TensorDataset( torch.stack(self.states), torch.tensor(self.actions), torch.tensor(self.probs, dtype=torch.float32), torch.tensor(self.rewards, dtype=torch.float32), torch.tensor(self.dones, dtype=torch.float32), torch.tensor(self.values, dtype=torch.float32) ) return DataLoader(dataset, batch_size=32, shuffle=True) def compute_advantages(self, rewards, values, dones): """改进的GAE计算,带移动平均标准化""" T = len(rewards) advantages = torch.zeros_like(rewards) last_gae = 0 non_terminals = 1.0 - dones # 计算GAE for t in reversed(range(T)): next_value = values[t+1] if t < T-1 else 0.0 next_non_terminal = non_terminals[t+1] if t < T-1 else 0.0 delta = rewards[t] + self.gamma * next_value * next_non_terminal - values[t] last_gae = delta + self.gamma * self.lambda_ * next_non_terminal * last_gae advantages[t] = last_gae # 移动平均标准化 alpha = 0.99 batch_mean = advantages.mean().item() batch_std = advantages.std().item() self.global_return_mean = alpha * self.global_return_mean + (1-alpha)*batch_mean self.global_return_std = alpha * self.global_return_std + (1-alpha)*batch_std advantages = (advantages - self.global_return_mean) / (self.global_return_std + 1e-8) returns = advantages + values returns = torch.clamp(returns, -3.0, 3.0) # 放宽裁剪范围 return returns, advantages def learn(self, n_epochs=10): dataloader = self.prepare_data() for _ in range(n_epochs): for batch in dataloader: states, actions, old_probs, rewards, dones, values = batch """ # 没有GPU这一步先注释 states, actions, old_probs, rewards, dones, values = \ states.to(self.device), actions.to(self.device), old_probs.to(self.device), rewards.to(self.device), dones.to(self.device), values.to(self.device) """ # 计算GAE with torch.no_grad(): returns, advantages = self.compute_advantages(rewards, values, dones) policy, new_values = self.actor_critic(states) # 损失计算 dist = Categorical(policy) new_log_probs = dist.log_prob(actions) prob_ratio = (new_log_probs - old_probs.detach()).exp() weighted_probs = advantages * prob_ratio clipped_probs = torch.clamp(prob_ratio, 1-self.policy_clip, 1+self.policy_clip) actor_loss = -torch.min(weighted_probs, clipped_probs * advantages).mean() critic_loss = 0.5 * (new_values.squeeze() - returns).pow(2).mean() entropy = dist.entropy().mean() entropy_coef = max(0.01, 0.05 * (1 - self.global_step / 1e5)) loss = actor_loss + 0.5 * critic_loss - entropy_coef * entropy self.optimizer.zero_grad() loss.backward() # 梯度裁剪 torch.nn.utils.clip_grad_norm_(self.actor_critic.parameters(), max_norm=1.0, norm_type=2.0) # 参数更新 self.optimizer.step() self.scheduler.step() # 记录指标 self.writer.add_scalar('Loss/Actor', actor_loss.item(), self.global_step) self.writer.add_scalar('Loss/Critic', critic_loss.item(), self.global_step) self.writer.add_scalar('Metrics/Entropy', entropy.item(), self.global_step) #self.writer.add_scalar('Grad/Total', total_grad, self.global_step) self.global_step += 1 self.reset_memory()
03-08
import torch import os import time import numpy as np from agent_target_dqn.conf.conf import Config from copy import deepcopy import torch.nn.functional as F class Algorithm: def __init__(self, model, optimizer, device=None, logger=None, monitor=None): self.device = device self.model = model self.optim = optimizer self.logger = logger self.monitor = monitor self.num_head = Config.NUMB_HEAD self._gamma = Config.GAMMA self.target_model = deepcopy(self.model) self.last_report_monitor_time = 0 self.train_step = 0 def learn(self, list_sample_data): t_data = list_sample_data obs = torch.stack([frame.obs for frame in t_data]) action = torch.stack( [frame.act if not torch.any(torch.isinf(frame.act)) else torch.zeros_like(frame.act) for frame in t_data] ) rew = torch.stack([frame.rew for frame in t_data]) _obs = torch.stack([frame._obs for frame in t_data]) not_done = torch.stack([frame.done for frame in t_data]) legal_action = torch.stack([frame.legal_action for frame in t_data]) self.target_model.eval() with torch.no_grad(): # Calculate the target Q-values for each head # 计算各个头的目标q值 q_targets = [] for head_idx in range(self.num_head): q_targets_head = ( rew[:, head_idx].unsqueeze(1) + self._gamma * (self.target_model(_obs)[0][head_idx]).max(1)[0].unsqueeze(1) * not_done[:, None] ) q_targets.append(q_targets_head) q_targets = torch.cat(q_targets, dim=1) * legal_action # Calculate the Q-values for each head # 计算各个头的q值 self.model.train() q_values = [] for head_idx in range(self.num_head): q_values_head = self.model(obs)[0][head_idx].gather(1, action[:, head_idx + 0].long().unsqueeze(1)) q_values.append(q_values_head) q_values = torch.cat(q_values, dim=1) * legal_action self.optim.zero_grad() loss = F.mse_loss(q_targets[:, 0].float(), q_values[:, 0].float()) for head_idx in range(1, self.num_head): loss += F.mse_loss(q_targets[:, head_idx].float(), q_values[:, head_idx].float()) loss.backward() model_grad_norm = torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0).item() self.optim.step() self.train_step += 1 if self.train_step % Config.TARGET_UPDATE_FREQ == 0: self.update_target_q() value_loss = loss.detach().item() target_q_value = q_targets.mean().detach().item() q_value = q_values.mean().detach().item() # Periodically report monitoring # 按照间隔上报监控 now = time.time() if now - self.last_report_monitor_time >= 60: monitor_data = { "value_loss": value_loss, "target_q_value": target_q_value, "q_value": q_value, "model_grad_norm": model_grad_norm, } self.monitor.put_data({os.getpid(): monitor_data}) self.logger.info( f"value_loss: {value_loss}, target_q_value: {target_q_value},\ q_value: {q_value},\ model_grad_norm: {model_grad_norm}" ) self.last_report_monitor_time = now def update_target_q(self): self.target_model.load_state_dict(self.model.state_dict()) 帮我在此代码的基础上增加ddqn,如果有需要在其他地方更改的代码,给我说
最新发布
08-11
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import rospy import numpy as np from geometry_msgs.msg import WrenchStamped from nav_msgs.msg import Odometry class TutorialNMPCController: def __init__(self): super(TutorialNMPCController, self).__init__() # NMPC控制器参数初始化 self._prediction_horizon = 0 self._control_horizon = 0 self._dt = 0.0 # 权重矩阵 self._Q = np.zeros(shape=(6, 6)) # 状态误差权重 self._R = np.zeros(shape=(6, 6)) # 控制输入权重 self._P = np.zeros(shape=(6, 6)) # 终端状态权重 # 约束参数 self._u_min = np.zeros(shape=(6,)) # 控制输入下限 self._u_max = np.zeros(shape=(6,)) # 控制输入上限 self._x_min = np.zeros(shape=(6,)) # 状态下限 self._x_max = np.zeros(shape=(6,)) # 状态上限 # 其他参数 self._solver_options = {} # 求解器选项 self._warm_start = False # 是否使用热启动 # 控制器状态 self._prev_control = np.zeros(shape=(6,)) self._predicted_states = None self._predicted_controls = None self._solver_status = None self._solve_time = 0.0 self._warm_start_solution = None # 系统状态 self._is_init = False self.odom_is_init = False self.current_odom = None # ROS相关初始化 self._control_pub = rospy.Publisher('thruster_output', WrenchStamped, queue_size=10) self._odom_sub = rospy.Subscriber('odom', Odometry, self._odom_callback) # 从ROS参数服务器加载参数 self._load_parameters() # 验证关键参数 if self._control_horizon <= 0: rospy.logwarn(f"Invalid control_horizon: {self._control_horizon}, setting to default (5)") self._control_horizon = 5 self._is_init = True rospy.loginfo(f"NMPC Controller initialized with control horizon: {self._control_horizon}") rospy.loginfo("NMPC Controller initialized successfully") def _odom_callback(self, msg): """里程计消息回调函数""" self.current_odom = msg self.odom_is_init = True # 提取位置和姿态信息 self.current_position = np.array([ msg.pose.pose.position.x, msg.pose.pose.position.y, msg.pose.pose.position.z ]) # 四元数转欧拉角 (简化处理,实际应用需使用完整转换) self.current_orientation = np.array([0, 0, 0]) # 示例,需完善 rospy.logdebug("Received odometry message") def _load_parameters(self): """从ROS参数服务器加载NMPC控制器参数""" if rospy.has_param('~prediction_horizon'): self._prediction_horizon = rospy.get_param('~prediction_horizon') rospy.loginfo(f'Prediction Horizon = {self._prediction_horizon}') else: rospy.logwarn('Parameter prediction_horizon not found, using default') self._prediction_horizon = 10 if rospy.has_param('~control_horizon'): self._control_horizon = rospy.get_param('~control_horizon') rospy.loginfo(f'Control Horizon = {self._control_horizon}') else: rospy.logwarn('Parameter control_horizon not found, using default') self._control_horizon = 5 if rospy.has_param('~dt'): self._dt = rospy.get_param('~dt') rospy.loginfo(f'Sampling Time = {self._dt}') else: rospy.logwarn('Parameter dt not found, using default') self._dt = 0.1 # 加载权重矩阵 # ... 其他参数加载代码保持不变 ... def _reset_controller(self): """重置NMPC控制器状态""" # 重置逻辑保持不变... def update_controller(self): """更新NMPC控制器并发布控制指令""" if not self._is_init: rospy.logwarn("Controller not initialized, skipping update") return False if not self.odom_is_init: rospy.logwarn("Odometry not received yet, skipping update") return False # 获取当前状态和参考轨迹 current_state = self._get_current_state() reference_trajectory = self._get_reference_trajectory() # 构建NMPC优化问题 problem = self._construct_optimization_problem(current_state, reference_trajectory) # 求解优化问题 solution, status, solve_time = self._solve_optimization_problem(problem) # 检查求解状态 if status != 'optimal': rospy.logwarn(f"NMPC solver did not converge: {status}") if self._prev_control is not None: # 使用上一次控制输入作为备份 control_input = self._prev_control else: # 如果没有上一次控制输入,则使用零控制 control_input = np.zeros(shape=(6,)) else: # 提取最优控制输入 control_input = self._extract_control_input(solution) self._prev_control = control_input # 保存当前控制输入供下次使用 # 打印控制输入用于调试 rospy.loginfo(f"Control input: {control_input}") # 发布控制指令 self.publish_control_wrench(control_input) # 保存求解信息用于调试 self._solver_status = status self._solve_time = solve_time return True def _get_current_state(self): """获取当前系统状态""" if self.current_odom is None: rospy.logerr("No odometry data available") return np.zeros(6) # 从里程计消息中提取状态 [x, y, z, vx, vy, vz] state = np.array([ self.current_odom.pose.pose.position.x, self.current_odom.pose.pose.position.y, self.current_odom.pose.pose.position.z, self.current_odom.twist.twist.linear.x, self.current_odom.twist.twist.linear.y, self.current_odom.twist.twist.linear.z ]) return state def _get_reference_trajectory(self): """获取参考轨迹""" # 示例:生成一个简单的参考轨迹 (保持当前位置) current_state = self._get_current_state() ref_traj = np.tile(current_state, (self._prediction_horizon + 1, 1)) # 添加一个小的目标偏移 (测试用) ref_traj[:, 0] += 1.0 # x方向偏移1米 return ref_traj def _construct_optimization_problem(self, current_state, reference_trajectory): """构建NMPC优化问题 (需根据具体求解器实现)""" # 实际应用中需替换为真实优化问题构建逻辑 # 这里使用简化示例,返回一个模拟问题 return {"current_state": current_state, "reference": reference_trajectory} def _solve_optimization_problem(self, problem): """求解NMPC优化问题""" # 添加调试日志 rospy.loginfo(f"Solving optimization problem with control horizon: {self._control_horizon}") # 确保 control_horizon 至少为1 control_horizon = max(1, self._control_horizon) # 返回固定控制输入 (x方向100推力) solution = np.tile(np.array([100, 0, 0, 0, 0, 0]), (control_horizon, 1)) rospy.loginfo(f"Solution shape: {solution.shape}") status = 'optimal' solve_time = 0.01 return solution, status, solve_time def _extract_control_input(self, solution): """从优化解中提取控制输入""" if solution is None or solution.size == 0: rospy.logerr("Received empty solution, using default control") return np.array([100, 0, 0, 0, 0, 0]) # 返回默认控制 # 提取第一个控制输入 return solution[0] def publish_control_wrench(self, control_input): """发布控制指令""" msg = WrenchStamped() msg.header.stamp = rospy.Time.now() msg.header.frame_id = 'base_link' msg.wrench.force.x = control_input[0] msg.wrench.force.y = control_input[1] msg.wrench.force.z = control_input[2] msg.wrench.torque.x = control_input[3] msg.wrench.torque.y = control_input[4] msg.wrench.torque.z = control_input[5] self._control_pub.publish(msg) rospy.logdebug(f"Published control wrench: {control_input}") if __name__ == '__main__': print('Tutorial - NMPC Controller') rospy.init_node('tutorial_nmpc_controller') try: node = TutorialNMPCController() # 设置更新频率 rate = rospy.Rate(10000) # 10Hz while not rospy.is_shutdown(): node.update_controller() rate.sleep() except rospy.ROSInterruptException: print('caught exception') print('exiting') 学习
05-14
import os import re import time import torch import torch.nn as nn import torch.optim as optim import pandas as pd import numpy as np import joblib # 添加缺失的导入 from datetime import datetime from torch.utils.data import Dataset, DataLoader, random_split from tqdm import tqdm import pickle import mysql.connector from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity from gensim.models import Word2Vec import spacy nlp = spacy.load("en_core_web_sm", disable=["parser", "ner"]) STOPWORDS = spacy.lang.en.stop_words.STOP_WORDS def clean_text(text): return re.sub(r'[^a-zA-Z0-9\s]', '', str(text)).strip().lower() def tokenize(text): """优化tokenize函数,移除冗余检查""" doc = nlp(clean_text(text)) return [token.text for token in doc if token.text not in STOPWORDS] # 移除isalnum检查 def preprocess(text): tokens = tokenize(text) return " ".join(tokens) class SemanticMatchModel(nn.Module): def __init__(self, input_dim): super().__init__() self.fc1 = nn.Linear(input_dim, 256) self.bn1 = nn.BatchNorm1d(256) self.fc2 = nn.Linear(256, 128) self.bn2 = nn.BatchNorm1d(128) self.fc3 = nn.Linear(128, 64) self.bn3 = nn.BatchNorm1d(64) self.fc4 = nn.Linear(64, 1) self.dropout = nn.Dropout(0.3) self.relu = nn.ReLU() self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.relu(self.bn1(self.fc1(x))) x = self.dropout(x) x = self.relu(self.bn2(self.fc2(x))) x = self.dropout(x) x = self.relu(self.bn3(self.fc3(x))) x = self.dropout(x) x = self.sigmoid(self.fc4(x)) return x class QADataset(Dataset): def __init__(self, qa_pairs, tfidf_vectorizer, negative_ratio=1.0): self.qa_pairs = qa_pairs self.vectorizer = tfidf_vectorizer self.samples = [] # 构建正样本 for i, (q, a) in enumerate(self.qa_pairs): self.samples.append((q, a, 1)) # 优化负样本构建逻辑 if negative_ratio > 0: total_pairs = len(self.qa_pairs) all_answers = [a for _, a in self.qa_pairs] # 预先生成负样本索引 neg_indices = np.random.choice( len(all_answers), size=int(total_pairs * negative_ratio), replace=True ) for idx, (q, a) in enumerate(self.qa_pairs): sample_count = int(negative_ratio) start = idx * sample_count end = start + sample_count for j in range(start, end): if j < len(neg_indices): neg_a = all_answers[neg_indices[j]] # 确保不是当前答案 if neg_a != a: self.samples.append((q, neg_a, 0)) def __len__(self): return len(self.samples) def __getitem__(self, idx): q, a, label = self.samples[idx] q_vec = self.vectorizer.transform([preprocess(q)]).toarray()[0] a_vec = self.vectorizer.transform([preprocess(a)]).toarray()[0] pair_vec = np.concatenate((q_vec, a_vec)) return torch.tensor(pair_vec, dtype=torch.float32), torch.tensor(label, dtype=torch.float32) class KnowledgeBase: def __init__(self, host='localhost', user='root', password='hy188747', database='ubuntu_qa', table='qa_pair', model_dir=r"D:\NLP-PT\PT4\model", negative_ratio=1.0): print("🔄 初始化知识库...") self.host = host self.user = user self.password = password self.database = database self.table = table self.model_dir = model_dir self.negative_ratio = negative_ratio os.makedirs(self.model_dir, exist_ok=True) self.qa_pairs = [] self.q_texts = [] self.a_texts = [] self.semantic_model = None self.word2vec_model = None self.tfidf_vectorizer = None self.tfidf_matrix = None # 调整初始化顺序 self.load_data_from_mysql() self.load_or_cache_processed_questions() self.load_cached_tfidf() self.load_cached_word2vec_model() # 最后加载模型(确保依赖项已初始化) model_path = os.path.join(self.model_dir, 'semantic_match_model.pth') if os.path.exists(model_path): self.load_model() else: print("⚠ 语义匹配模型未训练,请先训练模型。") def load_data_from_mysql(self): print("🔄 正在连接 MySQL,加载问答数据...") try: conn = mysql.connector.connect( host=self.host, user=self.user, password=self.password, database=self.database ) cursor = conn.cursor() query = f"SELECT question_text, answer_text FROM {self.table}" cursor.execute(query) rows = cursor.fetchall() self.qa_pairs = [(row[0], row[1]) for row in rows] self.q_texts = [pair[0] for pair in self.qa_pairs] self.a_texts = [pair[1] for pair in self.qa_pairs] print(f"✅ 成功从 MySQL 加载 {len(self.qa_pairs)} 条问答数据。") except Exception as e: print(f"❌ 数据库连接失败: {e}") self.qa_pairs = [] finally: if conn.is_connected(): conn.close() def load_or_cache_processed_questions(self): cache_path = os.path.join(self.model_dir, 'processed_questions.pkl') if os.path.exists(cache_path): print("🔄 使用缓存预处理后的分词文本。") with open(cache_path, 'rb') as f: self.processed_q_list = pickle.load(f) else: print("🔄 正在预处理问题文本(首次较慢)...") self.processed_q_list = [preprocess(q) for q in tqdm(self.q_texts)] with open(cache_path, 'wb') as f: pickle.dump(self.processed_q_list, f) print("✅ 预处理缓存已保存。") def load_cached_tfidf(self): cache_tfidf_matrix = os.path.join(self.model_dir, 'tfidf_matrix.npz') cache_qa_list = os.path.join(self.model_dir, 'tfidf_qa.pkl') tfidf_path = os.path.join(self.model_dir, 'tfidf_vectorizer.pkl') if os.path.exists(tfidf_path) and os.path.exists(cache_tfidf_matrix) and os.path.exists(cache_qa_list): print("🔄 加载 TF-IDF 缓存版本。") self.tfidf_vectorizer = joblib.load(tfidf_path) self.tfidf_matrix = np.load(cache_tfidf_matrix)['tfidf'] with open(cache_qa_list, 'rb') as f: self.tfidf_qa = pickle.load(f) else: print("🔄 创建并构建 TF-IDF(首次较慢)...") self.tfidf_vectorizer = TfidfVectorizer( tokenizer=lambda x: x.split(), lowercase=False, max_features=10000 ) self.tfidf_qa = self.processed_q_list self.tfidf_matrix = self.tfidf_vectorizer.fit_transform(self.tfidf_qa).toarray() print("✅ TF-IDF 构建完成。") joblib.dump(self.tfidf_vectorizer, tfidf_path) np.savez_compressed(cache_tfidf_matrix, tfidf=self.tfidf_matrix) with open(cache_qa_list, 'wb') as f: pickle.dump(self.tfidf_qa, f) def load_cached_word2vec_model(self): word2vec_path = os.path.join(self.model_dir, 'word2vec.model') if os.path.exists(word2vec_path): print("🔄 加载缓存中的 Word2Vec 模型...") self.word2vec_model = Word2Vec.load(word2vec_path) else: print("🔄 训练 Word2Vec 模型(首次较慢)...") tokenized_questions = [preprocess(q).split() for q in self.q_texts] self.word2vec_model = Word2Vec( sentences=tokenized_questions, vector_size=100, window=5, min_count=1, workers=4, epochs=10 ) self.word2vec_model.save(word2vec_path) print("✅ Word2Vec 模型训练完成并保存。") def sentence_to_vec(self, sentence): """修复空向量问题""" tokens = preprocess(sentence).split() if not tokens: return np.zeros(100) # 默认向量大小 if self.word2vec_model: vecs = [self.word2vec_model.wv[w] for w in tokens if w in self.word2vec_model.wv] return np.mean(vecs, axis=0) if vecs else np.zeros(self.word2vec_model.vector_size) else: vec = self.tfidf_vectorizer.transform([preprocess(sentence)]).toarray()[0] return vec def build_model(self, epochs=10, batch_size=128, lr=1e-3): # 创建数据集 full_dataset = QADataset(self.qa_pairs, self.tfidf_vectorizer, negative_ratio=self.negative_ratio) # 划分训练集/验证集 train_size = int(len(full_dataset) * 0.8) val_size = len(full_dataset) - train_size train_dataset, val_dataset = random_split(full_dataset, [train_size, val_size]) # 创建数据加载器 train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=2) val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=2) # 初始化模型 sample_input, _ = full_dataset[0] input_dim = sample_input.shape[0] self.semantic_model = SemanticMatchModel(input_dim) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") self.semantic_model.to(device) criterion = nn.BCELoss() optimizer = optim.Adam(self.semantic_model.parameters(), lr=lr) scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'max', patience=2, factor=0.5) # 训练模型 best_val_acc = 0.0 print("\n开始模型训练...") start_time = time.time() for epoch in range(epochs): self.semantic_model.train() total_loss, total_correct, total_samples = 0.0, 0, 0 for X_batch, y_batch in tqdm(train_loader, desc=f"Epoch {epoch + 1}/{epochs} - 训练中"): X_batch, y_batch = X_batch.to(device), y_batch.to(device) optimizer.zero_grad() outputs = self.semantic_model(X_batch).squeeze() loss = criterion(outputs, y_batch) loss.backward() optimizer.step() total_loss += loss.item() * len(y_batch) preds = (outputs >= 0.5).float() total_correct += (preds == y_batch).sum().item() total_samples += len(y_batch) train_loss = total_loss / total_samples train_acc = total_correct / total_samples # 验证阶段 self.semantic_model.eval() val_loss, val_correct, val_samples = 0.0, 0, 0 with torch.no_grad(): for X_val, y_val in val_loader: X_val, y_val = X_val.to(device), y_val.to(device) outputs_val = self.semantic_model(X_val).squeeze() loss_val = criterion(outputs_val, y_val) val_loss += loss_val.item() * len(y_val) preds_val = (outputs_val >= 0.5).float() val_correct += (preds_val == y_val).sum().item() val_samples += len(y_val) val_loss /= val_samples val_acc = val_correct / val_samples # 更新学习率 scheduler.step(val_acc) print(f"Epoch [{epoch + 1}/{epochs}] | " f"Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.4f} | " f"Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.4f}") # 保存最优模型 if val_acc > best_val_acc: best_val_acc = val_acc model_path = os.path.join(self.model_dir, 'semantic_match_model.pth') torch.save(self.semantic_model.state_dict(), model_path) print(f"✅ 新的最优模型已保存 (Val Acc: {best_val_acc:.4f})") end_time = time.time() print(f"\n训练完成,共耗时 {end_time - start_time:.2f} 秒。") # 加载最优模型权重 model_path = os.path.join(self.model_dir, 'semantic_match_model.pth') self.semantic_model.load_state_dict(torch.load(model_path, map_location=device)) self.semantic_model.eval() def load_model(self): """加载训练好的语义匹配模型""" input_dim = self.tfidf_matrix.shape[1] * 2 model_path = os.path.join(self.model_dir, 'semantic_match_model.pth') self.semantic_model = SemanticMatchModel(input_dim) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") self.semantic_model.load_state_dict(torch.load(model_path, map_location=device)) self.semantic_model.to(device) self.semantic_model.eval() print("✅ 语义匹配模型加载完成。") def retrieve(self, query, semantic_topk=100): # 粗检 query_tfidf = self.tfidf_vectorizer.transform([preprocess(query)]).toarray()[0] tfidf_scores = cosine_similarity([query_tfidf], self.tfidf_matrix).flatten() query_sent_vec = self.sentence_to_vec(query) sent_vecs = np.array([self.sentence_to_vec(q) for q in self.q_texts]) sent_scores = cosine_similarity([query_sent_vec], sent_vecs).flatten() # 归一化句子向量相似度到[0,1] sent_scores = (sent_scores + 1) / 2 sim_scores = tfidf_scores + sent_scores # 确保有足够的数据 if len(sim_scores) == 0: return ("抱歉,知识库中没有找到相关信息。", 0.0) topk_indices = np.argpartition(sim_scores, -semantic_topk)[-semantic_topk:] topk_indices = topk_indices[np.argsort(sim_scores[topk_indices])[::-1]] # 精检 if self.semantic_model: device = next(self.semantic_model.parameters()).device with torch.no_grad(): batch_inputs = [] for i in topk_indices: q = preprocess(self.q_texts[i]) a = preprocess(self.a_texts[i]) q_vec = self.tfidf_vectorizer.transform([q]).toarray()[0] a_vec = self.tfidf_vectorizer.transform([a]).toarray()[0] pair_input = np.concatenate((q_vec, a_vec)) batch_inputs.append(pair_input) if batch_inputs: batch_inputs = torch.tensor(np.stack(batch_inputs), dtype=torch.float32).to(device) batch_scores = self.semantic_model(batch_inputs).squeeze().cpu().numpy() semantic_scores = batch_scores else: semantic_scores = np.zeros(len(topk_indices)) # 综合得分(添加归一化权重) coarse_scores = sim_scores[topk_indices] / 2.0 # 归一化到[0,1] final_scores = 0.3 * coarse_scores + 0.7 * semantic_scores best_idx_in_topk = np.argmax(final_scores) best_idx = topk_indices[best_idx_in_topk] return self.qa_pairs[best_idx], final_scores[best_idx_in_topk] else: best_idx = topk_indices[0] if topk_indices.size > 0 else 0 return self.qa_pairs[best_idx], sim_scores[best_idx] def recommend_similar(self, query, topk=3): """修复索引越界问题""" query_tfidf = self.tfidf_vectorizer.transform([preprocess(query)]).toarray()[0] scores = cosine_similarity([query_tfidf], self.tfidf_matrix).flatten() # 安全获取topk索引 if len(scores) == 0: return [] if len(scores) < topk: topk = len(scores) topk_idx = np.argpartition(scores, -topk)[-topk:] topk_idx = topk_idx[np.argsort(scores[topk_idx])[::-1]] return [(self.q_texts[i], self.a_texts[i]) for i in topk_idx] class FeedbackRecorder: def __init__(self, file_path='unanswered_questions.csv'): self.file_path = file_path if not os.path.exists(self.file_path): with open(self.file_path, 'w', newline='', encoding='utf-8') as f: import csv csv.writer(f).writerow(['time', 'question']) def record_question(self, question): with open(self.file_path, 'a', newline='', encoding='utf-8') as f: import csv writer = csv.writer(f) writer.writerow([datetime.now().isoformat(), question]) def main(): kb = KnowledgeBase( host='localhost', user='root', password='hy188747', database='ubuntu_qa', table='qa_pair', model_dir=r"D:\NLP-PT\PT4\model", negative_ratio=1.0 ) if input("是否重新训练语义匹配模型?(y/n): ").strip().lower() == 'y': kb.build_model( epochs=5, batch_size=128, lr=1e-3 ) recorder = FeedbackRecorder() print("\n🎯 智能知识问答系统已启动(输入'q'退出聊天)\n") while True: query = input("🧐 问题:") if query.strip().lower() == 'q': break try: result, score = kb.retrieve(query) if result: print(f"💡 回答:{result[1]}") print(f"📊 匹配信心分数: {score:.4f}\n") else: print("⚠ 没有找到合适的答案,已将你的问题记录下来。") recorder.record_question(query) print("🔥 相似问题推荐:") similar_questions = kb.recommend_similar(query, topk=3) for q, a in similar_questions: print(f"Q: {q}\nA: {a}\n") except Exception as e: print(f"❌ 检索过程中发生错误: {e}") if __name__ == "__main__": main()
07-05
# 定义贝叶斯线性层 class BayesianLinear(nn.Module): """修正的贝叶斯全连接层""" def __init__(self, in_features, out_features): super().__init__() self.in_features = in_features self.out_features = out_features # 权重参数分布参数 self.weight_mu = nn.Parameter(torch.Tensor(out_features, in_features)) self.weight_rho = nn.Parameter(torch.Tensor(out_features, in_features)) # 偏置参数分布参数 self.bias_mu = nn.Parameter(torch.Tensor(out_features)) self.bias_rho = nn.Parameter(torch.Tensor(out_features)) # 初始化参数 self.reset_parameters() # 观测噪声 self.log_sigma = nn.Parameter(torch.zeros(1)) def reset_parameters(self): """初始化参数""" # 权重初始化 nn.init.kaiming_normal_(self.weight_mu, nonlinearity='relu') nn.init.normal_(self.weight_rho, mean=-3, std=0.1) # 偏置初始化 nn.init.zeros_(self.bias_mu) nn.init.normal_(self.bias_rho, mean=-3, std=0.1) def forward(self, x): # 采样权重 if self.training: weight_sigma = torch.log1p(torch.exp(self.weight_rho)) weight_epsilon = torch.randn_like(self.weight_mu) weight = self.weight_mu + weight_sigma * weight_epsilon # 采样偏置 bias_sigma = torch.log1p(torch.exp(self.bias_rho)) bias_epsilon = torch.randn_like(self.bias_mu) bias = self.bias_mu + bias_sigma * bias_epsilon else: weight = self.weight_mu bias = self.bias_mu # 确保输入维度正确 if x.dim() == 1: x = x.unsqueeze(0) # 添加批次维度 # 执行线性变换 return F.linear(x, weight, bias) def kl_loss(self): """计算KL散度损失""" # 权重KL项 weight_sigma = torch.log1p(torch.exp(self.weight_rho)) kl = -0.5 * torch.sum(torch.log(weight_sigma.pow(2) + 1e-8) - self.weight_mu.pow(2) - weight_sigma.pow(2) + 1) # 偏置KL项 bias_sigma = torch.log1p(torch.exp(self.bias_rho)) kl += -0.5 * torch.sum(torch.log(bias_sigma.pow(2) + 1e-8) - self.bias_mu.pow(2) - bias_sigma.pow(2) + 1) return kl / (self.in_features * self.out_features) # 归一化 def observation_noise(self): """返回观测噪声""" return torch.exp(self.log_sigma) + 1e-6 # 确保正值 class BayesianNN(nn.Module): def __init__(self, input_dim=8, hidden_dim=50, output_dim=1): super().__init__() self.fc1 = BayesianLinear(input_dim, hidden_dim) self.fc2 = BayesianLinear(hidden_dim, hidden_dim) self.fc3 = BayesianLinear(hidden_dim, output_dim) self.relu = nn.ReLU() def forward(self, x): x = self.relu(self.fc1(x)) x = self.relu(self.fc2(x)) x = self.fc3(x) return x def kl_loss(self): return self.fc1.kl_loss() + self.fc2.kl_loss() + self.fc3.kl_loss() def observation_noise(self): # 假设我们只使用最后一层的观测噪声 return self.fc3.observation_noise() # 初始化模型 model = BayesianNN() device = torch.device("cuda") model.to(device) optimizer = optim.Adam(model.parameters(), lr=0.01) def train(model, train_loader, optimizer, epochs=500, kl_weight=0.1): device = next(model.parameters()).device model.train() for epoch in range(epochs): total_nll = 0.0 total_kl = 0.0 total_loss = 0.0 num_batches = 0 for batch_idx, (x_batch, y_batch) in enumerate(train_loader): x_batch = x_batch.to(device) y_batch = y_batch.to(device) # 确保输入维度正确 if x_batch.dim() == 1: x_batch = x_batch.unsqueeze(0) if y_batch.dim() == 1: y_batch = y_batch.unsqueeze(1) optimizer.zero_grad() outputs = model(x_batch) # 获取标量噪声 sigma = model.observation_noise() # 计算负对数似然损失 squared_diff = (outputs - y_batch).pow(2) nll_loss = (0.5 * squared_diff / sigma.pow(2)).mean() + sigma.log() # 计算KL散度 kl_loss = model.kl_loss() # 总损失 batch_loss = nll_loss + kl_weight * kl_loss batch_loss.backward() # 梯度裁剪 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) optimizer.step() # 更新统计 total_nll += nll_loss.item() total_kl += kl_loss.item() total_loss += batch_loss.item() num_batches += 1 # 每50个epoch打印一次 if epoch % 50 == 0: avg_nll = total_nll / num_batches avg_kl = total_kl / num_batches avg_loss = total_loss / num_batches print(f"Epoch {epoch}: Avg NLL={avg_nll:.4f}, Avg KL={avg_kl:.4f}, Avg Total={avg_loss:.4f}") # 保存模型 torch.save(model.state_dict(), 'bayesian_model.pth') print("Training completed. Model saved.") if __name__ == "__main__": train(model, train_loader, optimizer, epochs=100, kl_weight=0.1) 在目前这个代码的基础上,给出最后可视化结果的图像,包含随着训练epoch的相关指标变化,以及真实值和预测值之间的关系,涨落,分布,等等
07-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值