본문 바로가기
Weekly Question

Q. 텍스트 데이터를 모델에 적용하기 전 전처리 과정 + 코드

by milk2 2025. 4. 15.

A. 텍스트 전처리 전체 흐름 요약

텍스트(문장) → 토큰화 → 정수 인코딩 → 패딩 → 텐서 변환

 

import json

file_path = '/content/일상생활및구어체_한영_valid_set.json'

with open(file_path, 'r', encoding='utf-8') as f:
    raw_json = json.load(f)

data = raw_json['data']

print(f"전체 문장 쌍 개수: {len(data)}")

# 샘플 하나 확인
print("예시 샘플:")
print(f"한국어: {data[0]['ko']}")
print(f"영어: {data[0]['mt']}")
더보기

전체 문장 쌍 개수: 150000

예시 샘플:

한국어: >아, 진짜요?

영어: Oh, really?

1. 텍스트 정제 : 특수 기호 제거, 소문자 통일, 불용어 제거..

예: "I'm a student!" → "i am a student"

 

+ 기계번역에선 불용어 제거를 하지 않는 것이 일반적이기에 코드 X

 

2. 토큰화 : 문장을 단어 혹은 형태소로 쪼개는 과정도구 : nltk(영어), Okt(한국어), KoNLPy(한국어)

예: I am a student → ["I", "am", "a", "student"]

import nltk
nltk.download('punkt')  # 영어 토큰화용 룰 데이터
nltk.download('punkt_tab')

from nltk.tokenize import word_tokenize

def tokenize_ko(sentence):
    return sentence.strip().replace(">", "").split()  # 간단하게 띄어쓰기 기준

    # 후에 Okt 로 토큰화 더 자연스럽게 진행할 수 있음

def tokenize_en(sentence):
    return word_tokenize(sentence.strip().replace(">", ""))
tokenized_pairs = [] # 문자 토큰들..

for item in data:
    ko = item['ko']
    en = item['mt']
    tokenized_ko = tokenize_ko(ko)
    tokenized_en = tokenize_en(en)
    tokenized_pairs.append((tokenized_ko, tokenized_en)) # 토큰화된 한글/영어 문장 쌍을 하나의 튜플로 묶어서 리스트에 추가

print("샘플 토큰화 결과:")
print("한국어:", tokenized_pairs[0][0]) # 0번째 문장 한국어 토큰 리스트
print("영어:", tokenized_pairs[0][1]) # 0번째 문장 영어 토큰 리스트
더보기

샘플 토큰화 결과:

한국어: ['아,', '진짜요?']

영어: ['Oh', ',', 'really', '?']

 

+ 토큰 길이 측정

ko_lengths = [len(pair[0]) for pair in tokenized_pairs]
en_lengths = [len(pair[1]) for pair in tokenized_pairs]

print(f"한국어 평균 길이: {sum(ko_lengths)/len(ko_lengths):.2f}, 최대 길이: {max(ko_lengths)}")
print(f"영어 평균 길이: {sum(en_lengths)/len(en_lengths):.2f}, 최대 길이: {max(en_lengths)}")
더보기

한국어 평균 길이: 6.53, 최대 길이: 78

영어 평균 길이: 11.52, 최대 길이: 69

+ MAX_LENGTH = 80으로 설정 .. (최대길이 78 + a)

3. 어휘 사전 구축 & 정수 인코딩 : 각 단어를 고유한 숫자 인덱스로 바꿔줌→ "나는 학생" → [4, 5]

word2index = { "<pad>": 0, "<sos>": 1, "<eos>": 2, "<unk>": 3, "나는": 4, "학생": 5, ... }

 

4. 특수 토큰 추가토큰 용도 인덱스 (보통 ..)

<sos> 문장 시작 표시 1
<eos> 문장 끝 표시 2
<pad> 고정 길이로 맞추기 위한 패딩 0
<unk> 사전에 없는 단어 처리용 3

 

→ "나는 학생" → [1, 4, 5, 2] (sos, eos 포함)

PAD_token = 0  # 패딩용
SOS_token = 1  # 문장 시작
EOS_token = 2  # 문장 끝
UNK_token = 3  # 사전에 없는 단어

 

5. 패딩 : 모든 문장을 동일한 길이로 맞추기 위한 과정

MAX_LENGTH = 10 → [1, 4, 5, 2] → [1, 4, 5, 2, 0, 0, 0, 0, 0, 0] # PAD = 0
  • PAD_token을 뒤에 붙여 고정 길이로 맞춤
  • PyTorch 모델 입력 시 필수

6. 텐서 변환 : 파이토치 모델에 넣기 위해 torch.tensor()로 바꿈

tensor = torch.tensor([1, 4, 5, 2, 0, 0, 0, 0, 0, 0], dtype=torch.long)

 

 

+ 코드에는 어휘 사전 구축 + 텐서 변환 + 패딩 한 번에 진행

class Lang:
    def __init__(self, name):
        self.name = name
        self.word2index = {} # 단어 -> 인덱스
        self.index2word = {} # 인덱스 -> 단어
        self.word2count = {} # 단어 등장 횟수
        self.n_words = 4 # 전체 개순데 특수 토큰 4개 먼저 등록된 상태로 시작

        # 특수 토큰 미리 등록
        self.addWord("<pad>")
        self.addWord("<sos>")
        self.addWord("<eos>")
        self.addWord("<unk>")

    def addSentence(self, sentence, tokenizer):
        for word in tokenizer(sentence):
            self.addWord(word)

    def addWord(self, word):
        if word not in self.word2index:
            self.word2index[word] = self.n_words # 딕셔너리로 저장
            self.index2word[self.n_words] = word
            self.word2count[word] = 1
            self.n_words += 1
        else:
            self.word2count[word] += 1

    # addSentence로 문장 받아서 addWord로 보내서 어휘 사전에 Word 추가
input_lang = Lang("ko") # input_lang : 한국어 문장을 위한 어휘 사전 인스턴스
output_lang = Lang("en") # output_lang : 영어 문장을 위한 어휘 사전 인스턴스

for ko, en in tokenized_pairs:
    input_lang.addSentence(" ".join(ko), tokenize_ko)
    output_lang.addSentence(" ".join(en), tokenize_en)

########################################################
# tokenized_pairs 리스트
#[
#  (['안녕하세요', '.'], ['Hello', '.']),
#  (['저는', '학생입니다'], ['I', 'am', 'a', 'student']),
#  ...
#]
########################################################

print(f"한국어 어휘 수: {input_lang.n_words}")
print(f"영어 어휘 수: {output_lang.n_words}")
더보기

한국어 어휘 수: 170947

영어 어휘 수: 35294

문장(토큰들)을 숫자 인덱스 시퀀스로 변환하고 텐서로 바꾸는 작업

토큰 리스트 -> 숫자 인덱스 리스트 -> 패딩 처리 -> PyTorch 텐서로 변환 해야 함

문장 하나를 인덱스 텐서로 바꾸는 함수

import torch

def tensorFromSentence(lang, sentence, tokenizer, max_length):
    indexes = [SOS_token] # 시작 토큰 추가
    for word in tokenizer(sentence):
        index = lang.word2index.get(word, UNK_token) # 단어를 인덱스로 변환
        indexes.append(index)
    indexes.append(EOS_token) # 끝 토큰 추가

    # 패딩
    while len(indexes) < MAX_LENGTH:
        indexes.append(PAD_token)

    return torch.tensor(indexes[:max_length], dtype = torch.long)

# 파이토치 형태로 싹 바꾸기

input_tensors =  [tensorFromSentence(input_lang, " ".join(pair[0]), tokenize_ko, MAX_LENGTH) for pair in tokenized_pairs]
output_tensors = [tensorFromSentence(output_lang, " ".join(pair[1]), tokenize_en, MAX_LENGTH) for pair in tokenized_pairs]

근데 왜 리스트를 문자열로 만들었다가 다시 토크나이징 하는 걸까?

이미 tokenized_pairs의 각 문장은 토큰화된 리스트 형태로 저장돼 있는데?

-> Lang 클래스의 addSentence나 tensorFromSentence가 문자열을 받는 설계이기 때문!

 

+_+ 이후 데이터 로더 구성 ~~

 

정리

단계 예시 입력 예시 출력

정제 I'm a student! i am a student
토큰화 i am a student ["i", "am", "a", "student"]
인덱싱 ["i", "am", ...] [4, 7, 2, 10]
특수토큰 추가 [4, 7, 2, 10] [1, 4, 7, 2, 10, 2]
패딩 [1, 4, 7, 2, 10, 2] [1, 4, 7, 2, 10, 2, 0, 0, 0, 0]
텐서 변환 [1, 4, 7, 2, ...] torch.tensor([...])

 

'Weekly Question' 카테고리의 다른 글

Q. Word2Vec와 FastText의 차이점  (0) 2025.04.16