ITS's Dev Story

내가 만든 소스코드를 실험해 보기 위해 TreePlotter 모듈을 만들어 컴파일했는데 이렇게 에러가 났다.

ImportError : No moudle named matplotlib

에러 : matplotlib 모듈이 없습니다!!!

왜 이런 에러가 날까? 내가 짠 TreePlotter 모듈의 소스코드를 일부부분만 공개한다.

-------------------------------------------------------------------------------------------

* Numpy 기계학습의 모든 문서는 제가 머신러닝을 공부하고, 제 프로젝트에 필요한 알고리즘을 찾기 위해 정리하는 일종의 '요점정리 노트' 입니다. 혹여나 오해 없으시기 바랍니다. (2016.03.14 추가)

-------------------------------------------------------------------------------------------


import matplotlib.pyplot as plt

decisionNode = dict(boxstyle="sawtooth", fc="0.8")
leafNode = dict(boxstyle="round4", fc="0.8")
arrow_args = dict(arrowstyle="<-")

def getNumLeafs(myTree):
    numLeafs = 0
    firstStr = myTree.keys()[0]
    secondDict = myTree[firstStr]
... (이하 생략)

이 부분에 matplotlib을 import해주는 부분이 있어서이다. 그렇다면 그 라이브러리를 설치해야 한다.

우선 공식 홈페이지에 가서, 64비트 기반 Installer를 다운로드 받았다.


근데... 파이썬 경로를 못 잡는다. 어쩔 수 없다. whl 파일로 설치해보는 걸로 한다. 근데 이번에는 pip이 없다. whl파일을 설치하려면 pip이 필요하다. 공식 홈페이지에 있는 get-pip.py 파일로 설치해보는걸로 한다.

이제 내가 구한 파일을 실행한다.

이게 무슨 일인고?

어? 어? 어어어? 제대로 된 파일이름이 아니란다. 알아보니 중간에 수정하면 설치가 안된다고 한다.

다시 파일을 다운받아서 설치한다.

어? 되는 것 같은데?

이번에도 설치가 안된다.

그래서 구글에 에러 내용을 똑같이 쳤다. Could not find a version that satisfies the requirement mathplotlib...

검색해보니 한 스택오버플로우 게시글이 나온다. 여기서는 미러 서버를 이용한 해결방법을 제시하고 있다.

그대로 실행해 본다.

Sucessfully installed!

그럼 지금까지 한게 헛수고란 말인가? (커맨드로 수동 설치할 경우 잘못 설치하면 그 과정이 헛수고가 되는 경우가 많음)

matplotlib import도 잘 된다.

에러가 나던 treePlotter 모듈 import도 잘 된다!

이제 의사결정 트리를 직접 동작해 본다.

제대로 동작하는 모습을 볼 수 있다. 이를 해결하기 위해 1시간 정도가 소요되었다. 1시간 동안 헛수고를 하면서 수동설치기의 근본적인 문제를 찾아내고 이를 해결해 나가는 시간이 꼭 헛수고만은 아닐 것이라 생각한다. 오늘은 여기까지 하기로 했다.

오늘은 트리를 직접 만들어 보는 실습을 하기로 했다. 트리는 의사결정 기술이라고 해서 분류 기술 중 가장 일반적으로 사용되는 기술이라고 한다. 의사결정 트리의 기초가 되는것은 바로 프로그래밍을 하게 되면 종종 보게 되는 플로우차트(flowchart)이다. 얼마 전 내가 토론대회에서 논제결과를 발표했을 때 반론으로 "플로우차트가 비전문가들이 알아보기 힘드니 풀어서 설명해라"라고 나왔는데, Flowchart는 소스코드를 디자인화시킨 것이지 비전문가들에게 완벽하게 코드를 이해시키기 위한 그림이 아니다. 혹시나 헷갈리는 일이 없었으면 한다.

-------------------------------------------------------------------------------------------

* Numpy 기계학습의 모든 문서는 제가 머신러닝을 공부하고, 제 프로젝트에 필요한 알고리즘을 찾기 위해 정리하는 일종의 '요점정리 노트' 입니다. 혹여나 오해 없으시기 바랍니다. (2016.03.14 추가)

-------------------------------------------------------------------------------------------


(출처 : Wikipedia, 문제가 될 경우 다른 사진으로 대체)

플로우차트가 왜 머신러닝에서 언급되냐 하면 데이터 집합을 구분하기 위해 필요하기 때문이다. 의사결정 트리, 즉 플로우차트를 만들기 위해서는 하나의 최초 의사결정을 만들어야 한다. 의사결정의 구조는 다음과 같다.

최초 의사결정은 데이터를 분할하는데 영향을 주는 속성으로 결정되게 되고, 이러한 결정은 모든 속성에 대해 측정을 하면서 얻어지며, 결정된 속성으로 분할하는 경우 가장 좋은 결과를 얻게 되는 것이다. 

그런 다음 데이터 집합을 하위 집합으로 분할한다. 하위 집합은 최초 의사결정의 가지로 모이게 된다. 가지에 있는 데이터가 모두 같은 분류에 속하면 계속 반복하지 않아도 되지만, 만약 그렇지 않다면 그 과정을 다시 반복해야 할 것이다.

이렇게 생기는 데이터 집합을 분할하기 전과 후의 변화를 정보 이득이라고 하며, 데이터 집합에 대한 정보 측정 방법을 섀넌 엔트로피, 줄여서 엔트로피라고 한다. 아마도 정보 이득과 엔트로피라는 용어가 혼란스럽게 들릴 수도 있다. 하지만 이 단어들이 혼란스러운 것은 당연한 이야기이다. 이에 대한 답은 이 글을 읽는 독자들이 알아서 찾아 나가기를 바란다.

우선, 나는 데이터 집합의 엔트로피를 계산하는 함수를 만들어 보기로 하였다. 예제에 나온 소스코드는 다음과 같다.

def calcShannonEnt(dataSet):
    numEntries = len(dataSet)
    labelCounts = {}
    for featVec in dataSet:
        currentLabel = featVec[-1]
        if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries
        shannonEnt -= prob * log(prob,2) #log base 2
    return shannonEnt

그 다음 집합을 분할하는 함수를 만들어 보았다. 소스코드는 아래와 같다.

def splitDataSet(dataSet, axis, value):
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            retDataSet.append(reducedFeatVec)
    return retDataSet
그 다음 데이터 분할 시 가장 좋은 속성을 선택하는 함수를 만들었다. 소스코드는 아래와 같다.
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1  
    baseEntropy = calcShannonEnt(dataSet)
    bestInfoGain = 0.0; bestFeature = -1
    for i in range(numFeatures):       
        featList = [example[i] for example in dataSet]
        uniqueVals = set(featList)   
        newEntropy = 0.0
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value)
            prob = len(subDataSet)/float(len(dataSet))
            newEntropy += prob * calcShannonEnt(subDataSet)     
        infoGain = baseEntropy - newEntropy  
        if (infoGain > bestInfoGain):     
            bestInfoGain = infoGain     
            bestFeature = i
    return bestFeature         
그 다음, 트리 만들기 함수를 만들었다. 소스코드는 아래와 같다.
def createTree(dataSet,labels):
    classList = [example[-1] for example in dataSet]
    if classList.count(classList[0]) == len(classList): 
        return classList[0]
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureToSplit(dataSet)
    bestFeatLabel = labels[bestFeat]
    myTree = {bestFeatLabel:{}}
    del(labels[bestFeat])
    featValues = [example[bestFeat] for example in dataSet]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:]
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree 

최근 Github에 내가 만든 머신러닝 소스코드를 올려 공유하고 있다. 주소는 아래와 같다.

https://github.com/itsss/machinelearning


오늘은 여기까지 하기로 하고, 내일 실제로 동작해보는 것으로 했다.

전년도 12월말 경, 만드로 전자의수 Mark 4를 제작하신 만드로용님에게 30만원 상당의 전자의수 키트를 받았다.

▲ 만드로 전자의수를 제작하신 만드로용님, 근전도 센서를 이용한 의수 제어를 시연하고 있다.

▲ 전시중인 Mark.4 전자의수 키트

키트를 받자마자 우선 3D 프린터 출력물의 베드를 제거했다. 쉽게 뗄 수 있다고 생각하였지만 큰 오산이었다. :) (베드란? 3D 프린터에서 인쇄 실물이 만들어 놓여지는 공간)

▲ 베드를 모두 제거한 출력물, 오른쪽 상단에 보이는 출력물은 제거를 하지 못했다. 제일 어려운 부분.

그 다음에는 왼쪽 상단에서 2번째에 보이는 손가락을 접합했다. 여기서 아주 큰 실수가 하나 있었는데 순간접착제 접합을 잘못 해버려서 떼기 위해 엄청 고생했다. 순간접착제를 사용할 때에는 꼭 테스트를 해 보고 붙여야 한다. 구멍의 크기가 다른 것은 다 이유가 있다. (생각을 좀 하고 살자! 생각을!!!)

▲ 회로 조립

그 다음, 아두이노가 들어갈 기판을 납땜하였다. 여기에는 4핀, 6핀, 10핀 커넥터가 각각 3개, 1개, 1개 들어가고, 아두이노 나노 보드와, 모터드라이브 2개, 저항 10K 4개, 저항 150 1개, 15핀 헤더 소켓 2개, LED 5개, 방열판 2개, 모터드라이버 소켓이 들어간다. 위 사진에서는 아두이노 나노와, 10핀 커넥터, LED 조립을 하지 않은 상태이다.

▲ 접합 완료된 손가락 조립

접합이 완료된 손가락을 조립했다. 이 부분에서 한 쪽은 너무 높고, 한쪽은 너무 낮아 무언가 미스가 있음을 확인하고 살펴보았더니 같은 짝이 아님을 알 수 있었다.


▲ 모터 커플러 조립 및 선 납땜.

▲ 베어링 및 스프링 조립. 스프링을 자르고 고정하는 과정이 제일 힘들었다. 스프링철사를 어느 정도 크기로 잘라야 할지는 그 안에서 늘어날 정도로 여유를 둬야 한다. 더 자세한 건 직접 경험을 해 보는것이 좋다.

▲ 회로 조립 완료.

시간이 날 때마다 짬짬이 하기 때문에 오늘은 여기까지 하기로 하였다. 내가 직접 조립을 해 보니 단순 조립하고는 차원이 달랐다. 실수를 할 때마다 창의력을 발휘해야 하는 순간들이 많았다. 너무 친절한 설명서는 실패를 줄이기 때문에 스스로 생각하는 시간을 없애 버릴 것 같다. 단순히 레고처럼 설명서를 보고 따라 조립을 한다면 단순 노동에 불과할 것이다. 사람을 위한 아름다운 기술에 대해 의미를 다시한번 생각해 보고, 메이킹을 직접 해보면서 창의력을 발휘할 수 있는 순간들이 있다면 진정한 메이커로써 성장이 가능할 것이다.