item
우선, 회사에서 데이터 모델링 작업을 하다가 라벨이 심각하게 불균등하고, 게다가 데이터 정보량도 거의 없어 모델링이 필요한가? rule 기반으로 분류 작업을 하면 안될까??? 라는 고민을 하다가 현재 데이터에서 주성분 몇개 빼고 정보량이 없는 상태에서 모델간의 성능 비교를 해보자는 취지로 진행하였다.
그냥 주성분 전부 다써서 기본 로지스틱 모델만 돌려도 성능이 꽤나오는 깔끔한 데이터니 해보실분들은 따로 해보길 권한다.
1. ML용 Input 데이터 구성
우선, Pyspark ML을 이용하기 위해서는 vectorassembler을 이용해 머신러닝 알고리즘용 featurevector를 구성한다. feature가 dense vector 또는 sparse vector로 구성되는데 리소스에 따라 효율이 좋은 벡터 형태로 자동 구성해준다.
- 참고
https://sikmulation.tistory.com/45
아래와 같이 벡터
from pyspark.ml import Pipeline
from pyspark.ml.feature import VectorAssembler
# 분류모델
from pyspark.ml.classification import LogisticRegression
# 튜닝
from pyspark.ml.tuning import ParamGridBuilder, TrainValidationSplit
from pyspark.ml.tuning import CrossValidator
# metric
from pyspark.ml.evaluation import MulticlassClassificationEvaluator, BinaryClassificationEvaluator
un_cols = ["Class", "V1", "V2","V3", "V4", "V5", "V6", "V7", "V8", "V9","V10", "V11" ,"V12", "V13", "V14", "V15", "V16", "V17", "V18", "V19"]
input_col = data.drop(*un_cols).columns
assembler = VectorAssembler(inputCols = input_col, outputCol = "features", handleInvalid = "keep")
Input_df = assembler.transform(data).select("features", "Class")
Input_df.printSchema()
root
|-- features: vector (nullable = true)
|-- Class: integer (nullable = true)
2. train/test 데이터
데이터를 train, test 용으로 임의로 7:3로 나눴다. 오버샘플링이나 언더샘플링 등 데이터 불균형에 대한 처리는 아무것도 안했다. 일단은 그냥 한번 진행 해보자.
train, test = Input_df.randomSplit([0.7, 0.3], seed = 1234)
train.groupBy("Class").count().show()
+-----+------+
|Class| count|
+-----+------+
| 1| 356|
| 0|199138|
+-----+------+
test.groupBy("Class").count().show()
+-----+-----+
|Class|count|
+-----+-----+
| 1| 136|
| 0|85177|
+-----+-----+
3.모델(Test, CV)
로지스틱 모형에서 하이퍼 파라미터를 5차 교차검증으로 튜닝해 테스트하는 코드이다. 여기서 https://sikmulation.tistory.com/51
# 모델 선언
lr = LogisticRegression(labelCol = "Class", featuresCol = "features")
# 파라미터 그리드 설정
paramGrid = ParamGridBuilder().addGrid(lr.regParam, [0.0, 1.0])\
.addGrid(lr.maxIter, [100])\
.addGrid(lr.elasticNetParam, [0.1, 0.25, 0.5, 0.75, 0.9])\
.build()
# metricName: areaUnderROC, areaUnderPR
evaluator = BinaryClassificationEvaluator(labelCol = "Class", metricName = "areaUnderPR")
# 5fold - cv
cv = CrossValidator(
estimator = lr,
estimatorParamMaps = paramGrid,
evaluator = evaluator,
numFolds = 5
)
# cv
model2 = cv.fit(train)
model_prediction2 = model2.transform(test)
model_prediction2.groupBy("Class", "prediction").count().show()
+-----+----------+-----+
|Class|prediction|count|
+-----+----------+-----+
| 1| 0.0| 150|
| 0| 0.0|85148|
| 0| 1.0| 1|
+-----+----------+-----+
4.모델성능 평가
모델의 성능평가 하는 코드이다. 어차피 언더피팅으로 모델이 제대로 학습될 일이 없다. 왜??? 데이터의 정보량이 굉장히 부족하니까. 이런 상황이 feature를 직접 구성해서 모델을 돌려야 하는 경우에는 다반사다. 그래서 확률값 기준으로 어떻게 모델을 평가할 지 고민을 헀고, 아래 코드는 상위 10% 확률값에서 성능 지표의 각각 값에 대해 뽑는 함수이다.
def get_perform(data):
global f1score, lift
print("quantile","|","cutoff", "|","lift" ,"|", "Recall", "|", "Precision", "|", "f1score")
n_percent = [i / 100 for i in range(99, 89, -1)]
data = data.withColumn("prob", vector_to_array("probability").getItem(1))
pop = data.filter(col("Class") == '1').count()/data.count()
for i in range(0,len(n_percent)):
model_prob = data.approxQuantile("prob", [n_percent[i]], 0.0001)
model_result = data.filter(col("prob") >= model_prob[0])
model_numerator = model_result.filter(col("Class") =='1').count()/model_result.count()
lift = model_numerator/pop
#calculate accuracy, sensitivity, specificity and precision
cm_dt_result = data.withColumn("nw_prod", when(col("prob") > model_prob[0], 1).otherwise(0))
cm_dt_result = cm_dt_result.crosstab("nw_prod", "Class")
cm_dt_result = cm_dt_result.toPandas()
cm_dt_result.sort_values(by = ['nw_prod_Class'])
TP = cm_dt_result["1"][0]
FP = cm_dt_result["0"][0]
TN = cm_dt_result["0"][1]
FN = cm_dt_result["1"][1]
Accuracy = (TP+TN)/(TP+FP+TN+FN) # 정확도
Recall = TP/(TP+FN) # 정밀도(Sensitivity), 민감도
Specificity = TN/(TN+FP) # 특이도
Precision = TP/(TP+FP) # 정밀도
f1score = 2 * Recall * Precision / (Recall+Precision) #f1score
print(round((1- n_percent[i]) * 100, 4), "%", "|", "%0.6f" %model_prob[0], "|", "%0.6f" %lift, "|", "%0.6f" %Recall, "|", "%0.6f" %Precision, "|", "%0.6f" %f1score)
return f1score, lift
quantile | cutoff | lift | Recall | Precision | f1score
1.0 % | 0.007434 | 18.666448 | 0.186667 | 0.032864 | 0.055888
2.0 % | 0.005657 | 13.309772 | 0.266667 | 0.023419 | 0.043057
3.0 % | 0.004877 | 9.992073 | 0.300000 | 0.017578 | 0.033210
4.0 % | 0.004384 | 8.323478 | 0.333333 | 0.014641 | 0.028050
5.0 % | 0.004030 | 7.058309 | 0.353333 | 0.012415 | 0.023987
6.0 % | 0.003754 | 6.214863 | 0.373333 | 0.010931 | 0.021240
7.0 % | 0.003544 | 5.613341 | 0.393333 | 0.009873 | 0.019262
8.0 % | 0.003347 | 5.164336 | 0.413333 | 0.009083 | 0.017775
9.0 % | 0.003191 | 4.736366 | 0.426667 | 0.008330 | 0.016341
10.0 % | 0.003051 | 4.333283 | 0.433333 | 0.007621 | 0.014979
(0.014978684180205095, 4.3332825322391555)
'Data Science > 머신러닝' 카테고리의 다른 글
로지스틱 회귀모형(logistic regression)에서 변수 중요도(feature importance) (0) | 2023.08.29 |
---|---|
엘라스틱 넷 패널티를 활용한 서포트 벡터 머신의 변수선택법 (0) | 2023.07.10 |
[머신러닝] ROC 커브, PR 커브 모델 성능 평가시 무엇을 언제 쓸까? (0) | 2023.06.27 |
[PySpark] 5폴드 교차검증(5-fold crossvalidation) 과 모델선택 (0) | 2023.06.27 |
오버피팅(overfitting)과 언더피팅(underfitting)이 뭐길래? (0) | 2023.06.27 |