机器学习 1 - Linear regression with multiple variables
简述
最近在coursera上看Stanford的经典机器学习课程,总体上感觉跟国内某大学的研究生课程内容其实差不多。先从经典的线性回归模型开始写起吧。本文的模型实例来自于课程练习——根据房屋大小和房屋数量的统计数据来预估某房产的售价,多元线性回归的数据集在ex1data2文件中可以找到,一元数据集在ex1data1中。
PS:由于一元线性回归也属于一种特殊的多元线性回归,我决定偷懒直接上多元线性回归。
算法过程
主要包括几个方面:
- 数据清理(本例中主要是feature normalize)
- 算法
- gradient descent(梯度下降)
- normal equation(正规方程)
数据清理
主要是让数据集处于一种“密集稳定”的状态,否则不经过整理的数据集会让你在选择学习率的时候异常尴尬。(我亲自试过,你也可以试试不清理数据直接用梯度下降,看看会发生什么)不整理数据,有时候你可能会只能选用非常小的学习率,才能保证梯度下降的时候不会“跑飞”。(迭代到不收敛)然而这个学习率可能会非常的小,导致你在训练的时候,可能要迭代几千万次才能训练一点点,甚至根本无法训练。
一般来说,我们选择将数据“正态化”.根据统计学基础知识,算出均值和标准差,使得数据呈标准正态分布即可。在matlab中,均值函数为mean()
,标准差函数为std()
。
梯度下降
想要使用一个线性方程来拟合数据集,当数据集是N元的时候,则需要选择N个变量来与这些未知数组成线性方程,这N个变量记作theta。在迭代的首次,当然可以随意选择一组未知数开始。
接下来,就是选择一个代价函数cost()
,注意此函数的自变量当然是N个theta,使得代价函数最小(与实际情况拟合的最好)。一般来说,我们选择最小二乘法来判断代价的大小。
接下来,为了判断N元函数cost()
的最小值点,就是一个求导的过程了。这里要插一句题外话,线性回归代价函数的最小值就是极小值,因此不存在迭代到某个极小值之后卡死在某点而不能继续寻找最小值的情况,具体的可以自己证明。
梯度下降算法采用了梯度的概念,在某点沿着梯度的方向移动一小段距离,然后重复迭代这个过程,直到完成收敛。
正规方程
正规方程前面与梯度下降一样,只不过最小二乘拟合的答案直接通过数学方法由线性代数方程解出来。优点是:不需要迭代,不需要清理数据(因为不需要调整学习率,所以自然不需要整理数据呀),程序写起来非常简单。缺点是:当参数过多的时候,逆矩阵运算量太大;当参数存在线性关系的时候,逆矩阵当然是不存在的~这个时候你没法求逆矩阵了(当然可以用伪逆矩阵来求,但是不推荐)。不过就我个人经验而言,一般来说参数不太可能会出现线性相关的情况。如果出现了,可能会存在意义有重复参数,这个时候就要清理一下数据集,把不必要的参数项给去掉,就不会求解的时候出现矩阵不可逆的尴尬情况了。
程序
程序使用matlab写的,coursera是可以提交答案的~具体的提交方法可以参考pdf说明。
多元线性回归的主函数:
%% Initialization
%% ================ Part 1: Feature Normalization ================
%% Clear and Close Figures
clear ; close all; clc
fprintf('Loading data ...\n');
%% Load Data
data = load('ex1data2.txt');
X = data(:, 1:2);
y = data(:, 3);
m = length(y);
% Print out some data points
fprintf('First 10 examples from the dataset: \n');
fprintf(' x = [%.0f %.0f], y = %.0f \n', [X(1:10,:) y(1:10,:)]');
fprintf('Program paused. Press enter to continue.\n');
pause;
% Scale features and set them to zero mean
fprintf('Normalizing Features ...\n');
[X, mu, sigma] = featureNormalize(X);
% Add intercept term to X
X = [ones(m, 1) X];
std(X(:,2))
std(X(:,2))
X(:,2)
%% ================ Part 2: Gradient Descent ================
fprintf('Running gradient descent ...\n');
% Choose some alpha value
alpha = 0.01;
num_iters = 400;
% Init Theta and Run Gradient Descent
theta = zeros(3, 1);
computeCostMulti(X,y,theta)
[theta, J_history] = gradientDescentMulti(X, y, theta, alpha, num_iters);
% Plot the convergence graph
figure;
plot(1:numel(J_history), J_history, '-b', 'LineWidth', 2);
xlabel('Number of iterations');
ylabel('Cost J');
% Display gradient descent's result
fprintf('Theta computed from gradient descent: \n');
fprintf(' %f \n', theta);
fprintf('\n');
% Estimate the price of a 1650 sq-ft, 3 br house
% Recall that the first column of X is all-ones. Thus, it does
% not need to be normalized.
price = 0; % You should change this
parameter = [1650,3];
parameter = (parameter-mu)./sigma;
parameter = [1,parameter];
price = theta'*parameter';
fprintf(['Predicted price of a 1650 sq-ft, 3 br house ' ...
'(using gradient descent):\n $%f\n'], price);
fprintf('Program paused. Press enter to continue.\n');
pause;
%% ================ Part 3: Normal Equations ================
fprintf('Solving with normal equations...\n');
%% Load Data
data = csvread('ex1data2.txt');
X = data(:, 1:2);
y = data(:, 3);
m = length(y);
% Add intercept term to X
X = [ones(m, 1) X];
% Calculate the parameters from the normal equation
theta = normalEqn(X, y);
% Display normal equation's result
fprintf('Theta computed from the normal equations: \n');
fprintf(' %f \n', theta);
fprintf('\n');
% Estimate the price of a 1650 sq-ft, 3 br house
price = 0; % You should change this
parameter = [1,1650,3];
price = theta'*parameter';
fprintf(['Predicted price of a 1650 sq-ft, 3 br house ' ...
'(using normal equations):\n $%f\n'], price);
多元线性回归的梯度下降函数:
function [theta, J_history] = gradientDescentMulti(X, y, theta, alpha, num_iters)
% Initialize some useful values
m = length(y); % number of training examples
J_history = zeros(num_iters, 1);
for iter = 1:num_iters
delta = zeros(size(X,2),1);
for i=1:size(X,2)
delta(i,1)=sum((theta'*X'-y').*(X(:,i))')*alpha/m;
end
theta = theta - delta;
% Save the cost J in every iteration
J_history(iter) = computeCostMulti(X, y, theta);
end
end
数据正态化整理:
function [X_norm, mu, sigma] = featureNormalize(X)
X_norm = X;
mu = zeros(1, size(X, 2));
sigma = zeros(1, size(X, 2));
for i=1:size(X,2)
mu(1,i)=mean(X(:,i));
sigma(1,i)=std(X(:,i));
end
for i=1:size(X,2)
X_norm(:,i)= (X_norm(:,i)-mu(1,i))/sigma(1,i);
end
end
正规方程函数:
function [theta] = normalEqn(X, y)
theta = zeros(size(X, 2), 1);
theta = (X'*X)^(-1)*X'*y;
end
总结
总体来说,是一个比较简单的算法,高等数学和线性代数基础扎实的同学理解起来应该非常容易。