자연어 처리의 시작은 텍스트의 토큰화 입니다.
텍스트를 토큰화 하는 과정과 패딩과 임베딩에 대해서도 알아보겠습니다.
1. 텍스트의 토큰화
import os
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Embedding
import numpy as np
# 전처리할 텍스트를 정합니다.
text = '오늘은 자연어 처리를 배울거야'
# Tokenizer 객체 생성 및 fit_on_texts로 단어 인덱스 학습
tokenizer = Tokenizer()
tokenizer.fit_on_texts([text])
# texts_to_sequences로 텍스트를 시퀀스로 변환
sequence = tokenizer.texts_to_sequences([text])
# 단어 인덱스 확인
word_index = tokenizer.word_index
print("\n단어 인덱스: \n", word_index)
# 결과
# 단어 인덱스:
# {'오늘은': 1, '자연어': 2, '처리를': 3, '배울거야': 4}
토큰화 하는 과정에 대한 설명입니다.
해당 코드 블록은 자연어 처리에서 중요한 텍스트 전처리 과정을 보여줍니다.
1. tokenizer = Tokenizer()
• 토크나이저 객체 생성
• 텍스트를 숫자로 변환하기 위한 도구입니다.
• 기본적으로 공백을 기준으로 단어를 분리합니다.
• 한글 텍스트도 잘 처리합니다.
2. tokenizer.fit_on_texts([text])
• 단어 사전(어휘 목록) 구축
• 입력된 텍스트('오늘은 자연어 처리를 배울거야')에서 모든 단어를 추출하고, 빈도수에 따라 내림차순으로 정렬해 각 단어에 고유한 인덱스를 부여합니다.
• 가장 많이 등장한 단어가 인덱스 1, 그 다음이 2, ... 식으로 번호가 매겨집니다.
• 여기서는 각 단어가 한 번씩만 등장하므로 순서대로 인덱스가 부여됩니다.
3. sequence = tokenizer.texts_to_sequences([text])
• 텍스트를 숫자 시퀀스로 변환
• 위에서 만든 단어 사전을 이용해 문장을 숫자 배열로 변환합니다.
• 예를 들어:
- 입력: '오늘은 자연어 처리를 배울거야'
- 출력: [[1, 2, 3, 4]] (각 단어가 해당 인덱스로 변환됨)
• 이중 리스트 형태인 이유는 입력이 리스트 형태([text])이기 때문입니다.
이렇게 변환된 숫자 시퀀스는 딥러닝 모델에 입력하기 적합한 형태이며, 특히 임베딩 레이어에 입력하여 단어 벡터로 변환할 수 있습니다.
-- Claude 3.7 Sonnet 설명
2. 단어의 빈도수 세기
# 전처리하려는 세 개의 문장을 정합니다.
docs = ["생쥐와 미니피그 모델에서 림프절 이식 후 림프관 신생 과정을 림프조영술과 자기공명영상(MRI)으로 시각화해 림프계 재건의 생리학적 기전에 대한 기초 자료를 제시했다이 중 원인이 세균성이나 결핵성이라면 사망률도 높고 치료 후에도 인지기능 장애, 뇌혈관 장애, 경련 발작 반복 등 후유증이 생길 수 있기 때문에 빠른 원인 진단과 치료가 중요하다.",
"연구팀은 인공지능 기반의 중추신경계 감염 원인 및 예후 예측 모델을 개발하고 그 효과를 분석했다.",
"딥러닝 모델이 세포핵 주변의 구조 차이를 확인해 예후와 원인을 예측하며, 세포의 질량, 부피, 단백질 밀도 등 정량적 지표를 예측에 중요한 요소로 활용하는 것을 확인했다."]
# 토큰화 함수를 이용해 전처리 하는 과정입니다.
tokenizer = Tokenizer()
tokenizer.fit_on_texts(docs)
# 단어의 빈도수를 계산한 결과를 각 옵션에 맞추어 출력합니다.
print("\n단어 카운트:\n", tokenizer.word_counts)
# 단어 카운트:
# OrderedDict([('생쥐와', 1), ('미니피그', 1), ('모델에서', 1), ('림프절', 1), ('이식', 1), ('후', 1), ('림프관', 1), ('신생', 1), ('과정을', 1), ('림프조영술과', 1), ('자기공명영상', 1), ('mri', 1), ('으로
# ', 1), ('시각화해', 1), ('림프계', 1), ('재건의', 1), ('생리학적', 1), ('기전에', 1), ('대한', 1), ('기초', 1), ('자료를', 1), ('제시했다이', 1), ('중', 1), ('원인이', 1), ('세균성이나', 1), ('결핵성이라면
# ', 1), ('사망률도', 1), ('높고', 1), ('치료', 1), ('후에도', 1), ('인지기능', 1), ('장애', 2), ('뇌혈관', 1), ('경련', 1), ('발작', 1), ('반복', 1), ('등', 2), ('후유증이', 1), ('생길', 1), ('수', 1), ('있
# 기', 1), ('때문에', 1), ('빠른', 1), ('원인', 2), ('진단과', 1), ('치료가', 1), ('중요하다', 1), ('연구팀은', 1), ('인공지능', 1), ('기반의', 1), ('중추신경계', 1), ('감염', 1), ('및', 1), ('예후', 1), ('
# 예측', 1), ('모델을', 1), ('개발하고', 1), ('그', 1), ('효과를', 1), ('분석했다', 1), ('딥러닝', 1), ('모델이', 1), ('세포핵', 1), ('주변의', 1), ('구조', 1), ('차이를', 1), ('확인해', 1), ('예후와', 1), ('원인을', 1), ('예측하며', 1), ('세포의', 1), ('질량', 1), ('부피', 1), ('단백질', 1), ('밀도', 1), ('정량적', 1), ('지표를', 1), ('예측에', 1), ('중요한', 1), ('요소로', 1), ('활용하는', 1), ('것을', 1),
# ('확인했다', 1)])
# 출력되는 순서는 랜덤입니다.
print("\n문장 카운트: ", tokenizer.document_count)
# 문장 카운트: 3
print("\n각 단어가 몇 개의 문장에 포함되어 있는가:\n", tokenizer.word_docs)
# 각 단어가 몇 개의 문장에 포함되어 있는가:
# defaultdict(<class 'int'>, {'자기공명영상': 1, '치료': 1, '후에도': 1, '생길': 1, '신생': 1, '림프조영술과': 1, '세균성이나': 1, '림프관': 1, '재건의': 1, '기초': 1, '치료가': 1, '있기': 1, '생리학적': 1, '기전에': 1, '반복': 1, '수': 1, '높고': 1, '후유증이': 1, '과정을': 1, '이식': 1, '인지기능': 1, '생쥐와': 1, '림프계': 1, '제시했다이': 1, '중요하다': 1, '등': 2, '사망률도': 1, '때문에': 1, '뇌혈관': 1, '모델에서': 1, '으로': 1, '림프절': 1, '결핵성이라면': 1, '발작': 1, '원인': 2, '진단과': 1, '경련': 1, '중': 1, '장애': 1, '대한': 1, '시각화해': 1, '자료를': 1, '원인이': 1, '빠른': 1, '후': 1, '미니피
# 그': 1, 'mri': 1, '그': 1, '기반의': 1, '및': 1, '모델을': 1, '연구팀은': 1, '인공지능': 1, '예측': 1, '예후': 1, '분석했다': 1, '감염': 1, '중추신경계': 1, '효과를': 1, '개발하고': 1, '요소로': 1, '차이를
# ': 1, '모델이': 1, '확인했다': 1, '예측하며': 1, '지표를': 1, '중요한': 1, '세포의': 1, '밀도': 1, '질량': 1, '세포핵': 1, '활용하는': 1, '딥러닝': 1, '주변의': 1, '부피': 1, '예후와': 1, '예측에': 1, '원
# 인을': 1, '확인해': 1, '구조': 1, '정량적': 1, '것을': 1, '단백질': 1})
print("\n각 단어에 매겨진 인덱스 값:\n", tokenizer.word_index)
# 각 단어에 매겨진 인덱스 값:
# {'장애': 1, '등': 2, '원인': 3, '생쥐와': 4, '미니피그': 5, '모델에서': 6, '림프절': 7, '이식': 8, '후': 9, '림프관': 10, '신생': 11, '과정을': 12, '림프조영술과': 13, '자기공명영상': 14, 'mri': 15, '으로
# ': 16, '시각화해': 17, '림프계': 18, '재건의': 19, '생리학적': 20, '기전에': 21, '대한': 22, '기초': 23, '자료를': 24, '제시했다이': 25, '중': 26, '원인이': 27, '세균성이나': 28, '결핵성이라면': 29, '사망
# 률도': 30, '높고': 31, '치료': 32, '후에도': 33, '인지기능': 34, '뇌혈관': 35, '경련': 36, '발작': 37, '반복': 38, '후유증이': 39, '생길': 40, '수': 41, '있기': 42, '때문에': 43, '빠른': 44, '진단과': 45,
# '치료가': 46, '중요하다': 47, '연구팀은': 48, '인공지능': 49, '기반의': 50, '중추신경계': 51, '감염': 52, '및': 53, '예후': 54, '예측': 55, '모델을': 56, '개발하고': 57, '그': 58, '효과를': 59, '분석했다': 60, '딥러닝': 61, '모델이': 62, '세포핵': 63, '주변의': 64, '구조': 65, '차이를': 66, '확인해': 67, '예후와': 68, '원인을': 69, '예측하며': 70, '세포의': 71, '질량': 72, '부피': 73, '단백질': 74, '밀도':
# 75, '정량적': 76, '지표를': 77, '예측에': 78, '중요한': 79, '요소로': 80, '활용하는': 81, '것을': 82, '확인했다': 83}
이번에는 3개의 문장을 docs 에 넣어서 docs 를 토큰화 하면서 단어의 빈도수, 단어 카운트, 문장 카운트, 단어 인덱스를 추출하는 과정입니다.
아래는 tokenizer 에 대한 설명입니다.
Keras Tokenizer의 주요 속성 설명
Keras의 Tokenizer 클래스는 텍스트 전처리를 위한 다양한 속성을 제공합니다. 각 속성에 대해 자세히 설명해드리겠습니다.
1. tokenizer.word_counts
• 설명: 각 단어가 전체 코퍼스에서 몇 번 등장했는지 빈도수를 저장
• 자료형: collections.OrderedDict
• 예시: OrderedDict([('장애', 2), ('등', 2), ('원인', 2), ...])
• 활용: 자주 등장하는 단어와 희귀 단어를 구분할 때 유용
2. tokenizer.document_count
• 설명: 학습에 사용된 문서(문장)의 총 개수
• 자료형: int
• 예시: 3 (3개의 문장을 학습했을 경우)
• 활용: 문서 빈도 계산이나 통계 작업에 활용
3. tokenizer.word_docs
• 설명: 각 단어가 몇 개의 문서(문장)에 등장했는지 카운트
• 자료형: defaultdict(<class 'int'>)
• 예시: defaultdict(<class 'int'>, {'장애': 1, '등': 2, ...})
• 활용: TF-IDF 계산 등 단어의 문서 분포 분석에 유용
4. tokenizer.word_index
• 설명: 각 단어에 부여된 고유 인덱스 값 (빈도수 기준 내림차순)
• 자료형: dict
• 예시: {'장애': 1, '등': 2, '원인': 3, ...}
• 활용: 텍스트를 숫자 시퀀스로 변환할 때 사용
추가로 알면 좋은 Tokenizer 속성 및 기능
5. tokenizer.index_word
• 설명: word_index의 반대로, 인덱스를 키로 단어를 값으로 가짐
• 자료형: dict
• 예시: {1: '장애', 2: '등', 3: '원인', ...}
• 활용: 숫자 시퀀스를 다시 단어로 변환할 때 유용
6. tokenizer.filters
• 설명: 토큰화 과정에서 제거할 문자들 (기본값: 특수문자, 구두점 등)
• 활용: 필요에 따라 수정하여 특정 문자를 보존하거나 제거 가능
• 예시: tokenizer = Tokenizer(filters='!"#$%&()*+,-./:;<=>?@[\\]^_{|}~\t\n')`
7. tokenizer.num_words
• 설명: 사용할 최대 단어 개수 (가장 빈도 높은 단어들만 유지)
• 활용: 어휘 크기를 제한하여 희귀 단어나 노이즈 제거
• 예시: tokenizer = Tokenizer(num_words=1000) (상위 1000개 단어만 사용)
8. tokenizer.oov_token
• 설명: Out-of-Vocabulary(미등록 단어) 처리를 위한 토큰
• 활용: 학습 데이터에 없는 단어 처리 방식 지정
• 예시: tokenizer = Tokenizer(oov_token="<OOV>")
9. tokenizer.texts_to_matrix()
• 설명: 텍스트를 원-핫 인코딩이나 TF-IDF 등의 행렬로 변환
• 활용: 다양한 벡터화 방식으로 텍스트 표현
• 예시: matrix = tokenizer.texts_to_matrix(texts, mode='tfidf') 모드: 'binary', 'count', 'tfidf', 'freq'
10. tokenizer.sequences_to_texts()
• 설명: 숫자 시퀀스를 다시 텍스트로 변환
• 활용: 모델 출력 결과를 사람이 읽을 수 있는 텍스트로 변환
• 예시: texts = tokenizer.sequences_to_texts(sequences)
이러한 다양한 속성과 메서드를 활용하면 텍스트 전처리를 더 효과적으로 수행할 수 있습니다.
-- Claude 3.7 Sonnet 설명
3. 패딩과 임베딩
# 전처리할 텍스트를 정합니다.
texts = ['오늘은 자연어 처리를 배울거야', '내일은 딥러닝을 배울거야', '릴리는 어렵겠지만 머신러닝을 배울거야', '잘해보자']
# Tokenizer 객체 생성 및 fit_on_texts로 단어 인덱스 학습
tokenizer = Tokenizer()
tokenizer.fit_on_texts(texts)
# texts_to_sequences로 텍스트를 시퀀스로 변환
sequence = tokenizer.texts_to_sequences(texts)
# 단어 인덱스 확인
word_index = tokenizer.word_index
print("\n단어 인덱스:\n", word_index)
# {'배울거야': 1, '오늘은': 2, '자연어': 3, '처리를': 4, '내일은': 5, '딥러닝을': 6, '릴리는': 7, '어렵겠지만': 8, '머신러닝을': 9, '잘해보자': 10}
# 패딩을 통해 시퀀스 길이를 맞춥니다.
print("\n시퀀스:\n", sequence)
# [[2, 3, 4, 1], [5, 6, 1], [7, 8, 9, 1], [10]]
padded_sequence = pad_sequences(sequence, 4)
print("\n패딩된 시퀀스:\n", padded_sequence)
# [[ 2 3 4 1]
# [ 0 5 6 1]
# [ 7 8 9 1]
# [ 0 0 0 10]]
# 임베딩 레이어가 포함된 모델 정의
vocab_size = len(word_index) + 1 # 단어 개수 + 패딩
embedding_dim = 8 # 임베딩 차원
model = Sequential()
model.add(Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=4))
# 임베딩 결과 추출
embedding = model.predict(padded_sequence)
print("\n임베딩 결과:\n", embedding)
# 임베딩 결과:
# [[[ 0.01916753 0.03314694 0.01245119 -0.03737315 -0.03800883
# -0.04605414 -0.02355322 -0.01956273]
# [ 0.03752443 -0.00019611 -0.00257126 0.01134013 -0.01533241
# -0.00577404 0.03766963 0.01410129]
# [ 0.03529078 0.01131129 0.00496322 -0.02320131 -0.02905967
# -0.00133227 -0.01116673 -0.00874171]
# [ 0.01764137 -0.03504394 -0.02588794 -0.03373344 -0.02673971
# 0.00229133 -0.00639019 0.0352829 ]]
# [[ 0.04232022 0.00493821 -0.00325092 -0.00515879 -0.01233885
# 0.03171523 0.00180597 0.04414293]
# [ 0.01621506 0.0378188 0.02141258 0.02396617 0.0095077
# 0.02657965 0.04265741 -0.01447652]
# [ 0.01984793 -0.03372462 -0.03791568 0.02152542 0.00783087
# -0.04161739 -0.00813531 0.02713681]
# [ 0.01764137 -0.03504394 -0.02588794 -0.03373344 -0.02673971
# 0.00229133 -0.00639019 0.0352829 ]]
# [[-0.04171902 0.02535174 0.00441008 -0.00566369 -0.02400956
# 0.04939996 -0.04542769 0.00688082]
# [-0.01048481 -0.02960598 0.01588121 0.00503615 0.03939933
# -0.04783677 -0.01931008 0.04938486]
# [-0.00653007 0.01851237 0.00429329 -0.02756555 0.04957687
# 325092 -0.00515879 -0.01233885
# 0.03171523 0.00180597 0.04414293]
# [ 0.04232022 0.00493821 -0.00325092 -0.00515879 -0.01233885
# 0.03171523 0.00180597 0.04414293]
# [-0.00105438 0.00724179 0.04904902 0.00209807 0.02452053
# -0.00152276 -0.03907032 -0.0056323 ]]]
여기서는 패딩을 통해 시퀀스의 길이를 맞추는 것과 임베딩 레이어가 포함된 모델에 대한 결과를 추출하는 과정입니다.
아래는 코드에 대한 설명과 패딩과 임베딩의 중요성에 대한 설명입니다.
1.시퀀스
sequence = tokenizer.texts_to_sequences(texts)
print("\n시퀀스:\n", sequence)
# [[2, 3, 4, 1], [5, 6, 1], [7, 8, 9, 1], [10]]
• 토크나이저가 변환한 4개 문장의 시퀀스를 출력합니다. word_index 값으로 배열를 만듦
• 각 시퀀스의 길이가 다른 것을 확인할 수 있습니다(4개, 3개, 4개, 1개).
2. 패딩
padded_sequence = pad_sequences(sequence, 4)
print("\n패딩된 시퀀스:\n", padded_sequence)
• pad_sequences 함수를 사용해 모든 시퀀스 길이를 4로 맞춥니다.
• 길이가 부족한 시퀀스는 앞에 0을 채워 길이를 맞춥니다.
3. 임베딩 모델 정의 및 결과 추출
vocab_size = len(word_index) + 1 # 단어 개수 + 패딩
embedding_dim = 8 # 임베딩 차원
• 어휘 크기는 단어 개수 + 1(패딩용 0)로 설정합니다.
• 임베딩 차원은 각 단어를 표현할 벡터의 크기로, 여기서는 8로 설정했습니다.
model = Sequential()
model.add(Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=4))
• 케라스의 Sequential 모델을 생성합니다.
• Embedding 레이어를 추가하여 단어 인덱스를 벡터로 변환합니다.
- input_dim: 어휘 크기
- output_dim: 임베딩 벡터 차원
- input_length: 입력 시퀀스 길이
embedding = model.predict(padded_sequence)
print("\n임베딩 결과:\n", embedding)
• 패딩된 시퀀스를 모델에 입력하여 임베딩 결과를 얻습니다.
• 결과는 (문장 수, 시퀀스 길이, 임베딩 차원) 형태의 3차원 배열입니다.
-- Claude 3.7 Sonnet 설명
패딩(Padding)이 필요한 이유
1. 균일한 입력 크기 필요성
• 딥러닝 모델 요구사항: 대부분의 딥러닝 모델은 고정된 크기의 입력을 요구합니다.
• 배치 처리: 여러 데이터를 한 번에 처리하려면 모든 데이터의 크기가 동일해야 합니다.
2. 다양한 문장 길이 처리
• 자연어의 특성: 문장마다 길이가 다릅니다(예: "잘해보자"는 1개 단어, "오늘은 자연어 처리를 배울거야"는 4개 단어).
• 일관성 유지: 패딩을 통해 모든 시퀀스를 동일한 길이로 맞춰 일관된 처리가 가능합니다.
3. 패딩 방식
• 앞쪽 패딩(pre-padding): 시퀀스 앞에 0을 추가 (기본값)
• 뒤쪽 패딩(post-padding): 시퀀스 뒤에 0을 추가
• 최대 길이 지정: 가장 긴 시퀀스에 맞추거나, 사용자가 지정한 길이로 설정
임베딩(Embedding)이 필요한 이유
1. 단어의 의미 표현
• 원-핫 인코딩의 한계: 단어 간 의미적 관계를 표현할 수 없고, 차원이 너무 커집니다.
• 의미 공간 매핑: 임베딩은 단어를 의미 공간의 벡터로 변환하여 비슷한 의미의 단어는 가까운 위치에 배치됩니다.
2. 차원 축소 효과
• 효율성: 어휘 크기가 10,000개라면 원-핫 인코딩은 10,000차원이 필요하지만, 임베딩은 보통 100~300차원으로 충분합니다.
• 계산 효율: 낮은 차원의 벡터로 표현하여 계산량을 크게 줄입니다.
3. 단어 간 관계 학습
• 의미적 유사성: "왕-남자+여자=여왕"과 같은 벡터 연산이 가능합니다.
• 문맥 정보 포함: 비슷한 문맥에서 사용되는 단어들은 비슷한 임베딩 벡터를 갖게 됩니다.
4. 전이 학습 가능
• 사전 학습된 임베딩: Word2Vec, GloVe, FastText 등 대규모 코퍼스에서 사전 학습된 임베딩을 활용할 수 있습니다.
• 미세 조정: 특정 작업에 맞게 임베딩을 추가로 학습(fine-tuning)할 수 있습니다.
이러한 패딩과 임베딩 기법은 자연어 처리에서 텍스트 데이터를 효과적으로 처리하고 모델에 입력하기 위한 필수적인 전처리 단계입니다.
-- Claude 3.7 Sonnet 설명
자연어 처리에 관해서 토큰과 패딩, 임베딩에 대해서 공부했습니다.
예전에 콜센터에서 통화 녹음을 텍스트로 처리한 후 긍정의 말, 부정의 말 등 키워드를 사전화 해서 통화분석하는 프로젝트를 해본 경험이 있는데, 이때 통화 녹음을 이와 같은 방법으로 텍스트를 전처리하고, 키워드 추출하고 인덱스를 통해서 간단한 분석 과정을 가졌는데.. 이제는 ai 를 통해서 더 정확하고 명확한 분석이 가능하다고 생각됩니다.
'AI 공부' 카테고리의 다른 글
자연어 처리3 - LSTM+CNN (0) | 2025.06.06 |
---|---|
자연어 처리2 - LSTM (0) | 2025.06.06 |
CNN(컨볼루션 신경망) 딥러닝 모델 2 (3) | 2025.06.02 |
CNN(컨볼루션 신경망) 딥러닝 모델 1 (4) | 2025.06.02 |
산점도를 이용한 결과 (0) | 2025.05.31 |