《Spark机器学习进阶实战》——2.2.4 特征处理

举报
华章计算机 发表于 2019/05/31 01:11:40 2019/05/31
【摘要】 本书摘自《Spark机器学习进阶实战》——书中的第2章,第2.2.4节,作者是马海平、于俊、吕昕、向海。

2.2.4 特征处理

数据预处理对数据进行了初步的抽取和清洗,更进一步,可以从数据中提取有用的特征用于机器学习建模,接下来介绍数据特征处理的方法。

在数据分析中,我们把数据对象称作样本,数据对象拥有一些基本特性,叫作特征或维度。例如,对于一个学生信息的样本数据,每一个样本对应一个学生,而数据集中学生的ID、年级、成绩等则是学生的特征。

1. 特征向量化

除了基本的统计分析之外,机器学习模型要求输入特征向量,原始特征需要转化为特征向量,才能用于机器学习模型的训练,下面介绍各类特征向量化的方法。

常用特征包括数值特征、类别特征、文本特征、统计特征等。

1)数值特征:数值类型的特征,如年龄、温度等,一般可以直接作为特征向量的维度使用,它可以取无穷多的值。

2)类别特征:具有相同特性的特征,如一幅图片的颜色(黑色、棕色、蓝色等)就属于类别特征,类别特征有可穷举的值。类别特征不能直接使用,一般对类别特征进行编号,将其转化为数值特征。

3)文本特征:从文本内容中提取出来的特征,如电影评论,文本特征也不能直接使用,需要进行分词、编码等处理,接下来会具体介绍文本特征的处理方法。

4)统计特征:从原始数据中使用统计方法得到的高级特征,常用的统计特征包括平均值、中位数、求和、最大值、最小值等。统计特征比原始特征包含更多的信息,通常使用统计特征可以得到更好的模型训练效果,统计特征和数值特征一样可以直接作为特征向量的维度使用。

5)其他特征:还有一些特征不属于上述特征的范畴,如音频、视频特征,地理位置特征等。这些特征需要使用特殊的处理方法,如图像需要转化为SIFT特征,音频需要转化为MFCC特征等。

实践中,类别特征的可取值比数值特征要少得多,在进行统计分析时更容易处理,所以我们有时需要通过分段,把数值特征转化为类别特征以便于分析建模,例如我们可以把连续的身高特征分成150cm以下、150cm~180cm、180cm以上三个类别。

2. 文本特征处理

文本特征是一类常见的特征,相比类别特征和数值特征,它的处理要复杂得多,一般对文本特征的处理,需要经过分词、去停用词、词稀疏编码等步骤,MLlib为这些处理步骤提供了相应的方法。

(1)分词

MLlib提供Tokenization方法对文本进行分词,RegexTokenizer基于正则表达式匹配提供了更高级的分词。默认用多个空格(\s+)作为分隔符,可以通过参数pattern指定分隔符,分词的样例代码如下:

import org.apache.spark.ml.feature.{RegexTokenizer, Tokenizer}

val sentenceDataFrame = spark.createDataFrame(Seq(

  (0, "Hi I heard about Spark"),

  (1, "I wish Java could use case classes"),

  (2, "Logistic,regression,models,are,neat")

)).toDF("label", "sentence")


val tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words")

val regexTokenizer = new RegexTokenizer()

  .setInputCol("sentence")

  .setOutputCol("words")

  .setPattern("\\W") // alternatively .setPattern("\\w+")

   .setGaps(false)


val tokenized = tokenizer.transform(sentenceDataFrame)

tokenized.select("words", "label").take(3).foreach(println)


val regexTokenized = regexTokenizer.transform(sentenceDataFrame)

regexTokenized.select("words", "label").take(3).foreach(println)

(2)去停用词

停用词是那些需要从输入数据中排除掉的词,这些词出现频繁,却并没有携带太多有意义的信息。MLlib提供StopWordsRemover方法实现这一功能。停用词表通过stopWords参数来指定。可以通过调用loadDefaultStopWords(language:string)调用默认的停用词表,默认词表提供Jenglish、french、germon、danish等几种语言的停用词,但对于中文停用词需要自己提供。代码示例如下:

import org.apache.spark.ml.feature.StopWordsRemover


val remover = new StopWordsRemover()

  .setInputCol("raw")

  .setOutputCol("filtered")


val dataSet = spark.createDataFrame(Seq(

  (0, Seq("I", "saw", "the", "red", "baloon")),

  (1, Seq("Mary", "had", "a", "little", "lamb"))

)).toDF("id", "raw")


remover.transform(dataSet).show()

(3)词稀疏编码

分词和去停用词之后把一篇文章变成了一个词的集合,现在需要把这个集合用数值来表示,我们使用MLlib提供的StringIndexer方法来实现这一需求。StringIndexer给每个词按照出现频率安排一个编号索引,索引的范围是[0,vocab_size),vocab_size为词表的大小,示例代码如下:

import org.apache.spark.ml.feature.StringIndexer


val df = spark.createDataFrame(

  Seq((0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"), (5, "c"))

).toDF("id", "category")


val indexer = new StringIndexer()

  .setInputCol("category")

  .setOutputCol("categoryIndex")


val indexed = indexer.fit(df).transform(df)

indexed.show()

此外,MLlib还为文本处理提供了Ngram、TF/IDF、word2vec等高级方法,可以在实践中查看相关资料。

3. 特征预处理

在前面的章节中我们介绍了对各类特征进行处理的方法,在使用生成的特征进行训练之前,对特征进行预处理有助于优化模型训练效果,提升模型训练速度。MLlib提供了丰富的特征预处理方法,下面介绍3种最常用的特征预处理方法。

(1)特征归一化

特征归一化是用来统一特征范围的方法,它对特征进行标准化,将特征值的大小映射到一个固定的范围内,从而避免特征量级差距过大影响模型训练的情形,此外特征归一化还能加速训练的收敛。

MLlib提供3种归一化方法:StandardScaler、MinMaxScaler和MaxAbsScaler。Standard-Scaler对所有数据减去均值除以标准差,处理后的数据均值变为0,标准差变为1;MinMax-Scaler将每个特征调整到一个特定的范围(通常是[0,1]),在转化过程中可能把0转化为非0的值,因此可能会破坏数据的稀疏性;MaxAbsScaler转换将每个特征调整到[-1,1]的范围,它通过每个特征内的最大绝对值来划分,不会破坏数据的稀疏性。

其中,StandardScaler是使用最广泛的归一化方法,使用StandardScaler方法进行特征归一化的示例代码如下。

import org.apache.spark.SparkContext._

import org.apache.spark.mllib.feature.StandardScaler

import org.apache.spark.mllib.linalg.Vectors

import org.apache.spark.mllib.util.MLUtils

// Spark程序data文件夹下的测试数据

val data = MLUtils.loadLibSVMFile(sc, "data/MLlib/sample_libsvm_data.txt")

val scaler1 = new StandardScaler().fit(data.map(x => x.features))

val scaler2 = new StandardScaler(withMean = true, withStd = true).fit(data.map(x => x.features))

// scaler3是与scaler2相同的模型,并且会产生相同的转换

val scaler3 = new StandardScalerModel(scaler2.std, scaler2.mean)

// data1是单位方差

val data1 = data.map(x => (x.label, scaler1.transform(x.features)))

//如果不将这些特征转换成密度向量,那么零均值转换就会增加。稀疏向量例外

// data2将是单位方差和零均值

val data2 = data.map(x => (x.label, scaler2.transform(Vectors.dense(x.features.toArray))))

(2)正则化

正则化是指计算某个特征向量的p-范数(p=0,范数是指向量中非零元素的个数;p=1,范数为绝对值之和;p=2,范数是指通常意义上的模;p=无穷,范数是取向量的最大值),然后对每个元素除以p-范数,以将特征值正则化。正则化后不仅可以加快梯度下降求最优解的速度,还能提高模型精度。

MLlib提供Normalizer方法实现特征正则化,示例代码如下:

import org.apache.spark.SparkContext._

import org.apache.spark.MLlib.feature.Normalizer

import org.apache.spark.MLlib.linalg.Vectors

import org.apache.spark.MLlib.util.MLUtils

// Spark程序data文件夹下的测试数据

val data = MLUtils.loadLibSVMFile(sc, "data/MLlib/sample_libsvm_data.txt")

// 默认情况下,p=2,计算2阶范数

val normalizer1 = new Normalizer()

// p正无穷范数

val normalizer2 = new Normalizer(p = Double.PositiveInfinity)

// data1中的每个样本将使用L2范数进行标准化

val data1 = data.map(x => (x.label, normalizer1.transform(x.features)))

// data2中的每个样本将使用无穷范数进行标准化

val data2 = data.map(x => (x.label, normalizer2.transform(x.features)))

(3)二值化

二值化是一个将数值特征转换为二值特征的处理过程,根据一个阈值将数值特征分为两类,值大于阈值的特征二值化为1,否则二值化为0。二值化能够大大减少特征的复杂度,提高训练效率,但在二值化的过程中损失了一些信息,这可能会影响训练的效果,二值化的代码示例如下:

import org.apache.spark.ml.feature.Binarizer


val data = Array((0, 0.1), (1, 0.8), (2, 0.2))

val dataFrame = spark.createDataFrame(data).toDF("label", "feature")


val binarizer: Binarizer = new Binarizer()

  .setInputCol("feature")

  .setOutputCol("binarized_feature")

  .setThreshold(0.5)


val binarizedDataFrame = binarizer.transform(dataFrame)

val binarizedFeatures = binarizedDataFrame.select("binarized_feature")

binarizedFeatures.collect().foreach(println)

若训练数据的指标维数太高,则容易造成模型过拟合,且相互独立的特征维数越高,在测试集上达到相同的效果表现所需要的训练样本的数目就越大。此外,指标数量过多,训练、测试以及存储的压力也都会增大,可运用主成分分析等手段,以Spark为工具对数据集进行计算降维。

2


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。