You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

One-Hot编码应在Train/Test Split前还是后?案例解析

One-Hot Encoding 时机选择:Train/Test Split 前还是后?

我在纠结One-Hot Encoding(哑变量创建)应该在Train/Test Split前还是后执行,看到的观点互相矛盾:有人说提前编码会导致数据泄漏,但也有行业案例显示全量数据提前编码是惯例,Kaggle上则建议拆分后编码避免泄漏。

我的问题:

  1. 到底应该在Train/Test Split前还是后执行One-Hot Encoding?
  2. 下面的案例里数据泄漏发生在什么地方?

示例数据

我们有两列数据:web_views(目标变量)和website(非序数分类特征,假设不删除哑变量),数据框代码如下:

import pandas as pd
from sklearn.model_selection import train_test_split
import numpy as np
df = pd.DataFrame({'web_views': [100,200,300,400], 
                  'website': ['Youtube','Facebook','Instagram', 'Google']})

场景1:拆分前执行One-Hot Encoding

np.random.seed(123)

df_before_split = pd.concat([df.drop('website', axis = 1), pd.get_dummies(df['website'])], axis=1) 

# 创建X和y数据框
X_before_split = df_before_split.drop('web_views', axis = 1)
y_before_split = df_before_split['web_views']

# 执行训练测试拆分
X_train_before_split, X_test_before_split, y_train_before_split, y_test_before_split = train_test_split(X_before_split, y_before_split, test_size = 0.20)

查看拆分后的数据集:

# 查看拆分前编码的训练集
X_train_before_split
# 查看拆分前编码的测试集
X_test_before_split

场景2:拆分后执行One-Hot Encoding

# 先执行训练测试拆分再编码
X = df.drop('web_views', axis = 1)
y = df['web_views']

# 执行数据拆分
np.random.seed(123)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.20)

# 对训练集和测试集分别编码
X_train = pd.concat([X_train.drop('website', axis = 1), pd.get_dummies(X_train['website'])], axis=1)
X_test = pd.concat([X_test.drop('website', axis = 1), pd.get_dummies(X_test['website'])], axis=1)

查看编码后的数据集:

# 拆分后编码的训练集
X_train
# 拆分后编码的测试集
X_test

线性回归建模验证

导入线性回归模型:

from sklearn.linear_model import LinearRegression

拆分前编码的模型

regressor_before_split = LinearRegression()  
regressor_before_split.fit(X_train_before_split, y_train_before_split)

y_pred_before_split = regressor_before_split.predict(X_test_before_split)
y_pred_before_split

该模型返回符合预期的预测值。

拆分后编码的模型

regressor_after_split = LinearRegression()  
regressor_after_split.fit(X_train, y_train)

y_pred_after_split = regressor_after_split.predict(X_test)
y_pred_after_split

场景2报错信息

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-92-c63978a198c8> in <module>()
      2 regressor_after_split.fit(X_train, y_train)
      3 
----> 4 y_pred_after_split = regressor_after_split.predict(X_test)
      5 y_pred_after_split

C:\Anaconda3\lib\site-packages\sklearn\linear_model\base.py in predict(self, X)
    254             Returns predicted values.
    255         """
--> 256         return self._decision_function(X)
    257 
    258     _preprocess_data = staticmethod(_preprocess_data)

C:\Anaconda3\lib\site-packages\sklearn\linear_model\base.py in _decision_function(self, X)
    239         X = check_array(X, accept_sparse=['csr', 'csc', 'coo'])
    240         return safe_sparse_dot(X, self.coef_.T,
--> 241                                dense_output=True) + self.intercept_
    242 
    243     def predict(self, X):

C:\Anaconda3\lib\site-packages\sklearn\utils\extmath.py in safe_sparse_dot(a, b, dense_output)
    138         return ret
    139     else:
--> 140         return np.dot(a, b)
    141 
    142 

<__array_function__ internals> in dot(*args, **kwargs)

ValueError: shapes (1,1) and (3,) not aligned: 1 (dim 1) != 3 (dim 0)

我的思考

  • 拆分前编码可确保测试集与训练集特征维度一致,模型能正常预测;而拆分后编码会导致测试集与训练集特征维度不匹配(如示例中测试集仅1个特征,训练集有3个)。
  • 是否我在某个环节引入了数据泄漏?

解答

问题1:编码时机的正确选择

正确的做法是先拆分数据集,再基于训练集的信息做One-Hot Encoding,然后用训练集的编码规则去转换测试集。

为什么不能拆分前编码?因为提前对全量数据编码,相当于测试集的类别信息被提前暴露给了训练过程——虽然One-Hot本身不会像均值/标准差那样直接泄露统计信息,但如果测试集中存在训练集没有的类别,提前编码会让模型“提前知道”这些类别存在,破坏了测试集作为“未知数据”的独立性。不过在一些简单场景(比如所有类别在训练集里都完整覆盖),提前编码看似没问题,但这是不规范的,一旦遇到测试集有新类别就会出问题,而且不符合机器学习“训练-验证”的核心逻辑。

问题2:案例中的数据泄漏点

场景1中,拆分前对全量数据做One-Hot Encoding,相当于让训练集间接获取了测试集的类别分布信息。比如你的示例里,测试集包含的类别(如Google)在全量编码时被创建了对应的哑变量,训练集的特征空间包含了这个测试集独有的类别特征,这就属于数据泄漏——模型本应该只基于训练数据学习,却提前接触到了测试数据的类别信息。

场景2报错的原因及解决方法

场景2报错是因为你分别对训练集和测试集独立做了One-Hot Encoding,导致两者的特征维度不匹配。正确的做法是用训练集拟合一个One-Hot编码器,再用这个编码器去转换测试集,确保特征维度一致:

修改后的场景2代码:

# 先拆分数据集
X = df.drop('web_views', axis = 1)
y = df['web_views']
np.random.seed(123)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.20)

# 使用sklearn的OneHotEncoder(推荐),基于训练集拟合
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(sparse_output=False, drop=None)
encoder.fit(X_train)

# 转换训练集和测试集
X_train_encoded = encoder.transform(X_train)
X_test_encoded = encoder.transform(X_test)

# 转换为DataFrame方便查看(可选)
X_train_df = pd.DataFrame(X_train_encoded, columns=encoder.get_feature_names_out())
X_test_df = pd.DataFrame(X_test_encoded, columns=encoder.get_feature_names_out())

这样处理后,训练集和测试集的特征维度完全一致,模型就能正常预测了。

总结

  • 永远遵循“先拆分,再基于训练集做特征工程”的原则,避免数据泄漏
  • 不要独立对训练/测试集做编码,要用训练集拟合的编码器统一转换,保证特征维度一致
  • 行业中所谓的“全量提前编码”往往是在数据预处理阶段做了类别对齐(比如确保训练/测试集类别完全一致),但这不是通用做法,规范流程还是拆分后编码

内容的提问来源于stack exchange,提问作者Beans On Toast

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 13:43:04