(1)读入数据
(2)分析数据格式和确定使用的模型
(3)数据预处理
(4)使用所选模型进行测试并改进
(5)应用不同算法(模型)对比效果
(6)使用集成学习算法提升回归效果
(7)网格搜索调参数
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import time
# 模型预测
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso
from sklearn.tree import DecisionTreeRegressor
# 集成学习
from sklearn.ensemble import RandomForestRegressor
import xgboost as xgb
# 参数搜索
from sklearn.model_selection import GridSearchCV,cross_val_score,StratifiedKFold
# 评价指标
from sklearn.metrics import make_scorer
from sklearn.metrics import mean_squared_error, mean_absolute_error,accuracy_score
from sklearn.model_selection import learning_curve, validation_curve
warnings.filterwarnings("ignore")# 消除警告
# 初始化图形参数
plt.rcParams['figure.figsize'] = (16,9)# 设置大小
# 图形美化
plt.style.use('ggplot')
# 图例无法显示中文的解决方法:设置参数
plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
这里使用在阿里巴巴天池下载的二手车交易数据https://tianchi.aliyun.com/?spm=5176.12281973.J_9711814210.8.3dd53eafkBCu9m
# 读取数据,以空格划分
used_car = pd.read_csv(r'C:\Desktop\数据挖掘实践\大作业\used_car.csv', sep=' ')
# 输出数据大小
print('数据大小:',used_car.shape)
'''可以看到数据一共有150000条,31个属性'''
# 预览头10行数据
used_car.head(10)
'''
可以看到:
model、bodyType、fuelType、gearbox这几个属性有缺失值
'''
# 查看对应数据列名和是否存在NAN缺失信息
used_car.info()
可以看到:model、bodyType、fuelType、gearbox这几个属性有缺失值
print('各列缺失值统计结果为:')
print(used_car.isnull().sum())
# 查看数值特征列的统计信息
used_car.describe()
# 默认根据所有属性去除,keep设置保留第一条一样的数据
used_car.drop_duplicates(keep='first')
# 如果有多个众数的情况,用used_car.mode()[0]第一个众数填充
used_car['model'] = used_car['model'].fillna(used_car['model'].mode()[0])
used_car['bodyType'] = used_car['bodyType'].fillna(used_car['bodyType'].mode()[0])
used_car['fuelType'] = used_car['fuelType'].fillna(used_car['fuelType'].mode()[0])
used_car['gearbox'] = used_car['gearbox'].fillna(used_car['gearbox'].mode()[0])
used_car.info()
# 提取数值型属性名(exclude除去分类型)
numerical_cols = used_car.select_dtypes(exclude='object').columns
numerical_cols
# 提取分类型属性名(include包含分类型)
categorical_cols = used_car.select_dtypes(include='object').columns
categorical_cols
# 选择特征列名
feature_cols = [col for col in numerical_cols if col not in ['SaleID','name','regDate','creatDate','price','model','brand','regionCode','seller']]
feature_cols = [col for col in feature_cols if 'Type' not in col]
X = used_car[feature_cols]
y = used_car['price']
print('特征数据大小:',X.shape)
print('标签数据大小:',y.shape)
plt.hist(y)
可以看到标签主要分布再0-2000内
数据归一化在后面优化结果里
def modelKFold2MAE(X,y,model,k):
# k折交叉验证
train_scores = [] # 存放训练集精度
test_scores = [] # 存放测试集精度
# k折交叉验证(n_splits=k)
sk = StratifiedKFold(n_splits=k,shuffle=True,random_state=0)
for train_ind,test_ind in sk.split(X,y):
# 训练数据
X_train = X.iloc[train_ind].values
y_train = y.iloc[train_ind]
# 测试数据
X_test = X.iloc[test_ind].values
y_test = y.iloc[test_ind]
# 训练模型
model.fit(X_train,y_train)
# 预测
pred_train = model.predict(X_train)
pred_test = model.predict(X_test)
# 精度(使用平均错误率MAE)
train_score = mean_absolute_error(y_train,pred_train)
train_scores.append(train_score)
test_score = mean_absolute_error(y_test,pred_test)
test_scores.append(test_score)
print('训练集的平均MAE:',np.mean(train_scores))
print('测试集的平均MAE:',np.mean(test_scores))
return train_scores,test_scores
'''
可以看到计算出来的MAE很大
'''
lr = LinearRegression()
k = 5
train_scores,test_scores = modelKFold2MAE(X,y,lr,k)
lr = LinearRegression()
lr.fit(X,y)
# 绘制特征v_2的值与标签的散点图
subsample_index = np.random.randint(low=0, high=len(y), size=50)
plt.scatter(X['v_2'][subsample_index], y[subsample_index], color='black')
plt.scatter(X['v_2'][subsample_index], lr.predict(X.loc[subsample_index]), color='red')
plt.xlabel('v_2')
plt.ylabel('price')
plt.legend(['True Price','Predicted Price'],loc='best')
print('The predicted price is obvious different from true price')
plt.show()
发现模型的预测结果(红色点)与真实标签(黑色点)的分布差异较大
且出现了部分预测值小于0的情况
这说明该模型存在问题
print('It is clear to see the price shows a typical exponential distribution')
plt.figure(figsize=(15,5))
plt.subplot(1,2,1)
sns.distplot(y)
plt.subplot(1,2,2)
sns.distplot(y[y < np.quantile(y, 0.9)])
通过作图我们发现数据的标签(price)呈现长尾分布,不利于我们的建模预测
故对标签数据进行log(x+1)变换,使标签贴近于正态分布
# 对标签数据进行log(x+1)变换,使标签贴近于正态分布
y_ln = np.log(y+1)
lr = LinearRegression()
lr.fit(X,y_ln)
# 绘制特征v_2的值与标签的散点图
subsample_index = np.random.randint(low=0, high=len(y_ln), size=50)
plt.scatter(X['v_2'][subsample_index], y_ln[subsample_index], color='black')
plt.scatter(X['v_2'][subsample_index], lr.predict(X.loc[subsample_index]), color='red')
plt.xlabel('v_2')
plt.ylabel('price')
plt.legend(['True Price','Predicted Price'],loc='best')
print('The predicted price seems normal after np.log transforming')
plt.show()
# 使用对数化之后的数据进行线性回归预测
lr = LinearRegression().fit(X,y_ln)
# 这里直接调用cross_val_score函数进行交叉验证
scores = cross_val_score(lr,
X=X,y=y_ln,
verbose=1,cv=5,
scoring=make_scorer(mean_absolute_error))
print('线性回归5折交叉验证的平均MAE:',np.mean(scores))
可以看到对数化之后的数据使用线性回归模型进行5折交叉验证之后的平均MAE明显比原始数据训练出来的模型的MAE小
说明对数据进行处理比没有处理的数据要更适合该模型
scores = pd.DataFrame(scores.reshape(1,-1))
scores.columns = ['cv' + str(x) for x in range(1, 6)]
scores.index = ['MAE']
scores
'''
定义一个绘制学习率曲线和验证曲线的函数
输入:estimator为传入的模型
title为图像的标题
cv为k折交叉验证的k
n_jobs为learning_curve函数中的n_jobs参数
'''
def plotLearningCurve(estimator,title,X,y,ylim=None,cv=None,n_jobs=1,train_size=np.linspace(.1, 1.0, 5 )):
# 画布
plt.figure()
plt.title(title)
# 设置y轴区间
if ylim is not None:
plt.ylim(*ylim)
plt.xlabel('Training example')
plt.ylabel('score')
# 调用learning_curve函数
train_sizes, train_scores, test_scores = learning_curve(estimator,X,y,
cv=cv,n_jobs=n_jobs,
train_sizes=train_size,
scoring = make_scorer(mean_absolute_error))
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
#区域
plt.grid()
plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.1,
color="r")
plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.1,
color="g")
# 绘制两条线
plt.plot(train_sizes, train_scores_mean, 'o-', color='r',
label="Training score")
plt.plot(train_sizes, test_scores_mean,'o-',color="g",
label="Cross-validation score")
plt.legend(loc="best")
return plt
训练曲线和验证曲线相聚距离较小,说明该模型是一个地方差且低偏差的
一个比较好的模型
models = [LinearRegression(),
Ridge(),
Lasso(),
DecisionTreeRegressor()]
result = dict()
# 遍历所有模型
for model in models:
model_name = str(model).split('(')[0]
# 5折交叉验证
scores = cross_val_score(model,
X=X,y=y_ln,
verbose=1,cv=5,
scoring=make_scorer(mean_absolute_error))
print('{0} 5折交叉验证的平均MAE:{1}'.format(model_name,np.mean(scores)))
result[model_name] = scores
result = pd.DataFrame(result)
result.index = ['cv' + str(x) for x in range(1, 6)]
result
根据5折交叉验证的平均MAE可以分析出以下结果:
线性回归对于该数据效果最好
其次是回归树
Lasso的效果最差
这里使用两种集成学习,一种是并行boosting迭代型的xgboost算法;一种是串行的随机森林算法
forest = RandomForestRegressor(n_estimators=120,random_state=0,max_depth=7)
forest_scores = cross_val_score(forest,
X=X,y=y_ln,
verbose=1,cv=5,
scoring=make_scorer(mean_absolute_error))
print('随机森林5折交叉验证的平均MAE:',np.mean(forest_scores))
# xgb回归模型
'''
n_estimators=120使用120个基学习器
learning_rate=0.1学习率
防止过拟合:设置
subsample=0.8
colsample_bytree=0.9列名筛选
max_depth=7最大深度
'''
xgr = xgb.XGBRegressor(n_estimators=120, learning_rate=0.1, gamma=0, subsample=0.8,\
colsample_bytree=0.9, max_depth=7) #,objective ='reg:squarederror'
xgr_scores = cross_val_score(xgr,
X=X,y=y_ln,
verbose=1,cv=5,
scoring=make_scorer(mean_absolute_error))
print('XGBRegressor折交叉验证的平均MAE:',np.mean(xgr_scores))
fig = plt.figure(figsize=(20,10))
cv = [i for i in range(5)]
plt.plot(cv,forest_scores,'go-',label='随机森林')
plt.plot(cv,xgr_scores,'r>--',label='xgboost')
#设置x轴刻度
ticks = plt.xticks(cv)
#设置x轴和y轴的名称
xlabel = plt.xlabel('cv')
ylabel = plt.ylabel('MAE',rotation=0)
plt.legend(loc='best')
从上图可以看出
对于该数据,xgboost的效果要比随机森林好许多
xgr = xgb.XGBRegressor(gamma=0, subsample=0.8,colsample_bytree=0.9, max_depth=7)
param_grid = {
'learning_rate': [0.01,0.05,0.1,0.2,0.25],
'n_estimators':[75,100,125,150,175]
}
# 带交叉验证的网格搜索,5折交叉验证
xgb_gs = GridSearchCV(xgr, param_grid, cv=5)
xgb_gs.fit(X,y)
print('最佳参数:',xgb_gs.best_params_)
print('最佳参数的训练精度:{:.2f}'.format(xgb_gs.best_score_))
xgb_gs_result = pd.DataFrame(xgb_gs.cv_results_)
# 显示前5行
display(xgb_gs_result.head())
主要分析DecisionTreeRegressor、随机森林和xgboost的结果
因为随机森林和xgboost使用的基分类器都是决策树
# 去除DecisionTreeRegressor的结果
dtr_scores = []
for i in range(len(result['DecisionTreeRegressor'])):
dtr_scores.append(result['DecisionTreeRegressor'].iloc[i])
fig = plt.figure(figsize=(20,10))
cv = [i for i in range(5)]
plt.plot(cv,dtr_scores,'bo-',label='回归树')
plt.plot(cv,forest_scores,'g>--',label='随机森林')
plt.plot(cv,xgr_scores,'r>--',label='xgboost')
#设置x轴刻度
ticks = plt.xticks(cv)
#设置x轴和y轴的名称
xlabel = plt.xlabel('cv')
ylabel = plt.ylabel('MAE',rotation=0)
plt.legend(loc='best')
发现相比不使用集成学习的回归树,两种集成学习算法(随机森林和xgboost)的MAE更小,说明集成学习对于数据有较好的作用
其中以xgboost作用更为明显。
1、在整个的数据挖掘流程中,预处理是不是可有可无的一个步骤?主要解决哪些问题,有什么作用?
答:预处理是一个很重要且必不可少的步骤
主要解决:
(1)缺失数据填补或删除
(2)处理离群点或噪声点
(3)将数据成模型需要的格式(离散或是连续值)
(4)数据标准化
作用:
(1)可以处理缺失数据,否则模型无法运行
(2)确保模型不会因为数据格式等问题导致精度下降
2、不同算法之间有无优劣之分?有没有一种算法是最优的?
答:不同算法之间无法直观比较优劣,没有一种算法可以说是最优的。
对于不同的任务使用的算法不一样:分类任务使用分类算法、聚类任务使用聚类算法。
对于不同的数据适用的算法也不一样。存在对于数据A线性回归算法比回归树精度高,但是对于数据B回归树精度比线性回归算法精度高的情况。