机器学习之算法分析与优化:偏差和方差

机器学习之算法分析与优化:偏差和方差

  • 简述
  • 包含的文件
  • 正则化线性回归

简述:

  • 偏差指模型的预测值与真实值的偏离程度。偏差越大,预测值偏离真实值越厉害。
  • 方差指模型预测值的离散程度或者变化范围。方差越大,数据分布越分散,函数波动越大,泛化能力越差。
  • 我们将实现正则化线性回归用于学习不同的偏差、方差的模型。
  • 数据下载:https://github.com/peedeep/Coursera/tree/master/ex5
机器学习之算法分析与优化:偏差和方差

包含的文件

  • ex5.m - 整个练习的Octave/MATLAB脚本
  • ex5data1.mat - 数据集
  • featureNormalize.m - 特征归一化
  • fmincg.m - 最优化函数 (类似于fminunc)
  • plotFit.m - 绘制多项式拟合
  • trainLinearReg.m - 使用代价函数训练线性回归
  • linearRegCostFunction.m - 正则化线性回归代价函数
  • learningCurve.m - 学习曲线
  • polyFeatures.m - 将数据映射到多项式特征空间
  • validationCurve.m - 生成交叉验证曲线

正则化的线性回归

首先实现正则化线性回归,利用水库中水位的变化预测从大坝流出的水量。

然后通过调试学习算法的诊断过程,并验证偏差与方差的影响。

1、数据可视化:

从可视化的数据集开始,该数据集包含有关水位x的变化以及流出大坝y的水量的历史记录。

数据分为三部分:

  • 训练集X,y:确定参数θ
  • 交叉验证集Xval, yval:用于确定正则化参数
  • 测试集Xtest, ytest:评估模型的性能

画出训练集:

%% =========== Part 1: Loading and Visualizing Data =============
clear; close all; clc
load('ex5data1.mat');
m = size(X, 1);
% Plot training data
plot(X, y, 'rx', 'MarkerSize', 10, 'LineWidth', 1.5);
xlabel('Change in water level (x)');
ylabel('Water flowing out of the dam (y)');
fprintf('Program paused. Press enter to continue.\\n');
pause;
机器学习之算法分析与优化:偏差和方差

2、正则化的线性回归代价函数:

机器学习之算法分析与优化:偏差和方差

完成linearRegCostFunction.m

function [J] = linearRegCostFunction(X, y, theta, lambda)
m = length(y);
J = 0;
theta_1 = [0; theta(2:end)];
J = sum(X * theta -y) / (2 * m) + lambda / (2 * m) * theta_1' * theta_1;
end

测试θ初始化为[1, 1],期待得到的代价J结果为303.993192

%% =========== Part 2: Regularized Linear Regression Cost =============
theta = [1; 1];
J = linearRegCostFunction([ones(m, 1) X], y, theta, 1);
fprintf(['Cost at theta = [1 ; 1]: %f '...
'\\n(this value should be about 303.993192)\\n'], J);
fprintf('Program paused. Press enter to continue.\\n');
pause;

3、正则化的线性回归梯度下降:

机器学习之算法分析与优化:偏差和方差

linearRegCostFunction.m加入梯度计算

function [J, grad] = linearRegCostFunction(X, y, theta, lambda)
m = length(y);
J = 0;
grad = zeros(size(theta));
theta_1 = [0; theta(2:end)];
J = sum((X * theta -y).^2) / (2 * m) + lambda / (2 * m) * theta_1' * theta_1;
grad = (X' * (X * theta -y)) / m + lambda / m * theta_1;
end

测试θ初始化为[1, 1],期待得到的代价梯度grad结果为[-15.30; 598.250]

%% =========== Part 3: Regularized Linear Regression Gradient =============
theta = [1 ; 1];
[J, grad] = linearRegCostFunction([ones(m, 1) X], y, theta, 1);
fprintf(['Gradient at theta = [1 ; 1]: [%f; %f] '...
'\\n(this value should be about [-15.303016; 598.250744])\\n'], ...
grad(1), grad(2));
fprintf('Program paused. Press enter to continue.\\n');
pause;

4、拟合线性回归:

通过trainLinearReg.m计算权重θ

function [theta] = trainLinearReg(X, y, lambda)
initail_theta = zeros(size(X, 2), 1);
costFunction = @(t)linearRegCostFunction(X, y, t, lambda);
options = optimset('MaxIter', 200, 'GradObj', 'on');
theta = fmincg(costFunction, initail_theta, options);
end

此处我们将把λ设置为0,因为这里的θ只有两维,正则化对于这么少的特征没有什么作用。

画出最佳的拟合线,如下图:

机器学习之算法分析与优化:偏差和方差

发现这个模型并不能很好的去拟合(欠拟合),原因是这些数据是非线性的,产生了高偏差

偏差与方差

机器学习中的一个重要概念是偏差方差折衷。具有高偏差的模型对于数据而言不够复杂,并且往往会欠拟合,而具有高方差的模型则会过度拟合训练数据。

  • 偏差指模型的预测值与真实值的偏离程度。偏差越大,预测值偏离真实值越厉害。
  • 方差指模型预测值的离散程度或者变化范围。方差越大,数据分布越分散,函数波动越

学习曲线:

用代码生成学习曲线对调试学习算法很有帮助,learningCurve.m返回训练集和交叉验证集的误差向量:

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);
for i = 1 : m
theta = trainLinearReg(X(1:i, :), y(1:i), lambda);
error_train(i) = linearRegCostFunction(X(1:i, :), y(1:i), theta, 0);
error_val(i) = linearRegCostFunction(Xval, yval, theta, 0);
end
end

为了画出学习曲线,需要计算在大小不同的训练集时候的训练和交叉验证的误差。

%% =========== Part 5: Learning Curve for Linear Regression =============
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])

训练误差和交叉验证误差随着训练集大小而改变,如下图:

机器学习之算法分析与优化:偏差和方差

可以看出,增加样本数目,无法嫌住降低交叉验证集误差,无法提高泛化能力。

多项式回归

上面已经说到,存在一个问题,线性回归模型对于数据来讲过于简单,拟合不了非线性数据(欠拟合),预测值与真实值相差很大,产生了高偏差,为了解决这个问题,我们需要添加更多特征:使用多项式回归,假设函数如下:

机器学习之算法分析与优化:偏差和方差

首先需要通过函数polyFeatures.m将现有的特征映射成更高次幂 X -> [X, X二次方,X三次方,...],得到更多的特征:

function [X_poly] = polyFeatures(X, p)
X_poly = zeros(numel(X), p);
for i = 1:p
X_poly(:,i) = X.^i;
end

1、学习多项式回归:

由于特征的高次幂可能会使特征值变得很大,影响训练的效率,所以我们需要用featureNormalize.m对所有特征进行归一化处理:

function [X_norm, mu, sigma] = featureNormalize(X)
mu = mean(X);
X_norm = bsxfun(@minus, X, mu);
sigma = std(X_norm);
X_norm = bsxfun(@rdivide, X_norm, sigma);
end

使用featureNormalize处理训练集、交叉验证集、测试集的特征:

%% =========== Part 6: Feature Mapping for Polynomial Regression =============
p = 8;
x_poly = polyFeatures(X, p);
[X_poly, mu, sigma] = featureNormalize(x_poly);

X_poly = [ones(m, 1) X_poly];
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];
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];
fprintf('Normalized Training Example 1:\\n');
fprintf(' %f \\n', X_poly(1, :));
fprintf('\\nProgram paused. Press enter to continue.\\n');
pause;

拿到归一化之后的特征后,我们再进行模型训练:

%% =========== Part 7: Learning Curve for Polynomial Regression =============
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));
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('Program paused. Press enter to continue.\\n');
pause;

训练集的预测值与真实值:

机器学习之算法分析与优化:偏差和方差

发现对于训练集的预测特别好,但是这个多项式拟合太复杂了,意味着可能过拟合,泛化能力差。

训练集与交叉验证集的误差:

机器学习之算法分析与优化:偏差和方差

发现训练集的误差特别低,但是交叉验证的误差很高,训练误差和交叉验证误差之间存在差距,表明存在高方差问题(过拟合)。

2、调整正则化参数:

有一种方式解决过拟合问题,就是加入正则项(罚项)。

λ = 1:

机器学习之算法分析与优化:偏差和方差

机器学习之算法分析与优化:偏差和方差

发现多项式拟合符合数据的走向,且数学曲线显示交叉验证集和训练集最后保持想对低的值,没有高偏差或者高方差问题。

λ = 100:

机器学习之算法分析与优化:偏差和方差

机器学习之算法分析与优化:偏差和方差

发现多项式拟合不符合数据的趋势,说明正则化太多不能很好的拟合训练数据。

3、使用交叉验证数据集选择λ

从上面的例子可以看出,λ太大或者太小都不能很好的拟合数据,我们需要选择一个最佳λ,找到了最佳的λ后,我们再评估一下模型是否对未知的测试数据能够很好的拟合。

实现一个自动选择λ的函数validationCurve

function [lambda_vec, error_train, error_val] = validationCurve(X, y, Xval, yval)
lambda_vec = [0 0.001 0.003 0.01 0.03 0.1 0.3 1 3 10]';
error_train = zeros(length(lambda_vec), 1);
error_val = zeros(length(lambda_vec), 1);
for i = 1:length(lambda_vec)
lambda = lambda_vec(i);
theta = trainLinearReg(X, y, lambda);
error_train(i) = linearRegCostFunction(X, y, theta, 0);
error_val(i) = linearRegCostFunction(Xval, yval, theta, 0);
end
end

画出误差的曲线:

%% =========== Part 8: Validation for Selecting Lambda =============
[lambda_vec, error_train, error_val] = validationCurve(X_poly, y, X_poly_val, yval);
close all;
plot(lambda_vec, error_train, lambda, error_val);
legend('Train', 'Cross Validation');
xlabel('lambda');
ylabel('Error');
fprintf('lambda\\t\\tTrain Error\\tValidation Error\\n');
for i = 1:length(lambda_vec)
\tfprintf(' %f\\t%f\\t%f\\n', ...
lambda_vec(i), error_train(i), error_val(i));
end
fprintf('Program paused. Press enter to continue.\\n');
pause;
机器学习之算法分析与优化:偏差和方差

发现最佳的λ值是3,此处交叉验证集的误差值最低。

4、计算测试集的误差

机器学习之算法分析与优化:偏差和方差

机器学习之算法分析与优化:偏差和方差

从上图看出,对于未知数据(测试集)拟合的效果还是可以的。


分享到:


相關文章: