scikit-learn,一个高级的 Python 库!
一、库的简介
Scikit-learn是Python机器学习领域最经典、最全面的库之一,它构建在NumPy、SciPy和Matplotlib之上,提供了统一且高效的机器学习算法接口。在实际生活中,Scikit-learn的应用已经渗透到各个角落:金融机构用它构建信用评分模型,电商平台用它实现个性化推荐,医疗机构用它辅助疾病诊断,教育机构用它预测学生表现。当你在邮箱中看到智能垃圾邮件过滤,在社交媒体上看到内容推荐,或在自动驾驶汽车中看到障碍物识别时,背后往往都有Scikit-learn的影子。它的设计哲学强调一致性、易用性和性能,使得从数据预处理到模型部署的整个机器学习流程变得标准化和可重复,让机器学习不再是少数专家的专利,而是每个开发者都能掌握的工具。
二、安装库
安装Scikit-learn及其依赖:
python
# 基础安装
pip install scikit-learn
# 安装完整版本(包含所有可选依赖)
pip install scikit-learn[all]
# 使用conda安装
conda install scikit-learn
# 验证安装
import sklearn
print(f"Scikit-learn版本: {sklearn.__version__}")
# 检查重要依赖版本
import numpy as np
import scipy as sp
import matplotlib as mpl
print(f"NumPy版本: {np.__version__}")
print(f"SciPy版本: {sp.__version__}")
print(f"Matplotlib版本: {mpl.__version__}")
# 安装可选依赖以支持更多功能
pip install joblib threadpoolctl # 并行计算支持
pip install pandas # 更好的数据处理
pip install matplotlib seaborn # 可视化支持
对于需要GPU加速或特定算法扩展的用户:
bash
# 安装Intel扩展以优化性能 pip install scikit-learn-intelex # 安装其他机器学习库以扩展功能 pip install xgboost lightgbm catboost # 梯度提升树实现 pip install imbalanced-learn # 处理不平衡数据
三、基本用法
1. 数据准备和预处理
python
import numpy as np
import pandas as pd
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, LabelEncoder, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
# 1.1 加载内置数据集
print("可用的内置数据集:")
print([d for d in dir(datasets) if not d.startswith('_')][:10])
# 加载鸢尾花数据集
iris = datasets.load_iris()
X_iris = iris.data
y_iris = iris.target
feature_names = iris.feature_names
target_names = iris.target_names
print(f"\n鸢尾花数据集:")
print(f" 特征形状: {X_iris.shape}")
print(f" 目标形状: {y_iris.shape}")
print(f" 特征名: {feature_names}")
print(f" 类别名: {target_names}")
# 加载糖尿病数据集用于回归
diabetes = datasets.load_diabetes()
print(f"\n糖尿病数据集:")
print(f" 样本数: {diabetes.data.shape[0]}")
print(f" 特征数: {diabetes.data.shape[1]}")
# 1.2 创建模拟数据集
from sklearn.datasets import make_classification, make_regression, make_blobs
# 创建分类数据集
X_class, y_class = make_classification(
n_samples=1000,
n_features=20,
n_informative=15,
n_redundant=5,
n_clusters_per_class=2,
weights=[0.7, 0.3], # 不平衡类别
random_state=42
)
print(f"\n模拟分类数据集:")
print(f" 形状: {X_class.shape}")
print(f" 类别分布: {np.bincount(y_class)}")
# 创建回归数据集
X_reg, y_reg = make_regression(
n_samples=500,
n_features=10,
n_informative=8,
noise=10.0,
random_state=42
)
# 创建聚类数据集
X_cluster, y_cluster = make_blobs(
n_samples=300,
n_features=2,
centers=3,
cluster_std=1.0,
random_state=42
)
# 1.3 数据分割
# 分类数据分割
X_class_train, X_class_test, y_class_train, y_class_test = train_test_split(
X_class, y_class, test_size=0.2, random_state=42, stratify=y_class
)
print(f"\n分类数据分割:")
print(f" 训练集: {X_class_train.shape}, 测试集: {X_class_test.shape}")
# 回归数据分割
X_reg_train, X_reg_test, y_reg_train, y_reg_test = train_test_split(
X_reg, y_reg, test_size=0.2, random_state=42
)
# 1.4 数据预处理
# 创建包含不同类型特征的模拟数据
np.random.seed(42)
n_samples = 1000
data = pd.DataFrame({
'age': np.random.randint(18, 70, n_samples),
'income': np.random.normal(50000, 15000, n_samples),
'gender': np.random.choice(['Male', 'Female'], n_samples),
'education': np.random.choice(['High School', 'Bachelor', 'Master', 'PhD'], n_samples),
'missing_feature': np.random.choice([1, 2, 3, np.nan], n_samples, p=[0.3, 0.3, 0.3, 0.1]),
'target': np.random.choice([0, 1], n_samples)
})
print(f"\n原始数据形状: {data.shape}")
print(f"缺失值统计:\n{data.isnull().sum()}")
# 定义数值型和分类型特征
numeric_features = ['age', 'income', 'missing_feature']
categorical_features = ['gender', 'education']
# 创建预处理管道
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')), # 中位数填充缺失值
('scaler', StandardScaler()) # 标准化
])
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')), # 众数填充缺失值
('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False)) # 独热编码
])
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
]
)
# 应用预处理
X = data.drop('target', axis=1)
y = data['target']
X_processed = preprocessor.fit_transform(X)
print(f"\n预处理后数据形状: {X_processed.shape}")
print(f"预处理后特征数: {X_processed.shape[1]}")
# 获取特征名称(独热编码后)
numeric_feature_names = numeric_features
categorical_feature_names = preprocessor.named_transformers_['cat'].named_steps['onehot'].get_feature_names_out(categorical_features)
all_feature_names = list(numeric_feature_names) + list(categorical_feature_names)
print(f"\n所有特征名称 ({len(all_feature_names)}个):")
print(all_feature_names)
2. 监督学习:分类算法
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, confusion_matrix, classification_report
from sklearn.preprocessing import StandardScaler
import seaborn as sns
# 设置样式
plt.style.use('seaborn-v0_8-darkgrid')
sns.set_palette("husl")
# 创建更复杂的分类数据集
from sklearn.datasets import make_classification
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=15,
n_redundant=5,
n_classes=2,
weights=[0.8, 0.2], # 不平衡数据
flip_y=0.05, # 添加噪声
random_state=42
)
# 数据分割和标准化
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print(f"训练集形状: {X_train_scaled.shape}")
print(f"测试集形状: {X_test_scaled.shape}")
print(f"训练集类别分布: {np.bincount(y_train)}")
print(f"测试集类别分布: {np.bincount(y_test)}")
# 创建图形
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()
# 存储模型性能
model_performance = {}
# 2.1 逻辑回归
from sklearn.linear_model import LogisticRegression
print("\n" + "="*50)
print("1. 逻辑回归")
print("="*50)
lr_model = LogisticRegression(
penalty='l2', # L2正则化
C=1.0, # 正则化强度的倒数
solver='lbfgs', # 优化算法
max_iter=1000,
random_state=42
)
# 训练模型
lr_model.fit(X_train_scaled, y_train)
# 预测
y_pred_lr = lr_model.predict(X_test_scaled)
y_pred_proba_lr = lr_model.predict_proba(X_test_scaled)[:, 1]
# 评估
accuracy_lr = accuracy_score(y_test, y_pred_lr)
precision_lr = precision_score(y_test, y_pred_lr, average='weighted')
recall_lr = recall_score(y_test, y_pred_lr, average='weighted')
f1_lr = f1_score(y_test, y_pred_lr, average='weighted')
roc_auc_lr = roc_auc_score(y_test, y_pred_proba_lr)
model_performance['逻辑回归'] = {
'准确率': accuracy_lr,
'精确率': precision_lr,
'召回率': recall_lr,
'F1分数': f1_lr,
'ROC-AUC': roc_auc_lr
}
print(f"准确率: {accuracy_lr:.4f}")
print(f"精确率: {precision_lr:.4f}")
print(f"召回率: {recall_lr:.4f}")
print(f"F1分数: {f1_lr:.4f}")
print(f"ROC-AUC: {roc_auc_lr:.4f}")
# 可视化混淆矩阵
cm_lr = confusion_matrix(y_test, y_pred_lr)
sns.heatmap(cm_lr, annot=True, fmt='d', cmap='Blues', ax=axes[0])
axes[0].set_title('逻辑回归 - 混淆矩阵')
axes[0].set_xlabel('预测标签')
axes[0].set_ylabel('真实标签')
# 2.2 支持向量机
from sklearn.svm import SVC
print("\n" + "="*50)
print("2. 支持向量机")
print("="*50)
svm_model = SVC(
kernel='rbf', # 径向基函数核
C=1.0, # 正则化参数
gamma='scale', # 核系数
probability=True, # 启用概率估计
random_state=42
)
# 训练模型
svm_model.fit(X_train_scaled, y_train)
# 预测
y_pred_svm = svm_model.predict(X_test_scaled)
y_pred_proba_svm = svm_model.predict_proba(X_test_scaled)[:, 1]
# 评估
accuracy_svm = accuracy_score(y_test, y_pred_svm)
precision_svm = precision_score(y_test, y_pred_svm, average='weighted')
recall_svm = recall_score(y_test, y_pred_svm, average='weighted')
f1_svm = f1_score(y_test, y_pred_svm, average='weighted')
roc_auc_svm = roc_auc_score(y_test, y_pred_proba_svm)
model_performance['支持向量机'] = {
'准确率': accuracy_svm,
'精确率': precision_svm,
'召回率': recall_svm,
'F1分数': f1_svm,
'ROC-AUC': roc_auc_svm
}
print(f"准确率: {accuracy_svm:.4f}")
print(f"精确率: {precision_svm:.4f}")
print(f"召回率: {recall_svm:.4f}")
print(f"F1分数: {f1_svm:.4f}")
print(f"ROC-AUC: {roc_auc_svm:.4f}")
# 可视化混淆矩阵
cm_svm = confusion_matrix(y_test, y_pred_svm)
sns.heatmap(cm_svm, annot=True, fmt='d', cmap='Greens', ax=axes[1])
axes[1].set_title('支持向量机 - 混淆矩阵')
axes[1].set_xlabel('预测标签')
axes[1].set_ylabel('真实标签')
# 2.3 随机森林
from sklearn.ensemble import RandomForestClassifier
print("\n" + "="*50)
print("3. 随机森林")
print("="*50)
rf_model = RandomForestClassifier(
n_estimators=100, # 树的数量
max_depth=None, # 树的最大深度
min_samples_split=2, # 内部节点再划分所需最小样本数
min_samples_leaf=1, # 叶节点所需最小样本数
max_features='sqrt', # 寻找最佳分割时考虑的特征数
bootstrap=True, # 有放回抽样
random_state=42,
n_jobs=-1 # 使用所有CPU核心
)
# 训练模型
rf_model.fit(X_train_scaled, y_train)
# 预测
y_pred_rf = rf_model.predict(X_test_scaled)
y_pred_proba_rf = rf_model.predict_proba(X_test_scaled)[:, 1]
# 评估
accuracy_rf = accuracy_score(y_test, y_pred_rf)
precision_rf = precision_score(y_test, y_pred_rf, average='weighted')
recall_rf = recall_score(y_test, y_pred_rf, average='weighted')
f1_rf = f1_score(y_test, y_pred_rf, average='weighted')
roc_auc_rf = roc_auc_score(y_test, y_pred_proba_rf)
model_performance['随机森林'] = {
'准确率': accuracy_rf,
'精确率': precision_rf,
'召回率': recall_rf,
'F1分数': f1_rf,
'ROC-AUC': roc_auc_rf
}
print(f"准确率: {accuracy_rf:.4f}")
print(f"精确率: {precision_rf:.4f}")
print(f"召回率: {recall_rf:.4f}")
print(f"F1分数: {f1_rf:.4f}")
print(f"ROC-AUC: {roc_auc_rf:.4f}")
# 特征重要性
feature_importance = rf_model.feature_importances_
sorted_idx = np.argsort(feature_importance)[-10:] # 取最重要的10个特征
axes[2].barh(range(len(sorted_idx)), feature_importance[sorted_idx])
axes[2].set_yticks(range(len(sorted_idx)))
axes[2].set_yticklabels([f'特征_{i}' for i in sorted_idx])
axes[2].set_title('随机森林 - 特征重要性')
axes[2].set_xlabel('重要性分数')
# 2.4 梯度提升树
from sklearn.ensemble import GradientBoostingClassifier
print("\n" + "="*50)
print("4. 梯度提升树")
print("="*50)
gb_model = GradientBoostingClassifier(
n_estimators=100,
learning_rate=0.1,
max_depth=3,
subsample=0.8, # 子采样比例
random_state=42
)
# 训练模型
gb_model.fit(X_train_scaled, y_train)
# 预测
y_pred_gb = gb_model.predict(X_test_scaled)
y_pred_proba_gb = gb_model.predict_proba(X_test_scaled)[:, 1]
# 评估
accuracy_gb = accuracy_score(y_test, y_pred_gb)
precision_gb = precision_score(y_test, y_pred_gb, average='weighted')
recall_gb = recall_score(y_test, y_pred_gb, average='weighted')
f1_gb = f1_score(y_test, y_pred_gb, average='weighted')
roc_auc_gb = roc_auc_score(y_test, y_pred_proba_gb)
model_performance['梯度提升树'] = {
'准确率': accuracy_gb,
'精确率': precision_gb,
'召回率': recall_gb,
'F1分数': f1_gb,
'ROC-AUC': roc_auc_gb
}
print(f"准确率: {accuracy_gb:.4f}")
print(f"精确率: {precision_gb:.4f}")
print(f"召回率: {recall_gb:.4f}")
print(f"F1分数: {f1_gb:.4f}")
print(f"ROC-AUC: {roc_auc_gb:.4f}")
# 学习曲线
train_sizes = np.linspace(0.1, 1.0, 10)
train_scores = []
test_scores = []
for size in train_sizes:
# 采样
n_samples = int(size * len(X_train_scaled))
X_subset = X_train_scaled[:n_samples]
y_subset = y_train[:n_samples]
# 训练
gb_temp = GradientBoostingClassifier(n_estimators=50, random_state=42)
gb_temp.fit(X_subset, y_subset)
# 评估
train_score = gb_temp.score(X_subset, y_subset)
test_score = gb_temp.score(X_test_scaled, y_test)
train_scores.append(train_score)
test_scores.append(test_score)
axes[3].plot(train_sizes * len(X_train_scaled), train_scores, 'o-', label='训练分数')
axes[3].plot(train_sizes * len(X_train_scaled), test_scores, 's-', label='测试分数')
axes[3].set_title('梯度提升树 - 学习曲线')
axes[3].set_xlabel('训练样本数')
axes[3].set_ylabel('分数')
axes[3].legend()
axes[3].grid(True, alpha=0.3)
# 2.5 K近邻
from sklearn.neighbors import KNeighborsClassifier
print("\n" + "="*50)
print("5. K近邻算法")
print("="*50)
knn_model = KNeighborsClassifier(
n_neighbors=5, # K值
weights='uniform', # 权重函数
algorithm='auto', # 算法
p=2 # 距离度量(2表示欧氏距离)
)
# 训练模型
knn_model.fit(X_train_scaled, y_train)
# 预测
y_pred_knn = knn_model.predict(X_test_scaled)
y_pred_proba_knn = knn_model.predict_proba(X_test_scaled)[:, 1]
# 评估
accuracy_knn = accuracy_score(y_test, y_pred_knn)
precision_knn = precision_score(y_test, y_pred_knn, average='weighted')
recall_knn = recall_score(y_test, y_pred_knn, average='weighted')
f1_knn = f1_score(y_test, y_pred_knn, average='weighted')
roc_auc_knn = roc_auc_score(y_test, y_pred_proba_knn)
model_performance['K近邻'] = {
'准确率': accuracy_knn,
'精确率': precision_knn,
'召回率': recall_knn,
'F1分数': f1_knn,
'ROC-AUC': roc_auc_knn
}
print(f"准确率: {accuracy_knn:.4f}")
print(f"精确率: {precision_knn:.4f}")
print(f"召回率: {recall_knn:.4f}")
print(f"F1分数: {f1_knn:.4f}")
print(f"ROC-AUC: {roc_auc_knn:.4f}")
# 不同K值的性能
k_values = range(1, 21)
k_scores = []
for k in k_values:
knn_temp = KNeighborsClassifier(n_neighbors=k)
knn_temp.fit(X_train_scaled, y_train)
score = knn_temp.score(X_test_scaled, y_test)
k_scores.append(score)
axes[4].plot(k_values, k_scores, 'o-')
axes[4].set_title('K近邻 - 不同K值的性能')
axes[4].set_xlabel('K值')
axes[4].set_ylabel('准确率')
axes[4].grid(True, alpha=0.3)
# 2.6 朴素贝叶斯
from sklearn.naive_bayes import GaussianNB
print("\n" + "="*50)
print("6. 朴素贝叶斯")
print("="*50)
nb_model = GaussianNB()
# 训练模型
nb_model.fit(X_train_scaled, y_train)
# 预测
y_pred_nb = nb_model.predict(X_test_scaled)
y_pred_proba_nb = nb_model.predict_proba(X_test_scaled)[:, 1]
# 评估
accuracy_nb = accuracy_score(y_test, y_pred_nb)
precision_nb = precision_score(y_test, y_pred_nb, average='weighted')
recall_nb = recall_score(y_test, y_pred_nb, average='weighted')
f1_nb = f1_score(y_test, y_pred_nb, average='weighted')
roc_auc_nb = roc_auc_score(y_test, y_pred_proba_nb)
model_performance['朴素贝叶斯'] = {
'准确率': accuracy_nb,
'精确率': precision_nb,
'召回率': recall_nb,
'F1分数': f1_nb,
'ROC-AUC': roc_auc_nb
}
print(f"准确率: {accuracy_nb:.4f}")
print(f"精确率: {precision_nb:.4f}")
print(f"召回率: {recall_nb:.4f}")
print(f"F1分数: {f1_nb:.4f}")
print(f"ROC-AUC: {roc_auc_nb:.4f}")
# 概率分布可视化
probabilities = nb_model.predict_proba(X_test_scaled)
positive_probs = probabilities[y_test == 1, 1]
negative_probs = probabilities[y_test == 0, 1]
axes[5].hist(positive_probs, bins=20, alpha=0.7, label='正类', density=True)
axes[5].hist(negative_probs, bins=20, alpha=0.7, label='负类', density=True)
axes[5].set_title('朴素贝叶斯 - 概率分布')
axes[5].set_xlabel('预测概率')
axes[5].set_ylabel('密度')
axes[5].legend()
axes[5].grid(True, alpha=0.3)
plt.suptitle('分类算法性能比较', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 模型性能比较
print("\n" + "="*60)
print("模型性能总结")
print("="*60)
performance_df = pd.DataFrame(model_performance).T
print(performance_df.round(4))
# 绘制性能比较图
fig, ax = plt.subplots(figsize=(12, 6))
metrics = ['准确率', '精确率', '召回率', 'F1分数', 'ROC-AUC']
x = np.arange(len(metrics))
width = 0.15
for i, (model_name, metrics_dict) in enumerate(model_performance.items()):
values = [metrics_dict[metric] for metric in metrics]
offset = width * (i - len(model_performance) / 2)
ax.bar(x + offset, values, width, label=model_name)
ax.set_xlabel('评估指标')
ax.set_ylabel('分数')
ax.set_title('分类算法性能比较')
ax.set_xticks(x)
ax.set_xticklabels(metrics)
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
ax.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
# 交叉验证比较
print("\n" + "="*60)
print("交叉验证结果(5折)")
print("="*60)
cv_scores = {}
for model_name in model_performance.keys():
if model_name == '逻辑回归':
model = lr_model
elif model_name == '支持向量机':
model = svm_model
elif model_name == '随机森林':
model = rf_model
elif model_name == '梯度提升树':
model = gb_model
elif model_name == 'K近邻':
model = knn_model
elif model_name == '朴素贝叶斯':
model = nb_model
scores = cross_val_score(model, X_train_scaled, y_train, cv=5, scoring='accuracy')
cv_scores[model_name] = {
'平均准确率': scores.mean(),
'标准差': scores.std()
}
print(f"{model_name}: {scores.mean():.4f} (±{scores.std():.4f})")
3. 监督学习:回归算法
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, explained_variance_score
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
import seaborn as sns
# 设置样式
plt.style.use('seaborn-v0_8-darkgrid')
sns.set_palette("husl")
# 创建更复杂的回归数据集
from sklearn.datasets import make_regression
X_reg, y_reg = make_regression(
n_samples=1000,
n_features=15,
n_informative=10,
noise=20.0,
bias=100.0,
random_state=42
)
# 添加非线性关系
X_reg[:, 0] = X_reg[:, 0] ** 2 # 平方项
X_reg[:, 1] = np.sin(X_reg[:, 1]) # 正弦项
X_reg[:, 2] = np.log(np.abs(X_reg[:, 2]) + 1) # 对数项
# 数据分割和标准化
X_train, X_test, y_train, y_test = train_test_split(
X_reg, y_reg, test_size=0.2, random_state=42
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print(f"训练集形状: {X_train_scaled.shape}")
print(f"测试集形状: {X_test_scaled.shape}")
print(f"目标值范围: [{y_reg.min():.2f}, {y_reg.max():.2f}]")
# 创建图形
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()
# 存储模型性能
reg_performance = {}
# 3.1 线性回归
from sklearn.linear_model import LinearRegression
print("\n" + "="*50)
print("1. 线性回归")
print("="*50)
lr_reg = LinearRegression()
# 训练模型
lr_reg.fit(X_train_scaled, y_train)
# 预测
y_pred_lr = lr_reg.predict(X_test_scaled)
# 评估
mse_lr = mean_squared_error(y_test, y_pred_lr)
mae_lr = mean_absolute_error(y_test, y_pred_lr)
r2_lr = r2_score(y_test, y_pred_lr)
evs_lr = explained_variance_score(y_test, y_pred_lr)
reg_performance['线性回归'] = {
'均方误差': mse_lr,
'平均绝对误差': mae_lr,
'R²分数': r2_lr,
'解释方差': evs_lr
}
print(f"均方误差 (MSE): {mse_lr:.4f}")
print(f"平均绝对误差 (MAE): {mae_lr:.4f}")
print(f"R²分数: {r2_lr:.4f}")
print(f"解释方差: {evs_lr:.4f}")
# 可视化预测 vs 实际值
axes[0].scatter(y_test, y_pred_lr, alpha=0.6, s=30)
axes[0].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
axes[0].set_xlabel('实际值')
axes[0].set_ylabel('预测值')
axes[0].set_title('线性回归 - 预测 vs 实际')
axes[0].grid(True, alpha=0.3)
# 3.2 岭回归
from sklearn.linear_model import Ridge
print("\n" + "="*50)
print("2. 岭回归 (L2正则化)")
print("="*50)
ridge_reg = Ridge(alpha=1.0, random_state=42)
# 训练模型
ridge_reg.fit(X_train_scaled, y_train)
# 预测
y_pred_ridge = ridge_reg.predict(X_test_scaled)
# 评估
mse_ridge = mean_squared_error(y_test, y_pred_ridge)
mae_ridge = mean_absolute_error(y_test, y_pred_ridge)
r2_ridge = r2_score(y_test, y_pred_ridge)
evs_ridge = explained_variance_score(y_test, y_pred_ridge)
reg_performance['岭回归'] = {
'均方误差': mse_ridge,
'平均绝对误差': mae_ridge,
'R²分数': r2_ridge,
'解释方差': evs_ridge
}
print(f"均方误差 (MSE): {mse_ridge:.4f}")
print(f"平均绝对误差 (MAE): {mae_ridge:.4f}")
print(f"R²分数: {r2_ridge:.4f}")
print(f"解释方差: {evs_ridge:.4f}")
# 不同alpha值的性能
alphas = np.logspace(-3, 3, 50)
ridge_scores = []
for alpha in alphas:
ridge_temp = Ridge(alpha=alpha)
ridge_temp.fit(X_train_scaled, y_train)
score = ridge_temp.score(X_test_scaled, y_test)
ridge_scores.append(score)
axes[1].semilogx(alphas, ridge_scores)
axes[1].set_xlabel('Alpha (正则化强度)')
axes[1].set_ylabel('R²分数')
axes[1].set_title('岭回归 - 不同Alpha值的性能')
axes[1].grid(True, alpha=0.3)
# 3.3 Lasso回归
from sklearn.linear_model import Lasso
print("\n" + "="*50)
print("3. Lasso回归 (L1正则化)")
print("="*50)
lasso_reg = Lasso(alpha=0.1, random_state=42)
# 训练模型
lasso_reg.fit(X_train_scaled, y_train)
# 预测
y_pred_lasso = lasso_reg.predict(X_test_scaled)
# 评估
mse_lasso = mean_squared_error(y_test, y_pred_lasso)
mae_lasso = mean_absolute_error(y_test, y_pred_lasso)
r2_lasso = r2_score(y_test, y_pred_lasso)
evs_lasso = explained_variance_score(y_test, y_pred_lasso)
reg_performance['Lasso回归'] = {
'均方误差': mse_lasso,
'平均绝对误差': mae_lasso,
'R²分数': r2_lasso,
'解释方差': evs_lasso
}
print(f"均方误差 (MSE): {mse_lasso:.4f}")
print(f"平均绝对误差 (MAE): {mae_lasso:.4f}")
print(f"R²分数: {r2_lasso:.4f}")
print(f"解释方差: {evs_lasso:.4f}")
# 特征选择效果
coef_lasso = lasso_reg.coef_
non_zero_coef = np.sum(coef_lasso != 0)
print(f"非零系数数量: {non_zero_coef}/{len(coef_lasso)}")
axes[2].bar(range(len(coef_lasso)), coef_lasso)
axes[2].set_xlabel('特征索引')
axes[2].set_ylabel('系数值')
axes[2].set_title('Lasso回归 - 特征系数')
axes[2].axhline(y=0, color='r', linestyle='-', alpha=0.3)
axes[2].grid(True, alpha=0.3)
# 3.4 弹性网络
from sklearn.linear_model import ElasticNet
print("\n" + "="*50)
print("4. 弹性网络 (L1+L2正则化)")
print("="*50)
elastic_reg = ElasticNet(alpha=0.1, l1_ratio=0.5, random_state=42)
# 训练模型
elastic_reg.fit(X_train_scaled, y_train)
# 预测
y_pred_elastic = elastic_reg.predict(X_test_scaled)
# 评估
mse_elastic = mean_squared_error(y_test, y_pred_elastic)
mae_elastic = mean_absolute_error(y_test, y_pred_elastic)
r2_elastic = r2_score(y_test, y_pred_elastic)
evs_elastic = explained_variance_score(y_test, y_pred_elastic)
reg_performance['弹性网络'] = {
'均方误差': mse_elastic,
'平均绝对误差': mae_elastic,
'R²分数': r2_elastic,
'解释方差': evs_elastic
}
print(f"均方误差 (MSE): {mse_elastic:.4f}")
print(f"平均绝对误差 (MAE): {mae_elastic:.4f}")
print(f"R²分数: {r2_elastic:.4f}")
print(f"解释方差: {evs_elastic:.4f}")
# 不同l1_ratio的性能
l1_ratios = np.linspace(0, 1, 20)
elastic_scores = []
for l1_ratio in l1_ratios:
elastic_temp = ElasticNet(alpha=0.1, l1_ratio=l1_ratio, random_state=42)
elastic_temp.fit(X_train_scaled, y_train)
score = elastic_temp.score(X_test_scaled, y_test)
elastic_scores.append(score)
axes[3].plot(l1_ratios, elastic_scores, 'o-')
axes[3].set_xlabel('L1比例 (0=L2, 1=L1)')
axes[3].set_ylabel('R²分数')
axes[3].set_title('弹性网络 - 不同L1比例的性能')
axes[3].grid(True, alpha=0.3)
# 3.5 随机森林回归
from sklearn.ensemble import RandomForestRegressor
print("\n" + "="*50)
print("5. 随机森林回归")
print("="*50)
rf_reg = RandomForestRegressor(
n_estimators=100,
max_depth=None,
min_samples_split=2,
min_samples_leaf=1,
random_state=42,
n_jobs=-1
)
# 训练模型
rf_reg.fit(X_train_scaled, y_train)
# 预测
y_pred_rf = rf_reg.predict(X_test_scaled)
# 评估
mse_rf = mean_squared_error(y_test, y_pred_rf)
mae_rf = mean_absolute_error(y_test, y_pred_rf)
r2_rf = r2_score(y_test, y_pred_rf)
evs_rf = explained_variance_score(y_test, y_pred_rf)
reg_performance['随机森林回归'] = {
'均方误差': mse_rf,
'平均绝对误差': mae_rf,
'R²分数': r2_rf,
'解释方差': evs_rf
}
print(f"均方误差 (MSE): {mse_rf:.4f}")
print(f"平均绝对误差 (MAE): {mae_rf:.4f}")
print(f"R²分数: {r2_rf:.4f}")
print(f"解释方差: {evs_rf:.4f}")
# 特征重要性
feature_importance_reg = rf_reg.feature_importances_
sorted_idx_reg = np.argsort(feature_importance_reg)[-10:]
axes[4].barh(range(len(sorted_idx_reg)), feature_importance_reg[sorted_idx_reg])
axes[4].set_yticks(range(len(sorted_idx_reg)))
axes[4].set_yticklabels([f'特征_{i}' for i in sorted_idx_reg])
axes[4].set_title('随机森林回归 - 特征重要性')
axes[4].set_xlabel('重要性分数')
# 3.6 梯度提升回归
from sklearn.ensemble import GradientBoostingRegressor
print("\n" + "="*50)
print("6. 梯度提升回归")
print("="*50)
gb_reg = GradientBoostingRegressor(
n_estimators=100,
learning_rate=0.1,
max_depth=3,
subsample=0.8,
random_state=42
)
# 训练模型
gb_reg.fit(X_train_scaled, y_train)
# 预测
y_pred_gb = gb_reg.predict(X_test_scaled)
# 评估
mse_gb = mean_squared_error(y_test, y_pred_gb)
mae_gb = mean_absolute_error(y_test, y_pred_gb)
r2_gb = r2_score(y_test, y_pred_gb)
evs_gb = explained_variance_score(y_test, y_pred_gb)
reg_performance['梯度提升回归'] = {
'均方误差': mse_gb,
'平均绝对误差': mae_gb,
'R²分数': r2_gb,
'解释方差': evs_gb
}
print(f"均方误差 (MSE): {mse_gb:.4f}")
print(f"平均绝对误差 (MAE): {mae_gb:.4f}")
print(f"R²分数: {r2_gb:.4f}")
print(f"解释方差: {evs_gb:.4f}")
# 残差分析
residuals = y_test - y_pred_gb
axes[5].scatter(y_pred_gb, residuals, alpha=0.6, s=30)
axes[5].axhline(y=0, color='r', linestyle='--', alpha=0.5)
axes[5].set_xlabel('预测值')
axes[5].set_ylabel('残差')
axes[5].set_title('梯度提升回归 - 残差图')
axes[5].grid(True, alpha=0.3)
plt.suptitle('回归算法性能比较', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 模型性能比较
print("\n" + "="*60)
print("回归模型性能总结")
print("="*60)
reg_performance_df = pd.DataFrame(reg_performance).T
print(reg_performance_df.round(4))
# 绘制性能比较图
fig, ax = plt.subplots(figsize=(12, 6))
metrics = ['均方误差', '平均绝对误差', 'R²分数', '解释方差']
x = np.arange(len(metrics))
width = 0.15
# 对于误差指标,值越小越好,我们取负值以便比较
for i, (model_name, metrics_dict) in enumerate(reg_performance.items()):
values = []
for metric in metrics:
value = metrics_dict[metric]
if metric in ['均方误差', '平均绝对误差']:
values.append(-value) # 负值,这样在图上"更高"表示更好
else:
values.append(value)
offset = width * (i - len(reg_performance) / 2)
ax.bar(x + offset, values, width, label=model_name)
ax.set_xlabel('评估指标')
ax.set_ylabel('分数 (误差指标为负值)')
ax.set_title('回归算法性能比较')
ax.set_xticks(x)
ax.set_xticklabels(metrics)
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
ax.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
# 多项式特征扩展
print("\n" + "="*60)
print("多项式特征扩展效果")
print("="*60)
# 创建多项式特征
poly = PolynomialFeatures(degree=2, include_bias=False)
X_train_poly = poly.fit_transform(X_train_scaled)
X_test_poly = poly.transform(X_test_scaled)
print(f"原始特征数: {X_train_scaled.shape[1]}")
print(f"多项式特征数 (degree=2): {X_train_poly.shape[1]}")
# 在线性回归上测试多项式特征
lr_poly = LinearRegression()
lr_poly.fit(X_train_poly, y_train)
y_pred_poly = lr_poly.predict(X_test_poly)
r2_poly = r2_score(y_test, y_pred_poly)
print(f"多项式线性回归 R²分数: {r2_poly:.4f}")
print(f"相比普通线性回归改进: {r2_poly - r2_lr:.4f}")
4. 无监督学习:聚类和降维
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs, make_moons, make_circles
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.mixture import GaussianMixture
from sklearn.decomposition import PCA, KernelPCA
from sklearn.manifold import TSNE, Isomap
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
import seaborn as sns
# 设置样式
plt.style.use('seaborn-v0_8-darkgrid')
sns.set_palette("husl")
# 创建多种形状的数据集
np.random.seed(42)
# 1. 球形聚类数据
X_sphere, y_sphere = make_blobs(
n_samples=300,
n_features=2,
centers=3,
cluster_std=0.8,
random_state=42
)
# 2. 新月形数据
X_moon, y_moon = make_moons(
n_samples=300,
noise=0.1,
random_state=42
)
# 3. 环形数据
X_circle, _ = make_circles(
n_samples=300,
noise=0.05,
factor=0.5,
random_state=42
)
# 4. 复杂形状数据
X_complex = np.concatenate([
make_blobs(n_samples=100, centers=1, cluster_std=0.3, random_state=42)[0] + [2, 2],
make_blobs(n_samples=100, centers=1, cluster_std=0.3, random_state=42)[0] + [-2, -2],
make_moons(n_samples=100, noise=0.05, random_state=42)[0] * 1.5 + [0, 2]
])
# 标准化数据
scaler = StandardScaler()
datasets = {
'球形数据': scaler.fit_transform(X_sphere),
'新月形数据': scaler.fit_transform(X_moon),
'环形数据': scaler.fit_transform(X_circle),
'复杂数据': scaler.fit_transform(X_complex)
}
# 创建可视化
fig, axes = plt.subplots(4, 5, figsize=(20, 16))
# 存储聚类性能
clustering_results = {}
# 遍历每个数据集
for row_idx, (dataset_name, X) in enumerate(datasets.items()):
print(f"\n{'='*60}")
print(f"数据集: {dataset_name}")
print(f"{'='*60}")
# 显示原始数据
axes[row_idx, 0].scatter(X[:, 0], X[:, 1], s=30, alpha=0.7, c='gray')
axes[row_idx, 0].set_title(f'{dataset_name}\n原始数据')
axes[row_idx, 0].set_xlabel('特征1')
axes[row_idx, 0].set_ylabel('特征2')
axes[row_idx, 0].grid(True, alpha=0.3)
# 4.1 K-Means聚类
print("\n1. K-Means聚类:")
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
y_kmeans = kmeans.fit_predict(X)
# 评估
silhouette_kmeans = silhouette_score(X, y_kmeans)
calinski_kmeans = calinski_harabasz_score(X, y_kmeans)
davies_kmeans = davies_bouldin_score(X, y_kmeans)
clustering_results[f'{dataset_name}_KMeans'] = {
'轮廓系数': silhouette_kmeans,
'Calinski-Harabasz指数': calinski_kmeans,
'Davies-Bouldin指数': davies_kmeans
}
print(f" 轮廓系数: {silhouette_kmeans:.4f}")
print(f" Calinski-Harabasz指数: {calinski_kmeans:.4f}")
print(f" Davies-Bouldin指数: {davies_kmeans:.4f}")
# 可视化
scatter = axes[row_idx, 1].scatter(X[:, 0], X[:, 1], c=y_kmeans, s=30, alpha=0.7, cmap='viridis')
centers = kmeans.cluster_centers_
axes[row_idx, 1].scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.8, marker='X')
axes[row_idx, 1].set_title('K-Means聚类\n(红色X为聚类中心)')
axes[row_idx, 1].set_xlabel('特征1')
axes[row_idx, 1].set_ylabel('特征2')
axes[row_idx, 1].grid(True, alpha=0.3)
# 4.2 DBSCAN聚类
print("\n2. DBSCAN聚类:")
dbscan = DBSCAN(eps=0.3, min_samples=5)
y_dbscan = dbscan.fit_predict(X)
# 评估(只计算有聚类的数据)
unique_labels = np.unique(y_dbscan)
n_clusters = len(unique_labels) - (1 if -1 in unique_labels else 0)
if n_clusters > 1:
silhouette_dbscan = silhouette_score(X[y_dbscan != -1], y_dbscan[y_dbscan != -1])
calinski_dbscan = calinski_harabasz_score(X[y_dbscan != -1], y_dbscan[y_dbscan != -1])
davies_dbscan = davies_bouldin_score(X[y_dbscan != -1], y_dbscan[y_dbscan != -1])
else:
silhouette_dbscan = calinski_dbscan = davies_dbscan = np.nan
clustering_results[f'{dataset_name}_DBSCAN'] = {
'轮廓系数': silhouette_dbscan,
'Calinski-Harabasz指数': calinski_dbscan,
'Davies-Bouldin指数': davies_dbscan,
'聚类数': n_clusters,
'噪声点比例': np.sum(y_dbscan == -1) / len(y_dbscan)
}
print(f" 聚类数: {n_clusters}")
print(f" 噪声点比例: {np.sum(y_dbscan == -1) / len(y_dbscan):.2%}")
if not np.isnan(silhouette_dbscan):
print(f" 轮廓系数: {silhouette_dbscan:.4f}")
# 可视化
scatter = axes[row_idx, 2].scatter(X[:, 0], X[:, 1], c=y_dbscan, s=30, alpha=0.7, cmap='viridis')
axes[row_idx, 2].set_title('DBSCAN聚类\n(-1为噪声点)')
axes[row_idx, 2].set_xlabel('特征1')
axes[row_idx, 2].set_ylabel('特征2')
axes[row_idx, 2].grid(True, alpha=0.3)
# 4.3 层次聚类
print("\n3. 层次聚类:")
hierarchical = AgglomerativeClustering(n_clusters=3, linkage='ward')
y_hierarchical = hierarchical.fit_predict(X)
# 评估
silhouette_hierarchical = silhouette_score(X, y_hierarchical)
calinski_hierarchical = calinski_harabasz_score(X, y_hierarchical)
davies_hierarchical = davies_bouldin_score(X, y_hierarchical)
clustering_results[f'{dataset_name}_Hierarchical'] = {
'轮廓系数': silhouette_hierarchical,
'Calinski-Harabasz指数': calinski_hierarchical,
'Davies-Bouldin指数': davies_hierarchical
}
print(f" 轮廓系数: {silhouette_hierarchical:.4f}")
print(f" Calinski-Harabasz指数: {calinski_hierarchical:.4f}")
print(f" Davies-Bouldin指数: {davies_hierarchical:.4f}")
# 可视化
scatter = axes[row_idx, 3].scatter(X[:, 0], X[:, 1], c=y_hierarchical, s=30, alpha=0.7, cmap='viridis')
axes[row_idx, 3].set_title('层次聚类\n(ward linkage)')
axes[row_idx, 3].set_xlabel('特征1')
axes[row_idx, 3].set_ylabel('特征2')
axes[row_idx, 3].grid(True, alpha=0.3)
# 4.4 高斯混合模型
print("\n4. 高斯混合模型:")
gmm = GaussianMixture(n_components=3, random_state=42)
y_gmm = gmm.fit_predict(X)
# 评估
silhouette_gmm = silhouette_score(X, y_gmm)
calinski_gmm = calinski_harabasz_score(X, y_gmm)
davies_gmm = davies_bouldin_score(X, y_gmm)
clustering_results[f'{dataset_name}_GMM'] = {
'轮廓系数': silhouette_gmm,
'Calinski-Harabasz指数': calinski_gmm,
'Davies-Bouldin指数': davies_gmm
}
print(f" 轮廓系数: {silhouette_gmm:.4f}")
print(f" Calinski-Harabasz指数: {calinski_gmm:.4f}")
print(f" Davies-Bouldin指数: {davies_gmm:.4f}")
# 可视化
scatter = axes[row_idx, 4].scatter(X[:, 0], X[:, 1], c=y_gmm, s=30, alpha=0.7, cmap='viridis')
# 绘制高斯分布的等高线
if dataset_name == '球形数据': # 只对球形数据绘制等高线
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
np.linspace(y_min, y_max, 100))
Z = gmm.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
axes[row_idx, 4].contourf(xx, yy, Z, alpha=0.3, cmap='viridis')
axes[row_idx, 4].set_title('高斯混合模型\n(软聚类)')
axes[row_idx, 4].set_xlabel('特征1')
axes[row_idx, 4].set_ylabel('特征2')
axes[row_idx, 4].grid(True, alpha=0.3)
plt.suptitle('聚类算法在不同形状数据集上的表现', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 降维算法演示
print("\n" + "="*60)
print("降维算法演示")
print("="*60)
# 创建高维数据
from sklearn.datasets import make_classification
X_high_dim, y_high_dim = make_classification(
n_samples=500,
n_features=50,
n_informative=10,
n_redundant=10,
n_classes=3,
random_state=42
)
print(f"高维数据形状: {X_high_dim.shape}")
print(f"类别数: {len(np.unique(y_high_dim))}")
# 标准化
X_high_dim_scaled = StandardScaler().fit_transform(X_high_dim)
# 创建降维可视化
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()
# 5.1 PCA降维
print("\n1. PCA (主成分分析):")
pca = PCA(n_components=2, random_state=42)
X_pca = pca.fit_transform(X_high_dim_scaled)
print(f" 解释方差比: {pca.explained_variance_ratio_}")
print(f" 累计解释方差: {np.sum(pca.explained_variance_ratio_):.4f}")
scatter = axes[0].scatter(X_pca[:, 0], X_pca[:, 1], c=y_high_dim, s=30, alpha=0.7, cmap='viridis')
axes[0].set_title(f'PCA降维\n(累计方差: {np.sum(pca.explained_variance_ratio_):.2%})')
axes[0].set_xlabel('主成分1')
axes[0].set_ylabel('主成分2')
axes[0].grid(True, alpha=0.3)
# 5.2 核PCA
print("\n2. 核PCA:")
kpca = KernelPCA(n_components=2, kernel='rbf', gamma=15, random_state=42)
X_kpca = kpca.fit_transform(X_high_dim_scaled)
scatter = axes[1].scatter(X_kpca[:, 0], X_kpca[:, 1], c=y_high_dim, s=30, alpha=0.7, cmap='viridis')
axes[1].set_title('核PCA降维\n(RBF核)')
axes[1].set_xlabel('成分1')
axes[1].set_ylabel('成分2')
axes[1].grid(True, alpha=0.3)
# 5.3 t-SNE降维
print("\n3. t-SNE降维:")
tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X_high_dim_scaled)
scatter = axes[2].scatter(X_tsne[:, 0], X_tsne[:, 1], c=y_high_dim, s=30, alpha=0.7, cmap='viridis')
axes[2].set_title('t-SNE降维\n(perplexity=30)')
axes[2].set_xlabel('t-SNE 1')
axes[2].set_ylabel('t-SNE 2')
axes[2].grid(True, alpha=0.3)
# 5.4 Isomap降维
print("\n4. Isomap降维:")
isomap = Isomap(n_components=2, n_neighbors=10)
X_isomap = isomap.fit_transform(X_high_dim_scaled)
scatter = axes[3].scatter(X_isomap[:, 0], X_isomap[:, 1], c=y_high_dim, s=30, alpha=0.7, cmap='viridis')
axes[3].set_title('Isomap降维\n(n_neighbors=10)')
axes[3].set_xlabel('Isomap 1')
axes[3].set_ylabel('Isomap 2')
axes[3].grid(True, alpha=0.3)
# 5.5 不同PCA组件数的解释方差
print("\n5. PCA组件数 vs 解释方差:")
n_components_range = range(1, 21)
explained_variances = []
for n in n_components_range:
pca_temp = PCA(n_components=n)
pca_temp.fit(X_high_dim_scaled)
explained_variances.append(np.sum(pca_temp.explained_variance_ratio_))
axes[4].plot(n_components_range, explained_variances, 'o-')
axes[4].axhline(y=0.95, color='r', linestyle='--', alpha=0.5, label='95%方差线')
axes[4].set_xlabel('PCA组件数')
axes[4].set_ylabel('累计解释方差')
axes[4].set_title('PCA组件数 vs 解释方差')
axes[4].legend()
axes[4].grid(True, alpha=0.3)
# 找到达到95%方差所需的最小组件数
for n, var in zip(n_components_range, explained_variances):
if var >= 0.95:
print(f" 达到95%解释方差所需最小组件数: {n}")
break
# 5.6 聚类性能比较
print("\n6. 降维后聚类性能:")
# 使用K-Means在原始数据上
kmeans_original = KMeans(n_clusters=3, random_state=42)
y_pred_original = kmeans_original.fit_predict(X_high_dim_scaled)
silhouette_original = silhouette_score(X_high_dim_scaled, y_pred_original)
# 使用K-Means在PCA降维数据上
kmeans_pca = KMeans(n_clusters=3, random_state=42)
y_pred_pca = kmeans_pca.fit_predict(X_pca)
silhouette_pca = silhouette_score(X_pca, y_pred_pca)
# 使用K-Means在t-SNE降维数据上
kmeans_tsne = KMeans(n_clusters=3, random_state=42)
y_pred_tsne = kmeans_tsne.fit_predict(X_tsne)
silhouette_tsne = silhouette_score(X_tsne, y_pred_tsne)
print(f" 原始数据轮廓系数: {silhouette_original:.4f}")
print(f" PCA降维后轮廓系数: {silhouette_pca:.4f}")
print(f" t-SNE降维后轮廓系数: {silhouette_tsne:.4f}")
# 可视化聚类性能
methods = ['原始数据', 'PCA', 't-SNE']
scores = [silhouette_original, silhouette_pca, silhouette_tsne]
bars = axes[5].bar(methods, scores, color=['skyblue', 'lightgreen', 'lightcoral'])
axes[5].set_ylabel('轮廓系数')
axes[5].set_title('降维对聚类性能的影响')
axes[5].grid(True, alpha=0.3, axis='y')
# 添加数值标签
for bar, score in zip(bars, scores):
height = bar.get_height()
axes[5].text(bar.get_x() + bar.get_width()/2., height + 0.01,
f'{score:.3f}', ha='center', va='bottom')
plt.suptitle('降维算法比较与可视化', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 聚类算法性能总结
print("\n" + "="*60)
print("聚类算法性能总结")
print("="*60)
# 转换为DataFrame以便更好显示
clustering_df = pd.DataFrame(clustering_results).T
print(clustering_df.round(4))
# 绘制聚类算法比较热图
fig, ax = plt.subplots(figsize=(12, 8))
metrics = ['轮廓系数', 'Calinski-Harabasz指数', 'Davies-Bouldin指数']
datasets_names = list(datasets.keys())
algorithms = ['KMeans', 'DBSCAN', 'Hierarchical', 'GMM']
# 为每个指标创建热图数据
for metric_idx, metric in enumerate(metrics):
heatmap_data = np.zeros((len(datasets_names), len(algorithms)))
for i, dataset in enumerate(datasets_names):
for j, algo in enumerate(algorithms):
key = f'{dataset}_{algo}'
if key in clustering_results:
heatmap_data[i, j] = clustering_results[key][metric]
# 归一化(对于Davies-Bouldin指数,值越小越好)
if metric == 'Davies-Bouldin指数':
# 反转值,使得颜色映射一致(深色表示好)
heatmap_data = -heatmap_data
# 绘制热图
ax = plt.subplot(1, 3, metric_idx + 1)
im = ax.imshow(heatmap_data, cmap='YlOrRd', aspect='auto')
ax.set_xticks(range(len(algorithms)))
ax.set_xticklabels(algorithms, rotation=45)
ax.set_yticks(range(len(datasets_names)))
ax.set_yticklabels(datasets_names)
ax.set_title(metric)
# 添加数值
for i in range(len(datasets_names)):
for j in range(len(algorithms)):
value = clustering_results.get(f'{datasets_names[i]}_{algorithms[j]}', {}).get(metric, np.nan)
if not np.isnan(value):
text = ax.text(j, i, f'{value:.2f}',
ha="center", va="center", color="black", fontsize=9)
plt.suptitle('聚类算法性能热图比较', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
四、高级用法
1. 模型选择和超参数调优
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, GridSearchCV, RandomizedSearchCV, cross_val_score, learning_curve, validation_curve
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
# 设置样式
plt.style.use('seaborn-v0_8-darkgrid')
sns.set_palette("husl")
# 创建更复杂的数据集
X, y = make_classification(
n_samples=2000,
n_features=30,
n_informative=20,
n_redundant=5,
n_repeated=5,
n_classes=2,
weights=[0.85, 0.15], # 高度不平衡
flip_y=0.05, # 噪声
random_state=42
)
# 数据分割和标准化
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print(f"训练集形状: {X_train_scaled.shape}")
print(f"测试集形状: {X_test_scaled.shape}")
print(f"训练集类别分布: {np.bincount(y_train)}")
print(f"测试集类别分布: {np.bincount(y_test)}")
# 创建图形
fig, axes = plt.subplots(3, 3, figsize=(15, 12))
axes = axes.flatten()
# 1. 基础模型比较
print("\n" + "="*60)
print("1. 基础模型性能比较")
print("="*60)
models = {
'逻辑回归': LogisticRegression(max_iter=1000, random_state=42),
'支持向量机': SVC(probability=True, random_state=42),
'随机森林': RandomForestClassifier(random_state=42, n_jobs=-1),
'梯度提升树': GradientBoostingClassifier(random_state=42)
}
base_results = {}
for idx, (name, model) in enumerate(models.items()):
# 训练模型
model.fit(X_train_scaled, y_train)
# 预测
y_pred = model.predict(X_test_scaled)
y_pred_proba = model.predict_proba(X_test_scaled)[:, 1]
# 评估
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_pred_proba)
base_results[name] = {
'准确率': accuracy,
'精确率': precision,
'召回率': recall,
'F1分数': f1,
'ROC-AUC': roc_auc
}
print(f"\n{name}:")
print(f" 准确率: {accuracy:.4f}")
print(f" 精确率: {precision:.4f}")
print(f" 召回率: {recall:.4f}")
print(f" F1分数: {f1:.4f}")
print(f" ROC-AUC: {roc_auc:.4f}")
# 可视化基础模型比较
metrics = ['准确率', '精确率', '召回率', 'F1分数', 'ROC-AUC']
x = np.arange(len(metrics))
width = 0.2
for i, (model_name, scores) in enumerate(base_results.items()):
values = [scores[metric] for metric in metrics]
offset = width * (i - len(base_results) / 2)
axes[0].bar(x + offset, values, width, label=model_name)
axes[0].set_xlabel('评估指标')
axes[0].set_ylabel('分数')
axes[0].set_title('基础模型性能比较')
axes[0].set_xticks(x)
axes[0].set_xticklabels(metrics, rotation=45)
axes[0].legend()
axes[0].grid(True, alpha=0.3, axis='y')
# 2. 网格搜索调优 - 随机森林
print("\n" + "="*60)
print("2. 随机森林网格搜索调优")
print("="*60)
# 定义参数网格
param_grid_rf = {
'n_estimators': [50, 100, 200],
'max_depth': [None, 10, 20, 30],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4],
'max_features': ['sqrt', 'log2']
}
# 创建网格搜索对象
rf_base = RandomForestClassifier(random_state=42, n_jobs=-1)
grid_search_rf = GridSearchCV(
estimator=rf_base,
param_grid=param_grid_rf,
cv=3, # 3折交叉验证
scoring='f1', # 使用F1分数作为评估指标
n_jobs=-1, # 使用所有CPU核心
verbose=1
)
# 执行网格搜索
print("开始网格搜索...")
grid_search_rf.fit(X_train_scaled, y_train)
print("网格搜索完成!")
# 输出最佳参数和分数
print(f"最佳参数: {grid_search_rf.best_params_}")
print(f"最佳交叉验证F1分数: {grid_search_rf.best_score_:.4f}")
# 在测试集上评估最佳模型
best_rf = grid_search_rf.best_estimator_
y_pred_best_rf = best_rf.predict(X_test_scaled)
y_pred_proba_best_rf = best_rf.predict_proba(X_test_scaled)[:, 1]
accuracy_best_rf = accuracy_score(y_test, y_pred_best_rf)
f1_best_rf = f1_score(y_test, y_pred_best_rf)
roc_auc_best_rf = roc_auc_score(y_test, y_pred_proba_best_rf)
print(f"\n最佳模型在测试集上的表现:")
print(f" 准确率: {accuracy_best_rf:.4f}")
print(f" F1分数: {f1_best_rf:.4f}")
print(f" ROC-AUC: {roc_auc_best_rf:.4f}")
# 可视化网格搜索结果
cv_results_rf = pd.DataFrame(grid_search_rf.cv_results_)
# 选择重要的列
important_cols = ['param_n_estimators', 'param_max_depth', 'param_min_samples_split',
'param_min_samples_leaf', 'mean_test_score', 'std_test_score']
cv_results_simple = cv_results_rf[important_cols].copy()
cv_results_simple = cv_results_simple.sort_values('mean_test_score', ascending=False)
print(f"\n前10个最佳参数组合:")
print(cv_results_simple.head(10).to_string())
# 绘制不同参数组合的性能
axes[1].scatter(range(len(cv_results_simple)), cv_results_simple['mean_test_score'],
alpha=0.6, s=30, c=cv_results_simple['std_test_score'], cmap='viridis')
axes[1].set_xlabel('参数组合索引')
axes[1].set_ylabel('平均交叉验证F1分数')
axes[1].set_title('随机森林网格搜索结果')
axes[1].grid(True, alpha=0.3)
axes[1].set_ylim([cv_results_simple['mean_test_score'].min() - 0.02,
cv_results_simple['mean_test_score'].max() + 0.02])
# 3. 随机搜索调优 - 梯度提升树
print("\n" + "="*60)
print("3. 梯度提升树随机搜索调优")
print("="*60)
# 定义参数分布
param_dist_gb = {
'n_estimators': [50, 100, 200, 300],
'learning_rate': [0.001, 0.01, 0.1, 0.2, 0.3],
'max_depth': [3, 5, 7, 9],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4],
'subsample': [0.6, 0.7, 0.8, 0.9, 1.0]
}
# 创建随机搜索对象
gb_base = GradientBoostingClassifier(random_state=42)
random_search_gb = RandomizedSearchCV(
estimator=gb_base,
param_distributions=param_dist_gb,
n_iter=50, # 随机尝试50个参数组合
cv=3,
scoring='f1',
random_state=42,
n_jobs=-1,
verbose=1
)
# 执行随机搜索
print("开始随机搜索...")
random_search_gb.fit(X_train_scaled, y_train)
print("随机搜索完成!")
# 输出最佳参数和分数
print(f"最佳参数: {random_search_gb.best_params_}")
print(f"最佳交叉验证F1分数: {random_search_gb.best_score_:.4f}")
# 在测试集上评估最佳模型
best_gb = random_search_gb.best_estimator_
y_pred_best_gb = best_gb.predict(X_test_scaled)
y_pred_proba_best_gb = best_gb.predict_proba(X_test_scaled)[:, 1]
accuracy_best_gb = accuracy_score(y_test, y_pred_best_gb)
f1_best_gb = f1_score(y_test, y_pred_best_gb)
roc_auc_best_gb = roc_auc_score(y_test, y_pred_proba_best_gb)
print(f"\n最佳模型在测试集上的表现:")
print(f" 准确率: {accuracy_best_gb:.4f}")
print(f" F1分数: {f1_best_gb:.4f}")
print(f" ROC-AUC: {roc_auc_best_gb:.4f}")
# 可视化随机搜索结果
cv_results_gb = pd.DataFrame(random_search_gb.cv_results_)
cv_results_gb_simple = cv_results_gb[['param_n_estimators', 'param_learning_rate',
'param_max_depth', 'mean_test_score', 'std_test_score']]
cv_results_gb_simple = cv_results_gb_simple.sort_values('mean_test_score', ascending=False)
# 绘制学习率和树数量的关系
scatter = axes[2].scatter(cv_results_gb_simple['param_n_estimators'],
cv_results_gb_simple['param_learning_rate'],
c=cv_results_gb_simple['mean_test_score'],
s=50, alpha=0.7, cmap='viridis')
axes[2].set_xlabel('树的数量 (n_estimators)')
axes[2].set_ylabel('学习率 (learning_rate)')
axes[2].set_title('梯度提升树随机搜索结果\n(颜色表示F1分数)')
axes[2].grid(True, alpha=0.3)
# 添加颜色条
cbar = plt.colorbar(scatter, ax=axes[2])
cbar.set_label('F1分数')
# 4. 学习曲线分析
print("\n" + "="*60)
print("4. 学习曲线分析")
print("="*60)
# 选择最佳模型进行分析
best_model = best_rf # 使用随机森林最佳模型
train_sizes, train_scores, test_scores = learning_curve(
estimator=best_model,
X=X_train_scaled,
y=y_train,
train_sizes=np.linspace(0.1, 1.0, 10),
cv=3,
scoring='f1',
n_jobs=-1,
random_state=42
)
# 计算平均值和标准差
train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)
# 绘制学习曲线
axes[3].plot(train_sizes, train_mean, 'o-', color='blue', label='训练分数')
axes[3].fill_between(train_sizes, train_mean - train_std, train_mean + train_std, alpha=0.2, color='blue')
axes[3].plot(train_sizes, test_mean, 's-', color='green', label='交叉验证分数')
axes[3].fill_between(train_sizes, test_mean - test_std, test_mean + test_std, alpha=0.2, color='green')
axes[3].set_xlabel('训练样本数')
axes[3].set_ylabel('F1分数')
axes[3].set_title('学习曲线分析')
axes[3].legend(loc='best')
axes[3].grid(True, alpha=0.3)
# 5. 验证曲线分析
print("\n" + "="*60)
print("5. 验证曲线分析 - 树的数量")
print("="*60)
# 分析n_estimators对性能的影响
param_range = [10, 50, 100, 200, 300, 400, 500]
train_scores_vc, test_scores_vc = validation_curve(
estimator=RandomForestClassifier(random_state=42, n_jobs=-1),
X=X_train_scaled,
y=y_train,
param_name='n_estimators',
param_range=param_range,
cv=3,
scoring='f1',
n_jobs=-1
)
# 计算平均值和标准差
train_mean_vc = np.mean(train_scores_vc, axis=1)
train_std_vc = np.std(train_scores_vc, axis=1)
test_mean_vc = np.mean(test_scores_vc, axis=1)
test_std_vc = np.std(test_scores_vc, axis=1)
# 绘制验证曲线
axes[4].plot(param_range, train_mean_vc, 'o-', color='blue', label='训练分数')
axes[4].fill_between(param_range, train_mean_vc - train_std_vc, train_mean_vc + train_std_vc, alpha=0.2, color='blue')
axes[4].plot(param_range, test_mean_vc, 's-', color='green', label='交叉验证分数')
axes[4].fill_between(param_range, test_mean_vc - test_std_vc, test_mean_vc + test_std_vc, alpha=0.2, color='green')
axes[4].set_xlabel('树的数量 (n_estimators)')
axes[4].set_ylabel('F1分数')
axes[4].set_title('验证曲线 - 树的数量')
axes[4].legend(loc='best')
axes[4].set_xscale('log')
axes[4].grid(True, alpha=0.3)
# 6. 交叉验证策略比较
print("\n" + "="*60)
print("6. 不同交叉验证策略比较")
print("="*60)
from sklearn.model_selection import KFold, StratifiedKFold, ShuffleSplit, StratifiedShuffleSplit
cv_strategies = {
'KFold (k=5)': KFold(n_splits=5, shuffle=True, random_state=42),
'StratifiedKFold (k=5)': StratifiedKFold(n_splits=5, shuffle=True, random_state=42),
'ShuffleSplit': ShuffleSplit(n_splits=5, test_size=0.2, random_state=42),
'StratifiedShuffleSplit': StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=42)
}
cv_results_comparison = {}
for strategy_name, cv in cv_strategies.items():
scores = cross_val_score(best_model, X_train_scaled, y_train,
cv=cv, scoring='f1', n_jobs=-1)
cv_results_comparison[strategy_name] = {
'平均F1分数': scores.mean(),
'标准差': scores.std(),
'最小分数': scores.min(),
'最大分数': scores.max()
}
print(f"\n{strategy_name}:")
print(f" 平均F1分数: {scores.mean():.4f}")
print(f" 标准差: {scores.std():.4f}")
print(f" 分数范围: [{scores.min():.4f}, {scores.max():.4f}]")
# 可视化交叉验证策略比较
strategy_names = list(cv_results_comparison.keys())
mean_scores = [cv_results_comparison[s]['平均F1分数'] for s in strategy_names]
std_scores = [cv_results_comparison[s]['标准差'] for s in strategy_names]
y_pos = np.arange(len(strategy_names))
bars = axes[5].barh(y_pos, mean_scores, xerr=std_scores,
color=['skyblue', 'lightgreen', 'lightcoral', 'gold'])
axes[5].set_yticks(y_pos)
axes[5].set_yticklabels(strategy_names)
axes[5].set_xlabel('F1分数')
axes[5].set_title('交叉验证策略比较')
axes[5].grid(True, alpha=0.3, axis='x')
# 添加数值标签
for bar, mean_score, std_score in zip(bars, mean_scores, std_scores):
width = bar.get_width()
axes[5].text(width + 0.01, bar.get_y() + bar.get_height()/2,
f'{mean_score:.3f} (±{std_score:.3f})',
ha='left', va='center', fontsize=9)
# 7. 模型集成方法
print("\n" + "="*60)
print("7. 模型集成方法比较")
print("="*60)
from sklearn.ensemble import VotingClassifier, BaggingClassifier, AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
# 定义基础模型
base_models = [
('lr', LogisticRegression(max_iter=1000, random_state=42)),
('rf', RandomForestClassifier(n_estimators=100, random_state=42)),
('gb', GradientBoostingClassifier(n_estimators=100, random_state=42))
]
# 7.1 投票分类器
print("\n投票分类器:")
voting_clf = VotingClassifier(
estimators=base_models,
voting='soft' # 软投票(使用概率)
)
voting_clf.fit(X_train_scaled, y_train)
y_pred_voting = voting_clf.predict(X_test_scaled)
f1_voting = f1_score(y_test, y_pred_voting)
print(f" F1分数: {f1_voting:.4f}")
# 7.2 Bagging分类器
print("\nBagging分类器:")
bagging_clf = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=50,
max_samples=0.8,
max_features=0.8,
random_state=42,
n_jobs=-1
)
bagging_clf.fit(X_train_scaled, y_train)
y_pred_bagging = bagging_clf.predict(X_test_scaled)
f1_bagging = f1_score(y_test, y_pred_bagging)
print(f" F1分数: {f1_bagging:.4f}")
# 7.3 AdaBoost分类器
print("\nAdaBoost分类器:")
adaboost_clf = AdaBoostClassifier(
estimator=DecisionTreeClassifier(max_depth=1),
n_estimators=100,
learning_rate=0.1,
random_state=42
)
adaboost_clf.fit(X_train_scaled, y_train)
y_pred_adaboost = adaboost_clf.predict(X_test_scaled)
f1_adaboost = f1_score(y_test, y_pred_adaboost)
print(f" F1分数: {f1_adaboost:.4f}")
# 可视化集成方法比较
ensemble_methods = ['投票分类器', 'Bagging', 'AdaBoost']
ensemble_scores = [f1_voting, f1_bagging, f1_adaboost]
bars = axes[6].bar(ensemble_methods, ensemble_scores,
color=['skyblue', 'lightgreen', 'lightcoral'])
axes[6].set_ylabel('F1分数')
axes[6].set_title('集成方法性能比较')
axes[6].grid(True, alpha=0.3, axis='y')
# 添加数值标签
for bar, score in zip(bars, ensemble_scores):
height = bar.get_height()
axes[6].text(bar.get_x() + bar.get_width()/2., height + 0.01,
f'{score:.3f}', ha='center', va='bottom', fontsize=10)
# 8. 特征选择方法
print("\n" + "="*60)
print("8. 特征选择方法比较")
print("="*60)
from sklearn.feature_selection import SelectKBest, f_classif, RFE, SelectFromModel
feature_selection_results = {}
# 8.1 方差阈值
print("\n1. 基于方差阈值:")
from sklearn.feature_selection import VarianceThreshold
selector_var = VarianceThreshold(threshold=0.1)
X_train_var = selector_var.fit_transform(X_train_scaled)
X_test_var = selector_var.transform(X_test_scaled)
print(f" 原始特征数: {X_train_scaled.shape[1]}")
print(f" 筛选后特征数: {X_train_var.shape[1]}")
# 使用简化特征集训练模型
model_var = RandomForestClassifier(n_estimators=100, random_state=42)
model_var.fit(X_train_var, y_train)
y_pred_var = model_var.predict(X_test_var)
f1_var = f1_score(y_test, y_pred_var)
print(f" F1分数: {f1_var:.4f}")
feature_selection_results['方差阈值'] = {
'特征数': X_train_var.shape[1],
'F1分数': f1_var
}
# 8.2 单变量特征选择
print("\n2. 单变量特征选择 (SelectKBest):")
selector_kbest = SelectKBest(score_func=f_classif, k=15)
X_train_kbest = selector_kbest.fit_transform(X_train_scaled, y_train)
X_test_kbest = selector_kbest.transform(X_test_scaled)
print(f" 选择特征数: {X_train_kbest.shape[1]}")
model_kbest = RandomForestClassifier(n_estimators=100, random_state=42)
model_kbest.fit(X_train_kbest, y_train)
y_pred_kbest = model_kbest.predict(X_test_kbest)
f1_kbest = f1_score(y_test, y_pred_kbest)
print(f" F1分数: {f1_kbest:.4f}")
feature_selection_results['SelectKBest'] = {
'特征数': X_train_kbest.shape[1],
'F1分数': f1_kbest
}
# 8.3 递归特征消除 (RFE)
print("\n3. 递归特征消除 (RFE):")
selector_rfe = RFE(
estimator=RandomForestClassifier(n_estimators=50, random_state=42),
n_features_to_select=15,
step=5
)
X_train_rfe = selector_rfe.fit_transform(X_train_scaled, y_train)
X_test_rfe = selector_rfe.transform(X_test_scaled)
print(f" 选择特征数: {X_train_rfe.shape[1]}")
model_rfe = RandomForestClassifier(n_estimators=100, random_state=42)
model_rfe.fit(X_train_rfe, y_train)
y_pred_rfe = model_rfe.predict(X_test_rfe)
f1_rfe = f1_score(y_test, y_pred_rfe)
print(f" F1分数: {f1_rfe:.4f}")
feature_selection_results['RFE'] = {
'特征数': X_train_rfe.shape[1],
'F1分数': f1_rfe
}
# 8.4 基于模型的特征选择
print("\n4. 基于模型的特征选择:")
selector_model = SelectFromModel(
estimator=RandomForestClassifier(n_estimators=100, random_state=42),
threshold='median'
)
X_train_model = selector_model.fit_transform(X_train_scaled, y_train)
X_test_model = selector_model.transform(X_test_scaled)
print(f" 选择特征数: {X_train_model.shape[1]}")
model_from_model = RandomForestClassifier(n_estimators=100, random_state=42)
model_from_model.fit(X_train_model, y_train)
y_pred_model = model_from_model.predict(X_test_model)
f1_model = f1_score(y_test, y_pred_model)
print(f" F1分数: {f1_model:.4f}")
feature_selection_results['基于模型'] = {
'特征数': X_train_model.shape[1],
'F1分数': f1_model
}
# 可视化特征选择方法比较
methods_fs = list(feature_selection_results.keys())
feature_counts = [feature_selection_results[m]['特征数'] for m in methods_fs]
f1_scores_fs = [feature_selection_results[m]['F1分数'] for m in methods_fs]
x_fs = np.arange(len(methods_fs))
width_fs = 0.35
bars1 = axes[7].bar(x_fs - width_fs/2, feature_counts, width_fs, label='特征数', color='skyblue')
bars2 = axes[7].bar(x_fs + width_fs/2, f1_scores_fs, width_fs, label='F1分数', color='lightgreen')
axes[7].set_xlabel('特征选择方法')
axes[7].set_ylabel('值')
axes[7].set_title('特征选择方法比较')
axes[7].set_xticks(x_fs)
axes[7].set_xticklabels(methods_fs, rotation=45)
axes[7].legend()
axes[7].grid(True, alpha=0.3, axis='y')
# 9. 最终模型比较
print("\n" + "="*60)
print("9. 最终模型性能总结")
print("="*60)
# 收集所有模型的性能
final_results = {
'基础模型': {
'逻辑回归': base_results['逻辑回归']['F1分数'],
'支持向量机': base_results['支持向量机']['F1分数'],
'随机森林': base_results['随机森林']['F1分数'],
'梯度提升树': base_results['梯度提升树']['F1分数']
},
'调优后模型': {
'随机森林(调优)': f1_best_rf,
'梯度提升树(调优)': f1_best_gb
},
'集成方法': {
'投票分类器': f1_voting,
'Bagging': f1_bagging,
'AdaBoost': f1_adaboost
}
}
# 打印最终结果
for category, models in final_results.items():
print(f"\n{category}:")
for model_name, score in models.items():
print(f" {model_name}: {score:.4f}")
# 可视化最终模型比较
categories = list(final_results.keys())
all_models = []
all_scores = []
colors = []
color_palette = ['skyblue', 'lightgreen', 'lightcoral', 'gold', 'violet', 'orange']
color_idx = 0
for category in categories:
for model_name, score in final_results[category].items():
all_models.append(f"{category}\n{model_name}")
all_scores.append(score)
colors.append(color_palette[color_idx % len(color_palette)])
color_idx += 1
y_pos_final = np.arange(len(all_models))
bars_final = axes[8].barh(y_pos_final, all_scores, color=colors)
axes[8].set_yticks(y_pos_final)
axes[8].set_yticklabels(all_models)
axes[8].set_xlabel('F1分数')
axes[8].set_title('最终模型性能比较')
axes[8].grid(True, alpha=0.3, axis='x')
# 添加数值标签
for bar, score in zip(bars_final, all_scores):
width = bar.get_width()
axes[8].text(width + 0.01, bar.get_y() + bar.get_height()/2,
f'{score:.3f}', ha='left', va='center', fontsize=9)
plt.suptitle('高级模型选择与调优技术', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 总结
print("\n" + "="*60)
print("模型选择与调优总结")
print("="*60)
# 找出最佳模型
best_overall_model = None
best_overall_score = 0
for category, models in final_results.items():
for model_name, score in models.items():
if score > best_overall_score:
best_overall_score = score
best_overall_model = f"{category} - {model_name}"
print(f"最佳模型: {best_overall_model}")
print(f"最佳F1分数: {best_overall_score:.4f}")
# 计算性能提升
base_rf_score = base_results['随机森林']['F1分数']
improvement = (best_overall_score - base_rf_score) / base_rf_score * 100
print(f"相比基础随机森林提升: {improvement:.1f}%")
# 模型复杂度分析
print("\n模型复杂度分析:")
print(f"- 基础随机森林: 100棵树")
print(f"- 调优后随机森林: {best_rf.n_estimators}棵树, 最大深度={best_rf.max_depth}")
print(f"- 训练时间: 网格搜索调优显著增加了训练时间,但提升了模型性能")
五、实际应用场景
1. 房价预测系统
python
class HousePricePredictor:
def __init__(self):
self.models = {}
self.scaler = None
self.feature_selector = None
self.best_model = None
self.feature_importance = None
def prepare_data(self):
"""准备房价预测数据"""
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
# 加载加利福尼亚房价数据集
print("加载加利福尼亚房价数据集...")
california = fetch_california_housing()
X = pd.DataFrame(california.data, columns=california.feature_names)
y = california.target
print(f"数据集形状: {X.shape}")
print(f"目标变量范围: [{y.min():.2f}, {y.max():.2f}]")
# 添加一些衍生特征
X['RoomPerHousehold'] = X['AveRooms'] / X['AveOccup']
X['IncomePerRoom'] = X['MedInc'] / X['AveRooms']
X['PopulationDensity'] = X['Population'] / X['AveOccup']
X['BedroomRatio'] = X['AveBedrms'] / X['AveRooms']
# 添加一些多项式特征
X['MedInc_squared'] = X['MedInc'] ** 2
X['HouseAge_squared'] = X['HouseAge'] ** 2
X['AveRooms_log'] = np.log1p(X['AveRooms'])
print(f"添加衍生特征后形状: {X.shape}")
# 分割数据
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
return X_train, X_test, y_train, y_test, X.columns
def build_pipeline(self):
"""构建完整的机器学习管道"""
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, RobustScaler
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.linear_model import Ridge, Lasso, ElasticNet
from sklearn.svm import SVR
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
# 定义预处理步骤
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', RobustScaler()) # 使用RobustScaler处理异常值
])
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, 'all') # 所有特征都是数值型
]
)
# 定义多个模型
models = {
'岭回归': Ridge(alpha=1.0, random_state=42),
'Lasso回归': Lasso(alpha=0.1, random_state=42),
'弹性网络': ElasticNet(alpha=0.1, l1_ratio=0.5, random_state=42),
'支持向量回归': SVR(kernel='rbf', C=100, gamma=0.1),
'随机森林': RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1),
'梯度提升': GradientBoostingRegressor(n_estimators=100, random_state=42),
'XGBoost': XGBRegressor(n_estimators=100, random_state=42, n_jobs=-1),
'LightGBM': LGBMRegressor(n_estimators=100, random_state=42, n_jobs=-1)
}
# 创建完整管道
pipelines = {}
for name, model in models.items():
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('feature_selector', SelectKBest(score_func=f_regression, k=10)),
('regressor', model)
])
pipelines[name] = pipeline
return pipelines
def train_and_evaluate(self, X_train, X_test, y_train, y_test):
"""训练和评估所有模型"""
from sklearn.model_selection import cross_val_score, GridSearchCV
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import numpy as np
pipelines = self.build_pipeline()
results = {}
print("\n" + "="*60)
print("开始训练和评估模型")
print("="*60)
for name, pipeline in pipelines.items():
print(f"\n训练 {name}...")
# 训练模型
pipeline.fit(X_train, y_train)
# 预测
y_pred = pipeline.predict(X_test)
# 评估指标
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
# 交叉验证
cv_scores = cross_val_score(pipeline, X_train, y_train,
cv=5, scoring='neg_mean_squared_error')
cv_rmse = np.sqrt(-cv_scores.mean())
results[name] = {
'模型': pipeline,
'MSE': mse,
'RMSE': rmse,
'MAE': mae,
'R2': r2,
'CV_RMSE': cv_rmse,
'预测值': y_pred
}
print(f" RMSE: {rmse:.4f}")
print(f" MAE: {mae:.4f}")
print(f" R²: {r2:.4f}")
print(f" 交叉验证RMSE: {cv_rmse:.4f}")
self.models = results
return results
def hyperparameter_tuning(self, X_train, y_train):
"""超参数调优"""
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
print("\n" + "="*60)
print("超参数调优")
print("="*60)
# 选择梯度提升树进行调优
gb_pipeline = Pipeline(steps=[
('scaler', StandardScaler()),
('regressor', GradientBoostingRegressor(random_state=42))
])
# 定义参数网格
param_grid = {
'regressor__n_estimators': [100, 200, 300],
'regressor__learning_rate': [0.01, 0.05, 0.1],
'regressor__max_depth': [3, 5, 7],
'regressor__min_samples_split': [2, 5, 10],
'regressor__min_samples_leaf': [1, 2, 4]
}
# 网格搜索
grid_search = GridSearchCV(
estimator=gb_pipeline,
param_grid=param_grid,
cv=3,
scoring='neg_mean_squared_error',
n_jobs=-1,
verbose=1
)
print("开始网格搜索...")
grid_search.fit(X_train, y_train)
print("网格搜索完成!")
print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳分数: {-grid_search.best_score_:.4f}")
self.best_model = grid_search.best_estimator_
return grid_search
def feature_importance_analysis(self, X_train, y_train, feature_names):
"""特征重要性分析"""
from sklearn.ensemble import RandomForestRegressor
from sklearn.inspection import permutation_importance
import matplotlib.pyplot as plt
import seaborn as sns
print("\n" + "="*60)
print("特征重要性分析")
print("="*60)
# 使用随机森林计算特征重要性
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
# 获取特征重要性
importance = rf.feature_importances_
indices = np.argsort(importance)[::-1]
print("特征重要性排名:")
for i in range(min(15, len(feature_names))):
print(f"{i+1:2d}. {feature_names[indices[i]]:30s} {importance[indices[i]]:.4f}")
# 可视化特征重要性
plt.figure(figsize=(12, 8))
top_n = min(15, len(feature_names))
plt.barh(range(top_n), importance[indices[:top_n]][::-1])
plt.yticks(range(top_n), [feature_names[i] for i in indices[:top_n]][::-1])
plt.xlabel('特征重要性')
plt.title('Top 15 特征重要性')
plt.grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.show()
# 排列重要性
print("\n计算排列重要性...")
result = permutation_importance(rf, X_train, y_train,
n_repeats=10, random_state=42, n_jobs=-1)
# 可视化排列重要性
sorted_idx = result.importances_mean.argsort()[::-1]
plt.figure(figsize=(12, 8))
top_n = min(15, len(feature_names))
plt.boxplot(result.importances[sorted_idx[:top_n]].T,
vert=False, labels=[feature_names[i] for i in sorted_idx[:top_n]])
plt.xlabel("排列重要性")
plt.title("Top 15 排列重要性")
plt.grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.show()
self.feature_importance = importance
return importance
def create_interpretation_dashboard(self, X_test, y_test, feature_names):
"""创建模型解释性仪表板"""
import matplotlib.pyplot as plt
from sklearn.inspection import PartialDependenceDisplay
import shap
import warnings
warnings.filterwarnings('ignore')
print("\n" + "="*60)
print("模型解释性分析")
print("="*60)
# 选择最佳模型
if self.best_model is None:
print("没有最佳模型,使用随机森林")
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_test, y_test)
else:
model = self.best_model
# 创建图形
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 1. 预测 vs 实际值
y_pred = model.predict(X_test)
axes[0, 0].scatter(y_test, y_pred, alpha=0.6, s=30)
axes[0, 0].plot([y_test.min(), y_test.max()],
[y_test.min(), y_test.max()], 'r--', lw=2)
axes[0, 0].set_xlabel('实际房价')
axes[0, 0].set_ylabel('预测房价')
axes[0, 0].set_title('预测 vs 实际值')
axes[0, 0].grid(True, alpha=0.3)
# 添加R²分数
from sklearn.metrics import r2_score
r2 = r2_score(y_test, y_pred)
axes[0, 0].text(0.05, 0.95, f'R² = {r2:.3f}',
transform=axes[0, 0].transAxes,
fontsize=12, verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
# 2. 残差分析
residuals = y_test - y_pred
axes[0, 1].scatter(y_pred, residuals, alpha=0.6, s=30)
axes[0, 1].axhline(y=0, color='r', linestyle='--', alpha=0.5)
axes[0, 1].set_xlabel('预测房价')
axes[0, 1].set_ylabel('残差')
axes[0, 1].set_title('残差分析')
axes[0, 1].grid(True, alpha=0.3)
# 3. 误差分布
axes[1, 0].hist(residuals, bins=30, edgecolor='black', alpha=0.7)
axes[1, 0].axvline(x=0, color='r', linestyle='--', alpha=0.5)
axes[1, 0].set_xlabel('残差')
axes[1, 0].set_ylabel('频数')
axes[1, 0].set_title('误差分布')
axes[1, 0].grid(True, alpha=0.3)
# 添加统计信息
mean_error = np.mean(residuals)
std_error = np.std(residuals)
axes[1, 0].text(0.05, 0.95, f'均值 = {mean_error:.3f}\n标准差 = {std_error:.3f}',
transform=axes[1, 0].transAxes,
fontsize=10, verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
# 4. 部分依赖图(选择最重要的两个特征)
if hasattr(model, 'feature_importances_'):
importance = model.feature_importances_
elif self.feature_importance is not None:
importance = self.feature_importance
else:
# 计算特征重要性
from sklearn.ensemble import RandomForestRegressor
temp_model = RandomForestRegressor(n_estimators=100, random_state=42)
temp_model.fit(X_test, y_test)
importance = temp_model.feature_importances_
top_features_idx = np.argsort(importance)[-2:]
top_features = [feature_names[i] for i in top_features_idx]
print(f"最重要的两个特征: {top_features}")
try:
# 创建部分依赖图
fig_pdp, ax_pdp = plt.subplots(figsize=(10, 6))
display = PartialDependenceDisplay.from_estimator(
model, X_test, features=top_features_idx,
feature_names=feature_names, ax=ax_pdp
)
ax_pdp.set_title('部分依赖图 (最重要的两个特征)')
ax_pdp.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
except Exception as e:
print(f"无法创建部分依赖图: {e}")
# 显示特征重要性条形图作为替代
axes[1, 1].barh(range(len(top_features)), importance[top_features_idx])
axes[1, 1].set_yticks(range(len(top_features)))
axes[1, 1].set_yticklabels(top_features)
axes[1, 1].set_xlabel('特征重要性')
axes[1, 1].set_title('最重要特征的重要性')
axes[1, 1].grid(True, alpha=0.3, axis='x')
plt.suptitle('房价预测模型解释性仪表板', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 5. SHAP值分析(如果可用)
try:
print("\n计算SHAP值...")
# 使用TreeExplainer(对于树模型)
if hasattr(model, 'estimators_') or hasattr(model, 'tree_'):
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
# 创建SHAP摘要图
plt.figure(figsize=(12, 8))
shap.summary_plot(shap_values, X_test, feature_names=feature_names,
show=False, max_display=15)
plt.title('SHAP特征重要性摘要')
plt.tight_layout()
plt.show()
# 创建SHAP依赖图(最重要的特征)
if len(top_features) > 0:
plt.figure(figsize=(10, 6))
shap.dependence_plot(top_features_idx[0], shap_values, X_test,
feature_names=feature_names, show=False)
plt.title(f'SHAP依赖图: {top_features[0]}')
plt.tight_layout()
plt.show()
else:
print("模型不支持TreeExplainer,使用KernelExplainer")
# 使用样本数据进行计算(为了性能)
X_sample = X_test[:100]
explainer = shap.KernelExplainer(model.predict, X_sample)
shap_values = explainer.shap_values(X_sample)
plt.figure(figsize=(12, 8))
shap.summary_plot(shap_values, X_sample, feature_names=feature_names,
show=False, max_display=10)
plt.title('SHAP特征重要性摘要 (基于样本)')
plt.tight_layout()
plt.show()
except Exception as e:
print(f"SHAP分析失败: {e}")
def run_complete_analysis(self):
"""运行完整的房价预测分析"""
print("="*70)
print("开始房价预测系统分析")
print("="*70)
# 1. 准备数据
X_train, X_test, y_train, y_test, feature_names = self.prepare_data()
# 2. 训练和评估模型
results = self.train_and_evaluate(X_train, X_test, y_train, y_test)
# 3. 超参数调优
grid_search = self.hyperparameter_tuning(X_train, y_train)
# 4. 特征重要性分析
importance = self.feature_importance_analysis(X_train, y_train, feature_names)
# 5. 模型解释性分析
self.create_interpretation_dashboard(X_test, y_test, feature_names)
# 6. 模型性能总结
print("\n" + "="*70)
print("模型性能总结")
print("="*70)
# 创建性能比较表
performance_df = pd.DataFrame({
'模型': list(results.keys()),
'RMSE': [results[m]['RMSE'] for m in results.keys()],
'MAE': [results[m]['MAE'] for m in results.keys()],
'R²': [results[m]['R2'] for m in results.keys()],
'CV_RMSE': [results[m]['CV_RMSE'] for m in results.keys()]
})
# 排序并显示
performance_df = performance_df.sort_values('RMSE')
print(performance_df.to_string(index=False))
# 找出最佳模型
best_model_name = performance_df.iloc[0]['模型']
best_rmse = performance_df.iloc[0]['RMSE']
best_r2 = performance_df.iloc[0]['R²']
print(f"\n最佳模型: {best_model_name}")
print(f"测试集RMSE: {best_rmse:.4f}")
print(f"测试集R²: {best_r2:.4f}")
# 计算平均绝对百分比误差
best_model = results[best_model_name]['模型']
y_pred_best = best_model.predict(X_test)
mape = np.mean(np.abs((y_test - y_pred_best) / y_test)) * 100
print(f"平均绝对百分比误差 (MAPE): {mape:.2f}%")
# 预测区间估计
residuals = y_test - y_pred_best
std_residuals = np.std(residuals)
print(f"\n预测区间分析:")
print(f"残差标准差: {std_residuals:.4f}")
print(f"95%预测区间宽度: ±{1.96 * std_residuals:.4f}")
# 业务解释
print(f"\n业务解释:")
print(f"1. 模型可以解释房价变异的 {best_r2*100:.1f}%")
print(f"2. 平均预测误差为 ${best_rmse*100000:,.0f} (假设房价以10万美元为单位)")
print(f"3. 预测准确率: {100 - mape:.1f}%")
return {
'最佳模型': best_model,
'性能表': performance_df,
'特征重要性': importance,
'预测值': y_pred_best,
'实际值': y_test
}
# 运行房价预测系统
if __name__ == "__main__":
predictor = HousePricePredictor()
results = predictor.run_complete_analysis()
Scikit-learn通过其统一的API设计和全面的算法覆盖,使得机器学习从理论研究走向实际应用成为可能。它的真正价值不仅在于提供了丰富的算法实现,更在于建立了一套标准化的机器学习工作流程。从数据预处理到模型评估,从超参数调优到模型部署,Scikit-learn都提供了优雅而高效的解决方案。这使得数据科学家能够专注于业务问题本身,而不是算法实现的细节。
随着机器学习技术的不断发展,Scikit-learn也在持续进化。新的版本提供了更快的算法实现、更好的内存管理,以及与深度学习框架的更好集成。同时,Scikit-learn建立的生态系统(如Scikit-learn-contrib项目)进一步扩展了其功能边界,使其能够应对更复杂的机器学习挑战。
你在使用Scikit-learn时有什么特别的技巧或遇到过什么有趣的挑战?或者你有关于机器学习的独特应用场景想要分享吗?欢迎在评论区交流你的经验和想法!
更多推荐



所有评论(0)