전체 글 (312)

반응형

Hadoop MapReduce 파이썬 구현 (단어 빈도수 세기)

 

Map-Reduce 란?

 

본 포스팅에서는 하둡이 분산처리를 하는 방법인 맵리듀스를 파이썬으로 구현하는 방법을 간단하게 다루겠습니다. 하둡은 자바로 코딩되어 있지만 파이썬, C++ 등의 언어를 이용해서 하둡을 이용할 수 있습니다. 가장 간단하게는 파이썬 코드를 Jython 을 이용해 자바의 jar 파일로 만드는 것인데, 이것은 매우 불편합니다. 특히, Jython 에서 제공하지 않는 파이썬의 기능, 패키지를 이용할 때 문제가 됩니다. 그래서 하둡에서는 standard input, standard output 을 인터페이스로해서 파이썬과 같은 다른 언어에서도 하둡을 이용하는 방법을 제공하고 있습니다. 이번 포스팅에서는 파이썬으로 맵리듀스 코드를 구현하고 테스트 해보는 것을 정리하였습니다. 맵리듀스의 프로세스는 아래와 같습니다. 

 

출처 -  http://www.admin-magazine.com/HPC/Articles/MapReduce-and-Hadoop

 

맵 리듀스는 기본적으로 Split -> Map -> Shuffle -> Reduce 의 절차를 갖습니다. 여기서 Split-Map을 합쳐서 맵 태스크, Shuffle-Reduce 를 합쳐서 리듀스 태스크라고도 부릅니다. Split 은 인풋데이터를 쪼개서 인풋을 키-쌍 값으로 만들어주는 작업이고, Map 은 키-쌍 값을 인풋으로 받아 list(키-값 쌍)을 내보냅니다. Shuffle 에서는 list(키-값 쌍) 을 인풋으로 받아 키-값 쌍을 내보내고 Reduce 에서는 Shuffle 의 결과인 키-값 쌍 입력으로 받아 마지막으로 list(키-값 쌍) 을 내보냅니다. 

 

Map-Reduce 의 인풋/아웃풋

 


 Input Output 
 Split  텍스트  <k1, v1>
 Map  <k1, v1>  list (<k2, v2>)
 Shuffle  list (<k2, v2>)  <k2, list(v2)>
 Reduce  <k2, list(v2)>  list (<k3, v3>)

 

단어 빈도수 세기 예제

 

맵 리듀스는 왜 필요할까요? 예를 들어, 100개의 문서가 있을 때, 이 문서들에서 단어의 빈도수를 세는 프로그램을 작성한다고 해봅시다. 그런데 문서의 크기가 각각 1TB 라서 500GB 램을 갖는 하나의 컴퓨터에서 실행할 수 없다고 해봅시다. 만약, 분산 컴퓨팅에 대한 학습이 안 되어있는 프로그래머라면, 우선 메모리가 충분하지 않으므로 문서의 일부만 불러와서 단어수를 세고, 결과를 어딘가에 저장하고, 메모리에서 지우는 것을 반복하는 것을 생각해볼 수 있을 것입니다. 물론 이것도 좋은 해법이겠지만, 단점은 시간이 오래걸린다는 것입니다. 만약 충분한 수의 컴퓨터가 있다면 문서의 총 크기는 100TB 이므로, 예를 들어, 각각 500GB 의 메모리를 갖는 200개의 컴퓨터를 활용해서 문제를 해결하고자 하는 것이 분산 컴퓨팅이고 분산컴퓨팅에 사용되는 유명한 방법이 바로 맵리듀스라고 할 수 있습니다.

 

단어 빈도수 세기 예제에서의 자료 구조 흐름

 

인풋데이터

We hold these truths to be self-evident. Governments long established should not. Such has been the patient. .....

 

Splitting 결과 (<k1, v1>)

(0, We hold these truths to be self-evident, ..)

(138, Governments long established should not ...)

(256, Such has been the patient ...) 

 

이렇게 나눠진 문서는 각 노드에 정해진 양만큼 할당이 됩니다. 맵 태스크가 하는 일은 이 키-값 쌍을 인풋으로 받아 다음과 같은 리스트를 만들어주는 것입니다. 총 100개의 문장이라면 다음과 같은 100개의 리스트가 생깁니다. 

 

Map 결과 (list (<k2, v2>))

('We' : 1, 'hold' : 1 ...) 

('Government' : 1, 'long' : 1 ...)

('Such' : 1, 'has', 1 ...)

 

맵 태스크의 결과는 문장별 단어의 빈도수를 갖고 있는 리스트입니다 (여러번 단어가 나오더라도 각각 1을 같습니다.). 이를 키-값 쌍으로 변화하기 위해 셔플링 (Shuffling) 을 수행합니다. 셔플링은 각 단어별로 문장 내에서 찾은 빈도수의 리스트를 갖고 있습니다. 

 

Shuffle 결과 (<k2, list(v2)>)

('We' : [1,1,1])

('Government : [1,1,1])

('Such' : [1,1,1])

 

리듀스 태스크는 셔플링의 결과 키-값 쌍을 입력으로 받아 최종 결과를 출력합니다. 

 

Reduce 결과 (list (<k3, v3>))

('We' : 100)

('Government' : 10)

 

파이썬 Mapper 

 

Map Task 는 인풋 데이터를 적절히 쪼갠 후, 여러 개의 키-값 쌍 (key-value pair) 으로 만드는 과정입니다. 단어 수를 세는 문제에서는 먼저 텍스트를 라인 단위로 나눈 후 (스플릿), 각 라인별로 단어를 쪼개서 출력합니다. Map task 를 실습해보기 위해 hadoop.txt 라는 샘플 텍스트 파일을 만든 후 아래와 같이 실행하였습니다. 

#!/usr/bin/env python
"""mapper.py"""

import sys

# input comes from STDIN (standard input)
for line in sys.stdin:
    # remove leading and trailing whitespace
    line = line.strip()
    # split the line into words
    words = line.split()
    # increase counters
    for word in words:
        # write the results to STDOUT (standard output);
        # what we output here will be the input for the
        # Reduce step, i.e. the input for reducer.py
        #
        # tab-delimited; the trivial word count is 1
        print ('%s\t%s' % (word, 1))

hadoop.txt
0.00MB

cat hadoop.txt | python mapper.py

MapReduce       1
framework.      1
The     1
idea    1
is      1
based   1
on      1
the     1

 

위와 같이 (단어, 1) 의 키-값 쌍이 아웃풋으로 나오게 됩니다. 이제 이 아웃풋을 리듀서에 전달하면 됩니다. 참고로 이 Mapper 가 실제 hadoop 에서 실행될 때, 아웃풋이 셔플 단계에 의해 sorting 되고 적절한 수의 노드에 나누어서 전달 됩니다. 나누어서 전달할 때도 랜덤하게 나누는 것이 아니라 sorting 된 채로 나누어지기 때문에 효율을 최대화할 수 있는 방법으로 데이터를 전달합니다. 하지만 본 포스팅에서 이 부분은 다루지 않습니다. 이 부분은 하둡에 의해 제어되며, 프로그래머는 코드를 통해 맵리듀스의 각 프로세스의 데이터 구조 프로토콜에 맞게 아웃풋을 내주기만 하면됩니다.  

 

파이썬 Reducer 작성

 

Reduce task 는 map task 의 output 을 input 으로 받아 원하는 결과를 집계해주는 작업입니다. 실제로 hadoop 에서는 reduce task 는 shuffle 과 reduce 로 나뉩니다. reduce 작업에 사용되는 노드는 하나일 수도 있지만, 여러개의 노드를 사용하기도 합니다. 이러한 작업이 복잡해보이지만 분산해서 처리하는 일은 hadoop 에서 제어하는 부분에 속합니다. 즉, 코드를 작성하는 사람은 코드의 로직에만 집중하면됩니다. 

#!/usr/bin/env python
"""reducer.py"""

from operator import itemgetter
import sys

current_word = None
current_count = 0
word = None

# input comes from STDIN
for line in sys.stdin:
    # remove leading and trailing whitespace
    line = line.strip()

    # parse the input we got from mapper.py
    word, count = line.split('\t', 1)

    # convert count (currently a string) to int
    try:
        count = int(count)
    except ValueError:
        # count was not a number, so silently
        # ignore/discard this line
        continue

    # this IF-switch only works because Hadoop sorts map output
    # by key (here: word) before it is passed to the reducer
    if current_word == word:
        current_count += count
    else:
        if current_word:
            # write result to STDOUT
            print ('%s\t%s' % (current_word, current_count))
        current_count = count
        current_word = word

# do not forget to output the last word if needed!
if current_word == word:
    print ('%s\t%s' % (current_word, current_count))

 

우선 코드가 잘 작동하는지 알아보기 위해 mapper 의 아웃풋을 '키' 인 단어를 기준으로 아래와 같이 정렬합니다.  

cat hadoop.txt | python mapper.py | sort -k 1

(Figure 1
(typically      1
1).     1
Adobe,  1
Amazon  1
Amazon, 1
An      1
An      1
Apache  1
Automatic       1
Because 1
Elastic 1
Google  1
Google  1
Hadoop  1

 

다음으로 위 명령어의 아웃풋을 piping 을 통해 reducer 에 전달한 후, '값' 인 빈도수를 기준으로 내림차순 정렬하면 최종 결과를 얻게 됩니다. 

cat hadoop.txt | python mapper.py | sort -k1,1 | python reducer.py | sort -k 2 -r

to      8
MapReduce       8
is      7
that    6
in      6
and     6
a       6
on      5
reduce  4
can     4
be      4
are     4
map     3
by      3
The     3
which   2

 

본 포스팅에서는 파이썬을 통해 맵리듀스 코드를 작성하는 방법을 포스팅하였습니다. 하지만 이는 분산 컴퓨팅은 아닙니다. 다음 포스팅에서는 이러한 파이썬 맵리듀스 코드를 하둡으로 실행해서 분산 처리하는 방법 대해 다루어보겠습니다. 

 

References

반응형
반응형

Wallpaper 콜렉션 사이트


마이크로소프트의 좋은 배경화면들을 무료로 받을 수 있는 사이트를 알게되어 공유합니다 (https://wallpaperhub.app/). 이 사이트가 좋은점은 다양한 기기에 맞는 해상도로 사진들을 다운로드를 받을 수 있습니다. 콜렉션 페이지에 가면 Microsoft 의 서피스 랩탑 등 다양한 기기의 디폴트 배경화면 콜렉션을 볼 수 있습니다. 서피스 Wallpaper 에 좋은 사진들이 많은듯합니다. 



반응형
반응형


Scikit-learn Gradient Boosting 모델 예측값이 매번 달라지는 문제와 해결


아래 코드는 k-fold cross-validation 을 통해 best parameter 를 찾은 후, test set 에 대해 예측하는 코드이다. 이 코드에서 random_state=2020 을 지정하지 않으면, GridSearchCV 를 통해 구한 best parameters set이 매번 달라졌다. 즉 이말은 Gradient Boosting Tree 가 fitting 할 때마다 달라진다는 뜻이다. 


def cv_and_prediction(x_vars, name, model=False): train_x = metadata_valid[x_vars] train_y = metadata_valid["case"] test_x = metadata_test[x_vars] test_y = metadata_test["case"] model = GradientBoostingClassifier(random_state=2020) param_test = { "n_estimators": range(50, 100, 25), "max_depth": [1, 2, 4], "learning_rate": [0.0001, 0.001, 0.01, 0.1], "subsample": [0.7, 0.9], "max_features": list(range(1, len(x_vars), 2)), } gsearch = GridSearchCV( estimator=model, param_grid=param_test, scoring="roc_auc", n_jobs=4, iid=False, cv=5, ) gsearch.fit(train_x, train_y) print(name) print("Best CV Score", gsearch.best_score_) print("Best Params", gsearch.best_params_) model = GradientBoostingClassifier(**gsearch.best_params_) model.fit(train_x, train_y) metadata_test[name] = model.predict_proba(test_x)[:, 1] return model


왜 이런문제가 발생하는가 ?


Decision trees can be unstable because small variations in the data might result in a completely different tree being generated. This problem is mitigated by using decision trees within an ensemble.


The problem of learning an optimal decision tree is known to be NP-complete under several aspects of optimality and even for simple concepts. Consequently, practical decision-tree learning algorithms are based on heuristic algorithms such as the greedy algorithm where locally optimal decisions are made at each node. Such algorithms cannot guarantee to return the globally optimal decision tree. This can be mitigated by training multiple trees in an ensemble learner, where the features and samples are randomly sampled with replacement.


구글링을 통해 문제가 발생할 수 있을만한 원인을 찾았다. 


1. 우선 Decision tree 를 적합하는 방법은 heuristic algorithm (greedy algorithm) 이다. 왜냐하면 optimal 한 decision tree 를 찾는 것은 np-complete 문제이기 때문이다. 하지만 직접적인 문제는 아닌듯하다. greedy 한 방법으로 tree 를 만들더라도 매번 같은 greedy 한 방법을 이용하면 같은 tree 가 생성되기 때문이다.


2. Gradient Boosting 과 같은 ensemble 방법에서는 매 iteration 마다 subsample 을 만들어서 negative gradient 를 예측하는 모델을 만들어서 합치게 되는데 이 때, subsample 이 random 하게 설정되기 때문에 매 번 모형이 달라지고 이로 인해 예측 값이 조금씩 달라지게 된다.  


정확한 원인이 무엇이든 Gradient boosting tree 를 만드는 것에는 randomness가 있으며 reproducible 한 코드를 작성하고 싶으면 GradientBoostingClassifier(random_state=2020) 와 같이 random_state 를 지정해야한다. 


참고

https://scikit-learn.org/stable/modules/tree.html#tree

반응형
반응형

구글 번역기는 하나의 긴 문장을 두 개의 문장으로 쪼개주기도 한다.

 

임상 정보 기반 위험도 평가부는 임상 정보와 유전체 정보에 따른 위험도를 산출하는 모듈로, 유전체/가족력 정보를 통해 대상자를 몇 가지 표현형 (phenotype) 으로 나눈 후, 각 표현형의 나이에 따른 유방암 발생 확률에 임상 정보를 통해 추정한 상대 위험도 (relative risk)를 곱하는 방법을 통해 위험도를 산출한다. 

 

The clinical information-based risk assessment unit is a module that calculates the risk according to clinical information and genomic information. After dividing the subject into several phenotypes through genome / family history information, the risk of breast cancer according to the age of each phenotype and clinical It is calculated by the squared relative risk estimated from the information.

 

빠르게 영문 번역해야할 일이 있어 번역기의 도움을 받았는데 정말 좋은 것 같다. RNN seq to seq 의 개념에서 마침표를 하나의 단어로 인식한다면 기술적으로 불가능한것은 아니겠지만 어떻게 트레이닝을 했을지 정말 놀랍다. 

반응형
반응형


[딥러닝 강의] [1] 데이터 기반 이미지 분류 (Data-driven Image Classification)

이미지 분류 개요
이미지 분류는 컴퓨터 비전 분야의 주요 문제이다. 사람은 고양이를 보고 고양이라고 쉽게 분류할 수 있지만, 컴퓨터에게는 매우 어려운 문제이다. 사람은 고양이를 실체가 있는 물체로 인식하는 반면 컴퓨터에게는 0~255 사이의 숫자로 표현되기 때문이다. 이미지 분류의 문제는 이러한 사람과 컴퓨터의 사물을 보는 차이에서 온다. 예를 들어, 고양이를 관측한 위치 (viewpoint variation), 빛 (Illumination), 고양이의 자세 (deformation), 가림 (Occlusion), 고양이의 종류 (Intraclass variation) 등에 따라 컴퓨터에게 고양이는 상당히 다르게 인식된다. 단지 고양이 뿐만이 아니다. 여러 물체를 인식하고 이를 분류하는 문제는 매우 challenging 하다고 할 수 있다. 

그림. 사람과 컴퓨터의 이미지 인식 차이


딥러닝 이전 시대에 이미지를 분류하기 위하여 다양한 시도들이 있어왔다. 대표적으로 사진으로부터 고양이의 귀, 고양이의 눈, 고양이의 코가 '존재한다는 정보' 를 얻는 알고리즘을 만들고 이를 통해 고양이임을 판단하고자 한 방법이 시도되었다. 이를 규칙 기반 방법 (Rule-based approach) 하지만 이러한 방법의 문제는 1) 불안정하다는 것 (고양이의 귀, 코는 종에 따라 다르고, 자세와 위치에 따라 다르게 인식된다.) 과 2) 규칙을 만드는 것이 어렵다는 것이다. 예를 들어, 말해 고양이가 아니라 사진으로부터 개나 트럭을 분류해내고 싶으면 어떨까? 클래스마다 사람이 새로운 규칙을 만들어주어야한다. 즉 이러한 방법은 확장성 (scalability) 이 떨어진다. 

데이터 기반 방법
이러한 문제를 해결하기 위해 제시된 것이 데이터 기반 방법 (Data-driven approach) 이다. 데이터 기반 방법은 사람이 직접 알고리즘을 만드는 것이 아니라 데이터를 기반으로 모델을 만들어 문제를 해결하고자 하는 방법이다. 결론부터 말하면 이미지 분석에 있어 데이터 기반 방법은 규칙 기반 방법보다 거의 모든 측면에서 효과적이다. 또한 사람이 학습하는 방법과도 비슷하다. 

그림. Rule-based approach vs. Data-driven approach


데이터 기반 방법은 수많은 이미지와 레이블 (ex. 개, 고양이 등) 이 있는 데이터셋을 통해 모델을 학습 (training) 한다. 이 때, 모델을 학습하는 것을 머신 러닝 (Machine Learning) 이라고 말한다. 이렇게 학습된 머신러닝 모델은 새로운 이미지를 인풋으로 받아 그 이미지의 레이블을 예측 (prediction or test) 한다. 즉, 이미지를 분류하는 모델을 만드는 방법 중 데이터 기반 방법을 머신러닝이라고 하며, 머신러닝 모델은 데이터셋을 통해 이미지를 학습하는 과정과 새로운 이미지를 예측하는 과정이 있다. 머신러닝 모델의 학습을 위한 데이터셋은 직접 웹 등으로부터 수집할 수 있다. 또한 ImageNet, CIFAR 등 공개된 질 좋은 데이터셋도 있기 때문에 누구나 직접 머신러닝 모델을 구축해볼 수 있다. 예를 들어, CIFAR-10 데이터셋은 10개의 클래스, 50000개의 트레이닝셋, 10000개의 테스트셋으로 구성된다.  


그림. CIFAR-10 데이터셋


이미지 분류를 위한 첫 번째 알고리즘 : Nearest neighbor

데이터 기반 모델의 첫 번째 간단한 예시로 nearest neighbor (NN) 방법을 살펴보자. KNN 방법은 먼저 학습 데이터셋을 저장한 후에, 예측 단계에서는 투입된 이미지와 가장 가까운 데이터의 레이블을 통해 예측 하는 방법이다. 그러면 이미지와 이미지의 가까운 정도 (distance) 는 어떻게 구할 수 있을까? 다양한 지표 (metric) 이 있지만 그 중 한 가지인 L1 distance 는 다음과 같다.


$$ d_1 (I_1, I_2) = \sum_{p} | I_1^p - I_2^p | $$


L1 distance 를 distance metric 으로 사용하는 nearest neighbor 을 파이썬 코드로 아래처럼 구현할 수 있다. 


import numpy as np

class NearestNeighbor(object):
  def __init__(self):
    pass

  def train(self, X, y):
    """ X is N x D where each row is an example. Y is 1-dimension of size N """
    # the nearest neighbor classifier simply remembers all the training data
    self.Xtr = X
    self.ytr = y

  def predict(self, X):
    """ X is N x D where each row is an example we wish to predict label for """
    num_test = X.shape[0]
    # lets make sure that the output type matches the input type
    Ypred = np.zeros(num_test, dtype = self.ytr.dtype)

    # loop over all test rows
    for i in xrange(num_test):
      # find the nearest training image to the i'th test image
      # using the L1 distance (sum of absolute value differences)
      distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
      min_index = np.argmin(distances) # get the index with smallest distance
      Ypred[i] = self.ytr[min_index] # predict the label of the nearest example

    return Ypred


위 코드를 사용해서 테스트셋에 대한 성능을 평가한 결과 38.6 % 정도의 성능을 보인다. 우선, 임의로 찍은 수치 (10 %) 보다 성능이 좋기 때문에 모델을 만드는데 큰 노력을 들이지 않았다는 점을 감안해 괜찮은 수치이다. 하지만 state-of-the-art 인 Convolutional Neural Network 기반 모델의 성능 (95 %) 과 비교하면 매우 낮다. 


L1 외에도 다양한 metric 을 쓸 수 있다. 예를 들어, L2 distance 는 다음과 같다.


$$ d_2 (I_1, I_2) = \sqrt{\sum_{p} ( I_1^p - I_2^p )^2} $$



L1 L2 distance 의 차이

L1 과 L2 는 p-norm 계열의 distance measure 이다. 그러면 L1 과 L2  의 차이는 L2 distance 는 L1 distance 를 사용하는 것보다 차이가 큰 것에 더 관대하지 않다는 것이다. 즉, L1 이 아닌 L2 distance 를 쓴다는 것은 여러개의 dimension 에서 적당한 차이를 보이는 것보다 하나의 dimension 에서 큰 차이를 보이는것에 더 페널티를 많이준다는 의미다. 


K-nearest neighbor 

nearest neighbor (NN) 은 단점이 많은 알고리즘이다. 먼저, NN 은 단 하나의 label 만 prediction 에서 고려하기 때문에 안정성이 떨어지는 결과를 보여준다. 한 마디로 성능이 떨어진다. 이를 보완하기 위해 k-nearest neighbor (KNN) 를 활용할 수 있다. 이 방법은 테스트 (또는 예측) 단계에서 인풋과 가까운 순으로 총 k 개의 데이터의 레이블을 구한 후, 가장 빈번하게 나오는 레이블로 예측하는 방법이다. 이렇게 여러개로부터 가장 빈번하게 나오는 것을 예측 결과로 하는 것을 머신러닝에서는 voting 이라고 부른다. 


그림.  k-nearest neighbor


위 그림을 보면 NN 대신에 K-nearest neighbor 을 사용하면 어떤 장점이 있는지 확인할 수 있다. NN 에는 이상치 (outlier) 를 중심으로 섬과 같은 지역 (decision boundary) 가 생긴다는 것을 볼 수 있다. 하지만 KNN 의 경우, 이상치에 더 둔감하다. NN 의 경우 이상치에 민감하기 때문에 트레이닝 데이터에 국한된 규칙을 배울 가능성이 높다. KNN 을 사용하면 처음 보는 데이터 (unseen data) 대한 성능 (generalization)이 높을 것임을 짐작할 수 있다. 그리고 처음 보는 데이터를 올바르게 예측하는 것이 머신러닝 방법을 사용한 예측 모델링의 주요 목적이다. K-nearest neighbor 를 실제로 사용할 때  한가지 고려 사항은 k 를 어떻게 정하냐는 것이다. k 가 높으면 물론 좋겠지만 그만큼 테스트 단계에서의 계산량이 매우 많아진다는 단점이 있다. 


Validation set 을 통한 Hyperparameter tuning

K-nearest neighbor 에서 우리가 정해야할 것은 kdistance function 이다. 이를 알고리즘 자체의 파라미터, hyperparameter 라고 한다. 그리고 이를 조정하는 방법을 hyperparameter tuning 이라고 한다. 머신러닝에서 hyperparameter tuning 을 하는 주 방법은 여러번 해봐서 가장 좋은것을 찾는 것이다. 이것은 별다른 아이디가 없을 때 좋은 방법이다. 하지만 hyperparameter 를 찾는데 테스트셋을 사용할 수는 없다. 머신러닝에서 테스트셋은 성능을 평가하는 것 이외에 다른 목적으로 쓰일 수 없다. 만약 테스트셋으로 hyperparameter tuning을 하면 모델이 테스트셋에 과적합 (overfitting) 되며, 다른 처음보는 데이터에 적용했을 때 상당히 성능이 떨어지는 문제를 겪게 된다. 테스트셋은 반드시 모델 구축이 끝난 후, 단 한 번 평가 되어야 한다.  따라서 방법은 트레이닝 셋의 일부를  hyperparameter tuning 을 위하여 분할하여 사용하는 것이다. 이를 validation set 이라고 부른다. validation set 을 통한 hyperparameter tuning 은 머신러닝에서 중요한 개념 중 하나이다. Validation set 을 통한 Hyperparameter tuning 을 아래와 같은 파이썬 코드로 구현해볼 수 있다. 


# assume we have Xtr_rows, Ytr, Xte_rows, Yte as before
# recall Xtr_rows is 50,000 x 3072 matrix
Xval_rows = Xtr_rows[:1000, :] # take first 1000 for validation
Yval = Ytr[:1000]
Xtr_rows = Xtr_rows[1000:, :] # keep last 49,000 for train
Ytr = Ytr[1000:]

# find hyperparameters that work best on the validation set
validation_accuracies = []
for k in [1, 3, 5, 10, 20, 50, 100]:
  
  # use a particular value of k and evaluation on validation data
  nn = NearestNeighbor()
  nn.train(Xtr_rows, Ytr)
  # here we assume a modified NearestNeighbor class that can take a k as input
  Yval_predict = nn.predict(Xval_rows, k = k)
  acc = np.mean(Yval_predict == Yval)
  print 'accuracy: %f' % (acc,)

  # keep track of what works on the validation set
  validation_accuracies.append((k, acc))


Nearest neighbor  잘 작동할까?


nearest neighbor 은 매우 이해하기 쉬운 알고리즘인 반면 여러 단점이 존재한다. 


1) 현실적인 단점은 NN은 테스트 단계에서 시간이 오래걸린다는 것이다. 모든 n 개의 트레이닝 데이터에 대해서 distance 를 구해야하기 때문에 O(n) 의 복잡성을 갖는다. 따라서 현실의 어플리케이션에 적용하기는 매우 어렵다. (NN 의 테스트 단계에서의 계산 복잡성을 줄이기 위해 Approximate Nearest Neighbor (ANN) 도 연구가 활발히 이루어지고 있는 분야이다. ANN 을 구현한 FLANN 라는 것도 있다. 기본 아이디어는 kdtree 나 k-means 알고리즘 등을 통해 데이터 인덱싱을 먼저 수행하여 테스트 단계에서 조회할 데이터를 줄이는 것이다.) 


2) NN 우리의 물체의 유사성에 대한 인식 (perceptual similarity) 과 부합하지 않는 결과를 내놓을 수 있다. 아래 그림은 모두 original 데이터와 L2 distance 를 기준으로 같은 유사성을 보이도록 이미지를 변환한 것이다 예를 들어 우리가 original 과 shifted 이미지를 인식할 때, 피사체의 위치에 차이만 있을 뿐 물체에는 아무런 차이가 없다. 하지만 단지 물체가 옆으로 이동한 것만으로도 상당한 distance 가 생기게 된다는 것이다. 


그림. 원본 이미지와  같은 L2 distance 를 갖는 이미지 모음


Raw-pixel 을 이용한 모델은 왜 실패하는가?


왜 이런 문제가 생길까? 문제는 물체의 특성이 아닌 각 픽셀을 기준으로 차이를 비교한다는 것이다. 물론 픽셀이 이미지의 특성을 반영하지만, 픽셀 값의 많은 부분이 물체 인식과 크게 관련이 없다. 아래 그림은 CIFAR-10 데이터셋에 t-SNE 를 적용해 이미지를 2차원으로 표현한 것이다. 이렇게 고차원을 저차원으로 바꾸는 것을 dimension reduction 또는 embedding 이라고도 부른다.  고차원 데이터를 공간상에 표현하는 것이 힘들기 때문에 이와 같이 저차원으로 바꾸어 표현하고자 한 것이다. 이상적으로는 CIFAR의 10개의 클래스에 맞게 군집화 되어야한다. 하지만 그림을 자세히 살펴보면 물체에 인식과 관련있는 특성 뿐 아니라 배경, 또는 색깔 등에 큰 영향을 받는다는 것을 알 수 있다. 즉, 픽셀은 물체에 대한 인식 뿐 아니라 불필요한 것까지 표현한다. 따라서 raw-pixel 을 기준으로 nearest neighbor 을 적용 하는 것은 적절하지 않은 방법이다. 단지, nearest neighbor 뿐 아니라 raw-pixel 을 output 으로 direct mapping 하는 대부분의 머신러닝 알고리즘은 같은 이유로 이미지 데이터에 효과적으로 적용되기 어렵다. 어떻게 이미지를 '우리의 인식과 관련 있는 특성' 으로 요약할 수 있을지에 관한 것이 representation learning 이며, 이것은 딥러닝 (deep learning) 의 핵심 아이디어이다.  


그림. CIFAR-10 의 tSNE를 통한 2차원 임베딩


Summary

  • 데이터 기반 이미지 분류의 기본 개념 1) 이미지-레이블 데이터셋을 통해 모델 학습 2) 새로운 데이터에 대해 예측
  • Nearest Neighbor 을 통해 이미지 분류 하는법 + Validation set 을 통해 hyperparameter tuning 하는 방법  
  • training, validation, test set 의 개념. test set 은 모든 프로세스에서 반드시 한 번만 사용되어야 한다.
  • Raw-pixel 에 을 output 으로 direct mapping 해서 모델을 만드는 것이 실패하는 이유 (인식과 표현의 차이)

참고


반응형

'Soft skills > Lecture' 카테고리의 다른 글

[딥러닝강의] [2] GPU 와 딥러닝 소프트웨어  (2) 2020.02.07
반응형

하둡과 맵리듀스 스파크의 관계  

 

하둡 (Hadoop)

 

https://www.ibmbigdatahub.com/blog/what-hadoop

 

 

"하둡은 간단한 프로그래밍 인터페이스를 통해 클러스터 환경에서 대용량 데이터에 대한 분산 처리를 하는 소프트웨어 라이브러리다.

 

하둡의 정식명칭은 아파치 하둡 (Apache Hadoop) 이며 scalable, distributed computing 을 위한 소프트웨어 플랫폼입니다. 하둡은 오픈 소스 프로젝트이고 아파치 재단에 의해 관리됩니다. 하둡은 "데이터를 읽고 처리하는 속도가 데이터의 양을 따라잡지 못한다" 라는 문제에서 출발했습니다. 하드디스크 입출력 속도는 보통 100MB/s 정도입니다. 하지만 기업환경에서 마주하는 데이터는 최소 기가바이트 (GB) 에서 테라바이트(TB), 페타바이트 (PB) 까지 이르는 경우가 많습니다.  하둡의 필요성을 생각해보기 위해 예를 들어 설명해보겠습니다. 아래의 상황에서 하둡이 유용할 수 있습니다. 

 

1) 하드 디스크는 1TB짜리 인데, 3TB 데이터를 저장하고 싶을 때

2) 100 개의 파일에 대해 똑같은 작업을 해서 결과를 합치는 분산 컴퓨팅을 하고자 할 때 

3) TB 단위 이상의 데이터를 빠르게 Disk I/O (로드 또는 저장) 하고 싶을 때 

 

하둡의 두 가지 키워드는 scalable distributed computing 입니다. scalable 은 확장 가능하다는 뜻으로 자원을 추가하더라도 코드의 수정 등을 할 필요 없이 동일한 방법으로 프로세싱을 할 수 있다는 의미입니다. distributed computing 은 분산 컴퓨팅을 의미하여 Job 을 적절한 크기로 쪼갠 후, 분산된 자원원을 통해 계산하고 결과를 합치는 작업을 말합니다. 

 

 

맵 리듀스 (Map-Reduce)

 

 

A BIG DATA PROCESSING FRAMEWORK BASED ON MAPREDUCE WITH APPLICATION TO INTERNET OF THINGS (2016)

 

"맵 리듀스는 아파치 하둡 프로젝트의 서브 프로젝트로, 분산 컴퓨팅을 위한 프레임워크다. "

 

하둡의 맵리듀스는 distributed computing 을 위한 프레임워크입니다. 맵리듀스는 하둡의 서브 프로젝트라고 볼 수 있습니다. 맵리듀스는 Job 의 스케쥴링, 모니터링, 에러 핸들링 등을 수행합니다. 맵 리듀스는 인풋 데이터를 독립적인 청크로 나누고 이를 병렬적으로 처리합니다. 맵리듀스는 하둡 파일 시스템 HDFS (Hadoop Distributed File System) 으로부터 데이터를 불러오고 저장할 수 있습니다. 맵 리듀스의 이해를 위해 다음과 같은 예를 들어봅시다. 

 

ex) 100개의 문서가 있고, 그 문서에서 단어의 빈도수 예를 들면, 'dog', 'cat', 'bird', 'fish', ... 의 수를 세서 결과를 보여주는 프로그램을 작성하라. 100개 이상의 컴퓨터를 보유하고 있다. 문서의 크기가 다 다르고 메모리 문제로 한 컴퓨터에서는 평균 1개의 문서만 로드할 수 있다. 

 

간단하게 생각해보면 100개의 컴퓨터에 각각 1개의 문서를 할당해 단어 빈도수를 세고, 이를 합치면 되겠죠. 이것도 분산 컴퓨팅의 일종입니다. 하지만 이것은 그렇게 간단한 문제는 아닙니다. 예를 들어, 실행 도중 몇몇 컴퓨터에서 갑자기 오류가 날 수 있습니다. 또 각각의 컴퓨터에서 계산한 단어 빈도수를 한 컴퓨터에서 합산해서 보여주고자 할 때, 컴퓨팅 자원의 문제로 합산이 불가능할 수 있습니다. 또한 각 문서의 크기가 달라 한 컴퓨터당 하나의 문서를 할당하는 것이 비효율적일 수도 있습니다. 즉, 다양한 상황을 고려한 하나의 분산 컴퓨팅 프레임워크가 필요합니다. 

 

https://stackoverflow.com/questions/47531863/why-key-is-not-unique-in-mapreduce-function

 

 

맵 리듀스는 '분산 컴퓨팅을 실제로 어떻게 할 것인가?' 에 대한 잘 알려진 답변입니다. 맵 리듀스는 크게 리듀스로 나뉩니다. 맵에서 인풋데이터는 "키-값 쌍" 으로 변화되고, 스플릿 단위로로 나뉘어서 각 노드에 할당됩니다. 맵은 리듀스를 위한 키-값쌍을 만들어냅니다. 이 때, 키-값쌍은 사용자에 의해 정의될 수 있습니다. 리듀스 태크스는 맵의 아웃풋 키-값 쌍을 인풋으로 받아 최종 결과를 출력합니다. 예를 들어, 위 문제에서 인풋 데이터를 (문장의 오프셋, 문장 텍스트) 의 키-값 쌍으로 정의해볼 수 있습니다. 이렇게 인풋 데이터를 키-값 쌍으로 나누고 노드에 할당하는 것을 Splitting 이라고 합니다. Splitting 에서 인풋데이터는 입력 스플릿 (input split) 또는 단순히 스플릿이라고 부르는 고정된 크기의 조각으로 분리합니다. 문서 단위로 노드에 할당하는 것보다 스플릿 단위로 할당하는 것이 더욱 빠릅니다. 또한 한 컴퓨터에서 불가능했던 일을 가능하게 할 수 있습니다. 

 

 

스파크 (Spark)

 

https://dzone.com/articles/apache-spark-fast-big-data

 

"스파크는 하둡 위에서 동작할 수 있는 인메모리 프로세싱 엔진이다." 

 

위 맵 리듀스 절차는 통해 분산 컴퓨팅을 어떻게하는지 감을 잡으셨을 거라고 생각합니다. 하지만 실제로 이를 코드로 구현하는 것은 복잡합니다. 하둡 라이브러리의 객체를 상속 받아 메소드 오버라이딩을 하고 최종적으로 구현된 클래스를 모아 jar 형태로 만들어 배치형태로 하둡에 제출해야합니다. 즉, 하둡의 맵-리듀스는 ad-hoc 데이터 분석에 적합하지 않습니다. 따라서 맵-리듀스를 보다 유저 친화적으로 할 수 있는 다양한 방법이 제시되었습니다. 그 중 하나가 스파크입니다. 

 

스파크의 기본적인 특성

  • 스파크는 분산 인메모리 프로세싱 엔진입니다. 스파크는 반드시 하둡과 같이 사용될 필요는 없습니다. 하지만 스파크는 하둡을 지원합니다. 예를 들면 스파크는 하둡이 사용하는 파일 시스템인 HDFS (Hadoop Distributed File System) 의 데이터를 읽어올 수도 있고, 반대로 데이터를 쓸 수도 있습니다. 
  • 스파크는 HDFS 에 저장된 데이터를 하둡 코어 라이브러리를 호출함으로써 메모리로 불러온 후, 변환 및 계산 등을 거쳐 최종 원하는 결과물을 산출합니다. 스파크는 인메모리 프로세싱을 하기 때문에 Disk I/O 가 많이 일어나는 하둡의 맵-리듀스보다 특정 작업 (ex. multi-pass map reduce)에서는 더 빠르게 수행될 수 있습니다. 
  • 스파크는 여러가지의 분산 데이터베이스나 파일 시스템을 수용할 수 있습니다. 하둡 뿐만 아니라 카산드라를 사용을 할 수도 있습니다. 카산드라는 CQL (Cassandra Query Language) 이라는 고유의 SQL 문법이 있는데, 이는 SQL 의 서브셋으로 다양한 데이터 추출을 이용하기가 힘들다고 알려져 있습니다. 카산드라에 스파크를 결합해 추가적인 데이터 프로세싱을 사용할 수 있기 때문에 카산드라 자체만을 통해 못하는 일을 할 수 있습니다.
  • 스파크의 한 가지 장점은 stream processing 이라고 하는 실시간 데이터처리 입니다. 스파크는 실시간 데이터 처리를 위해 세팅이 될 수 있고, micro-batches 단위로 데이터 프로세싱을 수행할 수 있으며, 결과를 하둡 HDFS, 카산드라 같은 파일 시스템에 저장할 수 있습니다. 

 

스파크의 아키텍쳐

 

https://towardsdatascience.com/getting-started-with-apache-spark-ad9d59e71f6f

 

스파크의 주요 개념은 Resilient Distributed Datasets (RDD) 와 directed acyclic graph (DAG) execution engine 입니다. RDD 는 스파크에서 사용되는 데이터셋으로 각 단어는 다음과 같은 뜻이 있습니다. 

  • Resilient: 분산되어 있는 데이터에 에러가 생겨도 복구할 수 있는 능력
  • Distributed: 클러스터의 여러 노드에 데이터를 분산에서 저장 
  • Dataset: 분산된 데이터의 모음 

 

스파크에서 RDD 에 대해 수행할 수 있는 연산은 크게 1) transfomation 2) action 으로 나눌 수 있습니다. transformation 은 인풋데이터로부터 새로운 데이터셋을 만들어내는 것이고 (ex. map, filter), action 데이터셋으로부터 계산 (ex. reduce or count)을 하는 것입니다. 스파크에서는 DAG engine 을 통해 맵리듀스의 느린 부분 (multi-pass map reduce, multi-stage execution model)을 제거해 상당한 성능 향상도 가능합니다. 

 

RDD 를 통한 분석 워크 플로우는 다음과 같이 요약해볼 수 있습니다.

 

 

https://www.edureka.co/blog/spark-architecture/#Overview

 

Reference

 

반응형

'Soft skills > Knowledge' 카테고리의 다른 글

Data Science와 Data Engineering 은 무엇이 다를까?  (0) 2019.12.31
반응형

Data Science와 Data Engineering 은 무엇이 다를까?

 

Data Science

 

Data Science 는 이름에서도 알 수 있듯, Data + Science 이다. '데이터' 를 통해 '과학' 을 하는 것입니다. 사실 Data science란 용어는 매우 모호한 단어입니다. 전통적인 연구 (Traditional research) 에서도 데이터를 통해 과학을 하는데 이와 무엇이 다르냐고 할 수 있습니다. 또한 오래전부터 있어왔던 개념인 정보학 (Informatics) 과는 무엇이 다른 것일까요? 이러한 단어의 모호함 때문에 Data science 라는 이름만 보고는 무엇을 하는 건지 명확히 아는데 어려움을 겪습니다.

 

 

 

데이터과학은 구체적으로 '도메인 빅데이터'에 '통계' 와 'IT 기술'을 접목시켜 과학을 하는 것입니다. 이는 위그림에 잘 표현되어 있습니다. 데이터과학에는 3가지 구성 요소가 있습니다. 1) Computer science 2) Statistics 3) Domain knowledge 입니다. 과학이란 새로운 사실을 발견하는 것입니다. 사전적 정의에 따르면 과학은 "검증된 방법으로 얻어 낸 자연계에 관한 체계적 지식 체계" 이지만, 자연계이외에도 포괄적으로 빅데이터에 통계, IT 기술을 접목시켜 Business insight 도출, 의사 결정을 하는 것도 데이터과학이라고 부릅니다. 즉, 과학이라는 것은 데이터과학에서 폭넓은 의미로 쓰이며, '도메인 빅데이터'에 '통계' 와 'IT 기술'을 접목해 문제를 해결하는 것 정도로 데이터 과학을 요약할 수 있습니다. 

 

데이터 과학자 (Data scientist) 는 크게 두 가지 종류가 있다고 생각합니다. 첫 번째는 머신러닝 전문가로서, 여러 도메인의 문제 해결에 도움을 주는 데이터 과학자 입니다 (Machine learning engineer or scientist). 즉, 위 그림에서 Machine learning 에 중점을 두면서 여러 도메인의 문제를 푸는 사람입니다. 이 경우 머신러닝의 최신 동향 파악 및 최신 기술 (State-of-the-art) 의 활용에 강점이 있습니다. 두 번째는 특정 도메인에 국한해 빅데이터에 IT 와  통계 지식을 접목해 문제를 해결하는 사람 (Domain expert) 입니다. 위 그림에서 Domain knowledge 에 폭넓은 이해와 함께 필요한 IT 기술, 통계학적 지식을 접목해 문제를 해결하는 사람입니다. 이 경우 도메인 지식을 활용한 Feature 선정, 공학도메인에서 문제를 스스로 찾고 이를 공학적으로 formulation 하는 능력에 강점이 있습니다. 

 

Data Engineering

 

위 데이터 과학의 정의를 보면 데이터과학자들의 역할이 매우 광범위합니다. 데이터 공학 (Data engineering) 은 대규모의 데이터를 효율적으로 관리하고 처리하며 가공을 통해 분석에 적합한 형태 or 데이터과학자가 사용할 수 있는 형태로 만드는 것과 관련이 있습니다. 큰 회사의 경우 데이터의 크기가 매우 크고, 이를 저장하기 위하여 분산 시스템을 활용하며, Database management system, text file 에 데이터를 저장합니다. 이러한 데이터 저장 시스템에 데이터를 저장하고 필요할 때 추출해서 처리하기 위한 방법론을 데이터 공학이라고 이야기합니다. 데이터 과학자와 데이터 공학자는 카레이서와 카트를 만드는 사람으로 비유할 수 있습니다. 카레이서는 카트가 없다면 달릴 수 없듯, 빅데이터 분석에서 데이터공학자 없이 데이터 과학자가 존재하기 힘듭니다. 하지만 데이터 공학자는 실제 데이터과학 접목을 통해 Product 를 만들었을 때, 덜 주목 받을 수 있습니다. 왜냐하면 전체 Product 개발의 pipeline 을 봤을 때, 초기 단계에 있는 일을 하기 때문입니다. 데이터 공학은 빅데이터 처리 시스템이 갖추어지지 않은 작은 규모의 기관에서는 그 역할이 축소될 수 있고, 데이터과학자가 겸임을 하기도 합니다.  

 

https://www.elderresearch.com/consulting-services/data-engineering

 

위 그림은 대규묘 데이터 활용의 파이프라인에서 Data engineering 이 담당하는 역할을 잘보여줍니다. 데이터 공학자는 비지니스 데이터를 분산 시스템에 정형화된 형태로 저장 (위 그림의 Immutable data store) 하고 필요할 때, 이를 Extract, Load, Transform (ETL) 해서 분석에 적합한 형태 (위 그림의 Analytic base table) 로 만듭니다. 데이터 과학자는 이 데이터를 통해 데이터 과학을 합니다. 

 

데이터 과학과 데이터 공학의 공통점과 차이점은 아래와 같이 요약을 해볼 수 있습니다. 

 

공통점 데이터과학  데이터공학 
소프트웨어 기술 
빅데이터
통계학
머신러닝
도메인지식
빅데이터 Infrastructure 
분산처리 기술 (Hadoop, Spark)
분산시스템 데이터처리 Pipeline 
실시간 처리 시스템

 

 

반응형

'Soft skills > Knowledge' 카테고리의 다른 글

하둡과 맵리듀스 스파크의 관계  (0) 2020.01.19
반응형

One shot learning, Siamese Network 이해


Problem Definition 


이미지 인식 분야에서 각 label 의 이미지 수가 적을 때 이를 인식하고 분류하는 것은 challenging 합니다. 예를 들어 얼굴 인식 분야에서는 단 몇 장의 이미지만을 통해 동일인인지 여부를 구분해야하는 문제가 있습니다. 물론 augmentation 과 같은 방법으로 샘플 수를 늘려서 CNN 으로 multi-class classification 문제를 풀도록 학습시키는 방법이 가능합니다. 하지만 이 경우, 새로운 사람이 데이터 베이스에 추가되었을 때, 모델을 새로 학습해야한다는 문제가 생깁니다. 즉, 실시간 시스템에 적합하지 않은 방법입니다. 


이러한 문제를 해결하는 한 가지 방법은 "Distance function" 을 이용하는 것입니다. 


D(x_1, x_2) = degree of difference between images 


Distance function 을 이용해, 예를 들어, D(Image1, Image2) <= k 인 경우 같은 사람이라고 예측하고, > k 인 경우 다른 사람이라고 예측할 수 있습니다. 또는 DB 에 있는 모든 Image 에 대해사 distance 를 계산한 후에 가장 distance 가 작은 사람으로 분류할 수 있습니다. 이러한 아이디어의 의미는 face recognition 에서는 누구인지 구분하는 것보다 "동일인지 여부" 가 중요하다는 것입니다. 또한 이를 이용해 multiclass classification 을 할 수도 있습니다. 이는 아이디어 측면에서 머신러닝의 고전적인 방법인 nearest neighbor 방법과 비슷합니다. 본 포스팅은 Distance function 의 인풋으로 넣기 위한 representation 을 만드는 함수 f 를 딥러닝으로 학습하는 방법에 관한 것입니다. 이미지의 distance 를 계산하기 위한 representation learning 개념입니다. 


Siamese Network + Triple loss


Our approach is to build a trainable system that nonlinearly maps the raw images of faces to points in a low dimensional space so that the distance between these points is small if the images belong to the same person and large otherwise. Learning the similarity metric is realized by training a network that consists of two identical convolutional networks that share the same set of weights - a Siamese Architecture (Sumit Chopra, 2005, CVPR)

함수 f 를 딥러닝 방법을 통해 학습할 수 있는 한 가지 방법은 Siamese network 을 이용하는 것입니다. Siamese network 는 2005년에 CVPR 에 발표된 방법으로, weights 를 공유하는 두 개의 CNN 입니다. 이 CNN은 Image1과 Image2를 f(Image1) 과 f(Image2) 라는 vector representation 으로 변환시킵니다. 그리고 f 를 학습시키기 위해 loss function 을 정의하고, distance 를 나타낼 수 있는 representation 을 만드는 방향으로 weights가 학습됩니다. 이미지1과 이미지2의 distance 를 f(Image1)과 f(Image2)의 l2 norm 으로 정의해봅시다. 


$$ D(x_1, x_2) = ||f(x_1) - f(x_2) || ^2 $$


loss function 으로 사용할 수 있는 한 가지는 Triplet loss 입니다. Triplet loss 는 3개의 이미지로부터 loss function 을 만드는 방법이며, 아이디어는 같은 사람의 이미지의 distance 가 다른 사람의 이미지의 distance 보다 작아지도록 하자는 것입니다. 이는 기술적으로  D(Anchor, Positive)D(Anchor, Negative) 보다 작아지도록 잡자는 것입니다. 이 때, Anchor 와 Positive 는 같은 사람의 다른 이미지이고, Negative 는 Anchor, Positive 와 다른 사람입니다. 이 때, alpha 라는 margin 을 주어 충분한 차이가 벌어지도록 합니다. 



이러한 아이디어를 이용해 실제로 정의되는 triplet loss 는 아래와 같습니다.


$$ L(A, P, N) = max(||f(A) - f(P)||^2 - || f(A)-f(N) || ^2 + \alpha, 0) $$


실제 배치 트레이닝시 loss 는 아래와 같이 계산될 것입니다. m 은 배치 샘플 수입니다. 


$$ J = \sum^{m}_{i=1} L(A^i, P^i, N^i) $$


Triplet loss 의 문제는, A, P, N 을 random 하게 골랐을 때, loss 가 0이 되는 것이 너무 쉽게 만족한다는 것입니다. 그래서 실제 트레이닝시 중요한 것은 구분하기 어려운 샘플을 고르는 것입니다. 즉, A-P 와 A-N의 distance 의 차이가 크지 않은 두 이미지를 우선적으로 사용하는 것이 좋습니다. 이 방법은 Triplet loss 를 제시한 논문에 설명되어 있습니다.  

https://omoindrot.github.io/triplet-loss


트레이닝 데이터에서 Triple loss 로 하나의 CNN 을 구현한 후 학습한 후, 테스트 데이터에서는 예를 들어, 이미지의 representation 을 얻은 후, DB 에 있는 representation 과의 distance가 k 미만일 때, 같은 사람이라고 verification 할 수 있습니다.


Siamese Network + Binary loss


아래와 같이 문제를 Binary classification 으로 formulation 해서 face recognition 을 구현할 수도 있습니다. 실제로 많이 사용하며, 성능도 괜찮은 방법입니다. 




https://medium.com/predict/face-recognition-from-scratch-using-siamese-networks-and-tensorflow-df03e32f8cd0


이 방법에서 Siamese Network 는 두 이미지를 input 으로 하고, 같은 사람을 1, 다른 사람을 0 로 encoding 된 label 을 통해 training 됩니다. triplet loss 를 사용한 방법에서는 max(l2 distance 의 차이+alpha, 0) 을 줄이는 방향으로 training 이 되었지만, binary classification 을 활용한 방법의 경우 구조가 약간다릅니다. 


Siamese network와 binary classification 을 결합한 방법에서 CNN 이후 아키텍쳐는 다음과 같습니다.  


1. 두 이미지에 대해 CNN 을 통과시켜 나온 두 representation 의 l1 vector 를 구합니다 (l1 vector는 CNN 으로 변환된 벡터의 absolute distance 를 원소로 갖는 vector 입니다).

2. l1 vector 를 hidden layer 에 통과시킨후 output layer 에서 sigmoid 변환을 합니다. 

3. Binary cross entropy 를 loss function 으로 모델을 학습합니다. 

4. 모델은 0-1 사이의 값을 갖는 output 내보냅니다. 이 값은 크면 클 수록 두 이미지가 비슷하다라는 것이므로 "similarity" 라고 할 수 있습니다. 

5. 이 과정을 거치면 X1, X2 에 대한 CNN 의 output representation f(X1), f(X2) 는 같은 사람에 대해서 distance가 작게, 다른 사람에 대해서는 distance 가 크게 나오게 됩니다. 



참고 


반응형
반응형

층화 무작위배정을 실제로 하는 방법


본 포스팅에서는 임상시험에서 층화 무작위배정 을 실제로 어떻게 하는지에 대해 알아보겠습니다.층화 무작위배정 은 공변량에 대해 층화하여 무작위 배정을 하는 방법입니다. 층화 무작위배정 을 하는 이유는 1) 해당 공변량 대해 Randomization 되어있도록 해서 bias 를 줄이고자 하는 것입니다. 이는 treatment/control group 간에 공변량의 분포가 다른 경우, bias 가 생기기 때문입니다. 하지만 공변량에 대해 treatment/control group 이 randomization 되어있다면, 해당 이론적으로는 공변량에 대해서는 분포가 다를 수 없어 이로 인한 bias 가 생기지 않게 됩니다. 2) 또한 공변량을 보정하면 공변량의 effect 를 제외하여 treatment effect size 의 참값을 더욱 잘 추정할 수 있습니다. 이를 통해 검정력을 증가시킬 수 있습니다. 


다음과 같은 상황을 예로 들어 봅시다. 


1. 총 4개의 기관이 임상 시험에 참여

2. 기관당 임상시험 참여자는 20명

3. 나이를 공변량으로 잡고 Block size 는 6,6,6,2 로 설정한다. 

구체적으로 기관 별로 아래와 같은 

- 20~30세 : 6명

- 30~40세 : 6명

- 40~50세 : 6명

- 50~60세 : 2명


4. treatment/control group 의 배정 비율은 1:1 이다.

즉, Block size가 6이면 treatment 3명, control 3명을 무작위 배정한다.


전략


1. 기관별로 층화 -> 똑같은 코드를 4번 돌리는 것으로 구현 

2. 나이를 사이즈 6,6,6,2 로 층화 무작위 배정 -> for 문으로 구현

3. 블록별 무작위 배정을 위해 블록 별로 0~1사이의 난수를 발생하고 (이중 for 문으로 구현 ), 이 값을 기준으로 sorting 해서 중위수를 기준으로 두 그룹으로 나눈 후, A, B 약을 배정한다. 

blockrand <- function(R, N, blocksize){ result <- data.frame(block=numeric(N), item=numeric(N), drug=character(N), rand=numeric(N), random_no=character(N), stringsAsFactors=FALSE) index <- 1 for (i in c(1:ceiling(N/blocksize))){ if (i == (ceiling(N/blocksize))){ blocksize <- N - (i-1)*blocksize # 2 } for (j in c(1:blocksize)){ result[index, 'block'] <- i result[index, 'item'] <- j if (j <= blocksize/2){ result[index, 'drug'] <- 'A' } else { result[index, 'drug'] <- 'B' } result[index, 'rand'] <- runif(1) index = index + 1 } } result <- result[order(result$block, result$rand), ] result$random_no <- c(paste0(R,'-R', as.character(c(1:(ceiling(N/blocksize)*blocksize))))) return (result) } inst1 <- blockrand('01', 20, 6) inst2 <- blockrand('02', 20, 6) inst3 <- blockrand('03', 20, 6) inst4 <- blockrand('04', 20, 6)

이를 위하여 blockrand 라는 함수를 만듭니다. 이 함수는 기관명 R, 기관별 대상자 수 N, 블록 사이즈 blocksize 를 parameter 로 받아 결과 테이블을 dataframe 형식으로 생성하여 반환하는 함수입니다. 


결과 예시

 

block item         drug       rand random_no

3      1    3 A 0.03323463     01-R1

5      1    5   B 0.23377875     01-R2

1      1    1 A 0.58872123     01-R3

4      1    4   B 0.64914300     01-R4

2      1    2 A 0.86294140     01-R5

6      1    6   B 0.86923215     01-R6

8      2    2 A  0.01529889     01-R7

9      2    3 A 0.11754757     01-R8

11     2    5   B 0.13073496     01-R9

10     2    4   B 0.46934007    01-R10

7      2    1 A  0.56171635    01-R11

12     2    6   B 0.61339224    01-R12

14     3    2 A 0.11352796    01-R13

13     3    1 A 0.62667742    01-R14

15     3    3 A 0.82257959    01-R15

18     3    6   B 0.85910418    01-R16

17     3    5   B 0.87995944    01-R17

16     3    4   B 0.92778095    01-R18

20     4    2   B 0.70824728    01-R19

19     4    1 A 0.76212643    01-R20


이러한 층화 무작위배정은 머신러닝에서 특정 변수의 분포를 train/valid/test set 에서 맞추어줘서 모델의 일반화 성능을 보다 정확히 평가하고 싶은 경우에 사용되기도 합니다. 


반응형
반응형

다중 비교 (Multiple comparison) 문제와 보정 방법 


본 포스팅에서는 다중 비교가 무엇인지, 어떤 방법으로 보정할 수 있는지를 알아보겠습니다. 다중 분석이 문제가 된다는 점을 이해하기 위해 먼저 예를 설명하겠습니다.폐암과 연관성이 있는 요인을 찾기 위한 연구를 수행한다고 해봅시다. 예를 들어, 나이, 체질량 지수, 식습관, 음주습관, 운동량 등 100 가지의 요인에 대해 폐암과의 연관성을 유의 수준 0.05 하에서 비교하고자 합니다. 이를 위해 폐암 환자와 정상인을 수집했고, 연속형 변수에 대해서는. T-test, 범주형 변수는 카이제곱 검정을 수행하여 최종적으로, 운동량, BMI, 학력, 우유 섭취량, 경제력이 폐암과 연관성이 있다고 나왔다고 합시다. 이 때, 이 변수들이 폐암과의 연관성이 있다고 결론 내릴 수 있을까요? 물론 각각의 검정은 유의 수준 0.05 하에서 검정한 것이기 때문에, 연관성이 실제로 있을 가능성이 높은 것이 사실입니다. 하지만 100가지 요인에 대한 검정을 하나의 연구로 볼 때, 모든 변수가 연관성이 실제로 없더라도 평균적으로 5개의 잘못된 결과를 얻게 됩니다. 만약에 이러한 잘못된 결론이 학계에 보고되면 잘못된 과학적 결론을 내릴 수 있습니다. 따라서, 개별 검정 뿐 아니라 한 연구의 에러를 줄이는 방법이 필요합니다. 즉, 한 연구에서 나올 수 있는 잘못된 결과를 줄이자 라는 것이 다중검정 보정의 핵심입니다. 


여기서 family-wise type 1 error 의 개념이 등장합니다. Family-wise type 1 error rate (FWER) 란 한 연구에서 적어도 한 개의 잘못된 결론 (false positive)이 나올 수 있는 확률을 의미합니다. 만약 이것이 0.05 라면 한 연구에서 적어도 1개의 잘못된 결론이 나올 확률이 0.05 라는 것입니다. FWER 를 통제하는 방법으로 잘 알려진 것이 본페로니 보정 방법입니다. 본페로니 보정은  test 의 수가 n 이고, FWER 를 0.05 로 통제할 때, 개별 테스트의 유의수준을 alpha/m 으로 설정시킵니다. 모든 검정이 실제로 연관성이 없는 경우 (null 인 경우), 아래 식이 m 이 클 수록 대략적으로 만족됩니다. 


$$ FWER= 1-(1-\alpha/m)^m = \alpha  $$


<본페로니 방법을 통한 FWER 통제>


하지만 본페로니 방법의 문제는 너무 보수적 (strict/conservative) 이라는 것입니다. 보수적이라는 뜻은 귀무가설을 웬만해선 기각하지 않는다는 것으로, false positive 는 상당히 줄일 수 있지만, false negative 는 많아지게 됩니다. 예를 들어 test 의 개수가 1000개인데, 연관성이 없는 것이 900개, 연관성이 있는 것이 100개 일 때, 900개 중에 1개라도 잘못나올 확률은 1-(1-0.05/1000)^900 < 0.05 입니다. 물론 false positive 가 적어져서 좋긴 하지만, false negative 가 많아진다는 문제가 생깁니다. 따라서, 다중 비교 검정의 핵심은 어떻게 false positive 를 줄이면서, false negative 도 줄일 수 있는가? 입니다.


다중 검정 보정 방법


그러면 어떻게 false positive, false-negative 의 타협을 보는가 (FWER 은 본페로니 수준이면서, false negative 를 줄일 수 있는가)에 문제에 있어서 도입되는 한 가지 방법이 바로 multi-step 보정 방법입니다. Multi-step 보정법은 step-down 방법, step-up 방법으로 나뉘는데, 예를 들어, step-down 방법은 Holm's 방법, step-up 방법은 Hochberg 방법이 있습니다. 이러한 multi-step 보정 방법은 fwe 는 그대로 두면서 false negative 는 줄이는 방법으로 알려져 있습니다. 이를 수리적으로 보이는 것은 다소 어렵지만 simulation 을 통해서 bonferonni 방법보다 효율적이라는 것이 많이 알려져 있습니다. Multi-step 보정 방법은 모든 검정에서 나온 p-value 를 정렬 (sorting) 한 후, 각 검정마다 각기 다른 p-value cutoff 를 적용시키는 방법입니다. Step-down 방법은 p-value 가 가장 작은 검정부터, step-up 방법은 p-value 가 큰 검정부터 귀무가설 기각 여부를 보게됩니다. 


FWER 이 아닌 False Discovery Rate (FDR) 을 줄이는 방법이 최근 많이 사용되고 있습니다. FDR 을 다중검정에서 사용할 때의 의미는 false postive, false negative 를 줄이는데 집중할 것이 아니라, 내가 귀무가설을 기각한 검정 중 틀린 것 (false positive/true positive+false positive = discoveries) 의 비율을 줄이자는 것입니다. 최근 FDR 통제에 많이 쓰이는 방법 중 하나가 Benjamin-Hochberg 방법입니다. 이 방법은 FDR 를 잘 통제한다고 알려진 방법입니다. 


$$ FDR = \frac{false_{positive}}{true_{positive}+false_{positive}} $$


Holm's 방법, Hochberg 방법, Benjamin-Hochberg 방법 모두 간단하며, 검색해보면 쉽게 알 수 있기 때문에 본 포스팅에서는 따로 설명하지 않고, 아래 시각적으로 설명한 그림을 첨부하였습니다. Benjamin-Hochberg 방법의 경우 이전 제 포스팅에서 설명했습니다.



 

<Holm's, Hochberg, Benjamin-Hochberg Procedure 의 비교>



다중 검정 보정이 필요한 상황


1. 수많은 요인들에 대한 연관성 분석을 수행할 때


이 예로는 유전체학 분야를 들 수 있습니다. genomics 분야에서 microarray 와 같은 high-throughput 기술의 발달됨에 따라, 수많은 유전자 변이 마커, 유전자 발현 (gene expression) 과 표현형의 연관성을 보는 연구가 수행되었습니다. 이러한 Genomics 분야의 발전은 다중 검정 보정이 생겨난 이유이기도 합니다. 일반적으로 한 사람에서 300만개의 단일염기다형성 (single-nucleotide polymorphism) 이 있습니다. 이러한 마커들과 질병의 연관성을 보는 연구에서 multiple comparison 문제가 생길 수 밖에 없고, 수 많은 false positive 가 생기게 됩니다. 이 분야의 default 라고 얘기할 수 있는 보정 방법은 FDR 을 통제하는 Benjamin-Hochberg 방법입니다. 


2. 임상 시험 


임상 시험의 수많은 상황에서 다중 비교 문제가 생기게 됩니다. 신약 개발을 예를 든다면, 아래와 같은 상황이 발생할 수 있습니다. 


1) 비교 그룹 수가 3개 이상인 경우 :ANOVA 검정 후 유의해서, 사후검정을 할 때, 다중 검정이 발생하게 됩니다. 예를 들어, 대조약, 저투여군, 고투여군 3그룹을 비교할 때 생길 수 있습니다. 이러한 임상시험 세팅에서는 다중성을 반드시 보정해주어야합니다. 


2) 하위군 분석 : 임상 시험 대상자를 어떤 공변량을 기준으로 하위군으로 나누어 신약의 효과를 평가할 때 발생합니다. 대표적으로 다중 검정이 발생하는 사례로, 하위군 분석 결과를 확증적으로 인정받기 위해서는 임상시험계획서에 이를 명기하고 다중검정 보정법을 제시해야합니다. 


3) 중간 분석 : 임상 시험에서 시험 기간이 종료되기 전에 중간에 데이터를 오픈해서 테스트를 하는 경우도 있습니다. 이 경우도 대표적으로 다중 검정이 발생하는 사례로, 각 절차에서 O-brien fleming 방법을 사용한 다중검정 보정법이 종종 사용됩니다.

반응형
반응형