声明:本节内容并非自己原创,看过一些书籍和视频整理所得,后面会将引用到的文献写出来
一、原理
线性回归算法是机器学习的基础,蕴含着机器学习中的一些重要的基本思想,很多机器学习的算法都是从这些基础算法演变而来,如果函数曲线是一条直线,那被称为线性回归,如果曲线是一条二次曲线,就被称为二次回归。线性回归可分为单变量线性回归和多变量线性回归,这里我们分开讲一下:
二、单变量线性回归
先将公式推导的过程展示出来,这里公式推导直接是多变量的,单变量也类似,只是特征个数更少,最后结合具体的实例进行代码的实现:
单变量线性方程:
我们的目的是求出一条直线使得所有的点到这条直线对应点的距离之和最小,也就是我们的损失函数:
关于如何求解这个方程,这里我们使用梯度下降算法:梯度下降算法是说从某个起始theta对值开始,寻找最快的下降方向往下走,在路途上也不断地寻找最快下降的方向更改路线,最后到达一个局部最低点。
首先我们将上式写成矩阵的形式:
即
这样我们将式子代入求解得:
最后我们的梯度下降函数就是:
接下来我们用代码实现一下,举一个例子:如果你要开一个饭店,想根据人口数量预测一下利润,那我们就可以根据已有的数据利用线性回归预测一下。
这里我使用的是jupyter编写的python代码实现的,首先是导入需要的包以及数据集(需要的在下方留言)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
data=pd.read_csv('data1.txt',names=['population','profit'])
data.head()
我们取数测试一下
我们使用matplotlib下的一个绘图的包
data.plot.scatter('population','profit',label='population')
plt.show()
我们在第一行开始插入一列常数1:
data.insert(0,'ones',1)
data.head()
接下来我们进行切分,将人口数量和利润切开
X=data.iloc[:,0:-1]#第一列到倒数第二列的所有行
X.head()
y=data.iloc[:,-1]#最后一列
y.head()
切片出来的数据是DataFrame格式需要转换成数组格式。转换的方法是:使用df.values(比较简单)
X=X.values
X.shape#获取转换后数组的维度
接下来我们定义一下损失函数:
def costFunction(X,y,theta):
inner=np.power(X @ theta-y,2)
return np.sum(inner)/(2 * len(X))
然后初始化一下theta:定义为(2,1)维接下来定义梯度下降算法:
def gradientDescent(X,y,theta,alpha,iters):
costs=[]
for i in range(iters):
theta=theta-(X.T @ (X@theta-y))*alpha/len(X)
cost=costFunction(X,y,theta)
costs.append(cost)
if i%100 ==0:
print(cost)
return theta,costs
初始化一下参数,iters定义的是迭代次数为2000次,每100次输出损失函数
alpha=0.02
iters=2000
theta,costs=gradientDescent(X,y,theta,alpha,iters)
可视化损失函数:
fig,ax=plt.subplots()#fig是图像,ax是一个实例
ax.plot(np.arange(iters),costs)
ax.set(xlabel='iters',ylabel='cost',title='cost vs iters')
plt.show()
拟合函数可视化
x=np.linspace(y.min(),y.max(),100)#定义横坐标的最大最小值以及包含的个数
y_=theta[0,0]+theta[1,0]*x#拟合曲线
fig,ax=plt.subplots()
ax.scatter(X[:,1],y,label='training data')
ax.plot(x,y_,'r',label='predict')
ax.legend()#初始化一下图标
ax.set(xlabel='population',ylabel='profit')
plt.show()
三、多变量线性回归
多变量的线性回归的结果并不能像单变量那么方便的展示在二维平面,这里我们没有进行拟合函数的可视化操作,只是对比了损失函数和迭代次数之间的关系,首先我们先来看一下数据集,假设你现在要卖房子,你想看一下房子能卖多少钱,房子的价格和面积、卧室数目之间的关系我们已经有了:第一列是房子的面积,第二列是卧室的数目,第三列是房子的价格。
接下来我们进行数据的预处理:特征归一化,之所以要特征归一化是消除特征值之间的量纲影响,各特征值处于同一数量级,还可以提升模型的收敛速度和精度。
特征归一化的两种方法:
2、Min-Max normalization:
量化后的特征服从[0,1]
代码实现:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
data=pd.read_csv('data2.txt',names=['size','bedrooms','price'])
def normalize_feature(data):#特征归一化
return (data-data.mean())/data.std()
data=normalize_feature(data)
data.head()
然后我们作分离,前三列为x,最后一列为y
X=data.iloc[:,0:-1]
y=data.iloc[:,-1]
可以使用.head查看一下,这里就不演示了。
接着将dataFrame转换成数组
X=X.values
y=y.values
y=y.reshape(47,1)
损失函数不变:
def costFunction(X,y,theta):#X,y,theta并不是一个数,而是一个矩阵
inner=np.power(X @ theta-y,2)
return np.sum(inner)/(2 * len(X))
theta=np.zeros((3,1))#theta是一个3x1的矩阵
梯度下降算法做了部分改变:
def gradientDescent(X,y,theta,alpha,iters,isprint=False):
costs=[]
for i in range(iters):
theta=theta-(X.T @ (X@theta-y))*alpha/len(X)
cost=costFunction(X,y,theta)
costs.append(cost)
if i%100 ==0:
if isprint:
print(cost)
return theta,costs
我们设置它的alpha学习率和迭代次数:
candinate_alpha=[0.0003,0.003,0.03,0.0001,0.001,0.01]
iters=2000
接下来我们输出一下:
fig,ax=plt.subplots()
for alpha in candinate_alpha:
_,costs=gradientDescent(X,y,theta,alpha,iters)
ax.plot(np.arange(iters),costs,label=alpha)
ax.legend()
ax.set(xlabel='iters',ylabel='cost',title='cost vs iters')
plt.show()
这就是theta取值在不同迭代次数下的损失函数的变化。
最后我们总结一下梯度下降算法和正规方程之间的关系(这是我从网上截取的一张图片):
四、岭回归
岭回归是一种专门用于共线性数据分析的有偏估计回归方法,实质上是一种改良的最小二乘法,通过放弃最小二乘法的无偏性,以损失部分信息、降低精度为代价获得回归系数更为符合实际,更可靠的回归方法,对病态数据的拟合要强于线性回归。具有L2正则化的线性最小二乘法(降低高维的权重)。
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression,SGDRegressor,Ridge
from sklearn.metrics import mean_squared_error
def linear():
"""
线性回归预测房价
"""
#获取数据集
lb = load_boston()
print(lb.target)
#分割数据集
x_train,x_test,y_train,y_test = train_test_split(lb.data,lb.target,test_size=0.25)
#特征标准化(特征值与目标值都需要标准化,实例化两个标准化API)
std_x = StandardScaler()
x_train = std_x.fit_transform(x_train)
x_test = std_x.transform(x_test)
std_y = StandardScaler()
y_train = std_y.fit_transform(y_train.reshape(-1,1))#0.19版本之后要求数据必须是二维的
y_test = std_y.transform(y_test.reshape(-1,1))
#estimator预测
#正规方程求解方式预测结果
lr = LinearRegression()
lr.fit(x_train,y_train)
# print(lr.coef_)
#预测房子价格
y_predict = std_y.inverse_transform(lr.predict(x_test))
# print("房价预测为:",y_predict)
# print(std_y.inverse_transform(y_test))
#使用梯度下降算法进行预测
sgd = SGDRegressor()
sgd.fit(x_train,y_train)
#预测房子价格
sgd_y_predict = std_y.inverse_transform(sgd.predict(x_test))
print("房价预测为:",sgd_y_predict)
# print(std_y.inverse_transform(y_test))
#使用岭回归进行预测分析
rd = Ridge(alpha=1.0)
rd.fit(x_train,y_train)
rd_y_predict=std_y.inverse_transform(rd.predict(x_test))
#使用均方误差计算两种预测方法的好坏
print("使用正规方程计算的误差:",mean_squared_error(std_y.inverse_transform(y_test),y_predict))
print("使用梯度下降算法计算的误差:",mean_squared_error(std_y.inverse_transform(y_test),sgd_y_predict))
print("使用岭回归计算的误差:", mean_squared_error(std_y.inverse_transform(y_test), rd_y_predict))
return None
if __name__ == "__main__":
linear()
由于数据集太小的原因,从上面的结果可能看不出梯度下降算法和岭回归算法的优势,不过相对于大数据的应用我们更常用后两种算法,正规方程计算用于出现过拟合现象。
这里我写出来我参考的一些资料:
周志华,《机器学习》
李航,《统计学习方法》
https://www.cnblogs.com/hiyoung/archive/2018/10/09/9763599.html
https://blog.csdn.net/qq_36829091/article/details/79918968
还有b站的一些视频讲解(主推up主:ladykaka007)