一、概述
作業分兩部分,第一部分必做,第二部分選做。
簡單到爆炸,很容易滿分,就是有幾個小細節要注意。

首先是submit錯誤問題,報錯Submission failed: Error using submitWithConfiguration>validateResponse ,這裡要配置curl和環境變量,當然win10是已經配置好的,可以略過這一步。
配置好之後修改作業中的lib檔案夾中的submitWithConfiguration檔案,修改131行和134行代碼,在POST後加-s,如下:
json_command = sprintf('echo jsonBody=%s | curl -k -X POST -s -d @- %s', body, url);
然後就可以正常傳回了,其實不正常傳回的話,網站也收到你的代碼并判分了,隻是看不到optional部分是否做對,因為網站上隻有分數,沒有nice work的提示,是以還需要這個。
二、分析
1、單變量線性回歸
其他的函數都是現成的,隻需要自己寫兩個函數,一個是代價函數cost,一個是梯度下降函數Gradient descent。
代價函數指的是預測值與真實值之差,求出這個差的平方和再除以2m即可,類似方差。
注意以下幾點:
代價函數的公式如下:
對應的代碼如下:
J=(X*theta-y)'*(X*theta-y)/(2*m);
在這裡,我們的X、theta、y都是矩陣,X*theta求出的是一個向量,也就是預測值組成的向量,減去y則是誤差,求出的是一個誤差向量。
我們需要誤差的平方和——這時使用矩陣計算可以省去循環計算。
對向量取轉置然後與原向量相乘,則可以直接得到平方和,然後除以2m即可。
一定要明确這裡的變量都是矩陣或者向量,而公式中的都是實數,是以需要明确實數運算與矩陣運算的關系,對應起來,也就不難了。
梯度下降函數是為了更新theta的值,進而求出符合條件的theta。
原理就是求出某點的梯度,沿着梯度方向走alpha的長度,求出新的點坐标,更新,直到求出的新的坐标與原坐标的差極小,這時就是收斂了,可以傳回符合條件的theta。
公式如下:
對應的代碼如下:
for iter = 1:num_iters
temp0=theta(1)-alpha/m*(sum(X*theta-y));
temp1=theta(2)-alpha/m*(sum((X*theta-y).*X(:,2)));
if(abs(theta(1)-temp0)<=0.0001&&abs(theta(2)-temp1)<=0.0001)
theta(1)=temp0;
theta(2)=temp1;
break;
end
theta(1)=temp0;
theta(2)=temp1;
J_history(iter) = computeCost(X, y, theta);
end
首先注意一點,theta中的元素要求都改變之後,一起更新,這也就要求要用temp暫時儲存theta的值,然後一起指派。
注意到這裡的求和是求一個向量的和,是以使用sum函數,求theta(2)的時候,比求theta(1)多了一個乘xi,這個xi指的不是X,而是“X的第二列的對應項”,是以要用點乘。
我在這裡設定了一個收斂判斷,如果變化量下雨等于0.0001,則視為收斂,退出循環。
這樣基礎分的100分就拿到了。
2、多變量線性回歸
選做部分又分為兩部分,其一是梯度下降求theta,其二是正規方程求theta。它們如果最後結果相同,說明計算正确。
①、梯度下降
與單變量不同,多變量的梯度下降有兩個要點:
其一是Feature Normalization,也就是資料标準化。
在課程中這是通過求出每個特征的平均值和範圍,然後令每個特征值減去平均值,再除以範圍得到的。
這裡的範圍就是最大值減去最小值。但是題目中不同,題目中分母部分要求是standard deviations,也就是标準差,用它來代替範圍。
其二是對于代價函數和梯度下降函數,要令其對n個特征的樣本仍能正确求解,這要對代碼進行更改。
首先是資料标準化,代碼如下:
mu=mean(X);
sigma=std(X);
for i=1:size(X,2)
X_norm(:,i)=(X_norm(:,i)-mu(i))/sigma(i);
end
matlab中的mean函數,求的是矩陣每一列元素的平均值,傳回的是一個橫着的向量;
std函數也一樣,求的是每一列元素的标準差,傳回橫着的向量;
然後需要做的就是對X中的每一個值,減去對應的平均值,再除以對應的标準差。
注意這裡是矩陣除以實數,實數都不一樣,是以不能直接除,但可以按列循環除。
然後是多元變量的代價函數,代碼如下:
J=((X*theta-y)'*(X*theta-y))/(2*m);
沒有變化,因為我們使用矩陣計算,一進制和多元在算法上沒有任何差別。
最後是梯度下降函數,代碼如下:
m = length(y); % number of training examples
J_history = zeros(num_iters, 1);
temp=zeros(length(theta),1);
for iter = 1:num_iter
for i=1:length(theta)
temp(i)=theta(i)-alpha/m*(sum((X*theta-y).*X(:,i)));
end
for i=1:length(theta)
theta(i)=temp(i);
end
J_history(iter) = computeCostMulti(X, y, theta);
end
這裡由于theta的元素不止兩個,是以temp也要開一個向量來存儲,然後循環計算temp的值,循環更新theta即可。
這樣就完成了多元線性回歸有關梯度下降方面的代碼。
接下來要實作選擇不同的theta值畫圖像。
這個有點麻煩,最開始我是直接将下面這段代碼複制多次來實作在一張圖上畫多個圖像的:
% Choose some alpha value
alpha = 0.2;
num_iters = 400;
% Init Theta and Run Gradient Descent
theta = zeros(3, 1);
[theta, J_history] = gradientDescentMulti(X, y, theta, alpha, num_iters);
% Plot the convergence graph
figure;
plot(1:numel(J_history), J_history, '-g', 'LineWidth', 2);
hold on
% Display gradient descent's result
fprintf('Theta computed from gradient descent: \n');
fprintf(' %f \n', theta);
fprintf('\n');
但是隻能在多個視窗畫多個圖像,hold on沒有用,接下來我隻好選擇為theta開矩陣,循環計算theta和J_history了,如下:
% Choose some alpha value
alpha=[0.01 0.05 0.1 0.2 0.5];
num_iters = 400;
% Init Theta and Run Gradient Descent
theta = zeros(3, 5);
J_history=zeros(num_iters, 5);
for i=1:5
[theta(:,i), J_history(:,i)] = gradientDescentMulti(X, y, theta(:,i), alpha(i), num_iters);
fprintf('Theta computed from gradient descent: \n');
fprintf(' %f \n', theta(:,i));
fprintf('\n');
end
% Plot the convergence graph
figure;
plot(1:numel(J_history(:,1)), J_history(:,1), '-b', 'LineWidth', 2);
hold on
plot(1:numel(J_history(:,2)), J_history(:,2), '-g', 'LineWidth', 2);
plot(1:numel(J_history(:,3)), J_history(:,3), '-r', 'LineWidth', 2);
plot(1:numel(J_history(:,4)), J_history(:,4), '-y', 'LineWidth', 2);
plot(1:numel(J_history(:,5)), J_history(:,5), '-k', 'LineWidth', 2);
xlabel('Number of iterations');
ylabel('Cost J');
hold off
注意是theta(:,i)而不是theta(i),畫圖也要選多個顔色才看的清楚。
效果如圖:
可以看出,在一定範圍内,alpha越大,收斂得越快。
在計算1650,3這一樣例的時候,注意把特征标準化,然後再計算,如下:
price = 0; % You should change this
sell=zeros(1,2);
sell(1)=(1650-mu(1))/sigma(1);
sell(2)=(3-mu(2))/sigma(2);
sell=[1 sell];
price=sell*theta(:,5);
如果計算正确,則這一結果會與下面的正規方程的結果相等。
②、正規方程法
這個簡單多了,沒有資料标準化,沒有梯度下降,很簡潔,代碼量也很少。隻有一行,如下:
theta=(inv(X'*X))*X'*y;
隻需要把公式翻譯過去即可。如果對方程的推導有疑問,可以參見下面這一部落格:
線性回歸 正規方程詳細推導過程
然後直接計算即可。
③、結果
如圖,最終預測結果都是293081.464335美刀,說明計算正确。
三、總結
機器學習的第一次上機學習,感覺還不錯,應該是難度有點低,沒有什麼挑戰性,但可能是第一次的緣故吧,對于matlab的一些函數調用還不是很熟,應該多加練習。