본문 바로가기
  • 머킹이의 머신로그
프로젝트/개인 프로젝트

[자연어 개인 프로젝트] Okt로 토큰화 하기

by 머킹 2023. 9. 25.
728x90

제주도 사투리 Okt 토큰화와 원문 & 번역 병렬  문장 구조 쌍으로 정리하기

안녕하세요 머킹입니다.

오늘에서야 드디어 토큰화가 끝났는데요.

별로 없던 데이터가 토큰화를 마치니 3백만 개가 넘어가서 깜짝 놀랐습니다.

 


일단 저는 Okt 를 사용하기로 하고, 이제 모든 문장을 토큰화했는데요.

저처럼 하시면 나중에 후회하니까 고생 두 번 하지 않기를 바랍니다..

import csv
import json
from konlpy.tag import Okt
import os
from tqdm import tqdm

# Okt 형태소 분석기 초기화
okt = Okt()

# 데이터 디렉토리 경로
base_directory = '/content/drive/MyDrive/jeju/'

# 모든 데이터 폴더 선택 (Training 및 Validation)
data_directories = [
    os.path.join(base_directory, 'Training'),
    os.path.join(base_directory, 'Validation')
]

# 형태소 분석 결과 저장 딕셔너리
results = {'Training': [], 'Validation': []}

def analyze_text(text):
    okt_result = okt.morphs(text)
    return {
        'Okt': okt_result
    }

# 모든 데이터 폴더에서 JSON 파일 목록 가져오기
for data_directory in data_directories:
    data_files = [os.path.join(root, file) for root, dirs, files in os.walk(data_directory) for file in files if file.endswith('.json')]

    for data_file in tqdm(data_files, desc=f"{data_directory} 데이터 처리중"):
        with open(data_file, 'r', encoding='utf-8') as file:
            data = json.load(file)
            for idx, utterance in enumerate(data['utterance']):
                text = utterance['dialect_form']
                result = analyze_text(text)
                results[data_directory.split('/')[-1]].append({
                    'Utterance': f'Utterance_{idx + 1}',
                    'Okt': result['Okt']
                })

# 결과 출력 (일부만 출력)
for data_type, data_results in results.items():
    print(f"\n{data_type} 데이터 형태소 분석 결과 (일부):")
    for data_result in data_results[:10]:
        print(f"\n{data_result['Utterance']}:")
        print(f"Okt 형태소 분석 결과: {' '.join(data_result['Okt'])}")

# CSV 파일 경로 설정
output_csv_file = '/content/drive/MyDrive/jeju/analysis_results.csv'

# CSV 파일 헤더 설정
csv_columns = ['Data_Type', 'Utterance', 'Okt']

# CSV 파일에 형태소 분석 결과 저장
with open(output_csv_file, 'w', newline='', encoding='utf-8') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=csv_columns)
    writer.writeheader()  # 헤더 쓰기

    for data_type, data_results in results.items():
        for data_result in tqdm(data_results, desc=f"{data_type} 데이터 처리중"):
            writer.writerow({
                'Data_Type': data_type,
                'Utterance': data_result['Utterance'],
                'Okt': ' '.join(data_result['Okt'])
            })

print(f"분석 결과가 {output_csv_file} 경로에 저장되었습니다.")

이 코드를 이용해서 이제 모든 문장을 Okt로 형태소 분석하였습니다.

그런데 transformer 코드를 작성하다 보니 저는 번역기를 만들고 있는데

원문과 번역을 나누지 않았단 것을 발견했습니다.. (왜 이제 알았을까요?)

 

아무튼 그래서 급하게 코드를 수정했습니다..

이미 토큰화도 마친 채..

원문과 번역문을 병렬 구조쌍으로 만들어야 하는데 토큰화가 먼저일까요 구조로 만드는 게 먼저일까요?

답은 토큰화가 먼저입니다.

 

보통 이런 순서를 따릅니다.

원문과 번역문 데이터 로드: 먼저 원문과 번역문 데이터를 로드합니다.

토큰화: 원문 및 번역문을 토큰화하여 각각의 토큰 목록을 생성합니다. 이때, 토큰화는 언어 모델 또는 형태소 분석기를 사용하여 수행할 수 있습니다.

병렬 문장 쌍 생성: 원문과 번역문의 토큰 목록을 이용하여 병렬 문장 쌍을 생성합니다. 쌍은 원문 문장과 해당 번역문 문장으로 구성됩니다.

순서를 지키지 않으면 토큰화를 하기 전에 데이터가 무작위로 섞일 수 있으며, 이로 인해 원문과 번역문 간의 대응 관계가 깨질 수 있습니다. 따라서 일반적으로 위의 순서를 따르는 것이 좋습니다.

 

그래서 저는 이 순서대로 다시 코드를 작성했습니다.

처음에는 혹시 모르니까 꼭 샘플로 테스트해보세요.

 

import csv
import json
import os
from konlpy.tag import Okt

# Okt 형태소 분석기 초기화
okt = Okt()

# 데이터 디렉토리 경로
base_directory = '/content/drive/MyDrive/jeju/'

# CSV 파일 경로 설정 (원문 및 번역 모두 저장)
csv_file = '/content/drive/MyDrive/jeju/parallel_data.csv'

# CSV 파일 헤더 설정
csv_columns = ['Utterance', 'Original', 'Translation']

# 데이터 저장 리스트
parallel_data = []

# 모든 데이터 폴더 선택 (Training 및 Validation)
data_directories = [
    os.path.join(base_directory, 'Training'),
    os.path.join(base_directory, 'Validation')
]

# 원문 및 번역 데이터를 읽어와서 병렬 구조 쌍으로 만들기
for data_directory in data_directories:
    data_files = [os.path.join(root, file) for root, dirs, files in os.walk(data_directory) for file in files if file.endswith('.json')]

    for data_file in data_files:
        with open(data_file, 'r', encoding='utf-8') as file:
            data_json = json.load(file)
            for idx, utterance in enumerate(data_json['utterance']):
                original_text = utterance['dialect_form']
                translation_text = utterance['standard_form']  # 번역 데이터로 사용할 열 선택

                # 토큰화 수행
                original_tokens = okt.morphs(original_text)
                translation_tokens = okt.morphs(translation_text)

                parallel_data.append({
                    'Utterance': f'Utterance_{idx + 1}',
                    'Original': ' '.join(original_tokens),  # 토큰을 공백으로 구분하여 저장
                    'Translation': ' '.join(translation_tokens)  # 토큰을 공백으로 구분하여 저장
                })

# CSV 파일에 병렬 데이터 저장
with open(csv_file, 'w', newline='', encoding='utf-8') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=csv_columns)
    writer.writeheader()  # 헤더 쓰기

    for parallel_item in parallel_data:
        writer.writerow({
            'Utterance': parallel_item['Utterance'],
            'Original': parallel_item['Original'],
            'Translation': parallel_item['Translation']
        })

print(f"병렬 데이터가 {csv_file} 경로에 저장되었습니다.")

저는 처음에 50개의 데이터만 가지고 실험했습니다.

텍스트는 처리하는데 시간이 매우 오래 걸리니까 꼭 샘플로 하세요..

이렇게 원문과 번역, 그리고 토큰화된 문장까지 잘 만들었습니다.

이제 전체 코드를 돌리면 또 몇 시간 뒤에 결과를 알 수 있겠네요!

 

역시 프로젝트를 하면 왜 그렇게 빨리 실력이 느는지 알 것 같습니다.

하나하나 다 부딪혀보니 꽤나 의미도 깊어서 저도 점점 이 프로젝트에 애착이 더 생기는 것 같아요.

 

다음에는 문장구조를 만들고 트랜스포머 코드처럼 차근차근 모델을 만들어 보겠습니다.