Coursra-MachineLearning 第二次作业总结

来源:互联网 发布:易语言ce修改器源码 编辑:程序博客网 时间:2024/06/06 02:00

Logistic Regression

1.1 Visualizing the data

我们的目标是根据ex2data1里面的数据,把被接受入学的数据和被拒绝入学的数据都标注在一张坐标图上。
为了表示区分,接受入学的样本在图上用“黑色小十字”来表示,而拒绝入学的样本在图上用“黄色小圆点”来表示。
如果要做这个事情,第一步就是要把混杂在一起的数据分成两堆。y=1的为一堆,y=0的为一堆。

  • 难点1 find函数
    我能够猜到 pos = find(y ==1) 是将y向量中等于1那些值所在的位置记录下来放到一个向量中,这个向量就是pos。这里主要是了解find这个函数的用法
    这里写图片描述
    在document里面有很多find函数,就是这个Find indices of nonzero elements.
    其中有一个 Elements Equal to Specific Values用法。

  • 难点2 矩阵切片的再认识
    X(pos,1)这个我也能猜出来是,它表示的是X这个矩阵中第一列中的行序数在pos向量中的那些值。我的疑问是,这个矩阵切片操作还可以这么表示,一个变量用向量,另一个变量用常数。仔细想想其实之前也用过,比如X = data(:,[1,2])中逗号前的部分“:”其实就是一种向量的表示方法,也就是说作为矩阵定位的变量是可以用向量来表示的;而逗号后面的部分“[1,2]”其实是第一列和第二列构成的矩阵,从这个表示方法来推测,其实1,2这些所谓的常数,其实应该是代表列位置的索引。总结一下,其实一个矩阵A的切片方式A(X,Y),X表示的行的部分,X可以由表示行的那些索引构成的向量,Y表示列的部分,可以由表示列的索引构成的向量。对于这个例子来说我还可以构造一个矩阵 s = data([1,15,25],[1,3])

搞清了这两点,我们就可以画图了,参考第二周作业笔记中对于画图函数的理解。以在坐标轴上标注出“被接受”的样本为例,最重要的是两个向量,一个向量是被接受样本的x坐标向量,另一个是被接受样本的y坐标向量,x坐标向量可以用X(pos,1)表示,y坐标向量可以用y(pos,2),在加上一些图形属性的参数就是
plot(X(pos, 1), X(pos, 2), ‘k+’,’LineWidth’, 2, ‘MarkerSize’, 7);
同理“被拒绝”样本的x,y坐标轴向量为X(neg, 1), X(neg, 2),绘图函数为:
plot(X(neg, 1), X(neg, 2), ‘ko’, ‘MarkerFaceColor’, ‘y’, ‘MarkerSize’, 7);

1.2 Implementation

1.2.1 sigmoid function

其实这个函数非常简单,只是有两点要注意
1. 指数函数的表示方法
如果是以e为底的那么就用 exp(x)来表示,如果是以其它自然数为底的比如说2x,那么就用2^x来表示就可以了。
2. 向量形式的实现
这个sigmoid函数是以向量的形式来实现的,在用1除以1+e^z的时候要用“./”而不是普通除,这个如果不是看人家的代码还真不容易发现。
所谓向量化可以这么理解:我需要对向量中的每个元素进行同一种运算。
比如以sigmoid函数作为例子,假设待计算的向量为α=a1a2a3,那么经过sigmoid函数的运算,得到的结果应该是sigmoid(α)=11+ea111+ea211+ea3。感觉是不是有点像函数式编程的map函数。相当于一个向量经过函数的处理,向量中所有的元素都经过函数的映射成为一个新的向量。
引入向量运算的理由是经常有大量的数据经常用同一个函数进行运算,按照一般程序的处理方法,就得用循环语句了。如果能把接受同样过程处理的数据打包批量进行处理就好了,这个就是向量化的思想。
Matlab里面大多数函数都已经实现了向量化,我们在实现函数的时候也要尽可能的实现向量化,具体来说就是如果可以用Matlab提供现有运算工具就能实现对向量(或者矩阵)中每个元素的处理就可以直接用先有工具的组合就行了。但是如果没有,那就只能写循环语句了。以这个sigmoid函数为例,待处理数据初始的状态为α=a1a2a3,exp()运算已经支持向量化,1+exp(α)向量加标量也是可以作用于向量所有的元素的,所以这个也不用怎么修改,那么现在中间运算的结果为1+ea11+ea21+ea3,之后需要用1除以向量中的每个元素变成11+ea111+ea211+ea3。正好Matlab里面有这个运算工具就是“点运算”所以 1 ./ (1 + exp( alpha ))。
这里面还涉及到一个知识点,就是矩阵和标量之间的运算,标量和矩阵之间的运算相当于标量和矩阵中的每一个元素进行运算,对于除法来说,标量在左边和在右边是完全不一样的,你可以做一个实验,构造一个矩阵A,之后用2除以A和A除以2,结果是不同的,这点很容易知道,但是如果是2除以A要用 “2 ./ A” A除以2用“A ./ 2”或者“A / 2 ”都可以。其它的乘法以及加减法加点或者不加点运算效果都是一样的。只有除法要注意。

1.2.2 Cost function and gradient

这块还是要用向量的方式实现,可以用第二周发现的方法,我们用几个小的观察一下规律嘛(有点像高中学数列的时候)
1. 关于cost function的实现,我自己没想出来向量形式的实现方式,只能用循环的方法实现(好Low啊),后来看了网友的答案得到启发。我来推导一下这个是怎么实现的。
首先还是用一个小数来把问题具体化,设m=3,n=2(三个样本,两个特征)。
系数矩阵X111x1(1)x1(2)x1(3)x2(1)x2(2)x2(3)
参数向量为θ=θ0θ1θ2
x(1)表示的是第一组样本,用向量的形式表示是x(1)=1x1(1)x2(1)
如果用样本矩阵来表示系数矩阵就应该是X=111x1(1)x1(2)x1(3)x2(1)x2(2)x2(3)=x(1)x(2)x(3)
其实这个形式就跟CS229-notes1里面page10的那个系数矩阵表达方法一样的。我为什么要写一遍系数矩阵,是因为后面要划归为向量形式要对这种形式比较熟悉。
接下来我们把J(θ)式子展看看看能不能发现一些规律。当m=3的时候
J(θ)=13{y(1)log(hθ(x(1)))+(1y(1))log(1hθ(x(1)))+y(2)log(hθ(x(2)))+(1y(2))log(1hθ(x(2)))+y(3)log(hθ(x(3)))+(1y(3))log(1hθ(x(3))}
我们把y(i)log(hθ(x(i)))的部分归集在一起看,也就是下图显示的部分
这里写图片描述
这部分就是y(1)log(hθ(x(1)))+y(2)log(hθ(x(2)))+y(3)log(hθ(x(3)))看出来点什么呢?
这部分其实就是向量y(1)y(2)y(3) 和 向量 log(hθ(x(1)))log(hθ(x(2)))log(hθ(x(3)))的内积。
向量y(1)y(2)y(3)就是向量y
log(hθ(x(1)))log(hθ(x(2)))log(hθ(x(3)))这块又怎么理解呢?它其实看以看成向量x(1)x(2)x(3) 带入公式log(hθ())的运算结果。但是x(1)x(2)x(3)这个向量中的每一个元素也是一个列向量啊。貌似到这里就有点卡壳了。怎么办呢?还是把这个形式具体到底。我们把式子展开,并且把向量带入进去,观察一下它的形式,看看能不能找到什么规律
log(hθ())中的hθ(x)是一个复合函数的简写形式,把它展开写的形式是sigmoid(θTx(i)),注意这个内层函数我特意用的是θTx(i)的方式来写而不是θTx,这是由于θTx这种写法是线性函数的一般写法,这种一般的写法其实对于变量x的数量是没有限制的(甚至对于次数也是没有限制的多项式的回归也是用这个形式,还可以x替换成其它各种奇怪的函数比如三角函数),而具体到某一个问题的时候,比如就是我们设计的这个例子,m = 3,n=2的情况。那么f(x)=θ0+θ1x1+θ2x2,那么我如果要往这个函数里面赋值的话,就必须一次性把x1x2这两个都赋值。换句话说一旦当特征的数量参数的数量固定下来了,f(x)=θTx(i)是接受向量作为参数的。
* 方法一:把log(hθ(x(1)))log(hθ(x(2)))log(hθ(x(3)))彻底展开就是
log(11+e(θ0+θ1x(1)1+θ2x(1)2))log(11+e(θ0+θ1x(2)1+θ2x(2)2))log(11+e(θ0+θ1x(3)1+θ2x(3)2))log(sigmoid(θ0+θ1x(1)1+θ2x(1)2))log(sigmoid(θ0+θ1x(2)1+θ2x(2)2))log(sigmoid(θ0+θ1x(3)1+θ2x(3)2)))
其实这就看出来了,就是向量θ0+θ1x(1)1+θ2x(1)2θ0+θ1x(2)1+θ2x(2)2θ0+θ1x(3)1+θ2x(3)2带入公式log(sigmoid())的结果。
* 方法二:因此向量 log(hθ(x(1)))log(hθ(x(2)))log(hθ(x(3)))又可以看成向量 x(1)x(2)x(3)代入log(sigmoid(θTx(i)))后运算的结果,也就是
log(hθ(x(1)))log(hθ(x(2)))log(hθ(x(3)))=log(sigmoid(θTx(1)))log(sigmoid(θTx(2)))log(sigmoid(θTx(3)))=log(sigmoid(θ0+θ1x(1)1+θ2x(1)2))log(sigmoid(θ0+θ1x(2)1+θ2x(2)2))log(sigmoid(θ0+θ1x(3)1+θ2x(3)2)))
也就相当于向量θ0+θ1x(1)1+θ2x(1)2θ0+θ1x(2)1+θ2x(2)2θ0+θ1x(3)1+θ2x(3)2带入公式log(sigmoid())的结果,
而向量θ0+θ1x(1)1+θ2x(1)2θ0+θ1x(2)1+θ2x(2)2θ0+θ1x(3)1+θ2x(3)2其实就是Xθ
因此log(hθ(x(1)))log(hθ(x(2)))log(hθ(x(3)))等价于log(sigmoid(Xθ))
由此我们可以得到y(1)log(hθ(x(1)))+y(2)log(hθ(x(2)))+y(3)log(hθ(x(3)))可以写为向量y 与向量 log(sigmoid(Xθ)) 的内积,也可以用matlab的向量点乘运算。
同理(1y(1))log(1hθ(x(1)))+(1y(2))log(1hθ(x(2)))+(1y(3))log(1hθ(x(3))可以看成(1y)这个向量(Matlab里面向量和标量加减相当于向量中的每个元素和标量进行加减运算)和 向量 log(1sigmoid(Xθ))的内积。于是用代码来表示可以是这样

J = -1 / m * (dot(y,log(sigmoid(X * theta))) + dot((1 - y ),log(1 - sigmoid(X * theta))));

另外我还发现不知道是效率高咋滴,大家都不喜欢用向量内积的方式,而是先用点乘的方式之后在用sum把一个向量里面的所有分量相加

J = -1 / m * sum (y .* log(sigmoid(X * theta)) + (1 - y ) .* log(1 - sigmoid(X * theta)));

2. 求梯度,其实梯度就是参数迭代公式中的偏导部分,为啥这个叫做梯度,其实这个是斜率,参数要按照一个角度下降,控制这个角度多大的就是这个梯度的大小。
参数收敛公式:θj:=θjαJ(θ)θj
其实这个公式没什么牛逼的地方,思想很简单就是把θ一点一点的减小,减小的方式是沿着对应变量的导数方向一点一点下降。偏导数部分叫做“梯度”(gradient),查了一下牛津高阶,梯度和坡度、斜率是一个词。明白了这点我们专心求公式。
我们依然用小数来具体化,设m = 3 ,n=2,j = 1,则
J(θ)θ1=13{(hθ(x(1))y(1))x1(1)+(hθ(x(2))y(2))x1(2)+(hθ(x(3))y(3))x1(3)}
这种多个乘积相加的形式应该本能的反映出这是向量点积于是,可以分成
hθ(x(1))y(1)hθ(x(2))y(2)hθ(x(3))y(3)x(1)1x(2)2x(3)3的内积,
x(1)1x(2)2x(3)3这个不就是向量x1吗?
hθ(x(1))y(1)hθ(x(2))y(2)hθ(x(3))y(3)可以拆成两个向量减法hθ(x(1))hθ(x(2))hθ(x(3))y(1)y(2)y(3),而y(1)y(2)y(3)这就是y啊,而hθ(x(1))hθ(x(2))hθ(x(3))根据上一点就是sigmoid(Xθ)
所以J(θ)θ1的偏导就等于13(sigmoid(Xθ)y)x1
于是全部参数的梯度一次性求出就是 1m((sigmoid(xθ)y)X)
看了一下答案,发现(sigmoid(Xθ)y)X的位置搞反了,你想嘛,(sigmoid(Xθ)y)是一个1 x 3 的列向量,X是一个 3 x 3的矩阵,首先就没法乘,所以位置调过来就对了犯这样的错误主要是推导的时候想当然了
θ0=13(sigmoid(Xθ)y)x0
θ1=13(sigmoid(Xθ)y)x1
θ2=13(sigmoid(Xθ)y)x2
所以

θ=1m((sigmoid(xθ)y)X)
但是其实不是,之所以弄错还是因为(sigmoid(xθ)y)太不直观,看起像1/3那样可以直接提到外面去,其实不是。(sigmoid(xθ)y)是一个向量,好吧那么我把这个再具体一下设(sigmoid(xθ)y)S=s1s2s3于是
θ=x0TSx1TSx2TS=Sx0TSx1TSx2T=s1x(1)0+s2x(2)0+s3x(3)0s1x(1)1+s2x(2)1+s3x(3)1s1x(1)2+s2x(2)2+s3x(3)2=x(1)0x(1)1x(1)2x(2)0x(2)1x(2)2x(3)0x(3)1x(3)2s1s2s3

x(1)0x(1)1x(1)2x(2)0x(2)1x(2)2x(3)0x(3)1x(3)2就是XT,所以θ=XTSθ=XT(sigmoid(Xθ)y)
(PS:这里为了方便把1/3略去了)
所以这个推导还不是很容易的。

grad = 1 / m * (X' * (sigmoid(X * theta)-y));

这两行代码用了两天,平均一天一行代码(囧)

1.2.3 Learning parameters using fminunc

这个不需要我们自己实现,需要注意的是,这个使用的套路是,我们实现一个函数,这个函数再作为参数传递给优化的函数

1.2.3.1 plotDecisionBoundary.m

1.2.3.1.1 contour函数的使用方法

这个分成两种情况,第一种情况是系数矩阵的列小于3,也就是特征只有两个的情况,特征如果只有两个的情况下,决策边界就是平面图形上的一条直线,画图的方法比较容易直接看程序吧。
当系数矩阵的列大于3的时候,画图利用的是轮廓图的方法。为了便于直观的思考,我们这里只考虑特征值有三个的情况,也就是决策边界图形是一个平面(x+y+z=0是一个平面)

要搞清contour()这个函数是怎么用的,就要首先搞清在Matlab里面三维图形是怎么画的
假设Matlab里面有一个三维绘制函数draw3D,那么根据二维图像的绘制方式这个函数只要接受三个参数就可以了,draw3D(x,y,z),第一个参数x是坐标在x轴方向上的分量构成的向量,第二个参数是坐标在y轴上的分量构成的向量,第三个参数z是坐标在z轴上的分量构成的向量。因为三维空间上的一个点就是(x,y,z),如果这三个分量都确定了,这个点就确认了,如果函数的参数是这样设置的,那么函数使用起来就特别直观,而实际上Matlab里面的3D绘图函数上是接收矩阵作为参数的,不知道为啥弄得那么复杂。在Matlab里最基础的绘制3D图形的函数叫做mesh,不叫draw3D。
以mesh()为例,它的参数有这样三种情况
1. 如果x,y,z是同型的二维矩阵,那么这三个矩阵中处于相同位置的值,构成了一个坐标点,比如x(1,1),y(1,1),z(1,1)构成了属于图形上的一个点,但是如果x(1,1),y(1,1),z(1,2),这三个值构成的坐标点虽然是一个点,但是这个点不在图形上。
2. 如果只接受一个参数,即mesh(z),那么z必须是一个二维的矩阵,那么在这种情况下,这个矩阵如何映射到图上的点呢?从直觉上我们能感觉x,y的坐标可能会跟矩阵的行方向索引以及列方向所以相关,最自然的感觉是对于Z矩阵中的某一个元素Z(i,j),Z(i,j)的值作为z轴方向的分量,行方向上的索引i作为x轴方向上的分量,列方向上的索引j作为y轴方向上的分量,即(i,j,Z(i,j))是图形上的一个点。实际情况是,z(i,j)作为z轴方向上的分量没问题,而x,y其实正好和想象中的相反,我们设Z是一个m x n 的矩阵,首先X方向的范围是 1-n,Y方向的范围是1-m ,而且对于矩阵Z中的任意一个元素z(i,j),行方向的索引i是坐标点在y轴方向上的分量,而列方向上的索引j是x轴方向上的索引,即(j,i,Z(i,j))是图形上的一个点。这种感觉用这张图表示比较形象。这里写图片描述
我们可以用这么一个矩阵做一个测试
zz =

8    8    98    8    97    7    77    7    6

利用mesh(ZZ)得到的为
这里写图片描述
3. 如果x是一个向量,y也是一个向量,z是一个矩阵,那么这两个向量和一个矩阵通过何种方式组合成一个图像上的点呢,如果理解了上一点,也就是只接受一个矩阵作为参数的情况,那么这中情况也比较好理解。在上一种情况中,咱们直接说结论吧,对于矩阵中的任意一个点Z(i,j)来说,它对应的坐标为(j , i , Z( i , j )),也就是拿矩阵的列方向索引为x坐标轴的分量,拿矩阵的行方向索引作为y坐标轴的分量。但是如果这里增加了X和Y向量的话,矩阵的行列方向的索引就不能直接作为坐标的分量了,而应该作为X向量和Y向量的索引,根据这个索引得到向量中的某一个值,这个值作为坐标里的一个分量。也就是多了一层映射。具体来说就是Z(i,j),用列方向索引j,在X向量找第j个分量(即X(j))作为坐标x轴方向上的分量;用行方向上的索引i在Y向量找第i个分量(即Y(i))作为坐标在y轴方向的的分量。总结一下,如果给了三个参数X向量,Y向量,Z矩阵,那么对于矩阵中的任何一个点Z(i,j),( X(j) , Y(i) , Z( i , j ) )是图形上的一个点。注意正是由于这种关系,那么X向量的长度要和Z的列的数量一样,Y向量的长度要和Z的行的数量一样。
这个 x,y,z上存在这样的映射关系,length(x)=n,length(y)=m,z是一个m x n的二维矩阵(即 [m,n]=size(z)),那么(x[j],y[i],z[i,j])构成的点在图形上
这里写图片描述
mesh是以网格的方式来作图,surf就在网格上铺了一层表面。
了解了三维作图的方法,等高线就容易理解了,等高线的图形你可以理解为做出三维的图形之后,系统自动的给你截取等高线,所以contour和mesh使用参数的方法一样。
为什么这里用到contour函数,这是因为,对于多特征(超过2个)的分类问题,决策边界不再是一个直线而是一个立体,或者超维的立体,这个时候我们想在二维的平面上表现出决策边界只能在Z=0所在的平面上截出这个超维图像的边界。

1.2.3.1.2 meshgrid函数的使用方法

在实际的绘图过程中,我们一般按照什么步骤去绘图呢,类似于二维的图像绘制,最方便的方法是构造X矩阵,Y矩阵,之后将X、Y带入到Z = f(X,Y)的式子中去得到Z矩阵(注意这个式子一定要是向量兼容的形式)。之后把X,Y,Z带入到mesh(X,Y,Z)就可以了。
而构造X,Y矩阵最方便的方法是用meshgrid函数。meshgrid,有两种用法,第一种是[X,Y] = meshgrid(gv),第二种是[X,Y] = meshgrid(xgv,ygv)。
1. 先说第一种,[X,Y] = meshgrid(gv),它的作用是
* 把gv这个向量按行扩展成一个length(gv) x length(gv)的矩阵之后之后赋值给X
* 把X这个矩阵转置赋值给Y。
2. 再说第二种,[X,Y] = meshgrid(xgv,ygv),它的构造的方法是:
* 设 xgv和ygv都是行向量,且m = length(xgv),n = length(xgv)
* 生成的矩阵无论是X还是Y,都是 n x m型的矩阵
* if m>n : a 按行扩展到n行构成一个矩阵赋给X,b先转置成列向量按列扩展到m列构成一个矩阵赋值给Y
* if m

1.2.3.1.3 如果不用meshgrid函数,也就是采用mesh(xgv,ygv,ZZ)的方法

在这两次作业中的代码中(ex1.m以及plotDecisionBoundary.m),没有采用meshgrid的方法,而是采用这样的方法:
1. 分别生成两个X,Y向量,之后再生成一个length(X) x length(Y)的全零矩阵Z
2. 填充Z矩阵,填充的方法是:从顺序上是Z的第一行开始从左到右一行一行的填充,填充的内容对于Z(i,j)来说是用x(i),y(j)带入Z(x,y)来得到。用代码上来表示就是

for i = 1 : length(X)
for j = 1 : length(Y)
Z(i,j) = f(X(i) , Y(j))

3. 将Z矩阵转置(这步还是挺聪明的)
4. 讲X,Y,Z代入mesh(X,Y,Z)

第3步还是相当的聪明的

1.2.3.2 mapFeature.m

虽然绘图里用到这个函数,但是这个步骤还没用到,正则化部分里会用到,所以在下面展开说

1.2.4 Evaluating logistic regression

这部分的主要任务是完成predict.m这个程序,这个程序没有什么太多难的。
主要要注意的还是find的用法,比如调用sigmoid函数的结果向量为S,那么在S中找到值大于等于0.5的那些分量所在的位置,把它记录下来并且保存在一个向量中,pos = find(S>=0.5)
之后将P这个零向量中相应的位置赋值为1 p(pos) =1。我一开始惊讶于,原来还可以这么用,其实pos = find(S>=0.5),p(pos) =1 也是向量的运算,S>=0.5其实是对S中的每一个分量都执行这个操作。p(pos) = 1可以看成pos中的每一个分量i取出来带到p向量里面p(i) =1

Regularized logistic regression

2.1 Visualizing the data

这个没有要实现的,直接执行就行

2.2 Feature mapping

  1. 为什么要引入featuremapping这个函数,主要目的是由于决策边界不能由直线来表示,于是我们可以通过在现有的特征基础上进行组合的方法生成一些新的特征的方法来使我们的拟合曲线变成比较复杂的形态。具体来说就是两个特征x1,x2,我们可以通过对其多次的重复乘得到一个新的feature。比如我要构造一个6次方的特征,可以有x16,x15x2,x14x22,x13x23,x12x24,x1x25,x26 这7个组合,其实x16可以看成6个x1相乘,x15x2可以看成5个x1和一个x2相乘,到底有几种组合,这是一个排列组合的问题,这个不太擅长就先跳过去啦,有谁比较擅长可以教教我。如果从0次开始一直到6次的所有组合都加起来一共有28个。也就是如果我们有两个features,如果我们要映射到6次的时候,一共会构造出28个features。这个就是mapfeature的思想。
  2. 再说一下代码中技巧
    在代码中有一个end+1的用法,如果我们对一个矩阵A进行操作的时候,end代表A最后一行的索引数。
    可以参考这个文档

  3. 由于mapfeature构造了28个特征,那么theta也得相应的增加,也得有28个。

2.3 Cost function and gradient

总的来说如果是搞定了costFunction.m的内容的这个并不是很难,但是我也踏了一些坑,我先说一下自己的思路:
1. 计算cost
目标是写出公式J(θ)=1mmi=1[y(i)log(hθ(x(i)))(1y(i))log(1hθ(x(i)))]+λmj=1nθj2
在计算J(θ)的时候,分成两块,前一部分和costFunction.m里面的J一样,直接拿过来用,主要是计算后面的λ的部分。由于这里参与计算的是θ1θn的部分,matlab表示就是从theta(2)到theta(n)的部分。于是我就把theta(2)到theta(n)的部分拿出来变成一个新的向量,这里也用到向量切片的技巧。我用的方法是:

t = theta(2:size(theta,1));

其实有更加优雅的方法

t = theta(2:end)

于是后面的部分用代码表示就是:

lambda / ( 2 * m ) * sum(t .* t)

其实还有更优雅的表达方法

lambda / ( 2 * m ) * t' * t

这里我还踩了一个坑,因为θ要计算平方嘛,一开始我想的是不是有公式,我猜这个公式是不是pow这类的后来在系统的提示下还真找到一个pow2()的函数,我也没验证就直接用了,结果就怎么都算不对,后来一点一点定位,先定位到J不对,在定位到这个后面的小项不对,正常来说应该这项在第一次计算为0的,结果我计算出值来后来才发现这个问题。
ps:pow2()不是计算平方的公式是计算以2为底的指数的公式
2. 计算偏导数
这块我就是按照题目里的提示分成两部分做的,先计算θ0保存在一个变量g1里面。之后在计算剩下的部分,还是利用之前costFunction.m里面的推到的方法得到了向量的表达方法,将计算结果放到另一个向量g2里面,之后再把这两个向量合二为一赋值给grad,也用到向量的一些小技巧

grad = [g1 ; g2]

注意因为grad是列向量,所以g1和g2中间用得是分号。
3. 更加优雅的思路
这是我见过最聪明的方法了。因为其实在正则化的过程中,其实θ0其实不参与正则化,那么直接把θ向量中的θ0变成0就可以了。
那么偏导数这部分计算公式都可以不变
思路是这样的
J(θ)=1mmi=1[y(i)log(hθ(x(i)))(1y(i))log(1hθ(x(i)))]+λmnj=1θj2
注意观察这个公式,这个公式可以分成两个部分,前一部分和普通的代价函数计算一样,后一部分是参与正则化的参数。那么我们可以建立两个参数向量第一个参数向量包含从θ0θn的全部参数,这个向量值参与公式前一部分的计算;第二个参数向量是在第一个参数向量的基础上把θ0赋值为0后生成的向量,这个向量只参与公式后一部分的计算。
本质上,我们梯度下降的核心公式是这个:θj:=θjαJ(θ)θj
其中,

J(θ)θj={1mmi=1(hθ(x(i))y(i))xj(i),1mmi=1(hθ(x(i))y(i))xj(i)+λmθj,if j=0if j>=1

我们依然用一个小的数来具体化,设n=2,则参数有三个需要确定θ0,θ1,θ2
J(θ)θ0J(θ)θ1J(θ)θ2=1mmi=1(hθ(x(i))y(i))x0(i)1mmi=1(hθ(x(i))y(i))x1(i)+λmθ11mmi=1(hθ(x(i))y(i))x2(i)+λmθ2=1mmi=1(hθ(x(i))y(i))x0(i)1mmi=1(hθ(x(i))y(i))x1(i)1mmi=1(hθ(x(i))y(i))x2(i)+λm0θ1θ2
1mmi=1(hθ(x(i))y(i))x0(i)1mmi=1(hθ(x(i))y(i))x1(i)1mmi=1(hθ(x(i))y(i))x2(i)这个就是没有正则化条件下的结果,即XT(sigmoid(Xθ)y)0θ1θ2就是向量θθ0赋值为0而形成的向量。因此我们只要生成一个新的向量t = [0;theta(2:end)],不更改原来的式子而仅仅在在原来非正则化式子的基础上加上一个新的向量就可以了。

grad = ( X' * (sigmoid(X*theta) - y ) )/ m + lambda/m * theta_1 ;

我之前的思路就相当于把这个公式:
J(θ)θj={1mmi=1(hθ(x(i))y(i))xj(i),1mmi=1(hθ(x(i))y(i))xj(i)+λmθj,if j=0if j>=1

分开计算,在计算第一个公式的时候相当于用到了每个样本的第一个分量x0,而计算第二个公式的时候相当于用到了每个样本从x1xn的分量,如果从向量化的角度来看,就相当把样本矩阵分成两部分。
而其实通过上面的推导步骤可以发现其实上面的条件公式也是可以整合成一个公式的,只要弄出两个θ向量即可,相当于把参数向量分成两种来进行考虑,这种思路的代码非常工整。

原创粉丝点击