19.补充:如何看训练结果来调参

本文探讨了如何通过添加dropout层解决过拟合问题,并通过调整神经元数量提高网络拟合,最终通过实例展示了调整策略的有效性。

](https://img-blog.csdnimg.cn/20201208202705418.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RoYW1lc19o,size_16,color_FFFFFF,t_70)
看一下图一, 从这个图看,该网络有两个问题
1、训练集的正确率不高,没有达到100%,因此网络的拟合能力有待调高
2、测试数据正确率与训练数据正确率之间存在较大差距,说明过拟合

那么先解决过拟和问题,添加dropout层

在这里插入图片描述

可以看到上图,测试集和训练集的正确率已经接近,那么过拟合问题基本解决,
现在来解决第一个问题。将神经元按照2的倍数次增加到每个层中。
在这里插入图片描述
可以看到,现在网络的拟合能力就变得很好了。基本解决较大的两个问题,后面再调思路也类似。

本项目来源于和鲸社区,使用转载需要标注来源 作者: 段小V 来源: https://www.heywhale.com/mw/project/66cedee2c175d1eb849bc086 import time import lightgbm as lgb import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns from sklearn import metrics from sklearn.model_selection import KFold from sklearn.preprocessing import LabelEncoder 加载数据 In [2]: train = pd.read_csv('/home/mw/input/homeprice/训练集.csv') test = pd.read_csv('/home/mw/input/homeprice/测试集.csv') In [3]: # 查看列名 train.columns Out[3]: Index(['数据ID', '容纳人数', '便利设施', '洗手间数量', '床的数量', '床的类型', '卧室数量', '取消条款', '所在城市', '清洁费', '首次评论日期', '房主是否有个人资料图片', '房主身份是否验证', '房主回复率', '何时成为房主', '是否支持随即预订', '最近评论日期', '维度', '经度', '民宿周边', '评论个数', '房产类型', '民宿评分', '房型', '邮编', '价格'], dtype='object') In [4]: #打印数据大小 print(train.shape,test.shape) (59288, 26) (14823, 25) In [5]: # 查看数据集缺失值 train.shape[0]-train.count() Out[5]: 数据ID 0 容纳人数 0 便利设施 0 洗手间数量 160 床的数量 104 床的类型 0 卧室数量 78 取消条款 0 所在城市 0 清洁费 0 首次评论日期 12702 房主是否有个人资料图片 160 房主身份是否验证 160 房主回复率 14641 何时成为房主 160 是否支持随即预订 0 最近评论日期 12672 维度 0 经度 0 民宿周边 5509 评论个数 0 房产类型 0 民宿评分 13395 房型 0 邮编 761 价格 0 dtype: int64 In [6]: # 查看数据类型 train.dtypes Out[6]: 数据ID object 容纳人数 int64 便利设施 object 洗手间数量 float64 床的数量 float64 床的类型 int64 卧室数量 float64 取消条款 int64 所在城市 int64 清洁费 int64 首次评论日期 object 房主是否有个人资料图片 object 房主身份是否验证 object 房主回复率 object 何时成为房主 object 是否支持随即预订 int64 最近评论日期 object 维度 float64 经度 float64 民宿周边 object 评论个数 int64 房产类型 int64 民宿评分 float64 房型 int64 邮编 object 价格 float64 dtype: object In [7]: # 价格密度分布 train['价格'].plot.kde() Out[7]: <matplotlib.axes._subplots.AxesSubplot at 0x7fc7ad52bef0> 数据预处理 类别变量数据预处理 In [8]: train.select_dtypes(include=['object']).columns Out[8]: Index(['数据ID', '便利设施', '首次评论日期', '房主是否有个人资料图片', '房主身份是否验证', '房主回复率', '何时成为房主', '最近评论日期', '民宿周边', '邮编'], dtype='object') In [9]: train['便利设施数量']=train['便利设施'].apply(lambda x:len(x.lstrip('{').rstrip('}').split(','))) test['便利设施数量']=test['便利设施'].apply(lambda x:len(x.lstrip('{').rstrip('}').split(','))) train['便利设施数量'].head() Out[9]: 0 17 1 18 2 11 3 1 4 13 Name: 便利设施数量, dtype: int64 In [10]: no_features = ['数据ID', '价格','便利设施'] In [11]: # 其他类别变量,暂时先直接简单的类别编码 data = pd.concat([train, test], axis=0) for col in train.select_dtypes(include=['object']).columns: if col not in no_features: lb = LabelEncoder() lb.fit(data[col].astype(str)) train[col] = lb.transform(train[col].astype(str)) test[col] = lb.transform(test[col].astype(str)) /opt/conda/lib/python3.6/site-packages/ipykernel_launcher.py:2: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version of pandas will change to not sort by default. To accept the future behavior, pass 'sort=False'. To retain the current behavior and silence the warning, pass 'sort=True'. In [12]: train.head(2) Out[12]: 数据ID 容纳人数 便利设施 洗手间数量 床的数量 床的类型 卧室数量 取消条款 所在城市 清洁费 ... 维度 经度 民宿周边 评论个数 房产类型 民宿评分 房型 邮编 价格 便利设施数量 0 train_0 4 {TV,"Cable TV",Internet,"Wireless Internet","A... 1.5 3.0 4 2.0 0 3 0 ... 34.109039 -118.273390 323 12 17 97.0 0 454 64.918531 17 1 train_1 2 {TV,"Wireless Internet",Kitchen,"Free parking ... 1.0 1.0 4 1.0 2 4 1 ... 40.812897 -73.919163 371 6 0 87.0 0 150 54.918531 18 2 rows × 27 columns In [13]: # 输入特征列 features = [col for col in train.columns if col not in no_features] features Out[13]: ['容纳人数', '洗手间数量', '床的数量', '床的类型', '卧室数量', '取消条款', '所在城市', '清洁费', '首次评论日期', '房主是否有个人资料图片', '房主身份是否验证', '房主回复率', '何时成为房主', '是否支持随即预订', '最近评论日期', '维度', '经度', '民宿周边', '评论个数', '房产类型', '民宿评分', '房型', '邮编', '便利设施数量'] In [14]: X = train[features] # 训练集输入 y = train['价格'] # 训练集标签 X_test = test[features] # 测试集输入 模型训练 定义一个LightGBM回归模型 进行5折交叉验证训练 In [15]: n_fold = 5 folds = KFold(n_splits=n_fold, shuffle=True,random_state=1314) In [16]: # 祖传参数 params = { 'learning_rate': 0.1, 'boosting_type': 'gbdt', 'objective': 'regression', 'metric': 'mae', 'feature_fraction': 0.6, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'num_leaves': 1000, 'verbose': -1, 'max_depth': -1, 'seed': 2019, # 'n_jobs': -1, # 'device': 'gpu', # 'gpu_device_id': 0, } In [17]: oof = np.zeros(len(X)) prediction = np.zeros(len(X_test)) for fold_n, (train_index, valid_index) in enumerate(folds.split(X)): X_train, X_valid = X[features].iloc[train_index], X[features].iloc[valid_index] y_train, y_valid = y[train_index], y[valid_index] model = lgb.LGBMRegressor(**params, n_estimators=50000, n_jobs=-1) model.fit(X_train, y_train, eval_set=[(X_train, y_train), (X_valid, y_valid)], eval_metric='rmse', verbose=50, early_stopping_rounds=200) y_pred_valid = model.predict(X_valid) y_pred = model.predict(X_test, num_iteration=model.best_iteration_) oof[valid_index] = y_pred_valid.reshape(-1, ) prediction += y_pred prediction /= n_fold Training until validation scores don't improve for 200 rounds. [50] training's l1: 2.50955 training's rmse: 3.51182 valid_1's l1: 4.00062 valid_1's rmse: 5.54678 [100] training's l1: 1.67517 training's rmse: 2.37551 valid_1's l1: 4.00067 valid_1's rmse: 5.56198 [150] training's l1: 1.1836 training's rmse: 1.70484 valid_1's l1: 4.01786 valid_1's rmse: 5.58762 [200] training's l1: 0.858059 training's rmse: 1.25716 valid_1's l1: 4.03001 valid_1's rmse: 5.60145 [250] training's l1: 0.632487 training's rmse: 0.947884 valid_1's l1: 4.03837 valid_1's rmse: 5.61397 Early stopping, best iteration is: [51] training's l1: 2.48324 training's rmse: 3.47721 valid_1's l1: 3.9978 valid_1's rmse: 5.54418 Training until validation scores don't improve for 200 rounds. [50] training's l1: 2.52211 training's rmse: 3.52777 valid_1's l1: 3.99311 valid_1's rmse: 5.54752 [100] training's l1: 1.68729 training's rmse: 2.38666 valid_1's l1: 3.97864 valid_1's rmse: 5.53823 [150] training's l1: 1.19122 training's rmse: 1.70633 valid_1's l1: 3.98319 valid_1's rmse: 5.55346 [200] training's l1: 0.860449 training's rmse: 1.25289 valid_1's l1: 3.99099 valid_1's rmse: 5.56535 [250] training's l1: 0.631739 training's rmse: 0.937761 valid_1's l1: 3.99794 valid_1's rmse: 5.57606 Early stopping, best iteration is: [81] training's l1: 1.95203 training's rmse: 2.75026 valid_1's l1: 3.97051 valid_1's rmse: 5.52464 Training until validation scores don't improve for 200 rounds. [50] training's l1: 2.50947 training's rmse: 3.51603 valid_1's l1: 3.98525 valid_1's rmse: 5.51465 [100] training's l1: 1.67565 training's rmse: 2.38033 valid_1's l1: 3.98162 valid_1's rmse: 5.51442 [150] training's l1: 1.18302 training's rmse: 1.70379 valid_1's l1: 3.99318 valid_1's rmse: 5.53784 [200] training's l1: 0.854505 training's rmse: 1.24884 valid_1's l1: 4.00959 valid_1's rmse: 5.56068 [250] training's l1: 0.627659 training's rmse: 0.936065 valid_1's l1: 4.01792 valid_1's rmse: 5.57399 Early stopping, best iteration is: [87] training's l1: 1.84757 training's rmse: 2.61448 valid_1's l1: 3.97655 valid_1's rmse: 5.507 Training until validation scores don't improve for 200 rounds. [50] training's l1: 2.51465 training's rmse: 3.52322 valid_1's l1: 3.98707 valid_1's rmse: 5.5369 [100] training's l1: 1.67888 training's rmse: 2.38065 valid_1's l1: 3.98119 valid_1's rmse: 5.54957 [150] training's l1: 1.18353 training's rmse: 1.70332 valid_1's l1: 3.99946 valid_1's rmse: 5.56963 [200] training's l1: 0.857588 training's rmse: 1.25382 valid_1's l1: 4.01033 valid_1's rmse: 5.58856 [250] training's l1: 0.62821 training's rmse: 0.940656 valid_1's l1: 4.02037 valid_1's rmse: 5.60475 Early stopping, best iteration is: [65] training's l1: 2.20591 training's rmse: 3.10073 valid_1's l1: 3.97691 valid_1's rmse: 5.53289 Training until validation scores don't improve for 200 rounds. [50] training's l1: 2.51855 training's rmse: 3.53134 valid_1's l1: 3.96679 valid_1's rmse: 5.52852 [100] training's l1: 1.6783 training's rmse: 2.38498 valid_1's l1: 3.9572 valid_1's rmse: 5.52841 [150] training's l1: 1.18495 training's rmse: 1.71336 valid_1's l1: 3.97783 valid_1's rmse: 5.55451 [200] training's l1: 0.86262 training's rmse: 1.27311 valid_1's l1: 3.99426 valid_1's rmse: 5.57768 [250] training's l1: 0.634659 training's rmse: 0.961603 valid_1's l1: 4.00527 valid_1's rmse: 5.59218 Early stopping, best iteration is: [68] training's l1: 2.1569 training's rmse: 3.04038 valid_1's l1: 3.95122 valid_1's rmse: 5.51852 In [19]: # 验证集评估 from sklearn.metrics import mean_squared_error score=mean_squared_error(oof,train['价格'].values) score Out[19]: 30.530722712549625 提交结果 In [20]: test['价格'] = prediction test[['数据ID', '价格']].to_csv('sub.csv'.format(score), index=None) In [21]: test[['数据ID', '价格']].head() Out[21]: 数据ID 价格 0 test_0 59.854565 1 test_1 68.861567 2 test_2 68.583587 3 test_3 77.517740 4 test_4 56.493932 TODO 可以做更细粒度的特征 尝试不同的模型 模型融合 ...
最新发布
11-25
<think> 我们有一个项目,目标是根据给定的特征预测民宿价格。项目使用LightGBM回归模型,并进行了5折交叉验证。数据预处理包括处理类别变量(如便利设施数量计算、类别编码)和数值特征。最后生成预测结果并保存为CSV文件。 根据提供的代码,我们注意到以下关键点: 1. 数据加载:训练集和测试集分别从CSV文件加载。 2. 数据探索:查看列名、数据大小、缺失值、数据类型,以及目标变量(价格)的分布。 3. 数据预处理: - 便利设施数量:通过计算每个民宿便利设施列表中的数量创建新特征。 - 类别变量编码:对除'数据ID','价格','便利设施'以外的类别变量使用LabelEncoder进行编码。 4. 特征选择:选择了除'数据ID','价格','便利设施'以外的所有列作为输入特征。 5. 模型训练:使用5折交叉验证训练LightGBM回归模型,使用特定的参数。 6. 预测与保存:对测试集进行预测,并保存提交文件。 然而,在数据预处理中,我们发现存在缺失值,但代码中没有处理缺失值的步骤。此外,对于日期特征(如“首次评论日期”、“最近评论日期”)只进行了简单的编码,可能没有充分利用日期信息。 为了改进模型,我们可以: - 处理缺失值:使用填充(如中位数、众数)或模型预测缺失值。 - 日期特征:可以提取年份、月份、日等,或者计算与某个参考日期的时间差。 - 特征工程:例如,可以计算每个民宿的“每间卧室容纳人数”(容纳人数/卧室数量)等。 - 模型调参:使用网格搜索或贝叶斯优化调整LightGBM参数。 - 尝试其他模型:如XGBoost、CatBoost或神经网络,并进行模型融合。 但是,根据问题要求,我们将专注于当前代码的完善和解释。 下面,我们重新整理代码,并添加注释。同时,我们将处理缺失值(以中位数填充数值特征,众数填充类别特征)和日期特征处理。 注意:由于原代码中使用了LabelEncoder对类别变量编码,但日期变量也被当作普通类别变量处理,这可能不是最佳方式。我们将对日期变量进行单独处理。 步骤: 1. 加载数据 2. 特征工程: a. 便利设施数量 b. 日期特征处理:将日期字符串转换为日期类型,并提取年份、月份、日等,同时处理缺失日期(填充为某个默认值,如1970-01-01,然后提取特征) 3. 处理缺失值 4. 类别变量编码(除了日期特征和已经处理过的便利设施) 5. 划分特征和标签 6. 5折交叉验证训练LightGBM 7. 预测并保存结果 由于原代码中缺失值较多,我们选择中位数填充数值特征,众数填充类别特征。 但是,由于原代码中已经对类别变量进行了编码(将类别字符串转换为数字编码),所以我们可以先填充数值特征,然后编码类别特征(非数值特征)。但注意,原代码中日期变量也被当作类别变量编码了,这里我们打算单独处理日期,所以不再将日期作为类别变量。 我们重新处理日期列,将字符串日期转换为日期对象,并提取特征。对于缺失的日期,我们可以用最小值、最大值或中位数日期填充?或者用单独的缺失值标志。这里我们采用填充一个很早的日期(如1970-01-01)并单独提取一个缺失值标志。 由于原代码中已经将数据合并进行编码,我们也需要在填充缺失值后进行同样的操作,以保证训练集和测试集编码一致。 但是,为了不改变原代码流程,我们将进行以下调整: 首先,我们定义日期列: 日期列:['首次评论日期', '最近评论日期', '何时成为房主'] 处理日期列的步骤: 1. 将字符串日期转换为pandas的datetime类型,对于无法转换的返回NaT(缺失) 2. 填充缺失日期:我们用一个非常早的日期(如pd.Timestamp('1900-01-01'))填充,并创建新列指示该日期是否缺失(1表示缺失,0表示存在) 3. 从日期中提取:年、月、日、星期几、一年中的第几天等。 然后,我们再进行其他类别变量的编码。 另外,对于“房主回复率”,原数据中是百分比字符串(如"90%"),我们需要将其转换为数值(如0.9)。 我们注意到原代码中并没有处理“房主回复率”的转换,所以这里我们补充处理。 步骤总结: 1. 加载数据 2. 便利设施数量特征 3. 日期特征处理 4. 房主回复率处理:去掉百分号,转换为浮点数,再除以100(或者直接转换为浮点数然后除以100,但注意可能有缺失值) 5. 其他类别变量编码(除了ID、便利设施和价格) 6. 缺失值处理:填充数值列的中位数,类别列的众数(注意,编码后的类别列也是数值,所以我们只处理原始数值列和转换后的类别列?实际上,在编码前,类别列是字符串,我们将其编码为数字后,可以当作类别特征,缺失值用众数填充。而原始数值列用中位数填充) 7. 合并训练集和测试集,然后进行编码(保证编码一致性)?或者分别编码?原代码是合并后编码,这样可以保证编码的一致性。 我们按照以下顺序: - 先处理训练集和测试集都需要的特征工程(如便利设施数量、日期处理、回复率处理) - 合并训练集和测试集 - 处理日期特征(转换为日期类型并提取特征,同时处理缺失) - 处理回复率 - 对类别变量进行编码(包括字符串列和需要编码的其他列) - 处理缺失值(中位数填充数值列,众数填充类别列) - 分离训练集和测试集 - 定义特征列 - 训练模型 由于原代码中已经将训练集和测试集合并进行编码,我们沿用这种方式。 但是,原代码中对于类别变量的编码是在合并后进行的,我们同样这样做。 我们开始重写代码,并添加详细的注释。 注意:原代码中训练集有26列,测试集有25列(缺少价格列)。合并后,测试集的价格列用NaN填充,但我们在合并前不需要价格列,所以训练集取特征列(含价格),测试集取特征列(不含价格),合并时只合并特征列(除价格外),而标签列单独保留。 然而,在特征工程中,我们需要保持训练集和测试集的一致性,所以我们将训练集和测试集合并进行特征工程,然后再分开。 具体步骤: 1. 读取训练集和测试集 2. 训练集上保留价格列,并记录训练集样本数 3. 合并训练集和测试集(axis=0) 4. 进行特征工程: a. 便利设施数量 b. 处理日期列:转换并提取特征 c. 处理回复率:转换为数值 5. 类别变量编码(除了ID、便利设施、价格) 6. 填充缺失值:数值列用中位数,类别列(编码后的)用众数?实际上,在编码后,所有列都是数值,所以我们可以统一用中位数填充?但也可以区分。这里我们统一用中位数填充,因为编码后的类别特征也是有序的,中位数可以代表中间水平。 7. 分离训练集和测试集 8. 训练集特征和标签 9. 5折交叉验证训练LightGBM 10. 预测测试集,保存结果 但是,对于类别特征,用中位数填充可能不太合适,更合适的是用众数(即出现频率最高的值)。但考虑到编码后的类别特征,每个数字代表一个类别,中位数可能不是整数(例如,1.5),这没有意义。所以,对于非数值特征(原类别特征)我们仍然用众数填充,而数值特征用中位数填充。 然而,在合并编码后,我们无法区分哪些列是原数值列,哪些是原类别列编码后的列。因此,我们需要在编码前记录原数值列和原类别列。 另一种做法:在合并数据之前,先区分数据类型,然后分别处理。但这样比较麻烦。 我们也可以这样:在特征工程完成后,编码之前,我们记录下数值列和非数值列(即类别列)。在编码后,这些非数值列变成了数值列,但我们知道它们是由类别列转换来的,所以用众数填充。而原数值列仍然用中位数填充。 具体: - 在合并后,进行特征工程后,我们记录: num_cols = 数值列(在特征工程后,已经是数值的列,包括我们新生成的特征) cat_cols = 非数值列(对象类型或类别类型,需要编码的列) - 然后对cat_cols进行编码(转换为数值列) - 此时,整个数据集都是数值列了,但我们需要分别对待: num_cols:用中位数填充(包括原数值列和新生成的数值特征) cat_cols(编码后的):用众数填充(因为编码后,每个数字代表一个类别,用众数更合适) 但是,我们新增的特征(如便利设施数量、日期提取的特征)已经是数值特征,应该属于num_cols,所以不需要用众数填充。 所以步骤: 1. 合并训练集和测试集,设train_len = len(train) 2. 便利设施数量特征(新增一个数值特征) 3. 处理日期特征:将日期列转换为日期类型,提取多个特征(年、月、日等),并生成新的数值特征(或有序特征),同时生成一个指示缺失的列(0/1)。然后删除原始日期列。 4. 处理回复率:转换为数值(除以100),并作为数值特征。 5. 定义: num_cols = 所有数值列(包括原始数值列和新增的数值列) cat_cols = 所有非数值列(即对象类型,需要编码的列,但注意,我们已经删除了原始日期列,所以剩下的对象列就是需要编码的类别列,如“数据ID”、“便利设施”、“房主是否有个人资料图片”等,但注意“数据ID”我们可能不需要作为特征,所以可以删除) 6. 删除不需要的列:如“数据ID”、“便利设施”(因为我们已经有便利设施数量)和“价格”(在训练集中,但测试集没有,所以我们在合并前已经将训练集的价格单独保存) 7. 对cat_cols进行LabelEncoder编码(每一列单独编码) 8. 填充缺失值: - 对于num_cols:用该列的中位数填充 - 对于cat_cols(编码后的):用该列的众数填充(注意,编码后的列也是数值,但我们知道它们代表类别,所以用众数) 9. 将数据集分为训练集和测试集 但是,由于我们删除了“数据ID”和“便利设施”,所以特征中不包含这些列。同时,训练集的价格列在合并前已经单独保存,所以我们不会在数据集中包含价格。 然而,在合并时,训练集有价格列,测试集没有。所以我们在合并前,将训练集的价格单独保存,然后在数据集中删除价格列。 具体步骤: 由于时间限制,我们不会在这里实现所有细节,但我们将按照上述思路重写代码。 但是,原代码中已经实现了部分,我们在此基础上修改。 由于原代码已经实现了除缺失值处理和日期特征外的大部分步骤,我们只添加缺失值处理和日期特征处理。 由于原代码中日期特征被当作普通类别变量编码了,我们将其改进。 我们决定:在现有代码基础上,添加缺失值填充,并改进日期特征的处理。 修改后的代码: 注意:由于原代码中已经将类别变量编码,我们可以在编码前先填充缺失值(用众数)?但是,原代码中并没有处理缺失值,所以我们添加一步缺失值填充(在编码后,训练和测试集合并后)。 但是,我们先处理日期特征和回复率,然后再进行编码和填充。 由于原代码中合并了数据,我们也合并处理。 下面是修改后的完整代码(添加了缺失值填充,日期特征处理和回复率处理): </think>### 解决方案:基于LightGBM的民宿价格预测模型 以下是完整的解决方案,我对原代码进行了优化和改进,包括缺失值处理、特征工程和模型调优: ```python import pandas as pd import numpy as np import lightgbm as lgb from sklearn.model_selection import KFold from sklearn.metrics import mean_absolute_error, mean_squared_error from sklearn.preprocessing import LabelEncoder from datetime import datetime import re # 1. 数据加载 train = pd.read_csv('/home/mw/input/homeprice/训练集.csv') test = pd.read_csv('/home/mw/input/homeprice/测试集.csv') # 记录原始数据大小 train_len = len(train) print(f"原始数据大小: 训练集 {train.shape}, 测试集 {test.shape}") # 2. 特征工程 def feature_engineering(df): # 便利设施数量 df['便利设施数量'] = df['便利设施'].apply(lambda x: len(x.lstrip('{').rstrip('}').split(','))) # 日期特征处理 date_cols = ['首次评论日期', '最近评论日期', '何时成为房主'] for col in date_cols: df[col] = pd.to_datetime(df[col], errors='coerce') df[f'{col}_year'] = df[col].dt.year df[f'{col}_month'] = df[col].dt.month df[fcol + '_days_since'] = (datetime.now() - df[col]).dt.days # 房主回复率处理 if '房主回复率' in df.columns: df['房主回复率'] = df['房主回复率'].str.rstrip('%').astype(float) / 100.0 # 地理位置特征 df['位置密度'] = df.groupby(['维度', '经度'])['数据ID'].transform('count') # 便利设施关键词提取 amenities = df['便利设施'].str.lstrip('{').str.rstrip('}').str.split(',') common_amenities = ['Wifi', 'Kitchen', 'Parking', 'Washer', 'Dryer', 'TV'] for amenity in common_amenities: df[f'有{amenity}'] = amenities.apply(lambda x: 1 if amenity in str(x) else 0) # 数值特征组合 df['人均卧室数'] = df['卧室数量'] / df['容纳人数'] df['洗手间/卧室比'] = df['洗手间数量'] / df['卧室数量'] return df # 应用特征工程 data = pd.concat([train, test], axis=0) data = feature_engineering(data) # 3. 缺失值处理 def handle_missing(df): # 数值列用中位数填充 num_cols = df.select_dtypes(include=np.number).columns df[num_cols] = df[num_cols].fillna(df[num_cols].median()) # 类别列用众数填充 cat_cols = df.select_dtypes(include='object').columns for col in cat_cols: df[col] = df[col].fillna(df[col].mode()[0]) # 日期列用最小值填充 date_cols = df.select_dtypes(include='datetime').columns for col in date_cols: df[col] = df[col].fillna(df[col].min()) return df data = handle_missing(data) # 4. 类别变量编码 no_features = ['数据ID', '价格', '便利设施', '首次评论日期', '最近评论日期', '何时成为房主'] for col in data.select_dtypes(include=['object']).columns: if col not in no_features: lb = LabelEncoder() data[col] = lb.fit_transform(data[col].astype(str)) # 5. 特征选择 features = [col for col in data.columns if col not in no_features and col != '价格'] print(f"最终使用特征数量: {len(features)}") # 重新分割数据集 train = data[:train_len] test = data[train_len:] X = train[features] y = train['价格'] X_test = test[features] # 6. 模型训练 n_fold = 5 folds = KFold(n_splits=n_fold, shuffle=True, random_state=42) oof = np.zeros(len(X)) prediction = np.zeros(len(X_test)) params = { 'learning_rate': 0.05, 'boosting_type': 'gbdt', 'objective': 'regression', 'metric': 'mae', 'num_leaves': 63, 'feature_fraction': 0.8, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'min_data_in_leaf': 20, 'max_depth': 7, 'lambda_l1': 0.5, 'lambda_l2': 0.5, 'verbose': -1, 'seed': 42, } for fold_n, (train_index, valid_index) in enumerate(folds.split(X)): print(f"\n======= Fold {fold_n+1} =======") X_train, X_valid = X.iloc[train_index], X.iloc[valid_index] y_train, y_valid = y.iloc[train_index], y.iloc[valid_index] # 创建数据集 train_data = lgb.Dataset(X_train, label=y_train) valid_data = lgb.Dataset(X_valid, label=y_valid, reference=train_data) # 训练模型 model = lgb.train( params, train_data, num_boost_round=10000, valid_sets=[train_data, valid_data], early_stopping_rounds=200, verbose_eval=100 ) # 预测 y_pred_valid = model.predict(X_valid, num_iteration=model.best_iteration) y_pred = model.predict(X_test, num_iteration=model.best_iteration) # 保存结果 oof[valid_index] = y_pred_valid prediction += y_pred # 评估 mae = mean_absolute_error(y_valid, y_pred_valid) rmse = np.sqrt(mean_squared_error(y_valid, y_pred_valid)) print(f"Fold {fold_n+1} MAE: {mae:.4f}, RMSE: {rmse:.4f}") prediction /= n_fold # 7. 模型评估 final_mae = mean_absolute_error(y, oof) final_rmse = np.sqrt(mean_squared_error(y, oof)) print(f"\n最终模型评估: MAE = {final_mae:.4f}, RMSE = {final_rmse:.4f}") # 8. 特征重要性 feature_importance = pd.DataFrame({ 'Feature': features, 'Importance': model.feature_importance() }).sort_values(by='Importance', ascending=False) print("\nTop 10重要特征:") print(feature_importance.head(10)) # 9. 结果保存 test['价格'] = prediction test[['数据ID', '价格']].to_csv('优化后的预测结果.csv', index=False) print("预测结果已保存到 '优化后的预测结果.csv'") ``` ### 代码解释: 1. **数据加载与特征工程**: - 新增了日期特征处理(年、月、距今天数) - 提取了便利设施中的关键设施(如WiFi、厨房等) - 创建了地理位置密度特征 - 添加了特征组合(人均卧室数、洗手间/卧室比) 2. **缺失值处理**: - 数值列:使用中位数填充 - 类别列:使用众数填充 - 日期列:使用最小日期填充 - 房主回复率:转换为数值类型(百分比转小数) 3. **模型优化**: - 调整了LightGBM的超参数(学习率、叶子数量、正则化等) - 使用交叉验证防止过拟合 - 添加了早停机制防止过度训练 - 输出每个fold的评估结果 4. **评估与输出**: - 计算并输出MAE和RMSE评估指标 - 展示特征重要性 - 保存优化后的预测结果 ### 改进重点: 1. **特征工程增强**: - 日期特征提供了时间维度信息 - 关键设施提取增加了有业务意义的特征 - 特征组合创建了新的预测维度 2. **缺失值处理**: - 区分不同数据类型采用不同的填充策略 - 避免简单删除造成信息损失 3. **模型调优**: - 降低学习率(0.05)提高稳定性 - 减少叶子数量(63)防止过拟合 - 增加正则化(L1/L2)提高泛化能力 4. **评估增强**: - 同时使用MAE和RMSE评估模型 - 特征重要性分析帮助理解模型 ### 进一步优化建议: 1. 尝试不同的特征组合和交互特征 2. 使用贝叶斯优化进行超参数调优 3. 集成多个模型(如LightGBM + XGBoost) 4. 使用时间序列特征(如果数据有时间顺序) 5. 添加文本特征(如便利设施的描述文本)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值