알 수 없는 프로젝트

신현호·2022년 5월 24일
1

프로젝트

목록 보기
1/1
post-thumbnail

흉포한 포식자가 초록색 피식자들을 잡아먹고 있습니다.

피식자들의 목표는 30초동안 살아남는 것입니다.

이번 라운드에서는 10마리 중 2마리가 살아남았네요.

곧 있으면 다음 라운드가 시작될텐데요, 우리 불쌍한 피식자들에게 생존을 위한 힌트를 줄 방법은 없을까요?

이를 위해 여태까지 진행되었던 10000개의 라운드에서 다음 정보들을 수집했습니다.

predator_r (포식자 반지름. pixel)
predator_pos_x (포식자 초기 x 위치. pixel)
predator_pos_y (포식자 초기 y 위치. pixel)
predator_vel_x (포식자 초기 x 속력. pixel/sec)
predator_vel_y (포식자 초기 y 속력. pixel/sec)

prey_r (피식자 반지름. pixel)
prey_pos_x (피식자 초기 x 위치. pixel)
prey_pos_y (피식자 초기 y 위치. pixel)
prey_vel_x (피식자 초기 x 속력. pixel/sec)
prey_vel_y (피식자 초기 y 속력. pixel/sec)

survived (피식자의 30초간 생존 여부. boolean)

한 라운드에는 포식자 하나와 피식자 10마리가 포함되므로 총 10만개의 데이터가 수집되었고 이 데이터를 바탕으로 포식자와 피식자의 정보를 통해 피식자의 생존 여부를 예측하는 모델을 만들어보려고 합니다.

현재 조건에서 산다, 죽는다를 알려준다면 피식자가 자신의 반지름, 위치, 그리고 속력을 정하는데에 도움이 되겠죠.


데이터 쪼개기 (train, val, test)

모델을 만들기 위해 데이터를 학습용(train), 검증용(val), 테스트용(test) 세 그룹으로 나눕니다.
학습용은 말그대로 학습에 이용할 데이터이고 검증용, 테스트용은 모델의 성능을 평가하기 위한 데이터입니다.

여기서 검증용 데이터로 평가된 모델의 성능이 나쁠 경우, 해당 모델은 버려집니다. 즉, 검증용 데이터는 간접적으로 학습에 관여합니다.

그러나 테스트용 데이터로 평가된 모델의 성능이 나쁘다해도 해당 모델은 버려지지 않습니다. 즉, 테스트용 데이터는 간접적으로도 학습에 관여하지 않습니다.

이렇게 평가용 데이터를 두 그룹으로 나눈 이유는 모델이 처음보는 데이터에 대해서도 좋은 예측 성능을 낼 수 있도록 하기 위해서입니다.
만약 검증용 없이 테스트용 데이터 한 그룹만 둔다면 테스트 데이터로 측정된 모델의 성능이 나빴을 때 어떻게 할 도리가 없습니다.
그냥 놔두자니 성능이 마음에 들지 않고, 모델을 버리자니 테스트 데이터가 간접적으로 학습에 영향을 준 셈이 되어 모델의 처음보는 데이터에 대한 예측 능력을 장담할 수 없을 테니까요.


문제 난이도 파악 (기준모델)

본격적으로 모델을 만들기 전에 먼저 풀려는 문제의 난이도를 파악합니다.
대강 예측해도 맞추기 쉬운 문제라면 난이도가 낮은 것이겠죠.

학습용 데이터를 살펴보니 생존률이 전체의 약 39% 이므로 그냥 무조건 죽는다고 대강 예측해볼 수 있겠네요.

그렇게 했을 때의 예측 정확도는 약 61% 입니다.

선택지가 두 개밖에 없는 것을 고려했을 때 그리 쉬운 문제는 아니죠.


모델의 선택 기준

이제 본격적으로 모델을 만듭니다.
선택 가능한 모델은 굉장히 다양한데요, 이 중에 어떤 모델을 고를지 결정하려면 기준이 필요합니다. 그 기준은 바로 모델의 예측 성능입니다.

그런데 예측 성능을 측정하는 방법도 여러가지라 그 중에서 현재 상황에 가장 적절한 것을 골라야 합니다.

이를 위해서는 다음 질문에 답하는 것이 큰 도움이 됩니다.

죽는다고 예측했는데 사는 경우 vs 산다고 예측했는데 죽는 경우

어느 쪽이 더 심각한 오류인가?

피식자의 입장에서 생각해보겠습니다.
만약 모델이 죽는다고 예측한다면 피식자는 자신의 초기 위치나 속력을 바꿀 것입니다. 그런데 만약 예측이 잘못된 것이었다면 피식자는 괜한 일을 벌여 자신의 죽을 확률을 높인 셈이 됩니다.
따라서 "죽는다고 예측했는데 사는 경우"는 꽤나 심각한 오류입니다.

다음으로 모델이 산다고 예측한다면 피식자는 자신의 초기 위치나 속력을 바꾸지 않을 것입니다. 그런데 만약 예측이 잘못된 것이었다면 피식자는 꼼짝없이 죽게 됩니다.
따라서 "산다고 예측했는데 죽는 경우"는 정말 심각한 오류입니다.

사실 후자가 더 심각해보이긴 하지만 전자도 심각하지 않다고 말할 수는 없습니다. 따라서 두 오류를 동시에 고려하는 성능 평가 지표인 f1을 사용하는 것이 적절합니다.


전처리 및 모델 선정

조금 더 나은 성능을 위해 다음과 같은 전처리를 진행했습니다.

  1. 시작하자마자 죽는 데이터 제거
  1. 특성 공학

    predator_v=(predator_vel_x)2+(predator_vel_y)2predator\_v = \sqrt{(predator\_vel\_x)^2 + (predator\_vel\_y)^2}
    (포식자 속력)

    prey_v=(prey_vel_x)2+(prey_vel_y)2prey\_v = \sqrt{(prey\_vel\_x)^2 + (prey\_vel\_y)^2}
    (피식자 속력)

    angle_dif=arctan(predator_vel_ypredator_vel_x)arctan(prey_vel_yprey_vel_x)angle\_dif = |\arctan(|\dfrac{predator\_vel\_y}{predator\_vel\_x}|)-\arctan(|\dfrac{prey\_vel\_y}{prey\_vel\_x}|)|
    (포식자와 피식자의 진행방향이 이루는 각도)

    init_dist=(predator_pos_xprey_pos_x)2+(predator_pos_yprey_pos_y)2init\_dist = \sqrt{(predator\_pos\_x-prey\_pos\_x)^2 + (predator\_pos\_y-prey\_pos\_y)^2}
    (포식자와 피식자의 초기 거리)

이제 다양한 모델을 만들고 학습용, 검증용 데이터를 이용하여 모델의 f1 score 를 계산한 후 이를 비교하여 최적의 모델을 찾습니다.

제가 찾은 최적의 모델은 RandomForestClassifier 에 하이퍼파라미터

class_weight='balanced',
max_depth=14,
min_samples_leaf=30

을 준 모델입니다.

이 모델의 검증용 데이터에 대한 정확도는 0.64348, f1 score 는 0.57134 입니다.
또한 테스트 데이터에 대한 정확도는 0.6405, f1 score 는 0.5646 입니다.
여러 모델 중 가장 나은 것을 고른 것인데 성능이 그리 좋지는 않습니다.
정확도가 기준모델에 비해 3% 밖에 개선되지 않았죠.
피식자에게는 좋지 않은 소식이네요.


생존 전략

이제 각 정보가 예측에 어떤 영향을 미쳤는지 알아보겠습니다.
예를 들어 반지름이 크면 생존률이 떨어지더라는 정보가 있다면 피식자에게 큰 도움이 되겠죠.

다음은 예측에 사용된 각 feature 에 대한 shap value 를 시각화한 그림입니다.

여기서 가로축은 살아남을 확률에 미친 영향이고 색깔은 각 feature의 값을 의미합니다.

구체적으로 predator_radius 에서 왼쪽에 빨간 점이 많은데 이는 predator_radius 가 크면(빨간색) 살아남을 확률이 적어진다(왼쪽)는 것을 의미합니다.

같은 방식으로 해석하면 다음과 같은 결론을 얻을 수 있습니다.

우리 모델은 다음과 같이 판단한다.

1. 반지름이 클 수록 생존에 불리
2. 속력이 빠를수록 생존에 불리
3. 초기 거리가 멀수록 생존에 유리
4. 진행방향 각도 차이가 작을수록 생존에 유리

피식자가 살아남기 위해서는 반지름과 속력을 줄이고 포식자와 최대한 떨어져서 포식자와 같은 방향으로 움직이는 것이 유리하겠네요.
다만 모델의 성능이 높지 않기에 이것이 훌륭한 생존 전략이 될지는 미지수입니다.


개선 사항

개인적으로 30초 동안의 생존 여부 대신 잡아먹힐때까지 걸린 시간을 수집했다면 조금 더 나은 성능의 모델을 만들 수 있지 않았을까 하는 아쉬움이 남습니다.





피식자 파이팅...!

profile
수학요정니모

0개의 댓글