이미지 로딩 중...

커스텀 데이터셋 수집 및 전처리 완벽 가이드 - 슬라이드 1/11
A

AI Generated

2025. 11. 16. · 5 Views

커스텀 데이터셋 수집 및 전처리 완벽 가이드

AI 모델 학습을 위한 데이터셋을 직접 수집하고 전처리하는 전체 과정을 다룹니다. 웹 크롤링부터 데이터 정제, 라벨링, 검증까지 실무에서 바로 사용할 수 있는 방법을 배웁니다.


목차

  1. 웹 크롤링으로 데이터 수집하기 - BeautifulSoup과 Selenium 활용
  2. 결측치와 중복 데이터 처리하기 - pandas로 데이터 정제
  3. 텍스트 데이터 전처리하기 - 토큰화와 정규화
  4. 데이터 라벨링 자동화하기 - 규칙 기반과 약한 지도학습
  5. 데이터 증강으로 데이터셋 확장하기 - Augmentation 기법
  6. 데이터셋 분할하기 - Train, Validation, Test Split
  7. 이미지 데이터 수집 및 전처리하기 - 크롤링과 리사이징
  8. CSV와 JSON으로 메타데이터 관리하기 - 데이터셋 정보 저장
  9. 데이터 품질 검증하기 - 자동화된 품질 체크
  10. 데이터 버전 관리하기 - DVC로 데이터셋 추적

1. 웹 크롤링으로 데이터 수집하기 - BeautifulSoup과 Selenium 활용

시작하며

여러분이 AI 모델을 학습시키려는데, 공개 데이터셋으로는 부족한 상황을 겪어본 적 있나요? 특히 특정 도메인이나 한국어 데이터가 필요할 때, 원하는 품질과 양의 데이터를 찾기가 정말 어렵습니다.

이런 문제는 실제 AI 프로젝트에서 가장 먼저 마주하는 장벽입니다. 좋은 모델을 만들려면 좋은 데이터가 필수인데, 시중에 있는 데이터셋만으로는 한계가 있습니다.

결국 직접 데이터를 수집해야 하는 순간이 옵니다. 바로 이럴 때 필요한 것이 웹 크롤링 기술입니다.

웹사이트에서 필요한 데이터를 자동으로 수집하고, 원하는 형태로 가공하여 AI 학습에 바로 사용할 수 있는 데이터셋을 만들 수 있습니다.

개요

간단히 말해서, 웹 크롤링은 웹페이지에서 원하는 정보를 자동으로 추출하는 프로그래밍 기법입니다. 실무에서는 이미지 데이터, 텍스트 리뷰, 뉴스 기사, 상품 정보 등 다양한 종류의 데이터를 수집할 때 웹 크롤링을 활용합니다.

예를 들어, 감성 분석 모델을 만든다면 영화 리뷰 사이트에서 수만 개의 리뷰와 평점을 크롤링하여 학습 데이터로 사용할 수 있습니다. 기존에는 데이터를 하나하나 수동으로 복사해야 했다면, 이제는 크롤링 스크립트를 한 번 작성하면 수천, 수만 건의 데이터를 자동으로 수집할 수 있습니다.

웹 크롤링의 핵심 특징은 세 가지입니다. 첫째, 자동화를 통해 대량의 데이터를 빠르게 수집할 수 있습니다.

둘째, 원하는 형식으로 데이터를 구조화할 수 있어 후처리가 쉽습니다. 셋째, 정기적으로 실행하여 최신 데이터를 지속적으로 확보할 수 있습니다.

이러한 특징들이 AI 프로젝트의 데이터 파이프라인을 구축하는 데 필수적입니다.

코드 예제

import requests
from bs4 import BeautifulSoup
import pandas as pd
import time

# 크롤링할 URL 리스트
base_url = "https://example-review-site.com/reviews?page="
all_reviews = []

# 여러 페이지에서 리뷰 수집
for page in range(1, 11):  # 10페이지 크롤링
    # 페이지 요청
    response = requests.get(base_url + str(page))
    soup = BeautifulSoup(response.content, 'html.parser')

    # 리뷰 요소 찾기
    reviews = soup.find_all('div', class_='review-item')

    for review in reviews:
        # 텍스트와 평점 추출
        text = review.find('p', class_='review-text').text.strip()
        rating = int(review.find('span', class_='rating').text)

        all_reviews.append({'text': text, 'rating': rating})

    # 서버 부하 방지를 위한 대기
    time.sleep(1)

# DataFrame으로 변환 후 CSV 저장
df = pd.DataFrame(all_reviews)
df.to_csv('reviews_dataset.csv', index=False, encoding='utf-8-sig')
print(f"총 {len(all_reviews)}개의 리뷰를 수집했습니다.")

설명

이것이 하는 일: 이 코드는 리뷰 사이트에서 여러 페이지에 걸쳐 리뷰 텍스트와 평점을 자동으로 수집하여 CSV 파일로 저장하는 전체 과정을 수행합니다. 첫 번째로, requests 라이브러리로 웹페이지의 HTML을 가져옵니다.

for 루프를 통해 여러 페이지를 순회하면서 각 페이지의 HTML 응답을 받아옵니다. 이렇게 하는 이유는 한 페이지에만 있는 데이터로는 충분하지 않고, 수천 개의 샘플이 필요하기 때문입니다.

그 다음으로, BeautifulSoup이 HTML을 파싱하여 DOM 트리 구조로 만듭니다. find_all 메서드로 'review-item' 클래스를 가진 모든 div 요소를 찾고, 각 리뷰에서 텍스트와 평점을 추출합니다.

내부에서는 CSS 선택자를 사용하여 정확한 위치의 요소를 찾아내는 작업이 일어납니다. 마지막으로, 추출한 데이터를 딕셔너리 형태로 리스트에 저장한 후, pandas DataFrame으로 변환하여 CSV 파일로 저장합니다.

최종적으로 구조화된 데이터셋 파일을 얻게 되며, 이는 즉시 AI 모델 학습에 사용할 수 있는 형태입니다. 여러분이 이 코드를 사용하면 수작업으로 몇 주가 걸릴 작업을 몇 분 만에 완료할 수 있습니다.

또한 데이터가 일관된 형식으로 저장되어 전처리 과정이 훨씬 간단해지고, 언제든 스크립트를 재실행하여 최신 데이터를 확보할 수 있습니다.

실전 팁

💡 크롤링 전에 해당 웹사이트의 robots.txt 파일을 확인하여 크롤링이 허용되는지 반드시 확인하세요. 법적 문제를 방지하고 윤리적인 데이터 수집을 실천해야 합니다.

💡 time.sleep()을 사용하여 요청 간 적절한 간격을 두세요. 너무 빠른 요청은 서버에 부담을 주고 IP 차단의 원인이 됩니다. 실무에서는 1-3초 간격이 적절합니다.

💡 동적 콘텐츠(JavaScript로 로드되는 데이터)는 BeautifulSoup만으로 수집할 수 없습니다. 이런 경우 Selenium이나 Playwright를 사용하여 브라우저를 자동화해야 합니다.

💡 예외 처리를 반드시 추가하세요. 네트워크 오류, 요소를 찾지 못하는 경우 등에 대비하여 try-except 블록으로 감싸면 중간에 크롤링이 멈추는 것을 방지할 수 있습니다.

💡 수집한 데이터는 즉시 저장하세요. 모든 크롤링이 끝난 후 한 번에 저장하면 중간에 오류가 발생했을 때 모든 데이터를 잃게 됩니다. 일정 개수마다 중간 저장하는 것이 안전합니다.


2. 결측치와 중복 데이터 처리하기 - pandas로 데이터 정제

시작하며

여러분이 크롤링으로 수집한 데이터를 보니 빈 값도 있고, 같은 데이터가 여러 번 중복되어 있는 상황을 겪어본 적 있나요? 실제로 웹에서 수집한 raw 데이터는 거의 항상 불완전하고 노이즈가 많습니다.

이런 문제는 모델 학습에 직접적인 악영향을 미칩니다. 결측치가 있으면 학습이 불가능하거나 편향된 결과를 만들고, 중복 데이터는 모델이 특정 패턴을 과도하게 학습하게 만듭니다.

쓰레기 데이터를 넣으면 쓰레기 결과가 나온다는 말이 있습니다. 바로 이럴 때 필요한 것이 데이터 정제 과정입니다.

결측치를 적절히 처리하고 중복을 제거하여 깨끗한 데이터셋을 만들면, 모델의 성능이 크게 향상됩니다.

개요

간단히 말해서, 데이터 정제는 수집한 raw 데이터에서 오류, 결측치, 중복을 제거하고 일관성을 확보하는 과정입니다. 실무에서는 데이터 과학 프로젝트 시간의 60-80%가 이 정제 과정에 소요됩니다.

예를 들어, 고객 리뷰 데이터를 수집했는데 일부 리뷰에 텍스트가 없거나, 같은 리뷰가 여러 번 수집된 경우, 이를 그대로 사용하면 모델이 제대로 학습할 수 없습니다. 기존에는 Excel에서 수작업으로 데이터를 확인하고 정제했다면, 이제는 pandas를 사용하여 수십만 건의 데이터를 몇 줄의 코드로 자동 정제할 수 있습니다.

데이터 정제의 핵심 특징은 세 가지입니다. 첫째, 결측치를 제거하거나 적절한 값으로 대체하여 데이터의 완전성을 확보합니다.

둘째, 중복 레코드를 제거하여 데이터의 독립성을 보장합니다. 셋째, 이상치를 탐지하고 처리하여 데이터 품질을 높입니다.

이러한 과정을 거쳐야 신뢰할 수 있는 AI 모델을 만들 수 있습니다.

코드 예제

import pandas as pd
import numpy as np

# 데이터셋 로드
df = pd.read_csv('reviews_dataset.csv')

# 데이터 기본 정보 확인
print(f"원본 데이터: {len(df)}개 레코드")
print(f"결측치 현황:\n{df.isnull().sum()}")

# 결측치 처리: 텍스트가 없는 리뷰 제거
df = df.dropna(subset=['text'])

# 빈 문자열 처리
df = df[df['text'].str.strip() != '']

# 중복 데이터 제거
df = df.drop_duplicates(subset=['text'], keep='first')

# 이상치 처리: 평점 범위 확인 (1-5점)
df = df[(df['rating'] >= 1) & (df['rating'] <= 5)]

# 텍스트 길이가 너무 짧은 데이터 제거 (최소 10자)
df = df[df['text'].str.len() >= 10]

# 정제 결과 저장
df.to_csv('reviews_cleaned.csv', index=False, encoding='utf-8-sig')
print(f"정제된 데이터: {len(df)}개 레코드 ({len(df)/len(pd.read_csv('reviews_dataset.csv'))*100:.1f}% 유지)")

설명

이것이 하는 일: 이 코드는 수집한 리뷰 데이터에서 결측치, 중복, 이상치를 체계적으로 제거하여 학습에 적합한 깨끗한 데이터셋을 만드는 전체 정제 파이프라인입니다. 첫 번째로, 데이터를 로드한 후 isnull().sum()으로 각 컬럼의 결측치 개수를 확인합니다.

이는 데이터 품질을 파악하는 중요한 첫 단계입니다. 결측치가 많은 컬럼이 있다면 해당 필드의 수집 방식을 재검토해야 할 수도 있습니다.

그 다음으로, dropna()로 텍스트 필드가 비어있는 레코드를 제거하고, 공백만 있는 문자열도 필터링합니다. drop_duplicates()는 완전히 동일한 텍스트를 가진 리뷰를 하나만 남기고 제거합니다.

내부적으로는 해시 기반 알고리즘을 사용하여 빠르게 중복을 찾아냅니다. 세 번째로, 평점이 정상 범위(1-5점)를 벗어나는 이상치를 필터링하고, 텍스트 길이가 너무 짧아서 의미 있는 정보를 담지 못하는 데이터도 제거합니다.

마지막으로 정제된 데이터를 새 파일로 저장하면서 원본 대비 몇 퍼센트의 데이터가 유지되었는지 출력하여 정제 과정의 영향을 확인할 수 있습니다. 여러분이 이 코드를 사용하면 데이터 품질이 크게 향상되어 모델 학습 시 수렴 속도가 빨라지고 성능이 개선됩니다.

또한 정제된 데이터는 일관성이 있어 예측 시에도 안정적인 결과를 보장하며, 데이터 품질 문제로 인한 디버깅 시간을 크게 줄일 수 있습니다.

실전 팁

💡 결측치를 무조건 제거하기보다는 상황에 따라 평균값, 중앙값, 또는 모드로 대체하는 것을 고려하세요. 특히 수치 데이터의 경우 fillna()를 사용한 대체가 더 나을 수 있습니다.

💡 중복 제거 전에 어떤 필드 조합으로 중복을 판단할지 신중히 결정하세요. 텍스트가 같아도 평점이 다르면 다른 리뷰일 수 있습니다. subset 파라미터를 적절히 설정해야 합니다.

💡 데이터 정제 전후의 통계를 비교하여 로그로 남기세요. describe() 메서드로 기본 통계량을 확인하면 정제 과정이 데이터에 미친 영향을 정량적으로 파악할 수 있습니다.

💡 정제 과정은 단계별로 나누어 중간 결과를 저장하세요. 각 단계에서 제거된 데이터를 별도로 저장하면 나중에 정제 기준을 조정할 때 유용합니다.

💡 이상치 탐지에는 IQR(사분위수 범위) 방법도 활용하세요. 단순 범위 필터링보다 통계적으로 더 정교한 이상치 탐지가 가능합니다.


3. 텍스트 데이터 전처리하기 - 토큰화와 정규화

시작하며

여러분이 텍스트 데이터를 모델에 바로 넣으려는데, 특수문자, 이모지, 불필요한 공백 등이 섞여있어서 난감한 상황을 겪어본 적 있나요? 실제 웹에서 수집한 텍스트는 사용자가 자유롭게 입력한 것이라 형식이 제각각입니다.

이런 문제는 NLP 모델의 성능을 크게 저하시킵니다. 같은 의미의 단어라도 대소문자가 다르거나 띄어쓰기가 다르면 모델은 다른 단어로 인식합니다.

또한 불필요한 특수문자나 HTML 태그가 섞여있으면 노이즈가 됩니다. 바로 이럴 때 필요한 것이 텍스트 전처리입니다.

텍스트를 토큰화하고 정규화하여 모델이 이해하기 쉬운 일관된 형태로 변환하면, 학습 효율과 성능이 크게 향상됩니다.

개요

간단히 말해서, 텍스트 전처리는 raw 텍스트를 모델이 처리할 수 있는 표준화된 형태로 변환하는 일련의 과정입니다. 실무에서는 감성 분석, 텍스트 분류, 챗봇 등 거의 모든 NLP 작업에서 전처리가 필수입니다.

예를 들어, 고객 리뷰에 "정말 좋아요!!!", "정말 좋아요", "정말좋아요"가 섞여있다면, 이들을 동일한 형태로 정규화해야 모델이 일관되게 학습할 수 있습니다. 기존에는 정규표현식을 수동으로 작성하여 하나하나 처리했다면, 이제는 강력한 NLP 라이브러리들을 활용하여 체계적이고 효율적으로 전처리할 수 있습니다.

텍스트 전처리의 핵심 특징은 네 가지입니다. 첫째, 소문자 변환과 공백 정규화로 형식을 통일합니다.

둘째, 토큰화를 통해 텍스트를 의미 단위로 분리합니다. 셋째, 불필요한 문자나 불용어를 제거하여 노이즈를 줄입니다.

넷째, 어간 추출이나 표제어 추출로 단어를 기본형으로 통일합니다. 이러한 과정을 통해 모델이 핵심 의미에 집중할 수 있게 됩니다.

코드 예제

import re
import pandas as pd
from konlpy.tag import Okt

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

def preprocess_text(text):
    # HTML 태그 제거
    text = re.sub(r'<[^>]+>', '', text)

    # URL 제거
    text = re.sub(r'http\S+|www.\S+', '', text)

    # 이메일 제거
    text = re.sub(r'\S+@\S+', '', text)

    # 특수문자 제거 (한글, 영문, 숫자, 공백만 유지)
    text = re.sub(r'[^가-힣a-zA-Z0-9\s]', ' ', text)

    # 연속된 공백을 하나로
    text = re.sub(r'\s+', ' ', text)

    # 양쪽 공백 제거
    text = text.strip()

    # 형태소 분석 및 토큰화 (명사만 추출)
    tokens = okt.nouns(text)

    # 길이가 2 이상인 토큰만 유지 (한글자 제거)
    tokens = [token for token in tokens if len(token) >= 2]

    return ' '.join(tokens)

# 데이터프레임에 적용
df = pd.read_csv('reviews_cleaned.csv')
df['processed_text'] = df['text'].apply(preprocess_text)

# 전처리 전후 비교 출력
for i in range(3):
    print(f"원본: {df['text'].iloc[i]}")
    print(f"전처리: {df['processed_text'].iloc[i]}\n")

df.to_csv('reviews_preprocessed.csv', index=False, encoding='utf-8-sig')

설명

이것이 하는 일: 이 코드는 한국어 텍스트에서 노이즈를 제거하고 형태소 분석을 통해 의미 있는 명사만 추출하여 모델이 학습하기 좋은 형태로 변환하는 전처리 파이프라인입니다. 첫 번째로, 정규표현식을 사용하여 HTML 태그, URL, 이메일 주소를 제거합니다.

웹에서 크롤링한 데이터에는 이런 요소들이 자주 포함되는데, 이들은 텍스트의 의미와 무관한 노이즈입니다. re.sub() 함수가 패턴에 매칭되는 모든 문자열을 찾아서 제거하는 방식으로 동작합니다.

그 다음으로, 특수문자를 제거하고 연속된 공백을 하나로 통일합니다. 이 과정에서 한글, 영문, 숫자, 공백만 남기고 나머지는 모두 제거됩니다.

이렇게 하면 이모지, 특수기호 등이 모두 정리되어 일관된 형식을 갖추게 됩니다. 세 번째로, KoNLPy의 Okt 형태소 분석기를 사용하여 텍스트를 명사 단위로 토큰화합니다.

nouns() 메서드는 텍스트에서 의미를 담고 있는 명사들을 추출하는데, 이는 한국어 자연어처리에서 매우 효과적인 방법입니다. 마지막으로 한 글자짜리 토큰을 제거하여 의미 있는 단어만 남기고, 이를 공백으로 연결하여 최종 전처리 텍스트를 만듭니다.

여러분이 이 코드를 사용하면 텍스트 데이터의 차원이 줄어들어 모델 학습 속도가 빨라지고, 불필요한 노이즈가 제거되어 분류 정확도가 향상됩니다. 또한 토큰화된 텍스트는 TF-IDF나 워드 임베딩 같은 벡터화 과정에 바로 사용할 수 있어 전체 파이프라인이 간결해집니다.

실전 팁

💡 한국어 처리에는 KoNLPy 외에도 Mecab, Komoran 등 다양한 형태소 분석기가 있습니다. 도메인에 따라 성능이 다르므로 여러 분석기를 테스트해보고 가장 적합한 것을 선택하세요.

💡 명사만 추출하는 것이 항상 최선은 아닙니다. 감성 분석에서는 형용사와 동사도 중요한 정보를 담고 있으므로, morphs() 메서드로 모든 형태소를 추출한 후 품사 태깅을 활용하는 것이 더 나을 수 있습니다.

💡 불용어(stopwords) 리스트를 만들어 '그리고', '그래서' 같은 의미 없는 단어를 제거하세요. 도메인별로 자주 등장하지만 의미가 없는 단어들을 수집하여 커스텀 불용어 사전을 만들면 효과적입니다.

💡 숫자 처리 전략을 명확히 하세요. 가격이나 날짜처럼 의미 있는 숫자는 남기고, 의미 없는 숫자는 제거하거나 '<NUM>' 같은 토큰으로 대체하는 방법도 고려해보세요.

💡 전처리 결과를 샘플링하여 수동으로 확인하는 습관을 들이세요. 자동화된 전처리가 의도치 않게 중요한 정보를 제거할 수 있으므로, 무작위로 100개 정도를 선택해 검토하는 것이 안전합니다.


4. 데이터 라벨링 자동화하기 - 규칙 기반과 약한 지도학습

시작하며

여러분이 수집한 수천 개의 데이터에 일일이 라벨을 붙여야 하는 상황을 상상해보셨나요? 지도학습을 위해서는 라벨이 필수인데, 수작업으로 라벨링하려면 막대한 시간과 비용이 듭니다.

이런 문제는 특히 스타트업이나 소규모 팀에서 AI 프로젝트를 시작할 때 가장 큰 장벽입니다. 전문 라벨링 업체에 맡기면 비용이 많이 들고, 직접 하자니 시간이 너무 오래 걸립니다.

데이터는 있는데 라벨이 없어서 프로젝트가 진행되지 못하는 경우가 많습니다. 바로 이럴 때 필요한 것이 라벨링 자동화 기법입니다.

규칙 기반 방법이나 약한 지도학습(weak supervision)을 활용하면 초기 라벨을 빠르게 생성할 수 있고, 이를 기반으로 모델을 학습시킨 후 점진적으로 개선할 수 있습니다.

개요

간단히 말해서, 라벨링 자동화는 수동 라벨링 없이 또는 최소한의 수동 작업으로 데이터에 라벨을 부여하는 기법들의 집합입니다. 실무에서는 완벽한 라벨보다 빠르게 프로토타입을 만드는 것이 더 중요할 때가 많습니다.

예를 들어, 리뷰 감성 분석에서 '좋다', '최고' 같은 긍정 키워드가 있으면 긍정으로, '나쁘다', '최악' 같은 부정 키워드가 있으면 부정으로 자동 라벨링할 수 있습니다. 이렇게 만든 라벨이 100% 정확하지 않아도 초기 모델 학습에는 충분합니다.

기존에는 모든 데이터를 수동으로 라벨링한 후에야 모델 개발을 시작할 수 있었다면, 이제는 자동 라벨링으로 빠르게 시작하고 중요한 샘플만 수동으로 검증하여 점진적으로 품질을 높일 수 있습니다. 라벨링 자동화의 핵심 특징은 네 가지입니다.

첫째, 도메인 지식을 규칙으로 코드화하여 빠르게 라벨을 생성할 수 있습니다. 둘째, 여러 약한 라벨링 함수를 결합하여 정확도를 높일 수 있습니다.

셋째, 불확실한 샘플만 골라 수동 라벨링하여 효율을 극대화할 수 있습니다. 넷째, 라벨 노이즈를 감안하도록 모델을 학습시킬 수 있습니다.

이러한 방법들로 라벨링 비용을 크게 줄일 수 있습니다.

코드 예제

import pandas as pd
import numpy as np

# 감성 키워드 사전 정의
positive_keywords = ['좋', '최고', '만족', '훌륭', '완벽', '추천', '감동', '멋진']
negative_keywords = ['나쁘', '최악', '실망', '별로', '후회', '불만', '형편없', '짜증']

def rule_based_labeling(text):
    """규칙 기반 라벨링 함수"""
    text_lower = text.lower()

    # 긍정/부정 키워드 카운트
    pos_count = sum(1 for keyword in positive_keywords if keyword in text_lower)
    neg_count = sum(1 for keyword in negative_keywords if keyword in text_lower)

    # 규칙 적용
    if pos_count > neg_count:
        return 'positive', pos_count - neg_count  # 신뢰도도 함께 반환
    elif neg_count > pos_count:
        return 'negative', neg_count - pos_count
    else:
        return 'neutral', 0

# 데이터 로드
df = pd.read_csv('reviews_preprocessed.csv')

# 평점 기반 라벨링 (약한 지도학습)
df['rating_label'] = df['rating'].apply(lambda x: 'positive' if x >= 4 else ('negative' if x <= 2 else 'neutral'))

# 텍스트 규칙 기반 라벨링
df[['text_label', 'confidence']] = df['text'].apply(lambda x: pd.Series(rule_based_labeling(x)))

# 두 라벨이 일치하는 경우만 최종 라벨로 사용 (신뢰도 높임)
df['final_label'] = df.apply(
    lambda row: row['rating_label'] if row['rating_label'] == row['text_label'] else 'uncertain',
    axis=1
)

# 통계 출력
print(f"자동 라벨링 결과:")
print(df['final_label'].value_counts())
print(f"\n수동 검토 필요 (uncertain): {(df['final_label'] == 'uncertain').sum()}건")

# 확실한 라벨만 저장
df_labeled = df[df['final_label'] != 'uncertain']
df_labeled.to_csv('reviews_auto_labeled.csv', index=False, encoding='utf-8-sig')

설명

이것이 하는 일: 이 코드는 긍정/부정 키워드 규칙과 평점 정보라는 두 가지 약한 신호를 결합하여 텍스트 데이터에 감성 라벨을 자동으로 부여하고, 신뢰도가 높은 라벨만 선별하는 파이프라인입니다. 첫 번째로, 도메인 지식을 활용하여 긍정/부정 키워드 사전을 만듭니다.

rule_based_labeling 함수는 텍스트에서 이 키워드들의 출현 빈도를 세어 감성을 판단합니다. 키워드 차이를 신뢰도로 사용하여 얼마나 확실한 판단인지도 함께 기록합니다.

그 다음으로, 평점 정보를 활용한 두 번째 라벨링 방법을 적용합니다. 일반적으로 4점 이상은 긍정, 2점 이하는 부정 리뷰일 가능성이 높습니다.

이렇게 두 가지 독립적인 방법으로 라벨을 생성하면 각각의 약점을 보완할 수 있습니다. 세 번째로, 두 방법의 결과가 일치하는 경우만 최종 라벨로 채택합니다.

이는 앙상블 방식으로, 여러 방법이 동의하는 경우 정확도가 훨씬 높아지는 원리를 활용한 것입니다. 불일치하는 경우는 'uncertain'으로 표시하여 나중에 수동 검토하거나 제외할 수 있게 합니다.

여러분이 이 코드를 사용하면 수천 건의 데이터를 몇 초 만에 라벨링할 수 있습니다. 물론 100% 정확하지는 않지만, 70-80%의 정확도만 있어도 초기 모델을 학습시키기에 충분하며, 불확실한 샘플만 수동으로 검토하면 되므로 라벨링 비용을 크게 줄일 수 있습니다.

실전 팁

💡 키워드 사전은 실제 데이터를 보면서 지속적으로 업데이트하세요. TF-IDF로 각 클래스의 특징 단어를 추출하여 키워드 사전에 추가하면 정확도가 개선됩니다.

💡 부정어 처리에 주의하세요. '좋지 않다'는 부정인데 '좋' 키워드 때문에 긍정으로 분류될 수 있습니다. '안', '못', '않' 같은 부정어 앞뒤 문맥을 고려하는 로직을 추가해야 합니다.

💡 Snorkel 같은 약한 지도학습 프레임워크를 활용하면 더 정교한 라벨 집계가 가능합니다. 여러 라벨링 함수의 정확도를 자동으로 추정하고 가중 평균하여 최종 라벨을 만들어줍니다.

💡 액티브 러닝을 결합하세요. 모델이 가장 불확실해하는 샘플들을 우선적으로 수동 라벨링하면 적은 노력으로 큰 성능 향상을 얻을 수 있습니다.

💡 라벨 노이즈를 처리할 수 있는 모델을 사용하세요. CrossEntropy 대신 Focal Loss를 사용하거나, Co-teaching 같은 noisy label learning 기법을 적용하면 불완전한 라벨로도 좋은 성능을 낼 수 있습니다.


5. 데이터 증강으로 데이터셋 확장하기 - Augmentation 기법

시작하며

여러분이 겨우 1000개의 라벨링된 데이터를 확보했는데, 좋은 성능을 내기에는 턱없이 부족한 상황을 겪어본 적 있나요? 특히 이미지나 텍스트 도메인에서 충분한 양의 데이터를 모으는 것은 정말 어려운 일입니다.

이런 문제는 딥러닝 모델이 대량의 데이터를 필요로 한다는 근본적인 특성 때문입니다. 데이터가 부족하면 과적합이 발생하여 학습 데이터에서는 잘 동작하지만 실제 환경에서는 성능이 떨어집니다.

더 많은 데이터를 수집하려면 시간과 비용이 많이 듭니다. 바로 이럴 때 필요한 것이 데이터 증강입니다.

기존 데이터를 변형하여 새로운 학습 샘플을 생성함으로써 적은 비용으로 데이터셋의 크기와 다양성을 크게 늘릴 수 있습니다.

개요

간단히 말해서, 데이터 증강은 원본 데이터에 다양한 변형을 가해 새로운 학습 샘플을 생성하여 데이터셋을 확장하는 기법입니다. 실무에서는 이미지 분류에서 회전, 크롭, 색상 변경 등을 적용하고, 텍스트에서는 동의어 치환, 역번역, 문장 재구성 등을 사용합니다.

예를 들어, "이 제품 정말 좋아요"라는 리뷰를 "이 상품 진짜 마음에 들어요"로 변형하면 의미는 같지만 다른 표현의 샘플을 추가할 수 있습니다. 기존에는 데이터가 부족하면 더 많은 데이터를 수집하는 수밖에 없었다면, 이제는 증강 기법으로 기존 데이터를 효율적으로 활용하여 모델의 일반화 성능을 높일 수 있습니다.

데이터 증강의 핵심 특징은 네 가지입니다. 첫째, 원본 데이터의 본질적 의미를 유지하면서 표현만 다양화합니다.

둘째, 모델이 다양한 변형에 강건해지도록 학습시킵니다. 셋째, 과적합을 방지하고 일반화 성능을 향상시킵니다.

넷째, 추가 데이터 수집 없이 데이터셋을 확장할 수 있어 비용 효율적입니다. 이러한 장점 때문에 거의 모든 딥러닝 프로젝트에서 증강 기법이 사용됩니다.

코드 예제

import pandas as pd
import random
from googletrans import Translator

# 동의어 사전
synonyms = {
    '좋': ['훌륭', '멋진', '괜찮', '나이스'],
    '나쁘': ['별로', '안좋', '형편없'],
    '정말': ['진짜', '매우', '너무', '완전'],
    '제품': ['상품', '물건', '아이템']
}

def synonym_replacement(text, n=2):
    """동의어 치환 증강"""
    words = text.split()
    new_words = words.copy()

    # n개의 단어를 무작위로 동의어로 치환
    random_word_list = list(set([word for word in words if word in synonyms]))
    random.shuffle(random_word_list)

    num_replaced = 0
    for random_word in random_word_list:
        if num_replaced >= n:
            break
        synonym = random.choice(synonyms[random_word])
        new_words = [synonym if word == random_word else word for word in new_words]
        num_replaced += 1

    return ' '.join(new_words)

def random_insertion(text, n=1):
    """무작위 삽입 증강"""
    words = text.split()
    for _ in range(n):
        add_word = random.choice(words)
        random_idx = random.randint(0, len(words))
        words.insert(random_idx, add_word)
    return ' '.join(words)

def random_swap(text, n=1):
    """무작위 교환 증강"""
    words = text.split()
    for _ in range(n):
        if len(words) >= 2:
            idx1, idx2 = random.sample(range(len(words)), 2)
            words[idx1], words[idx2] = words[idx2], words[idx1]
    return ' '.join(words)

def random_deletion(text, p=0.1):
    """무작위 삭제 증강"""
    words = text.split()
    if len(words) == 1:
        return text
    new_words = [word for word in words if random.random() > p]
    return ' '.join(new_words) if new_words else random.choice(words)

# 데이터 로드
df = pd.read_csv('reviews_auto_labeled.csv')

# 증강 데이터 생성
augmented_data = []

for idx, row in df.iterrows():
    # 원본 데이터 추가
    augmented_data.append(row.to_dict())

    # 각 샘플당 3개의 증강 버전 생성
    text = row['text']
    label = row['final_label']

    augmented_data.append({
        'text': synonym_replacement(text),
        'rating': row['rating'],
        'final_label': label
    })

    augmented_data.append({
        'text': random_swap(text, n=2),
        'rating': row['rating'],
        'final_label': label
    })

    augmented_data.append({
        'text': random_deletion(text, p=0.1),
        'rating': row['rating'],
        'final_label': label
    })

# 증강된 데이터셋 저장
df_augmented = pd.DataFrame(augmented_data)
df_augmented = df_augmented.drop_duplicates(subset=['text'])  # 중복 제거

print(f"원본 데이터: {len(df)}개")
print(f"증강 후 데이터: {len(df_augmented)}개 ({len(df_augmented)/len(df):.1f}배 증가)")

df_augmented.to_csv('reviews_augmented.csv', index=False, encoding='utf-8-sig')

설명

이것이 하는 일: 이 코드는 네 가지 텍스트 증강 기법을 사용하여 원본 데이터의 3-4배에 해당하는 새로운 학습 샘플을 자동으로 생성하는 데이터 증강 파이프라인입니다. 첫 번째로, synonym_replacement 함수는 텍스트에서 무작위로 단어를 선택하여 미리 정의한 동의어 사전에서 유사한 의미의 단어로 치환합니다.

이렇게 하면 "이 제품 정말 좋아요"가 "이 상품 진짜 훌륭해요"처럼 변형되어 의미는 같지만 다른 표현의 샘플이 생성됩니다. 그 다음으로, random_swap은 문장 내 단어의 순서를 무작위로 바꾸고, random_insertion은 기존 단어를 무작위 위치에 삽입하며, random_deletion은 일정 확률로 단어를 제거합니다.

이러한 변형들은 모델이 단어 순서나 일부 누락에도 강건하게 학습하도록 돕습니다. 세 번째로, 원본 데이터의 각 샘플에 대해 여러 증강 기법을 적용하여 복수의 변형 버전을 생성합니다.

중요한 점은 라벨은 그대로 유지한다는 것입니다. 증강된 모든 샘플은 원본과 같은 의미를 가지므로 동일한 라벨을 사용해야 합니다.

마지막으로 중복된 텍스트를 제거하여 최종 증강 데이터셋을 완성합니다. 여러분이 이 코드를 사용하면 1000개의 데이터로 시작해서 3000-4000개의 학습 샘플을 확보할 수 있습니다.

이는 모델이 더 다양한 표현을 학습하게 하여 과적합을 방지하고, 실제 환경에서 마주칠 수 있는 다양한 문장 구조에 대한 대응력을 키워줍니다. 또한 클래스 불균형 문제가 있을 때 소수 클래스만 집중적으로 증강하여 균형을 맞출 수도 있습니다.

실전 팁

💡 증강의 강도를 적절히 조절하세요. 너무 과도하게 변형하면 원본의 의미가 왜곡될 수 있습니다. 증강 전후 샘플을 비교하며 의미가 유지되는지 확인하는 것이 중요합니다.

💡 역번역(back-translation)도 매우 효과적인 증강 기법입니다. 한국어를 영어로 번역했다가 다시 한국어로 번역하면 의미는 같지만 다른 표현의 문장이 생성됩니다. Google Translate API를 활용할 수 있습니다.

💡 EDA(Easy Data Augmentation) 논문에서 제시한 기법들을 참고하세요. 논문에서는 짧은 문장에는 적은 수의 변형을, 긴 문장에는 많은 수의 변형을 적용하는 것을 권장합니다.

💡 최신 기법으로는 BERT나 GPT 같은 언어모델을 활용한 문맥 기반 증강이 있습니다. 문맥을 고려하여 더 자연스러운 동의어 치환이 가능하며, nlpaug 라이브러리를 사용하면 쉽게 구현할 수 있습니다.

💡 증강 데이터만으로 검증하지 마세요. 검증 세트와 테스트 세트는 반드시 원본 데이터만 사용해야 합니다. 증강 데이터로 검증하면 실제 성능보다 과대평가될 수 있습니다.


6. 데이터셋 분할하기 - Train, Validation, Test Split

시작하며

여러분이 전체 데이터로 모델을 학습시켰는데, 실제 환경에 배포하니 성능이 형편없게 나온 경험이 있나요? 이는 학습 데이터에만 과적합되어 새로운 데이터를 제대로 처리하지 못하기 때문입니다.

이런 문제는 데이터를 적절히 분할하지 않아 모델의 일반화 성능을 제대로 평가하지 못했을 때 발생합니다. 학습에 사용한 데이터로 평가하면 당연히 좋은 결과가 나오지만, 이는 실제 성능을 반영하지 못합니다.

실무에서는 배포 후 성능 저하로 인한 재작업이 필요하게 됩니다. 바로 이럴 때 필요한 것이 올바른 데이터셋 분할 전략입니다.

학습, 검증, 테스트 세트로 적절히 나누고 각각의 역할을 명확히 하면, 모델의 실제 성능을 정확히 예측하고 과적합을 방지할 수 있습니다.

개요

간단히 말해서, 데이터셋 분할은 전체 데이터를 학습용(Train), 검증용(Validation), 테스트용(Test) 세트로 나누어 모델의 학습과 평가를 올바르게 수행하는 과정입니다. 실무에서는 일반적으로 60-70%를 학습, 15-20%를 검증, 15-20%를 테스트에 할당합니다.

예를 들어, 1만 개의 데이터가 있다면 7000개로 학습하고, 1500개로 하이퍼파라미터를 조정하며, 나머지 1500개로 최종 성능을 평가합니다. 각 세트는 완전히 독립적이어야 합니다.

기존에는 단순히 순서대로 나누거나 무작위로 분할했다면, 이제는 클래스 분포를 고려한 stratified split, 시계열 데이터의 시간 순서 보존, 그룹 단위 분할 등 데이터 특성에 맞는 전략적 분할이 필요합니다. 데이터셋 분할의 핵심 특징은 네 가지입니다.

첫째, 각 세트가 독립적이어야 데이터 누수(leakage)를 방지할 수 있습니다. 둘째, 클래스 분포가 균등하게 유지되어야 편향을 막을 수 있습니다.

셋째, 검증 세트로 모델을 튜닝하고 테스트 세트로 최종 평가하여 과적합을 감지할 수 있습니다. 넷째, 재현 가능하도록 random seed를 고정해야 합니다.

이러한 원칙을 지켜야 신뢰할 수 있는 모델 평가가 가능합니다.

코드 예제

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 데이터 로드
df = pd.read_csv('reviews_augmented.csv')

# 클래스 분포 확인
print("전체 데이터 클래스 분포:")
print(df['final_label'].value_counts(normalize=True))

# Stratified Split: 클래스 비율을 유지하면서 분할
# 먼저 Train+Val (80%)과 Test (20%)로 분할
train_val_df, test_df = train_test_split(
    df,
    test_size=0.2,
    stratify=df['final_label'],  # 클래스 비율 유지
    random_state=42  # 재현성을 위한 시드 고정
)

# Train+Val을 다시 Train (75%)과 Val (25%)로 분할
# 전체의 60% Train, 20% Val, 20% Test
train_df, val_df = train_test_split(
    train_val_df,
    test_size=0.25,  # train_val의 25% = 전체의 20%
    stratify=train_val_df['final_label'],
    random_state=42
)

# 분할 결과 확인
print(f"\nTrain set: {len(train_df)}개 ({len(train_df)/len(df)*100:.1f}%)")
print(f"Validation set: {len(val_df)}개 ({len(val_df)/len(df)*100:.1f}%)")
print(f"Test set: {len(test_df)}개 ({len(test_df)/len(df)*100:.1f}%)")

# 각 세트의 클래스 분포 확인
print("\nTrain set 클래스 분포:")
print(train_df['final_label'].value_counts(normalize=True))
print("\nValidation set 클래스 분포:")
print(val_df['final_label'].value_counts(normalize=True))
print("\nTest set 클래스 분포:")
print(test_df['final_label'].value_counts(normalize=True))

# 데이터셋 저장
train_df.to_csv('train_dataset.csv', index=False, encoding='utf-8-sig')
val_df.to_csv('val_dataset.csv', index=False, encoding='utf-8-sig')
test_df.to_csv('test_dataset.csv', index=False, encoding='utf-8-sig')

print("\n데이터셋 분할 완료 및 저장됨!")

설명

이것이 하는 일: 이 코드는 전체 데이터셋을 학습, 검증, 테스트용으로 stratified 방식으로 분할하여 각 세트의 클래스 비율이 원본과 동일하게 유지되도록 하는 전문적인 데이터 분할 파이프라인입니다. 첫 번째로, 원본 데이터의 클래스 분포를 확인합니다.

이는 분할 후에도 동일한 비율이 유지되는지 검증하기 위한 기준점이 됩니다. 예를 들어 긍정 60%, 부정 40%의 분포라면, 모든 세트에서 이 비율이 유지되어야 공정한 평가가 가능합니다.

그 다음으로, train_test_split 함수를 두 번 사용하여 계층적으로 분할합니다. 첫 번째 분할에서 테스트 세트(20%)를 분리하고, 두 번째 분할에서 나머지를 학습(60%)과 검증(20%)으로 나눕니다.

stratify 파라미터를 사용하면 sklearn이 자동으로 각 클래스의 비율을 계산하여 균등하게 분배합니다. 세 번째로, random_state를 42로 고정하여 재현성을 보장합니다.

같은 코드를 다시 실행하면 정확히 동일한 분할 결과를 얻을 수 있어, 실험 결과를 비교하거나 협업할 때 필수적입니다. 마지막으로 각 세트의 크기와 클래스 분포를 출력하여 분할이 올바르게 수행되었는지 확인하고, 세 개의 CSV 파일로 저장합니다.

여러분이 이 코드를 사용하면 모델 학습 시 검증 세트로 하이퍼파라미터를 조정하고, 최종적으로 한 번도 본 적 없는 테스트 세트로 성능을 평가하여 실제 배포 환경의 성능을 정확히 예측할 수 있습니다. 또한 클래스 불균형이 있어도 모든 세트에서 동일한 비율을 유지하여 편향 없는 평가가 가능합니다.

실전 팁

💡 테스트 세트는 정말로 마지막까지 건드리지 마세요. 모델 개발 중에 테스트 세트를 보면서 조정하면 간접적으로 테스트 세트에 과적합하게 됩니다. 최종 모델이 완성된 후 딱 한 번만 평가하세요.

💡 시계열 데이터는 무작위 분할하면 안 됩니다. 미래를 예측하는 모델인데 미래 데이터로 학습하면 현실적이지 않습니다. 시간 순서대로 분할하여 과거로 학습하고 미래로 테스트해야 합니다.

💡 교차 검증(Cross-Validation)을 고려하세요. 데이터가 적을 때는 K-Fold CV로 모든 데이터를 학습과 검증에 번갈아 사용하면 더 안정적인 성능 추정이 가능합니다. sklearn의 StratifiedKFold를 사용하세요.

💡 그룹이 있는 데이터는 GroupKFold를 사용하세요. 예를 들어 같은 사용자의 리뷰가 여러 개 있다면, 한 사용자의 데이터가 학습과 테스트에 동시에 들어가지 않도록 사용자 단위로 분할해야 합니다.

💡 데이터 분할 후 각 세트를 별도 폴더에 저장하고 README 파일에 분할 비율, 날짜, random seed를 기록하세요. 나중에 실험을 재현하거나 다른 사람과 공유할 때 필수 정보입니다.


7. 이미지 데이터 수집 및 전처리하기 - 크롤링과 리사이징

시작하며

여러분이 이미지 분류 모델을 만들려는데, 필요한 카테고리의 이미지를 충분히 모으기가 쉽지 않은 상황을 겪어본 적 있나요? 공개 데이터셋에는 원하는 클래스가 없거나, 있어도 실제 환경과 다른 경우가 많습니다.

이런 문제는 특히 특정 도메인이나 한국 환경에 특화된 이미지 데이터가 필요할 때 더욱 심각합니다. 예를 들어 한국 음식 분류 모델을 만들려면 직접 이미지를 수집해야 합니다.

수작업으로 다운로드하기에는 시간이 너무 오래 걸립니다. 바로 이럴 때 필요한 것이 이미지 크롤링과 자동 전처리 파이프라인입니다.

웹에서 이미지를 대량으로 수집하고, 크기와 형식을 통일하여 모델 학습에 바로 사용할 수 있는 형태로 만들 수 있습니다.

개요

간단히 말해서, 이미지 데이터 수집 및 전처리는 웹에서 이미지를 자동으로 다운로드하고, 크기 조정, 형식 변환, 정규화 등을 수행하여 학습 가능한 데이터셋을 만드는 과정입니다. 실무에서는 Google Images, Flickr, Unsplash 같은 사이트나 자체 서비스의 이미지를 크롤링합니다.

예를 들어, '불고기' 이미지를 1000장 수집하려면 검색 결과 페이지를 크롤링하여 이미지 URL을 추출하고 다운로드합니다. 수집 후에는 모든 이미지를 224x224 같은 고정 크기로 리사이징하고 RGB 형식으로 통일합니다.

기존에는 이미지를 하나하나 저장하고 포토샵으로 크기를 조정했다면, 이제는 스크립트로 수천 장의 이미지를 자동으로 수집하고 일괄 전처리할 수 있습니다. 이미지 데이터 처리의 핵심 특징은 네 가지입니다.

첫째, 크롤링으로 대량의 이미지를 빠르게 수집할 수 있습니다. 둘째, 리사이징과 형식 통일로 모델 입력에 적합한 형태로 만들 수 있습니다.

셋째, 손상된 이미지를 자동으로 필터링하여 데이터 품질을 확보할 수 있습니다. 넷째, 폴더 구조로 클래스별 이미지를 정리하여 관리와 로딩이 쉬워집니다.

이러한 과정을 통해 컴퓨터 비전 프로젝트의 기초를 탄탄히 할 수 있습니다.

코드 예제

import requests
from PIL import Image
from io import BytesIO
import os
from pathlib import Path

def download_images(query, num_images=100, output_dir='images'):
    """이미지 다운로드 및 전처리"""
    # 출력 디렉토리 생성
    Path(output_dir).mkdir(parents=True, exist_ok=True)

    # 간단한 예시: Unsplash API 사용 (실제로는 API 키 필요)
    base_url = f"https://source.unsplash.com/featured/?{query}"

    downloaded = 0
    for i in range(num_images):
        try:
            # 이미지 다운로드
            response = requests.get(base_url, timeout=10)
            img = Image.open(BytesIO(response.content))

            # RGB 변환 (RGBA, 흑백 등을 RGB로 통일)
            if img.mode != 'RGB':
                img = img.convert('RGB')

            # 리사이징: 비율 유지하며 224x224로 조정
            img = img.resize((224, 224), Image.Resampling.LANCZOS)

            # 파일명 생성 및 저장
            filename = f"{query}_{i:04d}.jpg"
            filepath = os.path.join(output_dir, filename)
            img.save(filepath, 'JPEG', quality=95)

            downloaded += 1
            if downloaded % 10 == 0:
                print(f"{downloaded}개 이미지 다운로드 완료...")

        except Exception as e:
            print(f"이미지 {i} 다운로드 실패: {e}")
            continue

    print(f"\n총 {downloaded}개 이미지 수집 완료!")
    return downloaded

# 여러 카테고리의 이미지 수집
categories = ['bulgogi', 'kimchi', 'bibimbap']

for category in categories:
    print(f"\n{category} 이미지 수집 중...")
    output_path = f"dataset/{category}"
    download_images(category, num_images=100, output_dir=output_path)

# 데이터셋 구조 확인
print("\n데이터셋 구조:")
for category in categories:
    path = f"dataset/{category}"
    count = len([f for f in os.listdir(path) if f.endswith('.jpg')])
    print(f"{category}: {count}개 이미지")

설명

이것이 하는 일: 이 코드는 여러 카테고리의 이미지를 웹에서 자동으로 수집하고, 크기와 형식을 통일하여 클래스별 폴더에 저장하는 완전한 이미지 데이터 수집 파이프라인입니다. 첫 번째로, download_images 함수는 주어진 검색어로 이미지를 다운로드합니다.

이 예시에서는 Unsplash를 사용했지만, 실무에서는 Selenium으로 Google Images를 크롤링하거나 자체 API를 사용합니다. requests로 이미지 URL에서 바이너리 데이터를 받아와 PIL Image 객체로 변환합니다.

그 다음으로, 이미지 전처리를 수행합니다. convert('RGB')로 모든 이미지를 RGB 형식으로 통일하는데, 이는 일부 PNG가 RGBA(알파 채널 포함)이거나 흑백인 경우가 있기 때문입니다.

resize() 메서드로 224x224 픽셀로 크기를 조정하는데, 이는 대부분의 CNN 모델(ResNet, VGG 등)이 기본적으로 사용하는 입력 크기입니다. LANCZOS 알고리즘을 사용하여 고품질로 리사이징합니다.

세 번째로, 전처리된 이미지를 클래스별 폴더에 저장합니다. 'dataset/bulgogi/', 'dataset/kimchi/' 같은 구조로 저장하면 PyTorch의 ImageFolder나 TensorFlow의 image_dataset_from_directory가 자동으로 클래스를 인식하여 로딩할 수 있습니다.

예외 처리를 통해 손상된 이미지나 네트워크 오류를 건너뛰고 계속 진행합니다. 여러분이 이 코드를 사용하면 수백 장의 이미지를 몇 분 만에 수집하고 전처리할 수 있습니다.

모든 이미지가 동일한 크기와 형식을 갖추고 있어 별도의 전처리 없이 바로 모델에 입력할 수 있으며, 폴더 구조 덕분에 데이터 로더 구현이 매우 간단해집니다.

실전 팁

💡 Google Images 크롤링에는 google-images-download나 icrawler 라이브러리를 추천합니다. 검색어로 대량의 이미지를 쉽게 수집할 수 있지만, 저작권을 확인하고 연구/교육 목적으로만 사용하세요.

💡 이미지 품질 검증을 추가하세요. PIL로 이미지를 열 수 없거나, 크기가 너무 작거나(예: 50x50 미만), 파일 크기가 이상한 경우를 필터링하여 저품질 이미지를 제거하세요.

💡 중복 이미지 제거는 필수입니다. imagehash 라이브러리로 perceptual hash를 계산하여 거의 동일한 이미지를 찾아 제거하면 모델이 중복 데이터에 과적합하는 것을 방지할 수 있습니다.

💡 리사이징 시 종횡비를 유지하는 방법도 고려하세요. 정사각형으로 강제 리사이징하면 이미지가 왜곡될 수 있습니다. 대신 긴 쪽을 224로 맞추고 짧은 쪽을 패딩하거나, 중앙을 224x224로 크롭하는 방법도 있습니다.

💡 대용량 데이터셋은 HDF5나 TFRecord 형식으로 저장하세요. 수만 장의 작은 이미지 파일보다 하나의 큰 파일로 저장하면 I/O 성능이 크게 향상되어 학습 속도가 빨라집니다.


8. CSV와 JSON으로 메타데이터 관리하기 - 데이터셋 정보 저장

시작하며

여러분이 수천 개의 이미지와 텍스트 파일을 수집했는데, 어떤 데이터가 어떤 클래스인지, 언제 수집했는지, 품질은 어떤지 등의 정보를 추적하기 어려운 상황을 겪어본 적 있나요? 파일 이름만으로는 충분한 정보를 담을 수 없습니다.

이런 문제는 프로젝트가 커지고 여러 사람이 협업할 때 더욱 심각해집니다. 어떤 이미지가 검증되었는지, 어떤 데이터에 문제가 있는지, 버전 관리는 어떻게 할지 등을 체계적으로 관리하지 않으면 혼란스러워집니다.

데이터를 재처리하거나 분석할 때도 메타데이터가 없으면 매우 불편합니다. 바로 이럴 때 필요한 것이 메타데이터 관리 시스템입니다.

CSV나 JSON 파일로 데이터셋의 모든 정보를 구조화하여 저장하면, 데이터를 효율적으로 관리하고 재사용할 수 있습니다.

개요

간단히 말해서, 메타데이터는 데이터에 대한 데이터로, 각 샘플의 파일 경로, 라벨, 크기, 수집 일시, 품질 점수 등의 부가 정보를 의미합니다. 실무에서는 이미지 데이터셋의 경우 이미지 파일 경로, 클래스 라벨, 해상도, 라벨링한 사람, 검증 여부 등을 CSV나 JSON으로 관리합니다.

예를 들어, 의료 이미지 데이터셋이라면 환자 ID, 촬영 날짜, 진단명, 의사 소견 등을 메타데이터로 저장하여 나중에 필터링하거나 분석할 때 활용합니다. 기존에는 파일 이름에 정보를 넣거나(예: image_class1_verified_20230101.jpg) 별도의 문서로 관리했다면, 이제는 구조화된 포맷으로 저장하여 pandas로 쉽게 로딩하고 쿼리할 수 있습니다.

메타데이터 관리의 핵심 특징은 네 가지입니다. 첫째, CSV는 표 형태 데이터에 적합하고 Excel로도 열람 가능합니다.

둘째, JSON은 계층적 구조나 복잡한 속성을 표현하기 좋습니다. 셋째, 메타데이터를 통해 데이터 필터링, 통계 분석, 버전 관리가 쉬워집니다.

넷째, 팀원들과 데이터셋 정보를 공유하고 일관성을 유지할 수 있습니다. 이러한 체계적 관리가 데이터 과학 프로젝트의 재현성과 협업을 가능하게 합니다.

코드 예제

import pandas as pd
import json
from datetime import datetime
from pathlib import Path
import os

# 이미지 데이터셋의 메타데이터 생성
def create_image_metadata(dataset_dir):
    """이미지 파일들의 메타데이터를 수집"""
    metadata = []

    for class_dir in os.listdir(dataset_dir):
        class_path = os.path.join(dataset_dir, class_dir)
        if not os.path.isdir(class_path):
            continue

        for img_file in os.listdir(class_path):
            if not img_file.endswith(('.jpg', '.png', '.jpeg')):
                continue

            img_path = os.path.join(class_path, img_file)

            # 이미지 정보 수집
            from PIL import Image
            try:
                img = Image.open(img_path)
                width, height = img.size

                metadata.append({
                    'file_path': img_path,
                    'file_name': img_file,
                    'class': class_dir,
                    'width': width,
                    'height': height,
                    'format': img.format,
                    'size_bytes': os.path.getsize(img_path),
                    'collected_date': datetime.now().strftime('%Y-%m-%d'),
                    'verified': False,  # 수동 검증 필요
                    'quality_score': None  # 나중에 품질 평가 추가
                })
            except Exception as e:
                print(f"이미지 처리 오류 {img_path}: {e}")

    return pd.DataFrame(metadata)

# 메타데이터 생성 및 저장
df_meta = create_image_metadata('dataset')

# CSV로 저장 (표 형태, Excel 호환)
df_meta.to_csv('dataset_metadata.csv', index=False, encoding='utf-8-sig')

# JSON으로도 저장 (복잡한 구조에 적합)
metadata_json = {
    'dataset_name': 'Korean Food Images',
    'version': '1.0',
    'created_date': datetime.now().strftime('%Y-%m-%d'),
    'total_images': len(df_meta),
    'classes': df_meta['class'].unique().tolist(),
    'class_distribution': df_meta['class'].value_counts().to_dict(),
    'samples': df_meta.to_dict('records')
}

with open('dataset_metadata.json', 'w', encoding='utf-8') as f:
    json.dump(metadata_json, f, ensure_ascii=False, indent=2)

# 메타데이터 활용 예시: 클래스별 통계
print("클래스별 이미지 수:")
print(df_meta['class'].value_counts())

print("\n평균 이미지 크기:")
print(f"Width: {df_meta['width'].mean():.0f}px")
print(f"Height: {df_meta['height'].mean():.0f}px")

# 특정 조건으로 필터링
small_images = df_meta[(df_meta['width'] < 200) | (df_meta['height'] < 200)]
print(f"\n저해상도 이미지: {len(small_images)}개 (재수집 필요)")

설명

이것이 하는 일: 이 코드는 이미지 데이터셋의 모든 파일을 스캔하여 파일 정보, 이미지 속성, 클래스 라벨 등을 수집하고 CSV와 JSON 두 가지 형식으로 메타데이터를 저장하는 관리 시스템입니다. 첫 번째로, create_image_metadata 함수는 데이터셋 폴더를 재귀적으로 탐색하며 모든 이미지 파일을 찾습니다.

각 이미지를 PIL로 열어서 해상도, 형식, 파일 크기 같은 기본 정보를 추출합니다. os.path.getsize()로 바이트 단위 크기를 얻고, datetime으로 수집 날짜를 기록합니다.

그 다음으로, 수집한 정보를 딕셔너리 리스트로 만들어 pandas DataFrame으로 변환합니다. 이렇게 하면 to_csv()로 간편하게 CSV 파일로 저장할 수 있고, Excel에서도 바로 열람할 수 있습니다.

'verified'와 'quality_score' 같은 필드는 초기에는 비워두고 나중에 수동 검증이나 자동 품질 평가 후 업데이트합니다. 세 번째로, JSON 형식으로도 저장합니다.

JSON은 계층적 구조를 표현하기 좋아서 데이터셋 전체 정보(이름, 버전, 생성일)와 개별 샘플 정보를 함께 담을 수 있습니다. class_distribution 같은 통계 정보도 포함하여 나중에 빠르게 확인할 수 있게 합니다.

마지막으로, 메타데이터를 활용한 분석 예시를 보여줍니다. pandas의 강력한 필터링과 집계 기능으로 저해상도 이미지를 찾거나, 클래스별 분포를 확인하거나, 평균 크기를 계산하는 등의 작업을 몇 줄의 코드로 수행할 수 있습니다.

여러분이 이 코드를 사용하면 데이터셋의 전체 현황을 한눈에 파악할 수 있고, 품질 문제가 있는 데이터를 빠르게 찾아낼 수 있습니다. 또한 팀원들과 메타데이터 파일을 공유하면 누가 어떤 데이터를 담당하는지 명확해지고, Git으로 버전 관리도 가능합니다.

실전 팁

💡 메타데이터에 데이터 출처(source URL)를 반드시 기록하세요. 나중에 데이터 품질 문제가 발견되었을 때 출처를 추적하거나, 저작권 이슈가 생겼을 때 해당 데이터를 제거할 수 있습니다.

💡 데이터 버전 관리를 위해 Git LFS나 DVC(Data Version Control)를 사용하세요. 메타데이터는 Git으로 관리하고 실제 이미지 파일은 DVC로 관리하면 협업과 재현성이 크게 향상됩니다.

💡 라벨 품질을 추적하는 필드를 추가하세요. 'labeler_id', 'confidence', 'review_status' 같은 필드로 누가 라벨링했는지, 얼마나 확실한지, 검토되었는지 등을 기록하면 라벨 품질 관리가 쉬워집니다.

💡 메타데이터 스키마를 문서화하세요. README나 별도 문서에 각 필드의 의미, 허용 값, 필수 여부 등을 명시하면 팀원들이 일관되게 메타데이터를 관리할 수 있습니다.

💡 대규모 데이터셋은 SQLite나 PostgreSQL 같은 데이터베이스를 고려하세요. 수십만 건 이상의 메타데이터는 CSV보다 DB가 훨씬 효율적이며, 복잡한 쿼리와 인덱싱이 가능합니다.


9. 데이터 품질 검증하기 - 자동화된 품질 체크

시작하며

여러분이 수만 개의 데이터를 수집했는데, 일부는 손상되었거나 라벨이 잘못되었거나 중복된 상황을 뒤늦게 발견한 경험이 있나요? 모델을 학습시킨 후에야 데이터 문제를 발견하면 처음부터 다시 해야 합니다.

이런 문제는 데이터 수집 과정에서 불가피하게 발생하는 오류들이 검증 없이 그대로 학습에 사용될 때 생깁니다. 손상된 이미지, 빈 텍스트, 잘못된 라벨, 중복 샘플 등이 섞여있으면 모델 성능이 저하되고 학습 과정에서 오류가 발생합니다.

수동으로 일일이 확인하기에는 데이터가 너무 많습니다. 바로 이럴 때 필요한 것이 자동화된 데이터 품질 검증 시스템입니다.

다양한 품질 체크를 자동으로 수행하여 문제가 있는 데이터를 사전에 걸러내면, 깨끗한 데이터로 효율적인 학습이 가능합니다.

개요

간단히 말해서, 데이터 품질 검증은 수집된 데이터에 오류, 이상치, 불일치가 없는지 자동으로 확인하고 문제가 있는 데이터를 식별하는 과정입니다. 실무에서는 이미지 데이터의 경우 파일 무결성, 최소 해상도, 올바른 형식 등을 체크하고, 텍스트 데이터의 경우 최소 길이, 인코딩 오류, 라벨 일관성 등을 검증합니다.

예를 들어, 손상된 JPEG 파일이나 50x50보다 작은 이미지, 10자 미만의 텍스트는 자동으로 플래그를 지정하여 제거하거나 재수집합니다. 기존에는 학습 중 오류가 발생해야 문제를 인지했다면, 이제는 학습 전에 체계적인 검증 파이프라인을 실행하여 모든 문제를 사전에 해결할 수 있습니다.

데이터 품질 검증의 핵심 특징은 다섯 가지입니다. 첫째, 완전성 검사로 결측값이나 빈 필드를 찾습니다.

둘째, 일관성 검사로 라벨이 정의된 클래스에 속하는지 확인합니다. 셋째, 유효성 검사로 데이터 형식과 범위가 올바른지 검증합니다.

넷째, 중복 검사로 동일한 샘플이 여러 번 포함되지 않았는지 확인합니다. 다섯째, 품질 보고서를 생성하여 데이터 현황을 시각화합니다.

이러한 검증을 통해 데이터 품질을 보장할 수 있습니다.

코드 예제

import pandas as pd
from PIL import Image
import os
from collections import Counter
import hashlib

class DatasetQualityChecker:
    def __init__(self, metadata_csv):
        self.df = pd.read_csv(metadata_csv)
        self.issues = []

    def check_file_existence(self):
        """파일 존재 여부 확인"""
        print("파일 존재 여부 체크 중...")
        missing_files = []
        for idx, row in self.df.iterrows():
            if not os.path.exists(row['file_path']):
                missing_files.append(row['file_path'])
                self.issues.append(f"파일 없음: {row['file_path']}")
        print(f"누락된 파일: {len(missing_files)}개")
        return missing_files

    def check_image_integrity(self):
        """이미지 파일 무결성 확인"""
        print("\n이미지 무결성 체크 중...")
        corrupted = []
        for idx, row in self.df.iterrows():
            try:
                img = Image.open(row['file_path'])
                img.verify()  # 파일이 손상되었는지 확인
            except Exception as e:
                corrupted.append(row['file_path'])
                self.issues.append(f"손상된 이미지: {row['file_path']} - {e}")
        print(f"손상된 이미지: {len(corrupted)}개")
        return corrupted

    def check_minimum_resolution(self, min_width=100, min_height=100):
        """최소 해상도 체크"""
        print(f"\n최소 해상도 체크 중 ({min_width}x{min_height})...")
        low_res = self.df[(self.df['width'] < min_width) | (self.df['height'] < min_height)]
        for idx, row in low_res.iterrows():
            self.issues.append(f"저해상도: {row['file_path']} ({row['width']}x{row['height']})")
        print(f"저해상도 이미지: {len(low_res)}개")
        return low_res

    def check_class_distribution(self):
        """클래스 분포 확인 및 불균형 경고"""
        print("\n클래스 분포 분석 중...")
        distribution = self.df['class'].value_counts()
        print(distribution)

        # 불균형 감지 (최대/최소 비율이 10배 이상)
        max_count = distribution.max()
        min_count = distribution.min()
        if max_count / min_count > 10:
            self.issues.append(f"심각한 클래스 불균형: 최대{max_count} vs 최소{min_count}")
            print("⚠️  심각한 클래스 불균형 감지!")

    def check_duplicates(self):
        """중복 이미지 검사 (파일 해시 기반)"""
        print("\n중복 이미지 체크 중...")
        hashes = {}
        duplicates = []

        for idx, row in self.df.iterrows():
            try:
                with open(row['file_path'], 'rb') as f:
                    file_hash = hashlib.md5(f.read()).hexdigest()
                    if file_hash in hashes:
                        duplicates.append((row['file_path'], hashes[file_hash]))
                        self.issues.append(f"중복: {row['file_path']} == {hashes[file_hash]}")
                    else:
                        hashes[file_hash] = row['file_path']
            except Exception as e:
                continue

        print(f"중복 이미지: {len(duplicates)}쌍")
        return duplicates

    def generate_report(self, output_file='quality_report.txt'):
        """품질 검증 보고서 생성"""
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write("=" * 50 + "\n")
            f.write("데이터셋 품질 검증 보고서\n")
            f.write("=" * 50 + "\n\n")
            f.write(f"총 샘플 수: {len(self.df)}\n")
            f.write(f"발견된 이슈: {len(self.issues)}개\n\n")
            f.write("이슈 목록:\n")
            for issue in self.issues:
                f.write(f"- {issue}\n")
        print(f"\n품질 보고서 저장: {output_file}")

# 품질 검증 실행
checker = DatasetQualityChecker('dataset_metadata.csv')
checker.check_file_existence()
checker.check_image_integrity()
checker.check_minimum_resolution(min_width=200, min_height=200)
checker.check_class_distribution()
checker.check_duplicates()
checker.generate_report()

print(f"\n총 {len(checker.issues)}개의 품질 이슈 발견")

설명

이것이 하는 일: 이 코드는 데이터셋의 다섯 가지 주요 품질 측면을 자동으로 검증하고, 발견된 모든 이슈를 기록하여 상세한 품질 보고서를 생성하는 완전한 QA 시스템입니다. 첫 번째로, check_file_existence 메서드는 메타데이터에 기록된 파일 경로가 실제로 존재하는지 확인합니다.

os.path.exists()로 각 파일을 체크하여 누락된 파일 목록을 만듭니다. 이는 파일이 이동되거나 삭제된 경우를 감지합니다.

그 다음으로, check_image_integrity는 이미지 파일이 손상되지 않았는지 검증합니다. PIL의 verify() 메서드는 이미지 파일 구조를 검사하여 헤더나 데이터가 손상된 경우 예외를 발생시킵니다.

손상된 파일은 학습 중 오류를 일으키므로 사전에 제거해야 합니다. 세 번째로, check_minimum_resolution은 이미지가 최소 크기 요구사항을 만족하는지 확인합니다.

너무 작은 이미지는 리사이징 시 품질이 크게 저하되므로 재수집하는 것이 좋습니다. check_class_distribution은 클래스 불균형을 감지하여 경고하고, 필요시 언더샘플링이나 오버샘플링을 수행하도록 안내합니다.

네 번째로, check_duplicates는 MD5 해시를 사용하여 완전히 동일한 파일을 찾아냅니다. 파일 이름이 다르더라도 내용이 같으면 중복으로 간주하여 제거합니다.

마지막으로 generate_report는 모든 검증 결과를 텍스트 파일로 저장하여 팀원들과 공유하거나 문서화할 수 있게 합니다. 여러분이 이 코드를 사용하면 학습 전에 모든 데이터 품질 문제를 발견하고 해결할 수 있습니다.

손상된 파일이나 저품질 데이터로 인한 학습 오류를 사전에 방지하고, 품질 보고서를 통해 데이터 수집 프로세스의 개선점을 파악할 수 있습니다.

실전 팁

💡 검증을 CI/CD 파이프라인에 통합하세요. 새 데이터가 추가될 때마다 자동으로 품질 검사를 실행하고, 문제가 있으면 알림을 보내도록 설정하면 지속적인 품질 관리가 가능합니다.

💡 Great Expectations 라이브러리를 사용하면 더 정교한 데이터 검증이 가능합니다. 통계적 기대값을 정의하고 자동으로 검증하며, 상세한 HTML 보고서를 생성해줍니다.

💡 라벨 품질도 검증하세요. 같은 이미지에 대해 여러 라벨러의 라벨이 일치하는지 확인하거나, 사전 학습된 모델로 라벨을 예측하여 수동 라벨과 비교하면 라벨링 오류를 찾을 수 있습니다.

💡 품질 점수를 계산하여 저품질 샘플을 우선 순위화하세요. 블러 정도, 밝기, 대비 등을 종합하여 품질 점수를 매기고, 낮은 점수의 샘플을 수동 검토하거나 제거하세요.

💡 검증 결과를 시각화하세요. matplotlib이나 seaborn으로 클래스 분포, 해상도 분포, 품질 점수 히스토그램 등을 그래프로 만들면 데이터 현황을 직관적으로 파악할 수 있습니다.


10. 데이터 버전 관리하기 - DVC로 데이터셋 추적

시작하며

여러분이 데이터를 계속 수정하고 추가하다가, 예전 버전으로 돌아가고 싶거나 어떤 데이터로 어떤 실험을 했는지 기억이 안 나는 상황을 겪어본 적 있나요? 코드는 Git으로 관리하는데 데이터는 관리 방법이 없어서 혼란스럽습니다.

이런 문제는 데이터 과학 프로젝트에서 매우 흔합니다. 데이터가 GB 단위로 커지면 Git에 올릴 수도 없고, 'dataset_v1', 'dataset_v2_final', 'dataset_v3_really_final' 같은 폴더가 난무하게 됩니다.

어떤 모델이 어떤 데이터로 학습되었는지 추적이 안 되면 실험 재현이 불가능합니다. 바로 이럴 때 필요한 것이 DVC(Data Version Control)입니다.

Git처럼 데이터를 버전 관리하고, 어떤 코드가 어떤 데이터 버전과 연결되는지 추적하여 완벽한 재현성을 확보할 수 있습니다.

개요

간단히 말해서, DVC는 대용량 데이터 파일과 ML 모델을 Git과 연동하여 버전 관리하고, 실험 결과를 추적할 수 있는 오픈소스 도구입니다. 실무에서는 데이터셋을 수정할 때마다 DVC로 커밋하고, Git으로 코드와 함께 관리합니다.

예를 들어, 데이터 증강 후 정확도가 올라갔다면, 증강된 데이터를 DVC로 커밋하고 학습 코드를 Git으로 커밋하여 이 둘을 연결합니다. 나중에 이 시점으로 정확히 돌아갈 수 있습니다.

기존에는 데이터를 Dropbox나 Google Drive에 수동으로 백업했다면, 이제는 DVC가 자동으로 버전을 관리하고 AWS S3, Google Cloud Storage 같은 원격 스토리지와 동기화할 수 있습니다. DVC의 핵심 특징은 네 가지입니다.

첫째, Git과 유사한 명령어로 데이터를 버전 관리할 수 있습니다. 둘째, 실제 데이터는 원격 스토리지에 저장하고 메타데이터만 Git에 저장하여 효율적입니다.

셋째, 파이프라인을 정의하여 데이터 전처리부터 모델 학습까지 전체 워크플로우를 추적할 수 있습니다. 넷째, 팀원들과 데이터를 쉽게 공유하고 협업할 수 있습니다.

이러한 기능으로 ML 프로젝트의 재현성과 협업이 크게 향상됩니다.

코드 예제

# DVC 설치 및 초기화 (터미널 명령)
# pip install dvc
# dvc init

# 데이터 디렉토리를 DVC로 추적
# dvc add dataset/
# 이 명령은 dataset.dvc 파일을 생성하고 실제 데이터는 .dvc/cache로 이동

# dataset.dvc 파일을 Git에 커밋
# git add dataset.dvc .gitignore
# git commit -m "Add dataset v1.0"

# 원격 스토리지 설정 (S3 예시)
# dvc remote add -d storage s3://my-bucket/dvc-storage
# dvc push  # 데이터를 원격 스토리지에 업로드

# Python 코드에서 데이터 버전 정보 기록
import json
from datetime import datetime

def save_dataset_version_info(version, description, metrics=None):
    """데이터셋 버전 정보를 JSON으로 저장"""
    version_info = {
        'version': version,
        'created_at': datetime.now().isoformat(),
        'description': description,
        'total_samples': metrics.get('total_samples') if metrics else None,
        'train_samples': metrics.get('train_samples') if metrics else None,
        'val_samples': metrics.get('val_samples') if metrics else None,
        'test_samples': metrics.get('test_samples') if metrics else None,
        'class_distribution': metrics.get('class_distribution') if metrics else None
    }

    with open('dataset_version.json', 'w', encoding='utf-8') as f:
        json.dump(version_info, f, indent=2, ensure_ascii=False)

    print(f"데이터셋 버전 {version} 정보 저장 완료")
    return version_info

# 사용 예시
metrics = {
    'total_samples': 10000,
    'train_samples': 6000,
    'val_samples': 2000,
    'test_samples': 2000,
    'class_distribution': {'positive': 5500, 'negative': 4500}
}

save_dataset_version_info(
    version='1.0',
    description='초기 데이터 수집 및 전처리 완료. 웹 크롤링으로 수집한 리뷰 데이터',
    metrics=metrics
)

# Git과 DVC로 함께 커밋
# git add dataset_version.json
# git commit -m "Dataset v1.0: 초기 수집 완료"
# dvc push  # 데이터를 원격 스토리지에 백업
# git push  # 메타데이터를 GitHub에 백업

# 특정 버전으로 되돌리기
# git checkout v1.0  # Git 커밋 체크아웃
# dvc checkout  # 해당 시점의 데이터 복원

print("\nDVC 워크플로우:")
print("1. dvc add dataset/ - 데이터 추적 시작")
print("2. git add dataset.dvc - 메타데이터 Git에 추가")
print("3. git commit - 버전 커밋")
print("4. dvc push - 원격 스토리지에 데이터 업로드")
print("5. git push - GitHub에 메타데이터 업로드")

설명

이것이 하는 일: 이 코드는 DVC를 사용하여 데이터셋을 버전 관리하는 전체 워크플로우를 보여주며, Python으로 버전 메타데이터를 기록하는 헬퍼 함수를 제공합니다. 첫 번째로, DVC 명령어들이 어떻게 작동하는지 설명합니다.

dvc add dataset/을 실행하면 DVC가 dataset 폴더의 모든 파일을 해싱하여 .dvc/cache로 이동하고, dataset.dvc라는 작은 메타데이터 파일을 생성합니다. 이 파일에는 실제 데이터의 해시 정보만 들어있어 Git으로 관리하기 적합합니다.

그 다음으로, 원격 스토리지 설정을 통해 팀원들과 데이터를 공유할 수 있게 합니다. dvc remote add로 S3, GCS, Azure Blob 같은 클라우드 스토리지를 연결하면, dvc push로 데이터를 업로드하고 dvc pull로 다운로드할 수 있습니다.

Git LFS와 달리 DVC는 여러 클라우드 제공자를 지원합니다. 세 번째로, save_dataset_version_info 함수는 데이터셋의 버전 정보와 통계를 JSON으로 기록합니다.

총 샘플 수, 클래스 분포, 수집 방법 등을 문서화하여 나중에 어떤 데이터로 실험했는지 정확히 알 수 있게 합니다. 이 JSON 파일은 Git으로 관리되므로 코드 변경과 함께 추적됩니다.

마지막으로, Git과 DVC를 함께 사용하는 워크플로우를 보여줍니다. 데이터를 수정하면 dvc add로 새 버전을 만들고, Git으로 코드와 메타데이터를 커밋하여 특정 코드 버전이 특정 데이터 버전과 정확히 연결되게 합니다.

git checkoutdvc checkout으로 과거 시점으로 완벽히 복원할 수 있습니다. 여러분이 이 코드를 사용하면 실험의 완전한 재현성을 확보할 수 있습니다.

6개월 전 실험을 정확히 재현하거나, 팀원이 같은 데이터로 작업하거나, 데이터 변경이 모델 성능에 미친 영향을 추적하는 것이 모두 가능해집니다.

실전 팁

💡 .dvc/config 파일을 Git에 커밋하되, 크레덴셜은 환경 변수로 관리하세요. AWS 키 같은 민감 정보는 절대 Git에 올리면 안 됩니다. dvc remote modify로 크레덴셜 설정을 로컬에만 유지할 수 있습니다.

💡 DVC 파이프라인을 사용하여 전처리 자동화하세요. dvc run으로 크롤링 → 정제 → 분할 같은 단계를 정의하면, 데이터가 변경될 때 필요한 단계만 자동으로 재실행됩니다.

💡 큰 데이터셋은 디렉토리 단위로 관리하세요. 개별 파일을 추적하면 오버헤드가 큽니다. dvc add images/ 처럼 폴더 전체를 추적하는 것이 효율적입니다.

💡 실험 추적에는 MLflow나 Weights & Biases와 DVC를 함께 사용하세요. DVC로 데이터를 관리하고, MLflow로 모델과 메트릭을 추적하면 완벽한 실험 관리 시스템을 구축할 수 있습니다.

💡 DVC의 메트릭 기능을 활용하세요. dvc metrics show로 모델 성능을 추적하고, dvc plots로 학습 곡선을 시각화할 수 있어 여러 실험을 쉽게 비교할 수 있습니다.


#Python#웹크롤링#데이터전처리#데이터라벨링#데이터증강#AI

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.