机器学习数据预处理的正确流程是什么?附二分类场景验证请求
你的整体流程框架遵循了数据泄露防控的核心原则,是可行的,但部分步骤顺序需要调整,同时补充一些关键细节。以下是优化后的完整流程及疑问解答:
一、优化后的完整预处理流程
步骤1. 加载数据
import pandas as pd df = pd.read_csv("data.csv")
步骤2. 划分训练集与测试集
核心规则:必须先划分,再做任何预处理,彻底杜绝数据泄露
from sklearn.model_selection import train_test_split # 若数据存在类别不平衡,用stratify保证训练/测试集的类别比例一致 train_df, test_df = train_test_split( df, test_size=0.33, random_state=42, stratify=df['target'] )
步骤3. 仅针对训练集做探索性数据分析(EDA)
import matplotlib.pyplot as plt import seaborn as sns # 基础统计与结构分析 train_df.info() train_df.describe() # 分布与相关性可视化 sns.displot(train_df) sns.boxplot(data=train_df) corr_matrix = train_df.corr() # 查看特征与目标变量的相关性排序 corr_matrix["target"].sort_values(ascending=False) sns.scatterplot(x="Column X", y="target", data=train_df)
步骤4. 训练集异常值处理
必须在缺失值填充、数据缩放前完成,避免异常值干扰统计量(如均值、方差)
# 基于四分位数法识别异常值范围 q1 = train_df.quantile(0.25) q3 = train_df.quantile(0.75) iqr = q3 - q1 lower_bound = q1 - 1.5 * iqr upper_bound = q3 + 1.5 * iqr # 处理异常值:根据业务场景选择删除、截断或替换 train_df = train_df[~((train_df < lower_bound) | (train_df > upper_bound)).any(axis=1)]
步骤5. 训练集缺失值处理
# 查看缺失值统计 train_df.isnull().sum() # 定位含缺失值的行 train_df[train_df["Column"].isnull()] # 针对性处理缺失值 # 数值型特征:用中位数(不受异常值影响)填充 train_df["numerical_col"] = train_df["numerical_col"].fillna(train_df["numerical_col"].median()) # 分类特征:用众数填充或新增"缺失"类别 train_df["categorical_col"] = train_df["categorical_col"].fillna(train_df["categorical_col"].mode()[0])
步骤6. 训练集特征工程(含分类数据处理)
将分类数据编码整合到特征工程中,先完成编码再做其他特征操作:
import numpy as np from sklearn.preprocessing import OneHotEncoder # 1. 分离特征与目标变量 X_train = train_df.drop("target", axis=1) y_train = np.where(train_df["target"] == "yes", 1, 0) # 2. 分类特征编码 onehot = OneHotEncoder(handle_unknown="ignore", sparse_output=False) categorical_cols = X_train.select_dtypes(include=["object", "category"]).columns encoded_cols = onehot.fit_transform(X_train[categorical_cols]) # 合并编码后的特征 X_train = pd.concat([ X_train.drop(categorical_cols, axis=1), pd.DataFrame(encoded_cols, columns=onehot.get_feature_names_out(categorical_cols)) ], axis=1) # 3. 删除高相关特征 corr_matrix = X_train.corr() high_corr_pairs = [(i,j) for i in corr_matrix.columns for j in corr_matrix.columns if i<j and abs(corr_matrix[i][j])>0.8] for pair in high_corr_pairs: X_train = X_train.drop(pair[1], axis=1) # 4. 偏态特征转换 skew_threshold = 0.5 skewed_cols = X_train.skew()[abs(X_train.skew()) > skew_threshold].index for col in skewed_cols: # 对数变换(避免负数值,加1) X_train[f"log_{col}"] = np.log(X_train[col] + 1) X_train.drop(col, axis=1, inplace=True)
步骤7. 数据缩放
仅对训练集执行fit操作,测试集仅做transform;二分类任务无需缩放目标列,回归任务仅在使用神经网络等对尺度敏感的模型时才需缩放目标列。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train)
步骤8. 不平衡数据处理(SMOTE)
必须在缩放之后执行,因为SMOTE基于欧氏距离计算,尺度不一致会导致采样偏差
from imblearn.over_sampling import SMOTE sm = SMOTE(random_state=42) X_train_balanced, y_train_balanced = sm.fit_resample(X_train_scaled, y_train)
步骤9. 主成分分析(PCA)- 降维
必须在缩放之后执行,否则尺度大的特征会主导方差计算,导致PCA结果偏差;仅在特征数量过多、模型训练困难时使用,注意PCA会丢失特征可解释性。
from sklearn.decomposition import PCA # 可选:设置n_components为小数(如0.95)表示保留95%的方差 pca = PCA(n_components=50) X_train_pca = pca.fit_transform(X_train_balanced)
步骤10. 建模、拟合、评估、预测
核心:测试集的预处理必须完全复用训练集的管道,不能重新fit,推荐用Pipeline封装所有步骤避免出错:
from sklearn.pipeline import Pipeline from sklearn.linear_model import RidgeClassifier from sklearn.metrics import ( accuracy_score, recall_score, precision_score, f1_score, confusion_matrix ) # 封装完整预处理+模型管道 pipe = Pipeline([ ("onehot", OneHotEncoder(handle_unknown="ignore", sparse_output=False)), ("scaler", StandardScaler()), ("smote", SMOTE(random_state=42)), ("pca", PCA(n_components=50)), ("model", RidgeClassifier()) ]) # 训练管道 pipe.fit(train_df.drop("target", axis=1), y_train) # 测试集预测与评估 y_test = np.where(test_df["target"] == "yes", 1, 0) y_pred = pipe.predict(test_df.drop("target", axis=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) print(f"模型评估结果:准确率={accuracy:.2f}, 精确率={precision:.2f}, 召回率={recall:.2f}, F1分数={f1:.2f}") print("混淆矩阵:") print(confusion_matrix(y_test, y_pred, labels=[1,0]))
步骤11. 保存模型与预处理管道
必须保存完整管道,而非单独模型,否则无法处理新数据
import joblib # 保存完整管道 joblib.dump(pipe, "binary_classification_pipeline.joblib") # 加载使用 loaded_pipe = joblib.load("binary_classification_pipeline.joblib") new_pred = loaded_pipe.predict(new_data)
二、关键疑问解答
仅针对训练集做EDA是否正确?
完全正确。测试集必须模拟真实生产环境中的未知数据,任何基于测试集的分析都会导致数据泄露,使模型评估结果失真。异常值处理是否必须提前?
是的。异常值会严重影响均值、方差等统计量,进而干扰缺失值填充、数据缩放的效果,因此必须在这些步骤之前处理。特征工程是否需要提前处理?
是的。新创建的特征(如对数变换后的特征、编码后的分类特征)需要和原始特征一起参与后续的缩放、降维等操作,因此必须在这些步骤之前完成。回归任务中是否需要缩放目标列?
大部分传统回归模型(如线性回归、决策树、随机森林)不需要。仅在使用对尺度敏感的模型(如神经网络、支持向量机回归)时,才需要缩放目标列。应用PCA前是否需要缩放数据?
必须。PCA基于特征的方差排序,尺度大的特征会主导方差计算,导致PCA结果偏向这些特征,因此必须先缩放所有特征到同一尺度。
内容的提问来源于stack exchange,提问作者Yara1994

