资讯详情 西瓜书第三章Python实现:线性模型维度对齐与NumPy实战
📅 2026/10/12 10:09:28
简介本资源是《机器学习》周志华著俗称“西瓜书”第三章“线性模型”的配套Python实践代码包面向机器学习初学者与高校课程学习者聚焦对率回归与线性判别分析两大核心算法的原理验证与实操落地。资源包含8个文件涵盖5个功能明确的Python脚本分别实现3.3/3.4/3.5节算法及交叉验证对比、1个Excel格式的Diabetes数据集、1个CSV格式的breast_cancer数据集以及1个txt格式的西瓜数据集3.0α原始样本整体压缩包仅78KB轻量易用。已有3196人学习下载说明其在教学辅助与课后复现中具备广泛参考价值。读者可直接运行代码复现教材实验结果清晰对比10折交叉验证与留一法在不同UCI数据集上的泛化性能差异并获得完整可调试的LDA与逻辑回归实现流程含数据加载、模型训练、预测评估与结果可视化等关键环节显著降低理论到代码的转化门槛。1. 为什么你照着《西瓜书》第三章推导完公式一写 Python 就报错维度不匹配“西瓜书”第三章讲线性模型表面看就是 y wᵀx b 这么简单——但真正动手用 NumPy 实现最小二乘、梯度下降、对数几率回归时90% 的人卡在第一行X.shape和y.shape不对齐上。这不是你数学没学好而是周志华教授写的是理论推导范式向量内积、矩阵求导、凸优化而 Python 数值计算要的是严格对齐的数组形状、明确的广播规则、可微分的数值稳定性处理。我带过三届西电、山大机器学习课设学生最常发来的截图是ValueError: operands could not be broadcast together或dot() argument after 1st must be a matrix or 2-d array。这篇笔记不讲定义、不复述教材只聚焦一个目标用最简、最贴近西瓜书数学表达的 Python 代码跑通第三章全部核心算法——线性回归、对数几率回归逻辑回归、线性判别分析LDA——且每一步都告诉你为什么这么写、shape 怎么对、参数怎么初始化、loss 怎么监控。适合正在啃西瓜书第三章、手边有习题3.3/3.4/3.5想验证、或期末前突击实现的同学。所有代码基于纯 NumPy无 sklearn零依赖可直接粘贴进.py文件运行。2. 线性回归从西瓜书公式到 NumPy 实现的三步对齐西瓜书公式 (3.10) 给出闭式解w (XᵀX)⁻¹Xᵀy*。但直接套用会翻车——因为书中 X 是 m×(d1) 设计矩阵含偏置列而初学者常把原始特征 X 当成 m×d忘了拼接全 1 列。本节带你用三步完成“理论→代码”的严格对齐。2.1 构造符合西瓜书定义的设计矩阵 X西瓜书明确要求X 的第 0 列为全 1 向量对应偏置项 b见公式 3.2。很多同学用np.hstack([np.ones((m,1)), X_raw])但若X_raw是 (m,) 一维数组hstack会报错。必须先确保X_raw是二维。import numpy as np def build_design_matrix(X_raw, add_biasTrue): 构造西瓜书标准设计矩阵 X X_raw: shape (m, d) 或 (m,) - 自动reshape为 (m, 1) add_bias: 是否添加全1偏置列对应公式3.2 返回: X, shape (m, d1) if add_bias else (m, d) if X_raw.ndim 1: X_raw X_raw.reshape(-1, 1) # 强制转为 (m, 1) if add_bias: X np.hstack([np.ones((X_raw.shape[0], 1)), X_raw]) else: X X_raw return X # 示例模拟西瓜书表3.1的“好瓜”数据仅用密度、含糖率两特征 # 假设我们有8个样本密度和含糖率单位已归一化 X_raw np.array([ [0.697, 0.460], [0.774, 0.376], [0.634, 0.264], [0.608, 0.318], [0.556, 0.215], [0.403, 0.237], [0.481, 0.149], [0.437, 0.211] ]) # shape (8, 2) y np.array([1, 1, 1, 1, 0, 0, 0, 0]) # 1好瓜0坏瓜注意这是分类标签但线性回归先拟合连续值 X build_design_matrix(X_raw) # shape (8, 3): [1, x1, x2] print(fX shape: {X.shape}, y shape: {y.shape}) # X shape: (8, 3), y shape: (8,)关键说明build_design_matrix强制处理了一维输入避免reshape错误np.ones((m,1))生成列向量与hstack兼容。这一步是后续所有矩阵运算正确的前提——西瓜书公式默认 X 已含偏置列代码必须先构造它。2.2 闭式解实现严格遵循公式 (3.10)并处理不可逆情况公式 (3.10) 要求计算(XᵀX)⁻¹但XᵀX可能奇异如特征共线、样本数 特征数。西瓜书未展开此细节但实际代码必须防御。def linear_regression_closed_form(X, y, ridge_alpha0.0): 线性回归闭式解含岭回归正则化 X: design matrix, shape (m, d1) y: target vector, shape (m,) ridge_alpha: 岭回归正则化系数0.0 为纯最小二乘 返回: w, shape (d1,), 其中 w[0] 是偏置 b # 计算 X.T X (d1, d1) XtX X.T X # 添加岭回归正则项XtX alpha * I if ridge_alpha 0.0: XtX ridge_alpha * np.eye(XtX.shape[0]) # 使用 np.linalg.pinv 替代 np.linalg.inv自动处理奇异矩阵 # pinv 返回 Moore-Penrose 伪逆比 inv 更鲁棒 XtX_pinv np.linalg.pinv(XtX) # w (X^T X)^{-1} X^T y w XtX_pinv X.T y return w # 执行闭式解 w_closed linear_regression_closed_form(X, y) print(f闭式解 w {w_closed.round(4)}) # w[0] 是 b, w[1:] 是特征权重参数说明ridge_alpha是西瓜书未提但工程必备的参数。当XᵀX接近奇异时如特征高度相关np.linalg.inv直接报错而pinv仍能返回合理解。ridge_alpha1e-5是常见起点可防止数值震荡。这不是“加了正则”而是让西瓜书公式在真实数据上稳定运行的必要补丁。2.3 梯度下降实现手动推导损失函数与梯度对标西瓜书习题3.4西瓜书习题3.4要求推导梯度下降更新式。公式 (3.11) 给出损失函数 J(w) 1/2m Σ(yᵢ - wᵀxᵢ)²。其梯度 ∇J(w) -1/m Xᵀ(y - Xw)。代码必须严格按此实现而非调用高级库。def linear_regression_gd(X, y, lr0.01, max_iter1000, tol1e-6): 线性回归梯度下降实现西瓜书习题3.4推导式 X: design matrix, shape (m, d1) y: target vector, shape (m,) lr: 学习率 max_iter: 最大迭代次数 tol: 损失变化容忍度 返回: w_history, loss_history m, d_plus1 X.shape w np.random.normal(0, 0.01, sized_plus1) # 随机初始化小方差防爆炸 loss_history [] for i in range(max_iter): # 计算预测值: X w, shape (m,) y_pred X w # 计算损失: J(w) 1/(2m) * sum((y - y_pred)^2) loss np.mean((y - y_pred) ** 2) / 2 loss_history.append(loss) # 计算梯度: ∇J(w) -1/m * X.T (y - y_pred) grad - (X.T (y - y_pred)) / m # 更新 w: w : w - lr * grad w_new w - lr * grad # 检查收敛损失变化小于 tol if i 0 and abs(loss_history[-2] - loss) tol: print(fGD converged at iteration {i}) break w w_new return w, loss_history # 执行梯度下降 w_gd, loss_hist linear_regression_gd(X, y, lr0.05, max_iter2000) print(fGD解 w {w_gd.round(4)})逻辑说明y_pred X w是向量化计算等价于西瓜书中的 Σwⱼxⱼgrad表达式完全复刻习题3.4推导结果lr0.05是针对本例数据的经验值太大易震荡太小收敛慢。注意此处 y 是 0/1 标签但线性回归输出是连续值后续用于分类需阈值判断——这正是西瓜书引出对数几率回归的动机。3. 对数几率回归从线性回归到概率建模的跃迁西瓜书第三章核心转折点在于线性回归输出连续值但分类任务需要概率输出。公式 (3.18) 定义对数几率函数y ln(p/(1-p)) wᵀx b即 p 1/(1exp(-(wᵀxb)))。实现难点不在公式而在损失函数选择与梯度计算——西瓜书用极大似然导出交叉熵损失但初学者常误用 MSE。3.1 交叉熵损失函数与梯度严格对标西瓜书公式 (3.27)西瓜书公式 (3.27) 给出对数几率回归的损失函数负对数似然J(w) -Σ[yᵢ ln(σ(zᵢ)) (1-yᵢ) ln(1-σ(zᵢ))]其中 σ(z) 1/(1exp(-z))。其梯度 ∇J(w) 1/m Σ(σ(zᵢ) - yᵢ) xᵢ。代码必须用此梯度而非线性回归的 MSE 梯度。def sigmoid(z): 稳定计算 sigmoid避免 exp(z) 溢出 # 对 z0 和 z0 分别处理 z_pos np.where(z 0, z, 0) z_neg np.where(z 0, z, 0) return 1 / (1 np.exp(-z_pos)) * (1 - np.exp(-z_neg)) np.exp(z_neg) / (1 np.exp(z_neg)) def logistic_regression_loss_grad(X, y, w): 计算对数几率回归的交叉熵损失与梯度 X: design matrix, shape (m, d1) y: binary labels, shape (m,), values in {0,1} w: weights, shape (d1,) 返回: loss (scalar), grad (shape (d1,)) z X w # 线性组合shape (m,) y_pred_prob sigmoid(z) # 预测概率shape (m,) # 交叉熵损失J(w) -mean( y*log(p) (1-y)*log(1-p) ) # 使用 np.clip 防止 log(0) - -inf p_clip np.clip(y_pred_prob, 1e-15, 1 - 1e-15) loss -np.mean(y * np.log(p_clip) (1 - y) * np.log(1 - p_clip)) # 梯度∇J(w) 1/m * X.T (p - y) grad X.T (y_pred_prob - y) / len(y) return loss, grad # 验证梯度计算数值梯度检验确保解析梯度正确 def numerical_gradient(func, X, y, w, eps1e-5): 数值梯度检验用于 debug grad_num np.zeros_like(w) for i in range(len(w)): w_plus w.copy() w_minus w.copy() w_plus[i] eps w_minus[i] - eps loss_plus, _ func(X, y, w_plus) loss_minus, _ func(X, y, w_minus) grad_num[i] (loss_plus - loss_minus) / (2 * eps) return grad_num # 测试梯度 w_test np.array([0.0, 0.0, 0.0]) loss_test, grad_analytic logistic_regression_loss_grad(X, y, w_test) grad_numeric numerical_gradient(logistic_regression_loss_grad, X, y, w_test) print(fAnalytic grad: {grad_analytic.round(6)}) print(fNumeric grad: {grad_numeric.round(6)}) print(fGradient error: {np.max(np.abs(grad_analytic - grad_numeric)):.2e})关键说明sigmoid函数做了数值稳定处理避免exp(100)溢出np.clip防止log(0)梯度X.T (p - y)是西瓜书公式 (3.27) 的向量化实现。数值梯度检验是调试此类算法的后悔药——每次改梯度公式务必跑一遍误差 1e-5 才可信。3.2 对数几率回归训练SGD 与收敛监控西瓜书强调对数几率回归是凸优化问题但 SGD 实现需注意 batch size 与学习率。def logistic_regression_sgd(X, y, lr0.1, max_iter1000, batch_size1, tol1e-5): 对数几率回归随机梯度下降SGD batch_size1 为纯 SGD1 为 mini-batch m, d_plus1 X.shape w np.random.normal(0, 0.01, sized_plus1) loss_history [] for epoch in range(max_iter): # 随机打乱数据 indices np.random.permutation(m) X_shuffled X[indices] y_shuffled y[indices] # mini-batch 更新 for i in range(0, m, batch_size): X_batch X_shuffled[i:ibatch_size] y_batch y_shuffled[i:ibatch_size] # 计算当前 batch 的损失与梯度 loss_batch, grad_batch logistic_regression_loss_grad(X_batch, y_batch, w) w w - lr * grad_batch # 每轮计算全量损失 loss_full, _ logistic_regression_loss_grad(X, y, w) loss_history.append(loss_full) # 收敛检查 if len(loss_history) 1 and abs(loss_history[-2] - loss_full) tol: print(fLogistic GD converged at epoch {epoch}) break return w, loss_history # 训练对数几率回归 w_lr, loss_lr logistic_regression_sgd(X, y, lr0.1, max_iter500) print(fLogistic Regression w {w_lr.round(4)}) # 预测计算概率并阈值化 z_pred X w_lr p_pred sigmoid(z_pred) y_pred (p_pred 0.5).astype(int) print(fPredictions: {y_pred}) print(fTrue labels: {y})参数说明lr0.1是对数几率回归常用起点比线性回归大因 sigmoid 梯度在 z0 附近最大batch_size1是纯 SGD收敛波动大但快tol1e-5比线性回归更严因交叉熵损失曲面更平缓。注意y_pred 是概率需0.5才得分类结果——这正是西瓜书图3.3所示的S型曲线决策边界。4. 线性判别分析LDA西瓜书公式 (3.39) 的完整实现与降维验证西瓜书公式 (3.39) 给出 LDA 投影方向w ∝ S_w⁻¹ (μ₀ - μ₁)其中 S_w 是类内散度矩阵。实现难点在于1S_w 必须可逆2多类扩展需广义特征值分解3投影后如何可视化。本节聚焦二类 LDA并验证其降维效果。4.1 类内散度矩阵 S_w 与类间散度矩阵 S_b 的构造西瓜书定义 S_w Σₖ Σ_{x∈Xₖ} (x - μₖ)(x - μₖ)ᵀS_b (μ₀ - μ₁)(μ₀ - μ₁)ᵀ。注意此处 x 是原始特征向量不含偏置列与线性回归的 X 不同。def compute_lda_matrices(X_raw, y): 计算 LDA 的类内散度矩阵 S_w 和类间散度矩阵 S_b X_raw: raw features, shape (m, d) y: binary labels, shape (m,), values in {0,1} 返回: S_w (d,d), S_b (d,d), mu0, mu1 # 分离两类样本 X0 X_raw[y 0] X1 X_raw[y 1] # 计算各类均值 mu0 np.mean(X0, axis0) # shape (d,) mu1 np.mean(X1, axis0) # shape (d,) # 计算类内散度矩阵 S_w S_0 S_1 # S_k Σ (x_i - mu_k)(x_i - mu_k).T S0 np.zeros((X_raw.shape[1], X_raw.shape[1])) for x in X0: diff (x - mu0).reshape(-1, 1) S0 diff diff.T S1 np.zeros((X_raw.shape[1], X_raw.shape[1])) for x in X1: diff (x - mu1).reshape(-1, 1) S1 diff diff.T S_w S0 S1 # 计算类间散度矩阵 S_b (mu0 - mu1)(mu0 - mu1).T diff_mu (mu0 - mu1).reshape(-1, 1) S_b diff_mu diff_mu.T return S_w, S_b, mu0, mu1 # 计算矩阵 S_w, S_b, mu0, mu1 compute_lda_matrices(X_raw, y) print(fS_w shape: {S_w.shape}, S_b shape: {S_b.shape})逻辑说明X_raw是原始特征无偏置列因 LDA 作用于原始空间S_w通过显式循环计算清晰对应西瓜书定义S_b是外积结果为秩1矩阵。注意S_w 必须满秩才能求逆若不满秩如样本少于特征数需加正则项S_w alpha * I。4.2 求解最优投影方向 w 并验证分类效果西瓜书公式 (3.39) 给出 w ∝ S_w⁻¹(μ₀ - μ₁)。但S_w可能奇异故用np.linalg.pinv。def lda_project_and_classify(X_raw, y, w_ldaNone): LDA 投影与分类 若 w_lda 为 None则计算最优 w否则直接用给定 w 返回: projected points (m,), accuracy if w_lda is None: S_w, S_b, mu0, mu1 compute_lda_matrices(X_raw, y) # 使用伪逆处理奇异 S_w S_w_pinv np.linalg.pinv(S_w) w_lda S_w_pinv (mu0 - mu1) # shape (d,) w_lda w_lda / np.linalg.norm(w_lda) # 归一化 # 投影到一维z X_raw w_lda z X_raw w_lda # shape (m,) # 计算投影后的类中心 z0 z[y 0] z1 z[y 1] z0_mean np.mean(z0) z1_mean np.mean(z1) # 决策边界取类中心中点 threshold (z0_mean z1_mean) / 2 y_pred (z threshold).astype(int) # 计算准确率 acc np.mean(y_pred y) return z, w_lda, threshold, acc # 执行 LDA z_lda, w_lda, th_lda, acc_lda lda_project_and_classify(X_raw, y) print(fLDA Accuracy: {acc_lda:.3f}) print(fLDA projection w {w_lda.round(4)}) print(fThreshold: {th_lda:.4f}) # 可视化投影结果需 matplotlib import matplotlib.pyplot as plt plt.figure(figsize(8, 4)) plt.scatter(z_lda[y0], np.zeros_like(z_lda[y0]), cred, labelBad Melon, alpha0.7) plt.scatter(z_lda[y1], np.zeros_like(z_lda[y1]), cgreen, labelGood Melon, alpha0.7) plt.axvline(th_lda, colorblack, linestyle--, labelfThreshold{th_lda:.3f}) plt.xlabel(Projected Value z) plt.title(LDA Projection of Watermelon Data) plt.legend() plt.grid(True, alpha0.3) plt.show()关键说明w_lda是单位向量保证投影尺度一致决策阈值取两类投影均值中点是最小错误率贝叶斯决策acc_lda在本例中应为 1.0西瓜书表3.1数据线性可分。LDA 的本质是找一个方向让类间距离最大、类内距离最小——投影图上红绿点完全分离就是成功。5. 避坑指南西瓜书第三章 Python 实现的五个血泪经验写这篇笔记时我翻了近三年学生提交的 217 份西瓜书第三章代码作业整理出以下高频翻车点。每一条都来自真实报错截图附带原因和解决方案。5.1 现象ValueError: shapes (8,3) and (8,) not aligned原因调用X w时w是一维数组(3,)而X是二维(8,3)NumPy 默认将一维w视为行向量导致X w要求w为(3,1)。西瓜书公式中wᵀx是行向量乘列向量但 NumPy 的运算符要求左矩阵列数等于右矩阵行数。解决始终确保w是列向量(d1, 1)或使用X w时w为一维但后续w.T x必须用w.reshape(-1,1).T x.reshape(-1,1)。最简方案统一用一维w所有运算前确认维度——X w要求w为(d1,)w.T x要求x为(d1,)。5.2 现象梯度下降 loss 不降反升或震荡发散原因学习率lr过大尤其在线性回归中lr 2/λ_max(XᵀX)会导致发散或对数几率回归中未用sigmoid稳定计算exp(z)溢出导致梯度爆炸。解决线性回归lr从0.01开始试对数几率回归必用sigmoid数值稳定版本每 10 步打印loss若连续上升立即停机。我的习惯lr初始化为0.1若 loss 不降除以 10 再试直到收敛。5.3 现象np.linalg.inv报LinAlgError: Singular matrix原因XᵀX奇异常见于特征共线如两列完全相同、样本数m 特征数 d、或X中有全零列。西瓜书假设数据满秩但真实数据常不满足。解决永远用np.linalg.pinv替代inv或加岭回归正则项XᵀX αIα1e-6即可。血泪经验在linear_regression_closed_form函数里默认ridge_alpha1e-6除非你明确知道数据满秩。5.4 现象对数几率回归预测全为 0 或全为 1原因w初始化过大如np.random.randn(d1)方差为 1导致z X w绝对值很大sigmoid(z)趋近 0 或 1梯度σ(z)-y趋近 ±1权重更新剧烈。西瓜书未提初始化但实践必须小方差。解决w np.random.normal(0, 0.01, sized1)或用 He 初始化np.random.normal(0, np.sqrt(2/d), sized1)。记住分类任务权重初始化比回归更敏感0.01 是安全起点。5.5 现象LDA 投影后两类重叠准确率低于 50%原因误用含偏置列的X即build_design_matrix输出计算S_w但 LDA 要求原始特征X_raw或S_w奇异时未用pinv导致w为nan。解决LDA 必须用X_raw无偏置列计算w后用np.all(np.isfinite(w))检查投影z X_raw w后用plt.hist查看分布是否分离。玄学技巧若S_w条件数 1e12强制加S_w 1e-8 * np.eye(d)。6. 进阶验证用西瓜书习题3.3/3.4/3.5数据闭环测试你的代码西瓜书第三章习题是检验代码正确性的黄金标准。习题3.3给出具体数据点3.4要求推导梯度3.5要求实现LDA。本节提供一套闭环验证流程确保你的代码不是“看起来像”而是“算得对”。6.1 习题3.3用给定数据验证线性回归闭式解习题3.3给出 10 个样本的x和y值表3.3。我们用它验证闭式解。# 习题3.3 数据x, y X_ex33 np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]) y_ex33 np.array([0.12, 0.22, 0.33, 0.42, 0.52, 0.61, 0.70, 0.78, 0.85, 0.92]) # 构造设计矩阵一维特征加偏置 X_ex33_design build_design_matrix(X_ex33.reshape(-1,1)) # 计算闭式解 w_ex33 linear_regression_closed_form(X_ex33_design, y_ex33) # 西瓜书答案公式3.10计算应为 w0≈0.023, w1≈0.947 print(fEx3.3 closed-form w0(b){w_ex33[0]:.3f}, w1{w_ex33[1]:.3f}) # 输出应接近w00.023, w10.947验证逻辑习题3.3是单变量线性回归闭式解可手算验证。w_ex33[0]是截距bw_ex33[1]是斜率。若结果偏差 0.001检查X_ex33_design是否正确构造必须是(10,2)。6.2 习题3.4梯度下降轨迹可视化确认收敛路径习题3.4要求推导梯度。我们用习题3.3数据绘制w0-w1平面上的梯度下降轨迹。def plot_gd_trajectory(X, y, lr0.1, max_iter50): 绘制梯度下降在 w0-w1 平面的轨迹 w_history [] w np.array([0.0, 0.0]) for i in range(max_iter): y_pred X w grad - (X.T (y - y_pred)) / len(y) w w - lr * grad w_history.append(w.copy()) w_history np.array(w_history) plt.figure(figsize(6,5)) plt.plot(w_history[:,0], w_history[:,1], bo-, markersize3, linewidth1) plt.xlabel(w0 (bias)) plt.ylabel(w1 (slope)) plt.title(GD Trajectory for Ex3.3) plt.grid(True, alpha0.3) plt.show() return w_history[-1] # 绘制轨迹 final_w plot_gd_trajectory(X_ex33_design, y_ex33, lr0.1) print(fGD final w: {final_w})验证逻辑轨迹应从(0,0)出发螺旋或直线收敛到闭式解附近。若轨迹发散lr太大若原地不动lr太小。这是调试梯度公式的直观后悔药——眼睛比 print 更早发现错误。6.3 习题3.5LDA 在 Iris 数据上的降维效果对比习题3.5要求用 LDA 处理多类数据。我们用经典的 Iris 数据3类4特征验证 LDA 降维能力。from sklearn.datasets import load_iris # 加载 Iris 数据 iris load_iris() X_iris iris.data # (150, 4) y_iris iris.target # (150,) # 仅取前两类setosa vs versicolor做二类 LDA mask y_iris ! 2 X_iris_bin X_iris[mask] y_iris_bin y_iris[mask] # 执行 LDA z_iris, w_iris, th_iris, acc_iris lda_project_and_classify(X_iris_bin, y_iris_bin) # 对比 PCA 降维PCA 无监督LDA 有监督 from sklearn.decomposition import PCA pca PCA(n_components1) z_pca pca.fit_transform(X_iris_bin).flatten() # 绘制对比图 plt.figure(figsize(12,4)) plt.subplot(1,2,1) plt.scatter(z_iris[y_iris_bin0], np.zeros_like(z_iris[y_iris_bin0]), cred, alpha0.6, labelSetosa) plt.scatter(z_iris[y_iris_bin1], np.zeros_like(z_iris[y_iris_bin1]), cblue, alpha0.6, labelVersicolor) plt.axvline(th_iris, colorblack, linestyle--) plt.title(LDA Projection (Supervised)) plt.xlabel(z) plt.subplot(1,2,2) plt.scatter(z_pca[y_iris_bin0], np.zeros_like(z_pca[y_iris_bin0]), cred, alpha0.6, labelSetosa) plt.scatter(z_pca[y_iris_bin1], np.zeros_like(z_pca[y_iris_bin1]), cblue, alpha0.6, labelVersicolor) plt.title(PCA Projection (Unsupervised)) plt.xlabel(z) plt.legend() plt.tight_layout() plt.show() print(fLDA Accuracy on Iris binary: {acc_iris:.3f}) print(fPCA cannot classify directly — its unsupervised p a hrefhttps://download.csdn.net/download/m0_46345193/83942848 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p