吴恩达机器学习课后练习(转侵删)

注:此博客非原创,为github上clone下来把每一个练习的md整合而成,供学习使用。

如有侵权,请联系e-mail:1479354694@qq.com。我会及时删除并致歉

源文件github链接:


近期受新冠状病毒疫情的影响,宅家进行机器学习的学习与2021研究生入学考试的准备当中去,自觉效率甚低,关于Machine learning也有诸多不解。在观看云课堂吴恩达老师的机器学习课程时,有诸多感慨同样也有着诸多不解。故此 在github上找到了这位ML同僚的练习翻译与解析。收益良多,十分感谢!

篇幅较长,关于源数据在github上有很多,注解中的链接就有。

程序设计练习1: 线性回归

介绍

    在本练习中,你将实现线性回归,并明确它对数据的作用。在开始这个练习之前,我们强烈建议你观看视频讲座并完成相关主题的复习问题。

    要开始练习,你需要下载起始代码并将其内容解压缩到你希望完成练习的目录中。如果需要,请在开始本练习之前使用Octave/MATLAB中的cd命令更改到此目录。

    你也可以在课程网站的“环境设置说明”中找到安装Octave/MATLAB的说明。

本练习中包含的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ex1.m - 指导你完成练习的 Octave/MATLAB 脚本
ex1_multi.m - 练习的后面部分 Octave/MATLAB 脚本
ex1data1.txt - 单变量线性回归数据集
ex1data2.txt - 多变量线性回归数据集
submit.m - 将你的答案发送到我们服务器的提交脚本

[*] warmUpExercise.m - Octave/MATLAB 中的简单示例函数
[*] plotData.m - 展示数据集的函数
[*] computeCost.m - 线性回归代价函数
[*] gradientDescent.m - 梯度下降函数
[†] computeCostMulti.m - 多变量代价函数
[†] gradientDescentMulti.m - 多变量梯度下降
[†] featureNormalize.m - 用于规范化功能的函数
[†] normalEqn.m - 计算正规方程的函数

* 标示必须完成的文件
† 标示可选练习

    在整个练习中,你将使用脚本ex1.m和ex1_multi.m。
这些脚本为题目设置数据集并调用你将要编写的函数。你不需要修改这两个脚本其中任何一个,只需要按照本作业中的说明修改其他文件中的函数。

    对于这个编程练习你只需要完成第一部分练习实现单变量线性回归,第二部分的多变量线性回归是可选的。

在哪里寻求帮助

    本课程的练习使用非常适合数值计算的高级编程语言Octave或MATLAB。如果你没有安装Octave或MATLAB,请参阅课程网站上“环境设置说明”中的安装说明。

    在Octave/MATLAB命令行中输入help紧跟函数名称会显示内建的函数说明。比如输入help plot会显示绘图函数的帮助信息。更多Octave和MATLAB的函数说明请在Octave官网MATLAB官网查阅。

    我们也非常鼓励使用在线讨论与其他学生讨论练习。但是,不要查看任何源代码或与他人共享源代码。


1、简单的Octave/MATLAB函数

    ex1.m的第一部分给出了使用Octave/matlab的语法和作业提交流程。在warmupexercise.m文件中,有一个Octave/matlab函数的框架。通过填充以下代码修将其修改为返回5 x 5单位矩阵的函数:

1
A = eye(5);

    当你完成后,运行(假设你在正确的目录中,
在Octave/matlab的提示下键入“ex1”)ex1.m文件,应该会看到类似下面的输出:

1
2
3
4
5
6
7
ans =
Diagonal Matrix
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

    现在,ex1.m将暂停,直你按下其他键,然后运行下一任务的代码。如果要退出,键入ctrl-c将在运行过程中终止程序。

1.1 提交答案

    完成部分练习后,你可以提交答案并在Octave/MATLAB。提交脚本将提示你输入登录的e-mail和提交的token,并询问你要提交哪些文件。你可以网页中获得本次作业的token。

            ==你现在应该提交答案==

    你可以多次提交答案,我们只考虑最高分数。


2、单变量线性回归

    在这部分练习中,你将实现单变量线性回归来预测快餐车的利润问题。假设你是一家餐厅的CEO并且正在考虑在不同城市开设分店的问题。该连锁店在早已经其他城市有快餐车并且你有各个城市的利润数据和人口数据。
你希望使用此数据帮助选择下一步要扩展的城市。

    文件ex1data1.txt包含线性回归问题的数据集。第一列是一个城市的人口,第二列是该城市快餐车的利润,利润负值表示损失。

    已经设置了ex1.m来为你加载数据。

2.1 绘制数据

    在开始任何任务之前,通过可视化来理解数据通常是很有用的。对于此数据集,你可以使用散点图来可视化数据,因为它只有两个属性需要绘制(利润和人口)。(许多在现实生活中遇到的其他问题是多维的,不能在二维平面上绘制)
在ex1.m中,将数据集从数据文件加载到变量 Xy 中:

1
2
3
data = load('ex1data1.txt'); % read comma separated data
X = data(:, 1); y = data(:, 2);
m = length(y); % number of training examples

    接下来,脚本调用plotData函数来创建这些数据的散点图。你的任务是完成plotDat.m来绘图;修改文件并填写以下代码:

1
2
3
plot(x, y, 'rx', 'MarkerSize', 10); % Plot the data
ylabel('Profit in $10,000s'); % Set the y−axis label
xlabel('Population of City in 10,000s'); % Set the x−axis label

    现在,当你继续运行ex1.m,我们的最终结果应该如图1所示,有红色“x”标记和轴标记。
要了解plot命令的更多信息,你可以在Octave/MATLAB命令提示符中键入help plot,或者在线搜索绘图文档。
(为了将标记改为红色的“x”,我们在plot命令中使用了选项“rx”);

Figure 1: Scatter plot of training data
### 2.2 梯度下降

    在这部分中,你将使用梯度下降法拟合线性回归中的参数θ到我们的数据集。

2.2.1 更新方程

    线性回归的目标是使代价函数最小化

    该假设函数的线性模型如下
    回想一下,模型的参数是`$θ_j$`,你将通过调整它来最小化代价函数J(θ)。一种方法是使用批量梯度下降算法,批量梯度下降每次都迭代更新下面的式子:
    每一步的梯度下降,参数`$θ_j$`都会更加接近使代价函数J(θ)最低的最优值。

#

注意:我们将每个样本作为一行存储在Octave/MATLAB中的X矩阵中。考虑到截距项(θ0),我们在X上增加了额外的第一列,并把它的所有值设为1。这样我们就可以把$θ_0$当做另一个简单“特征”。

2.2.2 实现

在ex1.m中我们已经建立了线性回归的数据。接下来,我们向数据中添加第一列(全部为1)以适应$θ_0$截距项。我们还将初始化参数,使为θ为0,学习率为0.01。

1
2
3
4
X = [ones(m, 1), data(:,1)]; % Add a column of ones to x
theta = zeros(2, 1); % initialize fitting parameters
iterations = 1500;
alpha = 0.01;

2.2.3 计算代价函数

    当你执行低度下降学习最小化代价函数J(θ)时,通过计算代价函数来监控收敛是很有帮助的。在这一小节,你将实现一个函数来计算J(θ),这样你就可以查看自己梯度下降算法的收敛。你的下一个任务是完成computeCost.m文件(计算J(θ)的函数)中的代码。当你做这个作业的时候要记得变量X和y不是标量,而是矩阵,矩阵中的每一行代表训练集中的一个样本。完成该功能后,ex1.m中的下一步将使用值为0的θ运行computeCost,你会看到代价函数的结果打印到屏幕上,并且为32.07。

            ==你现在应该提交答案==

2.2.4 梯度下降

    接下来你将在gradientDescent.m文件中实现梯度下降算法。

    循环结构已经为你写好了,你只需要补充每次迭代时更新θ的代码就好。

    在写代码时,请确保你已经理解了要优化的内容以及正在更新的内容。请记住,代价函数J(θ)是通过向量θ参数化的,而不是X和y。也就是说,我们是通过改变向量θ的值来最小化J(θ)的值,而不是通过改变X或y来最小化J(θ)的值。如果不确定的话请参考讲义和视频中的公式。

    确认梯度下降是否正确执行的一个方法是检查J(θ)的值是否每次都递减。给出的gradientDescent.m文件中每次迭代都调用了computeCost并且打印了代价函数的值。假设你已经完美实现了梯度下降法和computeCost,你的J(θ)不应该出现增加的值,而是最终收敛于一个稳定值的算法。

    当你完成后,ex1.m将使用最终参数来绘制线性拟合。结果应该如图2所示:

Figure 2: Training data with linear regression fit
    最终值θ也将用来预测35000和70000人的利润。请注意ex1.m中以下行使用矩阵乘法而不是显式求和或循环来计算预测。这是Octave/MATLAB中代码向量化的一个例子。

            ==你现在应该提交答案==

1
2
predict1 = [1, 3.5] * theta;
predict2 = [1, 7] * theta;

2.3 调试

    下面是实现梯度下降时需要记住的一些要点:

  • Octave / MATLAB数组索引从一开始,而不是零。如果将$θ_0$$θ_1$存储在名为theta的向量中,则值将为theta(1)和theta(2)。
  • 如果在运行时出现错误,请检查你的矩阵排列,以确保你正在相加和相乘维度可兼容的矩阵。使用size命令打印变量的维度信息将有助于调试。
  • 默认情况下,Octave/MATLAB将数学运算符解释为矩阵运算符。这是大小不兼容错误的常见来源。如果在Octave/MATLAB中不想要矩阵乘法,则需要添加“.”符号。例如,AB执行矩阵乘法,而A.B执行逐元素乘法。

2.4 可视化J(θ)

    为了更好地理解代价函数J(θ),现在要在一个二维网格中绘制$θ_0$$θ_1$的值。这部分你不需要编写新代码,但是你要理解你写过的代码是如果绘制这些图像的。

    在ex1.m的下一步中,有代码使用你写过的computeCost函数计算J(θ)的网格值。

1
2
3
4
5
6
7
8
9
% initialize J vals to a matrix of 0's
J vals = zeros(length(theta0 vals), length(theta1 vals));
% Fill out J vals
for i = 1:length(theta0 vals)
for j = 1:length(theta1 vals)
t = [theta0 vals(i); theta1 vals(j)];
J vals(i,j) = computeCost(x, y, t);
end
end

    执行上面的代码后,你将得到一个J(θ)值的二维数组。然后脚本ex1.m将使用这些值使用surf和contour命令生成J(θ)的曲面图和等高线图。这些图应该类似于图3:

Figure 3: Cost function J(θ)
    这些图的目的是向你展示J(θ)如何随着θ0和θ1变化。 代价函数J(θ)是碗状的并且具有全局最小值。(这在等高线图中比在3D表面图中更容易看到)。该最小值是`$θ_0$`和`$θ_1$`的最佳点,并且梯度下降的每一步都更接近该点。

选做练习

    如果你已经完成了前面部分的内容,那么恭喜你!你已经掌握了线性回归,并且可以将其运用到自己的数据集上。
    本次作业的内容还包括下面的可选作业题,它将是你对线性回归有更深层次的理解,如果你学有余力,我们鼓励你完成下面的部分。


3、多变量线性回归

    在这一部分你将使用多变量线性回归预测房屋价格。假设你准备卖掉自己的房子并且想知道卖多少才算一个好的市场价,比较好的方法就是收集最近的房屋成交信息并建立一个房屋估价模型。

    文件ex1data2.txt包含俄勒冈州波特兰市的房价训练集。第一列是房子的大小(单位是平方英尺),第二列是卧室的数量,第三列是房子的价格。

    准备好的ex1_multi.m脚本将帮你一步步完成本次练习。

3.1 特征标准化

    ex1_multi.m从加载和展示数据集中的一些值开始。观察这些值会注意到房屋大小大约是卧室数量的1000倍。当特征值差别比较大的时候,进行特征缩放可以使梯度下降收敛得更快。

    你目前的任务是完成featureNormalize.m中的代码完成以下要求:

  • 从数据集中减去每个特征的平均值
  • 在减去平均值之后,再用特征值除以它们各自的“标准差”

    标准偏差是一种衡量特征值在特定范围内变化幅度的方法(大多数数据点将位于平均值的±2标准偏差范围内),这是获取值范围(max-min)的替代方法。在Octave/MATLAB中,可以使用“std”函数计算标准差。例如,在featureNormalize.m中,X(:,1)包含了训练集中所有x1(房屋大小)的值,所以std(X(:,1))会计算房屋大小的标准差。在调用featureNormalize.m的时候,矩阵X的第一列$x_0$=1还没有添加(详情见ex1_multi.m)。

    你将对所有特征执行上面的操作,并且你的代码应该使用所有数据(任意数量的特性/示例)。

            ==你现在应该提交答案==

注意:在对特征进行规范化时,重要的是要存储用于标准化的值——用于计算的平均值和标准差。在从模型中学习参数之后,我们想要预测我们以前没有见过的房价。给定一个新的x值(房屋面积和卧室数量),首先,我们必须使用之前从训练集中计算出的均值和标准差对x进行归一化。

3.2 梯度下降

    以前,你在单变量回归问题上实现了梯度下降。现在唯一的不同是,矩阵X中多了一个特征。假设函数和批处理梯度下降更新规则仍然不变。

    你应该完成computeCostMulti.m和gradientDescentMulti.m文件中的代码以实现多变量线性回归的代价函数和梯度下降。如果你之前完成单变量作业题的代码支持多变量,你在这里可以直接用它。

    确保你的代码支持任意数量的特征,并且支持向量化。你可以使用“size(X, 2)”来找出数据集中有多少个特征。

            ==你现在应该提交答案==

注意:在多元情况下,代价函数也可以写成如下矢量形式,矢量化的版本在使用像Octave/MATLAB这样的数字计算工具时非常有效。


如果你是矩阵运算的专家,你可以证明这两种形式是等价的。

3.2.1 可选练习:选择学习率

    在这部分练习中,你将尝试为数据集尝试不同的学习率,并找到快速收敛的学习率。你可以通过修改ex1_multi.m并更改设置学习率的代码部分来更改学习率。

    然后ex1_multi.m调用你的gradientDescent.m函数并在选定学习率的情况下迭代50次梯度下降。该函数还会在向量J中返回J(θ)历史值。在最后一次迭代之后,ex1_multi.m脚本将绘制J值关于迭代次数的函数图。

    如果你的学习率选在合适的范围内,那么你的图应该如图4所示。如果你的图和图4的差别较大,尤其是J(θ)在增加甚至看起来很离谱,你应该调整学习率再试一次。我们建议在对数刻度上尝试学习率α的值,步长约为前一个值的3倍(即0.3,0.1,0.03,0.01等)。
如果要查看曲线的整体趋势,你可能还需要调整正在运行的迭代次数。

Figure 4: Convergence of gradient descent with an appropriate learning rate
> **注意**:如果你的学习速度太大,J(θ)可能会发散并“爆炸”,从而导致数值太大以至于计算机无法计算。这种情况下,Octave/MATLAB会返回NaN。NaN代表“非数字”,通常由包括-∞和+∞在内的的未定义操作引起。

Octave/MATLAB提示:为了比较不同的学习速率是如何影响收敛的,在同一个图中绘制几个不同学习速率代价函数图是有帮助的。在Octave/MATLAB中,这可以通过在plot函数之间使用“hold on”命令多次进行梯度下降进行实现。具体一点就是说,如果你尝试了三个不同的alpha值(你应该尝试更多的值),并将代价函数存储在J1、J2和J3变量中,那么你可以使用以下命令将它们绘制在同一个图上:

1
2
3
4
plot(1:50, J1(1:50), ‘b’);
hold on;
plot(1:50, J2(1:50), ‘r’);
plot(1:50, J3(1:50), ‘k’);

最后一个参数‘b’,‘r’,‘k’指定各个图的颜色。

    注意随着学习速率的变化,收敛曲线的变化。
在学习速率较小的情况下,你应该会发现梯度下降需要很长时间才能收敛到最优值。相反,在学习速率较大的情况下,梯度下降可能不会收敛,甚至可能偏离!

    用你找到的最佳学习率运行ex1_multi.m脚本来运行梯度下降,直到收敛到最终的θ值。然后用这个θ值来预测1650平方尺大小、3个卧室的房子的价格。稍后你会用这个值来验证正规方程。在做预测的时候不要忘记特征标准化。

3.3 正规方程

    在课堂视频中,你们学过线性回归的封闭解是

    使用这个公式不需要任何特征缩放,你将在一个计算中得到一个精确值:像梯度下降一样,没有“循环,直到收敛”。

    在normalEqn.中完成代码以使用上面的公式计算θ。记住,我们虽然不需要特征缩放,但是仍然需要在X矩阵中添加值为1的列作为截距项($θ_0$)。ex1.m中的代码会把值为1的列加到X中。

            ==你现在应该提交答案==

    现在你已经通过正规方程得到了θ的值,用它来为1650平方尺、三个卧室的房子进行估值,你会发现和前面用梯度下降预测到的值是完全拟合的。

提交和评分

    在完成任务的各个模块后,请务必使用提交系统向我们的服务器提交你的作业。以下是这个练习的每个部分如何得分的细则。

    你可以多次提交,但我们只考虑最高分。

程序设计练习2:逻辑回归

介绍

    在这个练习中,你将实现逻辑回归并将其运用在两个不同的数据集上。在开始这个练习之前,我们强烈建议你观看视频讲座并完成相关主题的复习题。

    要开始练习,你需要下载起始代码并将其内容解压缩到你希望完成练习的目录中。如果需要,请在开始本练习之前使用Octave/MATLAB中的cd命令更改到此目录。

    你也可以在课程网站的“环境设置说明”中找到安装Octave/MATLAB的说明。

本练习中包含的文件

ex2.m - 指导你完成本次练习的Octave/MATLAB脚本
ex2_reg.m - 练习后面部分的Octave/MATLAB脚本
ex2data1.txt - 第一部分的数据集
ex2data2.txt - 第二部分的数据集
submit.m - 将你的答案发送到我们服务器的提交脚本
mapFeature.m - 用于生成多项式特征的函数
plotDecisionBoundary.m - 绘制分类器决策边界的函数
[*] plotData.m - 绘制2D分类数据的函数
[*] sigmoid.m - S形函数
[*] costFunction.m - 逻辑回归代价函数
[*] predict.m - 逻辑回归预测函数
[*] costFunctionReg.m - 正规化逻辑回归函数

* 表示你要完成的文件

    在整个练习中,你将使用ex2.m和ex2_reg.m脚本。这些脚本为后面的问题设置数据集,并调用你将要编写的函数。你无需修改它们两个,你只需按照本作业中的说明修改其他文件中的功能。

在哪里寻求帮助

    本课程的练习使用非常适合数值计算的高级编程语言Octave或MATLAB。如果你没有安装Octave或MATLAB,请参阅课程网站上“环境设置说明”中的安装说明。

    在Octave/MATLAB命令行中输入help紧跟函数名称会显示内建的函数说明。比如输入help plot会显示绘图函数的帮助信息。更多Octave和MATLAB的函数说明请在Octave官网MATLAB官网查阅。

    我们也非常鼓励使用在线讨论与其他学生讨论练习。但是,不要查看任何源代码或与他人共享源代码。


1、逻辑回归

    在这部分练习中,你将构建一个逻辑回归模型预测一个学生是否会被大学录取。假设你是某个大学院系的管理人员,你打算根据申请人两次考试的成绩来判断他们被录取的可能性。你有可以用作逻辑回归训练集的以前申请者的历史数据。每个训练样本都包含历史申请人两次考试的成绩和最终的录取结果。

    你的任务是建立一个根据申请者两次考试的分数预测他们被录取可能性的分类模型。本大纲和ex2.m中的框架代码将指导你完成这项练习。

1.1 可视化数据

    如果可能的话,在实现任何学习算法之前可视化数据总是好的。在ex2.m的第一部分,该代码会加载数据并调用函数plotData将其显示在二维图中。

    现在你将完成plotData中的代码,最终结果如图1所示,其中两个轴代表两次考试成绩,正负样本用不同标记表示。

Figure 1: Scatter plot of training data
    为了让你更熟悉绘图,我们没有实现plotData.m中的代码,所以你可以尝试自己实现它。当然,是否自己实现是可选的,我们在下面给出了我们已经实现的代码,你可以直接复制或者参考。如果你要复制我们的代码,请参阅Octave/MATLAB的说明文档确保你完全理解了每行命令的含义。
1
2
3
4
5
6
7
% Find Indices of Positive and Negative Examples
pos = find(y==1); neg = find(y == 0);
% Plot Examples
plot(X(pos, 1), X(pos, 2), 'k+','LineWidth', 2, ...
'MarkerSize', 7);
plot(X(neg, 1), X(neg, 2), 'ko', 'MarkerFaceColor', 'y', ...
'MarkerSize', 7);

1.2 实现

1.2.1 热身练习:S函数

    在开始实际成本函数之前,请回想一下逻辑回归假设定义为:

    其中g为S函数,它的定义为:
    首先你需要在sigmoid.m中实现S函数,这样你才能在其他程序中调用它。完成之后在Octave/MATLAB命令行中用一些值通过调用sigmoid(x)来测试一下。当x为正数且很大的时候,S的值应该接近1;当x为负数且很小的时候,S的值应该接近0;S(0)的值应该是0.5。你的代码还要可以处理向量和矩阵,对于矩阵,S函数将作用在矩阵的每个元素上。

    你可以在Octave/MATLAB命令行中键入submit提交答案进行评分。提交脚本会提示你输入登录的e-mail和token,你可以网页中获取本次作业的token。

            ==你现在应该提交答案==

1.2.2 代价函数和梯度

    现在你将实现逻辑回归的代价函数和梯度下降。完成costFunction.m的代码以返回代价值和梯度值。

    回顾一下逻辑回归中的代价函数:

    并且该代价函数的梯度函数是一个和θ长度相同的向量,且第j个元素(for j = 0, 1, . . . , n)的定义如下:
    注意,虽然这个梯度下降函数看起来与线性回归的梯度下降函数相同,但实际上是不同的,因为线性和逻辑回归的代价函数`$h_θ(x)$`的定义是不同的。

    当你完成后,ex2.m会使用θ的初始值调用你的costFunction函数,你应该看到代价值大约是0.693。

            ==你现在应该提交答案==

1.2.3使用fminunc学习参数

    在前面的作业中,你通过实现梯度下降找到了线性回归模型的最优参数。你写了一个代价函数并计算它的梯度函数,然后做了一个梯度下降的步骤。这一次,不用梯度下降,而是使用Octave/MATLAB内置的一个名为fminunc的函数。

    Octave/MATLAB的fminunc是一个寻找无约束函数最小值的优化求解器。对于逻辑回归,我们想优化代价函数J(θ)的参数θ。

    具体而言,在给定固定数据集(X和y值)的情况下,你将使用fminunc查找逻辑回归代价函数的最佳参数θ。 你将传递给fminunc以下输入:

  • 我们要优化参数的初始值
  • 当给定训练集和特定θ时,计算逻辑回归代价值和相对于数据集(X,y)的θ的梯度的函数

    在ex2.m中我们已经写好了用正确参数调用fminunc的代码。

1
2
3
4
5
6
% Set options for fminunc
options = optimset('GradObj', 'on', 'MaxIter', 400);
% Run fminunc to obtain the optimal theta
% This function will return theta and the cost
[theta, cost] = ...
fminunc(@(t)(costFunction(t, X, y)), initial theta, options);

    在这段代码中,我们首先定义了与fminunc一起使用的一些选项。具体来说,我们将GradObj选项为on,它告诉fminunc,我们的函数返回代价值和梯度。这允许fminunc在最小化函数时使用梯度。此外,我们将MaxIter选项设置为400,这样fminunc在终止之前最多可以运行400次。

    为了指定我们最小化的实际函数,我们使用简写来指定带有@(t)(costFunction(t,X,y))的函数。这样会创建一个带有参数t的函数,该函数调用costFunction。 这允许我们包装costFunction以用于fminunc。

    如果你已经正确完成了costFunction,fminunc将收敛于正确的优化参数并返回最终的代价值和θ值。注意,通过使用fminunc,你不必自己编写任何循环,也不必像梯度下降那样设置学习率。这都是由fminunc完成的:你只需要提供一个计算代价和梯度的函数。

    一旦fminunc运行结束,ex2.m将使用最优的θ调用costFunction函数,你会看到代价值大约是0.203。

    最终的θ值将用于训练数据的决策边界,执行结果类似于图2。我们还鼓励你查看plotDecisionBoundary中的代码以了解如何使用θ绘制这样的决策边界。

Figure 2: Training data with decision boundary
#### 1.2.4 评估逻辑回归

    在训练到了参数后,你应该使用模型来预测某个特定的学生是否会被录取。对于一个第一次考试成绩为45,第二次考试成绩为85的学生,最后被录取的概率应该大约是0.776。

    另一种评估训练到的参数质量的方法是看模型对我们的训练集的预测情况。这部分你的任务是完成predict.m中的代码。对于给定的训练集和参数θ,预测函数的预测值为1或0。

    完成predict.m中的代码后,ex2.m脚本将通过计算正确的示例百分比来继续报告分类器的训练准确性。

            ==你现在应该提交答案==

2、正则化逻辑回归

    这部分练习中,你将实现正则化逻辑回归来预测工厂生产的芯片质量是否合格。在质检过程中,芯片会经过各种测试以确保其能正常工作。

    假设你是某工厂的生产经理,并且你有一些芯片在两次不同测试中的测试结果。从这两个测试中,你想知道这些芯片是否合格。为了帮你做决定,你有一个以前芯片测试结果的数据集,从中你可以构建逻辑回归模型。

    你会ex2_reg.m脚本来完成这部分练习。

2.1 数据可视化

    与本练习的前几部分类似,plotData用于生成如图3所示的图,其中坐标轴是两次测试分数,使用不同的标记显示合格(y = 1,接受)和不合格(y = 0,拒绝)示例。

Figure 3: Plot of training data
    图3显示了我们的数据集不能通过一条直线将其分为正示例和负示例。因此,逻辑回归的直接应用在这个数据集上不会有很好的效果,因为逻辑回归只能找到一个线性的决策边界。

2.2 特征映射

    更好地拟合数据的一种方法是从每个数据点创建更多的特性。在提供的函数mapFeature.m,我们将把特征映射到x1和x2的所有多项式项直到特征的6次方。

    这个映射的结果是,我们的两个特征向量(两个QA测试的分数)被转换成一个28维的向量。在这个高维特征向量上训练逻辑回归分类器将具有更复杂的决策边界,并且在二维图中呈现非线性。虽然特征映射允许我们构建一个更具表现力的分类器,但它也更容易过度拟合。在练习的下一部分中,你将实现正则化逻辑回归来拟合数据,并亲自查看正则化如何帮助解决过拟合问题。

2.3 代价函数和梯度

    现在你将编写代码来计算正则化逻辑回归的代价函数和梯度。请完成costFunctionReg.m中的代码并返回代价值和梯度。

    回顾一下正则化逻辑回归的代价函数是:

    请注意,你不能正则化参数θ0。在Octave/MATLAB中,回想一下,索引从1开始,因此,你不应该在代码中正则化θ(1)参数(对应于θ0)。代价函数的梯度是一个向量,其中第j个元素的定义如下:
    当你完成后,ex2_reg.m会用θ的初始值(初始值全为0)调用costFunctionReg函数,你应该会看到代价值大约是0.693。

            ==你现在应该提交答案==

2.3.1 使用fminunc学习参数

    和前面部分相似,你将使用fminunc学习参数θ。如果你已经正确完成了正则化逻辑回归的代价函数和梯度(在costFunctionReg.m中),你应该可以使用fminunc逐步完成ex2_reg.m的下一部分以学习参数θ。

2.4 绘制决策边界

    为了帮你可视化通过此分类器学习到的模型,我们已经提供了可以绘制非线性决策边界的函数plotDecisionBoundary.m以区分积极和消极样本。在plotDecisionBoundary.m中,我们通过计算分类器在等间距网格上的预测来绘制非线性决策边界,然后绘制预测从y = 0到y = 1变化的等高线图。

    在学习到参数θ后,下一步就是在ex_reg.m中绘制如图4所示的决策边界:

Figure 4: Training data with decision boundary (λ = 1)
### 2.5 可选练习

    在这部分练习中,你将尝试为数据集使用不同的正则化参数,以了解正则化是如何防止过拟合的。

    注意到随着λ的变化决定边界的变化。λ较小时,你应该发现,对于每个训练样本分类器得到几乎都是正确的结果,但是它做了一个非常复杂的边界,因此过度拟合数据(如图5)。这不是一个好的决策边界:例如,它预测一个点x =(−0.25,1.5)被接受(y = 1),这在训练样本中似乎是一个错误的决策。

    λ较大时,你应该看到一个图显示了一个简单的决定边界仍然把正样本和负样本分的相当好。然而,如果λ值太高,你就不会得到一个不错的选择,决定边界不会很好地跟踪数据,因此欠拟合(图6)。

    你不需要为这些可选的(未评分的)练习提交任何解决方案。

Figure 5: No regularization (Overfitting) (λ = 0)
Figure 6: Too much regularization (Underfitting) (λ = 100)
## 提交和评分

    完成作业的各个部分后,请务必使用提交系统将你的作业提交给我们的服务器。以下是对此练习的每个部分进行评分的细则。

    你可以多次提交作业,但我们只考虑最高分。

程序设计练习3:多类分类和神经网络

介绍

    在这个练习中,你将实现一对多逻辑回归和神经网络来识别手写数字。在开始编程之前,我们强烈建议观看视频课程并完成相关主题问题的复习。

    要开始练习,你需要下载起始代码并将其内容解压缩到你希望完成练习的目录中。如果需要,请在开始本练习之前使用Octave/MATLAB中的cd命令更改到此目录。

    你也可以在课程网站的“环境设置说明”中找到安装Octave/MATLAB的说明。

本练习包含的文件

ex3.m - 指导你完成第一部分练习的Octave/MATLAB脚本
ex3_nn.m - 指导你完成第二部分练习的Octave/MATLAB脚本
ex3data1.mat - 手写数字的训练集
ex3weights.mat - I神经网络练习的初始权值
submit.m - 提交作业的脚本
displayData.m - 帮助可视化数据集的函数
fmincg.m - 最小化函数程序(类似于fminunc)
sigmoid.m - S函数
[*] lrCostFunction.m - 逻辑回归代价函数
[*] oneVsAll.m - 训练一个one-vs-all多类分类器
[*] predictOneVsAll.m - 使用one-vs-all多类分类器预测
[*] predict.m - 神经网络预测函数
* 表示你要完成的文件

    在本练习中,你将使用ex3.m和ex3_nn.m脚本。这些脚本为题目设置数据集并且调用你编写的函数。你不需要修改这些脚本,只需要按照作业说明定义其他函数。

在哪里寻求帮助

    本课程的练习使用非常适合数值计算的高级编程语言Octave或MATLAB。如果你没有安装Octave或MATLAB,请参阅课程网站上“环境设置说明”中的安装说明。

    在Octave/MATLAB命令行中输入help紧跟函数名称会显示内建的函数说明。比如输入help plot会显示绘图函数的帮助信息。更多Octave和MATLAB的函数说明请在Octave官网MATLAB官网查阅。

    我们也非常鼓励使用在线讨论与其他学生讨论练习。但是,不要查看任何源代码或与他人共享源代码。


1、多分类

    在这个练习中,你将使用逻辑回归和神经网络识别手写数字(0到9)。自动手写数字识别在今天得到了广泛的应用——从识别信封上的邮政编码到识别银行支票上的金额。这个练习将向你展示如何将你所学的方法用于这个分类任务。

    在本练习的第一部分中,你将扩展之前对逻辑回归的实现,并将其应用于one-vs-all分类。

1.1 数据集

    你会在ex3data1.mat中得到一个包含5000个手写数字识别样本的训练集。mat格式意味着该数据集保存为原生的Octave/MATLAB矩阵格式,而不是像csv那样的文本格式。这些矩阵可以通过load命令直接加载到你的程序中。加载之后,维度和值都无误的矩阵已经被加载到内存中了,而且该矩阵已经被命名了,你不需要对其重命名。

1
2
3
% Load saved matrices from file
load('ex3data1.mat');
% The matrices X and y will now be in your Octave environment

    ex3data1.mat中有5000个训练样本,其中每个训练样本都是一个20px * 20px的灰度数字图像。每个像素由一个浮点数表示,该浮点数表示该位置的灰度强度。20×20像素的网格被“展示”成一个400维的向量。这些训练示例中的每一个都变成了数据矩阵X中的一行。这样就给了我们一个5000×400矩阵X,其中每一行都是手写数字图像的训练示例。

    训练集的第二部分是包含训练集标签的5000维向量y,为了使它与Octave/MATLAB索引更加兼容,在没有零索引的情况下,我们将数字0映射到值10。因此,数字“0”被标记为“10”,而“1”到“9”的数字按其自然顺序被标记为“1”到“9”。

1.2 数据可视化

    你将从可视化训练集的一个子集开始。在ex3.m的Part 1中,代码随机从X中选择100行并将其传递给函数displayData。该函数将每一行映射到20px * 20px的灰度图像,并将图像一起显示。我们提供了displayData函数,同时也鼓励你观察该函数是如何工作的。
运行代码之后,你应该会看到一张图如图1所示:

Figure 1: Examples from the dataset
### 1.3 向量化逻辑回归

    你将使用多个one-vs-all逻辑回归模型来构建一个多类分类器。由于有10个类(1-10个数字),你将需要训练10个单独的逻辑回归分类器。为了使训练有效,确保代码良好地支持向量化非常重要。在本节中,你将实现逻辑回归的向量化版本,它不使用任何for循环。你可以使用上一个练习中的代码作为这个练习的开始。

1.3.1 向量化代价函数

    我们将从编写成本函数的向量化版本开始。回想一下,在非正则化逻辑回归中,成本函数是:

    为了对每个元素求和,我们必须计算hθ(x(i)),其中:
是S函数。这样一来我们就可以用矩阵乘法快速计算所有样本。其中X和他和θ的定义为:
然后通过计算得到矩阵Xθ,我们有:
在上个等式中,假设a和b都是向量,我们使a.T * b = b.T * a。这样我们就可以在一行代码中计算所有样本i的θ.T*x(i)(i为上标,表示第i个样本)。     你的任务就是在lrCostFunction.m中写非正则化代价函数的代码。你的实现应该使用我们上面的策略来计算的θ.T*x(i)。你还应该对代价函数的其余部分使用向量化的方法,一个完全向量化的lrCostFunction.m应该是没有任何循环的。

1.3.2 向量化梯度

    回想一下(非正则化)逻辑回归成本的梯度是一个向量,其中第j个元素定义为:

    为了在数据集上对该操作进行向量化,我们首先明确地为所有θj写出所有偏导数,
    注意,当(`$h_{θ}(x^{(i)}) - y^{(i)}$`)是一个标量(单个数字)时,`$x^{(i)}$`是一个向量。为了最后一步求导,我们让`$β_i = (h_θ(x^{(i)}) - y^{(i)}$`并观察:
    上面的表达式允许我们在不适用任何循环的情况下计算所有偏导数。 如果你对线性代数比较熟悉,我们建议你通过上面的矩阵乘法,因为矢量化版本执行相同的计算。 你现在应该实现等式1来计算正确的矢量化梯度。 一旦结束后,就完成了通过实现梯度来完成函数lrCostFunction.m。

调试技巧:向量化代码有时很棘手。调试的一个常见策略是使用size函数打印出正在处理的矩阵的大小。例如,给定一个大小100×20(100个样本,20特征)的数据矩阵的和一个维度维度是20×1θ的向量,你可以观察到Xθ是一个有效的乘法操作,虽然θX不是。此外,如果你有代码的非向量化版本,你可以比较向量化代码和非向量化代码的输出,以确保它们产生相同的输出。

1.3.3 向量化正则化逻辑回归

    在实现了逻辑回归向量化之后,现在你将向成本函数添加正则化。回想一下,对于正则化逻辑回归,成本函数的定义是

    注意,你不能正则化偏差项`$θ_0$`。

    相应地,正规化的逻辑回归代价函数对于$θ_j$的偏导数被定义为

    现在在lrCostFunction中修改代码以实现正则化。 同样,代码中不应该出现任何循环。

            ==你现在应该提交答案==

1.4 One-vs-all分类器

    在这部分练习中,你将通过训练多个规范化逻辑回归分类器来实现one-vs-all分类,每个规范化逻辑回归分类器对应于我们数据集中的K个类(图1)。在手写数字识别训练集中,K=10,但是你的代码应该接受任意值的K。

    你现在应该完成oneVsAll.m中的代码为每个类训练一个分类器。特别是,你的代码应该在一个矩阵θ中返回所有分类器的参数,每一行Θ对应于学习到的逻辑回归参数的一个类。你可以使用一个从1到K的for循环来实现这一点,独立地训练每个分类器。

    注意,这个函数的参数y是从1到10的标签向量,其中我们将数字“0”映射到标签10(以避免与索引混淆)。

    当训练的分类器类k∈{1,…K},会想要一个标签y的m维向量($y_j$∈0,1表)示第j训练实例是否属于类K($y_j$ = 1),或者如果它属于一个不同的类($y_j$ = 0)。你可能会发现逻辑阵列有助于这项任务。

Ocvate/MATLAB技巧:在Octave/MATLAB中的逻辑数组是包含二进制(0或1)的数组。在Octave/MATLAB中对于向量a(m * 1)和标量b计算a==b时,会返回一个和a大小相同的向量。该向量的元素值在a中的元素等于b的位置为1,否则为0。为了看清楚他们是如何工作的,在你的Octave/MATLAB环境中执行以下代码:

1
2
3
a = 1:10; % Create a and b
b = 3;
a == b % You should try different values of b here

    此外,你将在本练习中使用fmincg(而不是fminunc)。
fmincg的工作原理与fminunc类似,但在处理大量参数时效率更高。

    在你正确完成oneVsAll.m中的代码之后,ex3.m将调用oneVsAll函数来训练多类分类器。

            ==你现在应该提交答案==

1.4.1 One-vs-all预测

    在训练了你的one-vs-all分类器之后,你可以使用它来预测给定图像中包含的数字。对于每个输入,你应该使用训练过的逻辑回归分类器计算它属于每个类的“概率”。你的one-vs-all预测函数将选择相应的逻辑回归分类器输出的概率最大的类,并返回类标签(1,2,…,或K)作为输入示例的预测。

    你现在应该完成predictOneVsAll.m中的代码来做预测。

    当你完成之后,ex3.m将会用你学习到的θ调用predictOneVsAll函数。可以看到,训练集的准确率约为94.9%(即正确分类了训练集中94.9%的样本)。

            ==你现在应该提交答案==

2、神经网络

    在前面的练习中,你实现了多分类逻辑回归来识别手写数字。然而,逻辑回归只是一个线性分类器,不能形成更复杂的假设。

    在这部分练习中,你将使用和前面同样的训练集实现一个神经网络来识别手写数字。神经网络将能够表示非线性假设的复杂模型。本周,你将使用我们已经训练过的神经网络中的参数。 你的目标是实现前馈传播算法以使用我们的权重进行预测。在下周的练习中,你将编写用于学习神经网络参数的反向传播算法。

    ex3_nn.m脚本将带你逐步完成本次练习。

2.1 模型表示

    我们的神经网络如图2所示,总共有3层——一个输入层、一个隐藏层和一个输出层。回想一下,我们的输入是数字图像的像素值。由于图像大小20×20,因此给了我们400个输入层单位(不包括额外的偏差项)。和之前一样,训练数据将被加载到变量X和y中。

    我们已经为你提供了我们训练的一组神经网络参数($θ^{(1)}$,$θ^{(2)}$)。他们存在ex3weights.mat中并可以使用ex3_nn.m加载到Theta1和Theta2中。参数具有针对神经网络的大小,其在第二层中具有25个单元并且具有10个输出单元(对应于10个数字类)。

1
2
3
4
5
6
% Load saved matrices from file
load('ex3weights.mat');
% The matrices Theta1 and Theta2 will now be in your Octave
% environment
% Theta1 has size 25 x 401
% Theta2 has size 10 x 26
Figure 2: Neural network model
### 2.2 前馈传播与预测

    现在你将实现神经网络的前馈传播。你需要完成predict.m中的代码来返回神经网络的预测。

    你应该实现前馈计算,该计算为每个样本i计算$h_θ(x^{(i)})$并返回相关的预测。 类似于one-vs-all分类策略,来自神经网络的预测将是具有最大输出($h_θ(x)$)k的标签。

实现注意:矩阵X以行为单位包含样本。当你完成predict.m中的样本时,你需要在矩阵中添加全为1的列。矩阵Theta1和矩阵Theta2以行为单位为每一项包含参数。就是说,Theta1的第一行对应第二层中的第一个隐藏单元。在Octave/MATLAB中,当你计算z(2)=θ(1)a(1)时,确保你索引了(如果必要的话,转置)X这样你就得到了a(l)作为列向量。

    一旦你完成之后,ex3_nn.m将使用加载的Thea1和Theta2参数调用predict函数。你应该看到准确率大约是97.5%。之后,一个交互式序列将每次从训练集中启动一个显示图像,而控制台将打印出显示图像的预测标签。要停止图像序列,请按Ctrl-C。

    ==你现在应该提交答案==

提交和评分

    完成此任务后,请确保使用submit函数将你的解决方案提交到我们的服务器。下面是这个练习的每个部分是如何得分的说明。

    你可以多次提交作业,但我们只考虑最高分。

程序设计练习4:神经网络学习

介绍

    在这个练习中,你将实现神经网络的反向传播算法并将其应用到手写数字识别的任务中。在开始编程之前,我们强烈建议你线观看视频课程并完成相关主题的复习问题。

    要开始练习,你需要下载起始代码并将其内容解压缩到你希望完成练习的目录中。如果需要,请在开始本练习之前使用Octave/MATLAB中的cd命令更改到此目录。

    你也可以在课程网站的“环境设置说明”中找到安装Octave/MATLAB的说明。

本练习包含的文件

ex4.m - 指导你完成练习的Octave/MATLAB脚本
ex4data1.mat - 手写数字识别的训练集
ex4weights.mat - exercise 4的神经网络参数
submit.m -提交作业的脚本
displayData.m - 可视化数据集的脚本
fmincg.m - 最小化函数 (类似于fminunc)
sigmoid.m - S函数
computeNumericalGradient.m - 数值计算梯度
checkNNGradients.m - 帮助检查你的梯度的代码
debugInitializeWeights.m - 初始化权重的函数
predict.m - 神经网络预测函数
[*] sigmoidGradient.m - 计算S函数的梯度
[*] randInitializeWeights.m - 随机初始化权重
[*] nnCostFunction.m - 神经网络代价函数

* 表示你需要完成的文件

    在本练习中,你将使用ex4.m脚本。该脚本为题目设置数据集并且调用你编写的函数。你不需要修改这些脚本,只需要按照作业说明定义其他函数。

在哪里寻求帮助

    本课程的练习使用非常适合数值计算的高级编程语言Octave或MATLAB。如果你没有安装Octave或MATLAB,请参阅课程网站上“环境设置说明”中的安装说明。

    在Octave/MATLAB命令行中输入help紧跟函数名称会显示内建的函数说明。比如输入help plot会显示绘图函数的帮助信息。更多Octave和MATLAB的函数说明请在Octave官网MATLAB官网查阅。

    我们也非常鼓励使用在线讨论与其他学生讨论练习。但是,不要查看任何源代码或与他人共享源代码。


1、神经网络

    在前面的练习中,你实现了神经网络的前馈传播并根据我们提供的权重使用它来预测手写数字的识别。在本练习中,你将实现反向传播算法来学习神经网络的参数。

    ex4.m脚本将指导你逐步完成本练习。

1.1 数据可视化

    在ex4.m的第一部分,代码加载数据集并通过调用displayData函数将其展示在一个二维图中(图1)。

Figure 1: Examples from the dataset
    这和你在前面的练习中使用的是同一个数据集。在ex3data1.mat中有5000个训练样本,每一个训练样本是一个20px * 20px的灰度数字图像。 每个像素由一个浮点数表示,该浮点数表示该位置的灰度强度。20×20像素的网格被“展示”成一个400维的向量。这些训练示例中的每一个都变成了数据矩阵X中的一行。这样就给了我们一个5000×400矩阵X,其中每一行都是手写数字图像的训练示例。
    训练集的第二部分是包含训练集标签的5000维向量y,为了使它与Octave/MATLAB索引更加兼容,在没有零索引的情况下,我们将数字0映射到值10。因此,数字“0”被标记为“10”,而“1”到“9”的数字按其自然顺序被标记为“1”到“9”。

1.2 模型表示

    我们的神经网络如图2所示,总共有3层——一个输入层、一个隐藏层和一个输出层。回想一下,我们的输入是数字图像的像素值。由于图像大小20×20,因此给了我们400个输入层单位(不包括额外的偏差项)。和之前一样,训练数据将被加载到变量X和y中。

    我们已经为你提供了我们训练的一组神经网络参数($θ^{(1)}$,$θ^{(2)}$)。他们存在ex4weights.mat中并可以使用ex4.m加载到Theta1和Theta2中。参数具有针对神经网络的大小,其在第二层中具有25个单元并且具有10个输出单元(对应于10个数字类)。

1
2
3
4
5
6
% Load saved matrices from file
load('ex3weights.mat');
% The matrices Theta1 and Theta2 will now be in your Octave
% environment
% Theta1 has size 25 x 401
% Theta2 has size 10 x 26
Figure 2: Neural network model
### 1.3 前馈和代价函数

    现在你将实现神经网络的代价函数和梯度。首先完成nnCostFunction.m中的代码返回代价。

    回想一下神经网络的代价(没有正则化)

其中`$h_θ(x^{(i)})$`如图2所示计算,K=10是可能标签的总数。注意,`$h_θ(x^{(i)})_k$` = `$a_k^{(3)}$`是第k个输出单元的激活(输出值)。另外,回想一下,原来的标签(在变量y中)是1,2,…, 10,为了训练神经网络,我们需要将标签重新编码为只包含0或1值的向量,像这样
    例如,如果`$x^{(i)}$`是一个数字5的图像,那么对应的`$y^{(i)}$`(你在代价函数中使用的)应该是一个10维的向量,其中`$y_5$`=1,其他元素为0。

    你应该实现前馈计算,该计算为每个样本计算$h_θ(x^{(i)})$并且对所有样本的代价求和。你的代码应该适用于具有任意数量标签任意规模的数据集(你可以认为总会有至少K≥3个标签)。

实现注意:矩阵X以行为单位包含样本(例如X(i, :)是第i个训练样本,表示为一个n * 1的向量)。当你在nnCostFunction.m中写代码时,你需要在X矩阵中添加全为1的列。神经网络中每个单元的参数用Theta1和Theta2表示为一行。具体地说,Theta1的第一行对应于第二层中的第一个隐藏单元。你可以在示例中使用for循环来计算代价。

    一旦你完成了,ex4.m将使用加载的Theta1和Theta2参数调用nnCostFunction.m。你应该看到成本大约是0.287629。

            ==你现在应该提交答案==

1.4 正则化的代价函数

    正则化的神经网络的代价函数如下:

    假设神经网络有三层——一个输入层,一个隐藏层和一个输出层。但是,你的代码应该适用于任意数量的输入单元、隐藏单元和输出单元。虽然为了清楚起见,我们已明确列出了`$Θ^{(1)}$`和`$Θ^{(2)}$`的上述索引,但请注意,你的代码应使用任何大小的`$Θ^{(1)}$`和`$Θ^{(2)}$`。

    请注意,你不应该将与偏差对应的那一项正则化。对矩阵Theta1和矩阵Theta2来说,所对应的就是它们的第一列。你现在应该正则化你的代价函数。注意,你可以先使用现有的nnCostFunction.m计算非正则化代价函数J然后为其添加正则项。

    一旦完成,ex4.m将使用加载了Theta1和Theta2的参数集调用你的nnCostFunction,并且λ=1。你应该看到成本约为0.383770。

            ==你现在应该提交答案==

2、反向传播

    在这部分练习中,你将使用反向传播算法为神经网络代价函数计算梯度。你需要完成nnCostFunction.m,以便返回适当的梯度值。 计算出梯度后,你将能够通过使用高级优化器(如fmincg)最小化成本函数J(Θ)来训练神经网络。

    你将首先实现反向传播算法来计算(未正则化的)神经网络的参数的梯度。当你验证了你的梯度计算(非正则化的例子)是正确的之后,你将实现正则化神经网络的梯度。

2.1 Sigmoid梯度

    为了帮你开始这部分练习,你第一个需要实现的是sigmoid梯度函数。sigmoid函数的梯度计算公式如下:

    当你结束之后,尝试在Octave/matlab命令行中通过调用sigmoidGradient(z)来测试一些值。对于z的比较大的值(包括正值和负值),梯度应该接近于0。当z=0时,梯度应该正好是0.25。你的代码还应该处理向量和矩阵。对于矩阵,函数应该对每个元素执行sigmoid梯度函数。

            ==你现在应该提交答案==

2.2 随机初始化

    在训练神经网络时,随机初始化参数以进行对称破坏非常重要。随机初始化比较好的一个策略是在范围[-E,E]中随机均匀地为θ选择值。你应该让E=0.12。这个范围的值确保参数保持在较小的范围内,并使学习更有效。

    你的工作是完成randInitializeWeights.m中的代码为θ初始化权重,修改文件并加入以下代码:

1
2
3
% Randomly initialize the weights to small values
epsilon init = 0.12;
W = rand(L out, 1 + L in) * 2 * epsilon init − epsilon init;

            ==这部分练习不需要提交任何代码==

2.3 反向传播

Figure 3: Backpropagation Updates.
    现在你将实现反向传播算法。回想一下,反向传播算法背后的直观理解如下。 给定一个训练样本(`$x^{(t)}$`, `$y^{t}$`),我们将首先运行一个“向前传递”来计算整个网络中的所有激活,包括假设函数的输出值。然后我们为第l层的每个节点(神经元)j计算误差`$δ^{(l)}_j$`,它来权衡该节点对输出中的任何错误“负责”的程度。

    对于输出节点,我们可以直接测量网络激活和真实目标值之间的差异,并使用它来定义$δ^{(3)}_j$(因为第3层是输出层)。对于隐藏单元,你将根据(l+1)层中节点的误差项的加权平均值计算$δ^{(l)}_j$

    详细一点就是,这是反向传播算法(图3)。你应该在一个循环中实现步骤1到4,该循环每次处理一个样本。具体来说,你应该为t=1:m实现一个for循环并在for循环中加入下面的四个步骤,第t次迭代在第t个训练样本($x^{(t)}$, $y^{t}$)上执行计算。步骤5将累积梯度除以m得到神经网络代价函数的梯度。

    1、将输入层的值($a^{(1)}$)设置为第t个训练样本$x^{(t)}$。执行一个前馈传递(图2)为第二层和第三层计算激活($z^{(2)}$, $a^{(2)}$, $z^{(3)}$, $a^{(3)}$)。请注意,你需要添加+1项以确保层a(1)和a(2)的激活矢量也包括偏置单元。在Octave/MATLAB中,如果a_1是一个列向量,为列向量添加1对应a_1 = [1; a_1]。

    2、对在第三层(输出层)的每个输出单元k,设置

其中`$y_k$`∈ {0,1},指示当前训练样本是属于类k(`$y_k$`= 1),还是属于不同的类(`$y_k$`= 0)。 你可能会发现逻辑数组对此任务有帮助(在之前的编程练习中进行了解释)。

    3、对于隐藏层l=2,设置

    4、使用下面这个式子从这个例子中积累梯度。注意,你应该跳过或者移除`$δ^{(2)}_0$`。在Octave/MATLAB中,移除`$δ^{(2)}_0$`对应的操作是delta_2 = delta_2(2:end)。
    5、用1除以累积梯度,得到神经网络代价函数的(非正则化)梯度:
> **Octave/MATLAB技巧**:只有在成功完成前馈后才能实现反向传播算法成本函数。 在实现反向传播算法时,如果遇到尺寸不匹配错误(Octave/MATLAB中的“不一致参数”错误),使用size函数打印出正在使用的变量的大小通常很有用。

    在实现反向传播算法之后,脚本ex4.m将对你的实现运行梯度检查。梯度检查会使你增强代码正确计算梯度的信心。

2.4 梯度检查

    在你的神经网络里,你在最小化代价函数J(θ)。要对参数执行梯度检查,可以设想将参数$Θ^{(1)}$$Θ^{(2)}$“展开”为长矢量θ。通过这样做,你可以将成本函数视为J(θ),并使用以下梯度检查程序。

此处数学符号较多,为避免歧义直接贴原图
    这两个值彼此近似的程度将取决于J的细节。但是,假设E= 0.0004,你通常会发现,上面的左边和右边将至少符合4个有效数字(通常更多)。

    我们在computeNumericalGradient.m中实现了计算数值梯度的函数。虽然不要求你修改文件,但我们强烈建议你查看代码以了解它是如何工作的。

    在ex4.m的下一步中,它将运行提供的函数checkNNGradients.m,它将创建一个小的神经网络和数据集,用于检查梯度。如果你的反向传播实现正确,你应该看到相对差异小于1e-9。

实际提示:当执行梯度检查时,使用具有相对较少数量的输入单元和隐藏单元的小型神经网络更加有效,因此具有相对少量的参数。 θ的每个维度需要对成本函数进行两次评估,这可能代价比较高。 在函数checkNNGradients中,我们的代码创建了一个小型随机模型和数据集,与computeNumericalGradient一起用于梯度检查。 此外,在确信梯度计算正确后,应在运行学习算法之前关闭梯度检查。

实用提示:梯度检查适用于任何计算成本和梯度的函数。具体来说,可以使用相同的computeNumericalGradient。函数来检查其他练习的梯度实现是否也正确(例如,逻辑回归的成本函数)。

            ==一旦成本函数通过(非正则化)神经网络成本函数的梯度检查,就应该提交神经网络梯度函数(反向传播)。==

2.5 正则化神经网络

    在你成功地实现了反向传播算法之后,你将向梯度添加正则化。为了解释正则化,在使用反向传播计算梯度之后,可以将其作为附加项添加进来。

    具体地说,在你计算完$\Delta^{l}_{ij}$之后使用反向传播,你应该使用

    注意,你不能正则化用于偏差项的`$θ^{(l)}$`的第一项。还有就是,参数θ的i是从1开始索引的,j是从0开始索引的。因此,
    令人困惑的是,在Octave/MATLAB中索引是从1开始的(所有的i和j都是),因此Theta1(2,1)实际上对应的是`$θ^{(l)}_{2,0}$`。

    现在,修改在nnCostFunction中计算梯度的代码,使之符合正则化。完成后,执行ex4.m脚本将继续在你的实现上运行梯度检查。如果你的代码是正确的,你应该期望看到的相对差异小于1e-9。

            ==你现在应该提交答案==

2.6 使用fmincg学习参数

    成功实现神经网络成本函数和梯度计算后,ex4.m脚本的下一步将使用fmincg来学习一个好的集合参数。

    训练完成后,ex4.m脚本将通过计算正确的示例百分比来继续报告分类器的训练准确性。 如果你的实现是正确的,你应该看到报告训练精度约为95.3%(由于随机初始化,这可能会变化约1%)。 通过训练神经网络进行更多迭代,可以获得更高的训练精度。 我们鼓励你尝试训练神经网络以进行更多迭代(例如,将MaxIter设置为400)和改变正则化参数λ。 通过正确的学习设置,可以使神经网络完全适合训练集。

3、可视化隐藏层

    理解神经网络学习内容的一种方法是可视化隐藏单元捕获的表示。 非正式地,给定一个特定的隐藏单元,可视化其计算内容的一种方法是找到一个输入x,使其激活(即激活值$(a^{(l)}_i)$接近1)。 对于你训练的神经网络,请注意第i行$Θ^{(1)}$是401维向量,该向量表示第i个隐藏单元的参数。 如果我们丢弃偏差项,我们得到一个400维向量,表示从每个输入像素到隐藏单位的权重。

    因此,将隐藏单元捕获的“表示”可视化的一种方法是将这个400维的向量重新塑造成20×20的图像并显示出来。ex4.m的下一步是使用displayData函数,它将向你显示一个包含25个单元的图像(类似于图4),每个单元对应于网络中的一个隐藏单元。

    在经过训练的网络中,你应该发现隐藏的单元大致对应于在输入中查找笔画和其他模式的检测器。

Figure 4: Visualization of Hidden Units.
### 3.1 可选(不评分)练习

    这部分练习中,你将尝试不同的神经网络学习设置,以了解神经网络的性能如何随正则化参数λ和训练步骤数(使用fmincg时的MaxIter选项)而变化。

    神经网络是非常强大的模型,可以形成高度复杂的决策边界。 如果没有正则化,神经网络可以“过度拟合”训练集,使其在训练集上获得接近100%的准确度,但对于之前未见过的新例子则不然。 你可以将正则化λ设置为较小的值,将MaxIter参数设置为较高的迭代次数,以便为你自己查看。

    当你更改学习参数λ和MaxIter时,你还可以自己查看隐藏单元可视化的变化。

            ==这部分不需要提交作业==

提交作业和评分

    完成各个部分之后,请使用提交功能将你的代码提交到我们的服务器。以下是本次作业评分的细则。

    你可以多次提交作业,但我们只考虑最高分。

程序设计练习5:正则化线性回归和偏差vs方差

介绍

    在这个练习中,你将实现正则化线性回归并用它来研究具有不同偏差方差属性的模型。在开始这个练习之前,我们强烈建议你观看视频讲座并完成相关主题的复习问题。

    要开始练习,你需要下载起始代码并将其内容解压缩到你希望完成练习的目录中。如果需要,请在开始本练习之前使用Octave/MATLAB中的cd命令更改到此目录。

    你也可以在课程网站的“环境设置说明”中找到安装Octave/MATLAB的说明。

本练习中包含的文件

ex5.m - 指导你完成本次练习的Octave/MATLAB脚本
ex5data1.mat - 数据集
submit.m - 提交作业的脚本
featureNormalize.m - 特征归一化函数
fmincg.m - 函数最小化例程(类似于fminunc)
plotFit.m - 绘制多项式拟合
trainLinearReg.m - 用你的代价函数训练线性回归
[*] linearRegCostFunction.m - 正则化线性回归代价函数
[*] learningCurve.m - 生成学习曲线
[*] polyFeatures.m - 将数据映射到多项式特征空间
[*] validationCurve.m - 生成交叉验证曲线

* 表示你要完成的文件

    在整个练习中,你将使用ex5.m脚本。这些脚本为后面的问题设置数据集,并调用你将要编写的函数。你无需修改它们两个,你只需按照本作业中的说明修改其他文件中的功能。

在哪里寻求帮助

    本课程的练习使用非常适合数值计算的高级编程语言Octave或MATLAB。如果你没有安装Octave或MATLAB,请参阅课程网站上“环境设置说明”中的安装说明。

    在Octave/MATLAB命令行中输入help紧跟函数名称会显示内建的函数说明。比如输入help plot会显示绘图函数的帮助信息。更多Octave和MATLAB的函数说明请在Octave官网MATLAB官网查阅。

    我们也非常鼓励使用在线讨论与其他学生讨论练习。但是,不要查看任何源代码或与他人共享源代码。


1、正则化线性回归

    在练习的前半部分,你将使用水库水位的变化实现正则化线性回归来预测大坝的出水量。在下半部分中,你将对调试学习算法进行一些诊断,并检查偏差vs方差的影响。

    提供的ex5.m脚本将指导你完成本次练习。

1.1 可视化数据集

    我们将从可视化包含水位变化(x)和流出大坝的水量(y)的历史记录的数据集开始。

    这个数据集分为三个部分:

  • 你的模型将要学习的训练集:X, y
  • 确定正则化参数的交叉验证集:Xval, yval
  • 用于评估性能的测试集。这些是你的模型在训练期间不知道的“不可见”样本:Xtest、ytest

    然后ex5.m将绘制训练集(图1)。在接下来的部分中,你将实现线性回归,并使用它将直线拟合到数据中,并绘制学习曲线。在此之后,你将实现多项式回归,以便更好地匹配数据。

Figure 1: Data
### 1.2 正则化线性回归代价函数

    回顾一下,正则化线性回归代价函数如下:

    其中λ是一个正则化参数,它控制正则化程度(因此,有助于防止过度拟合)。正则化项对总成本J进行了惩罚。随着模型参数`$θ_j$`的大小增加,惩罚也增加。 请注意,你不应该将`$θ_0$`项正则化。 (在Octave/MATLAB中,`$θ_0$`项表示为theta(1),因为Octave/MATLAB中的索引从1开始)。

    你现在应该完成linearRegCostFunction.m中的代码。你的任务是写一个函数来计算正则化线性回归代价函数。如果可能的话,试着向量化你的代码并且避免写循环。当你完成之后,ex5.m会使用初始化的theta([1; 1])调用你的函数。你应该期望输出值为303.993。

            ==你现在应该提交答案==

1.3 正则化线性回归梯度

    相应地,$θ_j$的正则化线性回归成本的偏导数定义为

    在linearRegCostFunction.m中添加代码来计算梯度,并在grad变量中返回它。当你完成后,ex5.m将会使用初始化的theta([1; 1])调用你的梯度函数。你应该期望看到[-15.30; 598.250]的梯度。

            ==你现在应该提交答案==

1.4 拟合线性回归

    一旦你的代价函数和梯度函数正确运行之后,ex5.m接下来会运行trainLinearReg.m中的代码来计算θ的最优值。该训练函数使用fmincg优化代价函数。

    在这部分中,我们设置正则化参数λ为0。因为我们目前线性回归的实现是尝试拟合一个2维的θ,正则化对于如此低维度的θ将不会非常有用。在练习的后面部分,你将使用带正则化的多项式回归。

    最后,ex5.m脚本应该绘制最拟合的直线,结果如图2所示。最佳拟合线告诉我们该模型不是很适合数据集,因为数据具有非线性模式。 虽然如图所示可视化最佳拟合是调试学习算法的一种可能方法,但是可视化数据和模型并不总是那么容易。 在下一节中,你将实现一个函数来生成学习曲线,这些曲线可以帮助你调试学习算法,即使数据不容易可视化。

Figure 2: Linear Fit
## 2、偏差-方差

    机器学习中的一个重要概念是偏差 - 方差权衡。 具有高偏差的模型对于数据来说不够复杂并且倾向于拟合,而具有高方差的模型过度拟合训练数据。

    在本练习的这一部分中,你将在学习曲线上绘制训练和测试误差,以诊断偏差-方差问题。

2.1 学习曲线

    你现在将完成生成用于调试学习算法曲线的代码。回想一下,学习曲线将训练和交叉验证错误绘制为训练集大小的函数。 你的工作是填写learningCurve.m,以便它返回训练集和交叉验证集的误差向量。

    为绘制学习曲线,我们需要不同训练集大小的训练和交叉验证集误差。 要获得不同的训练集大小,你应该使用原始训练集X的不同子集。具体来说,对于训练集大小为i,你应该使用前面的第i个示例(即X(1:i,:)和y(1:i))。

    你可以使用trainLinearReg函数找到θ参数。注意,lambda是作为参数传递给learningCurve函数的。在学习到θ参数之后,你应该在训练集和交叉验证集上计算误差。回顾一下对于一个数据集的训练误差:

    特别要注意的是,训练误差不包括正则化项。 计算训练误差的一种方法是使用现有的成本函数,并仅在使用它来计算训练误差和交叉验证误差时将λ设置为0。 在计算训练集误差时,请确保在训练子集上计算它(即X(1:n,:)和y(1:n))(而不是整个训练集)。 但是,对于交叉验证错误,你应该在整个交叉验证集上计算它。 你应该将计算出的错误存储在向量error_train和error_val中。

    当你完成后,ex5.m将打印学习曲线并生成类似于图3的图。

Figure 3: Linear regression learning curve
            *==你现在应该提交答案==*

    在图3中可以观察到,当训练示例的数量增加时,训练错误和交叉验证错误都很高。这反映了模型中的一个高偏差问题——线性回归模型过于简单,无法很好地拟合我们的数据集。在下一节中,你将实现多项式回归,以便为该数据集更好地拟合模型。

3、多项式回归

    我们线性模型的问题是它对于数据来说太简单了并导致欠拟合(高偏差)。在练习的这一部分,你会通过添加更多特性来解决这个问题。

    对于使用多项式回归,我们的假设形式为:

    注意,通过定义`$x_1$` = `$(waterLevel)$`,`$x_2$` = `$(waterLevel)^2$` ,...,`$x_p$` = `$(waterLevel)^p$` ,我们得到一个线性回归模型,其中特征是原始(waterLevel)的各种幂。

    现在你将在数据集中使用现有特征x的更高次幂添加更多的特征项。这部分你的任务是完成polyFeatures.m的代码,该函数将大小为m×1的原始训练集X映射到其更高的幂。具体地,当大小为m×1的训练集X被传递到函数中时,函数应返回am×p矩阵X_poly,其中第一列包含原始值X,第二列包含X的2次幂,第三列包含X的3次幂,以此列推。注意,这个函数中你不需要考虑X的0次幂。

    现在你有一个函数可以将特征映射到更高的维度,并且ex5.m的第6部分会将它应用到训练集、测试集合交叉验证集上(截至目前还没使用过)。

            ==你现在应该提交答案==

3.1 学习多项式回归

    在你完成polyFeatures.m之后,ex5.m脚本将继续使用线性回归成本函数训练多项式回归。请记住,即使我们在特征向量中有多项式项,我们仍然在解决线性回归优化问题。多项式项只是变成了我们可以用于线性回归的特征。我们使用的是与在本练习的前一部分中编写的相同的成本函数和梯度。

    对于这部分练习,你将使用一个8次多项式。事实证明,如果我们直接对预测数据进行培训,那么由于特征会严重缩放,因此效果不佳(例如:一个x=40的样本会有一个特征项$x_8$= $40^8$= 6.5 * $10^{12}$)。因此,需要使用特征规范化。

    在为多项式回归学习θ参数之前,ex5.m会先调用featureNormalize并且规范化训练集的特征,分别存储mu和参数sigma。我们已经为你实现了这个功能,并且和第一个练习的函数相同。

    在学习参数θ之后,你应该看到为λ= 0的多项式回归生成的两个图(图4,5)。

Figure 4: Polynomial fit, λ=0
Figure 5: Polynomial learning curve, λ = 0
    从图4中,你应该看到多项式拟合能够很好地跟踪数据点—因此,获得了较低的训练误差。然而,多项式拟合是非常复杂的,甚至在极端情况下下降。这说明多项式回归模型对训练数据拟合过度,不能很好地推广。

    为了更好地理解非正则化(λ= 0)模型的问题,你可以看到学习曲线(图5)显示了低训练误差较低但交叉验证误差较高的相同效果。 训练和交叉验证错误之间存在差距,表明存在高方差问题。

    克服过度拟合(高方差)问题的一种方法是在模型中添加正则化。在下一节中,你会尝试不同的λ参数,看看正则化会导致一个更好的模型。

3.2 可选(不评分)练习:调整正则化参数

    在本节中,你将观察正则化参数如何影响正则化多项式回归的偏差-方差。你应该现在修改ex5.m中的lambda参数。并尝试λ=1,100。对于每个值,脚本都应该生成一个多项式拟合数据,并生成一条学习曲线。

    对于λ=1,你应该看到一个多项式拟合,它很好地跟随数据趋势(图6)和一个学习曲线(图7),表明交叉验证和训练误差都收敛到一个相对较低的值。 这表明λ=1正则化多项式回归模型没有高偏差或高方差问题。 实际上,它在偏差和方差之间实现了良好的平衡。

    对于λ=100,你应该看到不符合数据的多项式拟合(图8)。在这种情况下,正则化过多,模型无法拟合训练数据。

            ==可选练习不需要提交作业==

Figure 6: Polynomial fit, λ = 1
Figure 7: Polynomial learning curve, λ = 1
Figure 8: Polynomial fit, λ = 100
### 3.3 使用交叉验证机选择λ

    前面部分的练习,你观察到的值λ可以在培训和交叉验证集上显著影响正规化多项式回归的结果。特别是,一个模型没有正规化(λ=0)适合训练集,但不能泛化。相反,具有过多正则化(λ= 100)的模型不适合训练集和测试集。 λ的良好选择(例如,λ= 1)可以提供对数据的良好拟合。

    在本节中,你将实现一个自动方法来选择λ参数。 具体而言,你将使用交叉验证集来评估每个λ值的好坏程度。 在使用交叉验证集选择最佳λ值之后,我们可以在测试集上评估模型,以估计模型在实际看不见的数据上的表现。

    你的任务是完成validationCurve.m中的代码。 具体来说,你应该使用trainLinearReg函数来使用不同的λ值训练模型,并计算训练误差和交叉验证误差。 你应该在以下范围内尝试λ:{0,0.001,0.003,0.01,0.03,0.1,0.3,1,3,10}。

Figure 9: Selecting λ using a cross validation set
    完成代码后,ex5.m的下一部分将运行你可以绘制交叉验证曲线误差的函数。你可以选择要使用的λ参数。 你应该看到类似于图9的图。在此图中,我们可以看到λ的最佳值约为3。由于数据集的训练和验证分割中的随机性,交叉验证误差有时可能低于训练误差。

            ==你现在可以提交答案==

3.4 可选(不评分)练习:计算测试集误差

    在本练习的前一部分中,你实现了代码来计算正则化参数λ的各种值的交叉验证错误。 但是,为了更好地指示模型在现实世界中的性能,重要的是评估测试集上的“最终”模型,该模型未在训练集的任何部分中使用(也就是说,它既不用于选择λ参数,也不学习模型参数θ)。

    对于本可选(不评分)练习,你应该使用你发现的最佳λ计算测试误差。在我们的交叉验证中,我们获得了λ=3的测试误差3.8599。

            ==可选练习不需要提交作业==

3.5 可选(不评分)练习:用随机选择的样本绘制学习曲线

    在实践中,特别是对于小型训练集,当你绘制学习曲线以调试算法时,通常有助于对多组随机选择的示例进行平均以确定训练误差和交叉验证误差。

    具体而言,要确定i个样本的训练误差和交叉验证误差,你应首先从训练集中随机选择i个样本,并从交叉验证集中随机选择i个样本。 然后,你将使用随机选择的训练集学习参数θ,并在随机选择的训练集和交叉验证集上评估参数θ。 然后应重复上述步骤多次(比如50),并且应使用平均误差来确定i个样本的训练误差和交叉验证误差。

    对于此可选(不评分)练习,你应该实施上述策略来计算学习曲线。 作为参考,图10示出了我们针对λ= 0.01的多项式回归获得的学习曲线。 由于随机选择了样本,你的数字可能略有不同。

Figure 10: Optional (ungraded) exercise: Learning curve with randomly selected examples

提交和评分

    在完成任务的各个模块后,请务必使用提交系统向我们的服务器提交你的作业。以下是这个练习的每个部分如何得分的细则。

    你可以多次提交,但我们只考虑最高分。

程序设计练习6:支持向量机

介绍

    在本练习中,你将实现支持向量机来构建一个垃圾邮件分类器。在开始这个练习之前,我们强烈建议你观看视频讲座并完成相关主题的复习问题。

    要开始练习,你需要下载起始代码并将其内容解压缩到你希望完成练习的目录中。如果需要,请在开始本练习之前使用Octave/MATLAB中的cd命令更改到此目录。

    你也可以在课程网站的“环境设置说明”中找到安装Octave/MATLAB的说明。

本练习中包含的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ex6.m - 前半部分练习的Octave/MATLAB脚本
ex6data1.mat - 样本集1
ex6data2.mat - 样本集2
ex6data3.mat - 样本集3
svmTrain.m - SVM训练函数
svmPredict.m - SVM预测函数
plotData.m - 绘制2维图
visualizeBoundaryLinear.m - 绘制线性边界
visualizeBoundary.m - 绘制非线性边界
linearKernel.m - SVM的线性内核
[*] gaussianKernel.m - 用于SVM的高斯核
[*] dataset3Params.m - 用于数据集3的参数

ex6 spam.m - 第二部分练习的Octave/MATLAB脚本
spamTrain.mat - 垃圾邮件训练集
spamTest.mat - 垃圾邮件测试集
emailSample1.txt - 样本邮件1
emailSample2.txt - 样本邮件2
spamSample1.txt - 垃圾邮件样本1
spamSample2.txt - 垃圾邮件样本2
vocab.txt - 词汇表
getVocabList.m - 加载词汇表
porterStemmer.m - 干扰函数
readFile.m - 将文件读入字符串
submit.m - 提交你的作业至我们的服务器
[*] processEmail.m - 邮件预处理
[*] emailFeatures.m - 从电子邮件中提取特征

* 表示你需要完成的文件

    在整个练习中,你将使用脚本ex6.m。
这些脚本为题目设置数据集并调用你将要编写的函数。你不需要修改这两个脚本其中任何一个,只需要按照本作业中的说明修改其他文件中的函数。

在哪里寻求帮助

    本课程的练习使用非常适合数值计算的高级编程语言Octave或MATLAB。如果你没有安装Octave或MATLAB,请参阅课程网站上“环境设置说明”中的安装说明。

    在Octave/MATLAB命令行中输入help紧跟函数名称会显示内建的函数说明。比如输入help plot会显示绘图函数的帮助信息。更多Octave和MATLAB的函数说明请在Octave官网MATLAB官网查阅。

    我们也非常鼓励使用在线讨论与其他学生讨论练习。但是,不要查看任何源代码或与他人共享源代码。


1、支持向量机

    在前半部分练习中,你将使用支持向量机(SVM)与各种2D样本数据集。使用这些数据集进行实验将帮助你直观地了解支持向量机如何工作,以及如何使用支持向量机的高斯内核。在接下来的练习中,你将使用支持向量机来构建垃圾邮件分类器。

    提供的ex6.m脚本将会指导你完成前半部分练习。

1.1 样本数据集1

    我们将从一个2D示例数据集开始,该数据集可以由线性边界分隔。 脚本ex6.m将绘制训练数据(图1)。 在该数据集中,正例(用+表示)和负例(用o表示)的位置暗示了由间隙表示的自然分离。 但是,请注意在最左边有一个异常值正例+大约(0.1,4.1)。 作为本练习的一部分,你还将了解此异常值如何影响SVM决策边界。

Figure 1: Example Dataset 1
    在练习的这一部分中,你将尝试使用SVMs中不同的C参数值。非正式地说,C参数是一个正值,它控制了对错误分类的训练示例的惩罚。一个大的C参数告诉SVM尝试正确地对所有示例进行分类。 C起着类似于`$\frac{1}{λ}$`的作用,其中λ是我们之前用于逻辑回归的正则化参数。

    ex6.m的下一部分将使用我们已经给出的代码svmTrain.m运行SVM训练(C = 1)。
当C = 1时,你应该发现SVM将决策边界置于两个数据集之间的间隙中,并对最左侧的数据点进行错误分类(图2)。

Figure 2: SVM Decision Boundary with C = 1 (Example Dataset 1)
> **实现注意**:大多数SVM软件包(包括svmTrain.m)会自动为你添加额外的特征`$x_0 = 1$`,并自动负责学习截距项`$θ_0$`。 因此,在将训练数据传递给SVM软件时,无需自己添加此额外特征`$x_0 = 1$`。 特别是,在Octave / MATLAB中,你的代码应该使用训练样例`$x∈R^n$`(而不是`$x∈R^{n+1}$`); 例如,在第一个示例中,数据集`$x∈R^2$`。

    你的任务是在这个数据集中尝试不同的C值。具体来说,你应该将脚本中的C值更改为C = 100并再次运行SVM训练。当C = 100时,你应该会发现SVM现在正确地分类了每个示例,但是它的决策边界似乎不是很好地拟合数据(图3)。

Figure 3: SVM Decision Boundary with C = 100 (Example Dataset 1)

1.2 高斯核支持向量机

    在本练习的这一部分中,你将使用SVM进行非线性分类。
特别是,你将在不可线性分离的数据集上使用具有高斯内核的SVM。

1.2.1 高斯核

    为了利用支持向量机找到非线性决策边界,首先需要实现高斯核函数。你可以把高斯核想象成一个相似函数,它度量两个样本$(x^{(i)},x^{(j)})$之间的“距离”。高斯内核也由带宽参数,参数化σ,决定相似性度量以多快的速度减少(到0)为例进一步分开。

    你现在应该完成gaussianKernel.m中的代码来计算两个样本$(x^{(i)},x^{(j)})$之间的高斯核。高斯核的定义如下:

    一旦你完成gaussianKernel.m函数之后,ex6.m脚本会在两个提供的样本上测试你的核函数,并且你看到的值应该是0.324652。

            ==你现在应该提交答案==

1.2.2 样本数据集2

    ex6.m的下一部分将加载并绘制数据集2(图4)。 从图中可以看出,没有线性决策边界来区分此数据集的正负样本。 但是,通过将SVM与高斯核一起使用,你将能够学习非线性决策边界,该边界在数据及上会表现地很好。

Figure 4: Example Dataset 2
    如果你正确地实现了高斯核函数,ex6.m将在此数据集中使用高斯核训练SVM。

    图5为高斯核支持向量机的决策边界。该决策边界能够正确地分离出大部分正、负样本,并能很好地跟踪数据集的轮廓。

Figure 5: SVM (Gaussian Kernel) Decision Boundary (Example Dataset 2)
#### 1.2.3 样本数据集3

    在本练习的这一部分中,你将获得有关如何使用具有高斯内核的SVM的更多实用技巧。ex6.m的下一部分将加载并显示第三个数据集(图6)。 你将使用带有此数据集的高斯内核的SVM。

Figure 6: Example Dataset 3
    在提供的数据集ex6data3.mat中,你将获得变量X,y,Xval,yval。ex6.m中提供的代码使用从dataset3Params.m加载的参数,使用训练集(X,y)训练SVM分类器。

    你的任务是使用交叉验证集Xval,yval来确定要使用的最佳C和σ参数。你应该编写必要的其他代码来帮助你探索参数C和σ。 对于C和σ,我们建议在乘法步骤中尝试值(例如,0.01,0.03,0.1,0.3,1,3,10,30)。 请注意,你应该尝试C和σ的所有可能的值对(例如,C = 0.3和σ= 0.1)。 例如,如果你尝试上面列出的C和$σ^2$的8个值中的每一个,你最终将训练和评估(在交叉验证集上)总共$8^2$ = 64个不同的模型。

    确定要使用的最佳C和σ参数后,应修改dataset3Params.m中的代码,填写找到的最佳参数。 对于我们的最佳参数,SVM返回了如图7所示的决策边界。

Figure 7: SVM (Gaussian Kernel) Decision Boundary (Example Dataset 3)
> **实现注意**:在实现交叉验证以选择要使用的最佳C和σ参数时,需要评估交叉验证集上的误差。回想一下,对于分类问题,误差被定义为错误分类的交叉验证样本比例。在Octave / MATLAB中,你可以使用mean(double(predictions ~= yval))计算此误差,其中predictions是包含来自SVM的所有预测的向量,yval是来自交叉验证集的真实标签。你可以使用svmPredict函数生成交叉验证集的预测。

            ==你现在应该提交答案==

2、垃圾邮件分类

    如今,许多电子邮件服务提供垃圾邮件过滤器,能够将电子邮件精确地分类为垃圾邮件和非垃圾邮件。在本部分练习中,你将使用SVMs构建自己的垃圾邮件过滤器。

    你将训练一个分类器以分类给定的电子邮件x是垃圾邮件(y = 1)还是非垃圾邮件(y = 0)。特别是,你需要将每封电子邮件转换为特征向量$x∈R^n$。练习的以下部分将引导你了解如何通过电子邮件构建此类特征向量。

    在本练习的其余部分中,你将使用脚本ex6_spam.m。 本练习中包含的数据集基于SpamAssassin Public Corpus的一个子集。出于本练习的目的,你将只使用电子邮件的正文(不包括电子邮件标题)。

2.1 预处理邮件

    在开始机器学习任务之前,从数据集中查看示例通常是很有洞察力的。图8显示了一个示例电子邮件,其中包含URL、电子邮件地址(末尾)、数字和金额。虽然许多电子邮件包含类似类型的实体(例如,数字、其他URL或其他电子邮件地址),但几乎每封电子邮件中的特定实体(例如,特定URL或特定金额)都是不同的。因此,处理电子邮件时经常使用的一种方法是“规范化”这些值,以便所有url都被处理相同,所有数字都被处理相同,等等。例如,我们可以用惟一的字符串“httpaddr”替换电子邮件中的每个URL,以表示存在一个URL。这样做的效果是让垃圾邮件分类器根据是否存在某个URL而不是某个特定的URL来进行分类决策。这通常会提高垃圾邮件分类器的性能,因为垃圾邮件发送者经常随机分配URL,因此在新垃圾邮件中再次看到特定URL的几率非常小。

Figure 8: Sample Email
    在processEmail.m中,我们实现了以下电子邮件预处理和规范化步骤:
  • 字母小写:整个电子邮件被转换成小写字母,这样就忽略了大写(例如,IndIcaTE被当作Indicate对待)。
  • 删除HTML:所有HTML标记都将从电子邮件中删除。许多电子邮件通常都带有HTML格式; 我们删除所有HTML标记,以便只保留内容。
  • 规范URL:所有的URL会被替换成文本“httpaddr”。
  • 规范邮件地址:所有的邮件地址都被替换成文本“emailaddr”。
  • 规范数字:所有的数字都被替换成文本“number”。
  • 规范美元:所有的美元符号($)都被替换成文本“dollar”。
  • 词干提取:单词被简化成词根形式。例如,“discount”,“discounts”,“discounted”和“discounting”都被“discount”所代替。有时候实际上是从末尾删除了额外的字符,所以“include”、“includes”、“included”和“including”都被替换为“includ”。
  • 删除非单词:非文字和标点符号已被删除。所有空白(制表符、换行符、空格)都已被修剪为一个空格字符。

    这些预处理步骤的结果如图9所示。虽然预处理留下了单词片段和非单词,但事实证明,使用这种形式执行特征提取要容易得多。

Figure 9: Preprocessed Sample Email
#### 2.1.1 词汇列表

    在对电子邮件进行预处理之后,我们将为每个电子邮件创建一个单词列表(例如,图9)。下一步是选择我们想要在分类器中使用的单词和我们想要省略的单词。

    对于本练习,我们仅选择最常出现的单词作为我们考虑的单词集(词汇表)。由于训练集中很少出现的单词仅在几封电子邮件中,因此可能会导致模型过度训练我们的训练集。完整的词汇表列在文件vocab.txt中,如图10所示。我们的词汇表是通过选择在垃圾邮件语料库中至少出现100次的所有单词来选择的,从而得到1899个单词的列表。 在实践中,经常使用具有大约10,000到50,000个单词的词汇表。

Figure 10: Vocabulary List
    给定词汇表,我们现在可以将预处理的电子邮件(例如,图9)中的每个单词映射到包含词汇表中单词的索引的单词索引列表。 图11显示了示例电子邮件的映射。具体而言,在示例电子邮件中,单词“everyone”首先被标准化为“anyon”,然后映射到词汇表列表中的索引86。
Figure 11: Word Indices for Sample Email
    你现在的任务是完成processEmail.m中的代码以执行此映射。在代码中,你将获得一个字符串str,它是已处理电子邮件中的单个单词。你应该在词汇表vocabList中查找单词,并查找词汇表中是否存在该单词。如果单词存在,则应将单词的索引添加到单词indices变量中。 如果单词不存在,因此不在词汇表中,则可以跳过单词。

    一旦你实现了processEmail.m,脚本ex6 spam.m将在电子邮件示例上运行你的代码,你应该看到类似于图9和11的输出。

Octave/MATLAB提示:在Octave/MATLAB中,可以用strcmp函数比较两个字符串。例如,strcmp(str1,str2)仅当两个字符串相等时才返回1。在提供的启动代码中,vocabList是一个“单元格数组”,其中包含词汇表中的单词。在Octave/MATLAB中,单元格数组与普通数组(即,但它的元素也可以是字符串(在Octave/MATLAB的矩阵/向量中不能是字符串),并且可以使用大括号而不是方括号对它们进行索引。具体来说,要获得索引i处的单词,可以使用vocabList{i}。你还可以使用length(vocabList)来获得词汇表中的单词数量。

            ==你现在应该提交答案==

2.2 从邮件中提取特征

    现在你将实现特征提取,即将每个邮件转为向量$R^n$。在这个练习中,你的n等于词汇表中的单词总数。具体地,电子邮件的特征$x_i∈\{0,1\}$对应于字典中的第i个单词是否出现在电子邮件中。 也就是说,如果第i个单词在电子邮件中,则$x_i = 1$,如果第i个单词不在电子邮件中,则$x_i = 0$

    因此,对于一个典型的电子邮件,这个功能看起来像:

    你现在应该在emailFeatures.m中完成代码,以便在给定单词索引的情况下为电子邮件生成特征向量。

    一旦你实现了emailFeatures.m,ex6_spam.m的下一部分将在电子邮件样本上运行你的代码。你应该看到特征向量的长度为1899,非零条目为45。

            ==你现在应该提交答案==

2.3 为垃圾邮件分类训练SVM

    完成特征提取功能后,ex6_spam.m的下一步将加载预处理的训练数据集,该数据集将用于训练SVM分类器。 spamTrain.mat包含4000个垃圾邮件和非垃圾邮件的训练样本,而spamTest.mat则包含1000个测试样本。使用processEmail和emailFeatures函数处理每个原始电子邮件并将其转换为向量$x^{(i)}∈R^{1899}$

    加载数据集后,ex6_spam.m将继续训练SVM,以便在垃圾邮件(y = 1)和非垃圾邮件(y = 0)电子邮件之间进行分类。 训练完成后,你应该看到分类器的训练准确率约为99.8%,测试精度约为98.5%。

2.4 垃圾邮件的最佳预测指标

    为了更好地理解垃圾邮件分类器的工作原理,我们可以检查参数,以查看分类器认为哪些字最能预测垃圾邮件。 ex6_spam.m的下一步是在分类器中找到具有最大正值的参数,并显示相应的单词(图12)。 因此,如果电子邮件包含诸如“guarantee”,“remove”,“dollar”,和“price”(图12中所示的最佳预测指标)之类的单词,则可能将其归类为垃圾邮件。

Figure 12: Top predictors for spam email
### 2.5 可选练习(不评分):试试你自己的邮件

    现在你已经训练了垃圾邮件分类器,你可以开始在自己的电子邮件中进行尝试。在启动代码中,我们提供了两个电子邮件样本(emailSample1.txt和emailSample2.txt)和两个垃圾邮件样本(spamSample1.txt和spamSample2.txt)。ex6_spam.m的最后一部分在第一个垃圾邮件示例上运行垃圾邮件分类器,并使用学习的SVM对其进行分类。 你现在应该尝试我们提供的其他示例,看看分类器是否正确。 你也可以使用自己的电子邮件替换示例(纯文本文件)来尝试自己的电子邮件。

            ==这部分不需要提交作业==

2.6 可选练习(不评分):建立自己的数据集

    在本练习中,我们提供了一个预处理的训练集和测试集。 这些数据集是使用你现在已完成的相同函数(processEmail.m和emailFeatures.m)创建的。对于此可选(未评级)练习,你将使用SpamAssassin Public Corpus的原始电子邮件构建你自己的数据集。

    在这个可选的(不评分)练习中,你的任务是从公共语料库下载原始文件并提取它们。在提取它们之后,你应该在每个电子邮件上运行processEmail和emailFeatures函数,以便从每个电子邮件中提取一个特征向量。这将允许你构建一个样本的数据集X,y。然后,你应该将数据集随机分为训练集、交叉验证集和测试集。

    在构建自己的数据集时,我们还鼓励你尝试构建自己的词汇表(通过选择数据集中出现的高频词)并添加你认为可能有用的任何其他功能。

    最后,我们还建议尝试使用高度优化的SVM工具箱,例如LIBSVM

            ==这部分不需要提交作业==

提交作业和评分

    完成各个部分之后,请使用提交功能将你的代码提交到我们的服务器。以下是本次作业评分的细则。

    你可以多次提交作业,但我们只考虑最高分。

程序设计练习7:k均值聚类与主成分分析

介绍

    在本练习中,你将实现K均值聚类算法并将其应用于压缩图像。在第二部分中,你将使用主成分分析来寻找人脸图像的低维表示。在开始编程练习之前,我们强烈建议观看视频课程并完成相关主题的复习问题。

    要开始练习,你需要下载起始代码并将其内容解压缩到你希望完成练习的目录中。如果需要,请在开始本练习之前使用Octave/MATLAB中的cd命令更改到此目录。

    你也可以在课程网站的“环境设置说明”中找到安装Octave/MATLAB的说明。

本练习中包含的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ex7.m - K-means上第一个练习的Octave/MATLAB脚本 
ex7_pca.m - PCA上第二个练习的Octave/MATLAB脚本
ex7data1.mat - PCA样本集
ex7data2.mat - K-means样本集
ex7faces.mat - 人脸数据集
bird_small.png - 样本图片
displayData.m - 展示存储在矩阵中的2D数据
drawLine.m - 在现有的图形上画一条线
plotDataPoints.m - 初始化k-means中心
plotProgresskMeans.m - 绘制k-means的每一步
runkMeans.m - 运行K-means算法
submit.m - 提交脚本
[*] pca.m - 进行主成分分析
[*] projectData.m - 将数据集投射到较低维度空间
[*] recoverData.m - 从投影中恢复原始数据
[*] findClosestCentroids.m - 寻找最近的聚类中心(在K-means中使用)
[*] computeCentroids.m - 计算聚类中心平均值(K-means中使用)
[*] kMeansInitCentroids.m - 初始化k-means聚类中心

* 表示必须完成的文件

    在第一部分练习中,你将使用脚本ex7.m,第二部分你将使用ex7_pca.m脚本。这些脚本为题目设置数据集并调用你将要编写的函数。你不需要修改这两个脚本其中任何一个,只需要按照本作业中的说明修改其他文件中的函数。

在哪里寻求帮助

    本课程的练习使用非常适合数值计算的高级编程语言Octave或MATLAB。如果你没有安装Octave或MATLAB,请参阅课程网站上“环境设置说明”中的安装说明。

    在Octave/MATLAB命令行中输入help紧跟函数名称会显示内建的函数说明。比如输入help plot会显示绘图函数的帮助信息。更多Octave和MATLAB的函数说明请在Octave官网MATLAB官网查阅。

    我们也非常鼓励使用在线讨论与其他学生讨论练习。但是,不要查看任何源代码或与他人共享源代码。


1、K均值聚类

    在这个练习中,你将实现K均值算法并将它用于图像压缩。你将首先从一个2D样本数据集开始,它将帮助你直观了解K-means算法的工作原理。之后,你将使用K-means算法进行图像压缩,方法是将图像中出现的颜色数量减少到该图像中最常见的颜色数量。 你将使用ex7.m进行这部分练习。

1.1 实现K-means

    K-means算法是一种自动将相似的数据样本聚在一起的方法。具体地说,你有一个训练集$\{x^{(1)},...,x^{(m)}\}$,(其中$x^{(i)} ∈R^n$),并想将数据分为几个内聚的“簇”。它背后的知识是猜测一个初始化中心点开始,然后反复将样本分配到最接近的聚类中心,然后根据已经分配的结果重新计算聚类中心来改进这个猜测。

    K-means算法如下:

1
2
3
4
5
6
7
8
9
10
11
% Initialize centroids
centroids = kMeansInitCentroids(X, K);
for iter = 1:iterations
% Cluster assignment step: Assign each data point to the
% closest centroid. idx(i) corresponds to cˆ(i), the index
% of the centroid assigned to example i
idx = findClosestCentroids(X, centroids);
% Move centroid step: Compute means based on centroid
% assignments
centroids = computeMeans(X, idx, K);
end

    该算法的内环重复执行两个步骤:(i)将每个训练实例$x^{(i)}$分配到其最近的聚类中心,(ii)使用分配给它的点重新计算每个聚类中心的均值。K-means算法总是收敛到中心点的最终均值集。注意,收敛解可能并不总是理想的,它依赖于聚类中心的初始设置。因此,在实际操作中,K-means算法通常会运行几次,并具有不同的随机初始化。从不同的随机初始化中选择这些不同解的一种方法是选择成本函数值(失真)最低的解。

    在下一节中,你将分别实现K-means算法的两个阶段。

1.1.1 寻找最近的聚类中心

    在K-means算法的“集群分配”阶段,给定当前聚类中心的位置,该算法将每个训练样本$x^{(i)}$分配到其最近的中心。具体来说,对于我们设置的每个样本i设置$c^{(i)}:= j$,然后最小化$||x^{(i)} − µ_j||^2$,其中$c^{(i)}$是最靠近$x^{(i)}$的中心下标,$µ_j$是第j个聚类中心的坐标值。注意,$c^{(i)}$对应于启动代码中的idx(i)。

    你的任务是完成findClosestCentroids.m中的代码。该函数接受数据矩阵X和聚类中心内所有中心的位置,并应输出一个一维数组idx,其中包含索引(值在{1,…, K},其中K为距离每个训练样本最近中心的总数)。

    你可以通过每个训练样本和每个中心的循环来达到此目的。

    一旦你完成了findClosestCentroids.m中的代码,脚本ex7.m将调用你的代码,应该看到与前3个样本的中心分配对应的输出[1 3 2]。

            ==你现在应该提交答案==

1.1.2 计算聚类中心均值

    给定每个中心点的赋值,算法的第二阶段对每个中心点重新计算赋值点的均值。具体地说,对于每个中心点k我们设置

1
µ_k := \frac{1}{|C_k|}\sum_{i∈C_k}x^{(i)}

其中$C_k$是分配给中心k的一组样本。具体地说,如果两个样本$x^{(3)}$$x^{(5)}$被分配给中心 k = 2,那么你应该更新 $µ_2 := \frac{1}{2}(x^{(3)} + x^{(5)})$

    现在你应该在computeCentroids.m完成代码。你可以使用中心上的循环来实现这个函数。你还可以对样本使用循环;但是,如果你可以通过不使用这种循环的向量化实现,你的代码可能运行得更快。

    完成computeCentroids.m中的代码后,脚本ex7.m将运行你的代码并在K-means的第一步之后输出中心。

            ==你现在应该提交答案==

1.2 样本集上的K-means

    在完成两个函数(findClosestCentroids和computeCentroids)之后,ex7.m中的下一步将在2D数据集上运行K-means算法,以帮助你了解K-means的工作原理。 你的函数是从runKmeans.m脚本中调用的。我们鼓励你查看函数以了解其工作原理。请注意,代码调用你在循环中实现的两个函数。

    当你运行下一个步骤时,K-means代码将生成一个可视化图像,在每次迭代中引导你了解算法的过程。多次按enter键,查看K-means算法的每个步骤如何更改中心和集群分配。最后,你的图形应该如图1所示。

Figure 1: The expected output.
### 1.3 随机初始化

    设计ex7.m样本数据集的聚类中心的初始分配,以便你将看到与图1中相同的图。实际上,初始化质心的一个好策略是从训练集中选择随机样本。

    在本练习的这一部分中,你应该使用以下代码完成函数kMeansInitCentroids.m:

1
2
3
4
5
% Initialize the centroids to be random examples
% Randomly reorder the indices of examples
randidx = randperm(size(X, 1));
% Take the first K examples as centroids
centroids = X(randidx(1:K), :);

    上面的代码首先随机遍历示例的索引(使用randperm)。然后,根据指标的随机排列选择前K个样本。这允许随机选择示例,而不用冒两次选择相同示例的风险。

            ==这部分练习不需要做任何提交==

1.4 用K-means进行图像压缩

Figure 2: The original 128x128 image.
    在这个练习中,你会将K-means应用到图像压缩上。在直观的24位彩色图像表示中,每个像素表示为三个8位无符号整数(范围从0到255),指定红、绿和蓝强度值。这种编码通常称为RGB编码。我们的图像包含成千上万种颜色,在这部分练习中,你将把颜色的数量减少到16种颜色。

    通过进行这种缩小,可以以有效的方式表示(压缩)照片。具体来说,你只需要存储16种所选颜色的RGB值,并且对于图像中的每个像素,你现在只需要在该位置存储颜色的索引(其中只需要4位来表示16种可能性)。
通过进行这种缩小,可以以有效的方式表示(压缩)照片。 具体来说,你只需要存储16种所选颜色的RGB值,并且对于图像中的每个像素,你现在只需要在该位置存储颜色的索引(其中只需要4位来表示16种可能性)。

    在本练习中,你将使用K-means算法来选择将用于表示压缩图像的16种颜色。具体来说,你将把原始图像中的每个像素作为一个数据样本,并使用K-means算法找到在三维RGB空间中对像素进行最佳分组(集群)的16种颜色。一旦你计算了图像上的集群中心,你将使用16种颜色替换原始图像中的像素。

1.4.1 像素上的K-means

    在Octave/MATLAB中,图片可以用下面的方式读取:

1
2
3
4
5
6
7
% Load 128x128 color image (bird small.png)
A = imread('bird small.png');
% You will need to have installed the image package to used
% imread. If you do not have the image package installed, you
% should instead change the following line to
%
% load('bird small.mat'); % Loads the image into the variable A

    这将创建一个三维矩阵A,其前两个索引标识一个像素位置,其最后一个索引代表红色,绿色或蓝色。 例如,A(50,33,3)给出第50行第33列的像素的蓝色强度。

    ex7.m中的代码首先加载图像,然后将其再改造以创建m×3像素颜色矩阵(其中m=16384=128×128),并在其上调用K-means函数。

    在找到表示图像的K = 16个颜色后,现在可以使用findClosestCentroids函数将每个像素位置分配给其最近的聚类中心。这允许你使用每个像素的聚类中心分配来表示原始图像。注意,你已经显著减少了描述图像所需的bit数。原始图像在128×128像素的位置上每一个位置都需要24位,因此总大小为128×128×24 = 393 216位。新的表示需要一些开销存储,以16种颜色的字典的形式,每种颜色都需要24位,但是图像本身每个像素位置只需要4位。因此,最终使用的比特数是16×24 + 128×128×4 = 65,920位,相当于将原始图像压缩约6倍。

    最后,你可以通过仅基于质心分配重构图像来查看压缩的效果。具体地说,可以用分配给每个像素位置的聚类中心的平均值替换它。图3显示了我们得到的重建结果。尽管生成的图像保留了原始图像的大部分特征,但我们也看到了一些压缩伪影。

Figure 3: Original and reconstructed image (when using K-means to compress the image).

            ==这部分练习不需要做任何提交==

1.5 可选练习(不评分):使用你自己的图片

    在本练习中,修改我们提供的代码,以便在你自己的图片上运行。注意,如果你的图片非常大,那么K-means可能需要很长时间才能运行。因此,我们建议你在运行代码之前将图像的大小调整为可管理的大小。你还可以尝试改变K以查看对压缩的影响。


2、主成分分析

    在本练习中,你将使用主成分分析(PCA)来执行降维。你将首先使用一个示例2D数据集进行实验,以直观了解PCA如何工作,然后在5000个人脸图像数据集的更大数据集上使用它。

    提供的ex7_pca.m脚本会指导你完成前半部分练习。

2.1 样本集

    为了帮助你了解PCA的工作原理,你将首先从一个2D数据集开始,该数据集具有一个大变化方向和一个较小变化方向。 脚本ex7_pca.m将绘制训练数据(图4)。 在本练习的这一部分中,你将可视化使用PCA将数据从2D减少到1D时发生的情况。实际上,你可能希望将数据从256维减少到50维; 但是在这个例子中使用低维数据可以让我们更好地可视化算法。

Figure 4: Example Dataset 1
### 2.2 实现PCA

    在本部分练习中,你将实现PCA。PCA由两个计算步骤组成:首先,计算数据的协方差矩阵。然后,使用Octave/MATLAB的SVD函数计算特征向量$U_1, U_2,…,U_n$。这些将与数据变化的主要组成部分相对应。

    在使用PCA之前,首先通过从数据集中减去每个要素的平均值来标准化数据,然后缩放每个维度以使它们处于相同的范围内,这一点很重要。在提供的脚本ex7_pca.m中,已使用featureNormalize函数为你执行此规范化。

    规范化数据后,你可以运行PCA来计算主成分。 你的任务是完成pca.m中的代码以计算数据集的主成分。 首先,你应该计算数据的协方差矩阵,其由下式给出:

1
Σ = \frac{1}{m}X^TX

其中X是数据矩阵,其中包含行中的样本,m是样本的数量。 注意,Σ是n×n矩阵而不是求和运算符。

    计算协方差矩阵后,可以在其上运行SVD来计算主成分。在Octave/MATLAB中,你可以使用以下命令运行SVD:[U, S, V] = svd(Sigma),其中U将包含主成分,S将包含对角矩阵。

    完成pca.m后,ex7_pca.m脚本将在示例数据集上运行PCA并绘制找到的相应主成分(图5)。 该脚本还将输出找到的顶部主成分(特征向量),你应该会看到输出约为[-0.707 -0.707]。 (Octave/MATLAB可能会输出负数,因为U1和-U1对于第一个主成分是同等有效的选择。)

Figure 5: Computed eigenvectors of the dataset

            ==你现在应该提交答案==

2.3 使用PCA降维

    在计算主成分之后,你可以通过将每个样本投影到较低维度空间$x^{(i)}→z^{(i)}$(例如,将数据从2D投影到1D)来使用它们来减少数据集的要素维度。 在本练习的这一部分中,你将使用PCA返回的特征向量,并将示例数据集投影到一维空间中。

    实际上,如果你使用的是学习算法,如线性回归或神经网络,你现在可以使用投影数据而不是原始数据。 通过使用投影数据,你可以更快地训练模型,因为输入中的维度较少。

2.3.1 将数据投影到主成分上

    你现在应该在projectData.m中完成代码。 具体来说,你将获得一个数据集X,主成分U和要减少到K的所需维数。你应该将X中的每个示例投影到U中的顶部K组件上。请注意,U中的前K个组件是 通过U的前K列,即U_reduce = U(:, 1:K)。

    完成projectData.m中的代码后,ex7_pca.m会将第一个示例投影到第一个维度,你应该看到大约1.481的值(如果得到$-U_1$而不是$U_1$,则可能看到-1.481)。

            ==你现在应该提交答案==

2.3.2 重建数据的近似值

    将数据投影到较低维空间后,你可以通过将数据投影回原始高维空间来近似恢复数据。 你的任务是完成recoverData.m以将Z中的每个样本投影回原始空间并在X_rec中返回恢复的近似值。

    完成recoverData.m中的代码后,ex7_pca.m将恢复第一个样本的近似值,你应该看到值约为[-1.047 -1.047]。

            ==你现在应该提交答案==

2.3.3 可视化投影

    在完成projectData和recoverData之后,ex7_pca.m现在将执行投影和近似重建,以显示投影如何影响数据。 在图6中,原始数据点用蓝色圆圈表示,而投影数据点用红色圆圈表示。投影有效地仅保留$U_1$给出的方向的信息。

Figure 6: The normalized and projected data after PCA.
### 2.4 面部图像数据集

    在本练习的这一部分中,你将在面部图像上运行PCA,以了解它如何在实践中用于降低尺寸。 数据集ex7faces.mat包含面部图像的数据集X,每个32×32为灰度。 X的每一行对应于一个面部图像(长度为1024的行向量)。 ex7_pca.m的下一步将加载并可视化这些面部图像中的前100个(图7)。

Figure 7: Faces dataset
#### 2.4.1 基于面部图像的PCA

    要在面部数据集上运行PCA,我们首先通过从数据矩阵X中减去每个要素的平均值来规范化数据集。脚本ex7_pca.m将为你执行此操作,然后运行你的PCA代码。 运行PCA后,你将获得数据集的主成分。请注意,U(每行)中的每个主成分都是长度为n的向量(对于面数据集,n = 1024)。事实证明,我们可以通过将每个主成分重新塑造成与原始数据集中的像素对应的32×32矩阵来可视化这些主成分。 脚本ex7_pca.m显示描述最大变化的前36个主成分(图8)。 如果需要,还可以更改代码以显示更多主成分,以了解它们如何捕获越来越多的详细信息。

Figure 8: Principal components on the face dataset
#### 2.4.2 降维

    现在你已经计算了面部数据集的主成分,你可以使用它来减少面部数据集的维度。这允许你使用较小输入尺寸(例如,100维)的学习算法而不是原始1024维度。 这有助于加快学习算法的速度。

    ex7_pca.m的下一部分将仅将面部数据集投影到前100个主成分上。具体地说,现在通过向量$z^{(i)}∈R^{100}$描述每个面部图像。

    要了解降维中丢失的内容,可以仅使用投影数据集恢复数据。在ex7_pca.m中,执行数据的近似恢复,并且原始和投影的面部图像并排显示(图9)。从重建中,你可以观察到面部的一般结构和外观被保留,同时细节丢失。这是数据集大小的显着减少(超过10倍),可以帮助显着加快你的学习算法。例如,如果你正在训练神经网络来执行人物识别(给定面部图片,预测人物的身份),则可以使用仅100维度的尺寸缩减输入而不是原始像素。

Figure 9: Original images of faces and ones reconstructed from only the top 100 principal components.

2.5 可选练习(不评分):可视化PCA

    在前面的K-means图像压缩练习中,你在三维RGB空间中使用了K-means算法。在ex7_pca.m脚本的最后一部分中,我们提供了使用scatter3函数可视化此3D空间中最终像素分配的代码。 每个数据点都根据其分配的群集着色。 你可以在图上拖动鼠标以旋转并以3维方式检查此数据。

    事实证明,在3维或更大维度上可视化数据集可能很麻烦。因此,通常希望仅以丢失一些信息为代价以2D显示数据。 在实践中,PCA通常用于减少数据的维度以用于可视化目的。 在ex7_pca.m的下一部分中,脚本将PCA的实现应用于三维数据,将其缩小为2维,并在2D散点图中显示结果。 可以将PCA投影视为旋转,选择最大化数据传播的视图,这通常对应于“最佳”视图

Figure 10: Original data in 3D
Figure 11: 2D visualization produced using PCA
## 提交和评分

    完成作业的各个部分后,请务必使用提交系统将你的作业提交给我们的服务器。以下是对此练习的每个部分进行评分的细则。

    你可以多次提交作业,但我们只考虑最高分。

程序设计练习8:异常检测和推荐系统

介绍

    在本练习中,你将实现异常检测算法,并将其应用于检测网络中的故障服务器。在第二部分中,你将使用协同过滤来构建电影推荐系统。在开始编程练习之前,我们强烈建议观看视频课程并完成相关主题的复习问题。

    要开始练习,你需要下载起始代码并将其内容解压缩到你希望完成练习的目录中。如果需要,请在开始本练习之前使用Octave/MATLAB中的cd命令更改到此目录。

    你也可以在课程网站的“环境设置说明”中找到安装Octave/MATLAB的说明。

本练习中包含的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ex8.m - 用于第一部分练习的Octave / MATLAB脚本
ex8_cofi.m - 用于第二部分练习的Octave / MATLAB脚本
ex8data1.mat - 用于异常检测的第一个样本集
ex8data2.mat - 用于异常检测的第二个样本集
ex8_movies.mat - 电影评论数据集
ex8_movieParams.mat - 提供用于调试的参数
multivariateGaussian.m - 计算高斯分布的概率密度函数
visualizeFit.m - 高斯分布和数据集的2D图
checkCostFunction.m - 梯度检查协同过滤
computeNumericalGradient.m - 以数字方式计算梯度
fmincg.m - 函数最小化例程(类似于fminunc)
loadMovieList.m - 将电影列表加载到单元格数组中
movie_ids.txt - 电影列表
normalizeRatings.m - 协同过滤的均值归一化
submit.m - 提交脚本
[*] estimateGaussian.m - 用对角协方差矩阵估计高斯分布的参数
[*] selectThreshold.m - 找到异常检测的阈值
[*] cofiCostFunc.m - 实现协同过滤的代价函数

* 表示必须完成的文件

    在整个练习的第一部分(异常检测)中,你将使用脚本ex8.m。对于协同过滤的第二部分,你将使用ex8_cofi.m。这些脚本为问题设置数据集,并调用你要编写的函数。 你只需按照此作业中的说明修改其他文件中的函数。

在哪里寻求帮助

    本课程的练习使用非常适合数值计算的高级编程语言Octave或MATLAB。如果你没有安装Octave或MATLAB,请参阅课程网站上“环境设置说明”中的安装说明。

    在Octave/MATLAB命令行中输入help紧跟函数名称会显示内建的函数说明。比如输入help plot会显示绘图函数的帮助信息。更多Octave和MATLAB的函数说明请在Octave官网MATLAB官网查阅。

    我们也非常鼓励使用在线讨论与其他学生讨论练习。但是,不要查看任何源代码或与他人共享源代码。


1、异常检测

    在本练习中,你将实现一个异常检测算法来检测服务器计算机中的异常行为。这些特性度量每个服务器的吞吐量(mb/s)和响应延迟(ms)。在你的服务器运行时,你收集了m = 307个关于它们行为的样本,因此有一个未标记的数据集$\{x^{(1)},…,x^{(m)}\}$。你怀疑这些样本中的绝大多数都是正常运行的服务器的“正常”(非异常)样本,但是也可能有一些服务器在这个数据集中异常运行的样本。

    你将使用高斯模型来检测数据集中的异常样本。 你将首先开始使用2D数据集,以便可视化算法正在执行的操作。 在该数据集上,你将拟合高斯分布,然后找到概率非常低的值,因此可以认为是异常。之后,你将异常检测算法应用于具有多个维度的较大数据集。你将使用ex8.m进行这部分练习。

    ex8.m的第一部分将可视化数据集,如图1所示。

Figure 1: The first dataset.
### 1.1 高斯分布

    要执行异常检测,首先需要根据数据的分布匹配模型。

    给定训练集$\{x^{(1)},...,x^{(m)}\}$(其中$x^{(i)}∈R^n$),你想估计每个特征$x_i$的高斯分布。 对于每个特征 i=1,,,n,你需要找到适合第i维$\{x^{(1)}_i,...,x^{(m)}_i\}$(每个样本的第i维)的数据的参数$μ_i$$σ^2_i$

    给出的高斯分布如下:

1
p(x;μ,σ^2)=\frac{1}{\sqrt{2πσ^2}}e^{-\frac{(x-μ)^2}{2σ^2}}

其中μ是平均值,$σ^2$控制方差。

1.2 估计高斯分布的参数

    你可以使用下面的方程估计第i个特征的参数$(μ_i,σ^2_i)$。估计平均值你可以使用:

1
μ_i = \frac{1}{m}\sum^m_{j=1}x^{(j)}_i,

估计方差可以使用:

1
σ^2_i = \frac{1}{m}\sum^m_{j=1}(x^{(j)_i - μ_i})^2.

    你的任务是完成estimateGaussian.m中的代码。这个函数以数据矩阵X为输入,应该输出一个n维向量mu,其包含所有n个特征的均值;另一个n维向量sigma2包含所有特征的方差。你可以在每个特征和每个训练样本上使用for循环来实现这一点(尽管向量化实现可能更有效;如果你愿意,可以随意使用向量化实现)。 请注意,在Octave / MATLAB中,当计算$σ^2_i$时,var函数将(默认情况下)使用$\frac{1}{m-1}$而不是$\frac{1}{m}$

    完成estimateGaussian.m中的代码后,ex8.m的下一部分将可视化拟合高斯分布的轮廓。你应该得到类似于图2的图。从你的图中,你可以看到大多数样本都位于具有最高概率的区域中,而异常样本位于概率较低的区域中。

Figure 2: The Gaussian distribution contours of the distribution fit to the dataset.

            ==你现在应该提交答案==

1.3 选择阈值ε

    现在你已经估计了高斯参数,你可以研究哪些样本在给定此分布时具有非常高的概率,哪些样本具有非常低的概率。低概率样本更可能是我们数据集中的异常。 确定哪些样本是异常的一种方法是基于交叉验证集选择阈值。 在本练习的这一部分中,你将实现一个算法,使用交叉验证集上的$F_1$分数选择阈值ε。

    你现在应该完成selectThreshold.m中的代码。在此我们将使用一个交叉验证集$\{(x^{(1)}_{cv},y^{(1)}_{cv}),...,(x^{(m_{cv})}_{cv},y^{(m_{cv})}_{cv})\}$,其中标签y = 1对应于异常样本,y = 0对应于正常示样本。对于每个交叉验证样本,我们计算$p(x^{i}_{cv})$。所有这些概率的向量$p(x^{(1)}_{cv}),...,p(x^{(m_{cv})}_{cv})$会在向量pval中传递给selectThreshold.m函数。相应的标签$y^{(1)}_{cv},...,y^{(m_{cv})}_{cv}$会在向量yval中传递给selectThreshold.m函数。

    函数selectThreshold.m应该返回两个值;第一个是选定的阈值ε。如果样本x具有低概率p(x)<ε,则认为它是异常的。该函数还应该返回$F_1$分数,该分数告诉你在给定某个阈值时你在查找真实异常方面的表现如何。对于许多不同的ε值,你将通过计算当前阈值正确和错误分类的样本数来计算得到的$F_1$分数。

    $F_1$分数使用精度(prec)和召回率(rec)计算:

1
F_1 = \frac{2·prec·rec}{prec+rec},

计算prec和rec:

1
2
3
prec = \frac{tp}{tp+fp}

rec = \frac{tp}{tp+fn}

其中:

  • tp是真阳性的数量:真实值标注这是一个异常,我们的算法正确地将其归类为异常。
  • fp是假阳性的数量:真实值标注它不是异常,但我们的算法错误地将其归类为异常。
  • fn是假阴性的数量:真实值标注它是一个异常,但我们的算法错误地将其归类为不是异常的。

    在提供的代码selectThreshold.m中,已经存在一个循环,它将尝试许多不同的ε值并根据$F_1$分数选择最佳ε。

    你现在应该在selectThreshold.m中完成代码。你可以在所有交叉验证样本上使用for循环实现F1分数的计算(以计算值tp,fp,fn)。你应该看到epsilon的值约为8.99e-05。

实现注意:为了计算tp,fp和fn,你可以使用向量化实现而不是遍历所有样本。这可以通过向量和单个数字之间的Octave / MATLAB相等测试来实现。如果在n维二元向量中有几个二进制值$v∈\{0,1\}^n$,你可以通过使用:sum(v==0)找出这个向量中有多少个值是0。 你还可以将逻辑和运算符应用于此类二进制向量。 例如,让cvPredictions是交叉验证集数量大小的二进制向量,如果算法认为$x^{(i)}_{cv}$是异常,则第i个元素为1,否则为0。 然后,你可以使用以下方法计算误报数:fp = sum((cvPredictions == 1) & (yval == 0))。

Figure 3: The classified anomalies.
    完成selectThreshold.m中的代码后,ex8.m中的下一步将运行异常检测代码并圈出绘图中的异常(图3)。

            ==你现在应该提交答案==

1.4 高维数据集

    脚本ex8.m的最后一部分将运行你在更现实和更难的数据集上实现的异常检测算法。 在此数据集中,每个示例由11个特征描述,捕获计算服务器的更多属性。

    该脚本将使用你的代码来估计高斯参数($μ_i$$σ^2_i$),评估你估计高斯参数的训练数据X的概率,并对交叉验证集Xval进行评估。 最后,它将使用selectThreshold来找到最佳阈值ε。 你应该看到一个大约1.38e-18的值epsilon,发现了117个异常。

2、推荐系统

    在本练习的这一部分中,你将实现协同过滤学习算法并将其应用于电影的数据集。此数据集包含1到5等级的。数据集具有$n_u$ = 943个用户,并且$n_m$ = 1682 电影。对于本练习的这一部分,你将使用脚本ex8_cofi.m。

    在本练习的下一部分中,你将实现函数cofiCostFunc.m,该函数计算协作适配目标函数和梯度。 在实现代价函数和梯度之后,你将使用fmincg.m来学习协同过滤的参数。

2.1 电影数据集

    脚本ex8_cofi.m的第一部分将加载数据集ex8_movies.mat,在Octave / MATLAB环境中提供变量Y和R。

    矩阵Y(电影数 x 用户数的矩阵)存储$y^{(i,j)}$(从1到5)。 矩阵R是二进制值指示符矩阵,其中R(i,j) = 1,如果用户j给出电影i,否则R(i,j) = 0。协同过滤的目的是预测电影用户尚未的电影,即R(i,j) = 0的条目。这将允许我们向用户推荐具有最高预测的电影。

    为了帮助你理解矩阵Y,脚本ex8_cofi.m将计算第一部电影(玩具总动员)的平均电影,并将平均输出到屏幕。

    在本练习的这一部分中,你还将使用矩阵X和Theta:

    X的第i行对应于第i个电影的特征向量`$x^{(i)}$`,并且对于第j个用户,Theta的第j行对应于一个参数向量`$θ^{(j)}$`。`$x^{(i)}$`和`$θ^{(j)}$`都是n维向量。 出于本练习的目的,你将使用n = 100,因此,`$x^{(i)}∈R^{100}$`和`$θ^{(j)}∈R^{100}$`。相应地,X是`$n_m$`×100矩阵,并且Theta是`$n_u$`×100矩阵。

2.2 协同过滤学习算法

    现在,你将开始实施协同过滤学习算法。你将从实现代价函数开始(没有正则化)。

    电影推荐设置中的协同过滤算法考虑一组n维参数向量$x^{(1)},...,x^{(n_m)}$$θ^{(1)},...,θ^{(n_u)}$,其中模型预测用户j对电影i的为$y^{(i,j)}=(θ^{(j)})^Tx^{(i)}$。给定一些数据集由一些用户在某些电影上产生的一组组成,你希望学习参数向量$x^{(1)},...,x^{(n_m)}$$θ^{(1)},...,θ^{(n_u)}$产生最佳拟合(最小化平方误差)。

    你将完成cofiCostFunc.m中的代码以计算协作过滤的代价函数和梯度。请注意,函数的参数(即你尝试学习的值)是X和Theta。为了使用现成的最小化器(如fmincg),已设置代价函数以将参数展开为单个向量参数。你之前在神经网络编程练习中使用过相同的向量展开方法。

2.2.1 协同过滤代价函数

    协同过滤代价函数(无正则化)由下式给出:

    你现在应该修改cofiCostFunc.m以在变量J中返回此代价值。请注意,仅当R(i,j)= 1时,你应该累积用户j和电影i的代价值。

    完成该函数后,脚本ex8_cofi.m将运行你的代价函数。你应该会看到22.22的输出。

            ==你现在应该提交答案==

实现注意:我们强烈建议你使用向量化实现来计算J,因为稍后将通过优化包fmincg多次调用。像往常一样,最简单的方法是首先编写一个非向量化的实现(以确保你有正确的答案),并将其修改为向量化实现(检查向量化步骤不会改变算法的输出)。为了提出一个向量化实现,下面的技巧可能会有所帮助:你可以使用R矩阵将所选条目设置为0。例如,R .* M 将在M和R之间进行逐元素乘法; 因为R只有值为0或1的元素,所以只有当R中的对应值为0时才会将M的元素设置为0。因此,sum(sum(R. * M))是M的所有元素,R中的对应元素等于1。

2.2.2 协同过滤梯度

    现在,你应该实现梯度(没有正则化)。 具体来说,你应该完成cofiCostFunc.m中的代码以返回变量X_grad和Theta_grad。注意,X_grad应该是与X相同大小的矩阵,并且类似地,Theta_grad是与Theta相同大小的矩阵。 代价函数的梯度由下式给出:

    请注意,该函数通过将它们展开到单个向量中来返回两组变量的梯度。在完成计算梯度的代码之后,脚本ex8_cofi.m将运行梯度检查(checkCostFunction)以数字方式检查梯度的实现。如果你的实现是正确的,你应该发现分析和数值梯度匹配密切合作。

           ==你现在应该提交答案==

实现注意:你可以在不使用向量化实现的情况下获得此作业的全部成绩,但你的代码运行速度会慢得多(几个小时),因此我们建议你尝试对你的实现进行向量化。

首先,你可以通过电影上的for循环(用于计算$\frac{∂J}{∂x^{(i)}_k}$)和用户上的for循环(用于计算$\frac{∂J}{∂x^{(j)}_k}$)来实现梯度。当你第一次实现梯度时,你可以从非向量化版本开始,通过实现另一个内部for循环来计算求和中的每个元素。 以这种方式完成梯度计算后,你应该尝试向量化你的实现(向量化内部for循环),这样你只剩下两个for循环(一个电影集上的for循环来针对每一部电影计算$\frac{∂J}{∂x^{(i)}_k}$,以及一个用户集上的for循环针对每个用户计算$\frac{∂J}{∂x^{(j)}_k}$)。

实现提示:要实现向量化,你会发现这个很有用:你应该想办法计算所有与$x^{(i)}_1,x^{(i)}_2,...,x^{(i)}_n$相关的导数(如与特征向量$x^{(i)}$对应的导数)。让我们为第i部电影的特征向量定义导数:

要对上面的表达式进行向量化,你可以从索引Theta和Y开始,只选择感兴趣的元素(即r(i,j)= 1的元素)。直观地说,当你考虑第i部电影的特征时,你只需要关注给电影评分的用户,这样你就可以从Theta和Y中删除所有其他用户。

具体来说,你可以将idx = find(R(i,:)== 1)设置为已电影i的所有用户的列表。这将允许你创建临时矩阵Thetatemp = Theta(idx,:)和Ytemp = Y(i,idx),它们会转换为Theta和Y,只为你提供已评定第i部电影的用户集。这将允许你将导数写为:

1
X_{grad}(i, :) = (X(i, :) ∗ Theta^T_{temp} − Y_{temp}) ∗ Theta_{temp}.

(注意:上面的向量化计算会返回一个行向量。)

在对$x^{(i)}$进行向量化导数计算之后,你应该使用类似的方法来相对于$θ^{(j)}$对导数进行向量化。

2.2.3 正则化代价函数

    具有正则化的协同过滤的代价函数由下式给出

    你现在应该将正则化添加到代价函数J的原始计算中。完成后,脚本ex8_cofi.m将运行你的正则化代价函数,你应该会看到大约31.34的代价值。

            ==你现在应该提交答案==

2.2.4 正则化梯度

    现在你已经实现了正则化代价函数,你应该继续为梯度实现正则化。你应该在cofiCostFunc.m中添加实现,以通过添加正则化项的贡献来返回正则化梯度。请注意,正则化代价函数的梯度由下式给出:

    这意味着你只需要将`$λx^{(i)}$`添加到前面描述的X_grad(i,:)变量中,并将`$λθ^{(j)}$`添加到前面描述的Theta_grad(j,:)变量中。

    在你完成计算梯度的代码之后,脚本ex8_cofi.m将运行另一个梯度检查(checkCostFunction),对梯度的实现进行数值检查。

            ==你现在应该提交答案==

2.3 学习电影推荐

    完成协作过滤代价函数和梯度后,你现在可以开始训练算法,为自己制作电影推荐。在ex8_cofi.m脚本的下一部分中,你可以输入自己的电影首选项,以便稍后在算法运行时,你可以获得自己的电影推荐! 我们已根据自己的喜好填写了一些值,但你应该根据自己的喜好进行更改。 可以在文件影片idx.txt中找到所有影片列表及其在数据集中的编号。

2.3.1 推荐

    在将附加添加到数据集之后,脚本将继续训练协作过滤模型。 这将学习参数X和Theta。 要预测用户j的电影i的,你需要计算$(θ^{(j)})^Tx^{(i)}$。 脚本的下一部分计算所有电影和用户的,并根据之前在脚本中输入的显示它推荐的电影(图4)。请注意,由于不同的随机初始化,你可能会获得一组不同的预测。

Figure 4: Movie recommendations

提交和评分

    完成作业的各个部分后,请务必使用提交系统将你的作业提交给我们的服务器。以下是对此练习的每个部分进行评分的细则。

    你可以多次提交作业,但我们只考虑最高分。
-------------本文结束感谢您的阅读-------------