Ana içeriğe geç

Fashion-MNIST Veri Seti ile Sınıflandırıcı Olarak Otokodlayıcı Eğitimi

Bu eğitimde, Keras ile Python’da bir otokodlayıcıyı sınıflandırıcı olarak nasıl kullanacağınızı öğreneceksiniz. Örnek olarak Fashion-MNIST veri setini kullanacaksınız.
· 15 dk. oku

Yapay Zeka ile Keşfedin

Not: Bu eğitim, çoğunlukla evrişimli sinir ağı ve evrişimli otokodlayıcı kullanarak sınıflandırmanın pratik uygulamasını kapsar. CNN ve otokodlayıcıya henüz aşina değilseniz, CNN ve Otokodlayıcı eğitimlerine göz atmak isteyebilirsiniz.

Daha spesifik olarak, bugün şu başlıkları ele alacaksınız:

  • Öncelikle Fashion-MNIST Verisi hakkında kısa bir bilgi verilecektir. Python’un çeşitli kütüphanelerini kullanarak veriyi yükleyecek, keşfedip analiz edeceksiniz,
  • Ardından verinizi ön işlemeden geçireceksiniz: yeniden boyutlandırma, yeniden ölçekleme, görsellerin veri tiplerini doğrulama ve veriyi eğitim/doğrulama olarak ayırmayı öğreneceksiniz.
  • Tüm bunlar tamamlandıktan sonra evrişimli otokodlayıcı modeli kuracaksınız: veriyi nasıl modele dönüştüreceğinizi ve ağı nasıl oluşturacağınızı öğreneceksiniz. Sonrasında modeli derleyip eğitecek, doğruluk ve kayıp grafiklerini görselleştirip modeli kaydedeceksiniz.
  • Daha sonra fashion-mnist verinizi segmentlere ayıracaksınız: etiketlerinizi önce one-hot vektörlere dönüştürecek, eğitim ve doğrulama görsellerini ilgili etiketleriyle böleceksiniz. Ardından, otokodlayıcı mimarisinde kullanacağınız aynı kodlayıcı fonksiyonunu, tam bağlantılı katmanlar eşliğinde tanımlayacaksınız.
  • Eğitilmiş bir modelin ağırlıklarını yeni modelin birkaç katmanına nasıl yükleyeceğinizi, eğitilmiş ve yeni modelin ağırlık matrislerini nasıl doğrulayacağınızı, yeni modelin bazı katmanlarını nasıl eğitilemez yapacağınızı ve son olarak yeni sınıflandırma modelini nasıl derleyip eğitip ağırlıklarını kaydedeceğinizi öğreneceksiniz.
  • Modeli tüm katmanlar eğitilebilir olacak şekilde yeniden eğitecek, modeli değerlendirecek, doğruluk ve kayıp grafiklerini görselleştirecek, test verisi üzerinde tahminler yapacak, olasılıkları sınıf etiketlerine dönüştürecek ve modelinizin doğru ve yanlış sınıflandırdığı bazı test örneklerini çizeceksiniz.
  • Son olarak, modelinizin hangi sınıfları (yanlış) sınıflandırdığını daha derinden anlamanızı sağlayacak sınıflandırma raporunu görselleştireceksiniz.

Fashion-MNIST Veri Seti

Veri setini yüklemeye ve işlemeye başlamadan önce, ne tür bir veri seti olduğu, veri boyutları ve kaç farklı sınıf içerdiği hakkında kısa bir fikir sahibi olmak iyi olacaktır.

Fashion-MNIST veri seti, 10 kategoride toplam 70.000 moda ürününün 28x28 gri tonlamalı görüntülerinden oluşur; kategori başına 7.000 görüntü vardır. Eğitim setinde 60.000, test setinde 10.000 görüntü bulunur. Fashion-MNIST, daha iyi sonuçlar üretmek için orijinal MNIST’e bir alternatiftir; görüntü boyutları ile eğitim/test bölmeleri orijinal MNIST’e benzerdir. Veri seti bu URL üzerinden ücretsizdir ve bilgisayarınıza indirmeden tensorflow veya keras ile doğrudan yüklenebilir.

MNIST’e benzer şekilde Fashion-MNIST de 10 sınıftan oluşur; ancak el yazısı rakamlar yerine sandalet, gömlek, pantolon gibi 10 farklı moda aksesuarı sınıfı bulunur.

Elinizdeki görev, bir evrişimli otokodlayıcıyı eğitmek ve otokodlayıcının kodlayıcı kısmını tam bağlantılı katmanlarla birleştirerek test setinden gelen yeni bir örneği doğru biçimde tanımaktır.

İpucu: MNIST veri setiyle sınıflandırma görevleri için Çok Katmanlı Algılayıcı (MLP) nasıl uygulanır öğrenmek isterseniz, bu eğitime göz atın.

Aşağıdaki kodda, not defterinde os.environ kullanarak ortam değişkenlerini ayarlıyorsunuz. Keras’ı başlatmadan önce Keras arka ucu TensorFlow’un ilk GPU’yu kullanmasını sınırlamak için bunu yapmak iyidir. Eğittiğiniz makinede GPU 0 varsa 1 yerine 0 kullandığınızdan emin olun. Bunu terminalinizde basit bir komutla kontrol edebilirsiniz: örneğin, nvidia-smi

import os
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="0" #model will be trained on GPU 0

Veriyi Yükleme

Sonraki adımda numpy, matplotlib ve en önemlisi Keras gibi gerekli tüm modülleri içe aktaracaksınız; çünkü bugün Keras’ı çatı olarak kullanacaksınız!

import keras
from matplotlib import pyplot as plt
import numpy as np
import gzip
%matplotlib inline
from keras.models import Model
from keras.optimizers import RMSprop
from keras.layers import Input,Dense,Flatten,Dropout,merge,Reshape,Conv2D,MaxPooling2D,UpSampling2D,Conv2DTranspose
from keras.layers.normalization import BatchNormalization
from keras.models import Model,Sequential
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adadelta, RMSprop,SGD,Adam
from keras import regularizers
from keras import backend as K
from keras.utils import to_categorical
Using TensorFlow backend.
/usr/local/lib/python2.7/dist-packages/h5py/__init__.py:34: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
  from ._conv import register_converters as _register_converters

Burada, gzip dosyasını açan, dosyayı bytestream.read() ile okuyan bir fonksiyon tanımlıyorsunuz. Bu fonksiyona görüntü boyutunu ve toplam görüntü sayısını geçiyorsunuz. Ardından np.frombuffer() kullanarak buf değişkeninde saklanan dizeyi float32 tipinde bir NumPy dizisine dönüştürüyorsunuz.

NumPy dizisine dönüştürüldükten sonra, diziyi üç boyutlu bir diziye veya tensöre yeniden şekillendiriyorsunuz; birinci boyut görüntü sayısı, ikinci ve üçüncü boyut ise görüntünün boyutudur. Son olarak data NumPy dizisini döndürüyorsunuz.

def extract_data(filename, num_images):
    with gzip.open(filename) as bytestream:
        bytestream.read(16)
        buf = bytestream.read(28 * 28 * num_images)
        data = np.frombuffer(buf, dtype=np.uint8).astype(np.float32)
        data = data.reshape(num_images, 28,28)
        return data

Şimdi, eğitim ve test dosyalarını ve bunların görüntü sayılarını geçirerek extract_data() fonksiyonunu çağıracaksınız.

train_data = extract_data('train-images-idx3-ubyte.gz', 60000)
test_data = extract_data('t10k-images-idx3-ubyte.gz', 10000)

Benzer şekilde, gzip dosyasını açan, dosyayı bytestream.read() ile okuyan ve bu kez etiket boyutunu (1) ve toplam görüntü sayısını alan bir etiket çıkarma fonksiyonu tanımlıyorsunuz. Ardından np.frombuffer() ile buf değişkeninde saklanan dizeyi int64 tipinde bir NumPy dizisine dönüştürüyorsunuz.

Bu kez, labels değişkeni 60.000 x 1 boyutunda bir sütun vektörü döndüreceğinden, diziyi yeniden şekillendirmeniz gerekmez. Son olarak labels NumPy dizisini döndürürsünüz.

def extract_labels(filename, num_images):
    with gzip.open(filename) as bytestream:
        bytestream.read(8)
        buf = bytestream.read(1 * num_images)
        labels = np.frombuffer(buf, dtype=np.uint8).astype(np.int64)
        return labels

Şimdi, eğitim ve test etiket dosyalarını ve bunların görüntü sayılarını geçirerek etiket çıkarma fonksiyonunu çağıracaksınız.

train_labels = extract_labels('train-labels-idx1-ubyte.gz',60000)
test_labels = extract_labels('t10k-labels-idx1-ubyte.gz',10000)

Eğitim ve test verilerini yükledikten sonra, bugün üzerinde çalışacağınız veri seti hakkında fikir edinmek için veriyi analiz etmeye hazırsınız!

Veri Keşfi

Şimdi veri setindeki görüntülerin nasıl göründüğünü analiz edelim ve ayrıca NumPy dizisinin .shape özelliği yardımıyla görüntülerin boyutlarına bakalım:

# Shapes of training set
print("Training set (images) shape: {shape}".format(shape=train_data.shape))

# Shapes of test set
print("Test set (images) shape: {shape}".format(shape=test_data.shape))
Training set (images) shape: (60000, 28, 28)
Test set (images) shape: (10000, 28, 28)

Yukarıdaki çıktıda, eğitim verisinin şeklinin 60000 x 28 x 28 olduğunu görüyorsunuz; çünkü 28 x 28 boyutunda 60.000 eğitim örneği var. Benzer şekilde, test verisinin şekli 10000 x 28 x 28’dir; çünkü 10.000 test örneği vardır.

Not: Evrişimli otokodlayıcı ile yeniden yapılandırma görevinde eğitim ve test etiketlerine ihtiyaç duymazsınız. Eğitim görüntüleriniz sınıflandırma görevindeki etiketlere benzer şekilde hem girdi hem de yer gerçeği (ground truth) olarak görev yapacaktır.

Ancak sınıflandırma görevi için, eğitimde daha sonra yapacağınız gibi görüntülerle birlikte etiketlere de ihtiyacınız olacak. Bu eğitimde elinizdeki görev temel olarak eğitim ve test görüntüleriyle ilgilense de, veriyi daha iyi kavramanıza yardımcı olabilecek keşif amaçlarıyla etiketleri kullanacağız.

Hadi, kategorik sınıf etiketlerine karşılık gelen sınıf adlarını içeren bir sözlük oluşturalım:

# Create dictionary of target classes
label_dict = {
 0: 'A',
 1: 'B',
 2: 'C',
 3: 'D',
 4: 'E',
 5: 'F',
 6: 'G',
 7: 'H',
 8: 'I',
 9: 'J',
}

Şimdi veri setinizdeki birkaç görüntüye bakalım:

plt.figure(figsize=[5,5])

# Display the first image in training data
plt.subplot(121)
curr_img = np.reshape(train_data[10], (28,28))
curr_lbl = train_labels[10]
plt.imshow(curr_img, cmap='gray')
plt.title("(Label: " + str(label_dict[curr_lbl]) + ")")

# Display the first image in testing data
plt.subplot(122)
curr_img = np.reshape(test_data[10], (28,28))
curr_lbl = test_labels[10]
plt.imshow(curr_img, cmap='gray')
plt.title("(Label: " + str(label_dict[curr_lbl]) + ")")
Text(0.5,1,'(Label: E)')
örnek görüntüler

Yukarıdaki iki grafiğin çıktısı, eğitim ve test verilerinden birer örnek görüntüdür ve bu görüntüler bir yanda 0 veya A, diğer yanda 4 veya E sınıf etiketleriyle ilişkilendirilmiştir. Benzer şekilde, diğer harfler farklı etiketlere sahip olacaktır; ancak aynı harfler aynı etiketi alır. Bu, tüm 6.000 adet E sınıfı görüntüsünün 4 sınıf etiketine sahip olduğu anlamına gelir.

Veri Ön İşleme

Veri setindeki görüntüler gerçekten de 0 ile 255 arasında piksel değerlerine sahip 28 x 28 boyutlarında gri tonlamalıdır; bu nedenle veriyi modele beslemeden önce ön işleme çok önemlidir. Önce eğitim ve test setindeki her 28 x 28 görüntüyü ağa besleyebileceğiniz 28 x 28 x 1 boyutunda bir matrise dönüştüreceksiniz:

train_data = train_data.reshape(-1, 28,28, 1)
test_data = test_data.reshape(-1, 28,28, 1)
train_data.shape, test_data.shape
((60000, 28, 28, 1), (10000, 28, 28, 1))

Sonraki adımda, eğitim ve test NumPy dizilerinin veri tipini kontrol etmeyi unutmayın; float32 olmalıdır. Değilse bu formata çevirmeniz gerekir, ancak veriyi okurken zaten dönüştürdüğünüz için bunu tekrar yapmanıza gerek yok. Ayrıca piksel değerlerini 0 - 1 aralığına ölçeklemeniz gerekir. Hadi bunu yapalım!

Eğitim ve test veri tiplerini doğrulamayı unutmayın:

train_data.dtype, test_data.dtype
(dtype('float32'), dtype('float32'))

Sonrasında, eğitim ve test verisini maksimum piksel değerine göre yeniden ölçekleyin:

np.max(train_data), np.max(test_data)
(255.0, 255.0)
train_data = train_data / np.max(train_data)
test_data = test_data / np.max(test_data)

Yeniden ölçeklemeden sonra eğitim ve test verisinin maksimum değerinin 1.0 olduğunu doğrulayalım!

np.max(train_data), np.max(test_data)
(1.0, 1.0)

Tüm bunlardan sonra veriyi bölmek önemlidir. Modelimizin iyi genelleme yapabilmesi için, eğitim verisini ikiye böleceksiniz: eğitim ve doğrulama seti. Modelinizi verinin %80’iyle eğitip kalan %20’siyle doğrulayacaksınız.

Bu, modeli eğitim aşamasında görmediği veri üzerinde doğrulayacağınız için aşırı uyum ihtimalini azaltmaya da yardımcı olur.

Veriyi uygun şekilde bölmek için scikit-learn’ün train_test_split modülünü kullanabilirsiniz:

from sklearn.model_selection import train_test_split
train_X,valid_X,train_ground,valid_ground = train_test_split(train_data,
                                                             train_data,
                                                             test_size=0.2,
                                                             random_state=13)

Not: Yukarıdaki veri bölmeyi iki kez kullanacaksınız; birincisi evrişimli otokodlayıcı ile yeniden yapılandırma görevi için ve bu görevde eğitim ve test etiketlerine ihtiyacınız yok. Bu yüzden eğitim görüntülerini iki kez geçiriyorsunuz. Eğitim görüntüleriniz, sınıflandırma görevindeki etiketlere benzer biçimde hem girdi hem de yer gerçeği olarak hizmet edecek.

Ancak sınıflandırma görevi için, eğitimin ilerleyen kısmında yapacağınız gibi görüntülerle birlikte etiketlerinizi de geçeceksiniz.

Şimdi ağı tanımlayıp veriyi ağa beslemeye hazırsınız. Daha fazla uzatmadan bir sonraki adıma geçelim!

Evrişimli Otokodlayıcı

Görüntüler 28 x 28 x 1 veya 30976 boyutlu bir vektördür. Görüntü matrisini bir diziye çevirir, 0 ile 1 arasına yeniden ölçekler, 28 x 28 x 1 boyutuna yeniden şekillendirir ve bunu ağa girdi olarak verirsiniz.

Ayrıca 128’lik bir yığın (batch) boyutu kullanacaksınız; 256 veya 512 gibi daha büyük yığın boyutları da tercih edilebilir; bu, modeli eğittiğiniz sisteme bağlıdır. Öğrenme parametrelerinin belirlenmesine ve tahmin doğruluğuna önemli ölçüde etki eder.

batch_size = 64
epochs = 200
inChannel = 1
x, y = 28, 28
input_img = Input(shape = (x, y, inChannel))
num_classes = 10

Muhtemelen bildiğiniz gibi, otokodlayıcı iki kısımdan oluşur: kodlayıcı (encoder) ve kod çözücü (decoder).

Kodlayıcı: 4 Evrişim bloğundan oluşur; her blokta bir evrişim katmanı ve ardından bir yığın normalizasyon katmanı bulunur. Birinci ve ikinci evrişim bloklarından sonra maksimum havuzlama (max-pooling) katmanı kullanılır.

  • İlk evrişim bloğu 3 x 3 boyutunda 32 filtreye sahip olacak ve ardından bir aşağı örnekleme (max-pooling) katmanı gelecek,
  • İkinci blok 3 x 3 boyutunda 64 filtreye sahip olacak ve ardından bir başka aşağı örnekleme katmanı gelecek,
  • Kodlayıcının üçüncü bloğu 3 x 3 boyutunda 128 filtreye sahip olacak,
  • Kodlayıcının dördüncü bloğu 3 x 3 boyutunda 256 filtreye sahip olacak.

Kod Çözücü: 3 Evrişim bloğundan oluşur; her blokta bir evrişim katmanı ve ardından bir yığın normalizasyon katmanı bulunur. İkinci ve üçüncü evrişim bloklarından sonra yukarı örnekleme katmanı kullanılır.

  • İlk blok 3 x 3 boyutunda 128 filtreye sahip olacak,
  • İkinci blok 3 x 3 boyutunda 64 filtreye ve ardından bir yukarı örnekleme katmanına sahip olacak,
  • Üçüncü blok 3 x 3 boyutunda 32 filtreye ve ardından bir başka yukarı örnekleme katmanına sahip olacak,
  • Son kod çözücü katmanı 3 x 3 boyutunda 1 filtreye sahip olacak ve tek kanallı girişi geri yapılandıracaktır.

Maksimum havuzlama katmanı her kullanımda girişi iki kat aşağı örneklerken, yukarı örnekleme katmanı her kullanımda girişi iki kat yukarı örnekler.

Not: Filtre sayısı, filtre boyutu, katman sayısı, modeli eğittiğiniz epoch sayısı gibi değerlerin tümü hiperparametrelerdir ve kendi sezginize göre belirlenmelidir; bu hiperparametreleri değiştirerek yeni deneyler yapıp modelinizin performansını ölçmekte özgürsünüz. Derin öğrenme “sanatını” böylece yavaş yavaş öğreneceksiniz!

Daha sonra sınıflandırma amacıyla kodlayıcı ağırlıklarını kullanacağınız için, kodlayıcı ve kod çözücü fonksiyonlarını ayrı oluşturalım!

def encoder(input_img):
    #encoder
    #input = 28 x 28 x 1 (wide and thin)
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(input_img) #28 x 28 x 32
    conv1 = BatchNormalization()(conv1)
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1)
    conv1 = BatchNormalization()(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) #14 x 14 x 32
    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1) #14 x 14 x 64
    conv2 = BatchNormalization()(conv2)
    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2)
    conv2 = BatchNormalization()(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) #7 x 7 x 64
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2) #7 x 7 x 128 (small and thick)
    conv3 = BatchNormalization()(conv3)
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3)
    conv3 = BatchNormalization()(conv3)
    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv3) #7 x 7 x 256 (small and thick)
    conv4 = BatchNormalization()(conv4)
    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv4)
    conv4 = BatchNormalization()(conv4)
    return conv4

def decoder(conv4):    
    #decoder
    conv5 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv4) #7 x 7 x 128
    conv5 = BatchNormalization()(conv5)
    conv5 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv5)
    conv5 = BatchNormalization()(conv5)
    conv6 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv5) #7 x 7 x 64
    conv6 = BatchNormalization()(conv6)
    conv6 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv6)
    conv6 = BatchNormalization()(conv6)
    up1 = UpSampling2D((2,2))(conv6) #14 x 14 x 64
    conv7 = Conv2D(32, (3, 3), activation='relu', padding='same')(up1) # 14 x 14 x 32
    conv7 = BatchNormalization()(conv7)
    conv7 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv7)
    conv7 = BatchNormalization()(conv7)
    up2 = UpSampling2D((2,2))(conv7) # 28 x 28 x 32
    decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(up2) # 28 x 28 x 1
    return decoded

Model oluşturulduktan sonra, RMSProp eniyileyicisi ile derlemeniz gerekir.

Ayrıca loss argümanı ile kayıp türünü de belirtmelisiniz. Bu durumda, her yığın sonrasında tahmin edilen çıktı ile yer gerçeği arasındaki kayıp, piksel bazında ortalama kare hata ile hesaplandığından, kayıp türü ortalama kare hatadır:

autoencoder = Model(input_img, decoder(encoder(input_img)))
autoencoder.compile(loss='mean_squared_error', optimizer = RMSprop())

Yukarıda oluşturduğunuz katmanları summary fonksiyonuyla görselleştirelim. Bu, her katmandaki parametre (ağırlık ve bias) sayısını ve modelinizdeki toplam parametreleri gösterecektir.

autoencoder.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         (None, 28, 28, 1)         0         
_________________________________________________________________
conv2d_16 (Conv2D)           (None, 28, 28, 32)        320       
_________________________________________________________________
batch_normalization_15 (Batc (None, 28, 28, 32)        128       
_________________________________________________________________
...

batch_normalization_28 (Batc (None, 14, 14, 32)        128       
_________________________________________________________________
up_sampling2d_4 (UpSampling2 (None, 28, 28, 32)        0         
_________________________________________________________________
conv2d_30 (Conv2D)           (None, 28, 28, 1)         289       
=================================================================
Total params: 1,758,657
Trainable params: 1,755,841
Non-trainable params: 2,816
_________________________________________________________________

Artık modeli Keras’ın fit() fonksiyonuyla eğitme zamanı! Model 200 epoch boyunca eğitilir. fit() fonksiyonu bir geçmiş (history) nesnesi döndürür; bu sonucu autoencoder_train içinde saklayarak, daha sonra eğitim ve doğrulama arasındaki kayıp grafiğini çizmek için kullanabilir, böylece model performansınızı görsel olarak analiz edebilirsiniz.

Modeli Eğitme

autoencoder_train = autoencoder.fit(train_X, train_ground, batch_size=batch_size,epochs=epochs,verbose=1,validation_data=(valid_X, valid_ground))
Train on 48000 samples, validate on 12000 samples
Epoch 1/200
48000/48000 [==============================] - 19s - loss: 0.0202 - val_loss: 0.0114s: 0.020
Epoch 2/200
48000/48000 [==============================] - 17s - loss: 0.0087 - val_loss: 0.0071  
...  
Epoch 199/200
48000/48000 [==============================] - 18s - loss: 7.0886e-04 - val_loss: 8.9876e-04
Epoch 200/200
48000/48000 [==============================] - 18s - loss: 7.0929e-04 - val_loss: 0.0010

Nihayet! Modeli fashion-mnist veri setinde 100 epoch boyunca eğittiniz. Şimdi, model performansını görselleştirmek için eğitim ve doğrulama arasındaki kayıp grafiğini çizelim.

loss = autoencoder_train.history['loss']
val_loss = autoencoder_train.history['val_loss']
epochs = range(200)
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
eğitim doğrulama kayıp grafiği

Sonuçta, doğrulama kaybı ile eğitim kaybının uyumlu olduğunu görebilirsiniz. Modelinizin aşırı uyum göstermediğini işaret eder: doğrulama kaybı artmak yerine azalıyor ve eğitim süresince eğitim ve doğrulama kaybı arasında neredeyse bir boşluk yok.

Dolayısıyla modelinizin genelleme yeteneğinin iyi olduğunu söyleyebilirsiniz.

Ancak eldeki görev, yukarıda eğitilen modelin kodlayıcı kısmını kullanarak fashion mnist görüntülerini sınıflandırmaktır. Şimdi bir sonraki kısma geçelim!

Modeli Kaydetme

Sınıflandırma görevinizde kodlayıcı ağırlıklarına ihtiyaç duyacağınızdan, önce tam otokodlayıcı ağırlıklarını kaydedelim. Kodlayıcı ağırlıklarını nasıl çıkarabileceğinizi birazdan öğreneceksiniz.

autoencoder.save_weights('autoencoder.h5')

Fashion Mnist Görüntülerini Segmentlere Ayırma

Şimdi eğitilmiş otokodlayıcının başını, yani kodlayıcı kısmını kullanacak ve az önce eğittiğiniz otokodlayıcının ağırlıklarını yalnızca modelin kodlayıcı kısmına yükleyeceksiniz.

Fashion mnist görüntülerini sınıflandırmak için kodlayıcıya birkaç yoğun (tam bağlantılı) katman ekleyeceksiniz.

  • Önce etiketleri one-hot vektörlere dönüştürelim.

    One-hot kodlama hakkında bilgisi olmayanlar için:

One-hot kodlamada, kategorik veriyi bir sayı vektörüne dönüştürürsünüz. Bunun nedeni, makine öğrenmesi algoritmalarının doğrudan kategorik verilerle çalışamamasıdır. Her kategori veya sınıf için bir mantıksal sütun üretirsiniz. Her örnek için bu sütunlardan yalnızca biri 1 değerini alabilir. Bu yüzden one-hot kodlama denir.

Sorununuz için one-hot vektörü bir satır vektörü olacaktır ve her görüntü için boyutu 1 x 10’dur. Burada önemli olan, vektörün temsil ettiği sınıf dışında tüm elemanlarının sıfır olması ve o sınıf için 1 olmasıdır.

Hadi etiketleri one-hot vektörlere dönüştürelim:

# Change the labels from categorical to one-hot encoding
train_Y_one_hot = to_categorical(train_labels)
test_Y_one_hot = to_categorical(test_labels)

# Display the change for category label using one-hot encoding
print('Original label:', train_labels[0])
print('After conversion to one-hot:', train_Y_one_hot[0])
('Original label:', 9)
('After conversion to one-hot:', array([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]))

C quite açık, değil mi?

  • Son adım, otokodlayıcı eğitim aşamasında da yaptığınız gibi veriyi eğitim ve doğrulama olarak bölmektir. Hızla bu adımı sınıflandırma için de yapalım. Daha önce kullandığınız aynı random_state’i kullanacağınıza ve bu kez biraz önce one-hot vektörüne dönüştürdüğünüz etiketleri de geçeceğinize dikkat edin.
train_X,valid_X,train_label,valid_label = train_test_split(train_data,train_Y_one_hot,test_size=0.2,random_state=13)

Son bir kez eğitim ve doğrulama setlerinin şekillerine bakalım.

train_X.shape,valid_X.shape,train_label.shape,valid_label.shape
((48000, 28, 28, 1), (12000, 28, 28, 1), (48000, 10), (12000, 10))

Şimdi sınıflandırma modelini tanımlayalım. Otokodlayıcı mimarisinde kullandığınız kodlayıcı kısmını aynen kullanacağınızı unutmayın.

def encoder(input_img):
    #encoder
    #input = 28 x 28 x 1 (wide and thin)
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(input_img) #28 x 28 x 32
    conv1 = BatchNormalization()(conv1)
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1)
    conv1 = BatchNormalization()(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) #14 x 14 x 32
    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1) #14 x 14 x 64
    conv2 = BatchNormalization()(conv2)
    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2)
    conv2 = BatchNormalization()(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) #7 x 7 x 64
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2) #7 x 7 x 128 (small and thick)
    conv3 = BatchNormalization()(conv3)
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3)
    conv3 = BatchNormalization()(conv3)
    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv3) #7 x 7 x 256 (small and thick)
    conv4 = BatchNormalization()(conv4)
    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv4)
    conv4 = BatchNormalization()(conv4)
    return conv4

Kodlayıcı fonksiyonunun üzerine yığacağınız tam bağlantılı katmanları tanımlayalım.

def fc(enco):
    flat = Flatten()(enco)
    den = Dense(128, activation='relu')(flat)
    out = Dense(num_classes, activation='softmax')(den)
    return out
encode = encoder(input_img)
full_model = Model(input_img,fc(encode))
for l1,l2 in zip(full_model.layers[:19],autoencoder.layers[0:19]):
    l1.set_weights(l2.get_weights())

Not: Sıradaki adım oldukça önemlidir. Otokodlayıcının kodlayıcı kısmındaki ağırlıkların, sınıflandırma modelinin kodlayıcı fonksiyonuna yüklediklerinizle aynı olup olmadığından emin olmak için, her iki modeldeki aynı katmanlardan birinin ağırlıklarını daima yazdırmalısınız. Benzer değillerse, otokodlayıcı sınıflandırma stratejisini kullanmanın bir anlamı kalmaz.

Hadi her iki modelin ilk katman ağırlıklarını yazdıralım.

autoencoder.get_weights()[0][1]
array([[[ 0.22935028, -0.800786  ,  0.42421195, -0.6509941 ,
         -0.82958347, -0.44448015,  0.04182598, -0.05483926,
          0.44611776,  0.7123421 , -0.4499234 ,  0.16125064,
          0.1174996 ,  0.12156075,  0.8391102 , -0.44067   ,
          0.02915774, -0.7223025 ,  0.33398604, -0.69252896,
          0.04369332, -0.3793029 ,  0.37535954,  0.34269437,
          0.8863593 , -0.2114254 ,  0.21323568, -0.4076597 ,
          0.2965019 ,  0.11617199, -0.22282824, -0.9501956 ]],

       [[ 0.23096658,  0.3701021 ,  0.78717273, -0.5014979 ,
         -1.3326751 , -0.73818666,  2.6434395 , -0.7560537 ,
         -0.52561104, -0.67917436,  2.0205429 ,  0.14013338,
         -0.9140436 ,  0.169709  ,  0.09063474, -0.20975377,
         -0.11247484, -0.09702996,  0.17846109,  0.40699893,
         -0.5722246 , -1.0119121 ,  0.30877167,  0.6645408 ,
         -0.68007207, -0.57144946, -0.68339616,  0.45407826,
          1.0148963 ,  0.88867754, -0.57179326,  0.01268557]],

       [[ 0.23020297,  0.14018346, -0.37600747, -0.6213855 ,
         -0.4104492 , -0.2036299 ,  0.12469969,  0.08351921,
          0.20644444, -0.01170571, -0.07618313,  0.23164392,
         -0.38417578,  0.3481844 , -0.8055927 ,  0.76824665,
          0.06819476,  0.93830526,  0.31898668,  0.51119566,
          0.4445658 , -0.4568496 ,  0.1269397 , -0.34482956,
         -1.3285302 , -0.20479   , -0.17618039, -0.22546193,
         -0.35588196,  0.9971566 , -0.03546353, -0.7294457 ]]],
      dtype=float32)
full_model.get_weights()[0][1]
array([[[ 0.22935028, -0.800786  ,  0.42421195, -0.6509941 ,
         -0.82958347, -0.44448015,  0.04182598, -0.05483926,
          0.44611776,  0.7123421 , -0.4499234 ,  0.16125064,
          0.1174996 ,  0.12156075,  0.8391102 , -0.44067   ,
          0.02915774, -0.7223025 ,  0.33398604, -0.69252896,
          0.04369332, -0.3793029 ,  0.37535954,  0.34269437,
          0.8863593 , -0.2114254 ,  0.21323568, -0.4076597 ,
          0.2965019 ,  0.11617199, -0.22282824, -0.9501956 ]],

       [[ 0.23096658,  0.3701021 ,  0.78717273, -0.5014979 ,
         -1.3326751 , -0.73818666,  2.6434395 , -0.7560537 ,
         -0.52561104, -0.67917436,  2.0205429 ,  0.14013338,
         -0.9140436 ,  0.169709  ,  0.09063474, -0.20975377,
         -0.11247484, -0.09702996,  0.17846109,  0.40699893,
         -0.5722246 , -1.0119121 ,  0.30877167,  0.6645408 ,
         -0.68007207, -0.57144946, -0.68339616,  0.45407826,
          1.0148963 ,  0.88867754, -0.57179326,  0.01268557]],

       [[ 0.23020297,  0.14018346, -0.37600747, -0.6213855 ,
         -0.4104492 , -0.2036299 ,  0.12469969,  0.08351921,
          0.20644444, -0.01170571, -0.07618313,  0.23164392,
         -0.38417578,  0.3481844 , -0.8055927 ,  0.76824665,
          0.06819476,  0.93830526,  0.31898668,  0.51119566,
          0.4445658 , -0.4568496 ,  0.1269397 , -0.34482956,
         -1.3285302 , -0.20479   , -0.17618039, -0.22546193,
         -0.35588196,  0.9971566 , -0.03546353, -0.7294457 ]]],
      dtype=float32)

Voila! Her iki dizi de tamamen aynı görünüyor. O halde daha fazla gecikmeden modeli derleyip eğitime başlayalım.

Sonraki adımda modelin kodlayıcı kısmını, yani ilk on dokuz katmanı eğitilemez yapacaksınız. Kodlayıcı kısmı zaten eğitildiği için onu yeniden eğitmenize gerek yok. Yalnızca Tam Bağlantılı kısmı eğiteceksiniz.

for layer in full_model.layers[0:19]:
    layer.trainable = False

Modeli derleyelim!

full_model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adam(),metrics=['accuracy'])

Modelin özetini de yazdıralım. Modelin ilk on beş katmanını eğitilemez yaptığınız için eğitilemez parametreler de olmalıdır.

full_model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         (None, 28, 28, 1)         0         
_________________________________________________________________
conv2d_55 (Conv2D)           (None, 28, 28, 32)        320       
_________________________________________________________________
batch_normalization_53 (Batc (None, 28, 28, 32)        128       
_________________________________________________________________
conv2d_56 (Conv2D)           (None, 28, 28, 32)        9248      
_________________________________________________________________
batch_normalization_54 (Batc (None, 28, 28, 32)        128       
_________________________________________________________________
max_pooling2d_11 (MaxPooling (None, 14, 14, 32)        0         
_________________________________________________________________
conv2d_57 (Conv2D)           (None, 14, 14, 64)        18496     
_________________________________________________________________
batch_normalization_55 (Batc (None, 14, 14, 64)        256       
_________________________________________________________________
conv2d_58 (Conv2D)           (None, 14, 14, 64)        36928     
_________________________________________________________________
batch_normalization_56 (Batc (None, 14, 14, 64)        256       
_________________________________________________________________
max_pooling2d_12 (MaxPooling (None, 7, 7, 64)          0         
_________________________________________________________________
conv2d_59 (Conv2D)           (None, 7, 7, 128)         73856     
_________________________________________________________________
batch_normalization_57 (Batc (None, 7, 7, 128)         512       
_________________________________________________________________
conv2d_60 (Conv2D)           (None, 7, 7, 128)         147584    
_________________________________________________________________
batch_normalization_58 (Batc (None, 7, 7, 128)         512       
_________________________________________________________________
conv2d_61 (Conv2D)           (None, 7, 7, 256)         295168    
_________________________________________________________________
batch_normalization_59 (Batc (None, 7, 7, 256)         1024      
_________________________________________________________________
conv2d_62 (Conv2D)           (None, 7, 7, 256)         590080    
_________________________________________________________________
batch_normalization_60 (Batc (None, 7, 7, 256)         1024      
_________________________________________________________________
flatten_4 (Flatten)          (None, 12544)             0         
_________________________________________________________________
dense_7 (Dense)              (None, 128)               1605760   
_________________________________________________________________
dense_8 (Dense)              (None, 10)                1290      
=================================================================
Total params: 2,782,570
Trainable params: 1,607,050
Non-trainable params: 1,175,520
_________________________________________________________________

Modeli Eğitme

Artık modeli Keras’ın fit() fonksiyonuyla eğitme zamanı! Model 10 epoch boyunca eğitilir. fit() fonksiyonu bir geçmiş (history) nesnesi döndürür; bu sonucu fashion_train içinde saklayarak, daha sonra eğitim ve doğrulama arasındaki doğruluk ve kayıp grafiklerini çizmek için kullanabilir, böylece model performansınızı görsel olarak analiz edebilirsiniz.

classify_train = full_model.fit(train_X, train_label, batch_size=64,epochs=100,verbose=1,validation_data=(valid_X, valid_label))
Train on 48000 samples, validate on 12000 samples
Epoch 1/100
48000/48000 [==============================] - 6s - loss: 0.3747 - acc: 0.8732 - val_loss: 0.2888 - val_acc: 0.8935
Epoch 2/100
48000/48000 [==============================] - 6s - loss: 0.2216 - acc: 0.9178 - val_loss: 0.2942 - val_acc: 0.9010
Epoch 3/100
48000/48000 [==============================] - 5s - loss: 0.1762 - acc: 0.9340 - val_loss: 0.2868 - val_acc: 0.9078
...
Epoch 74/100
48000/48000 [==============================] - 6s - loss: 0.0182 - acc: 0.9953 - val_loss: 0.8069 - val_acc: 0.9109
Epoch 75/100
48000/48000 [==============================] - 6s - loss: 0.0102 - acc: 0.9971 - val_loss: 0.7872 - val_acc: 0.9125
Epoch 76/100
11776/48000 [======>.......................] - ETA: 3s - loss: 0.0084 - acc: 0.9977

Nihayet! Modeli fashion-MNIST üzerinde sadece 10 epoch boyunca eğittiniz ve eğitim doğruluğu ile kaybı gözlemlediğinizde, modelin harika iş çıkardığını söyleyebilirsiniz; 10 epoch sonunda eğitim doğruluğu %99 ve doğrulama kaybı %98’dir.

Sınıflandırma modelini kaydedelim!

full_model.save_weights('autoencoder_classification.h5')

Sonraki adımda, ilk on dokuz katmanı False tutmak yerine True yaparak modeli yeniden eğiteceksiniz! Hızla bunu yapalım.

for layer in full_model.layers[0:19]:
    layer.trainable = True
full_model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adam(),metrics=['accuracy'])

Şimdi tüm modeli son bir kez eğitelim!

classify_train = full_model.fit(train_X, train_label, batch_size=64,epochs=100,verbose=1,validation_data=(valid_X, valid_label))
Train on 48000 samples, validate on 12000 samples
Epoch 1/100
48000/48000 [==============================] - 13s - loss: 0.1584 - acc: 0.9718 - val_loss: 0.7902 - val_acc: 0.8960
Epoch 2/100
48000/48000 [==============================] - 12s - loss: 0.1049 - acc: 0.9759 - val_loss: 0.8327 - val_acc: 0.8893
Epoch 3/100
48000/48000 [==============================] - 12s - loss: 0.0792 - acc: 0.9804 - val_loss: 0.6947 - val_acc: 0.9099
...
loss: 0.0123 - acc: 0.9971 - val_loss: 0.6827 - val_acc: 0.9217
Epoch 98/100
48000/48000 [==============================] - 13s - loss: 0.0097 - acc: 0.9975 - val_loss: 0.7074 - val_acc: 0.9211
Epoch 99/100
48000/48000 [==============================] - 13s - loss: 0.0081 - acc: 0.9984 - val_loss: 0.6846 - val_acc: 0.9205
Epoch 100/100
48000/48000 [==============================] - 13s - loss: 0.0090 - acc: 0.9977 - val_loss: 0.6739 - val_acc: 0.9226

Modeli son bir kez daha kaydedelim.

full_model.save_weights('classification_complete.h5')

Model değerlendirmesini somutlaştıralım ve eğitim ile doğrulama verileri arasındaki doğruluk ve kayıp grafiklerini çizelim:

accuracy = classify_train.history['acc']
val_accuracy = classify_train.history['val_acc']
loss = classify_train.history['loss']
val_loss = classify_train.history['val_loss']
epochs = range(len(accuracy))
plt.plot(epochs, accuracy, 'bo', label='Training accuracy')
plt.plot(epochs, val_accuracy, 'b', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
grafik
grafik

Yukarıdaki iki grafikten, eğitim ve doğrulama kaybı arasında büyük bir fark olduğu için modelin aşırı uyum yaptığını görebilirsiniz. Aşırı uyumu gidermek için Dropout gibi bazı düzenlileştirme tekniklerini kullanmanız gerekebilir. Keras ile Python’da CNN eğitimi üzerine bu eğitimi takip edebilirsiniz.

Test Setinde Model Değerlendirmesi

Son olarak, modelinizi test verisi üzerinde de değerlendirelim ve nasıl performans gösterdiğini görelim!

test_eval = full_model.evaluate(test_data, test_Y_one_hot, verbose=0)
print('Test loss:', test_eval[0])
print('Test accuracy:', test_eval[1])
('Test loss:', 0.7068972043234281)
('Test accuracy:', 0.9205)

Etiketleri Tahmin Etme

predicted_classes = full_model.predict(test_data)

Aldığınız tahminler kayan noktalı değerler olduğundan, bunları gerçek test etiketleriyle karşılaştırmak pratik olmayacaktır. Bu nedenle çıktıyı yuvarlayarak kayan değerleri tam sayıya dönüştüreceksiniz. Daha sonra, bir satırda en yüksek değere sahip indeks numarasını seçmek için np.argmax() kullanacaksınız.

Örneğin, bir test görüntüsü için tahminin [0 1 0 0 0 0 0 0 0 0] olduğunu varsayalım; bu durumda çıktı sınıf etiketi 1 olmalıdır.

predicted_classes = np.argmax(np.round(predicted_classes),axis=1)
predicted_classes.shape, test_labels.shape
((10000,), (10000,))
correct = np.where(predicted_classes==test_labels)[0]
print "Found %d correct labels" % len(correct)
for i, correct in enumerate(correct[:9]):
    plt.subplot(3,3,i+1)
    plt.imshow(test_data[correct].reshape(28,28), cmap='gray', interpolation='none')
    plt.title("Predicted {}, Class {}".format(predicted_classes[correct], test_labels[correct]))
    plt.tight_layout()
Found 9204 correct labels
görüntüler
incorrect = np.where(predicted_classes!=test_labels)[0]
print "Found %d incorrect labels" % len(incorrect)
for i, incorrect in enumerate(incorrect[:9]):
    plt.subplot(3,3,i+1)
    plt.imshow(test_data[incorrect].reshape(28,28), cmap='gray', interpolation='none')
    plt.title("Predicted {}, Class {}".format(predicted_classes[incorrect], test_labels[incorrect]))
    plt.tight_layout()
Found 796 incorrect labels
görüntüler

Sınıflandırma Raporu

Sınıflandırma raporu, yanlış sınıflandırılan sınıfları daha ayrıntılı şekilde belirlemenize yardımcı olur. Verilen on sınıf içinde modelin hangi sınıfta zayıf performans gösterdiğini gözlemleyebilirsiniz.

from sklearn.metrics import classification_report
target_names = ["Class {}".format(i) for i in range(num_classes)]
print(classification_report(test_labels, predicted_classes, target_names=target_names))
             precision    recall  f1-score   support

    Class 0       0.83      0.89      0.86      1000
    Class 1       0.99      0.99      0.99      1000
    Class 2       0.89      0.87      0.88      1000
    Class 3       0.92      0.93      0.92      1000
    Class 4       0.85      0.90      0.88      1000
    Class 5       0.99      0.99      0.99      1000
    Class 6       0.81      0.72      0.76      1000
    Class 7       0.96      0.98      0.97      1000
    Class 8       0.98      0.98      0.98      1000
    Class 9       0.98      0.96      0.97      1000

avg / total       0.92      0.92      0.92     10000

Daha derine inmek ister misiniz?

Bu eğitim, Python ve Keras ile hem otokodlayıcı hem de tam bağlantılı evrişimli sinir ağını kullanmaya iyi bir başlangıç oldu. Kolayca ya da biraz daha fazla çabayla adımları takip edebildiyseniz, tebrikler! Aynı model mimarisini koruyup farklı türde herkese açık veri setleriyle deneyler yapmayı deneyin.

Hâlâ ele alınacak çok şey var; neden DataCamp’in Python ile Derin Öğrenmeye Giriş kursunu almıyorsunuz? Bu arada, henüz yapmadıysanız Keras dokümantasyonunu da mutlaka inceleyin. Tüm fonksiyonlar, argümanlar, daha fazla katman vb. hakkında daha çok örnek ve bilgi bulacaksınız. Python’da sinir ağlarıyla çalışmayı öğrenirken kuşkusuz vazgeçilmez bir kaynak olacaktır!

Eğer (Keras ile) derin öğrenmenin temellerini ve pratikte nasıl kullanıldığını açıklayan bir kitap okumayı tercih ediyorsanız, François Chollet’nin Deep Learning in Python kitabını kesinlikle okumalısınız.

Konular

İlgili