페이지

2023년 2월 8일 수요일

AI가 온라인 학습 이력 데이터 들을 학습하여 미래 문제 정답 확률을 예측할 수 있을까?-2편

1편에 이어서 이번 호에서는 훈련을 위한 데이터세트 구성 및 분할 그리고 모델 정의 및 검증, 구축, 훈련 그리고 추론 절차에 대하여 살펴본다.

텐서플로우 데이터 생성

 아래 그림 10과 같이 텐서플로우 텐서플로우 tf.data 의 from_generator method 와 lambda 함수를 사용하여 generator 에 ①과 같이 seq 를 tensor argument 로 전달하고 output type 을 정의한다. ② skill_with_answer, ③은 skill, ④는 correct 를 정의.

<그림 10. 텐서플로우 데이터세트 생성>

 모델에 입력될 형태는 자연어처리와 마찬가지로 입력 sequence 의 user_id 당 문제 풀이 이력들이 달라서 최대 200 ( 100 skill X 2 (correct)) 에 맞추어 padding 과 batch 구성이 필요하다. 아래 그림 11의 ① 은 tf.data 의 padded_batch method 를 사용하여 batch 크기를 정의하고 ② 의 padding 값은 맨 위에 정의된 MASK_VALUE 인 -1 로 사용. ③은 아래 그림 12와 같이 [문제풀이 길이, 200], [문제풀이 길이, 101]로 차원을 구성. ④의 length는 데이터세트 분할에 사용된다.

<그림 11. padded_batch 구성>

  아래 그림 12의 ①은 tf.data.iterator method 를 사용하여 데이터세트의 요소를 가져온다. 데이터세트는 skill_with_answer 와 skill + label 이 리턴 되므로 ③과 같이 32 크기의 batch 중 첫번째 레코드만 표시했을 때 행렬의 차원이 (문제풀이 길이(122), skill_with_answer 최대수(200))로 보여지고, ④는 차원이 (문제풀이길이(122), skill 수 100 + 라벨 1 = 101) 차원으로 표시되어 데이터베이스가 잘 구성됨을 알 수 있다. ②는 padding value -1 이 채워진 모습.


<그림 12. 데이터세트 확인>

데이터세트 분할

위 그림 11의 length 를 total_size 로 하여 여기에 20%를 test로 할당하고 나머지는 train dataset 으로, 그리고 train dataset 의 20%를 validation dataset 으로 할당한다.

손실함수

  본 과제의 손실함수는 skill 과 관련한 응답 즉 0과 1에 대하여 실제 학습 이력에 있는 값과 예측 값의 차이가 손실이 된다. 이를 위하여 앞의 그림 12의 ④와 같은, 훈련 데이터세트의 skill+label 행렬(y_true)와 예측값(y_pred)를 입력하면 벗겨낸 ground truth 라벨 값(y_true)과 각 스킬들 로부터 예측된 값(y_pred)을 리턴하는 아래 그림 13의 ①의 함수가 필요하다. ②를 통하여 skill+label 의 101차원의 y_true 행렬로부터 실제 label 값이 있는 텐서 요소만을 판별하는 mask 행렬을 만든다. ③으로 실제 값을 추려 냄. 나머지는 모두 0으로 변함. ④에서 skill 과 label 을 분리. ⑤에서 차원을 유지하면서 skill 에 y_pred 를 곱한 요소들의 합을 리턴. ⑥ 에서 label 실제 값과 예측 값을 리턴. ⑦ 은 이들 값들을 입력으로  ⑧의 tf.keras.metrics.binary_crossentropy API를 호출하여 손실을 리턴.


<그림    13. 손실 함수>

심층망 모델 정의

모델은 아래 그림 14의 ①과 같이 순차적 정보를 처리하는 LSTM 모델을 사용한다. 입력으로 최대값 200 차원의skill_with_answer 인코딩이 들어가고 출력으로 100 skill 별 확률 값이 나온다. 앞에서 살펴본 바와 같이, 한 문제 씩 풀이할 때마다, 학생의 지식 상태 즉 skill 별 문제를 맞출 확률은 동적으로 변화한다. 

<그림 14. return_sequence 와 time distributed>

이러한 문제를 반영하려면 위의 ①과 같이 모델을 정의할 때 return_sequence=True 로 선언하여 각 timestep 별 hidden state 값이 모두 나오게 하고, ②의 TimeDistributed 기능으로 매 스텝마다 cost 가 계산되고 하위 스텝으로 오류가 전파되도록 해야 한다. 즉, many-to-many 경우로 만든다. 

아래 그림 15는 LSTM 모델 정의를 표시한다. ①의 nb_features 와 nb_skills 는 각각 skill_with_answer 최대치(200)와 skills 최대치(100)로 모델 inputs 와outputs 의 행렬 차원 변수로 사용된다. 

<그림 15.     LSTM 모델 정의>

②는 keras 로 모델 입력은 (None(batch 가 올 자리), nb_features) 즉 skill_with_answer 가 입력 값이다. 그 다음 층(layer)은 ③masking layer, 그리고 LSTM layer 에 ④와 같이 return_sequence=True 를 설정. ⑤ dropout 은 과적합을 피하기위한 설정이며 값은 0.3 이다. 그 다음은 ⑥과 같이 dense layer 에 activation 은 이진 분류 즉 correct 의 0 과 1을 구분하기에 sigmoid 함수를 사용. 마지막으로 ⑦ 에 TimeDistributed layer 통해 output 이 출력되도록 정의. 

아래 그림 16은 정의한 모델 요약이다. ①에 입력층에 200 차원이 보인다. skill_with_answer 가 입력 값이고 ②는 masking layer, ③에 lstm 층은 unit 이 100으로 설정 되어있다. ④에 TimeDistributed 층이고 outputs 는 skills 의 최대치인 100 이 출력되어 매번 사용자가 문제 풀이한 전체 skill 에 대한 정답 확률 값이 TimeDistributed 를 통하여 매 스텝마다 cost 가 계산되고 하위 스텝으로 오류가 전파된다. 즉 동적으로 각 단계에서의 사용자의 학습 상태가 업데이트 된다.

<그림 16. 모델 요약>

모델 훈련

아래 그림 17은 1000 epoch 모델 훈련을 시행했을 때 훈련과 검증데이터의 정확도와 손실 결과다. 검증데이터 손실은 0.085를 향해 지속적인 감소가 보인다. 검증(validation)데이터세트의 정확도도 0.82를 향해 점진적으로 우상향을 보여서 일단 과적합(ovefit)에 대한 우려는 없어 보인다. 

<그림 17. 모델 훈련 결과>

저장된 모델 weight 로드

 tf.keras.callbacks.ModelCheckpoint callback 함수를 통하여 model.fit 동안에 best_model_weights 로 저장된 최고 성능 모델을 아래 그림 18와 같이 다시 로드한다.

<그림 18. 모델 weight 로드>

모델 평가

아래 그림 19에 testset에 대한 성능 결과가 정확도 0.8198, AUC 가 0.8610 으로 표시되었다. 이는 DKT 논문에서 보여준 Assistment AUC 결과와 동일한 결과다.

<그림 19. 모델 테스트 결과>

추론(predict)을 위한 input 형태 구성

이제 완성된 모델을 통하여 특정 사용자(user_id)의 skill_with_answer 값을 모델에 입력하면,  모델은 이전 model.summary()에서 살펴본 것처럼 skill 별 맞출 확률을 출력한다. 다만 여기서 모델에 입력되기위해서는 skill_with_answer 값들이 이전 그림 12와 같은 데이터세트 형태의 입력 형태가 되어야한다.  

특정 사용자의 학습 상태 취약점 예측

아래 그림 20은 user_id 96409 의 model.predict 출력이다. ①에 입력되는 sa[0]는 96409 user 의 데이터프레임 df_96409 로부터 그림 12와 같은 데이터세트 형태로 변환된 후에 요소가 추출되어 넘파이 array sa 에 저장된 값이다. 첫번째 batch sa[0]가 입력되어 데이터프레임으로 변환된 출력이 아래 그림이다. 27번째 문제풀이에서 담고 있는 skill 별 정답 확률을 이용하여 이 이후에 맞추기 어려운 혹은 쉬운 문제를 찾아낼 수 있다.

<그림 20. 96409 user 의 skill 별 정답 확률 출력>

 user_id 96409 의 경우에 가장 문제를 맞추는데 어려워 보이는 문제 유형 top5 를 출력한 결과가 아래 그림 21에 나타나 있다. ①은 df_96409 같은 데이터프레임이 입력되고, 매개변수 1을 전달하여 마지막 문제풀이 상태에서 오름차순으로 정렬하여 Top5 를 출력하는 inference 함수를 작성하여 실행한 코드이다.  Inference() 함수는 매개변수 1부터 4까지에 따라 Top5 lowest, Top5 highest, single most lowest, single most highest 와 같은 정답을 맞출 확률을 출력한다. 출력 중, ②는 skill 번호와 함께, ③ 문제를 맞출 확률과 함께 출력되고 있다.

<그림 21. 가장 취약한 문제 유형 오름차순 Top5 출력>

결언

1편에 이어서 데이터세트 구성, 훈련 및 추론에 대해서 살펴보았다. 모델 평가로 AUC도 Assistment 데이터세트로 평가한 논문 결과와 유사하게 나왔고 LSTM TimeDistributed 신경망을 통하여 특정 사용자의 skill(문제 유형) 과 correct(정오답 여부) 학습 이력을 입력으로, 문제 풀이 이력 전 과정의 skill 별 문제를 맞출 확률을, 학생의 학습 상태의 취약점 예측이라는 관점에서 얻을 수 있었다.