吴恩达机器学习作业双语言复现:MATLAB与Python实战解析

发布时间:2026/8/31 20:11:37
吴恩达机器学习作业双语言复现:MATLAB与Python实战解析 简介本资源是面向计算机、电子信息工程及数学等专业学习者的吴恩达机器学习课程作业配套实现覆盖课程核心算法如线性回归、逻辑回归、神经网络、SVM、PCA等的Matlab与Python双语言完整复现解决初学者在课程实践环节中代码实现难、环境配置杂、调试无从下手等问题。压缩包共含源码、数据集、中文说明文档等多类文件以.m/.py脚本为主支撑算法实现csv数据文件用于训练验证pdf/docx文档提供任务说明与运行指引整体大小为71.29MB。已有565人下载学习适合具备基础编程与线性代数知识的学习者作为课程辅助参考资料可直接运行参考、对比原理推导与代码结构、定位常见报错原因并基于现有框架自主扩展功能或适配新数据。 先说明一下吴恩达那门《Machine Learning》课程虽然用的是 Octave/MATLAB但这几年 Python 生态越来越成熟很多人在复现作业时都更愿意用 Python 写一遍用 MATLAB 再跑一遍来对照结果。我这个压缩包里的东西就是把这两套完整方案整理好源码、数据、说明文档都齐了适合正在啃这门课、想动手把每个算法从公式变成代码的人。下面我详细拆解里面的内容和踩过的坑。1. 双语言复现的整体思路与内容设计1.1 为什么同一份作业要同时用 MATLAB 和 Python 各写一遍先说这个包的核心设计思路。吴恩达这门课的作业原本是要求用 Octave 或者 MATLAB 写的因为课程老版本本身就是基于这个环境。但实际工作中Python 才是机器学习落地的主流语言。我整理这份资料的时候特意把每个练习都用 Python 重写了一遍同时保留 MATLAB 版本这么做有三个原因。第一个原因是为了理解算法本身。MATLAB 的向量化写法非常接近课程讲义里的数学公式你用 MATLAB 做题的时候基本就是把公式翻译成矩阵运算这对搞清楚梯度下降、正规方程、反向传播这些算法的推导过程帮助极大。Python 版本在工程上更灵活但如果你一上来就用 scikit-learn 里的现成类很容易变成调包侠并不知道底层发生了什么。第二个原因是为了对应不同人的环境需求。学生党可能学校机房装了 MATLAB或者老师要求必须用 MATLAB 提交作业而自学党手上根本不会有 MATLAB 的正版授权用 Python 的 numpy 和 matplotlib 就能全部搞定。所以两个版本都放出来无论你处于哪种环境都能直接上手。第三个原因是对照验证。同一个数据集同一套参数MATLAB 算出来的代价函数值和 Python 算出来的应该完全一致梯度下降收敛曲线也应该长得一样。如果你在两套代码里跑出来的结果对不上那一定是某一版写错了这本身就是一种很好的调试手段。1.2 压缩包的目录结构与文件说明我拿到这个.rar包之后第一件事是先看它的目录结构因为结构合理的项目能省很多时间。整个包打开之后大致是这样组织的基于Matlab和Python分别实现吴恩达机器学习课程作业/ ├── matlab版本/ │ ├── ex1_线性回归/ │ │ ├── ex1.m │ │ ├── ex1_multi.m │ │ ├── gradientDescent.m │ │ ├── computeCost.m │ │ ├── featureNormalize.m │ │ ├── normalEqn.m │ │ └── data/ │ │ ├── ex1data1.txt │ │ └── ex1data2.txt │ ├── ex2_逻辑回归/ │ ├── ex3_多分类与神经网络/ │ ├── ex4_神经网络反向传播/ │ ├── ex5_正则化偏差方差/ │ ├── ex6_支持向量机/ │ ├── ex7_K均值与PCA/ │ └── ex8_异常检测与协同过滤/ ├── python版本/ │ ├── ex1_线性回归/ │ │ ├── linear_regression.py │ │ ├── multi_linear_regression.py │ │ ├── gradient_descent.py │ │ ├── normal_equation.py │ │ ├── feature_normalize.py │ │ └── data/ │ ├── ex2_逻辑回归/ │ ... └── 说明文档.md每个练习目录下都有独立的data文件夹原始数据集没有做任何删改跟课程官网下载的完全一致。对应每个练习的说明文档详细写了运行方法、环境版本、关键代码行号还有我在复现过程中踩过的坑。1.3 这个资料适合谁来用说实话这个包不太适合完全零基础的人。它更适合已经看完了吴恩达课程视频、大概理解了每个算法怎么回事但写代码时不知道从哪下手的同学。因为这里面的代码不是逐行带读的教程而是给你一份可以跑通的参考实现你可以边看课程边对着代码理解公式也可以自己写完遇到 bug 了拿来对照。另外对准备面试的人也有用。面试机器学习岗位常会被问梯度下降的实现细节、逻辑回归的损失函数怎么推导、反向传播的维度怎么匹配。手写代码熟悉一遍绝对比死背八股文有用得多。我的建议是先把 MATLAB 版本当成“公式翻译机”看弄懂每行代码对应公式的哪个部分再用 Python 版本体会工程实现上的不同取舍。2. MATLAB 版代码的核心细节与实操要点2.1 MATLAB 版的编程范式向量化是灵魂吴恩达在课程里反复强调向量化这在 MATLAB 版本里体现得淋漓尽致。最典型的就是线性回归里的代价函数计算新手最容易写成 for 循环累加但课程的标准写法是一行矩阵运算搞定function J computeCost(X, y, theta) m length(y); predictions X * theta; sqrErrors (predictions - y) .^ 2; J 1 / (2 * m) * sum(sqrErrors); end这里X是样本矩阵每一行是一个样本第一列是 1对应偏置项theta是参数列向量。X * theta就是一次性算出所有样本的预测值减y得到误差向量逐元素平方后求和再除以2m正好就是课程里的代价函数公式。再比如梯度下降的更新步骤function [theta, J_history] gradientDescent(X, y, theta, alpha, num_iters) m length(y); J_history zeros(num_iters, 1); for iter 1:num_iters theta theta - (alpha / m) * (X * (X * theta - y)); J_history(iter) computeCost(X, y, theta); end end这里只有一层 for 循环迭代次数没有内层针对样本数的循环。X * (X * theta - y)这一行X是 X 的转置乘上误差向量得到的恰好是所有特征梯度之和。这是整套 MATLAB 代码的核心套路能不用循环就不用循环一个矩阵乘法干掉一个维度。2.2 ex1 线性回归的完整跑通流程拿第一个练习来举例。打开ex1.m开头是加载数据并绘图data load(data/ex1data1.txt); X data(:, 1); y data(:, 2); m length(y); plot(X, y, rx, MarkerSize, 10); ylabel(Profit in $10,000s); xlabel(Population of City in 10,000s);接着加一列全 1 作为偏置项X [ones(m, 1), data(:,1)]; theta zeros(2, 1); iterations 1500; alpha 0.01;这里有个新手易错点矩阵拼接的方向。ones(m, 1)是 m 行 1 列全 1放在第一列即X [全1列, 原特征列]这样theta(1)对应偏置项theta(2)对应特征权重顺序不能反。如果你在 Python 里用np.column_stack或者np.hstack也要保持同样的顺序。然后调用梯度下降theta gradientDescent(X, y, theta, alpha, iterations);跑完以后把拟合直线和原始数据画在同一张图上再用predict([1, 3.5])预测 35000 人口城市的利润。正常结果theta约等于[-3.6303, 1.1664]35000 人口对应利润约4519美元。这是验证代码是否正确的标准结果如果对不上八成是梯度下降写错了或者学习率设置有问题。2.3 特征缩放与正规方程的取舍到了多变量线性回归ex1_multi就涉及特征缩放。原始数据里房子面积是几百到几千卧室数量是 1 到 5量级差太多梯度下降会很难收敛。标准做法是均值归一化function [X_norm, mu, sigma] featureNormalize(X) mu mean(X); sigma std(X); X_norm (X - mu) ./ sigma; end注意这里./是逐元素除法mu和sigma都是 1×n 的行向量X是 m×n 矩阵广播机制会让每一列减去对应列的均值、除以对应列的标准差。归一化之后跑梯度下降收敛速度快得多代价函数曲线也不会出现震荡。正规方程则绕过了迭代过程直接用闭式解一步到位function [theta] normalEqn(X, y) theta pinv(X * X) * X * y; end这里我用的是pinv伪逆而不是inv逆矩阵因为当X * X不可逆时特征冗余或者样本数少于特征数pinv依然能给出结果inv会直接报错。实际复现时这一点要特别留意课程里 Andrew Ng 也强调过优先用伪逆。2.4 MATLAB 版其他练习的重点提示ex2 逻辑回归的核心是写出sigmoid函数和带正则化的代价函数。注意sigmoid必须支持向量输入function g sigmoid(z) g 1 ./ (1 exp(-z)); end这里的./和exp都是逐元素操作。逻辑回归的梯度下降公式长得和线性回归一样但h(x)变成了sigmoid(X * theta)这个点理解了整个练习就通了一半。ex3 手写数字识别多分类要用到 one-vs-all就是训练 10 个二分类器每个分类器负责判断“是不是数字 k”。ex4 是神经网络前向传播和反向传播这是整个课程代码量最大的一个练习我在 3.3 节会再展开讲。ex5 正则化偏差方差、ex6 SVM、ex7 K-means 与 PCA、ex8 异常检测与协同过滤每个都对应一个独立的机器学习主题。3. Python 版复现时的重写策略与关键代码3.1 为什么不能简单把 MATLAB 代码“翻译”成 Python很多人觉得 Python 版就是把 MATLAB 的.*换成*把ones(m,1)换成np.ones((m,1))实际上没那么简单。两种语言的索引方式、矩阵存储顺序、库的调用习惯完全不同。Python 默认索引从 0 开始MATLAB 从 1 开始这在切片和矩阵拼接时特别容易搞混。比如取 X 的第 2 到第 5 行MATLAB 是X(2:5, :)Python 是X[1:5, :]右边界一个是闭区间一个是开区间。还有 MATLAB 里end表示最后一个索引Python 里要么用-1要么用数组的shape属性显式计算。所以我在写 Python 版的时候不是逐行对照 MATLAB 代码翻译而是重新按照 Python 的思维习惯写一遍。向量化照样向量化但用的是 numpy 的接口而且充分利用matplotlib做可视化对比。3.2 线性回归 Python 版的实现与可视化对比Python 版的线性回归核心函数长这样import numpy as np def compute_cost(X, y, theta): m len(y) predictions X theta sqr_errors (predictions - y) ** 2 return 1 / (2 * m) * np.sum(sqr_errors) def gradient_descent(X, y, theta, alpha, num_iters): m len(y) cost_history [] for _ in range(num_iters): theta theta - (alpha / m) * (X.T (X theta - y)) cost_history.append(compute_cost(X, y, theta)) return theta, cost_history注意这里是矩阵乘法运算符*是逐元素乘法这是无数新手踩坑的重灾区。在 numpy 里X * theta如果维度不匹配会报错或者做广播但在逻辑上完全不是你想要的矩阵乘法必须用或者np.dot()。加载数据并画图import matplotlib.pyplot as plt data np.loadtxt(data/ex1data1.txt, delimiter,) X data[:, 0].reshape(-1, 1) y data[:, 1].reshape(-1, 1) plt.scatter(X, y, markerx, cred) plt.xlabel(Population of City in 10,000s) plt.ylabel(Profit in $10,000s) plt.title(Training Data) plt.show()跑完梯度下降后把代价函数随迭代次数变化的曲线画出来可以直观看到收敛情况。如果代价函数曲线是上升的或者震荡的说明学习率太大如果下降得特别慢说明学习率太小。这部分可视化的价值是 MATLAB 版本同等具备的但 Python 的画图接口用起来我个人觉得更顺手一些。3.3 神经网络的 Python 重构与维度匹配细节ex4 反向传播是整个课程作业里最劝退的一个练习Python 版尤其容易出维度问题。这个练习的神经网络结构是 400 个输入单元20×20 像素展开1 个隐藏层25 个单元10 个输出单元数字 0-9 的分类。前向传播的向量化实现核心是def forward_propagate(X, theta1, theta2): m X.shape[0] a1 np.hstack([np.ones((m, 1)), X]) z2 a1 theta1.T a2 np.hstack([np.ones((m, 1)), sigmoid(z2)]) z3 a2 theta2.T h sigmoid(z3) return a1, z2, a2, z3, h这里theta1的维度是 25×401theta2的维度是 10×26因为偏置项已经拼进a1和a2里了所以theta的列数必须对应上一层节点数加 1。反向传播里最关键的公式是误差项的计算省略正则化部分def backprop(params, input_size, hidden_size, num_labels, X, y, reg_lambda): theta1 params[:hidden_size * (input_size 1)].reshape(hidden_size, input_size 1) theta2 params[hidden_size * (input_size 1):].reshape(num_labels, hidden_size 1) a1, z2, a2, z3, h forward_propagate(X, theta1, theta2) y_matrix np.eye(num_labels)[y] delta3 h - y_matrix delta2 delta3 theta2[:, 1:] * sigmoid_gradient(z2) theta1_grad delta2.T a1 / len(X) theta2_grad delta3.T a2 / len(X) ...这里最容易写错的是delta2的计算。注意theta2[:, 1:]是去掉偏置项对应的那一列因为偏置节点没有传入误差。sigmoid_gradient(z2)是sigmoid(z2) * (1 - sigmoid(z2))这是反向传播里梯度计算的关键环节。跑通之后用提供的测试代码算梯度数值梯度和反向传播得到的梯度差距应该在 1e-9 量级以内。我在包里的说明文档中特别标注了这一点如果在 1e-2 量级基本就是theta2[:, 1:]没去掉偏置列或者y_matrix构建错了。3.4 Python 版整个包的环境依赖与运行示例这个包的 Python 版依赖比较简单只需要三个核心库pip install numpy scikit-learn matplotlib scipy我没有用 TensorFlow 或 PyTorch因为课程作业本身就是让你从零手写算法用深度学习框架就失去了练习的意义。但 ex6 SVM 这个练习里课程原版用的是libsvmPython 版我用的是 scikit-learn 里的svm.SVC功能上完全等价接口更友好。ex7 的 PCA 我额外用了sklearn.decomposition.PCA做了一次对照实验发现手写 PCA 和库函数 PCA 的结果基本一致只是符号可能相反特征向量的方向不唯一这是正常的。运行方式很简单每个练习目录下在终端里执行python linear_regression.py或者python multi_linear_regression.py代码里我加了足够的print输出每一步算出来的关键数值都会打印出来比如初始代价、最终代价、theta 值、训练准确率等方便你对答案。4. 常见问题与排查技巧实录4.1 环境配置类问题先说说最容易卡住人的环境问题。MATLAB 版本方面我测试过 R2016b 到 R2022b 都能正常运行但如果你用的是学校统一安装的老版本比如 R2014a需要注意pinv这类函数在特别古老的版本里行为可能不一致其他 API 基本没变化。Python 版本有个典型坑numpy 的版本差异。我最早写这套代码的时候用的是 numpy 1.19后来 numpy 2.0 发布后有些老代码会因为np.float被移除而报错。所以我在说明文档里建议的版本号是numpy1.21,2.0如果你已经安了 numpy 2.x 还报错最简单的办法是建一个虚拟环境装老版本python -m venv ml_course_env source ml_course_env/bin/activate # Windows 上改为 ml_course_env\Scripts\activate pip install numpy2.0 matplotlib scipy还有一个很常见的坑是中文路径。如果你把压缩包解压到带中文的路径下比如C:\Users\张三\桌面\机器学习作业MATLAB 可能识别不了数据文件Python 的np.loadtxt在某些老版本里也会因为编码问题打不开文件。建议把整个目录放到纯英文路径下再运行。4.2 代码结果对不上标准答案这是复现作业时最让人头大的问题。我在对照过程中遇到过几种情况整理成表格方便你们自查症状可能原因检查方法代价函数越来越大学习率过大导致参数更新步子太大越过最优点把学习率调小 10 倍0.01 改成 0.001再试或者画出代价函数曲线看是否震荡梯度下降收敛极慢特征没有归一化或者学习率太小检查featureNormalize有没有正确执行均值应为 0 附近、标准差应为 1预测结果完全不对特征矩阵拼接顺序反了或偏置列被当成了普通特征打印X的前几行确认第一列全是 1逻辑回归决策边界画不出来sigmod写成了sigmoid但没用逐元素除法检查有没有用1 / (1 np.exp(-z))少一个点就是标量除法PCA 结果符号不对特征向量方向不唯一SVD 分解出来的 U 和库函数结果差一个负号不是错误不用改投影后的数据才是你要关注的4.3 向量化代码的调试技巧我调试这套代码时发现向量化代码最大的困难是中间结果的维度对不上。一个很笨但很有效的方法是每个关键步骤后打印当前矩阵的shape然后手动推一遍维度。比如在两个矩阵做乘法之前先确认左边矩阵的列数等于右边矩阵的行数。比如前向传播里z2 a1 theta1.Ta1是 (m, 401)theta1是 (25, 401)转置后是 (401, 25)相乘得到 (m, 25)这个维度恰好是隐藏层 25 个单元的输入。如果发现a1和theta1.T形状不匹配立即检查是不是忘了拼偏置列或者theta1的 reshape 顺序搞错了。另一个技巧是“梯度检查”也就是用数值方法近似计算梯度跟反向传播算出来的梯度做对比。这个在 ex4 里是课程要求的一部分但我在 ex2、ex3 的代码里也加了同样的检查函数。数值梯度的核心公式是def numerical_gradient(f, theta, epsilon1e-4): num_grad np.zeros_like(theta) for i in range(len(theta)): theta_plus theta.copy() theta_plus[i] epsilon theta_minus theta.copy() theta_minus[i] - epsilon num_grad[i] (f(theta_plus) - f(theta_minus)) / (2 * epsilon) return num_grad如果是调试阶段可以把参数向量设得小一点比如所有元素都是 0.01 或随机小值跑一次梯度检查误差小于 1e-7 就说明反向传播逻辑正确。4.4 作业代码与课程版本不匹配的问题很多人拿着我这份代码去对答案发现自己运行的结果和网上一些博客贴出来的答案不一样就开始怀疑代码写错了。这里要提醒一个关键点吴恩达这门课有多个版本不同年份的数据集可能略有不同尤其是 ex2 的逻辑回归数据有些版本是ex2data1.txt有些版本在后面的练习里数据做了调整。所以你的结果应该以课程官网下载的作业说明 PDF 里给出的预期输出为准而不是以某个博客的截图为标准。另外ex8 的协同过滤算法里课程给出了一个训练好的参数和电影评分预测结果。我复现后发现用 MATLAB 和 Python 跑出来的推荐结果在前几名上有细微出入原因是最小化算法的终止条件不同MATLAB 的fmincg和 Python 的scipy.optimize.minimize实现细节不一样。这不影响正确性只要关键指标比如代价函数值、训练集误差和课程讲义一致就行。5. 这套资料还可以怎么扩展使用5.1 把 Python 版升级成可复用模块如果你已经跑通了每个练习可以更进一步把这八个练习的功能封装成自己的机器学习工具包。比如把gradient_descent、normal_equation、sigmoid、cost_function这些函数放到一个ml_utils.py文件里以后做其他项目或者写作业就直接from ml_utils import *不用重复造轮子。我自己的习惯是给这些函数写 docstring标注清楚输入输出的维度要求、支持的边界情况比如X是否已经包含偏置列、以及参考的公式编号。这样过了两个月自己回头用也不用重新读一遍代码逻辑。5.2 对比学习同一算法在不同库实现下的效率差距另一个扩展方向是做性能对比实验。举个例子线性回归用三种方式实现手写梯度下降、调用 scikit-learn 的LinearRegression它默认用最小二乘法、以及调用scipy.optimize.minimize里的优化器。你会发现手写的梯度下降在数据量小的时候几百个样本差别不大但数据量到几万条时正规方程因为要算X * X的逆速度会急剧下降而 scikit-learn 的LinearRegression内部用了更稳定的scipy.linalg.lstsq速度更快数值也更稳。这类对比能帮你建立“什么时候该用哪种算法”的工程直觉这是单纯刷课程作业得不到的收获。5.3 结合热门的进阶课程继续深入把这门课和吴恩达后来的深度学习课程结合起来学效果更好。举个例子作业 ex4 里的手写数字识别你可以用同样的数据在 PyTorch 里搭一个三层神经网络输入层 784、隐藏层 128、输出层 10跑几个 epoch 看看准确率能不能超过课程作业里手工反向传播的效果。你还能用我之前提过的那个gptprompt 工程笔记如果你对这块感兴趣的话里的思维方式把算法实现里的每一步“提示”自己理解清楚——该用向量化的时候别偷懒用循环该检查维度的时候别怕麻烦打印 shape。如果一个练习你能同时写出 MATLAB、纯 Python numpy、PyTorch 三个版本对这门课的理解深度绝对远超大多数人。最后再分享一个小技巧我在跑 ex5 偏差方差的那个练习时发现把训练误差和验证误差随多项式次数变化的曲线画在同一张图里比任何文字解释都直观。低次多项式时两条曲线都很高这是欠拟合高偏差高次多项式时训练误差接近 0 但验证误差极高这是过拟合高方差。找好那个平衡点比背多少个公式都有用。这套资料里的代码画图部分都保留了完整的可视化逻辑你多改几次参数跑一跑很多问题就彻底通了。本文还有配套的精品资源点击获取