机器学习进阶<11>基于集成学习的多源数据融合的电商用户购买行为预测系统
引言
在实际电商场景中,单一数据源和单一模型往往难以准确预测用户行为。本项目构建一个融合多源数据、多模型集成的进阶预测系统,解决以下复杂问题:
-
多源异构数据:用户行为日志、商品属性、历史订单、时序特征
-
类别不平衡:购买行为通常只占整体行为的1-5%
-
特征维度灾难:高维稀疏特征需要有效处理
-
实时性要求:部分场景需要实时预测能力
1. 多源数据融合特征工程
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
class MultiSourceFeatureEngineer:
def __init__(self):
self.feature_generators = {
'temporal': self._extract_temporal_features,
'sequence': self._extract_sequence_features,
'embedding': self._extract_embedding_features,
'cross': self._extract_cross_features
}
def _extract_temporal_features(self, user_logs):
"""提取复杂时序特征"""
features = {}
# 周期性特征
user_logs['hour_sin'] = np.sin(2 * np.pi * user_logs['hour']/24)
user_logs['hour_cos'] = np.cos(2 * np.pi * user_logs['hour']/24)
# 时间衰减特征
current_time = datetime.now()
user_logs['time_decay'] = np.exp(
-(current_time - user_logs['timestamp']).dt.total_seconds() / (24*3600*7)
)
# 会话特征
session_features = self._extract_session_features(user_logs)
features.update(session_features)
return features
def _extract_sequence_features(self, user_logs):
"""使用Transformer提取序列特征"""
from transformers import BertTokenizer, BertModel
import torch
# 将用户行为序列编码
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
sequences = user_logs.groupby('user_id')['action'].apply(
lambda x: ' '.join(x.astype(str))
)
# 使用预训练BERT获取序列表示
model = BertModel.from_pretrained('bert-base-uncased')
encoded = tokenizer(sequences.tolist(), return_tensors='pt',
padding=True, truncation=True)
with torch.no_grad():
outputs = model(**encoded)
sequence_embeddings = outputs.last_hidden_state.mean(dim=1)
return {
'sequence_embedding': sequence_embeddings.numpy(),
'sequence_length': sequences.str.len()
}
代码分析:MultiSourceFeatureEngineer类
class MultiSourceFeatureEngineer:
def __init__(self):
self.feature_generators = {
'temporal': self._extract_temporal_features,
'sequence': self._extract_sequence_features,
'embedding': self._extract_embedding_features,
'cross': self._extract_cross_features
}
-
模块化设计:通过字典映射不同类型特征提取方法,便于扩展和维护
-
关注点分离:每个方法负责一种特定类型的特征工程
-
策略模式:可动态选择特征提取策略
时序特征提取分析
def _extract_temporal_features(self, user_logs):
# 周期性特征编码 - 创新点:将时间转换为连续循环特征
user_logs['hour_sin'] = np.sin(2 * np.pi * user_logs['hour']/24)
user_logs['hour_cos'] = np.cos(2 * np.pi * user_logs['hour']/24)
# 时间衰减特征 - 创新点:使用指数衰减模拟记忆效应
current_time = datetime.now()
user_logs['time_decay'] = np.exp(
-(current_time - user_logs['timestamp']).dt.total_seconds() / (24*3600*7)
)
技术优势:
-
周期性编码:避免了将小时直接编码为离散变量(0-23)导致的边界不连续问题
-
时间衰减:合理模拟了用户行为的时效性,近期行为权重更高
-
标准化处理:衰减系数1/7(一周)可调,根据业务场景优化
序列特征提取创新点
def _extract_sequence_features(self, user_logs):
# 使用Transformer提取序列特征
from transformers import BertTokenizer, BertModel
import torch
# 行为序列编码
sequences = user_logs.groupby('user_id')['action'].apply(
lambda x: ' '.join(x.astype(str))
)
# 使用预训练BERT获取序列表示
model = BertModel.from_pretrained('bert-base-uncased')
encoded = tokenizer(sequences.tolist(), return_tensors='pt',
padding=True, truncation=True)
-
预训练模型迁移学习:利用BERT的强大表征能力提取行为序列特征
-
注意力机制:自动学习行为之间的依赖关系,无需手工设计规则
-
上下文感知:BERT的Transformer架构能捕捉长期依赖关系
2. 多层次动态集成模型
import lightgbm as lgb
import catboost as cb
from sklearn.ensemble import RandomForestClassifier, VotingClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.calibration import CalibratedClassifierCV
class DynamicEnsembleModel:
def __init__(self, n_folds=5):
self.n_folds = n_folds
self.base_models = self._initialize_base_models()
self.meta_models = self._initialize_meta_models()
self.dynamic_weight_model = self._initialize_weight_model()
def _initialize_base_models(self):
"""初始化多样化基模型"""
return {
'lgb': lgb.LGBMClassifier(
n_estimators=200,
learning_rate=0.05,
num_leaves=31,
subsample=0.8,
colsample_bytree=0.8,
random_state=42,
n_jobs=-1
),
'catboost': cb.CatBoostClassifier(
iterations=300,
learning_rate=0.05,
depth=6,
l2_leaf_reg=3,
verbose=0,
random_state=42
),
'rf': RandomForestClassifier(
n_estimators=200,
max_depth=10,
min_samples_split=5,
random_state=42,
n_jobs=-1
),
'neural_net': MLPClassifier(
hidden_layer_sizes=(128, 64, 32),
activation='relu',
solver='adam',
alpha=0.0001,
batch_size=256,
max_iter=200,
random_state=42
)
}
def _initialize_meta_models(self):
"""初始化元模型层"""
return {
'logistic': LogisticRegression(
C=1.0,
solver='lbfgs',
max_iter=1000,
random_state=42
),
'xgboost': xgb.XGBClassifier(
n_estimators=100,
learning_rate=0.1,
max_depth=4,
random_state=42,
n_jobs=-1
),
'svm': CalibratedClassifierCV(
SVC(probability=True, kernel='rbf'),
cv=3
)
}
def _initialize_weight_model(self):
"""初始化动态权重学习模型"""
from sklearn.linear_model import Ridge
return Ridge(alpha=1.0, random_state=42)
def fit_dynamic_weights(self, X_train, y_train, X_val, y_val):
"""学习动态权重:根据验证集表现分配模型权重"""
# 第一阶段:基模型训练
base_predictions = {}
for name, model in self.base_models.items():
model.fit(X_train, y_train)
pred_proba = model.predict_proba(X_val)[:, 1]
base_predictions[name] = pred_proba
# 第二阶段:学习最优权重组合
base_pred_matrix = np.column_stack(list(base_predictions.values()))
# 使用岭回归学习权重
self.dynamic_weight_model.fit(base_pred_matrix, y_val)
weights = self.dynamic_weight_model.coef_
# 确保权重非负且和为1
weights = np.maximum(weights, 0)
weights = weights / weights.sum()
# 创建动态加权模型
weighted_models = []
for (name, model), weight in zip(self.base_models.items(), weights):
weighted_models.append((name, model, weight))
return weighted_models
架构设计分析
class DynamicEnsembleModel:
def __init__(self, n_folds=5):
self.base_models = self._initialize_base_models() # 第一层:多样化基模型
self.meta_models = self._initialize_meta_models() # 第二层:元学习器
self.dynamic_weight_model = self._initialize_weight_model() # 第三层:权重学习
-
多样性保证:第一层使用不同类型的基模型(树模型、神经网络)
-
元学习能力:第二层学习如何组合基模型的预测
-
动态适应性:第三层根据数据特点动态调整权重
基模型初始化分析
def _initialize_base_models(self):
return {
'lgb': lgb.LGBMClassifier( # 轻量梯度提升树
n_estimators=200,
learning_rate=0.05,
num_leaves=31, # 控制模型复杂度
subsample=0.8, # 行采样,增加多样性
colsample_bytree=0.8, # 列采样,增加多样性
random_state=42,
n_jobs=-1
),
'catboost': cb.CatBoostClassifier( # 类别特征友好
iterations=300,
learning_rate=0.05,
depth=6,
l2_leaf_reg=3, # L2正则化防止过拟合
verbose=0,
random_state=42
),
'neural_net': MLPClassifier( # 神经网络捕捉非线性
hidden_layer_sizes=(128, 64, 32), # 金字塔结构
activation='relu',
solver='adam',
alpha=0.0001, # L2正则化系数
batch_size=256,
max_iter=200,
random_state=42
)
}
-
异质模型组合:结合树模型和神经网络的优势
-
正则化配置:每个模型都有防止过拟合的机制
-
随机种子控制:确保结果可复现
动态权重学习算法分析
def fit_dynamic_weights(self, X_train, y_train, X_val, y_val):
# 第一阶段:基模型训练
base_predictions = {}
for name, model in self.base_models.items():
model.fit(X_train, y_train)
pred_proba = model.predict_proba(X_val)[:, 1]
base_predictions[name] = pred_proba
# 第二阶段:学习最优权重组合
base_pred_matrix = np.column_stack(list(base_predictions.values()))
# 使用岭回归学习权重
self.dynamic_weight_model.fit(base_pred_matrix, y_val)
weights = self.dynamic_weight_model.coef_
-
数据驱动权重分配:基于验证集性能自动学习最优权重
-
岭回归优势:处理多重共线性,防止权重过拟合
-
端到端优化:直接优化集成模型的预测性能
3. 自适应集成策略
class AdaptiveEnsemble:
def __init__(self, confidence_threshold=0.8):
self.confidence_threshold = confidence_threshold
self.models = []
self.confidence_estimators = []
def add_model(self, model, confidence_estimator):
"""添加模型及其置信度估计器"""
self.models.append(model)
self.confidence_estimators.append(confidence_estimator)
def predict_adaptive(self, X):
"""自适应预测:基于置信度选择模型"""
predictions = []
confidences = []
for model, conf_estimator in zip(self.models, self.confidence_estimators):
pred = model.predict_proba(X)
conf = conf_estimator.predict_proba(X)
predictions.append(pred)
confidences.append(conf)
predictions = np.array(predictions) # shape: (n_models, n_samples, n_classes)
confidences = np.array(confidences) # shape: (n_models, n_samples, n_classes)
# 自适应加权
final_pred = np.zeros_like(predictions[0])
for i in range(X.shape[0]):
# 对每个样本选择置信度最高的模型
model_conf = confidences[:, i, :].max(axis=1)
best_model_idx = np.argmax(model_conf)
if model_conf[best_model_idx] > self.confidence_threshold:
# 高置信度:使用单个模型
final_pred[i] = predictions[best_model_idx, i]
else:
# 低置信度:使用加权组合
weights = self._calculate_dynamic_weights(confidences[:, i, :])
final_pred[i] = np.average(predictions[:, i], axis=0, weights=weights)
return final_pred
def _calculate_dynamic_weights(self, confidences):
"""基于置信度计算动态权重"""
# 使用softmax归一化
weights = np.exp(confidences) / np.exp(confidences).sum(axis=0)
return weights.mean(axis=1)
置信度驱动决策分析
class AdaptiveEnsemble:
def __init__(self, confidence_threshold=0.8):
self.confidence_threshold = confidence_threshold
self.models = []
self.confidence_estimators = []
def predict_adaptive(self, X):
# 对每个样本选择置信度最高的模型
for i in range(X.shape[0]):
model_conf = confidences[:, i, :].max(axis=1)
best_model_idx = np.argmax(model_conf)
if model_conf[best_model_idx] > self.confidence_threshold:
# 高置信度:使用单个模型
final_pred[i] = predictions[best_model_idx, i]
else:
# 低置信度:使用加权组合
weights = self._calculate_dynamic_weights(confidences[:, i, :])
final_pred[i] = np.average(predictions[:, i], axis=0, weights=weights)
-
样本级自适应:每个样本独立选择最优模型或组合
-
置信度阈值:根据模型对样本的把握程度动态调整策略
-
软硬决策结合:高置信度用硬决策(单模型),低置信度用软决策(组合)
置信度估计实现
def _calculate_dynamic_weights(self, confidences):
"""基于置信度计算动态权重"""
# 使用softmax归一化
weights = np.exp(confidences) / np.exp(confidences).sum(axis=0)
return weights.mean(axis=1)
数学原理:
-
Softmax函数:
-
概率解释:将置信度转化为概率分布,保证权重非负且和为1
-
温度参数:可引入温度参数控制权重分布集中度
4. 在线学习与概念漂移检测
from river import tree, ensemble, drift
class OnlineEnsembleWithDriftDetection:
def __init__(self, window_size=1000):
self.window_size = window_size
self.models = {
'arf': ensemble.AdaptiveRandomForestClassifier(
n_models=10,
drift_detector=drift.ADWIN(delta=0.002)
),
'sgt': tree.HoeffdingTreeClassifier(
grace_period=100,
split_confidence=0.01
),
'oza': ensemble.OzaBaggingClassifier(
model=tree.HoeffdingTreeClassifier(),
n_models=10
)
}
self.drift_detectors = {
'page_hinkley': drift.PageHinkley(min_instances=30, delta=0.005),
'kswin': drift.KSWIN(window_size=window_size, stat_size=30)
}
self.drift_history = []
def update_and_predict(self, X, y):
"""在线更新并预测,检测概念漂移"""
predictions = {}
drift_flags = {}
for model_name, model in self.models.items():
# 在线学习
for xi, yi in zip(X, y):
model.learn_one(xi, yi)
# 预测
preds = [model.predict_one(xi) for xi in X]
predictions[model_name] = preds
# 概念漂移检测
for drift_name, detector in self.drift_detectors.items():
for xi, yi in zip(X, y):
detector.update(yi)
if detector.drift_detected:
drift_flags[f"{model_name}_{drift_name}"] = True
self._handle_drift(model_name)
return predictions, drift_flags
def _handle_drift(self, model_name):
"""处理概念漂移"""
print(f"概念漂移检测到!重置模型: {model_name}")
self.drift_history.append({
'model': model_name,
'timestamp': datetime.now(),
'window': self.window_size
})
# 部分重置模型
if model_name == 'arf':
self.models[model_name] = ensemble.AdaptiveRandomForestClassifier(
n_models=10,
drift_detector=drift.ADWIN(delta=0.002)
)
在线学习框架分析
from river import tree, ensemble, drift
class OnlineEnsembleWithDriftDetection:
def __init__(self, window_size=1000):
self.window_size = window_size
self.models = {
'arf': ensemble.AdaptiveRandomForestClassifier(
n_models=10,
drift_detector=drift.ADWIN(delta=0.002) # 自适应窗口漂移检测
),
'sgt': tree.HoeffdingTreeClassifier(
grace_period=100, # 节点分裂最小样本数
split_confidence=0.01 # 分裂置信度
)
}
-
自适应随机森林:每个树配备独立的漂移检测器
-
霍夫丁树:理论保证的在线学习树,适用于数据流
-
多检测器策略:结合ADWIN、Page-Hinkley、KSWIN多种检测方法
概念漂移处理机制
def update_and_predict(self, X, y):
"""在线更新并预测,检测概念漂移"""
drift_flags = {}
for drift_name, detector in self.drift_detectors.items():
for xi, yi in zip(X, y):
detector.update(yi)
if detector.drift_detected:
drift_flags[f"{model_name}_{drift_name}"] = True
self._handle_drift(model_name) # 触发漂移处理
-
ADWIN:自适应窗口,无需参数调优,检测渐进漂移
-
Page-Hinkley:累积和检验,检测突然漂移
-
KSWIN:基于KS检验,检测分布变化
模型重置策略分析
def _handle_drift(self, model_name):
"""处理概念漂移"""
print(f"概念漂移检测到!重置模型: {model_name}")
self.drift_history.append({
'model': model_name,
'timestamp': datetime.now(),
'window': self.window_size
})
# 部分重置模型
if model_name == 'arf':
self.models[model_name] = ensemble.AdaptiveRandomForestClassifier(...)
5. 模型解释与业务洞察
import shap
import plotly.graph_objects as go
from plotly.subplots import make_subplots
class ModelInterpretationDashboard:
def __init__(self, model, X, feature_names):
self.model = model
self.X = X
self.feature_names = feature_names
self.shap_explainer = shap.TreeExplainer(model)
def create_interactive_dashboard(self):
"""创建交互式模型解释仪表板"""
shap_values = self.shap_explainer.shap_values(self.X)
# 创建多子图仪表板
fig = make_subplots(
rows=2, cols=3,
subplot_titles=(
'全局特征重要性',
'个体预测解释',
'特征依赖关系',
'SHAP摘要图',
'决策路径分析',
'模型对比热图'
)
)
# 1. 全局特征重要性
global_importance = np.abs(shap_values).mean(0)
fig.add_trace(
go.Bar(x=global_importance, y=self.feature_names,
orientation='h', name='全局重要性'),
row=1, col=1
)
# 2. SHAP摘要图
fig.add_trace(
go.Scatter(
x=shap_values[:, 0],
y=self.X.iloc[:, 0],
mode='markers',
marker=dict(
size=8,
color=shap_values[:, 0],
colorscale='RdBu',
showscale=True
),
name='SHAP值分布'
),
row=2, col=1
)
# 3. 决策路径可视化
self._plot_decision_path(fig, row=2, col=2)
fig.update_layout(
height=900,
showlegend=False,
title_text="模型解释与业务洞察仪表板"
)
return fig
def _plot_decision_path(self, fig, row, col):
"""可视化单个样本的决策路径"""
# 选择一个样本进行解释
sample_idx = 0
sample = self.X.iloc[sample_idx]
# 获取决策路径
if hasattr(self.model, 'decision_path'):
decision_path = self.model.decision_path(sample.values.reshape(1, -1))
# 创建决策路径图
path_trace = go.Scatter(
x=range(len(decision_path.indices)),
y=decision_path.data,
mode='lines+markers',
name='决策路径',
line=dict(width=2, color='red')
)
fig.add_trace(path_trace, row=row, col=col)
SHAP解释框架分析
import shap
class ModelInterpretationDashboard:
def __init__(self, model, X, feature_names):
self.shap_explainer = shap.TreeExplainer(model) # 树模型专用解释器
def create_interactive_dashboard(self):
shap_values = self.shap_explainer.shap_values(self.X)
# SHAP值计算:每个特征对每个预测的贡献度
# 公式:f(x) = E[f(z)] + Σ φ_i,其中φ_i是SHAP值
SHAP原理优势:
-
理论保证:基于博弈论的Shapley值,唯一满足四大公理
-
全局与局部统一:既解释单个预测,又提供全局重要性
-
特征交互:可分解为单个特征和特征交互的贡献
交互式可视化创新
from plotly.subplots import make_subplots
fig = make_subplots(
rows=2, cols=3,
subplot_titles=(
'全局特征重要性',
'个体预测解释',
'特征依赖关系',
'SHAP摘要图',
'决策路径分析',
'模型对比热图'
)
)
# 1. 全局特征重要性
global_importance = np.abs(shap_values).mean(0)
fig.add_trace(
go.Bar(x=global_importance, y=self.feature_names,
orientation='h', name='全局重要性'),
row=1, col=1
)
# 2. 个体预测解释:瀑布图
sample_idx = 0
shap.plots.waterfall(shap_values[sample_idx])
-
多维信息整合:6个子图从不同角度解释模型
-
交互式探索:Plotly支持缩放、悬停查看详情
-
业务对齐:特征重要性排序帮助业务理解关键因素
决策路径分析深化
def _plot_decision_path(self, fig, row, col):
"""可视化单个样本的决策路径"""
if hasattr(self.model, 'decision_path'):
decision_path = self.model.decision_path(sample.values.reshape(1, -1))
# 提取决策路径中的节点
path_nodes = self._extract_decision_nodes(decision_path)
# 创建决策树可视化
self._visualize_decision_tree(path_nodes, fig, row, col)
项目实战:完整训练流水线
"""
集成学习进阶项目:多源数据融合的动态集成系统
作者:free-elcmacom
日期:2025-12-12
"""
import pandas as pd
import numpy as np
import warnings
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
matplotlib.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
warnings.filterwarnings('ignore')
# 设置随机种子确保可重复性
np.random.seed(42)
# ==================== 第一部分:多源数据生成与特征工程 ====================
class MultiSourceDataGenerator:
"""生成模拟多源数据"""
@staticmethod
def generate_user_behavior_data(n_users=1000, n_days=30):
"""生成用户行为数据"""
user_ids = [f"user_{i}" for i in range(n_users)]
dates = pd.date_range(end=pd.Timestamp.now(), periods=n_days, freq='D')
data = []
for user_id in user_ids:
for date in dates:
# 每天生成1-5条行为记录
n_actions = np.random.randint(1, 6)
for _ in range(n_actions):
hour = np.random.randint(0, 24)
minute = np.random.randint(0, 60)
timestamp = date + pd.Timedelta(hours=hour, minutes=minute)
# 行为类型:0=浏览,1=点击,2=加入购物车,3=购买
action = np.random.choice([0, 1, 2, 3], p=[0.5, 0.3, 0.15, 0.05])
# 页面停留时间(秒)
stay_time = np.random.exponential(60)
data.append({
'user_id': user_id,
'timestamp': timestamp,
'hour': hour,
'action': action,
'stay_time': min(stay_time, 600) # 最大10分钟
})
return pd.DataFrame(data)
@staticmethod
def generate_user_profiles(n_users=1000):
"""生成用户画像数据"""
user_ids = [f"user_{i}" for i in range(n_users)]
profiles = []
for user_id in user_ids:
age = np.random.randint(18, 65)
gender = np.random.choice(['M', 'F'])
income_level = np.random.choice(['low', 'medium', 'high'])
registration_days = np.random.randint(1, 365 * 3) # 注册0-3年
# 设备偏好
device = np.random.choice(['mobile', 'desktop', 'tablet'], p=[0.6, 0.3, 0.1])
# 地理位置
region = np.random.choice(['north', 'south', 'east', 'west'])
profiles.append({
'user_id': user_id,
'age': age,
'gender': gender,
'income_level': income_level,
'registration_days': registration_days,
'device_preference': device,
'region': region
})
return pd.DataFrame(profiles)
@staticmethod
def generate_product_data(n_products=100):
"""生成商品数据"""
product_ids = [f"product_{i}" for i in range(n_products)]
products = []
for product_id in product_ids:
category = np.random.choice(['electronics', 'clothing', 'books', 'home', 'food'])
price = np.random.uniform(10, 1000)
rating = np.random.uniform(3.0, 5.0)
review_count = np.random.randint(0, 1000)
# 商品特征
discount_rate = np.random.uniform(0, 0.5) # 0-50%折扣
stock_level = np.random.choice(['high', 'medium', 'low'])
products.append({
'product_id': product_id,
'category': category,
'price': price,
'rating': rating,
'review_count': review_count,
'discount_rate': discount_rate,
'stock_level': stock_level
})
return pd.DataFrame(products)
@staticmethod
def generate_training_labels(n_users=1000):
"""生成训练标签(是否购买)"""
user_ids = [f"user_{i}" for i in range(n_users)]
labels = []
for user_id in user_ids:
# 基础购买概率:20%
base_prob = 0.2
# 根据用户ID添加一些模式
user_num = int(user_id.split('_')[1])
if user_num % 5 == 0:
base_prob += 0.3 # 特定用户群购买率高
if user_num % 7 == 0:
base_prob -= 0.1 # 特定用户群购买率低
# 添加随机性
purchase_prob = min(max(base_prob + np.random.uniform(-0.1, 0.1), 0), 1)
# 生成标签
purchase = 1 if np.random.random() < purchase_prob else 0
labels.append({
'user_id': user_id,
'purchase': purchase,
'purchase_prob': purchase_prob
})
return pd.DataFrame(labels)
class MultiSourceFeatureEngineer:
"""多源特征工程"""
def __init__(self):
self.feature_names = []
def extract_features(self, user_behavior, user_profiles, product_data, labels):
"""提取多源特征"""
features = []
# 1. 用户行为特征
user_features = self._extract_user_behavior_features(user_behavior)
features.append(user_features)
# 2. 用户画像特征
profile_features = self._extract_profile_features(user_profiles)
features.append(profile_features)
# 3. 商品特征(聚合)
product_features = self._extract_product_features(product_data, user_behavior)
features.append(product_features)
# 4. 时序特征
temporal_features = self._extract_temporal_features(user_behavior)
features.append(temporal_features)
# 5. 交叉特征
cross_features = self._extract_cross_features(user_features, profile_features)
features.append(cross_features)
# 合并所有特征
all_features = pd.concat(features, axis=1)
# 获取标签
y = labels.set_index('user_id')['purchase']
# 对齐索引
all_features = all_features.loc[y.index]
self.feature_names = all_features.columns.tolist()
return all_features, y
def _extract_user_behavior_features(self, user_behavior):
"""提取用户行为特征"""
features = pd.DataFrame(index=user_behavior['user_id'].unique())
# 行为统计
behavior_stats = user_behavior.groupby('user_id').agg({
'action': ['count', 'mean', 'std'],
'stay_time': ['mean', 'sum', 'max']
})
behavior_stats.columns = ['_'.join(col).strip() for col in behavior_stats.columns.values]
# 行为类型分布
action_dummies = pd.get_dummies(user_behavior['action'], prefix='action')
action_dummies['user_id'] = user_behavior['user_id']
action_dist = action_dummies.groupby('user_id').sum()
# 会话特征
user_behavior['date'] = user_behavior['timestamp'].dt.date
daily_actions = user_behavior.groupby(['user_id', 'date']).size()
session_features = daily_actions.groupby('user_id').agg(['mean', 'std', 'max'])
session_features.columns = ['daily_actions_' + col for col in session_features.columns]
# 合并
features = pd.concat([features, behavior_stats, action_dist, session_features], axis=1)
return features.fillna(0)
def _extract_profile_features(self, user_profiles):
"""提取用户画像特征"""
features = user_profiles.set_index('user_id')
# 编码分类变量
categorical_cols = ['gender', 'income_level', 'device_preference', 'region']
for col in categorical_cols:
dummies = pd.get_dummies(features[col], prefix=col)
features = pd.concat([features, dummies], axis=1)
features.drop(col, axis=1, inplace=True)
# 数值特征标准化
numerical_cols = ['age', 'registration_days']
for col in numerical_cols:
features[col] = (features[col] - features[col].mean()) / features[col].std()
return features
def _extract_product_features(self, product_data, user_behavior):
"""提取商品聚合特征"""
features = pd.DataFrame(index=user_behavior['user_id'].unique())
# 用户与商品的交互
user_product = user_behavior.copy()
# 模拟商品ID分配(实际中应从数据中获取)
product_ids = product_data['product_id'].tolist()
user_product['product_id'] = np.random.choice(product_ids, len(user_product))
# 合并商品特征
user_product = user_product.merge(product_data, on='product_id', how='left')
# 用户级别的商品特征聚合
agg_features = user_product.groupby('user_id').agg({
'price': ['mean', 'min', 'max'],
'rating': 'mean',
'discount_rate': 'mean'
})
agg_features.columns = ['_'.join(col).strip() for col in agg_features.columns.values]
# 商品类别偏好
category_dummies = pd.get_dummies(user_product['category'], prefix='category')
category_dummies['user_id'] = user_product['user_id']
category_pref = category_dummies.groupby('user_id').sum()
# 合并
features = pd.concat([features, agg_features, category_pref], axis=1)
return features.fillna(0)
def _extract_temporal_features(self, user_behavior):
"""提取时序特征"""
features = pd.DataFrame(index=user_behavior['user_id'].unique())
# 时间窗口统计
latest_date = user_behavior['timestamp'].max()
user_behavior['days_since_last'] = (latest_date - user_behavior['timestamp']).dt.days
recency_features = user_behavior.groupby('user_id')['days_since_last'].agg(['min', 'mean'])
recency_features.columns = ['days_since_last_' + col for col in recency_features.columns]
# 活跃时段特征
user_behavior['hour_sin'] = np.sin(2 * np.pi * user_behavior['hour'] / 24)
user_behavior['hour_cos'] = np.cos(2 * np.pi * user_behavior['hour'] / 24)
hour_features = user_behavior.groupby('user_id')[['hour_sin', 'hour_cos']].mean()
# 时间衰减特征
user_behavior['time_decay'] = np.exp(-user_behavior['days_since_last'] / 7)
decay_features = user_behavior.groupby('user_id')['time_decay'].agg(['mean', 'sum'])
decay_features.columns = ['time_decay_' + col for col in decay_features.columns]
# 合并
features = pd.concat([features, recency_features, hour_features, decay_features], axis=1)
return features.fillna(0)
def _extract_cross_features(self, user_features, profile_features):
"""提取交叉特征"""
features = pd.DataFrame(index=user_features.index)
# 用户行为与画像的交互
# 选择几个关键特征进行交叉
if 'action_count' in user_features.columns and 'age' in profile_features.columns:
features['action_age_interaction'] = user_features['action_count'] * profile_features['age']
if 'stay_time_mean' in user_features.columns and 'registration_days' in profile_features.columns:
features['engagement_seniority'] = user_features['stay_time_mean'] * profile_features['registration_days']
return features.fillna(0)
# ==================== 第二部分:动态集成模型 ====================
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, roc_auc_score, classification_report, confusion_matrix
import lightgbm as lgb
import xgboost as xgb
from sklearn.svm import SVC
from sklearn.calibration import CalibratedClassifierCV
class DynamicEnsembleModel:
"""动态集成模型"""
def __init__(self, n_folds=5):
self.n_folds = n_folds
self.base_models = {}
self.meta_model = None
self.dynamic_weights = None
self.is_fitted = False
def _initialize_base_models(self):
"""初始化基模型"""
models = {
'random_forest': RandomForestClassifier(
n_estimators=100,
max_depth=10,
min_samples_split=5,
random_state=42,
n_jobs=-1
),
'xgboost': xgb.XGBClassifier(
n_estimators=100,
learning_rate=0.1,
max_depth=5,
random_state=42,
n_jobs=-1,
eval_metric='logloss'
),
'lightgbm': lgb.LGBMClassifier(
n_estimators=100,
learning_rate=0.05,
num_leaves=31,
random_state=42,
n_jobs=-1
),
'logistic': LogisticRegression(
C=1.0,
max_iter=1000,
random_state=42
),
'svm': CalibratedClassifierCV(
SVC(probability=True, kernel='rbf', random_state=42),
cv=3
),
'neural_net': MLPClassifier(
hidden_layer_sizes=(64, 32),
activation='relu',
solver='adam',
alpha=0.0001,
max_iter=500,
random_state=42
)
}
return models
def fit(self, X_train, y_train, X_val=None, y_val=None):
"""训练动态集成模型"""
print("训练动态集成模型...")
# 初始化模型
self.base_models = self._initialize_base_models()
# 如果没有验证集,从训练集分割
if X_val is None or y_val is None:
X_train, X_val, y_train, y_val = train_test_split(
X_train, y_train, test_size=0.2, random_state=42, stratify=y_train
)
# 第一阶段:训练基模型
base_predictions = {}
base_scores = {}
print("\n基模型训练结果:")
print("-" * 50)
for name, model in self.base_models.items():
print(f"训练 {name}...")
model.fit(X_train, y_train)
# 在验证集上预测
if hasattr(model, 'predict_proba'):
y_pred_proba = model.predict_proba(X_val)[:, 1]
else:
y_pred_proba = model.predict(X_val)
# 计算性能
auc_score = roc_auc_score(y_val, y_pred_proba)
base_scores[name] = auc_score
# 保存预测结果
base_predictions[name] = y_pred_proba
print(f" {name}: AUC = {auc_score:.4f}")
print("-" * 50)
# 计算动态权重(基于验证集性能)
self.dynamic_weights = self._calculate_dynamic_weights(base_scores)
print("\n模型权重分配:")
for name, weight in self.dynamic_weights.items():
print(f" {name}: {weight:.4f}")
# 第二阶段:训练元模型(使用基模型的预测作为特征)
print("\n训练元模型...")
meta_features = self._create_meta_features(base_predictions)
self.meta_model = LogisticRegression(
C=1.0,
max_iter=1000,
random_state=42
)
self.meta_model.fit(meta_features, y_val)
# 在完整训练集上重新训练基模型
print("\n在完整训练集上重新训练基模型...")
for name, model in self.base_models.items():
model.fit(X_train, y_train)
self.is_fitted = True
print("模型训练完成!")
return self
def _calculate_dynamic_weights(self, base_scores):
"""计算动态权重"""
# 基于AUC计算权重
scores = np.array(list(base_scores.values()))
# 归一化到[0, 1]
if scores.max() > scores.min():
normalized_scores = (scores - scores.min()) / (scores.max() - scores.min())
else:
normalized_scores = np.ones_like(scores)
# 使用softmax得到权重
weights = np.exp(normalized_scores) / np.exp(normalized_scores).sum()
# 创建权重字典
weight_dict = {name: weight for name, weight in zip(base_scores.keys(), weights)}
return weight_dict
def _create_meta_features(self, base_predictions):
"""创建元特征(基模型的预测结果)"""
meta_features = np.column_stack(list(base_predictions.values()))
return meta_features
def predict(self, X):
"""预测"""
if not self.is_fitted:
raise ValueError("模型尚未训练,请先调用fit方法")
# 获取基模型的预测
base_preds = {}
for name, model in self.base_models.items():
if hasattr(model, 'predict_proba'):
pred = model.predict_proba(X)[:, 1]
else:
pred = model.predict(X)
base_preds[name] = pred
# 创建元特征
meta_features = self._create_meta_features(base_preds)
# 元模型预测
final_pred = self.meta_model.predict_proba(meta_features)[:, 1]
return final_pred
def predict_class(self, X, threshold=0.5):
"""预测类别"""
pred_proba = self.predict(X)
return (pred_proba >= threshold).astype(int)
class AdaptiveEnsemble:
"""自适应集成策略"""
def __init__(self, confidence_threshold=0.7):
self.confidence_threshold = confidence_threshold
self.models = []
self.model_names = []
self.is_fitted = False
def add_model(self, model, name):
"""添加模型"""
self.models.append(model)
self.model_names.append(name)
def fit(self, X, y):
"""训练所有模型"""
print("训练自适应集成模型...")
for model, name in zip(self.models, self.model_names):
print(f"训练 {name}...")
model.fit(X, y)
self.is_fitted = True
return self
def predict_adaptive(self, X):
"""自适应预测"""
if not self.is_fitted:
raise ValueError("模型尚未训练,请先调用fit方法")
# 获取所有模型的预测和置信度
predictions = []
confidences = []
for model in self.models:
if hasattr(model, 'predict_proba'):
pred_proba = model.predict_proba(X)
pred = pred_proba.argmax(axis=1)
confidence = pred_proba.max(axis=1)
else:
pred = model.predict(X)
confidence = np.ones(len(X)) * 0.5 # 如果没有概率,使用默认置信度
predictions.append(pred)
confidences.append(confidence)
predictions = np.array(predictions) # shape: (n_models, n_samples)
confidences = np.array(confidences) # shape: (n_models, n_samples)
# 自适应集成
final_predictions = np.zeros(len(X), dtype=int)
for i in range(len(X)):
# 获取当前样本的所有模型置信度
sample_confidences = confidences[:, i]
# 找到最高置信度
max_confidence = sample_confidences.max()
best_model_idx = sample_confidences.argmax()
if max_confidence >= self.confidence_threshold:
# 高置信度:使用单个最佳模型
final_predictions[i] = predictions[best_model_idx, i]
else:
# 低置信度:使用加权投票
weights = self._calculate_weights(sample_confidences)
weighted_votes = np.zeros(len(np.unique(predictions[:, i])))
for j, model_pred in enumerate(predictions[:, i]):
weighted_votes[model_pred] += weights[j]
final_predictions[i] = weighted_votes.argmax()
return final_predictions
def _calculate_weights(self, confidences):
"""计算权重"""
# 使用softmax
weights = np.exp(confidences) / np.exp(confidences).sum()
return weights
# ==================== 第三部分:模型评估与可视化 ====================
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import roc_curve, auc, precision_recall_curve
class ModelEvaluator:
"""模型评估器"""
@staticmethod
def evaluate_model(y_true, y_pred, y_pred_proba=None, model_name="Model"):
"""评估模型性能"""
print(f"\n{'=' * 50}")
print(f"{model_name} 评估结果")
print(f"{'=' * 50}")
# 基础指标
accuracy = accuracy_score(y_true, y_pred)
print(f"准确率: {accuracy:.4f}")
if y_pred_proba is not None:
auc_score = roc_auc_score(y_true, y_pred_proba)
print(f"AUC: {auc_score:.4f}")
# 分类报告
print("\n分类报告:")
print(classification_report(y_true, y_pred))
# 混淆矩阵
cm = confusion_matrix(y_true, y_pred)
print("混淆矩阵:")
print(cm)
return {
'accuracy': accuracy,
'auc': auc_score if y_pred_proba is not None else None,
'confusion_matrix': cm
}
@staticmethod
def plot_model_comparison(results, model_names, feature_importance=None):
"""绘制模型比较图"""
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 1. 准确率比较
accuracies = [results[name]['accuracy'] for name in model_names]
axes[0, 0].bar(model_names, accuracies, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4'])
axes[0, 0].set_title('模型准确率比较', fontsize=14, fontweight='bold')
axes[0, 0].set_ylabel('准确率', fontsize=12)
axes[0, 0].set_ylim(0, 1)
for i, acc in enumerate(accuracies):
axes[0, 0].text(i, acc + 0.02, f'{acc:.3f}', ha='center', fontweight='bold')
# 2. ROC曲线
ax = axes[0, 1]
for name in model_names:
if 'y_pred_proba' in results[name]:
y_true = results[name]['y_true']
y_pred_proba = results[name]['y_pred_proba']
fpr, tpr, _ = roc_curve(y_true, y_pred_proba)
roc_auc = auc(fpr, tpr)
ax.plot(fpr, tpr, lw=2, label=f'{name} (AUC = {roc_auc:.3f})')
ax.plot([0, 1], [0, 1], 'k--', alpha=0.6)
ax.set_xlabel('假正率', fontsize=12)
ax.set_ylabel('真正率', fontsize=12)
ax.set_title('ROC曲线', fontsize=14, fontweight='bold')
ax.legend(loc='lower right')
ax.grid(True, alpha=0.3)
# 3. 混淆矩阵热图(最后一个模型)
ax = axes[1, 0]
last_model = model_names[-1]
cm = results[last_model]['confusion_matrix']
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=ax)
ax.set_title(f'{last_model}混淆矩阵', fontsize=14, fontweight='bold')
ax.set_xlabel('预测标签', fontsize=12)
ax.set_ylabel('真实标签', fontsize=12)
# 4. 特征重要性图(如果有)
ax = axes[1, 1]
if feature_importance is not None and len(feature_importance) > 0:
# 只显示前10个最重要的特征
top_features = feature_importance.head(10)
colors = plt.cm.viridis(np.linspace(0.3, 0.9, len(top_features)))
bars = ax.barh(range(len(top_features)), top_features['importance'], color=colors)
ax.set_yticks(range(len(top_features)))
ax.set_yticklabels(top_features['feature'], fontsize=10)
ax.set_xlabel('重要性分数', fontsize=12)
ax.set_title('Top 10 特征重要性', fontsize=14, fontweight='bold')
ax.invert_yaxis() # 最重要的特征在顶部
ax.grid(True, alpha=0.3, axis='x')
# 在条形上添加数值
for i, (bar, importance) in enumerate(zip(bars, top_features['importance'])):
ax.text(importance + 0.001, i, f'{importance:.4f}',
va='center', fontsize=9, fontweight='bold')
else:
ax.text(0.5, 0.5, '特征重要性图\n(需要特征重要性数据)',
ha='center', va='center', fontsize=14, transform=ax.transAxes)
ax.set_title('特征重要性', fontsize=14, fontweight='bold')
ax.axis('off')
plt.tight_layout()
plt.show()
@staticmethod
def plot_learning_curves(train_scores, val_scores, model_name):
"""绘制学习曲线"""
plt.figure(figsize=(10, 6))
plt.plot(train_scores, label='训练集', color='#FF6B6B', linewidth=2)
plt.plot(val_scores, label='验证集', color='#4ECDC4', linewidth=2)
plt.xlabel('训练轮次', fontsize=12)
plt.ylabel('准确率', fontsize=12)
plt.title(f'{model_name}学习曲线', fontsize=14, fontweight='bold')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# ==================== 第四部分:主程序 ====================
def main():
"""主函数"""
print("=" * 70)
print("集成学习进阶项目:多源数据融合的动态集成系统")
print("=" * 70)
# 1. 生成模拟数据
print("\n1. 生成模拟数据...")
generator = MultiSourceDataGenerator()
user_behavior = generator.generate_user_behavior_data(n_users=500, n_days=30)
user_profiles = generator.generate_user_profiles(n_users=500)
product_data = generator.generate_product_data(n_products=50)
labels = generator.generate_training_labels(n_users=500)
print(f"用户行为数据: {user_behavior.shape}")
print(f"用户画像数据: {user_profiles.shape}")
print(f"商品数据: {product_data.shape}")
print(f"标签数据: {labels.shape}")
# 2. 特征工程
print("\n2. 特征工程...")
feature_engineer = MultiSourceFeatureEngineer()
X, y = feature_engineer.extract_features(
user_behavior, user_profiles, product_data, labels
)
print(f"特征矩阵: {X.shape}")
print(f"标签分布: {y.value_counts().to_dict()}")
# 3. 划分训练集和测试集
print("\n3. 划分训练集和测试集...")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
print(f"训练集: {X_train.shape}")
print(f"测试集: {X_test.shape}")
# 4. 训练动态集成模型
print("\n4. 训练动态集成模型...")
dynamic_ensemble = DynamicEnsembleModel()
dynamic_ensemble.fit(X_train, y_train)
# 预测
y_pred_proba_dynamic = dynamic_ensemble.predict(X_test)
y_pred_dynamic = dynamic_ensemble.predict_class(X_test, threshold=0.5)
# 5. 训练对比模型
print("\n5. 训练对比模型...")
# 随机森林
print("训练随机森林...")
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)
y_pred_rf = rf_model.predict(X_test)
y_pred_proba_rf = rf_model.predict_proba(X_test)[:, 1]
# XGBoost
print("训练XGBoost...")
xgb_model = xgb.XGBClassifier(n_estimators=100, random_state=42, eval_metric='logloss')
xgb_model.fit(X_train, y_train)
y_pred_xgb = xgb_model.predict(X_test)
y_pred_proba_xgb = xgb_model.predict_proba(X_test)[:, 1]
# 自适应集成
print("训练自适应集成...")
adaptive_ensemble = AdaptiveEnsemble(confidence_threshold=0.7)
adaptive_ensemble.add_model(rf_model, "Random Forest")
adaptive_ensemble.add_model(xgb_model, "XGBoost")
adaptive_ensemble.add_model(LogisticRegression(max_iter=1000, random_state=42), "Logistic Regression")
adaptive_ensemble.fit(X_train, y_train)
y_pred_adaptive = adaptive_ensemble.predict_adaptive(X_test)
# 6. 模型评估
print("\n6. 模型评估...")
evaluator = ModelEvaluator()
# 收集结果
results = {}
# 动态集成模型
results['Dynamic Ensemble'] = evaluator.evaluate_model(
y_test, y_pred_dynamic, y_pred_proba_dynamic, "动态集成模型"
)
results['Dynamic Ensemble']['y_true'] = y_test
results['Dynamic Ensemble']['y_pred_proba'] = y_pred_proba_dynamic
# 随机森林
results['Random Forest'] = evaluator.evaluate_model(
y_test, y_pred_rf, y_pred_proba_rf, "随机森林"
)
results['Random Forest']['y_true'] = y_test
results['Random Forest']['y_pred_proba'] = y_pred_proba_rf
# XGBoost
results['XGBoost'] = evaluator.evaluate_model(
y_test, y_pred_xgb, y_pred_proba_xgb, "XGBoost"
)
results['XGBoost']['y_true'] = y_test
results['XGBoost']['y_pred_proba'] = y_pred_proba_xgb
# 自适应集成
# 注意:自适应集成没有概率输出,只评估分类
results['Adaptive Ensemble'] = evaluator.evaluate_model(
y_test, y_pred_adaptive, None, "自适应集成"
)
results['Adaptive Ensemble']['y_true'] = y_test
# 计算特征重要性(使用随机森林模型)
feature_importance = pd.DataFrame({
'feature': X.columns,
'importance': rf_model.feature_importances_
}).sort_values('importance', ascending=False)
# 7. 可视化
print("\n7. 可视化结果...")
evaluator.plot_model_comparison(results, ['Random Forest', 'XGBoost', 'Dynamic Ensemble', 'Adaptive Ensemble'],
feature_importance)
# 8. 总结
print("\n8. 项目总结")
print("-" * 50)
best_model = None
best_accuracy = 0
for name, result in results.items():
if result['accuracy'] > best_accuracy:
best_accuracy = result['accuracy']
best_model = name
print(f"最佳模型: {best_model} (准确率: {best_accuracy:.4f})")
# 特征重要性分析
print("\n特征重要性分析(前10个特征):")
top_features = feature_importance.head(10)
print(top_features.to_string(index=False))
# 单独可视化特征重要性
plt.figure(figsize=(12, 8))
colors = plt.cm.viridis(np.linspace(0.3, 0.9, len(top_features)))
bars = plt.barh(range(len(top_features)), top_features['importance'], color=colors)
plt.yticks(range(len(top_features)), top_features['feature'], fontsize=11)
plt.xlabel('重要性分数', fontsize=12)
plt.title('Top 10 特征重要性', fontsize=14, fontweight='bold')
plt.gca().invert_yaxis()
plt.grid(True, alpha=0.3, axis='x')
# 在条形上添加数值
for i, (bar, importance) in enumerate(zip(bars, top_features['importance'])):
plt.text(importance + 0.001, i, f'{importance:.4f}',
va='center', fontsize=9, fontweight='bold')
plt.tight_layout()
plt.show()
print("\n项目运行完成!")
print("=" * 70)
# ==================== 运行程序 ====================
if __name__ == "__main__":
# 检查必要的库
try:
import lightgbm
import xgboost
print("所有依赖库已安装,开始运行项目...")
except ImportError as e:
print(f"缺少依赖库: {e}")
print("请运行: pip install lightgbm xgboost scikit-learn pandas numpy matplotlib seaborn")
exit(1)
# 运行主程序
main()
1. 模型比较综合图

生成图片:包含4个子图的综合比较图表
图片内容总结:
这张综合图表全面比较了四个模型(随机森林、XGBoost、动态集成、自适应集成)的性能表现。左上角的柱状图直观显示各模型在测试集上的准确率,动态集成模型以最高的准确率脱颖而出。右上角的ROC曲线展示了各模型的分类能力,曲线越靠近左上角表示性能越好,AUC值越高说明模型区分正负样本的能力越强。左下角的混淆矩阵显示了动态集成模型的预测结果细节,对角线上的数值表示正确分类的样本数。右下角的特征重要性图展示了模型认为最重要的特征,帮助理解哪些因素对购买行为预测影响最大。
技术价值:这张图提供了从宏观准确率到微观预测细节的全方位模型评估,帮助数据科学家快速识别最佳模型并理解其决策依据。
2. 特征重要性条形图
生成图片:水平条形图展示前10个最重要的特征

图片内容总结:
这张条形图按重要性从高到低排列了影响用户购买行为的10个关键特征。最长的条形代表最重要的特征,例如"time_decay_mean"(时间衰减均值)可能表示近期行为的权重最高,"stay_time_mean"(平均停留时间)反映用户的参与度,"age"(年龄)可能捕捉特定年龄段的购买倾向。每个条形的高度直接对应特征对预测的贡献度,为业务决策提供直接参考。
业务洞察:
-
时间因素:时间衰减特征重要性最高,说明近期用户行为比历史行为更有预测力
-
参与度指标:停留时间相关特征的重要性表明用户参与度是购买的关键前置信号
-
用户属性:年龄等人口统计特征仍然发挥重要作用
-
行为模式:行为统计特征(如行为次数、行为类型分布)提供了用户习惯的量化指标
应用价值:
-
市场营销:可将资源集中在最重要的特征对应的用户群体
-
产品优化:重点优化与高重要性特征相关的用户体验
-
特征工程:指导后续的特征选择和工程方向
-
业务解释:为模型预测提供可解释的业务理由
更多推荐
所有评论(0)