天天看點

【多變量線性回歸】學習記錄序思路實作終

由于最近時間比較緊,要學的東西也比較多,是以這篇文章會寫得比較粗略,主要目的也是儲存自己的代碼,以及友善自己日後回憶。

思路

J 函數

首先我們将要定義一個 J J J 函數,意在表達目前函數與訓練資料間的差異值, J J J 函數的值越大,表示在參數為 θ \theta θ 時目前函數 h θ ( x ) h_{\theta}(x) hθ​(x) 與訓練資料 y y y 的拟合程度越差。下面給出 J J J 函數的定義式:

J θ = 1 2 m ∗ ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) J_{\theta} = \frac {1} {2m} * \sum_{i=1}^{m} {(h_{\theta}(x^{(i)})-y^{(i)})} Jθ​=2m1​∗i=1∑m​(hθ​(x(i))−y(i))

J J J 函數有一些喜人的性質,比如局部最優解等于全局最優解,正是這個性質使得我們可以放心地使用梯度下降算法來訓練算法。

下面是 J J J 函數關于 θ \theta θ 值的 surf 圖和 contour 圖,圖三中的 mark 表示經過數次學習後使 J J J 函數達到收斂的參數的所在位置。

【多變量線性回歸】學習記錄序思路實作終
【多變量線性回歸】學習記錄序思路實作終

梯度下降

可以被想象為一個小球處于某個初始狀态,不斷向山谷即極小值點滾動的過程。這裡的山谷指的是 J J J 函數的值關于 θ \theta θ 的變化。

θ j = θ j − α 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \theta_{j} = \theta_{j} - \alpha \frac {1} {m} \sum_{i=1}^m {(h_{\theta}(x^{(i)})-y^{(i)})} x^{(i)}_j θj​=θj​−αm1​i=1∑m​(hθ​(x(i))−y(i))xj(i)​

向量化

内置的向量庫要比手寫循環快很多,是以代碼實作的時候盡可能用向量表示。

實作

主腳本(my_mul.m)

clear;
close all;
clc;

% ====================================================

fprintf('load the ex1data2.txt...\n');
data = load('ex1data2.txt');
X = data(:, 1:2);
y = data(:, 3);

m = length(y); % number of sample case
fprintf('load successful!\n\n\n');


fprintf('init the data...\n');
[X, mu, stdd] = init_data(X);
X = [ones(m,1), X];
fprintf('init successful!\n\n\n');

% ====================================================

theta = [0; 0; 0];
alpha = 0.2;
iteration = 50;
fprintf('init the parameter, successful!\n');

[theta, J_his] = gra_des(X, y, theta, alpha, iteration);
cur_J = J_his(size(J_his), 1);
fprintf('cur_J = %f\n',cur_J);

plot((1:50), J_his(:, 1));

query = [1650, 3];
query = [ones(1,1), (query - mu) ./ stdd * 3];
tmp = query * theta;

fprintf('Predicted price of a 1650 sq-ft, 3 br house is %f\n',tmp);

           

資料初始化(init_data.m)

為了避免各個次元的資料之間相差過大,梯度下降時不收斂,把所有資料通過加減和縮放變為均值為 0,标準差為 1 的資料。并把縮放記錄儲存下來,在預測其他資料時需要使用。

function [res, mu, stdd] = init_data(X) 
	mu = mean(X); minn = min(X); maxx = max(X);
	stdd = maxx - minn;
	for i = 1:size(X, 2)
		X(:, i) = X(:, i) - mu(1, i);
		if maxx(1, i) ~= minn(1, i)
			X(:, i) = X(:, i) / stdd(1, i) * 3;
		end;
	end;
	res = X;
	
           

計算 J函數 (calc_J.m)

這個很簡單,不多bb。

function J = calc_J(X, y, theta)
	m = length(y);
	J = 0;
	J = 1/(2*m) * sum(((X*theta)-y).^2);
	
           

梯度下降 (gra_des.m)

把 J J J 函數的變化曲線也儲存下來,這樣調 α \alpha α (學習速率) 的時候很友善。

function [theta, J_his] = gra_des(X, y, theta, alpha, iteration) 
	m = length(y);
	J_his = zeros(m, 1);

	for iter = 1:iteration
		theta = theta - (alpha/m) * (X'*(X*theta-y));
		J_his(iter, 1) = calc_J(X, y, theta);
	end;

           

現在一看簡單得一匹,我為什麼當時實作了那麼久?

繼續閱讀