备注: Coursera上Andrew Ng的机器学习课程有8次编程作业。本帖记录我练习过程中学到的知识,希望对大家有帮助。Andrew NG机器学习线性回归编程作业详细分析,这篇耗费巨大心血,非常适合小白去做NG课程作业时参考,部分代码不好理解(先放弃),重点理解NG留的那些空的代码,然后参考我整理的另一篇博客,线性规划的Matlab代码总结,结合着看,慢慢就懂了,加油
在本次练习中,需要实现一个单变量的线性回归。假设有一组历史数据<城市人口,开店利润>,现需要预测在哪个城市中开店利润比较好?
历史数据如下:第一列表示城市人口数,单位为万人;第二列表示利润,单位为10,000$
5.5277 9.1302
8.5186 13.6620
7.0032 11.8540
.....
......
Matlab的工作目录
使用matlab中图形化的Current Folder面板可以修改当前工作目录
只有进入工作目录, Matlab才能默认找到该目录下的各种文件。
可以使用命令来调整工作目录
pwd 查看当前工作目录
用于当前工作目录的路径。例如:
pwd
ans =C:\MATLAB7\work
cd 进入某目录,用于切换当前工作目录。例如:
cd(‘c:/toolbox/matlab/demos’) %切换当前工作目录到demos
cd .. %切换当前工作目录到matlab
ls 列出当前目录下的内容
直接打出ls就可以
m脚本文件
matlab是解释型的语言,在命令行界面可以输入命令执行。脚本文件就是把多个命令合在一起,在命令行调用这个脚本文件就可以执行文件里面的一句句命令。
例如在命令行输入两条命令
执行后可以在Workspace窗口看到已有的变量
我们也可以使用脚本文件来完成相同的事情。新建一个文件,内容为
c = 7
d = c*7
保存到当前工作目录下,命名script1.m。然后在命令界面输入script1,就相当于执行了文件里的这两条语句。之后在Workspace窗口可以看到变量c和d。
备注:matlab 的editor怎么打开?
直接点File下的新建m文件图标
Ctrl+N
m函数文件
函数文件用来定义matlab中的函数,可以供上层调用。 函数文件要保存为 函数名.m ,才可以通过函数名来调用。经过我的测试,文件名和文件中的函数名不一致时,以文件名为准。
function 返回值 = 函数名(输入参数)
% YOUR CODE HERE
End
返回值和输入参数都可以有多个,之间用逗号隔开。返回值有多个的时候要用方括号包起来。
function [返回值1, 返回值2] = 函数名(输入1,输入2,输入3)
% YOUR CODE HERE
End
示例:
我们新建一个f1.m,内容如下
function s = f1(a)
s = a+8;
end
保存到工作目录后就可以使用这个函数
5.语句中的分号
语句不带分号会输出运行结果,如果语句带分号则不输出结果。
6.第一次编程作业的文件如下图
脚本文件ex1用来执行单变量线性回归,ex1_multi.m用来执行多变量线性回归。submit.m用来提交你的作业到服务器,本文不包含对这部分代码的分析。
1.初始化Initialization
%% Initialization
clear all; close all; clc
备注:
1.两个百分号%%是matlab中用来表示代码块的注释。从%%开始到下一个%%之间会作为一个代码块,在matlab中查看时会用黄白相间显示
2.clear 清除工作区的所有变量。还可以后面跟变量名来清除某个变量。
3.close all 关闭所有窗口(显示图像的figure窗口)
4.clc 清除命令窗口的内容(就是命令界面以前的命令)
5.% 代表注释行
2. 基础函数 Part 1:Basic Function
% Complete warmUpExercise.m
fprintf('Running warmUpExercise ... \n');
fprintf('5x5 Identity Matrix: \n');
warmUpExercise()
fprintf('Program paused. Press enter to continue.\n');
pause;
完成warmUpExercise.m 的函数
注释:
1.fprintf的用法
fprintf和c语言中的printf用法类似,用于格式化输出,也支持%d等占位符,也可以直接输出字符串,\n表示换行符。Matlab中字符串用单引号括起来。fprintf函数可以将数据按指定格式写入到文本文件中。
数据的格式化输出:fprintf(fid,format,variables)
按指定的格式将变量的值输出到屏幕或指定文件
fid为文件句柄,若缺省,则输出到屏幕
format用来指定数据输出时采用的格式
%d 整数
%e实数:科学计算法形式
%f实数:小数形式
%g由系统自动选取上述两种格式之一
%s输出字符串
fprintf(fid,format,A)
说明:fid为文件句柄,指定要写入数据的文件,format是用来控制所写数据格式的格式符,与fscanf函数相同,A是用来存放数据的矩阵。
2.\n是换行,英文是New line,表示使光标到行首。
\r是回车,英文是Carriage return,表示使光标下移一格。
3.pause用来暂停。
中间调用了warmUpExercise函数,也就是warmUpExercise.m对应的函数。这个函数要求输出一个5*5的单位矩阵,直接使用eye函数就可以了。
3.函数warmUpExercise()如下
function A = warmUpExercise()
A = [];
A = eye(5);
end
注释:
1.A= [ ] ,A是空矩阵
2.eye() ,单位矩阵
输入参数无,返回值A。
之后在命令界面可以调用这个函数
4. 画图Part 2: Plotting
fprintf('Plotting Data ...\n')
data = csvread('ex1data1.txt');
X = data(:, 1); y = data(:, 2);
m = length(y);
plotData(X, y);
fprintf('Program paused. Press enter to continue.\n');
pause;
注释:
1.data = csvread(‘ex1data1.txt’)和data = load(‘ex1data1.txt’);意思一样都是加载数据,使用load函数来读取文件,会自动返回生成的矩阵
2.CSVREAD()函数用法
第一种:M = CSVREAD(‘FILENAME’) ,直接读取csv文件的数据,并返回给M
第二种:M = CSVREAD(‘FILENAME’,R,C) ,读取csv文件中从第R-1行,第C-1列的数据开始的数据,这对带有头文件说明的csv文件(如示波器等采集的文件)的读取是很重要的。
第三种:M = CSVREAD(‘FILENAME’,R,C,RNG),其中 RNG = [R1 C1 R2 C2],读取左上角为索引为(R1,C1) ,右下角索引为(R2,C2)的矩阵中的数据。
P.S:matlab认为CSV第1行第1列的单元格坐标为(0,0)
csvread函数只试用与用逗号分隔的纯数字文件
3.该部分先从文件读取数据,然后调用plotData来画图,在工作区窗口可以看到data的类型97行2列的矩阵。可以看出ex1data1.txt中有97行数据。
4.
5.冒号表示所有
a(:,4)表示矩阵a的第4列元素,这个结果是列向量(m*1矩阵)
a(3,:)表示a的第3行元素,是行向量(1*n矩阵)
引用向量中的元素时,括号里只有1个数字,例如b是向量
b(3)表示b中第3个元素。
6.m = length(y); length函数,返回向量y的长度。这也是我们的训练集中实例的个数。
7.最后调用plotData(X, y)来画图。
5.函数plotData()如下
function plotData(x, y)
figure; % open a new figure window
plot(x, y, 'rx', 'MarkerSize', 10); % Plot the data
ylabel('Profit in $10,000s'); % Set the yaxis label xlabel('Population of City in 10,000s'); % Set the xaxis label
End
注释:
1. figure; 表示打开一个新的画图窗口
2. plot用来画点。’rx’表示红色,x型。’MarkerSize’, 10 表示大小是10 。plot的用法非常灵活,可以参考 官方文档 。此处的格式为
plot(X1,Y1,LineSpec, ‘PropertyName’, PropertyValue,…)
3.xlabel和ylabel用于设置坐标说明。
在我们的脚本ex1中,X存放了数据第一列,y存放了数据的第二列,调用画图函数就可以画出散点图了
6.梯度下降Part 3: Gradient descent
%% ======== Part 3: Gradient descent ===================
fprintf('Running Gradient Descent ...\n')
X = [ones(m, 1), data(:,1)]; % Add a column of ones to x
theta = zeros(2, 1); % initialize fitting parameters
% Some gradient descent settings
iterations = 1500;
alpha = 0.01;
% compute and display initial cost
computeCost(X, y, theta)
% run gradient descent
theta = gradientDescent(X, y, theta, alpha, iterations);
% print theta to screen
fprintf('Theta found by gradient descent: ');
fprintf('%f %f \n', theta(1), theta(2));
% Plot the linear fit
hold on; % keep previous plot visible
plot(X(:,2), X*theta, '-')
legend('Training data', 'Linear regression')
hold off % don't overlay any more plots on this figure
% Predict values for population sizes of 35,000 and 70,000
predict1 = [1, 3.5] *theta;
fprintf('For population = 35,000, we predict a profit of %f\n',...
predict1*10000);
predict2 = [1, 7] * theta;
fprintf('For population = 70,000, we predict a profit of %f\n',...
predict2*10000);
fprintf('Program paused. Press enter to continue.\n');
pause;
注释:
1.
ones(x,y) X行Y列的单位阵
zeros(x,y) X行Y 列的零矩阵
2.hold on 和hold off是相对使用的:
前者的意思是,你在当前图的轴(坐标系)中画了一幅图,再画另一幅图时,原来的图还在,与新图共存,都看得到;
后者表达的是,你在当前图的轴(坐标系)中画了一幅图,此时,状态是hold off,则再画另一幅图时,原来的图就看不到了,在轴上绘制的是新图,原图被替换了。
3.第4行 X = [ones(m, 1), data(:,1)] ,是给X最左边添加一列,全为1
4.
C语言中’\n’是换行的意思,一般放到printf()这类函数中使用,比如:
printf(“this is a test\n Please check it\n”);
结果是:
this is a test
Please check it
5.computeCost(X, y, theta)函数用来计算代价
6.theta = zeros(2, 1),设置参数theta的初值,此处设置的初值都是0
7.假设函数、代价函数和梯度下降算法的向量表示
假设函数的向量表示如下:
代价函数的表示如下:
使用梯度下降算法求解 θ 的向量表示如下:
证明过程如下:
8.matlab的legend用法
用Matlab画图时,有时候需要对各种图标进行标注,例如,用“+”代表A的运动情况,“*”代表B的运动情况。
legend函数的基本用法是:
LEGEND(string1,string2,string3, …)
分别将字符串1、字符串2、字符串3……标注到图中,每个字符串对应的图标为画图时的图标。
例如:
plot(x,sin(x),’.b’,x,cos(x),’+r’)
legend(‘sin’,’cos’)这样可以把”.”标识为’sin’,把”+”标识为”cos”
还可以用LEGEND(…,’Location’,LOC) 来指定图例标识框的位置。
7.computeCost()函数
function J = computeCost(X, y, theta)
m = length(y);
J = 0;
J = sum((X*theta - y).^2) / (2 * m);
end
注释:
1.此时
2.函数sum( )的用法
a=sum(x); %列求和
a=sum(x,2); %行求和
a=sum(x(:)); %矩阵求和
假定x为一个矩阵:
sum(x)以矩阵x的每一列为对象,对一列内的数字求和。
sum(x,2)以矩阵x的每一行为对象,对一行内的数字求和。
3.
这个求和可以用循环来解决,不过matlab的专长是矩阵,我们应该利用向量和矩阵的特点实现同时计算多个变量。这叫做向量化计算,是非常重要的。
这里说一下matlab中的运算符,基本的有四则运算+ – * /和^幂运算,如果是两个矩阵运算,会按照矩阵运算的规则,
例如矩阵乘法和矩阵除法(乘以逆矩阵)。如果想对矩阵中的每个元素做计算,要使用点号. ,例如矩阵A和矩阵B对应位置元素相乘,应该用 A .* B另外,矩阵和标量做运算,结果是对矩阵中每个元素做运算。例如A中每个月元素加2,A+2
先来看求和号里面的第i项,对应第i个数据。
先用一个变量求出h(x)
hx = X * theta
注意个矩阵运算的结果中包含了i从0到m所有的结果
此时X是一个m*2的矩阵,theta是一个2*1的矩阵
这里用 .^ 表示对矩阵中每个元素平方,而不是求矩阵的平方。
上面的向量所有元素加起来就是累加结果
sum((hx – y).^2)
最后求得J
J = sum((hx – y).^2) / (2*m)
备注:function [输出变量] = 函数名称(输入变量)
8. 梯度下降函数gradientDescent()
function [theta, J_history] = gradientDescent(X, y, theta, alpha, num_iters) %定义梯度下降函数
m = length(y); %训练实例的个数
for iter = 1:num_iters
H = X * theta;%假设函数
T = [0 ; 0];%
for i = 1 : m,
T = T + (H(i) - y(i)) * X(i,:)';
end
theta = theta - (alpha * T) / m;
J_history(iter) = computeCost(X, y, theta);
end
end
注释:
1.J_history用来记录每次迭代时的代价值。
2.先表示求和号里面,第j个参数对应的是
3. X(i,:)’表示第i行所有元素进行转置
4. 用手抄代码整理出来。会贴在此处。
9.Part 4: Visualizing J(theta_0, theta_1)
%% ====== Part 4: Visualizing J(theta_0, theta_1) =============
fprintf('Visualizing J(theta_0, theta_1) ...\n')
% Grid over which we will calculate J
theta0_vals = linspace(-10, 10, 100);
theta1_vals = linspace(-1, 4, 100);
% initialize J_vals to a matrix of 0's
J_vals = zeros(length(theta0_vals), length(theta1_vals));
% Fill out J_vals
for i = 1:length(theta0_vals)
for j = 1:length(theta1_vals)
t = [theta0_vals(i); theta1_vals(j)];
J_vals(i,j) = computeCost(X, y, t);
end
end
% Because of the way meshgrids work in the surf command, we need to
% transpose J_vals before calling surf, or else the axes will be flipped
J_vals = J_vals';
% Surface plot
figure;
surf(theta0_vals, theta1_vals, J_vals)
xlabel('\theta_0'); ylabel('\theta_1');
% Contour plot
figure;
% Plot J_vals as 15 contours spaced logarithmically between 0.01 and 100
contour(theta0_vals, theta1_vals, J_vals, logspace(-2, 3, 20))
xlabel('\theta_0'); ylabel('\theta_1');
hold on;
plot(theta(1), theta(2), 'rx', 'MarkerSize', 10, 'LineWidth', 2);
注释:
1.linspace的用法
2.具体画图这个不具体分析,目前功力不够
10. 多变量代价函数computeCostMulti()
function J = computeCostMulti(X, y, theta)
m = length(y); % number of training examples
J = 0;
% ====================== YOUR CODE HERE ======================
J = sum((X * theta - y).^2) / (2 * m);
% ==========================================================
end
11.多变量的梯度下降函数 gradientDescentMulti()
function [theta, J_history] = gradientDescentMulti(X, y, theta, alpha, num_iters)
%GRADIENTDESCENTMULTI Performs gradient descent to learn theta
% theta = GRADIENTDESCENTMULTI(x, y, theta, alpha, num_iters) updates theta by
% taking num_iters gradient steps with learning rate alpha
% Initialize some useful values
m = length(y); % number of training examples
n = size(X , 2);
J_history = zeros(num_iters, 1);
for iter = 1:num_iters
% ====================== YOUR CODE HERE ======================
% Instructions: Perform a single gradient step on the parameter vector
% theta.
%
% Hint: While debugging, it can be useful to print out the values
% of the cost function (computeCostMulti) and gradient here.
%
H = X * theta;
T = zeros(n , 1);
for i = 1 : m,
T = T + (H(i) - y(i)) * X(i,:)';
end
theta = theta - (alpha * T) / m;
% ============================================================
% Save the cost J in every iteration
J_history(iter) = computeCostMulti(X, y, theta);
end
end
**注释:
由于单变量中我们的计算方法同样适用于多变量,这里的代码不需要改变,直接用ex1中的代码即可。**
12.特征归一化Feature Normalization
%% ======= Part 1: Feature Normalization ================
%% Clear and Close Figures
clear all; close all; clc
fprintf('Loading data ...\n');
%% Load Data
data = csvread('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];
13.特征归一化函数featureNormalize(X)
function [X_norm, mu, sigma] = featureNormalize(X)
%FEATURENORMALIZE Normalizes the features in X
% FEATURENORMALIZE(X) returns a normalized version of X where
% the mean value of each feature is 0 and the standard deviation
% is 1. This is often a good preprocessing step to do when
% working with learning algorithms.
% You need to set these values correctly
X_norm = X;
mu = zeros(1, size(X, 2));
sigma = zeros(1, size(X, 2));
% ====================== YOUR CODE HERE ======================
% Instructions: First, for each feature dimension, compute the mean
% of the feature and subtract it from the dataset,
% storing the mean value in mu. Next, compute the
% standard deviation of each feature and divide
% each feature by it's standard deviation, storing
% the standard deviation in sigma.
%
% Note that X is a matrix where each column is a
% feature and each row is an example. You need
% to perform the normalization separately for
% each feature.
%
% Hint: You might find the 'mean' and 'std' functions useful.
%
m = size(X , 1);
mu = mean(X);
for i = 1 : m,
X_norm(i, :) = X(i , :) - mu;
end
sigma = std(X);
for i = 1 : m,
X_norm(i, :) = X_norm(i, :) ./ sigma;
end
%mu , sigma , X_norm
% ============================================================
end
注释:
由于缩放之后,输入新参数预测的时候,需要对输入做相同的缩放,才可以得出正确的结果,因此此函数返回了缩放时用到的均值和标准差。
使用到了mean和std函数,可以在matlab中使用help mean和help std来查看用法。
可以求出每列的均值和标准差,之后可以对每行进行缩放
mu = mean(X);
sigma = std(X);
for i=1:size(X,1)
X_norm(i, :) = (X(i, :) - mu) ./ sigma;
End
也可以采用空间换时间的方法,把mu和sigma拷贝一份复制成多行的 就可以直接用元素对应运算了
13.梯度下降Gradient Descent
%% ======== Part 2: Gradient Descent ================
% ====================== YOUR CODE HERE ======================
fprintf('Running gradient descent ...\n');
alpha = 0.3;
num_iters = 100;
theta = zeros(3, 1);
[theta, J_history] = gradientDescentMulti(X, y, theta, alpha, num_iters);
figure;
plot(1:numel(J_history), J_history, '-b', 'LineWidth', 2);
xlabel('Number of iterations');
ylabel('Cost J');
fprintf('Theta computed from gradient descent: \n');
fprintf(' %f \n', theta);
fprintf('\n');
============================================================
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;
注释:
ex1_multi.m的第85行可以修改学习率。经过试验,学习率和迭代步数适当增加后,可以得到和正规方程相同的结果
14.正规方程 Normal Equations
%% ================ Part 3: Normal Equations ================
fprintf('Solving with normal equations...\n');
% ====================== YOUR CODE HERE ======================
% Instructions: The following code computes the closed form
% solution for linear regression using the normal
% equations. You should complete the code in
% normalEqn.m
%
% After doing so, you should complete this code
% to predict the price of a 1650 sq-ft, 3 br house.
%
%% 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
% ====================== YOUR CODE HERE ======================
price = 0; % You should change this
% ============================================================
fprintf(['Predicted price of a 1650 sq-ft, 3 br house ' ...
'(using normal equations):\n $%f\n'], price);
关键代码
computeCost
J=sum((X*theta-y).^2)/(2*m);
gradientDescent
theta = theta - (1/m)*alpha*(X.'*(X*theta-y));
J_history(iter) = computeCost(X, y, theta);
featureNormalize
mu=mean(X,1);
sigma = std(X);
X_norm = (X - ones(size(X, 1), 1) * mu) ./ (ones(size(X, 1), 1) * sigma);
computeCostMulti
J = (X * theta - y).' * (X * theta - y) / (2*m);
gradeDescentMulti
theta = theta - (1/m)*alpha*(X.'*(X*theta-y));
J_history(iter) = computeCostMulti(X, y, theta);
normalEqn
theta=(inv(X.'*X))*X.'*y;