在机器学习项目中,数据预处理和特征工程往往占据了70%以上的工作量。作为一名从业多年的数据科学家,我深刻体会到特征处理和聚类分析在实际项目中的重要性。特征工程就像烹饪前的食材处理,直接影响最终模型的"口感";而聚类分析则是探索数据内在结构的显微镜,能发现我们肉眼难以察觉的模式。
本系列最后一讲将系统梳理特征选择、特征提取和聚类分析三大核心技术。这些方法在我参与的金融风控、医疗影像分析、推荐系统等多个领域都有广泛应用。不同于教科书式的理论讲解,我会结合多年实战经验,重点分享那些只有踩过坑才知道的实用技巧。
特征选择的核心目标是去除冗余和噪声特征,解决维度灾难问题。根据我的项目经验,一个好的判据需要满足四个基本原则:
单调性:判据值J越大,分类错误率越小。这个原则确保判据与模型性能正相关。在实际项目中,我常用AUC或F1-score作为J的代理指标。
对称性:判据应满足Jij=Jji,Jii=0,Jij≥0。这个性质保证了特征间比较的公平性。例如在文本分类中,词频与标签的互信息判据就需要满足这个条件。
可加性:对独立特征判据值应具有可加性。这个特性在特征组合评估时特别重要。比如在电商用户画像中,年龄和性别通常是独立特征。
非减性:增加特征时判据值不应减小。这个原则防止优质特征被错误过滤。我在金融反欺诈项目中就遇到过因违反此原则导致重要风险特征被误删的情况。
这是监督学习中最直观的判据,核心是计算类内离散度矩阵SW和类间离散度矩阵SB:
python复制# Python实现示例
def scatter_matrices(X, y):
classes = np.unique(y)
SW = np.zeros((X.shape[1], X.shape[1]))
SB = np.zeros_like(SW)
overall_mean = np.mean(X, axis=0)
for c in classes:
X_c = X[y == c]
mean_c = np.mean(X_c, axis=0)
SW += (X_c - mean_c).T @ (X_c - mean_c)
SB += len(X_c) * (mean_c - overall_mean).reshape(-1,1) @ (mean_c - overall_mean).reshape(1,-1)
return SW, SB
实际项目中,我常用J2=tr(SW⁻¹SB)这个判据,因为它考虑了类内分布的协方差结构。在医学影像分析中,这个判据能有效识别出区分不同病症的关键区域。
当知道特征的概率分布时,Bhattacharyya距离是更好的选择。它衡量两类分布的重叠程度:
code复制JB = -ln ∫√[p(x|ω1)p(x|ω2)]dx
在异常检测项目中,我用这个判据成功识别出了信用卡欺诈交易的关键特征。计算时需要注意,对于高维数据直接积分会遭遇维度灾难,通常需要先降维或采用核密度估计。
熵判据适用于无监督和监督场景。在推荐系统中,我常用条件熵来评估用户行为特征的重要性:
python复制def conditional_entropy(feature, target):
# 计算特征给定目标的条件熵
_, counts = np.unique(feature, return_counts=True)
probs = counts / len(feature)
return -np.sum(probs * np.log2(probs))
注意:实际计算时需要对连续特征先离散化。我通常使用等频分箱而非等宽分箱,可以避免长尾分布带来的问题。
对于小样本数据,Wilcoxon秩和检验比t检验更鲁棒。在A/B测试特征筛选中,我发现它对非正态数据表现更好:
python复制from scipy.stats import ranksums
def wilcoxon_feature_score(feature, label):
group1 = feature[label == 0]
group2 = feature[label == 1]
_, p_value = ranksums(group1, group2)
return -np.log10(p_value) # 转换为显著性得分
分支定界(BAB)能保证找到全局最优子集,但计算复杂度高。在我的实践中,当特征数超过30时就不太实用。算法关键点:
python复制# BAB算法伪代码
def branch_and_bound(features, criterion, current_set=[], best_set=None, best_score=0):
if not features:
return current_set, criterion(current_set)
for f in features:
new_set = current_set + [f]
score = criterion(new_set)
if score > best_score:
best_set, best_score = new_set, score
if upper_bound(features - {f}) > best_score:
result, result_score = branch_and_bound(features - {f}, criterion, new_set, best_set, best_score)
if result_score > best_score:
best_set, best_score = result, result_score
return best_set, best_score
以模型性能为导向,但计算成本高。递归特征消除(RFE)是典型代表:
python复制from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
# 在金融风控中的实际应用
estimator = LogisticRegression(penalty='l1', solver='saga', max_iter=1000)
selector = RFE(estimator, n_features_to_select=15, step=1)
selector = selector.fit(X_train, y_train)
selected_features = X_train.columns[selector.support_]
实战经验:RFE配合L1正则化逻辑回归效果最好。每次迭代建议用交叉验证评估,避免过拟合。
LDA目标是找到使类间方差最大、类内方差最小的投影方向。计算步骤:
实际应用中,我常用以下技巧:
python复制from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# 医疗诊断数据降维示例
lda = LinearDiscriminantAnalysis(n_components=2)
X_lda = lda.fit_transform(X_std, y)
# 可视化
plt.scatter(X_lda[:,0], X_lda[:,1], c=y, cmap='rainbow')
plt.xlabel('LD1')
plt.ylabel('LD2')
plt.title('LDA投影结果')
python复制from sklearn.decomposition import PCA
# 电商用户行为分析案例
pca = PCA(n_components=0.95) # 保留95%方差
X_pca = pca.fit_transform(X_std)
# 绘制累计方差图
plt.plot(np.cumsum(pca.explained_variance_ratio_))
plt.xlabel('主成分数')
plt.ylabel('累计解释方差')
plt.axhline(y=0.95, color='r', linestyle='--')
踩坑记录:在时间序列分析中直接应用PCA效果不好,建议先用滑动窗口提取统计特征。
K-L变换在最小均方误差意义下是最优的,但实际应用中与PCA差别不大。主要区别:
| 特性 | PCA | K-L变换 |
|---|---|---|
| 数据要求 | 需要中心化 | 原始数据即可 |
| 优化目标 | 最大方差 | 最小重建误差 |
| 计算复杂度 | O(n³) | O(n³) |
| 监督版本 | 无 | 可通过类加权实现 |
在推荐系统中,我常用加权K-L变换来处理隐式反馈数据:
python复制def weighted_kl(X, weights):
"""加权样本的K-L变换"""
weighted_mean = np.average(X, axis=0, weights=weights)
centered = X - weighted_mean
weighted_cov = centered.T @ (centered * weights.reshape(-1,1))
eigenvalues, eigenvectors = np.linalg.eigh(weighted_cov)
return eigenvalues[::-1], eigenvectors[:, ::-1]
| 距离类型 | 公式 | 适用场景 | 注意事项 |
|---|---|---|---|
| 欧氏距离 | √∑(xi-yi)² | 连续变量,各向同性数据 | 对量纲敏感,需标准化 |
| 马氏距离 | √[(x-y)ᵀS⁻¹(x-y)] | 考虑特征相关性的数据 | 协方差矩阵估计要准确 |
| 余弦相似度 | (x·y)/(‖x‖‖y‖) | 文本、高维稀疏数据 | 只考虑方向不考虑长度 |
| 杰卡德距离 | 1 - | A∩B | / |
| DTW距离 | 动态时间规整算法 | 时间序列、语音识别 | 计算复杂度高 |
在用户画像聚类中,我通常组合使用多种距离:
当数据量大时,距离矩阵计算会成为瓶颈。我的优化策略:
python复制from scipy.spatial.distance import cdist
D = cdist(X, X, 'mahalanobis', VI=np.linalg.inv(cov_matrix))
python复制from sklearn.neighbors import BallTree
tree = BallTree(X, metric='haversine') # 地理坐标数据
虽然K-means简单,但用好需要技巧:
初始中心选择:
K值确定方法:
python复制# K值选择示例
from sklearn.metrics import silhouette_score
silhouette_scores = []
for k in range(2, 10):
kmeans = KMeans(n_clusters=k, random_state=42)
labels = kmeans.fit_predict(X)
silhouette_scores.append(silhouette_score(X, labels))
plt.plot(range(2,10), silhouette_scores)
plt.xlabel('Number of clusters')
plt.ylabel('Silhouette Score')
FCM通过隶属度函数实现软聚类,在医学图像分割中效果显著:
python复制from sklearn.extensions.fuzzy_kmeans import FuzzyKMeans
fcm = FuzzyKMeans(n_clusters=3, m=2)
fcm.fit(X)
membership = fcm.predict_proba(X)
# 可视化隶属度
plt.scatter(X[:,0], X[:,1], c=membership.argmax(axis=1), alpha=0.1)
for i in range(membership.shape[1]):
plt.scatter(X[:,0], X[:,1], c='black', alpha=membership[:,i], s=5)
参数m控制模糊程度:
| 指标名称 | 计算公式 | 优化方向 | 特点 |
|---|---|---|---|
| 轮廓系数 | (b-a)/max(a,b) | 越大越好 | 兼顾簇内紧致和簇间分离 |
| Calinski-Harabasz | [tr(Bk)/(k-1)]/[tr(Wk)/(n-k)] | 越大越好 | 对凸簇敏感 |
| Davies-Bouldin | 1/k ∑ max[(si+sj)/d(ci,cj)] | 越小越好 | 计算简单 |
| Dunn指数 | min intercluster / max intracluster | 越大越好 | 对噪声敏感 |
当有真实标签时:
python复制from sklearn.metrics import adjusted_rand_score
true_labels = [...] # 真实标签
cluster_labels = [...] # 聚类结果
ari = adjusted_rand_score(true_labels, cluster_labels)
通过数据扰动评估聚类鲁棒性:
python复制from sklearn.utils import resample
def cluster_stability(X, model, n_iter=10):
scores = []
for _ in range(n_iter):
X_sample = resample(X)
labels1 = model.fit_predict(X)
labels2 = model.fit_predict(X_sample)
scores.append(adjusted_rand_score(labels1, labels2))
return np.mean(scores)
数据泄漏:在特征选择时使用全部数据(包括测试集)会导致评估偏差。正确做法:
维度诅咒:当特征数接近样本数时,很多判据会失效。解决方案:
特征交互:单变量筛选可能遗漏重要组合特征。建议:
数据预处理:
算法选择:
超参数调优:
python复制from sklearn.cluster import MiniBatchKMeans
mbk = MiniBatchKMeans(n_clusters=10, batch_size=1000)
mbk.fit(X_large)
GPU加速:
在线学习:
业务场景:对千万级用户进行行为分群,实现精准营销。
技术方案:
特征工程:
聚类方法:
效果评估:
经验总结:
项目背景:对肺部CT图像进行自动分型。
技术路线:
特征提取:
降维:
聚类:
关键发现:
业务需求:识别信用卡异常交易模式。
解决方案:
特征选择:
异常检测:
系统集成:
效果指标:
自动特征生成:
可解释特征选择:
深度聚类:
大规模聚类:
领域自适应聚类:
Python库:
可视化工具:
生产级工具:
在实际项目中,我通常会根据数据规模和业务需求选择合适的技术栈。对于中小规模数据,scikit-learn生态足够使用;而对于超大规模数据,则需要借助分布式计算框架。