MLOps

데이터 인식

백악기작은펭귄 2021. 12. 31.
반응형

데이터 인식

데이터의 요약과 통계는 데이터 자체의 특성을 잘 드러내기는 하나, 잠재적인 문제를 발견하기에는 다소 부족한 면이 있다. TFDV가 어떻게 데이터에서 잠재적인 문제를 발견하고 어떤 도움을 줄 수 있는지 알아보도록 하자.


데이터셋 비교

머신러닝의 데이터셋에는 훈련 세트와 검증 세트가 있다. 검증 데이터는 학습 데이터로 학습된 모델이 제대로 학습되었는지를 검증하는 역할을 하기 때문에, 둘의 대표성이 동등해야 한다. 검증 데이터의 스키마가 학습 데이터와 같은지, feature의 열 또는 값이 유의미한 수준으로 누락되었는지 등을 살펴보고 이를 처리할 수 있는 방법이 필요하다.

 

TFDV는 이러한 질문에 빠르게 답을 줄 수 있다. 다음과 같은 방식을 이용해 두 데이터셋을 불러와 통계를 비교해보자.

train_stats = tfdv.generate_statistics_from_tfrecord(
    data_location=train_tfrecord_filename
)

val_stats = tfdv.generate_statistics_from_tfrecord(
    data_location=val_tfrecord_filename
)

tfdv.visualize_statistics(lhs_statistics=val_stats, rhs_statistics=train_stats,
                          lhs_name='VAL_DATASET', rhs_name='TRAIN_DATASET')

학습 데이터셋과 검증 데이터셋의 비교 (출처: 텐서플로우 공식 가이드)

 

위 그림의 학습 데이터셋과 검증 데이터셋은 상당히 깔끔한 분포를 가지고 있다. 하지만 만약 두 데이터셋 간의 통계가 유의미한 수준으로 다르고, 그 통계를 제공하는 feature가 모델 학습에 중요한 feature라면, 데이터 캡처 방법 수정 등을 통해 문제를 해결해야 한다.

 

이처럼 TFDV를 사용하면 스키마에 관한 데이터 통계를 손쉽게 검증할 수 있으며, 다음 코드를 통해 이상치(anomarly)를 탐지하고 이를 표시할 수 있다.

anomalies = tfdv.validate_statistics(statistics=val_stats, schema=schema)
tfdv.display_anomalies(anomalies)

 

다음은 default로 설정된 이상치 프로토콜을 보여준다. 여기에는 머신러닝 워크플로우를 자동화하는 데 유용한 정보가 포함된다.

anomaly_info {
    key: "company"
    value {
        description: "The feature was present in fewer examples than expected."
        severity: ERROR
        short_description: "Column dropped"
        reason {
            type: FEATURE_TYPE_LOW_FRACTION_PRESENT
            short_description: "Column dropped"
            description: "The feature was present in fewer examples than expected."
        }
        path {
            step: "company"
        }
    }
}

스키마 업데이트

앞의 이상치 프로토콜은 데이터셋에서 자동으로 생성된 스키마와의 차이를 탐지하는 방법을 보여준다. TFDV는 데이터에 관한 도메인 정보에 따라 스키마를 수동으로 설정하는 기능 또한 제공한다. 앞서 특정 feature가 학습 예제 중 일정 수준 이상에 포함되어야 한다고 판단되는 경우 이를 스키마 업데이트를 통해 반영할 수 있다.

 

이를 위해서는 먼저 스키마를 직렬화된 위치에서 로드해야 한다.

schema = tfdv.load_schema_text('schema_location')

 

이후 특정 feature의 min_fraction 값을 원하는 비율(여기서는 90%)로 설정한다.

feature = tfdv.get_feature(schema, 'feature_name')
feature.presence.min_fraction = 0.9

 

특정 도메인을 업데이트하여 필요 없는 내용을 제거할 수도 있다.

# 미국 state 목록에서 알래스카(AK) 제거하기
state_domain = tfdv.get_domain(schema, 'state')
state_domain.value.remove('AK')

 

스키마가 검증되면 다음과 같이 스키마 파일을 직렬화하여 다음 위치에 생성한다.

tfdv.write_schema_text(schema, 'schema_location')

 

통계를 다시 확인하여 업데이트된 이상치를 확인해보자.

updated_anomalies = tfdv.validate_statistics(val_stats, schema)
tfdv.display_anomalies(updated_anomalies)

 

학습 및 서빙 환경에서 다양한 feature를 사용할 수 있도록 스키마를 조정할 수도 있다. 자세한 내용은 링크를 참고하자.


데이터 스큐 및 드리프트

TFDV는 두 데이터셋 간 통계적 차이를 감지하는 skew_comparator를 제공한다. 통계적 정의와는 달리, TFDV에서는 두 데이터셋의 service_statistics 간의 차이에 대한 L-infinity Norm으로 정의된다. 두 데이터셋 간의 차이가 특정 feature에 대한 L-infinity Norm의 임계값을 초과한다면, TFDV는 앞서 정의한 이상치 감지를 사용하여 이상치를 표시한다.

NOTE
Norm은 선형대수학에서 등장하는 개념으로, 벡터를 비교하기 위해 자주 사용된다.
그중 L-infinity Norm은 두 벡터 사이의 차이를 정의하기 위해 사용하는 표현식(서비스 통계량)으로, 벡터 항목의 최대 절댓값이라고도 한다. 예를 들어, 벡터 [3, -10, -5]의 L-infinity Norm은 |-10| = 10이다.
벡터 [2, 4, -1]과 벡터 [9, 1, 8]을 비교하는 상황을 생각해보자. L-infinity Norm을 구하기 위해서는 먼저 둘의 차이를 계산한다

                                                                          [2, 4, -1] - [9, 1, 8] = [-7, 3, -9]

이렇게 계산된 벡터의 L-infinity Norm이 특정 임계값을 넘기면 이상치를 가진다고 판단한다.
즉, TFDV에서 두 벡터는 두 데이터셋의 요약 통계값이며, 이로부터 반환되는 Norm은 두 통계량 사이 가장 큰 값의 절댓값이다.

 

다음 코드는 데이터셋 간의 왜곡(skew)을 비교하는 방법을 보여준다.

# 임계값 설정
tfdv.get_feature(schema, 'company').skew_comparator.infinity_norm.threshold = 0.01 

skew_anomalies = tfdv.validate_statistics(statistics=train_stats,
                                          schema=schema,
                                          serving_statistics=serving_stats)

학습 데이터셋과 서빙 데이터셋 간의 데이터 skew 시각화 (출처: DATACREW Magazine, JUHYUNG SON)

 

TFDV는 각각 다른 날 수집한 두 학습 데이터셋과 같이 동일한 유형의 두 데이터셋의 통계를 비교하는 drift_comparator도 제공한다. 드리프트가 감지되면 모델 아키텍처를 확인하거나 feature engineering을 다시 수행하는 등의 처리를 고려해봐야 한다.

 

skew_comparator와 전반적인 사용방법은 같다. 먼저 비교할 feature에 대해 drift_comparator를 정의해야 한다. 이후 기준 및 비교 대상 데이터셋 통계를 인수로 넣고 validate_statistics를 호출할 수 있다.

tfdv.get_feature(schema, 'company').drift_comparator.infinity_norm.threshold = 0.01 

drift_anomalies = tfdv.validate_statistics(statistics=train_stats_today,
                                          schema=schema,
                                          previous_statistics=train_stats_yesterday)

 

skew_comparator와 drift_comparator의 L-infinity Norm은 데이터 입력 파이프라인에 문제가 있음을 알려주는 데이터셋 간의 큰 차이를 보여주는데 유용하다. L-infinity Norm은 단일 숫자만 반환하므로 스키마가 데이터셋 간의 변동을 탐지하는 데 더 유용하다.


데이터셋 편향

입력 데이터셋의 잠재된 문제 중에는 편향(bias)도 있다. 여기서 편향이란 현실 세계와 동떨어진 데이터로 이해할 수 있다. 사실상 전체 집합을 사용할 수 없기 때문에 표본으로 추출된 데이터셋에는 어떤 방식으로든 편향이 존재할 수밖에 없지만, 이를 미리 알고 최대한 줄이려고 하는 시도는 중요하다.

간혹 공정성(fairness)과 편향을 혼동하기도 하는데, 둘은 다른 의미임을 기억하자. 편향은 실제 데이터와의 괴리(ex. 샘플 내 남녀 비율이 크게 다른 경우)를 나타내지만, 공정성은 특정 집단에게 이질적인 영향을 미치는 모델의 예측(ex. 샘플 내 남자 지원자의 합격율이 높아 실제에서도 그럴 것이라 예측)을 나타낸다.

 

우리가 확인할 수 있는 편향을 선택 편향(Selection bias)이라고 한다. 앞서 설명한 TFDV의 통계 시각화를 이용해 선택 편향을 확인할 수 있다. 미국 state의 통계를 확인할 때, 가장 이상적인 것은 실제 state 별 모집단의 분포를 반영하는 것이다. 즉, 텍사스는 플로리다보다 인구수가 많으므로 더 많은 샘플을 확보하는 것이 좋을 것이다. 만약 이러한 분포에서 선택 편향이 발견되었고, 그것이 모델의 성능에 악영향을 미칠 것이라 판단된다면, 데이터를 추가로 수집하거나 오버/언더 샘플링을 이용해 분포를 맞춰줘야 한다.

 

이상치 프로토콜을 사용하여 이런 문제를 자동으로 경고할 수 있으며 데이터 도메인 지식(ex. 미국 state별 인구수)을 이용하여 데이터셋이 편향되지 않도록 제한할 수도 있다.

 

편향에 관한 자세한 내용은 구글 머신러닝 집중 과정의 설명을 참고하자.


TFDV에서 데이터 슬라이싱

TFDV를 사용하여 선택한 feature에서 데이터셋을 슬라이싱하여 데이터 편향을 확인할 수도 있다.

 

미국 state 데이터를 예시로 들어보겠다. 다음 코드를 사용하면 캘리포니아(CA)에서만 통계를 얻도록 데이터를 슬라이싱할 수 있다.

from tensorflow_data_validation.utils import slicing_util

slice_fn1 = slicing_util.get_feature_value_slicer(
    features={'state':[b'CA']} # feature 값은 이진수 값 list로 제공해야 한다.
)

slice_options = tfdv.StatsOptions(slice_functions=[slice_fn1])
slice_stats = tfdv.generate_statistics_from_csv(
    data_location='./data/consumer-complaints.csv',
    stats_options=slice_options
)

 

몇 가지 헬퍼 함수를 사용하여 슬라이싱 된 통계를 시각화한다.

from tensorflow_metadata.proto.v0 import statistics_pb2

def display_slice_keys(stats):
  print(list(map(lambda x: x.name, slice_stats.datasets)))

def get_sliced_stats(stats, slice_key):
  for sliced_stats in stats.datasets:
    if sliced_stats.name == slice_key:
      result = statistics_pb2.DatasetFeatureStatisticsList()
      result.datasets.add().CopyFrom(sliced_stats)
      return result
    print('Invalid Slice Key')

def compare_slices(stats, slice_key1, slice_key2):
  lhs_stats = get_sliced_stats(stats, slice_key1)
  rhs_stats = get_sliced_stats(stats, slice_key2)
  tfdv.validate_statistics(lhs_stats, rhs_stats)

# 시각화
tfdv.visualize_statistics(get_sliced_stats(slice_stats, 'state_CA'))

 

다음 코드를 사용해 캘리포니아의 통계를 전체 결과와 비교할 수 있다.

compare_slices(slice_stats, 'state_CA', 'All Examples')

feature 값으로 슬라이싱한 데이터 시각화 (출처: DATACREW Magazine, JUHYUNG SON)

반응형

'MLOps' 카테고리의 다른 글

머신러닝 파이프라인에 TFDV 통합하기  (0) 2022.01.03
GCP를 사용한 대용량 데이터셋 처리  (0) 2022.01.02
데이터 검증  (0) 2021.12.27
데이터 준비  (0) 2021.12.21
데이터 수집  (0) 2021.12.19

댓글