요즘 데이터 사이언스를 독학으로 하고 있는데, 혹은 수업에서 들은 것보다 더 깊은 내용으로 도전해보고 싶은데 좋은 교재를 추천해줄 수 없냐는 요청을 많이 받는다. 불행히도 그런 질문을 하시는 분들 대부분이 수학 & 통계학을 필자 스타일로 공부하신 적이 없는 분들이다. 당장 데이터 사이언스 책을 추천하기 전에 수학과 통계학을 추천해야할 판국인 셈이다. 그 중에는 데이터 사이언스 공부를 하는 이유가 최소한 어떤 스토리가 돌아가고 있는지는 알고 싶은 분도 있을 것이고, 또 컴퓨터 과학 (컴퓨터 공학, 전자 공학), 산업 공학, 경제학 등등의 유사학문을 하신 분들이 수리통계학을 다 까먹었다가 직장 생활 중에 복구시키고 싶은 경우도 있을 것 같아, 아래에 짧게나마 괜찮아 보이는 교과서들을 추천한다.

참고로 많은 교과서들이 대학원 수업 교재 수준으로 어려워서 읽기를 포기하고 싶어지거나, 반대로 지나치게 코드 Copy & Paste 위주여서 추천할 교재 구하기가 쉽지 않다. 최근 읽은 책 중 Kalman Filter 설명하는 책에서 저자가 비슷한 고민을 했던 후기를 봤다.

There are classic textbooks in the field, such as Grewal and Andrew’s excellent Kalman Filtering. But sitting down and trying to read many of these books is a dismal experience if you do not have the required background. Typically the first few chapters fly through several years of undergraduate math, blithely referring you to textbooks on topics such as Itō calculus, and present an entire semester’s worth of statistics in a few brief paragraphs. They are good texts for an upper undergraduate course, and an invaluable reference to researchers and professionals, but the going is truly difficult for the more casual reader. Symbology is introduced without explanation, different texts use different terms and variables for the same concept, and the books are almost devoid of examples or worked problems. I often found myself able to parse the words and comprehend the mathematics of a definition, but had no idea as to what real world phenomena they describe. “But what does that mean?” was my repeated thought.

 

1. 선형대수

가장 좋은 학습법은 이과 1학년 애들이 듣는 선형대수학 수업 교실에 앉아서, 같은 교재로 증명도 따라가고, 숙제로 제출하고, 시험도 치면서 온 몸에 선형대수학을 내재화시키는 것이다. 솔직한 심정으로는 그렇게 한 학기를 보냈다고해서, 그 수업에서 A+를 받았다고해서 선형대수학 마스터라는 칭호를 달아주고 싶은 생각도 없을만큼 부족하다.

하지만, Top class의 Data Scientist가 목표가 아니라면 굳이 그렇게까지 깊에 선형대수학을 알아야할 것 같지는 않고, 또 좋은 수학 교과서는 대부분 영어라서 힘들테니 번역이 잘 된 직장인 버젼 책 하나를 추천한다.

프로그래머를 위한 선형대수

링크를 따라가보면 온라인 버젼을 확인할 수 있다. 이 책을 기획하셨던 출판사 담당자 분과 한번 이야기를 나눈 적이 있었는데, 책의 수준에 자신감이 넘치기 때문에 온라인으로 공개해도 결국은 책을 살 것이라고 생각한다고 하셨다. 저 책이 잘 팔리는 것과 필자의 주머니 사정에는 아무런 관계가 없지만, 그 담당자 분의 말씀에 99.99% + 0.01% 공감할 수 있을만큼 좋은 책이다.

책 이름에 “프로그래머”를 넣어놓은 이유는 아마 책을 좀 잘 팔기 위해서인 것 같고, 정작 책 내용 중에 프로그래머를 위한 배려는 생각보다 많지 않다. 굳이 따지면 “‘수학을 잘 모르지만 알아야하는 사람들을 위한‘ 선형대수”인 책인데, N차원으로 확장되는 일이 허다한 선형대수의 개념을 2, 3차원의 그래프를 이용해서 굉장히 쉽게 설명을 해 놨다. 실제로 머신러닝을 현장에서 활용할 때, 물론 대학원 수준의 고급 수학 지식을 갖고 있으면 좋겠지만, 최소한 위의 책에서 등장하는 개념을 쉽게쉽게 소화할 수 있어야 Professional하게 모델링을 할 수 있을 것이라고 생각한다.

 

2. 회귀분석

선형대수학을 어느 정도 알고 있다면, 0차원은 점, 1차원은 선, 2차원은 평면, 3차원은 공간 같은 개념을 쉽게 받아들이게 될 것이다. 그런 측면에서 학부 기초 통계학은 1개의 평균, 1개의 분산을 찾는 0차원의 지식이고, 회귀분석은 데이터를 설명해내는 직선을 찾는 1차원의 지식이라고 보면 된다. (참고로, 학부 수준 회귀분석과 같은 논리를 확장해서 평면, 공간을 찾아내는 작업도 대학원 회귀분석 때 배운다.)

따라서 회귀분석을 이해하려면 기본적인 통계학과 선형대수를 이해해야한다. 필자는 경제학과에서 회귀분석을 공부하다보니, 계량경제학이라는 이름의 수업만 들었다. 딱히 통계학 전공자 분들이 어떤 회귀분석 책을 보시고, 공학 전공자 분들이 유사한 내용을 어떤 책으로 배우시는지 잘 모르기 때문에 잘 모르는 책들을 함부로 추천할 수는 없고, 경제학 side에서 회귀분석을 공부하겠다는 분께 가장 괜찮은 교재는 (전) 고려대학교 경제학과의 김창진 교수님 강의 노트다. (모교 교수님들 죄송합니다 ;;;;)

대학원 유학 중 고대 출신인 친구들이 갖고 있어서 봤던 노트 필기인데, 공부 좀 했다고 까불며 모교의 석, 박 대학원 계량경제학 수업 기출문제들을 웃으며 풀 수 있게 된 시점에도 여전히 존경심이 우러나는 노트였었던 걸로 기억한다. 수식과 해석, 직관과 응용이 매우 높은 수준으로 정리되어 있던데, 그 노트에 있는 회귀분석을 다 이해한다면 어지간한 머신러닝에 쓰이는 모든 수학 & 통계학이 하나도 어렵게 느껴지지 않을 것이라는데 매우 강한 확신을 갖고 있다. (실제로 계량경제학 잘하는 경제학과 출신들이 필자의 수업을 “편안하게” 듣는다 ㅋㅋ)

어디서 구하냐고? 고대 출신이 아니라서 잘 모르겠는데, 친구들 말에 따르면 고대 안암 캠퍼스 정경대 쪽에 있는 후문 복사집들에서 구할 수 있단다. 최근 수강생 한 분이 거기서 복사본을 구하셨다고 들었다.

그런데, 학부 고학년 수준의 통계학 기반 지식이 전혀 없이 독학하시는 분들이 그 노트를 보고 뚝딱 이해하기는 좀 어려울 것 같다. 요즘 뒤져보니 Youtube 동영상 중에 계량경제학 수업들이 괜찮은게 몇 개 있던데, 그 수업들 좀 들으면서 보조 교재 정도로 쓰시는건 어떨까?

(출처: 9gag.com)
– 자매품: “머신러닝에 수학 & 통계학이 필요한가요?”,
“개발 경력 X년인데, 데이터 사이언티스트 될 수 있나요?”

 

3. 머신러닝

우선 대단히 미안한 말이지만, 국내에 나와있는 거의 대부분의 ‘머신러닝 쉽게 따라하기’ 같은 종류의 책들은 불쏘시게 종이 이상의 가치가 없다. 너무 대담한 표현인가? 필자도 자주 유사한 기획을 가진 출판사들에게서 연락을 받기 때문에, 어떤 생각을 갖고 저런 책들을 기획하고 있는지, 그리고 정작 그런 기획에 팔려서 책을 쓰는 사람들이 어떤 사람들인지 상당히 많은 정보가 있다. 결국 팔려야된다는 경제논리를 갖고 출판되는 책이, “수학”이라는 장벽을 달고 나오면 아무래도 출판사들의 호응을 받기가 쉽지 않은게 냉정한 현실이다. 그런데 정작 머신러닝을 수박 겉핡기가 아니라 제대로 공부하겠다고 위의 수학 & 수리통계학 지식을 쌓는 공부를 했는데, 이걸 좀 활용해서 머신러닝 이해도를 깊게하면 좋지 않을까?

아래의 2개 책을 추천한다.

ISLR, 따라하며 배우는 데이터 사이언스

사실 이미 추천했던 책인데, 입문자에게 다른 더 좋은 책을 아직 딱히 만나본 적이 없다. 혹시나 이미 두 권 다 봤는데, 아직 수준이 낮은 것 같고, 더 높은 수준을 원한다고? 꼭 저 위의 두 권이 아니더라도, 서평 중에 “수학이 너무 많이 나와서 어려웠다”는 류의 평이 들어가 있는 머신러닝 교과서라면 대부분 괜찮은 책일 것이다. 반대로 “코드가 환상적으로 잘 돌아간다”와 같은 평이 달려있으면 무조건 멀리해라. 당신을 개발자로 만들어주지 Data Scientist로 만들어줄 책은 아니니까.

데이터 사이언스 수업 커리큘럼을 짜면서, 계속 강의를 하면서 항상 부족한 지식을 더 채워넣어야 수강생 분들께 많은 내용을 전달해드릴 수 있다는 생각으로 나름대로 시간을 내서 공부를 계속하고 있다. 그런데, 내용의 깊이가 더해질수록 수학적인 훈련이 없으면 이해하기 어려워 보이는 자료들이 참 많다. (수학 공부 좀 많이할껄ㅠㅠ) 수업에는 이해한 내용을 바탕으로 최대한 수식을 개념과 직관과 그림으로 바꿔서 설명하고 있지만, 누군가의 필터를 거치지 않고 직접 이해하고 싶은 욕구가 있으신 분들은 더더욱 수학 지식이 간절하지 않을까 싶었다. 글 첫머리에 언급한 Kalman Filtering 교재 저자의 후기에 나왔던 저 고민은 학부 고학년 수준을 넘어간 지식을 습득하고 활용하려는 사람들 모두가 느끼는 공통적인 “괴로움”이 아닐까?

 

나가며 – 실력있는 Data Scientist는?

마음에 드는 개발자 뽑기가 참 어렵다. 돈 많이 주면 실력있는 개발자 구할 수 있는거 아니냐고들 하는데, 돈 문제는 둘째고, 그 전에 실력있는 개발자에 대한 기준도 사람마다 참 다른 거 같다. 대부분의 개발자들은 “뭘 빨리 만들어낸다”, “빨리 코드를 친다”, “복잡한 걸 구현해낸다”는 쪽에서 개발자의 실력을 따지는 것 같은데, 짧은 기간 개발자를 설득, 이해, 공유하는 시간을 거치면서 느낀바는, 비지니스의 스토리를 잘 이해하고 처음 개발하는 순간부터 어떤 스펙으로 접근해야할지 큰 그림을 그릴 수 있는 개발자들이 진짜 “실력있는” 개발자인 것 같다.

같은 맥락에서 괜찮은 Data Scientist 는 코드를 잘 치거나, 수학을 잘 하는 사람이 아니라, 필요한 이론을 적재적소에 잘 활용할 수 있는 “머리가 말랑말랑”한 사람이 아닐까 싶다. 위에서 언급한 책들에 있는 내용들은 굳이 따지자면 (기초) “이론”이다. 수학(Math)과 통계학(Stat)은 과학(Science)을 하기 위한 “언어”니까. 저런 언어에 익숙해지고, 그걸 머신러닝이라는 과학을 이해하는데 쓸 수 있게 되었다고 끝나는 것이 아니다. 정말 “실전”에서 지식이 쌓이려면 어떤 데이터는 어떻게 전처리를 하고, 그래서 무슨 모델링을 해 볼 수 있겠다는 그림을 머리 속에 그릴 수 있어야 한다. 그런 “그림”이 지식의 범위가 성장하면서 점점 다르게 그려지기는 했지만, 필요한 상황에 맞게 적절한 모델을 만드는건 정말 힘든 일이더라. 공부하시는 분들 모두의 건투를 빈다.

 


Full disclosure. 위에서 추천한 그 어떤 책도 저와 개인적인 관련이 없음을 밝힙니다. 다만 언젠가 “따라하며 배우는 데이터 과학” 책을 쓰신 권재명 박사님을 뵙게되면 제 홍보로 책 좀 팔렸으면 밥 한끼 사달라고 부탁은 드리고 싶습니다 ㅋㅋ

얼마전, 증권사에서 퀀트 팀 주니어로 일하신다는 분의 메일을 받았다.

글로벌 자산배분 업무라는 허울 좋은 일을 시작했는데, 현실은 매일 뉴스 체크하면서 직관에 의존한 배분만…

시장의 많은 데이터 속에서 유의미한 Implication을 뽑아내는 능력을 기르는….

이론적인 모델은 아니더라도 시장의 패턴을 이용해 (Portfolio를) 분산하는 모델을 여러가지 만들어내는…

메일을 읽자마자 10년쯤 전에 초짜 투자은행가로 첫 사회생활을 시작했던 무렵이 떠오르더라. 바로 위에 있던 형 하나랑 밤새 일하면서, 우리가 만드는 이런 허접한 Valuation 모델만 믿고 몇 조원을 지르겠다는 대기업 회장님들은 도대체 뭐냐고 낄낄거렸는데, 아마 저 분도 은행에서 자기네 펀드 사고 있는 사람들에게 비슷한 감정을 갖고 있을 것이다.

지적인 욕구와 스스로에 대한 자괴감이 스스로를 잡아먹는 걸 느끼고 런던행 비행기를 타던 그 무렵, 대학원을 가면 뭔가 뚝딱 대단한 모델을 배워서 한번에 쏴아아악~ 해결할 수 있을 줄 알았다 ㅋㅋㅋ

런던, 보스턴을 거치며 긴 대학원 시절 내내, 필자의 일상사는 그런 멋진 모델이 아니라 한 줄 따라가기도 벅찬 수식과의 지난한 싸움이었다. 처음에는 여느 경제학도처럼 “저런 단순한 수식으로 세상을 어떻게 설명하냐”는 회의감도 있었고, 모델 하나하나가 어떤 단순화로 세상의 특징을 잡아내는지 깨닫고 난 다음부터는 수식 속에 담긴 경제학적 함의를 찾아내기 위해 고민하기도 했고, 나중에 내 논문을 쓰면서는 모델의 수학적인 엄밀함을 위해 필요한 수학적인 내공에 좌절하기도 했다.

저 분도 자기가 꿈꾸는 모델을 만들고 세상에 검증해보고 싶다면 아마 필자같은 좌절스러운 몇 년을 보내면서 수학적으로 열심히 훈련을 받으셔야할 것이다. 시중에 돌아다니는 (초급용) 데이터 사이언스 관련 서적들 봐서는 답 없을 것이라고, 쓸데없는데 시간 쓰지 말고 대학원을 가셔야된다고 조언을 드릴 수 밖에 없었다. (책 쓰자는 연락이 벌써 몇 군데서나 왔냐….그렇다고 고난이도로 쓰면 아무도 안 사는 책이 될테니.. 쩝) 물론, 꼭 10년전에 학부 교수님이 필자를 뜯어 말리셨듯이, 저 분께 같은 길을 권유할 생각은 전혀 없다.

 

1. 대학원에서 만나는 머신러닝 수업 – Convex Optimization

필자의 수업을 듣고 가신 어느 개발자 분이 큰 맘을 먹고 모 대학 빅데이터 대학원 과정을 들어가셨다. 수업이 쉽다고 매번 불평하시는데, 아마 그 분 실력이 뛰어나서 직장인 대상 석사 프로그램 수준이 좀 부족하게 느껴졌을 수는 있겠다 싶더라.

그 분이 요즘 공부하는 내용이라고 링크를 하나 보내주셨는데, Convex Optimization이라는 수업이다. 자연과학과 공학 교육으로 유명한 카네기 멜론 대학 (CMU)의 통계학 & 머신러닝 전공 대학원 수업인데, 예상대로 수준낮은 MOOC 강의들 (ex. Coursera, Udemy 등)과는 현격한 차이가 있더라.

Convexity를 알아야하는 이유는 우리가 수식으로, 컴퓨터로 풀어내는 모든 계산이 함수의 convexity 또는 concavity를 이용한 최적화 (optimization) 작업이기 때문이다. 수식으로 못 풀어낼 때 머신러닝에서 가장 흔히 쓰이는 방법이 Gradient descent, 이 때 정확성과 계산 속도의 trade off를 겪으면서 쓰는 작업이 Stochastic gradient descent라고 한다. 요기까지가 필자가 일반인 대상으로 수업하는 내용이고, 아마 시중에 알려진 수학적인 도전(?)일 것이다.

대학원 수업에서는 함수 형태에 따라 항상 under-estimate하는 경우를 보정해주는 sub-gradient라는 내용도 다루고, 오차 투성이일 수 밖에없는 1차 함수 approximation을 탈피해서, 2차 이상의 함수로 gradient decent를 하는 proximal gradient descent라는 방법도 배운다. 왜? 이런거 배워서 어디 써먹냐고? 데이터 모델링은 언제나 오차에 노출되어 있는 작업이다. 당연히 인간이 할 수 있는 최대한의 보정작업은 본인의 수학 실력이 올라갈수록 레벨업이 될 수 밖에 없다.

저 링크의 수업은 뒷 부분이 더 재미(?)있다. Duality를 다루는 부분에서는 Regularization에 들어가는 조건식이 사실상 constraint라는 관점에서 출발해서, 목적함수를 constraint에 맞춰서 값을 구하나, constraint를 목적함수에 맞춰서 구하나 결국에는 같은 종류의 최적화 작업이 돌아간다는 걸 보여준 다음에, 그 아이디어를 (블록체인 류의) 네트워크를 푸는데도 쓰고, (또 블록체인에 적용되는) 네트워크 + 게임이론 문제 푸는데도 쓴다. 굳이 블록체인에 국한될 것도 없는게, 모든 수학적인 문제 풀이는 다들 같은 선상에서 출발한다. (이걸 경제학과 파이낸스에서 배웠는데, 강의 노트가 하나도 새롭지 않다면 믿으시겠는가?)

참고로 저 Duality가 필자의 수업 시간에 SVM 설명하면서 왜 SVM은 Regularization을 반대로 하고 있는지에 대한 수학적인 설명이기도 하다.

강의 후반엔 Newton method라고, 최적값을 trial-and-error로 찾아가는 작업이 활용되는 방식을 위에 설명한 sub-gradient, proximal gradient, duality 모델들에서 활용하는 걸 보여주는데, 굳이 따지자면 이 부분이 계산 통계학 (Computational Statistics)과 머신러닝이 만나는 지점이 아닐까 싶다. Logistic regression을 풀 때 손으로 풀 수가 없기 때문에 Newton method로 solution을 찾아가는 것도 잘 안 가르쳐줄 일반인용 강좌만 보던 분께 계산 통계학의 근간인 Newton method와 그 방법을 빌려서 머신러닝의 계산비용을 절감시키는 컴퓨터 사이언스의 만남은 무척 낯설지도 모른다.

 

2. 대학원에서 만나는 머신러닝 수업 – Statistical Machine Learning

몇 달전에 어느 대학 통계학과 4학년 학생한테 데이터 마이닝 수업을 하나 듣고 났더니 이제 다 알게 된 것 같다는 식으로 좀 자뻑하는 메일을 받은 적이 있다. 어이가 없어서 ISLR 수준에서 벗어나고 그런 주장을 해야되지 않겠냐고 강한 어조로 타일렀는데, 얼마나 알아들었을라나?

대학원에서 위의 수업을 듣고 나면 좀 더 레벨업된 통계학 + 머신러닝 융합 수업을 들을 수 있다. 같은 교수가 강의하는 Statistical Machine Learning 이다. 주제는 학부생들용 ISLR과 비슷할지도 모른다. Linear, non-linear, non-parametric regression을 배운 다음에, Clustering과 Ridge, Lasso의 개념, Graph theory에서 나오는 Tree model, network model을 배우는 수업이니까. 당연하겠지만 내용의 깊이는 현격하게 차이가 난다. 제대로된 대학원 수업이니까.

네트워크 이론 설명하면서 잠깐 다뤘던 Centrality, complexity를 Clustering에 적용하면서 수학적으로 범위가 주어질 수 밖에 없고, 이걸 바탕으로 Clustering 작업시 계산 비용을 최적화할 수 있는 컨셉이 여기에 잘 설명되어 있다. 물론 같은 아이디어를 Network에 적용할 수도 있고, 그건 수업 후반부인 여기에서 다루더라.

수식을 제쳐놓고 간단하게 아이디어만 정리하면, 저런 수학적으로 제한될 수 밖에 없는 최소값, 최대값 없이 단순하게 Centrality와 complexity를 cluster나 network에서 계산한다면 모든 가능성을 다 뒤져야한다. 데이터가 많지 않다면 큰 문제가 안 되겠지만, 우리가 다루는 데이터들은 “빅데이터”들이다.

이런 종류의 inequality로 통계량의 범위를 예측하는 작업을 처음했던게 학부 2학년 경제통계에서 Chebyshev’s inequality를 썼던 건데, 그 땐 정말 왜 저런거 공부하나, 도대체 어디에 써먹나는 생각을 했던게 사실이다. 머리에 지식이 더 들어오고 나서야 비로소 저런 지식이 내 계산의 효율을 끌어올려주고, 내 모델을 업그레이드 해 주고, 내 몸 값을 올려준다는 사실을 깨닫게 되었다.

(Source: Beauty of Maths)

 

나가며 – 깊이란?

서울 시내 모처에서 교수로 재직중인 선배들과 사업 이야기를 한 적이 있다. 형님들이 너무 Valuation을 낮게 잡고 시작하시는 것 같길래, 좀 더 스스로를 띄우시라고 시장에 있는 다른 “스타트업”이라는 곳들이 얼마나 수준 낮은 “기술”을 들고 있는지를 농담삼아 던졌었다. 형님들이 웃으면서 그러시더라.

학부생들이 한다는 기술이라는게 다 그렇지 뭐

누군가는 기분 나쁘게, 또 누군가는 씁쓸하게, 또 다른 누군가는 속이 시원하게 들을 멘트라고 생각한다.

예전에 어느 헤지펀드 면접을 보는데, 상황을 하나 던져주고, 어떤 Trading algorithm을 만들 수 있는지 논리를 쌓아보라는 질문을 받은 적이 있다. 가볍게 데이터 셋의 분포를 보고 regression 타입을 정하는 작업을 한참 설명했더니, 이렇게 질문하더라.

Is it deep enough?

듣기 좀 속 쓰린 코멘트였는데, 면접 끝나고 집에 오면서 생각해보니 너무 Shallow한 모델이었다는 사실을 인정할 수 밖에 없었다. 학부생들 기술이라는게 다 그렇다는 저 코멘트에 분명 누군가는 기분이 나쁘겠겠지만, Not-so-deep-enough라는 팩트마저 부인하긴 어렵지 않을까? 링크 걸린 수업의 강의 노트 한번 보시고, 내 모델은, 내 지식은 얼마나 deep enough인지 한번 고민해보시길 바란다. 참고로, deep enough라는 칭찬 받기 참 힘들다.

얼마전, 증권사에서 퀀트 팀 주니어로 일하신다는 분의 메일을 받았다.

글로벌 자산배분 업무라는 허울 좋은 일을 시작했는데, 현실은 매일 뉴스 체크하면서 직관에 의존한 배분만…

시장의 많은 데이터 속에서 유의미한 Implication을 뽑아내는 능력을 기르는….

이론적인 모델은 아니더라도 시장의 패턴을 이용해 (Portfolio를) 분산하는 모델을 여러가지 만들어내는…

메일을 읽자마자 10년쯤 전에 초짜 투자은행가로 첫 사회생활을 시작했던 무렵이 떠오르더라. 바로 위에 있던 형 하나랑 밤새 일하면서, 우리가 만드는 이런 허접한 Valuation 모델만 믿고 몇 조원을 지르겠다는 대기업 회장님들은 도대체 뭐냐고 낄낄거렸는데, 아마 저 분도 은행에서 자기네 펀드 사고 있는 사람들에게 비슷한 감정을 갖고 있을 것이다.

지적인 욕구와 스스로에 대한 자괴감이 스스로를 잡아먹는 걸 느끼고 런던행 비행기를 타던 그 무렵, 대학원을 가면 뭔가 뚝딱 대단한 모델을 배워서 한번에 쏴아아악~ 해결할 수 있을 줄 알았다 ㅋㅋㅋ

런던, 보스턴을 거치며 긴 대학원 시절 내내, 필자의 일상사는 그런 멋진 모델이 아니라 한 줄 따라가기도 벅찬 수식과의 지난한 싸움이었다. 처음에는 여느 경제학도처럼 “저런 단순한 수식으로 세상을 어떻게 설명하냐”는 회의감도 있었고, 모델 하나하나가 어떤 단순화로 세상의 특징을 잡아내는지 깨닫고 난 다음부터는 수식 속에 담긴 경제학적 함의를 찾아내기 위해 고민하기도 했고, 나중에 내 논문을 쓰면서는 모델의 수학적인 엄밀함을 위해 필요한 수학적인 내공에 좌절하기도 했다.

저 분도 자기가 꿈꾸는 모델을 만들고 세상에 검증해보고 싶다면 아마 필자같은 좌절스러운 몇 년을 보내면서 수학적으로 열심히 훈련을 받으셔야할 것이다. 시중에 돌아다니는 (초급용) 데이터 사이언스 관련 서적들 봐서는 답 없을 것이라고, 쓸데없는데 시간 쓰지 말고 대학원을 가셔야된다고 조언을 드릴 수 밖에 없었다. (책 쓰자는 연락이 벌써 몇 군데서나 왔냐….그렇다고 고난이도로 쓰면 아무도 안 사는 책이 될테니.. 쩝) 물론, 꼭 10년전에 학부 교수님이 필자를 뜯어 말리셨듯이, 저 분께 같은 길을 권유할 생각은 전혀 없다.

(Source: pinterest.com)

 

1. 대학원에서 만나는 머신러닝 수업 – Convex Optimization

필자의 수업을 듣고 가신 어느 개발자 분이 큰 맘을 먹고 모 대학 빅데이터 대학원 과정을 들어가셨다. 수업이 쉽다고 매번 불평하시는데, 아마 그 분 실력이 뛰어나서 직장인 대상 석사 프로그램 수준이 좀 부족하게 느껴졌을 수는 있겠다 싶더라.

그 분이 요즘 공부하는 내용이라고 링크를 하나 보내주셨는데, Convex Optimization이라는 수업이다. 자연과학과 공학 교육으로 유명한 카네기 멜론 대학 (CMU)의 통계학 & 머신러닝 전공 대학원 수업인데, 예상대로 수준낮은 MOOC 강의들 (ex. Coursera, Udemy 등)과는 현격한 차이가 있더라.

Convexity를 알아야하는 이유는 우리가 수식으로, 컴퓨터로 풀어내는 모든 계산이 함수의 convexity 또는 concavity를 이용한 최적화 (optimization) 작업이기 때문이다. 수식으로 못 풀어낼 때 머신러닝에서 가장 흔히 쓰이는 방법이 Gradient descent, 이 때 정확성과 계산 속도의 trade off를 겪으면서 쓰는 작업이 Stochastic gradient descent라고 한다. 요기까지가 필자가 일반인 대상으로 수업하는 내용이고, 아마 시중에 알려진 수학적인 도전(?)일 것이다.

대학원 수업에서는 함수 형태에 따라 항상 under-estimate하는 경우를 보정해주는 sub-gradient라는 내용도 다루고, 오차 투성이일 수 밖에없는 1차 함수 approximation을 탈피해서, 2차 이상의 함수로 gradient decent를 하는 proximal gradient descent라는 방법도 배운다. 왜? 이런거 배워서 어디 써먹냐고? 데이터 모델링은 언제나 오차에 노출되어 있는 작업이다. 당연히 인간이 할 수 있는 최대한의 보정작업은 본인의 수학 실력이 올라갈수록 레벨업이 될 수 밖에 없다.

저 링크의 수업은 뒷 부분이 더 재미(?)있다. Duality를 다루는 부분에서는 Regularization에 들어가는 조건식이 사실상 constraint라는 관점에서 출발해서, 목적함수를 constraint에 맞춰서 값을 구하나, constraint를 목적함수에 맞춰서 구하나 결국에는 같은 종류의 최적화 작업이 돌아간다는 걸 보여준 다음에, 그 아이디어를 (블록체인 류의) 네트워크를 푸는데도 쓰고, (또 블록체인에 적용되는) 네트워크 + 게임이론 문제 푸는데도 쓴다. 굳이 블록체인에 국한될 것도 없는게, 모든 수학적인 문제 풀이는 다들 같은 선상에서 출발한다. (이걸 경제학과 파이낸스에서 배웠는데, 강의 노트가 하나도 새롭지 않다면 믿으시겠는가?)

참고로 저 Duality가 필자의 수업 시간에 SVM 설명하면서 왜 SVM은 Regularization을 반대로 하고 있는지에 대한 수학적인 설명이기도 하다.

강의 후반엔 Newton method라고, 최적값을 trial-and-error로 찾아가는 작업이 활용되는 방식을 위에 설명한 sub-gradient, proximal gradient, duality 모델들에서 활용하는 걸 보여주는데, 굳이 따지자면 이 부분이 계산 통계학 (Computational Statistics)과 머신러닝이 만나는 지점이 아닐까 싶다. Logistic regression을 풀 때 손으로 풀 수가 없기 때문에 Newton method로 solution을 찾아가는 것도 잘 안 가르쳐줄 일반인용 강좌만 보던 분께 계산 통계학의 근간인 Newton method와 그 방법을 빌려서 머신러닝의 계산비용을 절감시키는 컴퓨터 사이언스의 만남은 무척 낯설지도 모른다.

 

2. 대학원에서 만나는 머신러닝 수업 – Statistical Machine Learning

몇 달전에 어느 대학 통계학과 4학년 학생한테 데이터 마이닝 수업을 하나 듣고 났더니 이제 다 알게 된 것 같다는 식으로 좀 자뻑하는 메일을 받은 적이 있다. 어이가 없어서 ISLR 수준에서 벗어나고 그런 주장을 해야되지 않겠냐고 강한 어조로 타일렀는데, 얼마나 알아들었을라나?

대학원에서 위의 수업을 듣고 나면 좀 더 레벨업된 통계학 + 머신러닝 융합 수업을 들을 수 있다. 같은 교수가 강의하는 Statistical Machine Learning 이다. 주제는 학부생들용 ISLR과 비슷할지도 모른다. Linear, non-linear, non-parametric regression을 배운 다음에, Clustering과 Ridge, Lasso의 개념, Graph theory에서 나오는 Tree model, network model을 배우는 수업이니까. 당연하겠지만 내용의 깊이는 현격하게 차이가 난다. 제대로된 대학원 수업이니까.

네트워크 이론 설명하면서 잠깐 다뤘던 Centrality, complexity를 Clustering에 적용하면서 수학적으로 범위가 주어질 수 밖에 없고, 이걸 바탕으로 Clustering 작업시 계산 비용을 최적화할 수 있는 컨셉이 여기에 잘 설명되어 있다. 물론 같은 아이디어를 Network에 적용할 수도 있고, 그건 수업 후반부인 여기에서 다루더라.

수식을 제쳐놓고 간단하게 아이디어만 정리하면, 저런 수학적으로 제한될 수 밖에 없는 최소값, 최대값 없이 단순하게 Centrality와 complexity를 cluster나 network에서 계산한다면 모든 가능성을 다 뒤져야한다. 데이터가 많지 않다면 큰 문제가 안 되겠지만, 우리가 다루는 데이터들은 “빅데이터”들이다.

이런 종류의 inequality로 통계량의 범위를 예측하는 작업을 처음했던게 학부 2학년 경제통계에서 Chebyshev’s inequality를 썼던 건데, 그 땐 정말 왜 저런거 공부하나, 도대체 어디에 써먹나는 생각을 했던게 사실이다. 머리에 지식이 더 들어오고 나서야 비로소 저런 지식이 내 계산의 효율을 끌어올려주고, 내 모델을 업그레이드 해 주고, 내 몸 값을 올려준다는 사실을 깨닫게 되었다.

(Source: Beauty of Maths)

 

나가며 – 깊이란?

서울 시내 모처에서 교수로 재직중인 선배들과 사업 이야기를 한 적이 있다. 형님들이 너무 Valuation을 낮게 잡고 시작하시는 것 같길래, 좀 더 스스로를 띄우시라고 시장에 있는 다른 “스타트업”이라는 곳들이 얼마나 수준 낮은 “기술”을 들고 있는지를 농담삼아 던졌었다. 형님들이 웃으면서 그러시더라.

학부생들이 한다는 기술이라는게 다 그렇지 뭐

누군가는 기분 나쁘게, 또 누군가는 씁쓸하게, 또 다른 누군가는 속이 시원하게 들을 멘트라고 생각한다.

예전에 어느 헤지펀드 면접을 보는데, 상황을 하나 던져주고, 어떤 Trading algorithm을 만들 수 있는지 논리를 쌓아보라는 질문을 받은 적이 있다. 가볍게 데이터 셋의 분포를 보고 regression 타입을 정하는 작업을 한참 설명했더니, 이렇게 질문하더라.

Is it deep enough?

듣기 좀 속 쓰린 코멘트였는데, 면접 끝나고 집에 오면서 생각해보니 너무 Shallow한 모델이었다는 사실을 인정할 수 밖에 없었다. 학부생들 기술이라는게 다 그렇다는 저 코멘트에 분명 누군가는 기분이 나쁘겠겠지만, Not-so-deep-enough라는 팩트마저 부인하긴 어렵지 않을까? 링크 걸린 수업의 강의 노트 한번 보시고, 내 모델은, 내 지식은 얼마나 deep enough인지 한번 고민해보시길 바란다. 참고로, deep enough라는 칭찬 받기 참 힘들다.

일반에 딥러닝으로 알려져 있는 머신러닝 모델의 가장 기본이 바로 신경망(Neural Network) 모델이다. 다른 글에서 신경망 모델로 Sin(X) 그래프를 추적하는 예제를 하나 풀어봤었는데, 기본적으로 아래처럼 네트워크가 100% 꽉 들어차 있는 Fully Connected Neural Network 모델이 기본형이다. (Deep Neural Network, 일반에는 딥러닝으로 알려져 있다.) 여기서 몇 개의 노드(node)가 사라지는 경우, 또 넣었다 뺐다 하는 경우(Dropout 이라고 한다) 등 다양한 경우로 응용을 할 수 있는데, 이렇게 옵션을 추가하는 이유는 신경망 모델의 노드가 늘어나면 늘어날수록 계산 비용(CPU, RAM, 전력, 시간 등)이 기하급수적으로 늘어나기 때문이다. (and over-fitting을 방지하기 위해서이기도 하다.)

딥러닝이라고 알려지게 된 가장 큰 이유는 신경망 모델이 단층적인 모델에서 다층적인 모델로 변화했기 때문이다. 위에서 말한대로 원래 신경망 모델에 계산 비용이 많이 들어가고, 또 다층적인 모델이되면서 계산 비용이 더더욱 많이 들어가기 때문에 예전에는 슈퍼컴퓨터를 이용하지 않으면 일반인이 쉽게 결과물을 접하기 힘들었었다.

위의 예시를 보면 단층 모델(Sinlge layer)이 다층 모델(Multi layer)보다 빨리 결과물을 볼 수 있는 대신 정확도가 떨어질 가능성이 높고, 같은 맥락으로 변수 4개 들어간 들어간 다층 모델보다 2개만 들어간 다층 모델이 계산 비용이 적으면서 정확도를 손해보게 된다. 슈퍼컴퓨터에만 계산을 의존하던 시절에는 많은 변수가 들어간 다층 모델을 고르는데 매우 조심스러웠지만, 최근에 CPU (중앙처리장치)의 멀티코어를 이용하고 심지어는 GPU (그래픽 카드)의 행렬(Matrix) 연산 능력까지 활용할 수 있게 되면서 일반적인 PC 여러대를 뭉치면 괜찮은 성과를 얻을 수 있게 되었다. 알려진 내용에 따르면 구글 딥마인드의 바둑 인공지능 알파고가 이세돌 9단과 경기할 때 1202개의 CPU와 176개의 GPU를 연산에 활용했다고 한다.

정말 일반 PC로 구현할 수 있다면 집에 있는 데스크탑이나 갖고 다니는 노트북으로 구현할 수 있을까? 물론이다.

이런 수요가 많기 때문에 소프트웨어 엔지니어 (이하 “개발자”)들이 많이 쓰는 개발 언어인 Python으로 유명한 패키지(TensorFlow, scikit-learn 등)가 나와있고, 통계학자들이 많이 활용하는 R에도 아래와 같은 패키지들이 자주 쓰인다. 특히 아래 리스트의 마지막에 있는 mxnet은 Amazon이 딥러닝에 적용하는 패키지로 알려져 있고, 컨펌되지 않은 평가에 따르면 구글에서 열심히 홍보하고 있는 TensorFlow보다 성능은 더 뛰어나다고 한다. 둘의 차이점에 대해서 더 궁금하신 분은 여기를 참고하시면 된다.

딥러닝을 R로 구현하는 패키지

R Packages Backend Computing Resources
nnet C/C++ Single thread
neuralnet C/C++ Single thread
DARCH C/C++ Single thread
deepnet R Single thread
H2O JAVA Multi-threads, multi-nodes
mxnet C/C++/CUDA Multi-threads, GPUs, multi-nodes

이 중에서 CPU의 싱글 코어만 쓰는 nnet다른 글에서 Sin(X) 그래프 추적하는데 적용해봤다. (궁금하시면 링크타고가서 확인해보시면 됩니다.)

명령어들 자체의 성능이나 활용법은 약간씩 다른데, 우선 이번 글에서는 Deep Neural Network를 공개된 패키지를 쓰지않고 직접 한번 만들어볼 생각이다.

굳이 이걸 Scratch부터 만들어보는 이유는 딥러닝에 대한 일반의 오해를 종식시키고 싶은 마음에서다. 인공지능이 무슨 알아서 척척척 박사라서 모든 걸 다해낸다고 착각들을 많이하고, 특히 개발자분들이 TensorFlow로 DNN이니 CNN이니 같은 약자만 쓰면서 정작 이 모델들을 설명해라고 하면 “블랙박스(BlackBox)”라는 표현을 쓰는게 안타까워서다. 그리고 이렇게 기본부터 이해를해야 저 위에서 잠깐 언급한대로 어떤 노드(Node)는 없애거나 넣었다 뺐다를 반복하면서 계산비용을 줄이는 아이디어(Dropout)를 쉽게 이해할 수 있을 것 같아서이기도 하다.

 

1. 간략한 용어 설명

  • 입력값(Input Layer): 초기 변수
  • 결과값(Output): 결과값이 0과 1 중 하나로 나올 경우를 Classification, 그 이외의 경우는 Regression 문제로 정의한다. 쓰이는 테크닉들이 다르다.
  • 편차(Bias): 각 Layer에서 처리하지 못하는 오차값
  • 가중치(Weight): 입력값이 뉴런에게 주는 가중치
  • 뉴런(Neuron): 위의 그림에서 초록색 원들을 말한다. (다른 교과서에서는 Perceptron이라고 표현하기도 한다.)
  • Hidden Layer(s): 변수들을 처리하는 가상의 계산 단계. 층위의 숫자가 많을수록 모델의 정확도는 올라가겠지만, 계산 비용이 많이 든다

 

2. 모델이란?

보통 어떤 모델을 만들고 나면 외부에 설명할때는 활용도에 중심을 두고 설명하겠지만, 모델링을 하는 사람들 입장에서 듣고 싶은 주제는,

  1. 어떤 변수들을 입력값으로 넣었고,
  2. Hidden Layer를 어떻게 구성했으며 (M x N),
  3. 시스템의 Loss Function은 무엇이었으며,
  4. Regularization은 어떻게 활용했는지 등

이다.

딥러닝이라고 불리는 모델이 장점이자 단점인 부분은, 모델 그 자체로는 수학적인 뒷받침이 있기보다는 단순히 변수들끼리 모든 조합의 가능성을 다 고려해서 가장 높은 결과값을 찾는 단순한 연산의 반복일 뿐이라서 모델링 그 자체만 놓고보면 복잡성의 깊이는 낮다. (변수들을 넣었다 뺐다 하는 식의 업그레이드 모델은 일단 예외로 한다.) 그렇기 때문에 더더욱 Over-fitting 이슈에서 자유로울 수 없는 모델이기는 하지만, 이 문제는 다른 글에서 논의하기로 하자

뭔가 복잡한 계산이 있을 것 같지만, 실제로는 단순한 행렬 계산으로 값을 구한다. 예를 들면 2×3 행렬과 3×2 행렬을 곱하면 2×2 행렬이 나온다. 고등학교 2학년 수학이다. 실제로 이용되는 수학적 사고의 틀은 이걸로 충분하다.

아래에 간단하게 가중치와 편차(추정식의 Level-높낮이-을 지정하는 값) 행렬을 만들어봤다.

 
weight.i = 0.01*matrix(rnorm(layer.size(i)*layer.size(i+1)),
 			nrow = layer.size(i),
			ncol = layer.size(I+1))
bias.i  = matrix(0, nrow=1, ncol = layer.size(i+1))

보통은 가중치와 편차를 하나의 행렬로 묶는다.

weight  = 0.01*matrix(rnorm((layer.size(i)+1)*layer.size(i+1)),
 			nrow = layer.size(i)+1,
			ncol = layer.size(i+1))

딥러닝이라고 불리는 계산법, 혹은 Deep Neural Network 모델이 결정해야하는 값은 위의 Weight 값이 전부다. Input 데이터를 Weight 값으로 계산해주면 Output 값이 나온다. 그 Output이 누군가가 이 상품을 살지 말지에 대한 확률일수도 있고, 이세돌이 어느 점에 착점할 것이라는 예측값일 수도 있다. 그 모든 값은 바로 위의 Weight 값을 얼마나 잘 짜느냐에 달려있다.

 

3. 계산

자 이제 행렬 계산법을 응용해서 계산을 해 보자.

# 2X2 차원의 입력값 (Input, 혹은 Feature라고 불린다)
input = matrix(1:4, nrow = 2, ncol = 2)

# 2x3 차원의 가중치
weights = matrix(1:6, nrow = 2, ncol =3)
# 1x3 차원의 오차항
bias = matrix(1:3, nrow = 1, ncol = 3)

# 입력값 x 가중치 + 오차항
input %*% weights + bias

# 당연히 차원이 맞지 않아 계산이 안 된다

입력값이 1×2 행렬이었으면 입력값 x 가중치가 1×3 행렬이 되어서 덧셈이 정상적으로 진행되었겠지만, 위의 경우에는 아마 에러값이 튀어나올 것이다. 이럴 때 머신러닝에서 이용하는 해결책은 크게 두 가지다. 첫번 째는 그냥 단순하게 오차항을 두 번 쓰는 것이다. 물론 이렇게하면 메모리를 많이 소모하므로 (Computational cost가 비싸다), Sweep 이라는 함수를 이용한다. 두 방법의 결과는 같다.

 
# 해결책 1: 오차항을 두번 반복해서 2X3을 만듦
s1 = input %*% weights + matrix(rep(bias, each=2), ncol=3) 
 
# 해결책 2: sweep 함수 이용
s2 = sweep(input %*% weights, 2, bias, '+')
 
all.equal(s1, s2)
[1] TRUE

 

4. Prediction 에 적용

아주 간단하게 Prediction에 적용하는 함수를 만들어 보자. 참고로 아래에 쓰인 pmax 함수는 Global Max 값이 아니라 Local Max 값을 찾는데 쓰이는 함수다. Neural Network를 비롯한 모든 머신러닝 함수는 수학식으로 나온 값을 찾는게 아니라 (Closed-form solution이라고 부른다), 시행착오(Trial-and-error)를 거치면서 값을 찾는데, 함수가 한 방향으로 증가, 감소하는게 아니라면 시작점이 어디냐에 따라 다른 값을 찾게 되는 경우가 있다.

말을 바꾸면, 시작점이 어디냐에 따라 결과값이 바뀌는 경우가 생길 수 있는데, Global Max 값을 찾는데 계산비용이 많이 들기 때문에 아래처럼 Local Max 값을 찾는 함수를 쓰는 경우가 많다.

# Prediction
predict.dnn = function(model, data = X.test) {

  # 데이터를 행렬로 변형
  new.data = data.matrix(data)
 
  # sweep을 이용해 hidden layer의 차원을 맞춰줌
  hidden.layer = sweep(new.data %*% model$W1 ,2, model$b1, '+')

  # hidden.layer 값 중 최대치를 골라서 score -예측값-를 찾는데 적용
  hidden.layer = pmax(hidden.layer, 0)
  score = sweep(hidden.layer %*% model$W2, 2, model$b2, '+')
 
  # Loss Function: softmax (cross-entropy 라고도 알려져 있다)
  score.exp = exp(score)
  probs = sweep(score.exp, 1, rowSums(score.exp), '/') 
 
  # 가장 확률이 높은 경우를 고른다
  labels.predicted = max.col(probs)
  return(labels.predicted)
}

 

5. Neural Network를 훈련(Training) 시키는 함수 생성

데이터를 훈련(Training)에 쓰는 방식을 결정하는 함수를 만들어보자. 여기서는 Hidden layer를 몇 개로 할지, Learning rate을 얼마로 잡을지 등등을 지정할 수 있다.

train.dnn = function(x, y, traindata=data, testdata=NULL,
                  # hidden layer과 변수의 갯수를 고름 (아래는 1x6으로 지정)
                  hidden = c(6), 

                  # 최대 반복 수치 지정 (클수록 속도를 희생하고 정확성을 끌어올린다)
                  maxit=2000,

                  # delta loss (반복할 때마다 약간씩 수정하는 값의 크기)
                  abstol=1e-2,

                  # learning rate (학습 속도, 작을수록 느리지만 대신 정확하다)
                  lr = 1e-2,

                  # regularization rate (추가적인 feature를 도입하는 속도, 각 feature간 cross-product, 제곱 등이 쓰인다)
                  reg = 1e-3,

                  # 총 100번의 결과값을 보여준다
                  display = 100,
                  random.seed = 1)
{
  # seed를 지정하면 같은 seed 값에서 같은 결과값을 볼 수 있다
  set.seed(random.seed)
 
  # 훈련 집합의 전체 크기
  N = nrow(traindata)
 
  # 데이터 불러오기
  X = unname(data.matrix(traindata[,x]))
  Y = traindata[,y]
  if(is.factor(Y)) { Y = as.integer(Y) }

  # 행과 열에 각각 인덱스 생성 
  Y.len   = length(unique(Y))
  Y.set   = sort(unique(Y))
  Y.index = cbind(1:N, match(Y, Y.set))
 
  # input 값들
  D = ncol(X)

  # classification이 적용되는 값의 종류 (ex. 0과 1)
  K = length(unique(Y)) # unique(Y) 값이 100개라면 총 100개의 가능성으로 결과값을 보여준다 (사과, 배, 참외 등등등)
  H =  hidden
 
  # 최초 시작점을 정한다 - local max를 찾을 때 최초 시작점을 어떻게 정하느냐가 매우 중요하다. 
  W1 = 0.01*matrix(rnorm(D*H), nrow = D, ncol = H)
  b1 = matrix(0, nrow = 1, ncol = H)
 
  W2 = 0.01*matrix(rnorm(H*K), nrow = H, ncol = K)
  b2 = matrix(0, nrow = 1, ncol = K)
 
  # Training 데이터 전체를 Training에 활용 (N을 그룹으로 쪼개서 bagging을 할 수도 있음)
  batchsize = N
  loss = 100000
 
  # Training 데이터를 NN에 적용
  i = 0
  while(i = maxit && loss > abstol ) {
 
    # iteration index
    i = i +1
 
    # forward 로 계산 (아래에 backward로 계산도 있음)
    # 1은 행, 2는 열
    hidden.layer = sweep(X %*% W1 ,2, b1, '+')

    # neurons : ReLU
    hidden.layer = pmax(hidden.layer, 0)
    score = sweep(hidden.layer %*% W2, 2, b2, '+')
 
    # softmax (Loss function을 지정. Cross entropy라고도 불린다)
    score.exp = exp(score)
    probs = sweep(score.exp, 1, rowSums(score.exp), '/') 
 
    # Loss 값을 찾는다 - 매우 작은 값이 나와야 모델이 제대로 "학습"한 것이다
    corect.logprobs = log(probs[Y.index])
    data.loss  = sum(corect.logprobs)/batchsize
    reg.loss   = 0.5*reg* (sum(W1*W1) + sum(W2*W2))
    loss = data.loss + reg.loss
 
    # 결과값을 보여줌
    if( i %% display == 0) {
        if(!is.null(testdata)) {
            model <- list( D = D,
                           H = H,
                           K = K,
                           # 가중치와 오차
                           W1 = W1, 
                           b1 = b1, 
                           W2 = W2, 
                           b2 = b2)
            labs = predict.dnn(model, testdata[,-y])
            accuracy = mean(as.integer(testdata[,y]) == Y.set[labs])
            cat(i, loss, accuracy, "n")
        } else {
            cat(i, loss, "n")
        }
    }
 
    # backward 로 계산
    dscores = probs
    dscores[Y.index] = dscores[Y.index] -1
    dscores = dscores / batchsize
 
 
    dW2 = t(hidden.layer) %*% dscores 
    db2 = colSums(dscores)
 
    dhidden = dscores %*% t(W2)
    dhidden[hidden.layer == 0] = 0
 
    dW1 = t(X) %*% dhidden
    db1 = colSums(dhidden) 
 
    # update ....
    dW2 = dW2 + reg*W2
    dW1 = dW1  + reg*W1
 
    W1 = W1 - lr * dW1
    b1 = b1 - lr * db1
 
    W2 = W2 - lr * dW2
    b2 = b2 - lr * db2
 
  }
 
  # 최종 결과값

  model = list( D = D,
                 H = H,
                 K = K,
                 # 가중치와 오차
                 W1= W1, 
                 b1= b1, 
                 W2= W2, 
                 b2= b2)
 
  return(model)
}

 

6. 데이터로 테스트 해보기

흔히들 쓰는 R의 기본 데이터인 iris 데이터로 모델 테스트를 진행해보자.

 
# 0. 데이터 불러오기
summary(iris)
plot(iris)
 
# 1. Test 셋과 Training 셋으로 데이터 분리
samp = c(sample(1:50,25), sample(51:100,25), sample(101:150,25))
 
# 2. 모델 훈련 시키기
ir.model = train.dnn(x=1:4, y=5, traindata = iris[samp,], testdata = iris[-samp,], hidden=6, maxit=2000, display=50)
 
# 3. 예측값 계산
labels.dnn = predict.dnn(ir.model, iris[-samp, -5])
 
# 4. 결과 확인
table(iris[-samp,5], labels.dnn)
# labels.dnn
# 1 2 3
#setosa 25 0 0
#versicolor 0 24 1
#virginica 0 0 25
 
# 정확성 확인
mean(as.integer(iris[-samp, 5]) == labels.dnn)
# 0.98

위의 그래프에서 확인할 수 있듯이, 훈련이 반복될수록 loss값은 작아지고, 정확도는 높아진다. 약 1000번의 훈련이 반복되고 나면 정확성이 거의 100%에 수렴하는 것을 확인할 수 있다.

 

7. R에서 가장 많이 쓰이는 Neural Network 패키지 중 하나인 nnet와 결과값 비교

 
library(nnet)
ird = data.frame(rbind(iris3[,,1], iris3[,,2], iris3[,,3]),
			species = factor(c(rep("s",50), rep("c",50), rep("v",50))))
ir.nn2 = nnet(species ~ ., data = ird, subset = samp, size = 6, rang = 0.1, decay = 1e-2, maxit = 2000)
 
labels.nnet = predict(ir.nn2, ird[-samp,], type="class")
table(ird$species[-samp], labels.nnet)
# labels.nnet
# c s v
#c 22 0 3
#s 0 25 0
#v 3 0 22
 
# accuracy
mean(ird$species[-samp] == labels.nnet)
# 0.96

필자가 직접 만든 Neural Network 계산식과 널리 쓰이고 있는 R 머신러닝 패키지 nnet의 결과값이 96%정도 일치하는 것으로 나왔다. 100%가 나왔으면 같은 방식으로 계산하는구나라고 생각하겠지만, 약간 차이가 나는 것을 보니 nnet 패키지에 뭔가 다른 계산법이 숨겨져 있는 것 같다.

 

8. 나가며

위의 계산이 대단히 복잡해보이거나 어렵다고 생각할수도 있지만, 사실은 고등학교 수학시간에 배운 행렬 계산의 반복에 불과하다. 쫄지말자.

얼마전에 친구 하나가 왜 그래픽 카드가 그렇게 불티나게 팔리는지, 그래픽 카드가 왜 머신러닝의 핵심인지 묻더라. 그래픽 카드는 계산을 빨리하는 도구가 아니라, 3차원 그래픽을 위해서 행렬을 계산하는 보조 계산 장치라고 생각하면 된다. 근데 i5니 i7이니 불리는 CPU보다 계산속도는 늦어도 메모리는 엄청나게 커서 3차원의 행렬 계산을 한꺼번에 잘 처리해준다. 위에서 봤듯이, 딥러닝의 핵심은 행렬 계산을, 그것도 엄청나게 큰 데이터에서 한꺼번에 처리하는 능력이다. 그래서 그래픽 카드가, 특히 성능 좋은 그래픽 카드가 갑자기 불티나게 팔려나가는 것이다.

(물론 비트코인을 비롯한 가상화폐 채굴 때문이기도 한데, 가상 화폐 채굴에 쓰이는 모듈도 대규모 데이터를 행렬로 계산하는 능력이 중요해서 그렇다.)

 

데이터 사이언스, 데이터 과학, 데이터 과학자, 머신러닝, 딥러닝, R

주변에서 데이터 사이언티스트가 될려면 어떤 공부를 해야되는지 묻는 경우가 참 많다. 전에 다녔던 크리테오라는 프랑스 리타게팅 광고 회사의 세일즈 팀 사람들은 Data Analyst 와 Data Scientist가 무슨 차이가 있는지 잘 모르고 날 더러 그냥 데이터 팀 사람이라고 불렀고, 그 중 누구는 자기가 데이터 쪽으로 커리어를 전환하고 싶은데, 나더러 Data Scientist가 될려면 어떻게 해야되냐고 묻더라. 개발자 중에서 요즘 머신러닝 공부하고 있는데, 무슨무슨 수업 다 듣고나면 이제 자기는 데이터 사이언티스트 되는거 아니냐고 하는 경우도 있었다. 필자가 심심하면 찾아가는 모 랩탑 브랜드 커뮤니티에는 자기가 Udemy의 Data Analysis 수업을 듣고 있는데, 앞으로 데이터 사이언티스트가 될려면 뭘 더 공부해야되냐고 느닷없이 개인 메세지를 보내시는 분도 있었다.

간단히 정리하면, 그런 질문 하시는 분들 정말, 진짜 너무너무너무 많다.

솔직한 대답을 드리자면, “저도 몰라요.”가 될 것 같다.

필자의 백그라운드는 Economics 학부, Financial Econometrics 석사, 그리고 Financial Math 박사 출신이고, 석사 시절부터 연구 주제 탓에 MCMC Simulation과 Network Theory라는 통계학과 수학의 한 분야를 깊게 공부했었다. 중간에 친구따라 강남가는 꼴로 연구실 근처에 있는 MIT에서 머신러닝 수업 몇 개듣고 이거 내가 알고 있던 Simulation이랑 같은 수리통계학 쓰고 있구나고 이해했던 정도가 전부다. 처음 데이터 사이언스 쪽 직장을 찾던 시절 쓸 수 있었던 프로그래밍 스킬셋이라고 해봐야 R이랑 Matlab이 내세울 수 있는 수준이고, SQL은 정말 기본만 했다. 다른 코딩 언어는 쓸 줄 모르기는 커녕, 별로 들어본 적도 없는 적이 많았다.

회사 가보니 Data Visulization에 필요한 Tableau라는 프로그램을 가르쳐주길래 공들여 배웠고, 데이터 베이스 접근하는 것 때문에 SQL도 Hive 알게되고, 나아가서는 Hadoop 어떻게 쓰는지까지 공부를 하게됐다. 사실 더 깊게 Data Engineer들 쪽 영역에 손을 댔으면 Spark 같은 것도 공부했으면 됐을텐데, 필자의 주 업무가 Data Science였지 Engineering 쪽이 아니어서 크게 관심을 두질 않았고, 솔직히 공부할 시간도 없었다. 당장 Data Science 업무에 쏟을 시간도 부족했었으니까. (개인적인 백그라운드가 더 궁금하신 분은 링크의 제일 하단을 참조)

시장에서 요즘 데이터 사이언티스트를 많이 뽑는데, 정작 사람은 없다보니까 다양한 분야 출신들을 모으고 있고, 그러니까 더더욱 뭐가 Data Science인지 잘 모르는 상태가 지속이 된 것 같다. 그 탓에 배울 지식이 엄청나게 많아보이는 착시효과가 있는데, 이건 꼭 알았으면 좋겠다 싶은 내용들을 한번 정리해보자.

 

1. R or Python

이 두 개의 프로그래밍 언어는 시장의 스탠다드가 되어버렸다. 둘 중 어느쪽이 더 우월하냐고하면 필자도 대답을 못하겠고, 굳이 따지자면 R 쪽이 약간 더 쉬울 것 같기는 하다. 대체로 R은 통계학 출신들이 많이 쓰고, Python은 개발자 출신들이 많이 쓴다. 실제로도 Python for production 이라고 이야기들 하고, R for deep statistical analysis 라고 이야기 한다. Random sampling 같은 조금 깊은 통계 지식을 활용하고 싶으면 Python에는 없는 기능들이 R에 잘 구비되어 있고, 반대로 결과물을 프로그램에 얹으려고하면 Python만한 프로그래밍 언어가 없는 것 같다.

데이터 사이언스에서 R vs. Python으로 무슨 경쟁체제인 것처럼 몰아가는데, 둘이 상호 보완적인 관계라고 하면 더 맞지 않을까?

아뭏튼, 최소한 둘 중 하나는 잘 해야 어디가서 데이터 사이언티스트라고 할 수 있다. 관련해서 더 궁금하신 분은 여기에서부터 글타래를 따라가시면 된다.

 

2. 머신러닝

데이터로 뭘 하는걸 사이언스라고 주장하는 학문 분야가 나오게 된 가장 중요한 두 가지 “혁명”을 꼽으라면 빅데이터머신러닝을 들 수 있다. 굳이 따지자면 빅데이터가 있기 때문에 머신러닝이 의미가 있는 것이기는 하지만, 기술적인 지식이라는 측면에서 빅데이터를 배울 수는 없으니 (데이터를 어떻게 “배우”냐? 데이터를 활용하는 “지식”을 “배워”야지 ㅋㅋ “머신”이 “러닝”하게 양보하자), 당연히 머신러닝이 데이터 사이언스 업무의 필수 스킬셋이 될 수 밖에 없다. (참고, 빅데이터 만드는 백엔드 개발자는 또 다른 종류의 직업이다)

이쪽 업계에서 R이나 Python 같은 프로그래밍 언어가 크게 성공한 가장 중요한 이유가 바로 머신러닝을 실제로 돌려볼 수 있는 몇 안 되는 무료 패키지기 때문이다.

이미 많은 오픈소스 코드들이 돌아다니기 때문에, 코드를 적용한다는 측면에서 난이도는 그렇게 높지 않다. 솔찍말 학부 통계학 + 회귀분석 정도 알고 있으면 따라가는데 아무 문제 없다. 대부분의 경우에 학부 통계학과 회귀분석을 몰라서 문제고, 그 코드들이 작동하는 배경지식이 없어서 문제더라. (강의 초점을 모델 속에 숨겨진 직관직관을 받쳐주는 수학&통계학에 집중하는 것도 같은 이유다.)

학부 때 컴퓨터 사이언스 해야되는거 아니냐고? 아니다. 정말 아니다.

차라리 로봇 공학, 기계, 산업 공학 같은 공대 전공이나, 경제학 같은 전공이 훨씬 더 적합한 전공이다. 이런 전공들도 학부, 석사 시절에 코딩 많이하고, 문제에 대한 접근 방식이 수학 식을 세우고, 그 문제를 풀어나가는 방식이고, 뭔가 잘못 되었을 때는 코드만 쳐다보는게 아니라 수식도 함께 쳐다본다. 굳이 차이가 있다면 수학을 코딩하는 거지, 개발자들처럼 IT 시스템의 일부를 구현하는 코딩을 하지 않는 것일 뿐이다. 머신러닝도 같은 방식으로 수리 통계학 모델링이 들어간 코딩이다.

그럼 학부 때 수학이랑 통계학 엄청 공부했어야되는거 아니냐고? 그냥 학부 1-2학년 수준의 수학 지식에 3학년 정도에 배우는 회귀분석만 알면 된다. 이건 필자의 수업을 들은 이학, 공학, 경제학 “박사 학위자”들의 공통적인 의견이다.

물론 머신러닝에 대해서 깊이 있게 알려면 아래 3번을 보자.

 

3. 수리통계학

개발자들이 하는 가장 큰 착각이 바로 머신러닝은 공학이지 수학이 아니라는 거다. 여기에 필자는 정면으로 반박하고 싶다.

머신러닝은 수학이지 공학이 아니다. 머신러닝 입문자들이 듣는 Coursera의 Andrew Ng 아저씨 수업을 평가한 Quora 글을 보라.

“What most people missed was that they approach machine learning as some kind of programming project, while in reality it is actually more of an applied math project with computational approach, and they really needed to focus on learning the math, which, in this case, is linear algebra.”

머신러닝은 한 단어로 요약하면 응용수학이다. 선형대수학과 회귀분석을 섞고, 발전하다보니 더 고급 수학과 더 고급 통계학을 쓰는 그런 응용수학이다. 필자가 데이터 사이언티스트라고 소개하니 머신러닝을 공학이라고 생각해서 “어, 공대 출신이 아닌데 어떻게 데이터 사이언티스트에요?” 라고 질문하는 벤쳐 캐피탈리스트도 있더라.

정리하면, 확률론 + 선형대수 + 회귀분석이 머신러닝의 근간이고, 이걸 정말 잘하고 싶으면 대학원 수준의 수학과 통계학을 공부해야한다. 수업 왔던 개발자들 일부가 커리어를 접고 대학원으로 방향을 튼 이유도 같은 맥락일 것이다. 그냥 갖다 쓰는 방식의 개발과는 근본적으로 다른 학문이라는 걸 이해했기 때문일 것이고, 데이터 사이언스 영역에서 인정받고 싶다면 수리통계학 깊이가 깊어야한다는 사실을 깨달으셨기 때문일듯.

당장 빅데이터로 작업할려고 전처리할 때 Dimensionality reduction에 쓰이는 PCA나 FA 분석이 어떻게 작동하는지 머신러닝한다는 개발자들에게 물어봐라. 둘 중 어떤 방법을 써야하는지도 모르고, 프로그램에서 디폴트로 되어 있는 PCA만 쓰고 있을 것이다. (애시당초 “그냥 데이터 크기 줄여주는거 아냐?” 수준을 너무 많이 봤다.) PCA는 Total variation을 볼 때, FA는 변수들간 C0-variation을 볼 때 쓰는 테크닉들이다. 이 차이를 몰라서 학계에서 유명한 통계 패키지 하나가 계산을 거꾸로 하던 바람에 1990년대에 쓴 모든 논문을 다시 써야하는 사건들도 발생했다.

왜? variance & co-variance matrix하나 그려놓고, 모든 숫자를 다 쓰는지, off-the-diagonal term만 쓰는지에 따라 연구 결과는 당연히 다르게 나올 수 밖에 없다. 심지어는 자기 논문을 스스로 비판하고 저널에서 내려달라고 하던 연구자도 있을 정도였다.

무슨 말인지 잘 모르겠다고? 근데 이거 사실 자연계열 1, 2학년 선형대수학 수업에서 배운 지식이다. (그리고 필자가 페이X북 Data Scientist 면접볼 때 나왔던 질문이기도 하다.)

 

4. 데이터 시각화 패키지 지식

빅데이터 분석을 해서 결과값을 보여주는데 최적화된 수 많은 툴들이 있다. 당장 R로도 예쁜 그래프들 참 많이 그릴 수 있는데, 필자는 회사 생활하면서 Tableau로 작업하는 걸 배웠다. 좋더라. 비싸서 그렇지. 그리고 가격경쟁력이 좀 더 있는 툴로 Qilk도 써 본적이 있다. 왜 기업들이 Tableau 쓰는지 알겠다는 생각도 들었고, 이것도 참 쓸만하다는 생각도 했다.

사실 R로 작업하기로 맘 먹으면 Java Script로 그래프 만드는 걸 처리해주는 패키지도 있기 때문에, 왠만한 일들은 다 R로 해결할 수 있다. 당장 Sankey, Sunburst 같은 그래프들 한번 그릴려고 시도해봐라. 아마 아직도 R말고 다른 패키지로 저런 복잡한 그래프를 쉽게 Customizing 하기 힘들 것이다.

 

5. Data Modeling 센스

마지막으로 가장 중요한 스킬 셋이다. 위에 4가지 지식은 사실 없으면 배우면 된다. 그런데 Data Modeling 센스 없으면 데이터로 분석을 요하는 그 어떤 종류의 직업도 할 수가 없다. (아마 데이터 서버 관리하는 엔지니어가 이쪽 관련 직군에서 유일한 대안이 아닐까 싶다.)

가장 단순하게는 엑셀로 그래프를 잘 그려서 보는 사람들이 한번에 쉽게 이해할 수 있도록 해 주는 것도 포함될 것이고, 복잡하게는 어떤 통계 모델로 가설들을 검증할 수 있을지에 대한 아이디어들이 포함된다.

필자가 석사 처음들어가서 논문을 쓸 때였다. 논문 주제로 뭐 하나를 가져갔다가 입이 좀 걸걸한 교수님께 거의 모욕 수준의 평가를 듣고 나왔는데, 지금 생각해보면 그 교수님 그래도 착했다는 생각이 든다. 이런 말도 안 되는거 하지말고 주제 제대로 잡으라고 리스트를 쭈욱 뽑아주셨는데, 그 예시들 보면서 정말 눈물이 찔끔 나더라. 주제라고 뽑아주시는 리스트에 있는 내용들을 어떻게 테스트하는지 다 수업시간에 배웠는데, 실제로 이런 방식의 테스트를 하겠다는 생각을 그전까지 한번도 해 본 적이 없었다.

비싼 돈 내고 공부하러와서 여태까지 답답한 공부만 하고 있었다는 쪽팔림에 속으로 울었다.

그 분수령같은 사건 이후로, 어떤 통계 모델을 써서 테스트해야되겠다는 그림을 머릿속으로 그릴 수 있게 되었으니 그 교수님께 참 감사해야된다 싶다. (물론 그 때 같이 수업 들었던 친구들 대다수가 아직도 그 교수님의 독설에 많은 반감을 갖고 있기는 하다 ㅋㅋ)

다른 글에서 소개한 한국 제약산업의 리베이트 관행에 대한 분석, 스페인 은행들 Network 분석, 나중에 연구지원금 받고 손을 댔던 Systemic externality 논문들 모두가 결국엔 그 날 모욕을 들으면서 얻은 Data Modeling 센스에서 나왔지 않을까 싶다. 필자가 운영하고 있는 강좌 중 가장 어려운 수업이 바로 데이터 사이언스 모델링에 관한 수업이다. 당연하겠지만, 이런 내용을 이해하려면 통계학, 머신러닝 전반에 대한 여러 지식이 필요하고, 수요만큼 공급이 없는 스킬셋이다.

 

사실 이 중에 3개만 잘 갖추고 있어도 데이터 사이언티스트로 먹고사는데 별 지장없을 것 같기는 하다 ㅋㅋ

신경망 모델 예시 (Source: Wikipedia)

요즘 인터넷 기사들을 보면 머신러닝이라는 단어보다, 신경망(Neural network) 모델이라는 (좀 더 전문적인 스멜이 나는) 단어를 쓰기 시작했다. 그리고 이 단어가 “딥러닝”이라는 개념과 맞닿아있다는 걸 인지하고 있는 기자들도 많이 늘어난 것 같다. (뿌듯하다 ㅋㅋ)

그런데 정작 신경망 모델이 어떻게 돌아가는지 제대로 된 작동 원리를 전혀 모르는지 기자하는 친구들이 필자에게 가끔 물어보는 전화가 온다. 전화로 설명하기가 좀 힘들어서 Wikipedia에 그림하나를 보내줬는데, 그래도 이해가 잘 안 된단다. 신경망이나 딥러닝이 뭔지에 대해서 말들은 많이 하는데, 정작 이걸 어떻게 쓰는건지 아는 사람은 별로 없다.

왼쪽에 있는 그림이 신경망 모델의 예시다. 그래도 뭔 말인지 잘 모르겠다고?

일단 Input이 3개가 있고, Output이 2개가 있다. 일반적인 모델은 3개의 변수가 2개를 어떻게 설명하는지만 본다. 신경망 모델은 3개의 변수가 서로 어떻게 영향을 줬고, 그래서 뭔가 가공된 지식(그림에서는 Hidden으로 된 4개의 가상 변수)이 생기고, 그 가공된 지식이 2개 결과물이 어떤 영향을 주는지를 본다. 인간의 인지도 실제로 사물의 부분들을 조합해서 어떤 이미지를 만들고, 그 이미지가 기억 속에 있는 특정 사물과 매칭되는 것을 찾아서 어떤 사물을 봤다고 최종적으로 인식한다. (노트북의 스크린, 키보드, 터치 패드를 각각 보고 난 다음에, 저렇게 3개가 다 있는 건 PC도 아니고, 스마트폰도 아니고, 노트북이라고 인식하는거다)

보통 이걸 어떻게 쓰는지 설명할 때 사진에 있는 고양이를, 차를, 사람을 인식하는데 쓰는데 그런 뻔한 사례말고 좀 복잡한 그래프 인식하는 예시를 보자. (우리는 Machine learner가 아니라 Data scientist니까!)

 

1. 신경망 모델 예시 – Sin(X) 그래프

y= sin (x) 라는 그래프를 한번 생각해보자. 학부 시절도 아니고 새파란 고딩 때 한번쯤 배웠던 기억이 날 것이다. x가 0에서 2π 사이를 오가는 동안 y 값은 0에서 1까지 올라갔다가, -1까지 내려갔다가, 다시 0으로 돌아오는 주기(cycle) 함수다. 요렇게 생겼다.


일정하게 증가, 감소만 하는 함수는 찾기가 쉽다. 그런데 이런식으로 오르락, 내리락이 반복되면 찾기가 좀 어렵다. 특히 선형 모델로 이런 주기 함수의 matching function (매칭 함수)을 찾아내는 건 거의(가 아니라 완전히) 불가능 하다.

그런데 자연계의 많은 사건들이 한방향으로 증가, 감소만 하지는 않는다. 이래서 통계학과 학부 2학년 때 회귀분석에서 배운 지식만으로는 matching function을 찾는게 불가능한 것이다.

 
x<-runif(200, -10, 10)
y<-sin(x)

 

위의 그래프처럼 -10에서 +10까지 값을 200개 뽑아서, Sin(X) 값을 찾아준다.

 
index<-sample(1:length(x),round(0.75*length(x)),replace=FALSE)
reg.train<-data.frame(X=x[index],Y=y[index])
reg.test<-data.frame(X=x[-index],Y=y[-index])

더 깊게 들어가기 전에 솔직하게 말하면, 신경망 모델 (Neural Network)은 좀 구린 모델이다. 내가 갖고 있는 데이터에서는 완벽하게 맞아들어가는 함수식을 찾아내도, 정작 새로운 데이터에 적용하면 결과값이 엉망으로 나오는 경우다 매우 흔하다. (이걸 Over-fitting, 과적합 문제라고 한다.)

그래서 일부의 데이터를 샘플로 뽑아서 훈련 집합 (Training set)으로, 나머지를 검증 집합 (Test set)으로 나눈 다음, 신경망으로 찾아낸 모델이 새로운 데이터에도 얼마나 잘 맞는지 검증을 해 보는 작업이 필수적이다. (특히 위의 사례처럼 아래 위로 움직임이 많은 주기 함수의 경우, 이런 작업을 거치지 않은 함수식은 십중팔구 시간 낭비가 될 가능성이 높다.)

위의 코드를 간략하게 설명하면, length(x)는 x에 있는 데이터 개수(200개)를 가르쳐주고, 1:200은 모든 데이터 셋을 골랐다는 뜻이다. 각 데이터별로 번호표가 붙어있을테고 (1번부터 200번), 그 중에서 75%를 샘플로 뽑는데, 이미 뽑았던 숫자는 다시 안 뽑는다는 의미에서 replace = FALSE가 들어가 있다. (Lotto 번호 고를 때 같은 번호를 2번 고르진 않는다.)

이렇게 데이터 각각에 번호를 붙여서 index라는 변수로 만들고, reg.train이라는 변수에는 75%의 샘플을 집어넣고, reg.test라는 변수에는 나머지 25%를 넣는다. (-index는 index에 안 들어간 숫자들 전체를 말한다.)

 
library(nnet)
weight<-runif(10, -1, 1)
reg.model.1<-nnet(reg.train$X,reg.train$Y,size=3,maxit=50,Wts=weight,linout=TRUE)

 

자 이제 신경망 모델을 돌려보자. R이 좋은게, 다양한 (이라 쓰고 헤아릴 수 없이 많은) 패키지들이 존재하고, 덕분에 사용자가 새로 함수를 만들어야할 경우가 매우 적다. 여기서도 어떤 천재가 만들어놓은 신경망 모델 패키지 (nnet)를 불러와서 쓰자.

3번째 라인은 nnet을 이용해서 훈련 집합 (Training Set, 우리의 경우는 reg.train이었다.) 의 변수 X와 Y를 넣는다. 그리고 size = 3 은 Hidden으로 된 가상 변수를 3개만 쓰겠다는 뜻이고, maxit는 iteration (반복작업, Trial-and-error를 몇 번 반복할지를 정한다)를 50번을 한다는 뜻이다. 여기서 iteration을 5000번 정도 하겠다고 설정하면 정확도는 매우 올라가겠지만, 아마 500번으로 한 것보다 시간은 몇 백배 더 걸리면서 정확도는 아주 쪼~끔 더 좋아질 것이다. 딱 정해진 숫자는 없고, 데이터마다 다른 숫자를 넣으면 되는데, 한 50번, 100번 올려가면서 얼마나 값 차이가 나는지 확인해보면 된다.

linout은 여러개 값이 가능한 경우를 모두 고려해라는 뜻인데, 하나 짚고 넘어가보자. weight는 각 변수에 얼마만큼의 가중치를 준 상태에서 모델을 테스트해볼지를 정하는 것이다. 글 처음에 나왔던 신경망 모델의 그림을 기준으로, 각각의 화살표 (3개 원에서 4개 원으로 가는 방향의 선들)에 어느만큼의 가중치를 줄 것인지인데, 이 가중치에 따라서 중간에 있는 가상 변수들이 최종 결과값에 미치는 가중치 값이 달라진다. 그리고, 그 가중치 값에 따라서 최종 결과값도 달라질 수 있다.

쉽게 말하면, 가중치를 얼마로 잡느냐에 따라 완전히 다른 결과값을 얻을 수도 있다. linout = TRUE는 그런 경우를 모두 고려하겠다는 뜻이다.

 
predict.model.1<-predict(reg.model.1,data.frame(X=reg.test$X))

 

자, 이제 R의 predict라는 함수를 활용해서, 위에 찾은 모델이 검증 집합(Test Set)에서 얼마나 좋은 결과를 보여주는지 살펴보자. predict라는 함수 자체가 잘 만들어져서 사실 복잡한 부분은 없다. 내 모델 (reg.model.1)을 지정하고, 데이터(data.frame(X=reg.test$X))를 지정해주면 된다. 여기서 data.frame은 숫자들을 데이터 프레임으로 만들어주는 함수고, reg.test$X는 reg.test라는 변수에서 X값들 (-10에서 +10사이 200개 값들 중 25%)을 지정해주는 거다.

 
rmse.reg<-sqrt(sum((reg.test$Y-predict.model.1)^2))

 

이제 Mean-Squared Error (MSE)라는 방법으로 (평균과 분산을 통합해서 예측값이 실제값이랑 얼마나 멀리 있는지 확인하는 방법) 위의 신경망 모델이 얼마나 잘 예측을 했는지 검증하면 된다.

 
plot(sin, -10, 10)
points(reg.test$X,predict.model.1)

 

plot of chunk NN_part1

실선은 위에서 보여준 Sin(X) 그래프고, 점선은 신경망 모델의 예측값이다. 보시다시피 0이상에서는 좀 맞는데, 0 미만에서는 완전히 틀렸다. (예상했던 바다.)

 
reg.model.2<-nnet(reg.train$X,reg.train$Y,size=7,maxit=50,Wts=runif(22, -1, 1),linout=TRUE)

똑같은 작업을 하는데 이번에는 모델을 살짝만 바꿔보자. 일단 Hidden에 나오는 가상 변수를 3개에서 7개로 늘린다. 다시말해서 3개의 특징만을 잡아내다가, 이제 7개의 특징을 잡아내겠다는거다. 당연하겠지만 좀 더 정확한 결과값이 나올 (가능성이 높을) 것이다. 그리고 Weight (가중치)를 좀 더 세분화했다.

 
predict.model.2<-predict(reg.model.2,data.frame(X=reg.test$X))
rmse.reg<-sqrt(sum((reg.test$Y-predict.model.2)^2))
plot(sin, -10, 10)
points(reg.test$X,predict.model.2)

 

다시 predict를 써보고, MSE 값을 구해서 예측이 얼마나 정확한지 살펴보자. 그리고 아래는 그래프다.

plot of chunk NN_part1

아까보다 훨씬 더 정확성이 높아졌다. 물론 아직도 완벽하지는 않다.

2. 신경망 모델 예시 – iris 데이터 셋

자 보통 R에서 데이터로 뭔가 작업을 하는 예시를 보여줄 때 제일 많이 쓰는 데이터가 iris 데이터다. 필자의 수업을 오면 필자가 만든 시뮬레이션 데이터를 이용할 수 있지만, 일단 여기서는 R의 기본 데이터 셋을 이용해보자.

 
data<-iris
scale.data<-data.frame(lapply(data[,1:4], function(x) scale(x)))
scale.data$Species<-data$Species
index<-sample(1:nrow(scale.data),round(0.75*nrow(scale.data)),replace=FALSE)
clust.train<-scale.data[index,]
clust.test<-scale.data[-index,]

 

iris 데이터를 불러와서 data 변수에 입력한 다음, 저 위 섹션 1에서 했던 것과 같은 작업을 했다. lapply를 쓴 이유는 데이터의 1행에서 4행까지의 숫자들에 scale 함수를 지정한 값을 얻기 위함이다. 왜 scale을 쓰냐고? 변수의 스케일을 맞춰주기 위함이다. (0.001 ~ 0.01 사이를 움직이는 값과, 100~1000을 움직이는 값이 사실은 10배 구간을 움직인다는 점에서 같다.)

length(x) 대신에 nrow(scale.data)를 썼는데, nrow는 데이터의 행의 갯수 값을 주는것이라서 여기서는 사용 예시가 같다.

 
clust.model<-nnet(Species~.,size=10,Wts=runif(83, -1, 1),data=clust.train)
predict.model.clust<-predict(clust.model,clust.test[,1:4],type="class")
Table<-table(clust.test$Species ,predict.model.clust)
accuracy<-sum(diag(Table))/sum(Table)

 

자, 위의 섹션1에서 봤듯이, Hidden 가상 변수의 숫자가 늘어나면 모델이 정확해지고, weight도 세분화되면 좀 더 정확한 결과값이 나온다. 여기서는 가상 변수가 10개, weight의 구간이 무려 83개다. predict 함수로 예측값을 찾고, 그걸 Table로 정리하고, 마지막으로 정확도(accuracy)를 측정해본다.

끝으로

여기서 데이터가 계속해서 흘러들어오고, 시간 단위로 모델을 업데이트 시키면 우리가 일반적으로 알고 있는 머신이 알아서 배우는 신경망 모델이 되고, Hidden 가상 변수를 복잡하게 셋팅하는걸 “딥러닝”이라고 바꿔서 부른다. (물론 “딥러닝”의 구체적인 정의는 학자마다 조금씩 다를 수 있다.)
이런 간단한 예제말고, 실제 데이터에 적용하는 사례를 보고 싶으면 필자의 수업에 찾아오면 된다 ㅋ

문제 소스, 코드 소스