缺失值处理
连续值
一般采用取平均值或者中位数来进行填充。
离散值
最频繁出现的类别(或者众数)来填充。
异常值清洗
异常数据可能给模型带来很大的偏差。
聚类清洗
例如,通过KMeans将训练样本分成若干簇,如果某一个簇里的样本数很少,而且簇质心和其他所有的簇都很远,那么这个簇里面的样本极有可能是异常特征样本。可将其从训练集过滤掉。同样道理还可以使用BIRCH和DBSCAN密度聚类算法。
异常点检测算法
使用iForest或者one-class-SVM来进行过滤所有的异常点。
特殊的特征处理
日期
对于20180519这样的值没办法直接使用。
一般有几种比较有代表性的方法:
- 时间差值法:使用UTC计算到某一天的时间差,从而将其转化为连续值。
- 根据时间的年月日,星期,小时数,将一个时间特征转化为若干个离散特征。
- 权重法:根据时间的新旧设置一个权重值。
地理位置
“成都市锦江区XX街道XX号”,可以转化为离散值和连续值。若是离散值的话,可以转化为多个离散特征,如城市名,区县,街道等。但是如果需要判别用户分布区域,则一般处理成连续值比较好,这时可以转化为经度和维度的连续特征。
特征的标准化和归一化
为什么需要标准化/归一化?
因为特征的数值范围不同的话,会影响梯度下降的速率。如果将目标函数的等值图画出来,数值范围大的特征,那个方向会需要更多次的迭代才能找到最优解。如果将每个特征的范围转化成一致的,那么等值图的图形会是一个圆形,梯度下降在各个方向的速率是一样的,可以更快地找到最优解。
不管是标准化还是归一化,它们的本质都是线性变换,是一个的形式,都是压缩后再进行平移。这样的性质决定了对数据进行改变后不会造成“失效”。从公式也可以看出,只是数据的均值和方差发生了变化,数据具体服从某种分布并没有得到改变。
min-max归一化
缩放仅仅和最大值,最小值的差别有关。在没有归一化之前,每个特征有自己的量纲,假设有两个特征,面积和房间数。因变量为房价。
在没有归一化之前,面积可以为0~1000,房间数为0~10,损失函数的等高线与参数的收敛关系可能是这样的
可以看到未归一化时图像的等高线类似于椭圆形状。归一化后
图像的等高线类似于圆形形状。明显可以看到最优解的寻优过程会变得平缓,更容易正确收敛到最优解。
z-score标准化
缩放和每个点都有关系,具体方法是求出均值和标准差,用(x-mean)/std来代替原特征。这样就变换到了均值为0,方差为1的分布(这里很容易误以为标准化后就符合正态分布,而是原始随机变量服从什么分布,标准化后就服从什么分布,只是均值和方差变了)。sklearn中使用StandardScaler来做z-score标准化。
可以看出,标准化后能够加速损失函数收敛速度。能够更快得出最优参数ω和b。z-score最适用的数据分布是近似高斯分布,其他分布的效果可能效果会变差,比如长尾分布,分布的改变可能会影响数据原有的表现情况。
标准化(z-score)和归一化(min-max)的选择
归一化输出值为0-1,而标准化根据标准差的大小输出值可能为负无穷到正无穷。
- 如果对输出结果范围有要求,就用归一化
- 如果数据较为稳定,不存在极端的最大最小值,就用归一化
- 如果数据存在异常值和较多噪音,就用标准化,可以间接通过中心化避免异常值和极端值的影响
白化 与 批标准化BatchNormalization
它们和z-score标准化有不少共同之处,详细内容可以看这篇文章。
范数标准化
L1范数标准化用,L2范数标准化用。
中心化
在PCA降维时,会涉及到中心化的操作。mean为特征x的均值,对x内的所有数据采用x-mean的操作,此时特征的均值为0。但是方差并不改变。在PCA中,这是因为PCA就是通过方差来进行降维。
小结
什么时候需要做标准化或者归一化?一般来说,标准化和归一化是为了加速收敛服务的,通过梯度下降求解的模型通常是需要归一化的,包括线性回归、逻辑回归、支持向量机、神经网络等模型。对但也有部分类似贝叶斯、CART、随机森林等基于概率分布的模型不用做。
离散特征的连续化处理
类似线性回归、逻辑回归这样的算法无法使用离散特征,所以需要对离散特征进行连续化处理。
序号编码 serial-encoding
通常用于处理类别间具有大小关系的数据,例如成绩,就可以分为低、中、高三档,并且存在“高>中>低”的排序关系。序号编码会按照大小关系对类别型特征赋予一个数值ID,例如高表示3,中表示为2,低表示为1,转换后依然保留了大小关系。
独热编码 one-hot encoding
独热编码是最常见的处理方法,比如一个特征有三个类别a/b/c。那对于a可以编码为1,0,0.b则为0,1,0.c则为0,0,1.sklearn的onehotEncoder可以做这个事情。独热编码在类别过多的情况下可能会发生维度灾难,这对内存是不小的考验,常常结合稀疏编码,不仅能够节省内存,还可以加快计算速度。
二进制编码 binary-encoding
第一步,先用序号编码给每个类别编码;第二步,将类别ID转化为相应的二进制。例如,B型血的ID为2,二进制表示为010。这种方法的优点是得到的结果维数少于独热编码。
特征嵌入 embedding
在NLP学习笔记(四)中提到了嵌入Embedding在深度学习NLP中的应用。其实不仅是NLP,在深度学习的其他应用中,也可以使用这一原理,它的原理都是通过具体的样本,找到在嵌入的特征矩阵中对应的向量。比如用户ID,如果有100万个,那嵌入的特征矩阵的行就是100万,列会取得比较小,比如20,这样就将每个用户ID转化为了一个20维的特征向量。
离散特征的离散化处理
虚拟编码 dummy coding
它的道理和独热编码类似,但是独热编码对所有类别进行编码,如果有N类的话就需要N位编码。而虚拟编码则可以少一位,只需要N-1位即可。比如A/B/C三类,只编码A.B,则10为A,01为B。剩下没有提到的00就默认为C了。
连续特征的离散化处理
通过将连续特征离散化,可能在不通过算法模型的前提下获得一些特征的本质,方便之后一些算法的处理。
阈值法
比如特征的取值范围为0-1,可以根据特征的分位数,将0-0.3设置为高,0.3-0.7设置为中,0.7-1设置为高。
一些高级思路
在LR+GBDT的经典模式中,就是使用GBDT先将连续特征转化为离散特征。比如用所有的连续值和标签输出来训练GBDT,假设最后得到两棵决策树,第一棵树有三个叶子节点,第二棵树有四个叶子节点。如果某个样本在第一棵决策树落到第二个叶子节点,在第二棵决策树落到第四个叶子节点,那么编码就是0,1,0,0,0,0,1。sklearn中可以用GradientBoostingClassifier的apply方法很方便地得到离散化后的特征,然后使用独热编码即可。
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import OneHotEncoder
X, y = make_classification(n_samples=10)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
gbc = GradientBoostingClassifier(n_estimators=2)
one_hot = OneHotEncoder()
gbc.fit(X_train, y_train)
X_train_new = one_hot.fit_transform(gbc.apply(X_train)[:, :, 0])
print (X_train_new.todense())
[[0. 1. 1. 0.]
[1. 0. 0. 1.]
[1. 0. 0. 1.]
[1. 0. 0. 1.]
[0. 1. 1. 0.]]
对于逻辑回归,可以将连续特征和离散特征一同放进模型进行训练,但是前提是离散特征要变化成0,1这样的数值型离散值。
组合特征
为了提高复杂关系的拟合能力,有时会把一阶离散特征两两组合,构成高阶组合特征。以广告点击预估问题为例,原始数据有语言和类型两种离散特征,第一张表是语言和类型对点击的影响。为了提高拟合能力,语言和类型可以组成二阶特征。第二张表示语言和类型的组合特征对点击的影响。
上述的规模只是2x2,但是在数据为用户ID和物品ID时容易出现超大的学习规模(mxn),这是因为在互联网环境下用户数量和物品数量都可以达到千万量级,如果拿LR来作为模型的话,几乎无法学习mxn规模的参数。
如何处理高维组合特征?
在这种情况下,一种行之有效的方法是将用户和物品分别用k维的低维向量表示(k<<m, k<<n),学习的规模变成了mxk+nxk。这其实等价于矩阵分解。至于为什么是这样一个规模,可以在个人总结:推荐算法篇(附协同过滤等) 综述——》基于协同过滤的推荐——》基于模型的协同过滤——》矩阵分解——》FunkSVD(隐含语义模型 Latent Factor Model) 中找到答案。
怎样有效地找到组合特征?
例如通过构建决策树,每一条从根节点到叶节点的路径都可以看成一种特征组合的方式:
(1)“年龄小于等于35”且“性别为女”
(2)“年龄小于等于35”且“物品类别为护肤”
(3)“用户类型=付费”且“物品类型=食品”
(4)“用户类型=付费”且“年龄小于等于40”
有两个样本:
样本一就可以编码成(1,1,0,0),同时满足(1)(2),不满足(3)(4),而样本二可以编码为(0,0,1,1)。
处理不平衡数据
一般是对于分类问题,标签中某一类的样本数量远远超过另外一类的样本数量。这导致训练出的模型在预测另外数量少的那一类别时准确率会很低。
阈值法
类似逻辑回归这样的模型的输出范围为0-1,默认当某个样本输出大于0.5就会被划分为正例,反之为反例。当数据不平衡时可能导致输出全部为反例,产生虚假的高准确度,导致过拟合的出现,分类失败。可以通过调整阈值,使得模型对于较少的类别更为敏感。也可以选择合适的评价标准,比如使用ROC或者F1 score.
权重法
对于每个类别加一个权重class weight。如果该类样本数多,那么它的权重就低,反之权重越高。
采样法
- 过采样(上采样):重复提取类别少的数据,直到两种类别样本数目接近。这种做法很大的缺点就是容易导致对类别少的那一类过拟合。
- 欠采样(下采样):抛弃大部分类别多的那一类数据,使两种类别样本数目接近。这可能导致欠拟合。所以另外一种做法是重复对样本数量多的那一类重复欠采样,这样可以构造多个样本集,分别进行训练,将结果进行集成。
- SMOTE:它和过采样类似,但并不是像过采样那样单纯重复生成类别少的那一类样本。而是通过K-近邻生成新的样本。举个例子,加入候选合成集合里有两个样本(x1,y),(x2,y),那么SMOTE采样后,可以得到一个新的训练样本.但缺点也存在,可能会生成一些异常点。但实验发现大部分和过采样效果相似。
小结
过采样在大部分时候效果比欠采样更好。遇到不平衡的情况时,使用过采样(或者SMOTE)+强正则模型(如XGBoost)也许是个不错的方法,可以先试着作为基准。
处理非结构化数据(这里主要说明文本数据)
处理文本数据有:词袋模型(Bag Of Words)、TF-IDF(Term Frequency-Inverse Document Frequency)、主题模型(Topic Model)、词嵌入模型(Word Embedding)
词袋模型和N-gram模型
将每篇文章看成一袋子词,并忽略每个词出现的顺序,将整段文本以此词为单位切分开,每篇文章可以表示成一个长向量,向量中的每一维代表一个单词,而该维的权重反映这个词在原文章中的重要程度。常用TF-IDF来计算权重,公式为
TF(t,d)为 单词t在文档d中出现的频率,IDF(t)是逆文档频率,用来衡量单词t对表达语义所起的重要性,表示为
直观来说就是若一个单词在非常多的文章里都出现,那么它可能是一个比较通用的词汇,对区分某篇文章特殊语义的贡献较小,因此对权重做一定惩罚。
在NLP学习笔记(一) : 数据预处理(词袋)、NLP学习笔记(二):创建特征及训练(关键词:词袋,TFIDF)分别进行了实战演示。
有时将所有词进行切分不是一种好的做法,如natural language processing一词,所表达的含义与三个词连续出现时大相径庭。因此也可以将连续出现的n个词(n<=N)组成的词组(N-gram)也作为一个单独的特征放到向量表示中去,构成N-gram模型。另外,同一个词可能有多种词性变化,却具有相似的含义。在实际应用中,一般会对单词进行词干抽取(word stemming),将不同磁性的单词统一成为同一词干的单词,例如happy是happiness的词干。
主题模型
主题模型能够计算出每篇文章的主题分布。典型的代表有LDA等。
词嵌入与深度学习模型
词嵌入是一类将词向量化的模型的统称,核心思想是将每个词都映射为低维空间(通常K=50-300维)上的一个稠密向量。K维空间的每一维可以看做一个隐含的主题,只不过不像主题模型中的主题那么直观。
假设每篇文章有N个词,每个词映射为K维的向量,那就可以用一个NxK维的矩阵来表示这篇文章。但是实际中直接输入很难获得令人满意的结果,因此还需要在此基础上加工出更高层的特征。传统浅层机器学习模型中,一个好的特征工程可以带来算法效果的显著提升。而深度学习模型正好为我们提供了一种自动进行特征工程的方式,每个隐层都可以认为对应不同抽象层次的特征。从这个角度来讲深度学习能够打败浅层模型也就顺理成章了。卷积神经网络和循环神经网络一方面很好抓住了文本的特征,另一方面又减少了网络中待学习的参数,提高了训练速度,并且降低了过拟合的风险。
谷歌2013年提出了word2vec,是最常用的词嵌入模型。它实际上是一种浅层的神经网络结构,分别有两种结构,CBOW(continuous bag of words)和skip-gram。
CBOW根据上下文出现的词语来预测当前词的生成概率,w(t)是当前词,w(t-2)、w(t-1)、w(t+1)、w(t+2)是上下文中出现的词:
输入层有上下文词数这么多个神经元,输入层中每个词都由独热编码方式表示,即所有词均表示成一个N维向量,输出层有词汇表大小个神经元,其中N为词汇表中单词的总数。隐藏层的神经元个数可以自己指定,K个隐含单元的取值可以由N维输入向量以及连接输入和隐含单元之间的NxK维权重矩阵计算得到,也就是embedding的维度,这个隐藏层也就是我们的词向量层。在CBOW中,还需要将各个输入词所计算出的隐含单元求和。训练目标是期望训练样本特定词对应的softmax概率最大。通过DNN的反向传播,我们可以训练出DNN模型的参数,同时得到所有词的词向量。这样当我们需要通过上下文预测中心词时,通过一次前向传播以及softmax函数就可以找到概率最大的词对应的神经元。
比如在CBOW中,预测中间出现的一个词,输入8个上下文词,最后输出的是所有词的softmax概率,投影时预测词的词向量为上下文的词向量平均值。
而Skip-gram是根据当前词来预测上下文中各词的生成概率:
对应的输入层只有一个神经元,输出层有N个神经元。隐藏层可以自己指定。同样通过DNN的反向传播算法进行训练,可以求出DNN模型参数,同时得到对应的词向量。这样当有新的需求,通过输入一个词便可以得到概率大小排前8的softmax概率对应的神经元所对应的词即可。
接下来训练权重,使得所有单词的整体概率最大化(极大似然估计),学习权重可以用反向传播算法实现,由于softmax存在的归一化项,推导出来的参数的迭代公式需要对词汇表中所有单词进行遍历。
这使得每次迭代过程非常非常缓慢,由此产生了Hierarchical Softmax(基于霍夫曼树)和Negative Sampling两种改进方法。训练得到维度为NxK和KxN的两个权重矩阵后,可以选择其中一个矩阵作为N个词的K维向量表示。
Word2Vec与LDA的区别和联系?
首先,LDA是利用文档中单词的共现关系来对单词按主题聚类,也可以理解为对“文档-单词”矩阵进行分解,得到“文档-主题”和“主题-单词”两个概率分布。而word2vec其实是对“上下文-单词”矩阵进行学习,其中上下文由周围几个单词组成,由此得到的词向量表示更多地融入了上下文共现的特征。也就是说如果两个单词对应的word2vec向量相似度较高,那它们很可能经常在同样的上下文中出现。
主题模型和词嵌入模型的区别?
主题模型和词嵌入两类方法最大的不同在于模型本身,主题模型是一种基于概率图模型的生成式模型,其似然函数可以写成若干条件概率连乘的形式,其中包括需要推测的隐含变量(即主题);词嵌入模型一般表示为神经网络的形式,似然函数定义在网络的输出之上,需要通过学习网络的权重以得到单词的稠密向量表示。
如何处理涉及图像数据的任务中,训练不足的情况?
在个人总结:CNN、tf.nn.conv2d(卷积)与 tf.nn.conv2d_transpose(反卷积)以及激活函数中,针对如何应对CNN过拟合的问题中有提到。