Data Leakage: Kontaminacja Danych Treningowych i Testowych

Dane odgrywają kluczową rolę w podejmowaniu decyzji i ważne jest, abyśmy rozumieli znaczenie czystości naszych danych treningowych i testowych. Kontaminacja danych treningowych i testowych to subtelny, ale potencjalnie destrukcyjny problem, który może prowadzić do błędnych wniosków i wyników modeli. 

Czym jest kontaminacja danych treningowych i testowych?

Przypomnijmy, że walidacja ma na celu pomiar, jak model radzi sobie na danych, które dotąd nie były brane pod uwagę. Kontaminacja danych treningowych i testowych występuje, gdy dane, które są przeznaczone do testowania i oceny modelu, wpływają na proces przygotowywania danych i uczenia modelu. To oznacza, że dane, które powinny być odseparowane od procesu uczenia modelu, w jakikolwiek sposób wpływają na ten proces.

Dlaczego to jest problem?

Kontaminacja danych treningowych i testowych jest problemem, ponieważ może prowadzić do nadmiernego optymizmu co do jakości modelu. Model, który działa dobrze na danych, które zostały wykorzystane do uczenia i testowania, może działać bardzo źle na zupełnie nowych danych, które nie były używane w procesie modelowania. To może mieć poważne konsekwencje w przypadku podejmowania rzeczywistych decyzji opartych na tych modelach.

Przykład 1:

Rozważmy przykład, w którym mamy zbiór danych dotyczący klasyfikacji chorób serca. Załóżmy, że w naszych danych mamy kolumnę “ciśnienie krwi”. Jeśli podzielimy dane na zbiór treningowy i testowy po zastosowaniu jakiegokolwiek preprocessingu i normalizacji, to znaczy, że statystyki dotyczące “ciśnienia krwi” w naszych danych testowych wpłyną na sposób, w jaki przeprowadzimy normalizację w zbiorze treningowym. To może doprowadzić do tego, że model będzie działać dobrze na danych testowych, ale źle na nowych danych pacjentów.

import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# Tworzenie przykładowego DataFrame z wynikami ciśnienia krwi
data = {
    'ciśnienie_krwi': [120, 130, 140, 150, 160, 170, 180, 190],
    'czy_chory': [0, 1, 1, 1, 1, 0, 0, 1]  # (0 - zdrowy, 1 - chory)
}

df = pd.DataFrame(data)

# Podział danych na zbiór treningowy i testowy
X = df[['ciśnienie_krwi']]
y = df['czy_chory']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42)

# Skalowanie Min-Max na całym zbiorze danych
scaler_all_data = MinMaxScaler()
X_scaled_all_data = scaler_all_data.fit_transform(X)

# Skalowanie Min-Max osobno dla zbioru treningowego i testowego
scaler_train = MinMaxScaler()
X_train_scaled = scaler_train.fit_transform(X_train)

scaler_test = MinMaxScaler()
X_test_scaled = scaler_test.fit_transform(X_test)

# Wyświetlanie wyników
print("Skalowanie Min-Max na całym zbiorze danych:")
print(X_scaled_all_data)

print("\nSkalowanie Min-Max osobno dla zbioru treningowego i testowego:")
print("Zbiór treningowy:")
print(X_train_scaled)
print("Zbiór testowy:")
print(X_test_scaled)
Skalowanie Min-Max na całym zbiorze danych:
[[0. ]
[0.14285714]
[0.28571429]
[0.42857143]
[0.57142857]
[0.71428571]
[0.85714286]
[1. ]]

Skalowanie Min-Max osobno dla zbioru treningowego i testowego:
Zbiór treningowy:
[[0. ]
[1. ]
[0.28571429]
[0.57142857]
[0.42857143]
[0.85714286]]
Zbiór testowy:
[[0.]
[1.]]

W tym przykładzie dane testowe wpływają na sposób normalizacji danych treningowych, co może prowadzić do nadmiernego optymizmu co do wyników modelu.

Przykład 2:

Załóżmy, że mamy zbiór danych zawierający informacje o wynikach egzaminów matematycznych i fizycznych uczniów. Jednym z atrybutów w zbiorze danych jest “wynik_egzaminu_fizyka”, ale w niektórych przypadkach brakuje danych. Chcemy przewidzieć, czy uczeń zdał egzamin matematyczny na podstawie wyniku egzaminu fizycznego.

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

# Tworzenie przykładowego DataFrame
data = {
    'wynik_egzaminu_matematyka': [80, 90, 70, 60, 85, 75, 78, 92],
    'wynik_egzaminu_fizyka': [75, 85, None, 55, None, 70, 80, None],
    'zdał_egzamin_matematyka': [1, 1, 0, 0, 1, 0, 1, 1]
}

df = pd.DataFrame(data)

# Uzupełnienie brakujących wartości średnią na całym zbiorze
imputer = SimpleImputer(strategy='mean')
df_imputed = imputer.fit_transform(df[['wynik_egzaminu_fizyka']])

# Podział danych na zbiór treningowy i testowy
X = df_imputed
y = df['zdał_egzamin_matematyka']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42)

# Uczenie modelu
model = LogisticRegression()
model.fit(X_train, y_train)

# Ocena modelu
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)

print(f'Dokładność modelu po imputacji na całym zbiorze: {accuracy}')

# Podział danych na zbiór treningowy i testowy
X = df[['wynik_egzaminu_fizyka']]
y = df['zdał_egzamin_matematyka']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42)

# Uzupełnienie brakujących wartości średnią
imputer = SimpleImputer(strategy='mean')
X_train_imputed = imputer.fit_transform(X_train)
X_test_imputed = imputer.transform(X_test)

# Uczenie modelu
model = LogisticRegression()
model.fit(X_train_imputed, y_train)

# Ocena modelu
y_pred = model.predict(X_test_imputed)
accuracy = accuracy_score(y_test, y_pred)

print(f'Dokładność modelu po imputacji osobno 
dla zbioru treningowego i testowego: {accuracy}')
Dokładność modelu po imputacji na całym zbiorze: 1.0
Dokładność modelu po imputacji osobno 
dla zbioru treningowego i testowego: 0.5

W tym przykładzie brakujące wartości w kolumnie “wynik_egzaminu_fizyka” są uzupełniane średnią wartością tej kolumny. Jednakże, uzupełnianie brakujących danych przed podziałem na zbiory treningowy i testowy może prowadzić do kontaminacji danych. Jeśli np. średnia jest obliczana na całym zbiorze danych, to w momencie trenowania modelu dane testowe mają dostęp do informacji ze zbioru treningowego, co może prowadzić do nadmiernego optymizmu w ocenie modelu. Aby uniknąć tego rodzaju kontaminacji, uzupełnianie brakujących danych powinno być wykonywane osobno na zbiorze treningowym i testowym przed procesem uczenia modelu.

Jak unikać kontaminacji danych treningowych i testowych?

  • Podział danych przed preprocessingiem: Dane powinny być podzielone na zbiór treningowy i (walidacyjny) testowy przed przeprowadzeniem jakiejkolwiek obróbki danych, włączając w to normalizację, imputację brakujących wartości itp.
  • Używanie potoków (pipelines): Potoki w bibliotece scikit-learn pozwalają na określenie sekwencji kroków przetwarzania danych, które będą przeprowadzane niezależnie na zbiorze treningowym i testowym.

Zobacz także:

  • Piotr Szymański

    Kategoria:

    Hejka! Zapraszam na skrót z minionych dwóch tygodni, który przyswoić możecie przy ciepłej herbatce w te mroczne, szare dni. W opublikowanym przez Google 14 listopada ostrzeżeniu wskazano kilka najważniejszych rodzajów oszustw internetowych. Uwagę zwrócono między na niebezpieczne techniki ataków typu cloaking, które nabierają nowego wymiaru dzięki wykorzystaniu sztucznej inteligencji. Cloaking polega na ukrywaniu przed użytkownikiem […]
  • Piotr Szymański

    Kategoria:

    Hejka po dłuższej przerwie! Zaczynamy świeżym tematem. Raptem kilkanaście godzin temu do użytkowników trafiła, zapowiedziana 25 lipca, funkcja SearchGPT od OpenAI, umożliwiająca, w przeciwieństwie do tradycyjnych modeli językowych, na integrację z internetem w czasie rzeczywistym. SearchGPT ma dostęp do aktualnych informacji z sieci, co pozwala na udzielanie odpowiedzi opartych na najnowszych danych. Ponadto SearchGPT dostarcza […]
  • Piotr Szymański

    Kategoria:

    Hejson! Dzisiejsza konsumpcja mediów ma to do siebie, że odbywa się na 5-6 calowym ekranie telefonu. Ma też to do siebie, że zanim zdjęcie dotrze do Ciebie, to przejdzie przez 6 konwersacji na jedynym słusznym messengerze, zatem zostanie 6-cio krotnie skompresowane. W międzyczasie, jak będziecie mieli pecha, to jakiś wujek zrobi screena, zamiast zapisać zdjęcie […]
  • Piotr Szymański

    Kategoria:

    Hej! Robimy bardzo dużo zdjęć, a co za tym idzie – wiele z nich jest niechlujnych, z zabałagnionym tłem. Możemy jednak chcieć wykorzystać je do pochwalenia się naszym ryjkiem na jakimś publicznym profilu, gdyż np. naturalne, miękkie światło korzystnie eksponuje naszą facjatę. Podejścia mogą być dwa – albo zdecydujemy się na blur bądź zupełne usunięcie […]
  • Piotr Szymański

    Kategoria:

    Strzałeczka. Nvidia przejęła OctoAI, startup specjalizujący się w optymalizacji modeli uczenia maszynowego. To już piąta akwizycja Nvidii w 2024 roku, co czyni aktualnie nam panujący rok rekordowym pod względem liczby przejęć. OctoAI, założone w 2019 roku przez Luisa Ceze, skupiło się na tworzeniu oprogramowania zwiększającego wydajność modeli uczenia maszynowego na różnych platformach sprzętowych. Oprogramowanie OctoAI […]