1. 程式人生 > >斯坦福大學機器學習筆記——多變數的線性迴歸以及梯度下降法注意事項(內有程式碼)

斯坦福大學機器學習筆記——多變數的線性迴歸以及梯度下降法注意事項(內有程式碼)

在前面部落格中介紹了單變數線性迴歸的實現過程,本文將介紹多變數線性迴歸演算法
兩者的對比如下:
1.資料方面的差異:
單變數線性迴歸資料:
單變量回歸
多變數線性迴歸資料:
多變量回歸
對於單變數線性迴歸來說,只有一個特徵(房子的大小),而對於多變數線性特徵迴歸特徵的數量為多個(房子的大小、臥室的數量等)
2.模型構成上的差異:
單變數模型:
單變數模型
多變數模型:
多變數模型

首先介紹多維特徵用矩陣形式的表示:
對於上述多變數的資料來說,我們一般使用:
n代表特徵的數量;m代表樣本的數量;
x(i)代表第i個訓練例項,是特徵矩陣中的第i行,是一個向量,例如:
x2=[14163240]
x(i

)j代表特徵矩陣中的第i行第j列,也就是第i個例項中的第j個特徵,例如:x(3)4=30
多變數的假設h可以表示為:hθ(x)=θ0+θ1x1+...+θnxn
為了使上面的式子更加簡化一些,可以引入x0=1,於是上述公式可以轉化為:
hθ(x)=θTx
其中:θ=[θ0;θ1;...;θn]的列向量,x=[x0;x1;...;xn]的列向量。
多變數的梯度下降:
這裡寫圖片描述
這裡寫圖片描述
以上就是多變數的梯度下降演算法,相對於原來的單變數的線性迴歸,多變數的線性迴歸只是引數的數量以及特徵的數量增加,其他地方原理基本相同。
多變數梯度下降法的注意事項:

1. 特徵縮放
當處理多維特徵問題的時候,要保證這些特徵具有相近的尺度,這樣可以使得梯度下降法收斂的更快。當尺度相差比較大時,如一個特徵為身高,單位是米,這該特徵的尺度一般為0-2,若另一個特徵為體重,單位是斤,則該特徵的尺度一般為0-300,所以對於這兩個尺度存在較大的差別,在梯度下降演算法收斂時會出現下面圖形的問題:
這裡寫圖片描述

這裡寫圖片描述
我們知道,梯度下降法是沿著損失函式最大的方向移動步長,所以對於上述圖形來說,下降的方向始終沿著垂直的方向移動,會出現圖中畫出的來回折返最終趨於中心最小值的方向移動。所以迭代次數要求非常多,收斂速度很慢。
如果我們對上述特徵的尺度進行歸一化,形成的損失的函式等高圖如上圖中的右圖圖所示,當沿著損失函式下降最大的方向移動時,即沿著等高線垂直的方向移動,使得收斂的速度很快。
要想完成特徵尺度相差不大的要求,要對特徵進行縮放,特徵縮放的方法有以下幾種:
a.對於每個特徵求出最大值,然後將每個樣本中的該特徵除以該特徵的最大值。使用公式可以表示為:
xi=ximax(xi)
對於上面的例子來說,若對於一個數據集,身高這個特徵的最大值為1.98,體重的這個特徵的最大值為256,這對於該資料集中的所有樣本的身高特徵都除以1.98,對於體重這個特徵都除以256,則就完成了特徵的縮放。
b.另外一種特徵縮放的方式為,對於每一個特徵,求出該特徵的平均值,然後用該特徵的取值減去均值(去均值化),最後用去均值化的結果除以該特徵的最大值,使用公式可以表示為:
x
i
=xiμimax(xi)

其中,μi為該特徵的均值。
c.實現特徵縮放最簡單的方法為:
xi=xiμisn
其中,μi為該特徵的均值,sn為標準差。
對尺度縮放之後的特徵取值範圍最大為-3~3,最小為-1/3~1/3,這是兩個範圍的臨界值。
2. 學習率的設定
梯度下降法收斂需要的迭代次數根據模型的不同而不同,我們可以根據繪製損失函式曲線可以得到大致的收斂迭代次數。也可以使用一些自動的測試方法判斷是否達到收斂,比如將代價函式與某個閾值進行比較,當代價函式小於該閾值時,則判斷為收斂,但是該方法的難點在於選擇一個合適的閾值是相當困難的。所以通常使用損失函式曲線,因為它不僅可以判斷收斂的迭代次數,同時能夠判斷梯度下降演算法是否正常工作(見下方)。
梯度下降演算法的迭代次數同時受到學習率的影響,當學習率設定過小時,迭代的次數會增加;當學習率設定過大時,梯度下降法可能不會收斂,具體的分析可以看http://blog.csdn.net/wyl1813240346/article/details/78366390,下面根據損失函式曲線看學習率的影響:
這裡寫圖片描述這裡寫圖片描述
出現上述樣式的損失函式的曲線都是由於學習率設定過大引起的,應該使用較小的學習率的值。
在設定學習率時,可以嘗試以下學習率:0.01,0.03,0.1,0.3,1,3,10以此方式遞增。

多變數線性迴歸的程式碼如下:

clear;
close all;

%% 資料處理和初始化

data = load('ex1data2.txt'); % ex1data.txt中第一列是特徵,第二列是標籤
feature = data(:,1:end-1);    % 取出特徵資料

max_feature = max(feature); % 每個特徵的最大值
mean_feature = mean(feature);   % 每個特徵的均值
std_feature = std(feature); % 每個特徵的標準差

% 實現特徵歸一化
feature = feature_normalized(feature, max_feature, mean_feature, std_feature);

result = data(:,end);  % 出標籤資料

% 繪製資料分佈,以確定使用哪種假設
% figure;
% plot3(feature(:,1),feature(:,2),result,'bo','MarkerSize', 3);   

[num_sample, num_feature] = size(feature);
feature = [ones(num_sample,1) feature];  % 將特徵進行擴充套件,在原來的基礎上增加一列全為1的矩陣

% 初始化
theta = zeros(num_feature+1,1); % 對theta進行初始化
iteration = 8000;  % 設定迭代次數
alpha = 0.01;    % 學習率

%% 訓練模型和測試資料

% 訓練模型,得到最優的theta值
[theta, all_theta, cost] = gradient_descent(theta, feature, result, iteration, alpha);

% 繪製隨著迭代次數增加,損失函式的變化過程
x = 1:iteration;
y = cost;
figure;
plot(x',y);

% 測試資料
test_data = [1520 4;5551 3];   %測試資料
test_data = feature_normalized(test_data, max_feature, mean_feature, std_feature);
[num_test_sample, num_test_feature] = size(test_data);  %測試資料的個數  
test_data = [ones(num_test_sample,1) test_data];  %測試資料的擴充
test_result = test_data * theta;    %測試資料的預測結果



上述程式碼用到三個函式,分別為:
1.進行特徵放縮:

function [normalized_feature, max_feature, mean_feature, std_feature] = feature_normalized(original_feature, max_feature, mean_feature, std_feature)

[num_sample,num_feature] = size(original_feature);
normalized_feature = zeros(num_sample, num_feature);

for i=1:num_feature
%     % 方式1
%     normalized_feature(:,i) = original_feature(:,i)/max_feature(i);
%     % 方式2
%     normalized_feature(:,i) = (original_feature(:,i)-mean_feature(i))/max_feature(i);
    % 方式3
    normalized_feature(:,i) = (original_feature(:,i)-mean_feature(i))/std_feature(i);
end

2.計算損失函式:

function cost = compute_cost(feature, result, theta)

m = length(result); %樣本的個數
cost = sum((feature*theta - result).^2)/(2*m);  %計算當前theta下的損失

end

3.實現梯度下降演算法:

function [theta, all_theta, cost] = gradient_descent(theta,feature,result,iteration,alpha)

[m,n] = size(feature);  % m代表樣本的個數,m代表所需要引數的個數
cost = zeros(iteration,1); % 儲存每次迭代的損失函式的值
all_theta = zeros(n, iteration);    % 儲存所有theta值

for i=1:iteration

    cost(i) = compute_cost(feature, result, theta); % 計算損失函式
%     cost(i) = sum((feature*theta - result).^2)/(2*m);

    for j=1:n
        theta(j) = theta(j) - alpha * sum((feature * theta - result) .*feature(:,j)) / m;
    end
%     theta(1) = theta(1) - alpha * sum((feature * theta - result) .*feature(:,1)) / m;   % 更新theta1
%     theta(2) = theta(2) - alpha * sum((feature * theta - result) .* feature(:,2)) / m;  % 更新theta2
%     theta(3) = theta(3) - alpha * sum((feature * theta - result) .* feature(:,3)) / m;  % 更新hteta3
    all_theta(:,i) = theta; % 將theta儲存起來

end

本人菜鳥一枚,有什麼不對的地方歡迎指正。