神刀安全网

Spark MLlib 1.6 — 特征抽取和变换

·  TF-IDF

· Word2Vec

· Model

· Example

· StandardScaler

· Model Fitting

· Example

· Normalizer

· Example

· ChiSqSelector

· Model Fitting

· Example

· ElementwiseProduct

· Example

· PCA

· Example

7.1 TF-IDF

TF-IDF 是一种特征向量化方法,这种方法多用于文本挖掘,通过算法可以反应出词在语料库中某个文档中的重要性。文档中词记为t,文档记为d , 语料库记为D . 词频TF(t,d) 是词t 在文档d 中出现的次数。文档频次DF(t,D) 是语料库中包括词t的文档数。如果使用词在文档中出现的频次表示词的重要程度,那么很容易取出反例,即有些词出现频率高反而没多少信息量, 如,”a” , “the” , “of” 。如果一个词在语料库中出现频率高,说明它在特定文档集中信息量很低。逆文档频次(inverse document frequency)是词所能提供的信息量的一种度量:

IDF(t,D) = log frac { | D | + 1 } { DF(t,D) + 1 }

此处 | D | 是语料库中总的文档数,注意到,公式中使用 log 函数,当词出现在所有文档中时,它的 IDF 值变为 0. IDF 加一个防止在此情况下分母为 0. TF-IDF 度量值表示如下:

TFIDF(t,d,D) = TF(t,d) /Dot  IDF(t,D)

对于 TF IDF 定义有多种, spark.mllib 中,分开定义 TF IDF

Spark.mllib 中实现词频率统计使用特征 hash 的方式,原始的特征通过 hash 函数,映射到一个索引值。后面只需要统计这些索引值的频率,就可以知道对应词的频率。这种方式避免设计一个全局 1 1 的词到索引的映射, 这个映射在映射大量语料库时需要花费更长的时间。但需要注意,通过 hash 的方式可能会映射到同一个值的情况,即不同的原始特征通过 Hash 映射后是同一个值。为了降低这种情况出现的概率,我们只能对特征向量升维。 i.e., hash 表的桶数,默认特征维度是 2^20 = 1,048,576.

注意: spark.mllib 不支持文本分段, 详见 Stanford nlp group http://nlp.stanford.edu/ scalanlp/chalk : https://github.com/scalanlp/chalk

TF 实际是统计词 hash 之后索引值的频次,可使用 HashingTF 方法并传入 RDD[Iterable[_]] IDF 需要使用 IDF 方法。需要注意,每条记录是可 iterable 的字符串或其它类型。

import org.apache.spark.rdd.RDD

import org.apache.spark.SparkContext

import org.apache.spark.mllib.feature.HashingTF

import org.apache.spark.mllib.linalg.Vector

val sc : SparkContext =

// Loaddocuments (one per line).

val documents : RDD [ Seq [ String ]] = sc . textFile ( "…" ). map ( _ . split ( "" ). toSeq )

val hashingTF = new HashingTF ()

val tf : RDD [ Vector ] = hashingTF . transform ( documents )

HashingTF 方法只需要一次数据交互,而 IDF 需要两次数据交互: 第一次计算 IDF 向量,第二次需要和词频次相乘

import org.apache.spark.mllib.feature.IDF

// …continue from the previous example

tf . cache ()

val idf = new IDF (). fit ( tf )

val tfidf : RDD [ Vector ] = idf . transform ( tf )

spark.mllib 支持乎略词频低于文档最小数,需要把 minDocFreq 这个数传给 IDF 构架函数。在此情况下,对应的 IDF 值设置为 0

import org.apache.spark.mllib.feature.IDF

// …continue from the previous example

tf . cache ()

val idf = new IDF ( minDocFreq = 2 ). fit ( tf )

val tfidf : RDD [ Vector ] = idf . transform ( tf )

7.2 Word2Vect ( 词到向量 )

Word2Vec 计算词表征向量的分布,这样可以利用相似相近的词表征分布在邻近的向量空间,好处就是易于产生新型模型,且模型预测的误差也容易解释。向量分布在自然语言处理中是很有用的,特定像命名实体识别,歧义消除,句法分析,词性标记和机器翻译。

7.2.1 模型

Word2vec 的实现中,我们使用 skip-gram 模型。 Skip-gram 的训练目标是学习词表征向量分布,这个分布可以用来预测句子所在的语镜。数学上,给定一组训练词 w_1, … w_T ,skip-gram 模型的目标是最大化平均 log- 似然。

/Frac{1}{T}  /Sigma|_{t= 1} ^{T}  /Sigma|_{j = -k} ^{j = k}  log {p(w_{t+j} | w_{t})}

此处 k 是训练样本窗口。

skip-gram 模型中,每个单词 w 关联两个向量 u_w v_w ,其中 u_w 是单词 w 的向量表示, v_w 是单词对应的语境。对于给定的单词 w_j 计算预测结果的正确概率由以下 softmax 模型。

P(w_i|w_j) = /Frac{exp(u |_{w_i} |^Tv_{w_j})} {/Sigma|_{ l =1} |^V exp( u |_l |^T v_{w_j})}

此处 是词组总数

使用 softmax 计算 skip-gram 模型的很耗时,因为 log{ p(w_i | w_j ) } 正比于 V 的大小 , 并且很容易就达到上百万计算。为了加速 Word2Vec 我们使用分层 softmax , 此方法可以降低计算复杂度,从原来的 log p(w_i | w_j) O(log(V)).

7.2.2 例子

下例子列举如何加载文本文件,将文本内容存放到 RDD[Seq[String]] RDD 构造一个 Word2Vec 实例,将输入数据送入此实例训练得到 Word2VecModel 模型。最终,我们展示特定词的前 40 个同义词。为了运行这个例子,首先下载 text8( http://mattmahoney.net/dc/text8.zip ) 数据,解压到特定的目录下。此处我们假设解压出来的文件还叫 text8 ,并且在当前目录。

import org.apache.spark._

import org.apache.spark.rdd._

import org.apache.spark.SparkContext._

import org.apache.spark.mllib.feature. { Word2Vec , Word2VecModel }

val input = sc . textFile ( "text8" ). map ( line => line . split ( "" ). toSeq )

val word2vec = new Word2Vec ()

val model = word2vec . fit ( input )

val synonyms = model . findSynonyms ( "china" , 40 )

for (( synonym , cosineSimilarity ) <- synonyms ) {

println ( s "$synonym$cosineSimilarity" )

}

// Saveand load model

model . save ( sc , "myModelPath" )

val sameModel = Word2VecModel . load ( sc , "myModelPath" )

7.3 standardscaler 标准化

标准化是通过变化将原始数据放缩到单位方差,通过平移数据得到均值为 0 (如果原数据均值不为 0 ,需要对采样数据求出样本均值,将原始数据减云样本均值,即得到均值为 0 的新数据)。

例如, 支持向量机的 RBF 核, L1 L2 空间的正则线性模型,这两个例子很能说明问题,经过标准化所有特征的计算能得到更好的结果。

标准化后的数据,在最优化过程中会更快的收敛,同时也会在模型训练时防止方差大的数据对整体数据的影响。

7.3.1 模型拟合

标准化需要配置以下参数:

1 withMean 默认是假 (false) 在标准化之前将原始数据以均值为中心,这样会使标准化后的数据分布相对紧密些,这种方法不适合于稀松的数据集,否则会触发异常。

2 withStd 默认是真 (true) , 意味将数据标准化到单位方差。

StandardScaler 中提供一个拟合方法将 RDD[Vector] 作为输入,学习输入的统计信息,将输入集合变换成单位标准差,变换结果可能(也可能不是)均值为 0 ,通过配置 StandardScaler 来实现。

模型支持 VectorTransformer 可以将标准向量变换成新的向量,或者将 RDD[Vector] 变换到新的 RDD[Vector]

如果特征向量某个维度的方差为 0 ,则特征向量这个维度的变换结果仍然是 0.0

7.3.2 例子

下例展示如何加载 libsvm 格式数据,将数据标准化后得到新的向量,此新向量的标准差是 1 ,均值可能(也可能不是) 0

StandardScalerScala docs API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.feature.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

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 ))

// scaler3is an identical model to scaler2, and will produce identical transformations

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

// data1will be unit variance.

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

// Withoutconverting the features into dense vectors, transformation with zero mean willraise

//exception on sparse vector.

// data2will be unit variance and zero mean.

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

7.4 正规化

将个别样本正规化为单位 L^p 范数,   在文本分类和聚类中经常使用。例如, L^2 空间正规化 TF-IDF 向量的点积,可以看作两个向量的 cos- 相似度 .s

正规化可配置参数:

1) p L^p 空间向量正规化,默认 p = 2

模型支持 VectorTransformer 可以将标准向量变换成新的向量,或者将 RDD[Vector] 变换到新的 RDD[Vector]

如果输入向量范数为 0 ,则直接返回输入向量

7.4.1 例子

下例展示如何加载 libsvm 格式数据,将数据正规化为 L^2 范数, L^/{Infinit} 范数

Normalizer ScalaDocs API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.feature.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

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

val normalizer1 = new Normalizer ()

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

// Eachsample in data1 will be normalized using $L^2$ norm.

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

// Eachsample in data2 will be normalized using $L^/infty$ norm.

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

7.5ChiSqSelector(ChiSq 选择器 )

在模型构造阶段,特征选择从特征向量中剔除相关的维度,即对特征空间进行降维,这样可以加速迭代过程,并提升学习效率。

ChiSqSelector 实现基于 chi-squared 的特征选择器,它处理归类特征的类标签, ChiSqSelector 基于 Chi-Squared 检验对特征进行排序,而不直接考虑特征向量的类别,选取排序靠前的特征向量,因为这些特征向量能很好的决定类别标签。这就好比选取对分类有决定意义的特征向量。

在实际中,选取检验集可以优化特征的数量。 (?)

7.5.1 模型拟合

ChiSqSelector 算法配置 numTopFeatures 参数来确定选取排名前多少个特征向量。

拟合方法的输入是归类特征的 RDD[LabeledPoint] ,通过学习统计信息,返回 ChiSqSelectorModel 模型,这个模型可以用于对特征空间进行降维。 这个模型可以处理输入 Vector, 得到降维后的 Vector , 或者对 RDD[Vector] 进行降维。

当然,也可以构造一个特征索引(索引按升序排列) , 对这个索引的数组训练 ChiSqSelectorModel 模型。

7.5.2 例子

下例展现 ChiSqSelector 的基础应用,输入矩阵的每个元素的范围 0 ~ 255

ChiSqSelectorScala Docs : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.feature.ChiSqSelector

importorg.apache.spark.SparkContext._
importorg.apache.spark.mllib.linalg.Vectors
importorg.apache.spark.mllib.regression.LabeledPoint
importorg.apache.spark.mllib.util.MLUtils
importorg.apache.spark.mllib.feature.ChiSqSelector
// Load some data in libsvm format
valdata=MLUtils.loadLibSVMFile(sc,"data/mllib/sample_libsvm_data.txt")
// Discretize data in 16 equal bins since ChiSqSelector requires categorical features
// Even though features are doubles, the ChiSqSelector treats each unique value as a category
valdiscretizedData=data.map{lp=>
  LabeledPoint(lp.label,Vectors.dense(lp.features.toArray.map{x=>(x/16).floor}))
}
// Create ChiSqSelector that will select top 50 of 692 features
valselector=newChiSqSelector(50)
// Create ChiSqSelector model (selecting features)
valtransformer=selector.fit(discretizedData)
// Filter the top 50 features from each feature vector
valfilteredData=discretizedData.map{lp=>
  LabeledPoint(lp.label,transformer.transform(lp.features))
}

7.6 Hadamard 乘积 (ElementwiseProduct)

ElementwiseProduct 对输入向量的每个元素乘以一个权重向量的每个元素,对输入向量每个元素逐个进行放缩。这个称为对输入向量 v 和变换向量 scalingVec 使用 Hadamard product( 阿达玛积 ) 进行变换, 最终产生一个新的向量 。用向量 w 表示 scalingVec ,则 Hadamard product 可以表示为

Vect(v_1, … , v_N)/o Vect(w_1, … , w_N) = Vect(v_1 w_1, … , v_N w_N)

Hamard 乘积需要配置一个权向量 scalingVec

1) scalingVec 变换向量

ElementwiseProduct 实现 VectorTransformer 方法, 就可以对向量乘以权向量,得到新的向量,或者对 RDD[Vector] 乘以权向量得到 RDD[Vector]

7.6.1 例子

下例展示如何对向量进行 ElementwiseProduct 变换

ElementwiseProductScala Docs API  : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.feature.ElementwiseProduct

importorg.apache.spark.SparkContext._
importorg.apache.spark.mllib.feature.ElementwiseProduct
importorg.apache.spark.mllib.linalg.Vectors
// Create some vector data; also works for sparse vectors
valdata=sc.parallelize(Array(Vectors.dense(1.0,2.0,3.0),Vectors.dense(4.0,5.0,6.0)))
valtransformingVector=Vectors.dense(0.0,1.0,2.0)
valtransformer=newElementwiseProduct(transformingVector)
// Batch transform and per-row transform give the same results:
valtransformedData=transformer.transform(data)
valtransformedData2=data.map(x=>transformer.transform(x))

7.7 PCA

PCA 可以将特征向量投影到低维空间,实现对特征向量的降维。

7.7.1 例子

下例展示如何计算特征向量空间的主成分,使用主成分对向量投影到低维空间,同时保留向量的类标签。

PCA Scala DocsAPI : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.feature.PCA

importorg.apache.spark.mllib.regression.LinearRegressionWithSGD
importorg.apache.spark.mllib.regression.LabeledPoint
importorg.apache.spark.mllib.linalg.Vectors
importorg.apache.spark.mllib.feature.PCA
valdata=sc.textFile("data/mllib/ridge-data/lpsa.data").map{line=>
  valparts=line.split(',')
  LabeledPoint(parts(0).toDouble,Vectors.dense(parts(1).split(' ').map(_.toDouble)))
}.cache()
valsplits=data.randomSplit(Array(0.6,0.4),seed=11L)
valtraining=splits(0).cache()
valtest=splits(1)
valpca=newPCA(training.first().features.size/2).fit(data.map(_.features))
valtraining_pca=training.map(p=>p.copy(features=pca.transform(p.features)))
valtest_pca=test.map(p=>p.copy(features=pca.transform(p.features)))
valnumIterations=100
valmodel=LinearRegressionWithSGD.train(training,numIterations)
valmodel_pca=LinearRegressionWithSGD.train(training_pca,numIterations)
valvaluesAndPreds=test.map{point=>
  valscore=model.predict(point.features)
  (score,point.label)
}
valvaluesAndPreds_pca=test_pca.map{point=>
  valscore=model_pca.predict(point.features)
  (score,point.label)
}
valMSE=valuesAndPreds.map{case(v,p)=>math.pow((v-p),2)}.mean()
valMSE_pca=valuesAndPreds_pca.map{case(v,p)=>math.pow((v-p),2)}.mean()
println("Mean Squared Error = "+MSE)
println("PCA Mean Squared Error = "+MSE_pca)

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Spark MLlib 1.6 — 特征抽取和变换

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮