YOLOv5 modelini android cihaz üzerinde çalıştırmak

YOLOv5 modelini android cihaz üzerinde çalıştırmak

Yolov5 modelini kullanarak ncnn ile android cihaz üzerinde nesne algılama uygulaması

Merhabalar

Kendime ve size not:

Burada eğitimi bitmiş yolov5 modelimizi android cihaz üzerinde çalışmak istediğimizde bizi nasıl bir yolculuk bekliyor bunu biraz irdeleyeceğiz.

Akla gelen ilk soruyla başlayayım neden sadece android ortamı:

Piyasaya baktığımızda akıllı telefon piyasasınını yüzde 80'nini domine eden... şaka şaka bir macbookum yok :) yoksa ikisinden de denerdim. Madem öyle huawei için de dene kardeşim o zaman dediğinizi duyar gibiyim yukarıdaki cümle gerçekten tamamlanır o vakit :)

Öncelikle derin öğrenme uygulamaları geliştirildiğinde seçilen öncelikli yolun modelin bir sunucuda çalıştırılıp sonuçlarının kullanıcıya gönderilmesi olduğunu görüyoruz ama söz konusu yavaş internet bağlantısı veya gizlilik olduğunda bu konudki öncelik değişmeye başlıyor. Bu sebeplerin dışında az kaynak ile çok iş yapmak (sunucu maliyeti) bu sektörün temel mottosu zaten ve kullanıcının kaynaklarını da kendi amacımız için kullanmak şimdilik ahlaki bir sorun yaratmıyor gibi :)

Mobil cihazlar üzerinde derin öğrenme nimetlerinden faydalanmak için baktığımızda büyük şirketlerin bu konuda kıyasıya bir yarış içinde olduğunu görüyoruz. Google tensorflow lite, facebook caffe2, tencent ncnn, Apple core ML vb şirketlerin sözleşmiş şekilde 2017 yılıyla beraber birer birer çerçeve yayınlayarak yarışı başlattıkları görülmekte ve her geçen gün bu noktada haberler paylaşıyorlar.

Neyse sorunumuza odaklanalım ...

Öncelikle kendi YOLOv5 modelimizi nasıl eğiteceğimiz ile ilgili kısa bir yolculuğu buradan buradan okuyabilirsiniz.

İlk karar vermemiz gereken şey modelimizi android cihaz üzerinde koşturacaksak bunu nasıl yapacağımız, bu nokta gelin yolov5'in öncelikle hangi formatlarda dönüşüm imkanı verdiğine bakalım.

Yolov5 modelimizi -şuan için- aşağıda sıralayacağımız formatlarda dönüştürebiliyoruz. Öncelikle unutmamanız gereken şey dönüşüm bazen önemsenmeyecek düzeyde de olsa hız ve doğruluk gibi durumlar için bir miktar kayıp getirecektir. Bu yazının konusu farklı formatlar üzerinden oluşabilecek kayıp miktarını önemsemeyecektir.

PyTorch , TorchScript, ONNX, OpenVINO, TensorRT, CoreML, TensorFlow*

  • PyTorch yolov5'in mevcut durumda desteklediği format *

Yolov5'in desteklediği formatlara baktığımızda bizi çerçeve seçiminde bir hayli özgür bıraktığını görebilmek mümkün neredeyse piyasadaki her çerçeve ile kullanılabilecek formatı veriyor. Özellikle ONNX formatının geliştirilen çoğu derin öğrenme çerçevesi tarafından desteklendiğini düşünürsek. Tabi yolov5'in bu yeteneğini pytorch tabanlı olmasına borçlu olduğunu unutmamak gerek.

Yazıyı uzatmamak adına her çerçeveden bahsetmek yerine seçtiğim çerçeveyi ve nedenini kısaca açıklamaya çalışayım.

Çerçevemiz ncnn çünkü:

  • Çerçevemiz pytorch, tensorflow gibi çerçevelerin aksine sadece derin öğrenme modellerinin akıllı telefonlarda kullanımını desteklemek için kendisini konumlar yani öğrenme işini yapmaz bu da onu bizim için benzersiz yapar çünkü olası bir durumda yolov5 modelinden farklı bir modeliniz varsa ve bu yazıyı okuyorsanız sonraki adımlar size de fayda sağlayacaktır.

  • Şurada görüleceği üzere coffe2'den de hızlı çalışmakta.

  • Github ve topluluk desteği güçlü olan bir çerçeve

  • Şu çalışmada ncnn'e ikna olmak için daha fazla neden bulabilirsiniz.

Bir karar verdiğimize göre ikinci aşamaya geçebiliriz peki ncnn çerçevesini nasıl kullanacağız. Öncelikle yapmamız gereken iki işlem var ve işlem kendi altında alt işlemlere sahip. Basit anlamda, öncelikle yolov5 modelini ONNX formatına çevirme oradan da ncnn modelinin anlayacağı hale getireceğiz. Daha sonra çeviriğimiz modelin android uygulamada nasıl kullanılacağını göreceğiz.

YOLOv5'i modelini ncnn modeline çevirme

Adsız sunu.jpg

Yolov5'i ncnn modeline direkt çevirme şuan için yok bunu yapabilmek için öncelikle bu tür model değişimleri için dünyada standart olan ONNX'i modeline çevirmemiz gerekiyor. Bunu yapmakta Yolov5 ile son derece kolay.

ONNX nedir?

python export.py --weights  ./trafficsign/weights/best.pt --img 640 --batch 2

Bu işlemi yaptıktan sonra best.onnx adında modeliniz oluşacaktır. Fakat dönüşümlerden kaynaklı fazlaca karmaşık olan ve büyük boyutlu olan modelimizi basitleştirmek için şu adreste yayınlanan aracı kullanıyoruz. Ya da aynı reponun web ortamında sunduğu şu adresten giriş ve çıkış modelini onnx seçerseniz sizlere optimize edilmiş modeli verecektir.

Dönüşüm sonunda modelin boyutunun yarı yarıya azaldığını göreceksiniz ki hedef ortamımız mobil alan olunca büyük bir avantaj sağlamış oluyoruz.

#kurulum
pip3 install -U pip && pip3 install onnx-simplifier

#kullanım
onnxsim best.onnx best_opt.onnx

Dönüşümden sonra optimize edilmiş best_opt.onnx modelimizi ncnn modeline çevirmeye geldi sıra bunun için iki yolumuz var ya ncnn reposunu indirip bilgisayarınızda derleyip öyle dönüşüm sağlayacaksınız ya da onnx-simplifier reposunun web ortamında sunduğu şu adresinden yararlanabilirsiniz.

1.yol web ortamında şu adresten giriş için onnx çıkış için ncnn seçmeniz yeterli.

2.yol ncnn reposunu şu adresten indirip derlemeniz tabi ki sizlere yararlı olacaktır. Olası hata durumlarında hazır araçlar işleri epey zorlaştıracaktır. Şuradan derleme ile ilgili detayları bulabilirsiniz.

Derleme işleminden sonra build klasörü içinde tools/onnx altında onnx2ncnn.exe'yi kullanabilirsiniz.

onnx2ncnn yolov5s-sim.onnx yolov5s.param yolov5s.bin

Unsupported slice step hatası

Basitleştirme durumların bazen hatalar alabilirsiniz. ncnn dilimleme hatasına manuel olarak müdahale etmek için şu adresten yararlanabilirsiniz.

Ya da ncnn'in desteklemediği dilim işlemini iptal etmek için ilk adımımız olan export işleminden önce common.py dosyına şu adreste bahsedildiği şekilde müdahale etmek gerekmektedir. #return self.conv(torch.cat([x,x,x,x],1)) satırını ekleyerek.

common.py:


class Focus(nn.Module):
    # Focus wh information into c-space
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super(Focus, self).__init__()
        self.conv = Conv(c1 * 4, c2, k, s, p, g, act)

    def forward(self, x):  # x(b,c,w,h) -> y(b,4c,w/2,h/2)
        return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
        #return self.conv(torch.cat([x,x,x,x],1))

Bütün bunlardan sonra mobil uygulamamıza geçebiliriz. Uygulamamız açıldığı anda kamerayı açmakta ve ekrana modelimize uygun nesneleri tespit etmeye başlamaktadır. Uygulama şu adresten örnek alınarak oluşturulmuştur.

Android uygulaması

Uygulamanın hazır halini şu adresten bulabilirsiniz.

Öncelikle eğer repodan uygulamayı indirdiyseniz modellerinizi assets içine kopyalamalı, ncnn reposundan ncnn-20220420-android-vulkan.zip sürümünü indirmeli, şu adresten yolov5ncnn_jni içinde sınıf sayılarını ve anchor box ayarlarını yapmalısınız.

  • Android uygulamayı indirme
git clone https://github.com/savhascelik/using-yolov5-in-ncnn.git
  • Modellerin assets klasörüne kopyalanması

image.png

  • ncnn-20220420-android-vulkan sürümünü veya siz yazıyı okurken üst sürümde olur indirme ve app/src/main/jni klasörüne kopyalanması

image.png

farklı bir ncnn vulkan sürümü yüklerseniz bu değişikliği CMakeList.txt klasöründe belirtin

set(ncnn_DIR ${CMAKE_SOURCE_DIR}/**ncnn-20220420-android-vulkan**/${ANDROID_ABI}/lib/cmake/ncnn)
  • yolov5ncnn_jni içinde sınıfları girme

image.png

  • yolov5ncnn_jni içinde anchor box ayarlarını yapma
 // stride 8
        {
            ncnn::Mat out;
            ex.extract("output", out);

            ncnn::Mat anchors(6);
            anchors[0] = 10.f;
            anchors[1] = 13.f;
            anchors[2] = 16.f;
            anchors[3] = 30.f;
            anchors[4] = 33.f;
            anchors[5] = 23.f;

            std::vector<Object> objects8;
            generate_proposals(anchors, 8, in_pad, out, prob_threshold, objects8);

            proposals.insert(proposals.end(), objects8.begin(), objects8.end());
        }

        // stride 16
        {
            ncnn::Mat out;
            ex.extract("760", out);

            ncnn::Mat anchors(6);
            anchors[0] = 30.f;
            anchors[1] = 61.f;
            anchors[2] = 62.f;
            anchors[3] = 45.f;
            anchors[4] = 59.f;
            anchors[5] = 119.f;

            std::vector<Object> objects16;
            generate_proposals(anchors, 16, in_pad, out, prob_threshold, objects16);

            proposals.insert(proposals.end(), objects16.begin(), objects16.end());
        }

        // stride 32
        {
            ncnn::Mat out;
            ex.extract("761", out);

            ncnn::Mat anchors(6);
            anchors[0] = 116.f;
            anchors[1] = 90.f;
            anchors[2] = 156.f;
            anchors[3] = 198.f;
            anchors[4] = 373.f;
            anchors[5] = 326.f;

            std::vector<Object> objects32;
            generate_proposals(anchors, 32, in_pad, out, prob_threshold, objects32);

            proposals.insert(proposals.end(), objects32.begin(), objects32.end());
        }

anchor box ayarlarında anchors'ları eğitim yaparken kullandığımız yolov5s.yaml dosyası içinde bulabilirsiniz

image.png

ayrıca kodda gözüken out sayılarını da Netron yazılımını kullanarak yolov5.param dosyasını görüntülediğinizde son permute katmanlarını tıklayarak alabilirsiniz.

image.png

image.png

bu değişikliklerden sonra kendi modelinizi nihayet deneyebilirsiniz. Modellinizi isimlerini değiştirmek isterseniz de yolov5ncnn_jni içinde ilgili satırları değiştirmeni yeterli

 int ret = yolov5->load_param(mgr, "yolov5s.param");
int ret = yolov5->load_model(mgr, "yolov5s.bin");

ve artık uygulamayı telefon yada emulatore build edebiliriz. Uygulama bu haliyle telefonda 4-7 arası fps hızında yapılacak optimizasyon ve düzeltmelerle bu hızın çok daha fazla artması mümkün ki yolov5'in iddiası da bu yönde zaten. Bu konular da başka bir yazının konusu olsun .

başka bir yazıda görüşmek üzere İyi öğrenmeler...