R에서 쓸 수 있는 기계 학습 패키지 중에서 maximum entropy 패키지 사용법을 정리해본다.


내가 사용한 패키지는 Timothy P. Jurka, Yoshimasa Tsuruoka가 제작한 maxent 패키지인데, 메모리 사용량을 최소화해서 데이터량이 매우 많아도 잘 동작한다고 한다. 이번에는 사용법을 익히는 것까지만 했기 때문에 실제로 어느정도의 데이터까지 다룰 수 있는지는 확인하지 못했지만, 곧 확인해볼 생각이다.


1. 패키지 설치

일반적인 R 패키지와 마찬가지로 install.packages('maxent') 명령으로 쉽게 설치가 가능하다.


2. 사용

R에 기본으로 포함되어 있는 iris 데이터셋을 이용해 학습과 평가를 수행해보았다.


library(maxent);


# shuffling the data

m = dim(iris)[1];

i = sample(1:m, m, replace=F);

data = iris[i, ];


# splite data

train = data[1:100, ];

test = data[101:150, ];


train_feature = train[, c(1,2,3,4)];

train_class = train[, 5];

test_feature = test[, c(1,2,3,4)];

test_class = test[, 5];


# training

model = maxent(train_feature, train_class);


# testing and measuring performances

pred = predict(model, test_feature);

result = test_class == pred[,1];

summary(result);

table(test_class, pred[,1]);


iris 데이터가 각 클래스별로 정렬이 되어 있기 때문에 smaple() 함수를 이용해 데이터를 섞은 다음에 100개의 학습 집합과 50개의 테스트 집합으로  나눴다. 그리고 각 집합에서 자질 부분과 레이블(class) 부분을 추출했다.

maxent() 함수를 이용해 학습을 수행한 후, 그 결과로 생성되는 모델을 predict() 함수에 넣어서 예측을 수행한다. 


한가지 아쉬운 부분은 maxent 패키지에는 평가와 관련된 기능이 없다는 것이다. 정확도나 confusion matrix를 출력해주는 기능이 없기 때문에 위와 같이 실제 정답과 predict() 결과를 비교한 다음에 summary() 함수나 table() 함수 등을 이용해 결과를 확인해야 한다.



 


WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  0개가 달렸습니다.
secret

파이썬 코드를 보다보면 가끔 함수 이름 위에 @로 시작하는 구문이 붙어 있는 것을 가끔 볼 수 있다. 

@verbose

def my_function():

print "hello, world."

만약 자바를 주로 사용하는 프로그래머라면 어노테이션(annotation)이랑 비슷한 것인가 하는 생각이 들 것이다. 하지만 미리 말하자면 파이썬의 데코레이터와 자바의 어노테이션은 많이 다르다. 


파이썬의 데코레이터는 그 이름에서 알 수 있듯이 기본적으로는 본체를 꾸며주는 역할을 한다. 위 예제에서는 my_function()함수가 본체에 해당한다. 즉, @verbose라는 데코레이터를 추가함으로서 본체인  my_function()에 뭔가가 더해지기는 했지만, 기본적으로  my_function()이 바뀌는 것은 아니라는 의미이다[각주:1]


파이썬에서 함수는 first citizen에 속한다. 이 말은 파이썬에서 함수를 다른 함수의 인자로 넘기거나 함수의 리턴 값으로 사용하는 것이 가능하며 매우 자연스러운 일이라는 뜻이다. 다음 코드와 같이 말이다.

def verbose(func):

print "Begin", func.__name__;

func();

print "End", func.__name__;



verbose()는 다른 함수를 인자로 받는다. 물론 C에서도 함수 포인터를 이용해 동일한 기능을 이용할 수 있다. 하지만, C는 어디까지나 포인터를 인자로 사용하는 것이지, 함수 자체를 인자로 사용하는 것은 아니다. 즉, C에서 함수는 first citizen이 아니다. 함수가 first citizen인 언어에서는 이를 이용한 다양한 기법들이 있으며, 이를 통해 다양하고 기발한 코딩이 가능하다. 여기서 설명하는  데코레이터도 그 중에 하나이다.


verbose()에 my_function를 인자로 넣으면 다음과 같은 실행 결과를 볼 수 있다.

>>> verbose(my_function)

Begin my_function

hello, world.

End my_function

간단한 예제이지만 우리는 이를 통해서 다른 함수를 인자로 받는 함수를 살펴보았다.  여기서 verbose()는 my_function() 자체의 동작을 변경시키지 않는다. 다만, my_function()의 앞 또는 뒤, 위 경우에는 둘 다,에 약간의 장식을 덧붙였을 뿐이다.


그런데, 이 방법은 엄밀히 말하면 my_function()에 장식을 붙인 것이라 하기 힘들다. 왜냐하면 우리가 호출한 함수는 my_function()이 아니라 verbose()이기 때문이다. 즉 클라이언트 입장에서는 my_function()에 장식이 덧붙여진 것이 아니라, 원래 저런 기능을 하는 verbose() 함수를 사용한 셈이다.


verbose()를 수정해서 이 문제를 해결해보자.

def verbose(func):

def new_func():

print "Begin", func.__name__;

func();

print "End", func.__name__;

return new_func;


verbose()에서  new_func()이라는  nested function을 정의한 다음에 마지막에 이 함수를 반환한다. 이것은 앞서 말한 바와 같이 파이썬에서는 함수가 first citizen이므로 이러한 코드는 전혀 문제가 없다. new_func()이 하는 일은 기존의 verbose()와 동일하다.


이제 바뀐 verbose()가 my_function()을 어떻게 장식해주는지 보자.

>>> my_function = verbose(my_function)

>>> my_function()

Begin my_function

hello, world.

End my_function

먼저  verbose()의 리턴값으로 my_function을 대체한다. 이렇게 하면 my_function()은 원래 기능과 함께  verbose()에서 제공하는 부가적인 기능까지 수행하는 새로운 함수가 된다. 즉,  verbose()가 my_function()에 장식을 달아준 것이다. 그리고 이 장식은 계속 유지된다.


파이썬의 데코레이터는 사실 위 기능을 좀 더 직관적이고 간결하게 명시할 수 있도록 해주는 syntactic sugar일 뿐이다. 이 글의 첫번째 코드와 같이 함수 정의 윗부분에 데코레이터를 달면(데코레이션) 우리가 위에서 했던 "my_function = verbose(my_function)" 부분을 따로 쓸 필요도 없어지고 코드의 의도도 명확해진다. 주의할 점은 데코레이터는 결국 함수, 좀 더 정확하게 말하면 callable object이므로 당연히 데코레이션을 하기 전에 해당 함수 또는 클래스를 정의해야 한다는 것이다.


클래스 이야기가 나온 김에 클래스로 데코레이터를 정의하는 방법을 살펴보도록 하자. 클래스를 함수처럼 호출되게 하려면 __call__() 맴버 함수를 정의하면 된다. 이를 이용해서 위  verbose()함수를 클래스로 바꿔보면 다음과 같다.

class Verbose:

def __init__(self, f):

print "Initializing Verbose."

self.func = f;


def __call__(self):

print "Begin", self.func.__name__

self.func();

print "End", self.func.__name__


@Verbose

def my_function():

print "hello, world."


print "Program start"

my_function();



앞서 사용했던 verbose()와 구별을 위해, 그리고 일반적인 코딩 규약에 따라 클래스 버전은 대문자 V로 시작한다. 코드 자체는 간단하기 때문에 설명할 것은 별로 없다. Verbose가 언제 초기화 되는지를 확인하기 위해 생성자에 메시지를 출력하도록 하였으며, __call__() 맴버 함수에서 return 부분은 필요치 않다. 이 예제를 파일로 저장한 다음 파이썬으로 실행해보면 다음과 같은 결과를 볼 수 있다. 여기서는 파일 이름을 decorator_test.py라 하였다.

$ python decorator_test.py

Initializing Verbose

Program start

Begin my_func

hello, world

End my_func


결과를 보면 알 수 있듯이 Verbose 클래스의 생성자가 먼저 실행된 다음에 프로그램의 메인 부분이 실행된다. 이를 통해 우리는  데코레이터가 함수 정의 단계에서 실행됨을 알 수 있다.  그 밖에는 앞에서 이야기 한 것과 동일하다.


마지막으로 인자를 갖는 함수에 대한 데코레이터를 만드는 방법에 대해 이야기해보자. 지금까지의 과정과 마찬가지로이것 역시 파이썬답게 간단하다. 우리가 할 일은 단지 '*args'와 '**kwargs'만 추가해주면 된다[각주:2].

class Verbose:

def __init__(self, f):

print "Initializing Verbose."

self.func = f;


def __call__(self, *args, **kwargs):  #여기 

print "Begin", self.func.__name__

self.func(*args, **kwargs);    #여기

print "End", self.func.__name__


@Verbose

def my_function(name):   #l 인자를 갖는 함수

print "hello,", name




참고 자료



  1. 이 정도 의미로 생각한다면 자바의 어노테이션도 동일한 것이라 볼 수도 있다. 하지만 자바의 어노테이션은 파이썬에 비해 자신이 원하는 대로 만들어 사용하기가 매우 어렵다. [본문으로]
  2. *args, **kwargs 는 파이썬에서 일종에 가변 길이 인자를 다루는 방법이라 할 수 있다. 개인적으로는 파이썬 문법 중에서 가장 난해한 부분이 이 *args, **kwargs라고 생각한다. 사실 어려운 내용은 아니지만 직접 쓸 일이 없다 보니 매번 까먹는 것 같다. [본문으로]

WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  3개가 달렸습니다.
  1. 잘 보고 갑니다.
  2. rakk4403 2016.01.01 21:10
    잘봤습니다. 좋은포스팅 감사합니다.
  3. 잘보고갑니다.
secret

.SUFFIXES: .o .cpp .cxx


CXX=g++

DEP_FILE=Makefile.dep

CPPFLAGS= -g -ggdb -DEBUG -D_DEBUG

#CPPFLAGS= -O2 -DNDEBUG -DNO_DEBUG

CPPFLAGS+= -fPIC -W -Wall -pthread -std=c++11


#Linux에서 플랫폼 알아내기

UNAME_P:=$(shell uname -p)

ifeq ($(UNAME_P), x86_64)

    PLATFORM=x86_64

else

    # Android

    PLATFORM=armeabi

endif


SRC=$(wildcard *.cpp)

#SRC+=$(wildcard other_dir/*.cpp) # 추가적인 디렉토리가 있는 경우

OBJ=$(SRC:.cpp=.o)


LIB_DIR=lib/


TARGET_SRC=$(wildcard *.cxx)

TARGET_NAME=$(TARGET_SRC:%.cxx=%)

TARGET_LIB=libOOO.a



INC=-I../OOO

LIBS=$(LIB_DIR)$(TARGET_LIB)

LIBS+= -lpthread

#LIB+=외부 라이브러리


.cpp.o:

-@echo "== Compile $< =="

$(CXX) $(CPPFLAGS) -c $< -o $@ $(INC)

.cxx.o:

$(CXX) $(CPPFLAGS) -c $< -o $@ $(INC)


all: lib slib $(TARGET_NAME)


lib: $(OBJ)

ar rsc $(LIB_DIR)$(TARGET_LIB).a $(OBJ)

ranlib $(LIB_DIR)$(TARGET_LIB).a 


slib: $(OBJ)

$(CXX) -shared -Wl,-soname,$(TARGET_LIB) $(CPPFLAGS) -o $(LIB_DIR)$(TARGET_LIB).so $(OBJ)


.SECONDEXPANSION:

$(TARGET_NAME): $$@.o $(OBJ)

$(CXX) -o $(BIN)$@ $< $(OBJ) -L $(LIB_DIR) $(LIB) $(INC)


#exe:

#    ?$(CXX) $(CPPFLAGS) -o $(BIN)$(TARGET) $(OBJ) main.o $(LIBS) $(INC)



dep:

$(CXX) -MM $(SRC) $(TARGET_SRC) > $(DEP_FILE)


clean:

rm -f $(OBJ) $(DEP_FILE) *~

rm -f $(BIN)$(TARGET_NAME)

rm -f $(LIB_DIR)$(TARGET_LIB).*

rm -f core*



제목 그대로 내가 주로 사용하는 Makefile의 기본 뼈대이다. 다른 고수분들처럼 make에 능통한 편은 아니라서 필요한 정도만 정의해서 사용하고 있다. 여러 명의 개발자가 투입되는 큰 규모의 프로젝트에 사용하기에는 부족하겠지만 이 정도면 자기 혼자서 또는 프로그램을 개발할 때 사용하기에는 딱 좋다.


먼저 말해둘 것은 여기서 말하는 make는 GNU make를 뜻한다. 유닉스 시스템에서 제공하는 다른 make는 위 Makefile이 제대로 동작하지 않을 가능성이 매우 높다.


맨 첫 줄의 .SUFFX는 이 Makefile에서 다룰 파일 확장자를 정의한다. 다른 사람들과 다른 점은 cpp, cxx 확장자를 모두 사용한다는 점이다. 자바와 달리 C++은 하나의 프로젝트에서 main()함수가 한 개여야만 한다. 따라서 두 개의 독립된 프로그램이 필요하지만, 모듈의 상당 부분을 공유하는 경우에는 부득이 각각을 별도의 프로젝트로(다른 디렉토리에) 만들어야 하는 경우가 생긴다. 이렇게 하는 대신에 나는 main() 함수가 들어간 파일은 cxx로 만들고, 공통 모듈에 해당하는 부분은 cpp 파일로 만든다. 그런 다음에 위와 같이 Makefile을 만들면 공통 모듈들만으로 library 파일을 만들고, 실행 프로그램은 그 라이브러리와 main()함수가 들어간 object 파일을 이용해 만들 수 있다.


"Linux에서 플랫폼 알아내기"라는 주석으로 시작하는 부분은 웹에 있는 다른 Makefile에서 참고한 것이다. Makefile에서 쉘 명령어를 사용하는 예제, if 문 사용하는 예제 용도로 적어놓았다.


그 다음은 패턴을 이용해 자동으로 소스 파일, 오브젝트 파일 리스트를 만드는 부분이다. 기존에는 보통 OBJ 변수에 파일 이름을 일일이 나열했어야 했는데, 이 방법을 사용하면 파일 관리가 훨씬 편해진다. 사실 make는 wildcard 이외에 다양한 기능들을 제공하고 있어서 그 자체로 어느 정도 수준의 프로그래밍이 가능할 정도이다. 


make에서 소스 파일 목록을 나열하는 방법에는 두 가지가 있다.

하나는 위와 갈이 $(wildcard *cpp) 구문을 이용해 모든 cpp 파일을 자동으로 포함시키는 방법이고, 다른 하나는 Makefile안에 직접 나열하는 방법이다. 편하기야 전자가 편하지만, 여러 파일에 main() 함수를 구현한 다음에 생성하고자 하는 프로그램에 따라 파일을 선택해서 쓰고 싶은 경우에는 컴파일 에러가 발생한다. 그래서 나는 main() 함수가 들어있는 소스 파일은 확장자를 cxx로 하고, 이 녀석들만 따로 컴파일하는 방법을 쓰고 있다.


".SECONDEXPANSION:" 으로 시작하는 부분은 *.cxx 파일이 여러 개일 때 각각의 이름으로 실행 파일을 만들어주는 부분이다. 위에서 *.cxx 파일들 이름 부분으로만 이루어진 $(TARGET_NAME) 변수를 만든 다음, 여기에 들어있는 각 원소에 대해 그 아래에 있는 make 명령을 실행한다.


dep, DEP 가 있은 부분은 소소 코드 의존성을 분석해주는 부분이다. 이것이 있으면 'make -j' 명령으로 여러 파일을 동시에 컴파일 할 때 컴파일 에러를 줄일 수 있다. 그런데 가끔 잘 안되는 경우도 있어서 거의 쓰지는 않는다.



 

그 밖에 부분은 평이하다. 좀 눈에 뛰는 부분이라면  .PHONY로 시작하는 부분일 것이다. 사실 소규모 프로젝트에서는 저 기능을 쓸 일이 거의 없기 때문에 없어도 된다. 많은 사람들에게 낯설어서인지 구글링을 해보면 .PHONY를 구글링해보면 많은 질문들을 볼수 있는데, 그 중에서 stack overflow에 올라온 이 질의응답이 간단하게 잘 설명해놓았다. 쉽게 말하면 .PHONY 키워드는 해당 타겟과 같은 이름의 파일이 있을 경우를 대비하기 위한 것이다. 즉, (그럴 가능성이 매우 낮지만) all이나 clean이라는 이름의 파일이 있을 경우에 make clean 을 수행하면 make는 아무런 동작을 하지 않을 것이다. 왜냐하면 clean은 확장자가 없고, 따라서 make가 이 파일을 어떻게 다뤄야 할지 모르기 때문이다. .PHONY는 이러한 오동작을 방지하기 위한 것으로 이 뒤에 기술된 키워드는 파일 이름이 아닌 make의 타겟으로만 인식하게 된다. 






WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  0개가 달렸습니다.
secret

이 글은 2 ~ 3 가지 정도, 또는 그 이상의 프로그래밍 언어를 알고 있는 사람을 대상으로 합니다. 특히 C/C++과 Python 또는 Perl과 같은 인터프리터 언어에 익숙하다고 가정하고 진짜 필요한 부분만 정리하려고 합니다.


(옥타브 설치나 인터프리터 실행 등에 대한 설명은 생략합니다.)


본 문서에서의 표기법


프로그램 실행 결과, 리턴값 등을 표시할 때는 '==>'를 사용한다.

옥타브 인터프리터 프롬프트는 '>>'로 표기하기로 한다.


주석문: # 또는 %

새로운 언어를 접할 때는 가장 먼저 주석문을 어떻게 다는지를 확인한다. 그 이유는 내가 이 언어를 배우기 위해 직접 코딩을 할 때 스스로 설명을 달고, 여러 가지 실험용 코드를 작성하면서 부분 부분 주석 처리를 해나가기 때문이다.

특이하게 옥타브에서는 주석문 기호로 #, % 두 가지를 지원한다.

* Line Comments

# 또는 % 사용. 

# 이건 주석문

% 이것도 주석문


* Block Comments

#{, #} 를 사용하거나 %{, %} 사용

#{

 #을 쓰던 %을 쓰던 상관은 없지만,

 에디터에 따라 syntax highlight 적용이 안되는 것이 있다.

 에디터 설정에 따라 편한 것을 쓰면 된다.

#}


문장 종결: 세미콜론(;)

문장 종결 기호로 세미콜론을 사용한다. 주의할 점은 세미콜론이 있느냐 없느냐에 따라서 동작이 달라진다는 것이다. 세미콜론이 있는 경우에 옥타브는 해당 문장을 수행만 하고 그 결과를 출력하지 않는다. 반면 세미콜론이 없는 경우에는 문장을 수행한 후 그 결과를 출력한다. 옥타브에서 다음 두 문장을 입력해보면 그 차이를 바로 알 수 있을 것이다.

>> a = 1

# 화면에 '1'이 출력됨

>> b = 1;

# 화면 출력 없음



화면 출력: disp(), printf()

disp(변수명)을 이용하면 화면 출력을 할 수 있다. printf()함수는 C에서의 그것과 동일하다.

disp()함수는 값을 출력한 후에 자동으로 다음 줄로 넘어간다.

>> disp ("value of pi ="), disp (pi);

value of pi =

 3.1416


연산자 간단 정리

할당(assignment) 연산자: =

증가 연산자: ++

감소 연산자: --

증가 연산자, 감소 연산자는 i++, ++i 형태 모두 지원하며 C/C++에서와 동일하게 동작한다.

나눗셈: / 옥타브는 기본적으로 실수 연산을 수행한다. 즉, 5/2 == 2.5

제곱: ^


도움말 보기: help

설명 생략


실행

my_octave.m 이라는 스크립트를 실행한다고 하면

1) 인터프리터에서 실행

>> cd target_directory

>> my_octave # 별도의 명령 없이 파일 이름을 입력하면 된다

>> run my_octave.m # 위와 동일 


2) 쉘에서 실행 - 다른 언어와 마찬가지로 'octave my_octave.m' 하면 된다.



제어문 기초

제어문 블록은 endXXX 형식으로 끝난다 (각 제어문 예제 참고). endXXX 대신 end라고만 해도 되지만, 가독성을 위해 endXXX를 사용하는 것이 좋을 듯 싶다.

- if문

if ( condition )

  body

elseif ( condition )

  body

else

  body

endif

' if ( condition ) ' 부분에서 괄호는 생략 가능하다. 이것은 다른 제어문들도 마찬가지이다


- while 문

while ( condition )

  body

endwhile;

 

- for 문

다른 언어들과 마찬가지로 옥타브도 while문과 for문의 용도가 다르다.

for var = expression  #expression은 순회 가능한 리스트

  body

endfor


for i = 1:10

  disp(i);

endfor



'Programming' 카테고리의 다른 글

내가 사용하는 Makefile  (0) 2012.11.27
[Octave - 필요한 것만 배우기] 1. 기초 문법  (0) 2012.09.12
C++에서 UTF-8 사용  (3) 2012.09.06
log(1+x) 계산하기  (1) 2012.07.27

WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  0개가 달렸습니다.
secret

자바나 파이썬에 비하면 C++의 UTF-8 지원은 미약한 것이 사실이다. 하지만, locale만 잘 설정하면 wstirng, win, wout, wifstream 등을 이용해 UTF-8 데이터를 쉽게 사용할 수 있다.

 

전체 locale 설정하기

한글(과 중국어)가 포함된 UTF-8 데이터를 다룬다면 다음과 같이 설정해주는 것이 좋다. 그리고 소스코드 역시 UTF-8 로 인코딩 되게 하는 것이 좋다. 다시 말하면, 프로그래밍할 때 사용하는 에디터도 UTF-8로 설정할 것을 권장한다.


#include <locale>

int main(void)

{

     std::locale::global(std::locale("ko_KR.UTF-8")); // 맨 처음 한번 실행

     ...

}



부분적으로 locale 설정하기

어떤 이유로 인해 특정 부분에서만 화면 입출력을 UTF-8로 하고자 한다면 다음과 같이 설정한 다음에 사용한다.

wcout.imbue(std::locale("ko_KR.UTF-8"));

win.imbue(std::locale("ko_KR.UTF-8"));



문자열 상수 입력

UTF-8 문자열 상수임을 표시하기 위해서는 왼쪽 큰따옴표 앞에 'L'을 붙여주면 된다.


wstring hangul = L"한글";

wstring chinese = L"葡萄酒跳蚤";

assert ( hangul[1] == L'글' ); // 두번째 글자는 '글'

assert ( chinese[0] == L'葡'); // 첫번째 글자는 '葡' 



파일 읽기, 쓰기

wifstream과 wstring을 이용하면 UTF-8로 인코딩된 파일을 읽을 수 있다.

wstring buff;

wifstream ifs("utf8_file.txt");

while ( getline(ifs, buff) ) { // 파일을 처음부터 끝까지 한줄씩 (마지막 \n 은 자동으로 제거됨) 읽음.

do_something();





'Programming' 카테고리의 다른 글

[Octave - 필요한 것만 배우기] 1. 기초 문법  (0) 2012.09.12
C++에서 UTF-8 사용  (3) 2012.09.06
log(1+x) 계산하기  (1) 2012.07.27
date 명령어[Linux/Unix] 사용  (0) 2012.07.26

WRITTEN BY
trowind
자연어처리, 프로그래밍, 여행, 음식, 삶의 기록

트랙백  0 , 댓글  3개가 달렸습니다.
  1. 오 내 세상 가득 빛을 내리고, http://lol.holtzingerfruit.com/styles/af.asp abercrombie
  2. 창밖을 봐 바람에 나뭇가지가 살며시 흔들리면 네가 사랑하는 사람이 널 사랑하고 있는거야.
  3. "ko_KR.UTF-8"로 하면 런타임 에러가 나는군요. 어떻게 설정을 해줘야 하는거죠?
secret