Machine Learning Data Preparation

DP4ML - Outlier

DP4ML - Outlier

Bài thứ 3 trong chuỗi các bài viết về chủ đề Data Preparation cho các mô hình ML. Trong bài này, chúng ta sẽ tìm hiểu về Outlier Data, mà tiếng việt của chúng ta gọi là dữ liệu ngoại lệ.

1. Outlier Data là gì?

Đôi khi làm việc với dữ liệu, chúng ta bắt gặp các mẫu rất lạ, khác nhiều so với những mẫu khác trong cùng bộ dữ liệu. Sự khác nhau đó có thể là về kiểu dữ liệu, phạm vi giá trị, phân phối dữ liệu, … Số lượng những mẫu lạ thường không lớn, và chúng đó được gọi là Outlier Data, hay “dữ liệu ngoại lệ”. Việc tồn tại Outlier Data có thể gây nhiễu, làm cho việc mô hình hóa trở nên khó khăn hơn. Giải pháp xóa bỏ Outlier Data, trong hầu hết các trường hợp, giúp cải thiện đáng kể hiệu quả của các ML model.

Outlier Data được sinh ra do một trong các nguyên nhân chủ yếu sau:

  • Xảy ra lỗi trong quá trình đo đạc, thu thập dữ liệu.
  • Dữ liệu bị hư hỏng (currption) trong quá trình lưu trữ hoặc chuyển đổi.
  • Dữ liệu bị tác động bởi các yếu tố ngẫu nhiên bên ngoài.

Trong thực tế, không có định nghĩa chính xác của Outlier Data, bởi vì nó phụ thuộc vào bài toán cụ thể. Đối với bài toán này, đó có thể là Outlier Data, nhưng đối với bài toán kia, nó lại có thể là giá trị hợp lệ. Để quyết định chính xác, cần phải tham khảo thêm ý kiến của chuyên gia trong lĩnh vực đó.

2. Các phương pháp nhận diện Outlier Data

Trước khi tìm hiểu các phương pháp giúp nhận diện ra Outlier Data, chúng ta sẽ sinh ra một bộ dữ liệu giả để thực hành cho phần này.

Dữ liệu sinh ra bao gồm 10.000 số ngẫu nhiên được phân phối theo Gaussian, có Mean là 50 và STD là 5. Sẽ có một vài số trong tập dữ liệu này nằm ở khá xa so với Mean, chúng ta sẽ coi đó như là Outlier Data.

Code thực hiện như sau:

# generate gaussian data
from numpy.random import seed
from numpy.random import randn
from numpy import mean
from numpy import std
# seed the random number generator
seed(1)
# generate univariate observations
data = 5 * randn(10000) + 50
# summarize
print( ' mean=%.3f stdv=%.3f ' % (mean(data), std(data)))

Chạy code trên ta có Output:

mean=50.049 stdv=4.994

2.1 Phương pháp sử dụng độ lệch chuẩn - STD

Nếu biết trước rằng tập dữ liệu mà ta đang làm việc tuân theo phân phối Gaussian hoặc gần với Gaussian thì chúng ta có thể sử dụng STD để xác định và loại bỏ Outlier Data. Bởi vì, Gaussian có đặc điểm là:

  • Các mẫu nằm trong phạm vi STD tính từ Mean sẽ chiếm khoảng 68% tổng số mẫu trong tập dữ liệu.
  • Các mẫu nằm trong phạm vi 2*STD tính từ Mean sẽ chiếm khoảng 95% tổng số mẫu trong tập dữ liệu.'
  • Các mẫu nằm trong phạm vi 3*STD tính từ Mean sẽ chiếm khoảng 99.7% tổng số mẫu trong tập dữ liệu.

Ví dụ, nếu tập dữ liệu có Mean là 50 và STD là 5 thì tổng số mẫu có giá trị trong khoảng [45;55] sẽ chiếm 68% tổng số mẫu.

Biết được như vậy rồi, chúng ta sẽ tính Mean và STD, sau đó định nghĩa Outlier Data là các mẫu nằm ngoài phạm vi của STD (hoặc 2STD, hoặc 3STD) tính từ Mean. Code dưới đây sẽ hiện thực phương pháp này:

# identify outliers with standard deviation
from numpy.random import seed
from numpy.random import randn
from numpy import mean
from numpy import std
# seed the random number generator
seed(1)
# generate univariate observations
data = 5 * randn(10000) + 50
# calculate summary statistics
data_mean, data_std = mean(data), std(data)
# define outliers là 3*STD
cut_off = data_std * 3
lower, upper = data_mean - cut_off, data_mean + cut_off
# identify outliers
outliers = [x for x in data if x < lower or x > upper]
print( ' Identified outliers: %d ' % len(outliers))
# remove outliers
outliers_removed = [x for x in data if x >= lower and x <= upper]
print( ' Non-outlier observations: %d ' % len(outliers_removed))

Chạy code trên, đầu tiên nó sẽ in ra số lượng Outliers, sau đó là số lượng mẫu sau khi đã loại bỏ Outliers.

Identified outliers: 29
Non-outlier observations: 9971

Bộ dữ liệu mà chúng ta đang sử dụng trong ví dụ này chỉ có 1 chiều (univariate), nhưng phương pháp này hoàn toàn có thể mở rộng ra được với dữ liệu nhiều chiều.

2.2 Phương pháp sử dụng khoảng tứ phân vị - Interquartile Range

a, Nhắc lại một chút về trung bình, trung vị, tứ phân vị.

Giả sử ta có dãy số sau: 6, 5, 8, 7, 12, 13, 15, 14, 2, 200, 1. Câu hỏi đặt ra là tìm giá trị trung bình, trung vị, tứ phân vị của dãy số đó.

  • Giá trị trung bình

Giá trị trung bình chính là tổng của tất cả các số, chia cho số lượng số, ở đây số lượng số là 11 số, như vậy giá trị trung bình cộng sẽ là:

$Mean = \frac{6+5+8+7+12+13+15+14+2+200+1}{11} = 25.72$

  • Giá trị trung vị

Bước 1: Sắp xếp dãy số ở trên theo thứ tự tăng dần, ta được kết quả:1, 2, 5, 6, 7, 8, 12, 13, 14, 15, 200

Bước 2: Trung vị là giá trị đứng ở vị trí giữa trong một dãy số đã được sắp xếp có thứ tự. Trước và sau trị số trung vị sẽ có 50% quan sát. Dãy số ở trên có 11 số (1, 2, 5, 6, 7, 8, 12, 13, 14, 15, 200) => Số ở chính giữa là 8 sẽ chia đôi bộ số làm 2, bên trái nó có 5 số, bên phải nó có 5 số, => 8 chính là số trung vị của tập hợp ở trên( trung vị cũng còn được gọi là tứ phân vị thứ nhì).

Điểm khác nhau cơ bản của giá trị trung vị trong việc mô tả dữ liệu so với giá trị trung bình là nó không bị sai lệch bởi một tỷ lệ nhỏ các giá trị cực lớn hoặc cực nhỏ (outliers), và do đó nó cung cấp một đại diện tốt hơn về giá trị đặc trưng.

Trung vị của một danh sách hữu hạn các số là số ở giữa, khi các số đó được liệt kê theo thứ tự từ nhỏ nhất đến lớn nhất. Nếu tập dữ liệu có số lượng quan sát lẻ, thì tập ở giữa được chọn. Ví dụ: danh sách bảy số sau 1, 3, 3, 6, 7, 8, 9 có giá trị trung vị là số 6. Nếu tập hợp có số lượng quan sát là chẵn thì không có giá trị giữa. Khi đó, giá trị trung vị thường được xác định là giá trị trung bình của hai giá trị giữa. Ví dụ, tập dữ liệu 1, 2, 3, 4, 5, 6, 8, 9 có giá trị trung vị là 4.5 nghĩa là (4 + 5) / 2.

  • Giá trị tứ phân vị

Điểm tứ phân vị (interquartile) là giá trị bằng số phân chia một nhóm các kết quả quan sát bằng số thành bốn phần, mỗi phần có số liệu quan sát bằng nhau(=25% số kết quả quan sát). Tứ phân vị có 3 giá trị, đó là tứ phân vị thứ nhất (Q1), thứ nhì (Q2) và thứ ba (Q3). Ba giá trị này chia một tập hợp dữ liệu (đã sắp xếp theo thứ tự từ từ bé đến lớn) thành 4 phần có số lượng quan sát đều nhau.

Xem lại dãy số 11 số ở trên của chúng ta (1, 2, 5, 6, 7, 8, 12, 13, 14, 15, 200):

Giá trị tứ phân vị thứ nhất Q1 bằng trung vị phần dưới, phần dưới là các số (1, 2, 5, 6, 7), là số 5.

Giá trị tứ phân vị thứ hai Q2 chính bằng giá trị trung vị, là số 8.

Giá trị tứ phân vị thứ ba Q3 bằng trung vị phần trên (12, 13, 14, 15, 200), là số 14.

b, Mô tả phương pháp

Trong thực tế, dữ liệu của chúng ta hiếm khi nào tuân theo phân phối Gaussian. Vì thế, phương pháp sử dụng Mean và STD không thể sử dụng được trong những trường hợp đó. Lúc này, tứ phân vị sẽ phát huy tác dụng. Phương pháp Interquartile Range, viết tắt là IQR, bao gồm các bước sau:

  • Bước 1:

Tính toán sai số giữa tứ phân vị thứ 3 và tứ phân vị thứ nhất: IQR = Q3 - Q1

  • Bước 2:

Tính toán giá trị cut_off bằng cách nhân IQR với hệ số k. Giá trị của k thể hiện mức độ Outlier của dữ liệu. Giá trị thông thường của nó là 1.5: cut_off = IQR * k.

  • Bước 3:

Tính toán giá trị giới hạn trên và giới hạn dưới của Outlier: lower, upper = Q1 - cut_off, Q3 + cut_off.

  • Bước 4:

Các mẫu có giá trị nằm ngoài khoảng giới hạn bởi [lower; upper] được coi là Outlier.

Code thực hiện phương pháp này như sau:

# identify outliers with interquartile range
from numpy.random import seed
from numpy.random import randn
from numpy import percentile
# seed the random number generator
seed(1)
# generate univariate observations
data = 5 * randn(10000) + 50
# calculate interquartile range
Q1, Q3 = percentile(data, 25), percentile(data, 75)
iqr = Q3 - Q1
print( ' Interquartile: Q1=%.3f, Q3=%.3f, IQR=%.3f ' % (Q1, Q3, iqr))
# calculate the outlier cutoff
cut_off = iqr * 1.5
lower, upper = Q1 - cut_off, Q3 + cut_off
# identify outliers
outliers = [x for x in data if x < lower or x > upper]
print( ' Identified outliers: %d ' % len(outliers))
# remove outliers
outliers_removed = [x for x in data if x >= lower and x <= upper]
print( ' Non-outlier observations: %d ' % len(outliers_removed))

Kết quả chạy code trên, đầu tiên sẽ in ra giá trị của tứ phân vị thứ nhất và thứ 3, sau đó là số lượng Outliers, và cuối cùng là số lượng mẫu còn lại trong tập dữ liệu sau khi đã xóa đi các Outliers:

Interquartile: Q1=46.685, Q3=53.359, IQR=6.674
Identified outliers: 81
Non-outlier observations: 9919

Đối với tập dữ liệu nhiều chiều, cách tiếp cận của phương pháp này cũng hoàn toàn tương tự.

2.3 Phương pháp sử dụng Local Outlier Factor (LOF)

LOF là một kỹ thuật khai thác ý tưởng về việc sử dụng các mẫu lân cận để phát hiện ngoại lệ. Mỗi mẫu sẽ được gán cho một giá trị Score thể hiện mức độ cô lập hoặc khả năng nó có thể là Outlier dựa trên quy mô của vùng lân cận của nó. Những mẫu có giá trị Score lớn nhất có nhiều khả năng là Outliers.

Thư viện Scikit-learn cung cấp lớp LocalOutlierFactor giúp chúng ta đơn giản hóa việc thực hiện phương pháp này.

Dưới đây, chúng ta sẽ sử dụng bộ dữ liệu Boston Housing Dataset để huấn luyện mô hình LinearRegression theo 2 cách: không xóa bỏ Outliers và có xóa bỏ Outliers sử dụng phương pháp LOF. Kết quả của 2 cách đó sẽ được mang ra so sánh với nhau.

a, Cách 1
# evaluate model on the raw dataset
from pandas import read_csv
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
# load the dataset
df = read_csv( 'housing.csv' , header=None)
# retrieve the array
data = df.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# fit the model
model = LinearRegression()
model.fit(X_train, y_train)
# evaluate the model
yhat = model.predict(X_test)
# evaluate predictions
mae = mean_absolute_error(y_test, yhat)
print( 'MAE: %.3f ' % mae)

Kết quả thực hiện:

MAE: 3.417
b, Cách 2
# evaluate model on training dataset with outliers removed
from pandas import read_csv
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.neighbors import LocalOutlierFactor
from sklearn.metrics import mean_absolute_error
# load the dataset
df = read_csv( ' housing.csv ' , header=None)
# retrieve the array
data = df.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# summarize the shape of the training dataset
print(X_train.shape, y_train.shape)
# identify outliers in the training dataset
lof = LocalOutlierFactor()
yhat = lof.fit_predict(X_train)
# select all rows that are not outliers
mask = yhat != -1
X_train, y_train = X_train[mask, :], y_train[mask]
# summarize the shape of the updated training dataset
print(X_train.shape, y_train.shape)
# fit the model
model = LinearRegression()
model.fit(X_train, y_train)
# evaluate the model
yhat = model.predict(X_test)
# evaluate predictions
mae = mean_absolute_error(y_test, yhat)
print( 'MAE: %.3f ' % mae)

Kết quả thực hiện:

(339, 13) (339,)
(305, 13) (305,)
MAE: 3.356

Số lượng mẫu giảm từ 339 -> 305 sau khi xóa bỏ Outliers theo phương pháp LOF. Giá trị MAE giảm từ 3.417 -> 3.356. Đó là một sự cải thiện hiệu suất đáng kể của model.

Ngoài LocalOutlierFactor, Scikit-learn còn cung cấp 1 lớp khác là IsolationForest (tất nhiên là thuật toán cũng khác) để loại bỏ Outliers. Cách sử dụng thì 2 cách hoàn toàn giống nhau. Bạn có thể thử thay LocalOutlierFactor bằng IsolationForest vào code trên rồi chạy lại xem kết quả như thế nào?

3. Kết luận

Kết thúc bài thứ 3 trong chuỗi bài viết về chủ đề Data Preparation cho ML model. Trong bài này, chúng ta đã tìm hiểu về Outliers và phương pháp xử lý chúng.

Toàn bộ code của bài này, các bạn có thể tham khảo tại đây.

Trong bài tiếp theo, chúng ta sẽ tiếp tục tìm hiểu về một vấn đề khác của Data Cleaning, đó là nhận diện và xử lý vấn đề dữ liệu bị thiếu - Missing Data. Mời các bạn đón đọc.

4. Tham khảo

[1] Jason Brownlee, “Data Preparation for Machine Learning”, Book: https://machinelearningmastery.com/data-preparation-for-machine-learning/.