차원 축소 - RDD 기반 API
차원 축소 - RDD 기반 API (Dimensionality Reduction)
고려 대상 변수의 수를 줄이는 차원 축소(Dimensionality Reduction) 기법을 소개하는 문서예요. 특이값 분해(SVD)와 주성분 분석(PCA)을 RowMatrix 기반으로 다루며, Python·Scala·Java 예제와 함께 성능 고려 사항까지 확인해 볼게요.
출처: 문서
본문
차원 축소(Dimensionality reduction)는 고려 대상 변수의 수를 줄이는 과정이에요. 원시적이고 잡음이 많은 피처에서 잠재 피처(latent features)를 추출하거나, 구조를 유지하면서 데이터를 압축하는 데 사용할 수 있어요. spark.mllib는 RowMatrix 클래스에 대한 차원 축소를 지원해요.
특이값 분해 (Singular value decomposition, SVD)
특이값 분해(SVD)는 행렬을 세 개의 행렬 $U$, $\Sigma$, $V$로 분해해요. 즉 $A = U \Sigma V^T$ 형태에요. 여기서:
- $U$는 직교 행렬(orthonormal matrix)로, 그 컬럼을 왼쪽 특이 벡터(left singular vectors)라고 해요.
- $\Sigma$는 내림차순의 비음수 대각선을 가진 대각 행렬로, 그 대각선을 특이값(singular values)이라고 해요.
- $V$는 직교 행렬로, 그 컬럼을 오른쪽 특이 벡터(right singular vectors)라고 해요.
대규모 행렬의 경우 보통 완전한 분해가 필요하지 않고 상위 특이값(top singular values)과 그와 연관된 특이 벡터만 필요해요. 이는 저장 공간을 절약하고, 잡음을 제거하며, 행렬의 저랭크(low-rank) 구조를 복구할 수 있어요.
상위 $k$개의 특이값을 유지하면, 그 결과 저랭크 행렬의 차원은 다음과 같아요:
$U$:$m \times k$,$\Sigma$:$k \times k$,$V$:$n \times k$.
성능 (Performance)
$n$이 $m$보다 작다고 가정해요. 특이값과 오른쪽 특이 벡터는 Gramian 행렬 $A^T A$의 고유값과 고유벡터에서 파생돼요. 왼쪽 특이 벡터 $U$를 저장하는 행렬은 사용자가 computeU 파라미터로 요청하면 $U = A (V S^{-1})$로 행렬 곱셈을 통해 계산돼요. 실제 사용할 방법은 계산 비용에 따라 자동으로 결정돼요:
- $n$이 작거나($n < 100$) $k$가 $n$에 비해 큰 경우($k > n / 2$), Gramian 행렬을 먼저 계산한 다음 드라이버에서 로컬로 그 상위 고유값과 고유벡터를 계산해요. 이는 각 executor와 드라이버에서 $O(n^2)$ 저장 공간, 드라이버에서 $O(n^2 k)$ 시간을 사용하는 단일 패스를 필요로 해요.
- 그 외의 경우, $(A^T A) v$를 분산 방식으로 계산해 드라이버 노드의 ARPACK으로 보내 $(A^T A)$의 상위 고유값과 고유벡터를 계산해요. 이는 $O(k)$ 패스, 각 executor에서 $O(n)$ 저장 공간, 드라이버에서 $O(n k)$ 저장 공간을 필요로 해요.
SVD 예제 (SVD Example)
spark.mllib는 RowMatrix 클래스에서 제공되는, 행 방향(row-oriented) 행렬에 대한 SVD 기능을 제공해요.
API에 대한 자세한 내용은 SingularValueDecomposition Python 문서를 참고하세요.
from pyspark.mllib.linalg import Vectors
from pyspark.mllib.linalg.distributed import RowMatrix
rows = sc.parallelize([
Vectors.sparse(5, {1: 1.0, 3: 7.0}),
Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0),
Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0)
])
mat = RowMatrix(rows)
# Compute the top 5 singular values and corresponding singular vectors.
svd = mat.computeSVD(5, computeU=True)
U = svd.U # The U factor is a RowMatrix.
s = svd.s # The singular values are stored in a local dense vector.
V = svd.V # The V factor is a local dense matrix.
전체 예제 코드는 Spark 저장소의 examples/src/main/python/mllib/svd_example.py 에서 찾을 수 있어요. U가 IndexedRowMatrix로 정의되어 있으면 같은 코드가 IndexedRowMatrix에도 적용돼요.
API에 대한 자세한 내용은 SingularValueDecomposition Scala 문서를 참고하세요.
import org.apache.spark.mllib.linalg.Matrix
import org.apache.spark.mllib.linalg.SingularValueDecomposition
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.linalg.distributed.RowMatrix
val data = Array(
Vectors.sparse(5, Seq((1, 1.0), (3, 7.0))),
Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0),
Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0))
val rows = sc.parallelize(immutable.ArraySeq.unsafeWrapArray(data))
val mat: RowMatrix = new RowMatrix(rows)
// Compute the top 5 singular values and corresponding singular vectors.
val svd: SingularValueDecomposition[RowMatrix, Matrix] = mat.computeSVD(5, computeU = true)
val U: RowMatrix = svd.U // The U factor is a RowMatrix.
val s: Vector = svd.s // The singular values are stored in a local dense vector.
val V: Matrix = svd.V // The V factor is a local dense matrix.
전체 예제 코드는 Spark 저장소의 examples/src/main/scala/org/apache/spark/examples/mllib/SVDExample.scala 에서 찾을 수 있어요. U가 IndexedRowMatrix로 정의되어 있으면 같은 코드가 IndexedRowMatrix에도 적용돼요.
API에 대한 자세한 내용은 SingularValueDecomposition Java 문서를 참고하세요.
import java.util.Arrays;
import java.util.List;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.mllib.linalg.Matrix;
import org.apache.spark.mllib.linalg.SingularValueDecomposition;
import org.apache.spark.mllib.linalg.Vector;
import org.apache.spark.mllib.linalg.Vectors;
import org.apache.spark.mllib.linalg.distributed.RowMatrix;
List<Vector> data = Arrays.asList(
Vectors.sparse(5, new int[] {1, 3}, new double[] {1.0, 7.0}),
Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0),
Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0)
);
JavaRDD<Vector> rows = jsc.parallelize(data);
// Create a RowMatrix from JavaRDD<Vector>.
RowMatrix mat = new RowMatrix(rows.rdd());
// Compute the top 5 singular values and corresponding singular vectors.
SingularValueDecomposition<RowMatrix, Matrix> svd = mat.computeSVD(5, true, 1.0E-9d);
RowMatrix U = svd.U(); // The U factor is a RowMatrix.
Vector s = svd.s(); // The singular values are stored in a local dense vector.
Matrix V = svd.V(); // The V factor is a local dense matrix.
전체 예제 코드는 Spark 저장소의 examples/src/main/java/org/apache/spark/examples/mllib/JavaSVDExample.java 에서 찾을 수 있어요. U가 IndexedRowMatrix로 정의되어 있으면 같은 코드가 IndexedRowMatrix에도 적용돼요.
주성분 분석 (Principal component analysis, PCA)
주성분 분석(PCA)은 첫 번째 좌표가 가능한 가장 큰 분산을 갖고, 그다음 좌표들도 각각 가능한 가장 큰 분산을 갖도록 하는 회전(rotation)을 찾는 통계적 방법이에요. 회전 행렬의 컬럼들을 주성분(principal components)이라고 해요. PCA는 차원 축소에서 널리 사용돼요.
spark.mllib는 행 방향 형식으로 저장된 tall-and-skinny 행렬과 모든 Vector에 대한 PCA를 지원해요.
다음 코드는 RowMatrix에서 주성분을 계산하고 이를 사용해 벡터를 저차원 공간으로 투영(project)하는 방법을 보여줘요.
API에 대한 자세한 내용은 RowMatrix Python 문서를 참고하세요.
from pyspark.mllib.linalg import Vectors
from pyspark.mllib.linalg.distributed import RowMatrix
rows = sc.parallelize([
Vectors.sparse(5, {1: 1.0, 3: 7.0}),
Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0),
Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0)
])
mat = RowMatrix(rows)
# Compute the top 4 principal components.
# Principal components are stored in a local dense matrix.
pc = mat.computePrincipalComponents(4)
# Project the rows to the linear space spanned by the top 4 principal components.
projected = mat.multiply(pc)
전체 예제 코드는 Spark 저장소의 examples/src/main/python/mllib/pca_rowmatrix_example.py 에서 찾을 수 있어요.
다음 코드는 RowMatrix에서 주성분을 계산하고 이를 사용해 벡터를 저차원 공간으로 투영하는 방법을 보여줘요.
API에 대한 자세한 내용은 RowMatrix Scala 문서를 참고하세요.
import org.apache.spark.mllib.linalg.Matrix
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.linalg.distributed.RowMatrix
val data = Array(
Vectors.sparse(5, Seq((1, 1.0), (3, 7.0))),
Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0),
Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0))
val rows = sc.parallelize(immutable.ArraySeq.unsafeWrapArray(data))
val mat: RowMatrix = new RowMatrix(rows)
// Compute the top 4 principal components.
// Principal components are stored in a local dense matrix.
val pc: Matrix = mat.computePrincipalComponents(4)
// Project the rows to the linear space spanned by the top 4 principal components.
val projected: RowMatrix = mat.multiply(pc)
전체 예제 코드는 Spark 저장소의 examples/src/main/scala/org/apache/spark/examples/mllib/PCAOnRowMatrixExample.scala 에서 찾을 수 있어요.
다음 코드는 소스 벡터에서 주성분을 계산하고, 연관된 레이블을 유지하면서 벡터를 저차원 공간으로 투영하는 방법을 보여줘요.
API에 대한 자세한 내용은 PCA Scala 문서를 참고하세요.
import org.apache.spark.mllib.feature.PCA
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.rdd.RDD
val data: RDD[LabeledPoint] = sc.parallelize(Seq(
new LabeledPoint(0, Vectors.dense(1, 0, 0, 0, 1)),
new LabeledPoint(1, Vectors.dense(1, 1, 0, 1, 0)),
new LabeledPoint(1, Vectors.dense(1, 1, 0, 0, 0)),
new LabeledPoint(0, Vectors.dense(1, 0, 0, 0, 0)),
new LabeledPoint(1, Vectors.dense(1, 1, 0, 0, 0))))
// Compute the top 5 principal components.
val pca = new PCA(5).fit(data.map(_.features))
// Project vectors to the linear space spanned by the top 5 principal
// components, keeping the label
val projected = data.map(p => p.copy(features = pca.transform(p.features)))
전체 예제 코드는 Spark 저장소의 examples/src/main/scala/org/apache/spark/examples/mllib/PCAOnSourceVectorExample.scala 에서 찾을 수 있어요.
다음 코드는 RowMatrix에서 주성분을 계산하고 이를 사용해 벡터를 저차원 공간으로 투영하는 방법을 보여줘요.
API에 대한 자세한 내용은 RowMatrix Java 문서를 참고하세요.
import java.util.Arrays;
import java.util.List;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.mllib.linalg.Matrix;
import org.apache.spark.mllib.linalg.Vector;
import org.apache.spark.mllib.linalg.Vectors;
import org.apache.spark.mllib.linalg.distributed.RowMatrix;
List<Vector> data = Arrays.asList(
Vectors.sparse(5, new int[] {1, 3}, new double[] {1.0, 7.0}),
Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0),
Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0)
);
JavaRDD<Vector> rows = jsc.parallelize(data);
// Create a RowMatrix from JavaRDD<Vector>.
RowMatrix mat = new RowMatrix(rows.rdd());
// Compute the top 4 principal components.
// Principal components are stored in a local dense matrix.
Matrix pc = mat.computePrincipalComponents(4);
// Project the rows to the linear space spanned by the top 4 principal components.
RowMatrix projected = mat.multiply(pc);
전체 예제 코드는 Spark 저장소의 examples/src/main/java/org/apache/spark/examples/mllib/JavaPCAExample.java 에서 찾을 수 있어요.
더 알아보기 (Learn more)
- 아파치 스파크 차원 축소 (원문)
- MLlib 가이드 (원문) — MLlib 전체 가이드
- ML Dimensionality Reduction — DataFrame 기반 차원 축소