Temel Bilesen Analizi (Principle Component Analysis (PCA))
Mustafa Germec, PhD
Merhaba sevgili veri bilimi meraklıları,
Bu yazımda Temel Bileşen Analizi ile ilgili kısa ve öz bilgiler paylaştım. Umarım faydalı bulursunuz. Şimdiden keyifli okumalar dilerim.
PCA yöntemi, denetimsiz (unsupervised) makine öğrenimi yöntemlerinden biridir. PCA’nın temel fikri, çok değişkenli verinin ana özelliklerini daha az sayıda değişken/bileşen ile temsil etmektir. Diğer bir ifade ile küçük miktarda bir bilgi kaybını göz önüne alıp değişken boyutunu azaltmaktır. Örneğin, elimizde 100 tane değişkenden oluşan bir veri seti olsun. Küçük bir bilgi kaybı göz önüne alınıp veri setindeki 100 değişken sayısını örneğin 3 değişkene indirme çabasıdır. 100 değişken yerine 3 değişken ile veri setindeki değişkenlik, varyans ve bilgi temsil edilir. Bunun bedeli de göz ardı edilebilecek ufak miktarda bir bilgiden fedakârlık yapmaktır.
Neden boyut indirgeme gibi bir kaygı var?
Bunun nedeni, örneğin doğrusal regresyon problemlerinde çoklu doğrusal bağlantı probleminden kurtulmak olabilir. Bir diğer nedeni, örneğin, bir yüz tanıma probleminde resimlere filtre yapma ihtiyacı olabilir. Bir diğer deyişle, PCA, gürültü azaltma aracı olarak kullanılabilir. Bu örneklerdeki benzer sebepler ile bir şekilde boyut indirgeme ihtiyacı olduğundan bu yöntem kullanılabilir.
PCA, bir boyut indirgeme yaklaşımıdır, yani veri setinin boyutunu küçük miktarda bir bilgi kaybını göze alarak indirgeme işlemidir.
PCA, nerede ve ne şekilde ihtiyaç görür?
Görüntü, regresyon, bazı varsayım problemlerinde, yani bir şekilde çok boyut söz konusu olduğunda kullanılabilir. Bir şekilde boyut indirgeme isteniyorsa, PCA kullanılabilir.
PCA yöntemi, birçok farklı konu ve alanda kullanılabilen temel bir tekniktir. Dolayısıyla sadece doğrudan bir şey yapmak için kullanılmaz. Ancak, örneğin, çok boyutlu bir veriyi örneğin 2 boyuta indirgemek suretiyle görselleştirmek için kullanılabilir. Veri setinde 50 değişkenin olduğunu varsayalım. Bu, veri setinin 50 boyutlu olduğunu gösterir. Bu veri setini PCA yöntemini kullanarak 2 boyuta indirgeyebiliriz. PCA, veri setinin bağımsız değişkenlerini, doğrusal kombinasyonları ile ifade edilen bileşenlere indirger. Dolayısıyla bu bileşenler arasında korelasyon yoktur. Teknik olarak, PCA bu bileşenleri PCA1 ve PCA2 olarak nasıl çıkarır? sorusunun yanıtı ise değişken gruplarının varyanslarını ifade eden öz değerleri ve veri setindeki değişkenleri gruplandırır. Gruplar arasında en fazla varyansa sahip olan gruplar, en önemli gruplardır ki bunlara asal bileşenler denir. Yani burada varyansa ve öz değerlere dayalı bir gruplama yapılmaktadır.
Uygulama
Kütüphaneleri import etme
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler, LabelEncoder
import warnings
warnings.filterwarnings("ignore")
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import cross_val_score, GridSearchCV
from sklearn.metrics import mean_squared_error, mean_absolute_error
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all'
pd.set_option('display.max_columns', None)
pd.set_option('display.width', 500)
Veri setini import etme
df = pd.read_csv('/kaggle/input/pca-datasets/hitters.csv')
df.columns = [col.lower() for col in df.columns]
df.head()
Bu çalışma, PCA ile ilgili olduğundan ve veri setindeki kategorik ve bağımlı değişken ile ilgilenmediğimizden dolayı bunlar veri setinden uzaklaştırılmıştır. Dolayısıyla, elde edilen değişkenler, çalışılmak istenilen veri setini oluşturmaktadır.
num_cols = [col for col in df.columns if df[col].dtype != "O" and 'salary' not in col]
df_pca = df[num_cols]
df_pca.head()
Veri setindeki eksik değerler şimdilik ilgi alanı olmadığından dolayı, veri setinden silinmiştir. Çünkü buradaki amaç, çok değişkenli bir verinin daha az değişken ile ifade edilmesidir. Örneğin, 16 değişkenli bir veri setinin 2 veya 3 bileşene indirgeme gibi.
df_pca.dropna(inplace=True)
print(f'Veri setindeki gozlemlerin ve kolonlarin sayisi sirasiyla {df_pca.shape[0]} ve {df_pca.shape[1]} dir.')
# Veri setindeki gozlemlerin ve kolonlarin sayisi sirasiyla 322 ve 16 dir.
Veriyi standartlaştırma
df_pca = StandardScaler().fit_transform(df_pca)
pca = PCA()
pca_fit = pca.fit_transform(df_pca)
pca_fit
Bu bileşenlerin başarısı, bileşenlerin açıkladığı varyans oranına göre belirlenir. Bileşenlerin açıkladığı varyans oranları aslında açıkladıkları bilgi oranıdır.
pca.explained_variance_ratio_
Kümülatif varyans oranlarini hesaplama
np.cumsum(pca.explained_variance_ratio_)
Bu hesaplama ile elde edilen değerler, PCA’nın oluşturduğu 16 adet yeni bileşenin hepsinin açıkladığı varyans oranıdır.
Optimum bilesen sayısını belirleme (Elbow metodu)
Bu yöntem ile en keskin geçisin nerede olduğu belirlenir.
pca = PCA().fit(df_pca)
sns.lineplot(np.cumsum(pca.explained_variance_ratio_))
plt.xlabel('Bilesen sayisi')
plt.ylabel('Kümülatif varyans orani')
plt.show()
Yukarıdaki grafik incelendiğinde 2 veya 3 tercih edilebilir. Veri görselleştirme için kullanıldığı durumda 2 boyut seçilmelidir. Regresyon problemi ile ilgilenildiğinde, çoklu doğrusal bağlantı problemini gidermek için değişken sayısı kadar bileşen oluşturmak tercih edilebilir. Böylece veri setinin içerisindeki bilginin tamamı korunmuş olur ancak bu durumda da birbirinden bağımsız olur. Yüksek korelasyon ve çoklu doğrusal bağlantı problemlerine sahip olmaz.
Final PCA modelini oluşturma
pca = PCA(n_components=3)
pca_fit = pca.fit_transform(df_pca)
pca.explained_variance_ratio_
np.cumsum(pca.explained_variance_ratio_)
Temel bilesen regresyon (PCR) modeli
Diyelim ki Hitters veri setini doğrusal bir modelleme ile modellemek istiyoruz ve değişkenler arasında çoklu doğrusal regresyon problemi var. Bu doğrusal regresyon problemlerinde sağlanması gereken varsayımlardandır. Değişkenler arasında yüksek korelasyon olması, çeşitli problemlere neden olduğundan istenmeyen bir durumdur. Diğer bir amaç ise, yukarıda PCA yöntemiyle çıkarılmış olan bileşenlerin neye karşılık geldiğini belirlemeye çalışmaktır.
Numerik kolonların dışında kalanları getirme
others = [col for col in df.columns if col not in num_cols]
others
# ['league', 'division', 'salary', 'newleague']
df[others].head()
pd.DataFrame(pca_fit, columns=['PC1', 'PC2', 'PC3']).head()
PCR yöntemiyle önce bir temel bilesen yöntemi uygulanıp değişkenlerin boyutu indirgenmekte ve sonrasında ise bu bileşenlerin üzerine bir regresyon modeli kurulmaktadır. Bunun doğrudan Python’da uygulandığı bir metot yoktur. Python’da olmayıp istatistikte olan bir konu olduğundan dolayı, bu işlemi veri bilimcinin kendisi yapmalıdır. Numerik bileşenlerin seçilip bileşenlerce ifade edilmesi ve üzerine regresyon modelinin kurulması durumunda temel regresyon modeli oluşturulur. Oluşturulan bu bileşenler, değişken olarak kullanılır. Dolayısıyla ‘pca_fit’ ve df[others] değişkenleri bir araya getirilir.
final_df = pd.concat([pd.DataFrame(pca_fit, columns=['PC1', 'PC2', 'PC3']), df[others]], axis=1)
final_df.head()
Böylece 16 değişkenin taşıdığı bilginin %82'si PC1, PC2 ve PC3 değişkenleri ile temsil edilmektedir. Bu gerçek bir ihtiyaçtır. Elde edilen final_df veri setini kullanarak tahminleme yapmak için lineer ve karar ağacı modelleri kullanılmıştır. Değişkenleri encode etmek için label encoding yöntemi kullanılmıştır.
Label encoding
def label_encoder(df, binary_cols):
label_encoder = LabelEncoder()
df[binary_cols] = label_encoder.fit_transform(df[binary_cols])
binary_cols = [col for col in final_df.columns if final_df[col].nunique() == 2]
for col in binary_cols:
label_encoder(final_df, col)
final_df.head()
Bağımlı değişkendeki eksik verilerin silinmesi
final_df.dropna(inplace=True)
final_df.shape
# (263, 7)
Bağımlı ve bağımsız değişkenlerin seçilmesi
y = final_df['salary']
X = final_df.drop('salary', axis=1)
Lineer regresyon modeli
lr = LinearRegression().fit(X, y)
y_pred = lr.predict(X)
print(f'Hata kareler ortalamasi = {mean_squared_error(y, y_pred)}')
print(f'Ortalama mutlak hata = {mean_absolute_error(y, y_pred)}')
print(f'Kök ortalama kare hatasi = {np.sqrt(mean_squared_error(y, y_pred))}')
# 5-katli capraz dogrulama ile rmse degerinin hesaplanmasi
print(f'5 katli capraz dogrulama ile rmse degeri = {np.mean(np.sqrt(-cross_val_score(lr, X, y, cv=5, scoring="neg_mean_squared_error")))}')
# Hata kareler ortalamasi = 112133.32935389846
# Ortalama mutlak hata = 228.87439847834383
# Kök ortalama kare hatasi = 334.86315018810063
# 5 katli capraz dogrulama ile rmse degeri = 345.6021106351967
sns.regplot(x=y, y=y_pred, data=final_df)
plt.show()Belirlenen kümelerin küme etiketlerini (labels) getirme
Ağaç modeli
cart = DecisionTreeRegressor().fit(X, y)
y_pred_cart = cart.predict(X)
print(f'Hata kareler ortalamasi = {mean_squared_error(y, y_pred)}')
print(f'Ortalama mutlak hata = {mean_absolute_error(y, y_pred)}')
print(f'Kök ortalama kare hatasi = {np.sqrt(mean_squared_error(y, y_pred))}')
# 5-katli capraz dogrulama ile rmse degerinin hesaplanmasi
print(f'5 katli capraz dogrulama ile rmse degeri = {np.mean(np.sqrt(-cross_val_score(cart, X, y, cv=5, scoring="neg_mean_squared_error")))}')
# Hata kareler ortalamasi = 112133.32935389846
# Ortalama mutlak hata = 228.87439847834383
# Kök ortalama kare hatasi = 334.86315018810063
# 5 katli capraz dogrulama ile rmse degeri = 394.0791736910673
sns.regplot(x=y, y=y_pred_cart, data=final_df)
plt.show()
Yukarıdaki grafiğe göre, modelin overfit olduğu anlaşılmaktadır.
Ağaç modeli için hiperparametre optimizasyonu
params = {'max_depth': range(1, 11),
'min_samples_split': range(2, 20)}
best_grid = GridSearchCV(cart, params, cv=5, n_jobs=-1, verbose=True).fit(X, y)
print(best_grid)
cart_final = cart.set_params(**best_grid.best_params_, random_state=1).fit(X, y)
print(f'5 katli capraz dogrulama ile rmse degeri = {np.mean(np.sqrt(-cross_val_score(cart_final, X, y, cv=5, scoring="neg_mean_squared_error")))}')
# Fitting 5 folds for each of 180 candidates, totalling 900 fits
# GridSearchCV(cv=5, estimator=DecisionTreeRegressor(), n_jobs=-1, param_grid={'max_depth': range(1, 11), 'min_samples_split': range(2, 20)}, verbose=True)
# 5 katli capraz dogrulama ile rmse degeri = 330.1964109339104
y_pred_cart_final = cart_final.predict(X)
sns.regplot(x=y, y=y_pred_cart_final, data=final_df)
plt.show()
Sonuç olarak, ağaç modelinin hiperparametre optimizasyonu sonrasında elde edilen hata değeri, lineer model ile elde edilen hata değerinden daha düşük hesaplanmıştır.
PCA görselleştirme
Burada amaç, PCA ile çok boyutlu bir veriyi iki boyutta görselleştirmektir.
Veri setini getirme ve bağımlı ve bağımsız değişkenleri seçme
dff = pd.read_csv('/kaggle/input/pca-datasets/breast_cancer.csv')
y = dff['diagnosis']
X = dff.drop(['id', 'diagnosis', 'Unnamed: 32'], axis=1)
Bu veri setindeki değişkenler, görsel veriler üzerinden numerik formlara getirilen değişkenlerdir.
Amaca yönelik veri setini indirgemek için tanımlanan ‘create_pca_df()’ fonksiyonu
def create_pca_df(X, y):
X = StandardScaler().fit_transform(X)
pca = PCA(n_components=2)
pca_fit = pca.fit_transform(X)
pca_df = pd.DataFrame(data = pca_fit, columns=['PC1', 'PC2'])
final_df = pd.concat([pca_df, pd.DataFrame(y)], axis=1)
return final_df
final_df = create_pca_df(X, y)
final_df.head()
Böylece veri setinin bağımsız değişkenleri PC1 ve PC2 olmak üzere iki bileşene indirgenir.
Veri setinin iki boyuta indirgendikten sonra görselleştirilmesi
def plot_pca(dataframe, target):
fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot(1, 1, 1)
ax.set_xlabel('PC1')
ax.set_ylabel('PC2')
ax.set_title(f'{target.capitalize()}')
targets = list(dataframe[target].unique())
colors = []
while len(colors) != len(targets):
colors.append(list(np.random.random_sample(3)))
for t, color in zip(targets, colors):
indices = dataframe[target] == t
ax.scatter(dataframe.loc[indices, 'PC1'], dataframe.loc[indices, 'PC2'], color=color, s=50)
ax.legend(targets)
ax.grid()
plt.show()
plot_pca(final_df, 'diagnosis')
Yukarıda tanımlanan fonksiyon (plot_pca()) genellenebilirliği yüksek bir fonksiyon olduğundan, bunu test etmek için başka bir veri seti olan ‘iris’ veri setine bu fonksiyonu uygulayalım. Bu veri seti, çiçeklerin taç ve çanak yaprak bilgileri üzerine kurulu bir veri setidir.
iris = sns.load_dataset('iris')
y = iris['species']
X = iris.drop('species', axis=1)
‘create_pca_df’ ve ‘plot_pca’ fonksiyonları bu anlamda hemen hemen tüm ihtiyaçlar için kullanılabilir. Sadece, bu fonksiyonlara gönderilecek bağımsız değişkenlerin (X) sayısal değişkenlerden oluşması gerekmektedir.
final_df = create_pca_df(X, y)
final_df.head()
plot_pca(final_df, 'species')
‘iris’ veri setine göre ‘breast_cancer’ veri setinin bileşenleri daha fazla birbirine geçmiş gibi gözükmektedir. Diğer senaryolara göre daha fazla iç içe geçme durumu var. ‘breast_cancer’ ve ‘iris’ veri setinin sınıflandırma problemine odaklanıldığında, bunların sınıflarının birbirinden daha kolay ayrıldığı görülmektedir.
Bir sonraki yazımda görüşmek üzere…
Sağlıcakla kalın…
Çalışmada kullanılan Hitters ve breast_cancer veri setlerine ilgili linkler tıklandıktan sonra ulaşılabilir.
WordPress | Kaggle | LinkedIn | GitHub | ResearchGate | Google Akademik