机器学习中回归算法基本流程
1. 如何开始
小朋友你是不是对机器学习有很多问好❓❓❓❓
看着别人一直调参数,一直调参数
而自己对着原理一直停留在ABC
so
我也有有很对问号
之前一直没有接触过机器学习的具体问题分析,
最近由于工作方向原因,需要学习一些机器学习针对具体业务中的实战项目,
那么,这篇文章就是我从接触到实战的一整个过程,
针对于入门和具体问题实战的一些我学习过程中的流程。
如果你之前不知道从机器学习中的哪一个方面开始,不妨看一哈这一篇赋予你灵气的文章,这一篇没有具体原理,打算在下一篇详细总结前人大师的杰作,这篇仅仅是入门+流程。
下面是我大概学习的流程
- 其实,我觉得,学习跟打游戏一样,一定要有 “主线” 和 ”副本“,
主线就是你的一个具体最终的目标要达到什么目的,最终是去做一个什么问题,这个问题你需要什么样的技术体系,是一个整体的方向;
那么副本就是,在已经确定了大体方向,了解了大体的结构方向,的一些细节和优化的方法。
那么问题来了,现在领导给你布置了具体的问题,让你去了解并做关于机器学习在某一方面回归预测问题。
- 主线:首先在问题中,天气的预测是一个回归类问题,那么我会一直以一个回归的问题和资料为出发点;我的大概流程就是:线性回归 – 逻辑回归
– 决策树(这是我重点看的,因为很多的思想是根据决策树往下推下去的,所以这部分很重要,包括:ID3、C4.5、CART) – 随机森林 – | 这里是个节点,既然看了这么多的理论,就需要实践一下,那肯定的经典就是房价预测,股票预测,等等一些案例的分析,看这些案例其实就是看一下在机器学习在预测中的一个流程,那么你会发现,这里需要的就是对数据如何的分析,所以就会要去熟悉一些函数(pandas、matplotlib、lgb
等等,这个也可以当作 “副本” 去稍后一股脑的看)。
那么在以上所说看懂,你才可以往下进行,也就是boosting集成学习,包括(Adaboost、GBDT、XGBoost),在我们看一一圈原理后,就需要去实践代码部分,主要包括其函数调用,如何调参数,每个参数对应的是什么含义,你只有知道了原理,才能快速的将参数调节的更有意义,而不是胡乱凭感觉调节,更重要的是要知道其评价函数,是估计你的模型的标准的函数,最终使评分更高,我们调节的才有意义,"主线"任务完成; - 副本:我的副线主要有两个部分,一部分是特征工程,另一部分就是数据的可视化,下面我把我找的两篇好的网站推荐给大家。
特征工程,附加代码分析等,很不错的小教程
数据可视化1
数据可视化2
2. 具体问题分析
在这里先分析一些典型的案例+代码,如果可以,由于预测降雨的回归部分还未完成,我希望一切流程完整,打包镜像,可以能够一键部署训练,再做分享。
我把竞赛网站贴出来足球运动员身价估计数据
3. 代码部分
开始代码部分----------------
XGBoost ----特征工程,参数调节,训练模型整个过程
''' 背景介绍: 每个足球运动员在转会市场都有各自的价码。本次数据练习的目的是根据球员的各项信息和能力值来预测该球员的市场价值'''
数据集网站:http://sofasofa.io/competition.php?id=7#c1
'''
## 必要的函数库
xgboost == 0.81
scikit-learn == 0.20.0
lightgbm
'''
import pandas as pd
import xgboost as xgb
import numpy as np
from xgboost import plot_importance
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn import metrics
from sklearn.metrics import mean_squared_error,explained_variance_score
import matplotlib.pyplot as plt
import lightgbm as lgb
## step1、读取数据,对数据分析,可视化
train_data = pd.read_csv('data/train.csv')
test_data = pd.read_csv('data/test.csv')
'''
corr()函数:列出每个特征之间的关联性
'''
print(len(train_data[1:]))
train_data.corr()
## step2 对数据进行处理,特征工程
'''
数据处理:对数据中缺失值,或者不合理的数据进行处理,
在本示例中:
1.将birth_date这列特征中11/9/90 -->> 11/9/1990 ,为了增加年龄的特征,方便计算
2.将work_rate_att,work_rate_def,转化为数值,Medium=1 High=2 Low=0
3.判断是否是守门员,gk这项特征值是描述守门员,是=1,否=0
'''
def full_birthDate(x):
if(x[-2:] == '00'):
return x[:-2]+'20'+x[-2:]
else:
return x[:-2]+'19'+x[-2:]
def trans(x):
if(x == 'Medium'):
return 1
elif(x == 'High'):
return 2
else:
return 0
def judge(x):
if(x > 0):
return 1
else:
return 0
'''
特征工程
增加age特征
转化work_rate_att_, work_rate_def_ 数值---0,1
删除特征中缺失的行 drop(axis=1)
'''
# 读取数据
def getData(train_data,test_data):
train = train_data
test = test_data
train['birth_date_'] = train['birth_date'].apply(lambda x: full_birthDate(x))
test['birth_date_'] = test['birth_date'].apply(lambda x: full_birthDate(x))
train['birth_date'] = pd.to_datetime(train['birth_date_'])
train['age'] = ((pd.Timestamp.now() - train['birth_date']).apply(lambda x: x.days) / 365).apply(lambda t: int(t))
test['birth_date'] = pd.to_datetime(test['birth_date_'],format='%m/%d/%Y', errors='coerce')
test['age'] = ((pd.Timestamp.now() - test['birth_date']).apply(lambda x: x.days) / 365).apply(lambda t: int(t))
train['work_rate_att_'] = train['work_rate_att'].apply(lambda x: trans(x)).apply(lambda t: int(t))
train['work_rate_def_'] = train['work_rate_def'].apply(lambda x: trans(x)).apply(lambda t: int(t))
test['work_rate_att_'] = test['work_rate_att'].apply(lambda x: trans(x)).apply(lambda t: int(t))
test['work_rate_def_'] = test['work_rate_def'].apply(lambda x: trans(x)).apply(lambda t: int(t))
train = train.drop('id',axis=1)
train = train.drop('birth_date',axis=1)
train = train.drop('birth_date_',axis=1)
train = train.drop('work_rate_att',axis=1)
train = train.drop('work_rate_def',axis=1)
test = test.drop('id',axis=1)
test = test.drop('birth_date',axis=1)
test = test.drop('birth_date_',axis=1)
test = test.drop('work_rate_att',axis=1)
test = test.drop('work_rate_def',axis=1)
return train,test
#得到处理的数据
train,test = getData(train_data,test_data)
print(train.columns.values) #train中的特征值 (10441,64)
print(train.y.values) #train中的标签 (10441,)
'''
由于不同位置'rw', 'rb', 'st', 'lw', 'cf', 'cam', 'cm', 'cdm', 'cb', 'lb', 'gk'这些能力值有所不同,
数据分析,新增特征
['rw', 'rb', 'st', 'lw', 'cf', 'cam', 'cm', 'cdm', 'cb', 'lb', 'gk'] 选择评分高,新增加特征best_pos
计算球员的身体素质(BMI)
判断该球员是否为守门员 (is_gk)
'''
def data_ana(train, test):
# 获得球员最擅长位置上的评分
positions = ['rw', 'rb', 'st', 'lw', 'cf', 'cam', 'cm', 'cdm', 'cb', 'lb', 'gk']
train['best_pos'] = train[positions].max(axis=1)
test['best_pos'] = test[positions].max(axis=1)
# 计算球员的身体质量指数(BMI)
train['BMI'] = 10000. * train['weight_kg'] / (train['height_cm'] ** 2)
test['BMI'] = 10000. * test['weight_kg'] / (test['height_cm'] ** 2)
# 判断一个球员是否是守门员
train['is_gk'] = train['gk'].apply(lambda x: judge(x))
test['is_gk'] = test['gk'].apply(lambda x: judge(x))
return train,test
train,test = data_ana(train,test)
print(train.columns.values) #(10441,67) 新增加3类
print(train.y.values) #(10441,)
'''
选择待训练的特征,这里的方法比较笨,将每个特征写进去
得到
x_inputs (10441,67)
y_inputs (10441,)
'''
x_inputs = train[['club', 'league', 'height_cm', 'weight_kg' ,'nationality' ,'potential' ,'pac',
'sho', 'pas','dri','def', 'phy' ,'international_reputation' ,'skill_moves',
'weak_foot' ,'preferred_foot', 'crossing' ,'finishing', 'heading_accuracy',
'short_passing', 'volleys' ,'dribbling', 'curve', 'free_kick_accuracy',
'long_passing', 'ball_control','acceleration' ,'sprint_speed' ,'agility',
'reactions', 'balance', 'shot_power' ,'jumping', 'stamina', 'strength',
'long_shots', 'aggression' ,'interceptions' ,'positioning', 'vision',
'penalties', 'marking', 'standing_tackle', 'sliding_tackle' ,'gk_diving',
'gk_handling', 'gk_kicking', 'gk_positioning', 'gk_reflexes', 'rw', 'rb', 'st',
'lw', 'cf', 'cam', 'cm', 'cdm', 'cb', 'lb', 'gk', 'y', 'age' ,'work_rate_att_',
'work_rate_def_', 'best_pos', 'BMI' ,'is_gk']].values
y_inputs = train['y'].values
## step3 对数据进行切分, train_x_inputs,test_x_inputs,train_y_classes,test_y_classes
(train_x_inputs,
test_x_inputs,
train_y_classes,
test_y_classes)=train_test_split(x_inputs,y_inputs,train_size=0.75,random_state=1)
## step4 半手动调参-------------------是个过程------调参成功需要注释掉-
# # 半手动调参-------------------是个过程------调参成功需要注释掉---------------------------------------------------
'''
step1: other_params={ 'eta': 0.3, 'n_estimators': 500, 'gamma': 0, 'max_depth': 6, 'min_child_weight': 1,
'colsample_bytree': 1, 'colsample_bylevel': 1, 'subsample': 1, 'reg_lambda': 1, 'reg_alpha': 0,
'seed': 33 }
params: 1. eta : 默认是0.3,别名是 leanring_rate,更新过程中用到的收缩步长,在每次提升计算之后,算法会直接获得新特征的权重。 eta通过缩减特征的权重使提升计算过程更加保守;[0,1]
2. gamma:默认是0,别名是 min_split_loss,在节点分裂时,只有在分裂后损失函数的值下降了(达到gamma指定的阈值),才会分裂这个节点。gamma值越大,算法越保守(越不容易过拟合);[0,∞]
3. max_depth:默认是6,树的最大深度,值越大,越容易过拟合;[0,∞]
4. min_child_weight:默认是1,决定最小叶子节点样本权重和,加权和低于这个值时,就不再分裂产生新的叶子节点。当它的值较大时,可以避免模型学习到局部的特殊样本。但如果这个值过高,会导致欠拟合。[0,∞]
5. max_delta_step:默认是0,这参数限制每颗树权重改变的最大步长。如果是 0 意味着没有约束。如果是正值那么这个算法会更保守,通常不需要设置。[0,∞]
6. subsample:默认是1,这个参数控制对于每棵树,随机采样的比例。减小这个参数的值算法会更加保守,避免过拟合。但是这个值设置的过小,它可能会导致欠拟合。 (0,1]
7. colsample_bytree:默认是1,用来控制每颗树随机采样的列数的占比; (0,1]
8. colsample_bylevel:默认是1,用来控制的每一级的每一次分裂,对列数的采样的占比; (0,1]
9. lambda:默认是1,别名是reg_lambda,L2 正则化项的权重系数,越大模型越保守
10. alpha:默认是0,别名是reg_alpha,L1 正则化项的权重系数,越大模型越保守;
11. seed:随机数种子,相同的种子可以复现随机结果,用于调参!
12. n_estimators:弱学习器的数量
(n_estimators一定初始化大一点,因为会自动在收敛的地方自己停 选择特征,顺便确定n_estimators)
step2: estimator = xgb.XGBRegressor(**other_params)
step3: param_test1 = {'n_estimators': np.linspace(100, 1000, 10, dtype=int)}
param_test1 = { 'max_depth':range(22,27) }
param_test1 = {'gamma': np.linspace(0, 1, 10)} -->(参数列表为等差数列[0,0.1,0.2,…,1.0])
param_test1 = {'gamma': np.linspace(0, 1, 10)}
param_test1 = {'reg_lambda': np.linspace(0, 100, 11)}--->>> param_test1 = {'reg_lambda': np.linspace(40, 60, 11)}
param_test1 = {'eta': np.logspace(-2, 0, 10)} (调节学习率,用一个等比数列)
(用来确定最佳的参数,需要一个一个调节)
step4: gsearch1 = GridSearchCV(estimator, cv_params, param_grid = param_test1,verbose=2, refit=True, cv=5, n_jobs=-1)
params:
n_jobs:int or None, optional (default=None) 并行数,默认为1,填-1表示使用所有线程
cv : 交叉验证,默认为None,即使用3折,可指定数量
verbose: 日志冗长度,控制详细程度,值越大,信息越多
final:
评价模型调参前后的分数:
def rmsle_cv(model):
#sklearn模块自带的评价函数 ,交叉验证 cros_val_score()
rmse = np.sqrt(-cross_val_score(model, X, y,
scoring="neg_mean_squared_error", cv=5))
return rmse
# 默认参数
default_xgb = xgb.XGBRegressor()
score = rmsle_cv(default_xgb)
print("default score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))
# 调参之后
tuned_gbr = xgb.XGBRegressor(learning_rate=0.3, n_estimators=140,
max_depth=2, min_child_weight=2,
subsample=1, colsample_bytree=1, gamma=0.02, reg_lambda=48)
score = rmsle_cv(tuned_gbr)
print("tuned score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))
'''
##------------------------------------------------------------------------------------------------------------------------------------
from sklearn.model_selection import KFold, cross_val_score
param_test1 = { 'n_estimators': np.linspace(100, 1000, 10, dtype=int) }
estimator = xgb.XGBRegressor(learning_rate=0.1, n_estimators=366, min_child_weight=1,subsample=0.85,colsample_bytree=0.8,
gamma=0,objective= 'reg:gamma', nthread=4, seed=27)
gsearch1 = GridSearchCV(estimator = estimator,
param_grid = param_test1,scoring='neg_median_absolute_error',n_jobs=4, iid=False, cv=5)
gsearch1.fit(train_x_inputs,train_y_classes)
print('Best params:',gsearch1.best_params_,gsearch1.best_score_)
def rmsle_cv(model,X,y):
'''
sklearn模块自带的评价函数 ,交叉验证 cros_val_score()
'''
rmse = np.sqrt(-cross_val_score(model, X, y,
scoring="neg_mean_squared_error", cv=5))
return rmse
# 默认参数
default_xgb = xgb.XGBRegressor()
score = rmsle_cv(default_xgb,train_x_inputs,train_y_classes)
print("default score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))
# 调参之后
tuned_gbr = xgb.XGBRegressor(learning_rate=0.3, n_estimators=140,
max_depth=2, min_child_weight=2,
subsample=1, colsample_bytree=1, gamma=0.02, reg_lambda=48)
score = rmsle_cv(tuned_gbr,train_x_inputs,train_y_classes)
print("tuned score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))
## step5 选择有用的特征,可视化信息
'''
用训练好的模型gsearch1,进行对测试集预测
y_true ---->> 测试真实值
y_pred ---->> 测试预测值
'''
test_prediction = gsearch1.predict(test_x_inputs)
y_true = list(test_y_classes)
y_pred = list(test_prediction)
def MAE_(xgb1,y_true,y_pred):
num = 0
for i in range(len(y_pred)):
num += np.abs(y_true[i] - y_pred[i])
#print((num*4799+6)/len(y_pre))
return (num*4799+6)/len(y_pred)
mse=mean_squared_error(y_true,y_pred)
evs=explained_variance_score(y_true,y_pred)
print("\nModel Report")
print("r2_score : %.4g" % metrics.r2_score(y_true, y_pred))
print ("均方误差 = ",round(mse,2))
print ("解释方差分 = ",round(evs,2))
num = MAE_(gsearch1,y_true, y_pred)
print(num)
'''
查看每一个特征的贡献度,然后进行排序,并且如果某些特征的贡献度很小,可以在训练时去除这类特征
'''
print('画特征重要性排序...')
print(gsearch1)
print(gsearch1.estimator)
# mdoel = XGBRegressor()
model = gsearch1.estimator
eval_set = [(test_x_inputs,test_y_classes)]
#mae xboost 的目标函数,可以自定义目标函数
model.fit(train_x_inputs,train_y_classes,eval_metric='mae',eval_set=eval_set,verbose=True)
#GridSearchCV 中没有get_booster()函数,需要把GridSearchCV中提取XGBRegressor()
feat_imp = pd.Series(model.get_booster().get_fscore()).sort_values(ascending=False)
# feat_imp.plot(kind='bar', title='Feature Importances')
# plt.ylabel('Feature Importance Score')
fig, ax = plt.subplots(1, 1, figsize=(8, 13))
plot_importance(model, max_num_features=25, height=0.5, ax=ax)
plt.show()
## 确定参数,进行训练
'''
参数:
'''
#objective= 'reg:gamma' reg:logistic
model = xgb.XGBRegressor(learning_rate=0.1, n_estimators=366, max_depth=23, min_child_weight=1,subsample=0.8,colsample_bytree=0.8,gamma=0,objective= 'reg:gamma', nthread=4, scale_pos_weight=1, seed=27)
#model = xgb.XGBRegressor(max_depth=6, learning_rate=0.05, n_estimators=500, silent=False, objective='reg:gamma')
eval_set = [(test_x_inputs,test_y_classes)]
model.fit(train_x_inputs,train_y_classes,eval_metric='mae',eval_set=eval_set,verbose=True)
总结资料!!!!!!
如果您需要代码,以及数据,移步链接:
资料链接
提取码:6xzl