FeatureTools 创建的特征构建不一致的模型

Features Created by FeatureTools Build Inconsistent Models

我有一个不平衡的数据集,其中包含来自 class 0 的 2 亿数据和来自 class 1 的 8000 数据。我采用了两种不同的方法来构建模型。

  1. 随机采样一个比率为 1:4 的新数据集。意思是 class 0 中的 32000 和 class 1 中的 8000。然后使用特征工具生成特征(在我的例子中生成了 70 个特征)并将数据集拆分为训练集和测试集 test_size = 0.2 和对少数群体进行分层 class。使用随机森林算法建立模型并预测测试集。

代码:

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. 拆分class 1 中的所有数据,使用 60% 用于训练集,40% 用于测试集。 Class 训练集的比率与第一种方法(1:4)相同,但测试集的比率为 1:200。使用featuretools(重新创建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.方法: (Class 0 为负,class 1 为正)

TN:6306

FP:94

TP:1385

FN:215

2。方法:

TN:576743

FP:63257

TP:361

FN:2839

第一个结果对我来说很好,但第二个结果很糟糕。这怎么可能?我知道我使用 class 1 中的较少数据来训练第二种方法的模型,但它应该不会有太大差异。我的意思是这比抛硬币还糟糕。两种方法都随机生成子集,我尝试了许多不同的子集,但结果与上面几乎相同。感谢任何形式的帮助。

编辑:我可能有想法但不确定...我在第一种方法中使用 train_test_split。因此,训练集和测试集共享一些 personal_id,但在第二种方法中,训练集和测试集具有完全不同的 personal_id。当模型遇到它之前没有看到的 personal_id 时,它无法正确预测并决定将其标记为 majority class。如果是这种情况,那么将准确地为给定的分类变量创建特征(过度拟合)。同样,当它遇到任何分类列的不同值时,它只会感到困惑。我怎样才能克服这样的问题?

Edit2:我测试了上面提到的想法并得到了奇怪的结果。首先,我从数据集中删除了 personal_id 列,但它最终得到了更好的模型。然后我以 personal_id 出现在训练集中也应该出现在测试集中的方式测试了我的第二种方法。我以为我会得到更好的模型,但它比以前更糟。我真的很困惑...

我同意该模型可能过度拟合并且未能在给定新个人 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。如果您觉得这有帮助,请告诉我。