手写算法-Python代码实现非线性回归

  • 生成非线性数据集
  • 1、用线性回归拟合
  • 2、多项式拟合
  • sklearn实现,校验系数的结果
  • 总结

生成非线性数据集

前面我们介绍了Python代码实现线性回归,今天,我们来聊一聊当数据呈现非线性时,这时我们继续用线性表达式去拟合,显然效果会很差,那我们该怎么处理?继续上实例(我们的代码里用到的数据集尽量直接由Python生成,因此,是可以全部跑通的,有需要的同学,建议大家粘贴复现一下,多思考,多动手,才可以学的更好。)

import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

#生成如下数据集
a= np.arange(1,11)
b=np.array([4500,5000,6000,8000,11000,15000,20000,30000,50000,100000])
data = np.c_[a,b]

#给x,y分别添加维度
x = data[:,0,np.newaxis]
y=data[:,1,np.newaxis]
plt.scatter(x,y)
plt.show()

1、用线性回归拟合

数据的分布如上图所示,这时候如果继续使用线性回归去拟合,这里继续使用上篇文章Python手写的类,和sklearn里面实现是一样的。
链接: 手写算法-Python代码推广多元线性回归

class normal():
    def __init__(self):
        pass

    def fit(self,x,y):
        m=x.shape[0]
        X = np.concatenate((np.ones((m,1)),x),axis=1)
        xMat=np.mat(X)
        yMat=yMat =np.mat(y.reshape(-1,1))

        xTx=xMat.T*xMat
        #xTx.I为xTx的逆矩阵
        ws=xTx.I*xMat.T*yMat
        return ws


model=normal()
w = model.fit(x,y)

#生成2个点画图
x_test=np.array([[1],[10]])
y_test = w[0] + x_test * w[1]


ax1= plt.subplot()
ax1.plot(x_test,y_test,c='r',label='线性回归拟合线')
ax1.scatter(x,y,c='b',label='真实分布')
ax1.legend()
plt.show()

可以看出拟合的效果很差,这里如果用

这种表达式去拟合应该会更好

2、多项式拟合

这里和大家介绍一个
#生成多项式
from sklearn.preprocessing import PolynomialFeatures
实现的功能是给X增加维度,具体的可以看官网或者看看我的这篇文章,里面有实现的原理解析,看一下明白了。
链接: 代码系列-python实现PolynomialFeatures(多项式)
我们继续用Python实现:

def multi_feature(x,n):
    c = np.empty((x.shape[0],0)) #np.empty((3,1))并不会生成一个3行1列的空数组,np.empty((3,0))才会生成3行1列空数组
    for i in range(n+1):
        h=x**i
        c=np.c_[c,h]
    return c

#先设置n=2
x_1 = multi_feature(x,2)
x_1   #输出变化后的x

model=normal()
#用新生成的x作为输入
w = model.fit(x_1,y)

报错了!!!
LinAlgError: Singular matrix
这个错误代表在对numpy的矩阵用np.linalg.inv方法时报错,也就是无法求逆矩阵,难道要屈服用sklearn吗?

不行!!

第一时间返回去看我们写的类,破案了!问题很简单,python写的类里面,我们给x添加了偏置,在这个生成的多项式x_1中,本身就有一列1,这样的话就有2列1,变成了一个非满秩矩阵,因此不需要再添加偏置,修改代码如下:

class normal():
    def __init__(self):
        pass

    def fit(self,x,y):
        #x_1中已经生成了一列1,不需要再加偏置,因此注释掉这2列。
        
        
        #m=x.shape[0]
        #X = np.concatenate((np.ones((m,1)),x),axis=1) 
        xMat=np.mat(x)
        yMat=yMat =np.mat(y.reshape(-1,1))

        xTx=xMat.T*xMat
        #xTx.I为xTx的逆矩阵
        ws=xTx.I*xMat.T*yMat
        return ws

model=normal()
#用新生成的x作为输入
w = model.fit(x_1,y)
w

这次没有问题了。(为了检查参数是否正确,我又马上调用了sklearn来跑了一遍,先透露结论:系数没错,后面上sklearn的结果)

画图看这次的拟合效果:

#计算x_1的拟合效果,下面是矩阵乘法
y_1 = np.dot(x_1,w) 

ax1= plt.subplot()
ax1.plot(x,y_1,c='r',label='n=2时,拟合效果图')
ax1.scatter(x,y,c='b',label='真实分布图')
ax1.legend(prop = {'size':15}) #此参数改变标签字号的大小
plt.show()

效果好很多了,设置n=5

#设置n=5
x_2 = multi_feature(x,5)


model=normal()
#用新生成的x_2作为输入,重新拟合
w = model.fit(x_2,y)


#计算x_2的拟合效果,下面是矩阵乘法
y_2 = np.dot(x_2,w) 
ax1= plt.subplot()
ax1.plot(x,y_2,c='r',label='n=5时,拟合效果图')
ax1.scatter(x,y,c='b',label='真实分布图')
ax1.legend(prop = {'size':15}) #此参数改变标签字号的大小
plt.show()

已经很完美了,由于点太少,显得拟合曲线不是那么平滑,多传入一些x值,

x_test = np.linspace(1,10,100)
x_3 = multi_feature(x_test,5)

#生成预测值y_3
y_3 = np.dot(x_3,w) 
ax1= plt.subplot()
ax1.plot(x_test,y_3,c='r',label='n=5时,拟合效果图')
ax1.scatter(x,y,c='b',label='真实分布图')
ax1.legend(prop = {'size':15}) #此参数改变标签字号的大小
plt.show()

sklearn实现,校验系数的结果

最后附上sklearn检验系数的结果,完全一样的:

from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(x_2, y)
print('截距为:',model.intercept_,'\n')
print('系数为:',model.coef_,'\n')
print(w)

总结

1、这篇文章主要告诉我们,做回归模型时,要了解数据,不能上来就调用sklearn里面的线性回归包,要根据数据的分布,选择合适的算法包,有时候还要对X特征进行一些预处理工作。

2、平时做机器学习还是调包,因为自己写的有很多不足的地方,且很难做到推广,但是,学习的过程中,还是建议多研究一下原理,这样可以走得更远。

3、大家有什么问题需要交流,我一定知无不言,期待一起进步。

本文地址:https://blog.csdn.net/weixin_44700798/article/details/110584147