常见线性回归|理论与算法实现

01 分类 v.s. 回归

之前我们学习了很多分类方法,在机器学习中,还有一种任务叫回归,回归和分类其实挺像的,都是对样本预测一个值,区别在于,

  • 分类:输出为离散值
  • 回归:输出为连续值

今天我们学习一波线性回归的理论和算法,不要小看线性回归,其实很多商业模型都少不了线性回归的功劳,把线性回归用到极致你也是大神。

简单来说,线性回归就是在已知x,y的情况下,求解y=wx的回归系数的过程。


02 标准线性回归

标准线性回归就是最简单的线性回归,其原理是最小化预测值与真实值的均方误差,即最小二乘法。根据原理,得到系数公式:

标准线性回归简单粗暴,易于求解和解释,但其缺点也显而易见:对非线性样本,欠拟合。

实现代码如下:

#标准线性回归函数,假设数据有m个样本,n个特征
def standRegres(xArr,yArr):
    xMat=np.mat(xArr);yMat=np.mat(yArr).T #shape(yMat)=1*m
    xTx=xMat.T*xMat #shape=n*n
    if np.linalg.det(xTx)==0.0: #矩阵行列式若为0,说明该矩阵不可逆
        print ('xTx为奇异矩阵,不可逆')
        return
    ws=xTx.I*(xMat.T*yMat) #shape=n*1,n为特征数
    return ws

测试一波:

dataMat,labelMat=loadDataSet(r'D:\DM\python\data\MLiA_SourceCode\machinelearninginaction\Ch08\ex0.txt')
ws=standRegres(dataMat,labelMat)

print ('回归系数w为:',round(float(ws[0]),4),round(float(ws[1]),4))
print ('回归方程:y={1}x+{0}'.format(round(float(ws[0]),4),round(float(ws[1]),4)))

输出如下,

均方误差为1.355左右,

用于测试的数据集较简单,可以作图看看线性回归的预测效果:

在上图中我们可以看到,预测值非常“直”,虽然捕捉到了真实值的大趋势,但没有捕捉到真实数据的潜在特征(非线性),下面我们改进一下标准回归算法,尝试捕捉潜在特征。


03 局部加权线性回归

局部加权线性回归认为附近的点有相似的回归特性,因此对待预测点附近的点加权。

之前我写利用拉勾网的数据写了一个人才价格计算器(相关文章在这里),其原理是将分类算法KNN用于回归,即对样本分类后将同一类别的样本标签设置为该类别的均值,这个人才价格计算器的原理其实和今天这个局部加权线性回归很相似。(当然,缺点也很相似,笑~)

利用这样的想法,局部加权线性回归的回归系数公式如下:

其中,W为权重矩阵,

由权重矩阵公式可知,
1. W是对角矩阵
2. 样本点x距离预测点xi越近,权重越大
3. 对同一个样本点,k越大,权重矩阵W越大,赋予给每个数据点的权重越大,当k减小,W也减小,赋予给各点的权重减小
4. 权重最先减小到0的是距离预测点较远的样本点

下面我们实现此算法

#局部加权线性回归函数,假设数据有m个样本,n个特征
#针对某个测试点(testPoint),计算一个ws
"""
因为针对每个测试点,都有一个weight赋权矩阵,因此每个测试点都有一个ws回归系数矩阵,
因此对于每个测试点,都需要利用lwlr()得到一个测试点的预测值testPoint*ws
因此lwlr()返回testPoint*ws
"""
def lwlr(testPoint,xArr,yArr,k=1.0):
    xMat=np.mat(xArr);yMat=np.mat(yArr) #shape(yMat)=m*1
    m=np.shape(xMat)[0]
    weight=np.mat(np.eye(m)) #构造一个m*m的对角矩阵
    #针对每个样本点j,计算针对测试点i的weight矩阵Wj,j的值
    for j in range(m):
        diffMat=testPoint-xMat[j,:]
        weight[j,j]=np.exp(diffMat*diffMat.T/(-2.0*k**2))
    xTx=xMat.T*weight*xMat
    if np.linalg.det(xTx)==0.0: #矩阵行列式若为0,说明该矩阵不可逆
        print ('xTx为奇异矩阵,不可逆')
        return
    ws=xTx.I*(xMat.T*weight*yMat)
    return testPoint*ws

def lwlrTest(testArr,xArr,yArr,k=1.0):
    m=np.shape(testArr)[0]
    yPred=np.zeros(m)
    for i in range(m):
        yPred[i]=lwlr(testArr[i],xArr,yArr,k)
    return yPred

测试一下

dataMat,labelMat=loadDataSet(r'D:\DM\python\data\MLiA_SourceCode\machinelearninginaction\Ch08\ex0.txt')
yPred1=lwlrTest(dataMat,dataMat,np.mat(labelMat).T,1.0)
yPred2=lwlrTest(dataMat,dataMat,np.mat(labelMat).T,0.03)
yPred3=lwlrTest(dataMat,dataMat,np.mat(labelMat).T,0.003)
rssError(labelMat,yPred1),rssError(labelMat,yPred2),rssError(labelMat,yPred3)

可以看到,训练集的均方误差随着k的减小而减小,但k过小可能会过拟合(相当于KNN的k取很小的情况,预测点至于其附近几个点相关,就会过拟合)

缺点:类比KNN。对每个点做预测时,都必须用到整个数据集,计算量较大。


04 岭回归ridge

若特征数比样本数还多(n>m),即特征矩阵X非满秩,那么xTx不可逆,在用上述推导的公式求解w时会出问题,此时怎么办呢?

我们让xTx加上一个值:xTx+lambda*I,通过加上lambda*I使矩阵非奇异,从而求逆矩阵(xTx+lambda*I),这就是缩减的原理,通过引入lambda来限制回归系数w之和,通过引入lambda惩罚项,减少不重要的参数。

当约束条件如下时,回归就是岭回归:

岭回归的回归系数公式如下:

其中,
1. lambda是一个常数,可以通过k折交叉验证,寻找最小的均方误差,从而找到最佳的lambda
2. I是一个n*n的单位对角矩阵,n为特征数

实现一下,

#求岭回归ws函数
def ridgeRegres(xMat,yMat,lam=0.2):
    xTx=xMat.T*xMat
    denom=xTx+lam*np.eye(np.shape(xMat)[1]) #xTx+lambda*I
    if np.linalg.det(denom)==0.0:
        print ('xTx为奇异矩阵,不可逆')
        return
    ws=denom.I*xMat.T*yMat
    return ws

#z-score标准化函数
def regularize(xMat):
    reMat=xMat.copy()
    reMeans=np.mean(reMat,0) #按列求均值
    reVar=np.var(reMat,0) #按列求标准差
    reMat=(reMat-reMeans)/reVar
    return reMat

#遍历lambda,求各lambda对应的回归系数ws函数
def ridgeTest(xArr,yArr):
    xMat=np.mat(xArr);yMat=np.mat(yArr).T #shape(yMat)=m*1
    yMean=np.mean(yMat,0)
    yMat=yMat-yMean
    xMat=regularize(xMat) #对输入数据标准化
    numTestPts=30 #设定遍历30个lambda
    wMat=np.zeros((numTestPts,np.shape(xMat)[1]))
    for i in range(numTestPts):
        ws=ridgeRegres(xMat,yMat,np.exp(i-10)) #指数级别遍历lambda
        wMat[i,:]=ws.T #n*1>>1*n
    return wMat

测试,我们用一组鲍鱼的数据来预测鲍鱼年龄,图中8条曲线分别表示w0~w7在30个不同的lambda下的值。

从结果看,回归系数有8个w0-w7,在不同lambda下会得到不同的系数,随着lambda增大,到足够大时(缩减足够大),各参数减小至接近0,趋于相等,这就是缩减,缩减特征。


05 套索回归lasso+前向逐步回归

同上节,当约束条件如下时,回归就是套索回归:

可以看到,岭回归对平方函数求极值,可使用最小二乘法,但lasso回归对绝对值求极值,求解较复杂,怎么办呢?

这时我们可以使用前向分步算法逼近求解

前向分布算法是一种贪心算法,每一步都对某个权重增加或减少一个很小的值,从而在每一步尽可能减小误差。这样做虽然循环会变多,但每一次循环的计算都变得十分简单。

实现一下

def stageWise(xArr,yArr,step=0.01,numIt=100):
    xMat=np.mat(xArr);yMat=np.mat(yArr).T #shape(yMat)=m*1
    yMean=np.mean(yMat,0)
    yMat=yMat-yMean
    xMat=regularize(xMat) #对输入数据标准化
    m,n=np.shape(xMat)
    returnMat=np.zeros((numIt,n))
    ws=np.zeros((n,1));wsBest=ws.copy
    for i in range(numIt): #对每次迭代
        minErr=np.inf #将最小误差初始化为正无穷
        for j in range(n): #对每个特征
            for sign in [-1,1]: #对每个加/减
                wsTest=ws.copy()
                wsTest[j]+=step*sign #对第j个系数进行加/减
                yTest=xMat*wsTest
                rss=rssError(yMat.A,yTest.A)
                if rss<minErr:
                    minErr=rss
                    wsBest=wsTest
        ws=wsBest.copy() #找到本次迭代最小误差对应的系数矩阵
        returnMat[i,:]=ws.T
    return returnMat

测试,

可以看到,w0,w1,w4,w5,w6这四个系数在每次迭代中都保持在0附近,可以认为这几个系数不重要,可以剔除这几个维度,达到降维的作用(lasso回归的特性之一)


06 总结

本文总结了几种线性回归的原理,并用python3实现了这些回归。

常见的线性回归包括标准线性回归、局部加权线性回归、岭回归Ridge、套索回归Lasso、前向逐步回归(lasso回归的近似求解)。除了这些之外,还有弹性网络回归ElasticNet Regression,它综合了Ridge和Lasso的优点,惩罚项是L1范数和L2范数的融合。

下期我们再来看看树回归,这样回归板块的大部分知识就掌握了,敬请期待~


07 参考

  • 《机器学习实战》 Peter Harrington Chapter8
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容