機器學習之算法分析與優化:偏差和方差

機器學習之算法分析與優化:偏差和方差

  • 簡述
  • 包含的文件
  • 正則化線性迴歸

簡述:

  • 偏差指模型的預測值與真實值的偏離程度。偏差越大,預測值偏離真實值越厲害。
  • 方差指模型預測值的離散程度或者變化範圍。方差越大,數據分佈越分散,函數波動越大,泛化能力越差。
  • 我們將實現正則化線性迴歸用於學習不同的偏差、方差的模型。
  • 數據下載: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、計算測試集的誤差

機器學習之算法分析與優化:偏差和方差

機器學習之算法分析與優化:偏差和方差

從上圖看出,對於未知數據(測試集)擬合的效果還是可以的。


分享到:


相關文章: