这一部分是对吴恩达机器学习检测评估的总结
当我们使用算法对某种样本进行回归的时候,需要某种评价标椎对其优劣进行评估,比如对多项式回归的合理次数,正则项的大小选择,样本数目或者样本特征值的选择等等,这些知识详见笔记,这里仅讨论学习函数的代码实现
主要分为以下几个部分:
1.正则化的线性回归
2.线性回归的学习曲线分析
3.正则化的多项式回归
4.正则化多项式回归的学习曲线分析
1.正则化的线性回归
首先仍然是数据的加载和可视化
%% =========== Part 1: Loading and Visualizing Data =============
fprintf('Loading and Visualizing Data ...\n')
load ('ex5data1.mat');%加载数据
m = size(X, 1);%样本容量
plot(X, y, 'rx', 'MarkerSize', 10, 'LineWidth', 1.5);
%绘制x,y图像
xlabel('Change in water level (x)');
ylabel('Water flowing out of the dam (y)');
fprintf('Program paused. Press enter to continue.\n');
pause;
然后进行线性回归,且看代码
基于模块化思想,分别定义代价函数、梯度、训练的函数
function [J, grad] = linearRegCostFunction(X, y, theta, lambda)
%计算代价函数和梯度的函数
m = length(y); % 样本容量
J = 0;
grad = zeros(size(theta));
J=(1/(2.0*m))*(sum((X*theta-y).^2))+(lambda/(2.0*m)*(theta(2:end)'*theta(2:end)));%代价函数
grad(1)=(1/m).*sum((X*theta)-y);
grad(2:end)=((1/m)*sum((X*theta-y).*X(:,2:end)))'+(lambda/m)*theta(2:end);
%代价函数
grad = grad(:);
end
以及
function [theta] = trainLinearReg(X, y, lambda)
%训练函数
initial_theta = zeros(size(X, 2), 1);
costFunction = @(t) linearRegCostFunction(X, y, t, lambda);
options = optimset('MaxIter', 200, 'GradObj', 'on');
theta = fmincg(costFunction, initial_theta, options);
end
再在主程序里面把拟合曲线可视化
lambda = 0;%先不考虑正则化问题
[theta] = trainLinearReg([ones(m, 1) X], y, lambda);
plot(X, y, 'rx', 'MarkerSize', 10, 'LineWidth', 1.5);
xlabel('Change in water level (x)');
ylabel('Water flowing out of the dam (y)');
hold on;
plot(X, [ones(m, 1) X]*theta, '--', 'LineWidth', 2)
hold off;
fprintf('Program paused. Press enter to continue.\n');
pause;
这样我们就有了拟合结果并且可以绘制图像:
2.线性回归的学习曲线分析
现在我们用学习曲线来评价这个拟合的质量,定义绘制学习曲线的函数:
function [error_train, error_val] = ...
learningCurve(X, y, Xval, yval, lambda)
m = size(X, 1);
error_train = zeros(m, 1);
error_val = zeros(m, 1);
%这两个参数一个是训练集的代价函数一个是交叉验证集的验证函数,从m=1时开始计算,一直算到m=m
%接下来实现这个循环
for i = 1:m %从1开始算到m
theta = trainLinearReg(X(1:i,:),y(1:i),lambda);
% X(1:i,:)从1到i个样本开始算,每一列就是每个特征都要算
% y(1:i)这是对应的label
% lambda是正则项,但是观察当下拟合情况的时候可以不用考虑正则化,即lambda=0
% 这里直接算出训练集的theta
[error_train(i), ~] = linearRegCostFunction(X(1:i,:),y(1:i),theta,0);
%用拟合出的theta加上对应样本计算代价函数,但是样本数是随theta变的
[error_val(i), ~] = linearRegCostFunction(Xval,yval,theta,0);
%用刚才的theta算代价函数算交叉训练集,但是这里是不随theta变化的,一直是这些样本
end
end
用刚才得到的结论绘图:
lambda = 0;
[error_train, error_val] = ...
learningCurve([ones(m, 1) X], y, ...
[ones(size(Xval, 1), 1) Xval], yval, ...
lambda);
plot(1:m, error_train, 1:m, error_val);
title('Learning curve for linear regression')
legend('Train', 'Cross Validation')
xlabel('Number of training examples')
ylabel('Error')
axis([0 13 0 150])
fprintf('# Training Examples\tTrain Error\tCross Validation Error\n');
for i = 1:m
fprintf(' \t%d\t\t%f\t%f\n', i, error_train(i), error_val(i));
end
fprintf('Program paused. Press enter to continue.\n');
pause;
这是随着样本数增大的代价函数图,可以看出来,它们的收敛值很高,且它们很接近,且随着样本数目增加,代价函数的变化很小这说明它们欠拟合了
但如果它们的收敛值差距很大,且随着样本数增大它们还会慢慢收敛,就说明是过拟合了
那么如果欠拟合,就要考虑多项式回归
3.正则化的多项式回归
我们来看一下8次多项式拟合的效果
首先还是要将有限的特征展开
定义这样一个函数:
function [X_poly] = polyFeatures(X, p)
X_poly = zeros(numel(X), p);
for i = 1:p
X_poly(:,i) = X.^i;
%由于本例中只有一个特征,所以自己和自己乘就行
end
end
由于数据的量级差距很大,所以需要归一化,这里使用的归一化方法比较复杂,其实可以用我们之前的方法,这个方法我只贴代码,不做解释,对了,归一化计算的均值是每一个变量的均值,标准差也是,它们不是总体的
function [X_norm, mu, sigma] = featureNormalize(X)
%FEATURENORMALIZE Normalizes the features in X
% FEATURENORMALIZE(X) returns a normalized version of X where
% the mean value of each feature is 0 and the standard deviation
% is 1. This is often a good preprocessing step to do when
% working with learning algorithms.
mu = mean(X);
X_norm = bsxfun(@minus, X, mu);
sigma = std(X_norm);
X_norm = bsxfun(@rdivide, X_norm, sigma);
% ============================================================
end
以上是归一化训练集的函数
注意,验证集和测试集的均值和标准差使用的是测试集的
% Map X_poly_test and normalize (using mu and sigma)
X_poly_test = polyFeatures(Xtest, p);
X_poly_test = bsxfun(@minus, X_poly_test, mu);
X_poly_test = bsxfun(@rdivide, X_poly_test, sigma);
X_poly_test = [ones(size(X_poly_test, 1), 1), X_poly_test]; % Add Ones
% Map X_poly_val and normalize (using mu and sigma)
X_poly_val = polyFeatures(Xval, p);
X_poly_val = bsxfun(@minus, X_poly_val, mu);
X_poly_val = bsxfun(@rdivide, X_poly_val, sigma);
X_poly_val = [ones(size(X_poly_val, 1), 1), X_poly_val]; % Add Ones
接下来在不考虑正则化的情况下用训练集计算theta
lambda = 0;
[theta] = trainLinearReg(X_poly, y, lambda);
接下来可视化一些东西
figure(1);
plot(X, y, 'rx', 'MarkerSize', 10, 'LineWidth', 1.5);
plotFit(min(X), max(X), mu, sigma, theta, p);
xlabel('Change in water level (x)');
ylabel('Water flowing out of the dam (y)');
title (sprintf('Polynomial Regression Fit (lambda = %f)', lambda));
这里plotFit函数是一个绘制拟合曲线的函数,这里只贴代码:
function plotFit(min_x, max_x, mu, sigma, theta, p)
%PLOTFIT Plots a learned polynomial regression fit over an existing figure.
%Also works with linear regression.
% PLOTFIT(min_x, max_x, mu, sigma, theta, p) plots the learned polynomial
% fit with power p and feature normalization (mu, sigma).
% Hold on to the current figure
hold on;
% We plot a range slightly bigger than the min and max values to get
% an idea of how the fit will vary outside the range of the data points
x = (min_x - 15: 0.05 : max_x + 25)';
% Map the X values
X_poly = polyFeatures(x, p);
X_poly = bsxfun(@minus, X_poly, mu);
X_poly = bsxfun(@rdivide, X_poly, sigma);
% Add ones
X_poly = [ones(size(x, 1), 1) X_poly];
% Plot
plot(x, X_poly * theta, '--', 'LineWidth', 2)
% Hold off to the current figure
hold off
end
接下来用同样的方法看看多项式的拟合效果:
figure(2);
[error_train, error_val] = ...
learningCurve(X_poly, y, X_poly_val, yval, lambda);
plot(1:m, error_train, 1:m, error_val);
title(sprintf('Polynomial Regression Learning Curve (lambda = %f)', lambda));
xlabel('Number of training examples')
ylabel('Error')
axis([0 13 0 100])
legend('Train', 'Cross Validation')
fprintf('Polynomial Regression (lambda = %f)\n\n', lambda);
fprintf('# Training Examples\tTrain Error\tCross Validation Error\n');
for i = 1:m
fprintf(' \t%d\t\t%f\t%f\n', i, error_train(i), error_val(i));
end
拟合效果和误差效果如下:
训练集的误差很小说明曲线和点拟合的相当好,但是这并不一定是好事,看验证集,与训练集有gap,而且它甚至是不收敛的,这就说明过拟合了,那么就要调整正则项lambda来改善过拟合的情况
定义一个检测函数:
function [lambda_vec, error_train, error_val] = ...
validationCurve(X, y, Xval, yval)
%这是一个返回一个lambda集合,训练集代价函数,验证集代价函数的函数
lambda_vec = [0 0.001 0.003 0.01 0.03 0.1 0.3 1 3 10]';
%定义这样的lambda集,记住把它转置
error_train = zeros(length(lambda_vec), 1);
error_val = zeros(length(lambda_vec), 1);
%初始化训练集代价函数,验证集代价函数,每一个lambda对应两个这样的值
for i=1:length(lambda_vec)
%开始挨个计算
lambda=lambda_vec(i);
theta = trainLinearReg(X, y, lambda);
用训练集计算theta
error_train(i) = linearRegCostFunction(X, y, theta, 0);
error_val(i) = linearRegCostFunction(Xval, yval, theta, 0);
用这样的theta计算两个误差
end
所以主程序:
[lambda_vec, error_train, error_val] = ...
validationCurve(X_poly, y, X_poly_val, yval);
并且将其可视化:
plot(lambda_vec, error_train, lambda_vec, error_val);
legend('Train', 'Cross Validation');
xlabel('lambda');
ylabel('Error');
fprintf('lambda\t\tTrain Error\tValidation Error\n');
for i = 1:length(lambda_vec)
fprintf(' %f\t%f\t%f\n', ...
lambda_vec(i), error_train(i), error_val(i));
end
如图所示:
λ要选择交叉验证集误差最小的!
所以从上图来看,λ=3的时候效果最好,把λ改成3,效果如下:
收敛值很低,舒服了