Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

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를 호출하여 학습을 가동합니다.

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.modelSentencePieceProcessor로 탑재하여 실제 한국어 교착어 문장의 분절 거동을 정밀 추적해 봅니다.

한국어_형태분석기 = 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))