【解决(几乎)任何机器学习问题】:处理分类变量篇(下篇)

接【解决(几乎)任何机器学习问题】:处理分类变量篇(上篇)http://t.csdnimg.cn/rnzto
这篇文章相当长,您可以添加至收藏夹,以便在后续有空时候悠闲地阅读。
让我们看看填⼊ NaN 值后 ord_4 列的值计数:
In [ X ]: df . ord_4 . fillna ( "NONE" ). value_counts ()
Out [ X ]:
N 39978
P 37890
Y 36657
A 36633
R 33045
U 32897
. .
.
K 21676
I 19805
NONE 17930
D 17284
F 16721
W 8268
Z 5790
S 4595
G 3404
V 3107
J 1950
L 1657
Name : ord_4 , dtype : int64
我们看到,有些数值只出现了⼏千次,有些则出现了近 40000 次。NaN 也经常出现。请注意,我已经从输出中删除了⼀些值。
现在,我们可以定义将⼀个值称为 " 罕⻅( rare "的标准了。⽐⽅说,在这⼀列中,稀有值的要求是计数⼩于 2000。这样看来,J 和 L 就可以被标记为稀有值了。使⽤ pandas,根据计数阈值替换类别⾮常简单。让我们来看看它是如何实现的。
In [ X ]: df . ord_4 = df . ord_4 . fillna ( "NONE" )
In [ X ]: df . loc [
. : df [ "ord_4" ]. value_counts ()[ df [ "ord_4" ]]. values < 2000 ,
. : "ord_4"
. : ] = "RARE"
In [ X ]: df . ord_4 . value_counts ()
Out [ X ]:
N 39978
P 37890
Y 36657
A 36633
R 33045
U 32897
M 32504
.
.
.
B 25212
E 21871
K 21676
I 19805
NONE 17930
D 17284
F 16721 W 8268
Z 5790
S 4595
RARE 3607
G 3404
V 3107
Name : ord_4 , dtype : int64

我们认为,只要某个类别的值⼩于 2000,就将其替换为罕⻅。因此,现在在测试数据时,所有未⻅过的新类别都将被映射为 "RARE",⽽所有缺失值都将被映射为 "NONE"。
这种⽅法还能确保即使有新的类别,模型也能在实际环境中正常⼯作。
现在,我们已经具备了处理任何带有分类变量问题所需的⼀切条件。让我们尝试建⽴第⼀个模型,并逐步提⾼其性能。
在构建任何类型的模型之前,交叉检验⾄关重要。我们已经看到了标签/⽬标分布,知道这是⼀个⽬标偏斜的⼆元分类问题。因此,我们将使⽤ StratifiedKFold 来分割数据。
import pandas as pd
from sklearn import model_selection
if _ name _ = " _ main _ ":df = pd.read_csv(" . /input/cat_train.csv")df["kfold"] = -1df = df.sample(frac=1).reset_index(drop=True)y = df.target.valueskf = model_selection.StratifiedKFold(n_splits=5)for f, (t_, v_) in enumerate(kf.split(X=df, y=y)):df.loc[v_, 'kfold'] = fdf.to_csv(" . /input/cat_train_folds.csv", index=False)

现在我们可以检查新的折叠 csv,查看每个折叠的样本数:  

In [ X ]: import pandas as pd
In [ X ]: df = pd . read_csv ( " . /input/cat_train_folds.csv" )
In [ X ]: df . kfold . value_counts ()
Out [ X ]:
4 120000
3 120000
2 120000
1 120000
0 120000
Name : kfold , dtype : int64
所有折叠都有 120000 个样本。这是意料之中的,因为训练数据有 600000 个样本,⽽我们做了5次折叠。到⽬前为⽌,⼀切顺利。
现在,我们还可以检查每个折叠的⽬标分布。
In [ X ]: df [ df . kfold = 0 ]. target . value_counts ()
Out [ X ]:
0 97536
1 22464
Name : target , dtype : int64
In [ X ]: df [ df . kfold = 1 ]. target . value_counts ()
Out [ X ]:
0 97536
1 22464
Name : target , dtype : int64
In [ X ]: df [ df . kfold = 2 ]. target . value_counts ()
Out [ X ]:
0 97535
1 22465
Name : target , dtype : int64
In [ X ]: df [ df . kfold = 3 ]. target . value_counts ()
Out [ X ]:
0 97535
1 22465
Name : target , dtype : int64
In [ X ]: df [ df . kfold = 4 ]. target . value_counts ()
Out [ X ]:
0 97535
1 22465
Name : target , dtype : int64

我们看到,在每个折叠中,⽬标的分布都是⼀样的。这正是我们所需要的。它也可以是相似的,并不⼀定要⼀直相同。现在,当我们建⽴模型时,每个折叠中的标签分布都将相同。
我们可以建⽴的最简单的模型之⼀是对所有数据进⾏独热编码并使⽤逻辑回归。
import pandas as pd
from sklearn import linear_model
from sklearn import metrics
from sklearn import preprocessingdef run(fold):df = pd.read_csv(" . /input/cat_train_folds.csv")features = [f for f in df.columns if f not in ("id", "target", "kfold")]
for col in features:df.loc[:, col] = df[col].astype(str).fillna("NONE")
df_train = df[df.kfold = fold].reset_index(drop=True)
df_valid = df[df.kfold = fold].reset_index(drop=True)
ohe = preprocessing.OneHotEncoder()
full_data = pd.concat([df_train[features], df_valid[features]], axis=0)
ohe.fit(full_data[features])
x_train = ohe.transform(df_train[features])
x_valid = ohe.transform(df_valid[features])
model = linear_model.LogisticRegression()
model.fit(x_train, df_train.target.values)
valid_preds = model.predict_proba(x_valid)[:, 1]
auc = metrics.roc_auc_score(df_valid.target.values, valid_preds)
print(auc)if _ name _ = " _ main _ ":run(0)
那么,发⽣了什么呢?
我们创建了⼀个函数,将数据分为训练和验证两部分,给定折叠数,处理 NaN 值,对所有数据进⾏单次编码,并训练⼀个简单的逻辑回归模型。
当我们运⾏这部分代码时,会产⽣如下输出:
❯ python ohe_logres . py
/ home / abhishek / miniconda3 / envs / ml / lib / python3 .7 / site
packages / sklearn / linear_model / _logistic . py : 939 : ConvergenceWarning : lbfgs
failed to converge ( status = 1 ):
STOP : TOTAL NO . of ITERATIONS REACHED LIMIT .
Increase the number of iterations ( max_iter ) or scale the data as shown
in :
https : / scikit - learn . org / stable / modules / preprocessing . html .
Please also refer to the documentation for alternative solver options :
https : / scikit - learn . org / stable / modules / linear_model . html #logistic
regression
extra_warning_msg = _LOGISTIC_SOLVER_CONVERGENCE_MSG )
0.7847865042255127
有⼀些警告。逻辑回归似乎没有收敛到最⼤迭代次数。我们没有调整参数,所以没有问题。我们看到AUC 为 0.785。
现在让我们对代码进⾏简单修改,运⾏所有折叠。
. .
        model = linear_model . LogisticRegression ()
        model . fit ( x_train , df_train . target . values )
        valid_preds = model . predict_proba ( x_valid )[:, 1 ]
        auc = metrics . roc_auc_score ( df_valid . target . values , valid_preds )
        print ( f"Fold = { fold } , AUC = { auc } " )
if _ name _ = " _ main _ " :
        for fold_ in range ( 5 ):
                run ( fold_ )
请注意,我们并没有做很⼤的改动,所以我只显⽰了部分代码⾏,其中⼀些代码⾏有改动。
这就打印出了:
python - W ignore ohe_logres . py
Fold = 0 , AUC = 0.7847865042255127
Fold = 1 , AUC = 0.7853553605899214
Fold = 2 , AUC = 0.7879321942914885
Fold = 3 , AUC = 0.7870315929550808
Fold = 4 , AUC = 0.7864668243125608
请注意,我使⽤"-W ignore "忽略了所有警告。
我们看到,AUC 分数在所有褶皱中都相当稳定。平均 AUC 为 0.78631449527。对于我们的第⼀个模型来说相当不错!
很多⼈在遇到这种问题时会⾸先使⽤基于树的模型,⽐如随机森林。在这个数据集中应⽤随机森林
时,我们可以使⽤标签编码(label encoding),将每⼀列中的每个特征都转换为整数,⽽不是之前讨论过的独热编码。
这种编码与独热编码并⽆太⼤区别。让我们来看看。
import pandas as pd
from sklearn import ensemble
from sklearn import metrics
from sklearn import preprocessingdef run(fold):df = pd.read_csv("./input/cat_train_folds.csv")features = [f for f in df.columns if f not in ("id", "target", "kfold")]for col in features:df.loc[:, col] = df[col].astype(str).fillna("NONE")for col in features:lbl = preprocessing.LabelEncoder()lbl.fit(df[col])df.loc[:, col] = lbl.transform(df[col])df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold == fold].reset_index(drop=True)x_train = df_train[features].valuesx_valid = df_valid[features].valuesmodel = ensemble.RandomForestClassifier(n_jobs=-1)model.fit(x_train, df_train.target.values)valid_preds = model.predict_proba(x_valid)[:, 1]auc = metrics.roc_auc_score(df_valid.target.values, valid_preds)print(f"Fold = {fold}, AUC = {auc}")if __name__ == "__main__":for fold_ in range(5):run(fold_)
我们使⽤ scikit-learn 中的随机森林,并取消了独热编码。我们使⽤标签编码代替独热编码。得分如下
❯ python lbl_rf . py
Fold = 0 , AUC = 0.7167390828113697
Fold = 1 , AUC = 0.7165459672958506
Fold = 2 , AUC = 0.7159709909587376
Fold = 3 , AUC = 0.7161589664189556
Fold = 4 , AUC = 0.7156020216155978
哇 巨⼤的差异! 随机森林模型在没有任何超参数调整的情况下,表现要⽐简单的逻辑回归差很多。
这就是为什么我们总是应该先从简单模型开始的原因。随机森林模型的粉丝会从这⾥开始,⽽忽略逻辑回归模型,认为这是⼀个⾮常简单的模型,不能带来⽐随机森林更好的价值。这种⼈将会犯下⼤错。在我们实现随机森林的过程中,与逻辑回归相⽐,折叠需要更⻓的时间才能完成。因此,我们不仅损失了 AUC,还需要更⻓的时间来完成训练。请注意,使⽤随机森林进⾏推理也很耗时,⽽且占⽤的空间也更⼤。
如果我们愿意,也可以尝试在稀疏的独热编码数据上运⾏随机森林,但这会耗费⼤量时间。我们还可以尝试使⽤奇异值分解来减少稀疏的独热编码矩阵。这是⾃然语⾔处理中提取主题的常⽤⽅法。
import pandas as pd
from scipy import sparse
from sklearn import decomposition
from sklearn import ensemble
from sklearn import metrics
from sklearn import preprocessingdef run(fold):# 读取数据df = pd.read_csv("./input/cat_train_folds.csv")features = [f for f in df.columns if f not in ("id", "target", "kfold")]# 填充空值for col in features:df.loc[:, col] = df[col].astype(str).fillna("NONE")# 分割训练和验证数据df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold == fold].reset_index(drop=True)# One Hot 编码ahe = preprocessing.OneHotEncoder()full_data = pd.concat([df_train[features], df_valid[features]], axis=0)ohe.fit(full_data[features])x_train = ohe.transform(df_train[features])x_valid = ohe.transform(df_valid[features])# 使用截断的奇异值分解(TruncatedSVD)降维svd = decomposition.TruncatedSVD(n_components=120)full_sparse = sparse.vstack((x_train, x_valid))svd.fit(full_sparse)x_train = svd.transform(x_train)x_valid = svd.transform(x_valid)# 使用随机森林分类器训练模型model = ensemble.RandomForestClassifier(n_jobs=-1)model.fit(x_train, df_train.target.values)# 预测并计算AUC值valid_preds = model.predict_proba(x_valid)[:, 1]auc = metrics.roc_auc_score(df_valid.target.values, valid_preds)print(f"Fold = {fold}, AUC = {auc}")if __name__ == "__main__":for fold_ in range(5):run(fold_)
我们对全部数据进⾏独热编码,然后⽤训练数据和验证数据在稀疏矩阵上拟合 scikit-learn 的
TruncatedSVD。这样,我们将⾼维稀疏矩阵减少到 120个特征,然后拟合随机森林分类器。
以下是该模型的输出结果:
❯ python ohe_svd_rf . py
Fold = 0 , AUC = 0.7064863038754249
Fold = 1 , AUC = 0.706050102937374
Fold = 2 , AUC = 0.7086069243167242
Fold = 3 , AUC = 0.7066819080085971
Fold = 4 , AUC = 0.7058154015055585
我们发现情况更糟。看来,解决这个问题的最佳⽅法是使⽤逻辑回归和独热编码。随机森林似乎耗时太多。也许我们可以试试 XGBoost。如果你不知道 XGBoost,它是最流⾏的梯度提升算法之⼀。由于它是⼀种基于树的算法,我们将使⽤标签编码数据。
import pandas as pd
import xgboost as xgb
from sklearn import metrics
from sklearn import preprocessingdef run(fold):# 读取数据df = pd.read_csv("./input/cat_train_folds.csv")features = [f for f in df.columns if f not in ("id", "target", "kfold")]# 填充空值for col in features:df.loc[:, col] = df[col].astype(str).fillna("NONE")# 标签编码for col in features:lbl = preprocessing.LabelEncoder()lbl.fit(df[col])df.loc[:, col] = lbl.transform(df[col])# 划分训练集和验证集df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold == fold].reset_index(drop=True)x_train = df_train[features].valuesx_valid = df_valid[features].values# 使用XGBoost分类器训练模型model = xgb.XGBClassifier(n_jobs=-1,max_depth=7,n_estimators=200)model.fit(x_train, df_train.target.values)# 预测并计算AUC值valid_preds = model.predict_proba(x_valid)[:, 1]auc = metrics.roc_auc_score(df_valid.target.values, valid_preds)print(f"Fold = {fold}, AUC = {auc}")if __name__ == "__main__":for fold_ in range(5):run(fold_)
必须指出的是,在这段代码中,我对 xgboost 参数做了⼀些修改。xgboost 的默认最⼤深度
(max_depth)是 3,我把它改成了 7,还把估计器数量(n_estimators)从 100改成了 200。
该模型的 5折交叉检验得分如下:
❯ python lbl_xgb . py
Fold = 0 , AUC = 0.7656768851999011
Fold = 1 , AUC = 0.7633006564148015
Fold = 2 , AUC = 0.7654277821434345
Fold = 3 , AUC = 0.7663609758878182
Fold = 4 , AUC = 0.764914671468069
我们可以看到,在不做任何调整的情况下,我们的得分⽐普通随机森林要⾼得多。
您还可以尝试⼀些特征⼯程,放弃某些对模型没有任何价值的列等。但似乎我们能做的不多,⽆法证明模型的改进。让我们把数据集换成另⼀个有⼤量分类变量的数据集。另⼀个有名的数据集是 美国成⼈⼈⼝普查数据( US adult census data 。这个数据集包含⼀些特征,⽽你的任务是预测⼯资等级。让我们来看看这个数据集。图 5显⽰了该数据集中的⼀些列。

这些特征⼤多不⾔⾃明。那些不明⽩的,我们可以不考虑。让我们先尝试建⽴⼀个模型。
我们看到收⼊列是⼀个字符串。让我们对这⼀列进⾏数值统计。
In [ X ]: import pandas as pd
In [ X ]: df = pd . read_csv ( " . /input/adult.csv" )
In [ X ]: df . income . value_counts ()
Out [ X ]:
= 50 K 24720
> 50 K 7841
我们可以看到,有 7841 个实例的收⼊超过 5 万美元。这占样本总数的 24%。因此,我们将保持与猫数据集相同的评估⽅法,即 AUC。 在开始建模之前,为了简单起⻅,我们将去掉⼏列特征,即
学历(fnlwgt)
年龄(age)
资本收益(capital.gain)
资本损失(capital.loss)
每周⼩时数(hours.per.week)
让我们试着⽤逻辑回归和独热编码器,看看会发⽣什么。第⼀步总是要进⾏交叉验证。我不会在这⾥ 展⽰这部分代码。留练习。
import pandas as pd
from sklearn import linear_model
from sklearn import metrics
from sklearn import preprocessingdef run(fold):# 读取数据df = pd.read_csv("./input/adult_folds.csv")num_cols = ["fnlwgt","age","capital.gain","capital.loss","hours.per.week"]df = df.drop(num_cols, axis=1)# 将目标变量映射为0和1target_mapping = {"<=50K": 0,">50K": 1}df.loc[:, "income"] = df.income.map(target_mapping)features = [f for f in df.columns if f not in ("kfold", "income")]# 填充空值for col in features:df.loc[:, col] = df[col].astype(str).fillna("NONE")# 划分训练集与验证集df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold == fold].reset_index(drop=True)# One Hot 编码ahe = preprocessing.OneHotEncoder()full_data = pd.concat([df_train[features], df_valid[features]], axis=0)ohe.fit(full_data[features])x_train = ohe.transform(df_train[features])x_valid = ohe.transform(df_valid[features])# 使用逻辑回归训练模型model = linear_model.LogisticRegression()model.fit(x_train, df_train.income.values)# 预测并计算AUC值valid_preds = model.predict_proba(x_valid)[:, 1]auc = metrics.roc_auc_score(df_valid.income.values, valid_preds)print(f"Fold = {fold}, AUC = {auc}")if __name__ == "__main__":for fold_ in range(5):run(fold_)

当我们运⾏这段代码时,我们会得到 

❯ python - W ignore ohe_logres . py
Fold = 0 , AUC = 0.8794809708119079
Fold = 1 , AUC = 0.8875785068274882
Fold = 2 , AUC = 0.8852609687685753
Fold = 3 , AUC = 0.8681236223251438
Fold = 4 , AUC = 0.8728581541840037
对于⼀个如此简单的模型来说,这是⼀个⾮常不错的 AUC!
现在,让我们在不调整任何超参数的情况下尝试⼀下标签编码的xgboost。
import pandas as pd
import xgboost as xgb
from sklearn import metrics
from sklearn import preprocessingdef run(fold):# 读取数据df = pd.read_csv("./input/adult_folds.csv")num_cols = ["fnlwgt","age","capital.gain","capital.loss","hours.per.week"]df = df.drop(num_cols, axis=1)# 将目标变量映射为0和1target_mapping = {"<=50K": 0,">50K": 1}df.loc[:, "income"] = df.income.map(target_mapping)features = [f for f in df.columns if f not in ("kfold", "income")]# 填充空值for col in features:df.loc[:, col] = df[col].astype(str).fillna("NONE")# 标签编码for col in features:lbl = preprocessing.LabelEncoder()lbl.fit(df[col])df.loc[:, col] = lbl.transform(df[col])# 划分训练集与验证集df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold == fold].reset_index(drop=True)x_train = df_train[features].valuesx_valid = df_valid[features].values# 使用XGBoost分类器训练模型model = xgb.XGBClassifier(n_jobs=-1)model.fit(x_train, df_train.income.values)# 预测并计算AUC值valid_preds = model.predict_proba(x_valid)[:, 1]auc = metrics.roc_auc_score(df_valid.income.values, valid_preds)print(f"Fold = {fold}, AUC = {auc}")if __name__ == "__main__":for fold_ in range(5):run(fold_)

让我们运⾏上⾯代码:

❯ python lbl_xgb . py
Fold = 0 , AUC = 0.8800810634234078
Fold = 1 , AUC = 0.886811884948154
Fold = 2 , AUC = 0.8854421433318472
Fold = 3 , AUC = 0.8676319549361007
Fold = 4 , AUC = 0.8714450054900602

这看起来已经相当不错了。让我们看看 max_depth 增加到 7 和 n_estimators 增加到 200时的得分。 

❯ python lbl_xgb . py
Fold = 0 , AUC = 0.8764108944332032
Fold = 1 , AUC = 0.8840708537662638
Fold = 2 , AUC = 0.8816601162613102
Fold = 3 , AUC = 0.8662335762581732
Fold = 4 , AUC = 0.8698983461709926
看起来并没有改善。
这表明,⼀个数据集的参数不能移植到另⼀个数据集。我们必须再次尝试调整参数,但我们将在接下来的章节中详细说明。
现在,让我们尝试在不调整参数的情况下将数值特征纳⼊ xgboost 模型。
import pandas as pd
import xgboost as xgb
from sklearn import metrics
from sklearn import preprocessingdef run(fold):# 读取数据df = pd.read_csv("./input/adult_folds.csv")num_cols = ["fnlwgt","age","capital.gain","capital.loss","hours.per.week"]# 将目标变量映射为0和1target_mapping = {"<=50K": 0,">50K": 1}df.loc[:, "income"] = df.income.map(target_mapping)features = [f for f in df.columns if f not in ("kfold", "income")]# 填充空值for col in features:if col not in num_cols:df.loc[:, col] = df[col].astype(str).fillna("NONE")# 标签编码for col in features:if col not in num_cols:lbl = preprocessing.LabelEncoder()lbl.fit(df[col])df.loc[:, col] = lbl.transform(df[col])# 划分训练集与验证集df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold == fold].reset_index(drop=True)x_train = df_train[features].valuesx_valid = df_valid[features].values# 使用XGBoost分类器训练模型model = xgb.XGBClassifier(n_jobs=-1)model.fit(x_train, df_train.income.values)# 预测并计算AUC值valid_preds = model.predict_proba(x_valid)[:, 1]auc = metrics.roc_auc_score(df_valid.income.values, valid_preds)print(f"Fold = {fold}, AUC = {auc}")if __name__ == "__main__":for fold_ in range(5):run(fold_)
因此,我们保留数字列,只是不对其进⾏标签编码。这样,我们的最终特征矩阵就由数字列(原样)和编码分类列组成了。任何基于树的算法都能轻松处理这种混合。
请注意,在使⽤基于树的模型时,我们不需要对数据进⾏归⼀化处理。不过,这⼀点⾮常重要,在使⽤线性模型(如逻辑回归)时不容忽视。
现在让我们运⾏这个脚本!
❯ python lbl_xgb_num . py
Fold = 0 , AUC = 0.9209790185449889
Fold = 1 , AUC = 0.9247157449144706
Fold = 2 , AUC = 0.9269329887598243
Fold = 3 , AUC = 0.9119349082169275
Fold = 4 , AUC = 0.9166408030141667
哇哦
这是⼀个很好的分数!
现在,我们可以尝试添加⼀些功能。我们将提取所有分类列,并创建所有⼆度组合。请看下⾯代码段中的 feature_engineering 函数,了解如何实现这⼀点。
import itertools
import pandas as pd
import xgboost as xgb
from sklearn import metrics
from sklearn import preprocessingdef feature_engineering(df, cat_cols):combi = list(itertools.combinations(cat_cols, 2))for c1, c2 in combi:df.loc[:, c1 + "_" + c2] = df[c1].astype(str) + "_" + df[c2].astype(str)return dfdef run(fold):df = pd.read_csv("./input/adult_folds.csv")num_cols = ["fnlwgt","age","capital.gain","capital.loss","hours.per.week"]# 将目标变量映射为0和1target_mapping = {"<=50K": 0,">50K": 1}df.loc[:, "income"] = df.income.map(target_mapping)cat_cols = [c for c in df.columns if c not in num_cols and c not in ("kfold", "income")]# 特征工程df = feature_engineering(df, cat_cols)features = [f for f in df.columns if f not in ("kfold", "income")]# 填充空值for col in features:if col not in num_cols:df.loc[:, col] = df[col].astype(str).fillna("NONE")# 标签编码for col in features:if col not in num_cols:lbl = preprocessing.LabelEncoder()lbl.fit(df[col])df.loc[:, col] = lbl.transform(df[col])# 划分训练集与验证集df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold == fold].reset_index(drop=True)x_train = df_train[features].valuesx_valid = df_valid[features].values# 使用XGBoost分类器训练模型model = xgb.XGBClassifier(n_jobs=-1)model.fit(x_train, df_train.income.values)# 预测并计算AUC值valid_preds = model.predict_proba(x_valid)[:, 1]auc = metrics.roc_auc_score(df_valid.income.values, valid_preds)print(f"Fold = {fold}, AUC = {auc}")if __name__ == "__main__":for fold_ in range(5):run(fold_)
这是从分类列中创建特征的⼀种⾮常幼稚的⽅法。我们应该仔细研究数据,看看哪些组合最合理。如果使⽤这种⽅法,最终可能会创建⼤量特征,在这种情况下,就需要使⽤某种特征选择来选出最佳特征。稍后我们将详细介绍特征选择。现在让我们来看看分数。
❯ python lbl_xgb_num_feat . py
Fold = 0 , AUC = 0.9211483465031423
Fold = 1 , AUC = 0.9251499446866125
Fold = 2 , AUC = 0.9262344766486692
Fold = 3 , AUC = 0.9114264068794995
Fold = 4 , AUC = 0.9177914453099201
看来,即使不改变任何超参数,只增加⼀些特征,我们也能提⾼⼀些折叠得分。让我们看看将
max_depth 增加到 7 是否有帮助。
❯ python lbl_xgb_num_feat . py
Fold = 0 , AUC = 0.9286668430204137
Fold = 1 , AUC = 0.9329340656165378
Fold = 2 , AUC = 0.9319817543218744
Fold = 3 , AUC = 0.919046187194538
Fold = 4 , AUC = 0.9245692057162671
我们再次改进了我们的模型。
请注意,我们还没有使⽤稀有值、⼆值化、独热编码和标签编码特征的组合以及其他⼏种⽅法。 从分类特征中进⾏特征⼯程的另⼀种⽅法是使⽤ ⽬标编码 。但是,您必须⾮常⼩⼼,因为这可能会使
您的模型过度拟合。⽬标编码是⼀种将给定特征中的每个类别映射到其平均⽬标值的技术,但必须始终以交叉验证的⽅式进⾏。这意味着⾸先要创建折叠,然后使⽤这些折叠为数据的不同列创建⽬标编码特征,⽅法与在折叠上拟合和预测模型的⽅法相同。因此,如果您创建了 5 个折叠,您就必须创建5 次⽬标编码,这样最终,您就可以为每个折叠中的变量创建编码,⽽这些变量并⾮来⾃同⼀个折叠。然后在拟合模型时,必须再次使⽤相同的折叠。未⻅测试数据的⽬标编码可以来⾃全部训练数据,也可以是所有 5个折叠的平均值。
让我们看看如何在同⼀个成⼈数据集上使⽤⽬标编码,以便进⾏⽐较。
import copy
import pandas as pd
from sklearn import metrics
from sklearn import preprocessing
import xgboost as xgbdef mean_target_encoding(data):df = copy.deepcopy(data)num_cols = ["fnlwgt","age","capital.gain","capital.loss","hours.per.week"]# 将目标变量映射为0和1target_mapping = {"<=50K": 0,">50K": 1}df.loc[:, "income"] = df.income.map(target_mapping)features = [f for f in df.columns if f not in ("kfold", "income") and f not in num_cols]# 填充空值和标签编码for col in features:if col not in num_cols:df.loc[:, col] = df[col].astype(str).fillna("NONE")lbl = preprocessing.LabelEncoder()lbl.fit(df[col])df.loc[:, col] = lbl.transform(df[col])encoded_dfs = []for fold in range(5):df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold != fold].reset_index(drop=True)for column in features:mapping_dict = dict(df_train.groupby(column)["income"].mean())df_valid.loc[:, column + "_enc"] = df_valid[column].map(mapping_dict)encoded_dfs.append(df_valid)encoded_df = pd.concat(encoded_dfs, axis=0)return encoded_dfdef run(df, fold):df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold != fold].reset_index(drop=True)features = [f for f in df.columns if f not in ("kfold", "income")]x_train = df_train[features].valuesx_valid = df_valid[features].valuesmodel = xgb.XGBClassifier(n_jobs=-1, max_depth=7)model.fit(x_train, df_train.income.values)valid_preds = model.predict_proba(x_valid)[:, 1]auc = metrics.roc_auc_score(df_valid.income.values, valid_preds)print(f"Fold = {fold}, AUC = {auc}")if __name__ == "__main__":df = pd.read_csv("./input/adult_folds.csv")df = mean_target_encoding(df)for fold_ in range(5):run(df, fold_)
必须指出的是,在上述⽚段中,我在进⾏⽬标编码时并没有删除分类列。我保留了所有特征,并在此基础上添加了⽬标编码特征。此外,我还使⽤了平均值。您可以使⽤平均值、中位数、标准偏差或⽬标的任何其他函数。
让我们看看结果。
Fold = 0 , AUC = 0.9332240662017529
Fold = 1 , AUC = 0.9363551625140347
Fold = 2 , AUC = 0.9375013544556173
Fold = 3 , AUC = 0.92237621307625
Fold = 4 , AUC = 0.9292131180445478
不错!看来我们⼜有进步了。不过,使⽤⽬标编码时必须⾮常⼩⼼,因为它太容易出现过度拟合。当我们使⽤⽬标编码时,最好使⽤某种平滑⽅法或在编码值中添加噪声。 Scikit-learn 的贡献库中有带平滑的⽬标编码,你也可以创建⾃⼰的平滑。平滑会引⼊某种正则化,有助于避免模型过度拟合。这并不难。
处理分类特征是⼀项复杂的任务。许多资源中都有⼤量信息。本章应该能帮助你开始解决分类变量的任何问题。不过,对于⼤多数问题来说,除了独热编码和标签编码之外,你不需要更多的东西。 要进⼀步改进模型,你可能需要更多!
在本章的最后,我们不能不在这些数据上使⽤神经⽹络。因此,让我们来看看⼀种称为 实体嵌⼊ 的技术。在实体嵌⼊中,类别⽤向量表⽰。在⼆值化和独热编码⽅法中,我们都是⽤向量来表⽰类别的。但是,如果我们有数以万计的类别怎么办?这将会产⽣巨⼤的矩阵,我们将需要很⻓时间来训练复杂的模型。因此,我们可以⽤带有浮点值的向量来表⽰它们。
这个想法⾮常简单。每个分类特征都有⼀个嵌⼊层。因此,⼀列中的每个类别现在都可以映射到⼀个嵌⼊层(就像在⾃然语⾔处理中将单词映射到嵌⼊层⼀样)。然后,根据其维度重塑这些嵌⼊层,使其扁平化,然后将所有扁平化的输⼊嵌⼊层连接起来。然后添加⼀堆密集层和⼀个输出层,就⼤功告成了。

出于某种原因,我发现使⽤ TF/Keras 可以⾮常容易地做到这⼀点。因此,让我们来看看如何使⽤
TF/Keras 实现它。此外,这是本书中唯⼀⼀个使⽤ TF/Keras 的⽰例,将其转换为 PyTorch(使⽤ cat in-the-dat-ii 数据集)也⾮常容易
import os
import gc
import joblib
import pandas as pd
import numpy as np
from sklearn import metrics, preprocessing
from tensorflow.keras import layers
from tensorflow.keras import optimizers
from tensorflow.keras.models import Model, load_model
from tensorflow.keras import callbacks
from tensorflow.keras import backend as K
from tensorflow.keras import utilsdef create_model(data, catcols):inputs = []outputs = []for c in catcols:num_unique_values = int(data[c].nunique())embed_dim = int(min(np.ceil((num_unique_values)/2), 50))inp = layers.Input(shape=(1,))out = layers.Embedding(num_unique_values + 1, embed_dim, name=c)(inp)out = layers.SpatialDropout1D(0.3)(out)out = layers.Reshape(target_shape=(embed_dim, ))(out)inputs.append(inp)outputs.append(out)x = layers.Concatenate()(outputs)x = layers.BatchNormalization()(x)x = layers.Dense(300, activation="relu")(x)x = layers.Dropout(0.3)(x)x = layers.BatchNormalization()(x)x = layers.Dense(300, activation="relu")(x)x = layers.Dropout(0.3)(x)x = layers.BatchNormalization()(x)y = layers.Dense(2, activation="softmax")(x)model = Model(inputs=inputs, outputs=y)model.compile(loss='binary_crossentropy', optimizer='adam')return modeldef run(fold):df = pd.read_csv("./input/cat_train_folds.csv")features = [f for f in df.columns if f not in ("id", "target", "kfold")]for col in features:df.loc[:, col] = df[col].astype(str).fillna("NONE")for feat in features:lbl_enc = preprocessing.LabelEncoder()df.loc[:, feat] = lbl_enc.fit_transform(df[feat].values)df_train = df[df.kfold == fold].reset_index(drop=True)df_valid = df[df.kfold != fold].reset_index(drop=True)model = create_model(df, features)xtrain = [df_train[features].values[:, k] for k in range(len(features))]xvalid = [df_valid[features].values[:, k] for k in range(len(features))]ytrain = df_train.target.valuesyvalid = df_valid.target.valuesytrain_cat = utils.to_categorical(ytrain)yvalid_cat = utils.to_categorical(yvalid)model.fit(xtrain,ytrain_cat,validation_data=(xvalid, yvalid_cat),verbose=1,batch_size=1024,epochs=3)valid_preds = model.predict(xvalid)[:, 1]print(metrics.roc_auc_score(yvalid, valid_preds))K.clear_session()if __name__ == "__main__":run(0)run(1)run(2)run(3)run(4)
你会发现这种⽅法效果最好,⽽且如果你有 GPU,速度也超快!这种⽅法还可以进⼀步改进,⽽且你⽆需担⼼特征⼯程,因为神经⽹络会⾃⾏处理。在处理⼤量分类特征数据集时,这绝对值得⼀试。当嵌⼊⼤⼩与唯⼀类别的数量相同时,我们就可以使⽤独热编码(one-hot-encoding)。
本章基本上都是关于特征⼯程的。让我们在下⼀章中看看如何在数字特征和不同类型特征的组合⽅⾯进⾏更多的特征⼯程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/478995.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用python绘制黄金价格变化曲线

首先你得从mt4把数据导出为csv&#xff1a;mt4如何导出数据-CSDN博客 1、引入必要的库 import numpy as np import pandas as pd import matplotlib.pyplot as plt 2、然后通过pandas载入csv数据 raw pd.read_csv("XAUUSDm1.csv", headerNone, index_colNone, p…

[工具探索]VSCode介绍和进阶使用

相比较GoLand、PhpStorm、PyCharm、WebStorm的重量级内存占用&#xff0c;从Windows系统来&#xff0c;各种卡死&#xff0c;换到MacOS倒不会卡死&#xff0c;但是内存占用太多&#xff0c;影响体验&#xff0c;决定换到VSCode。当然这个过程需要适应过渡期&#xff0c;旧伙计都…

怎么把照片缩小到200k?图片压缩这样做

怎么把照片缩小到200k&#xff1f;在日常生活中&#xff0c;将图片压缩到200K可以让我们轻松地将图片上传到社交媒体平台&#xff0c;而不会因为图片过大而导致上传失败。还可以减少存储空间占用&#xff0c;让我们可以保存更多的图片而不用担心手机或电脑存储空间不足。因此&a…

WebRTC最新版报错解决:FileNotFoundError: LASTCHANGE.committime (二十五)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

LeetCode 0590. N 叉树的后序遍历:深度优先搜索(DFS)

【LetMeFly】590.N 叉树的后序遍历&#xff1a;深度优先搜索(DFS) 力扣题目链接&#xff1a;https://leetcode.cn/problems/n-ary-tree-postorder-traversal/ 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 后序遍历 。 n 叉树 在输入中按层序遍历进行序列化表…

51_蓝桥杯_独立按键

一 电路 注意&#xff1a;J5跳帽接到2~3引脚&#xff0c;使按键S4-S5四个按键的另外一端接地&#xff0c;从而成为4个独立按键。 二 独立按键工作原理 三 代码 代码1&#xff1a;按下S7点亮L1指示灯&#xff0c;松开按键&#xff0c;指示灯熄灭&#xff0c;按下S6点亮L2指示灯…

Spring Boot与LiteFlow:轻量级流程引擎的集成与应用含完整过程

点击下载《Spring Boot与LiteFlow&#xff1a;轻量级流程引擎的集成与应用含完整过程》 1. 前言 本文旨在介绍Spring Boot与LiteFlow的集成方法&#xff0c;详细阐述LiteFlow的原理、使用流程、步骤以及代码注释。通过本文&#xff0c;读者将能够了解LiteFlow的特点&#xff…

【动态规划】【字符串】2167移除所有载有违禁货物车厢所需的最少时间

作者推荐 【深度优先搜索】【树】【有向图】【推荐】685. 冗余连接 II 本文涉及知识点 动态规划汇总 LeetCode2167移除所有载有违禁货物车厢所需的最少时间 给你一个下标从 0 开始的二进制字符串 s &#xff0c;表示一个列车车厢序列。s[i] ‘0’ 表示第 i 节车厢 不 含违…

前端|Day2:列表、表格、表单(黑马笔记)

Day2&#xff1a;列表、表格、表单 目录 Day2&#xff1a;列表、表格、表单一、列表1.无序列表2.有序列表3. 定义列表 二、表格1.基本使用2. 表格结构标签(了解)3.合并单元格 三、表单1.input 标签2.input 标签占位文本3.单选框4.上传文件5.多选框6.下拉菜单7.文本域8.label 标…

DS:循环队列的实现

创作不易&#xff0c;给个三连吧&#xff01;&#xff01; 一、前言 对于循环队列&#xff0c;博主也是源自于一道力扣的OJ题 力扣&#xff1a;循环队列的设置 后来我在网上查过&#xff0c;这个循环队列是有自己的应用场景的&#xff01;&#xff01;并不是出题者为了出题…

【数据分享】2014-2024年全国监测站点的逐时空气质量数据(15个指标\Excel\Shp格式)

空气质量的好坏反映了空气的污染程度&#xff0c;在各项涉及城市环境的研究中&#xff0c;空气质量都是一个十分重要的指标。空气质量是依据空气中污染物浓度的高低来判断的。 我们发现学者王晓磊在自己的主页里面分享了2014年5月以来的全国范围的到站点的逐时的空气质量数据&…

Java+SpringBoot:农业疾病防治新选择

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…