Caffe Fine-Tuning (Caffe'yi Kendi Verisetiniz ile Kullanma)

Merhaba,

Uygulamalarda genelde evrişimsel sinir ağlarını (convolutional neural network) rastgele başlatma ile sıfırdan eğitmiyoruz. Bunun nedeni gerekli ağ derinliği için yeterli veri setimizin olmamasıdır. Bunun yerine kendi verisetimize benzer çok büyük bir veriseti ile (örneğin 1000 kategoriye ayrılmış yaklaşık 1.000.000 ImageNet görüntüsü) eğitilmiş parametreleri başlangıç parametresi olarak alırız. Bu ağlar bir kaç GPU ile haftalarca eğitilmiş ağlardır. Ayrıca derin evrişimsel sinir ağlarında ilk katmanlar genel özelliklerin ağırlıklarını tutarken son katmanlara doğru sınıfa özgü özelliklerin ağırlıkları tutulur. Önceden eğitilmiş model, kendi sınıflandırma sorunumuzla alakalı özellikleri zaten barındırıyor olduğundan yarar sağlacaktır.

Elbette, veri kümemiz, örneğin tıbbi görüntüler veya Çin el yazısı karakterler gibi bazı spesifik alanlar ise ve bu alanda önceden eğitim almış ağlar bulunamazsa, o zaman ağın sıfırdan eğitim yapmayı düşünebiliriz.

Caffe ile önceden eğitilmiş ağ üzerinde kendi verisetimiz için ince ayar yaparak yeniden nasıl eğitilir inceleyelim.

Ön çalışmalar

Örnek olarak üzerinde çalışacağımız doku örneklerini buradan Outex_TC_00014 verisetini indirelim.
Outex_TC_00014 verisetinde 68 sınıf, bir sınıftan 3 farklı aydınlatma koşulu altında 20şer fotoğraf olmak üzere toplam 4080 adet resim bulunuyor.

Öncelikle verisetindeki tüm .bmp resimlerini .jpg olarak convert etmek için, mogrify kütüphanesinden yararlanılarak aşağıdaki komut terminalden çalıştıralım.

mogrify -path /home/yavuz/myprojects/myjupyter/images/Outex_TC_00014/images_jpg/ -format jpg /home/yavuz/myprojects/myjupyter/images/Outex_TC_00014/images/*.bmp

Bu sayede images klasörü altındaki tüm .bmp formatındaki resimler images_jpg klasörü altında .jpg formatına çevrilerek koplayanmış oldu.

Daha sonra ilk iki aydınlatma koşulu altındaki örnekleri train için, üçüncü aydınlatma koşulu altındaki örnekleri ise test için klasörlere ayıralım.

Train klasöründe 2720 adet, test klasöründe ise 1360 adet resim bulunmalıdır.

Aynı şekilde "FileFullPath Class" formatında(Örn: /home/yavuz/caffe/examples/outexTC14/train/002641.jpg 64) olacak şekilde test klasörü altındaki resimler için test.txt ve train klasörü altındaki resimler için ise train.txt oluşturalım.

Son olarak modelin iyi öğrenmesi için test.txt ve train.txt içerisindeki örneklerin sıralı şekilde olmaması gerekiyor. Sıralamayı karıştırmak için terminal üzerinden shuf paketinden yararlanarak aşağıdaki komut ile karıştırılmış train.txt ve test.txt ler üretelim.

shuf train.txt > train_rndm.txt
shuf test.txt > test_rndm.txt

Ön işlemler için yazdığım çalışma notebook'una buradan erişebilirsiniz.

Verisetinin jpg formatına çevrilmiş, train ve test klasörlerine ayrılmış şekilde düzenlediğim halini buradan indirebilirsiniz.

LMDB'ye Dönüştürme

Caffe'de verisetini LMDB formatına çevirmek için bir script örneği mevcut.
(caffe/examples/imagenet/create_imagenet.sh)

caffe/examples altına yeni bir klasör(outexTC14) açalım. 

caffe/examples/imagenet/ altındaki create_imagenet.sh ve make_imagenet_mean.sh scriptlerini examples altına yeni açtığımız klasör içerisine(caffe/examples/outexTC14/) kopyalayalım.

create_imagenet.sh ı düzenleyelim;

  • EXAMPLE=examples/outexTC14 : LMDB yi saklamak istediğimiz klasör
  • DATA=data/outexTC14 : test.txt ve train.txt dosyalarının da bulunduğu klasör
  • TRAIN_DATA_ROOT=/home/yavuz/caffe/examples/outexTC14/train/ : train resimlerinin bulunduğu klasör
  • VAL_DATA_ROOT=/home/yavuz/caffe/examples/outexTC14/test/ : test resimlerinin bulunduğu klasör
  • RESIZE=true : eğer resimleriniz 256x256 değilse
  • Sondaki kod parçacığını da aşağıdaki gibi $DATA/train_rndm.txt, $DATA/test_rndm.txt ve $EXAMPLE/outexTC14_train_lmdb, $EXAMPLE/outexTC14_test_lmdb olarak düzenlenir.
    GLOG_logtostderr=1 $TOOLS/convert_imageset \
        --resize_height=$RESIZE_HEIGHT \
        --resize_width=$RESIZE_WIDTH \
        --shuffle \
        $TRAIN_DATA_ROOT \
        $DATA/train_rndm.txt \
        $EXAMPLE/outexTC14_train_lmdb
    
    echo "Creating val lmdb..."
    
    GLOG_logtostderr=1 $TOOLS/convert_imageset \
        --resize_height=$RESIZE_HEIGHT \
        --resize_width=$RESIZE_WIDTH \
        --shuffle \
        $VAL_DATA_ROOT \
        $DATA/test_rndm.txt \
        $EXAMPLE/outexTC14_test_lmdb

make_imagenet_mean.sh'ı düzenleyelim;

EXAMPLE=examples/outexTC14
DATA=data/outexTC14
TOOLS=build/tools

$TOOLS/compute_image_mean $EXAMPLE/outexTC14_train_lmdb \
  $DATA/imagenet_mean.binaryproto

caffe/data klasörü altına da yeni bir klasör(outexTC14) açalım. Yukarıda make_imagenet_mean.sh ve create_imagenet.sh scriptleri içerisinde yeni açtığımız klasörü data/outexTC14 olarak belirtmiştik.

Yeni açtığımız caffe/data/outexTC14 klasörü içerisine yukarıda oluşturduğumuz train_rndm.txt ve test_rndm.txt dosyalarını kopyalayalım.

Şimdi scriptlerimizi çalıştırmaya hazırız.
Terminalden caffe klasörümüz içerisinde aşağıdaki komut ile önce create_imagenet.sh scriptimizi çalıştıralım.

~/caffe$ examples/outexTC14/create_imagenet.sh 

Bu script çalışması sonucunda caffe/examples/outexTC14/ içerisinde outexTC14_test_lmdb ve outexTC14_train_lmdb adında iki klasör oluşmuş olması gerekiyor.


Terminalden caffe klasörümüz içerisinde aşağıdaki komut ile önce make_imagenet_mean.sh scriptimizi çalıştıralım.

examples/outexTC14/make_imagenet_mean.sh

Bu script çalışması sonucunda caffe/data/outexTC14/ içerisinde imagenet_mean.binaryproto adında dosya oluşmuş olması gerekiyor.

Ağ Düzenleme

caffe/models klasörü altına yine aynı isimde yeni bir klasör(outexTC14) açalım. Fine-tuning için caffe içerisinde var olan FlickrStyleCaffeNet ağından yararlanalım.
caffe/models/finetune_flickr_style altındaki train_val.prototxt, solver.prototxt ve deploy.prototxt dosyalarını yeni açtığımız caffe/models/outexTC14 klasörü altına kopyalayalım.

train_val.prototxt'ı düzenleyelim;

  • name: "OutexTC14CaffeNet"
  • transform_param (Birinci TRAIN Layer'ında)
        mean_file: "data/outexTC14/imagenet_mean.binaryproto"
  • image_data_param (Birinci TRAIN Layer'ında)
        source: "data/outexTC14/train_rndm.txt"
  • transform_param (İkinci TEST Layer'ında)
        mean_file: "data/outexTC14/imagenet_mean.binaryproto"
  • image_data_param (Birinci TRAIN Layer'ında)
        source: "data/outexTC14/test_rndm.txt"
  • inner_product_param (En son layer)
        num_output: 68
    (Verisetimizdeki sınıf sayımız)
name: "OutexTC14CaffeNet"
layer {
  name: "data"
  type: "ImageData"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    mirror: true
    crop_size: 227
    mean_file: "data/outexTC14/imagenet_mean.binaryproto"
  }
  image_data_param {
    source: "data/outexTC14/train_rndm.txt"
    batch_size: 50
    new_height: 256
    new_width: 256
  }
}
layer {
  name: "data"
  type: "ImageData"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    mirror: false
    crop_size: 227
    mean_file: "data/outexTC14/imagenet_mean.binaryproto"
  }
  image_data_param {
    source: "data/outexTC14/test_rndm.txt"
    batch_size: 50
    new_height: 256
    new_width: 256
  }
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 96
    kernel_size: 11
    stride: 4
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
...
...
...
layer {
  name: "fc8_flickr"
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8_flickr"
  # lr_mult is set to higher than for other layers, because this layer is starting from random while the others are already trained
  param {
    lr_mult: 10
    decay_mult: 1
  }
  param {
    lr_mult: 20
    decay_mult: 0
  }
  inner_product_param {
    num_output: 68
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "accuracy"
  type: "Accuracy"
  bottom: "fc8_flickr"
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "fc8_flickr"
  bottom: "label"
  top: "loss"
}

solver.prototxt'ı düzenleyelim;

  • net: "models/outexTC14/train_val.prototxt"
  • test_iter: 27 (batch_size: 50 olduğundan ve 1360 test verimiz olduğundan yaklaşık olarak 1360/50 = 27)
  • base_lr: 0.0001 (learning rate)
  • snapshot_prefix: "models/outexTC14/outexTC14"
net: "models/outexTC14/train_val.prototxt"
test_iter: 27
test_interval: 100
# lr for fine-tuning should be lower than when starting from scratch
base_lr: 0.0001
lr_policy: "step"
gamma: 0.1
# stepsize should also be lower, as we're closer to being done
stepsize: 20000
display: 20
max_iter: 100000
momentum: 0.9
weight_decay: 0.0005
snapshot: 1000
snapshot_prefix: "models/outexTC14/outexTC14"
# uncomment the following to default to CPU mode solving
solver_mode: GPU

deploy.prototxt'ı düzenleyelim;
(train_val.prototxt'deki değişiklikler gibi)

  • name: "OutexTC14CaffeNet"
  •  inner_product_param
        num_output: 68

 

Son olarak .prototxt'lerimizi düzenledikten sonra train için bir ağırlık dosyasına ihtiyacımız olduğundan caffe/models/bvlc_reference_caffenet altındaki bvlc_reference_caffenet.caffemodel dosyasını kendi model klasörümüz caffe/models/outexTC14 klasörü altına kopyalayalım.

Eğitim

Bir ağın eğitimi için ihityacımız olan herşeyi hazırladık. ./caffe klasörü altında aşağıdaki fine-tuning train komutu ile eğitimi başlatalım.

./build/tools/caffe train -solver models/outexTC14/solver.prototxt -weights models/outexTC14/bvlc_reference_caffenet.caffemodel

Eğer log almak isterseniz yukarıdaki komut sonuna 2>&1 | tee -a log_outexTC14_20170812.log ekleyebilirsiniz.

Log çıktısını vcaffe ile görselleştirebilirsiniz.

./build/tools/caffe train -solver models/outexTC14_2/solver.prototxt -weights models/outexTC14_2/bvlc_reference_caffenet.caffemodel 2>&1 | tee -a log_outexTC14_20170812.log



Gördüğünüz gibi eğitim makul bir çizgide ilerliyor. Burada amacımız yüksek bir başarım elde etmek değil. Hazır eğitilmiş bir ağ üzerinden ince ayarlar yaparak ve kendi verisetimize göre düzenleyerek yeniden nasıl train edebileceğimizi görmekti.
Sizler de AlexNet için aynı adımları gerçekleştirebilirsiniz.

Sınıflandırma

Eğittimiz modeli kullanarak sınıflandırma yapmak istediğimizde sınıf isimlerimizin bulunduğu bir labels.txt dosyasına ihtiyacımız var.

train.txt de test.txt içerisinde belirttiğimiz sınıflara karşılık belirlediğimiz etiketleri aşağıdaki formatta caffe/models/outexTC14 altına labels.txt oluşturuyoruz.

0 doku0
1 doku1
2 doku2
3 doku3
4 doku4
...
67 doku67

Aşağıdaki komut ile deploy.prototxt, istediğimiz iterasyondaki .caffemodel, .binaryproto, labels.txt ve sınıflandırmasını istediğimiz resmin full path'ini belirterek çalıştırıyoruz.

./build/examples/cpp_classification/classification.bin models/outexTC14/deploy.prototxt models/outexTC14/outexTC14_iter_7000.caffemodel data/outexTC14/imagenet_mean.binaryproto data/outexTC14/labels.txt /home/yavuz/caffe/examples/outexTC14/test/002740.jpg

002740.jpg resminin %83,14 ile doku1 sınfına ait olduğunu görüyoruz.



Eksik ve hataları veya yorumlarınızı paylaşırsanız sevinirim.

Yönlendirme ve destekleri için tez danışmanım Erhan hocama teşekkürler.

Faydalı Linkler:

 

MQTT Nedir ve Yapısı

MQTT Nedir?

MQTT(Message Queuing Telemetry Transport) protokolü, internette yaygın olarak kullanılan makinalar arası (M2M) mesaj tabanlı bir protokoldür.
Lightweight oluşu ve düşük kaynak tüketmesiyle Internet of Things(IoT) ekosisteminde benimsenmiştir. Hemen hemen tüm IoT bulut platformları akıllı nesnelerden veri gönderip almak için MQTT protokolünü desteklemektedir.

Bu protokol, istek(request)-yanıt(response) yapısına dayalı HTTP'ye karşıt olarak yayın(publish)-abone(subscriber) yapısında TCP/IP bağlantısı kurulur.
TCP/IP protokolünün yazılabildiği Linux, Windows, Android, iOS, MacOS işletim sistemlerinde çalışır.


MQTT Mesaj Yapısı

MQTT protokolü yayıncı-abone yapısında bir mesaj yayınlayan bir client (yayıncı) mesajı alan diğer clientlara ayıracaktır (aboneler). Ayrıca, MQTT asenkron protokoldür, bu da mesajı beklerken clientı engellemediği anlamına gelir. HTTP protokolünün aksine, esas olarak eşzamanlı bir protokoldür. MQTT protokolünün bir başka özelliği, istemcinin (abone) ve yayıncının aynı anda bağlı olmasını gerektirmemesidir.


MQTT Yayıncı-Abone Mimarisi

MQTT'deki kilit unsur MQTT brokerıdir. MQTT brokerın asıl görevi, clientlara (abonelere) mesajlar göndermektir. Yani yayıncıdan mesajlar alır ve bu mesajları abonelere gönderir. Mesaj gönderirken, MQTT broker mesajı alacak olan clientları filtrelemek için konuyu(topic) kullanır. Konu bir dizedir ve konu seviyeleri yaratan konuları birleştirmek mümkündür.

Konu(topic) bir yayıncıyı abonelerine bağlayan sanal bir kanala benzer. Bu konu MQTT brokerı tarafından yönetilmektedir. Bu sanal kanal sayesinde, yayıncı abonelerden ayrılmıştır ve istemcilerin(yayıncılar veya aboneler) birbirlerini tanıması gerekmemektedir. Bu yapısı gereği bu protokolü mesaj üreticisine(yayıncı) ve mesaj tüketicisine(abone) doğrudan bağımlılık olmadan çok ölçeklenebilir hale getirir.

MQTT mimarisi aşağıdaki şekildedir:

 

MQTT brokerlarından Mosquitto'nun nasıl kurulacağı ve MQTT'nin nasıl kullanılacağı ile ilgili "Ubuntu üzerinde MQTT Mosquitto broker'ı kurma" yazımı okuyabilirsiniz.

Konuyla ilgili diğer faydalı linkler: