数值特征标准化
标准差是衡量数据集中数值分散程度或变异程度的一个重要指标,标准差越大,表示数据点越分散;标准差越小,表示数据点越集中(方差是标准差的平方)
py
import numpy as np
# 两组数据
group1 = [85, 90, 95, 100, 105, 110, 115]
group2 = [70, 80, 90, 100, 110, 120, 130]
print("Group1 均值:", np.mean(group1), "标准差:", np.std(group1, ddof=1))
print("Group2 均值:", np.mean(group2), "标准差:", np.std(group2, ddof=1))
# 输出:
# Group1 均值: 100.0 标准差: 11.83
# Group2 均值: 100.0 标准差: 21.60
标准化值 = (原始值 - 均值) / 标准差
py
原始值: [25, 30, 35, 40, 45]
均值 = 35
标准差 ≈ 7.906
标准化计算:
(25-35)/7.906 = -1.2649
(30-35)/7.906 = -0.6325
(35-35)/7.906 = 0
(40-35)/7.906 = 0.6325
(45-35)/7.906 = 1.2649
实际意义:
- 消除量纲影响:收入(几万元)和身高(厘米)不再因单位不同而影响模型
- 加速收敛:对于梯度下降算法,标准化后收敛更快
- 公平比较:所有特征在相同尺度上竞争,避免某些特征因数值大而主导模型
- 模型稳定性:特别是对距离敏感的模型(如KNN、SVM)很重要
py
# 手动计算标准差
data = [25, 30, 35, 40, 45]
# 1. 计算均值
mean = np.mean(data) # 35.0
# 2. 计算每个数据点与均值的差
deviations = [x - mean for x in data] # [-10, -5, 0, 5, 10]
# 3. 计算偏差平方
squared_deviations = [d**2 for d in deviations] # [100, 25, 0, 25, 100]
# 4. 计算方差(样本方差)
variance = sum(squared_deviations) / (len(data) - 1) # 250 / 4 = 62.5
# 5. 计算标准差
std_dev = np.sqrt(variance) # 7.906
print("标准差:", std_dev)
类别不平衡
py
# 假设二分类问题
原始数据形状: (1000, 20) # 1000个样本,20个特征
原始目标分布: [800, 200] # 类别0有800个样本,类别1有200个样本
不平衡比例: 4:1
应用SMOTE后
py
过采样后数据形状: (1600, 20) # 样本数增加
过采样后目标分布: [800, 800] # 两个类别都变成800个样本
平衡比例: 1:1
# 具体变化:
# 类别0:保持800个原始样本(不变)
# 类别1:200个原始样本 + 600个合成样本 = 800个样本
优缺点
py
# 优点
# 1. 有效缓解类别不平衡
# 2. 不会简单复制样本,减少过拟合风险
# 3. 提高少数类的分类性能
# 4. 易于实现和使用
# 缺点
# 1. 可能生成噪声样本
# 如果最近邻属于其他类别,合成的样本可能成为噪声
# 2. 不适用于高维稀疏数据
# 在高维空间中,最近邻可能不可靠
# 3. 计算成本较高
# 需要计算所有少数类样本的最近邻
# 4. 可能过拟合
# 如果少数类样本本身就很少,合成的样本可能只是"插值"结果
# 5. 不适用于多类别问题中的极端不平衡
# 可能需要特殊的变体如SMOTE-NC(针对分类特征)
替代方案
①、其他过采样方法
py
from imblearn.over_sampling import(
ADASYN, # 自适应合成采样
BorderlineSMOTE, # 边界SMOTE
SVMSMOTE, # 基于SVM的SMOTE
KMeansSMOTE # 基于聚类的SMOTE
)
②、欠采样方法
py
from imblearn.under_sampling import (
RandomUnderSampler, # 随机欠采样
NearMiss, # 近邻欠采样
TomekLinks, # 移除Tomek链接
ClusterCentroids # 聚类中心
)
③、结合过采样和欠采样
py
from imblearn.combine import SMOTETomek, SMOTEENN
适用场景
py
# 适用场景:
# 1. 少数类样本数 > 50(太少的话SMOTE效果有限)
# 2. 类别不平衡比例 < 10:1
# 3. 特征空间相对密集(不是稀疏特征)
# 4. 对少数类的精确识别很重要(如医疗诊断、欺诈检测)
# 不适用场景:
# 1. 数据极度不平衡(如1:1000)
# 2. 少数类样本数 < 20
# 3. 多标签分类问题
# 4. 时间序列数据
实践
py
# 1. 先划分训练集和测试集
# 在训练集上应用SMOTE,测试集保持原始分布
# 2. 使用交叉验证评估
# 在交叉验证的每个折叠内部应用SMOTE
# 3. 结合多种技术
# SMOTE + 适当的欠采样
# 4. 监控合成样本的质量
# 可视化检查合成的样本是否合理
# 示例代码:
from sklearn.model_selection import train_test_split
from imblearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
# 先划分数据
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# 创建包含SMOTE的管道
pipeline = Pipeline([
('smote', SMOTE(random_state=42)),
('classifier', RandomForestClassifier(random_state=42))
])
# 在训练集上训练(自动应用SMOTE)
pipeline.fit(X_train, y_train)
# 在原始测试集上评估
accuracy = pipeline.score(X_test, y_test)
改进:
py
if self.config.get('handle_imbalance', False):
print("\n4. 处理类别不平衡:")
try:
from imblearn.over_sampling import SMOTE
# 检查少数类样本数是否足够
class_counts = np.bincount(y)
minority_class = np.argmin(class_counts)
minority_count = class_counts[minority_class]
if minority_count < 10:
print(" 警告:少数类样本过少(<10),跳过SMOTE")
print(" 考虑使用其他方法或收集更多数据")
else:
# 多分类时指定采样策略
if len(class_counts) > 2:
smote = SMOTE(
random_state=self.config.get('random_state', 42),
sampling_strategy='not majority' # 对所有少数类过采样
)
else:
smote = SMOTE(
random_state=self.config.get('random_state', 42)
)
X_processed, y = smote.fit_resample(X_processed, y)
print(f" 过采样后数据形状: {X_processed.shape}")
print(f" 过采样后目标分布: {np.bincount(y)}")
print(f" 新增合成样本数: {len(y) - len(y_original)}")
except ImportError:
print(" 错误:未安装imbalanced-learn库")
print(" 安装命令:pip install imbalanced-learn")
except Exception as e:
print(f" SMOTE处理失败: {str(e)}")