728x90
SMALL
- 오늘은 자연어 처리(NLP)의 첫 시작을 알리며, 그를 위한 준비와 텍스트 전처리 과정의 일부(토큰화, 정제, 정규화 등)를 배우게 되었습니다.
🌳 자연어 처리 전 준비 단계
🌱 자연어 처리는?
- 자연어는 일상 생활에서 사용하는 언어를 의미하고, 그래서 자연어 처리(NLP)는 이 자연어의 의미를 분석해 컴퓨터가 이해하고 처리할 수 있도록 하는 기술을 의미합니다.
- 이 자연어 처리를 활용할 수 있는 분야는 엄청나게 다양한데 음성 인식, 내용 요약, 번역, 감성 분석, 텍스트 분류 (예를 들면, 스팸 메일이나 뉴스 기사 카테고리 분류하는 등), 질의 응답 시스템, 챗봇 등과 같은 분야에서 주로 사용되고 있습니다.
⚙️ 머신 러닝 실습 환경 세팅하기
🐍 아나콘다와 구글 코랩
- 아나콘다는 Python의 배포판으로, Numpy, Pandas, Jupyter Notebook, IPython, scikit-learn, matplotlib, seaborn, nltk과 같은 대부분의 패키지를 포함하고 있는 녀석입니다.
- 설치는 아나콘다 배포판에서 다운로드할 수 있습니다!
- 이 아나콘다가 이미 설치되어 있는데, 업데이트를 하고 싶으시면, 아래 명령어를 프롬프트에 입력하시면 되겠습니다.
conda update -n base conda
conda update --all
- 구글 코랩(Colab)은 주피터 노트북과 유사한 웹 기반 실습 환경으로, Tensorflow 등 64비트 플랫폼을 지원합니다.
- Google Colab에 직접 접속하거나 구글 검색창에 입력해보시면 바로 나올 겁니다!
🌱 주요 프레임워크와 라이브러리들 설치하기
💡 pip로 별도 설치해야 하는 것들 (아나콘다에 포함되지 않은 경우)
- Tensorflow: Google에서 공개한 머신러닝(ML) 및 딥러닝(DL) 오픈소스 라이브러리입니다. 설치는 아래와 같이 진행하시면 됩니다.
pip install tensorflow
import tensorflow as tf
print(tf.version)
- 이때, Python 버전과 호환되는 Tensorflow 버전을 반드시 확인하시고 설치하는 것이 가장 중요합니다! (Python 3.9 사용 시에는 Tensorflow 2.5 버전 이상 필요 – TensorFlow 설치 가이드 참고)
- 케라스(Keras): Tensorflow에 대한 추상화 API로, 딥러닝 코드를 간단하게 작성할 수 있게 해줍니다. (Tensorflow 내에서는 tf.keras, 순수 케라스는 keras로 표기하면 됩니다.)
pip install keras
import keras
print(keras.__version__)
- 젠심(Gensim): 머신러닝을 이용한 토픽 모델링이나 Word2Vec과 같은 다양한 자연어 처리 모델 학습을 지원해줍니다.
pip install gensim
import gensim
print(gensim.__version__)
- 사이킷런(Scikit-learn): 나이브 베이즈, 서포트 벡터 머신(SVM) 등 다양한 머신러닝 알고리즘을 제공해줍니다. 이 skikit-learn은 아나콘다에 기본적으로 포함되어 있는 경우도 많습니다.
pip install scikit-learn
import sklearn
print(sklearn.__version__)
🌱 추가적인 도구도 설치!
💡 NLTK(Natural Language Toolkit)
- 파이썬에서 자연어 처리를 쉽게 할 수 있도록 도와주는 라이브러리입니다.
pip install nltk
import nltk
nltk.download()
- 만약에 특정 NLTK Data(예를 들면 'treebank')가 없을 경우에는 아래와 같이 nltk에서 직접 다운로드할 수 있습니다. 설치 관련해서 오류가 발생한다면, NLTK Data GitHub에서 수동으로도 설치할 수 있습니다.
nltk.download('treebank')
💡 KoNLPy
- 한국어는 형태소 분석이 중요하기 때문에, KoNLPy는 다양한 형태소 분석기(Tokenizer)를 제공하는 것이 특징입니다.
pip install konlpy
import konlpy
print(konlpy.__version__)
- Windows에서 설치할 때, Windows 환경 오류를 해결하려면, JDK와 JPype을 설치해야 하는데,
- 우선 JDK는 1.7 버전 이상이 필요(Oracle JDK 다운로드)하고, JAVA와 Python을 연결해주는 JPype도 설치해주어야 합니다.
- JPype를 설치할 때는 OS와 Python 버전에 맞는 파일을 다운로드해주어야 하는데,
- 예를 들어서 Windows 64비트, Python 3.12 버전의 경우에는, Python과 Java의 비트(32비트/64비트)가 일치해야 하며, 불일치 시 오류가 발생할 수 있습니다.
pip install JPype1-1.5.2-cp312-cp312m-win_amd64.whl
- 가장 확실한 해결책은 기존 Java를 제거하고 최신 버전(JRE, JDK)으로 재설치하는 것입니다.
🌊 간단한 ML Workflow
- 수집(Acquisition)
- 머신러닝에 필요한 데이터를 확보하는 단계로, 텍스트, 음성, 영화 리뷰 등 다양한 소스에서부터 데이터를 수집합니다.
- 데이터 파일 형식은 txt, csv, xml 등으로 다양합니다.
- 점검 및 탐색 (Inspection and Exploration)
- 수집된 데이터의 구조와 특성을 확인하는 단계입니다.
- 노이즈 데이터, 변수 유형, 데이터 타입 등을 점검하고, 탐색적 데이터 분석(EDA)을 통해 시각화 및 간단한 통계 테스트를 진행합니다.
- 전처리 및 정제 (Preprocessing and Cleaning)
- 데이터를 머신러닝에 적합하게 정제하는 단계입니다.
- 자연어 처리의 경우에는 토큰화, 정제, 정규화, 불용어 제거 등의 작업을 수행하고, 복잡한 전처리의 경우에는 특정 머신러닝 기법을 활용할 수 있습니다.
- 모델링 및 훈련 (Modeling and Training)
- 적절한 머신러닝 알고리즘을 선택해서 모델을 설계하는 단계입니다.
- 데이터를 훈련용, 검증용, 테스트용으로 분리하여 사용합니다.
- 훈련용 데이터로 모델을 학습시키고, 검증용 데이터로 모델 성능을 개선하며, 테스트용 데이터로 최종 성능을 평가합니다.
- 학습이 완료되면 기계 번역이나 음성 인식, 텍스트 분류 등의 다양한 작업을 진행할 수 있습니다.
- 평가(Evaluation)
- 테스트 데이터를 사용해서 모델의 예측 성능을 측정하는 단계입니다.
- 실제 정답과의 비교를 통해 모델의 정확도 및 과적합 여부를 평가합니다.
- 배포(Deployment)
- 평가 결과가 만족스러우면, 모델을 실제 환경에 배포합니다.
- 배포 후 피드백을 받아 필요한 경우에는 모델을 업데이트하기 위해 다시 수집 단계로 돌아갈 수도 있습니다.
📜 텍스트 전처리
- 텍스트 전처리는 원본 텍스트(코퍼스)를 데이터 분석이나 머신러닝, 자연어 처리(NLP) 등과 같이 원하는 목적에 맞게 가공하는 과정입니다. 주요 단계는 다음과 같습니다.
- 토큰화 (Tokenization): 텍스트를 의미 있는 단위(단어, 문장, 또는 형태소 등)로 분리하는 과정
- 정제(Cleaning) 및 정규화(Normalization): 불필요한 기호 제거, 대소문자 통일, 어간/표제어 추출 등을 통해 텍스트를 정리하는 과정
- 불용어 제거(Stopwords Removal): 분석에 크게 기여하지 않는 단어(관사, 접속사 등)를 제거하는 과정
- 인코딩(Encoding) 및 패딩(Padding): 텍스트를 모델이 이해할 수 있는 숫자 형태나 일정 길이로 변환하는 과정(정수나 원-핫 인코딩 등 포함)
- 데이터 분리(Splitting Data): 학습, 검증, 테스트 데이터셋으로 나누는 과정
- 특히, 위 과정들은 언어별 특성에 따라서 추가적인 전처리 기법이 필요합니다.
✨ 토큰화 (Tokenization)
- 토큰화는 텍스트를 일정 기준으로 나누어 분석에 적합한 단위(토큰)로 만드는 작업입니다.
- 언어마다 토큰을 정의하는 기준이 다르며, 영어는 띄어쓰기가 명확하지만 한국어는 교착어의 특성으로 인해 단순 띄어쓰기만으로는 한계가 있습니다.
💡 영단어 토큰화하기
- word_tokenize (NLTK)
- 축약형(Don't)을 'Do'와 n't 등으로 분리하고, 구두점도 별도의 토큰으로 인식합니다.
- ['Do', "n't", 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name']
- WordPunctTokenizer (NLTK)
- 구두점이나 특수문자를 별도의 토큰으로 분리하여 처리합니다.
- ['Don', "'", 't', 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name']
- text_to_word_sequence (TensorFlow)
- 띄어쓰기를 기준으로 분리하고, 소문자화 등 간단한 전처리 과정을 포함합니다.
- ["don't", 'be', 'fooled', 'by', 'the', 'dark', 'sounding', 'name']
- TreebankWordTokenizer (Penn Treebank 규칙)
- 하이픈 단어는 하나로 유지하고, 축약형(doesn't)은 분리하는 등 세밀한 규칙을 적용합니다.
- ['Starting', 'a', 'home-based', 'restaurant', 'may', 'be', 'an', 'ideal.', 'it', 'does', "n't", 'have', 'a', 'food']
💡 영어 문장 토큰화하기
- sent_tokenize (NLTK)
- 마침표, 물음표, 느낌표 등 문장 경계에 해당하는 구두점을 기준으로 전체 텍스트를 문장 단위로 분리합니다.
- ['His barber kept his word.', 'But keeping such a huge secret to himself was driving him crazy.']
- ['I am actively looking for Ph.D. students.', 'and you are a Ph.D student.']
⚠️ 토큰화에서 고려해야 할 사항
- 토큰화를 수행할 때 단순히 띄어쓰기로 나누거나 모든 구두점을 제거하는 것만으로는 충분하지 않습니다.
- 구두점 및 특수문자 처리
- 구두점(마침표, 쉼표 등)은 문장의 경계를 파악하는 데 중요한 역할을 하며, 단어 내부에 포함된 경우 의미 전달에 기여할 수 있습니다.
- 'AT&T'나 'vs.' 와 같이 단어 자체에 포함된 구두점이 있는 경우에도 그렇고,
- 달러($), 슬래시(/) 등의 특수문자도 단순 제거하지 않고 상황에 따라 별도 토큰으로 처리하거나 보존해야 합니다.
- 또한, 숫자에 포함된 콤마(1,000 등)는 제거하면 의미가 달라질 수 있으므로 주의해야 합니다.
- 줄임말 및 단어 내 띄어쓰기
- 영어에서는 아포스트로피(')가 단어 축약을 표현하는데, "don't"를 "do"와 "n't"로 분리하거나 도메인에 따라 하나의 토큰으로 인식할 수 있어야 합니다.
- "rock 'n' roll"과 같이 단어 내 띄어쓰기가 있는 경우, 올바른 단일 단어 인식이 중요합니다.
- 토큰화 기준은 최종 분석 목적에 따라 달라질 수 있으므로, 위 사항들을 반영해서 적절한 방법을 선택하는 것이 좋을 듯 합니다.
✨ 한국어의 토큰화
- 한국어에서는 단어를 띄어쓰기 단위인 어절로 구분하지만, 영어와 달리 교착어의 특성을 가집니다.
- 여기서 교착어란 조사나 어미 같은 문법 요소들이 단어에 붙어 표현되는 언어를 말합니다.
- 예를 들어, '그'라는 기본 단어에 '가', '에게', '를', '와' 등의 여러 조사가 붙어서 '그가', '그에게', '그를', '그와' 등으로 변형됩니다.
- 이런 특성 때문에 단순히 띄어쓰기만으로는 같은 의미의 단어를 올바르게 인식하기 어렵고,
- 한국어 자연어 처리에서는 조사를 분리하는 형태소 토큰화가 필요합니다.
- 여기서 또 형태소는 뭔지... (고등학교 국어 수업으로 돌아간 느낌쓰)
- 형태소(Morpheme)라는 것은 더 이상 분해할 수 없는 최소의 의미 단위입니다. 여기서 형태소는 자립과 의존 형태소로 나누어지는데,
- 자립 형태소는 접사, 어미, 조사와 관계없이 단독으로 의미를 전달할 수 있는 형태소(체언-명사, 대명사, 수사, 수식언-관형사, 부사, 감탄사 등 존재)이고,
- 의존 형태소는 다른 형태소와 결합하여 의미를 보완하는 형태소(접사, 어미, 조사, 어간 등)를 말합니다.
- 결론적으로 한국어 전처리에서는 단순 어절 토큰화가 아닌, 형태소 분석을 통해서 각 구성 요소를 분리해야 하는 게 필요합니다.
- 마지막으로 띄어쓰기 문제도 있는데, 한국어는 '모아쓰기' 방식이기 때문에, 띄어쓰기가 정확히 지켜지지 않는 경우가 많습니다.
- 그래서 자연어 처리 시에 단순 띄어쓰기 기준으로 토큰화하면 오히려 노이즈가 발생할 수 있습니다.
💡 품사 태깅 (Part-of-Speech Tagging)
- 단어의 의미는 문맥에 따라 달라질 수도 있습니다.
- 쉬운 예로, '못'은 명사로 쓰이면 금속제 도구를 의미하지만, 부사로 쓰이면 '할 수 없음'을 나타내기도 한답니다.
- 따라서 토큰화 과정에서 각 단어 또는 형태소가 어떤 품사로 쓰였는지 구분하는 것이 중요합니다.
- 한국어에서는 KoNLPy의 Okt와 Kkma 도구를 사용해서 형태소 분석과 함께 각 토큰의 품사를 태깅할 수 있습니다.
- "열심히 코딩한 당신, 연휴에는 여행을 가보아요."라는 문장이 있다고 치면,
- OKT 품사 태깅에서는 [('열심히', 'Adverb'), ('코딩', 'Noun'), ('한', 'Josa'), ('당신', 'Noun'), (',', 'Punctuation'), ('연휴', 'Noun'), ('에는', 'Josa'), ('여행', 'Noun'), ('을', 'Josa'), ('가보아요', 'Verb'), ('.', 'Punctuation')]처럼 태깅이 되고,
- Kkma 품사 태깅에서는 [('열심히', 'MAG'), ('코딩', 'NNG'), ('하', 'XSV'), ('ㄴ', 'ETD'), ('당신', 'NP'), (',', 'SP'), ('연휴', 'NNG'), ('에', 'JKM'), ('는', 'JX'), ('여행', 'NNG'), ('을', 'JKO'), ('가보', 'VV'), ('아요', 'EFN'), ('.', 'SF')]와 같이 암호 비스무리하게(?) 결과가 나타나게 됩니다.
✨ 정제 및 정규화
💡 정제(Cleaning)
- 정제는 텍스트에서 분석에 방해가 되는 노이즈(특수문자, 불필요한 기호 등)를 제거하는 과정입니다.
- 토큰화 전에 불필요한 부분을 배제하고, 토큰화 후에도 남아있는 노이즈를 지속적으로 제거할 수 있습니다.
- 예를 들면, 크롤링해서 긁어온 HTML 태그나 뉴스 기사의 게재 시간과 같은 불필요한 정보들을 제거합니다.
💡 정규화(Normalization)
- 정규화는 서로 다른 표현 방법을 가진 단어들을 동일한 단어로 통합하는 작업입니다.
- 영어의 경우에는 대문자와 소문자를 통합하여 단어 수를 줄일 수도 있고, 예를 들어 'USA'와 'US' 같은 것들은 동일한 의미이기 때문에 하나로 통합할 수도 있습니다.
- 분석 목적에 맞지 않는 단어(불용어)들을 사전, 빈도, 길이 등을 기준으로 제거할 수도 있습니다.
- 그렇지만, 한국어는 평균 단어 길이가 2~3자로 짧기 때문에, 길이가 짧은 단어를 무조건 불용어로 간주하면 안 됩니다.
✨ 어간 추출과 표제어 추출
- 어간 추출과 표제어 추출 작업은 텍스트 정규화 기법 중의 하나로, 코퍼스 내 단어의 형태를 단순화하여 단어의 개수를 줄이고, Bag of Words와 같은 표현 방식에서 차원을 축소하는 데 주로 사용됩니다.
💡 표제어 추출 (Lemmatization)
- 단어들이 다양한 형태를 취하더라도 그 기본 사전형(표제어)으로 변환해서 동일한 단어로 인식하도록 하는 것이 주 목적입니다.
- 예를 들어, 'lives'는 'life'로, 'dies'는 'die'로 변환하는 방식입니다.
- 단어의 형태학적인 파싱을 통해 어간(단어의 핵심 의미)과 접사(추가 의미)를 분리하고, 문맥에 따른 품사 정보를 반영해서 변환하게 됩니다.
- 실제로 NLTK의 WordNetLemmatizer를 사용해서
- words = ['policy', 'doing', 'organization', 'have', 'going', 'love', 'lives', 'fly', 'dies', 'watched', 'has', 'starting']의 표제어를 추출하게 되면
['policy', 'doing', 'organization', 'have', 'going', 'love', 'life', 'fly', 'dy', 'watched', 'ha', 'starting']
- 위와 같이 나타나게 되는데, 여기서 "dy"나 "ha"와 같이 잘못된 결과가 나오는 이유는, 표제어 추출기가 해당 단어가 동사로 쓰였다는 품사 정보를 받지 못했기 때문입니다.
- 그래서 만약 "dies", "watched", "has"가 동사임을 명시하면,
- 예를 들어 lemma.lemmatize('dies', 'v')처럼 품사 정보를 함께 제공하여 올바른 표제어("die", "watch", "have")를 얻을 수가 있습니다.
💡 어간 추출 (Stemming)
- 단어에서 어간(Stem)을 추출해서 단어의 다양한 활용형을 하나의 기본 형태로 간소화하는 것이 주 목적입니다.
- 규칙 기반 알고리즘을 사용하기 때문에, 표제어 추출과 달리 문맥이나 품사 정보를 반영하지 않습니다. 그래서 사전에 존재하지 않는 단어 형태가 나올 수도 있습니다.
- 포터 알고리즘(Porter Stemmer)은 대표적인 어간 추출 알고리즘으로, 예를 들어 "formalize"은 "formal", "allowance"은 "allow", "electricical"은 "electric"으로 변환해줍니다.
- 또다른 예시로 랭커스터 스테머도 존재하는데, 같은 단어 리스트에 대해 두 알고리즘을 비교해보면,
- 포터 스테머는 예를 들어 "policy"를 "polici", "doing"을 "do" 등으로 변환하고,
- 반면, 랭커스터 스테머는 "policy"를 그대로 "policy", "organization"을 "org" 등으로 다르게 처리할 수 있습니다.
# 포터 알고리즘과 랭커스터 스태머 알고리즘 비교
from nltk.stem import PorterStemmer
from nltk.stem import LancasterStemmer
porter = PorterStemmer()
lancaster = LancasterStemmer()
words = ['policy', 'doing', 'organization', 'have', 'going', 'love',
'lives', 'fly', 'dies', 'watched', 'has', 'starting']
print('포터 스테머의 어간 추출 후:', [porter.stem(w) for w in words])
'''
포터 스테머의 어간 추출 후: ['polici', 'do', 'organ', 'have', 'go', 'love',
'live', 'fli', 'die', 'watch', 'ha', 'start']
'''
print('랭커스터 스테머의 어간 추출 후:', [lancaster.stem(w) for w in words])
'''
랭커스터 스테머의 어간 추출 후: ['policy', 'doing', 'org', 'hav', 'going', 'lov',
'liv', 'fly', 'die', 'watch', 'has', 'start']
'''
💡 한국어에서의 어간 추출
- 한국어는 체언(명사, 대명사, 수사), 수식언(관형사, 부사), 관계언(조사), 독립언(감탄사), 그리고 용언(동사, 형용사) 등으로 나뉩니다.
- 용언의 경우, 동사와 형용사는 어간(단어의 핵심 의미)과 어미(문법적 변화를 주는 부분)가 결합되어 사용됩니다.
- 한국어의 경우에는 규칙 활용과 불규칙 활용이 혼재되어 있어서 어간만 추출하게 되면, 어간과 어미를 어떻게 분리할지에 대한 추가적인 고려가 필요합니다.
- 예를 들어, "잡다"는 "잡"과 "다"로, "듣다"는 규칙에 따라 "듣/들"과 같이 변할 수 있습니다.
✨ 불용어(Stopword) 제거
- 불용어는 텍스트 내에서 자주 등장하지만, 분석 목적에 크게 기여하지 않는 단어들을 말합니다.
- 영어에서는 전치사, 관사, 접속사 등 약 100여 개 이상의 단어가 미리 nltk.corpus의 stopwords에 정의되어 있습니다.
- 또한, 사용자가 직접 불용어 사전을 정의할 수도 있습니다.
from nltk.corpus import stopwords
nltk.download('stopwords')
# NLTK에서 불용어 확인
stopwords_list = stopwords.words('english')
stopwords_list
example = "Family is not an important thing. It's everything."
stopwords_set = set(stopwords.words('english'))
word_tokens = word_tokenize(example)
print('불용어 제거 전:', word_tokens)
'''
불용어 제거 전: ['Family', 'is', 'not', 'an', 'important', 'thing', '.',
'It', "'s", 'everything', '.']
'''
print('불용어 제거 후:', [word for word in word_tokens if word not in stopwords_set])
# 불용어 제거 후: ['Family', 'important', 'thing', '.', 'It', "'s", 'everything', '.']
- 실무에서는 도메인에 따라서 불용어 사전을 직접 정의하는 방식으로 자주 사용되고, 그래서 단순히 미리 정의된 목록만 사용하는 것이 아니라, 데이터의 특성과 분석 목적에 맞게 조정하는 것이 중요할 것 같습니다.
🤔 27일차 회고
- 오늘은 어우... 코드보다 한글과 주석을 더 많이 살펴본 것 같습니다.
- 저는 졸업 작품에서 nltk 및 Okt를 활용해서 여러 노래의 가사들을 전처리했던 경험이 있습니다.
- 이 경험들을 조금이나마 더 살려서 이번 텍스트 전처리 과정, 그리고 앞으로 진행될 프로젝트에서 유용하게 사용하고 싶네요!
- 아직 전처리조차도 반 정도 진행이 되었는데, 텍스트 데이터를 다루는 전체 과정은 얼마나 더 길게, 그리고 더 세분화되어 진행될지 너무나도 기대가 됩니다.
- 그리고 배우면 배울수록 국어 공부를 더 열심히 해야겠다는 생각이... 🤔
728x90
LIST
'부트캠프 > LG U+' 카테고리의 다른 글
🤔 Words In My Bag (8) | 2025.03.11 |
---|---|
🤔 자연어를 전처리해보자 (0) | 2025.03.07 |
🤔 SQL 끝장내기 (2) | 2025.03.04 |
🤔 SQL과 날아올라 (4) | 2025.02.27 |
🤔 SQL의 꽃 🌼 (0) | 2025.02.26 |