이번 글에서는 토큰 분석기, Lexer를 파이썬을 이용해 만들어 보는 내용을 담았습니다.

이 글은 https://riptutorial.com/en/python/example/31584을 참고하여 작성하였습니다.

 

Python Language - Part 1: Tokenizing Input with Lex | python Tutorial

python documentation: Part 1: Tokenizing Input with Lex

riptutorial.com


먼저 ply 모듈에 대한 설치가 필요합니다.

conda나 pip 명령어를 활용하여, ply 모듈을 다운로드하여 주세요.

다운로드가 잘 되어있는지 확인은

import ply.lex 를 통해 확인하실 수 있습니다.

 


Code

전체적인 코드입니다.

import ply.lex as lex

tokens = [  # 사용되는 토큰 정의하기, 필수
    'NUMBER',
    'PLUS',
    'MINUS',
    'TIMES',
    'DIVIDE',
    'LPAREN',
    'RPAREN',
]  # 토큰 별로 정규식이 정의되어야 한다 함수를 통해서 혹은 단순히 아래처럼, 둘 다 규칙은 t_이름

t_PLUS = r'\+'
t_MINUS = r'-'
t_TIMES = r'\*'
t_DIVIDE = r'/'
t_LPAREN = r'\('
t_RPAREN = r'\)'


# 값 변환 등 액션이 필요한 토큰은 함수로 정의한다.
def t_NUMBER(t):  # t가 인스턴스 토큰
    r'\d+'
    t.value = int(t.value)
    return t


def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)


t_ignore = ' \t'


def t_error(t):
    print("Illegal character '%s'" % t.value[0])
    t.lexer.skip(1)


lexer = lex.lex()


while True:
    tok = lexer.token()
    if not tok:
        break  # No more input
    print(tok)

 

1) tokens 라는 리스트로 사용할 토큰들을 정의하는 과정이 필요합니다.

위 코드에서는 수식을 토큰들로 나누는 예제입니다.

 

2) 토큰들을 정의한 후, 각각의 토큰들이 어떤 형식으로 표현되는 토큰들인지를 정의해야 합니다.

어떤 토큰을 정의하는 것인지를 표현할 때는 < t_토큰 이름 > 형식으로 사용하시면 됩니다.

간단하게 정의를 할 때는 아래와 같이 정의할 수 있습니다. 표현은 정규식의 형태로 되어 있습니다.

t_PLUS = r'\+'
t_MINUS = r'-'
t_TIMES = r'\*'
t_DIVIDE = r'/'
t_LPAREN = r'\('
t_RPAREN = r'\)'

 

토큰을 표현하면서 추가적인 작업이 필요한 경우에는 함수로 구현할 수 있습니다.

def t_NUMBER(t):  # t가 인스턴스 토큰
    r'\d+'
    t.value = int(t.value)
    return t

함수 정의 맨 윗줄의 정규식이 토큰의 표현식이 됩니다. 이 함수의 매개변수로 들어오는 t는 인스턴스 토큰입니다. 위 같은 경우에는 숫자 자료형으로 변환시켜주기 위해서 함수로 정의를 했습니다.

 

t는 다음과 같은 속성값들을 가지고 있습니다.

1) t.type : 토큰의 타입을 의미합니다. 기본값으로는 t_ 전두어 다음으로 들어오는 이름입니다.

2) t.value : 실제 텍스트 값을 의미합니다.

3) t.lineno : 몇번째 줄인지를 표시하는 값인데, lexer 자체로는 라인을 알 수 없기 때문에 아래와 같은 함수를 정의하면 됩니다.

def t_newline(t):
      r'\n+'
      t.lexer.lineno += len(t.value)

4) t.lexpos : 들어오는 글의 시작부터 상대적인 위치를 나타냅니다.

 

 

만약에 무시하고 싶은 토큰이라면 아래 두 가지 방법 중 한 가지를 쓰면 됩니다.

def t_COMMENT(t):
       r'\#.*'
       pass
t_ignore_COMMENT = r'\#.*'

 

함수로 정의할 땐 반환값을 적지 않으면 됩니다. 혹은 < t_ignore_토큰 이름 > 으로 더 간편하게 정의할 수 있습니다. 딱히 토큰의 이름을 정의하지 않고도 무시하고 싶다면 < t_ignore > 로 정의할 수 있습니다.

 t_ignore  = ' \t'    # 스페이스와 탭을 무시하게 됩니다.

 

 

Literals : t.type 과 t.value가 문자 자체로 표현되는 토큰입니다. 

literals = [ '+', '-', '*', '/' ]
literals = "+-*/"

 

윗 줄과 아랫 줄은 같은 의미입니다. Literal 또한 함수로 정의하여 추가적인 작업을 하게끔 할 수 있습니다.  

 

literals = [ '{', '}' ]

def t_lbrace(t):
    r'\{'
    t.type = '{'  # 토큰 타입을 매칭되는 Literal로 설정합니다. (Literal이라면 꼭 필요합니다.)
    return t

 

에러는 t_error 함수로 다룰 수 있습니다. 

def t_error(t):
    print("옳지 않은 문자 '%s'" % t.value[0])
    t.lexer.skip(1) # 몇개의 문자를 넘어가는 함수입니다.

 

모든 준비가 끝나셨으면 lexer를 만드시면 됩니다.

lexer = lex.lex()

 

이런 지금까지의 준비 코드는 임의의 class 안에 정의하여 사용할 수도 있습니다. 

import ply.lex as lex  
 class MyLexer(object):            
       ...     # 위와 같은 내용들을 적습니다.

       # Build the lexer
       def build(self, **kwargs):
           self.lexer = lex.lex(module=self, **kwargs)

       def test(self, data):
           self.lexer.input(data)  # 데이터를 lexer에 넣는 함수입니다.
           for token in self.lexer.token():  # 토큰들을 확인할 수 있습니다.
               print(token)


 m = MyLexer()
 m.build() 
 m.test("3 + 4")  

 

추가적으로 반복문을 활용해서 분리해낸 토큰들을 얻을 수도 있습니다.

for i in lexer: 
    print(i)

 

 

다음 글로는 Lex와 연계되어 자주 사용되는 Yacc 모듈에 대해 알아보겠습니다.

결정트리 - Decision Tree

 스무고개 많이들 해보셨을 텐데요. 결정 트리는 이와 비슷하게 의사를 결정하는 알고리즘 구조입니다.

 지도학습(Supervisior Learning)* 분류에 속하는 이 알고리즘은 매우 직관적으로 데이터들을 어떤 식으로 분류하는 확인할 수 있습니다. 간단한 예시를 들어보겠습니다.

 

Q) 젊은 친구 John은 이런 날 나가서 놀까요?

 위의 질문에 나올 수 있는 답은 O, X 둘 중 하나라고 해보겠습니다. 

 그리고 이 답을 유추하기 위한 속성 값(Attribute)들이 주어집니다. 예를 들어, 

1) 비가 온다 / 오지 않는다 (Boolean Attribute)

2) 온도가 X 이다. (실수 Attribute)

3) 같이 놀 친구가 X명 있다. (정수 Attribute)

 위와 같은 속성값들은 예시이며, 그 외에도 다양하게 존재할 수 있습니다. 

간단한 결정트리 예시

 결정트리결정 트리 모델은 위 그림처럼 특징들을 이용해 분류를 하는 모델입니다. 분기를 어떤 기준으로 설정할 것이고, 어떤 기준을 가장 위에 놓을 것이냐가 결정 트리에서는 중요한 문제가 됩니다. 이를 선택하기 위한 기준으로 엔트로피라는 개념을 사용하는데, 쉽게 설명드리자면 상황마다 가장 깔끔하게 나눌 수 있는 기준을 찾아서 설정한다고 보시면 됩니다.

 결정트리에 대한 간략한 설명은 여기서 마치겠습니다.

 


* 지도학습(Supervisior Learning) - 머신러닝의 한 분야로써, 훈련데이터를 이용해 머신러닝 모델이 Input X가 주어질 때 Output Y를 출력하게끔 합니다. 분류(Classification), 회귀(Regression) 등의 세부 분야로 나뉩니다.

 

UCI Repository 살펴보기

https://archive.ics.uci.edu/ml/index.php

 UCI repository는 머신러닝과 지능시스템을 위한 여러 데이터들을 저장하고 있는 저장소입니다. 

UCI repository 홈 화면

 여러 데이터셋을 무료로 사용이 가능하며, 그중 monk's problem dataset을 살펴보겠습니다.

데이터셋에 대한 설명이 나옵니다.

 데이터셋에 대한 설명은 Attribute Information 부분에 되어있습니다. 분류(Classification) 문제에 사용하기 위한 클래스와 속성(Attributes)에 대해 간략하게 나타나 있습니다.

monks-problems 다운로드 페이지

 monks-1에 해당하는 test와 train을 다운로드합니다. 여기서 test는 결정 트리 모델이 잘 만들어졌는지 시험하기 위한 데이터셋이고, train은 결정트리 모델을 만들기 위해 필요한 데이터셋을 의미합니다. 데이터셋에 따라 위와 다르게 하나의 파일만 있는 경우도 있습니다. 

train 파일의 내용

 파일은 이전의 Attribute Information에서 설명한 내용들이 순서대로 띄어쓰기로 구분되어 작성되어 있습니다. 이를 파이썬에서 읽어 들여서 결정 트리를 만들어보겠습니다. 파일에 따라 쉼표나 다른 무언가로 구분되어 있는 경우도 있습니다.

 


Python with skLearn

1. 데이터 파일 읽기

 먼저 데이터 파일을 읽는 작업이 첫 번째로 이루어져야 할 작업입니다. 학습을 위한 train 데이터와 테스트를 위한 test 데이터로 구분하여 파일을 읽었습니다. 파일을 읽는 작업은 pandas의 read_csv 함수를 이용하였습니다.

 * 모듈들의 다운로드는 Anaconda 환경이라고 하면, conda instasll numpy 이런 식으로 가능합니다.

 

 

2. 데이터를 속성과 클래스로 구분하기

  머신러닝 모델은 함수에 비교되곤 하는데 함수처럼 속성 등 모델에 Input으로 들어가게 되는 값을 x, Input을 이용하여  모델이 출력해야 할 클래스 값을 y로 구분하였습니다. 위 과정을 통해 클래스와 속성이 섞여있는 데이터가 깔끔하게 분리되는 것을 확인할 수 있습니다.

 

 

3. 결정 트리 만들기 및 학습시키기

 결정 트리는 이미 사이킷런 라이브러리에 존재하고 있어서 쉽게 만들 수 있습니다. 미리 준비해둔 train 데이터를 통해 fit 함수를 통하여 결정 트리를 학습시킬 수 있습니다. 학습이 끝난 뒤 score 함수를 통해 x가 주어질 때 y를 얼마나 잘 출력해 내는지 확인할 수 있습니다.

 

score 결과

 위 값만 보고서는 결정 트리가 잘 만들어진 것인지에 대해 의문을 품을 수 있습니다. 이를 위해 시각적으로 결정 트리를 확인할 수 있는데, 이를 도와줄 프로그램이 추가적으로 설치가 필요합니다. 바로 GraphViz입니다.

msi 파일을 클릭하여 다운로드합니다

 msi 파일을 클릭하여 다운로드를 하시면 됩니다. 이 프로그램은 그래프 시각화에 필요한 소프트웨어입니다.

 

 

4. 트리 시각화하기

 설치한 GraphViz 프로그램을 환경변수 PATH에 넣어줍니다. 보통 설치하실 때 추가적으로 설정하신 부분이 없다면 저 위치에 존재하고 있습니다. 위 코드를 실행하고 나면 dt.png 파일이 경로에 생성되게 됩니다. 

 * 만약 위처럼 코드를 실행하였을 때, dot 파일을 찾을 수 없다고 뜨는 경우에는 graphviz 모듈을 다운로드해보시길 바랍니다. Anaconda 환경 기준으로 conda install graphviz 로 다운로드 할 수 있습니다.  

 

dt.png

 

 이상으로 간략하게 데이터셋을 이용한 머신러닝, 그중에서도 결정 트리를 생성했습니다. 이와 비슷하게 다른 데이터셋에 대해서도 적용하실 수 있을 것입니다. 다만 UCI에는 정말 다양한 데이터가 존재하기에 이처럼 결정 트리를 생성하기에는 조금 부적절한 데이터도 있을 수 있습니다.

 

 


 * 공부하며 작성하는 글이기에, 틀린 점이나 지적할 점이 있으시다면 댓글로 알려주시면 감사하겠습니다.

'AI > 머신러닝' 카테고리의 다른 글

[파이썬][ply.lex] 토큰 분석을 위한 Lexer 튜토리얼  (0) 2019.10.11

+ Recent posts