本文基于吴恩达老师第六周的练习,在这次的练习中,你将会实现正则化的线性回归并且用它去实现不同的偏差和方差的性质。
在前半部分的练习中,你将实现正则化线性回归通过水库中水位的变化去预测从大坝中流出的水量。在后半部分,你将会通过调试学习算法诊断和测试偏差和方差的影响。
我们将通过可视化数据集历史水位变化记录x,流出大坝的水量y开始。这个数据集被分为3部分:
回想正则化线性回归有以下的代价函数:
其中λ是正则系数,用来控制正则化的程度(防止过拟合)。正则项对总成本J进行惩罚。当模型中θj增加时,惩罚也会加大。记住θ0是不需要正则化的。
现在你需要完成linearRegCostFunction.m。你的任务是完成计算正则化线性回归的代价函数。如果可能,尝试向量化你的代码避免使用循坏。当你完成这部分的内容,你会看到一个结果为303.993。因为这个和下面正则化线性回归梯度的是写在一个脚本文件里面的,因此代码不在赘述。
这里的偏导公式如下:
在linearRegCostFunction.m添加代码去计算梯度值。完成了之后,你会看见一个期望的梯度为[-15.30; 598.250]。
代码:
function [J, grad] = linearRegCostFunction(X, y, theta, lambda)
%LINEARREGCOSTFUNCTION Compute cost and gradient for regularized linear
%regression with multiple variables
% [J, grad] = LINEARREGCOSTFUNCTION(X, y, theta, lambda) computes the
% cost of using theta as the parameter for linear regression to fit the
% data points in X and y. Returns the cost in J and the gradient in grad
% Initialize some useful values
m = length(y); % number of training examples
% You need to return the following variables correctly
h = X*theta;
J = (h-y)'*(h-y)/(2*m)+lambda*theta(2:end)'*theta(2:end)/(2*m);
grad = zeros(size(theta));
temp=theta;
temp(1,1)=0;%将第一个值为0
grad=X'*(h-y)/m+lambda*temp/m;
% ====================== YOUR CODE HERE ======================
% Instructions: Compute the cost and gradient of regularized linear
% regression for a particular choice of theta.
%
% You should set J to the cost and grad to the gradient.
%
% =========================================================================
grad = grad(:);
end
一旦你的代价函数和梯度能够正确的工作,ex5.m接下来的部分会运行trainLinearReg.m里面的代码去计算最优化的θ值。训练函数会使用fmincg函数优化代价函数。
在这部分,正则系数λ=0.因为我们现在对于线性回归是在2维的θ,正则化不会那么有效在那么低维的情况下。在接下来的练习中,你将会使用正则化多项式回归。
最后,脚本ex5.m也会画出最好的拟合线。结果告诉我们这个模型不能够很好的拟合数据,因为数据是非线性的。当你可视化了最好的拟合是一种可能的方式去判断你的学习算法是否可行,但是这种方法并不总是可行。在下一部分中,你将会实现画出学习曲线来帮助你判断你的学习算法即使可视化数据并不容易。
机器学习中一个重要的概念就是偏差和方差的比较。高偏差的模型对于数据不够复杂而趋于欠拟合,高方差的模型则会导致在训练集上过拟合。
在这部分的练习中,你将要画出训练误差和测试误差的学习曲线来判断偏差-方差问题。
你现在要完成产生学习曲线的代码,这对你调试你的学习算法会很有用。回想一下,学习曲线将训练和交叉验证误差绘制为训练集大小的函数。你需要完成learningCurve.m,以致于它能够返回一个训练集和交叉验证集的误差向量。
为了画出学习曲线,我们需要对于不同大小的训练集来找到对应的训练和交叉验证集误差。为了获得不同的训练集大小,你应该要使用训练集X的不同的子集。特别地,对于i个大小的训练集,你应该使用 X(1:i,:)和y(1:i)。
你可以使用trainLinearReg函数去找到θ的值,注意λ是learningCurve.m的传入参数,在得到θ的取值之后,你应该计算出训练和交叉验证集的误差。回想训练集的误差是如下定于的:
特别的,注意训练集误差是不包含正则项的。使用你现有的代价函数并将λ置为0是一种计算训练误差的方法。当你计算训练误差以及交叉验证误差,请你确认它是计算训练子集 (i.e., X(1:n,:) and y(1:n))而不是整个训练集。但是对于交叉验证误差,你应该计算整个的交叉验证集。并把误差保存在error_train和error_val这两个向量中。
当你完成了,ex5.m会输出如下的学习曲线。
观察上图,你可以看到当训练样本变高时交叉验证误差也是高的。这就反映了这个模型中有高偏差的问题,所以说这个线性回归模型过于简单而不能很好地拟合数据。在接下来的部分,你会完成多项式的回归去找到更好的模型来拟合数据集。
代码:
function [error_train, error_val] = ...
learningCurve(X, y, Xval, yval, lambda)
%LEARNINGCURVE Generates the train and cross validation set errors needed
%to plot a learning curve
% [error_train, error_val] = ...
% LEARNINGCURVE(X, y, Xval, yval, lambda) returns the train and
% cross validation set errors for a learning curve. In particular,
% it returns two vectors of the same length - error_train and
% error_val. Then, error_train(i) contains the training error for
% i examples (and similarly for error_val(i)).
%
% In this function, you will compute the train and test errors for
% dataset sizes from 1 up to m. In practice, when working with larger
% datasets, you might want to do this in larger intervals.
%
% Number of training examples
m = size(X, 1);
% You need to return these values correctly
error_train = zeros(m, 1);
error_val = zeros(m, 1);
% ====================== YOUR CODE HERE ======================
% Instructions: Fill in this function to return training errors in
% error_train and the cross validation errors in error_val.
% i.e., error_train(i) and
% error_val(i) should give you the errors
% obtained after training on i examples.
%
% Note: You should evaluate the training error on the first i training
% examples (i.e., X(1:i, :) and y(1:i)).
%
% For the cross-validation error, you should instead evaluate on
% the _entire_ cross validation set (Xval and yval).
%
% Note: If you are using your cost function (linearRegCostFunction)
% to compute the training and cross validation error, you should
% call the function with the lambda argument set to 0.
% Do note that you will still need to use lambda when running
% the training to obtain the theta parameters.
%
% Hint: You can loop over the examples with the following:
%
% for i = 1:m
% % Compute train/cross validation errors using training examples
% % X(1:i, :) and y(1:i), storing the result in
% % error_train(i) and error_val(i)
% ....
%
% end
%
% ---------------------- Sample Solution ----------------------
for i = 1:m
Xtr = X(1:i, :);
ytr = y(1:i);
theta = trainLinearReg(Xtr, ytr, lambda);
[error_train(i),grad1] = linearRegCostFunction(Xtr, ytr, theta, lambda);
[error_val(i),grad2] = linearRegCostFunction(Xval, yval, theta, lambda);
end
% -------------------------------------------------------------
% =========================================================================
end
以上是我第一次写的代码,图都能画出来但是不对,这部分没有得分,去网上百度了找到了下面的方法,有分,但是我觉得没什么差别,有大佬做到的话可以告诉我为什么:
for i = 1:m
theta = trainLinearReg(X(1:i,:),y(1:i),lambda);
error_train(i) = 1/(2*i) * sum((X(1:i,:) * theta - y(1:i)).^2);
error_val(i) = 1/(2*size(Xval,1)) * sum((Xval * theta - yval).^2);
end
线性回归模型的问题在对这个训练集过于简单,导致了欠拟合(高偏差)。在这个部分的练习中,你会通过增加更多的特征来解决这个问题。
使用多项式回归的假设如下:
通过定义x1=(waterLevel),x2 = (waterLevel)2,…, xp = (waterLevel)p,我们就获得一个有着初始数据(水位)的不同次数的线性回归模型。
现在,你将在数据集中加入更高次数的x。在此节中,你的任务是完成polyFeatures.m里面的函数,舍得这个函数能够映射出训练集m1的原始X的更高次数的数据。特别的,当一个训练集X传入函数,函数应该返回一个mp的矩阵X_poly,第一列是原始X,第二列是X2,第三列是X3,以此类推。注意你不需要增加x0在这个函数中。
现在你有了一个可以映射高维的特征,ex5.m的Part 6会应用这个训练集以及你还未使用过的交叉验证集。
代码:
function [X_poly] = polyFeatures(X, p)
%POLYFEATURES Maps X (1D vector) into the p-th power
% [X_poly] = POLYFEATURES(X, p) takes a data matrix X (size m x 1) and
% maps each example into its polynomial features where
% X_poly(i, :) = [X(i) X(i).^2 X(i).^3 ... X(i).^p];
%
% You need to return the following variables correctly.
X_poly = zeros(numel(X), p);
% ====================== YOUR CODE HERE ======================
% Instructions: Given a vector X, return a matrix X_poly where the p-th
% column of X contains the values of X to the p-th power.
%
%
for i=1:p
X_poly(:,i) = X.^i;
end
% =========================================================================
end
完成了上述函数之后,ex5.m会使用你的线性回归代价函数来训练多项式函数。
记住即使我们有了高次的项,我们解决的实际上还是线性回归的优化问题。多项式早就转化成了线性回归的特征。所以我们使用早就写好的代价函数和梯度是没有问题的。
在这部分中的练习中,你将会使用8次多项式。结果表明如果我们直接使用目标数据,并不会很好的起作用因为特征有着糟糕的规模。因此,我们需要特征的归一化。
接着的就不需要再自己动手写代码了。老师在这部分是让我们修改λ的值来观察高方差和高偏差。高偏差在上述说过,就是测试集和交叉验证集的错误都比较高,欠拟合。那么将λ设为0,结果如下:
我们可以看到训练误差和交叉验证误差有着较大的间隔,并且训练集的误差较低(都快没有了),但是交叉验证集的误差还是比较高,这就是过拟合,高方差的问题了。
接着是λ=1的图像:
可以看出,间隙变小了,并且交叉验证误差也小了。
在之前的练习中,你已经观察到λ的值可以极大地影响到在训练集和交叉验证集上的正则化多项式回归。特别地,一个没有经过正则化的模型可以很好地拟合训练集,但是泛化能力很差。反过来,过大的λ值并不能很好的拟合训练集和测试集。对于λ的适当选择可以较好的拟合数据。
在这节中,你会实现一种自动选择λ的方法。更具体的,你会使用交叉验证集去评估那个λ是比较好的。在使用了交叉验证集选择了最好的λ之后,我们可以通过在测试集上评估模型,评价他在看不见的数据上的表现会有多好。
你的任务是完成validationCurve.m的代码。特别的,你应该需要使用trainLinearReg 函数通过λ的不同取值来训练模型,然后计算训练误差以及交叉验证集误差。你应该在下列的范围内取λ的值 {0,0.001,0.003,0.01,0.03,0.1,0.3,1,3,10}。
function [lambda_vec, error_train, error_val] = ...
validationCurve(X, y, Xval, yval)
%VALIDATIONCURVE Generate the train and validation errors needed to
%plot a validation curve that we can use to select lambda
% [lambda_vec, error_train, error_val] = ...
% VALIDATIONCURVE(X, y, Xval, yval) returns the train
% and validation errors (in error_train, error_val)
% for different values of lambda. You are given the training set (X,
% y) and validation set (Xval, yval).
%
% Selected values of lambda (you should not change this)
lambda_vec = [0 0.001 0.003 0.01 0.03 0.1 0.3 1 3 10]';
% You need to return these variables correctly.
error_train = zeros(length(lambda_vec), 1);
error_val = zeros(length(lambda_vec), 1);
% ====================== YOUR CODE HERE ======================
% Instructions: Fill in this function to return training errors in
% error_train and the validation errors in error_val. The
% vector lambda_vec contains the different lambda parameters
% to use for each calculation of the errors, i.e,
% error_train(i), and error_val(i) should give
% you the errors obtained after training with
% lambda = lambda_vec(i)
%
% Note: You can loop over lambda_vec with the following:
%
% for i = 1:length(lambda_vec)
% lambda = lambda_vec(i);
% % Compute train / val errors when training linear
% % regression with regularization parameter lambda
% % You should store the result in error_train(i)
% % and error_val(i)
% ....
%
% end
%
%
m=size(X,1);
for i=1:length(lambda_vec),
lambda=lambda_vec(i);
theta = trainLinearReg(X, y, lambda);
error_train(i)=sum((X*theta-y).^2)/2/m;
error_val(i)=sum((Xval*theta-yval).^2)/2/m;
end
% =========================================================================
end
从上图可以看出,λ的最佳取值应该是在3左右,因为训练集和交叉验证集的随机划分,交叉验证的误差有时候会比训练集要低。