LightGBM是一个开源的梯度提升框架,它主要用于处理机器学习中的分类和回归问题
1. LightGBM的优缺点
1.1 优点:
- 高效性:
LightGBM采用了基于直方图的算法来高效地处理大规模数据集。 它通过将数据集按特征值进行分桶,并利用直方图算法进行快速的特征分裂,从而减少了计算和内存消耗
- 低内存使用:
LightGBM使用了压缩的内存存储方式,使得可以处理大规模的数据集而不会占用太多内存
- 高准确性:
LightGBM采用了leaf-wise的决策树生长策略,可以更快地找到最佳的分裂点,提高了模型的准确 性
- 可扩展性:
LightGBM支持并行化训练,可以利用多个CPU核心来加速模型训练过程
- 处理稀疏数据: LightGBM可以处理大规模的稀疏数据集,对于特征维度较高的问题也有很好的表现
1.2 缺点:
- 对噪声和异常值敏感: 由于 LightGBM采用了leaf-wise的生长策略,它对噪声和异常值比较敏感,可能会导致过拟合
- 参数调优困难: LightGBM有很多参数需要调 优,而且 不同的参数 组合可能会对模型的性能产生较大影响,所以需要一定的经验和时间去调整参数
2. GitHub文档
https://github.com/microsoft/LightGBM
3. 代码实例
3.1 数据展示
import numpy as np
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'SimHei' # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_excel('房价数据.xlsx')
df.head()
接下来将利用该数据集,构建价格与其它指标的一个LightGBM预测模型
3.2 数据处理
3.2.1 数据编码
from sklearn.preprocessing import LabelEncoder
# 使用 LabelEncoder 对 所属区域组 房间类型 进行标签编码
label_encoder = LabelEncoder()
df['所属区域组_new'] = label_encoder.fit_transform(df['所属区域组'])
# 输出每个类别及其编码结果
print("所属区域组标签编码结果:")
for class_label, encoded_label in zip(label_encoder.classes_, label_encoder.transform(label_encoder.classes_)):
print(f"{class_label}: {encoded_label}")
df['房间类型_new'] = label_encoder.fit_transform(df['房间类型'])
# 输出每个类别及其编码结果
print("房间类型标签编码结果:")
for class_label, encoded_label in zip(label_encoder.classes_, label_encoder.transform(label_encoder.classes_)):
print(f"{class_label}: {encoded_label}")
3.2.2 数据异常值处理
from scipy.stats import norm
heights = df['价格'].values
plt.figure(figsize=(10, 5) ,dpi=300)
plt.hist(heights, bins=30, density=True, alpha=0.6, color='g')
mu, std = norm.fit(heights)
xmin, xmax = plt.xlim()
x = np.linspace(xmin, xmax, 100)
p = norm.pdf(x, mu, std)
plt.plot(x, p, 'k', linewidth=2)
plt.legend(['正态分布', '价格数据'])
plt.xlabel('价格')
plt.ylabel('密度')
plt.title('价格正态分布图')
plt.show()
根据价格正态分布可知,价格存在一定的特殊价格,远离大部分数据,为了模型精确度考虑,把这部分数据集认为为异常值进行删除处理
# 定义函数 detect_outliers,接受两个参数:DataFrame 和需要检查的列名
def detect_outliers(df, column_names):
# 初始化一个空的 Index,用于存储异常值的索引
outlier_indices = pd.Index([])
# 遍历指定的列名列表
for column_name in column_names:
# 计算当前列的均值和标准差
mean = df[column_name].mean()
std = df[column_name].std()
# 计算异常值的阈值(标准差的3倍)
threshold = 3 * std
# 计算异常值的上下限(均值加减3倍标准差)
lower_limit = mean - threshold
upper_limit = mean + threshold
# 找出值在上下限之外的行
outliers = df[(df[column_name] < lower_limit) | (df[column_name] > upper_limit)]
# 将新找到的异常值索引与已有异常值索引合并
outlier_indices = outlier_indices.union(outliers.index)
# 返回所有异常值的索引列表
return outlier_indices
columns_to_check = ['价格']
# 使用指定的列数据来运行 detect_outliers 函数,并返回异常值的索引列表
outlier_indices = detect_outliers(df, columns_to_check)
if not outlier_indices.empty:
print("超出三个标准差范围的索引:")
print(outlier_indices)
else:
print("没有数据超出三个标准差的范围")
# 删除指定索引的行
df = df.drop(outlier_indices)
# 重置索引
df = df.reset_index(drop=True)
3.2.3 数据对数变换
df = df[df['价格'] > 0]
df = df.reset_index(drop=True)
df['价格_lg'] = np.log10(df['价格'])
df.head()
对数变换是一种常用的特征工程方法,一般对于数值大于0的长尾分布数据,可以采取对数变换的方法来转换特征值,整体上减缓长尾分布这种极偏的分布状态,为低值这一端争取更多的空间,将高值这一端尽可能的压缩,使得整体分布更加合理。进而增强模型的效果
from scipy.stats import norm
heights = df['价格_lg'].values
plt.figure(figsize=(10, 5) ,dpi=300)
plt.hist(heights, bins=30, density=True, alpha=0.6, color='g')
mu, std = norm.fit(heights)
xmin, xmax = plt.xlim()
x = np.linspace(xmin, xmax, 100)
p = norm.pdf(x, mu, std)
plt.plot(x, p, 'k', linewidth=2)
plt.legend(['正态分布', 'LG价格数据'])
plt.xlabel('LG价格')
plt.ylabel('密度')
plt.title('价格正态分布图')
plt.show()
3.3 数据分割
from sklearn.model_selection import train_test_split
X = df['价格_lg']
Y = df[['纬度', '经度', '最少住几晚', '评论数', '所属区域组_new', '房间类型_new']]
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(Y, X, test_size=0.3)
3.4 模型参数选择
from sklearn.model_selection import GridSearchCV
import lightgbm
# 定义模型
model = lightgbm.LGBMRegressor()
#设置参数
grids = {'n_estimators': [10,20,30,50,100,200,500], #基学习器数量
'subsample': [0.6, 0.7, 0.8, 0.9, 1.0], #训练时采样一定比例的数据
'colsample_bytree': [0.6, 0.7, 0.8, 0.9, 1.0], #特征采样占比
'learning_rate' : [0.01,0.03,0.1,0.2,0.3], #学习率
'reg_lambda':[0,0.1,0.2,0.5,0.7,0.9,1] #L2正则化
}
# 创建 GridSearchCV 对象
# n_jobs=-1 表示使用所有可用的CPU核心进行并行计算
# scoring 指定了评估指标,包括 accuracy 和 roc_auc_ovr_weighted
# refit='accuracy' 表示在搜索完成后,使用 accuracy 作为主要评价指标来重新拟合最佳模型
gscv = GridSearchCV(model, grids, cv=5, n_jobs=-1, scoring=['neg_mean_squared_error', 'r2'], refit='r2')
# 在训练数据上进行网格搜索和交叉验证,找到最佳模型
gscv.fit(X_train, y_train)
# 在测试数据上评估最佳模型的性能
score_best_estimator = gscv.score(X_test, y_test)
# 打印最佳模型的交叉验证最高得分、最佳超参数组合和测试数据上的准确性
print('交叉验证最高得分: {:.3f}'.format(gscv.best_score_))
print('最佳超参数组合: {}'.format(gscv.best_params_))
print('测试数据上的准确性: {:.3f}'.format(score_best_estimator))
# 将 GridSearchCV 的结果转为 DataFrame 方便查看
data = pd.DataFrame(gscv.cv_results_)
data
使用GridSearchCV进行了网格搜索和交叉验证,来找到最佳的LightGBM模型参数组合。通过定义模型,并设置一系列参数的候选值,然后创建GridSearchCV对象,并指定评估指标和refit参数。接着在训练数据上进行网格搜索和交叉验证,找到最佳模型。最后,在测试数据上评估最佳模型的性能,这里模型精确度在训练集为0.61在测试集为0.63,并没有想象的这么高,是因为主要考虑的是实现LightGBM模型,调参并没有引入太多模型参数,LightGBM有很多参数需要调优,而且不同的参数组合可能会对模型的性能产生较大影响,所以需要一定的经验和时间去调整参数,读者有兴趣提高模型精确度可以参考请文给出的网址引入更多模型参数进行迭代调参,当然也可以对原始数据集进行更多样化的数据预处理,例如这里为了节约运行速度采用的是标签编码,但是标签编码可能导致模型错误的认为类别在数值上存在间隔关系,为了避免这种情况可以采用One-Hot编码,另外的不一一赘述,会在文章末尾给出历史文章相关链接进行参考,如更多异常值检验方法等
3.5 构建模型
model = lightgbm.LGBMRegressor(colsample_bytree=0.8, learning_rate=0.03, n_estimators=200, reg_lambda=0.5, subsample=0.6)
# 使用训练数据拟合模型
model.fit(X_train, y_train)
# 使用拟合好的模型对测试数据进行预测
y_pred = model.predict(X_test)
3.6 模型预测可视化
plt.figure(figsize=(15, 5), dpi = 300)
plt.plot(range(0, len(y_train)), np.power(10, y_train), c = 'b', label='训练集',linestyle = '-')
plt.plot(range(len(y_train), len(y_train)+len(y_test)), np.power(10, y_test), c = 'r', label='测试集', linestyle = '-.')
plt.plot(range(len(y_train), len(y_train)+len(y_test)), np.power(10, y_pred), c = 'y', label='预测', linestyle = '-.')
plt.legend()
plt.ylabel('价格')
plt.title('真实值与预测值对比')
plt.show()
4. 往期推荐
特征选择(嵌入法)—— 递归特征消除 RFE、 RFE-CV
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~