You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
문장 내에서 형태소를 기반으로 한 특정 패턴을 추출하거나, 일치 여부를 판정하거나, 일부 형태소를 다른 형태소로 교체해야하는 작업을 하는 경우 조건문을 이용하는 수 밖에 없는데, 이 형태소 조건들이 복잡해질 경우 이 작업이 굉장히 고단해지는 문제가 있음.
예를 들어 숫자(SN) 뒤에 의존 명사(NNB)가 오고, 그 뒤에 주격 조사(JKS) 혹은 목적격 조사(JKO)가 나오는 문자열을 탐색한다고 하면
와 같이 장황하게 조건을 나열해야 한다. 게다가 이는 최적화된 탐색 알고리즘을 사용하기도 어려우므로 대량의 텍스트 내에서 탐색을 수행시 비효율적인 문제까지도 있음. 탐색 후 치환의 경우는 훨씬 더 복잡해지는 문제가 있다.
제안
문자열 일치/탐색/치환에 널리 쓰이는 정규표현식 문법을 형태소 탐색용으로 개량하여 사용한다. 그리고 Python3의 표준 정규표현식 모듈에서 제공하는 re.match, re.search, re.sub와 유사한 함수를 제공하여, 한국어 텍스트를 형태소 기반으로 일치/탐색/치환할 수 있도록 한다.
pattern=kiwi.Pattern(r"(/NN) /JKO (/VV /EF /SF?)") # re.compile과 유사하게 패턴을 미리 컴파일하여 최적화한다.m=pattern .match("밥을 먹어요?") # 일치 시 Match object 반환, 불일치 시 None 반환m.group() # "밥을 먹어요?" (일치된 전체 텍스트)m.group(1) # "밥" (첫번째 괄호로 지정된 텍스트)m.group(2) # "먹어요?" (두번째 괄호로 지정된 텍스트)m.span() # (0, 7) (일치된 전체 텍스트 영역)m.span(1) # (0, 1) (첫번째 괄호로 지정된 텍스트 영역)m.span(2) # (3, 7) (두번째 괄호로 지정된 텍스트 영역)m.token() # [Token(form='밥', tag='NNG', start=0, len=1), ..., Token(form='?', tag='SF', start=6, len=1)] (일치된 전체 텍스트의 형태소 목록)m.token(1) # [Token(form='밥', tag='NNG', start=0, len=1)] (첫번째 괄호로 지정된 텍스트의 형태소 목록)m.token(2) # [Token(form='먹', tag='VV', start=3, len=1), ..., Token(form='?', tag='SF', start=6, len=1)] (두번째 괄호로 지정된 텍스트의 형태소 목록)m=pattern.search("저도 밥을 먹어요?") # re.match 와 re.search의 관계와 동일. pattern=kiwi.Pattern(r"(/VV) /EP (/EF)")
result=pattern.sub(r"\1 \2", "길을 걸었다.") # \1과 \2는 각각 패턴 내의 첫번째, 두번째 괄호와 일치.# result: "길을 걷다." (걸었다->걷다 로 치환됨)result=pattern.sub(r"\1 \2", "옷을 걸었다.")
# result: "옷을 걸다." (걸었다->걸다 로 치환됨)pattern=kiwi.Pattern(r"/NNG")
result=pattern.sub(r"바다/NNG", "길을 걸었다.")
# result: "바다를 걸었다." (길->바다 로 치환됨, 이에 따라 뒤의 조사도 함께 변환됨)
형태소용으로 개량된 정규표현식
기본적으로 표현식은 각각의 형태소를 표현하기 위해 쓰이고, 문자에 대한 일치 여부는 되도록 하지 않는 것을 원칙으로 한다.
/품사태그 : 품사태그는 앞글자만 사용하고 뒷글자는 생략할 수 있다. 예를 들어 /NN은 일반명사(NNG), 고유명사(NNP), 의존명사(NNB)에 모두 일치할 수 있다. 마찬가지로 모든 동사/형용사를 지칭하는 데에는 /V, 모든 접미사에는 /XS를 사용하는 식으로 응용이 가능하다. 추가로 아무 태그도 명시하지 않은 /의 경우 모든 품사의 형태소와 일치할 수 있다.
형태/품사태그: 구체적으로 특정 형태소를 명시하기 위해서는 / 앞에 형태를 지정할 수 있다. 바다/NNG는 일반명사인 바다만을 지칭한다. 마찬가지로 형태가 바다인 모든 형태소를 가리키기 위해서 바다/와 같이 쓸 수 있다.
//SP: 문자 /는 형태소 상으로는 구두점(SP) 품사에 속하므로, / 문자 그 자체를 지칭하기 위해서는 //SP라고 표기한다.
연속된 형태소: 여러 형태소가 연속하는 것을 표현하기 위해서는 공백을 두고 각 형태소 표기를 연결한다. 즉, 명사 뒤에 조사가 오는 경우 /NN /J와 같이 쓸 수 있다. 여기서 공백은 형태소 사이를 구분하는 역할만 수행하며 실제 문자열 상의 공백과 일치하지는 않는다! 따라서 /NN /J나 /NN /J나 동일하게 연속하는 명사-조사 패턴을 가리킨다.
형태소 수량자: 정규표현식의 수량자 *, +, ?를 지원한다. 단, 이는 형태소를 수식하는데에 쓰인다. 즉 /NNG*의 경우 일반 정규표현식에서마냥 /NN, /NNG, /NNGG와 일치하는 것이 아니라 , /NNG, /NNG /NNG, /NNG /NNG /NNG 등과 일치한다. 나머지 수량자도 마찬가지.
[]: 일반 정규표현식과 마찬가지로 문자집합을 지원한다. 단, 형태나 품사태그 위치에서만 쓰일 수 있다. 예를 들어 /V[VA]는 동사와 형용사만을 지칭하고, [은는]/JX는 보조사 중 은과 는만을 지칭한다.
|: 여러 분기 중 하나와 일치하는 경우를 나타낸다. 즉 /NNG | /VV는 일반명사 혹은 동사 하나와 일치한다. 정규표현식과 마찬가지로 우선순위가 제일 낮다. /NNG | /VV+는 일반명사 오직 하나, 혹은 동사 하나 이상과 일치한다. 형태 내에서는 쓰일 수 없다.
,: 형태 내의 분기를 나타내기 위해 쓰인다. 품사 태그에는 쓰일 수 없다. 하늘,땅/NNG는 일반명사 중 하늘 혹은 땅 중 하나와 일치한다.
.: 형태 내의 글자 하나와 일치한다. 품사 태그에는 쓰일 수 없다. 가./NNG는 가로 시작하는 두 글자 일반명사 전부와 일치한다.
형태 내 수량자: *, +, ?를 형태 내에서도 사용할 수 있다. 예를 들어, 가.+미/NNG의 경우 가로 시작하고 미로 끝나는 세 글자 이상의 모든 일반명사와 일치한다. 또 얼마나?/의 경우 형태가 얼마이거나 얼마나인 모든 형태소와 일치한다.
(): 괄호는 정규표현식과 마찬가지로 캡처그룹을 지정하고, 우선순위를 조절하기 위해 사용된다. 단 형태 내에서는 쓰일 수 없고, 형태소 간에서만 쓰일 수 있다. /VV (/EF | /EC)는 /VV /E[FC]와 동일하다. (/NN /J)+는 명사-조사가 연속하여 여러번 등장하는 패턴(명사-조사, 명사-조사-명사-조사 등)을 나타낸다.
구현
Python쪽에서 쉽게 구현하는 방법으로는, 형태소 분석 결과를 문자열로 직렬화하여 나타낸 다음, 위 형태소용 정규표현식을 적당히 변환하여 이 직렬화된 문자열과 일치시키는 것이 있다. 그러나 궁극적으로는 C++ 내부로 형태소용 정규표현식 일치 엔진을 가지고 들어가는게 성능 상에서 크게 유리할 듯하다. 특히 내부적으로 각 형태소는 고유 id로 변환되어 16~32bit int로 처리되므로, 위의 형태소용 정규표현식을 파싱하여 int 배열에 대한 DFA를 생성하면 의외로 쉽게 구현 가능할지도 모른다.
The text was updated successfully, but these errors were encountered:
문제 상황
문장 내에서 형태소를 기반으로 한 특정 패턴을 추출하거나, 일치 여부를 판정하거나, 일부 형태소를 다른 형태소로 교체해야하는 작업을 하는 경우 조건문을 이용하는 수 밖에 없는데, 이 형태소 조건들이 복잡해질 경우 이 작업이 굉장히 고단해지는 문제가 있음.
예를 들어 숫자(SN) 뒤에 의존 명사(NNB)가 오고, 그 뒤에 주격 조사(JKS) 혹은 목적격 조사(JKO)가 나오는 문자열을 탐색한다고 하면
와 같이 장황하게 조건을 나열해야 한다. 게다가 이는 최적화된 탐색 알고리즘을 사용하기도 어려우므로 대량의 텍스트 내에서 탐색을 수행시 비효율적인 문제까지도 있음. 탐색 후 치환의 경우는 훨씬 더 복잡해지는 문제가 있다.
제안
문자열 일치/탐색/치환에 널리 쓰이는 정규표현식 문법을 형태소 탐색용으로 개량하여 사용한다. 그리고 Python3의 표준 정규표현식 모듈에서 제공하는
re.match
,re.search
,re.sub
와 유사한 함수를 제공하여, 한국어 텍스트를 형태소 기반으로 일치/탐색/치환할 수 있도록 한다.형태소용으로 개량된 정규표현식
기본적으로 표현식은 각각의 형태소를 표현하기 위해 쓰이고, 문자에 대한 일치 여부는 되도록 하지 않는 것을 원칙으로 한다.
/품사태그
: 품사태그는 앞글자만 사용하고 뒷글자는 생략할 수 있다. 예를 들어/NN
은 일반명사(NNG), 고유명사(NNP), 의존명사(NNB)에 모두 일치할 수 있다. 마찬가지로 모든 동사/형용사를 지칭하는 데에는/V
, 모든 접미사에는/XS
를 사용하는 식으로 응용이 가능하다. 추가로 아무 태그도 명시하지 않은/
의 경우 모든 품사의 형태소와 일치할 수 있다.형태/품사태그
: 구체적으로 특정 형태소를 명시하기 위해서는/
앞에 형태를 지정할 수 있다.바다/NNG
는 일반명사인 바다만을 지칭한다. 마찬가지로 형태가 바다인 모든 형태소를 가리키기 위해서바다/
와 같이 쓸 수 있다.//SP
: 문자/
는 형태소 상으로는 구두점(SP) 품사에 속하므로,/
문자 그 자체를 지칭하기 위해서는//SP
라고 표기한다./NN /J
와 같이 쓸 수 있다. 여기서 공백은 형태소 사이를 구분하는 역할만 수행하며 실제 문자열 상의 공백과 일치하지는 않는다! 따라서/NN /J
나/NN /J
나 동일하게 연속하는 명사-조사 패턴을 가리킨다.*
,+
,?
를 지원한다. 단, 이는 형태소를 수식하는데에 쓰인다. 즉/NNG*
의 경우 일반 정규표현식에서마냥/NN
,/NNG
,/NNGG
와 일치하는 것이 아니라/NNG
,/NNG /NNG
,/NNG /NNG /NNG
등과 일치한다. 나머지 수량자도 마찬가지.[]
: 일반 정규표현식과 마찬가지로 문자집합을 지원한다. 단, 형태나 품사태그 위치에서만 쓰일 수 있다. 예를 들어/V[VA]
는 동사와 형용사만을 지칭하고,[은는]/JX
는 보조사 중은
과는
만을 지칭한다.|
: 여러 분기 중 하나와 일치하는 경우를 나타낸다. 즉/NNG | /VV
는 일반명사 혹은 동사 하나와 일치한다. 정규표현식과 마찬가지로 우선순위가 제일 낮다./NNG | /VV+
는 일반명사 오직 하나, 혹은 동사 하나 이상과 일치한다. 형태 내에서는 쓰일 수 없다.,
: 형태 내의 분기를 나타내기 위해 쓰인다. 품사 태그에는 쓰일 수 없다.하늘,땅/NNG
는 일반명사 중 하늘 혹은 땅 중 하나와 일치한다..
: 형태 내의 글자 하나와 일치한다. 품사 태그에는 쓰일 수 없다.가./NNG
는가
로 시작하는 두 글자 일반명사 전부와 일치한다.*
,+
,?
를 형태 내에서도 사용할 수 있다. 예를 들어,가.+미/NNG
의 경우가
로 시작하고미
로 끝나는 세 글자 이상의 모든 일반명사와 일치한다. 또얼마나?/
의 경우 형태가얼마
이거나얼마나
인 모든 형태소와 일치한다.()
: 괄호는 정규표현식과 마찬가지로 캡처그룹을 지정하고, 우선순위를 조절하기 위해 사용된다. 단 형태 내에서는 쓰일 수 없고, 형태소 간에서만 쓰일 수 있다./VV (/EF | /EC)
는/VV /E[FC]
와 동일하다.(/NN /J)+
는 명사-조사가 연속하여 여러번 등장하는 패턴(명사-조사, 명사-조사-명사-조사 등)을 나타낸다.구현
Python쪽에서 쉽게 구현하는 방법으로는, 형태소 분석 결과를 문자열로 직렬화하여 나타낸 다음, 위 형태소용 정규표현식을 적당히 변환하여 이 직렬화된 문자열과 일치시키는 것이 있다. 그러나 궁극적으로는 C++ 내부로 형태소용 정규표현식 일치 엔진을 가지고 들어가는게 성능 상에서 크게 유리할 듯하다. 특히 내부적으로 각 형태소는 고유 id로 변환되어 16~32bit int로 처리되므로, 위의 형태소용 정규표현식을 파싱하여 int 배열에 대한 DFA를 생성하면 의외로 쉽게 구현 가능할지도 모른다.
The text was updated successfully, but these errors were encountered: