尧图精选

吴恩达机器学习作业Matlab与Python双实现:源码解析与避坑指南

🕒 发布时间:2026/10/1 6:08:47 📁 来源:尧图网络
简介基于Matlab和Python双语言实现的吴恩达机器学习课程作业资料包面向计算机、电子信息工程、数学等专业的学习者可作为课程配套参考资料适合有一定编程基础、希望对照两套实现来理解算法细节的人。压缩包为rar格式整体大小约71.29MB内含源码、数据集与说明文档两份代码覆盖线性回归、逻辑回归、神经网络、支持向量机等典型作业主题并涉及数据特征处理、代价函数、梯度下降、正则化等关键环节。目前已有566人学习浏览说明该资料在实际学习场景中具有一定参考价值。学习时可借助说明文档理清作业要求与思路再参考对应语言的源码进行调试和功能扩展需要特别留意代码仅作参考不应直接照搬使用者需具备自行排查报错和修改代码的能力。整体而言这套双语作业包既能巩固课程知识点又能帮助学习者提升将理论转化为可运行代码的动手能力是课程实战环节较为实用的参考资料。1. 吴恩达机器学习作业的Matlab与Python双实现这套源码到底解决什么问题吴恩达机器学习课程Coursera 上的 Machine Learning可能是国内从业者接触机器学习的第一站但它有个很现实的问题课程配套的编程作业是用 Octave/Matlab 写的。很多人在公司里用的是 Python 技术栈学完理论课后想复现一遍作业发现自己要么得装一个几十年没用过的 Matlab要么得把整套作业逻辑手工翻译成 Python。这套以基于Matlab和Python分别实现吴恩达机器学习课程作业为主题的源码包做的就是这件事——同一个作业两套语言实现配上数据和说明文档让你不用在两种语言之间来回对着伪代码猜。它适合三类人正在跟课程做作业、卡在某个 ex 练习上的学生想用 Python 快速验证课程里那些算法线性回归、逻辑回归、神经网络、SVM、K-means、PCA、异常检测、推荐系统的从业者以及准备面试、需要把经典算法的推演和代码对应起来的候选人。核心价值不是算法本身——那些推导到处都有——而是同一份作业在两种语言下的等价实现这正好是自学时最缺的对照样本。2. 作业全景与数据组织8个练习分别考什么数据集长什么样2.1 从 ex1 到 ex8每份作业对应的算法与核心知识点吴恩达这门课的编程作业一共 8 个对应课程视频的各个阶段。我先把它们拆开列一遍因为后面所有代码都要落到这些具体练习上作业编号主题核心算法/模型主要考点ex1线性回归单变量/多变量线性回归、正规方程梯度下降实现、学习率选取、特征缩放ex2逻辑回归二分类逻辑回归、正则化sigmoid 函数、代价函数、决策边界ex3多类分类一对多逻辑回归、手写数字识别one-vs-all、像素特征ex4神经网络前向传播、反向传播权重初始化、梯度检查、代价函数ex5偏差与方差正则化线性回归、学习曲线训练集/验证集/测试集划分、欠拟合过拟合判断ex6SVM支持向量机高斯核、C 参数、垃圾邮件分类ex7K-means 与 PCA聚类、主成分分析图像压缩、人脸特征降维ex8异常检测与推荐系统高斯分布、协同过滤阈值选择、成本函数、梯度这套作业的设计思路是从最基础的梯度下降一步步推到协同过滤每一份都在前一份的代码上扩展。所以跑的时候别跳着来ex4 的反向传播要调 ex3 训练好的权重ex8 的协同过滤要复用 ex2 的正则化思路。源码包里的目录结构也基本按这个顺序组织每个 ex 文件夹下再分 matlab 和 python 两个子目录。2.2 数据集与文件组织ex1data1.txt 这类文件是怎么被读进来的课程提供的数据文件都是纯文本格式最常见的是用逗号分隔的 .txt 文件。比如 ex1 的 ex1data1.txt每行两列第一列是人口第二列是利润中间用逗号隔开。没有表头没有引号。Matlab 里用 load 命令直接读Python 里用 numpy.loadtxt 读两者都不需要额外解析。数据文件在整个作业体系里分三类回归/分类用的表格数据ex1data1.txt、ex2data1.txt、ex2data2.txt图像数据ex3 的手写数字矩阵、ex7 的人脸图片以及文本数据ex6 的垃圾邮件样本。图像和文本的处理方式差异很大——图像是直接 load 成矩阵邮件则需要先做词干提取和词表映射——这决定了你在两种语言里写的预处理代码完全不一样。一个容易被忽略的点ex3 的手写数字数据是用 .mat 格式存的ex3data1.mat里面是一个 5000×400 的像素矩阵加上 5000×1 的标签向量。Matlab 里 load 进来直接能当变量用但 Python 得用 scipy.io.loadmat 读读出来还是字典结构要取 [X] 和 [y] 两个键。这个差异不算复杂但对第一次接触的人来说卡在数据读取上的时间往往比写算法还长。3. Matlab侧的实现路径从 ex1 到 ex3 的最小可运行脚本3.1 为什么 Matlab 侧要按课程原始顺序跑环境与目录约定课程本身是 Octave 写的Matlab 完全兼容但有两个前提一是要用 R2015a 之后的版本太老的版本对某些绘图函数支持不好二是每个 ex 目录下的脚本是相互引用的ex3 的 fmincg 函数在 ex2 里就有定义别删。Matlab 侧的最小运行方式是在命令行切到对应目录然后直接敲脚本名。比如跑线性回归cd ex1 % 加载数据ex1data1.txt 是两列第一列人口第二列利润 data load(ex1data1.txt); X data(:, 1); % 取第一列作为特征 y data(:, 2); % 取第二列作为标签 m length(y); % 样本数量 % 给 X 加一列全 1对应 theta0 X [ones(m, 1), X]; % 初始化参数两个 theta 值都置 0 theta zeros(2, 1); iterations 1500; alpha 0.01; % 调用课程自带的梯度下降函数 [theta, J_history] gradientDescent(X, y, theta, alpha, iterations);这里 gradientDescent 是课程提供或需要你自己补全的函数。关键的参数有三个学习率 alpha 设 0.01 是课程标准值这个数据集上能收敛但不算快迭代次数 1500 次能保证代价函数曲线拉到水平theta 初始化为全零是线性回归的安全起点因为代价函数是凸的不担心局部最优。3.2 Matlab 侧三个高频函数gradientDescent、costFunction、oneVsAll 的写法模式Matlab 侧最核心的代码其实就三块。第一块是代价函数几乎每个作业都有一个变体。第二块是梯度下降或高级优化器的调用。第三块是可视化比如绘制决策边界。这里给一个 ex2 逻辑回归代价函数的典型写法function [J, grad] costFunction(theta, X, y) m length(y); h sigmoid(X * theta); % 先算假设函数的输出sigmoid 是课程提供的函数 J (1/m) * (-y * log(h) - (1-y) * log(1-h)); grad (1/m) * X * (h - y); end注意这里的两个向量化技巧负号前面是逐元素运算然后用矩阵乘法一次性把 m 个样本的梯度累加完。这是这门课最强调的代码风格——所有的循环尽量用矩阵运算替代。误写成 for 循环不是不行但在 Matlab 里性能差距明显而且后面 ex4 的反向传播如果也是循环写法跑一次要等很久。另一个高频写法是 ex3 的 oneVsAllfunction [all_theta] oneVsAll(X, y, num_labels, lambda) m size(X, 1); n size(X, 2); all_theta zeros(num_labels, n 1); X [ones(m, 1) X]; for c 1:num_labels initial_theta zeros(n 1, 1); options optimset(GradObj, on, MaxIter, 50); theta fmincg((t)(lrCostFunction(t, X, (y c), lambda)), ... initial_theta, options); all_theta(c, :) theta; end end这段代码里 fmincg 是课程封装的高级优化器比 fminunc 更适合这类小规模问题。y c 把多类标签变成二分类标签c 从 1 到 10 循环十次MNIST 里数字 0 的标签是 10。lambda 是正则化系数ex3 的标准值是 0.1。3.3 Matlab 运行作业的常见顺序与检查点每跑完一个练习看什么输出跑 Matlab 侧时我一般按这个顺序做检查。ex1 跑完看两个东西代价函数 J 的终值是否在 4.4 左右课程标准答案以及绘制的拟合直线是否穿过散点的中心区域。ex2 跑完看决策边界是否把两类点分得基本干净训练准确率应该在 89% 上下。ex3 跑完看预测准确率课程标准是 94.9%用 fmincg或 95.3%用 fminunc 但迭代更多。这些数值是很好的验金石——如果你的结果和标准值差很多先别调参回头检查数据读取是不是错了。最常见的翻车就是把 X 和 y 读反或者忘了加 ones 那一列。Matlab 侧的调试相对简单断点打在代价函数里看每一步的维度对不对就行。4. Python侧移植的关键映射把 fminunc 换成 scipy把 plot 换成 matplotlib4.1 Python 侧没有 fminunc用 scipy.optimize.minimize 实现同样的高级优化Python 移植最核心的替换是优化器。Matlab 侧的 fminunc/fmincg 在 Python 里没有直接对应物最接近的是 scipy.optimize.minimize。区别在于Matlab 的高级优化器是传函数句柄进去就行而 scipy 需要你指定优化算法method 参数、是否提供梯度jac 参数、以及最大迭代次数。这里给一个 ex2 的完整替代写法import numpy as np from scipy.optimize import minimize def sigmoid(z): return 1 / (1 np.exp(-z)) def cost_function(theta, X, y, lam): m len(y) h sigmoid(X theta) # 加上正则化项注意 theta[0] 不参与正则化 reg (lam / (2 * m)) * np.sum(theta[1:] ** 2) J (1 / m) * (-y np.log(h) - (1 - y) np.log(1 - h)) reg # 梯度同样带正则化 grad (1 / m) * (X.T (h - y)) grad[1:] (lam / m) * theta[1:] return J, grad # 初始化与调用 theta_init np.zeros(X.shape[1]) # methodTNC 对应 Matlab 的拟牛顿类算法jac 告诉优化器我们提供了梯度 result minimize(cost_function, theta_init, args(X, y, 1.0), methodTNC, jacTrue, options{maxiter: 400}) theta result.x这段代码里最重要的参数是 methodTNC。我试过多种组合TNC截断牛顿法在这批作业上最稳定收敛行为和 Matlab 的 fmincg 最接近。如果你换成 methodBFGS 也能跑但有些练习特别是 ex4 反向传播会出现迭代中的数值震荡。jacTrue 表示代价函数返回梯度这个必须和 cost_function 的返回值签名严格对应——返回 (代价, 梯度) 的元组否则 scipy 会尝试用数值微分算梯度速度慢一个数量级还不准。4.2 多类分类与神经网络在 Python 里的实现差异索引从 0 开始是个坑Python 移植最隐蔽的坑是索引差异。Matlab 的数组从 1 开始Python 从 0 开始。这在一维数据上不算问题但 ex3 的 oneVsAll 里Matlab 用 (y c) 生成标签向量c 从 1 循环到 10Python 里如果照搬你拿到的 y 里数字 0 在课程原始数据中标记为 10循环范围还是 1 到 10但在比较标签时要注意数据本身的映射。这里给一个 Python 版 oneVsAllfrom scipy.optimize import minimize def one_vs_all(X, y, num_labels, lam): m, n X.shape X np.hstack([np.ones((m, 1)), X]) all_theta np.zeros((num_labels, n 1)) for c in range(1, num_labels 1): # 注意原始数据中数字 0 的标签是 10这里不需要改直接比较 y_binary (y c).astype(int) theta_init np.zeros(n 1) # 用与 ex2 相同的优化方式只是换了代价函数 result minimize(lr_cost_function, theta_init, args(X, y_binary, lam), methodTNC, jacTrue, options{maxiter: 50}) all_theta[c - 1, :] result.x return all_theta这里的 y_binary 是布尔值转 int 的结果sum 一下就能确认每个类别的样本数。如果你发现某个类别的准确率特别低先检查这个类别的样本数是不是很少——ex3 里数字 1 和 7 的样本分布并不均匀这会导致一对多分类器在这些类上表现偏弱。4.3 数据加载与可视化loadmat 和 matplotlib 的配合写法Python 侧读取 .mat 文件是另一处容易卡住的地方。用 scipy.io.loadmat 读出来的不是数组是字典。而且要小心如果 .mat 文件版本太新MATLAB R2020b 之后默认 v7.3 格式scipy 会直接报错需要回到 Matlab 里用 save 命令存成 v7.0 格式。这个坑我在 ex7 的 PCA 数据上踩过一次人群图像数据就是 v7.3 格式害得我额外装了个 h5py 兜底。可视化方面Matlab 的 plot 在 Python 里对应 matplotlib但有一个体验差异Matlab 直接 plot 就出图matplotlib 需要 plt.show() 才会弹窗在 Jupyter Notebook 里则要加 %matplotlib inline。决策边界的绘制逻辑是等价的——网格点预测后用 contour 或 scatter 画边界。这里给一个 ex2 决策边界的完整 Python 写法import matplotlib.pyplot as plt def plot_decision_boundary(theta, X, y): # 生成网格覆盖训练数据的范围 x1_min, x1_max X[:, 1].min() - 0.5, X[:, 1].max() 0.5 x2_min, x2_max X[:, 2].min() - 0.5, X[:, 2].max() 0.5 u np.linspace(x1_min, x1_max, 100) v np.linspace(x2_min, x2_max, 100) z np.zeros((len(u), len(v))) for i in range(len(u)): for j in range(len(v)): # 对每个网格点计算 sigmoid 输出大于 0.5 记为 1 z[i, j] sigmoid(np.array([1, u[i], v[j]]) theta) # 转置让坐标轴方向一致画等高线 plt.contour(u, v, z.T, levels[0.5], colorsg) plt.scatter(X[y 0, 1], X[y 0, 2], markero, cy) plt.scatter(X[y 1, 1], X[y 1, 2], marker, cb) plt.show()这段代码里 z.T 是必须的因为 numpy 的索引顺序是 (row, col)而绘图坐标是 (x, y)。我第一次写漏了转置画出来的决策边界方向完全反了排查了半天才发现是数组方向问题纯属血泪经验。5. 避坑指南Matlab与Python双跑吴恩达作业时最容易翻车的5个地方5.1 中文注释乱码与编码陷阱Matlab 2023 的默认编码不是 UTF-8现象用较新版 Matlab2020a 之后打开源码包里的 .m 文件中文注释全部变成乱码。原因Matlab 在 R2020a 之前的默认编码是 GBK之后的版本改成了 UTF-8。源码里如果用旧版保存中文注释在新版里打开就会乱码。反过来新代码拷到旧版 Matlab 也一样。这个不是代码逻辑问题但很影响阅读。解决统一用 UTF-8 保存 .m 文件并在文件首行加注释% Encoding: UTF-8声明编码。注意 Matlab 自带的编辑器有自动检测编码选项如果直接双击打开乱码用「打开」对话框选择 UTF-8 重新打开然后另存为覆盖原文件。5.2 数据维度对不上Matlab 是列向量Python 是行向量现象同一份代价函数代码在 Matlab 里能跑搬到 Python 里报 operands could not be broadcast together with shapes (100, 1) and (100,) 之类的广播错误。原因Matlab 的 (100, 1) 和 (1, 100) 维度不同但都能做矩阵乘法numpy 里一维数组 shape 是 (100,)没有列向量的概念。代价函数里的矩阵乘法在两种语言里的行为就分叉了。最常见的写法差异Matlab 里X * (h - y)没问题Python 里如果你的 X 是 (m, n) 而 (h - y) 是 (m,)矩阵乘法会报维度错必须先把 h-y reshape 或直接用 运算让 numpy 广播。解决在 Python 代码开头统一加断言assert X.shape[0] y.shape[0]然后强制y y.flatten()或theta theta.reshape(-1, 1)。我习惯一开始就把所有的 y 压成一维theta 保持一维。scipy 的 minimize 要求 theta 是一维的这是另一个必须 flatten 的理由。5.3 学习率分歧同样的 alpha0.01Python 里不收敛现象ex1 线性回归Matlab 里 alpha0.01 跑到 1500 次迭代收敛得很漂亮但 Python 里同样的数据和同样的迭代次数代价函数还在下降预测结果偏差很大。原因两种语言里的梯度下降收敛条件不同。Matlab 的梯度下降函数课程里写了收敛判断相邻两次迭代的 J 变化小于某阈值就停但 Python 移植时很多人的实现只按固定 epoch 数硬跑没加收敛判断。另一种可能是特征缩放没做好——ex1 多变量部分有特征缩放问题Matlab 脚本里 preprocess 函数处理了但 Python 移植的人可能直接用了原始特征。解决在 Python 侧别迷信固定迭代次数用 while 循环加收敛判断比如while i iterations and abs(J_history[-1] - J_history[-2]) 1e-5。同时确认你做了特征缩放X (X - mean) / std。这个差异不是玄学是两种语言对迭代到收敛的默认行为不同Matlab 脚本往往把判断条件写进了课程函数Python 人容易忽略。5.4 fminunc 与 minimize 的收敛差异数值结果不可能完全一致现象用 Matlab 的 fminunc 和 Python 的 scipy.optimize.minimize 跑同样的逻辑回归ex2得到的 theta 不完全一样预测准确率差 0.2% 左右。原因两者的内部实现不同。fminunc 默认用 BFGS 拟牛顿法scipy 里 methodTNC 是截断牛顿法。迭代路径不同最终收敛点会在极小值附近微小浮动。这是正常现象不是任何一边写错了。解决别追求数值一致以预测准确率和决策边界形态为准。ex2 的准确率只要在 89% 左右、决策边界视觉上合理就算正确。我建议把两边的准确率差值容忍度放到 1% 以内超过再排查代码差异。这也是检验你 Python 移植是否成功的好标准——不是逐位一致而是误差在一个合理范围内。5.5 ex4 反向传播的梯度检查Python 侧更容易踩数值微分的坑现象ex4 神经网络反向传播用课程提供的 checkNNGradients 函数检查梯度Matlab 侧通过但 Python 侧同样的代码报梯度差异过大。原因Python 里如果用 scipy.optimize.check_grad 或自己写数值梯度默认步长是 1e-6 左右。但 ex4 的代价函数包含正则化项和 sigmoid 的数值稳定性处理在极小步长下会出现浮点截断误差导致数值梯度不准确。另一个场景是很多人把反向传播写成循环三层网络对每层求 delta循环里的累加顺序不同会造成浮点误差累积。解决给数值梯度只做一次验算用 1e-4 的步长而不是 1e-6。同时检查你的反向传播是不是用了课程推荐的向量化实现而不是教程里的 for 循环版本。课程 pdf 里给了向量化伪代码照抄到 Python 时注意维度delta3 (a3 - y) 是 (m, 10)而 a2 是 (m, 25)矩阵乘法要对齐。这个检查过了之后ex4 基本就通关了。6. 用数值梯度检查验证你的 Python 移植是否忠实于 Matlab 实现所有作业跑通之后如果你想确认 Python 侧代码不是碰巧能跑可以用数值梯度检查做一次系统验证。这个方法是课程里 ex4 专门教的但可以推广到所有练习对任意代价函数 J(theta)数值梯度定义为 (J(theta eps) - J(theta - eps)) / (2 * eps)和你的解析梯度做比较。两者差异在 1e-5 以内算通过。def numerical_gradient(theta, cost_func, X, y, lam, eps1e-4): num_grad np.zeros(len(theta)) for i in range(len(theta)): theta_plus theta.copy() theta_minus theta.copy() theta_plus[i] eps theta_minus[i] - eps J_plus, _ cost_func(theta_plus, X, y, lam) # 只取代价部分 J_minus, _ cost_func(theta_minus, X, y, lam) num_grad[i] (J_plus - J_minus) / (2 * eps) return num_grad # 对比解析梯度与数值梯度 theta_test np.random.randn(n 1) * 0.01 _, grad_analytic cost_function(theta_test, X, y, lam) grad_numeric numerical_gradient(theta_test, cost_function, X, y, lam) diff np.linalg.norm(grad_analytic - grad_numeric) / np.linalg.norm(grad_analytic grad_numeric) print(f相对差异: {diff:.2e}) # 小于 1e-5 表示通过参数说明eps 取 1e-4 是经验值太小会触发浮点精度问题太大数值梯度本身就不准这正好是我在 5.5 里踩过的坑的延伸。theta_test 用随机值但范围控制在 ±0.01避免 sigmoid 饱和区域如果初始权重太大代价函数可能会溢出。这套方法拿来做两类验证一类是单个练习的代价函数如 ex2、ex5另一类是 ex4 整个反向传播的梯度。前者验证你写的解析式对不对后者验证反向传播的维度匹配和链式法则有没有写错。我自己的习惯是每移植完一个作业先用数值梯度检查扫一遍通过之后再跑到完整数据集上看准确率。两套语言对照着跑是熟悉这门课最好的方式——Matlab 侧看课程原始的行为Python 侧看工程实现的边界。数值梯度检查这个习惯我后来一直带到了深度学习项目里验证自定义层的反向传播时它就是后悔药。希望这些踩过的坑和参数经验能帮到你祝你用这套双实现把作业跑得明明白白。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →