Aug 15, 2019 - Reorganize Blog

무엇가 글로 쓰는 것의 의미

요즘 업무 시간을 따져보면 개발보다 문서화를 더 열심히 하고 있다. 게다가 친절한 글쓰기를 위해 한 번 쓰고 넘기지 않고 독자가 이해할 수 있을지 확인하고 수정하는 iteration이 시간 오래 걸린다.

그래도 이게 써놓고나면 나한테도 유용하고 누군가에게 설명할 때도 편리하다. 특히 요즘 기억력이 매우 떨어지고 있어서 한 달 전 코드를 보면 ‘과거의 내가 굉장히 똑똑했구나’ 감탄하며 가져다 쓰곤 한다.

한편으론 왜 글을 이렇게 밖에 못 썼을까 아쉬운 마음도 든다. 시간은 오래 걸리는데 생각만큼 퀄리티가 안 나온다.

그래도 글을 쓰는 이유는 결국 글을 잘 써야만 한다는 목표의식도 있고, 글을 쓰면서 무형의 마음이 유형의 글로 정제되어 성장한다는 감각이 있기 때문이다. 누구에게나 blog를 만들고 어떤 글이라도 올려보는 건 도움이 되는 활동일 것이다.

요즘 드는 생각은 블로그에 일기처럼 짧은 일상 글을 쓰고 싶다. 막상 혼자 보는 일기는 지속하기가 어렵고, 막 보여주긴 또 그러니까 누군가 보는듯 마는듯한 이 공간이 좋을 것 같다. 그래서 이제 새로운 글에 알맞게 블로그를 개편해보려한다.

깔끔한 현재 레이아웃을 대체로 유지하되, 카테고리 구분이 쉬운 template로 교체해보자. 그리고 댓글 기능도 고려했는데 역시 안 넣는게 좋을 것 같다. 아직 관리 운영할 여력은 없는 것 같다. 나중에 생각하면 추가하는 게 좋을듯.

how-to

웹 개발에는 익숙치 않다보니 template 바꾸는 방법도 검색했지만 아주 친절한 문서는 찾지 못했다. 그래서 초보도 따라할 수 있는 jekyll theme 변경법을 남겨본다. template 변경하면서 초보도 할 수 있는 몇가지 customizing 방법에 대해 남긴다.

그냥 template 파일을 덮어쓰니까 일단 작동은 한다… 거기에 몇가지 customize하면 충분할 것 같다.

  1. 사이드바에 카테고리 구분 추가
  2. 이미지가 div 박스를 뚫고 나온다
  3. 폰트 변경
  4. mathjax
  5. 웹 최상단 icon 추가
  6. code block coloring

사이드바에 카테고리 구분 추가

내가 쓰는 theme은 sidebar가 이미 구현되어 있다. 살펴보면 그냥 _includes/links-list.html 을 보라는 뜻이다.


<div class="sidebar well">
{% include links-list.html %\}
</div>

이 분이 쓰신 글을 참조하여 category를 화면에 뿌리는 것까지는 성공.

/_includes/links-list.html


<h1>Category</h1>
<ul>

{% for category in site.categories %}
<li>
    <a href="{{ root_url }}/{{ site.category_dir }}{{ category | first }}">
        <span class="name">{{ category | first }}</span> <span class="badge">{{ category | last | size }}</span>
    </a>
</li>
{% endfor %}

</ul>

링크를 누르면 redirect 주소로 http://127.0.0.1:4000/category/data-science가 찍힌다. 이 글을 참조해 해당 주소 요청을 받아주는 파일을 만들자. 먼저 /category/data-science.md를 만든다.

---
layout: category  
title: data-science
---

여기서 참조할 수 있게 /_layouts/category.html을 만든다. layout: default로 css 입히고 a태그로 post 링크 잡는 정도의 기능만 있다. 중요한 건 markdown의 title로 category 구분한다는 점. /category/*.md 파일 front matter에 title이 있어야 하는 이유다.


<div class="sidebar well">
    {% assign category = page.title %}
    <h1>{{category}}</h1>
    <ul>
        {% for post in site.categories[category] %}
        <li>
            <a href="{{ site.baseurl }}{{ post.url }}">
                {{ post.title }}
            </a>
            <small>{{ post.date | date_to_string }}</small>
        </li>
        {% endfor %}

    </ul>
</div>

완성된 모습. 진짜 별 것도 아닌 화면이지만 성취감도 있고 좀 더 예뻤으면 하는 아쉬움도 들고 참 복잡하다. 이래서 웹개발 하시는 분들이 많은게 아닐까.


이미지가 div 박스를 뚫고 나온다

markdown으로 삽입한 이미지가 post의 div 박스보다 크게 나와서 당황했었는데 theme.css에서 img와 body 태그에 한 줄만 추가하면 된다.

img {
  max-width: 100%;
}
body {
    overflow: auto;
}

폰트 변경

d2coding.ttf 폰트 파일을 font 폴더에 저장한다음 theme.css에 추가했다.

@font-face {
	  font-family: "D2Coding";
		  src: url('/font/D2Coding.ttf') format('truetype');
}
body {
    padding-top: 60px;
    padding-bottom: 60px;
    font-family: "D2Coding", menlo, consolas, courier, "courier new", fixed-width;
}

mathjax

블로그 글 따라 손쉽게 수식을 표현해주는 mathjax 기능을 넣었다. 나는 header.html의 head 태그 내부에 넣었는데 이 방식이 더 편한 것 같다.


{% if site.mathjax %}
<script type="text/x-mathjax-config">
MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}});
</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{% endif %}

그리고 site.mathjax를 받아주는 _config.yml 부분을 추가했다.

mathjax: true

역시 cdn을 쓰니까 굉장히 편리하다.


code block coloring

code block의 coloring이 예쁘지도 않고 빨간 음영이 눈에 띈다.

변경 방법을 찾아보니 /css/syntax.css 파일을 바꾸면 끝이다. 할 일은 좋은 소스를 찾기만 하면 된다. preview page에서 theme를 고르고 동일한 github으로 소스를 가져온다. 여기에 배경색을 설정하려면 .highlight,pre.highlight{background: #ffffff;}를 추가한다. 나는 github 테마를 골랐는데 이름 때문에 언젠가 한 번 써보고 싶었다. IDE도 dark theme만 쓰다보니 못 썼지만 드디어 써보는구나.


웹 최상단 icon 추가

명칭이 favicon인 걸 처음 알았다. 기본값은 지구본 모양으로 나오는데 다수의 git blog가 지구본 아이콘을 그대로 쓰고 있다. 그래도 내 관점에서는 즐겨찾기에서 보이는 수많은 지구본 아이콘이 참 불편하다. 방법은 참 간단하다. favicon 파일을 가져와 저장하고 header.html의 head 태그 내부에 한 줄만 추가하면 된다.

<link rel="icon" type="image/png" href="/favicon.png">

거의 blog 공사가 끝나간다. 남은 건 뭔지 기억이 잘 안 나지만 google에 등록하는 절차가 있던 것 같다. 거기에 네이버 robot도 긁어갈 수 있게 설정을 추가로 해보려한다.

P.S. 계속 windows 장비로 하다가 macbook을 쓰니 jekyll 쓰기도 편리하고 pycharm도 빠릿빠릿 돈다. 장비빨 덕에 더 좋은 글도 나올듯.

Aug 3, 2019 - Bayesian Mf

bayesian matrix factorization

matrix factorization이 필요한 프로젝트가 있어서 공부하다보니 hyper parameter를 tuning하지 않아도 되는 편리한 mf를 발견했고 바로 구현해봤다. bayesian 이론에 대해 탄탄한 기초는 없지만 논문 저자가 올린 matlab 코드를 참조했기 때문에 어렵지 않았다. 08년 논문이지만 공부하기에도 좋고 참조할만한 github도 적어서 공유한다.

구현해보니 장점은 bayesian의 장점인 빠른 수렴과 hyper parameter를 prior로 대체하여 latent 크기만 정하면 된다는 점,
단점은 dataset이 작으면 성능이 매우 안 나온다는 점을 체감했다. (원래 mf면 dataset이 크기 마련인데 프로젝트 특성상 어쩔 수 없는 부분)

아쉬운 점은 parallel learning을 구현하려했지만 제대로 성능이 나오지 않아 일단 방치했다..

참고 자료는
논문링크
논문저자matlab코드
논문구현github

구현한 소스코드는
bpmf.py - sklearn estimator로 구현
matrix_util.py - bpmf.py에서 사용하는 util 함수
test-data - sample dataset

# train data
triplet_train = numpy.array([0, 0, 5], # user 0, item 0, rating 5
                            [3, 1, 3], # user 3, item 1, rating 3
                             ...) 

# test data 
triplet_test = numpy.array([0, 1, 5], # user 0, item 1, rating ignored
                           [3, 1, 3], # user 3, item 1, rating ignored
                            ...) 
also_triplet_test = numpy.array([0, 1], # user 0, item 1
                                [3, 1], # user 3, item 1
                                 ...) 
# main function
from scipy import io

from model.bpmf import bpmf

# Triplets: {user_id, movie_id, rating}
raw = io.loadmat('moviedata.mat')
train_set = raw['train_vec'] - 1  # minus 1 to make data start from 0
test_set = raw['probe_vec'] - 1

n_user = 6040  # Abbreviation of person
n_item = 3952  # Abbreviation of movie

model = bpmf(max_value=5, min_value=0,
             n_user=n_user, n_item=n_item,
             early_stop_step=3, validatset=test_set,
             verbose=True)

model.fit(train_set[:, [0, 1]], train_set[:, 2])
model.predict(test_set)

# compatible with sklearn
from sklearn.model_selection import GridSearchCV


grid_search_pool = {'n_latent': [10, 15]}
cv_model = bpmf(max_value=5, min_value=0,
                n_user=n_user, n_item=n_item,
                early_stop_step=3)
grid_searcher = GridSearchCV(cv_model, grid_search_pool, cv=5, scoring='neg_mean_absolute_error',
                             n_jobs=2, error_score='raise')
grid_searcher.fit(train_set[:, [0, 1]], train_set[:, 2])
grid_searcher.best_params_

Jul 21, 2019 - Missing Days Log

현실의 데이터 사이언스는 아름답지 않았다.

다시 포스트를 올린 계기

거의 1년 간 블로그를 잊고 있던 와중에 낯선 메일을 받았다. 데이터 분석을 지망한다는 취업 준비생의 질문을 받았는데… 가장 먼저 부끄러움을 느꼈다. 초심을 완벽히 잊고 있었음을 반성하게 됐다.

지난 9개월을 변명하자면 굉장히 심신이 힘들었다. 현실의 데이터 분석은 전혀 멋지지 않고 교실에서 배운 거랑은 매핑이 안된다. 지금 느끼는 생생한 경험을 글로 남겨보려 한다.

그동안의 경험들 - 데이터 과학의 현실

(지극히 특수한 케이스일 수 있음)
올해 일어난 큰 변화는 소속팀의 존재 이유는 현업의 문제를 푸는 것으로 정의된 것이다. 그리하여

  1. 오직 현업 요청 프로젝트만 진행
  2. 2년차일지라도 사수 없이 프로젝트 담당

이 사소한 변화가 내게 현실을 알려줄 거라곤 생각 못했다. 몇가지 주요 챌린지만 쓰자면

  1. 일단 일 요청이 많다. (현업 몇백 명)
  2. 영업부서 과장급~팀장급 카운터 파트의 요청 (직급 차이)
  3. 커뮤니케이션 (그들은 데이터를 모르고, 나는 영업을 모른다)
  4. 고객이 이해하기 쉬운 언어로 (only 쉬운 알고리즘, 로직보단 스토리)
  5. 현업은 빠른 결과를 원한다 (야근)
  6. 데이터를 활용해서 뭔가 했다고 보고해야 한다 (성과압박, 포장)
  7. 고객님은 데이터에서 뭔가가 근사한게 나오길 원한다. (데이터는 only 운영데이터인데도)

이외에도 이런저런 챌린지가 많아 스트레스가 엄청났었고 오늘만 잘 넘기기를 기도하고 다녔다. (그 와중에도 고객님이 예쁘게 봐주셔서 인정은 받고 다녔다.) 이러한 어려움은 데이터 분석가로서 피할 수 없는 숙명이기도 하다는 것을 알리고 싶다.

왜 이렇게 힘들고 어려울까?

데이터 분석가는 현업이 아니지만 현업과 가까워야 한다.

데이터가 필요한 이유는 (1) 데이터 기반 의사결정, (2) 데이터 기반 서비스 2가지 이다. 이 2가지 아닌 방법으로 돈 벌고 있는 회사는 없는 것 같다. 결국 데이터가 실질적으로 활용되는 곳은 영업 현장이다. 분석가는 현업과 밀접하게 일해야되는 것이 맞다.

그렇다면 데이터 분석가를 현장 중심으로 정의하면 이렇다.

  1. 컨설턴트와 유사한 포지션으로 사내에 들어와
  2. 데이터 기반 솔루션을 만들고
  3. 연관 부서에 sales하는 role에 가깝다.
예시: 추천서비스

sales라는 단어를 설명하기 위해 영업 부서의 추천 서비스를 예로 들어보자. 기존에도 잘 굴러가고 있는데 영업 한 번 해본 적도 없는 데이터 분석가가 와서 이래라 저래라 하면 당연히 신뢰가 가지 않는다.
게다가 분석가는 처음부터 좋은 결과를 내놓기 매우 어렵다. 수차례의 trial-error를 겪으면서 더 좋아진다. 그런데 영업 담당자는 당장의 실적이 문제다. 분석가의 실수는 본인의 실적 마이너스가 되버리니 구조적으로 협업이 어려울 수 밖에 없다. 담당자를 설득하지 못하면 분석가는 추천 모델을 테스트하는 기회조차 없을 수 있다.

예시: 데이터 기반 의사결정

데이터 기반 의사결정은 정말로 현업을 알아야만 한다. 의사결정은 ‘우리는 뭘 해야하나요?’ 라는 질문에 답을 주는 것이다. 현업이 중요하게 생각하는 지표가 무엇인지, 그리고 할 수 있는 action이 무엇인지부터 알아야만 우리는 질문에 답할 수 있다. 실적을 높이기 위한 예측 모델에 현업이 컨트롤 할 수 없는 변수만 넣는다면?
또는 예측 모델의 y값이 전혀 현업에게 중요하지 않다면?

분석가가 사용할 데이터도 문제다.

데이터를 확보하기 위해 오래 전부터 기획 - 생산 - 점검의 단계를 거쳐온 넷플릭스와 같은 회사와는 달리 대부분의 회사는 기존에 가지고 있는 운영데이터 외에는 가치 있는 데이터가 소수다. 당연히 garbage in - garbage out이다. 좋은 데이터가 없는데 성과가 나오기 어렵다.

데이터 공부 해봤다 하는 사람은 data cleaning이 중요하다고 들었겠지만 보다 더 큰 문제는 data sourcing, 데이터를 가져오는 것 그 자체다. 뭐가 어디에 있는지, 이 데이터를 받을 수 있는 환경이 어디인지부터 확인하는 작업부터 쉽지 않다.

한탄

그냥 회사 차원에서 지원을 해줬으면 하는 경우도 있다. 내가 열심히 만든 데이터가 의미 없이 버려진다고 느낄 때도 있고
자원이 부족해 한정된 자원 안에서 알고리즘을 scale down(up, out이 아니다)해서 작업하기도 한다.
개인적으로 올 상반기는 내가 이 회사에서 의미있는 프로젝트를 하며 발전하고 있다는 느낌이 들지 않는 해였다.

데이터 사이언스를 지향한다면

일단 SKT, 네이버 등 top tier 회사를 가야한다. 왜냐하면 그래야 최소한 분석 환경에 대한 스트레스라도 안 받는다. (과제 시작하면 나는 맨날 분석 환경 걱정부터 한다) 쓸 수 있는 데이터도 많을 것이고 함께 일하는 동료나 다른 팀과 커뮤니케이션도 고통스럽지 않을 것이다. 전반적으로 데이터 문화가 어느 정도 수준에 올라야 한다. 특히 데이터가 직접적으로 매출에 크게 기여하면 좋다.
왓챠와 같은 데이터에 뿌리를 둔 스타트업은 여기에 해당하지 않을까?

데이터 사이언스 지망은 의미 없는 일, 비전도 없는 일을 특히나 싫어할 것이라고 생각한다. 오래된 non IT 기업에서 데이터 분석가로 일한다면 십중팔구 싫어하는 것들을 많이 만날 것이라고 감히 예견한다.

지금은

살 만하다. 사회생활을 조금 더 알아가는 거라고 생각한다. 오늘 글을 쓰면서 더욱 정리가 된 느낌이 든다. 이제 막 주어진 환경에서 나의 발전에 도움이 될 무엇을 향해 가고 있다.
최근 공부한 게 있는데 이 곳에 공유할 계획이다.

그리고 최우선은 언제나 심신건강이다.