1.引言:
看了Stanford的Andrew Ng老师的机器学习公开课中关于Logistic Regression的讲解写下此篇学习笔记总结一下。
2.原理:
按照我们老师的话说,做机器学习无非是三个重点:取模型,选代价函数,找极值
取模型:
找一个合适的预测函数(Andrew Ng的公开课中称为hypothesis),一般表示为h函数,该函数就是我们选取的模型,它用来预测输入数据的判断结果。这个过程时非常关键的,需要对数据有一定的了解或分析,知道或者猜测预测函数的“大概”形式,比如是线性函数还是非线性函数。如果对于一组数据,我们找不到相应的模型来描述,那么我们无法进行下去。
线性模型就是预测的的形式,这种形式可以用矩阵表示,即Y=θX+C(其中θ,X都为矩阵)
非线性模型就是出现了x的多次方
选代价函数:代价函数就是能表示样本的预测值h和实际值y 差值得一个函数,
代价函数有cost函数和J函数两种说法
cost函数主要是单个样本的差值,J函数就是对所有样本的Cost进行求和或取平均值
因为我们说的代价是所有样本的,所以J函数才是我们的代价函数
找极值:显然,J(θ)函数的值越小表示预测函数越准确(即h函数越准确),所以这一步需要做的是找到能使J(θ)函数的最小的θ值。我们选取的算法几乎是梯度下降算法或者一些变换形式,可以说没有梯度下降就没有现在的深度学习,这是一个神奇的算法。但是缺陷在于只能找到极小值,这要求我们在J函数的选取是要注意J函数只有一个极小值(即要求J函数能收敛)
3.具体实现过程:
找预测函数:
假设我们来预测房价和面积的关系:
我们可以画出一条绿色的直线来描述这个关系,这台直线的函数式即为,这个即为我们的假设函数h,为了区别于实际的房价值,我们把我们通过h函数预测出来的y值称为h。
取代价函数:(m是样本数)
我们肯定希望找到的那条线,距离每个点都很近,最好所有的点上都在这条线上,但是一条直线去拟合所有的点都在这条直线上肯定不现实,所以我们希望这些点尽量离这条直线近一点。
所以我们会想到垂直距离,但是垂直距离的公式比较复杂,所谓我们选取了作为我们的cost函数,这在图形上的意义即是竖直距离,当cost函数越小时,这条直线离这个点越近,当cost=0时,点就在直线上。
然后我们再取所有样本的平均值(再除以2的原因是求导抵消了,这个就相当于常数c,),
那么我们的总误差即J函数为:
找极值:
首先我们要明确找的是什么,我们要找的即使J函数最小的一组θ值,在这个问题中,即为θ0,θ1.
所以我们的步骤为:
1.随机开始一组theta值,我们一般取0或1之类的
2.利用梯度下降算法改变theta的值直到一个合适的值(即迭代)
展开即为
首先来讲一下梯度下降算法(有一些博客也叫梯度上升,因为他们求导后把负号号移出去了):
不管我们位于这个什么位置,只要我们想走到极小点,就要往梯度方向(导数)的反方向走,假如我们一开始在左边的圆点处,那我们一点一点往右边滑下去呗,即为.
α为步长,这个控制我们每次移动的距离
我们重复这个过程(),就能够到达极小点附近(而不是最小点)
大多数同学会有疑问,如果越过了最低点怎么样:即使越过了最低点,我们对该点求导,发现该店的导数为正,减去即为往左边移动。而且一般α取得合理就不会越过,因为越到下面导数越小,就像2-1-(1/2)-(1/4)-(1/8)-......-(1/2^n)一样永远大于0
接下来讲下α的作用和怎么选取合适的α:
如左图所示,首先我们随机选取了θ1为靠近最小值的圆点,假如我们选取了一个较大的α,那么我们减去了一个比较大的值,导致我们第一次移动到了最小值的右边,我们求导发现该点的导数更大,这就导致了我们第二次移动到原来那个点的左边,然后该点的导数更大,这就导致了每一次迭代不是想更小的移动,而是像更大的移动,这样就到不了最小值了。
当然α也不是越小越好,太小了,移动的太慢了,迭代500次可能还没有算出来,一个合适的α迭代100后就很接近极小值了
我们一般选取α为0.01,但是对于不同的数据,不同的代价函数,这个值会不一样,我们可以通过30倍的缩放来改变这个值
为什么梯度下降算法要求J函数只有一个极小值:
对于以上的J函数,假如我们选取不同的初始值θ0和θ1,那么我们将会到达不同的极小值,最后我们怎么确定哪个最小呢,有多少个最小的呢,所以我们在选取J函数的时候就要确保J函数只有一个极小值,即下面这样的图形
最后就直接上代码:
import numpy as np
import pylab
import json
def compute_error(b,m,data):
"""计算代价函数"""
cost=0
x=data[0]
y=data[1]
x=np.array(x)
y=np.array(y)
num=float(len(data))
cost=(m*x+b-y)**2
cost=np.sum(cost,axis=0)
cost=cost/(2*num)
return cost
def optimizer(data,starting_b,starting_m,learning_rate,num_iter):
"""梯度下降算法"""
b=starting_b
m=starting_m
for i in range(num_iter):
b,m=compute_gradient(b,m,data,learning_rate)
if i%100==0:
print(str(i)+" error:"+str(compute_error(b,m,data)))
plot_data(data,b,m)
return [b,m]
def compute_gradient(b_current,m_current,data,learning_rate):
"""每一次的迭代"""
b_gradient = 0
m_gradient = 0
num=float(len(data))
# x=data[:,0]
# y=data[:,1]
x=data[0]
y=data[1]
x=np.array(x)
y=np.array(y)
b_gradient=(m_current*x+b_current-y)*(1/num)#先前无法收敛在于偏导b和m的偏导写反了
b_gradient=np.sum(b_gradient,axis=0)
m_gradient=(m_current*x+b_current-y)*x*(1/num)
m_gradient=np.sum(m_gradient,axis=0)
m_new=m_current-(learning_rate*m_gradient)
b_new=b_current-(learning_rate*b_gradient)
return [b_new,m_new]
def plot_data(data,b,m):
""""画曲线"""
x=data[0]
y=data[1]
x=np.array(x)
y=np.array(y)
y_predict = m*x+b
pylab.plot(x,y,'o')#原来的元素
pylab.plot(x,y_predict,'k-')#预测的元素
pylab.show()
def linear_regression():
starting_b=0.0
starting_m=0.0
learning_rate=0.00001#学习步数太小拟合慢,太大就会不能convergence
#数据的大小会决定α,这次数据扩大了100倍,使用α要缩小100倍,标准化也行
num_iter=500
x=[63.030301,25.60160963,84.07966543,33.21034955,80.36386821,83.07007084,73.82586473,91.57007003,10.92153202,84.7031311,24.39083948,61.10565706,56.9543926,87.40964666,71.43630822,66.23807235,12.34436146,93.93842171,14.53165794,78.2440026]
y=[63.87392259,26.29752231,85.14718359,34.06340079,81.33633071,83.85031798,75.08598548,92.85616524,12.43963086,85.56767789,25.40299684,62.18816348,57.7284949,88.24736608,72.85552885,66.93011168,13.14443623,95.47745258,15.50435747,79.81954144]
data=[x,y]
print("first error:"+str(compute_error(starting_b,starting_m,data)))
[b,m]=optimizer(data,starting_b,starting_m,learning_rate,num_iter)
print("last error:"+str(compute_error(b,m,data)))
print(str(m))
print(str(b))
plot_data(data,b,m)
linear_regression()