机器学习与Python应用(二)
机器学习——线性回归算法
本贴为学习记录贴,有任何问题随时可以交流。
课程名称:机器学习与数据挖掘
使用教材:《Python机器学习》/赵涓涓,强彦主编.——北京:机械工业出版社,2019.06
记录时间:2021.03.21
本节说明:主要讲解教材第三章的线性回归算法应用题目,重点关注sklearn的数据集使用方法以及数据集处理思路。
文章目录
一、简要介绍
本节是教材书《Python机器学习》第3章线性回归算法中算法应用的讲解。
代码环境说明:所有代码均在Jupyter Notebook中执行(IPython方便查看数据的情况)
环境查看方式可以参考机器学习与Python应用(一)的第一节内容。
系统:Windows 10
Anaconda – 4.9.2
jupyter notebook – 6.0.1
Python – 3.7.4
numpy – 1.16.5
pandas – 1.2.3
sklearn – 0.21.3
matplotlib – 3.3.4
seaborn – 0.9.0
二、库的导入和使用
机器学习常用的第三方库有:numpy
、pandas
、matplotlib
和sklearn
。
numpy
:常用于数组或矢量运算,具有非常高的性能pandas
:常用于数据处理,具有高级的数据结构matplotlib
:常用于绘图和数据可视化seaborn
:常用于绘图和数据可视化sklearn
:常用于机器学习
本节内容主要是线性回归,因此导入以下库:
# 导入第三方库
import numpy as np
import pandas as pd
from sklearn import datasets
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
三、数据集
本节算法中,使用的数据集是波士顿房价(Boston house prices dataset)。该数据集共有506条数据,每条数据里面共有属性13种(包括需要预测的房价)。
(一)数据集导入
不同类型的数据集可以用不同的方法的导入。
-
使用
numpy
导入
1、导入:导出成ndarray格式np.load(file_name.npy) # 将npy数据加载 np.loadtxt(file_name.txt) # 导入txt数据
2、导出
np.save(file_name, ndarray) # 将ndarray存储到file_name中并保存成npy格式 np.savez(file_name, *args, *kwargs) # 以字典形式保存为npz格式 np.savetxt(file_name, X_1or2D, delimiter=' ') # 保存成out或txt格式
-
使用
pandas
导入
1、导入:
pandas
可导入的数据类型十分多,如:hdf5、json、html、csv、Excel等。pd.read_文件类型名(file_path) --> DataFrame类型
2、导出:
pandas
同样可以导出hdf5、json、html、csv、Excel等数据DataFrame.to_filetype('file_name')
-
对于本节内容,导进来的数据集是
Boston_house prices dataset
可以从sklearn.datasets
中加载进来,原因是在下载sklearn相关的包时,这些数据集就随着包安装在电脑里。加载该数据时,输入以下命令:boston_dataset = datasets.load_boston()
(二)数据集分析
数据加载进来后,需要先查看数据,包括数据集的介绍、数据集的类型、数据集的结构和数据集的内容等。
-
数据集的介绍
print(boston_dataset.DESCR) # 用于查看数据集的内容
“Boston_house prices dataset” 有506条数据,数据中的有13种属性(有数值型数据和分类型数据)作为特征,第14种属性为target,即通常所说的标签(被解释变量)。
- CRIM per capita crime rate by town - ZN proportion of residential land zoned for lots over 25,000 sq.ft. - INDUS proportion of non-retail business acres per town - CHAS Charles River dummy variable (= 1 if tract bounds river; 0 otherwise) - NOX nitric oxides concentration (parts per 10 million) - RM average number of rooms per dwelling - AGE proportion of owner-occupied units built prior to 1940 - DIS weighted distances to five Boston employment centres - RAD index of accessibility to radial highways - TAX full-value property-tax rate per $10,000 - PTRATIO pupil-teacher ratio by town - B 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town - LSTAT % lower status of the population - MEDV Median value of owner-occupied homes in $1000'
-
数据集的类型
print(type(boston_dataset))
“Boston_house prices dataset” 导入后生成的是
Bunch
类型,是一种继承dict
的数据类型,可以通过keys()
和values()
去访问对应的值。 -
数据集的结构及内容
for key, value in boston_dataset.items(): print('key为:{}\n其value的类型为:{}'.format(key, type(value)))
Bunch
具有字典的属性,因此可以配合上面数据集的介绍以及使用keys()
来查看该数据集的各个部分内容。
“Boston_house prices dataset” 里面包含了data
,target
,feature_names
,DESCR
,filename
五个部分。key type(value) - data ndarray # 数据集的特征 - target ndarray # 数据集的标签 - feature_names ndarray # 数据集的特征名称 - DESCR str # 数据集的介绍(在上面已经使用过) - filename str # 数据集的存储位置(绝对路径)
(三)数据集提取与转换
- 数据集的每项内容并非都是需要的,一般地,转换成数据集时会选取数据特征
data
、数据标签target
以及数据特征的名称feature_name
作为数据处理的基础,而转换的数据类型一般是Series
或DataFrame
。# 将数据集的data转换成DataFrame类型,此时index和column是默认的id data = pd.DataFrame(boston_dataset.data) # 将data的column用数据集的feature_name替代,此时DF结构具有列索引名称 data.columns = boston_dataset.feature_names # 生成新的一列PRICE,其值为数据集的target data['PRICE'] = boston_dataset.target
四、数据预处理与变量选取
我们在使用模型前,需要确定我们的自变量和因变量。在自变量的选择中,我们需要进一步对数据集采集下来的数据进行筛选,这里面会涉及到“特征工程”。但在本节中的任务里面规定了RM(房间数)作为特征变量(自变量),PRICE(房价)作为目标变量(因变量),因而大部分的特征筛选工作就被简化了。
这一章节主要讲解数据预处理思路和DataFrame
、Series
、ndarray
在案例中的应用。
上一节机器学习与Python应用(一)已经对三者的转化有过说明。
(一)数据预处理
-
数据类型的确认
生活中我们经常会遇到各种类型的数据,既有分类型也有数值型,有结构化的也有非结构化的,不同的数据类型对模型方法选择也会有所影响。因此在我们拿到数据后,需要对每个特征的数据类型进行识别。data.head() # 查看前五行的数据情况
data.dtypes # 查看每一列的数据类型
本节案例中所有的变量都是
float64
类型,即都是数值型数据。 -
缺失数据的识别
我们所搜集的各种数据并不都是完整的,实际上都会面对各种特征存在数据缺失的情况,因此在变量选取前需要对数据进行缺失值识别和处理。data.describe() # 查看数据的描述性统计(通过count来观察对应列的缺失值)
从上面数据集的描述以及
describe()
可以知道,本案例给出的数据没有缺失值的。常用的缺失值处理方法是采用剔除或填充,其中Pandas有比较高效的缺失值处理方法,具体方法可以查阅《利用Python数据分析》的第5章。 -
数据集的划分
为了使我们的模型更有效,一般都不会选择把所有的数据投入到模型训练中,通常会将一个数据集划分成训练集train
和测试集test
,模型使用训练集来找到一组比较合适的模型参数,模型训练出来的参数会可以用在测试集上来反馈模型的精度。在上面我们对数据集提取后得到的data
,是一个完整的数据,即包括了自变量和因变量。本节案例中给出的代码实例是将所有的数据作为训练集。
(二)变量选取
-
特征变量与目标变量的相关性
各类我们所获取到的特征变量并非所有都对目标变量有关系,因此一方面需要基本常识和专业知识来对特征变量进行筛选,另一方面需要用数理上来描述变量直接的相关性,两个方面缺一不可。"""输出相关系数矩阵""" data_corr = data.corr(method='pearson') # 输出的是Pearson相关系数矩阵 print(data_corr)
"""画出相关系数矩阵的热力图""" plt.figure(figsize=(11, 9),dpi=100) sns.heatmap(data=data_corr, vmax=0.3, annot=True, # 图中数字文本显示 fmt=".2f", # 格式化输出图中数字,即保留小数位数等 annot_kws={ 'size':8,'weight':'normal'}, # 设置字号、磅值 )
在本案例中,只需要考察单个特征变量RM和目标变量PRICE,因此没有给出这段代码,该部分是拓展内容。
-
特征变量与目标变量的选取
在数据预处理完后,需要确定我们模型中的自变量和目标变量。这就需要我们对数据进行再筛选,以便于模型中的计算,这里就涉及到ndarray
、Series
、DataFrame
三种数据结构的使用。- 整张数据表是以
DataFrame
类型展现的二维数组,具有index
和Columns
两组索引。前者可以理解为行索引,用于检索每一条数据,后者可以理解为列索引,用于查看每列特征变量的数据。print(type(data)) # 查看数据表的类型 print(data.ndim) # 查看数据表的维度 print(data.shape) # 查看数据表的行列数(数据条数和属性数)
- 对数据表进行索引,分为下标索引和标签名索引。
- 1、直接按行列索引,本质上使用
dict
类型的索引方法,先索引外层key、再索引内层key。
注意:
这如果只是检索某单一列,返回的是Series
类型。
如果是检索多列,可以用list将列框住# 直接按行列索引进行检索 data['RM'] # 返回数据表RM那一列的数据,返回类型是Series print(type(data['RM'])) data['RM'][0] # 返回数据表RM那一列、index为0的那一行的数据 data[['RM','PRICE']] # 返回数据表RM和PRICE那一列的数据,返回类型是DataFrame
- 2、按下标索引,使用
df.iloc[index_id, column_id]
,按行下标、列下标对数据框进行索引。
data.iloc[:,:] # 返回所有行和列 data.iloc[1:10, :] # 返回第2行到第9行(注意:下标是从0开始,左闭右开区间) data.iloc[1:10, :5] # 返回第2到9行和第1到9列 data.iloc[1:10, [0, 5]] # 返回第2到9行,第1列和第6列
- 3、按标签名索引,使用
df.loc[index_name, column_name]
,按行标签名、列标签名进行索引。
# 变量选取 data.loc[0:10,'RM'] # 返回行标签名为0与行标签名为10之间,列标签为'RM'的数据 data.loc[:, 'RM':'PRICE'] # 返回列标签名为'RM'与'PRICE'之间的数据
- 1、直接按行列索引,本质上使用
- 整张数据表是以
-
自变量和因变量的确定
在变量筛选完好,用x和y分别表示自变量和因变量,但必须注意的是这里的x和y最好都是二维数组ndarray
类型,机器学习中的许多模型要求传入的都是二维数组。如果变量是其他类型数据(如:Series
和DataFrame
),需要先转成数组类型(如ndarray
),再进行维度更改,如使用ndarray.reshape(m,n)
。注意:
DataFrame
进行索引时,维度不发生改变时,依旧是DataFrame
类型;降维到一维度生成的是Series
类型;维度为零时生成的是基本数据类型(如int
,float
,str
等)# 下面的 data.loc[,] 返回的是Series类型,转成数组时,需要使用 .values # 教材上的代码.as_matrix()在Pandas的更新中已经被剔除了,现在常用的是.values()来构建数组 # 变量筛选和数组转变 x = data.loc[:, 'RM'].values # 自变量只有一个,生成的是一维的数组,类型为ndarray y = data.loc[:, 'PRICE'].values # 因变量只有一个, 也是一维的数组,类型为ndarray print(type(data.loc[:, 'RM'])) print(type(x)) print(x.shape) # 对x和y进行维度转变 x = np.array([x]).T # 先把每个元素变成独立一行:即(1,m);再将其转置,变成(m,1)独立一列的一维数组 y = np.array([y]).T print(x.shape) # 对x和y进行维度转变(第二种方法) x.reshape(-1,1) # 这里的(-1,1)表示的是列数设置为1,行数由数据总数除以列数决定 y.reshape(-1,1)
五、模型使用
(一)模型的调用与实例化
本节使用的是线性回归模型,按照教材的例子,我们可以直接调用sklearn
的LinearRegression
进行拟合。
查阅sklearn.linear_model.LinearRegression的官方文档:
LinearRegression(*, fit_intercept=True, normalize=False, copy_X=True, n_jobs=None, positive=False)
注意:LinearRegression()
实际上是生成了一个估计器(Estimator
),需要用fit()
进行模型拟合。
fit_intercept
: 是否计算截距项,默认值为Truenormalize
: 是否为标准化,默认值为False(如果输入的变量是已经标准化了,需要输入True)copy_X
: 是否复制训练集,默认值为Truen_jobs
: 电脑中用于计算的CPU核心作业数,默认值为None(为-1时表示启用所有核心)postive
: 参数是否强制为正数,默认值为False
# 模型调用,由于使用的的是小型数据集上述的参数按默认值来即可,因此直接调用模型
lr_model = LinearRegression() # LinearRegression模型实例化
print(type(lr_model)) # 返回的是<class 'sklearn.linear_model.base.LinearRegression'>
(二)模型的拟合、预测及精度
在面向对象编程中,类对象实例化后可以调用其方法和属性。lr_model
是LinearRegression
的实例化,可以调用其方法,如fit()
;其属性是在调用方法fit()
进行模型拟合后才会允许访问。
根据官方文档显示,该模块实际上是对
scipy.lingalg.lstsq
和scipy.optimize.nnls
进行了包装,具体内容可以参考上面链接的官方文档。
- 方法:
fit(X, y, sample_wight=None)
:sample_wight是样本权重,主要针对不平衡样本的处理,默认值为Noneget_params(deep=True)
:默认值为True,返回上面Estimator所带有的参数,predict(X)
:使用自变量X和拟合的模型对因变量进行预测,返回的是与X数量一致的一维数组。score(X, y, sample_wight=None)
:返回可决系数R方,用于评价模型的精度,y为实际的目标变量值。set_params(**kwargs)
:修改原来的Estimator的参数,并更新Estimator。
- 属性:
coef_
:返回回归模型拟合出来的参数,返回的类型为一维或二维数组,数组shape由自变量X的shape决定。rank_
:返回X的秩,仅在X为稠密矩阵时才可用。singular_
:返回X的奇异值,尽在X为稠密矩阵时才可用。intercept_
:返回回归模型拟合出来的截距项,通常是float
类型或与Y数量一致的一维数组。
# 模型拟合
lr_model.fit(x, y)
print(lr_model.coef_) # 获取模型的参数,X是二维(只有一个特征),参数对应也是二维的(只有一个参数)
print(lr_model.singular_) # 获取X对应矩阵的奇异值
print(lr_model.intercept_) # 获取模型的截距项,Y是一维的,返回的是float类型数据
print(lr_model.predict([[7]])) # 对包含一个房间数RM为7的二维数组进行预测房价PRICE
print(lr_model.score(x,y)) # 输入数据集的X产生预测值Y_p来和实际值Y求R^2,以此评价模型的精度
六、数据可视化
通常在使用数据时,先通过散点图、线图等来观察数据的分布情况,以此来找到适合的模型进行拟合,从而提高模型的精度。在拟合完模型后,可以通过可视化的形式来看数据的预测值与实际值的拟合程度。
本教材中是在最后一部分对数据进行可视化,使用
matplotlib
来绘制散点图和直线图。
# 画出散点图(X轴为特征变量值x,Y轴为目标变量值y)
# s为点的大小,c为颜色,xlabel为轴标签名,alpha为点的透明度
plt.scatter(x, y, s=10, alpha=0.3, c='red')
# 画出线图(X轴为特征变量值x,Y轴为模型预测值y)
# linewidth为线的长度,c为颜色
plt.plot(x, lr_model.predict(x), c='blue', linewidth='3')
# 设定X轴和Y轴的标签名
plt.xlabel("Number of Rooms")
plt.ylabel("House Price")
# 展示图形
plt.show()
七、模型拓展与参考资料
到此为止,教材的内容已经结束了。经过一轮简单线性回归模型应用,大概能了解机器学习模型使用的流程。但在从实际数据集使用到模型实现这一过程来看,该模型是十分简单的。
(一)模型的改进
- 特征变量
在本案例中,我们实际选用的特征变量只有一个,其他特征变量与目标变量也具有较高的相关性,变量选取仍需要进一步考虑,进而可以实现多元线性回归模型;同时仍需要多变量选取时需要考虑变量内部的相关性问题,进一步优化变量的选取,方法可以选择主成分分析(PCA)等。 - 数据集的划分
在本案例中,我们并未划分训练集和测试集,无法根据训练集反馈的模型精度进一步调整模型。在机器学习中,需要对数据集进行划分,实际中常用sklearn.model_selection.train_test_split()
进行划分,数据集不同的划分会对模型造成一定的影响,这方面是需要注意的。 - 回归模型优化
模型中主要采用的是普通最小二乘法,实际上可以考虑使用加权最小二乘法、偏最小二乘法、岭回归模型等 - 模型的选择
在散点图中,数据展示了较为明显的线性关系,但仍可以看到许多点并非在回归直线附近,除了考虑更多变量参与模型训练外,还需要对模型进一步考虑,如支持向量回归(SVR)、提升树(Boosting tree)、和随机森林(Random Forest)等。 - 模型的比较
- 为了找到更适合的模型和参数,可以使用同一组训练集对不同的模型进行训练,再用测试集比较模型的精度。
- 通常来说,模型可以分成可解释的模型和黑箱模型,前者具有较高的解释性但精度较低,后者具有较高的精度但缺乏可解释性。在对机器学习可解释性的研究中,有关文章提出一些"与模型无关的解释方法(Model-Agnostic Methods)"来对模型进行解释,同时能够使用该方法来比较不同类型模型的精度,如部分依赖图(PDP)、个体条件期望(ICE)、累积局部效应图(ALE)等。
(二)案例完整代码
# 导入第三方库
import pandas as pd
import numpy as np
from sklearn import datasets
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
import seaborn as sns
# 导入数据(看数据结构)
boston_dataset = datasets.load_boston()
print(boston_dataset.keys())
for key, value in boston_dataset.items():
print('key为:{}\n其value的类型为:{}'.format(key, type(value)))
# 查看数据集的描述
print(boston_dataset.DESCR)
print(type(boston_dataset))
# 显示数据集里的data中前五行内容
data = pd.DataFrame(boston_dataset.data)
data.columns = boston_dataset.feature_names
data['PRICE'] = boston_dataset.target
print(data.shape)
# 用于数据的描述性统计,可以用于查看缺失值
print(data.describe())
# 计算数据的相关系数
data_corr = data.corr(method='pearson') # 输出的是Pearson相关系数矩阵
# 根据相关系数矩阵画出热力图
plt.figure(figsize=(11, 9), dpi=100)
sns.heatmap(data=data_corr,
vmax=0.1, # 图例的最大值
annot=True, # 图中数字文本显示
fmt=".2f", # 格式化输出图中数字,即保留小数位数等
annot_kws={
'size': 8, 'weight': 'normal'}, # 数字属性设置,例如字号、磅值
)
print(type(data)) # 查看数据表的类型
print(data.ndim) # 查看数据表的维度
print(data.shape) # 查看数据表的行列数(数据条数和属性数)
# 变量选取
x = data.loc[:, 'RM'].values # 自变量只有一个,生成的是一维的数组,类型为ndarray
y = data.loc[:, 'PRICE'].values # 因变量只有一个, 也是一维的数组,类型为ndarray
print(type(data.loc[:, 'RM']))
print(type(x))
print(x.shape)
# # 对x和y进行维度转变
x = np.array([x]).T # 先把每个元素
y = np.array([y]).T
print(x.shape)
# 拟合LR模型
# 传入的x和y是(m,n)和(m,1)二维结构
# LinearRegression(是否计算截距项, 是否标准化, 是否复制训练集,作业数CPU,取正值)
lr_model = LinearRegression()
lr_model.fit(x, y)
# 画出散点图
# s为点的大小,c为颜色,linewidth为线的长度,xlabel为轴标签名,alpha为点透明的
plt.scatter(x, y, s=10, alpha=0.3, c='green')
plt.plot(x, lr_model.predict(x), c='blue', linewidth='1')
plt.xlabel("Number of Rooms")
plt.ylabel("House Price")
plt.show()
(三)参考资料
- 模型使用:
1、https://blog.csdn.net/qq_42946328/article/details/111559346
2、https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html#sklearn.linear_model.LinearRegression - 数据可视化:
3、https://zhuanlan.zhihu.com/p/170572450?utm_source=wechat_session - 模型的改进:
4、https://blog.csdn.net/weixin_41779359/article/details/88782343?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
5、https://christophm.github.io/interpretable-ml-book/ice.html
6、https://zhuanlan.zhihu.com/p/90217007