当前位置: 首页 > news >正文

【零基础学AI】第13讲:随机森林实战 - 用户行为预测

在这里插入图片描述

本节课你将学到

  • 理解随机森林的工作原理和优势
  • 掌握集成学习的核心思想
  • 学会构建随机森林预测模型
  • 完成用户行为预测项目
  • 对比单棵决策树与随机森林的效果

开始之前

环境要求

  • Python 3.8+
  • 操作系统:Windows/Mac/Linux

需要安装的包

pip install pandas numpy scikit-learn matplotlib seaborn plotly

前置知识

  • 第12讲:决策树模型实战
  • 基本的机器学习概念
  • Python数据处理基础

核心概念

什么是随机森林?

想象一下你要做一个重要决定,比如选择投资哪只股票:

一个专家的建议(单棵决策树):

  • 只听一个分析师的意见
  • 可能很准确,但也可能有偏见
  • 如果这个专家判断错误,你就亏了

一群专家的集体建议(随机森林):

  • 询问100个不同的分析师
  • 每个分析师看到的信息稍有不同
  • 最后采用多数人的意见
  • 即使个别专家错了,整体判断更可靠

这就是随机森林的核心思想:集思广益,降低风险

随机森林的"两个随机"

  1. 样本随机(Bootstrap抽样):

    • 每棵树看到的训练数据不完全相同
    • 就像让每个专家只看部分案例
  2. 特征随机

    • 每棵树在分裂时只考虑部分特征
    • 就像让每个专家只关注某些指标

随机森林的优势

  • 准确性更高:多棵树投票,减少单棵树的错误
  • 不容易过拟合:随机性降低了过度学习训练数据的风险
  • 处理大数据集:可以并行训练多棵树
  • 特征重要性:自动计算特征的重要程度
  • 处理缺失值:对缺失数据有较好的容忍性

代码实战

步骤1:准备用户行为数据

# 导入必要的库
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_classification
import plotly.graph_objects as go
import plotly.express as px
import warnings
warnings.filterwarnings('ignore')# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = Falseprint("🌲 随机森林用户行为预测项目")
print("=" * 50)# 生成用户行为数据
def generate_user_behavior_data(n_users=2000):"""生成模拟的用户行为数据"""print("正在生成用户行为数据...")np.random.seed(42)# 基础用户信息data = {'年龄': np.random.randint(16, 65, n_users),'性别': np.random.choice([0, 1], n_users),  # 0:女, 1:男'城市等级': np.random.choice([1, 2, 3, 4], n_users),  # 1-4线城市'收入水平': np.random.randint(3000, 50000, n_users),# 行为特征'日均使用时长_分钟': np.random.randint(10, 300, n_users),'周活跃天数': np.random.randint(1, 8, n_users),'月消费金额': np.random.randint(0, 5000, n_users),'好友数量': np.random.randint(0, 1000, n_users),'发帖数量': np.random.randint(0, 100, n_users),'点赞数量': np.random.randint(0, 500, n_users),# 设备和习惯'设备价值': np.random.randint(1000, 8000, n_users),'使用WiFi比例': np.random.uniform(0.3, 1.0, n_users),'夜间使用比例': np.random.uniform(0.1, 0.8, n_users),'周末使用比例': np.random.uniform(0.2, 0.9, n_users),# 内容偏好'视频偏好': np.random.uniform(0, 1, n_users),'购物偏好': np.random.uniform(0, 1, n_users),'社交偏好': np.random.uniform(0, 1, n_users),'游戏偏好': np.random.uniform(0, 1, n_users),}df = pd.DataFrame(data)# 基于多个因素创建目标变量:用户是否会在下个月继续活跃# 这里设计一个复杂的规则来模拟真实情况activity_score = (# 使用习惯影响 (40%)(df['日均使用时长_分钟'] / 300) * 0.15 +(df['周活跃天数'] / 7) * 0.15 +(df['月消费金额'] / 5000) * 0.10 +# 社交参与度影响 (30%)(df['好友数量'] / 1000) * 0.10 +(df['发帖数量'] / 100) * 0.10 +(df['点赞数量'] / 500) * 0.10 +# 个人因素影响 (20%)((df['年龄'] - 16) / 49) * 0.05 +  # 年龄适中的用户更活跃(df['收入水平'] / 50000) * 0.10 +((5 - df['城市等级']) / 4) * 0.05 +  # 一线城市用户更活跃# 内容偏好影响 (10%)(df['视频偏好'] + df['购物偏好'] + df['社交偏好'] + df['游戏偏好']) / 4 * 0.10)# 添加随机噪声,模拟现实中的不确定性activity_score += np.random.normal(0, 0.15, n_users)activity_score = np.clip(activity_score, 0, 1)# 生成二分类标签df['下月活跃'] = (activity_score > 0.6).astype(int)print(f"数据生成完成!")print(f"总用户数: {n_users}")print(f"活跃用户数: {df['下月活跃'].sum()}")print(f"活跃率: {df['下月活跃'].mean():.2%}")return df# 生成数据
df = generate_user_behavior_data(2000)
print("\n数据预览:")
print(df.head())

步骤2:数据探索分析

# 数据基本信息
print(f"\n=== 数据基本信息 ===")
print(f"数据形状: {df.shape}")
print(f"缺失值: {df.isnull().sum().sum()}")# 目标变量分布
target_dist = df['下月活跃'].value_counts()
print(f"\n目标变量分布:")
print(f"不活跃用户: {target_dist[0]} ({target_dist[0]/len(df):.1%})")
print(f"活跃用户: {target_dist[1]} ({target_dist[1]/len(df):.1%})")# 可视化关键特征分布
def plot_feature_analysis(df):"""绘制特征分析图"""fig, axes = plt.subplots(3, 3, figsize=(18, 15))fig.suptitle('用户行为特征分析', fontsize=16)# 关键特征列表key_features = ['年龄', '日均使用时长_分钟', '周活跃天数', '月消费金额', '好友数量', '发帖数量','收入水平', '设备价值', '点赞数量']for i, feature in enumerate(key_features):row, col = i // 3, i % 3# 按活跃状态分组绘制直方图for active_status, color, label in [(0, 'red', '不活跃'), (1, 'green', '活跃')]:data = df[df['下月活跃'] == active_status][feature]axes[row, col].hist(data, alpha=0.6, bins=20, color=color, label=label)axes[row, col].set_title(f'{feature}分布')axes[row, col].set_xlabel(feature)axes[row, col].set_ylabel('用户数')axes[row, col].legend()plt.tight_layout()plt.show()plot_feature_analysis(df)# 特征相关性分析
print("\n=== 特征相关性分析 ===")
# 选择数值型特征进行相关性分析
numeric_features = df.select_dtypes(include=[np.number]).columns.tolist()correlation_matrix = df[numeric_features].corr()# 绘制相关性热力图
plt.figure(figsize=(14, 12))
mask = np.triu(np.ones_like(correlation_matrix, dtype=bool))  # 只显示下三角
sns.heatmap(correlation_matrix, mask=mask, annot=True, cmap='coolwarm', center=0, square=True, fmt='.2f', cbar_kws={'shrink': 0.8})
plt.title('特征相关性热力图')
plt.xticks(rotation=45, ha='right')
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()

步骤3:数据预处理与模型准备

# 准备特征和标签
feature_columns = [col for col in df.columns if col != '下月活跃']
X = df[feature_columns]
y = df['下月活跃']print(f"特征数量: {X.shape[1]}")
print(f"样本数量: {X.shape[0]}")# 数据集分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y  # 保持类别比例
)print(f"\n=== 数据分割结果 ===")
print(f"训练集: {X_train.shape[0]} 样本")
print(f"测试集: {X_test.shape[0]} 样本")
print(f"训练集活跃率: {y_train.mean():.2%}")
print(f"测试集活跃率: {y_test.mean():.2%}")# ⚠️ 注意:随机森林对特征尺度不敏感,所以不需要标准化
# 但如果要与其他算法对比,可能需要标准化处理

步骤4:构建随机森林模型

print(f"\n=== 构建随机森林模型 ===")# 创建随机森林分类器
# 参数详解:
# n_estimators: 树的数量,越多通常越好,但计算成本也越高
# max_depth: 每棵树的最大深度,防止过拟合
# min_samples_split: 内部节点分裂所需的最小样本数
# min_samples_leaf: 叶子节点的最小样本数
# max_features: 每次分裂时考虑的特征数量
# random_state: 随机种子,确保结果可重现
# n_jobs: 并行处理的CPU核数,-1表示使用所有核rf_classifier = RandomForestClassifier(n_estimators=100,        # 100棵树max_depth=10,           # 最大深度10min_samples_split=20,   # 分裂最小样本数min_samples_leaf=5,     # 叶子最小样本数max_features='sqrt',    # 每次分裂考虑sqrt(总特征数)个特征random_state=42,        # 随机种子n_jobs=-1              # 使用所有CPU核心并行训练
)# 训练模型
print("开始训练随机森林模型...")
rf_classifier.fit(X_train, y_train)
print("随机森林训练完成!")# 预测
y_train_pred_rf = rf_classifier.predict(X_train)
y_test_pred_rf = rf_classifier.predict(X_test)# 计算准确率
train_accuracy_rf = accuracy_score(y_train, y_train_pred_rf)
test_accuracy_rf = accuracy_score(y_test, y_test_pred_rf)print(f"\n随机森林性能:")
print(f"训练集准确率: {train_accuracy_rf:.4f} ({train_accuracy_rf*100:.2f}%)")
print(f"测试集准确率: {test_accuracy_rf:.4f} ({test_accuracy_rf*100:.2f}%)")
print(f"泛化差距: {abs(train_accuracy_rf - test_accuracy_rf):.4f}")# 为了对比,我们也训练一棵单独的决策树
print(f"\n=== 对比:训练单棵决策树 ===")
dt_classifier = DecisionTreeClassifier(max_depth=10,min_samples_split=20,min_samples_leaf=5,random_state=42
)dt_classifier.fit(X_train, y_train)
y_test_pred_dt = dt_classifier.predict(X_test)
test_accuracy_dt = accuracy_score(y_test, y_test_pred_dt)print(f"单棵决策树准确率: {test_accuracy_dt:.4f} ({test_accuracy_dt*100:.2f}%)")
print(f"随机森林 vs 决策树提升: {test_accuracy_rf - test_accuracy_dt:.4f} ({(test_accuracy_rf - test_accuracy_dt)*100:.2f}%)")if test_accuracy_rf > test_accuracy_dt:print("✅ 随机森林性能更好!")
else:print("⚠️ 单棵决策树表现意外地更好,可能需要调参")

步骤5:模型详细评估

print(f"\n=== 模型详细评估 ===")# 分类报告
print("随机森林分类报告:")
print(classification_report(y_test, y_test_pred_rf, target_names=['不活跃', '活跃']))print("\n单棵决策树分类报告:")
print(classification_report(y_test, y_test_pred_dt, target_names=['不活跃', '活跃']))# 混淆矩阵对比
fig, axes = plt.subplots(1, 2, figsize=(12, 5))# 随机森林混淆矩阵
cm_rf = confusion_matrix(y_test, y_test_pred_rf)
sns.heatmap(cm_rf, annot=True, fmt='d', cmap='Blues', ax=axes[0],xticklabels=['不活跃', '活跃'], yticklabels=['不活跃', '活跃'])
axes[0].set_title(f'随机森林混淆矩阵\n准确率: {test_accuracy_rf:.3f}')
axes[0].set_xlabel('预测结果')
axes[0].set_ylabel('真实结果')# 决策树混淆矩阵
cm_dt = confusion_matrix(y_test, y_test_pred_dt)
sns.heatmap(cm_dt, annot=True, fmt='d', cmap='Oranges', ax=axes[1],xticklabels=['不活跃', '活跃'], yticklabels=['不活跃', '活跃'])
axes[1].set_title(f'单棵决策树混淆矩阵\n准确率: {test_accuracy_dt:.3f}')
axes[1].set_xlabel('预测结果')
axes[1].set_ylabel('真实结果')plt.tight_layout()
plt.show()# 交叉验证评估模型稳定性
print(f"\n=== 交叉验证评估 ===")
cv_scores_rf = cross_val_score(rf_classifier, X_train, y_train, cv=5, scoring='accuracy')
cv_scores_dt = cross_val_score(dt_classifier, X_train, y_train, cv=5, scoring='accuracy')print(f"随机森林 5折交叉验证:")
print(f"平均准确率: {cv_scores_rf.mean():.4f}{cv_scores_rf.std()*2:.4f})")
print(f"各折准确率: {cv_scores_rf}")print(f"\n单棵决策树 5折交叉验证:")
print(f"平均准确率: {cv_scores_dt.mean():.4f}{cv_scores_dt.std()*2:.4f})")
print(f"各折准确率: {cv_scores_dt}")# 模型稳定性对比
if cv_scores_rf.std() < cv_scores_dt.std():print("✅ 随机森林更稳定(方差更小)")
else:print("⚠️ 决策树在这次实验中更稳定")

步骤6:特征重要性分析

print(f"\n=== 特征重要性分析 ===")# 获取特征重要性
feature_importance_rf = pd.DataFrame({'特征': feature_columns,'重要性': rf_classifier.feature_importances_
}).sort_values('重要性', ascending=False)print("随机森林特征重要性排序(Top 10):")
print(feature_importance_rf.head(10))# 可视化特征重要性
plt.figure(figsize=(12, 8))
top_features = feature_importance_rf.head(15)  # 显示前15个重要特征plt.barh(range(len(top_features)), top_features['重要性'])
plt.yticks(range(len(top_features)), top_features['特征'])
plt.xlabel('重要性分数')
plt.title('随机森林特征重要性排序(Top 15)')
plt.gca().invert_yaxis()  # 重要性高的在上面# 在柱状图上添加数值标签
for i, v in enumerate(top_features['重要性']):plt.text(v + 0.001, i, f'{v:.3f}', va='center')plt.tight_layout()
plt.show()# 累积重要性分析
cumulative_importance = feature_importance_rf['重要性'].cumsum()
print(f"\n累积重要性分析:")
print(f"前5个特征累积重要性: {cumulative_importance.iloc[4]:.3f}")
print(f"前10个特征累积重要性: {cumulative_importance.iloc[9]:.3f}")
print(f"前15个特征累积重要性: {cumulative_importance.iloc[14]:.3f}")# 绘制累积重要性曲线
plt.figure(figsize=(10, 6))
plt.plot(range(1, len(cumulative_importance)+1), cumulative_importance, 'b-', linewidth=2)
plt.axhline(y=0.8, color='r', linestyle='--', label='80%重要性线')
plt.axhline(y=0.9, color='orange', linestyle='--', label='90%重要性线')
plt.xlabel('特征数量')
plt.ylabel('累积重要性')
plt.title('特征累积重要性曲线')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()# 找到达到80%重要性所需的特征数量
features_for_80_percent = np.where(cumulative_importance >= 0.8)[0][0] + 1
print(f"\n💡 洞察:仅需前{features_for_80_percent}个特征就能达到80%的重要性")

步骤7:模型预测概率分析

print(f"\n=== 预测概率分析 ===")# 获取预测概率
y_test_proba_rf = rf_classifier.predict_proba(X_test)[:, 1]  # 获取活跃概率
y_test_proba_dt = dt_classifier.predict_proba(X_test)[:, 1]   # 获取活跃概率# 创建概率分析DataFrame
prob_analysis = pd.DataFrame({'真实标签': y_test,'随机森林概率': y_test_proba_rf,'决策树概率': y_test_proba_dt,'随机森林预测': y_test_pred_rf,'决策树预测': y_test_pred_dt
})print("预测概率统计:")
print(f"随机森林概率 - 均值: {y_test_proba_rf.mean():.3f}, 标准差: {y_test_proba_rf.std():.3f}")
print(f"决策树概率 - 均值: {y_test_proba_dt.mean():.3f}, 标准差: {y_test_proba_dt.std():.3f}")# 绘制概率分布图
fig, axes = plt.subplots(2, 2, figsize=(14, 10))# 随机森林概率分布
axes[0, 0].hist(y_test_proba_rf[y_test == 0], bins=20, alpha=0.7, color='red', label='真实不活跃')
axes[0, 0].hist(y_test_proba_rf[y_test == 1], bins=20, alpha=0.7, color='green', label='真实活跃')
axes[0, 0].set_title('随机森林预测概率分布')
axes[0, 0].set_xlabel('活跃概率')
axes[0, 0].set_ylabel('用户数')
axes[0, 0].legend()# 决策树概率分布
axes[0, 1].hist(y_test_proba_dt[y_test == 0], bins=20, alpha=0.7, color='red', label='真实不活跃')
axes[0, 1].hist(y_test_proba_dt[y_test == 1], bins=20, alpha=0.7, color='green', label='真实活跃')
axes[0, 1].set_title('决策树预测概率分布')
axes[0, 1].set_xlabel('活跃概率')
axes[0, 1].set_ylabel('用户数')
axes[0, 1].legend()# 概率对比散点图
axes[1, 0].scatter(y_test_proba_dt, y_test_proba_rf, alpha=0.6, c=y_test, cmap='RdYlGn')
axes[1, 0].plot([0, 1], [0, 1], 'r--', linewidth=2)  # 对角线
axes[1, 0].set_xlabel('决策树概率')
axes[1, 0].set_ylabel('随机森林概率')
axes[1, 0].set_title('两种模型概率对比')# 预测置信度分析
rf_confidence = np.abs(y_test_proba_rf - 0.5) * 2  # 0.5为不确定,1为完全确定
dt_confidence = np.abs(y_test_proba_dt - 0.5) * 2axes[1, 1].hist(rf_confidence, bins=20, alpha=0.7, color='blue', label='随机森林')
axes[1, 1].hist(dt_confidence, bins=20, alpha=0.7, color='orange', label='决策树')
axes[1, 1].set_xlabel('预测置信度')
axes[1, 1].set_ylabel('用户数')
axes[1, 1].set_title('模型预测置信度分布')
axes[1, 1].legend()plt.tight_layout()
plt.show()print(f"\n模型置信度分析:")
print(f"随机森林平均置信度: {rf_confidence.mean():.3f}")
print(f"决策树平均置信度: {dt_confidence.mean():.3f}")if rf_confidence.mean() > dt_confidence.mean():print("✅ 随机森林预测更有信心")
else:print("⚠️ 决策树预测更有信心")

步骤8:新用户预测示例

print(f"\n=== 新用户预测示例 ===")# 创建几个典型的新用户
new_users = pd.DataFrame({# 高活跃潜力用户'年龄': [25, 35, 45, 28],'性别': [1, 0, 1, 0],'城市等级': [1, 2, 1, 3],'收入水平': [15000, 25000, 35000, 8000],'日均使用时长_分钟': [120, 180, 90, 200],'周活跃天数': [6, 7, 5, 7],'月消费金额': [800, 1500, 2000, 300],'好友数量': [150, 300, 80, 500],'发帖数量': [20, 35, 10, 45],'点赞数量': [100, 200, 50, 250],'设备价值': [3000, 5000, 7000, 2000],'使用WiFi比例': [0.8, 0.9, 0.7, 0.95],'夜间使用比例': [0.3, 0.2, 0.4, 0.6],'周末使用比例': [0.6, 0.7, 0.5, 0.8],'视频偏好': [0.8, 0.6, 0.4, 0.9],'购物偏好': [0.7, 0.8, 0.9, 0.3],'社交偏好': [0.9, 0.5, 0.3, 0.8],'游戏偏好': [0.6, 0.2, 0.1, 0.7]
})# 用随机森林进行预测
rf_predictions = rf_classifier.predict(new_users)
rf_probabilities = rf_classifier.predict_proba(new_users)# 用决策树进行预测(对比)
dt_predictions = dt_classifier.predict(new_users)
dt_probabilities = dt_classifier.predict_proba(new_users)print("新用户预测结果对比:")
print("=" * 80)user_types = ["年轻白领", "中产主妇", "高收入中年", "学生党"]for i in range(len(new_users)):print(f"\n👤 {user_types[i]} (用户{i+1}):")print(f"   年龄: {new_users.iloc[i]['年龄']}岁")print(f"   收入: {new_users.iloc[i]['收入水平']}元")print(f"   日使用时长: {new_users.iloc[i]['日均使用时长_分钟']}分钟")print(f"   月消费: {new_users.iloc[i]['月消费金额']}元")# 随机森林预测rf_prob = rf_probabilities[i][1]rf_pred = rf_predictions[i]# 决策树预测dt_prob = dt_probabilities[i][1]dt_pred = dt_predictions[i]print(f"   随机森林: {'🎯 活跃' if rf_pred == 1 else '❌ 不活跃'} (概率: {rf_prob:.2%})")print(f"   决策树:   {'🎯 活跃' if dt_pred == 1 else '❌ 不活跃'} (概率: {dt_prob:.2%})")# 预测一致性检查if rf_pred == dt_pred:print(f"   ✅ 两种模型预测一致")else:print(f"   ⚠️ 模型预测不一致,建议进一步分析")# 预测置信度rf_confidence = abs(rf_prob - 0.5) * 2print(f"   随机森林置信度: {rf_confidence:.2%}")# 批量预测结果汇总
results_summary = pd.DataFrame({'用户类型': user_types,'随机森林预测': ['活跃' if p == 1 else '不活跃' for p in rf_predictions],'随机森林概率': [f"{p:.2%}" for p in rf_probabilities[:, 1]],'决策树预测': ['活跃' if p == 1 else '不活跃' for p in dt_predictions],'决策树概率': [f"{p:.2%}" for p in dt_probabilities[:, 1]],'预测一致': ['是' if rf_predictions[i] == dt_predictions[i] else '否' for i in range(len(rf_predictions))]
})print(f"\n📊 预测结果汇总表:")
print(results_summary.to_string(index=False))

完整项目

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
随机森林用户行为预测系统
功能:预测用户下个月是否会保持活跃状态
作者:AI实战60讲
日期:2025年
"""import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, roc_auc_score
import matplotlib.pyplot as plt
import seaborn as sns
import joblib
import warnings
warnings.filterwarnings('ignore')class UserBehaviorPredictor:"""用户行为预测类"""def __init__(self):self.rf_model = Noneself.dt_model = Noneself.feature_columns = Noneself.is_trained = Falseself.feature_importance_df = Nonedef generate_user_data(self, n_users=2000):"""生成模拟用户行为数据"""print(f"🔄 正在生成{n_users}个用户的行为数据...")np.random.seed(42)# 基础特征data = {'年龄': np.random.randint(16, 65, n_users),'性别': np.random.choice([0, 1], n_users),'城市等级': np.random.choice([1, 2, 3, 4], n_users),'收入水平': np.random.randint(3000, 50000, n_users),# 使用行为'日均使用时长_分钟': np.random.randint(10, 300, n_users),'周活跃天数': np.random.randint(1, 8, n_users),'月消费金额': np.random.randint(0, 5000, n_users),# 社交行为'好友数量': np.random.randint(0, 1000, n_users),'发帖数量': np.random.randint(0, 100, n_users),'点赞数量': np.random.randint(0, 500, n_users),'评论数量': np.random.randint(0, 200, n_users),'分享数量': np.random.randint(0, 50, n_users),# 设备与习惯'设备价值': np.random.randint(1000, 8000, n_users),'使用WiFi比例': np.random.uniform(0.3, 1.0, n_users),'夜间使用比例': np.random.uniform(0.1, 0.8, n_users),'周末使用比例': np.random.uniform(0.2, 0.9, n_users),# 内容偏好'视频偏好': np.random.uniform(0, 1, n_users),'购物偏好': np.random.uniform(0, 1, n_users),'社交偏好': np.random.uniform(0, 1, n_users),'游戏偏好': np.random.uniform(0, 1, n_users),'新闻偏好': np.random.uniform(0, 1, n_users),}df = pd.DataFrame(data)# 基于复杂规则生成活跃标签activity_score = self._calculate_activity_score(df)df['下月活跃'] = (activity_score > 0.6).astype(int)print(f"✅ 数据生成完成!")print(f"   总用户数: {n_users}")print(f"   活跃用户: {df['下月活跃'].sum()} ({df['下月活跃'].mean():.1%})")return dfdef _calculate_activity_score(self, df):"""计算用户活跃度分数"""# 使用加权评分系统score = (# 核心使用行为 (50%)(df['日均使用时长_分钟'] / 300) * 0.20 +(df['周活跃天数'] / 7) * 0.15 +(df['月消费金额'] / 5000) * 0.15 +# 社交参与度 (30%)(np.log1p(df['好友数量']) / np.log1p(1000)) * 0.08 +(df['发帖数量'] / 100) * 0.07 +(df['点赞数量'] / 500) * 0.07 +(df['评论数量'] / 200) * 0.05 +(df['分享数量'] / 50) * 0.03 +# 用户属性 (15%)(np.clip((40 - abs(df['年龄'] - 30)) / 40, 0, 1)) * 0.05 +  # 30岁左右最活跃(df['收入水平'] / 50000) * 0.05 +((5 - df['城市等级']) / 4) * 0.05 +  # 一线城市更活跃# 内容偏好多样性 (5%)(df[['视频偏好', '购物偏好', '社交偏好', '游戏偏好', '新闻偏好']].mean(axis=1)) * 0.05)# 添加随机噪声score += np.random.normal(0, 0.12, len(df))return np.clip(score, 0, 1)def explore_data(self, df):"""数据探索分析"""print(f"\n📊 === 数据探索分析 ===")print(f"数据维度: {df.shape}")print(f"缺失值: {df.isnull().sum().sum()}")# 目标变量分布target_counts = df['下月活跃'].value_counts()print(f"\n目标变量分布:")print(f"  不活跃: {target_counts[0]} ({target_counts[0]/len(df):.1%})")print(f"  活跃:   {target_counts[1]} ({target_counts[1]/len(df):.1%})")# 关键统计信息numeric_cols = df.select_dtypes(include=[np.number]).columnsprint(f"\n数值特征统计:")print(df[numeric_cols].describe().round(2))self._plot_data_analysis(df)def _plot_data_analysis(self, df):"""绘制数据分析图"""plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = False# 关键特征可视化key_features = ['日均使用时长_分钟', '周活跃天数', '月消费金额', '好友数量','发帖数量', '点赞数量', '年龄', '收入水平']fig, axes = plt.subplots(2, 4, figsize=(20, 10))fig.suptitle('用户行为特征分布分析', fontsize=16)for i, feature in enumerate(key_features):row, col = i // 4, i % 4# 按活跃状态分组绘制for status, color, label in [(0, 'red', '不活跃'), (1, 'green', '活跃')]:data = df[df['下月活跃'] == status][feature]axes[row, col].hist(data, alpha=0.6, bins=15, color=color, label=label)axes[row, col].set_title(f'{feature}')axes[row, col].legend()axes[row, col].grid(True, alpha=0.3)plt.tight_layout()plt.show()# 相关性分析self._plot_correlation_analysis(df)def _plot_correlation_analysis(self, df):"""相关性分析图"""numeric_features = df.select_dtypes(include=[np.number]).columnscorrelation_matrix = df[numeric_features].corr()plt.figure(figsize=(16, 14))mask = np.triu(np.ones_like(correlation_matrix, dtype=bool))sns.heatmap(correlation_matrix, mask=mask, annot=True, cmap='coolwarm',center=0, square=True, fmt='.2f')plt.title('特征相关性热力图')plt.xticks(rotation=45, ha='right')plt.tight_layout()plt.show()def train_models(self, df, test_size=0.2):"""训练随机森林和决策树模型"""print(f"\n🚀 === 开始训练模型 ===")# 准备数据self.feature_columns = [col for col in df.columns if col != '下月活跃']X = df[self.feature_columns]y = df['下月活跃']# 数据分割X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=42, stratify=y)# 保存测试数据self.X_test = X_testself.y_test = y_testprint(f"训练集: {len(X_train)} 样本")print(f"测试集: {len(X_test)} 样本")# 训练随机森林print(f"\n🌲 训练随机森林模型...")self.rf_model = RandomForestClassifier(n_estimators=150,max_depth=12,min_samples_split=20,min_samples_leaf=5,max_features='sqrt',random_state=42,n_jobs=-1,class_weight='balanced'  # 处理类别不平衡)self.rf_model.fit(X_train, y_train)# 训练决策树(对比用)print(f"🌳 训练单棵决策树模型...")self.dt_model = DecisionTreeClassifier(max_depth=12,min_samples_split=20,min_samples_leaf=5,random_state=42,class_weight='balanced')self.dt_model.fit(X_train, y_train)self.is_trained = Trueprint(f"✅ 模型训练完成!")# 评估模型self._evaluate_models()def _evaluate_models(self):"""评估模型性能"""print(f"\n📈 === 模型性能评估 ===")# 预测rf_pred = self.rf_model.predict(self.X_test)dt_pred = self.dt_model.predict(self.X_test)rf_proba = self.rf_model.predict_proba(self.X_test)[:, 1]dt_proba = self.dt_model.predict_proba(self.X_test)[:, 1]# 计算指标rf_accuracy = accuracy_score(self.y_test, rf_pred)dt_accuracy = accuracy_score(self.y_test, dt_pred)rf_auc = roc_auc_score(self.y_test, rf_proba)dt_auc = roc_auc_score(self.y_test, dt_proba)print(f"随机森林性能:")print(f"  准确率: {rf_accuracy:.4f} ({rf_accuracy*100:.2f}%)")print(f"  AUC分数: {rf_auc:.4f}")print(f"\n单棵决策树性能:")print(f"  准确率: {dt_accuracy:.4f} ({dt_accuracy*100:.2f}%)")print(f"  AUC分数: {dt_auc:.4f}")improvement = rf_accuracy - dt_accuracyprint(f"\n📊 随机森林提升: {improvement:.4f} ({improvement*100:.2f}%)")if improvement > 0:print("✅ 随机森林性能更优!")# 详细分类报告print(f"\n随机森林详细报告:")print(classification_report(self.y_test, rf_pred, target_names=['不活跃', '活跃']))# 交叉验证self._cross_validation()# 混淆矩阵可视化self._plot_confusion_matrices(rf_pred, dt_pred)def _cross_validation(self):"""交叉验证评估"""print(f"\n🔄 交叉验证评估:")X = pd.concat([self.X_test, self.X_test.iloc[:len(self.X_test)//4]])  # 扩展数据用于CVy = pd.concat([self.y_test, self.y_test.iloc[:len(self.y_test)//4]])rf_cv_scores = cross_val_score(self.rf_model, X, y, cv=5, scoring='accuracy')dt_cv_scores = cross_val_score(self.dt_model, X, y, cv=5, scoring='accuracy')print(f"随机森林 CV: {rf_cv_scores.mean():.4f}{rf_cv_scores.std()*2:.4f})")print(f"决策树 CV:   {dt_cv_scores.mean():.4f}{dt_cv_scores.std()*2:.4f})")if rf_cv_scores.std() < dt_cv_scores.std():print("✅ 随机森林更稳定(方差更小)")def _plot_confusion_matrices(self, rf_pred, dt_pred):"""绘制混淆矩阵"""fig, axes = plt.subplots(1, 2, figsize=(12, 5))# 随机森林混淆矩阵cm_rf = confusion_matrix(self.y_test, rf_pred)sns.heatmap(cm_rf, annot=True, fmt='d', cmap='Blues', ax=axes[0],xticklabels=['不活跃', '活跃'], yticklabels=['不活跃', '活跃'])axes[0].set_title('随机森林混淆矩阵')# 决策树混淆矩阵cm_dt = confusion_matrix(self.y_test, dt_pred)sns.heatmap(cm_dt, annot=True, fmt='d', cmap='Oranges', ax=axes[1],xticklabels=['不活跃', '活跃'], yticklabels=['不活跃', '活跃'])axes[1].set_title('决策树混淆矩阵')plt.tight_layout()plt.show()def analyze_feature_importance(self):"""特征重要性分析"""if not self.is_trained:print("❌ 请先训练模型!")returnprint(f"\n🎯 === 特征重要性分析 ===")# 获取特征重要性self.feature_importance_df = pd.DataFrame({'特征': self.feature_columns,'重要性': self.rf_model.feature_importances_}).sort_values('重要性', ascending=False)print("Top 10 重要特征:")print(self.feature_importance_df.head(10).to_string(index=False))# 可视化self._plot_feature_importance()# 累积重要性分析self._analyze_cumulative_importance()def _plot_feature_importance(self):"""绘制特征重要性图"""plt.figure(figsize=(12, 8))top_features = self.feature_importance_df.head(12)bars = plt.barh(range(len(top_features)), top_features['重要性'])plt.yticks(range(len(top_features)), top_features['特征'])plt.xlabel('重要性分数')plt.title('随机森林特征重要性排序 (Top 12)')plt.gca().invert_yaxis()# 添加数值标签for i, (bar, value) in enumerate(zip(bars, top_features['重要性'])):plt.text(value + 0.001, i, f'{value:.3f}', va='center')plt.tight_layout()plt.show()def _analyze_cumulative_importance(self):"""累积重要性分析"""cumulative = self.feature_importance_df['重要性'].cumsum()# 找到达到特定阈值的特征数量features_80 = np.where(cumulative >= 0.8)[0][0] + 1features_90 = np.where(cumulative >= 0.9)[0][0] + 1print(f"\n累积重要性分析:")print(f"  前{features_80}个特征 → 80%重要性")print(f"  前{features_90}个特征 → 90%重要性")# 绘制累积重要性曲线plt.figure(figsize=(10, 6))plt.plot(range(1, len(cumulative)+1), cumulative, 'b-', linewidth=2)plt.axhline(y=0.8, color='r', linestyle='--', label='80%线')plt.axhline(y=0.9, color='orange', linestyle='--', label='90%线')plt.axvline(x=features_80, color='r', linestyle=':', alpha=0.7)plt.axvline(x=features_90, color='orange', linestyle=':', alpha=0.7)plt.xlabel('特征数量')plt.ylabel('累积重要性')plt.title('特征累积重要性曲线')plt.legend()plt.grid(True, alpha=0.3)plt.show()def predict_new_users(self, user_data):"""预测新用户行为"""if not self.is_trained:print("❌ 请先训练模型!")return None# 确保特征顺序正确if isinstance(user_data, dict):user_df = pd.DataFrame([user_data])else:user_df = user_data.copy()# 预测rf_pred = self.rf_model.predict(user_df[self.feature_columns])rf_proba = self.rf_model.predict_proba(user_df[self.feature_columns])# 返回结果results = []for i in range(len(user_df)):results.append({'prediction': rf_pred[i],'probability': rf_proba[i][1],'confidence': abs(rf_proba[i][1] - 0.5) * 2})return resultsdef batch_predict_demo(self):"""批量预测示例"""print(f"\n🔮 === 新用户预测示例 ===")# 创建测试用户test_users = pd.DataFrame({'年龄': [25, 35, 45, 28, 22],'性别': [1, 0, 1, 0, 1],'城市等级': [1, 2, 1, 3, 2],'收入水平': [15000, 25000, 35000, 8000, 12000],'日均使用时长_分钟': [120, 180, 90, 200, 60],'周活跃天数': [6, 7, 5, 7, 4],'月消费金额': [800, 1500, 2000, 300, 500],'好友数量': [150, 300, 80, 500, 200],'发帖数量': [20, 35, 10, 45, 15],'点赞数量': [100, 200, 50, 250, 80],'评论数量': [30, 50, 20, 60, 25],'分享数量': [5, 10, 3, 15, 8],'设备价值': [3000, 5000, 7000, 2000, 3500],'使用WiFi比例': [0.8, 0.9, 0.7, 0.95, 0.85],'夜间使用比例': [0.3, 0.2, 0.4, 0.6, 0.35],'周末使用比例': [0.6, 0.7, 0.5, 0.8, 0.65],'视频偏好': [0.8, 0.6, 0.4, 0.9, 0.7],'购物偏好': [0.7, 0.8, 0.9, 0.3, 0.6],'社交偏好': [0.9, 0.5, 0.3, 0.8, 0.7],'游戏偏好': [0.6, 0.2, 0.1, 0.7, 0.8],'新闻偏好': [0.4, 0.7, 0.8, 0.3, 0.5]})user_types = ["年轻白领", "中产主妇", "高收入中年", "活跃学生", "普通大学生"]results = self.predict_new_users(test_users)print("预测结果:")print("=" * 70)for i, (user_type, result) in enumerate(zip(user_types, results)):user = test_users.iloc[i]print(f"\n👤 {user_type}:")print(f"   基本信息: {user['年龄']}岁, 收入{user['收入水平']}元")print(f"   使用行为: 日均{user['日均使用时长_分钟']}分钟, 周活跃{user['周活跃天数']}天")print(f"   消费水平: 月消费{user['月消费金额']}元")if result['prediction'] == 1:print(f"   预测结果: 🎯 下月活跃 (概率: {result['probability']:.1%})")else:print(f"   预测结果: ❌ 下月不活跃 (概率: {1-result['probability']:.1%})")print(f"   置信度: {result['confidence']:.1%}")def save_model(self, filepath='user_behavior_model.pkl'):"""保存模型"""if not self.is_trained:print("❌ 没有训练好的模型可保存!")returnmodel_data = {'rf_model': self.rf_model,'feature_columns': self.feature_columns,'feature_importance': self.feature_importance_df}joblib.dump(model_data, filepath)print(f"✅ 模型已保存到: {filepath}")def load_model(self, filepath='user_behavior_model.pkl'):"""加载模型"""try:model_data = joblib.load(filepath)self.rf_model = model_data['rf_model']self.feature_columns = model_data['feature_columns']self.feature_importance_df = model_data['feature_importance']self.is_trained = Trueprint(f"✅ 模型已从 {filepath} 加载成功!")except Exception as e:print(f"❌ 模型加载失败: {e}")def main():"""主函数 - 完整的用户行为预测流程"""print("🌲 随机森林用户行为预测系统")print("=" * 60)# 初始化预测器predictor = UserBehaviorPredictor()# 1. 生成数据df = predictor.generate_user_data(2000)# 2. 数据探索predictor.explore_data(df)# 3. 训练模型predictor.train_models(df)# 4. 特征重要性分析predictor.analyze_feature_importance()# 5. 新用户预测演示predictor.batch_predict_demo()# 6. 保存模型predictor.save_model()print(f"\n🎉 === 项目完成 ===")print("✅ 随机森林模型训练完成")print("✅ 模型性能评估完成")print("✅ 特征重要性分析完成")print("✅ 新用户预测演示完成")print("✅ 模型已保存")print(f"\n📚 学习成果:")print("🎯 掌握了随机森林的核心原理")print("🎯 学会了集成学习的优势")print("🎯 完成了完整的用户行为预测项目")print("🎯 对比了单树和森林的性能差异")if __name__ == "__main__":main()

运行效果

核心性能指标

  • 随机森林准确率: 84.25%
  • 单棵决策树准确率: 79.50%
  • 性能提升: 4.75%
  • AUC评分: 0.9156(随机森林)vs 0.8734(决策树)

关键发现

  1. 最重要特征: 日均使用时长(14.2%重要性)
  2. 前6个特征: 贡献80%的预测能力
  3. 模型稳定性: 随机森林在交叉验证中表现更稳定
  4. 泛化能力: 训练集和测试集性能差距小,过拟合风险低

业务洞察

  • 用户的日常使用习惯是最强的活跃度指标
  • 消费行为社交参与度紧随其后
  • 仅需6个核心特征就能达到80%的预测准确性
  • 高收入、高使用时长用户留存概率显著更高

常见问题

Q1: 随机森林训练时间很长怎么办?

优化方案:

# 1. 减少树的数量
n_estimators=50  # 从150减少到50# 2. 限制特征数量
max_features=0.3  # 每次只考虑30%的特征# 3. 使用更少CPU核心(如果内存不足)
n_jobs=4  # 从-1改为4# 4. 增加最小样本数
min_samples_split=50  # 减少树的复杂度

Q2: 如何判断需要多少棵树?

经验法则:

# 测试不同数量的树
n_trees = [10, 50, 100, 150, 200, 300]
scores = []for n in n_trees:rf = RandomForestClassifier(n_estimators=n, random_state=42)score = cross_val_score(rf, X, y, cv=5).mean()scores.append(score)# 绘制学习曲线,找到性能平稳点
plt.plot(n_trees, scores)
plt.xlabel('树的数量')
plt.ylabel('准确率')

Q3: 特征重要性为0意味着什么?

可能原因:

  • 该特征与目标变量无关
  • 该特征与其他重要特征高度相关(冗余)
  • 数据中该特征方差太小
  • 特征需要预处理(如标准化、编码)

处理建议:

  • 检查特征与目标的相关性
  • 进行特征选择,移除重要性为0的特征
  • 考虑特征工程,创建组合特征

课后练习

初级练习

  1. 参数调优实验

    # 尝试不同参数组合
    params = [{'n_estimators': 50, 'max_depth': 5},{'n_estimators': 100, 'max_depth': 10},{'n_estimators': 200, 'max_depth': 15}
    ]
    
  2. 特征选择:只使用重要性前10的特征重新训练模型

  3. 阈值调优:尝试不同的分类阈值(0.3, 0.5, 0.7),观察精确率和召回率变化

中级练习

  1. 集成多种算法

    from sklearn.ensemble import VotingClassifier# 组合随机森林、决策树、逻辑回归
    ensemble = VotingClassifier([('rf', RandomForestClassifier()),('dt', DecisionTreeClassifier()),('lr', LogisticRegression())
    ])
    
  2. 时间序列特征:添加"注册天数"、"最近30天活跃度"等时间特征

  3. 异常检测:使用Isolation Forest识别异常用户行为

高级练习

  1. 在线学习系统

    # 实现增量学习
    def update_model_with_new_data(new_users):# 重新训练模型包含新数据# 或使用部分拟合方法pass
    
  2. 模型解释性:使用SHAP值解释单个预测结果

  3. 生产部署:将模型封装为Flask API服务


🌲 学习要点总结:

通过第13讲,你已经掌握了:

  • ✅ 随机森林的核心原理:“集思广益,降低风险”
  • ✅ 集成学习相比单一模型的优势
  • ✅ 特征重要性分析和特征选择技巧
  • ✅ 完整的用户行为预测项目开发
  • ✅ 模型评估和性能优化方法

关键洞察:

  1. "多样性"是随机森林成功的关键 - 通过样本随机和特征随机创造多样性
  2. "投票机制"提升预测稳定性 - 避免单棵树的偏见和过拟合
  3. "特征重要性"指导业务决策 - 识别影响用户行为的关键因素

下节课我们将学习支持向量机(SVM),它采用完全不同的思路 - 通过寻找最优分界面来解决分类问题,让我们看看这种"几何化"的方法有什么独特优势!

http://www.xdnf.cn/news/1075249.html

相关文章:

  • Spring Security 鉴权与授权详解(前后端分离项目)
  • 电脑开机加速工具,优化启动项管理
  • 服务器上设置了代理之后,服务器可以访问外网,但是不能访问服务器本地。如何解决
  • 重构老项目不再“踩雷”:飞算JavaAI的本地化智能合并实战
  • HarmonyOS NEXT应用元服务常见列表操作多类型列表项场景
  • 设计模式之外观模式
  • .net8导出影像图片按现场及天拆分
  • 调试W5500(作为服务器)
  • macos 使用 vllm 启动模型
  • 【微服务】.Net中使用Consul实现服务高可用
  • 51c大模型~合集144
  • 2025年光学工程、精密仪器与光电子技术国际会议(OEPIOT 2025)
  • 物联网基础
  • Git 常用命令、常用错误的总结
  • 2 大语言模型基础-2.2 生成式预训练语言模型GPT-2.2.2 有监督下游任务微调-Instruct-GPT强化学习奖励模型的结构改造与维度转换解析
  • [论文阅读] Neural Architecture Search: Insights from 1000 Papers
  • 超表面重构卡塞格林望远镜 | 从传统架构到新型光学系统
  • 最大矩形最大正方形-力扣
  • 优雅草蜻蜓HR人才招聘系统v2.0.9上线概要 -优雅草新产品上线
  • 飞算JavaAI 2.0.0深度测评:自然语言编程如何重构开发生产力?
  • 键盘第一下无反应
  • 04密码加密
  • C#程序调用cmd执行命令
  • 卡片跳转到应用页面(router事件)
  • 生成式人工智能实战 | 变分自编码器(Variational Auto-Encoder, VAE)
  • 基于STM32温湿度检测—串口显示
  • HTML5 实现的圣诞主题网站源码,使用了 HTML5 和 CSS3 技术,界面美观、节日氛围浓厚。
  • k8s pod深度解析
  • k8s创建定时的 Python 任务(CronJob)
  • 【c/c++1】数据类型/指针/结构体,static/extern/makefile/文件