L7 机器学习——随机森林

一、集成学习 

1.1 什么是集成学习 

集成学习是通过构建并结合多个学习器来完成学习任务,其过程是:先产生一组“个体学习器”,再用某种策略将它们结合起来。个体学习器一般就是我们常见的机器学习算法,比如:决策树,神经网络等。

这里集成一般有两种:同质和异质。同质是指个体学习器全是同一类型,这种同质集成中的个体学习器又称“基学习器”。异质是指个体学习器包含不同类型得学习算法,比如同时包含决策树和神经网络。一般我们常用的都是同质的,即个体学习器都是同一类型的。

集成学习通过将多个基学习器结合,通常都会获得比单一学习器显著优越的泛化性能。

1.2 集成学习方法的分类

关于集成学习常见的结合方法,目前大多数分为两大类,第一类为Boosting,这一类个体之间学习器之间存在强依赖关系,必须使用串行的方法去学习。另外一类为Bagging,这一类方法个体学习器之间不存在强依赖关系,因此可用并行的方式去学习。

1.2.1 Bagging

Bagging的主要思想如下图所示,首先从数据集中采样出T个数据集,然后基于这T个数据集,每个训练出一个基分类器,再讲这些基分类器进行组合做出预测。Bagging在做预测时,对于分类任务,使用简单的投票法。对于回归任务使用简单平均法。若分类预测时出现两个类票数一样时,则随机选择一个。

从上面的图中也能够看出,Bagging非常适合并行处理,这对于大数据量下非常有好处。关于从原始数据集里采样出m个数据集,这里要说下,我们希望能够产生m个不同的子集,因为这样训练出来的基分类器具有比较大的差异,满足开头所说的“多样性”,有助于提高集成算法最终的性能。但是呢,又不能让基分类器性能太差,比如我们采样时,采样出来的子集每个都完全不相同,这样训练出来的基分类器性能就比较差,因为每个基分类器相当于只用了一小部分数据去训练。因此,Bagging中采样自助采样法(bootstrap sampling)。自助采样法是一种从给定训练集中有放回的均匀抽样,也就是说,每当选中一个样本,它等可能地被再次选中并被再次添加到训练集中。

1.2.2 Boosting

Boosting实际上是个迭代学习的过程。Boosting的工作机制为:先从初始训练集中训练出一个基学习器,再根据基学习器的表现对训练样本分布进行调整(比如增大被误分样本的权重,减小被正确分类样本的权重),使得先前基学习器做错的样本在后续的训练过程中受到更多关注,然后基于调整后的样本分布来训练下一个基学习器,如此重复,直到基学习器数目达到事先自定的值 T, 然后将这T个基学习器进行加权结合(比如错误率小的基学习器权重大,错误率大的基学习器权重小)。Boosting算法的典型代表有AdaBoost和XGBoost。Boosting算法可以用下图简略形象的描述下:

 1.2.3 Stacking

Stacking方法是先从初始数据集训练出初级学习器,然后“生成”一个新的数据集用于训练次级学习器。在这个新的数据集中,初级学习器的输出被当做样例输入特征,而初始样本的标记仍然被当做样例标记。其学习过程如下图所示:

这里有个需要注意的是, 如果直接用初级学习器的训练集来产生次级训练集, 则过拟合风险比较大,一般的处理办法是使用交叉验证来做。

二、随机森林

2.1 随机森林是什么?

随机森林(Random Forest, RF)是一种由决策树构成的(并行)集成算法,属于Bagging类型,通过组合多个弱分类器,最终结果通过投票或取均值,使得整体模型的结果具有较高的精确度和泛化性能,同时也有很好的稳定性,广泛应用在各种业务场景中。

随机森林有如此优良的表现,主要归功于「随机」和「森林」,一个使它具有抗过拟合能力,一个使它更加精准。RF使用了CART决策树作为基学习器。

2.2 代码实例

2.2.1 数据读取

本项目使用了一个人工合成的天气数据集,模拟了雨天、晴天、多云和雪天四种类型,在分析过程中,对数据进行了异常值处理,并通过描述性统计对数据进行了初步探索,接着,构建了随机森林模型进行预测,并生成了模型的重要特征图,该项目适用于初学者学习如何进行全面的数据分析和机器学习模型构建。数据集字段详情如下:

 

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report

data = pd.read_csv('./data/weather_classification_data.csv')
data

 2.2.2 数据检查与预处理

# Check the data information
data.info()

# Check the unique values of the class columns
characteristic = ['Cloud Cover','Season','Location','Weather Type']
for i in characteristic:
    print(f'{i}:')
    print(data[i].unique())
    print('-'*50)

注:若图片上的中文无法正确显示需要先设置字体

# Set the font to a Chinese font that is available on your system
plt.rcParams['font.sans-serif'] = ['SimHei']  # For Chinese characters
plt.rcParams['axes.unicode_minus'] = False  # To ensure minus signs are displayed correctly

# Plot the boxplot of the features
feature_map = {
    'Temperature': '温度',
    'Humidity': '湿度百分比',
    'Wind Speed': '风速',
    'Precipitation (%)': '降水量百分比',
    'Atmospheric Pressure': '大气压力',
    'UV Index': '紫外线指数',
    'Visibility (km)': '能见度'
}
plt.figure(figsize=(15, 10))

for i, (col, col_name) in enumerate(feature_map.items(), 1):
    plt.subplot(2, 4, i)
    sns.boxplot(y=data[col])
    plt.title(f'{col_name}的箱线图', fontsize=14)
    plt.ylabel('数值', fontsize=12)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

plt.tight_layout()
plt.show()

 

  1. 温度的异常值存在大量超出常识的温度,这里以超过60摄氏度认定为异常值,需要进行处理。
  2. 湿度百分比和降水量百分比,由于数值存在超过100%的值,认为超过100%的值为异常值,需要进行处理。
  3. 风速的高值可能是由于台风、龙卷风等极端天气事件,故不处理。
  4. 大气压力的异常值可能由于高海拔地区或气象现象(如低气压系统)引起。
  5. 能见度低可能是由于雾霾、雨雪等天气现象,这些异常值在特定条件下是正常的,故不处理。
print(f"温度超过60°C的数据量:{data[data['Temperature'] > 60].shape[0]},占比{round(data[data['Temperature'] > 60].shape[0] / data.shape[0] * 100,2)}%。")
print(f"湿度百分比超过100%的数据量:{data[data['Humidity'] > 100].shape[0]},占比{round(data[data['Humidity'] > 100].shape[0] / data.shape[0] * 100,2)}%。")
print(f"降雨量百分比超过100%的数据量:{data[data['Precipitation (%)'] > 100].shape[0]},占比{round(data[data['Precipitation (%)'] > 100].shape[0] / data.shape[0] * 100,2)}%。")

异常值占比很小,这里可以直接删除,或者将其赋值为100%,为了保持数据集的一致性和准确性,这里选择直接删除,可以避免它们对分析结果或模型训练产生负面影响。

# Reomve the outliers
print("删前的数据shape: ", data.shape)
data = data[(data['Temperature'] <= 60) & (data['Humidity'] <= 100) & (data['Precipitation (%)'] <= 100)]
print("删后的数据shape: ", data.shape)

 2.2.3 数据分析

# Check the descriptive statistics of the features
data.describe(include='all')

plt.figure(figsize=(20, 15))
plt.subplot(3, 4, 1)
sns.histplot(data['Temperature'], kde=True,bins=20)
plt.title('温度分布')
plt.xlabel('温度')
plt.ylabel('频数')

plt.subplot(3, 4, 2)
sns.boxplot(y=data['Humidity'])
plt.title('湿度百分比箱线图')
plt.ylabel('湿度百分比')

plt.subplot(3, 4, 3)
sns.histplot(data['Wind Speed'], kde=True,bins=20)
plt.title('风速分布')
plt.xlabel('风速(km/h)')
plt.ylabel('频数')

plt.subplot(3, 4, 4)
sns.boxplot(y=data['Precipitation (%)'])
plt.title('降雨量百分比箱线图')
plt.ylabel('降雨量百分比')

plt.subplot(3, 4, 5)
sns.countplot(x='Cloud Cover', data=data)
plt.title('云量 (描述)分布')
plt.xlabel('云量 (描述)')
plt.ylabel('频数')

plt.subplot(3, 4, 6)
sns.histplot(data['Atmospheric Pressure'], kde=True,bins=10)
plt.title('大气压分布')
plt.xlabel('气压 (hPa)')
plt.ylabel('频数')

plt.subplot(3, 4, 7)
sns.histplot(data['UV Index'], kde=True,bins=14)
plt.title('紫外线等级分布')
plt.xlabel('紫外线指数')
plt.ylabel('频数')

plt.subplot(3, 4, 8)
Season_counts = data['Season'].value_counts()
plt.pie(Season_counts, labels=Season_counts.index, autopct='%1.1f%%', startangle=140)
plt.title('季节分布')

plt.subplot(3, 4, 9)
sns.histplot(data['Visibility (km)'], kde=True,bins=10)
plt.title('能见度分布')
plt.xlabel('能见度(Km)')
plt.ylabel('频数')

plt.subplot(3, 4, 10)
sns.countplot(x='Location', data=data)
plt.title('地点分布')
plt.xlabel('地点')
plt.ylabel('频数')

plt.subplot(3, 4, (11,12))
sns.countplot(x='Weather Type', data=data)
plt.title('天气类型分布')
plt.xlabel('天气类型')
plt.ylabel('频数')

plt.tight_layout()
plt.show()

 

  • 温度:温度数据集中在较合理的范围内(主要在0°C到40°C),极端高温(>60°C)的数据已被清理。整体分布稍微左偏,说明较低温度的情况较多。
  • 湿度:湿度分布在合理范围内(20%到100%),中位数和平均值接近,说明数据分布相对对称。
  • 风速:数据集中在较低的风速范围内(0-20 km/h),极端高风速事件少见,数据左偏,低风速情况更为常见。
  • 降水量:降水量分布较均匀,中位数为54%,反映了各种天气条件下的降水概率。
  • 大气压力:大气压力主要集中在标准范围(990-1020 hPa),数据分布正常,没有明显的异常值。
  • 紫外线指数:紫外线指数大多较低,极端高指数的情况罕见,表明大部分时间的紫外线风险较低。
  • 能见度:能见度数据大多集中在5 km左右,反映了多数情况下的中等能见度条件。
  • 云量:多云(overcast)在数据集中出现频率较高。
  • 季节分布:冬季数据最多,可能是数据采集季节或地区气候特征的反映。
  • 地点分布:主要来自山区和内陆地区,这可能影响天气类型和其他气象特征的分布。
  • 天气类型:分布比较均匀,没有单一类别占据绝对优势。

2.2.4 随机森林模型

首先对分类特征进行编码

# Encode the categorical features
new_data = data.copy()
label_encoders = {}
categorical_features = ['Cloud Cover', 'Season', 'Location', 'Weather Type']
for feature in categorical_features:
    le = LabelEncoder()
    new_data[feature] = le.fit_transform(data[feature])
    label_encoders[feature] = le

for feature in categorical_features:
    print(f"'{feature}'特征的对应关系:")
    for index, class_ in enumerate(label_encoders[feature].classes_):
        print(f"  {index}: {class_}")
    print('-'*50)

划分数据集

# Split the data into training and testing sets
x = new_data.drop(['Weather Type'],axis=1)
y = new_data['Weather Type']

x_train,x_test,y_train,y_test = train_test_split(x,y,
                                                 test_size=0.3,
                                                 random_state=15) 
print('x_train:', x_train.shape)
print('x_test:', x_test.shape)
print('y_train:', y_train.shape)
print('y_test:', y_test.shape)

训练模型并进行预测

# Train the model using Random Forest Classifier
rf_clf = RandomForestClassifier(random_state=15)
rf_clf.fit(x_train, y_train)

# Predict the test set
y_pred_rf = rf_clf.predict(x_test)
class_report_rf = classification_report(y_test, y_pred_rf)
print(class_report_rf)

 2.2.5 结果分析

# Plot the feature importance
feature_importances = rf_clf.feature_importances_
features_rf = pd.DataFrame({'特征': x.columns, '重要度': feature_importances})
features_rf.sort_values(by='重要度', ascending=False, inplace=True)
plt.figure(figsize=(10, 8))
sns.barplot(x='重要度', y='特征', data=features_rf)
plt.xlabel('重要度')
plt.ylabel('特征')
plt.title('随机森林特征图')
plt.show()

随机森林模型的预测准确率很高,并且通过特征度分析,发现影响模型的主要因素有:温度、湿度、紫外线指数、能见度、大气压力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值