斯坦福大學機器學習筆記——多變數的線性迴歸以及梯度下降法注意事項(內有程式碼)
在前面部落格中介紹了單變數線性迴歸的實現過程,本文將介紹多變數線性迴歸演算法。
兩者的對比如下:
1.資料方面的差異:
單變數線性迴歸資料:
多變數線性迴歸資料:
對於單變數線性迴歸來說,只有一個特徵(房子的大小),而對於多變數線性特徵迴歸特徵的數量為多個(房子的大小、臥室的數量等)
2.模型構成上的差異:
單變數模型:
多變數模型:
首先介紹多維特徵用矩陣形式的表示:
對於上述多變數的資料來說,我們一般使用:
n代表特徵的數量;m代表樣本的數量;
多變數的假設h可以表示為:
為了使上面的式子更加簡化一些,可以引入
其中:
多變數的梯度下降:
以上就是多變數的梯度下降演算法,相對於原來的單變數的線性迴歸,多變數的線性迴歸只是引數的數量以及特徵的數量增加,其他地方原理基本相同。
多變數梯度下降法的注意事項:
1. 特徵縮放
當處理多維特徵問題的時候,要保證這些特徵具有相近的尺度,這樣可以使得梯度下降法收斂的更快。當尺度相差比較大時,如一個特徵為身高,單位是米,這該特徵的尺度一般為0-2,若另一個特徵為體重,單位是斤,則該特徵的尺度一般為0-300,所以對於這兩個尺度存在較大的差別,在梯度下降演算法收斂時會出現下面圖形的問題:
我們知道,梯度下降法是沿著損失函式最大的方向移動步長,所以對於上述圖形來說,下降的方向始終沿著垂直的方向移動,會出現圖中畫出的來回折返最終趨於中心最小值的方向移動。所以迭代次數要求非常多,收斂速度很慢。
如果我們對上述特徵的尺度進行歸一化,形成的損失的函式等高圖如上圖中的右圖圖所示,當沿著損失函式下降最大的方向移動時,即沿著等高線垂直的方向移動,使得收斂的速度很快。
要想完成特徵尺度相差不大的要求,要對特徵進行縮放,特徵縮放的方法有以下幾種:
a.對於每個特徵求出最大值,然後將每個樣本中的該特徵除以該特徵的最大值。使用公式可以表示為:
對於上面的例子來說,若對於一個數據集,身高這個特徵的最大值為1.98,體重的這個特徵的最大值為256,這對於該資料集中的所有樣本的身高特徵都除以1.98,對於體重這個特徵都除以256,則就完成了特徵的縮放。
b.另外一種特徵縮放的方式為,對於每一個特徵,求出該特徵的平均值,然後用該特徵的取值減去均值(去均值化),最後用去均值化的結果除以該特徵的最大值,使用公式可以表示為:
其中,
c.實現特徵縮放最簡單的方法為:
其中,
對尺度縮放之後的特徵取值範圍最大為-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
本人菜鳥一枚,有什麼不對的地方歡迎指正。