这里用到的数据是Andrew老师在coursera授课时作业中的数据,由于上传过程中公式格式有点乱,因此省掉了原理部分,具体的可以看Andrew老师的授课视频和讲义。
两分类问题,特征值为学生的两门课考试成绩,y值0、1决定学生是否被学校录取。
这里包含两端程序,都能实现功能。其中程序一没有采用matlab内建的fminunc函数,需要自己设定学习速率;程序二程序采用matlab内建的fminunc函数,不用自己设定学习速率,但是需要制定梯度下降迭代的次数。
程序一:
function [theta ,J,a] = logisticRegression(X, y)
% 利用梯度下降的算法求解出最小的J(theta)
alpha = 0.004; %设置学习速率
[m, n] = size(X); %训练样本的数量
X = [ones(m, 1) X]; %特征矩阵
initial_theta = zeros(n + 1,1); %初始化theta
prediction = X*initial_theta;
logistic = 1 ./ ( 1 + exp(-prediction)); %逻辑函数
sqrError = (logistic-y)'* X;
theta = initial_theta - alpha * (1/m)* sqrError';
couverg = (1/m) * sqrError'; % J(theta)求导,用于判断是否到达最低点
%求J(theta)
J= -1 * sum( y .* log(logistic) + (1 - y ) .* log( (1 - logistic))) / m ;
a=1;
Boolean = zeros(size(X,2),1);
%在最低点处退出循环,即导数等于0
while all(couverg(:) ~= Boolean(:)) % 用于判断时候到达最低点,如果到达最低点就停止循环
%while a ~= 400000 这里的400000是测试循环次数,作用是取适当的值
prediction2 = X*theta;
logistic1 = 1 ./ ( 1 + exp(-prediction2) );
sqrError2 = (logistic1-y)'* X;
J= -1 * sum( y .* log(logistic1) + (1 - y ) .* log(1 - logistic1))/ m ;
theta = theta - alpha * (1/m) * sqrError2';
couverg = (1/m) * sqrError2';
a = a+1;
End
程序二:
% ex2data1.txt文件前两列表示两门课的考试成绩,第三列表示是否录取
data = load('ex2data1.txt');
X = data(:, [1, 2]); y = data(:, 3); %成绩和录取结果
plotData(X, y);
hold on;
xlabel('Exam 1 score')
ylabel('Exam 2 score')
legend('Admitted','Not admitted')
hold off;
%读取X矩阵,即特征矩阵,其中m是训练的样本集,n是特征维数
[m, n] = size(X);
%在特征维数前面添加第零维特征=0
X = [ones(m, 1) X];
%初始化theta
initial_theta = zeros(n + 1, 1);
% 通过初始化计算代价函数梯度,并作为fminunc函数的第一个参数,指导fminunc的计算
[cost, grad] = costFunction(initial_theta, X, y);
options = optimset('GradObj','on','MaxIter', 400);
% 这里的fminunc是matlab内建的函数,好处是不用再设置学习速率α
[theta, cost] = ...
fminunc(@(t)(costFunction(t, X, y)), initial_theta, options);
plotDecisionBoundary(theta, X, y);
hold on;
xlabel('Exam 1 score')
ylabel('Exam 2 score')
legend('Admitted','Not admitted')
hold off;
% 预测第一门课45分、第二门课85分是否能被录取
prob = sigmoid([1 45 85] * theta);
fprintf(['For a student with scores 45 and 85, we predict an admission probability of %f\n\n'], prob);
fprintf('Train Accuracy: %f\n', mean(double(p == y)) * 100);
运行结果
程序一:
图1 部分训练样本(100*3)。其中100为训练样本,前两列是两门课的成绩,第三列表示是否录取
图2 训练样本数据
图3 程序一训练结果,其中theta参数,J为代价函数的值,a为是代价函数最小时的迭代次数
图4 决策边界
图5 程序一测试结果
程序二:
图6 决策边界
图7 测试结果
在上述实验中,可以得出结果,当一个学生的第一门课成绩为45分,第二门课成绩为85分时,由假设函数的定义,程序一中他被录取的比例是77.6%,程序二中被录取的比例是77.4323%。
在逻辑分类的代价函数推倒过程中,使用了最大似然估计,但是最大似然估计求的是最大值,而代价函数求的是最小值,因此相差一个负号,这里的代价函数其实是一个交叉熵。
对比程序一和程序二。程序一中,采用向量化运算,同时更新所有的theta,则可以使代价函数(凸函数)达到最小的位置,由于需要自己设置学习速率alpha,因此需要经过多次测试,选择合适的alpha,如果学习速率设置的不恰当,可能代价函数就不会收敛,这里我测试的结果是选择alpha为0.004,因此我首先通过设定循环次数a取得恰当的学习速率,再把此学习速率用于使代价函数收敛的循环中,可以看到循环次数还是达到15765648次。
程序二中,由于采用内建的fminunc函数可以不用设置alpha值,较为方便,但是个人感觉唯一的不好就是设置梯度下降的次数,难以把握何时取得代价函数最小的值,但通常设置的较大都能收敛得到结果,而且速度要快很多。
在程序具体的实现过程中,查阅文献可以看到,如果不采用梯度下降的方法,这里还有BFGS(变尺度法)、L-BFGS(限制变尺度法)等,这些算法的优点就是可以自动选择好的学习速率而且也通常比梯度下降要快很多,但是缺点就是算法更加复杂。