이번 글에서는 평소 게임을 좋아하는 제가, 직접 기획부터 데이터 추출 및 분석, 엔지니어링까지 진행했던 Elo-rating 프로젝트에 대해서 이야기해보려고 합니다. 원래는 체스나 스타크래프트 같은 경쟁 게임에서 실력 평가를 위해 만들어진 방식이지만, 이 원리를 잘 활용하면 교육 콘텐츠에서도 문제 난이도나 학생의 실력을 정확히 평가하는 데 사용할 수 있습니다.
기획 배경
Elo-rating을 도입하려고 했던 배경은, 이전까지 회사 내부에서는 학생과 문제 수준 평가를 대부분 주관적 판단(선생님이나 콘텐츠 제작자의 직관)에 의존해왔기 때문입니다. 이런 방식은 정확한 데이터 기반의 평가가 불가능하고, 무엇보다 일관성이 떨어졌습니다.
그래서 저는 Elo-rating을 활용하면 학생과 문제의 관계를 수치화하여 보다 정확하고 객관적인 정보를 얻을 수 있을 뿐만 아니라, 이를 기반으로 다양한 추천 모델, 문제 예측 모델, 랭킹 기반 게임화 등의 기능까지 확장할 수 있을 것으로 기대했습니다.
Elo-rating의 기본 개념
Elo-rating이란 본래 체스 선수들의 상대적 실력을 평가하기 위해 고안된 평가 방식입니다. 기본 원리는 단순히 승패만 보는 것이 아니라, 두 선수 간의 예상 승률을 산정하고, 실제 결과와의 차이를 통해 점수를 업데이트하는 방식입니다.
새로운 레이팅=기존 레이팅+K×(실제 결과−예상 승률)
•
새로운 레이팅: 경기가 끝난 후 업데이트된 Elo-rating 점수
•
기존 레이팅: 경기가 시작되기 전의 Elo-rating 점수
•
K(가중치): 점수의 변동 폭을 결정하는 값으로, 일반적으로 20~50을 사용하며, K 값이 클수록 점수 변동폭이 커집니다
변동 폭이 커집니다.
•
실제 결과: 승리(1), 무승부(0.5), 패배(0)로 표현됩니다.
•
예상 승률: 두 참가자의 기존 Elo-rating 차이를 기반으로 계산된 승리 확률
이 방식의 핵심은 실력 차이가 큰 두 참가자가 경기할 경우, 잘하는 사람(레이팅이 높은 사람)은 승리 시 점수를 적게 얻고, 패배 시 많은 점수를 잃게 됩니다. 반면 못하는 사람은 승리 시 큰 점수를 얻고, 패배 시에는 적은 점수만 잃습니다. 결과적으로 장기적으로 보면 실제 실력을 정확히 평가할 수 있습니다.
Elo-rating을 문제 풀이에 적용하기
이러한 Elo-rating의 특성을 활용하면 학생이 문제를 맞히고 틀리는 과정 또한 하나의 경기로 생각할 수 있습니다. 즉, 학생과 문제를 각각 Elo-rating을 가진 참가자처럼 간주하고, 다음과 같이 시스템을 구성할 수 있습니다.
•
문제를 맞히면 학생의 레이팅은 올라가고, 문제의 레이팅은 내려갑니다.
•
문제를 틀리면 학생의 레이팅은 내려가고, 문제의 레이팅은 올라갑니다.
이러한 과정을 계속 반복하게 되면 학생의 실제 실력과 문제의 실제 난이도를 보다 정밀하게 파악할 수 있게 됩니다.
실제 데이터 분석 및 Elo-rating 적용 (Python 코드)
제가 직접 진행했던 데이터 분석 예시를 보여드리겠습니다.
이 예제는 유저-문제 정오답 데이터를 데이터프레임 형태로 구성하여 테스트하는 방식입니다.
1. 초기 Elo-rating 설정
모든 학생과 문제는 초기 Elo-rating을 1500으로 설정합니다.
import pandas as pd
initial_rating = 1500.0
answer_df = pd.DataFrame({
'question_id': sorted(df['문제 번호'].unique()),
'rating': initial_rating
})
user_df = pd.DataFrame({
'student_id': sorted(df['학생'].unique()),
'rating': initial_rating
})
Python
복사
2. Elo-rating 계산을 위한 함수 정의
상대방과의 예상 승률 및 레이팅 점수 업데이트를 위한 함수입니다.
# 예측 승률 계산 함수
def calculate_win_probability(opponent_rating, my_rating, rating_diff=400):
return 1 / (1 + 10 ** ((opponent_rating - my_rating) / rating_diff))
# Elo-rating 업데이트 함수
def update_rating(current_rating, expected_win_rate, actual_result, k_factor=50):
return current_rating + k_factor * (actual_result - expected_win_rate)
Python
복사
3. 실제 데이터를 기반으로 한 Elo-rating 업데이트 과정
for question_id, student_id, is_correct in zip(df['문제 번호'], df['학생'], df['정답 유무']):
# 문제와 학생의 현재 Elo-rating 가져오기
question_rating = answer_df.loc[answer_df['question_id'] == question_id, 'rating'].iloc[0]
student_rating = user_df.loc[user_df['student_id'] == student_id, 'rating'].iloc[0]
# 문제와 학생의 예상 승률 계산
question_win_prob = calculate_win_probability(student_rating, question_rating)
student_win_prob = calculate_win_probability(question_rating, student_rating)
# 학생이 문제를 맞췄을 경우
if is_correct:
user_df.loc[user_df['student_id'] == student_id, 'rating'] = update_rating(
student_rating, student_win_prob, actual_result=1
)
answer_df.loc[answer_df['question_id'] == question_id, 'rating'] = update_rating(
question_rating, question_win_prob, actual_result=0
)
# 학생이 문제를 틀렸을 경우
else:
user_df.loc[user_df['student_id'] == student_id, 'rating'] = update_rating(
student_rating, student_win_prob, actual_result=0
)
answer_df.loc[answer_df['question_id'] == question_id, 'rating'] = update_rating(
question_rating, question_win_prob, actual_result=1
)
Python
복사
실제 데이터 확인
실제 데이터를 적용하면 문제 수가 학생 수보다 많아서 데이터의 분포를 바로 보기 어렵습니다. 그래서 문제 수만 루트 스케일로 조정하면 좀 더 쉽게 유저와 문제의 레이팅 분포를 비교할 수 있습니다.
유저 - 문제 레이팅 분포
아래처럼 문제 수만 루트 스케일을 적용하면 비슷한 분포를 얻을 수 있습니다.
해당 분포를 확인했을 때, 일반적으로 문제들은 학생들이 맞힐 수 있도록 제작되기 때문에 전체 정답률이 50%를 넘는 것을 가정하면, 학생의 평균 레이팅이 문제의 평균 레이팅보다 높게 형성되는 분포를 확인할 수 있습니다.
유저 - 문제(만 루트 스케일 적용) 레이팅 분포
실제 시스템 구축 및 문제점
이상적으로는 학생이 문제를 푸는 즉시 Elo-rating을 실시간으로 업데이트하는 것이 최선이지만, 현실적으로 서비스 성능과 자원의 한계 때문에 Airflow를 활용한 일 배치(batch) 형태로 데이터를 집계하고 처리했습니다.
•
매일 정해진 시간에 데이터 웨어하우스(BigQuery)에서 문제 풀이 기록을 시간순으로 정리하여 일괄 처리
•
전날의 마지막 레이팅을 기준으로 새로 추가된 데이터를 업데이트
배치 처리 방식의 문제점
•
하루 단위로 처리하기 때문에, 실시간으로 학생의 실력 변화를 즉시 반영하지 못했습니다.
•
문제 풀이 데이터가 개별 문제뿐 아니라 강의의 흐름과 문제 세트 전체의 맥락을 반영할 수 있다는 사실을 충분히 반영하지 못했음
Cold Start 문제점
•
모든 문제와 학생의 초기 레이팅을 동일한 점수로 세팅하고 출발하기 때문에, 실제로 의미 있는 레이팅 점수에 도달하기까지 상당한 시간이 걸립니다.
•
특히 교육 분야에서는 학년과 학기별로 배우는 개념이 달라지기 때문에, 이전 학기나 학년에서의 레이팅이 다음 학기로 그대로 이어졌을 때, 학생의 실제 실력을 정확히 반영하지 못할 가능성이 높습니다.
•
새 학년이나 학기가 되면 이전의 레이팅이 더 이상 유효하지 않은 경우가 발생하며, 이러한 맥락을 고려하지 않은 레이팅은 학생의 현재 실력을 과대 또는 과소 평가할 수 있습니다.
향후 개선 방안
이러한 문제점을 극복하기 위해 다음과 같은 개선 방안을 고려할 수 있습니다.
1. 실시간 업데이트 도입
•
Kafka와 같은 스트리밍 플랫폼을 도입하여 문제 풀이 결과를 실시간 이벤트로 처리하고, 즉시 Elo-rating을 업데이트할 수 있습니다.
•
문제 하나하나를 개별적으로 처리하거나 문제 세트 종료 시점에 바로 학생과 문제의 Elo-rating을 계산 및 업데이트하여 실시간성을 확보할 수 있습니다.
2. 시험 단위 또는 세트 단위 평가 강화
•
단순히 개별 문제의 정오답 결과뿐 아니라, 문제 세트나 시험 전체 결과를 종합하여 평가하는 방식으로 Elo-rating 계산을 발전시킬 수 있습니다.
•
학생이 문제를 풀기 전에 강의 시청 여부나 문제의 유형별 빈도, 문제 풀이 순서와 같은 추가적인 맥락을 고려하여 Elo-rating 모델의 정확성을 높일 수 있습니다.
3. 세부 Elo-rating 도입
•
기존에는 과목 단위(수학, 영어 등)로만 Elo-rating을 계산했지만
◦
수학의 경우, 세부적으로 성취기준 ['수와 연산, 변화와 관계, 도형과 측정, 자료와 가능성']의 네 가지 영역별로 Elo-rating을 산정할 수 있습니다.
◦
영어의 경우, 단어/문법과 같이 구분을 하거나 모의고사에 나오는 유형별로 Elo-rating을 산정할 수 있습니다
•
성취 기준이나 특정 단위로 레이팅을 관리하면, 학생 개개인이 잘하는 부분과 약한 부분을 정확하게 파악할 수 있습니다.
•
이렇게 세부적인 Elo-rating을 관리할 경우, 새로운 학년이나 학기로 진입했을 때 학생이 배우는 새로운 개념도 기존에 파악된 성취 기준 이나 특정 유형과의 연계를 통해 학생의 정확한 현재 실력을 빠르게 측정할 수 있습니다.
이러한 개선 방안들을 적용하면 Elo-rating의 정확성과 효율성을 크게 높일 수 있으며, 실제 서비스 환경에서의 적용 가능성도 더욱 높아질 것입니다.
마무리
이 프로젝트는 단순히 데이터를 다루는 것을 넘어 제가 개인적으로 관심 있는 게임 이론을 교육 분야의 데이터 엔지니어링 및 분석 영역에 접목한 시도였습니다. 다양한 이유로 실제로 서비스에 도입되지는 않았지만, 데이터를 바라보는 시각과 평가 방식을 다양화하는 좋은 경험이었습니다.
이 글이 Elo-rating의 다양한 응용 가능성을 고민하는 분들께 조금이나마 도움이 되었으면 합니다.