Bir önceki yazıda veri kalitesini düşüren sorunlara ve bunlara nasıl çözümler getireceğimize değinmiştim. O yazıda hatalı, eksik, anlamsız verilerle uğraşmıştık. Bu yazıda ise elimizdeki verileri analiz için uygun hale getirecek yöntemlerinden kovalama metodundan bahsediyor olacağım. Bunu yaparken amaçlarımız çalışma performansı, öğrenme performansı gibi başarım metriklerini arttırmak ve bazı dönüşümler yaparak veriyi kullanmayı planladığımız sınıflandırıcının anlayacağı hale çevirmek olacaktır.

Kova Metodu

Kova metodu veya bölmeleme (binning, discretization , veya quantization) nitelik değerlerinin gruplanması ve değerin yerine grup değerinin yazılmasıdır. Örneğin, üzerinde sayılar yazan kartları değerlerine göre sıralayıp, eleman sayısına göre 3 eşit parçaya ayırdığımızı ve her parçadaki elemanları artık parça numarası ile hesaba kattığımızı düşünün. Elimizde aynı adet veri olmasına rağmen artık çeşitler azalmıştır, yani düzleşmiştir. Kovalara ayırmanın temel amacı verinin düzleştirilmesidir. Düzleştirme işlemi verideki gürültüyü azaltır, bir çok analizde işi hızlandırır ve -bence en güzeli- verinin görselleştirilmesini kolaylaştırır. Makine öğrenmesi açısından en önemli kullanımı ise sayısal değerlerin kategorik (kesikli) hale getirilmesidir.

ML.net 0.10 sürümü itabariyle biri gözetimli biri gözetimsiz olarak 2 tür kovalama desteklemektedir.

Gözetimsiz yöntem

İlk olarak gözetimsiz olan ile başlayalım. ML.net şu an için gözetimsiz kovalama için eşit derinlikli (equal-depth) yöntemi kullanmaktadır. Bu yöntem yukarıda örneklemeye çalıştığım yöntemin neredeyse aynısıdır. Yani elemanlar sıralanır ve siz kaç kova istiyorsanız her kovaya elemanlar dağıtılır. Üretilen sonuçta her bir kova içinde eşide yakın sayıda eleman bulunmaktadır.

Daha açıklayıcı olmak için, örnek kodu inceleyelim:

using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Transforms.Normalizers;
using System;

namespace MlNETTemizleme
{
    class Program
    {
        public sealed class Kisi
        {
            public string Cinsiyet { get; set; }
            public double YillikGelir { get; set; }
            public Kisi(string cinsiyet, double yillikGelir)
            {
                Cinsiyet = cinsiyet;
                YillikGelir = yillikGelir;
            }
        }

        static void Main(string[] args)
        {
            var veriler = new[] {
                new Kisi("K", 14863),new Kisi("E", 5353),
                new Kisi("E", 12341),new Kisi("K", 18811),
                new Kisi("E", 29334),new Kisi("K", 65826),
                new Kisi("E", 12064),new Kisi("E", 16845),
                new Kisi("K", 35566),new Kisi("K", 8748),
                new Kisi("E", 7942),new Kisi("K", 8748),
                new Kisi("E", 7942),new Kisi("E", 18982),
                new Kisi("E", 1392),new Kisi("K", 35023),
                new Kisi("K", 35566),new Kisi("K", 8748),
                new Kisi("E", 7942),new Kisi("E", 18982),
                new Kisi("K", 46138),new Kisi("K", 10266),
            };

            var mlContext = new MLContext();
            var egitimVerisi = mlContext.Data.ReadFromEnumerable(veriler);

            var pipeline = mlContext.Transforms.Conversion.MapValueToKey("Cinsiyet")
                .Append(mlContext.Transforms.Normalize(new NormalizingEstimator.BinningColumn("GelirKova", "YillikGelir", numBins: 3)))
                ;

            var onizleme = pipeline.Fit(egitimVerisi).Transform(egitimVerisi).Preview();

            Console.WriteLine($"{onizleme.ColumnView[0].Column.Name,-20}{onizleme.ColumnView[1].Column.Name,-20} {onizleme.ColumnView[2].Column.Name,-20} {onizleme.ColumnView[3].Column.Name,-20}");
            foreach (var veri in onizleme.RowView)
            {
                Console.WriteLine($"{veri.Values[0].Value,-20}{veri.Values[1].Value,-20} {veri.Values[2].Value,-20} {veri.Values[3].Value,-20}");
            }
        }
    }
}

Bu örneğin çıktısı aşağıdaki gibi olacaktır:

Cinsiyet            Cinsiyet             YillikGelir          GelirKova
K                   1                    14863                0,5
E                   2                    5353                 0
E                   2                    12341                0,5
K                   1                    18811                0,5
E                   2                    29334                1
K                   1                    65826                1
E                   2                    12064                0,5
E                   2                    16845                0,5
K                   1                    35566                1
K                   1                    8748                 0
E                   2                    7942                 0
K                   1                    8748                 0
E                   2                    7942                 0
E                   2                    18982                1
E                   2                    1392                 0
K                   1                    35023                1
K                   1                    35566                1
K                   1                    8748                 0
E                   2                    7942                 0
E                   2                    18982                1
K                   1                    46138                1
K                   1                    10266                0,5

Buna göre artık yıllık gelir niteliği üç farklı değere ayrılmıştır. Düşük gelir için 0, orta gelir için 0.5 ve yüksek gelir için 1 değeri kabul edilebilir.

18982 yüksek gelir, 18811 orta gelir demek çok doğru gözükmemektedir. Eğitim için kullanacağımız veri azaldıkça bu tür pürüzler artacaktır. Bu gibi durumlarda veri ML.net içinde yer almayan algoritmaları kullanmanız gerekebilir.

Gözetimli yöntem

Eğer ki yıllık gelir ile cinsiyet arasında bir ilişki olduğunu biliyorsanız bununla korelasyon uygulayarak kovalama yapılmasını isteyebilirsiniz. Bu durumda kodda ufak bir değişiklik yapıyoruz:

 var pipeline = mlContext.Transforms.Conversion.MapValueToKey("Cinsiyet")
                .Append(mlContext.Transforms.Normalize(new NormalizingEstimator.SupervisedBinningColumn("GelirKova", "YillikGelir","Cinsiyet", numBins: 3)));

Bu değişiklik ile alacağımız sonuç aşağıdaki gibi olacaktır:

Cinsiyet            Cinsiyet             YillikGelir          GelirKova
K                   1                    14863                0,5
E                   2                    5353                 0
E                   2                    12341                0,5
K                   1                    18811                0,5
E                   2                    29334                0,5
K                   1                    65826                1
E                   2                    12064                0,5
E                   2                    16845                0,5
K                   1                    35566                1
K                   1                    8748                 0,5
E                   2                    7942                 0
K                   1                    8748                 0,5
E                   2                    7942                 0
E                   2                    18982                0,5
E                   2                    1392                 0
K                   1                    35023                1
K                   1                    35566                1
K                   1                    8748                 0,5
E                   2                    7942                 0
E                   2                    18982                0,5
K                   1                    46138                1
K                   1                    10266                0,5

Veriler arttıkça ve buradaki gibi rasgele oluşturulan veriler olmadıkça bu yöntemden güzel sonuçlar alabilirsiniz.

Kova metodu aynı zamanda verinin yumuşatılmasınıda sağlamaktadır. Örneklerdeki veri için her satır için kova içindeki elemanların ortalama değerini Yıllık Gelir olarak kabul ederseniz bu veri düzleşmiş/ütülenmiş olacaktır. Bu tip verilerin okunması oldukça kolaydır.

Sonuç

Kullanacağımız sınıflandırcıya göre elimizdeki sayısal değerleri dönüştürürken bu yönteme veya normalleştirme yöntemlerine çok sık başvuracağız. ML.net'in ilerleyen sürümlerinde farklı kova metotları desteklenmeye başlanırsa bu yazı içinde değineceğim. Fakat yazının içinde de bahsettiğim gibi her zaman için veriyi ML.net'den önce kendiniz de işlemden geçirebilirsiniz.

Faydalı Kaynaklar

https://mertricks.com/2014/11/29/veri-madenciligi-3-veri-hazirlama-bolum-1/

https://www.saedsayad.com/binning.htm

https://docs.microsoft.com/en-us/dotnet/machine-learning/how-to-guides/normalizers-preprocess-data-ml-net