4차산업 혁명, 직장인에게 퇴사란(?)

 * 사는 것보다 파는 것이 더 중요하다 -- 주식 고수들
 * 등산보다 하산이 더 중요하다 -- 등산하는 사람들
 * 빨리 올라가는 것보다 잘 내려오는 것이 더 중요하다 -- 정치인들
 * 이륙하는 것보다 착륙하는 것이 더 중요하다 -- 파일럿들
 이렇듯 회사 또한 입사하는 것보다 언제 어떻게 퇴사하는지가 중요하다. 우리는 도대체 언제 퇴사해야 할까? 잘 내려오지 못하면 이전보다 더 높이 올라 갈수가 없거든. 

 먼저 실리콘 밸리의 IT 업계를 얘기 한번 들어보자. 태평양을 건너 구글에 입사한 친구 왈, 1년차 퇴사율이 50%에 육박한다고 한다. 여기서 대부분의 한국 직장인들은 의아해 한다. "그리 좋은 회사를 와이!?". 우리는 퇴사라는 단어를 너무 부정적인 의미로 바라보기 때문이다. 내가 이해하는 (좋은 회사를 박차고 1년 이내 퇴사한) 그들의 이유는 의외로 대단히 간단명료하다. 좋은 회사에 취업하는 것이 그들의 목표가 아니기 때문이야.

 왜 한국 인재들은 대기업에 취업하는 것을 고집할까? 첫째도 둘째도 안전했으니까 그렇다. 과거의 한국 제조 산업을 보자. 고용 보장으로 애사심과 근면한 장인 정신을 고취하고 이를 통해 경쟁력을 세계 수준으로 끌어올려왔다. 우리는 모두 그러한 사회에서 근면성실을 최고의 덕목으로 '개근상'이라는 것이 있었고, 부득이한 조퇴 조차 눈치 보며 학교를 다녔지. 우리가 어른이 된 이후 직장 생활에서는 다를까? 연차 휴가를 회사 눈치보지 않고 사용하는 직장인이 과연 몇명이나 될까? 요즘도 농업적 근면성이 갑이다 (비웃음).

 각설하고 문제는 요즘의 회사가 경제구조가 빠르게 변하는 데에 있다. 근속 연수는 계속 짧아지고 일자리는 계속 사라지고 있다. 튼튼한 자동차, 반도체 등 제조 산업은 위기를 당면하고 있으며, On-demand 경제라는게 물 밀듯이 밀려온다. 이러한 상황에서 고용 보장, 대기업 같은 구시대적인 것만 붙잡고 늘어진다면 고민과 시름은 한없이 깊어진다.

 우리는 이때문에 앞으로 퇴사를 실패의 부정적 의미가 아니라 성장하기 위한 과정으로 이해해야하며, 직장인은 항상 퇴사를 진지하게 고민해야할 필요가 있다. 직장생활이 더러워서가 아니라 그게 닥쳐오는 현실과 미래이며, 좋은 회사를 찾는게 아니라 내가 성장하기 위한 회사를 찾아야 하며, 궁극적으로는 자신의 일과 자생력을 만들어 나아가야 한다고 본다.

 사실 이 글을 작성한 계기는 챠니윤의 "개발자 경력 관리 조언" 글을 읽은 것인데, 경력 관리를 통한 "취업전"보다는 자신에 대한 성찰이 먼저여야 하며, (이 글을 접하는 이에게 나마) 화려한 과거 경력이 미래의 일자리를 보장하지 않는다는 점을 말하고 싶었다.

Serverless Computing will be game changer

The core of the emerging Serverless Computing is the programming model paradigm, not container infra technology. One example is the existing event processing programming, which takes the stream data produced in the real world and emits it to the backend,
   frontend: App program -> emit streaming data to backend
   backend: kafka -> storm / spark / dataflow -> data store -> reuse
Inefficiency and disadvantages arising from the structure of the frontend and the backend are complex and slow (eg, distributed queuing, distributed streaming processing, etc.) and difficulty in constructing the server-side system. In particular, the task topology management problems of DAG-style or minibatch-style job are difficult. These are the Storm, Spark, which is now going to legacy.

So how does this change in Serverless?
  App program: pipeline of lightweight functions
To achieve this, runtime provisioning of lightweight function services is required, and that is Serverless Computing.

As another example, imagine the prediction model of Google Flights introduced in the previous post. The traditional approach would be to implement the prediction service by deploying the learned model with the existing dataset:


Machine Learning can also be implemented with the Serverless Computing Architecture in the fashionable DevOps style. The following is the Serverless Architecture of the event-driven prediction algorithm:


The advantage of this is that the complexity of the implementation and system is dramatically reduced, and it is possible to design the system with a decentralized architecture.

Serverless Computing 이란?

최근 대두되는 Serverless Computing의 핵심은 프로그래밍 모델 패러다임에 있다. 한가지 예를 들어, 기존 event processing programming을 보면 현실세계에서 생산되는 stream data를 넘겨받아 backend에서 분산처리하는 구조인데,
   frontend: App program -> emit streaming data to backend
   backend: kafka -> storm / spark / dataflow -> data store -> reuse
이렇게 frontend와 backend 를 나누면서 발생하는 비효율성과 단점은 server-side 시스템 구축이 어렵고 (e.g., 분산 큐, 분산 streaming 프로세싱 등) 복잡하며 느리며 특히, backend의 DAG-style 또는 minibatch-style 분산 컴퓨팅 작업 내 Task topology 관리 문제가 어렵다. 이런걸 하던 애들이 기존에 Apache Storm, Spark 이런건데 이것도 이제 legacy로 가는 거다.

그럼 Serverless에서는 이것이 어떻게 바뀌느냐.
   App program: pipeline of lightweight functions
끗~. 이를 달성하기 위해서 lightweight function 서비스의 runtime provisioning 이 필요한 것이고, 그걸 한다는게 Serverless Computing 이다.

또 다른 예로, 이전 포스트에서 소개한 Google Flights의 가격 변동 예측 모델을 상상해보자. 전통적인 방법은 다음과 같이 기존에 쌓아둔 dataset을 가지고 학습된 model을 deploy 해서 prediction 서비스를 구현 할 것이다:


Machine Learning 또한 요즘 유행하는 DevOps 스타일로 Serverless Computing Architecture로 구현가능하다. 다음은 Event-driven prediction 알고리즘의 Serverless Architecture이다:



이로써 얻는 장점은 구현과 시스템의 복잡도가 극적으로 줄어들며, decentralized architecture로 시스템을 설계 하는 것이 가능하다.

관련 오픈소스는 Tensorflow on Openwhisk 에서 확인해보시라 (주말 프로젝트라 속도가 느림).

Google Hotels & Flights로 보는 배치-성 data-driven vs. 실시간-성 event-driven

지난 달 Google Flights는 가격 변동 예측 서비스를 추가했다고 발표했다.

3일 후 가격 오를거 같다고 알려주는 중 ㅋ

Google Hotels, Trips, Gmail 등과 결합되어 시너지를 만들어 낼 것으로 예측하며 그러나 내가 제품 분석가는 아니므로 자세한 얘기는 스킵.

하고 싶은 얘기는 내가 요즘 보고 있는 백-엔드 기술 변화가 배치-성 data-driven에서 실시간-성 event-driven으로의 전환에서 오고 있다는 것이다. Database, Analytics, 그리고 Machine Learning 대부분의 분야에서 변화가 있을 것으로 조심스레 예측한다 (나도 요즘 Time-series Database, Analytics, and Machine Learning 에 대해 좀 더 심도 있게 파고 들고 있다).

가령, 가격 변동 예측도 요즘 유행하는 Deep Learning을 쓸 수 있는데, 다음은 구글링으로 굴러다니는 이미지 스틸:

A set of event tuples를 입력! 

타임 윈도우 이벤트 셋을 인풋으로 받아 시계열 예측을 설명하고 있는데 관련해서 자료를 찾으면 Elman Net, RNN 등을 활용해서 Time-series Predictions가 이미 꽤 연구되어 온 것을 볼 수 있고, 내 생각엔 좀 더 보강하면 학습과 예측 둘 다 실시간 온라인으로 반영할 수 있겠다는 생각이 든다.

최종 서비스로 나가기 까지의 processing, training 모든 과정 전부 event-driven programming 으로 가능하며 IoT 인터넷 시대에 맞춰 관련 기술의 확산은 점차 가속화 될 것으로 생각한다.

비전, 전망, 전략의 부재의 효과

소프트웨어는 상당한 지적 노동에 해당한다.
그래서 소프트웨어 산업은 지식인들로 구성 되는데 우리는 그곳에서 가짜와 삐딱이를 자주 보게 된다.

지식인이 삐딱선을 타는 이유는 하나다.
비전의 부재, 전망의 부재, 전략의 부재. 

어디로 가야할지 길을 모르니 모호하고 피상적인 트렌드를 토대로 사기를 치는 가짜로 전락하는거다.
이것이 기업은 물론 각 개인에게 있어 지식보다 비전과 전략이 필요한 이유다.

If This Then That

 최근 친구로부터 흥미로운 스타트업 이야기를 들었는데 그것이 IFTTT 이다.

 내용인즉슨, 현실 세계의 모든 action stream을 트리거로 연결한다는 것인데, 서비스로써의 유용성이나 가치보다 이것이 수집할 데이터에 대해 의미가 굉장히 크다.

개인적으로는 이것이 성공을 어느 정도 한다면 IoT Intelligence의 진입점에서 큰 획을 긋지 않을까 생각한다.

2016년 나는 어떻게 살았나? 상/하반기

작년을 보니까 내가 이러던 시절도 있구나 싶다 - http://blog.udanax.org/2015/11/2015.html

 구글이 공격적인 본색을 드러내기 직전이라 그랬던지 나는 AI 시대에 맞춰 장미빛 미래를 안이하게 그리고 있었고 (너무 부끄러울 정도로), 이 회사에 입사하는데 있어 큰 영향을 준 "그렉"이라는 사람은 원정 경기의 불리함을 이기지 못하고 방황하는 듯 보인다. 지못미.

 한편, 나는 올해 상반기는 Apache Horn arXiv:1608.00781 초록을 한편 작성하고 동료들과 현재 v02를 준비 중이며, 여름 6/7월은 교육 때문에 외부와 단절된 "용인 창조관"에서 눌러 살았으며, 8월 부터는 다시 Database 쪽을 하고 있다. 정부 관련이나 ETRI와의 인연의 끈은 가늘게 붙었다 떨어졌다 하는 듯 하고 그 외에는 특이사항이 별루 없다 (사실 연초에 재밌는 여러가지 일들이 있었으나 당분간은 비밀로 하자).

 음, 왠지 애기아빠가 되어버린 이후 급변하는 일상에서 다소 속도를 잃고 있으며 회사에서의 뚝뚝 끊기는 환경 변화 때문에 그나마 힘을 실어 보내던 내 비전에 대한 모멘텀 (관성) 이 ... 내가 길들여지는 느낌 .. 받는다.

 16년 마무리 글을 쓰기에는 이른 감이 있고 여러모로 아직 불확실한 부분이 많지만, 둘째 출산을 준비하면서 사용할 기력을 좀 남기기로 하고 일단 여기 까지만 맘 먹어본다.

Revisiting decentralized computing architecture (edge computing)

Apache Hadoop is dying, it's not simply because of inefficiency or slow performance against competitors such as Apache Spark. There's a fundamental changes of environments around the world, and they backend infra projects are dying together. Thesedays, the IoT internet age had dawned, we'll meet the new circumstance and if you ask me what's next generation architecture for it, I can say that's "edge computing". In near future, each device will be act as a node of network in the universal system and so weight lightening is already trend in many software area. I believe Data management and Analytics will meet new paradigm soon. Perhaps, we already seeing them.

미세먼지에 대한 중국과 한국의 태도

우리가 알고 있듯 미세먼지 주요 원인의 화석 연료를 태울때 발생하며 대부분 중국에서 유입된다 알고 있지만 최근 재미있는 사실을 발견했다. 중국은 경제 성장률을 유지하면서 석탄화력발전소 폐쇄 정책으로 석탄 사용량을 꾸준한 감소 시키고 있으며, 정부의 부단한 노력으로 지난 4년 간 수력과 풍력 등 저탄소 전력원의 비중은 19%에서 28%로 증가했다고 한다.

 

반면, 재벌 기업 친화적인 한국 정부는 20기에 달하는 석탄발전소 추가 건설계획을 경제성장 측면만 부각시켜 묵인하고 있다는 사실이다.

고등어 드립을 쳐도, 디젤차를 핑계로 경유 인상안을 내놓아도 왜 우리 국민은 이를 금새 잊고 이리도 미개하게 .. 흙흙

소프트웨어 개발자들의 유효기간

 소프트웨어와 알고리즘이 중요하다 강조하는 목소리들이 많은데, 뒷북 좀 그만치자 ...

 내 눈에는 그 가치가 급락 중으로 보인다. 

 믿기 어렵다면 Google Trends로 우선 Programmer를 한번 넣어보자. IT 기술과 시장은 커지는 반면 Programmer에 대한 검색량이 주는 이유는 뭘까? (software engineering도 똑같음)


이걸론 부족하겠지. Indeed.com 잡 트렌드도 보자 ㅋ.

 

Cloud computing의 빠른 진화로, 과거와 달리 IT 비지니스를 위해 더 이상 많은 인원이 달라붙을 필요가 없어졌기 때문이다. 아마 연식이 좀 된 사람들은 알 게다. DB와 리눅스를 잘 다루는 (고급) 서버 개발자와 (초/중급) 클라이언트 또는 웹 개발자를 찾던 시절을. 

 이제는 데이터베이스 전문가, 리눅스 전문가, 서버 전문가, 대형 서비스 경험자, 클라이언트 앱 개발자 기타 등등 인력을 충분히 조달해야 가능하던 일이 한 명 혼자 충분히 해결 가능하다. 

 우리는 요걸 Serverless 시대라고 말한다. 

 소프트웨어 개발자들은 초기 OS와 각종 유틸 전쟁을 넘어 웹의 출현과 함께 server-side technologies에 몰렸고, 오늘날 대부분의 서버 기술이 Clouds로 녹아든 이후엔 App 개발과 스타트업으로 몰리는 양상을 띄고 있다.

 개인 오픈소스 해커들도 사라지고 있는데 individual contribution 위주의 Unix, Linux, 그리고 Apache 활동 들은 대기업 company-driven contribution 형태로 넘어가고 있다.  

 그럼 이쯤해서 개발 랭귀지 동향은 어떻게 변화하고 있는가 보자. C++ 시대를 넘어 Java가 최고급 대우를 받던게 불과 수년 전인데 Scala나 Python으로 빠르게 넘어가는 걸 볼 수 있다. CPU, 메모리 최적화 Coding skills 그딴거 더 이상 필요없다. 논리 설계하면 발로 개발 하든 어쩌든 동작한다.


 통신 인터페이스는 어떻게 되어 가고 있을까? 굳이 길게 설명할 것도 Trends 결과를 붙일 필요도 없다. 웹 소켓과 JSON 따위의 Key/Value로 정리된다. 요즘 RDF나 XML, 그리고 JDBC, ODBC 써본 사람?

 그러니까 결론은 ... 소프트웨어 엔지니어링 노동력의 가치는 점차 자동화에 의해 대체되고 있고, 일기 쓰는 것보다 쉬워지고 있다. 창의적인 App 개발을 해야 살아남는 현실을 인식하지 못한다면 굉장히 위험하다. 소프트웨어 개발자의 유효기간은 이미 끝났다.

Include Aparapi into maven local repository

1. Clone the aparapi from the github and build
% git clone https://github.com/aparapi/aparapi.git
% cd aparapi
% ant -Dapp.sdk.dir=/home/edward.yoon/AMDAPPSDK-3.0/

2. Install the file 
% mvn install:install-file -Dfile=/home/edward.yoon/aparapi/com.amd.aparapi/dist/aparapi.jar -DgroupId=com.amd.aparapi -DartifactId=aparapi -Dversion=1.0 -Dpackaging=jar

3. Edits pom.xml file 
After installed, just declares the aparapi coordinate in pom.xml.
    <dependency>
      <groupid>com.amd.aparapi</groupid>
      <artifactid>aparapi</artifactid>
      <version>1.0</version>
    </dependency>
Build it, now the "aparapi" jar is able to retrieve from your Maven local repository.

Collective Artificial Intelligence

DeepMind executive director Demis Hassabis said "AlphaGo played himself, a different version of itself, millions and millions of times, and each time there is progressively a little bit better". And, I imagined just like:

알파고 2개가 서로 바둑을 뒀다는 걸 들으면서 나는 스미스를 떠올렸다. 병렬로 존재하며 다른 경험을 하고 서로 정보와 의견을 공유하는 우리의 스미스 요원:

Agent Smith - The Matrix

Today, many people says about Super-intelligence and its dangerousness. The people wonder who will made it. I personally think, we'll made it together like a internet. So, I believe the most important keywords are "collective" and "parallel". Anyway, it's just a natural phenomenon. Historically, the single-celled organisms had evolved into multi-cellular organisms. If you think it's dangerous, you maybe a cancer cell.

아마도 초지능으로 넘어가는 세계는 어떤 한 집단에 의해 개발되는게 아니라 인터넷처럼 우리 모두의 활동에 의한 것으로 상상한다. 그래서 Knowledge transfer, Collective Intelligence, Parallel computing 등에 주목 할 필요가 있다. GPU 카드 따위가 중요한게 아니라. 한편, 단세포가 뭉쳐 불현듯 다세포 생물로 진화 했듯, 우리도 그러한 진화 속에 있다고 본다. 그래서 인공지능이 위험하다는 발상은 암세포 만이 할 수 있다는.

First submission to the arXiv

and I'm preparing v02 w/ ma co-workers using shareLaTex.

Horn: A System for Parallel Training and Regularizing of Large-Scale Neural Networks

I introduce a new distributed system for effective training and regularizing of Large-Scale Neural Networks on distributed computing architectures. The experiments demonstrate the effectiveness of flexible model partitioning and parallelization strategies based on neuron-centric computation model, with an implementation of the collective and parallel dropout neural networks training. Experiments are performed on MNIST handwritten digits classification including results.
Subjects: Distributed, Parallel, and Cluster Computing (cs.DC); Learning (cs.LG); Neural and Evolutionary Computing (cs.NE)
Report number: EP-909420F9A6E94B3691E5EE413DAD353E
Cite as: arXiv:1608.00781 [cs.DC]
  (or arXiv:1608.00781v1 [cs.DC] for this version)

Convex Hull in Java

Convex Hull 이라는 것은 points set의 외곽면을 찾는 것인데, 기하학이나 이미지 처리에서도 쓰인다. 알고리즘 문제에서는 흔히 가장 distance가 길거나 하는 형태로 출현하는데, Java로 한번 짜보자.

참고로 Apache Hama를 가지고 distributed convex optimization 구현도 찾아볼 수 있다 :)
http://www.slideshare.net/rogerz1234567/distributed-convex-optimization-thesis-behroz-sikander 

import java.util.Scanner;

public class Main {

  private static Stack hull;
  private static Point first;

  public static void solve(Point[] pts) {
    int N = pts.length;
    Point[] points = new Point[N];
    for (int i = 0; i < N; i++)
      points[i] = pts[i];

    Point[] tmp = new Point[points.length];
    mergeSort(0, points.length - 1, points, tmp, false);

    first = points[0];

    mergeSort(1, points.length - 1, points, tmp, true);

    hull.push(points[0]);
    hull.push(points[1]);
    
    for(int i = 2; i < N; i++) {
      Point head = points[i];
      Point middle = hull.pop();
      Point tail = hull.peek();

      int turn = ccw(tail, middle, head);
      
      switch(turn) {
          case 1:
              hull.push(middle);
              hull.push(head);
              break;
          case -1:
              i--;
              break;
          case 0:
              hull.push(head);
              break;
      }
    }
  }

  public static void mergeSort(int low, int high, Point[] a, Point[] tmp,
      boolean isFirst) {
    if (low < high) {
      int mid = low + (high - low) / 2;

      mergeSort(low, mid, a, tmp, isFirst);
      mergeSort(mid + 1, high, a, tmp, isFirst);
      merge(low, mid, high, a, tmp, isFirst);
    }
  }

  private static void merge(int low, int mid, int high, Point[] a, Point[] tmp,
      boolean isFirst) {
    for (int i = low; i <= high; ++i) {
      tmp[i] = a[i];
    }

    int i = low;
    int j = mid + 1;
    int k = low;

    while (i <= mid && j <= high) {
      boolean comp;
      if (isFirst) {
        comp = tmp[i].compareTo2(tmp[j]) <= 0;
      } else {
        comp = tmp[i].compareTo(tmp[j]) <= 0;
      }

      if (comp) {
        a[k] = tmp[i];
        i++;
      } else {
        a[k] = tmp[j];
        j++;
      }
      k++;
    }

    while (i <= mid) {
      a[k] = tmp[i];
      k++;
      i++;
    }
  }

  static Point[] stack = new Point[1000000];
  public static class Stack {
    private int top;
    int size;

    public Stack(int size) {
      this.size = size;
      top = -1;
    }

    public void push(Point v) {
      if (top == size - 1) {
        System.out.println("stack is full");
      } else {
        size++;
        top++;
        stack[top] = v;
      }
    }

    public Point pop() {
      if (!isEmpty()) {
        size--;
        return stack[top--];
      } else {
        return null;
      }
    }

    public Point peek() {
      if (isEmpty())
        return null;

      return stack[top];
    }

    public boolean isEmpty() {
      return top == -1;
    }

    public int size() {
      return top + 1;
    }

    public Point[] getArray() {
      Point[] tmp = new Point[this.size()];
      for (int i = 0; i < this.size(); i++) {
        tmp[i] = stack[i];
      }
      return tmp;
    }
  }

  public static int ccw(Point p1, Point p2, Point p3) {
    double area2 = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
    if (area2 < 0)
      return -1;
    else if (area2 > 0)
      return +1;
    else
      return 0;
  }

  public static class Point implements Comparable {
    double x, y;

    public Point(int x, int y) {
      this.x = x;
      this.y = y;
    }

    public String toString() {
      return "(" + x + ", " + y + ")";
    }

    public int compareTo(Point that) {
      if (this.y < that.y)
        return -1;
      if (this.y > that.y)
        return +1;
      if (this.x < that.x)
        return -1;
      if (this.x > that.x)
        return +1;
      return 0;
    }

    public int compareTo2(Point q2) {
      double dx1 = this.x - first.x;
      double dy1 = this.y - first.y;
      double dx2 = q2.x - first.x;
      double dy2 = q2.y - first.y;

      if (dy1 >= 0 && dy2 < 0)
        return -1;
      else if (dy2 >= 0 && dy1 < 0)
        return +1;
      else if (dy1 == 0 && dy2 == 0) {
        if (dx1 >= 0 && dx2 < 0)
          return -1;
        else if (dx2 >= 0 && dx1 < 0)
          return +1;
        else
          return 0;
      } else {
        return -ccw(first, this, q2);
      }
    }

  }

  public static void main(String[] args) throws Exception {
    Scanner sc = new Scanner(System.in);
   
    int N = sc.nextInt();
    Point[] points = new Point[N];
    hull = new Stack(N + 1);
      
    for (int i = 0; i < N; i++) {
      int x = sc.nextInt();
      int y = sc.nextInt();
      points[i] = new Point(x, y);
    }
    solve(points);

    System.out.println(hull.size());
   
    sc.close();
  }

}

SEMPRE: Semantic Parsing with Execution

SEMPRE: Semantic Parsing with Execution (Slides)

QA 분야 쪽에 꽤 알려져있는 오픈소스로 스탠포드 학생이 만든 Semantic Parser다.

기본적으로 beam-based bottom up parser 이며, 모호한 질의가 들어왔을 때 수십만개가 될 수 있는데 beam-based로 전부 순회하지 않고 최적의 top K개 best-first search로 후보를 얻는다. 여하튼 결론적으로, maximum likelihood (w/ adagrad)로 k = 500 최적의 probabilistic parse tree 를 찾았다는 얘기다.


물론 요즘은 딥 뉴럴 네트워크로 넘어가는 것이 추세고, 그와 동시에 1년 사이 Sempre 기록은 박살 났으나 기본적인 학습 알고리즘은 같다. 다음 블로그에서는 MSR에서 내놓은 검색이나 QA에서 사용되는 CNN 기반 Deep Semantic Similarity Model을 알아보자!

AdaGrad, adaptive stochastic (sub)gradient descent

AdaGrad (아다그라드? ㅋ) 는 2011년도에 최초 제시된 것으로 상당히 최신 최적화 이론이다. 아래의 워싱톤 대학 자료가 상당히 잘 설명 되어 있는데,

- https://courses.cs.washington.edu/courses/cse547/15sp/slides/adagrad.pdf

Adagrad는 adaptive stochastic (sub)gradient descent로 이전 관측을 결합하는 것으로 매 iteration에 feature 별로 learning rate를 달리 하겠다는 개념이다 - "Adagrad provides a feature‐specific adaptive learning rate by incorporating knowledge of the geometry of past observations".

feature 별로 달리한다는게 상당히 매력적인데 왜냐하면 수많은 features에서 의미없는게 대다수이기 때문. 그리고, 일반 GD에서는 보통 그래디언트가 어떻게 되는지 모르기 때문에 모든 iteration에서 고정값을 사용한다. 그래서 AdaGrad를 잘만 사용하면 수렴 속도를 높일 수 있다.


가령 Learning Rate가 0.5라면, 해당 파라미터에 대해 에러를 50%만 반영한다는 의미다. 매 iteration에서 error가 줄어야하는데 중간에 에러가 커지는 현상이 일어나면 위와 같이 기울기가 올라가 버리는 경우이니 Learning Rate를 줄이는 것이다.


YodaQA - IBM 왓슨 PC용 오픈소스버전

"Yet anOther Deep Answering pipeline" 이라는YodaQA 프로젝트가 있다. 지식기반 질의응답 시스템인데, IBM 개발자들이 오픈소스로 내놓은 Apache UIMA, 그리고 스탠포드 대학에서 만든 Parser와 Solr 등을 사용한다.

자연어로 질문이 들어오면 1차적으로 파서가 형태소, 의존, 성분 등 구문 분석을 통해 Nouns, Named entities 그리고 LAT+SV (lexical answer type + selection verb) 등을 가지고 정보 검색을 수행한다. 기계학습은 Answer scoring에서 사용되는데 embedding-based selection이라고 질문의 vector embedding과 결과값을 로지스틱 회귀로 학습된걸로 일종에 분류 기반으로 랭킹하여 결과를 보여준다.

그래서 결과가 어떻냐? KB:62.9% on a collection of movie questions (Google: 59.9%). 특정 도메인에서의 정확도는 구글보다 좋다고 한다. 프로젝트 만든이와 채팅해보니 현재 Semantic Parsing 분야도 추가적으로 추진하고 있다고.

온라인 데모도 존재하는데 다음 사이트를 방문해보시라: http://ailao.eu/yodaqa/

Merging an upstream repository into your fork

  1. Open Git Bash.
  2. Change the current working directory to your local project.
  3. Check out the branch you wish to merge to. Usually, you will merge into master.
    git checkout master
    
  4. Pull the desired branch from the upstream repository. This method will retain the commit history without modification.
    git pull https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git BRANCH_NAME
    
  5. If there are conflicts, resolve them. For more information, see "Resolving a merge conflict from the command line".
  6. Commit the merge.
  7. Review the changes and ensure they are satisfactory.
  8. Push the merge to your GitHub repository.
    git push origin master
    

미쳐 산다는 것은 ..

Sing like no one is listening. 
Love like you’ve never been hurt. 
Dance like nobody’s watching, 
and live like it’s heaven on earth.

C/C++ vs. Java

요즘 본의 아니게 알고리즘 공부를 좀 하면서 깨달은 바에 대해 몇 개 적어본다.

1) Java의 System.in/out 은 굉장히 느리다. 얼마나 느리냐고? 다음은 10,000,000 정수를 읽는데 걸리는 수행시간이다:

Input MethodTime (sec)
C scanf("%d", &arg)3.78
Scanner.parseInt()29.52
BufferedReader + inline Integer.parseInt2.89
BufferedReader + Reader.nextInt method3.01

단순히 2 ~ 3배 느린게 아니라 10배 가까이 차이가 난다. 이 때문에 BufferedReader 그리고, 출력에 있어서는 StringBuilder를 사용해야만 시간 초과를 피할 수 있다. 9935번 문자열 폭발이 대표적인데 알고리즘이 문제가 아니라 System.out.print 속도가 느려서 해결하기 쉽지 않다.

2) 최적화된 C/C++ 프로그램 속도는 빠르지만 역시나 코드는 더러워서 볼 수 가 없다 :/ 빠른게 능사가 아니란 말이다. 아래는 내 코드 (수행속도: 70 ms) 와 속도로 1위한 사람의 C++ 코드 (수행속도: 0 ms) 다. 물론 개발자 차이가 있을 수 있는데 ㅋ 일반적으로 Java나 함수형 언어에 익숙한 개발자는 대게 코드가 수식과 같고, C/C++은 무슨 연유인지 알 수 없으나 네이밍도 그렇고 코드가 대게가 지저분. 이 때문에 알고리즘 구현과 수행속도는 개발자의 평가기준이 절대 될 수 없다고 생각한다. Jeff Dean께서 그리 말씀하셨다니 나도 한마디 거들면 C++은 혐오스럽다.

한번 직접보시라:
import java.util.Scanner;
 
public class Main {
  static int[] a;
  static int[] memo;
 
  private static int calculate(int i) {
    if (i == 0) memo[i] = a[i];
    else memo[i] = Math.max(a[i], memo[i - 1] + a[i]);
     
    if (i == a.length - 1) {
      return memo[i];
    } else {
      return Math.max(calculate(i + 1), memo[i]);
    }
  }

  public static void main(String[] args) throws Exception {
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    a = new int[n]; memo = new int[a.length];
 
    for (int i = 0; i < n; i++)
      a[i] = sc.nextInt();
 
    System.out.println(calculate(0));
    sc.close();
  }
}
vs.
#include 
#include 
 
namespace my
{
    struct istream{};
    istream cin;
 
    inline bool is_num( char c ){ return ( c >= '0' && c <= '9' ); }
    inline bool is_negative( char c ){ return c == '-'; }
    inline istream& operator>>( istream& in, int& out )
    {
        char c; out = 0; bool neg = false;
        while( c = getchar() ){ if( is_negative(c) ){ neg = true; break; } if( is_num(c) ){ out += (c-'0'); break; } }
        while( c = getchar() ){ if( !is_num(c) ) break; out *= 10; out += (c-'0'); }
        if( neg ) out *= -1;
        return in;
    }
 
    inline void calc_max( int& max, int value ){ if( max < value ) max = value; }
    inline void calc_min( int& min, int value ){ if( min > value ) min = value; }
}
 
int main( int argc, char** argv )
{
    int N;
    my::cin >> N;
 
    int curr_max = std::numeric_limits::min();
    int curr_min = 0;
    int temp_min = 0;
 
    int n, accum = 0;
    for( int i = 0; i < N; ++i )
    {
        my::cin >> n;
        accum += n;
 
        if( accum > curr_max )
        {
            curr_max = accum;
        }
 
        if( accum - temp_min > curr_max - curr_min )
        {
            curr_max = accum;
            curr_min = temp_min;
            temp_min = 0;
        }
 
        if( accum < temp_min )
        {
            temp_min = accum;
        }
    }
 
    printf("%d\n", curr_max-curr_min);
 
    return 0;
}

[백준 알고리즘 풀이] 7469번 K번째 숫자

쉬운 문제 같지만 Q함수 내에서 매번 정렬한다면 속도가 나오지 않을 것이다. 전체 배열을 한번 정렬할때 원래 index 값을 달아놓으면, 매 Q함수 내에서 해당 i, j 범위 내 K번째를 찾는 것으로 해결한다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Solution {

  static int[][] data;
  static int[][] temp;

  public static void main(String[] args) throws Exception {
    BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));

    StringTokenizer t = new StringTokenizer(sc.readLine());
    int n = Integer.parseInt(t.nextToken());
    int k = Integer.parseInt(t.nextToken());

    data = new int[n][2];
    temp = new int[n][2];

    t = new StringTokenizer(sc.readLine());
    for (int i = 1; i <= n; i++) {
      data[i - 1][0] = Integer.parseInt(t.nextToken());
      data[i - 1][1] = i;
    }

    mergeSort(0, n - 1);

    while (k > 0) {
      t = new StringTokenizer(sc.readLine());
      int x = Integer.parseInt(t.nextToken());
      int y = Integer.parseInt(t.nextToken());
      int z = Integer.parseInt(t.nextToken());
      System.out.println(q(x, y, z));
      k--;
    }
    sc.close();
  }

  private static int q(int i, int j, int k) {
    int c = 0;
    for (int x = 0; x < data.length; x++) {
      if (data[x][1] >= i && data[x][1] <= j) {
        c++;
        if (c == k) {
          return (data[x][0]);
        }
      }
    }
    return 0;
  }

  private static void mergeSort(int low, int high) {
    if (low < high) {
      int mid = low + (high - low) / 2;
      mergeSort(low, mid);
      mergeSort(mid + 1, high);
      merge(low, mid, high);
    }
  }

  private static void merge(int low, int mid, int high) {
    for (int i = low; i <= high; i++) {
      temp[i][0] = data[i][0];
      temp[i][1] = data[i][1];
    }

    int i = low;
    int j = mid + 1;
    int k = low;

    while (i <= mid && j <= high) {
      if (temp[i][0] < temp[j][0]) {
        data[k][0] = temp[i][0];
        data[k][1] = temp[i][1];
        i++;
      } else {
        data[k][0] = temp[j][0];
        data[k][1] = temp[j][1];
        j++;
      }
      k++;
    }

    while (i <= mid) {
      data[k][0] = temp[i][0];
      data[k][1] = temp[i][1];
      i++;
      k++;
    }
  }

}

[백준 알고리즘 풀이] 2216번 문자열과 점수

Sequence Alignment에서 잘 알려진 문제로 edit distance 계산하라는 문제다. 위키피디아에 잘 정리되있기 때문에 구체 설명은 하지 않는다. https://en.wikipedia.org/wiki/Edit_distance
import java.util.Scanner;

/*
 * 2216번 문자열과 점수
 */
public class Main {
 
  public static void main(String[] args) throws Exception {
    Scanner sc = new Scanner(System.in);
 
    int match = sc.nextInt() * -1;
    int gap = sc.nextInt() * -1;
    int mismatch = sc.nextInt() * -1;
    sc.nextLine();
 
    String a = sc.nextLine();
    String b = sc.nextLine();
 
    int[][] opt = new int[a.length() + 1][b.length() + 1];
 
    for (int i = 1; i <= a.length(); i++)
      opt[i][0] = opt[i - 1][0] + gap;
    for (int j = 1; j <= b.length(); j++)
      opt[0][j] = opt[0][j - 1] + gap;
 
    for (int i = 1; i <= a.length(); i++) {
      for (int j = 1; j <= b.length(); j++) {
        int diag;
        if (a.charAt(i - 1) == b.charAt(j - 1)) {
          diag = opt[i - 1][j - 1] + match;
        } else {
          diag = opt[i - 1][j - 1] + mismatch;
        }
 
        opt[i][j] = Math.min(
            Math.min(opt[i - 1][j] + gap, opt[i][j - 1] + +gap), diag);
      }
    }
 
    System.out.println(opt[a.length()][b.length()] * -1);
    sc.close();
  }
 
}

[백준 알고리즘 풀이] 2579번 계단 오르기

전형적인 동적 계획법으로 recursive와 memoization 기법을 활용했다. 한 계단 또는 두 계단이기 때문에 n - 1, n - 2 모든 경우의 트리에서 최대값을 출력하도록 구현한다.
import java.util.Scanner;

/*
 * 2579번 계단 오르기
 */
public class Main {
 
  static int[] x;
  static int[] memo;
 
  public static void main(String[] args) throws Exception {
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
 
    x = new int[n + 1];
    memo = new int[n + 1];
    for (int i = 1; i <= n; i++) {
      x[i] = sc.nextInt();
    }
 
    System.out.println(f(n, 2));
    sc.close();
  }
 
  private static int f(int n, int t) {
    if (n == 0)
      return x[0];
    if (n == 1) {
      if (t == 1) {
        return x[1];
      } else {
        return x[1] + x[0];
      }
    }
    if (t == 1) {
      if(memo[n] > 0)
        return memo[n];
          
      return memo[n] = x[n] + f(n - 2, 2);
    } else {
      return x[n] + Math.max(f(n - 1, 1), f(n - 2, 2));
    }
  }
  
}

[백준 알고리즘 풀이] 9020번 골드바흐의 추측

문제를 해결하는 방법은 많지만, 간단하게 두 소수의 합으로 표현가능한 짝수를 모조리 구해버린 다음 메모리에서 꺼내면 깔끔하게 클리어된다.
import java.util.Scanner;

/*
 * 9020번 골드바흐의 추측
 */
public class Main {
 
  public static void main(String[] args) throws Exception {
    boolean[] primes = new boolean[10002];
    primes[0] = true;
    primes[1] = true;
    for (int i = 2; i < primes.length; i++) {
      if (!primes[i]) {
        for (int j = 2; j < primes.length; j++) {
          if (i * j >= primes.length)
            break;
 
          primes[i * j] = true;
        }
      }
    }
 
    String[] memo = new String[10001];
    for (int i = 0; i < 10001; i++) {
      if (!primes[i]) {
        for (int j = i; j < 10001 - i; j++) {
          if (!primes[j] && (i + j) % 2 == 0) {
            if (i + j < 10001) {
              memo[i + j] = i + " " + j;
            }
          }
        }
      }
    }
 
    Scanner sc = new Scanner(System.in);
    int t = Integer.parseInt(sc.nextLine());
    while (t > 0) {
      System.out.println(memo[Integer.parseInt(sc.nextLine())]);
      t--;
    }
    sc.close();
  }
 
}

[백준 알고리즘 풀이] 9084번 동전

동전문제는 보통 완전 탐색으로는 속도가 나오지 않기 때문에 동적 계획법을 사용해야하는데, 총액과 동전을 2d 행렬로 놓고, 점진적으로 100원을 만들수 있는 경우의 수를 증가하면서 얻어가는 점화식을 도출하는게 핵심이다. 아래는 iterative 방식인데 recursive와 memoization으로도 가능하다.
import java.util.Scanner;

/*
 * 9084번 동전
 */
public class Main {
  static int[] a;
  static int[] memo;
 
  public static void main(String[] args) throws Exception {
    Scanner sc = new Scanner(System.in);
    int tests = sc.nextInt();
    while (tests > 0) {
      int n = sc.nextInt();
      int[] m = new int[n];
      for (int i = 0; i < n; i++)
        m[i] = sc.nextInt();
 
      int total = sc.nextInt();
      int[][] t = new int[n + 1][total + 1];
      for (int i = 0; i <= m.length; i++) {
        t[i][0] = 1;
      }
 
      for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= total; j++) {
          if (m[i - 1] > j) {
            t[i][j] = t[i - 1][j];
          } else {
            t[i][j] = t[i - 1][j] + t[i][j - m[i - 1]];
          }
        }
      }
 
      System.out.println(t[n][total]);
      tests--;
    }
    sc.close();
  }
}

[백준 알고리즘 풀이] 9095번 1, 2, 3 더하기

이 문제는 정수론에 integer partition 이라는건데, 추가된 조건은 1, 2, 3으로만 분할하라는 얘기다. 간단하게 iterative 또는 recursive로 완전탐색하는 것으로 구현 가능하다.
import java.util.Scanner;

/*
 * 9095번 1, 2, 3 더하기
 */
public class Main {
 
  public static void main(String[] args) throws Exception {
    Scanner sc = new Scanner(System.in);
 
    int tests = sc.nextInt();
 
    for (int i = 0; i < tests; i++) {
      int n = sc.nextInt();
      System.out.println(partition(n));
    }
    sc.close();
  }
 
  private static int partition(int n) {
    if (n == 0) {
      return 1;
    } else if (n < 0) {
      return 0;
    } else {
      int sum = 0;
      for (int i = 3; i > 0; --i) {
        sum += partition(n - i);
      }
      return sum;
    }
  }
}

[백준 알고리즘 풀이] 9935번 문자열 폭발

기본 아이디어는 문자열을 쌓다가 대상 비교 문자열 꼬다리 캐릭터와 현재의 캐릭터가 일치할 경우 뒤에서 부터 검사한 (backwarding) 후 제거하는거다. 애니팡 연속으로 터지는걸 생각하면 쉽다. 코드에서는 Stack을 하나 구현했는데 poll 대신에 그냥 index position만 변경해주는걸로 구현했다.

import java.io.BufferedReader;
import java.io.InputStreamReader;
 
/*
 * 9935번 문자열 폭발
 */
public class Main {
 
  public static void main(String[] args) throws Exception {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String input = br.readLine();
    String bomb = br.readLine();   
 
    Stack s = new Stack();
    for (int i = 0; i < input.length(); i++) {
      s.push(input.charAt(i));
 
      if (input.charAt(i) == bomb.charAt(bomb.length() - 1)) {
        s.checkAndRemove(bomb);
      }
 
    }
 
    s.printCurrent();
    br.close();
  }
 
  public static class Stack {
    private int top = 0;
    private char[] stack = new char[1000003];
 
    public void push(char x) {
      stack[top++] = x;
    }
 
    public boolean checkAndRemove(String bomb) {
      if (top < bomb.length())
        return false;
 
      int c = 0;
      boolean isTrue = true;
      for (int i = bomb.length() - 1; i >= 0; i--) {
        if (bomb.charAt(i) != stack[top - 1 - c]) {
          isTrue = false;
        }
        c++;
      }
 
      if (isTrue) {
        top = top - bomb.length();
        return true;
      } else {
        return false;
      }
    }
 
    public void printCurrent() {
      if(top == 0)
        System.out.println("FRULA");
      else {
        StringBuilder out = new StringBuilder();
        for (int i = 0; i < top; i++) {
          out.append(stack[i]);
        }
        System.out.println(out.toString());
      }
    }
  }
 
}

Maven is unable to connect to repository.apache.org with "peer not authenticated"

Symptom: Maven is unable to connect to repository.apache.org with "peer not authenticated".
    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.5:deploy (default-deploy) on project hama-parent: Failed to retrieve remote metadata org.apache.hama:hama-parent:0.7.1-SNAPSHOT/maven-metadata.xml: Could not transfer metadata org.apache.hama:hama-parent:0.7.1-SNAPSHOT/maven-metadata.xml from/to apache.snapshots.https (https://repository.apache.org/content/repositories/snapshots): peer not authenticated -> [Help 1]
    [ERROR] 
This is usually caused by using a SSL certificate on Nexus. you can use the keytool command like below:
 $ sudo keytool -importcert -file ~/Downloads/apache.crt -alias apache.org -keystore /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts

인공지능, 중산층과 빈곤층의 종말이다

 한 때는, 구글의 기술을 카피 하던 아파치 하둡같은 것이 우리에게 굉장히 cutting-edge 최첨단 선진 기술로 느껴질 때가 있었다. 검색창 달랑 하나 있는 허접한 UI 뒤에 숨어 눈에 보이지 않기 때문에 일어난 현상이라고 생각한다. 과연 기술만 그랬을까? 화려한 UI로 장식한 유사 서비스는 경쟁의 가능성을 보여주는 듯 했다. 지금은 이제 카피조차 여러운 그들과의 기술 간극을 경험하고 있을 것이다.

 내 뇌리에 강렬하게 남아있는 네이버 이해진 CSO (아마도 '08년도 신년사) 한마디가 있는데, 대략 요약하면 구글 맵스/어스를 대중에 공개 하기 전 각국 인터넷 회사 대표를 초대하여 보여줬고 본인은 그곳에서 큰 충격을 받았다는 얘기다. 당시 그분은 격앙된 목소리로 임직원에 긴장할 것을 요구했으나 당시 공감한 자는 많지 않았을 것으로 본다.

 요즘 왕성히 활동하는 구글의 딥 마인드는 '13년으로 거슬러 올라간다. 인수를 위해 래리 페이지가 직접 행차 하였는데 과연 당시 그가 본 것은 무엇 이었을까? TED 2014에서 그가 말한 것처럼 아마 미래를 보았을거다. 현재는 거의 머신러닝에 몰빵하는 분위기인데, 내가 짐작컨데 우리가 보고 있는 것은 빙산에 일각이라는거다.

 아내에게 컴퓨터가 그림 그리는 법을 배우고 프로 바둑 기사를 상대하려고 하는 것에 대해 얘기해주었는데, 아내는 크게 놀라지 않고 의외로 순순히 받아들였다. 아마도 과거 포토샵 필터와 체스 게임 수준으로 생각하는 것 같은데 그림이나 작곡, 바둑 같은 분야에서 인간이 십수년에 거쳐 학습한 결과의 수준을 몇 시간만에 학습한다는 사실. 나의 설레임은 바로 여기서 온다. 그 수준이 아마추어가 아니라 프로 세계의 최고이기 때문에.

 한편, 빅 데이터 오픈소스 분야에서 개발한지 10년이 다 되어가고 기업 인공지능 팀에 속한 내가 한가지 확실히 말할 수 있는건, 이미 기술의 갭은 너무도 크다는거다. NIPS에 올라온 페이퍼를 보며 나 또한 알량한 엔니지어링 스킬 하나 믿고 범접할 분야가 아님을 뼈저리게 느꼈고, 도무지 돈과 인력 투입을 급조하여 밀어 부칠 수 있을 것 같지도 않았다.

 나의 두려움은 여기서 오는데 이러한 기술의 간극으로 예상되는 것은 미처 기술을 확보 못한 기업 및 개인의 도태다. 특이점이니 뭐니 2045년을 지목하는거 같은데 내 생각엔 좀 더 빠를 것으로 본다.

 우리 눈에는 그 변화가 서서히 오겠지만 1~20년 이내 급변할 것으로 나는 생각하고, 그 결과를 (좀 과하지만) 상상 해보면 인공지능과 로봇이 인간을 대체 하고 노동 인구 감소로 인한 문명의 퇴행을 피하면서 엘리트 소수 그룹만 쾌적한 지구 위에 유토피아를 건설한다는거다. 다른 말로 하자면, 중산층과 빈곤층의 종말이다. 망상이 과하다거나 당장 죽기야 하겠나 묻는다면, 난 지금 이시각 아프리카에서 죽어가는 아이들을 누가 신경 쓰던가 묻겠다.