SentencePiece
구글의 SentencePiece는 사전의 형태소 분석 규칙이나 언어학적 전처리 없이, 오직 말뭉치(Corpus)의 통계적 빈도만을 사용하여 단어를 하위 단위(Subword)로 비지도 학습 분할해내는 현대 자연어 처리(NLP)의 핵심 기술입니다.
본 실습에서는 한영 번역 병렬 말뭉치 데이터를 정밀 분석하고 균형화(Balanced Dataset)한 뒤, SentencePiece 라이브러리를 가동하여 한국어 문장을 의미 있는 토큰 공간으로 통계적으로 수렴시키고 인코딩/디코딩하는 실제 가동 파이프라인 전체를 체계적으로 이해합니다.
1병렬 말뭉치 데이터 로딩 및 분석¶
학습에 사용할 한국어-영어 병렬 데이터셋(data/korean-english)을 불러옵니다.
이 데이터셋은 다양한 분야(학술, 일상, 뉴스 등)의 번역문과 함께 원천 정보인 source 메타데이터를 포함하고 있습니다.
import datasets
dataset_dir = 'data/korean-english'
한영데이터 = datasets.load_dataset('csv', data_files={
'train': f'{dataset_dir}/train.csv',
'validation': f'{dataset_dir}/validation.csv',
'test': f'{dataset_dir}/test.csv'
})
for split in 한영데이터:
print(f"{split:<12}: {len(한영데이터[split]):,}")데이터의 특징(Features) 정보와 열 구성을 확인합니다.
ko는 한국어 원문, en은 영어 번역문, source는 데이터 수집 경로 식별 번호입니다.
# features
print(한영데이터['train'].features)임의의 데이터 샘플 5개를 무작위로 추출하여 한국어 교착어 문장 및 매핑되는 번역 데이터 구조를 눈으로 점검합니다.
import pandas as pd
pd.DataFrame(한영데이터['train'].shuffle(seed=0).take(5))2데이터 분포 편향 분석 및 균형 데이터 구축¶
말뭉치가 수집된 출처(source)별 데이터 비율을 확인합니다.
특정 수집처에 데이터가 과도하게 쏠려 있을 경우, 토크나이저의 어휘 사전이 특정 도메인 용어에 편향되어 일반화 성능이 붕괴될 우려가 존재합니다.
분류 = pd.Series(한영데이터['train']['source'])
(pd.DataFrame({
'도수': 분류.value_counts(),
'비율': 분류.value_counts(normalize=True)})
.style.format({'도수': '{:,}', '비율': '{:.2%}'}))토크나이저가 다양한 도메인의 문장을 골고루 편향 없이 소화할 수 있도록, 가장 작은 빈도를 보이는 분류의 크기(min_count)를 기준으로 모든 수집 출처 데이터를 균등 셔플하여 균형화된 데이터셋(sample_dataset)을 새로 조립합니다.
# 가장 도수가 작은 분류의 도수로 각 분류의 샘플 수를 맞춤
min_count = 분류.value_counts().min()
balanced_samples = []
for source in 분류.unique():
source_samples = 한영데이터['train'].filter(lambda x: x['source'] == source)
balanced_samples.append(source_samples.shuffle(seed=0).take(min_count))
sample_dataset = datasets.concatenate_datasets(balanced_samples)조립 완료 후, 모든 도메인 분류가 정확하게 균일한 비율(12.50%)로 정돈되었는지 최종 시각 확인을 거칩니다.
분류 = pd.Series(sample_dataset['source'])
(pd.DataFrame({
'도수': 분류.value_counts(),
'비율': 분류.value_counts(normalize=True)})
.sort_index()
.style.format({'도수': '{:,}', '비율': '{:.2%}'}))3SentencePiece 비지도 모델 학습¶
SentencePiece를 학습시키기 위해, 한국어 코퍼스 데이터만 추출하여 로컬 파일(ko_sents.txt)에 매 줄마다 기록(Dump)합니다.
ko_dataset = sample_dataset.select_columns(['ko'])
with open('ko_sents.txt', 'w', encoding='utf-8') as f:
for sent in ko_dataset['ko']:
f.write(sent + '\n')저장된 텍스트 코퍼스에서 샘플 5줄을 읽어보며 개행 및 문자열 인코딩 상태를 사전 검수합니다.
with open('ko_sents.txt', 'r', encoding='utf-8') as f:
for _ in range(5):
print(f.readline().strip())3.1SentencePieceTrainer 학습 실행¶
sentencepiece 패키지의 API를 호출하여 학습을 가동합니다.
input: 학습 텍스트 소스 파일 경로model_prefix: 완성될 모델 및 어휘 단어장 파일 이름의 접두사. 여기서는ko_spm_5k로 설정하여ko_spm_5k.model및ko_spm_5k.vocab을 생성합니다.vocab_size: 단어장 크기를 5,000개로 지정합니다.model_type: 기본인 UNIGRAM 모델로 비지도 학습을 수행합니다.
import sentencepiece as spm
spm.SentencePieceTrainer.Train(
input='ko_sents.txt',
model_prefix='ko_spm_5k',
vocab_size=5000,
model_type='unigram'
)4학습 완료된 토크나이저 로딩 및 인코딩/디코딩 검증¶
학습이 끝난 ko_spm_5k.model을 SentencePieceProcessor로 탑재하여 실제 한국어 교착어 문장의 분절 거동을 정밀 추적해 봅니다.
한국어_형태분석기 = spm.SentencePieceProcessor()
assert 한국어_형태분석기.load('ko_spm_5k.model'), "모델 로드 실패"
예문 = '대통령께서 입장하십니다.'
형태소목록 = 한국어_형태분석기.encode(예문, out_type=str)
정수시퀀스 = 한국어_형태분석기.encode(예문, out_type=int)
# 시각적 분석을 위한 매핑 테이블 구성
pd.DataFrame([{정수: 형태소 for 형태소, 정수 in zip(형태소목록, 정수시퀀스)}])결과를 보면, 공백을 나타내는 기호(_ 혹은 폰트 환경에 따라 ▁ 등)가 각 단어의 결합 경계에 정확히 안착해 있으며, 조사인 께서, 접사 하, 십, 니다 등이 어떠한 언어학적 문법 사전 없이도 완벽하게 독립 토큰으로 비지도 분리되었음을 확인할 수 있습니다.
이제 학습 전체 데이터셋의 한국어 열에 일괄적으로 모델의 인코딩을 전개하여 로컬 디스크에 토큰화된 데이터셋을 영속 보존합니다.
한국어문장 = 한영데이터.select_columns('ko')
한국어형태분석문장 = 한국어문장.map(
lambda x: {'tokens': 한국어_형태분석기.encode(x['ko'], out_type=str)})
한국어형태분석문장.save_to_disk('ko_tokens')저장된 토큰 데이터셋을 다시 안정적으로 로드하여, 실제 토큰 시퀀스로 분절 변환되어 저장된 결과셋 5개를 출력하여 확인하고 실습을 완결합니다.
한국어형태분석문장 = datasets.load_from_disk('ko_tokens')
print(한국어형태분석문장.keys())
pd.DataFrame(한국어형태분석문장['train'].shuffle(seed=5).take(5))