Mean reciprocal rank (MRR) 은 랭킹 모델을 평가하기 위한 가장 간단한 방법중 하나 입니다. MRR은 질의(Q)에 대해 가장 적절한 아이템의 역순위 평균입니다. 역순위란 1을 제안된 리스트에서 적절한 아이템이 나타난 위치의 숫자로 나눈 것을 말합니다. 예를 보시면 쉽게 이해하실 수 있습니다.
Query
Proposed Results
Correct response
Rank
Reciprocal rank
cat
catten, cati,cats
cats
3
1/3
torus
torii,tori, toruses
tori
2
1/2
virus
viruses, virii, viri
viruses
1
1
Query 는 질의어/검색어이고, Proposed Results는 검색엔진의 결과로 제안된 내용입니다. Correc response는 질의어 입력시 우리가 원하는 제안 내용이고요, Rank는 결과에서 제안 내용이 위치한 순위 입니다. Reciprocal rank 는 1 나누기 Rank의 값 입니다. Mean Reciprocal rank 는 이러한 Reciprocal rank 를 평균한 값입니다. 이 내용을 공식으로 정리하면 아래와 같습니다.
MRR = 1/|Q| Sigma i=1 to |Q| 1/rank i
따라서, 위 예시에 대한 MRR을 구해보면 아래와 같습니다.
MRR= 1/3*(1/2+1/3+1/1)= 11/18 = 0.61111
MRR의 한계중 하나는 검색 결과 아이템들 중에 하나의 순위만 고려하고 다른 것의 순위들은 무시한다는 것입니다. 따라서, 웹페이지에서 검색 결과를 중요도 순으로 노출하는 것에 대한 성능을 측정하고자 할 경우 MRR은 좋은 지표가 아닙니다. 여러 개의 위치와 중요성을 고려하지 못하기 때문이지요.
Precision at k
Precision at k (P@k) 는 많이 사용되는 또 다른 지표중 하나 입니다. 이 지표는 상위 k개의 문서중에서 연관된 문서의 수로 정의 됩니다.
예를 들면, 구글에서 “손 소독제” 를 검색한다고 생각해보겠습니다. 검색 결과 처음 페이지에 나온 10개 문서중에서 8개가 '손 소독제'와 연관된 문서라면 Precision@10(P@10)은 8/10 = 0.8 입니다.
여러 질의들 Q에 대한 Precision@k를 찾기 위해서는 Q에 있는 각 질문들에 대한 P@k의 평균 값으로 계산할 수 있습니다.
P@k는 몇가지 한계점이 있습니다.
가장 중요한 것은 상위 k개 연관된 문서들의 위치를 고려하지 못한다는 것 입니다. 이것은 매우 중요할 수 있습니다. 첫페이지에서 첫번째 문서가 연관된 문서일 경우와 첫페이지에서 마지막 문서가 연관된 문서일 경우는 완전히 다를 수 있습니다. (왜냐하면 첫페이지의 마지막 문서는 실제로 사용자가 보지 못할 수 있기 때문이지요.) 반면에 상위 k개의 결과들이 연관된 문서인지 아닌지만 검토하면 되기 때문에 모델을 쉽게 평가할 수 있습니다.
매우 비슷한 방법으로 정의되면서, 많이 사용되는 평가지표가 바로 recall@k입니다.
AP@k
AP@k(Average Precision at k). 앞에서 말씀드린 것처럼 Precision@K에서는 순서를 고려하지 않은 단점이 있었습니다. AP@K는 위치를 고려한 평가 지표입니다. 이말은 추천 리스트중에 높은 위치에 추천한 항목이 더 관심있는 항목으로 해야 좋다라는 가정에서 평가하는 방법입니다. 일반적으로 가정이 맞는 상황이지요. 공식은 아래와 같습니다. rel(i)는 선호 여부로 0 또는 1입니다.
Recall@k
Recall@k(R@k)는 검색 결과로 가져온 k개의 문서들 중에서 얼마나 많은 (적합한) 관련 문서가 있는 가로 계산할 수 있습니다. 즉, Recall@10의 경우, 가져온 10개의 문서중에서 8개가 관련된 문서이고 전체 관련 문서수가 9개인 경우 Recall@10은 8/9입니다.
Precision@k의 경우 가져온 문서 중에서 얼마나 정확한 문서가 많은 가를 중심으로 판단하는 지표라면, Recall@k는 가져와야할 문서 중에서 얼마나 많은 문서를 가져왔는가를 중심으로 판단하는 지표 입니다. 둘다 단점으로는 가져온 위치/순위를 고려하지 못한다는 것 입니다.
nDCG(CG/DCG/nDCG)
nDCG는 랭킹 시스템의 학습을 평가하기위해 가장 많이 사용되는 평가지표 입니다. 이전의 평가 지표들과는 달리 nDCG는 문서의 순서와 관련성의 중요성을 고려한 지표입니다. 즉, 추천 리스트의 위쪽에 관련성이 높은 문서가 위치할 수록 높은 값을 갖습니다.
nDCG의 공식적인 정의를 말하기 전에 먼저 두가지 연관 지표에 대해서 소개해 드리겠습니다. 바로 Cumulative Gain (CG) 과 Discounted Cumulative Gain (DCG) 입니다.
Cumulative Gain (CG) 이란 질의 결과로 가져온 각 문서의 연관성 점수를 모두 합친 것을 말합니다. 아래와 같이 정의될 수 있습니다.
여기에서는 각 문서의 연관성 점수(보통 상수)는 주어진다고 가정합니다.
Discounted Cumulative Gain (DCG) 는 CG에 가중치가 부여된 버전입니다. 결과의 위치에 따른 연관성 점수를 적절하게 변환하고자 로그함수를 사용합니다. 이것은 시스템의 성능을 평가할때 상당히 유용한데, 왜냐하면 처음 몇개의 문서들이 좀더 높은 가중치를 갖게되기 때문입니다. 검색 결과의 상위에 노출되는 결과 일수록 더 높은 가중치를 준다는 말은 검색 결과를 평가하는 입장에서 보면 맞는 말인 것 같습니다. DCG는 아래와 같이 정의 됩니다.
분모에 로그가 있어서 순서(i)가 커질 수록(추천 리스트의 아래에 위치할 수록) 분모가 커지면 DCG값은 작아집니다. 숫자가 많이 커져도 분모에 로그를 취하기 때문에 단순히 위치로 나눈 값보다는적당히 커진 분모값이 됩니다.
또 다른 버전도 있습니다.
Normalized Discounted Cumulative Gain (NDCG) 는 실제 어플리케이션 평가에 더 적합하도록 DCG를 향상시켜서 고안된 것입니다. 가져온 문서들의 집합은 질의나 시스템에 따라 매우 다르기 때문에 nDCG는 DCG의 졍규화된 버전을 이용해서 성능을 평가합니다. 이상적인 시스템의 DCG로 평가하려는 시스템의 DCG를 나누는 것 입니다. 다른 말로 하면, 관련성을 기준으로 리스트 결과를 정렬하고, 해당 위치에서 이상적인 시스템에서 얻어지는 가장 좋은 DCG를 가지고 정규화하는(나누는) 것입니다.
여기서 IDCG는 “ ideal discounted cumulative gain”이고, 아래와 같이 정의합니다.
nDCG는 인기 많은 평가지표 입니다. 그러나 한계성도 있습니다. 주요 한계중 하나는 검색 결과 중에서 잘못된(연관성 없는) 문서를 가져온 것에 대한 패널티가 없다는 것 입니다. 이 말은 몇 개의 괜찮은 결과를 만드는 질의의 성능을 측정하는 것에 적합하지 않습니다. 특히, 검색 결과로 처음에 나오는 몇 개에만 관심이 있는 경우에는 더욱 적합하지 않습니다.
Lecture 13 - Debugging ML Models and Error Analysis | Stanford CS229: Machine Learning (Autumn 2018)
이번 강의의 주요 내용입니다.
오늘은 잘 동작하는 학습 알고리즘을 만들기 위한 조언에 대해 이야기 합니다. 오늘 자료는 깊이있는 수학적 내용은 아닙니다. 그러나 이번 수업에서 어찌보면 가장 이해하기 어려운 내용 중에 하나입니다. 과학과 예술 사이의 문제라고 할 수 있습니다. 어떤 사람들은 완전히 새로운 영역의 혁신적인 기계학습 연구를 할 때 이러한 조언들은 오히려 안 좋은 것이라고 말합니다. 그러나 기존에 연구된 유사한 문제를 유사한 방법으로 풀기위해서는 많이 도움되는 좋은 조언이라고 할 수 있습니다. 주요한 내용/아이디어는 다음과 같습니다. 1. 학습알고리즘을 디버깅하기 위한 진단방법은 무엇인가. 2. 에러 분석과 제거 분석은 무엇인가. 3. 기계학습 문제에서 통계적 최적화를 어떻게 시작할 것인가.
1. 학습알고리즘을 디버깅하기위한 진단방법은 무엇인가?
스팸 메일을 막기위한 Anti-spam 모델을 만든다고 가정해 보겠습니다. 모든 영어 단어를 피처로 이용하는 대신에 100개의 단어를 피처로 골랐습니다. 로지스틱 회귀와 경사 상향법을 가지고 모델을 구현하였습니다. 그래서 20%의 테스트 에러가 발생했는데 이것은 쓸수 있는 수준이 아닙니다. 자 이런 상황에서 여러분이라면 무엇을 하시겠습니까?
로지스틱 회귀와 같은 모델에 대한 일반적인 접근은 아래와 같은 내용들입니다. 즉, 훈련 데이터를 더 늘리거나, 피처의 갯수를 줄이거나, 더 많은 피처를 사용해볼 수 있겠지요. 또는, (이메일에서 해터 정보나 본문의 피처로 변경해서 훈련해보거나, 더 많은 반복을 실행해 볼 것 입니다. 다른 방법으로는 뉴턴 메소드를 이용해보거나, 러닝 레이트(학습률) 람다를 바꾸어 훈련해보거나 아예 모델을 바꾸어서 SVM을 사용하는 것을 시도 할 수도 있습니다. 그런데 이런 시도들은 사람에 따라서 달라집니다. 즉, 전에 프로젝트에서 어떤 것을 해봤고 효과가 좋았는지 등의 경험을 통해서 아무거나 선택되기 쉽습니다. 또는 리더의 지시 방향에 따라서 그저 선택되고 시도해 보게 되기도 합니다. 즉, 체계적이지 않다는 것이 문제 입니다.
가장 기본적이고 효과적인 진단 방법은 바이어스/베리언스(bias vs. variance) 분석입니다. 무엇이 문제인지를 진단하고 그 문제를 고치는 방법입니다. 보통 모델의 성능이 좋지 않다고 하면 오버피팅이나 언더피팅일 경우입닌다. 오버피팅은 variance가 높은 경우를 말합니다. 반대로 언더피팅은 bias가 높은 경우를 말합니다. 예를 들면 스팸을 분류하기위해서 너무 작은 수의 피처가 사용되면 bias가 클 확률이 높아집니다. 이처럼 모델의 성능의 안정성을 진단하는 방법중 하나가 바로 variance와 bias를 알아보는 것입니다. variance가 큰 경우는 훈련 에러가 테스트 에러보다 훨씬 작은 경우에 발생 합니다. 즉, 훈련에서는 잘 맞추는데 테스트에서는 못 맞추는 경우 입니다. Bias가 큰 경우는 훈련 에러와 테스트 에러가 모두 높은 경우 입니다. 아무리 훈련해도 에러를 줄이지 못하는 상황이 많습니다.
높은 variance를 가지는 모델의 에러와 데이터 량을 그래프로 그려보면 아래와 같습니다. 데이터 수가 많아져도 훈련 에러율과 테스트 에러율의 차이가 계속 발생하고, 훈련 때는 원하는 에러율보다 낮지만 테스트 할때는 높습니다. 그리고 테스트에서는 원하는 목표 에러율보다 낮아지지 않습니다. 목표에러와 테스트 에러의 차이가 많이 난다는 의미입니다. 데이터(m)가 늘어 남에 따라서 테스트 에러율이 낮아지고 있으므로 더 많은 데이터가 도움이 될 것 입니다. 그럼에도 불구하고 훈련 에러율과 테스트 에러율 사이에 차이가 있습니다. 높은 Variance가 있을 경우 아래와 같은 그래프를 보입니다.
다음은 높은 bias를 가진 러닝 커브의 예를 들어 보겠습니다. 높은 바이어스가 있는 경우 훈련 에러와 테스트 에러는 데이터가 많아 짐에 따라서 비슷해지지만, 심지어 훈련 에러율도 원하는 에러율 보다 않좋게 나오는 경우 입니다. 훈련이 잘 안되는 거죠. 이런 경우에는 아무리 많은 데이터 사례를 더 모아서 훈련해도 원한는 모델을 만들 수 없습니다.
Bias-variance is the single most powerful tool for analyzing the performance of a learning algorithm. guess some problems and find out whether the model has the problems or not.
일반적으로 많이 연구되지 않은 새로운 영역의 모델을 만들때 추천하는 방법은 먼저 지저분한 코드라도 빠르게(Quick and dirty code) 만들어서 돌려보고 Bias/Variance 진단을 해보는 것입니다.
앞서 살펴본 Bias / Variance 문제가 가장 흔하고 가장 중요한 진단 방법입니다. 그밖의 다른 문제에 대해서는 무엇이 잘못된 것인지를 알아내기 위한 당신의 진당방법을 만들기위한 당신의 창의성에 달려있습니다. 다른 예를 들어 보겠습니다. 로지스틱 회귀 모델을 통해 스팸 메일에 대해 2%의 에러율과 정상 메일에 대해 2%의 에러율을 얻었다고 합시다. 실제로 사용하기 어려운/받아들이기 힘든 수준입니다. 또 다른 성형 커널 기반의 SVM 모델을 이용해서 스팸 메일에 대해서 10%의 에러율을, 정상 메일에 대해서 0.01%의 에러율을 얻었습니다. 이정도 성능은 받아들일만 하다고 하겠습니다. 그런데 당신은 계산의 효율성을 위해서 로지스틱 회귀 모델을 사용하기를 원합니다. 어떻게 해야할 까요?
다른 일반적인 질문중 하나는 '알고리즘이 수렴하고 있는가?' 입니다. 오브젝티브 함수를 지켜봄으로써 알고리즘이 수렴하고 있는지를 말하기 어려운 경우가 종종 있습니다. 일반적으로 훈련 반복을 늘리면 오브젝티브 함수는 조금씩 올라가게 됩니다. 그런데 얼마나 더 많은 반복을 해야할까요? 더 많은 반복이 효과적일지 아닌지 어떻게 알까요? 보다 체계적인 방법이 없을까요?
아마도 무엇이 잘 못 되었는지 생각해 본다면 다음과 같은 질문을 할 수 있습니다. 최적화를 위해 올바른 함수를 사용하고 있는가? , 로지스틱 회귀가 맞나? 학습률은 적절한가? 등이 그것 입니다.
SVM은 보통 로지스틱 회귀를 능가하는 성능을 보입니다. 그러나 여러분은 어플리케이션을 위해서 로지스틱 회귀 모델의 배포를 원하는 상황입니다. SVM의 성능이 BLR을 능가함으로, 가중치를 적용한 SVM과 BLR의 정확도를 비교하면 a(θ SVM)이 더 커야 합니다.
먼저 최적화 알고리즘의 문제가 있으면 경사하강법은 수렴하지 않을 수 있습니다. 두번째로 잘못된 비용함수를 이용하면 학습되지 않을 수 있습니다. 즉, 진단해야할 것은 1. 옵디마이징 알고리즘이 잘못되서 수렴하지 못하는 건지 2. 아니면 비용함수를 잘못 만든 것인지 확인이 필요합니다. 로지스틱 회귀가 최대화 하려는 비용함수를 확인해볼 필요가 있습니다.
이를 위한 진단 방법은 아래와 같습니다. 앞서본 정확도 a(θ)와 비용함수 J(θ)를 가지고 무엇이 잘못된 것인지 진단 할 수 있습니다.
케이스1 의 경우는 최적화 알고리즘에 문제가 있는 경우이고, 케이스2의 경우는 비용함수가 문제인 경우입니다.
그래서 앞서 살펴 봤던 여러 개선 방법들 중에서 최적화 알고리즘의 문제를 해결하기 위한 방법들과 비용함수 문제를 해결하기 위한 방법들이 아래와 같이 매칭 됩니다.
SVM을 시도하는 것도 오브젝티브 함수를 최적화 하는 것을 고치기 위한 방법중 하나 입니다.
Debugging an RL algorithm
강화학습에 대한 상세 내용은 별도로 정리하겠습니다.
Error Analysis
일반적인 얼굴인식 시스템의 절차는 다음과 같습니다.
원본 사진에서 배경을 지우고
얼굴을 찾고
얼굴에서 눈, 코, 입을 찾고
모델(로지스틱 회귀)을 실행시켜서 최종 아웃풋 레이블을 만듭니다.
전체적인 플로우는 다음과 같습니다.
따라서 각각의 단계 / 모듈이 오류율을 어떻게 변화하는지를 확인할 수 있습니다.
Ablative analysis
Ablative analysis는 각 콤포넌트가 최종 성능에 각각 얼마나 기여했는지를 측정하는 방법입니다. 좋은 피처들이 없는 단순 선형 회귀 모델을 통해서 94%의 성능이 나오고 콤퍼넌트를 추가했을 때 99.9%로 모델 성능이 향상 된 것을 어떻게 설명할 것인가? 라는 질문에 대답할 수 있는 방법이 Ablative analysis입니다. 즉, 기본 베이스 라인 모델(94%)에서 시작해서 하나씩 콤퍼넌트가 추가될때 마다 성능(Accuracy)이 어떻게 변하는지 확인하는 방법입니다. 이런 Ablative analysis를 통해서 어떤 콤퍼넌트가 성능향상에 가장 기여를 많이 하는지 알수 있습니다.
A one-hotis a group ofbitsamong which the legal combinations of values are only those with a single high (1) bit and all the others low (0).
즉, 비트 들의 모임 중 하나만 1이고 나머지는 모두 0인 비트 들의 그룹을 원핫(One-hot)이라고 합니다.
그럼 인코딩(Encoding)이란 무엇일까요? 인코딩은 부호화라는 의미로 위키피디아에서는 아래와 같이 말하고 있습니다.
인코딩(encoding)은 컴퓨터를 이용해 영상 · 이미지 · 소리 데이터를 생성할 때 데이터의 양을 줄이기 위해 데이터를 코드화하고 압축하는 것이다.
쉽게 말하면 데이터를 압축/변형 하는 것은 인코딩, 이 압축/변형된 데이터를 원형으로 변환하는 것을 디코딩(Decoding)이라고 합니다.
그래서 원핫과 인코딩을 합친 One-hot Encoding(원핫 인코딩)이란
데이터를 One-hot 데이터 형태로 변형/압축하는 것
을 말합니다. 여기서 데이터는 이미지 데이터가 될 수도 있고 텍스트 데이터나 카테고리(범주형) 데이터가 될 수도 있습니다.
왜? 원핫 인코딩이 필요한가?
일반적인 인코딩은(부호화는) 정의에서 나온 것처럼 변형하거나 압축이 필요할 때 사용됩니다. 복잡하고 긴 이름이나 내용을 특정 규칙에 따라 변형 또는 매핑하여 축소할 때 효과적입니다. 파일 압축을 생각해보세요. 메일이나 메신저를 사용해서 용량이 큰 이미지나 동영상 파일을 전송할때 생각해보세요. 그냥 전송하는 것보다는 압축한 파일을 전송하는 것이 훨씬 빠르고 안정적입니다. 이처럼 원핫 인코딩의 경우에도 복잡한 데이터를 그대로 사용하지 않고 컴퓨터가 처리하기 쉽게 숫자로 변형해 주는 것입니다. 이렇게 함으로써 데이터를 처리하기 위해 필요한 메모리 양을 줄일 수 있고 처리를 빠르게 할 수 있습니다. 게다가 대부분의 통계나 머신러닝 모델들은 입력 데이터로 숫자값을 기본으로 합니다. 그래서 특히, 범주형 데이터를 원핫 인코딩해서 사용하는 경우가 많이 있습니다. 예를 들어서 주택 데이터 셋 중에서 주택 유형이라는 피처(데이터 항목)이 있고 이 항목의 실제 값들은 '아파트', ''연립주택', '다세대주택', '단독주택', '다중주택', '다가구주택', '기타' 로 구성되어있다고 하겠습니다. 이러한 값들을 그대로 모델의 입력값으로 사용할 수 없기 때문에 숫자로 바꾸어주는 것이 필요합니다. 그래서 이 법주형 데이터를 원핫 인코딩으로 변환할 경우, '아파트'는 '100000'으로 '연립주택'은 '010000'으로 변환되어 사용될 수 있습니다. 이렇게 원핫 인코딩된 값들은 분석 모델의 인풋 데이터로 사용할 수 있습니다.
어떻게 하나?
방법은 간단합니다. 범주형의 종류 개수(n) 크기의 벡터를 0으로 초기화하고, 특정 범주를 나타내기 위해 특정 위치의 값을 1로 설정하는 것 입니다. 위의 예에서도 잠깐 살펴본 주택 데이터 셋 예를 더 자세히 알아보겠습니다. 주택 유형이라는 범주형 데이터의 전체 범주 개수가 6개인 경우 벡터의 크기는 6이 됩니다. 이 벡터를 리스트로 표현하면 [0,0,0,0,0,0]이 됩니다. 크기가 6인 0벡터 이지요. 첫번째 범주인 '아파트'를 원핫 인코딩해보면 [1,0,0,0,0,0]로 나타 낼 수 있습니다. 첫번째 위치 값을 1로 설정하고 나머지는 모두 0인 원핫 벡터가 되었습니다. 다른 범주값까지 모두 원핫 인코딩 한 결과는 아래와 같습니다.
장점 및 단점
장점은 어떻게 인코딩/디코딩 되는지 쉽게 이해할 수 있다는 것 입니다. 구현도 쉽고 작은 데이터 셋에서 빠르게 동작한다는 장점이 있습니다.
단점은 범주가 추가되면 데이터의 크기가 바뀐다는 것입니다. 위에서 주택유형을 입력으로 쓰기 위해 6자리의 원핫 벡터로 인코딩해서 사용중이었는데 예를들어 전원주택 같은 새로운 주택유형이 하나 추가되면 7자리의 벡터로 바꾸어서 사용해야한 다는 것입니다. 이 말은 기존에 만들었던 모델이나 알고리즘에 변형이 필요하다는 의미 입니다. 매우 큰 변화이지요. 이처럼 향후의 확장성을 고려하여 더미 범주를 추가해서 인코딩하거나 해싱을 이용해서 인코딩 하기도 합니다. 이러한 문제를 OOD(Out Of Dictionary) 라고도 합니다. 예측이나 추론을 위해 원핫 인코딩을 실행했는데 모델이나 알고리즘을 개발할때에는 없던 새로운 범주가 입력되는 경우 인코딩 할 수 없어서 발생하는 에러를 말합니다.
정보저장을 위해 희소 벡터(Sparse Vector)를 사용하기 때문에 정보가 없는 공간에 대해서도 관리가 필요하게 되어서 메모리의 낭비가 발생한다는 것입니다. 위의 주택 유형 예에서 '아파트'를 '100000'으로 인코딩하면 첫번째 1만 값이 있고 나머지는 위치는 모두 정보가 없는 0이 차지하기 때문에 메모리 낭비가 발생하는 것입니다.
또 다른 단점은 범주형 데이터가 순서나 크기의 의미를 포함하고 있을 때 원핫 인코딩을 하게되면 이러한 정보들은 사용할 수 없게된다는 것입니다. 예를 들면 월요일, 화요일, 수요일, 목요일, 금요일, 토요일, 일요일을 원핫 인코딩하면 그저 0과 1일 집합으로 표현됩니다. 월요일은 [1,0,0,0,0,0,0], 토요일은 [0,0,0,0,0,1,0], 일요일은 [0,0,0,0,0,0,1] 이렇게 되겠지요. 이렇게 인코딩된 값들은 요일간에 순서나 크기를 표현하지 못합니다. 반면에 레이블 인코딩의 경우 일요일은 0, 월요일은 1, 화요일은 2, 수요일은 3, 목요일은 4, 금요일은 5, 토요일은 6으로 인코딩하면 요일간의 연계성 정보를 인코딩한 숫자에서도 찾을 수 있습니다. 즉, 수요일 3 다음에는 목요일 4라는 것을 알수 있지요. 이렇게 되면 5보다 큰 수가 의미를 갖을 수도 있습니다.
파이썬에서의 구현 방법
원핫 인코딩은 통계, 기계학습, 머신러닝, 딥러닝 등 쓰이는 범위가 넓습니다. 그래서 원핫 인코딩 기능은 여러 프레임웍과 페키지에서 제공하고 있습니다. 아래는 scikit-learn 패키지에서 구현한 예시 내용입니다.
# scikit-learn에서 필요한 모듈을 가져옵니다. from sklearn.preprocessing import OneHotEncoder
# 프로그램에서 사용할 인스턴스를 하나 만듭니다. one_hot_encoder = OneHotEncoder()
# 데이터 셋에서 범주형 데이터 항목의 값들을 인코더의 입력값 X로 설정 합니다. X = [['Apple'], ['Banana'], ['Cherry'], ['Date'], ['Egg'], ['Apple'], ['Cherry'], ['Cherry']] print('데이터 항목 X: ', X)
# 데이터 X를 이용해서 one hot encoding을 적용 실행 one_hot_encoder.fit(X)
# 인코딩된 카테고리들의 내용을 확인해 보는 명령어 categories = one_hot_encoder.categories_ print('범주 categories: ', categories)
# 특정 범주(여기서는 'Apple', X[0])에 해당하는 원핫 벡터를 찾아보는 명령어 one_hot_vector_for_alpha = one_hot_encoder.transform([X[0]]).toarray() print('Apple를 인코딩한 값(벡터): ', one_hot_vector_for_alpha)
# 특정 원핫 벡터(여기서는 'Apple'의 원핫 벡터 [[1. 0. 0. 0. 0.]])에 해당하는 범주 Category를 찾는 명령어 category_for_one_hot_vector = one_hot_encoder.inverse_transform(one_hot_vector_for_alpha) print('Apple 인코딩 벡터를 이용해서 찾은 범주 이름: ', category_for_one_hot_vector)
print('\n모든 범주와 원핫 인코딩을 출력 합니다.') for category in one_hot_encoder.categories_[0]: print(category,': \t', one_hot_encoder.transform([[category]]).toarray())
위의 내용을 실행하기 전에pip install sklearn명령을 통해 scikit-learn 패키지를 설치해 주어야 합니다. 아래는 패키지를 설치하고 위의 내용을 파일로 저장후 실행한 결과 입니다.
데이터 항목 X: [['Apple'], ['Banana'], ['Cherry'], ['Date'], ['Egg'], ['Apple'], ['Cherry'], ['Cherry']] 범주 categories: [array(['Apple', 'Banana', 'Cherry', 'Date', 'Egg'], dtype=object)] Apple를 인코딩한 값(벡터): [[1. 0. 0. 0. 0.]] Apple 인코딩 벡터를 이용해서 찾은 범주 이름: [['Apple']]
원핫 인코딩이 무엇인지에 대해서 알아보았고, 왜필요한지, 그리고 어떻게 구현할 수 있는지에 대해서 알아보았습니다. 이렇게 인코딩된 데이터를 이용하여 분석하거나 머신러닝 모델에서 입력 값으로 사용할 수 있습니다. 위에서 보신 것 처럼 Apple을 모델에서는 [[1. 0. 0. 0. 0.]]로 입력 받아 처리합니다. 결과 값을 분류하는 경우, 즉 과일 이름 중에 하나가 결과로 나와야하는 경우에는 역으로 [[1. 0. 0. 0. 0.]]로 나온 모델의 결과 값을 다시 'Apple' 로 바꾸어주는 것이 필요합니다. 이처럼 원핫 인코딩/디코딩은 범주형 데이터의 변환에 많이 쓰입니다.
지난 시간에는 딥러닝의 첫번째 시간으로 선형회귀 모델과 간단한 신경망을 비교해서 만들고 설명했습니다. 이미지 중에 고양이가 있는지 없는지를 분류하는 네트워크를 만들었습니다. 그 때정의한 Cost Function과 Loss Function(빨간색 박스안의 내용), 그리고 Update 방법은 아래와 같습니다.
업데이트를 할때 미분을 하는데 이때 아웃풋에 제일 가까운 레이어(w[3]) 부터 미분하는 것이 계산하기 좋다는 것 까지 지난 시간에 이야기 했습니다.
Backprop(역전파)
역전파, 백프로파게이션, Backprop을 해 보겠습니다. w[3]부터 계산합니다. J (Cost Function)의 값을 w[3] 값으로 미분합니다.
이렇게 계산을 진행하기 위해서는 사실 먼저 알아야할 전제사항/공식이 있는데요 아래와 같습니다.
J를 w[3]로 미분하는 위에 위에 있는 이미지에서, 마지막에 a[2]에 왜 Transpose 를 할까요? 바로 행렬간의 계산을 할 수있도록 해주기 위해서 입니다. 아래에서 처럼 transpose를 해주어야 (1, 2) 행렬이 됩니다.
계속 풀어보면 다음과 같습니다.
이것을 m개의 훈련 데이터에 대해서 평균을 내는 것은 아래와 같습니다.
w[3]에 대한 계산이 끝났으니 그 이후에는 w[2]에 대한 미분입니다. 어떻게 하면 더 효율적으로 계산할 수 있을까요?
로스 함수를 w[3]로 미분한다는 것은 아래의 오른쪽 내용 처럼 분리가 가능한데요.. 이 내용을 자세히 보면 이전에 구했던 왼쪽의 내용에서 변형할 수 있는 값을 유추할 수 있습니다. 바로 a[2]T가 z[3]를 w[3]로 미분한 것과 같다는 것 입니다.
그래서 w[2]로 미분하는 공식에 대하여 동등한 내용을 빨간색으로표시해 보면 아래와 같습니다.
그래서 동등한 내용으로 재 정리하면 아래와 같습니다. 그런데 중간에 행렬의 크기가 일치하지 않아서 행렬 연산이 안되기 때문에 Elementwise dot (모든 요소간의 곱)이 필요합니다. 그렇게 해서 행렬의 크기를 동일하게 맞출 수 있습니다.
상기 내용을 어떻게 프로그래밍 해야할지 걱정하지 않아도 됩니다. 많은 머신러닝 프레임웍에서 이런 계산을 대신해 주기 때문입니다.
포워드 프로파게이션을 하는 동안 계산 내용을 모두 메모리에 저장해 놓았다가 백워드 프로파게이션 시에 사용함으로써 메모리 사용을 줄이고 계산 속도를 높일 수 있습니다.
Imporving Neural Networks
A) Activation Function
지금까지 배운 내용으로 신경망을 만들고 훈련해도 만족할 만한 성과를 못 얻을 수 있습니다. 그래서 더 좋은 성능을 내는 신경망을 어떻게하면 만들 수 있는지에 대해서 알아보겠습니다.
제일 먼저 가능한 것은 Activation Funcion(활성화 함수)을 바꾸는 것 입니다.
시그모이드 함수나 탄젠트h 함수는 z 값이 아주 커지거나 작아지면 출력값의 차이가 작아져서 w를 업데이트 하는 영향이 거의 없어집니다. 즉, gradient vanishing 문제를 발생하게 됩니다. ReLu는 이런 문제를 해결하는 함수 입니다.
그럼 왜 활성화 함수가 필요할 까요?
위의 두 이미지에서 보면 알 수 있듯이 활성화 함수가 없다면 네트워크의 깊이가 아무리 깊어도 선형 함수의 분류기와 같을 뿐입니다.
보통 같은 레이어에서는 같은 활성화 함수를 이용합니다. 다른 레이어에는 다른 활성화 함수를 사용할 수 있습니다. 위에서 살펴본 시그모이드 함수(Sigmoid), 레루 함수(ReLu), 탄젠트h 함수(tanh) 이외에도 여러가지가 있지만 기본적인 함수는 이 정도가 됩니다.
B) Initialization Methods
두번째로는 초기화 방법에 대해서 알아보겠습니다. 아래 이미지는 평균으로 정규화할 경우 변화되는 내용을 설명합니다.
표준편차를 통해 정규한 내용이며 이럴 경우 최적값을 찾아가는 경로가 훨씬 단축되게 됩니다. 그래서 정규화를 하는 것이지요.
상기에서 사용한 평균이나 표준편차는 모두 훈련 데이터에서 계산된 것을 말하며 이것은 테스트 시에도 동일한 값을 이용해서 계산되야 합니다.
베니싱 그레디언트(Vanishing Gradient)에 대해서 좀더 자세히 알아보겠습니다. 아래와 같이 히든 레이어가 10개, 활성화 함수는 Identical 함수(그냥 통과하는 함수), 그리고 바이어스는 0인 신경망을 만들었다고 하겠습니다. 아래 계산에서도 볼 수 있듯이 w 의 값이 초기값에 L 승으로 곱한 결과가 됩니다. 따라서 초기 값이 1보다 조금만 커도 결과 값은 아주 큰 값으로 설정이 되고 1보다 작은 값을 설정을 해도 결과 값은 아주 작은 값으로 설정되게 됩니다. 0.9, 1.1 처럼 1 근처의 값으로 주면 그나마 변화량이 덜 할 것입니다. 그러나 조금만 커져도 엄청낙게 큰 값으로 바뀌고 조금만 더 작아져도 엄청나게 작은 값으로 바뀝니다. 그래서 활성화 함수가 중요합니다.
하나의 뉴런을 가지고 예를 들어보겠습니다. 보시는 것처럼 n이 커지면 w(i)가 작아집니다. 따라서 w(가중치)의 초기 값을 1/n으로 설정하는 것도 좋은 가중치 초기화 방법 중에 하나 입니다.
그래서 시그모이드 활성화 함수에 대해서는 아래와 같은 코드로 초기화 하는 것이 동작을 잘 합니다.
다른 초기화 방법으로 Xavier Initialization(자비어 초기화)이 있습니다.
C) Optimization
세번째 방법으로 옵티마이제이션에 대해서 알아보겠습니다.
아래는 이렇게 배치로 최적화 할때랑 아닐때랑을 비교한 비용함수 곡선입니다.
배치 그레디언트 디센트(Batch Gradient Descent) 방법을 2차원에서 그려보면 아래의 녹색 선과 같이 움직입니다. 이상적인 빨간색 경로보다는 돌아가는 것 같지만 하나의 이터레이션을 훨씬 빠르게 실행할 수 있습니다.
또 약간 다른 Gradient Descent와 Momentum(모멘텀) 알고리즘을 소개합니다. 일반적으로 GD를 쓰면 빨간색 선과 같이 움직이면서 최적값을 찾아가는데, 여기에 모멘텀을 더하면 녹색 선과 같이 움직이게 됩니다. GD는 지나온 과거의 움직임을 고려하지 않고 앞으로 나아 갑니다. 반면에 모멘텀은 과거 움직임을 고려해서 앞으로의 방향을 조정합니다. 처음 시작점에서 두번째 점으로 이동했을때 가로, 세로의 변화량을 보면 세로의 변화량은 큰데 가로의 변화량은 작습니다. 이것을 눈치첸 모멘텀은 가로를 더 많이 가게 조정해서 녹색 선의 세번째 점을 향하게 됩니다. 이런 방법으로 학습하면 훨씬 더 빠르게 최적화를 할 수 있습니다.
모멘텀의 구현방법은 아래와 같습니다.
물론 이러한 방법은 기본적인 내용이며, 이 밖에도 많은 최적화 방법이 있습니다. 그 중에서 특히, Adam과 RMSProp은 신경망에서 많이 쓰이는 최적화 방법들이 있습니다.