从矩阵分解到聚类:构建可评估电影推荐系统的实战指南

news2026/5/24 4:22:12
1. 项目概述从零构建一个可评估的推荐引擎做推荐系统这些年我最大的感受是理论模型千千万但真正决定项目成败的往往不是选择了最前沿的算法而是对基础模型深刻的理解、扎实的工程实现以及一套严谨的评估对比流程。很多刚入行的朋友容易陷入追求复杂模型的误区却忽略了像矩阵分解这类经典方法所蕴含的巨大能量和工程友好性。这次我想抛开那些花哨的深度网络回归到推荐系统的基石——协同过滤特别是它的核心实现技术矩阵分解。我们将一起动手用Python实现一个完整的电影推荐系统并围绕三个核心模型展开一场“公平对决”非负矩阵分解NMF、截断奇异值分解SVD及其迭代优化版本SVD-I最后再引入K-Means聚类来探索用户分群对推荐效果的提升。我们的目标非常明确不是简单地调用库跑通流程而是要深入每个模型的“黑箱”理解其数学直觉亲手调参优化并用同一套数据、同一套评估标准RMSE, MAE来客观地评判它们的性能高下。你会发现即使是最“古老”的SVD在精心调优和迭代改进后其表现依然能让人眼前一亮。这个项目适合所有希望夯实推荐系统基础、理解模型本质并掌握一套完整从数据处理到模型评估、对比分析实战方法的朋友。2. 核心思路与方案选型为什么是矩阵分解聚类在开始写代码之前我们必须想清楚两个问题第一为什么选择矩阵分解作为核心第二为什么要在矩阵分解之后加入聚类2.1 协同过滤与矩阵分解的必然性传统的协同过滤主要分两类基于记忆的如用户/物品的K近邻和基于模型的。基于记忆的方法直观但面临严重的可扩展性和数据稀疏性问题。想象一个百万用户、十万电影的平台用户-物品评分矩阵是极其稀疏的绝大多数单元格是空的。基于记忆的方法需要计算大规模相似度计算开销巨大且稀疏性导致近邻查找不可靠。矩阵分解正是为了解决这些问题而生。它的核心思想是“降维”和“挖掘潜在特征”。我们不再直接处理巨大的稀疏矩阵而是假设存在一个低维的“潜在空间”。在这个空间里每个用户和每个物品都可以用一个低维向量隐向量来表示。用户对物品的评分近似等于该用户的隐向量与物品隐向量的内积。NMF和SVD是实现这一思想的两条经典路径。选择它们进行对比是因为它们代表了两种不同的约束和优化思路NMF的非负约束带来更好的可解释性如“用户偏好成分”、“电影主题成分”均为正而SVD基于严格的数学分解能最优地保留原始矩阵的方差信息。2.2 引入K-Means聚层的战略意义单纯的矩阵分解实现了“全局”的个性化即系统为每个用户生成独一无二的推荐列表。但我们可以更进一步如果能把兴趣相似的用户聚成一类我们就能实施“群体级”的个性化策略。这带来了几个好处解决冷启动对于一个全新用户在没有评分记录时可以将其快速归类到某个用户群然后推荐该群体喜欢的电影这比全局热门推荐更精准。提升推荐多样性在给单个用户推荐时不仅可以参考其个人隐向量还可以融合其所属群体的热门或高潜物品避免推荐列表过于狭窄。可解释性与运营分群结果本身具有业务意义例如“科幻动作迷”、“文艺片爱好者”、“合家欢观众”等便于产品运营进行定向推送和活动设计。因此我们的技术路线图就清晰了先用矩阵分解模型NMF/SVD得到用户和电影的隐向量表示再利用K-Means对用户隐向量进行聚类最后在推荐阶段融合个体预测和群体偏好。这个“分解聚类”的混合架构在保证预测精度的同时增强了系统的鲁棒性和可解释性。3. 实战环境搭建与数据准备理论聊完我们进入实战环节。一切从准备好环境和数据开始。3.1 工具链选型与配置我选择Python作为实现语言因为它拥有最丰富成熟的机器学习生态。核心库包括NumPy/Pandas数据操作的基石。处理大规模矩阵运算和表格数据离不开它们。Scikit-learn (sklearn)本项目的主力军。它提供了生产级的NMF、TruncatedSVD和KMeans实现接口统一性能经过高度优化。SciPy用于高效处理稀疏矩阵。我们的评分矩阵非常稀疏使用scipy.sparse中的csr_matrix或lil_matrix可以节省大量内存。Surprise可选一个专注于推荐系统的库内置了多种协同过滤算法和便捷的评估工具。但为了更透彻地理解原理我们主要用sklearn手动实现流程。Matplotlib/Seaborn用于结果可视化绘制RMSE随参数变化曲线、聚类散点图等。安装非常简单一行命令搞定pip install numpy pandas scikit-learn scipy matplotlib seaborn。建议使用Jupyter Notebook或VS Code等交互式环境方便分步执行和调试。3.2 数据集处理的艺术与陷阱我们使用经典的MovieLens数据集如ml-latest-small。这个数据集虽然“小”但足以说明问题且便于快速实验。关键步骤和注意事项如下1. 加载与初探import pandas as pd ratings pd.read_csv(ratings.csv) movies pd.read_csv(movies.csv) print(ratings.head()) print(f评分记录数: {len(ratings)}) print(f独立用户数: {ratings[userId].nunique()}) print(f独立电影数: {ratings[movieId].nunique()})首先了解数据规模、字段含义userId, movieId, rating, timestamp。计算矩阵的稀疏度1 - len(ratings) / (num_users * num_movies)通常会发现超过99%的单元格是空的这直观地展示了数据稀疏性的挑战。2. 构建用户-物品评分矩阵这是核心步骤也是第一个容易踩坑的地方。from scipy.sparse import csr_matrix # 创建从原始ID到矩阵行列索引的映射 user_ids ratings[userId].unique() movie_ids ratings[movieId].unique() user_to_idx {uid: i for i, uid in enumerate(user_ids)} movie_to_idx {mid: i for i, mid in enumerate(movie_ids)} # 使用CSR格式构建稀疏矩阵 rows ratings[userId].map(user_to_idx) cols ratings[movieId].map(movie_to_idx) data ratings[rating].values R_sparse csr_matrix((data, (rows, cols)), shape(len(user_ids), len(movie_ids)))注意务必使用稀疏矩阵格式。直接构建一个(num_users, num_movies)的稠密矩阵对于MovieLens 100k约10万评分可能还能撑住但对于更大规模的数据内存会瞬间爆炸。CSR格式在存储和矩阵运算上都非常高效。3. 处理缺失值矩阵填充策略对比矩阵分解模型通常要求输入是稠密矩阵。我们需要对缺失的评分即用户未看过的电影进行填充。常见的策略有全局均值用所有评分的平均值填充。最简单但过于粗糙。用户均值用每个用户的历史评分均值填充。考虑了用户评分尺度差异有的用户手松有的手紧。物品均值用每部电影获得的评分均值填充。考虑了电影本身的质量差异。零值填充将缺失值填0。在某些场景下有效但可能改变数据的分布。在论文中作者发现使用“用户均值”填充在NMF和SVD上取得了较好的效果。我们在实现时可以设计一个灵活的填充函数便于后续对比不同填充策略的影响。def fill_matrix_with_user_mean(sparse_matrix): 用用户均值填充稀疏矩阵中的缺失值 dense_matrix sparse_matrix.toarray() user_means dense_matrix.sum(axis1) / (dense_matrix ! 0).sum(axis1) # 忽略0值计算均值 # 对于每个用户将其缺失位置评分为0的位置填充为其均值 row_indices, col_indices np.where(dense_matrix 0) for r, c in zip(row_indices, col_indices): dense_matrix[r, c] user_means[r] return dense_matrix4. 数据标准化虽然评分通常在1-5之间但为了优化过程更稳定尤其是对SVD可以进行标准化。常见方法是MinMaxScaler缩放到[0,1]区间或者StandardScaler去均值归一化方差。但要注意预测后需要将结果反向转换回原始评分尺度才能计算误差。对于电影评分我个人经验是如果使用带有偏置项user-bias, item-bias的模型标准化很重要对于基础的NMF/SVD由于评分范围固定有时不标准化影响也不大但进行标准化通常是一个好习惯。4. 核心模型原理与实现细节接下来我们深入每一个模型的内部理解其数学本质并看如何用代码实现。4.1 非负矩阵分解NMF可解释的“零件拆解”NMF的目标是将一个非负矩阵$R$我们的评分矩阵分解为两个非负矩阵$W$和$H$的乘积即$R \approx W \times H$。其中$W$是用户-特征矩阵$H$是特征-电影矩阵。这里的“特征”是潜在因子可以理解为“电影主题成分”或“用户偏好维度”。数学直觉你可以把原始评分矩阵$R$看作是由一堆“基础零件”$H$中的行即潜在因子向量按不同比例$W$中的权重组合而成的。NMF要求所有“零件”和“组合比例”都为非负这使得分解结果具有直观的解释性。例如一个潜在因子可能代表“科幻动作成分”那么$H$中该因子对应每部电影的值表示电影的科幻动作含量$W$中该因子对应用户的值表示用户对科幻动作的喜爱程度。实现与关键参数 在sklearn中使用sklearn.decomposition.NMF。from sklearn.decomposition import NMF # 假设 R_filled 是经过填充和标准化后的稠密矩阵 nmf_model NMF(n_components15, initrandom, solvercd, max_iter500, random_state42) W nmf_model.fit_transform(R_filled) # 用户特征矩阵 H nmf_model.components_ # 电影特征矩阵 R_pred np.dot(W, H) # 重建的评分矩阵n_components (r)这是最重要的超参数代表潜在因子的数量。太小模型欠拟合无法捕捉复杂模式太大模型过拟合且计算量增加。需要通过交叉验证寻找最佳值论文中发现在15附近取得较好结果。init初始化方法。random随机初始化nndsvd基于SVD的智能初始化后者通常收敛更快、结果更稳定。solver优化算法。cd坐标下降是默认且稳定的选择。max_iter最大迭代次数。确保模型充分收敛。4.2 奇异值分解SVD与截断SVD数学最优的降维完整的SVD将一个矩阵$R$分解为$R U \Sigma V^T$其中$U$和$V$是正交矩阵$\Sigma$是对角阵奇异值。在推荐系统中我们使用截断SVDTruncated SVD即只保留最大的k个奇异值及其对应的左右奇异向量得到$R \approx U_k \Sigma_k V_k^T$。这里的$U_k$和$V_k^T$就对应了用户和电影在k维潜在空间中的表示。与NMF的核心区别SVD没有非负约束其分解基于最小化Frobenius范数意义下的最优近似具有严格的数学最优性。但它分解出的因子可能包含负值可解释性不如NMF。实现from sklearn.decomposition import TruncatedSVD svd_model TruncatedSVD(n_components16, algorithmrandomized, n_iter10, random_state42) U svd_model.fit_transform(R_filled) # 用户潜在表示 Sigma svd_model.singular_values_ # 奇异值 V_transpose svd_model.components_ # 电影潜在表示的转置 # 注意TruncatedSVD的transform输出是 U * Sigma即用户向量。要得到完整的低秩近似 R_pred_svd np.dot(U, V_transpose) # 更准确地说R_pred_svd np.dot(U * Sigma, V_transpose)不对。 # 实际上TruncatedSVD的components_就是V^T而fit_transform返回的是U * Sigma。 # 所以重建矩阵为fit_transform(R) * components_ R_pred_svd svd_model.inverse_transform(svd_model.transform(R_filled))4.3 迭代SVDSVD-I用迭代精炼提升性能这是论文中提到的一个有趣技巧也是工程上常用的后处理步骤。基本思想是用某种简单方法如用户均值填充缺失值得到初始矩阵$R^{(0)}$。对$R^{(0)}$进行截断SVD分解得到低秩近似$\hat{R}^{(1)}$。用$\hat{R}^{(1)}$中对应的值替换原始稀疏矩阵$R$中缺失位置的值形成新的填充矩阵$R^{(1)}$。重复步骤2和3直到预测矩阵的变化小于某个阈值或达到最大迭代次数。为什么有效初始的填充值是粗糙的估计。通过迭代SVD模型利用已知评分的信息不断修正对缺失值的估计使得填充值越来越符合模型学习到的全局潜在结构从而通常能获得比单次SVD更低的预测误差。实现伪代码def iterative_svd(R_sparse, n_components, max_iter20, tol1e-4): # 1. 初始填充 R_current fill_matrix_with_user_mean(R_sparse) rmse_prev np.inf for i in range(max_iter): # 2. 执行SVD svd TruncatedSVD(n_componentsn_components, random_state42) U svd.fit_transform(R_current) Vt svd.components_ R_pred np.dot(U, Vt) # 本次迭代的预测矩阵 # 3. 用预测值更新原始矩阵的缺失位置 R_next R_sparse.toarray().copy() # 获取原始稀疏矩阵的稠密形式有0值 missing_mask (R_sparse.toarray() 0) # 找到缺失位置 R_next[missing_mask] R_pred[missing_mask] # 仅更新缺失位置 # 计算RMSE变化在训练集上或使用一个固定的验证集 # 这里简单计算预测矩阵与当前填充矩阵的RMSE变化 rmse np.sqrt(mean_squared_error(R_current[~missing_mask], R_pred[~missing_mask])) if abs(rmse_prev - rmse) tol: print(f在迭代{i1}次后收敛。) break rmse_prev rmse R_current R_next return R_pred, svd # 返回最终的预测矩阵和模型4.4 K-Means聚类发现用户群落在得到用户的潜在表示NMF的W矩阵或SVD的U矩阵后我们将其作为特征输入K-Means算法。关键步骤特征选择直接使用降维后的用户隐向量。它的维度例如15或16维已经比原始的电影空间可能上万维低了很多且包含了用户的兴趣信息。确定聚类数k这是一个关键超参数。可以使用肘部法则Elbow Method——绘制不同k值对应的聚类内误差平方和Inertia选择曲线拐点。聚类与评估执行K-Means并为每个用户打上簇标签。from sklearn.cluster import KMeans # 假设 user_features 是从NMF或SVD得到的用户矩阵形状为 (n_users, n_components) inertias [] K_range range(2, 21) for k in K_range: kmeans KMeans(n_clustersk, random_state42, n_initauto) kmeans.fit(user_features) inertias.append(kmeans.inertia_) # 绘制肘部法则图 plt.plot(K_range, inertias, bx-) plt.xlabel(k) plt.ylabel(Inertia) plt.title(Elbow Method For Optimal k) plt.show() # 假设根据图表选择 k5 optimal_k 5 final_kmeans KMeans(n_clustersoptimal_k, random_state42, n_initauto) user_clusters final_kmeans.fit_predict(user_features)5. 模型训练、评估与对比分析模型建好了接下来就是看它们到底谁更胜一筹。我们需要一套公平、严谨的评估流程。5.1 数据集划分与评估指标绝对不能在同一份数据上训练和测试我们采用经典的留出法Hold-out按时间戳或随机划分一部分数据作为测试集如20%。from sklearn.model_selection import train_test_split # 假设我们有一个包含所有(user_idx, movie_idx, rating)的列表 train_data, test_data train_test_split(all_ratings_list, test_size0.2, random_state42) # 根据train_data构建训练矩阵 R_train_sparse # 注意填充操作只能在训练集上进行用训练集的用户均值去填充训练矩阵然后用同样的均值或模型去处理测试集 # 更严谨的做法定义一个预处理管道在训练集上拟合填充器然后转换训练集和测试集。对于测试集我们评估的是模型对“已观测评分”的预测能力。常用的指标有均方根误差RMSE$RMSE \sqrt{\frac{1}{N} \sum (r_{ui} - \hat{r}_{ui})^2}$。对较大误差惩罚更重是推荐系统最常用的精度指标。平均绝对误差MAE$MAE \frac{1}{N} \sum |r_{ui} - \hat{r}_{ui}|$。解释更直观。我们的目标是最小化这些误差。5.2 超参数调优实战寻找最佳因子数以NMF为例我们需要寻找最佳的n_components。我们通过网格搜索配合交叉验证来完成。from sklearn.model_selection import GridSearchCV from sklearn.metrics import mean_squared_error, make_scorer import numpy as np # 由于NMF不支持直接输入稀疏矩阵进行填充后的拟合我们需要包装一下 # 定义一个简单的包装器处理填充和模型训练 class NMFRecommender: def __init__(self, n_components, fill_methoduser_mean): self.n_components n_components self.fill_method fill_method self.model NMF(n_componentsn_components, initnndsvd, random_state42) self.user_means_ None def fit(self, R_sparse): # 在训练集上计算用户均值 dense_train R_sparse.toarray() self.user_means_ np.zeros(dense_train.shape[0]) for i in range(dense_train.shape[0]): user_ratings dense_train[i] rated user_ratings ! 0 if rated.any(): self.user_means_[i] user_ratings[rated].mean() else: self.user_means_[i] dense_train.mean() # 全局均值回退 # 用用户均值填充训练矩阵 R_filled dense_train.copy() for i in range(R_filled.shape[0]): mask R_filled[i] 0 R_filled[i, mask] self.user_means_[i] self.model.fit(R_filled) return self def predict(self, user_idx, movie_idx): # 预测需要重建整个矩阵对于小规模可行大规模需优化 W self.model.transform(self.R_filled_train_) # 需要存储训练时的填充矩阵 H self.model.components_ R_pred np.dot(W, H) return R_pred[user_idx, movie_idx] # 由于上述包装和评估流程较复杂在实际中我们更常用简单的循环来搜索 component_range [5, 10, 15, 20, 25, 30] rmse_scores [] for r in component_range: nmf NMF(n_componentsr, initnndsvd, random_state42) # 在训练集填充矩阵上训练 W_train nmf.fit_transform(R_train_filled) H_train nmf.components_ R_train_pred np.dot(W_train, H_train) # 在测试集上预测需要为测试集用户和电影生成预测 # 这里简化处理用训练好的模型直接为测试集索引预测 # 注意测试集中的用户和电影必须在训练集中出现过否则是冷启动问题这里暂不考虑。 # 我们假设测试集是训练集的子集。 predictions [] truths [] for u_idx, m_idx, true_r in test_data: # test_data中是原始索引 pred_r R_train_pred[u_idx, m_idx] predictions.append(pred_r) truths.append(true_r) rmse np.sqrt(mean_squared_error(truths, predictions)) rmse_scores.append(rmse) print(fComponents: {r}, RMSE: {rmse:.4f})通过绘制n_components与RMSE的关系曲线我们可以找到使测试集误差最小的点即为最佳因子数。论文中NMF在15SVD在16附近取得最优这与我们的实验经验是吻合的。5.3 性能对比与结果分析将NMF、SVD-T和SVD-I在最佳参数下的性能进行对比模型最佳因子数 (r)填充方法测试集RMSE测试集MAE特点NMF15用户均值0.9180.723可解释性强分解结果非负适合评分这类非负数据。SVD-T16用户均值0.9180.720数学基础坚实计算稳定是最经典的矩阵降维方法。SVD-I16迭代优化0.8980.705通过迭代精炼填充值通常能获得比单次分解更优的精度。结果解读NMF vs. SVD-T在最优参数下两者达到了惊人的一致RMSE0.918。这说明对于电影评分这个具体任务两种方法在捕捉主要潜在模式的能力上旗鼓相当。选择哪一个可能更多取决于侧重点重可解释性选NMF重数学简洁和稳定性选SVD。SVD-I的优势迭代SVD将RMSE进一步降低了约0.02这是一个显著的提升。这验证了迭代精炼策略的有效性。其代价是增加了计算时间需要多次运行SVD但在对精度要求高的场景下是值得的。关于“过拟合”在调整因子数r时我们会观察到随着r增大训练误差持续下降但测试误差会先降后升。测试误差最低点对应的r就是最佳复杂度。盲目增大r只会导致模型记住训练数据中的噪声从而降低泛化能力。5.4 聚类如何提升推荐效果得到用户分群后我们可以设计混合推荐策略策略一群体热门推荐。对于群组内的用户除了个人预测评分还可以考虑该群体内评分最高的电影群体热门作为补充推荐项增加推荐的多样性和新颖性。策略二聚类中心作为先验。在为新用户冷启动生成隐向量时可以将其初始值设为其所属簇的质心从而更快地收敛到合理的偏好表示。评估聚类效果我们可以通过轮廓系数Silhouette Score来量化聚类的紧密度和分离度。更重要的是可以进行人工抽样分析随机检查每个簇的用户历史评分最高的电影看是否具有明显的主题一致性。例如你可能会发现一个簇的用户普遍喜欢《盗梦空间》、《星际穿越》而另一个簇喜欢《肖申克的救赎》、《阿甘正传》。这直观地证明了聚类抓住了用户的兴趣模式。6. 工程实践中的挑战、技巧与避坑指南纸上得来终觉浅绝知此事要躬行。下面分享一些在真实项目中积累的经验和踩过的坑。6.1 性能与扩展性优化当用户和物品数量达到百万甚至千万级时直接使用上述方法会面临巨大挑战。稀疏矩阵操作始终使用scipy.sparse格式存储和计算。sklearn的许多算法如TruncatedSVD支持稀疏矩阵输入但NMF的默认求解器可能不支持需要检查或使用其他库如nimfa。增量学习与在线更新用户行为是实时产生的。完全重新训练模型不现实。可以研究增量矩阵分解算法或采用交替最小二乘ALS这种易于并行化和增量更新的优化方法。对于SVD有增量SVD如sklearn.decomposition.IncrementalPCA的思路或流式学习算法。分布式计算对于超大规模数据需要借助Spark MLlib其ALS算法非常成熟或TensorFlow等分布式框架进行模型训练。6.2 冷启动问题的应对策略冷启动分为用户冷启动新用户和物品冷启动新电影。我们的“矩阵分解聚类”框架部分缓解了用户冷启动利用聚类新用户注册时可以通过一个简短的兴趣问卷将其映射到已有的用户聚类中然后推荐该聚类的热门物品。融合内容信息这是更强大的方法。将电影的元数据类型、导演、演员通过文本处理如TF-IDF或嵌入技术转化为特征向量与协同过滤的隐向量结合形成混合推荐系统。这样即使一部新电影没有任何评分也能根据其内容特征找到相似电影从而获得初始推荐能力。6.3 推荐质量评估超越RMSERMSE/MAE衡量的是预测评分的准确性但这不等于推荐列表的好坏。一个好的推荐系统还需要关注Top-N推荐精度如精确率PrecisionK、召回率RecallK、F1值。这衡量的是给用户推荐的K个物品中有多少是用户真正喜欢的。多样性推荐列表是否覆盖了用户不同的兴趣点计算推荐物品之间的相似度或衡量列表的品类分布。新颖性是否推荐了用户不太可能自己发现但可能感兴趣的非热门物品实时性/覆盖率系统能否快速对新行为做出反应能否为所有物品提供被推荐的机会在实际项目中需要结合A/B测试和线上业务指标如点击率、观看时长、转化率来综合评估系统效果。6.4 常见陷阱与调试技巧数据泄露在填充缺失值或进行特征工程时绝对不能使用测试集的信息。例如计算用户均值必须仅基于训练集然后将这个均值应用到测试集的对应用户上。评分偏差处理不同用户评分尺度差异很大。除了使用用户均值填充在矩阵分解模型中显式地建模用户偏置User Bias和物品偏置Item Bias是更专业的做法。预测公式变为$\hat{r}_{ui} \mu b_u b_i q_i^T p_u$其中$\mu$是全局均值$b_u$和$b_i$是偏置项。这能显著提升模型性能。隐向量初始化NMF和SVD的结果对初始化敏感。虽然sklearn的initnndsvd通常不错但对于非常重要的任务可以尝试多次随机初始化选择最优结果。迭代不收敛NMF或迭代SVD可能不收敛。增加max_iter减小tol容忍度或检查数据中是否有异常值如极端评分。内存溢出当尝试将大型稀疏矩阵转换为稠密矩阵进行填充时最容易发生。始终评估矩阵大小num_users * num_movies * 8 bytes如果过大必须寻求其他方案如使用仅处理观测值的优化算法如Spark ALS或采用分块、采样技术。构建一个工业级的推荐系统远比这个实验项目复杂它涉及实时数据管道、模型在线服务、复杂的排序策略等。但万变不离其宗深入理解NMF、SVD这些基础模型的原理和特性掌握从数据预处理、模型训练、评估到调优的完整闭环是你应对任何复杂推荐系统挑战的坚实起点。希望这个详细的实践指南和对比分析能帮助你不仅跑通代码更能建立起对推荐系统核心技术的直觉和自信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2639767.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…