0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

以KaggleDays数据集为例,编码方法介绍

zhKF_jqr_AI 来源:未知 作者:李倩 2018-11-22 09:10 次阅读

编者按:华沙大学机器学习科学家Wojciech Rosinski介绍了类别编码的主要方法。

介绍

这是特征工程方法系列的第一篇。在机器学习的实践中,特征工程是最重要而定义最松散的方面之一。它可以被视为艺术,没有严格的规则,创造性是其关键。

特征工程是要为机器学习模型创建更好的信息表示。即便使用非线性算法,如果使用原始数据,我们也无法建模数据集的变量之间的所有交互(关系)。因此,我们需要手工探查、处理数据。

这就带来了一个问题——深度学习怎么讲?深度学习是为了最小化手工处理数据的需求,使模型能够自行学习恰当的数据表示。在图像、语音、文本之类没有给定其他“元数据”的数据上,深度学习会表现得更好。而在表格式数据上,没有什么可以战胜梯度提升树方法,例如XGBoost或LightGBM。机器学习竞赛证明了这一点——几乎所有表格式数据的获胜方案中,基于决策树的模型是最佳的,而深度学习模型通常没法达到这么好的结果(但混合基于决策树的模型时效果非常好 ;-) )

特征工程的偏差是领域知识。取决于需要解决的问题,每个数据集应该使用不同的特征工程方法,原因正在于此。不过,仍有一些广泛使用的方法,至少值得尝试下能否提升模型表现。HJ vav Veen的讲演中提到了大量的实用信息。下面的一些方法正是根据讲演的描述实现的。

本文以KaggleDays数据集为例,编码方法介绍参考了上面的讲演。

数据集

数据来自reddit,包含问题和回答。目标是预测回答的赞数。之所以用这个数据集为例,是因为它包含文本和标准特征。

引入需要用到的库:

import gc

import numpy as np

import pandas as pd

加载数据:

X = pd.read_csv('../input/train.csv', sep="\t", index_col='id')

列:

['question_id',

'subreddit',

'question_utc',

'question_text',

'question_score',

'answer_utc',

'answer_text',

'answer_score']

每个question_id对应一个具体问题(见question_text)。每个question_id可能出现多次,因为每一行包含对这一问题的一个不同回答(见answer_text)。问题和回答的时间日期由_utc列提供。另外还包括问题发布的subreddit(版块)的信息。question_score是问题的赞数,而answer_score是回答的赞数。answer_score是目标变量。

数据需要根据question_id分为训练子集和验证子集,仿效Kaggle分训练集和测试集的做法。

question_ids = X.question_id.unique()

question_ids_train = set(pd.Series(question_ids).sample(frac=0.8))

question_ids_valid = set(question_ids).difference(question_ids_train)

X_train = X[X.question_id.isin(question_ids_train)]

X_valid = X[X.question_id.isin(question_ids_valid)]

类别特征和数值特征

机器学习模型只能处理数字。数值(连续、定量)变量是可以在有限或无限区间内取任何值的变量,它们可以很自然地用数字表示,所以可以在模型中直接使用。原始类别变量通常以字符串的形式存在,在传入模型之前需要变换。

subreddit是类别变量的一个好例子,其中包含41个不同的类别,例如:

['AskReddit', 'Jokes', 'politics', 'explainlikeimfive', 'gaming']

让我们看下最流行的类别(X.subreddit.value_counts()[:5]):

AskReddit 275667

politics 123003

news 42271

worldnews 40016

gaming 32117

Name: subreddit, dtype: int64

数值变量的一个例子是question_score,可以通过X.question_score.describe()浏览信息:

mean 770.891169

std 3094.752794

min 1.000000

25% 2.000000

50% 11.000000

75% 112.000000

max 48834.000000

Name: question_score, dtype: float64

类别特征编码

类别编码的两个基本方法是独热编码(onehot encoding)和标签编码(label encoding)。独热编码可以通过pandas.get_dummies完成。具备K个类别的变量的编码结果是一个K列的二值矩阵,其中第i列的值为1意味着这项观测属于第i类。

标签编码直接将类别转换为数字。pandas.factorize提供了这一功能,或者,pandas中category类型的列提供了cat.codes。使用标签编码能够保持原本的维度。

还有一些不那么标准的编码方法也值得一试,它们可能可以提升模型的表现。这里将介绍三种方法:

频数编码(count encoding)

labelcount编码

目标编码(target encoding)

频数编码

频数编码使用频次替换类别,频次根据训练集计算。这个方法对离群值很敏感,所以结果可以归一化或者转换一下(例如使用对数变换)。未知类别可以替换为1。

尽管可能性不是非常大,有些变量的频次可能是一样的,这将导致碰撞——两个类别编码为相同的值。没法说这是否会导致模型退化或者改善,不过原则上我们不希望出现这种情况。

def count_encode(X, categorical_features, normalize=False):

print('Count encoding: {}'.format(categorical_features))

X_ = pd.DataFrame()

for cat_feature in categorical_features:

X_[cat_feature] = X[cat_feature].astype(

'object').map(X[cat_feature].value_counts())

if normalize:

X_[cat_feature] = X_[cat_feature] / np.max(X_[cat_feature])

X_ = X_.add_suffix('_count_encoded')

if normalize:

X_ = X_.astype(np.float32)

X_ = X_.add_suffix('_normalized')

else:

X_ = X_.astype(np.uint32)

return X_

让我们编码下subreddit列:

train_count_subreddit = count_encode(X_train, ['subreddit'])

并查看结果。最流行的5个subreddit:

AskReddit 221941

politics 98233

news 33559

worldnews 32010

gaming 25567

Name: subreddit, dtype: int64

编码为:

221941 221941

98233 98233

33559 33559

32010 32010

25567 25567

Name: subreddit_count_encoded, dtype: int64

基本上,这用频次替换了subreddit类别。我们也可以除以最频繁出现的类别的频次,以得到归一化的值:

1.000000 221941

0.442609 98233

0.151207 33559

0.144228 32010

0.115197 25567

Name: subreddit_count_encoded_normalized, dtype: int64

LabelCount编码

我们下面将描述的方法称为LabelCount编码,它根据类别在训练集中的频次排序类别(升序或降序)。相比标准的频次编码,LabelCount具有特定的优势——对离群值不敏感,也不会对不同的值给出同样的编码。

def labelcount_encode(X, categorical_features, ascending=False):

print('LabelCount encoding: {}'.format(categorical_features))

X_ = pd.DataFrame()

for cat_feature in categorical_features:

cat_feature_value_counts = X[cat_feature].value_counts()

value_counts_list = cat_feature_value_counts.index.tolist()

if ascending:

# 升序

value_counts_range = list(

reversed(range(len(cat_feature_value_counts))))

else:

# 降序

value_counts_range = list(range(len(cat_feature_value_counts)))

labelcount_dict = dict(zip(value_counts_list, value_counts_range))

X_[cat_feature] = X[cat_feature].map(

labelcount_dict)

X_ = X_.add_suffix('_labelcount_encoded')

if ascending:

X_ = X_.add_suffix('_ascending')

else:

X_ = X_.add_suffix('_descending')

X_ = X_.astype(np.uint32)

return X_

编码:

train_lc_subreddit = labelcount_encode(X_train, ['subreddit'])

这里默认使用降序,subreddit列最流行的5个类别是:

0 221941

1 98233

2 33559

3 32010

4 25567

Name: subreddit_labelcount_encoded_descending, dtype: int64

AskReddit是最频繁的类别,因此被转换为0,也就是第一位。

使用升序的话,同样这5个类别编码如下:

40 221941

39 98233

38 33559

37 32010

36 25567

Name: subreddit_labelcount_encoded_ascending, dtype: int64

目标编码

最后是最有技巧性的方法——目标编码。它使用目标变量的均值编码类别变量。我们为训练集中的每个分组计算目标变量的统计量(这里是均值),之后会合并验证集、测试集以捕捉分组和目标之间的关系。

举一个更明确的例子,我们可以在每个subreddit上计算answer_score的均值,这样,在特定subreddit发帖可以期望得到多少赞,我们可以有个大概的估计。

使用目标变量时,非常重要的一点是不要泄露任何验证集的信息。所有基于目标编码的特征都应该在训练集上计算,接着仅仅合并或连接验证集和测试集。即使验证集中有目标变量,它不能用于任何编码计算,否则会给出过于乐观的验证误差估计。

如果使用K折交叉验证,基于目标的特征应该在折内计算。如果仅仅进行单次分割,那么目标编码应该在分开训练集和验证集之后进行。

此外,我们可以通过平滑避免将特定类别编码为0. 另一种方法是通过增加随机噪声避免可能的过拟合。

处置妥当的情况下,无论是线性模型,还是非线性模型,目标编码都是最佳的编码方式。

def target_encode(X, X_valid, categorical_features, X_test=None,

target_feature='target'):

print('Target Encoding: {}'.format(categorical_features))

X_ = pd.DataFrame()

X_valid_ = pd.DataFrame()

if X_test isnotNone:

X_test_ = pd.DataFrame()

for cat_feature in categorical_features:

group_target_mean = X.groupby([cat_feature])[target_feature].mean()

X_[cat_feature] = X[cat_feature].map(group_target_mean)

X_valid_[cat_feature] = X_valid[cat_feature].map(group_target_mean)

X_ = X_.astype(np.float32)

X_ = X_.add_suffix('_target_encoded')

X_valid_ = X_valid_.astype(np.float32)

X_valid_ = X_valid_.add_suffix('_target_encoded')

if X_test isnotNone:

X_test_[cat_feature] = X_test[cat_feature].map(group_target_mean)

X_test_ = X_test_.astype(np.float32)

X_test_ = X_test_.add_suffix('_target_encoded')

return X_, X_valid_, X_test_

return X_, X_valid_

编码:

train_tm_subreddit, valid_tm_subreddit = target_encode(

X_train, X_valid, categorical_features=['subreddit'],

target_feature='answer_score')

如果我们查看下编码后的值,就会发现不同reddit的平均赞数有明显的差别:

23.406061 220014

13.082699 98176

19.020845 33916

17.521887 31869

18.235424 25520

21.535477 24692

18.640282 20416

23.688890 20009

3.159401 18695

Name: subreddit_target_encoded, dtype: int64

AskReddit 220014

politics 98176

news 33916

worldnews 31869

gaming 25520

todayilearned 24692

funny 20416

videos 20009

teenagers 18695

Name: subreddit, dtype: int64

AskReddit中的回答平均能有23.4个赞,而politics和teenagers中的回答分别只有13.1个赞。这样的特征可能非常强大,因为它让我们可以在特征集中明确编码一些目标信息。

获取类别的编码值

无需修改编码函数,我们可以通过如下方式在验证集或测试集上合并取得的值:

encoded = train_lc_subreddit.subreddit_labelcount_encoded_descending.value_counts().index.values

raw = X_train.subreddit.value_counts().index.values

encoding_dict = dict(zip(raw, encoded))

X_valid['subreddit_labelcount_encoded_descending'] = X_valid.loc[:,

'subreddit'].map(

encoding_dict)

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编码
    +关注

    关注

    6

    文章

    835

    浏览量

    54456
  • 机器学习
    +关注

    关注

    66

    文章

    8116

    浏览量

    130547
  • 数据集
    +关注

    关注

    4

    文章

    1178

    浏览量

    24348

原文标题:特征工程方法:一、类别变量编码

文章出处:【微信号:jqr_AI,微信公众号:论智】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    存储器的编码方法

    ,上述编码运算包括相加或者相减运算。图2的示意图为。假设第一存储体中的数据“10110”,第二存储体中的
    发表于 11-15 15:44

    求一种准循环LDPC码的快速编码方法

    LDPC码的通用编码方法有哪些?准循环LDPC码的快速编码方法是什么?
    发表于 04-25 07:16

    一种实用的混沌保密编码方法

    基于实用符号动力学的基础理论,提出了一种实用的混沌保密编码方法,该方法借助于单峰的logistic映射处于混沌吸引子状态时产生的符号序列作为密钥,对信源编码信号进行加密
    发表于 11-18 00:17 12次下载

    一种宏块分裂的多描述视频编码方法

    一种宏块分裂的多描述视频编码方法:多描述编码是近年来提出的用于不可靠网络的视频编码方法。本文在块基编码的基础上,提出了一种基于宏块分裂的多描述编码方
    发表于 08-08 08:29 19次下载

    一种实用的混沌保密编码方法

    一种实用的混沌保密编码方法 基于实用符号动力学的基础理论,提出了一种实用的混沌保密编码方法,该方法借助于单峰的logistic映射处于混沌吸引子状态时产生的符号序列
    发表于 11-18 10:55 10次下载

    一类准循环LDPC码的快速编码方法

    简述了LDPC码的研究现状及编码方法。在此基础上分析了目前常用的编码实现方式,并针对一类准循环LDPC码的特点,提出一种更简洁的快速编码算法及设计实现思路。
    发表于 12-02 16:25 22次下载

    十进制数的其它编码方法

    十进制数的其它编码方法  也有用多于4位基2码,如用5位、7位、甚至10位基2码,来表示一个十进制数位的方案。有些属于无权码,有些属于有权码。表2.11给出4种编码
    发表于 10-13 17:16 6988次阅读

    定点小数的编码方法

    定点小数的编码方法  用定点小数引出数值的三种编码(原码、补码和反码)方法是最方便的。   (1) 原码表示法,是用机器数的最高一位代表符号,以下各位
    发表于 10-13 17:19 3082次阅读
    定点小数的<b class='flag-5'>编码方法</b>

    整数的编码方法

    整数的编码方法   与定点小数的三种编码方法类似,整数也可以用原码、补码和反码三种不同的编码方法表示。区别主要表现在:
    发表于 10-13 17:19 5324次阅读

    AVS立体视频编码方法

    提出一种基于AVS(audio video coding standard)的快速立体视频编码方法,对左路参考图像使用AVS编码编码,对右路目标图像同时在时间域和空间域进行预测. 使用两级神经分类器来快速确定预
    发表于 05-14 10:54 36次下载
    AVS立体视频<b class='flag-5'>编码方法</b>

    DNA计算中的单模板编码方法改进研究

    如何避免各种不期望的杂交是DNA 计算以及微阵列技术中的一个关键问题. 为了得到稳定可靠的杂交,必须探索一种可靠的、鲁棒性的编码方法. 单模板编码方法是Arita 提出的另一种模板编
    发表于 08-18 15:24 0次下载
    DNA计算中的单模板<b class='flag-5'>编码方法</b>改进研究

    一种低耦合翻转的数据总线编码方法

    一种低耦合翻转的数据总线编码方法
    发表于 01-07 20:32 2次下载

    一种新的基于素数的XML动态编码方法_田帅

    一种新的基于素数的XML动态编码方法_田帅
    发表于 03-19 11:45 0次下载

    基于多根多树结构的多播传感器网络编码方法_何杏宇

    基于多根多树结构的多播传感器网络编码方法_何杏宇
    发表于 03-19 19:19 0次下载

    改进的分形图像编码方法

    传统图像编码方法一般已成定式,发展潜力不大。分形图像编码方法思想新颖,是极具发展潜力的压缩方法,但分形编码存在编码耗时过长的缺点。本文基于分
    发表于 12-20 13:56 2次下载