FeatureTools 创建的特征构建不一致的模型
Posted
技术标签:
【中文标题】FeatureTools 创建的特征构建不一致的模型【英文标题】:Features Created by FeatureTools Build Inconsistent Models 【发布时间】:2021-10-03 13:42:26 【问题描述】:我有一个不平衡的数据集,其中包含来自 0 类的 2 亿个数据和来自 1 类的 8000 个数据。我采用了两种不同的方法来构建模型。
-
随机采样一个比例为 1:4 的新数据集。意思是 0 类的 32000 和 1 类的 8000。然后使用特征工具生成特征(在我的例子中生成了 70 个特征)并将数据集拆分为 test_size = 0.2 的训练集和测试集,并对少数类进行分层。使用随机森林算法构建模型并预测测试集。
代码:
import ....
df = pd.read_csv(...)
label = df['target']
es = ft.EntitySet(id='maintable')
es = es.entity_from_dataframe(entity_id='maintable',dataframe=df,make_index=True,
index='index',time_index='date_info',variable_types='personal_id': ft.variable_types.Categorical,
'category_id': ft.variable_types.Categorical, 'name': ft.variable_types.Categorical)
es.normalize_entity(base_entity_id='maintable',new_entity_id='personal_id')
es.normalize_entity(base_entity_id='maintable',new_entity_id='category_id')
es.normalize_entity(base_entity_id='maintable',new_entity_id='name')
fm, features = ft.dfs(entityset=es,target_entity='maintable',max_depth=3)
fm = fm.set_index(label.index)
fm['target'] = label
X = fm[fm.columns.difference(['target'])]
y = fm['target']
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=42,stratify=y,test_size=0.2)
rf = RandomForestClassifier(random_state=42,n_jobs=-1)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
rf.fit(X_train,y_train)
y_pred = rf.predict(X_test)
#print results
.....
-
从第 1 类中拆分所有数据,将 60% 用于训练集,40% 用于测试集。训练集的类比与第一种方法相同(1:4),但测试集的类比为 1:200。使用特征工具(再次创建 70 个特征),使用随机森林算法构建模型并预测测试集。
代码:
import ....
df = pd.read_csv(...)
# I merged randomly generated(with java) train and test sets to create features with featuretools. I created a column 'test_data' which takes two binary values (1 for test set 0 for train set) so I can separate train and test set for fitting model and predicting.
label = df['target','test_data']
es = ft.EntitySet(id='maintable')
es = es.entity_from_dataframe(entity_id='maintable',dataframe=df,make_index=True,
index='index',time_index='date_info',variable_types='personal_id': ft.variable_types.Categorical,
'category_id': ft.variable_types.Categorical, 'name': ft.variable_types.Categorical)
es.normalize_entity(base_entity_id='maintable',new_entity_id='personal_id')
es.normalize_entity(base_entity_id='maintable',new_entity_id='category_id')
es.normalize_entity(base_entity_id='maintable',new_entity_id='name')
fm, features = ft.dfs(entityset=es,target_entity='maintable',max_depth=3)
fm = fm.set_index(label.index)
fm['target','test_data'] = label
df_train = fm.loc[fm['test_data'] == 0]
df_test = fm.loc[fm['test_data'] == 1]
#Drop 'test_data' column because I dont need it anymore
df_train = df_train.drop(['test_data'],axis=1)
df_test = df_test.drop(['test_data'],axis=1)
X_train = df_train[df_train.columns.difference(['target'])]
y_train = df_train['target']
X_test = df_test[df_test.columns.difference(['target'])]
y_test = df_test['target']
rf = RandomForestClassifier(random_state=42,n_jobs=-1)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
rf.fit(X_train,y_train)
y_pred = rf.predict(X_test)
#print results
现在对我来说有趣的部分开始了。以下是两种方法的结果。
1.方法: (0类为负,1类为正)
TN:6306
FP:94
TP:1385
FN:215
2。方法:
TN:576743
FP:63257
TP:361
FN:2839
第一个结果对我来说非常好,但第二个结果很糟糕。这怎么可能?我知道我使用较少的 1 类数据来训练第二种方法的模型,但它不应该有太大差异。我的意思是它比硬币翻转更糟糕。子集是在这两种方法上随机生成的,我尝试了许多不同的子集,但结果与上面几乎相同。任何形式的帮助表示赞赏。
编辑:我可能有一个想法,但不确定...我在第一种方法中使用 train_test_split。因此,训练集和测试集共享一些personal_id,但在第二种方法中,训练集和测试集具有完全不同的personal_id。当模型遇到在它无法正确预测之前没有看到的personal_id 并决定将其标记为多数类时。如果是这种情况,那么正在为给定的分类变量(过度拟合)精确地创建特征。同样,当它遇到任何分类列的不同值时,它只会感到困惑。我该如何克服这样的问题?
Edit2:我测试了上面提到的想法并得到了奇怪的结果。首先,我从数据集中删除了 personal_id 列,但最终得到了更好的模型。然后我测试了我的第二种方法,即personal_id出现在训练集中也应该出现在测试集中。我以为我会得到更好的模型,但它比以前更糟。我真的很困惑……
【问题讨论】:
你能发布一个示例代码 sn-p 来显示问题吗? 我用代码编辑了我的问题。 【参考方案1】:我同意模型可能过度拟合,并且在给定新的个人 ID 的情况下无法泛化。我建议将标签与截止时间一起传递,以获得更结构化的训练和测试集。我将通过一个使用此数据的快速示例。
index name personal_id category_id date_info target
0 0 Samuel 3 C 2021-07-15 0
1 1 Samuel 3 C 2021-07-15 0
2 2 Samuel 3 C 2021-07-15 0
3 3 Samuel 3 C 2021-07-15 0
4 4 Rosanne 2 C 2021-05-11 0
.. ... ... ... ... ... ...
95 95 Donia 1 C 2020-09-27 1
96 96 Donia 1 C 2020-09-27 1
97 97 Fleming 1 A 2021-06-15 1
98 98 Fred 1 C 2021-02-28 0
99 99 Giacomo 1 A 2021-06-19 1
[100 rows x 6 columns]
首先,根据还包括目标列的时间索引创建截止时间。确保从原始数据中删除目标列。
target = df[['date_info', 'index', 'target']]
df.drop(columns='target', inplace=True)
然后,您可以像往常一样构造实体集。
import featuretools as ft
es = ft.EntitySet(id='maintable')
es = es.entity_from_dataframe(
entity_id='maintable',
dataframe=df,
index='index',
time_index='date_info',
variable_types=
'personal_id': ft.variable_types.Categorical,
'category_id': ft.variable_types.Categorical,
'name': ft.variable_types.Categorical
,
)
es.normalize_entity(base_entity_id='maintable', new_entity_id='personal_id', index='personal_id',)
es.normalize_entity(base_entity_id='maintable', new_entity_id='category_id', index='category_id')
es.normalize_entity(base_entity_id='maintable', new_entity_id='name', index='name')
现在,在 DFS 调用中,您可以传入目标截止时间。这种方法不会使用目标列来构建特征,并确保目标列与特征矩阵保持对齐。
fm, fd = ft.dfs(entityset=es, target_entity='maintable', max_depth=3, cutoff_time=target)
personal_id category_id name DAY(date_info) ... name.NUM_UNIQUE(maintable.MONTH(date_info)) name.NUM_UNIQUE(maintable.WEEKDAY(date_info)) name.NUM_UNIQUE(maintable.YEAR(date_info)) target
index ...
59 1 C Fred 28 ... 1 1 1 0
35 1 A Giacomo 19 ... 1 1 1 1
82 3 B Laverna 17 ... 1 1 1 0
25 2 C Rosanne 11 ... 1 1 1 0
23 1 A Giacomo 19 ... 1 1 1 1
然后,您可以将特征 maxtrix 拆分为训练和测试集。
from sklearn.model_selection import train_test_split
X_train, X_test = train_test_split(fm, test_size=.2, shuffle=False)
y_train, y_test = X_train.pop('target'), X_test.pop('target')
对于 AutoML,您可以使用 EvalML 找到最佳 ML 管道并绘制混淆矩阵。
from evalml import AutoMLSearch
from evalml.model_understanding.graphs import graph_confusion_matrix
automl = AutoMLSearch(
X_train=X_train,
y_train=y_train,
problem_type='binary',
allowed_model_families=['random_forest'],
)
automl.search()
y_pred = automl.best_pipeline.predict(X_test)
graph_confusion_matrix(y_test, y_pred).show()
您可以在链接页面中找到类似的machine learning examples。如果您觉得这有帮助,请告诉我。
【讨论】:
好的,谢谢,我会试试这个,但我有一个问题。您将 date_info 用于 time_index 和 cut_off 时间。不应该不一样吗?也许 cut_off_time = date_info - 90 天? 我实现了你的示例代码,这就是我得到的:TN: 72476, FP: 0, FN: 86, TP: 1893 Precision: 100%, Recall: 96% 这显然是过度拟合但我可以修复它。现在我应该用我的第二种方法来测试它。我需要在 java 中创建训练集和测试集,合并它们,使用特征工具创建特征,分离它们,拟合模型和预测。我希望我能得到一个好的模型。 您可以使用时间索引date_info
作为截止时间。这种方法可确保您仅使用截止时间之前存在的数据构建功能。每个标签都可以有对应的截止时间,这样模型训练就不会出现数据泄露。
如果要在截止时间排除数据,可以在DFS调用中使用include_cuttoff_time=False
。您可以在链接页面上找到有关handling time 的更多详细信息。
我想我理解我的问题。这行label = df['target','test_data']
和这两行fm = fm.set_index(label.index)
fm['target','test_data'] = label
导致了问题。合并时,特征矩阵(fm)和label
没有正确对齐。我在一个小数据集上对此进行了测试,我意识到应该标记为 1 类的行在合并过程完成后被标记为 0 类。这个错误导致模型失败。感谢@Jeff 的帮助。我会尝试解决这个问题并更新这篇文章。我仍然不清楚 cutoff_time,所以我可能还有问题。以上是关于FeatureTools 创建的特征构建不一致的模型的主要内容,如果未能解决你的问题,请参考以下文章