规则化

  规则化器缩放单个样本让其拥有单位$L^{p}$范数。这是文本分类和聚类常用的操作。例如,两个$L^{2}$规则化的TFIDF向量的点乘就是两个向量的cosine相似度。

  Normalizer实现VectorTransformer,将一个向量规则化为转换的向量,或者将一个RDD规则化为另一个RDD。下面是一个规则化的例子。

  1. import org.apache.spark.SparkContext._
  2. import org.apache.spark.mllib.feature.Normalizer
  3. import org.apache.spark.mllib.linalg.Vectors
  4. import org.apache.spark.mllib.util.MLUtils
  5. val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
  6. //默认情况下,p=2。计算2阶范数
  7. val normalizer1 = new Normalizer()
  8. val normalizer2 = new Normalizer(p = Double.PositiveInfinity)
  9. // Each sample in data1 will be normalized using $L^2$ norm.
  10. val data1 = data.map(x => (x.label, normalizer1.transform(x.features)))
  11. // Each sample in data2 will be normalized using $L^\infty$ norm.
  12. val data2 = data.map(x => (x.label, normalizer2.transform(x.features)))

  规则化的实现很简单,我们看它的transform方法。

  1. override def transform(vector: Vector): Vector = {
  2. //求范数
  3. val norm = Vectors.norm(vector, p)
  4. if (norm != 0.0) {
  5. //稀疏向量可以重用index
  6. vector match {
  7. case DenseVector(vs) =>
  8. val values = vs.clone()
  9. val size = values.size
  10. var i = 0
  11. while (i < size) {
  12. values(i) /= norm
  13. i += 1
  14. }
  15. Vectors.dense(values)
  16. case SparseVector(size, ids, vs) =>
  17. val values = vs.clone()
  18. val nnz = values.size
  19. var i = 0
  20. while (i < nnz) {
  21. values(i) /= norm
  22. i += 1
  23. }
  24. Vectors.sparse(size, ids, values)
  25. case v => throw new IllegalArgumentException("Do not support vector type " + v.getClass)
  26. }
  27. } else {
  28. vector
  29. }
  30. }

  求范数调用了Vectors.norm方法,我们可以看看该方法的实现。

  1. def norm(vector: Vector, p: Double): Double = {
  2. val values = vector match {
  3. case DenseVector(vs) => vs
  4. case SparseVector(n, ids, vs) => vs
  5. case v => throw new IllegalArgumentException("Do not support vector type " + v.getClass)
  6. }
  7. val size = values.length
  8. if (p == 1) {
  9. var sum = 0.0
  10. var i = 0
  11. while (i < size) {
  12. sum += math.abs(values(i))
  13. i += 1
  14. }
  15. sum
  16. } else if (p == 2) {
  17. var sum = 0.0
  18. var i = 0
  19. while (i < size) {
  20. sum += values(i) * values(i)
  21. i += 1
  22. }
  23. math.sqrt(sum)
  24. } else if (p == Double.PositiveInfinity) {
  25. var max = 0.0
  26. var i = 0
  27. while (i < size) {
  28. val value = math.abs(values(i))
  29. if (value > max) max = value
  30. i += 1
  31. }
  32. max
  33. } else {
  34. var sum = 0.0
  35. var i = 0
  36. while (i < size) {
  37. sum += math.pow(math.abs(values(i)), p)
  38. i += 1
  39. }
  40. math.pow(sum, 1.0 / p)
  41. }
  42. }

  这里分四种情况。当p=1时,即计算一阶范数,它的值为所有元素绝对值之和。当p=2时,它的值为所有元素的平方和。当p == Double.PositiveInfinity时,返回所有元素绝对值的最大值。
如果以上三种情况都不满足,那么按照下面的公式计算。

4.1