Spark ML을 사용하여 간단하게 ML을 비즈니스 로직에서 활용하고 싶을 때가 있다.
그리고 그 비즈니스 로직에서 ML을 여러 개 사용하고 싶을 때도 물론 있다.
하지만 문제는 이때부터 발생한다.
Spark ML에는 다양한 ML 기능이 RDD 혹은 DataFrame을 이용할 수 있도록 구현되어 있는데, 이 모델을 저장하고 로딩할 때 기본 라이브러리를 이용하면 각 모델마다 지연시간이 발생한다.
load 함수를 호출할 때마다 Spark Task로써 SVMModel, DecisionTreeModel, GradientBoostedTreesModel 등의 모델 로딩이 수행된다.
하지만 이 로딩 과정이라는게 위 언급한 모델에서는 별 것 없다.
결과는 단순히 의사 결정 나무일 뿐이고, GBT 같은 경우는 이 트리가 여러개 있을 뿐이다.
따라서 이러한 모델을 한 번 로딩해 놓고 인-메모리 스토리지에 객체로 저장해두고 사용해도 무방하지만, 근본적으로 더 쉬운 해결책이 있다.
바로 Model에 대한 Description이다.
각 모델들은 toDebugString이라는 모델을 뿌려주는 함수가 존재한다.
예를 들면 아래와 같다.
GradientBoostedTreesModel classifier of depth 2 with 8 nodes
Tree 0:
If (feature 0 <= 1.0)
If (feature 1 <= 2.0)
Predict: -0.356328434
Else (feature 1 > 2.0)
If (feature 1 <= 2.0)
Predict: -0.356328434
Else (feature 1 > 2.0)
Predict: 0.242148432
Else (feature 0 > 1.0)
Predict: 0.32325233
Tree 1:
If (feature 0 <= 1.0)
If (feature 1 <= 2.0)
Predict: -0.356328434
Else (feature 1 > 2.0)
Predict: 0.242148432
Else (feature 0 > 1.0)
Predict: 0.32325233
조금만 살펴보면 단순한 트리 구조를 가지고 있어, 이를 해석하는데 무리가 없음을 알 수 있다(?).
그리고 이 외의 것들도 모델 객체의 생성자 혹은 load, predict 등을 소스코드 내에서 따라가보면 금방 이해할 수 있을 것이다.
그리고 간단하게 구현한 프로토타입을 참고해서 이해해도 좋을 것 같다.
프로토타입은 간단하게 GradientBoostedTreesModel의 예시를 구현하였다.
TreeNode 객체는 일반적으로 의사 결정 나무를 Java에서 구현하는 예시를 참고하였다.
public class TreeNode {
TreeNode nodeRoot;
TreeNode nodeLeft;
TreeNode nodeRight;
int feature;
double threshold;
double predictLeft;
double predictRight;
}
이 노드 구성을 토대로 트리의 각 노드 별 If, Else, Predict에 따라 의사 결정 나무를 초기화해준다.
boolean isLeftLeaf = true;
for (String[] line : model) {
if (line[IDX_CLASS].equals("If")) {
int feature = Integer.parseInt(line[IDX_FEATURE]);
double threshold = Double.parseDouble(line[IDX_THRESHOLD]);
insertFeature(isLeftLeaf, feature, threshold);
isLeftLeaf = true;
} else if (line[IDX_CLASS].equals("Else")) {
if (!isLeftLeaf) moveToEmptyRoot();
isLeftLeaf = false;
} else if (line[IDX_CLASS].equals("Predict")) {
double predict = Double.parseDouble(line[IDX_PREDICT]);
insertPredict(isLeftLeaf, predict);
}
}
이렇게 구성한 의사 결정 나무에 predict 함수를 구현하여 feature 수에 따라 double[]로 Decision 결과를 반환하면 완성이다.
Decision 결과를 반환 시에 유의할 점은 GBT에 있다.
Spark ML 소스코드를 참고해보면 SUM과 VOTE 중 기본 계산 방식인 SUM에서 각 트리 마다의 가중치가 존재함을 확인할 수 있다.
사실 결과값에 각 트리 가중치만 반영해주면 그만이지만 Spark ML 코드 내에서는 BLAS.dot을 이용하여 벡터 내적을 수행한다.
BLAS.dot(new DenseVector(predictVec), new DenseVector(treeWeights));
이를 참고하여 Spark ML을 적용하면 모델 로딩에 따른 시간적 피해를 최소화할 수 있다.
'Development > Spark' 카테고리의 다른 글
Spark Streaming vs Structured Streaming 비교 체험(?) (0) | 2019.05.12 |
---|---|
Spark 공부 - Spark Streaming (0) | 2019.01.24 |
Spark 공부 - 구조, RDD, Storage Level (0) | 2019.01.24 |