다시 돌아보는 프로그래머스 인공지능 데브코스

정말 오랜만에 블로그에 다시 들어왔다. 그러다가 예전에 썼던 프로그래머스의 인공지능 데브코스를 수료하고 처음 올린 후기가 5월 16일자 글이라 조금 소름 돋았다. 데브코스 수료를 마친지도 딱 1년이 지났다. 그 사이에 취업도 하고 코로나도 끝나고 참 많은 일들이 있었다. 오늘은 내가 인공지능 데브코스를 참여하고 나서 그 이후 무슨 일들이 있었는지 후기를 남겨보려고 한다.

데브코스는 경영학 전공한 문과생이 인공지능 공부 좀 해보겠다며 대학원까지 갔는데 취업 못해서 아등바등 했을 때, 참으로 적절한 시기에 나에게 다가온 기회였다. 한창 취업에 열중하던 당시, 나는 다른 지원자들과 비교해서 인공지능에 대한 지식이 떨어진다고 생각하진 않았다. 나의 약점은 뭐 하나 제대로 이룬 것이 없다는 것이었다. 특별한 목적없이 대학원을 다니다보니 컴퓨터 비전이니. NLP니 전문적으로 아는 분야도 없이 그냥 여기 살짝 저기 살짝 혓바닥만 대보면서 대학원을 졸업했다. 취업시장은 냉랭했다. 졸업장만으론 취직이 되지도 않고 면접에서 내가 했다고 내세울게 하나도 없었으니 면접을 볼 때 마다 너무 창피했다.

막상 나 혼자 프로젝트를 해보려고 하니 나는 개발에 대한 지식이 너무 부족했다. 그 당시 내가 할 수 있는 것은 구글 코랩에 코드 좀 쓰고 학습 돌리고 테스트하고 그정도… 정규화 된 데이터가 없으면 아무것도 할 수 없었고 모델을 만들어도 모델을 서빙하거나 프로토 타입을 작성해서 보여줄 수도 없었다. 이제까지 몇 년이나 배웠는데 마음만 먹으면 뭐라도 만들 수 있겠지 생각했던 나는 나의 무능력에 너무 충격을 먹었다.

그래서 이번엔 정말 뭐라도 해보려고 여기저기 서치를 하다가 동기 중 한 명이 코딩부트캠프 같은 걸 수강해서 공부도 하고 프로젝트도 해볼 수 있었다는 소리를 듣고 솔깃해서 나도 부트캠프를 수강해야겠다고 생각했다. 그러던 중 찾은 것이 데브코스. 네이버 부스트캠프도 고려했었으나 데브코스의 면접 결과가 먼저 나와서 최대한 빨리 취업하고 싶었던 나는 데브코스를 선택했다. 인공지능 데브코스는 국비지원 과정이었다. 나는 내가 국비지원 취업교육같은 것을 받을 거라고 생각조차 안해봤어서 무슨 제도인지 하나도 몰랐는데 카드 하나 만드니까 교육과정을 무료로 수강할 수 있고 지원금까지 준다고 해서 놀랐다… 요즘엔 내가 나라에 받을 수 있는 혜택이 뭔지 찾아보지도 않으면 정말 손해보고 살겠구나 생각했다.

나는 진짜 이번에 취업 못하면 죽는다는 거창한 마음으로 교육 과정에 임했다. 꼬박꼬박 아침에 일어나서 줌 출석하고 인터넷 강의 듣는다고 하루종일 책상 앞에 앉아있고 정규 교육 시간 끝나면 과제하고 더 공부하고 싶은 것들 찾아보고… 일어나는 것도 귀찮아 하던 내가 백수 신세가 되고 발등에 불이 떨어지니 정말 뭐라도 하게 되더라. 나도 나 자신에게 놀랐다.

데브코스를 수료하며 나에게 남은 것들

인공지능 데브코스니까 인공지능에 대해서도 많이 배웠지만 프로젝트에 필요한 다른 기술들을 많이 배울 수 있어서 좋았다. 파이썬 쓸 줄만 알았지 뭔가를 만드는 건 할 수도 없었는데 데이터 베이스부터 클라우드 사용하는 법, 프론트를 잘 몰라도 잘 만들 수 있게 도와주는 프레임워크들도 배울 수 있어서 너무 좋았다. 솔직히 나 혼자 공부하려고 했으면 뭘 공부해야할지도 몰라서 쩔쩔 맸을거다. django까지 배우니까 정말 이제 마음만 먹으면 기본적인 수준의 프로젝트는 못할게 없겠구나 생각이 들었다. 모든 것을 마스터할 정도로 배운건 아니지만 어떤 걸 하려면 뭘 배워야하는지 길을 알게 되니까 프로젝트 시작할 때 막막함이 좀 사라졌다. 혼자 공부할 수 있는 능력이 정말 중요한 것 같다. (요즘은 ChatGPT같은 치트키도 생겨서 진짜 마음만 먹으면 못 배울게 없을듯)

좋은 사람들도 많이 만났다. 팀원들도 그렇고 다른 수강자들도 진지하게 수업을 듣고 서로서로 도와주는 분위기여서 취업 준비에 집중하기 좋았다. 솔직히 공부 하는 사람만 공부하고 중도 포기자가 엄청 많지 않을까 걱정했는데 생각보다 환경이 정말 좋았다. 데브코스 처음 지원할 때는 무슨 교육 좀 듣겠다는데 면접까지 봐야되나 싶었는데 확실히 학습 분위기가 통제되니까 다 이유가 있었구나 생각하게 되었다.

데브코스 마지막 과정에는 팀 프로젝트를 하게 되는데 그 때는 정말 힘들었다. 배우고 조금씩 써볼 때만해도 프로젝트 수행하는 데 어려움이 없을 줄 알았는데 실제로 해보니까 갖가지 문제에 부딪혔다. 팀끼리 경쟁이 있어서 튀어보는게 좋지 않을까해서 일부러 좀 어려운 주제를 선택했는데 모르는 것들을 공부하는 것도 힘들었는데 실제로 프로젝트로 구현을 하려니까 전혀 예상하지 못하던 에러들이 막 생겼다. 나름 내가 조장을 맡았는데 내가 해결해 줄 수 없는 문제가 너무 많아서 살짝 자신감이 떨어지기도 했다.

그래도 혼자서 프로젝트를 할 때보다 덜 막막했고 에러들도 서로서로 소통하고 도와서 잘 해결되었다. 도커랑 AWS가 가장 문제를 많이 일으켰는데 팀원들끼리 스터디를 해서 클라우드랑 컨테이너에 대해서도 많이 배우고 편한 기술들을 직접 만져보면서 알게 되니까 너무 좋았다. 나름 인공지능 프로젝트였는데 구현이나 학습보다 인공지능 모델 서빙 과정에서 더 많은 문제들이 발생해서 정작 모델은 조금 완성도가 떨어져서 아쉽긴했지만 그래도 내가 처음으로 아이디어 구상부터 서빙까지 프로젝트를 마쳤다고 생각하니까 너무 뿌듯했다.

데브코스 수료 그 1년 후...

데브코스를 수료하고 나서 다시 취업준비를 했는데 솔직히 여전히 힘들긴 했다. 그래도 대학원 졸업하고 바로 취업준비 했을 때 보다는 조금이라도 경험이 있으니까 면접 대답도 더 잘할 수 있게 되었고 좀 더 높은 단계까지 올라갈 수 있었다. 웨이브나 CJ 올리브네트웍스는 정말 입사하고 싶었는데 최종 면접에서 떨어져서 너무 아까웠다.

지금은 이커머스 관련 중견기업에서 신입 데이터 사이언티스트로 일하고 있다. 지금 돌아보니까 데브코스에서 겪었던 경험들이 취업 준비할 때보다 취업 후에 더 도움이 되었다. 막상 일을 시작하니까 어려운 논문을 읽고 복잡한 코드를 짜고 이런 것들 보다 데이터 베이스, 서버, 개발환경 등 여기저기서 사소한 문제들이 발생하는게 힘들었는데 직접 그런 문제들을 해결해 본 경험들이 한 번이라도 있으니까 좀 더 해결하기가 수월했다. 요즘은 ChatGPT 같은 것도 생겨서 일이 더 편해졌다. 이런 도구가 있어도 무슨 질문을 해야할지 모르면 사용도 못했을 텐데 프로세스들을 대강 꿰고 있으니까 무슨 질문을 해야 문제를 해결할지 알고 있으니 ChatGPT도 잘 활용할 수 있었다.

데이터 과학이나 인공지능 분야로 취업을 원하는 문과생들이 가장 부족한 부분이 개발 측면에 대한 지식인 것 같다. 요즘은 책이나 강의 같은 것들이 잘 나와서 머신 러닝이 어떻고 딥러닝이 어떻고 배우는 것은 정말 어렵지 않은데 막상 프로젝트 한 번 해보려고 하면 인공지능 외의 지식이 0이니까 뭐 하나 해보려고 해도 정말 막막하다. 나는 그런 사람들에게 인공지능 데브코스 같은 부트캠프를 추천한다. 데브코스를 수강하기 전에는 국비지원 취업 교육에 대한 편견이 있었는데 생각보다 정말 많은 도움이 되었다. 물론 배우려고 하는 의지가 중요하긴 하다. 그냥 출석만하고 강의만 듣는 걸로는 별로 얻어가는 것이 없을 수도 있다. 다만 정말 인공지능 분야에 취업하고 싶은 의지가 있다면 프로그래머스 인공지능 데브코스를 수강해보는 것을 추천한다. 나에게 데브코스는 정말 많은 것을 경험할 수 있는 기회였다. 👍👍

[프로그래머스 인공지능 데브코스가 궁금하다면 링크클릭]... 와 근데 홈페이지 많이 멋있어졌다 ㅋㅋㅋ

'About me' 카테고리의 다른 글

About me  (0) 2021.11.02

오늘은 공부얘기는 아니지만 인터넷에서 꽤 공감이 많이 가는 기사를 발견해서 포스팅해보려 한다. 열등감에 시달리는 다른 사람들도 꽤 많이 공감이 갈수도 있을 것같다. 내가 발견한 기사는 바로 이것이다. 링크 여배우 나탈리 포트만의 일화와 가면 증후군이라는 정신증을 잘 설명해 놓았다. (증후군이라는 단어가 붙었으나 서치 결과 실제 정신과 진단명은 아닌 듯 하다.)

가면 증후군이란?

가면 증후군은 많은 사람들이 느끼는 감정인 열등감과 관련되어 있는 정신증이다. 가면 증후군은 본인이 이룬 성과에 대한 평가가 실제보다 부풀려져있다고 여기는 것이다. 임포스터 신드롬이라고도 한다는데 가면 증후군이 있는 사람은 말 그대로 자신을 사기꾼, 가면을 쓰고 있는 사람이라고 여긴다. 그래서 언제나 내 가면이 벗겨지면 어떡하나 내 사기가 들키면 어떡하나 걱정하게 된다. 자신의 성공은 운 때문이지 자신의 능력 때문이 아니라는 것이다. 자신의 성공을 운으로 돌리는 것은 사람들이 흔히 가지고 있는 방어기제의 일종이라고 한다. 실패했을 때 느낄 좌절감에 대한 불안을 막기 위해 자기 자신의 능력을 낮추는 것이다. 아인슈타인도 이 증상이 있어서 주위 사람들에게 자신은 능력이 없는데 운이 좋아 좋은 평가를 받는 것 같다고 말하고 다녔다고 한다. 가면 증후군이 있는 사람들은 실패에 따른 불안을 줄이기 위해 일부러 이루지도 못할 허무맹랑한 목표를 세우거나 계속 해야할 일을 미루는 경향이 있다고 한다. 혹은 강박적으로 자신을 완벽주의자로 몰아세우기도 한다. 이런 태도는 자신의 평소 기분이나 식욕, 수면 습관들에 부정적인 영향을 줄 수도 있다.

나는 어떨까

나는 이 기사를 읽으면서 공감이 많이 되었다. 가면 증후군 기사를 읽으면서 공감을 했다고 밝히는 것 자체가 가면 증후군이 아니라는 뜻인가(?) 아무튼 가장 공감 되었던 부분은 내가 이룬 성공을 운으로 돌린다는 것이다. 나는 평소에 내가 너무 과분한 대접을 받고 있다고 생각해왔다. 대학교에 진학하고 나서 부터다. 그 이전에는 오히려 잘난척해서 눈총을 받았으면 받았지 그런 생각은 안했던 것 같다.
대학교에 진학하고 나서는 정말 많은 실패를 겪었던 것 같다. 성적, 학교 생활, 인간관계 등 많은 부분에서 실패를 경험했다. 그래서인지 나는 자신감이 점점 없어지기 시작했다. 대학만 잘가면 모든게 잘 풀릴 것 같던 세상에 되는 일이 하나도 없어서 나는 사실 그냥 운이 좋았던 것 뿐 아닐까? 라는 생각도 했다. 수능 시험에 우연히 내가 잘 아는 문제만 나왔을 수도 있다. 혹은 우연히 다른 사람들이 그날따라 컨디션이 안좋았을 수도 있다. 그런 생각들을 했다. 나는 사실 문제만 잘푸는 건데 공부를 잘한다고 오해를 받았던 건 아닐까하는 생각도 해본 적 있다. 사실 더 따지고 보면 우리집의 경제적 상황이나 태어난 국적까지 따지면 상황이 더 안좋은 사람들이 보기엔 내가 우리집에 태어난 것 자체가 운이다.
그런데 이런 생각이 들면 더 열심히 내 능력을 증명했어야 했는데 나는 해도 안될 것 같다고 생각하면서 어차피 인생은 다 운이라고 생각하며 나 자신을 합리화해왔던 것 같다. 반면 나탈리 포트만은 똑같이 자신의 능력이 과대평가 받고 있다고 생각했지만 그 생각 때문에 자기의 능력을 증명하려고 계속 어려운 수업만 들었다고 한다. 일부러 어려운 수업을 듣느라고 스트레스를 받기는 했겠지만 나보다는 긍정적인 방향인 것 같다. 나는 비록 하버드도 다니지 않고 6개국어를 할 수도 없어서 비교하는게 맞는가 싶긴 하다.
그래도 한가지 확실한 것은 실제로 이제까지 모든 결과가 운이었다고 하더라도 내 능력을 꼭꼭 숨기면서 될대로 살자는 태도는 잘못된 것이라는 점이다. 정신적 건강에 좋지 않을 뿐만 아니라 내가 노력으로 이룰 수 있는 성공까지 잃어버릴 수 있다. 나의 성과와 실패를 객관적으로 보는 것이 중요한 것 같다. 잘한 것은 인정하고 실패를 바탕으로 성장하는 것이 중요하다. 뭔가를 실패했다고 그게 내 한계라고 결정짓는 것은 너무 성급하다. 세상에 시간만 많으면 못할게 뭐가 있겠는가

가면 증후군을 해결하기

기사에 가면 증후군을 해결하는 여러가지 방법이 실려있었다. 하지만 가장 기억에 남는 방법은 구체적인 계획표 짜기 였다. 언뜻 생각하면 계획이 열등감이랑 무슨 상관인가 싶을 수도 있지만, 자신이 이룰 수 있는 계획을 세부적으로 짜고 그것을 하나하나 성취하면서 자기 자신을 칭찬해주라는 뜻이다. 또 그만 미루기 도 참 뼈를 때리는 말이다. 나는 평소에 심하게 할 일을 미루는 버릇이 있는데 아마 실패를 피하려고 하는 생각에서 온 것 같다. 근데 이건 뭔가 의식적으로 그만 미루려고 생각하더라도 고치기가 힘들 것 같다(...) 그리고 가장 중요한 것은 역시나 부정적인 생각 버리기 인 것 같다. 내 능력이 과대평가되었다는 생각보다는 인정할 건 인정하고 고쳐야 할 건 고치는 태도를 가지는 것이 맞다. 자기 능력에 대한 불신이나 연민보다는 그냥 자기 성장에 대해서만 생각하자. 오늘보다 내일 더 나아지면 된다는 생각으로 자기 성장에 대해서만 집중하자.

* <데이터 과학자와 데이터 엔지니어의 인터뷰 문답집>을 참고하여 작성되었습니다.

의사결정나무(Decision Tree) 뿌시기

의사결정나무란?

  • 의사결정나무는 데이터의 feature의 값에 따라서 분기를 나누면서 트리 구조를 만들고, 이를 이용해 분류나 회귀 문제를 푸는 모델이다.

의사결정나무의 장단점

  • 장점: 인간이 실제로 하는 판단과 과정이 비슷하기 때문에 직관적이고, 설명하기 쉽다.
  • 단점: 과적합의 위험이 크다.

의사결정나무가 만들어지는 방식

feature들을 통해서 분기를 나누면서 데이터를 예측하는 방식으로, 최적의 의사결정모델을 구하는 방식은 NP-hard 문제에 속한다. 따라서 의사결정나무는 분기를 나눌 feature를 정할 때, 휴리스틱 함수를 이용한다. 휴리스틱 함수는 위키백과의 정의에 따르면 "가용한 정보를 기반으로 각 분기 단계에서 어느 한 분기를 선택하기 위해 사용하는 다양한 탐색 알고리즘의 대안 함수"라고 정의 했다. 의사결정나무는 즉 모든 경우를 고려하여 최적의 트리를 만드는 방식이라기 보다는, 분기를 나눌때마다 휴리스틱 함수에 따라서 그때그때 최선의 feature를 선택하는 그리디 방식에 가깝다고 볼 수 있다.

의사결정나무를 만드는 데 사용되는 휴리스틱 함수는 최대정보이득, 최대정보이득비율, 지니계수가 있다.이 세 휴리스틱 함수는 각각 의사결정나무를 만드는 알고리즘인 ID3, C4.5, CART 알고리즘에서 사용된다.

  • 최대 정보 이득

최대 정보 이득을 구하기 위해서는 먼저 엔트로피에 대해서 알아야한다. 여기서 말하는 엔트로피는 열역학의 엔트로피는 아니고, 정보이론에서 말하는 엔트로피이다. 정보이론은 엔트로피라는 개념을 도입해 어떤 사건을 통해서 얻을 수 있는 "정보량"을 표현하고자 했다. 정보이론은 어떤 사건이 흔하게 발생하는 사건이면 그 사건은 새로운 정보를 주지 못하기 때문에 정보량이 낮고, 드물게 발생하는 사건이면 반대로 정보량이 높다고 한다. 엔트로피는 이 정보량의 평균을 의미한다.

이 정보량의 평균, 즉 엔트로피는 조건에 따라 변화하기도 한다. 아무런 데이터 없이 미래를 예측하는 것은 어렵다. 예상 가능한 미래의 사건이 수 없이 많을 것이고, 가능한 사건이 많다보니 각각의 확률도 낮아 결국 평균 정보량이 높아진다. 엔트로피가 높다. 하지만 과거의 데이터가 있다면 미래 예측은 좀 더 쉬워지게 된다. 이러한 사전정보로 인해 미래 예측에 대한 엔트로피가 낮아진다.

정보 이득(information gain)은 어떤 feature를 따라 분류했을 때 엔트로피가 낮아지는 정도라고 생각하면 된다. 이 경우엔 엔트로피를 최대한 많이 낮출 수 있는 feature를 찾아 분기를 만든다.

  • 최대 정보 이득비

정보 이득이 단순히 줄어드는 엔트로피의 값을 이용해 계산하는 것이라면, 정보 이득 비는 해당 feature에 따른 엔트로피와 그 feature가 주는 정보 이득 사이의 비율을 이용해서 계산한다. 이렇게 하는 이유는 feature의 종류의 수에 따른 불균형을 보정해주기 위한 것이다.

가능한 종류가 수없이 많은 feature의 경우, 당연하게도 정보이득이 굉장히 크다. 예를 들어 사람을 DNA 변수로 분류를 한다고 했을 때, 사람마다 전부 DNA가 다르기 때문에 각각 DNA의 확률도 낮아지게 되고 결국 정보 이득이 커질 수 밖에 없다.

하지만 이런 정보 이득은 실제 예측에서는 의미가 없다. 사람마다 전부 다른 DNA로 분류를 예측한다는 것은 과적합을 부를 뿐이다. 이런 단점을 보정해주기 위해 제안된 것이 최대 정보 이득비다. 앞서 언급한 DNA 정보같은 경우는 정보 이득이 상당히 크지만 DNA에 따른 엔트로피도 클 것이기 때문에 ''정보 이득 비''에서는 이것이 보정이 된다.

  • 지니 계수

지니계수는 CART에서 사용되는 휴리스틱 함수인데 CART는 다른 알고리즘과 약간 다른 것이 분기를 나눌때 항상 이진 트리의 형태로 나눈다. 예를 들어 성적이라는 변수에 상, 중, 하라는 값이 있을 때 다른 의사결정나무 알고리즘은 상, 중, 하로 분기를 나눌 수 있지만 CART는 성적이 상인 집합 또는 상이 아닌 집합 둘로만 나눌 수 있다. CART는 이런 식으로 모든 feature에 대해 두 집합으로 나눠보면서 지니계수를 계산하고 지니계수가 가장 낮은 feature를 선택한다.

지니계수를 사용하는 CART 알고리즘의 경우, 범주형 변수 뿐만 아니라 수치형 변수도 사용할 수 있다는 특징이 있다. 모든 분기에서 이진 분할을 사용하기 때문이다. (수치형 변수를 절반으로 나누면 된다) 또한 분류 뿐만 아니라 회귀 태스크에서도 지니계수 대신 MSE로 분기하는 방식으로 사용 가능하다.

왜 의사결정트리는 앙상블 모델에서 많이 쓰일까?

앙상블은 여러 모델을 융합하는 전략과 방법을 통칭해서 부르는 말이다. 앙상블의 주된 목적은 여러 모델의 결과를 사용해서 결과 값의 분산이나 바이어스를 줄이는 것이다. 그런데 이를 위해서는 여러 모델들이 다양한 값을 출력해야 한다. 모든 모델이 비슷한 값만 출력한다면 앙상블을 하나마나 의미가 없다.

이런점에서 의사결정트리가 앙상블에 최적인 모델이라고 볼 수 있다. 앙상블을 하는 과정에서 보통 학습 데이터에서 일정 비율의 샘플을 랜덤하게 추출해 여러개의 모델을 학습시키는 방법을 사용하는데 선형 회귀 등 다른 모델들은 학습 데이터가 조금 바뀌더라도 결과값이 크게 바뀌지 않는 반면 의사결정트리는 학습 데이터에 따라 트리 구조가 많이 바뀔 수 있다. 따라서 앙상블에서는 의사결정트리 모델을 많이 사용하게 된 것이다.

앙상블의 종류

앙상블에는 부스팅과 배깅이 있다. 부스팅은 여러개의 모델을 학습시킬 때, 전의 모델이 잘못 분류한 샘플에 대해 가중치를 줘서 다음 모델을 학습시킬 때 적용한다. 쉽게 말해서 부스팅에선 모델이 점점 발전하는 방식으로 학습이 된다고 할 수 있다. 이 방법은 마치 인간이 학습을 하는 과정과 비슷하다. 인간은 어떤 것을 처음 배울땐 서툴지만, 한번 두번 배우고나서 점점 과거에 실수했던 기억을 이용해 더 잘 배우게 된다.

반면 배깅의 경우는 모델들을 서로 독립적으로 유지시킨다. 대표적으로 랜덤 포레스트가 있다. 훈련 데이터셋에서 랜덤으로 샘플을 추출하고 각각 다른 데이터 셋 샘플을 만들어서 여러개의 모델을 훈련시킨다. 이렇게 훈련된 모델들은 서로 다른 데이터에서 학습되었으므로 상호 독립적이다. 이렇게 학습된 모델들은 투표(voting)를 이용해 최종 결과값을 결정하게 된다.

비유적으로 설명하면 부스팅은 직렬적, 배깅은 병렬적이다. 부스팅은 한 사람이 어떤 것을 여러 번 배우면서 익혀나가는 과정과 같다면 배깅은 여러 사람이 머리를 맞대고 의견을 모아 생각하는 것과 똑같다.

앙상블은 왜 사용하는가?

어떤 예측 모델의 불확실성을 설명할 때, 분산과 바이어스의 관점으로 설명할 수 있다. 분산은 훈련 데이터 셋이 가지고 있는 노이즈를 학습함에 의한 에러라면, 바이어스는 모델 자체의 성능에 따른 에러다.

부스팅 모델의 경우는 가중치를 이용해 모델을 점점 발전시키므로, 모델이 표현할 수 있는 범위를 늘려 모델의 "바이어스"에 따른 에러를 줄일 수 있다.

반면 배깅 모델의 경우는 훈련 데이터 셋에서 여러 샘플을 뽑아 여러 개의 모델을 학습시키므로 데이터 셋이 가지고 있는 노이즈가 희석 되어, 모델의 "분산"에 따른 에러를 줄일 수 있다.

앙상블의 결과를 병합하는 방법

기초 분류기의 결과를 병합하는 방법에는 투표(voting)와 스태킹(stacking)이 있다. 투표는 가장 많은 표를 얻은 결과를 출력하는 것이고, 스태킹은 모든 기초 분류기의 결과를 가중치에 따라 더한 값을 출력하는 것이다.

'Data Science' 카테고리의 다른 글

누락값(Missing value)를 다루는 방법들  (0) 2022.05.09
[논문 리뷰] ESRGAN  (0) 2022.03.25
[논문 리뷰] Attention is all you Need (transformer)  (0) 2022.03.04
[논문 리뷰] SRGAN  (0) 2022.02.18
빈도주의 vs 베이지안  (0) 2022.02.06

5월 6일, 내가 수강하던 부트캠프인 프로그래머스의 인공지능 데브코스가 끝났다. 작년 12월부터 장장 5개월 간의 프로그램이었다. 네이버의 부스트캠프나 KT의 에이블 스쿨과 동기간에 모집이라 고민하고 있었으나 프로그래머스가 먼저 합격 발표를 하기도 했고 다른 부트캠프에 도전하려면 이 합격을 취소하고 도전해야 하는 것이어서 그러자면 계획이 불안정해지는지라 그냥 프로그래머스를 믿고 프로그래머스 인공지능 데브코스에 참여했다. 이런 장기간의 교육을 무료로 듣게 해준다니 우리나라엔 정말 좋은 제도가 있네 싶었다. 아무튼 이 글은 부트캠프를 수강한 계기와 후기, 그리고 느낀점들을 정리해보기 위해 작성하는 글이다. 프로그래머스 데브코스에 관심있거나 다른 인공지능 부트캠프를 수강할 사람들이 알아두면 좋을 점도 정리해봤다.

나는 왜 부트캠프를 수강했는가?

부트캠프를 수강하게 된 가장 큰 원인은 대학원 졸업 후 취업에 실패한 것이다. 2021년 하반기 취업에 실패하고 2022년 상반기까지 기다려야 했는데 그 기간동안 뭐라도 해야할 것 같았다. 그때 마침 네이버 부스트 캠프를 발견하고 부트캠프 수강을 결심했다. 결론적으론 프로그래머스의 부트캠프에 참여하게 되긴 했지만...

그 외에도 다른 이유도 있었다. 2021년 상반기엔 데이터 분석 직무를 위주로 구직했는데 2022년 부터는 AI 직무에 도전해보고 싶었다. 데이터 분석 직무는 워낙 경쟁률이 높기도 했고 AI 직무에 비해 평균적으로 처우도 낮은 편이다. AI 엔지니어가 더 미래가 밝아보이기도 했다. 좀 더 전문직의 느낌이라고 해야하나. 공백기간에 부트캠프를 수강하는 김에 AI 엔지니어로 내 진로를 발전시킬 수 있다면 좋겠다고 생각했다.

프로그래머스 인공지능 데브코스 후기

나는 프로그래머스 인공지능 데브코스를 수강하기 위해 한 번의 코딩테스트와 한 번의 면접을 거쳤다. 너무 오래전의 일이라 확실히 기억은 안나지만 코딩테스트는 내 기준 쉬운 문제와 어려운 문제가 모두 있었고 나는 어려운 문제를 풀진 못했지만 통과했다. 면접은 이제까지 학습 경험이나 앞으로 공부할 계획, 의지 같은 것들을 보려고 한 것 같았다. 결과적으로는 합격했다. 들어가기 빡빡하다거나 요구치가 높다고 생각되진 않았다. 프로그래머스 인공지능 데브코스를 고민하는 사람들이 있다면 그냥 한 번 도전해보는 것을 추천한다.

교육과정은 동영상 강의, 라이브 특강, 주기적으로 모여서 팀 활동, 개별 스터디, 파이널 프로젝트 등으로 구성되었다. 아무래도 코로나의 영향으로 오프라인 강의는 없었고 모두 온라인에서 이뤄졌다. 온라인으로 배우면 집중도가 떨어지는 성격이라 좀 아쉬웠다. 수업내용의 난이도는 중하 ~ 중중 수준이다. 아예 인공지능을 처음 접하는 사람이라도 교육과정을 잘 따라온다면 수강할 수 있을 것이라고 생각한다. 그래도 몇몇 수업은 좀 어려워서 여러 번 강의를 봐야했다. 프로그램 내에서 자율적인 팀 활동을 권장하니까 스터디 모으기도 쉬워서 더 배우고 싶은 것이 있어서 스터디를 만들고 싶다면 맘대로 만들 수도 있을 것이다. 교재비도 지원을 해줘서 원하는 교재가 있다면 바로 볼 수 있었다.

부트캠프에 대해서 총평을 하자면 프로그램 자체는 좋으나 내가 바랬던 방향과는 조금 거리가 있었던 것 같다. 초심자를 타겟으로 만들어진 프로그램 같았고 새롭게 알게 된 지식도 많긴 했지만 기본을 좀 충실히 다졌다는 느낌이지 AI 엔지니어 취준생으로써 준비가 되었다는 느낌은 잘 모르겠다. 물론 기업마다 AI 엔지니어 신입사원에게 바라는 역량의 차이가 있긴 하겠지만 이 프로그램의 교육 내용으로만 취업을 하는 것은 무리다.

좀 더 상세하게 이 부트캠프의 장단점을 설명하겠다. 우선 이 부트캠프의 장점은 자유로운 분위기와 학습에 대한 지원이 풍부하다는 점인 것 같다. 요즘 유튜브 광고에서 부트 캠프 광고들이 종종 나오는데 하나같이 자기들은 엄청 빡세게 훈련을 시키며 하루종일 공부를 하게 되고 열몇개의 프로젝트를 하게 되며 등등으로 광고를 하던데 프로그래머스 데브코스는 그런 분위기는 아니다. 강의를 수강하는 것도 하루 안에 자유로운 시간에 듣기만 하면 되고 과제도 부담스럽지는 않다. 몇몇 강의는 분량이 좀 많긴한데 조금 힘든 정도? 그래서 남은 시간에 내가 원하는 공부를 할 수 있다는 장점이 있다. 특별히 바빴던 시기는 파이널 프로젝트를 진행할 때 정도밖에 없는 것 같다. 학습 지원도 정말 적극적이라고 느낀게 교재를 비롯한 학습에 대해 필요한 모든 자원을 무료로 지원해주신다. 교재는 물론 줌이나 노션같은 플랫폼, 구글 코랩 프로같은 비용도 지원해주시고 따로 공부하고 싶은 것이 있다면 소모임도 만들어주신다. 각 팀별로 전담 멘토가 있는데 멘토 분들도 질문하면 적극적으로 도와주셨다.

단점은 솔직히 취업 준비에 최적화된 것 같은 프로그램은 아니라는 점이다. 특히 나처럼 빨리 취업하고 싶은 사람한테는 교육 내용이 큰 도움이 되지는 않을 것 같다. 왜냐면 내가 이 부트캠프를 수강하면서 느낀 것이 취업에 중요한 것은 무슨 강의를 듣느냐보다는 스스로 관심분야를 공부하면서 자신을 발전시켜 나가는 것이다. 프로그래머스 데브코스는 스스로 공부하는 법을 깨닫게 해주고 공부하는 것을 지원해주시기는 하지만 관심없는 분야도 강의를 꼭 들어야하고 라이브 강의, 주말 팀활동 같은 것들에 모두 참여해야하니까 개별적인 공부에 집중하기가 좀 힘들었다. 강의가 수학, 통계, 파이썬, 머신러닝, 데이터 엔지니어링, 비전, 자연어처리, 추천시스템 등등... 너무 많은 영역을 5달의 강의로 모두 커버하려고 하다보니 더 깊게 공부하고 싶은 영역이 있는데 강의 진도상으로는 다른 영역의 강의를 들어야한다던가 하는 부분이 좀 아쉬웠다. 강의가 선택과 집중이 가능해서 관심있는 분야를 선택해서 들을 수 있거나 아니면 아예 전체적인 부분을 직접 경험하면서 느낄 수 있게 실전적인 프로젝트 위주로 돌아갔다면 더 좋았을 것 같다. 네이버 부스트캠프 같은 경우는 비전, 자연어 처리, 추천시스템 중에 하나를 정해서 반을 나누고 공부하던데 그런 방식도 괜찮을 것 같다.

물론 아예 도움이 되지 않은 것은 아니다. 멘토님들, 강사님들이 질문에 잘 대답해주셔서 내가 모르는 부분이 뭔지 더 공부해야 할 부분이 뭔지 잘 이해할 수 있었고, 다양한 분야에 대해서 공부하면서 데이터 사이언스 전반에 대한 이해도도 높아졌으며, 데이터 수집부터 모델 배포까지 모두 경험할 수 있었던 파이널 프로젝트를 통해 앞으로 프로젝트를 어떻게 수행해야할지에 대해 알게 되었다. 그리고 모델링 이외의 영역에도 관심을 갖게 되었는데 특히 백엔드 개발이나 MLops 영역에 관심이 많아 졌다. 부트캠프만으로 취업을 하는 것은 무리지만 취업을 할 수 있도록 공부 방향을 잡는 데에는 도움이 되었다.

부트캠프를 수강하면서 느낀점

내 생각에 비전공자가 데이터 사이언스 분야로 취업하는 데 가장 큰 걸림돌은 혼자서 공부할 때 일정 이상으로 성장하는 것이 쉽지 않다는 것이다. 처음 공부를 시작할때는 교재나 오픈 된 강의같은 것으로 공부하게 될 것이다. 그것이 비전공자들에게 익숙한 학습법이기 때문이다. 근데 이런 것들은 보통 두가지 문제점이 있디. 첫번째는 이런 교보재들이 항상 데이터 분석, 데이터 전처리, 모델링같은 것들에 집중하고 있다는 점이다. 실제로 AI가 서비스로 되기까지의 과정 중에 분석, 전처리, 모델링은 아주 작은 영역만을 차지한다. 데이터 추출, 적재, 데이터 관리, 모델 서빙, 서빙된 모델에 대한 관리, 최적화 등등의 많은 절차들이 있으며 AI 엔지니어가 되기 위해서 이 모든 과정에 대해 전문적이어야 하는 것은 아니지만, 적어도 이 파이프라인에 어떤 일들이 일어나는지, 어떤 기술들이 필요한지 정도는 "아는척"할 수 있는 수준만이라도 이해하고 있어야 면접관분들이 긍정적으로 보시는 것 같다. 두번째 문제점은 대부분이 최신기술을 반영하지 않는다는 점이다. 물론 데이터 사이언스 분야는 기술이 매우 빠른 속도로 발전하고 있기 때문에 이를 모두 반영하는 것은 불가능하니 따로 찾아서 공부해야하는 것은 당연하긴하다. 논문을 찾아보고 IT 기업들의 기술 블로그를 뒤져보고 여러 깃허브의 코드들을 참고해야한다. 비전공자, 특히 문과 출신들은 책을 읽고 공부하고 다 공부하고나면 더 높은 난이도의 책을 사서 공부하고 같은 과정으로 배우는 것에 익숙할텐데 이런 방법으로는 절대로 데이터 사이언스에 대해 깊게 공부할 수 없을 것이다. 수동적인 공부만 하다간 여름에도 집합 공부 겨울에도 집합 공부, 계속 똑같은 것만 공부하게 될 것이다.결국엔 언젠가 혼자 찾아서 공부해야할 순간이 온다.

이것이 부트캠프를 수강하면서 배운 가장 중요한 것이다. 나는 부트캠프 이전에는 수동적인 공부만 했다. 교재를 보고 강의를 듣고 다른 사람의 예제를 보고... 위에도 설명했듯이 이런 방법에는 한계가 있다. 취업을 하기 위해선 프로젝트 같은 것으로 내 실력을 증명해야한다. 근데 난 프로젝트에 필요한 데이터들을 어떻게 구하지? 내가 만든 모델을 어떻게 보여주지? 깃허브에 파이썬 코드를 올리면 그게 프로젝트인가? 남의 예제를 따라한 프로젝트에 내가 뭘 어필해야 하는거지? 이 프로젝트를 어떻게 더 정교하게 발전시키지? 등등등... 진짜 수 많은 질문에 부딪혔다. 그리고 나는 부트캠프를 수강하면서 하나의 가르침을 얻은 것이다. "인터넷에 많으니까 너가 알아서 찾아서 공부해!"

물론 이런 말은 부트캠프를 수강하기 전에도 정말 많이 들었던 말이다. 근데 차이는 부트캠프 수강 전에는 이런 말을 듣기만 했고 수강 후에는 이것을 실제로 실천했다는 점이다... 이제까지 혼자 공부해본 경험이 없었으니 애초에 알아서 공부하는 방법을 몰랐다. 근데 막상 무작정 시도해보니 감을 잡을 수 있었고 정말 세상에 공짜로 공개된 지식이 많구나 라는 것을 느꼈다.

앞으로 부트캠프를 수강할 사람들에게 말해주고 싶은 것은 1. 내가 취직하고 싶은 직무의 업무를 파악하고, 2. 알아야하는 기술과 실무 내용에 대해 파악하고 3. 내가 모르는 부분을 찾아서 집중적으로 공부하고 4. 배운 것을 프로젝트에 적용하면서 너가 아는 것을 남들에게 증명하는 과정으로 공부해보라는 것이다. 진짜 부트캠프에서 시키는대로 강의듣고 프로젝트 하라니까 프로젝트하고 과제하라니까 과제하고 이런식으로는 실력이 늘 수가 없고 결국에 면접에서 실력없고 이해도 낮은거 뽀록나서 면접 떨어진다. 근데 알아서 찾아서 공부하면 자기가 모르는 부분, 아는 부분에 대해 이해도가 높아지고 자연스럽게 업무의 흐름에 대해서 알게 되며 면접에서 모르는 질문에도 어떤식으로 대답해야할지 감이 온다. 문제는 내가 이거를 부트캠프 끝나갈 쯤에 깨달았다. 부트캠프의 장점은 학습에 많은 지원을 해준다는 점이니까 이를 이용해서 스스로 찾아서 공부하는 습관을 들여보라. 내가 아직 취업도 못했고 이 분야에 전문가도 아니긴하지만 적어도 이런 공부방법으로 공부해서 손해볼 일은 없다고 생각한다.

 

 

'Others' 카테고리의 다른 글

[자아성찰] 열등감? 가면 증후군?  (0) 2022.06.10
약 7개월의 구직... 중간점검...  (0) 2022.04.03

Introduction to flat files

Data Frame

  • pandas - 2차원의 구조 (행, 열)

Flat Files

  • 간단하고 만들기 쉬움
  • 색상이나 볼드체 서식이 없는 일반 텍스트
  • 한 라인마다 하나의 행을 의미
  • delimiter로 필드가 구분됨
  • 일반적으로 delimiter는 콤마,( csv)
  • pandas는 delimiter와 상관없이 모든 플랫 파일을 불러올 수 있다.
import pandas as pd

tax_data = pd.read_csv('us-tax-data-2016.csv')
tax_data.head(5)

Loading other flat files

  • delimiter를 sep= 인자에 넣어준다.
import pandas as pd
tax_data = pd.read_csv('us_tax_data_2016.tsv', sep='\t')

Modifying flat file imports

Limiting Columns

  • usecols 인자를 이용해 로드할 열을 선택한다.
  • 모든 열 이름이나 열 번호의 리스트를 사용할 수 있다.
col_names = ['STATEFIPS', 'STATE', 'zipcode', 'agi_stub', 'N1']
col_nums = [0, 1, 2, 3, 4]

# choose columns to load by name
tax_data_v1 = pd.read_csv('us_tax_data_2016.csv', usecols=col_names)

tax_data_v1 = pd.read_csv('us_tax_data_2016.csv', usecols=col_nums)

Limiting Rows

  • nrows 인자를 이용해서 불러올 행의 수를 제한한다.
  • skiprows 인자는 건너뛸 행의 수를 선택한다. 이 경우 header=None으로 설정해야 한다.

Assigning Columns Names

  • names 인자 사용
  • 모든 열의 이름의 리스트가 있어야 함
  • 일부 열만 바꾸려면 불러온 후에 수행해야 함
col_names = list(tax_data_first1000)

tax_data_next500 = pd.read_csv('us_tax_data_2016.csv', nrows=500, skiprows=1000, header=None, names=col_names)

Handing errors and missing data

Common Flat File import issue

  • 열 데이터 타입이 잘못됨
  • 값이 누락됨
  • pandas가 읽을 수 없는 값

Specifying data types

  • pandas는 자동으로 열의 데이터 타입을 유추한다.
  • 데이터 타입을 특정해주기 위해 dtype 인자를 사용한다. 각 키는 열 이름이고 값은 데이터 타입이 들어 있는 딕셔너리를 받는다.

Customizing Missing Data values

  • pandas는 자동적으로 결측치를 해석한다
  • na_value 인자를 이용해 missing values를 커스텀할 수 있다.
  • single value, list, dictionary가 들어갈 수 있다.
tax_data = pd.read_csv('us_tax_data_2016.csv', na_values={'zipcode':0})

Lines with errors

  • pandas가 읽을 수 없는 레코드
  • error_bad_lines=False인자를 추가하면 pandas는 에러가 나는 행을 건너 뛴다.
  • warn_bad_lines=True 인자를 추가하면 파싱할 수 없는 줄을 건너뛸 때, 메시지를 표시하게 된다.

Introduction to spreadsheets

spreadsheet

  • excel
  • 행과 열이 있는 데이터 셀이 있는 테이블에 데이터가 저장됨
  • 플랫 파일과 달리 자동으로 업데이트 되는 공식이 있을 수 있음
  • 여러 시트가 있을 수 있음

Loading spreadsheet

  • read_excel()
  • usecols'A:P'와 같은 excel 열문자의 범위 문자열도 허용함
survey_data = pd.read_excel('fcc_survey_with_headers.xlsx',
                           skiprows=2, 
                           usecols='W:AB, AR')

Getting data from multiple worksheets

Selecting sheet to Load

  • read_excel()는 기본으로 첫번째 sheet만 가져온다
  • sheet_name인자를 사용해서 달느 시트를 가져올 수 있다.
  • 시트의 이름이나 인덱스 넘버를 받는다(zero index).
  • read_excel()에 들어간 인자는 모든 시트에 적용된다.

Loading All sheet

  • sheet name에 None을 전달한다
  • OrderedDict 리턴

Putting It All Together

all_responses = pd.DataFrame()

# Key는 시트이름, value는 프레임
for sheet_name, frame in survey_responses.items():
    frame['Year'] = sheet_name
    all_responses = all_responses.append(frame)

Modifying imports: true/false type

Boolean Data

  • True/False
  • pandas는 True/False column을 float 데이터로 로드한다.
  • read_excel()dtype 인자로 어떤 column을 bool 타입으로 만들어 줄지 정할 수 있다.
  • 오직 True와 False 값만 가져야 하므로 N/A 값은 True로 인코딩된다.
  • 'Yes', 'No' 같이 0과 1이 아니고 값이 다를 경우 true_value 인자를 사용한다.
bool_data = pd.read_excel('fcc_survey_boolean.xlsx',
                         dtype={'AttendedBootcamp': bool,
                               ...},
                         true_values=['Yes'],
                         false_values=['No'])

Modifying imports: parding date

Date and Time Data

  • python의 datetime 데이터타입으로 저장된다.
  • pandas는 날짜/시간 데이터를 객체로 불러온다.
  • timespan을 계산하려면 datetime 데이터 타입을 이용해야 한다.
  • dtype이 아닌 parse_dates 인자를 사용한다!
# 시간 데이터가 여러 열에 분산되어 있을 경우 중첩 리스트 넣기
date_cols = ['Part1StartTime', 'Part1EndTime', [['Part2StartDate', 'Part2StartTime']]]

survey_df = pd.read_excel('fcc_survey.xlsx',
                         parse_dates=date_cols)

# 컬럼의 이름을 지정해주고 싶으면 dict
date_cols = {'Part1Start': 'Part1StartTime',
            'Part1End': 'Part1EndTime',
            'Part2Start':['Part2StartDate',
                         'Part2StartTime']}

Non-Standard Dates

  • parse_date는 pandas가 이해할 수 있는 형식에만 작동함
  • to_datetime() 인자를 이용
  • 데이터 프레임과 column을 format 형식을 이용해 datetime format으로 바꿔줌
  • strftime.org
format_string = '%m%d%Y %H:%M:%S'
survey_df['Part2EndTime'] = pd.to_datetime(survey_df['Part2EndTime'],
                                          format=format_string)

Introduction to databases

Relational Databases

  • table로 정리된 데이터
  • row와 column으로 이루어짐
  • primary key를 이용해 테이블들이 연결될 수 있음
  • 스프레드 시트나 플랫파일보다 더 많은 데이터를 처리하고 더 많은 동시 사용자를 지원함
  • SQL을 통해 데이터베이스와 인터페이스

Common realational databases

  • SQLite
    • 컴퓨터 파일로 저장되는 데이터 베이스

Connecting to Databases

  • 데이터 베이스로 연결할 수 있는 way를 만든다
  • database를 쿼리한다.

Creating a Database Engine

  • SQLAlchemy
    • create_engine()은 데이터베이스 커넥션을 다룰 수 있게 해준다.
      • database에 대한 문자열 URL이 필요함
      • sqlite:///filename.db

Quering Database

  • 데이터 베이스에서 데이터를 로드하기 위해pd.read_sql(query, engine) 사용

Refining imports with SQL queries

SELECTing Columns

  • SELECT [column names] FROM [table name];
  • WHERE을 이용해 필터링
    • 표준 연산자
      • =, >, >=, <, <=, <>
    • 문자열 필터링
    • AND, OR과 사용 가능

More complex SQL queries

  • Get unique values for one or more columns with SELECT DISTINCT
  • Aggregate function
    • sum, avg, max, min
    • count
    • group by

Loading multiple tables with join

  • database records는 unique한 identifier가 있다.
  • JOIN ON을 이용해 서로 다른 테이블을 identifier로 조인할 수 있다.
  • SQL 키워드의 순서
    • SELECT
    • FROM
    • JOIN
    • WHERE
    • GROUP BY

Introduction to JSON

  • common web data format(자바스크립트)
  • 테이블 형식이 아님
  • record가 모두 동일한 컬럼을 가질 필요가 없음
  • 속성-값 쌍을 가진 collection(dictiㅐnary와 비슷)
  • 중첩 가능

Reading JSON Data

  • read_json()
    • json 파일 경로나 json 문자열을 넣을 수 있음
    • dtype 인자로 데이터타입 명시
    • orient 인자로 레코드들이 어떻게 정렬될 것인지 정한다.(record oriented or column oriented)

Introduction to APIs

API

  • 어플리케이션이 다른 프로그램들과 어떻게 소통할지 정의한다.
  • 데이터베이스 구조를 몰라도 api를 통해 데이터를 얻을 수 있다.

request

  • request.get(url_string):url에서 데이터 가져오기
    • params: 딕셔너리를 받아 api에 값을 전달한다.
    • header: 딕셔너리를 받아 사용자 인증키를 전달한다.
  • response 객체를 리턴하며 response.json()메소드를 사용해야 결과값만 가져올 수 있다.

Working with nested JSONs

Nested JSONs

  • value 자체가 객체인 경우 JSON이 중첩된다.
  • pandas.io.json
    • JSON을 읽고 쓰기 위한 판다스의 서브모듈
    • json_normalize(): 딕셔너리나 딕셔너리의 리스트를 받아 데이터 프레임을 반환한다.
      • record_path인자는 파일경로에 폴더를 나열하는 것과 같이 중첩 데이터에 대한 속성 문자열 또는 문자열 목록을 사용한다.
      • meta: 속성의 리스트를 받아 어떤 속성을 로드할 것인지 결정
      • meta_prefix: 속성의 출처를 명확히하고 열 이름이 중복되지 않기 위해서 접두사 사용

Combining multiple datasets

  • appending: 데이터 프레임을 다른 데이터프레임에 row로 추가한다. (df1.append(df2, ignore_index=True))
  • merging: dataset을 column을 더해 합친다. merge()는 SQL의 조인과 같은 메소드다.
    • df.merge()
      • on
      • left_on, right-on

'Data Science > [DSF] Data engineering' 카테고리의 다른 글

What is Data Engineering?  (0) 2022.05.15
Data engineering for everyone  (0) 2022.04.10

What is data engineering?

In comes the data engineer

  • 데이터가 흩어져 있음
  • 데이터베이스가 어플리케이션에 최적화 되어 있고 분석에 최적화되어 있지 않음
  • 레가시 코드들이 데이터를 손상시킴

What is parallel

  • 메모리의 프로세싱 파워의 이득
  • 아이디어: task를 subtask로 나누어 여러 컴퓨터에 분산시킨다.

병렬처리의 리스크

  • 통신 오버헤드
    • task가 너무 작을 때
    • 프로세싱 유닛이 적을 때
    • 이 오버헤드로 인해 프로세싱 유닛이 늘어나도 속도가 선형으로 증가하지 않는다(pararell slowdown)

병렬 처리 구현방법

multiprocessing.Pool: 동일한 컴퓨터의 여러 코어에 작업을 분산처리

from multiprocessing import Pool
import pandas as pd

def take_mean_age(year_and_group):
    year, group = year_and_group
    return pd.DataFrame({'Age': group['Age'].mean()}, index=[year])

with Pool(4) as p:
    result = p.map(take_mean_age, athlete_events.groupby('Year'))

result_df = pd.concat(result)

dask: 추상화 제공 프레임워크

import dask.dataframe as dd

# partition dataframe into 4
athelte_events_dask = dd.from_pandas(athlete_events, npartitions=4)

result_df = athlete_events_dask.groupby('Year').Age.mean().compute()

Parallel computation frameworks

Hadoop

  • HDFS: 분산 파일 시스템
  • MapReduce: 빅데이터 처리 시스템

Hive

  • 하둡 에코시스템의 최상위 계층
  • SQL의 변형인 Hive SQL을 사용하여 구조화된 방식으로 쿼리를 할 수 있다.
  • 다른 데이터 처리 툴과 통합됨

Hive: an example

  • 일반 SQL 쿼리와 비슷하지만 분산처리로 작동한다
SELECT year, AVG(age)
FROM views.athlete_events
GROUP BY year 

Spark

  • 맵리듀스는 job 사이에 디스크를 많이 쓰지만, Spark는 가능한 한 메모리에서 작동한다.
  • 맵리듀스의 단점 보완

Resilient distributed datasets(RDD)

  • spark에서 사용
  • 여러개의 노드에 분산되어 저장하는 데이터
  • 데이터프레임과 달리 컬럼에 이름이 없음
  • 튜플의 리스트와 비슷함
  • Operation
    • Transformation: .map() or .filter() -> RDD 리턴
    • Action: .count() or .first() -> single result

PySpark

  • 스파크의 프로그래밍 언어 인터페이스
  • Python interface
  • pandas와 유사함

PySpark: an example

(athlete_events_spark
    .groupBy('Year')
    .mean('Age')
    .show())

Workflow scheduling frameworks

An example pipeline

  • 어떻게 스케줄링?
    • 매일 수동으로: 확장하기 어려움
    • cron: dependency 처리가 어려움(작업의 순서...)

DAGs(Directed Acyclic Graph)

  • Set of nodes
  • Directed edges
  • No cycles

The tools for the job

  • Linux's cron
  • spotify's Luigi
  • Airflow

Airflow: an example

# Create the DAG object
dag = DAG(dag_id='example_dag', ..., schedule_interval="0 * * * *")

# Define operations
start_cluster = StartClusterOperator(task_id='start_cluster', dag=dag)
ingest_customer_data = SparkJobOperator(task_id='ingest_customer_data', dag=dag)
ingest_product_data = SparkJobOperator(task_id='ingest_product_data', dag=dag)
enrich_customer_data = PythonOperator(task_id='enrich_customer_data', ..., dag=dag)

# Set up dependecy flow
start_cluster.set_downstream(ingest_customer_data)
ingest_customer_data.set_downstream(enrich_customer_data)
ingest_product_data.set_downstream(enrich_customer_data)

Extract

  • 의미: 데이터 스토리지에서 데이터를 추출하는 것

Extract from text files

  • unstructured
  • flat file은 row와 column으로 이루어져 있다.(ex. csv, tsv)

JSON

  • JavaScript Object Notation
  • semi-structured
  • number, string, boolean, null
  • array, object
  • python의 딕셔너리와 비슷, 매핑이 잘 됨(json 패키지)
  • 많은 웹서비스가 이 형태로 데이터를 전달함

Data on the Web

  • request

    • ex. Browss to Google
    • Request to Google server
    • Google responds with Web page
  • APIs

    • 데이터를 JSON 포맷으로 보냄
    • API: application programming interface
  • database

    • 기존 애플리케이션 데이터베이스에서 추출
      • 많은 Transaction(행 삽입)
      • OLTP
      • row-oriented
    • Analytical database
      • OLAP
      • Column-oriented
  • connection string/uri

    • postgresql://[user[:password]@][host][:port]

    • import sqlalchemy
      connection_uri = 'postgresql://repl:password@localhost:5432/pagila'
      db_engine = sqlalchemy.create_engine(connection_uri)
      
      import pandas as pd
      pd.read_sql('SELECT * FROM customer', db_engine)

Transform

  • selection of attribute (ex. email)
  • Transaltion of code values (ex. New York -> NY)
  • Data validation (ex. date input in 'created_at')
  • Splitting columns into multiple columns(email -> user, domain)
  • Joining from multiple sources

pyspark

import pyspark.sql

spark = pyspark.sql.SparkSession.builder.getOrCreate()
spark.read.jdbc('jdbc:postgresql://localhost:5432/pagila',
               properties={'user':'repl', 'password':'password'})

# jdbc: Spark가 여러 관계형 데이터베이스에 연결하는 데 도움이 되는 소프트웨어

example: join

customer_df # pyspark dataframe with customer data
rating_df # pyspark dataframe with rating data

# Groupby ratings
ratings_per_customer = ratings_df.groupBy('customer_id').mean('rating')

# Join on Customer ID
customer_df.join(
rating_per_customer,
customer_df.customer_id=ratings_per_customer.customer_id)

Loading

Analytics or applications databases

  • Analytics
    • Aggregate queries
    • online analysis processing(OLAP)
    • column-oriented
    • columns subset에 대해 쿼리함
    • 병렬화에 적합
  • Applications
    • lots of transaction
    • online transaction processing(OLTP)
    • row-oriented
    • 레코드를 넣고 빼는 것이 쉽고 빠름

MPP Databases

  • Massively parallel processing database
  • 쿼리가 하위작업으로 분리되어 여러 노드로 분산됨

example: Redshift

# Pandas .to_parquet() method
df.to_parquet('./s3://path/to/bucket/customer.parquet')
# pyspark .write.parquet() method
df.write.parquet('./s3://path/to/bucket/customer.parquet')
COPY customer
FROM 's3://path/to/bucket/customer.parquet'
FORMAT as parquet

Load to PostgrSQL

# Transformation on data
recommendations = transform_find_recommecdatins(ratings_df)

# Load into PostgreSQL database
recommendations.to_sql('recommendations',
                      db_engine,
                      schema='store',
                      if_exists='replace')

Putting it all together

  • ETL 과정을 하나의 함수로 초기화
def extract_table_to_df(tablename, db_engine):
    return pd.read_sql('SELECT * FROM {}'.format(tablename), db_engine)

def split_columns_transform(df, column, pat, suffixes):
    # Converts column into str and splits it on pat

def load_df_into_dwh(film_df, tablename, schema, db_engine):
    return pd.to_sql(tablename, db_engine, schema=schema, if_exists='replace')

db_engine = {...}

def etl():
    film_df = extract_table_to_df('film', db_engine['store'])
    film_df = split_columns_transform(film_df, 'rental_rate', '.', ['_dollar', '_cents'])
    load_df_into_dwh(film_df, 'film', 'store', db_engine['dwh'])

Airflow refresher

  • python으로 작성된 워크플로우 스케줄러
  • 비선형 그래프 작성 가능
  • DAGs

scheduling with DAGs in Airflow

from airflow.models import DAG
from airflow.operators.python_operator import PythonOperator

dag = DAG(dag_id='etl_pipeline',
         ...,
         schedule_interval='0 0 * * *') # cron 표현식

etl_task = pythonOperator(task_id='etl_task',
                         python_callable=etl,
                         dag=dag)

etl_task.set_upstream(wait_for_this_task)
# wait_for_this_task가 끝난 후 실행
  • cron 표현식: 분, 시간, 일, 월, 요일

From rating to recommendation

Our recommendation transform

  • 사용자가 대부분 높게 평가한 기술의 코스를 추천
  • 이미 평가 한 코스를 추천하지 않음
  • 가장 높은 등급의 코스 3개를 추천

Scheduling daily jobs

The loading phase

# pandas dataframe to sql
recommendations.to_sql(
"recommendations",
db_engine,
if_exists="append")

전체 ETL

def etl(db_engines):
    # Extract the data
    course = extract_course_data(db_engines)
    rating = extract_rating_data(db_engines)

    # Clean up courses data
    courses = transform_fill_programming_language(courses)

    # Get the average course rating
    avg_course_rating = transform_avg_rating(rating)
    courses_to_recommend = transform_courses_to_recommend(
    rating,
    courses,
    )

    # Calculate the recommendation
    recommendations = transform_recommendations(
    avg_course_rating,
    course_to_recommend,
    )

    # Load the recommendations into the database
    load_to_dwh(recommendations, db_engine)

Creating the DAG

from airflow.models import DAG
from airflow.operators.python_operator import PythonOperator

dag = DAG(dag_id='recommendations',
         scheduled_interval='0 0 * * *')

task_recommendations = PythonOperator(
task_id='recommendations_task',
python_callable=etl)

'Data Science > [DSF] Data engineering' 카테고리의 다른 글

Introduction to flat file  (0) 2022.05.15
Data engineering for everyone  (0) 2022.04.10

데이터가 누락되는 종류

  • 무작위로 누락: 변수의 종류와 상관없는 누락

  • 다른 변수와 관련있는 누락: 해당 변수가 아닌 다른 변수의 영향을 받아서 누락된 경우 (ex. 설문조사의 뒷면이 있는 지 모르고 응답하지 않아서 누락된 경우)

  • 해당 변수와 관련있는 누락: 누락된 해당 변수에 관련해서 누락된 경우(어떤 설문조사에서 일부 질문에 정치적인 성향 등의 이유로 채우지 않았을 경우)

  • 첫번째, 두번째는 제거하는 것이 좋지만, 세번째 경우 단순 제거하면 모델이 편향 될 가능성이 있다.

누락값 해결책

  • 삭제: 데이터 버리기(row) or 변수 버리기(col)

    • 단점: 모델이 편향될 수 있으며, 중요한 변수가 버려질 수 있음
  • 대표값(mean, median, mode)으로 대체

    • 단점: 대체한 변수가 다른 변수와 상관관계가 있을 때, 상관관계를 고려하지 않음, 데이터 전체의 분산이 줄어들게 됨(평균), 데이터 전체에 편향이 생김(최빈값)
  • 다중 대체법(multiple imputation, MICE)

    • 과정

      1. 대체하고자 하는 누락값을 비우고, 다른 누락값은 대표값으로 채운다.
      2. 다른 변수들을 X로하고, 대체하고자 하는 누락값이 속하는 변수를 Y로하는 선형회귀모델을 이용해 누락값을 채운다.
      3. 다른 누락값에 대해서도 똑같이 적용한다.
      4. 대체 전 값과 대체 후의 값의 차이가 0이 되도록 이 과정을 반복한다.
  • KNN 사용, 변수를 통해 가까운 데이터 K개를 골라 이 데이터들의 평균을 사용함

    • 단점: 오래걸림, outlier에 민감함, 변수의 scale을 조절해줘야 함

출처: https://subinium.github.io/missing-data-handling/

'Data Science' 카테고리의 다른 글

[ML] 의사결정나무(Decision Tree) 뿌시기  (0) 2022.05.21
[논문 리뷰] ESRGAN  (0) 2022.03.25
[논문 리뷰] Attention is all you Need (transformer)  (0) 2022.03.04
[논문 리뷰] SRGAN  (0) 2022.02.18
빈도주의 vs 베이지안  (0) 2022.02.06

위키백과의 정의에 따르면 자료형(data type)이란 "컴퓨터 과학과 프로그래밍 언어에서 실수치, 정수, 불린 자료형 따위의 여러 종류를 데이터로 식별하는 분류로서, 더 나아가 해당 자료형에 대한 가능한 값, 해당 자료형에서 수행을 마칠 수 있는 명령들, 데이터의 의미, 해당 자료형의 값을 저장하는 방식을 결정하는 것"이다.

파이썬을 조금이라도 배운 사람이라면 적어도 자료형이 무엇인지는 익숙할 것이다. int, float, list와 같은 예시들이 떠오를 수도 있을 것이다. 하지만 이런 자료형들은 어떻게 만들어진 것이고 무엇을 위해 존재하는 것일까? 그저 유용하게 사용하기만 했지 나는 이 자료형들의 존재 이유에 관해서는 별로 생각해본 적이 없었다. 이 자료형들의 의미에 대해서 알기위해선 과거 컴퓨터 언어들은 어떤 자료형을 가지고 있었는지 알아보면 좋을 것 같다. 예를 들면 여러 컴퓨터 언어 중 가장 할아버지라고 볼 수 있는 C언어다.

출처: 코딩도장

 구글에서 C언어의 자료형에 대해서 서치해보면 파이썬과는 사뭇 다른 분위기를 풍긴다. 파이썬은 흔히 수치형으로 퉁치고 넘어가는 자료형이 short, int, long, float, double... 등 바이트 수에 따라 굉장히 세분화하여 분류하고 있다.  char의 경우 파이썬의 문자열과는 달리 보통 글자 "하나"(character)를 뜻한다.  C언어의 경우 파이썬과는 달리 정적 언어이므로 변수를 선언할 때마다 int a같이 특정 자료형을 지정해주어야 하며 위의 자료형들은 변수를 선언할 때 사용하는 "기본 자료형"들이다.

 물론 위의 자료형들은 기본 자료형이고 이 기본 자료형들이 응용되어 더 복잡한 자료형을 만들기도 한다. 하지만 내가 느낀 것은 다른 자료형들도 결국은 저 "숫자"로 귀결된다는 것이다. 문자도 결국 아스키 코드 규칙에 따라 변형된 "숫자"이며, 포인터는 자료가 담긴 메모리의 주소를 "숫자"로 표현한다. C언어에서 배열은 메모리의 연속적으로 저장된 자료 중 맨 앞 자료가 담긴 메모리의 "포인터"이므로 결국 숫자다.

 나는 이런 개념들이 상당히 복잡하다고 느꼈고 숫자를 극한으로 이용하면 이렇게 까지 되는구나라고 생각했다. 하지만 한편으로는 간결해보이기도 한다. 하드웨어에는 0과 1로 이루어진 비트들이 존재하고, 이를 이용해 숫자를 표현하고, 이 숫자를 아스키 코드로 해석하면 문자가 되고, 메모리의 위치를 표시하면 포인터가 된다. 그리고 이 자료들을 메모리에 인접하게 저장하면 배열이 된다. 파이썬에선 잘 느껴지지 않던 자료형들 간의 관계가 너무 잘 보인다.

 파이썬도 결국 런타임이 C언어로 작성된 프로그래밍 언어다. 파이썬은 C언어와는 매우 다르지만 인터프리터가 작동하고 있는 내부에서는 C언어를 이용해 해석되고 있을 것이다. 굳이 그냥도 사용할 수 있는 C언어를 이용해 파이썬이라는 새로운 언어를 만든 것은 분명한 이유가 있을 것이다. 파이썬의 철학에서 이 이유를 추측할 수 있다. 파이썬은 가독성과 단순성, 효율성을 극도로 추구하고 있다. C언어는 한 번 읽고 이해하기가 어려운 것이 사실이며 낮은 가독성은 개발의 효율성을 떨어트리고 커뮤니케이션을 어렵게 한다. 파이썬은 C언어의 이 단점을 해결하기 위해 등장했다고 볼 수 있다.

여기서 파이썬의 자료형을 어떻게 봐야 좋을지에 대한 해답이 나온다. 파이썬의 자료형은 결국엔 C언어의 자료형을 응용해 더 다양한 기능을 수행할 수 있도록 만들어진 것이다. 그렇다면 이 파이썬의 자료형이 C언어의 어떤 번거로움을 해결하기 위해 만들어진 것인지 알게 된다면 그 자료형의 존재 이유가 밝혀지게 되는 것이다. 파이썬의 각 자료형들을 C언어의 관점에서 본다면 어떤 느낌인지 살펴보겠다. 밑의 자료형들은 파이썬의 공식문서 Built-in Types에 등장하는 분류대로 분류한 것이다. 

수치형(numerics)

파이썬의 수치형 자료형은 integer, float, complex 등 수를 나타내는 자료형들을 포괄적으로 부른다. C언어는 수치 자료형을 shor, int, long, float, double등 사용되는 메모리의 바이트를 기준으로 세세하게 나눠놓은 반면 왜 파이썬에서는 수치형을 하나로 뭉뚱그려 묶어서 부르는 경우가 많은 것일까? 파이썬 공식문서에 따르면 파이썬의 integer는 기본적으로 무한한 숫자를 표현할 수 있으며 float는 C언어의 double로 표현된다고 한다. 파이썬은 C언어와 달리 동적 언어이기 때문에 메모리를 할당하고 자료를 집어넣는 C언어와 달리 넣는 자료에 따라 메모리가 자동으로 할당되는 성격이 있기 때문에 굳이 바이트에 따라 수치 자료형을 나누지 않는 것으로 보인다. 또한 수치형에서 파이썬과 C언어가 근본적으로 다른 점은, 파이썬에서는 숫자가 웬만하면 숫자의 용도로 사용된다는 것이다. C언어는 숫자가 문자, 포인터 등의 형태로도 존재하는 것과 달리 파이썬에서는 수치형 자료형의 숫자들이 말 그대로 "숫자"그 자체의 목적으로 존재한다. 즉, 파이썬의 수치형 자료형은 C언어와 달리 상당한 유연성을 가졌으면서 용도 면에서는 더 명확한 자료형이라고 할 수 있다.

시퀀스(Sequence)

파이썬의 시퀀스 자료형은 리스트, 튜플, 문자열, 바이트 등을 포함하는 자료형이다.(다만 문자열과 바이트는 리스트, 튜플과는 조금 다른점이 있다) C언어의 배열과 비슷해보이는데 C언어의 배열과 파이썬의 시퀀스 자료형은 굉장히 큰 괴리감이 느껴졌다. 파이썬이 C언어의 바이트별로 다양한 수치 자료형들을 하나로 통합했다면, 시퀀스 자료형은 C언어의 배열을 세분화, 특수화해서 만들었다는 느낌이 강하다.

파이썬의 시퀀스 자료형은 공통되는 한가지 특징이 있고, 그 특징이 C언어의 배열과의 큰 차이점이다. 그것은 C언어가 데이터를 배열로 저장하는 것과 달리 파이썬은 포인터를 배열로 저장한다는 것이다. 포인터라는 것은 앞에서도 언급했다시피 메모리의 위치를 나타내는 것이다. C언어의 배열은 데이터가 일렬로 저장된 메모리의 맨 앞을 가리키는 포인터이다. C언어가 배열을 다룰땐 포인터를 이용해 배열의 맨 앞부분으로 접근하고, 배열의 끝을 알려주는 메모리가 나올때까지 옆으로 읽어 나간다. 때문에 옆으로 계속 읽어 나가기 위해선 배열이 한가지 자료형만으로 이루어져야 해석할 수 있고, 어디까지 읽어야 하는지 정해줘야 하기에 배열의 길이도 미리 선언해줘야 한다. 반면에 파이썬은 데이터 자체가 아닌 데이터가 담긴 포인터를 배열로 저장하기에 "데이터"의 자료형이 같을 이유가 없다. 어차피 배열 안은 똑같은 포인터 자료형이고, 포인터가 가리키는 데이터의 자료형은 마음대로 바꿀 수 있기 때문이다. 또한 파이썬은 포인터의 배열을 저장할 메모리를 일정 값으로 알아서 선언해주므로 시퀀스의 길이를 직접 선언해 줄 필요가 없다.

그런데 여기서 리스트와 튜플의 차이점이 드러난다. 파이썬에는 객체를 mutable 객체와 immutable 객체로 나눌 수 있는데 mutable 객체는 '뮤턴트' 처럼 값을 바꾸는게 가능하고 immutable 객체는 값을 바꾸는 것이 불가능하다. 리스트는 mutable이고 튜플은 immutable은 이유는, 리스트는 포인터의 배열의 크기가 데이터의 크기에 따라 동적으로 변하는 반면, 튜플은 튜플 생성시의 크기로 고정되기 때문이다. 그래서 리스트는 내부의 데이터가 바뀌더라도 바뀐 크기에 따라 메모리를 알아서 할당하지만 튜플은 그렇지 못하기때문에 오류가 생기는 것이다. 튜플에 + 연산으로 데이터를 추가할 수 있기 때문에 의아하게 생각할 수도 있으나 그것은 튜플에 데이터를 더 붙이는 것이 아니라, 튜플의 데이터와 + 연산해준 데이터를 순서대로 포함한 새로운 튜플을 생성하는 것뿐이다.

C언어의 배열은 튜플과 달리 배열 안의 값을 바꾸는 것에 대해서는 제한이 없다. 어차피 배열 안의 모든 자료형이 동일하기 때문에 다른 값으로 바꾼다고 해서 메모리 사용량이 변하지 않으므로 메모리 크기를 추가로 할당할 필요도 없긴하다. 근데 왜 파이썬은 mutable/immutable 객체를 구분하고 있는 것일까? 대부분의 파이썬 학습서에서 이 부분에 대한 설명은 없이 단순히 리스트는 가변객체이고 튜플은 불변객체라는 차이점이 있다는 식으로 넘어가기 때문에 따로 찾아보는 수밖에 없었다. immutable 객체가 필요한 이유는 크게 두가지로, '스레드 안전(thread-safety)'과 '시간과 메모리의 절약'이다.

스레드 안전은 어느 함수나 변수가 여러 스레드에 의해 호출되더라도 실행에 문제가 없는 것을 말한다. mutable 객체의 경우 값이 변할 수 있기 때문에 어떤 스레드에서 값을 변화시키면 다른 스레드에서의 작업이 잘못될 수 있다. 반면 immutable 객체는 그럴 걱정이 없다. 또한 mutable 객체가 값을 변화시키기 위해 실제로 필요한 메모리보다 더 많은 메모리를 할당하고 있는것과 달리, immutable 객체는 필요한 메모리만 할당시킨다. 때문에 mutable 객체보다 메모리 사용량이 적고 속도도 더 빠르다.

문자열 역시 시퀀스 타입에 속한다. 다만 다른 시퀀스와는 달리 문자 자료형으로 원소들의 자료형이 고정되고, immutable 객체이다. C언어도 문자열을 char의 배열로 다루지만 파이썬 문자열의 편의성이 훨씬 크다. 개인적인 느낌을 말하자면 C언어의 문자열은 문자의 탈을 쓴 배열이라는 느낌이지만 파이썬의 문자열은 더 직관적으로 문자열이라고 느껴진다. C언어에서 문자열을 다루는 것은 매우 어려운 반면 파이썬에서는 쉬운 이유다. 또한 파이썬의 문자열은 문자열끼리의 연산도 제공한다.

매핑(mapping)

매핑 자료형에는 딕셔너리가 속한다. 파이썬의 딕셔너리는 key와 value 구조를 가져서 시퀀스 자료형이 인덱스로 데이터에 접근하는 것처럼 key를 이용해 value에 접근할 수 있다. 딕셔너리의 key가 주어지면 해시함수를 통해 key를 인덱스로 매핑하고, 그 인덱스를 이용해 value를 찾는 방식이다. 이런 자료구조를 해시테이블(hash table)이라고도 한다. 

파이썬의 딕셔너리가 특이한 점은 배열과 해시테이블의 특징을 모두 가졌다는 것이다. 3.6버전부터 파이썬의 딕셔너리는 일정한 순서를 가지게 되었다. 그로 인해 for loop에서 더 유용하게 사용할 수 있게 되었다. 이는 파이썬 딕셔너리 자료형의 내부구조에 indicis 를 저장한 배열이 있기 때문이다. 다만 추가적인 메모리를 요구하기 때문에 시퀀스 자료형보다 메모리 낭비가 크다는 것은 단점이다. 파이썬의 딕셔너리는 JSON 형식의 파일을 다루는 데도 매우 유용하다. JSON 형식 자체가 딕셔너리와 완벽히 매칭되기 때문이다.

C언어에서는 딕셔너리와 같은 해시테이블을 사용하려면 직접 구현해야 한다. 이렇게 다른 언어에서는 직접 구현해서 사용해야하는 자료구조를 기본 자료형으로 제공하고 있다는 것은 파이썬의 철학을 잘 보여주는 사례라고 할 수 있다.

집합(Set)

집합 자료형에는 set과 frozenset이 있다. 두 자료형의 차이는 set은 mutable이고 frozenset은 immutable이라는 점이다. 집합 자료형은 데이터의 탐색이 O(1)로 빠르고 데이터들의 유일성을 보장한다. 사실 집합 자료형은 C언어 뿐만 아니라 다른 프로그래밍 언어에서도 잘 볼 수 없는 자료형이다. C언어에서 집합을 구현하는 것은 번거로운 일이라고 하는데 이런 자료형을 파이썬에선 built-in으로 사용할 수 있다는 것이 매우 편리하다.

마치며

글을 쓰다보니 다른 자료형에 비해 시퀀스 자료형에서 글이 너무 길어졌다. 사실 내가 이 글을 쓰게 된 계기가 시퀀스 자료형에 있기 때문이다. 파이썬을 배웠다고 하면서도 나는 이제까지 자료형들이 왜 존재하는지에 대해서 궁금해본적이 없는데, 리스트와 튜플에 대해 생각하다가 "리스트도 튜플이 할 수 있는 작업들을 모두 할 수 있는데 튜플이 왜 필요하지?" 라는 생각이 문득 들게 된 것이다. 나는 평소에도 배열을 쓸 일이 있으면 리스트를 사용하지 튜플을 사용한 적은 거의 없었고 딕셔너리의 items나 함수의 리턴값 정도로나 봐왔기 때문이다. 튜플에 대해 생각하다보니 다른 자료형들에 대해서도 생각하게 되었다. 그래서 결국 이번 기회에 한번 제대로 조사해보자는 생각으로 이 글을 작성하게 된 것이다. 좀 더 C언어랑 잘 비교하면서 본질을 꿰뚫 수 있는 글이 되었으면 좋았을텐데 C언어에 대한 사전 지식이 부족해서 아쉽다. 하지만 이번 기회에 자료형에 대해 깊게 이해하게 되면서 좀 더 파이썬과 가까워진 느낌이다. 앞으로도 종종 내가 가볍게 넘겨버려서 놓쳐버린 인사이트들을 다시 깊게 배우면서 되찾아야겠다.

부트캠프 파이널 프로젝트를 시작하며

4월 11일부터 나는 프로그래머스 인공지능 부트캠프의 마무리를 장식하는 파이널 프로젝트를 준비하고 있다. 사진을 초해상화(super-resolution)할 수 있는 GAN 모델인 ESRGAN을 웹 서비스 형태로 서빙하려고 한다. 파이널 프로젝트에 사용할 모델을 GAN 모델 중에서 ESRGAN을 선택한 이유는 단순히 GAN 모델이 창작물을 만들어내는 신기한 경험보다 일상생활에서 실용적으로 느낄 수 있는 서비스를 만들어보고 싶었기 때문이다. 다른 창작물들을 생성해내는 GAN 모델들의 경우 신기하긴 했지만 결과물이 썩 좋아보이지도 않았고 실제로 필요하다는 느낌이 오지 않았다. 하지만 ESRGAN의 경우 성능이 눈으로 바로 보이기도 하고, 일상생활에서도 인터넷에서 압축되어 화질이 떨어진 이미지들을 개선할 때 유용하게 사용할 수 있을 것 같았다. 그 동시에 내가 관심있는 GAN 프레임워크의 모델도 다뤄볼 수 있으니 이득이라고 생각했다.

처음엔 뮤직비디오 같이 저화질로 공개된 인터넷 영상들을 초해상화하는 서비스를 만들고 싶었지만, 동영상의 경우에는 inference에 걸리는 시간이 매우 오래걸리기 때문에 웹 서비스를 하는 것은 어려울 것 같다는 조언을 들어 이미지 쪽으로 방향을 바꿨다. 내가 구상한 구체적인 웹 서비스는 이미지를 업로드하면 모델의 연산을 거쳐 초해상화 된 이미지를 다운받을 수 있는 형태이다. 인터넷에 이미지 초해상화 관련 웹사이트들을 뒤져보니 역시나 비슷한 웹페이지들이 좀 있었다. 하지만 프로젝트를 할 때는 아이디어가 겹치는 다른 서비스들을 생각하지말고 우선 만들어보는 것이 중요하다고 어디선가 들었다(...) 실제로 계속 열어두고 서비스를 할 것도 아니니 괜찮겠지... 라는 생각으로 프로젝트 마무리까지 열심히 달려보기로 했다.

내가 생각한 프로젝트에 사용할 ESRGAN 모델은 사전학습되어 깃허브에 공개된 모델을 파인튜닝하여 사용하기로 했다. 아마추어 프로젝트이지만 그래도 성능이 좋은 모델을 만들고 싶었다. 성능이 안좋은 모델이 나오면 뭔가를 만들었다는 성취감보다는 아쉬운 마음이 남을 것 같았다. 사전학습 모델에 FFHQ 데이터 셋을 추가로 파인튜닝하여 인물 사진에 특화된 super-resolution 모델을 만들 계획이다. 고화질 인물 사진 데이터셋은 찾기 힘들 줄 알았는데 유명한 데이터셋이 있어서 의외로 금방 찾았다.

 

모델을 개선해야 해!

하지만 문제는 여기서 시작되었다. 명색이 파이널 프로젝트인데 그냥 사전 학습 모델을 파인튜닝하는 것만으로 괜찮을까? 라는 생각을 했다. 대체 내 능력으로 이 사전학습 모델을 그대로 사용하지 않고 개선하는 방법에는 무엇이 있는지 찾는 일은 너무 어려웠다. 고민고민하다가 모델의 inference time을 개선해보는 것은 어떨까하는 생각이 들었다. 사전학습 모델의 공개된 데모를 돌려보니 400 * 700 픽셀정도의 이미지를 넣으면 초해상화를 하는데 20초 정도의 시간이 걸렸다. 이 시간을 줄일 수 있다면 더 큰 이미지를 초해상화하는 데도 유용하게 쓰일 수 있겠다고 생각했다. 초해상화라는 것이 원래 적은 픽셀을 가진 저화질 이미지를 고화질 이미지로 바꾸는 것이긴 하지만 인터넷에서는 픽셀 수가 많아도 화질이 좋다고 할 수 없는 이미지들이 많다. 여러 웹사이트를 거치며 이미지가 압축되었거나 확대되었기 때문이다.

나는 솔직히 모델의 속도 개선에 대해서는 아직 시도해본 적이 없어 먼저 모델의 inference time을 줄이는 방법들을 찾아봐야 했다. 사전학습 모델을 사용할 것이므로 모델의 설계 단계에서 적용할 수 있는 방법들은 제외했다. 열심히 인터넷을 뒤져보니 모델의 inference time을 개선하는 방법은 크게 두가지 정도 있는 것 같다. Quantization(양자화)와 pruning(가지치기?)이다.

Quantizaton(양자화)

Quantization은 모델의 가중치 행렬의 값들을 더 낮은 바이트의 데이터 타입으로 변경하는 것이다. 모델의 가중치 행렬 속 값들의 데이터 타입을 보면 보통 float32 데이터 타입을 가질 것이다. Quantizaiton을 해주게되면 이 데이터 타입을 float16(half)나 int8 데이터 타입으로 변경한다. 이 과정을 거치면 모델의 성능이 떨어질 가능성도 있으나 전체적으로 메모리 사용량이 줄어들면서 모델의 inference time이 개선되게 된다. pytorch의 경우에 이 Quantization을 api 형태로 제공한다. 이 api를 사용할 경우 dynamic quantization이라고 해서 모든 가중치의 데이터타입을 바꾸는 것이 아닌 일부 가중치의 데이터 타입만 int8로 바꾸어 연산할 수 있다. 하지만 문제가 pytorch의 api를 사용하면 GPU가 아니라 CPU로 밖에 연산하지 못한다는 것이다. 속도를 빠르게 할 생각으로 GPU가 장착된 AWS EC2 P2 인스턴스를 제공받았는데 GPU를 못쓴다면 아무 의미가 없지않은가! 하긴 모델의 inference time을 줄이려는 시도는 원래 속도가 빠르던 GPU보단 CPU 쪽에서 더 수요가 많을테니...

future work라는데 언제 GPU에서 사용할 수 있게 될까요?

그래서 다른 방법으로 Quantization을 진행했다. 꼭 dynamic Quantization을 할 필요는 없다. pytorch 모델의 경우 model = model.half()와 같이 메소드를 사용하면 모델의 가중치를 float16으로 바꿔줄 수 있다. 이 경우에는 CPU에서 연산은 불가능해지고 GPU로만 연산이 가능했다. 또한 input tensor의 데이터 타입도 float16으로 바꿔줘야 한다.

하지만 나는 최악의 결과인 "효과 없음"을 보고 말았다. 정확히는 float32 모델로 연산시 21초 정도가 걸렸다면 float16을 적용한 후에는 20.xx초가 걸렸다. 사실상 큰 효과가 없었던 것이다. 정확한 이유는 알 수 없으나 내 추측에는 GPU의 문제인게 아니었을까 싶다. EC2 인스턴스에 장착되어 있던 GPU는 K80이었는데 엔비디아 홈페이지를 뒤져보니 K80은 float16 데이터 타입의 연산을 지원하지 않았다. 그럼에도 불구하고 나는 0.xx초가 줄어드는 효과를 봤기에 어떻게 잘하면 되는게 아닐까 어떤 부분의 코드를 잘못쓴게 아닐까하고 계속 시도해봤지만 역시나 똑같은 결과였다.

 

Pruning(가지치기)

Pruning의 경우는 모델의 가중치의 일부 값을 0으로 바꿔주는 방법이다. 무작위로 가중치를 골라 바꿔줄 수도 있고 모델의 영향을 별로 주지 않는 가중치만 0으로 바꿔줄 수도 있다. pruning을 하고나면 모델의 용량과 연산시 메모리 사용량이 작아지게 된다. pytorch는 pruning도 api로 제공한다. torch.nn.utils.prune 모듈을 사용하면 되며, 여기에 자세한 공식 튜토리얼이 소개되어 있다.

대충 이런모습, 근데 0으로 바꿔도 어차피 똑같이 텐서에 담겨 연산되는데 정말 속도가 빨라질까 의심되긴 했다.

환장하겠는건 pruning 조차 아무 효과가 없었다. 처음에는 pruning을 하면 모델의 성능이 많이 떨어지지 않을까 싶어서 소심하게 가중치 20% 정도를 pruning 해보았다. 그런데 inference time이 전혀 변화가 없었을 뿐더러 결과물에도 아무런 변화가 없어보였다. 나는 내가 너무 적게 pruning 했나 싶어 계속 pruning하는 가중치의 비율을 늘려보았지만 inference time은 변함없었고 결과물도 전혀 변화가 없었다. 시간이 변화가 없는건 그렇다쳐도 결과물까지 변화가 없는게 너무 이상해서 내가 어디에서 코드를 잘못친건가 계속 확인했는데 도저히 문제점을 찾을 수가 없었다. 서치해보니 pruning이 inference time에 별로 도움이 되지 않을수도 있다는 글을 보긴했지만 결과물까지 변함이 없는건 납득할 수 없었다. 아무리 찾아봐도 답이 나오지 않아 일단은 이 부분은 포기하기로 했다.

 

TensorRT

Quantization과 Pruning이 모델의 가중치를 변형시키는 방법이라고 하면, GPU 연산을 효율화하여 inference time을 줄이는 방법도 있다. 엔비디아의 경우 tensorRT 엔진이라고 하는 엔비디아 GPU 환경에서 사용할 수 있는 GPU 연산 최적화 SDK를 제공한다. tensorRT는 Quantization을 하거나 레이어의 연산같은 부분을 최적화시켜서 inference time을 줄일 수 있는데 pytorch와 tensorflow 등 다양한 프레임워크의 딥러닝 모델에서 사용할 수 있다. tensorRT 엔진은 엔비디아 도커르 사용해서 매우 쉽게 사용할 수 있었다. 다만 도커 이미지의 용량이 꽤 나갔다. tensorRT 엔진의 베이스 이미지 자체만 4GB 정도였다. 여기에 여러 필요한 라이브러리들을 설치하다보면 용량이 너무 쉽게 커졌다. 그래서 EC2 인스턴스의 스토리지를 20GB에서 50GB로 업그레이드하기도 했다.

그러나 이번에도 또 실패했다! 처음에는 tensorRT도 호환되는 GPU가 따로있어서 K80에선 돌려보지도 못하는 건가 싶었는데 구버전 tensorRT의 경우 K80도 지원한다고 쓰여있는 것을 발견해서 구버전 tensorRT로 도커 이미지를 만들고 torch2rt 라이브러리를 이용해서 pytorch 모델을 tensorRT 모델로 변경하려 했는데 이번에는 CUDA out of memory 에러와 마주하고 말았다;; K80은 이렇게 나약하단 말인가? 에러 난 부분을 보니 모델의 레이어를 전부 풀어서 input을 한번 통과시키는 과정이 있는 모양이었는데 그 부분에서 메모리가 터진 것으로 보였다. torch2rt의 깃허브 이슈에서 GPU 메모리가 터지면 torch.cuda.empty_cache()를 스크립트에 추가해서 GPU 캐시를 삭제해보라길래 vim으로 열어서 라이브러리 스크립트를 수정까지 해봤지만 GPU 사용량은 그대로였고 역시나 에러가 났다. 모델이 큰 것도 맞긴했는데 패러미터가 1천만개 정도로 BERT나 GPT 급으로 큰 모델도 아니었다. 대체 안에서 어떤 연산이 이뤄지길래 10GB 정도의 GPU 메모리를 전부 쓴단 말인가? 세번째로 시도한 방법을 실패하자 이쯤되니 좌절감이 장난아니었다.

다른 방법으로는 엔비디아의 trition inference server를 사용하는 방법도 있었다. 이 역시 엔비디아 도커를 통해 이용할 수 있었지만 GPU 호환이 맞지 않아 가볍게 컷됐다. triton inference server가 요구하는 GPU는 엔비디아 기준으로 6점 이상의 GPU인데 K80은 3점이다. 억지로 실행시킨다고 해도 오류를 뿜을 것이다. 

그 결말

이렇게 모델의 inference time을 줄이려는 내 노력은 실패로 돌아갔다. 이 방법들 말고는 input으로 주어지는 이미지의 크기를 제한해서 모델의 inference time을 줄이는 방법밖에는 떠오르지 않았다. 이 부분도 실험을 해보긴 해야겠지만 나름 큰 맘 먹고 다양한 방법들을 적용해서 모델의 inference time을 줄여보고 싶었는데 모두 실패해서 노력이 수포로 돌아가니 마음이 안좋았다. 최근 본 회사 면접때, 면전에 이제까지 한 프로젝트들이 시키는 것만 잘한 것 같다는 소리까지 들어서 조금 우울했는데 마음 다 잡고 이번에는 내가 스스로 생각해서 프로젝트에 기여해보리라는 생각에 다시 잘 해보려는데 이런 결과가 발생하다니. 사실 이 글을 남기는 것도 한 일들을 기록해서 내가 노력했다는 것을 알아주기라도 했으면 하는 마음에 적게 되었다. 

나는 결론적으로는 모델의 inference time을 줄여보려는 시도를 멈추고 다른 모델 개선방향을 찾아보고 있는 중이다. mlops 프레임워크를 도입해서 모델의 학습과 배포과정을 최적화해보면 어떨까 생각하고 있지만 내가 아직 사용해보지 않은 툴들이 많아서 잘 할 수 있을지는 모르겠다. 그래도 이왕 시작한 프로젝트 마무리 짓지 않으면 의미가 없다. 어떻게든 되게 하리라. 프로젝트가 마무리 될 쯤에는 꼭 내 노력으로 이뤄냈다는 성취감을 느껴보고 싶다.

ps. 여담이지만 머신러닝/딥러닝을 배우기로 결심한 이후로 나는 컴퓨터 앞에서 기도를 하는 일이 많아진 것 같다. 제발 한 번에 되게 해주세요, 제발 제 노력이 효과있게 해주세요 하고. 하지만 번번히 꼭 한 번씩은 실패한다. 딥러닝 모델에 blackbox가 있듯이 모델을 개발하는 과정에는 왠지 모르게 뭔가 노력으로 커버 되지 않는 영역이 있는 것 같다. 다른 사람들은 대체 이런 부분들을 어떻게 헤쳐나가는지 궁금하다 흑흑...

요구사항 분석의 개요

  • 사용자 요구의 타당성 조사, 비용과 일정에 대한 제약 설정
  • 정확하고 일관성 있게 분석해서 문서화해야 함

구조적 분석 기법

  • 자료의 흐름과 처리를 중심으로
  • 도형 중심의 분석용 도구 -> 사용자간 의사소통 용이
  • 하향식 방법, 시스템을 세분화할 수 있고 분석의 중복 배제 가능
  • 자료 흐름도(DFD), 자료 사전(DD), 소단위 명세서(Mini-spec), 개체 관계도(ERD), 상태전이도(STD), 제어 명세서

자료흐름도 (DFD) (= 자료 흐름 그래프, 버블차트)

  • 의미: 자료의 흐름 및 변환괒어과 기능을 도형 중심으로 기술하는 방법
  • 시스템 안의 프로세스와 자료 저장소 사이에 자료의 흐름을 나타내는 그래프
  • 자료는 process를 거쳐 변환될 때마다 새로운 이름이 부여됨
  • 프로세스(Process), 자료흐름(Flow), 자료저장소(Data store), 단말(Terminator)의 4가지 기본 기호로 표시

자료사전(DD)

  • 의미: 자료 흐름도에 있는 자료를 더 자세히 정의하고 기록한 것
  • 메타데이터라고도 한다.

표기 기호

  • =: 자료의 정의, ~로 구성되어 있다(be compose of)
  • +: 자료의 연결, 그리고(and)
  • (): 자료의 생략, 생략 가능한 자료(optional)
  • [|]: 자료의 선택, 또는(or)
  • {}: 자료의 반복, interation of
  • * *: 자료의 설명, 주석(comment)

+ Recent posts