机器学习实战-利用梯度下降实现单变量线性回归
文章目录
前言
线性回归是一种统计学上的分析方法,用于确定两种或多种变量之间是否存在线性关系,模型的目标是找到自变量和因变量之间的最佳线性关系,以便能够用自变量的值来预测因变量的值。
这是一个机器学习单变量线性回归的一个练习,数据集是一个csv文件,有两列,一列是人口一列是收益,接下来便通过线性回归,用自变量的值来预测因变量的值。
一、导包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
二、处理异常并导入数据集
1.处理异常
在 Matplotlib 中,中文字符和负号默认可能无法正常显示,这通常是因为 Matplotlib 的默认字体不支持中文,并且可能在处理负号时存在编码问题。因此,通常需要设置一些参数来确保中文和负号能够正确显示。
plt.rcParams['font.sans-serif']=['SimHei']#用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False#用来正常显示负号
第一行代码设置了 Matplotlib 中用于无衬线字体的默认字体为 ‘SimHei’。‘SimHei’ 是一种支持中文的字体,通过将其设置为默认字体,可以确保在图表中显示中文时不会出现乱码或缺失字符的问题。
第二行代码解决了负号显示的问题。在 Matplotlib 中,如果直接使用默认的字体设置,负号’-'可能会显示为方块或其他不正确的字符。通过将 axes.unicode_minus 设置为 False,Matplotlib 将使用传统的负号而不是试图从 Unicode 字体中查找它,从而避免了显示问题。
2.导入数据集
代码如下(示例):
path='D:\学习资料\机器学习\单变量线性回归\regress_data1.csv'
data=pd.read_csv(path)
把path设置成你自己电脑上的regress_data1.csv存储路径,如果报错OSError: [Errno] Invalid argument,说明是在转义的时候出现了问题,编译器把这个’'当成了转义字符。
此时的三种解决方案:
# 方法一:使用原始字符串:在字符串前加上 r 来告诉Python不要处理反斜杠作为转义字符。
path=r'D:\学习资料\机器学习\单变量线性回归\regress_data1.csv'
# 方法二:使用双反斜杠:用两个反斜杠 \\ 来代替一个,这样第一个反斜杠会转义第二个反斜杠,从而得到一个实际的反斜杠字符。
path='D:\\学习资料\\机器学习\\单变量线性回归\\regress_data1.csv'
# 方法三:使用正斜杠:在Python中,对于Windows路径,也可以使用正斜杠 / 而不是反斜杠 \。
path='D:/学习资料/机器学习/单变量线性回归/regress_data1.csv'
3.查看是否导入成功
print(data.head())
会得到:
data.describe() # 查看各列的描述性信息
会得到:
其中,调用这个describe()方法时,它会返回关于数值列的一些统计信息,包括计数(count)、均值(mean)、标准差(std)、最小值(min)、25% 分位数(25%)、中位数(50% 或 median)、75% 分位数(75%)和最大值(max)。
4.绘制人口-收益图,可视化查看
data.plot(kind='scatter',x='人口',y='收益',figsize=(12,8)) # figsize=(),将画出的图片的大小设置为12英寸*8英寸
plt.xlabel('人口',fontsize=18)
plt.ylabel('收益',rotation=0,fontsize=18) # rotation改善可读性,决定了标签文本相对于水平线的旋转角度,0是水平,90则是垂直,也可以45,-45等
plt.show()
会得到:
三、使用梯度下降来实现线性回归,以最小化代价函数
1.定义方法计算代价函数
首先呢,X是一个特征矩阵,通常是一个二维数组,每一行代表一个样本,每一列代表一个特征;y是真实标签值,通常是一个一维数组,每个元素对应x中的一个样本的真实输出;w是权重向量,一维的,长度与X的列数(即特征数)相同。
其次呢,w.T是w这个行向量的转秩,就会变成列向量,再与矩阵X相乘,就是m行n列的矩阵与一个n行m列的矩阵相乘,得到的是一个m行1列的列向量。
再其次,(Xw.T)-是算其与真实值的误差,再用power(((Xw.T)-y),2)把误差平方,保证是一个正数。
最后呢,返回的是平均平方误差,即线性回归模型在当前权重w下的代价。X.shape()会返回一个二元组(m,n),即说明X的行数是m,列数是n,这里用的X.shape[0],就是X的行数。
def computeCost(X,y,w):
inner=np.power(((X*w.T)-y),2) # 算与真实值之间的误差再平方 (m,n) * (n, 1) -> (m, 1)
return np.sum(inner)/(2*X.shape[0]) # X.shape[0]是X的行数,最后返回的是平均平方误差,即线性回归模型在当前权重w下的代价
2.添加偏置项
加上一行偏置项,当我们将数据表示为矩阵形式时,偏置项可以方便地作为一个额外的列添加到特征矩阵(X)中,其中这一列的所有值都是1。这样,我们就可以通过矩阵乘法来计算预测值。
通过添加偏置项,我们可以确保模型能够拟合数据的任何固定偏移,而不仅仅是特征之间的线性关系。这对于许多实际问题都是非常重要的,因为数据中通常包含一个不依赖于任何特征的固定部分,也就是线性方程y=k*x+b中的b。
data.insert(0,'ones',1)
print(data) # 输出一下data,看看是否正确插入了
会得到:
3.分离特征和目标变量(变量初始化)
接下来要对变量进行初始化,要把X设置为训练集数据,y设置为目标变量。
iloc函数是基于整数位置的索引,它接受行和列的整数位置,通常有两个参数:第一个参数用于选择行,第二个参数用于选择列。
前面说过shape会返回一个元组,所以shape[1]就是data的列数。
通常情况下,我们把除了最后一列之外的所有列视为特征,也就是赋值给X;将最后一列视为目标变量或标签,也就是y。
# set X(traindata) and y (target variable)
cols=data.shape[1] # data的列数存入cols
X=data.iloc[:,:cols-1] # 第一个冒号代表所有行,第二个代表从第一列到倒数第二列
y=data.iloc[:,cols-1:] # 第一个冒号代表所有行,第二个代表选取最后一列
# 经过这样处理后特征(X),目标变量(y)
接下来简单查看一下,看看是否分离特征成功。
print(X.head())
会得到:
print(y.head())
会得到:
接下来使用numpy中的matrix函数把X、y、w,全部转化为矩阵,并查看他们的形状是否正确符合要求。注意:这里的np.array([0,0]),相当于就是一个一维数组,只有两个值都是0。
X=np.matrix(X.values)
y=np.matrix(y.values)
w=np.matrix(np.array([0,0])) # 这里的意思是,一个矩阵,一行二列,都是0
print(X.shape,w.shape,y.shape)
会得到:
这个其实很对,这个数据集有97行3列,X选的是除了最后一列前面所有,所有共有两列;而w是只有两个元素的一维数组,也是对的;y选的是最后一列,也没问题。并且矩阵X乘上矩阵w的转秩矩阵,即(97,2)*(2,1)得到(97,1),刚好与上面结果相符合,所以是正确的。
4.使用批量梯度下降来计算结果
我们首先需要按照上面的公式定义一个批量梯度下降函数,如下:
def batch_gradientDescent(X,y,w,alpha,iters):
temp=np.matrix(np.zeros(w.shape)) # 用于存储每次迭代更新后的权重
parameters=int(w.ravel().shape[1]) # 获取权重向量的参数个数即特征的数量,ravel是将多维转成一维的,shape[1]返回列数,也就是特征数
cost=np.zeros(iters) # 用于存储每次迭代的代价(损失)
for i in range(iters):
error=(X*w.T)-y # 计算误差值
for j in range(parameters):
term=np.multiply(error,X[:,j]) # 对于每个特征,计算误差与特征值的乘积
temp[0,j]=w[0,j]-((alpha/len(X))*np.sum(term)) # 更新权重
w=temp # 临时权重赋值给权重向量
cost[i]=computeCost(X,y,w)
return w,cost
在这个函数中,w是权重向量,梯度下降的过程中会更新;alpha是学习速率,控制每次更新权重的步长;iters是迭代次数,即每次下降算法需要的运行次数。而temp是个临时权重,先构建一个与w的形状相同的全是0的矩阵temp,并把特征数赋值给parameters,用cost存储每次迭代的损失值。在迭代过程中,不断计算每次迭代的误差值,更新权重。
这里返回的权重w的第一个元素就是截距项,即k*x+b中的b,返回的第二个元素是斜率,即k
5.代入拟合的参数计算训练模型的误差
alpha=0.01 # 学习速率
iters=1000 # 迭代次数
g,cost=batch_gradientDescent(X,y,w,alpha,iters)
print(g)
print(computeCost(X,y,g)) # 输出训练模型的代价函数
会得到:
我们也可以根据需要,改变学习速率和迭代次数,以此来达到自己预期的效果。
6.可视化输出结果
(1)线性回归散点图(线性回归拟合图)
通过如下代码画出线性回归散点图/拟合图。其中,f相当于y=k*x+b中的y,g[0,0]是b,g[0,1]是k。
x=np.linspace(data['人口'].min(),data['人口'].max(),100) # x值是从人口的min到人口的max,均匀选出100个点
f=g[0,0]+(g[0,1]*x) # g是一行二列的w,g[0,0]是第一个元素,即截距项,g[0,1]是对应于x的斜率,f是预测值
fig,ax=plt.subplots(figsize=(12,8))
ax.plot(x,f,'r',label='预测值')
ax.scatter(data['人口'],data['收益'],label='训练数据')
ax.legend(loc=2) # 添加图例,并设置其位置为2(通常表示图例位于左上角),1234分别对应第一二三四象限,可以根据需要把图例放在想要的位置
ax.set_xlabel('人口',fontsize=18)
ax.set_ylabel('收益',rotation=0,fontsize=18)
ax.set_title('预测收益和人口规模',fontsize=18)
plt.show()
会得到:
在这个图中:
- 红色的线是通过线性回归模型得到的预测值,它表示了“人口”与“收益”之间的线性关系。
- 散点图表示了实际的数据点,即每一个点都代表了一个实际的人口规模和对应的收益。
- 图例(legend)标明了哪些内容是预测值,哪些是训练数据。
(2)代价随迭代次数变化图(损失函数随迭代次数变化图)
用以下代码可以绘制代价随迭代次数变化图(损失函数随迭代次数变化图),可以很直观的看到代价随迭代次数变化的规律
fig,ax=plt.subplots(figsize=(12,8)) # subplot和plot的主要区别在于subplot用于在同一画布上创建多个绘图区域,而plot则用于在单个绘图区域中绘制图形。
ax.plot(np.arange(iters),cost,'r') # np.arange(iters)生成了一个从0到iters-1的整数数组,这些整数代表了迭代的次数;arange生成等差数列
ax.set_xlabel('迭代次数',fontsize=18)
ax.set_ylabel('代价',rotation=0,fontsize=18)
ax.set_title('误差和训练Epoch数',fontsize=18)
plt.show()
会得到:
总结
本文用批量梯度下降对单变量线性回归进行了拟合,拟合结果也还不错。如果有哪里没有讲清楚的,欢迎您在评论区留言探讨;如果您感觉本文讲述的还比较清晰,希望您能给本文点个赞,谢谢!
标签:迭代,手把手,np,shape,梯度,线性,ax,data From: https://blog.csdn.net/m0_74277350/article/details/137171876