본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 30. · 3 Views
LLM 훈련 데이터 정제 완벽 가이드
LLM 모델의 성능을 좌우하는 훈련 데이터 정제 기법을 초급자도 이해할 수 있도록 실무 사례와 함께 설명합니다. 데이터 품질 문제부터 중복 제거, 다국어 처리까지 모든 과정을 다룹니다.
목차
1. 데이터 정제의 중요성
AI 스타트업에 입사한 지 한 달 된 김개발 씨는 오늘 처음으로 LLM 훈련 프로젝트에 투입되었습니다. 팀장님이 "데이터 정제가 제일 중요해요"라고 강조하셨지만, 정확히 무슨 의미인지 감이 잡히지 않았습니다.
도대체 데이터 정제가 왜 그렇게 중요한 걸까요?
데이터 정제는 LLM 모델의 성능을 결정짓는 가장 핵심적인 과정입니다. 마치 요리할 때 신선한 재료를 골라내고 손질하는 것처럼, 훈련에 사용할 데이터에서 불필요하거나 잘못된 정보를 제거하는 작업입니다.
좋은 데이터로 훈련된 작은 모델이 나쁜 데이터로 훈련된 큰 모델보다 성능이 좋다는 연구 결과도 있습니다.
다음 코드를 살펴봅시다.
# 데이터 품질 검증 기본 예제
def check_data_quality(text):
# 최소 길이 검증: 너무 짧은 텍스트는 학습에 도움이 안 됨
if len(text) < 50:
return False, "텍스트가 너무 짧습니다"
# 특수문자 비율 검증: 비정상적으로 많으면 노이즈
special_chars = sum(not c.isalnum() and not c.isspace() for c in text)
if special_chars / len(text) > 0.3:
return False, "특수문자 비율이 너무 높습니다"
# 반복 패턴 검증: 같은 문자가 계속 반복되는지 확인
if any(char * 10 in text for char in set(text)):
return False, "비정상적인 반복 패턴이 발견되었습니다"
return True, "정상 데이터"
김개발 씨는 첫 업무로 인터넷에서 수집한 100만 건의 텍스트 데이터를 검토하는 임무를 받았습니다. 데이터를 하나하나 들여다보니 이상한 점이 한두 가지가 아니었습니다.
깨진 문자, 의미 없는 반복, 광고 문구로 가득한 텍스트들이 눈에 띄었습니다. "이런 데이터로 모델을 훈련시키면 어떻게 될까요?" 김개발 씨가 옆자리 선배인 박시니어 씨에게 물었습니다.
박시니어 씨는 쓴웃음을 지으며 말했습니다. "작년에 그렇게 훈련시킨 모델이 있었어요.
사용자 질문에 광고 문구를 답변으로 내놓더라고요." 데이터 정제란 정확히 무엇일까요? 쉽게 비유하자면, 데이터 정제는 마치 금을 채굴한 후 불순물을 제거하는 제련 과정과 같습니다.
원석에는 귀중한 금과 함께 흙, 돌, 다른 금속이 섞여 있습니다. 이 불순물을 제거하지 않으면 순도 높은 금을 얻을 수 없습니다.
LLM 훈련 데이터도 마찬가지입니다. 웹에서 수집한 텍스트에는 유용한 정보와 함께 노이즈가 섞여 있습니다.
데이터 정제가 없던 초기 LLM 개발 시절에는 어땠을까요? 연구자들은 "데이터가 많으면 많을수록 좋다"는 생각으로 무작정 데이터를 긁어모았습니다.
그 결과 모델은 웹페이지의 광고 문구를 학습했고, 깨진 인코딩 문자를 정상적인 텍스트처럼 생성했으며, 저작권 있는 콘텐츠를 그대로 복사하는 문제가 발생했습니다. 더 큰 문제는 편향된 데이터를 학습해서 특정 집단에 대한 차별적 발언을 하는 모델이 탄생했다는 점입니다.
바로 이런 문제를 해결하기 위해 체계적인 데이터 정제 과정이 필수가 되었습니다. 데이터 정제를 제대로 하면 모델의 정확도가 크게 향상됩니다.
GPT-3 논문에서도 데이터 정제에 상당한 노력을 기울였다고 밝혔습니다. 또한 모델이 생성하는 답변의 품질이 일관되게 유지됩니다.
무엇보다 편향성과 유해한 콘텐츠를 사전에 차단할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 5번째 줄에서는 텍스트의 최소 길이를 검증합니다. "안녕" 같은 너무 짧은 텍스트는 학습에 도움이 되지 않기 때문입니다.
다음으로 9번째 줄에서는 특수문자의 비율을 계산합니다. 정상적인 문장에는 특수문자가 30퍼센트를 넘기 어렵습니다.
만약 넘는다면 깨진 데이터이거나 스팸일 가능성이 높습니다. 마지막으로 13번째 줄에서는 같은 문자가 10번 이상 연속으로 나타나는지 검사합니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 챗봇 서비스를 개발하는 회사라고 가정해봅시다.
고객 문의 데이터를 수집해서 LLM을 파인튜닝하려고 합니다. 이때 데이터 정제를 거치지 않으면 테스트 데이터, 스팸 문의, 욕설이 포함된 데이터가 그대로 학습됩니다.
반면 정제 과정을 거치면 의미 있는 고객 문의만 선별되어 모델의 품질이 크게 향상됩니다. 네이버, 카카오 같은 대기업에서도 자체 LLM을 만들 때 데이터 정제에 수개월을 투자한다고 알려져 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 너무 엄격한 기준으로 데이터를 필터링하는 것입니다.
예를 들어 "특수문자가 하나라도 있으면 제거"라는 규칙을 만들면 "C++ 프로그래밍" 같은 정상적인 데이터까지 걸러집니다. 따라서 도메인 특성을 고려한 유연한 기준이 필요합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 "아, 그래서 팀장님이 데이터 정제가 제일 중요하다고 하셨군요"라고 말했습니다.
데이터 정제를 제대로 이해하면 LLM의 성능을 좌우하는 핵심 요소를 다룰 수 있습니다. 모델의 크기를 두 배로 늘리는 것보다 데이터 품질을 두 배로 높이는 것이 더 효과적일 때가 많습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 데이터 정제 기준은 한 번에 완벽하게 만들 수 없습니다. 샘플 데이터로 실험하며 점진적으로 개선하세요
- 정제 전후 데이터셋의 통계를 비교해서 과도하게 데이터를 버리지 않았는지 확인하세요
- 정제된 데이터로 작은 모델을 먼저 훈련해보고 품질을 검증한 후 본격적인 훈련에 사용하세요
2. 일반적인 데이터 품질 문제
김개발 씨는 데이터 정제의 중요성을 깨달은 후, 본격적으로 100만 건의 데이터를 분석하기 시작했습니다. 하지만 어떤 문제를 찾아야 할지 막막했습니다.
박시니어 씨가 "실무에서 자주 마주치는 문제 패턴이 있어요"라며 노트북을 열어 보여주었습니다.
LLM 훈련 데이터에는 몇 가지 전형적인 품질 문제가 반복적으로 나타납니다. 인코딩 오류로 인한 깨진 문자, HTML 태그나 JavaScript 코드 같은 마크업 잔재, 과도한 공백이나 줄바꿈, 그리고 중복 데이터가 대표적입니다.
이런 문제를 자동으로 탐지하고 수정하는 파이프라인을 구축하는 것이 데이터 엔지니어의 핵심 업무입니다.
다음 코드를 살펴봅시다.
import re
from html import unescape
def clean_common_issues(text):
# HTML 엔티티 디코딩: → 공백, < → < 등
text = unescape(text)
# HTML 태그 제거: <div>, <span> 등
text = re.sub(r'<[^>]+>', '', text)
# JavaScript 코드 블록 제거
text = re.sub(r'<script.*?</script>', '', text, flags=re.DOTALL)
# 과도한 공백을 하나로 통일
text = re.sub(r'\s+', ' ', text)
# 앞뒤 공백 제거
text = text.strip()
return text
박시니어 씨가 화면에 띄운 데이터를 보니 정말 다양한 문제가 있었습니다. 어떤 텍스트는 중간중간 이상한 기호가 섞여 있었고, 어떤 것은 HTML 코드가 그대로 남아 있었습니다.
"이런 것들을 일일이 손으로 고칠 순 없잖아요?" 김개발 씨가 물었습니다. "맞아요.
그래서 자동화된 정제 파이프라인을 만드는 거예요." 박시니어 씨가 답했습니다. "실무에서 자주 마주치는 문제 유형을 알면 대부분 자동으로 처리할 수 있어요." 가장 흔한 문제는 무엇일까요?
첫 번째는 인코딩 오류입니다. 마치 외국어로 된 편지를 잘못된 사전으로 번역하면 이상한 문장이 나오는 것처럼, 텍스트도 잘못된 인코딩으로 읽으면 깨진 문자가 나타납니다.
예를 들어 UTF-8로 저장된 한글을 EUC-KR로 읽으면 "한글"이 "한글"로 보입니다. 웹에서 크롤링한 데이터는 다양한 인코딩이 섞여 있어서 이 문제가 특히 많이 발생합니다.
두 번째는 마크업 잔재입니다. 웹페이지에서 텍스트를 추출할 때 HTML 태그가 완전히 제거되지 않는 경우가 많습니다.
<div>, <span> 같은 태그가 텍스트 중간에 끼어 있거나, 같은 HTML 엔티티가 변환되지 않은 채로 남아 있습니다. 심지어 JavaScript 코드가 통째로 포함되는 경우도 있습니다.
세 번째는 공백 문제입니다. 어떤 데이터는 단어 사이에 공백이 여러 개 들어가 있고, 어떤 것은 줄바꿈이 수십 번 반복됩니다.
PDF에서 텍스트를 추출할 때 이런 문제가 자주 생깁니다. 페이지 레이아웃 정보가 그대로 남아서 불필요한 공백과 줄바꿈이 생기기 때문입니다.
네 번째는 중복 데이터입니다. 같은 뉴스 기사가 여러 사이트에 복사되어 있거나, 포럼 게시글이 여러 번 크롤링되는 경우가 있습니다.
중복 데이터로 모델을 훈련시키면 특정 패턴을 과도하게 학습하는 과적합 문제가 생깁니다. 이런 문제를 방치하면 어떻게 될까요?
실제로 있었던 사례입니다. 한 팀이 뉴스 기사 데이터로 요약 모델을 훈련시켰는데, 생성된 요약문에 <div class="article"> 같은 HTML 태그가 포함되어 나왔습니다.
데이터 정제 과정에서 HTML 태그를 제대로 제거하지 않았기 때문입니다. 사용자들은 이상한 기호가 섞인 요약문을 보고 서비스에 대한 신뢰를 잃었습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 5번째 줄의 unescape 함수는 HTML 엔티티를 실제 문자로 변환합니다.
는 공백으로, <는 < 기호로 바뀝니다. 다음으로 8번째 줄의 정규표현식은 여는 꺾쇠와 닫는 꺾쇠 사이의 모든 내용을 제거합니다.
이렇게 하면 대부분의 HTML 태그가 사라집니다. 11번째 줄은 조금 더 특별합니다.
JavaScript 코드는 여러 줄에 걸쳐 있을 수 있으므로 re.DOTALL 플래그를 사용해서 줄바꿈을 포함한 모든 문자를 매칭합니다. 14번째 줄은 연속된 공백 문자를 하나의 공백으로 통일합니다.
실제 프로젝트에서는 어떻게 사용할까요? 대형 포털 사이트에서 뉴스 추천 AI를 만든다고 가정해봅시다.
수백만 건의 뉴스 기사를 수집했는데, 각 언론사마다 HTML 구조가 다릅니다. 어떤 곳은 기사 본문에 광고 코드가 들어가 있고, 어떤 곳은 SNS 공유 버튼의 텍스트가 포함되어 있습니다.
위 코드 같은 정제 함수를 적용하면 이런 노이즈를 대부분 제거할 수 있습니다. 네이버나 다음 같은 포털도 기사 수집 후 비슷한 정제 과정을 거칩니다.
하지만 주의할 점이 있습니다. 정규표현식으로 HTML을 처리하는 것은 완벽하지 않습니다.
복잡한 HTML 구조는 정규표현식만으로 파싱하기 어렵기 때문입니다. 따라서 실무에서는 BeautifulSoup이나 lxml 같은 전문 파싱 라이브러리를 함께 사용하는 것이 좋습니다.
또한 텍스트마다 문제 유형이 다를 수 있으므로 샘플 데이터를 검토하며 정제 규칙을 지속적으로 개선해야 합니다. 김개발 씨는 박시니어 씨가 보여준 예제를 자신의 데이터에 적용해보았습니다.
놀랍게도 100만 건 중 30만 건 이상에서 HTML 태그가 발견되었습니다. "이걸 그대로 훈련에 사용했다면 큰일 날 뻔했네요." 데이터 품질 문제를 미리 파악하고 자동화된 정제 파이프라인을 구축하면 훨씬 깨끗한 데이터셋을 만들 수 있습니다.
문제 유형을 알면 해결책도 보입니다. 여러분도 자신의 데이터에서 어떤 문제가 가장 많이 나타나는지 먼저 파악해보세요.
실전 팁
💡 - 정제 전 데이터 샘플 100개 정도를 육안으로 검토하며 주요 문제 패턴을 파악하세요
- 정규표현식보다 전문 파싱 라이브러리가 더 안전할 때가 많습니다
- 정제 과정에서 제거된 텍스트를 로그로 남겨서 혹시 중요한 정보를 잘못 삭제하지 않았는지 확인하세요
3. 텍스트 전처리 기법
데이터의 명백한 오류를 수정한 김개발 씨는 이제 텍스트를 모델이 학습하기 좋은 형태로 가공해야 했습니다. 박시니어 씨가 "이제 본격적인 전처리를 해볼까요?"라며 새로운 코드를 보여주었습니다.
전처리라는 말은 들어봤지만 구체적으로 무엇을 하는 건지 궁금했습니다.
텍스트 전처리는 원시 텍스트를 모델이 효과적으로 학습할 수 있는 형태로 변환하는 과정입니다. 대소문자 통일, 구두점 정규화, 숫자 처리, 불용어 제거 등이 포함됩니다.
다만 LLM 시대에는 과거 전통적인 NLP와 달리 원문을 최대한 보존하는 것이 중요하므로, 필요한 최소한의 전처리만 수행하는 것이 트렌드입니다.
다음 코드를 살펴봅시다.
import re
import unicodedata
def preprocess_text(text):
# 유니코드 정규화: 같은 글자의 다른 표현을 통일
text = unicodedata.normalize('NFC', text)
# URL 제거 또는 특수 토큰으로 대체
text = re.sub(r'https?://\S+', '[URL]', text)
# 이메일 주소 처리
text = re.sub(r'\S+@\S+', '[EMAIL]', text)
# 연속된 구두점 정리 (!!!!! → !!)
text = re.sub(r'([!?.])\1{2,}', r'\1\1', text)
# 제어 문자 제거 (줄바꿈, 탭 제외)
text = ''.join(ch for ch in text if ch == '\n' or ch == '\t' or not unicodedata.category(ch).startswith('C'))
return text
김개발 씨가 전처리된 텍스트를 보니 확실히 깔끔해 보였습니다. 하지만 왜 URL을 삭제하지 않고 [URL]이라는 특수 토큰으로 바꾼 건지 궁금했습니다.
"URL을 그냥 삭제하면 안 되나요?" 박시니어 씨가 설명했습니다. "문맥을 보존하기 위해서예요.
예를 들어 '자세한 내용은 https://example.com 을 참고하세요'라는 문장에서 URL만 삭제하면 '자세한 내용은 을 참고하세요'가 되잖아요. 어색하죠.
[URL]로 바꾸면 '자세한 내용은 [URL] 을 참고하세요'가 되어서 문맥이 유지돼요." 텍스트 전처리는 정확히 무엇을 의미할까요? 쉽게 비유하자면, 전처리는 요리하기 전 재료를 다듬는 과정과 같습니다.
생선을 요리하기 전에 비늘을 긁고 내장을 제거하지만, 생선 자체는 그대로 유지합니다. 너무 많이 손질하면 정작 먹을 부분이 없어지기 때문입니다.
텍스트 전처리도 마찬가지입니다. 노이즈는 제거하되 의미 있는 정보는 최대한 보존해야 합니다.
과거 전통적인 NLP에서는 어땠을까요? 10년 전만 해도 텍스트를 아주 많이 가공했습니다.
모든 단어를 소문자로 바꾸고, 어간 추출을 해서 "running", "ran", "runs"를 모두 "run"으로 통일했습니다. 불용어라고 부르는 "the", "a", "is" 같은 단어를 삭제했습니다.
당시 모델들은 학습 능력이 제한적이어서 데이터를 단순화해야 했기 때문입니다. 하지만 LLM 시대에는 접근법이 달라졌습니다.
GPT나 BERT 같은 대형 언어 모델은 원문의 뉘앙스를 학습할 수 있습니다. "Running"과 "run"의 미묘한 차이, "The"가 있을 때와 없을 때의 의미 차이를 구분할 수 있습니다.
따라서 과도한 전처리는 오히려 정보 손실을 일으킵니다. 현대 LLM 훈련에서는 원문을 최대한 보존하면서 명백한 노이즈만 제거하는 것이 원칙입니다.
그렇다면 어떤 전처리가 필요할까요? 첫째, 유니코드 정규화입니다.
한글 "가"를 표현하는 방법이 여러 가지입니다. 완성형으로 저장할 수도 있고, "ㄱ" + "ㅏ"로 분리해서 저장할 수도 있습니다.
컴퓨터는 이 둘을 다른 문자로 인식하지만 사람 눈에는 똑같이 보입니다. 유니코드 정규화는 이런 변형을 하나의 표준 형태로 통일합니다.
둘째, 민감 정보 처리입니다. URL, 이메일, 전화번호, 주민등록번호 같은 개인정보는 특수 토큰으로 대체하거나 제거합니다.
이는 개인정보 보호뿐만 아니라 모델이 불필요하게 특정 URL이나 이메일을 암기하는 것을 방지합니다. 셋째, 극단적인 노이즈 제거입니다.
느낌표가 50개 연속으로 나오는 텍스트는 비정상적입니다. 이런 과도한 반복은 2~3개 정도로 제한하는 것이 좋습니다.
또한 제어 문자 중 출력할 수 없는 문자는 제거합니다. 위의 코드를 자세히 살펴보겠습니다.
5번째 줄의 unicodedata.normalize('NFC', text)는 유니코드를 정규화합니다. NFC는 Normalization Form Canonical Composition의 약자로, 문자를 가능한 한 완성형으로 합치는 방식입니다.
8번째 줄은 http나 https로 시작하는 URL을 찾아서 [URL] 토큰으로 대체합니다. 11번째 줄은 이메일 주소를 비슷한 방식으로 처리합니다.
14번째 줄은 흥미롭습니다. ([!?.])\1{2,}라는 정규표현식은 느낌표, 물음표, 마침표가 3번 이상 반복되는 패턴을 찾아서 2번만 반복되도록 줄입니다.
17번째 줄은 제어 문자를 필터링하는데, 줄바꿈과 탭은 의미가 있으므로 보존합니다. 실제 서비스에서는 어떻게 활용될까요?
카카오톡 챗봇을 만든다고 가정해봅시다. 사용자들의 대화 로그를 수집해서 모델을 훈련시키려고 합니다.
대화 중에는 URL이 자주 등장합니다. "이거 봐 https://..."처럼요.
이런 URL을 모두 학습시키면 모델이 무의미하게 URL 문자열을 암기합니다. [URL]로 대체하면 "링크가 공유되었다"는 패턴만 학습하고, 구체적인 URL은 암기하지 않습니다.
실제로 많은 기업이 이런 방식으로 전처리합니다. 주의해야 할 점이 있습니다.
코드 데이터를 다룬다면 전처리 방식이 완전히 달라집니다. 프로그래밍 코드에서는 대소문자가 의미를 가지며, URL도 중요한 정보일 수 있습니다.
따라서 도메인에 따라 전처리 규칙을 조정해야 합니다. 또한 너무 공격적으로 전처리하면 정보가 손실되므로, 샘플 데이터로 실험하며 적절한 균형점을 찾아야 합니다.
김개발 씨는 전처리 코드를 실행해보았습니다. 이메일 주소와 URL이 가득했던 데이터가 훨씬 깔끔해졌습니다.
"이제 모델이 학습하기 좋은 형태가 됐군요." 텍스트 전처리는 과거처럼 공격적으로 하는 것이 아니라, 필요한 최소한만 수행하는 것이 현대적인 접근법입니다. 원문의 정보를 최대한 보존하면서 명백한 노이즈만 제거하세요.
여러분의 데이터 도메인에 맞는 전처리 파이프라인을 설계해보세요.
실전 팁
💡 - 전처리 규칙을 추가할 때마다 샘플 데이터 100개를 검토하며 부작용이 없는지 확인하세요
- 개인정보 관련 규칙은 GDPR, 개인정보보호법 등 법적 요구사항을 고려하세요
- 전처리 전후 텍스트를 비교할 수 있는 시각화 도구를 만들면 디버깅이 쉬워집니다
4. 다국어 데이터 처리
한국어 데이터 정제에 자신감이 붙은 김개발 씨에게 새로운 임무가 떨어졌습니다. 팀장님이 "이번에는 영어, 일본어, 중국어 데이터도 함께 처리해야 해요"라고 말씀하셨습니다.
한국어만 해도 복잡했는데, 여러 언어를 동시에 다뤄야 한다니 막막했습니다.
다국어 데이터 처리는 각 언어의 특성을 고려한 정제 전략이 필요합니다. 언어 감지로 섞인 언어를 분리하고, 언어별 인코딩 문제를 해결하며, 문자 체계에 맞는 전처리를 적용해야 합니다.
특히 한중일 언어는 유니코드 범위가 겹치는 문자가 있어서 주의가 필요합니다.
다음 코드를 살펴봅시다.
from langdetect import detect, LangDetectException
import re
def process_multilingual_text(text):
try:
# 언어 감지
lang = detect(text)
except LangDetectException:
return None, "언어 감지 실패"
# 언어별 특수 처리
if lang == 'ko':
# 한국어: 자음/모음만 있는 불완전한 글자 제거
text = re.sub(r'[ㄱ-ㅎㅏ-ㅣ]+', '', text)
elif lang == 'ja':
# 일본어: 반각 카타카나를 전각으로 통일
text = text.translate(str.maketrans('アイウエオ', 'アイウエオ'))
elif lang == 'zh-cn' or lang == 'zh-tw':
# 중국어: 번체/간체 혼용 확인 필요
pass
return text, lang
박시니어 씨가 다국어 데이터 샘플을 보여주었습니다. 한 문장 안에 한국어, 영어, 일본어가 섞여 있는 텍스트도 있었고, 중국어 간체와 번체가 뒤섞인 것도 있었습니다.
"이런 데이터를 어떻게 처리하죠?" 김개발 씨가 물었습니다. "먼저 언어를 정확히 감지하는 게 중요해요.
그리고 각 언어의 특성에 맞게 처리해야 합니다." 박시니어 씨가 답했습니다. 다국어 데이터 처리는 왜 복잡할까요?
마치 세계 여행을 떠날 때 나라마다 다른 전압과 플러그를 사용하는 것과 같습니다. 한국은 220V를 쓰고, 미국은 110V를 씁니다.
플러그 모양도 다릅니다. 전자기기를 가져갈 때 각 나라에 맞는 어댑터가 필요합니다.
언어도 마찬가지입니다. 각 언어마다 문자 체계, 인코딩 방식, 띄어쓰기 규칙이 다르므로 언어별로 적절한 처리가 필요합니다.
가장 먼저 해결해야 할 문제는 언어 감지입니다. 텍스트가 어떤 언어인지 알아야 적절한 처리를 할 수 있습니다.
langdetect 같은 라이브러리는 통계적 방법으로 언어를 감지합니다. 하지만 완벽하지는 않습니다.
특히 짧은 텍스트나 여러 언어가 섞인 텍스트에서는 오감지가 발생합니다. "Hello world"처럼 영어 단어만 있는 짧은 문장도 때로는 네덜란드어로 감지되기도 합니다.
한국어 데이터에는 특별한 문제가 있습니다. 한글은 자음과 모음을 조합해서 글자를 만듭니다.
그런데 웹 데이터를 수집하다 보면 "ㅋㅋㅋ", "ㅇㅇ" 같은 불완전한 자음/모음만 있는 텍스트가 나옵니다. 이런 것들은 정상적인 문장이 아니므로 제거하는 것이 좋습니다.
또한 한국어는 띄어쓰기가 의미를 결정하는데, 웹 텍스트는 띄어쓰기가 엉망인 경우가 많습니다. 일본어는 또 다른 복잡함이 있습니다.
일본어는 히라가나, 카타카나, 한자를 혼용합니다. 여기에 반각 문자와 전각 문자가 있어서 같은 "ア"를 "ア"(반각)와 "ア"(전각) 두 가지로 표현할 수 있습니다.
컴퓨터는 이 둘을 다른 문자로 인식하므로 하나로 통일해야 합니다. 일반적으로 전각으로 통일하는 것이 표준입니다.
중국어는 간체와 번체 문제가 있습니다. 중국 본토는 간체자를 쓰고, 대만과 홍콩은 번체자를 씁니다.
같은 의미의 단어가 다른 문자로 표현됩니다. 예를 들어 "학습"이라는 뜻의 단어가 간체로는 "学习", 번체로는 "學習"입니다.
만약 간체 데이터와 번체 데이터가 섞여 있다면 하나로 통일하거나, 적어도 어떤 체계인지 태깅해야 합니다. 위의 코드를 살펴보겠습니다.
6번째 줄의 detect 함수는 텍스트의 언어를 감지합니다. 반환값은 'ko', 'en', 'ja' 같은 ISO 언어 코드입니다.
다만 이 함수는 텍스트가 너무 짧거나 특수문자만 있으면 예외를 발생시키므로 try-except로 처리합니다. 13번째 줄에서는 한국어 텍스트에서 자음/모음만 있는 부분을 제거합니다.
[ㄱ-ㅎㅏ-ㅣ]는 단독 자음과 모음을 매칭하는 정규표현식입니다. 16번째 줄은 일본어 반각 카타카나를 전각으로 변환합니다.
실제로는 모든 카타카나 문자에 대해 이 작업을 해야 하므로 더 복잡한 변환 테이블이 필요합니다. 실제 글로벌 서비스에서는 어떻게 활용될까요?
구글 번역기나 파파고 같은 서비스를 생각해봅시다. 사용자가 입력한 텍스트의 언어를 자동으로 감지하고, 각 언어에 맞는 전처리를 거쳐서 번역 모델에 입력합니다.
만약 한국어 텍스트에서 "ㅋㅋㅋ"를 제거하지 않으면 번역 결과에 이상한 문자가 나올 수 있습니다. 일본어 반각/전각을 통일하지 않으면 같은 단어를 다른 것으로 인식해서 번역 품질이 떨어집니다.
주의할 점이 있습니다. 언어 감지 라이브러리는 100퍼센트 정확하지 않습니다.
특히 한국어와 일본어처럼 한자를 공유하는 언어는 구분이 어려울 때가 있습니다. 따라서 감지 결과를 맹신하지 말고, 샘플 데이터를 검토하며 정확도를 확인해야 합니다.
또한 코드 예제에서 보여준 것은 기본적인 처리이고, 실제로는 각 언어마다 훨씬 더 세밀한 규칙이 필요합니다. 김개발 씨는 다국어 데이터를 언어별로 분리한 후 각각 적절한 전처리를 적용했습니다.
한국어에서는 "ㅋㅋㅋ" 같은 패턴이 수천 건 제거되었고, 일본어에서는 반각/전각 불일치가 해결되었습니다. "언어마다 고려할 점이 정말 많네요." 다국어 LLM을 만들 때는 각 언어의 특성을 이해하고 적절한 전처리를 적용하는 것이 핵심입니다.
하나의 규칙을 모든 언어에 적용하면 안 됩니다. 여러분도 다루는 언어의 특성을 깊이 공부하며 처리 파이프라인을 구축해보세요.
실전 팁
💡 - 언어 감지 결과를 데이터에 메타데이터로 저장해두면 나중에 언어별 분석이 쉬워집니다
- 다국어 데이터셋의 언어 분포가 너무 불균형하면 모델이 특정 언어에 편향될 수 있으니 샘플링 비율을 조정하세요
- OpenCC 같은 라이브러리를 사용하면 중국어 간체/번체 변환을 쉽게 할 수 있습니다
5. 중복 제거 전략
언어별 전처리까지 마친 김개발 씨는 데이터셋의 크기를 확인하고 깜짝 놀랐습니다. 100만 건이어야 할 데이터가 처리 후에도 95만 건이나 남아 있었습니다.
박시니어 씨가 "아직 중복 제거를 안 했잖아요"라며 웃었습니다. "중복이 그렇게 많나요?"
중복 데이터는 LLM 훈련에서 과적합을 일으키는 주요 원인입니다. 완전히 동일한 텍스트뿐만 아니라 거의 유사한 텍스트도 제거해야 합니다.
단순 해시 비교는 빠르지만 유사 중복을 찾지 못하고, MinHash LSH 같은 기법은 근사 유사도를 빠르게 계산할 수 있습니다. 대규모 데이터셋에서는 효율적인 중복 제거 알고리즘이 필수입니다.
다음 코드를 살펴봅시다.
from datasketch import MinHash, MinHashLSH
import hashlib
def create_minhash(text, num_perm=128):
# MinHash 객체 생성
m = MinHash(num_perm=num_perm)
# 텍스트를 단어 단위로 분리해서 해싱
words = text.split()
for word in words:
m.update(word.encode('utf-8'))
return m
def deduplicate_dataset(texts, threshold=0.8):
# LSH 인덱스 생성 (Jaccard 유사도 threshold 설정)
lsh = MinHashLSH(threshold=threshold, num_perm=128)
unique_texts = []
for idx, text in enumerate(texts):
minhash = create_minhash(text)
# 유사한 문서가 이미 있는지 확인
result = lsh.query(minhash)
if len(result) == 0:
# 유사한 게 없으면 추가
lsh.insert(idx, minhash)
unique_texts.append(text)
return unique_texts
박시니어 씨가 중복 데이터 샘플을 보여주었습니다. 어떤 뉴스 기사는 제목만 약간 다르고 본문은 완전히 같았습니다.
어떤 포럼 게시글은 같은 내용을 여러 번 복사해서 올린 것이었습니다. "이런 게 왜 문제가 되나요?" 김개발 씨가 물었습니다.
"모델이 같은 내용을 수십 번 보면 그것만 외우게 돼요. 다양성이 떨어지는 거죠." 박시니어 씨가 설명했습니다.
중복 제거는 왜 중요할까요? 쉽게 비유하자면, 같은 문제집을 열 번 푸는 것과 열 가지 다른 문제집을 한 번씩 푸는 것의 차이입니다.
같은 문제만 반복하면 그 문제는 완벽히 풀 수 있지만, 새로운 유형의 문제는 못 풉니다. 이것이 과적합입니다.
LLM도 마찬가지입니다. 중복 데이터가 많으면 특정 패턴만 과도하게 학습해서 일반화 능력이 떨어집니다.
가장 단순한 중복 제거 방법은 무엇일까요? 텍스트 전체의 해시값을 계산해서 비교하는 방법입니다.
파이썬에서는 hashlib.md5(text.encode()).hexdigest() 같은 코드로 해시를 만들 수 있습니다. 같은 텍스트는 항상 같은 해시값을 가지므로 해시값을 집합에 넣어두고 중복을 체크하면 됩니다.
이 방법은 매우 빠르지만 치명적인 단점이 있습니다. 글자 하나만 달라도 완전히 다른 해시값이 나온다는 점입니다.
실제 웹 데이터에는 거의 같은 텍스트가 많습니다. 예를 들어 뉴스 기사가 여러 포털에 신디케이션되면서 맨 앞에 "단독" 같은 단어가 추가되거나, 맨 뒤에 "저작권자 ⓒ" 같은 문구가 붙습니다.
내용은 99퍼센트 같지만 단순 해시로는 다른 문서로 인식됩니다. 이런 유사 중복을 찾기 위해 고안된 것이 MinHash와 LSH입니다.
MinHash는 어떻게 작동할까요? MinHash는 문서를 여러 개의 해시값으로 표현하는 기법입니다.
문서를 단어나 n-gram으로 쪼갠 후, 각 조각마다 해시값을 계산합니다. 그중 가장 작은 값 몇 개를 선택해서 문서의 시그니처로 사용합니다.
두 문서가 비슷하면 선택되는 최소 해시값도 비슷합니다. 이렇게 만든 시그니처로 Jaccard 유사도를 근사할 수 있습니다.
LSH는 MinHash를 더 빠르게 만들어줍니다. 문서가 백만 건이면 모든 쌍을 비교하려면 백만 곱하기 백만 나누기 2번의 비교가 필요합니다.
이는 현실적으로 불가능합니다. LSH(Locality Sensitive Hashing)는 유사한 문서끼리 같은 버킷에 들어갈 확률이 높은 해시 함수를 사용합니다.
이렇게 하면 같은 버킷에 있는 문서들끼리만 비교하면 되므로 속도가 비약적으로 빨라집니다. 위의 코드를 분석해봅시다.
create_minhash 함수는 텍스트로부터 MinHash 시그니처를 생성합니다. num_perm=128은 128개의 해시 함수를 사용한다는 의미입니다.
많을수록 정확하지만 느려집니다. 8번째 줄에서 텍스트를 단어로 쪼개고, 각 단어를 MinHash 객체에 업데이트합니다.
deduplicate_dataset 함수는 실제 중복 제거를 수행합니다. 14번째 줄에서 LSH 인덱스를 만들 때 threshold=0.8은 Jaccard 유사도가 0.8 이상이면 중복으로 간주한다는 의미입니다.
20번째 줄의 lsh.query는 현재 문서와 유사한 문서가 이미 있는지 검색합니다. 없으면 인덱스에 추가하고 결과에 포함시킵니다.
실제 프로젝트에서는 어떻게 사용될까요? GPT-3를 훈련시킬 때 OpenAI는 대규모 중복 제거를 수행했다고 밝혔습니다.
Common Crawl 같은 웹 크롤 데이터는 같은 내용이 여러 사이트에 복사되어 있는 경우가 많습니다. 중복 제거 후 데이터셋 크기가 절반 이하로 줄어들기도 합니다.
하지만 모델 성능은 오히려 향상됩니다. 적은 데이터로 더 나은 결과를 얻는 것입니다.
주의할 점이 있습니다. threshold 값을 너무 높게 설정하면 유사한 문서를 잡아내지 못하고, 너무 낮게 설정하면 서로 다른 문서를 중복으로 잘못 판단합니다.
일반적으로 0.7에서 0.9 사이 값을 사용하며, 샘플 데이터로 실험하며 적절한 값을 찾아야 합니다. 또한 MinHash는 단어 순서를 고려하지 않으므로 단어 순서가 중요한 경우 다른 방법을 고려해야 합니다.
김개발 씨는 중복 제거를 실행했습니다. 95만 건의 데이터가 65만 건으로 줄어들었습니다.
무려 30만 건이 중복이었던 것입니다. "이렇게 많을 줄은 몰랐어요." 중복 제거는 데이터 효율성을 크게 높여줍니다.
적은 데이터로 더 좋은 모델을 만들 수 있습니다. 여러분도 MinHash LSH 같은 효율적인 알고리즘을 활용해서 대규모 데이터셋의 중복을 제거해보세요.
실전 팁
💡 - threshold 값은 도메인에 따라 다릅니다. 뉴스는 0.9, 소셜미디어는 0.7 정도가 적당할 수 있습니다
- 중복 제거 전후 샘플을 비교하며 실제로 중복이 맞는지 확인하세요
- 매우 큰 데이터셋은 청크로 나눠서 처리하고 나중에 병합하는 방식이 메모리 효율적입니다
6. 데이터 검증 및 품질 보증
모든 정제 과정을 마친 김개발 씨는 최종 데이터셋을 팀장님께 보고하려고 했습니다. 하지만 박시니어 씨가 "잠깐, 품질 검증은 했어요?"라고 물었습니다.
정제는 다 했는데 검증은 또 뭘까요? 박시니어 씨가 마지막 단계를 설명했습니다.
데이터 정제 후에는 반드시 품질 검증 단계를 거쳐야 합니다. 통계적 분석으로 이상치를 찾고, 샘플링 검증으로 정제 규칙이 제대로 작동했는지 확인하며, 언어 분포와 길이 분포가 적절한지 점검합니다.
자동화된 품질 보증 파이프라인을 구축하면 안정적인 데이터셋을 유지할 수 있습니다.
다음 코드를 살펴봅시다.
import numpy as np
from collections import Counter
def validate_dataset_quality(texts):
report = {}
# 길이 분포 분석
lengths = [len(text) for text in texts]
report['avg_length'] = np.mean(lengths)
report['median_length'] = np.median(lengths)
report['min_length'] = min(lengths)
report['max_length'] = max(lengths)
# 이상치 탐지: 평균에서 3 표준편차 벗어난 것
mean_len = np.mean(lengths)
std_len = np.std(lengths)
outliers = [i for i, l in enumerate(lengths)
if abs(l - mean_len) > 3 * std_len]
report['outlier_count'] = len(outliers)
# 중복 검사 (정확한 중복)
duplicates = len(texts) - len(set(texts))
report['exact_duplicates'] = duplicates
# 특수문자 비율 분석
special_char_ratios = []
for text in texts:
if len(text) > 0:
ratio = sum(not c.isalnum() and not c.isspace()
for c in text) / len(text)
special_char_ratios.append(ratio)
report['avg_special_char_ratio'] = np.mean(special_char_ratios)
return report
박시니어 씨가 품질 검증 보고서를 보여주었습니다. 평균 길이, 중앙값, 이상치 개수 같은 통계가 한눈에 정리되어 있었습니다.
"이런 숫자가 왜 필요한가요?" 김개발 씨가 물었습니다. "정제 과정에서 실수가 있었는지 확인하기 위해서예요.
예를 들어 평균 길이가 갑자기 절반으로 줄었다면 뭔가 잘못된 거예요." 박시니어 씨가 설명했습니다. 데이터 검증은 왜 필요할까요?
마치 공장에서 제품을 만든 후 품질 검사를 하는 것과 같습니다. 아무리 정교한 제조 공정을 거쳐도 불량품이 나올 수 있습니다.
따라서 출하 전에 샘플을 뽑아서 기준에 맞는지 확인합니다. 데이터 정제도 마찬가지입니다.
정제 스크립트를 완벽하게 작성했다고 생각해도 예상치 못한 엣지 케이스가 있을 수 있습니다. 검증 단계에서 이를 잡아냅니다.
어떤 항목을 검증해야 할까요? 첫째, 길이 분포입니다.
텍스트의 평균 길이, 중앙값, 최솟값, 최댓값을 확인합니다. 만약 최솟값이 0이라면 빈 텍스트가 있다는 의미이고, 이는 정제 과정에서 모든 내용이 삭제된 것입니다.
최댓값이 비정상적으로 크다면 정제가 제대로 안 된 노이즈 데이터일 수 있습니다. 둘째, 이상치 탐지입니다.
통계에서 흔히 사용하는 방법은 평균에서 3 표준편차 이상 벗어난 값을 이상치로 판단하는 것입니다. 예를 들어 대부분의 텍스트가 500자 내외인데 어떤 텍스트는 5만 자라면 이상치입니다.
이런 데이터를 수동으로 검토해서 정말 정상인지 확인해야 합니다. 셋째, 중복 검사입니다.
앞에서 중복 제거를 했지만, 완벽하지 않을 수 있습니다. 정확히 동일한 텍스트가 남아 있는지 집합을 이용해 다시 한번 확인합니다.
만약 중복이 많이 남아 있다면 중복 제거 알고리즘의 파라미터를 조정해야 합니다. 넷째, 특수문자 비율입니다.
정상적인 텍스트는 특수문자 비율이 일정 범위에 있습니다. 너무 높으면 정제가 덜 된 것이고, 너무 낮으면 과도하게 정제된 것일 수 있습니다.
평균 비율을 계산해서 기대 범위에 있는지 확인합니다. 다섯째, 언어 분포입니다.
다국어 데이터셋이라면 각 언어가 얼마나 포함되었는지 확인합니다. 특정 언어가 전체의 90퍼센트를 차지한다면 다국어 모델이라기보다 사실상 단일 언어 모델이 될 수 있습니다.
위의 코드를 살펴보겠습니다. 7번째 줄부터는 각 텍스트의 길이를 계산해서 리스트로 만듭니다.
NumPy의 mean, median 함수로 평균과 중앙값을 쉽게 구할 수 있습니다. 14번째 줄에서는 이상치를 탐지합니다.
평균에서 3 표준편차를 벗어난 텍스트의 인덱스를 리스트로 수집합니다. 21번째 줄의 중복 검사는 간단합니다.
원본 리스트 길이에서 집합으로 변환한 길이를 빼면 중복 개수가 나옵니다. 25번째 줄부터는 각 텍스트의 특수문자 비율을 계산합니다.
알파벳과 숫자가 아니고 공백도 아닌 문자의 비율을 구합니다. 실제 프로젝트에서는 어떻게 활용될까요?
대기업의 AI 팀은 데이터 파이프라인에 자동화된 품질 게이트를 설치합니다. 새로운 데이터가 정제되면 자동으로 검증 스크립트가 실행되고, 보고서가 생성됩니다.
만약 품질 지표가 기준을 벗어나면 알람이 울리고, 데이터 엔지니어가 문제를 조사합니다. 이렇게 하면 문제가 있는 데이터가 훈련 파이프라인으로 들어가는 것을 사전에 차단할 수 있습니다.
또 다른 중요한 검증 방법은 샘플링 검증입니다. 자동화된 통계만으로는 찾을 수 없는 문제가 있습니다.
따라서 최종 데이터셋에서 무작위로 100개 정도를 샘플링해서 사람이 직접 읽어봅니다. "이 데이터로 모델을 훈련시켜도 괜찮을까?"라는 질문을 스스로에게 던져봅니다.
만약 샘플 중 10퍼센트 이상이 이상하다면 전체 데이터셋에 문제가 있을 가능성이 높습니다. 주의할 점이 있습니다.
통계는 경향성을 보여줄 뿐 절대적인 기준이 아닙니다. 예를 들어 특수문자 비율이 높다고 무조건 나쁜 것은 아닙니다.
프로그래밍 코드 데이터셋이라면 특수문자가 많은 것이 정상입니다. 따라서 도메인 지식을 바탕으로 검증 기준을 설정해야 합니다.
또한 검증은 한 번으로 끝나지 않습니다. 데이터 수집과 정제는 지속적으로 이루어지므로 매번 검증 파이프라인을 실행해야 합니다.
김개발 씨는 검증 스크립트를 돌려보았습니다. 다행히 대부분의 지표가 정상 범위에 있었습니다.
하지만 이상치가 50개 정도 발견되었습니다. 샘플을 확인해보니 정제 규칙에서 놓친 특이한 형태의 노이즈였습니다.
규칙을 보완한 후 다시 정제하니 이상치가 사라졌습니다. "검증을 안 했으면 이런 문제를 모르고 넘어갈 뻔했네요." 김개발 씨가 말했습니다.
박시니어 씨가 고개를 끄덕였습니다. "데이터 정제는 한 번에 완벽하게 할 수 없어요.
검증하고 개선하는 과정을 반복해야 합니다." 데이터 검증은 정제 파이프라인의 마지막 안전망입니다. 자동화된 검증과 샘플링 검증을 모두 활용하세요.
여러분의 데이터셋 품질을 정량적으로 측정하고 지속적으로 개선해나가세요.
실전 팁
💡 - 검증 보고서를 버전별로 저장해두면 시간에 따른 데이터 품질 변화를 추적할 수 있습니다
- 팀 내에서 샘플링 검증 기준을 문서화해서 누가 검증해도 일관된 결과가 나오도록 하세요
- Pandas Profiling 같은 도구를 사용하면 데이터 분포를 시각화해서 한눈에 파악할 수 있습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Ansible Ad-hoc 명령어 실습 완벽 가이드
Ansible의 Ad-hoc 명령어를 통해 즉각적인 서버 관리 작업을 수행하는 방법을 배웁니다. 플레이북 없이도 빠르게 여러 서버를 제어할 수 있는 실전 기술을 익혀봅니다.
데이터 증강으로 LLM 학습 데이터 늘리기
적은 데이터로도 효과적인 AI 모델을 만들 수 있는 데이터 증강 기법을 소개합니다. 동의어 대체부터 최신 LLM 활용까지, 실무에서 바로 쓸 수 있는 6가지 핵심 전략을 배워봅니다.
LLM 디자인 패턴 완벽 가이드
언어 모델 개발에 필요한 핵심 디자인 패턴을 초보 개발자도 쉽게 이해할 수 있도록 실무 스토리와 비유로 풀어낸 가이드입니다. RAG부터 프롬프트 체이닝까지, 현업에서 바로 적용할 수 있는 패턴을 소개합니다.
Ansible 인벤토리 관리 완벽 가이드
서버 수백 대를 관리하는 당신, 매번 IP 주소 외우고 계신가요? Ansible 인벤토리로 서버 관리를 체계화하는 방법을 알아봅니다. 정적 인벤토리부터 동적 인벤토리까지, 실무에서 바로 활용할 수 있는 베스트 프랙티스를 담았습니다.
Ansible 소개 및 설치 완벽 가이드
서버 수십 대를 손쉽게 관리하는 자동화 도구 Ansible의 기초부터 설치, 첫 명령어 실행까지 배웁니다. 초급 개발자를 위한 실무 중심 입문 가이드입니다.