PYTHON İLE MAKİNE ÖĞRENMESİNDE ÖZELLİK MÜHENDİSLİĞİ (I)

ÖZELLİK YARATMA

ÖZELLİK YARATMA GEREKLİLİĞİ

Özellik mühendisliği, ham verilerden makine öğrenmesi gibi işler için uygun olan özellikleri çıkarma eylemidir.

Çoğu makine öğrenmesi algoritması tablo verileriyle çalışır. Özellikler hakkında konuşurken, bu tabloların sütunlarında depolanan bilgilere atıfta bulunuruz. Örneğin, evler hakkındaki bilgilere bakıyorsak, özellikler, metrekare, oda sayısı, banyo sayısı gibi şeyler olabilir.

Bu eğitim, özellik mühendisliğini veri bilimi iş akışına nasıl dahil edeceklerine ilişkin bilgilerini genişletmek isteyen veri bilimciler için tasarlanmıştır.

Çoğu makine öğrenmesi algoritması, girdi verilerinin bir vektör veya matris olarak temsil edilmesini gerektirir. Ayrıca yine çoğu, verilerin normal olarak dağıldığını varsayar. Gerçek dünyada, çoğu zaman bu formatta olmayan verilerle karşılaşırız. Ayrıca birçok farklı veri türüyle de çalışmamız gerebilir. Sıklıkla karşılaşacağımız bazı veri türleri şunlardır:

  • Sürekli: Tam sayılar ya da ondalıklı sayılar,
  • Kategorik: belirli sayıdaki elemandan oluşan kümelerdeki sayılar ya da değerler,
  • Sıralı: büyüklük ya da küçüklüğe göre sıralanmış olan veriler,
  • Mantıksal: Doğru (gerçek) ya da yanlış değerleri (True/False),
  • Tarih/saat: Tarih ve saat değerleri.

Her bir veri tipiyle başaçıkmak yönetilebilir bir süreçtir, ancak iyi düşünülmüş bir yaklaşım gerektirir.

Özellik mühendisliği, makine öğrenmesi tartışmalarında genellikle göz ardı edilir. Ancak, gerçek dünya verileriyle çalışan herhangi bir veri bilimci, veri manipülasyonu ve özellik mühendisliğinin projenin en önemli süreçlerinden olduğunu bilir.

Bu eğitim süresince, birçok farklı veri türüyle nasıl başa çıkacağımızı ve bunları makine öğrenmesi için kolayca kullanılabilecek bir biçime nasıl dönüştürebileceğimizi ele alacağız.

Eğitim boyunca sırasında, tablo şeklindeki verilerle çalışırken çok yararlı olması nedeniyle pandas paketinden önemli ölçüde yararlanacağız. Bir CSV dosyasını oturuma yüklemek için read_csv() fonksiyonunu ve veri setinin ilk birkaç satırına hızlı bir şekilde bakmak için head() yöntemini kullanabiliriz:

In [1]:
# `pandas` paketinin oturuma yüklenmesi:
import pandas as pd
# `stackoverflow_survey.csv` verisinin oturuma yüklenmesi:
survey = pd.read_csv("http://veribilim.online/data/stackoverflow_survey.csv")
# `survey` veri setinin ilk beş satırının yazdırılması:
survey.head()
Out[1]:
SurveyDate FormalEducation ConvertedSalary Hobby Country StackOverflowJobsRecommend VersionControl Age Years Experience Gender RawSalary
0 2/28/18 20:20 Bachelor's degree (BA. BS. B.Eng.. etc.) NaN Yes South Africa NaN Git 21 13 Male NaN
1 6/28/18 13:26 Bachelor's degree (BA. BS. B.Eng.. etc.) 70841.0 Yes Sweeden 7.0 Git;Subversion 38 9 Male 70,841.00
2 6/6/18 3:37 Bachelor's degree (BA. BS. B.Eng.. etc.) NaN No Sweeden 8.0 Git 45 11 NaN NaN
3 5/9/18 1:06 Some college/university study without earning ... 21426.0 Yes Sweeden NaN Zip file back-ups 46 12 Male 21,426.00
4 4/12/18 22:41 Bachelor's degree (BA. BS. B.Eng.. etc.) 41671.0 Yes UK 8.0 Git 39 7 Male £41,671.00
In [2]:
# `survey` veri setindeki sütunların veri tiplerinin yazdırılması:
print(survey.dtypes)
SurveyDate                     object
FormalEducation                object
ConvertedSalary               float64
Hobby                          object
Country                        object
StackOverflowJobsRecommend    float64
VersionControl                 object
Age                             int64
Years Experience                int64
Gender                         object
RawSalary                      object
dtype: object

Genellikle bir veri seti, survey veri setinde olduğu gibi, birkaç farklı veri türüne sahip sütunlar içerir. Makine öğrenmesi modellerinin çoğu, özellikler arasında tutarlı bir veri türüne sahip olmamızı gerektirir. Benzer şekilde, çoğu özellik mühendisliği tekniği bir seferde yalnızca bir veri türüne uygulanabilir. Dolayısıyla, bir veri seti ile çalışırken, genellikle yalnızca belirli türlerdeki sütunlara erişebilmek isteriz:

In [3]:
# `survey` veri setinin sadece sayısal sütunlarından oluşan bir alt veri seti oluşturulması:
survey_sayisal = survey.select_dtypes(include=['int', 'float'])
In [4]:
# `survey_sayisal` veri setindeki sütunların yazdırılması:
print(survey_sayisal.columns)
Index(['ConvertedSalary', 'StackOverflowJobsRecommend', 'Age',
       'Years Experience'],
      dtype='object')

KATEGORİK ÖZELLİKLER

Kategorik değişkenler, doğası gereği "nitel" olan grupları temsil etmek için kullanılır. Bazı örnekler yeşil, sarı, siyah gibi renkler veya Almanya, Fransa veya İsviçre gibi ülkeler olabilir. Bunlar bir insan tarafından kolayca anlaşılabilse de, kategorik özellikleri makine öğrenmesi modellerinde kullanmak için sayısal değerler olarak kodlamak gerekir. Bunun için en yaygın iki yaklaşım, değişkenleri tek seferde kodlamak (one-hot-encoding) veya kukla (dummy) değişkenler kullanmaktır.

Şimdi, her iki kodlama türünü de kullanacağız ve oluşturulan sütun kümelerini karşılaştıracağız:

In [5]:
# `survey` veri setindeki "Country" sütununun "one-hot-encoding" yöntemiyle kodlanması: 
tek_seferde_kodlama = pd.get_dummies(survey, columns=['Country'], prefix='OH')
In [6]:
# `tek_seferde_kodlama` veri setinin ilk 5 satırının yazdırılması:
tek_seferde_kodlama.head()
Out[6]:
SurveyDate FormalEducation ConvertedSalary Hobby StackOverflowJobsRecommend VersionControl Age Years Experience Gender RawSalary OH_France OH_India OH_Ireland OH_Russia OH_South Africa OH_Spain OH_Sweeden OH_UK OH_USA OH_Ukraine
0 2/28/18 20:20 Bachelor's degree (BA. BS. B.Eng.. etc.) NaN Yes NaN Git 21 13 Male NaN 0 0 0 0 1 0 0 0 0 0
1 6/28/18 13:26 Bachelor's degree (BA. BS. B.Eng.. etc.) 70841.0 Yes 7.0 Git;Subversion 38 9 Male 70,841.00 0 0 0 0 0 0 1 0 0 0
2 6/6/18 3:37 Bachelor's degree (BA. BS. B.Eng.. etc.) NaN No 8.0 Git 45 11 NaN NaN 0 0 0 0 0 0 1 0 0 0
3 5/9/18 1:06 Some college/university study without earning ... 21426.0 Yes NaN Zip file back-ups 46 12 Male 21,426.00 0 0 0 0 0 0 1 0 0 0
4 4/12/18 22:41 Bachelor's degree (BA. BS. B.Eng.. etc.) 41671.0 Yes 8.0 Git 39 7 Male £41,671.00 0 0 0 0 0 0 0 1 0 0
In [7]:
# `survey` veri setindeki "Country" sütunu için kukla değişkenler yaratılması:
kukla = pd.get_dummies(survey, columns=['Country'], drop_first=True, prefix='DM')
# `kukla` veri setinin ilk 5 satırının yazdırılması:
kukla.head()
Out[7]:
SurveyDate FormalEducation ConvertedSalary Hobby StackOverflowJobsRecommend VersionControl Age Years Experience Gender RawSalary DM_India DM_Ireland DM_Russia DM_South Africa DM_Spain DM_Sweeden DM_UK DM_USA DM_Ukraine
0 2/28/18 20:20 Bachelor's degree (BA. BS. B.Eng.. etc.) NaN Yes NaN Git 21 13 Male NaN 0 0 0 1 0 0 0 0 0
1 6/28/18 13:26 Bachelor's degree (BA. BS. B.Eng.. etc.) 70841.0 Yes 7.0 Git;Subversion 38 9 Male 70,841.00 0 0 0 0 0 1 0 0 0
2 6/6/18 3:37 Bachelor's degree (BA. BS. B.Eng.. etc.) NaN No 8.0 Git 45 11 NaN NaN 0 0 0 0 0 1 0 0 0
3 5/9/18 1:06 Some college/university study without earning ... 21426.0 Yes NaN Zip file back-ups 46 12 Male 21,426.00 0 0 0 0 0 1 0 0 0
4 4/12/18 22:41 Bachelor's degree (BA. BS. B.Eng.. etc.) 41671.0 Yes 8.0 Git 39 7 Male £41,671.00 0 0 0 0 0 0 1 0 0

Kukla değişkenler oluşturduğumuzda Fransa sütununun eksik olduğunu fark ediyoruz. Bu algoritmaların çalışabilmesi için gerekli bir durumdur. Oluşturulan kukla değişkenlerden biri düşürülür (silinir).

Artık uygun olduğunda one-hot-encoding veya kukla değişkenler kullanmayı seçebiliriz.

In [8]:
# `survey` sütunundaki `Country` sütunundaki değerlerin çekilmesi:
ulkeler = survey['Country']
In [9]:
# `ulkeler` sütunundaki değerlerin saydırılması ve sonucun yazdırılması:
ulkeler_deger_sayim = ulkeler.value_counts()
ulkeler_deger_sayim
Out[9]:
South Africa    166
USA             164
Spain           134
Sweeden         119
France          115
Russia           97
UK               95
India            95
Ukraine           9
Ireland           5
Name: Country, dtype: int64
In [10]:
# 10 seferden daha az frekansa sahip değerler için bir mantıksal maske oluşturulması:
maske = list(ulkeler.isin(ulkeler_deger_sayim[ulkeler_deger_sayim < 10].index))
In [11]:
# maske uygulanabilmesi için `ulkeler` alt seçiminin pandas veri setine dönüştürülmesi:
ulkeler = pd.DataFrame(ulkeler)
In [12]:
# `maske` de True değerlere denk gelen ülkelerin "Diğer" olarak değerlendirilmesi:
ulkeler[maske] = "Diğer"
In [13]:
# güncellenen kategori değerlerinin saydırılması:
pd.value_counts(ulkeler.Country)
Out[13]:
South Africa    166
USA             164
Spain           134
Sweeden         119
France          115
Russia           97
UK               95
India            95
Diğer            14
Name: Country, dtype: int64

Artık düşük frekans kategorilerini gruplandırarak, büyük veri setleriyle çalışabiliriz.

SAYISAL ÖZELLİKLER

Önceki kısımda bahsedildiği gibi, çoğu makine öğrenmesi modeli verilerimizin sayısal biçimde olmasını gerektirir. Bununla birlikte, verilerimizin tamamı sayısal olsa bile, özelliklerimizi geliştirmek için hala yapabileceğimiz çok şey bulunabilir.

Örneğin, bazı durumlarda, bir değerin büyüklüğünü değil, yalnızca yönünü veya var olup olmadığını önemseyebiliriz. Bu durumlarda, bir sütunu ikiye ayırmak isteriz. survey veri setinde "ConvertedSalary" sütununda, bu anketi cevaplayanların ücretli olup olmadıklarına bağlı olarak, ücret aldıkları durumda belirli bir değer görüyoruz. Yeni bir sütunda ücretli olup olmadıkları bilgisini ikili (binary) bir veri türüyle oluşturabiliriz:

In [14]:
# 0'larla doldurulmuş olan yeni bir "Paid" isimli sütun yaratılması:
survey["Paid"] = 0
In [15]:
# "Paid" sütunundaki 0 değerlerinin "ConvertesSalary" sütunu 0'dan büyük olduğunda "1" değeri ile değiştirilmesi:
survey.loc[survey["ConvertedSalary"] > 0, "Paid"] = 1
In [16]:
# "Paid" ve "ConvertedSalary" sütunlarının ilk 5 satırlarının yazdırılması:
survey[["Paid", "ConvertedSalary"]].head()
Out[16]:
Paid ConvertedSalary
0 0 NaN
1 1 70841.0
2 0 NaN
3 1 21426.0
4 1 41671.0

Pek çok sürekli sayısal sütunda, tam değerleri görmeyi daha az önemseriz. Bunun yerine, içine düştüğü grubu önemseyebiliriz. Bu gruplama şekli, değerlerin grafiğini çizerken veya makine öğrenmesi modellerimizi basitleştirirken yararlı olabilir. Bu yöntem çoğunlukla, değişkenin kesin seviyesinin çok fazla sorun olmadığı durumlarda kullanılır (yaş, boy, ücretler gibi):

In [17]:
# "ConvertedSalary" sütununun 5 eşit gruba (bin) ayrılması:
survey["ConvertedSalary_bin_esit"] = pd.cut(survey["ConvertedSalary"], bins=5)
In [18]:
# "ConvertedSalary_bin" sütununun ilk 5 satırının yazdırılması:
survey[["ConvertedSalary_bin_esit", "ConvertedSalary"]].head()
Out[18]:
ConvertedSalary_bin_esit ConvertedSalary
0 NaN NaN
1 (-2000.0, 400000.0] 70841.0
2 NaN NaN
3 (-2000.0, 400000.0] 21426.0
4 (-2000.0, 400000.0] 41671.0

Şimdi de oluşturduğumuz grupları eşit olarak değil de, kendi tanımlamamıza göre yapalım ve her grubu isimlendirelim:

In [19]:
# `numpy` paketinin oturuma yüklenmesi:
import numpy as np
In [20]:
# grup (bin) sınırlarının tanımlanması:
gruplar = [-np.inf, 20000, 60000, 100000, 200000, np.inf]
In [21]:
# grup etiketlerinin tanımlanması:
grup_etiketleri = ["Çok Düşük", "Düşük", "Orta", "Yüksek", "Çok Yüksek"]
In [22]:
# "ConvertedSalary" sütununun tanmladığımız gruplara göre bölümlenmesi:
survey["ConvertedSalary_bin_ozel"] = pd.cut(survey["ConvertedSalary"], 
                                         bins=gruplar, labels = grup_etiketleri)
In [23]:
# "ConvertedSalary_bin_ozel" sütununun ilk 5 satırının yazdırılması:
survey[["ConvertedSalary_bin_ozel", 'ConvertedSalary']].head()
Out[23]:
ConvertedSalary_bin_ozel ConvertedSalary
0 NaN NaN
1 Orta 70841.0
2 NaN NaN
3 Düşük 21426.0
4 Düşük 41671.0