본문 바로가기
파이썬

파이썬 - 텍스트 데이터 전처리 파이프라인 (2) : Sklearn Pipeline

by Tiabet 2023. 8. 28.

1편에서 텍스트 데이터를 전처리하기 위해 전각-반각 변환, 정규식 적용, 맞춤법 검사 등을 살펴보았다. 2편에서는 파이프라인을 설계하기 위한 Sklearn의 Pipeline 함수에 대해서 간략하게 정리해보고자 한다. 

 

코드 전문 :

https://github.com/Tiabet/Project_Market/blob/master/%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%8D%B0%EC%9D%B4%ED%84%B0%20%EC%A0%84%EC%B2%98%EB%A6%AC/text_preprocessing_pipeline.py

 

 

Pipeline 이란?

Pipeline, 파이프라인이란 데이터에 대하여 여러 작업을 수행하되 한 작업의 결과값이 다음 작업의 입력값이 되게끔 설계하는 것을 말한다. 따라서 Pipeline은 꼭 싸이킷런의 함수를 가져와서 사용할 필요는 없다. 실력이 뛰어나다면 직접 구현해도 전혀 문제가 없을 것이다. 하지만 나는 싸이킷런의 Pipeline 

 

https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html

 

sklearn.pipeline.Pipeline

Examples using sklearn.pipeline.Pipeline: Feature agglomeration vs. univariate selection Pipeline ANOVA SVM Poisson regression and non-normal loss Permutation Importance vs Random Forest Feature Im...

scikit-learn.org

Parameter와 기능들에 대해서 요약된 싸이킷런의 공식 문서이다. 참고로 sklearn 말고 imblearn에도 pipeline을 구현해놓은 함수가 있다고 한다. 또한 tensorflow 등으로도 파이프라인을 설계할 수 있다.

https://www.tensorflow.org/guide/data?hl=ko 

 

tf.data: TensorFlow 입력 파이프라인 빌드  |  TensorFlow Core

PaLM API 및 MakerSuite Explore Generative AI를 사용 하여 Google의 대규모 언어 모델을 사용해 보세요. tf.data: TensorFlow 입력 파이프라인 빌드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저

www.tensorflow.org

Pipeline 적용 방식

나는 위와 같이 test 파일에서 함수들을 점검하면서 최종 완성시킨 뒤, 여러 함수들을 결합시키는 파이프라인으로 옮겼다.

def normalize_unicode(text):
    return unicodedata.normalize('NFKC', text)

def correct_spelling(text):
    try:
        spelled_sent = spell_checker.check(text)
        return spelled_sent.checked
    except Exception  as e:
        print("text", text)
        print("Error:", e)
        return text

def apply_regex(text): 
    text = re.sub(r'[^ 가-힣a-zA-Z\(\):]','',text)
    text = re.sub(r'[a-zA-Z]{1,2}', '', text)
    text = re.sub(r':\s?[\)D]|:\s?\(', '', text)
    text = re.sub(r'\b(\w+)(?: \1\b)+', r'\1', text)
    text = re.sub(r'\s+', ' ', text)
    return text

이런식으로 테스트했던 함수들을 모아놓은 뒤,

from sklearn.base import BaseEstimator, TransformerMixin

class NormalizeUnicodeTransformer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self

    def transform(self, X):
        print("Normalizing Unicode")
        return X.apply(normalize_unicode)

class CorrectSpellingTransformer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self

    def transform(self, X):
        print("Correcting Spelling")
        return X.apply(correct_spelling)

class ApplyRegexTransformer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self

    def transform(self, X):
        print("Applying Regex")
        return X.apply(apply_regex)

이런 식으로 Transformer 클래스들로 바꾸어주고, 

from sklearn.pipeline import Pipeline

preprocessing_pipeline = Pipeline([
        ('normalize_unicode', NormalizeUnicodeTransformer()),
        ('apply_regex', ApplyRegexTransformer()),
        ('correct_spelling', CorrectSpellingTransformer())
    ])

이렇게 파이프라인을 설계할 수 있었다.

 

그렇다면 이제 내가 파이프라인을 설계하면서 느꼈던 몇 가지 중요한 점들을 정리하고 글을 마쳐보고자 한다.

 

Scikit learn으로 Pipeline을 설계할 때의 유의점

1. 쉼표에 주의하자

    preprocessing_pipeline = Pipeline([
        ('normalize_unicode', NormalizeUnicodeTransformer()),
        ('apply_regex', ApplyRegexTransformer())
        ('correct_spelling', CorrectSpellingTransformer())
    ])

위 코드는 실행하면 "tuple object is not callable" 이라는 짧은 오류를 띄운다. 왜일까?

3번째 줄 마지막에 쉼표가 없기 때문이다. Sklearn의 파이프라인은 (작업명, 작업에 쓰일 변환기) 튜플로 이루어진 작업들을 순차적으로 실행하는데, 이를 쉼표로 구분하는 것으로 보인다. 

 

2. 같은 작업명을 두 번 사용하면 안 된다

preprocessing_pipeline = Pipeline([
        ('normalize_unicode', NormalizeUnicodeTransformer()),
        ('apply_regex', ApplyRegexTransformer()),
        ('correct_spelling', CorrectSpellingTransformer()),
        ('correct_spelling', CorrectSpellingTransformer())
    ])

위 코드를 실행하면 "Names provided are not unique" 이란 오류를 띄운다. 같은 작업을 두 번 실행하고 싶다고 해서 이런 식으로 동일한 작업명으로 사용하면 안 된다. 따라서 만약 같은 작업을 두 번 실행하고 싶다면 아래와 같이 작업명을 살짝 바꿔줘야한다.

preprocessing_pipeline = Pipeline([
        ('normalize_unicode', NormalizeUnicodeTransformer()),
        ('apply_regex', ApplyRegexTransformer()),
        ('correct_spelling', CorrectSpellingTransformer()),
        ('correct_spelling_2', CorrectSpellingTransformer())
    ])

 

3. Transformer (변환기) 를 구현하는 것이 편하다

정말 중요한 내용은 이 내용인 것 같다. 

테스트를 마친 함수들을 파이프라인에 맞춰 바꾸어달라고 챗지피티에 요구하면 다음과 같은 코드를 알려준다.

 

    preprocessing_pipeline = Pipeline([
        ('normalize_unicode', lambda x: x.apply(normalize_unicode)),
        ('correct_spelling', lambda x: x.apply(correct_spelling)),
        ('apply_regex', lambda x: x.apply(apply_regex))
    ])

Transformer, 즉 변환기를 구현하지 않고 lambda 를 이용하여 바로 데이터에 함수를 적용시키는 것이다. 하지만 이런 방식은 "This 'Pipeline' has no attribute 'transform'" 라는 AttributeError를 일으킨다. transform 이라는 메소드를 찾을 수 없다는 오류이다. 

위 오류에서 알 수 있듯이, 싸이킷런의 Pipeline은 그 안에 함수들이 반드시 transform 이라는 메소드를 갖고 있어야 한다. 그 이유에 대해서는 아래 블로그를 보고 알 수 있었다.https://databuzz-team.github.io/2018/11/11/make_pipeline/

 

<Scikit Learn>전처리를 위한 변환기 만들기 · Databuzz's Tech Blog

 

databuzz-team.github.io

간단히 정리하자면 싸이킷런의 Pipeline이 설계된 구조 그 자체 때문에, 파이프라인 안에 들어가는 함수들은 fit, transform, fit_transform 세 메소드와 get_params, set_params 두 메소드를 구현하고 있어야 한다. get_params 와  set_params 는 싸이킷런의 모든 estimator들이 기본적으로 갖고 있어야 하는 하이퍼파라미터 관련 메소드들이고, fit, transform, fit_transform 등은 변환기, 즉 transformer 클래스에서 호출되는 함수이므로 구현을 반드시 해놓아햐 한다. 즉, 싸이킷런의 Pipeline은 그 안에 Transformer 들이 들어있어야만 작동한다.

 

그래서 아래와 같이 Transformer를 따로 선언해준 것이다.

from sklearn.base import BaseEstimator, TransformerMixin

class NormalizeUnicodeTransformer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self

    def transform(self, X):
        print("Normalizing Unicode")
        return X.apply(normalize_unicode)

BaseEstimator 는 get_params,  set_params 함수들을 갖고 있고, TranformerMixin 은 fit_transform을 구현하고 있기 때문에, 사용자가 이 두 함수들을 상속하면 fit 과 transform만 수동으로 구현하여 Transformer를 완성시킬 수 있다.

 

그렇다면 어째서 싸이킷런의 Pipeline은 안에 Transformer가 아니면 작동하지 않게 설계되었을까? 여기서부터는 100% 내 뇌피셜인데, 그건 싸이킷런 자체가 머신러닝을 위한 라이브러리이기 때문이다. 캐글 같은 대회에서 사람들이 Pipeline을 다루는 방식을 보면 애초에 머신러닝 관련 패키지들을 Pipeline 안에 집어넣어 전처리부터 모델 결과 도출까지 깔끔하게 완성시킨다. 즉, 애초에 머신러닝을 목적으로 만들어진  Pipeline 함수를 이렇게 목적을 벗어나 텍스트 전처리에도 사용할 수 있는 것이 아닐까, 하는 생각을 해본다.