问题描述
我有一个不平衡数据集,其中有 2 亿个来自第 0 类的数据和 8000 个来自第 1 类的数据。我采用了两种不同的方法来构建模型。
- 随机采样一个比例为 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',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,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 列,但最终得到了更好的模型。然后我测试了我的第二种方法,个人 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',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
然后,您可以将特征矩阵拆分为训练集和测试集。
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。如果您觉得这有帮助,请告诉我。