New version
This commit is contained in:
parent
c798b8e4e0
commit
7bd9db3454
3 changed files with 3550 additions and 1685 deletions
|
@ -7497,9 +7497,9 @@ int main(int argc, char *argv[])
|
|||
|
||||
$ sudo cp MLO u-boot.img /home/kaan/Study/EmbeddedLinux/BusyBox/fat
|
||||
|
||||
10) Artık micro SD kartı BBB'ye takarak micro SD karttan boot işlemi yapıp U-Boot'un komut satırına düşebiliriz.
|
||||
Ancak boot işlemini otomatize etmek için daha önce görmüş olduğumuz yöntemi de kullanabiliriz. Bunun için FAT dosya sisteminde
|
||||
aşağıdaki içeriğe sahip "uEnv.txt" dosyasını (tabii dosyanın ismi böyle olmak zorunda değil) oluşturalım:
|
||||
10) Artık micro SD kartı BBB'ye takarak micro SD karttan boot işlemi yapıp U-Boot'un komut satırına düşebiliriz. Ancak boot
|
||||
işlemini otomatize etmek için daha önce görmüş olduğumuz yöntemi de kullanabiliriz. Bunun için FAT dosya sisteminde aşağıdaki
|
||||
içeriğe sahip "uEnv.txt" dosyasını (tabii dosyanın ismi böyle olmak zorunda değil) oluşturalım:
|
||||
|
||||
loadkernel=load mmc 0:2 0x82000000 /boot/vmlinuz-5.10.168-ti-r71
|
||||
loadfdt=load mmc 0:2 0x88000000 /boot/dtbs/5.10.168-ti-r71/am335x-boneblack.dtb
|
||||
|
@ -7510,6 +7510,8 @@ int main(int argc, char *argv[])
|
|||
Sonra U-Boot'un komut satırında aşağıdaki komutu girip çevre değişkenlerini save edebiliriz:
|
||||
|
||||
=> setenv bootcmd "load mmc 0:1 0x8A000000 /uEnv.txt;env import -t 0x8A000000;run uenvcmd"
|
||||
=> saveenv
|
||||
Saving Environment to FAT... OK
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -8883,22 +8885,23 @@ void exit_sys(const char *msg)
|
|||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
Girişte de belittiğimiz gibi systemd init sisteminde "bağımlılık yönetimi" olgusu vardır. Bağımlılık yönetimi "bu servis
|
||||
çalıştırılırsa şu servis de çalıştırılsın", "bu servis şu servis çalıştırılıyorsa çalıştırılsın" demenin bir yöntemidir.
|
||||
Bunu uygulamak için tipik olarak aşağıdaki direktifler kullanılmaktadır:
|
||||
Bunu uygulamak için tipik olarak aşağıdaki direktifler kullanılmaktadır. (Direktifler ve bölümlerdeki isimlerin büyük harf
|
||||
küçük harf duyarlılığı yoktur):
|
||||
|
||||
wantedBy [install]
|
||||
requiredBy [install]
|
||||
wants [unit]
|
||||
requires [unit]
|
||||
WantedBy [install]
|
||||
RequiredBy [install]
|
||||
Wants [unit]
|
||||
Requires [unit]
|
||||
|
||||
Biz burada ikinci sütuna bu direktiflerin hangi bölüm içerisinde kullanılabileceğini belirttik. Burada "by" soneki olmayan
|
||||
direktifler ("wants", "requires") "bu birimi çalıştıracaksan direktifte belirtilen birimi de çalıştırmalısın" anlamına
|
||||
gelmektedir. "by" soneki olan direktifler ("wantedby", "requşredBy") ise "eğer direktifte belirtilen birimi çalıştırıyorsan
|
||||
bu birimi de çalıştırmalısın" anlamına gelmektedir. Yani by'sız direktiflerle by'lı direktifler ters yönlüdür. Pekiyi "want"
|
||||
direktifler ("Wants", "Requires") "bu birimi çalıştıracaksan direktifte belirtilen birimi de çalıştırmalısın" anlamına
|
||||
gelmektedir. "By" soneki olan direktifler ("WantedBy", "RequiredBy") ise "eğer direktifte belirtilen birimi çalıştırıyorsan
|
||||
bu birimi de çalıştırmalısın" anlamına gelmektedir. Yani By'sız direktiflerle By'lı direktifler ters yönlüdür. Pekiyi "want"
|
||||
ile "require" arasında ne fark verdır? İşte "require" katı bir istek, "want" ise "zayif bir istek belirtmektedir. İstek
|
||||
"require" ile belirtilmişse eğer bu istek karşılanmazsa ilgili birimin çalıştırılması gerçekleştirilmez. Ancak istek "want"
|
||||
ile belirtilmişse istek karşılanmasa bile ilgili birim çalışırılır. Örneğin A birim dosyasında "wants" ile B birimi belirtilmiş
|
||||
olsun. Bu durumda A çalıştırılırken eğer B bir nedenden dolayı çalıştırılamazsa A yine çalıştırılır. A birim dosyasında "required"
|
||||
ile B belirtilmişse bu durumda B çalıştırılamazsa A servisi de çalıştırılmaz. Aynı durum ters yönde "wantedBy" ve "requiredBy"
|
||||
ile belirtilmişse istek karşılanmasa bile ilgili birim çalışırılır. Örneğin A birim dosyasında "Wants" ile B birimi belirtilmiş
|
||||
olsun. Bu durumda A çalıştırılırken eğer B bir nedenden dolayı çalıştırılamazsa A yine çalıştırılır. A birim dosyasında Rrequired"
|
||||
ile B belirtilmişse bu durumda B çalıştırılamazsa A servisi de çalıştırılmaz. Aynı durum ters yönde "WantedBy" ve "RequiredBy"
|
||||
için de geçerlidir.
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -8907,11 +8910,11 @@ void exit_sys(const char *msg)
|
|||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
Birimlerin çalıştırılmasında öncelik-sonralık ilişkisi de kurulabilmektedir. Eğer böyle bir ilişki belirtilmediyse systemd
|
||||
Birimlerin çalıştırılmasında öncelik-sonralık ilişkisi de kurulabilmektedir. Eğer böyle bir ilişki belirtilmediyse "systemd"
|
||||
ilgili birimleri paralel bir biçimde çalıştırmaktadır. Öncelik sonralık ilişkisi için "Before" ve "After" biçiminde iki
|
||||
direktif bulundurulmaktadır. Before direktifi "içinde bulunulan birim direktifte belirtilen birimden önce", After direktifi
|
||||
ise "içinde bulunulan birim direktifte belirtilen birimden sonra çalıştırılacak" anlamına gelmektedir. Befor ve After direktifleri [
|
||||
Unit] bölümü içerisinde bulunabilir. Örneğin:
|
||||
direktif bulundurulmuştur. Before direktifi "içinde bulunulan birim direktifte belirtilen birimden önce", After direktifi
|
||||
ise "içinde bulunulan birim direktifte belirtilen birimden sonra çalıştırılacak" anlamına gelmektedir. Before ve After direktifleri
|
||||
[Unit] bölümü içerisinde bulunabilir. Örneğin:
|
||||
|
||||
[Unit]
|
||||
Description=The nginx HTTP and reverse proxy server
|
||||
|
@ -8929,11 +8932,11 @@ void exit_sys(const char *msg)
|
|||
Burada bir "servis birim (service unit)" dosyası örneği verilmiştir. Buna göre önce "network.target" isimli birim çalıştırılacak
|
||||
sonra bu servis çelıştırılacaktır.
|
||||
|
||||
Pekiyi biz "After" ya da "Before" ile "bizden önce çalıştırılsın ya da sonra çalıştırılsın" biçimind bir belirleme yaptık
|
||||
ancak buradaki birimler için "require" ya da "want" ile bir bellirleme yapmadık, bu durumda ne olacaktır? İşte bu durumda
|
||||
yalnızca "After" ya da "Before" "eğer bu direktifte beelirtilen birim çalıştırılırsa önce ya da sonra çalıştırılsın" anlamına
|
||||
gelmektedir. Yani bu direktifte belirtilen birimler çalıştırılmazsa bu direktiflerin bir anlamı kalmamaktadır. İlgili birim
|
||||
yine çalıştırılacaktır.
|
||||
Pekiyi bizim After ya da Before ile "bizden önce çalıştırılsın ya da sonra çalıştırılsın" biçiminde bir belirleme yaptığımızı
|
||||
ancak buradaki birimler için "require" ya da "want" ile bir bellirleme yapmadığımızı varsayalım, bu durumda ne olacaktır?
|
||||
İşte bu durumda yalnızca After ya da Before "eğer bu direktifte belirtilen birim çalıştırılırsa önce ya da sonra çalıştırılsın"
|
||||
anlamına gelmektedir. Yani bu direktifte belirtilen birimler çalıştırılmazsa bu direktiflerin bir anlamı kalmamaktadır. İlgili
|
||||
birim yine çalıştırılacaktır.
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -8941,17 +8944,17 @@ void exit_sys(const char *msg)
|
|||
çalıştırıldığında bir biçimde çalıştırılmaktadır. Hedef birim dosyalarıyla aynı isimli ".wants" uzantılı dizinler olduğunu
|
||||
belirtmiştik. İşte bu dizinler içerisinde ilgili hedef çalıştırıldığında devreye girecek olan birimler belirtilmektedir. Örneğin
|
||||
"multi-user.target" hedefi için "multi-user.tartget.wants" dizini bulunmaktadır. systemd bir hedef birimi işletirken onun
|
||||
".wants" dizinindeki birimleri çalıştırmaktadır. O halde sistem yöneticisi hangi hedeften hareketle hangi birimin çalıştırılmasını
|
||||
istiyorsa o birim dosyasını ilgili hedefin ".wants" dizinine yerleştirmesi gerekir. Ancak hemen her zaman sistem yöneticisi
|
||||
bunu yapmak yerine kendi birim dosyasını "/etc/systemd/system" ya da "/lib/systemd/system" dizinlerine yerleştirip ilgili
|
||||
".wants" dizinininde bu birim dosyasına ilişkin bir sembolik link oluşturmaktadır. Sistemin işleyişinin aşamaları şöyledir:
|
||||
".wants" dizinindeki birimleri çalıştırmaktadır. O halde sistem yöneticisi belli bir hedef için belli bir birimi çalıştırmak
|
||||
istiyorsa o birimi o hedefin ".wants" dizinine yerleştirmelidir. Ancak hemen her zaman sistem yöneticisi bunu yapmak yerine
|
||||
kendi birim dosyasını "/etc/systemd/system" ya da "/lib/systemd/system" dizinlerine yerleştirip ilgili ".wants" dizinininde
|
||||
bu birim dosyasına ilişkin bir sembolik link oluşturmaktadır. Özetlersek sistemin işleyişinin aşamaları şöyledir:
|
||||
|
||||
systemd belli bir hedefi çalıştırmak istiyor ----> hedefin ".wants" dizinine bakıp oradaki birimleri çalıştırıyor --->
|
||||
".wants" dizininde birim dosyalarının kendileri değil sembolik bağlantıları var, bunların kendileri "/etc/systemd/system"
|
||||
ya da "/lib/systemd/system" içerisinde.
|
||||
ya da "/lib/systemd/system" içerisinde bulunuyor.
|
||||
|
||||
Örneğin biz bir servis (daemon) yazmış olalım ve bu servisin boot sırasında devreye girmesini isteyelim. Örneğin bizim
|
||||
servisimiz için servis birim dosyası "myservice.service" ismiyle şöyle oluşturulmuş olsun:
|
||||
Örneğin biz bir servis (daemon) yazmış olalım ve bu servisin boot sırasında devreye girmesini isteyelim. Servisimiz için
|
||||
için servis birim dosyasını "myservice.service" ismiyle şöyle oluşturulmuş olalım:
|
||||
|
||||
[Unit]
|
||||
Description=My Service
|
||||
|
@ -8961,24 +8964,29 @@ void exit_sys(const char *msg)
|
|||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Burada "myservice.service" dosyasını "/etc/systemd/system" dizini içerisine çekmiş olalım. Bu işlemi yapmamız boot sırasında
|
||||
bu birim dosyasının çalıştırılması için yeterli değildir. Bizim bu servis birim dosyasına referans eden sembolik bağlantıyı
|
||||
da ilgili hedefin (burada "multiuser.target") ".wants" dizinine de yerleştirmemiz gerekir. İşte bu işlemleri yapan "systemctl"
|
||||
komutunun iki arümanı vardır: "systemctl enable <birim_dosyasının_ismi>", "systemctl disable <birim_dosyasının_ismi>".
|
||||
Burada "myservice.service" dosyasını "/etc/systemd/system" dizini içerisine yerleştirip ilgili hedefin dizininde bu dosyaya
|
||||
sembolik bağlantı oluşturmamız gerekir.
|
||||
|
||||
Aslında ilgili birim dosyasını "/etc/systemd/system" dizini içerisine yerleştirip sembolik bağlantıyı manuel oluşturmak yerine
|
||||
bu işlemi "systemctl" komutuna da yaptırabiliriz. "systemctl" komutunun iki arümanı vardır:
|
||||
|
||||
systemctl enable <birim_dosyasının_ismi>
|
||||
systemctl disable <birim_dosyasının_ismi>
|
||||
|
||||
Enable işlemi servis birim dosyasını inceleyip sentaks hatalarını görüntüler ve onun için sembolik bağlantıyı ilgili hedefin
|
||||
".wants" dizinine yerleştirmekte, disable işlemi ise ".wants" dizinindeki sembolik bağlantıyı yok etmektedir. Tabii "enable
|
||||
işleminde "systemctl" komutunun ilgili birim dosyasını inceleyip oradaki "want" ve "require" direktiflerini dikkate alıp ilgili
|
||||
sembolik bağlantıları burada belirtilen hedeflerin ".wants" dizinlerine yerleştirdiğine dikkat ediniz.
|
||||
".wants" dizinine yerleştirmektedir. Disable işlemi ise ".wants" dizinindeki sembolik bağlantıyı yok etmektedir. Tabii "enable
|
||||
işleminde "systemctl" komutunun ilgili birim dosyasını inceleyip oradaki "want" ve "require" direktiflerini dikkate alarak
|
||||
ilgili sembolik bağlantıları birim dosyasında belirtilen hedeflerin ".wants" dizinlerine yerleştirdiğine dikkat ediniz.
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
Aslında hedef birimler (target units) biribirinden bağımsız değil, birbirini gerektirecek biçimde oluşturulmuştur. Örneğin
|
||||
aslında sistemimiz "graphical.target" ile açıldığında bu durum "mult-user.target" hedefinin de çalıştırılmasını gerektirmektedir.
|
||||
Buradaki temel "requires" ilişkisi şöyledir:
|
||||
Buradaki temel "Requires" ilişkisi şöyledir:
|
||||
|
||||
graphical.target ---> multi-user.target ---> basic.target ---> sysinit.target
|
||||
|
||||
ilgili hedef birim dosyalarında bu "requires" direktifinde belirtilen hedefler "After" olarak belirtilmiştir. Dolayısıyla
|
||||
ilgili hedef birim dosyalarında bu "Requires" direktifinde belirtilen hedefler "After" olarak belirtilmiştir. Dolayısıyla
|
||||
aslında bu hedeflerin çalıştırılması ters sırada şöyle yapılacaktır:
|
||||
|
||||
sysinit.target ---> basic.target ---> multi-user.target ---> graphical.target
|
||||
|
@ -9018,11 +9026,11 @@ void exit_sys(const char *msg)
|
|||
|
||||
ExecStart="/usr/local/bin/myserviced"
|
||||
|
||||
O halde bizim örnek için bir tane "myserviced" isimli bir servis programı yazıp "/usr/local/bin" dizinine çekmemiz gerekir.
|
||||
O halde bizim örnek için "myserviced" isimli bir servis programı oluşturup onu "/usr/local/bin" dizinine çekmemiz gerekir.
|
||||
Normal olarak servis programları (daemon'lar) bazı adımlardan geçilerek oluşturulmaktadır. Bu adımlar bu servis programlarının
|
||||
terminalden çalıştırılabilmesini sağlamaktadır. Ancak eğer biz "systemd" init sistemini kullanıyorsak bu adımları zaten
|
||||
bizim için "systemd" yapmaktadır. Dolayısıyla biz örneğimizde normal bir C programı yazar gibi servis programımızı yazabiliriz.
|
||||
Aslında servis programı bu bağlamda bir shell script olarak da yazılabilir. Servimize ilişkin C programı şöyle olsun:
|
||||
Aslında servis programları bu bağlamda bir shell script olarak da yazılabilir. Servimize ilişkin C programı şöyle olsun:
|
||||
|
||||
/* myserviced.c */
|
||||
|
||||
|
@ -9085,11 +9093,14 @@ void exit_sys(const char *msg)
|
|||
$ sudo systemctl restart myservice.service
|
||||
|
||||
Pekiyi restart önce stop sonra start anlamına mı gelmektedir? İşte aslında her zaman bu anlama gelmek zorunda değildir.
|
||||
İnit programları restart işlemi sırasında ilgili servis prosesine SIGHUP sinyali göndermektedir. Servis programları bu
|
||||
sinyal oluştuğunda sanki restart işlemi yapılıyormuş gibi ilgili konfigürasyon dosyalarını yeniden okuyabilirler. Tabii bu
|
||||
sinyal işlenmemişse gerçekten proses sonlandırılır. Bu durumda init programları onları yeniden çalıştırmaktadır.
|
||||
İnit programları restart işlemi sırasında ilgili servis prosesine SIGHUP sinyali gönderir. Servis programları da bu
|
||||
sinyal oluştuğunda sanki restart işlemi yapılıyormuş gibi ilgili konfigürasyon dosyalarını yeniden okuyabilirler. Tabii
|
||||
bu sinyal işlenmemişse gerçekten proses sonlandırılır. Bu durumda init programları onları yeniden çalıştırmaktadır.
|
||||
|
||||
Şimdi buaradaki servis birim dosyasının boot sırasında otomatik devreye girmesini sağlayalım. Bu işlemi "systemctl enable
|
||||
Aslında start, stop ve restart yalnızca servis birimleri ile değil diğer bazı birimlerle de kullanılabilmektedir. Örneğin
|
||||
bir hedef (target) birim de start edilebilir, stop edilebilir ve restart edilebilir.
|
||||
|
||||
Şimdi buradaki servis birim dosyasının boot sırasında otomatik devreye girmesini sağlayalım. Bu işlemi "systemctl enable
|
||||
<servis_birim_dosyası_ismi>" komutu ile yapabiliriz:
|
||||
|
||||
$ sudo systemctl enable myservice.service
|
||||
|
@ -9102,6 +9113,246 @@ void exit_sys(const char *msg)
|
|||
$ sudo systemctl disable myservice.service
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
55. Ders 31/10/2024 - Perşembe
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
Yukarıda da belirttiğimiz gibi birim dosyalarındaki bölümlerde kullanılan direktiflerle ilgili pek çok ayrıntı vardır. Biz
|
||||
yukarıda servis birim dosyasını şöyle oluşturmuştuk:
|
||||
|
||||
[Unit]
|
||||
Description=My Service
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/myserviced
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Buradaki [Service] bölümü içerisindeki "ExecStart" direktifi servis progarmının yol ifadesini belirtmektedir. Yani başka bir
|
||||
deyişle bu servis birim dosyası çalıştırıldığında çalıştırılacak programı belirtmektedir. Bazen servis durdurulurken de bazı
|
||||
programların çalıştırılması istenebilir. Bunun için de "ExecStop" direktifi kullanılmaktadır. Benzer biçimde servis restart
|
||||
edildiğinde de ayrıca bir programın çalıştırılması "ExecRestart" direktifiyle sağlanabilmektedir. Yine aynı bölümde
|
||||
"WorkingDirectory" direktifi ile servis programının çalıma dizini (current working directory) belirlenebilir "StandardInput",
|
||||
"StandardOutput" ve "StandardError" direktifleri ile de servis orıgramının stdin, stdout ve stderr dosyalarının nereye
|
||||
yönlendirileceği belirtilebilmektedir. (Default olarak bu direktifler kulanılmazsa yönlendirme "journaling log" sistemine
|
||||
yapılmaktadır.)
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
Sistem açıldığında default hedeften (default target) hareketle işlemlerin yapıldığını belirtmiştik. Hedeflerin de ayrık
|
||||
değil birbirine bağlı biçimde oluşturulduğunu anımsayınız. İşte biz de istersek kendi hedeflerimizi oluşturturabiliriz.
|
||||
Örneğin biz "mytarget.target" isimli bir hedef birim oluşturup bu hedefte "myservice.service" biriminin çalıştırılmasını
|
||||
sağlayalım. "mytarget.target" birimi de "multi-user.target" birimi çalıştırıldığı zaman çalıştırılacak olsun. Bu işlemi
|
||||
şu adımlardan geçerek gerçekleştirebiiriz:
|
||||
|
||||
1) "mytarget.target" birim dosyasını minimal olarak aşağıdaki gibi oluşturabiliriz:
|
||||
|
||||
[Unit]
|
||||
Description=My Target
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Burada [install] bölümünde bu hedef birim dosyasının "multi-user.target" hedefi ile ilişkilendirildiğini görüyorsunuz.
|
||||
|
||||
2) "myservice.service" birim dosyanının "mytarget.target" ile ilişkilendirilmesi gerekir. Dosya şöyle oluşturulabilir:
|
||||
|
||||
[Unit]
|
||||
Description=MyService
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/myserviced
|
||||
[Install]
|
||||
WantedBy=mytarget.target
|
||||
|
||||
Burada [Install] bölümü içerisinde WantedBy direktifi ile ilişkilendirmenin yapıldığını görüyorsunuz.
|
||||
|
||||
3) Şimdi iki birimi de enable etmemiz gerekir:
|
||||
|
||||
$ sudo systemctl enable mytarget.target
|
||||
Created symlink /etc/systemd/system/multi-user.target.wants/mytarget.target → /etc/systemd/system/mytarget.target.
|
||||
$ sudo systemctl enable myservice.service
|
||||
Created symlink /etc/systemd/system/mytarget.target.wants/myservice.service → /etc/systemd/system/myservice.service.
|
||||
|
||||
4) Artık "mytarget.target" birimini start ve stop edip test işlemini yapabiliriz. Örneğin:
|
||||
|
||||
$ sudo systemctl start mytarget.target
|
||||
|
||||
Tabii artık reboot yapıldığında da "mytarget.target" dolayısıyla da "myservice.service" çalıştırılacaktır.
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
Hedef ve servis birim dosyalarının dışında çok kullanılan bir birim dosyası da mount birim dosyasıdır. Bu mount birim dosyası
|
||||
tipik olarak sistem açıldığında birtakım dosya sistemlerinin otomatik mount edilmesini sağlamak için kullanılmaktadır. Gerçi
|
||||
daha önce de belirttiğimiz gibi "systemd" init sisteminde standart bazı hedeflerin çalıştırdığı programlar "mount -a" komutuyla
|
||||
"/etc/fstab" dosyasnındaki mount işlemlerinin yapılmasına yol açmaktadır. Yani biz "systemd" init sisteminde de aslında
|
||||
"/etc/fstab" dosyasından hareketle otomatik mount işlemlerini yapabiliriz. Ancak mount birimi bize daha fazla olanaklar sunmaktadır.
|
||||
|
||||
Bir mount birim dosyasının tipik içeriği aşağıdaki gibidir:
|
||||
|
||||
[Unit]
|
||||
Description=Mount my external drive
|
||||
After=multi-user.target
|
||||
[Mount]
|
||||
What=/dev/sdb1
|
||||
Where=/mnt/external
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Burada mount birimi için önemli olan asıl bölüm [Mount] isimli bölümdür. Bu bölümde "What" direktifi mount işleminde kullanılacak
|
||||
blok aygıtını "Where" direktifi ise mount noktasını belirtmektedir. Ancak mount birim dosyalarının isimleri mount noktasıyla
|
||||
ilişkili olacak biçimde tirelenerek verilmelidir. tireleme her dizin geçişi arasında bir '-' karakteri kullanılarak yapılmalıdır.
|
||||
Örneğin yukarıdaki mount birim dosyasının ismi "mnt-external.mount" biçiminde olmalıdır. Ya da örneğin mount noktası
|
||||
"/home/kaan/Study/EmbeddedLinux/fat16" ise bizim de mount birim dosyasının ismini "home-kaan-Study-EmbeddedLinux-fat16.mount"
|
||||
biçiminde vermemiz gerekir.
|
||||
|
||||
Yukarıdaki gibi bir mount birim dosyası oluşturduğumuzda artık onu "systemctl" komutu ile start ve stop edebiliriz. Start
|
||||
işlemi mount işlemini gerçekleştirecek stop işlemi de unmount işlemini gerçekleştirecektir. Örneğin:
|
||||
|
||||
$ sudo systemctl start mnt-external.mount
|
||||
$ sudo systemctl stop mnt-external.mount
|
||||
|
||||
Tabii bizim asıl amacımız sistem açıldığında otomatik mount işleminin gerçekleştirilmesidir. Örneğimizde bu mount birimi
|
||||
"multi-user.target" ile ilişkilendirilmiştir. Bizim yine bu mount biriminin boot sırasında devreye girmesini sağlamak için
|
||||
enable işlemini yapmamız gerekir. Örneğin:
|
||||
|
||||
$ sudo systemctl enable mnt-externel.mount
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
56. Ders 05/11/2024 - Salı
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
Pekiyi biz loop aygıtını kullanarak mount işlemininin mount birimi tarafından yapılmasını nasıl sağlayabiliriz? Buradaki
|
||||
sorun mount biriminin çalıştırılması öncesinde "losetup" gibi bir işlemin de yapılması zorunluluğudur. İşte bunu sağlayabilmek
|
||||
için mount birimine ek olarak bir de servis birim dosyasının oluşturulması ve mount birimi çalıştırıldığında servis biriminin
|
||||
de mount biriminden önce çalıştırılması gerekir. Bu sırada servis birimi "losetup" programı ile loop aygıtını kullanıma hazır
|
||||
hale getirecektir. Örneğin loop0 aygıtı için mount birimi şöyle oluşturulabilir:
|
||||
|
||||
# mnt-fat16.mount (mount point: /mnt/fat16)
|
||||
|
||||
[Unit]
|
||||
Description=Mount Loop Device
|
||||
Requires=loop0.service
|
||||
After=loop0.service
|
||||
[Mount]
|
||||
What=/dev/loop0
|
||||
Where=/mnt/fat16
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Burada Requires direktifinde "loop0.service" dosyası belirtilmiştir. "After" direktifinde de önce bu servis dosyasının
|
||||
çalıştırılması gerektiği belirtilmektedir. Birimimizin "Install" bölümünde birimimiz "multi-usr.target" birimi ile
|
||||
ilişkilendirilmiştir. Bu birimden anlamamız gereken "/dev/loop0" aygıtının "/mnt/fat16" noktasına mount edileceği ancak bu
|
||||
işlemden önce "loop0.service" biriminin çalıştırılacağıdır. Burada mount birimine bağlı olan service biriminde de bazı
|
||||
direktiflerin yerleştirilmesi gerekmektedir. Bu service birimi aşağıdaki gibi olabilir:
|
||||
|
||||
[Unit]
|
||||
Description=loop0-Service
|
||||
BindsTo=mnt-fat16.mount
|
||||
Before=mnt-fat16.mount
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/sbin/losetup /dev/loop0 /mnt/fat16.dat
|
||||
ExecStop=/sbin/losetup -d /dev/loop0
|
||||
RemainAfterExit=yes
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Burada kritik iki direktif vardır. "RemainAfterExit" ve "BindsTo" direktifleridir. Buradaki "BindsTo" direktifi mount birimi
|
||||
ile servis biriminin birbirine bağlı olduğunu belirtmektedir. Yani bunlardan biri çalıştırılır ya da durdurulursa diğeri de
|
||||
çalıştırılıp durdurulacaktır. "RemainAfterExit" eğer "yes" yapılmazsa bu servis çalıştırıldıktan sonra hemen bitimde "ExecStop"
|
||||
çalıştırılır ve istediğimiz şey gerçekleşmez. Buradaki "RemainAfterExit=yes" direktifi servis çalıştırıldıktan sonra "stop"
|
||||
yapılana kadar ExecStop direktifinde belirtilen programın çalıştırılmamasını sağlamaktadır.
|
||||
|
||||
Pekiyi servis birim dosyasında "Requires" ile mount birim dosyası belirtildiği halde (diğerinde de diğeri belirtildiği
|
||||
halde) neden hala "BindsTo" direktifine gereksinim duyulmaktadır? İşte require ve want direktifleri "çalıştırma için" için
|
||||
bağımlılık belirtmektedir. Durdurma için bağımlılık belirtmemektedir. "BindsTo" direktifi "durdurma için de bağımlılık"
|
||||
belirtir. Eğer biz yukrıdaki örnekte servis birim dosyasındaki "BindsTo" direktifini kaldırırsak bu birimlerin çalıştırılmasında
|
||||
bir sorun oluşmaz. Ancak bu birimlerden biri durudurulmak istendiğinde yalnızca durdurulmak istenen birim durdurulur, diğeri
|
||||
durdurulmaz. "BindsTo" direktifi diğerinin de durdurulmasını sağlamaktadır. "BindsTo" direktifi genellikle servis birim dosyalarına
|
||||
yerleştirilmektedir. Ancak mount birim dosyalarına da yerleştirilebilir. Ancak "BindsTo" direktifinin bağlanma ilişkisi kurulan
|
||||
tüm iki birime de (bunlar daha fazla da olabilir) yerleştirilmesi gerekmez.
|
||||
|
||||
Yukarıdaki işlemin kalıcı hale getirilebilmesi için bizim hem mount birim dosyasını hem de servis birim dosyasını enable
|
||||
etmemiz gerekir. Örneğin:
|
||||
|
||||
$ sudo systemctl enable mnt-fat16.mount
|
||||
$ sudo systemctl enable loop0.service
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
Diğer çok kullanılanm bir birim de "zamanlayıcı" birimidir. Zamanlayıcı birimi UNIX/Linux sistemlerinde kullanılan klasik
|
||||
"cron" mekanizmasına benzer amaçlarla kullanılıyor olsa da daha fazla özellikleri ve kullanım yeri vardır.
|
||||
|
||||
Bir zamanlayıcı birim dosyasının tipik görünümü aşağıdaki gibidir:
|
||||
|
||||
# mytimer.timer
|
||||
|
||||
[Unit]
|
||||
Description=Run Timer Unit
|
||||
[Timer]
|
||||
OnActiveSec=1s
|
||||
OnUnitActiveSec=10s
|
||||
Persistent=true
|
||||
Unit=mytask.service
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Buraki [Unit] bölümü diğer birimlerdeki gibi bağımlılıklarla ve öncelik sonralık ilişkisi ile ilgili direktifleri bulundurmaktadır.
|
||||
Birimin en önemli bölümü [Timer] isimli bölümdür. Bu bölümdeki "Unit" direktifi çalıştırılacak olan servisi belirtir. Buradaki
|
||||
servis bir kez çaıştırılabileceği gibi periyodik olarak da çalıştırılabilmektedir. Burada "OnUnitActiveSec" direktifi ilgili
|
||||
servisin (örneğimizdeki "mytask.service") hangi periyotta çalıştırılacağını belirtmektedir. OnActiveSec" ya da "OnBootSec"
|
||||
servisin ilk kez ne zaman çalıştırılacağını belirlemekte kullanılmaktadır. (Eğer bu ilk çalışma belirleemesi yapılmazsa zamanlatıcı
|
||||
birimi çalıştırılır fakat bir etki göstermez.) Burada belirtilen zamanlar konusunda şaşmalar olabilmektedir. Eğer zamanlamaya
|
||||
daha katı bir biçimde uyulması gerekiyorsa bu durumda "OnAccuracySec" direktifi ile duyralılık belirtilmelidir. Örneğin:
|
||||
|
||||
# mytimer.timer
|
||||
|
||||
[Unit]
|
||||
Description=Run Timer Unit
|
||||
[Timer]
|
||||
OnActiveSec=1s
|
||||
OnUnitActiveSec=10s
|
||||
Persistent=true
|
||||
Unit=mytask.service
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Tıpkı "cron" mekaznizmasında olduğu gibi bir işin belli bir tarihte ve zamanda yapılmasını da sağlayabiliriz. Bunun için [Timer]
|
||||
bölümündeki "OnCalender" direktifi kullanılmaktadır. Aşağıda çeşitli giriş örnekleri verilmiştir:
|
||||
|
||||
OnCalendar=15:00
|
||||
OnCalendar=Mon 08:00
|
||||
OnCalendar=1 12:00
|
||||
OnCalendar=1 Jan 00:00
|
||||
|
||||
Örneğin:
|
||||
|
||||
[Unit]
|
||||
Description=Run MyTask Service Every Minute
|
||||
[Timer]
|
||||
OnCalendar=22:24
|
||||
Persistent=true
|
||||
AccuracySec=1
|
||||
Unit=mytask.service
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
Burada her gün saat 22:24'te söz konusu servis çalıştırılacaktır.
|
||||
|
||||
Zamanlayıcı birim hakkındaki ayrıntılar için dokümanlara başvurabilirsiniz.
|
||||
|
||||
Tabii yine bu timer mekanizmasının boot zamanında otomatik devreye girmesi isteniyorsa buradaki servis birimi ile birlikte
|
||||
enable edilmeleri gerekir. Örneğin:
|
||||
|
||||
$ sudo systemctl enable mytimer.timer
|
||||
$ sudo systemctl enable mytask.service
|
||||
|
||||
O anda aktif olan (start edilmiş olan) timer birimlerinin listesini almak için "systemctl "list-timer" komutu kullanılabilir.
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
@ -9110,6 +9361,16 @@ void exit_sys(const char *msg)
|
|||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14785,7 +14785,7 @@ print(f'Prediction accuracy: {hit_count / count}')
|
|||
|
||||
Biz bu dizinden resimleri aşağıdaki gibi Dataset biçiminde oluşturabiliriz:
|
||||
|
||||
dataset = image_dataset_from_directory('Images', label_mode='binary', image_size=(128, 128), batch_size=1)
|
||||
dataset = image_dataset_from_directory('Images', label_mode='binary', image_size=(128, 128), batch_size=32)
|
||||
|
||||
Artık image_dataset_from_dirictory fonksiyonuyla elde ettiğimiz Dataset nesnesini daha önce görmüş olduğumuz parçalı verilerle
|
||||
eğitimde kullanabiliriz. Bir Dataset nesnesi içerisindeki bilgiler sınıfın take isimli metoduyla elde edilebilmektedir.
|
||||
|
@ -14801,6 +14801,18 @@ print(f'Prediction accuracy: {hit_count / count}')
|
|||
10 kez dolaşabiliriz. Aşağıda ilgili dizindeki 10 resmi görüntüleyen bir örnek verilmiştir.
|
||||
|
||||
Biz image_dataset_from_directory fonksiyonunu yalnız fit işlemlerinde değil, test ve kestirim işlemlerinde de kullanabiliriz.
|
||||
Fonksiyonun seubset parametresi 'training', 'validation' ya da 'both' biçiminde girilebilmektedir. 'training' eldeki resim
|
||||
kümesindenki bir grup resmin eğitim amacıylai 'validation' ise sınama amacıyla kullanılacağını belirtmektedir. Ancak
|
||||
subset parametresi girildiğinde validation_split paranetresinin ve seed parametresinin de girilmesi gerekmektedir. Örneğin
|
||||
biz eğitim ve sınama kümeleri için ayrışırmayı şöyle yapabiliriz:
|
||||
|
||||
training_dataset = image_dataset_from_directory('Images', label_mode='binary',
|
||||
image_size=(128, 128), subset='training', seed=123, validation_split=0.2, batch_size=32)
|
||||
|
||||
validation_dataset = image_dataset_from_directory('Images', label_mode='binary',
|
||||
image_size=(128, 128), subset='training', seed=123, validation_split=0.2, batch_size=32)
|
||||
|
||||
Bu biçimdeki kullanımda seed değerlerinin aynı girilmesi gerekmektedir.
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
import tensorflow
|
||||
|
@ -20005,47 +20017,421 @@ for presult in predict_result[:, 0]:
|
|||
az yer kaplayacak biçimde tasarlanmıştır.
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
76. Ders - 02/11/2024 - Cumartesi
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
Pekiyi yukarıdaki paragrafta açıkladığımız dosyalar ve formatlar Keras içerisinden nasıl kullanılmaktadır?
|
||||
|
||||
- Biz daha önce ".h5 dosyalarını ve .keras uzantılı dosyaları Sequential model sınıfının save metodu ile oluşturmuştuk. İşte
|
||||
bu dosyalar ve içerisindeki modeller tensorflow.keras.models modülündeki load_model fonksiyonuyla geri yüklenebilmektedir.
|
||||
- Biz daha önce ".h5" dosyalarını ve ".keras" uzantılı dosyaları Sequential model sınıfının save metodu ile oluşturmuştuk.
|
||||
İşte bu dosyalar ve içerisindeki modeller tensorflow.keras.models modülündeki load_model fonksiyonuyla geri yüklenebilmektedir.
|
||||
Zaten biz bu fonksiyonu daha önce de kullanmıştır. O halde elimizde ".h5" uzantılı ya da ".keras" uzantılı bir model varsa
|
||||
biz bunu bu load_model fonksiyonuyla yükeleyebiliriz. Bu fonksiyon bize bir Model nesnesi vermektedir. Örneğin:
|
||||
|
||||
model = load_model('iris.keras')
|
||||
|
||||
BURADA KALDIK
|
||||
- <pb içerisindeki SavedModel formatındaki bilgilerin Keras'tan kullanılması eklenecek>
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
Resim tanıma ve resimsel işlemler ve metinsel işlemler işlemler için son 10 yılda "vanishing gradient" denilen problemi
|
||||
azaltmak amacıyla çeşitli derin ağ mimarileri önerilmiştir. Bu mimarilerin dayandığı temel bira karmaşıktır. Ancak son yıllarda
|
||||
bu mimariler pek çok endüstriyel uygulamada tercih edilmektedir. Örneğin "denset" ya da "resnet" denilen mimariler resim tanımada
|
||||
en yaygın kullanılanlar arasındadır. Bu mimariler pek çok katman içedği için uygulamacı tarafından oluşturulması zahmetli mimarilerdir.
|
||||
İşte bu nedenle bu popüler ve yeni derin ağ mimarileri tensorflow.keras.aplication modülünde hazır bir biçimde bulundurulmaktadır.
|
||||
Hazır bulundurulan bu mimarilere ilişkin modeller genel olarak çıktı katmanı içermezler. Bu modellerde çıktı katmanları uygulamacı tarafından fonksiyonel biçimde
|
||||
oluşturulmaktadır. Siz de rsim tanıma ve sınıflandırma tarzı uygulamalr için densnet gibi resnet gibi hazır mimarileri kullanabilirsiniz.
|
||||
Kers içerisinde hazır bulundurulan bu modellerin kullanımları birbirlerine çok benzemektedir. İstenirse bu modeller daha önce eğitilmiş (predefined)
|
||||
ağırlık değerleriyle de kullanılablmektedir.
|
||||
Örneğin kaggle.com sitesindeki Models sekmesine girip "framework" için Keras seçildiğinde karşımıza çeşitli amaçlarla
|
||||
başkaları tarafından oluşturulmuş olan eğitilmiş ya da eğitilmemiş modeller çıkacaktır. Biz framework olarak Keras'ı
|
||||
seçtiğimiz için genellikle buradaki model dosyaları ".keras" uzantılı biçimde karşımıza çıkacaktır. Biz de bu dosyaları
|
||||
indirip yukarıda belirttiğimiz gibi load_model fonksiyonuyla yükleyebiliriz. Örneğin aşağıdaki siteden modeli indirdiğimizde
|
||||
"ResNet50.keras" isminde bir dosya elde etmiş olacağız:
|
||||
|
||||
Yukarıda sözünü ettiğimiz bu hazır modeller oldukça derin bir mimariye sahiptir. Dolayısıyla bu modellerin eğitilmesi daha önce yaptığımız
|
||||
modellere göre daha fazla zaman almaktadır. Eğitim için saatlerce zaman gerekebilmektedir.
|
||||
https://www.kaggle.com/models/paripatel2709/resnet
|
||||
|
||||
Örneğin biz popüler bir mimari olan densenet121'i kullanmak isteyelim. Bunun için önce bir DenseNet121 nesnesi yaratılır:
|
||||
Bu dosyayı da yularıda belirttiğimiz gibi load_model fonksiyonuyla yükleyebiliriz:
|
||||
|
||||
from tensorflow.keras.models import load_model
|
||||
|
||||
model = load_model('ResNet50.keras')
|
||||
model.summary()
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
from tensorflow.keras.models import load_model
|
||||
|
||||
model = load_model('ResNet50.keras')
|
||||
model.summary()
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
Şimdi Keras içerisinde tensorflow.keras.applications paketinde hazır bir biçimde bulunan modellerin nasıl yüklenerek
|
||||
kendi amaçlarımız doğrultusunda kullanılabileceğine bazı örnekler verelim.
|
||||
|
||||
Resim sınıflandırma ve anlamlandırmada kabul görmüş olan en önemli modellerden ikisi ResNet ve VGG modelleridir. Bu modeller
|
||||
onlarca katmana sahip olan çok ayrıntılı modellerdir. Biz burada bu modellerin iç yapısı üzerinde durmayacağız. Ancak bu
|
||||
modelleri açıklayan pek çok kaynak bulunmaktadır.
|
||||
|
||||
Keras içeisindeki ResNet modellerinin yanında bazı sayılar bulunmaktadır. Örneğin ResNet50, ResNet101, ResNet152 gibi. Bu
|
||||
sayılar modelin katman sayısı ile ilgilidir. Yüksek sayılarda daha fazla katman vardır. Dolayısıyla daha fazla eğitilebilir
|
||||
parametre bulunmaktadır. Yukarıda sözünü ettiğimiz bu hazır modeller oldukça derin bir mimariye sahiptir. Dolayısıyla bu
|
||||
modellerin eğitilmesi daha önce yaptığımız modellere göre daha fazla zaman almaktadır. Eğitim için saatlerce zaman
|
||||
gerekebilmektedir.
|
||||
|
||||
Örneğin biz popüler bir mimari olan DenseNet121'i kullanmak isteyelim. Bunun için önce bir DenseNet121 nesnesi yaratılır.
|
||||
DenseNet121 sınıfının __init__ metodunun parametrik yapısı şöyledri:
|
||||
|
||||
tf.keras.applications.DenseNet121(
|
||||
include_top=True,
|
||||
weights='imagenet',
|
||||
input_tensor=None,
|
||||
input_shape=None,
|
||||
pooling=None,
|
||||
classes=1000,
|
||||
classifier_activation='softmax'
|
||||
)
|
||||
|
||||
Metodun birinci parametresi True geçilirse model girdi ve çıktı katmanıyla bitlikte bir bütün olarak kullanılır. Genellikle
|
||||
bu parametre False biçimde geçilir. Çünkü genellikle uygulamacılar modeli bir bütün olarak kullanmak yerine modeli kendi
|
||||
amaçları doğrultusunda kullanırlar ve ince ayar (fine-tuning) yapmak isterler. Metodun weights parametresi önceden eğitimle
|
||||
elde edilmiş olan ağırlıkların kullanılıp kullanılmayacağını belirtmektedir. Burada biz nereden elde edilen ağırlıkların
|
||||
kullanılacağını belirtiriz. Bu parametre default olarak "imagenet" biçiminde girilmiştir. ImageNet resimlerden oluşan dev
|
||||
bir veritabanıdır. Bu veritabanı özellikle makine öğrenmesinde resimlerle ilgili işlemler yapan modellerin eğitilmesinde
|
||||
yaygın biçimde kullanılmaktadır. Burada weights parametresi None geçilirse model eğitilmemiş bir biçimde kullanılır. Yani
|
||||
bu durumda tüm eğitimi uygulamacının kendisi yapmak zorundadır. Bu parametreye ağırlıkların bulunduğu desteklenen bir formattaki
|
||||
dosyanın yol ifadesi de geçirilebilmektedir. Metodun input_shape parametresi girdi resimlerinin boyutunu belirtmektedir. Burada
|
||||
önemli bir noktayı da belirtmek istiyoruz. Biz "ImageNet" veritabanındaki resimlerden elde edilen ağırlıkları kullanmak istediğimizi
|
||||
düşünelim. Bu veritabanındaki eğitimler (224, 224, 3) boyutundaki resimlerle yapılmıştır. Eğer bizim resimlerimiz bu boyuttan
|
||||
büyük ise ya da küçük ise dönüştürme sırasında performans kayıpları oluşabilecektir. Bu nedenle bu sınıfları kullanıyorsanız
|
||||
eğitimin yapıldığı orijinal resim boyutuna ne kadar yakın bir boyut seçerseniz performans daha daha iyi olacaktır. Örneğin
|
||||
biz CIFAR-100 örneği için ResNet121 modelini kullanmak isteyelim. Ancak önceden eğitilmiş ağırlık değerleri yerine modelimizi
|
||||
biz kendi verilerimizle eğitmek isteyelim. Bu durumda ResNet121 nesnesi aşağıdaki gibi yaratılabilir. Eğer bu katmanın önünde
|
||||
bir girdi katmanı bulundurulacaksa bu durumda image_shape parametresi hiç girilmeyebilir.
|
||||
|
||||
from tensorflow.keras.applications.densenet import DenseNet121
|
||||
|
||||
dn121 = DenseNet121(include_top=False, weights=None, input_shape=(32, 32, 3))
|
||||
|
||||
Nesne yaratılırken girdi resimlerinin boyutları input_shape parametresiyle belirtilmektedir. weights parametresi ya None girilebilir ya da
|
||||
"imagenet" girilebilir. Eğer bu parametre "imagenet" olarak girilirse model eğitilirken ağırlık değerleri rastgele değerlerle değil "imagenet"
|
||||
veritabanındaki önceden eğitilmiş olan bir modelin ağırlık değerleri ile başlatılacaktır. include_top parametresi modelin tepesinde bir Dense katmanın
|
||||
bulundurulup bulundurulmayacağına ilişkindir. Bu parametre False geçilebilir.
|
||||
Burada include_top parametresi False geçildiği için modelin çıktı katmanını bizim oluşturmamız gerekir.
|
||||
|
||||
Pekiyi biz bu hazır modeli nasıl Cifar-100 örneğinde kullanabiliriz? Daha önceden de belirttiğimiz gibi bu tür hazır modellerin
|
||||
Keras'ta fonksiyonel bir biçimde kullanılması tavsiye edilmektedir. Ancak biz burada önce klasik Sequential modeli kullanacağız
|
||||
sonra fonksiyonel model ile örnek vereceğiz.
|
||||
|
||||
Önceden oluşturulmuş Keras modeli adeta bir katman gibi Sequential modele eklenmelidir. Zaten Model sınıflarının kendisi
|
||||
de aynı zamanda bir katman gibi kullanılabilmektedir. (Model sınıfın da aynı zamanda çoklu bir biçimde Layer sınıfından
|
||||
türetilmiş olduğunu anımsayınız.)
|
||||
|
||||
model = Sequential(name='ResNet121-Cifar-100')
|
||||
model.add(Input((32, 32, 3), name='Input'))
|
||||
model.add(DenseNet121(include_top=False, weights=None, input_shape=(32, 32, 3), name='DenseModelTest'))
|
||||
model.add(Reshape((-1, )))
|
||||
model.add(Dense(128, activation='relu', name='Dense-1'))
|
||||
model.add(Dense(128, activation='relu', name='Dense-2'))
|
||||
model.add(Dense(100, activation='softmax', name='Output'))
|
||||
model.summary()
|
||||
|
||||
Burada önce modele bir Input katmanı eklenmiştir. Sonra da Dense121 modelinin tamamı adeta bir katman gibi modele eklenmiştir.
|
||||
Biz ayrıca bu hazır modelin ucuna iki Dense katman ve bir de çıktı katmanı ekledik. Artık modeli compile edip fit işlemi
|
||||
uygulayabiliriz. Bu örnekte önceden eğitilmiş modelin ağırlıklarını kullanmadığımıza dikkat ediniz. Burada aslında biz Dense121
|
||||
nesnesi yaratılırken input_shape parametresini girmeyebilirdik. Çünkü modelimizin bir girdi katmanı olduğu için bu sınıf
|
||||
bu girdi katmanından hareketle zaten input_shape parametresini belirleyebilmektedir. Eğer biz hem girdi katmanı kullanıyorsak
|
||||
hem de bu input_shape parametresine argüman giriyorsak bu durumda bu iki resim boyutunun aynı olması gerekmektedir.
|
||||
|
||||
Biz yukarıdaki örnekte yalnızca modelin kendisinden faydalanmak istedik. Tabii öncedne eğitilmiş modelin ağırlıklaırnı da
|
||||
kullanabilirz. Bunun için Dense121 nesnesinde weights parametresi 'imagenet" biçiminde geçilmelidir:
|
||||
|
||||
model.add(DenseNet121(include_top=False, weights='imagenet', input_shape=(32, 32, 3), name='DenseModelTest'))
|
||||
|
||||
Biz DenseNet121 katmanında (bu aslında aynı zamanda katmanlardan oluşan model nesnesidir) önceden elde edilmiş ağırlıkları
|
||||
kullandığımızda artık kendi verilerimizle yaptığımız eğitimde bu katmanların eğitime dahil edilmemesini isteyebiliriz.
|
||||
Çünkü "imagenet" o kadar büyük bir veritabanıdır ki bizim kendi resimlerimizin bu ağırlıkları fayda sağlayacak değiştirmesi
|
||||
de oldukça güçtür. Bu nedenle biz bu katmanı eğitim işleminden muaf hale getirmek için katman sınıflarının trainable
|
||||
parametresinden faydalanabiliriz. Örneğin:
|
||||
|
||||
model.add(DenseNet121(include_top=False, weights='imagenet', input_shape=(32, 32, 3), trainable=False, name='DenseModelTest'))
|
||||
|
||||
Burada artık trainable=False argümanı kullanıldığı için kendi verilerimiz ile eğitim yapılırken bu katmandaki tüm katmanlar
|
||||
eğitimden muaf tutulacaktır. Tabii test ve kestirim işlemlerinde kullanılacaktır. Bu hazır katmanı eğitimden muaf tutmanın
|
||||
bir avantajı da eğitim sırasında geçen zamanın kısaltılmasıdır. Tabii buradaki DenseNet121 katmanın içerisinde katmanların da
|
||||
yalnızca bir bölümü için trainable=False işlemi de yapılabilir.
|
||||
|
||||
Daha öncedne de belirttiğimiz gibi hazır ağılıkların kullanılmasından sonra ayrıca modeli birkaç katman ekleyerek kendi
|
||||
veri kümemiz için eğitmeye "ince ayar (fine-tuning)" yapılması denilmektedir.
|
||||
|
||||
Dense121 sınıfının ğırlıkları toplam 1000 tane sınıf için uygulanan eğitimle oluşturulmuştur. Bu 1000 tane sınıf içerisinde
|
||||
pek çok farklı temadan resimler bulunmaktadır.
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
Aşağıda DenseNet121 sınıfının CIFAR-100 örneğinde Sequantial modeli ile kullanımına bir örnek verilmiştir. Bu örnekte
|
||||
önceden eğitilmiş modelin ağırlıkları kullanılmıştır. Burada verdiğimiz örnekten elde edilen başarı %X civarındadır.
|
||||
Buradaki önemli bir handikap CIFAR-100 resimlerinin gerçek eğitimde kullanılan 224x224x3'lük resimlere göre çok küçük
|
||||
olmasıdır. Resim büyütülüp orijinal boyuta yaklaştırıldıkaç performans da artmaktadır.
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
from tensorflow.keras.applications.densenet import DenseNet121
|
||||
from tensorflow.keras import Sequential
|
||||
from tensorflow.keras.layers import Input, Reshape, Dense
|
||||
|
||||
EPOCHS = 10
|
||||
|
||||
from tensorflow.keras.datasets import cifar100
|
||||
|
||||
(training_dataset_x, training_dataset_y), (test_dataset_x, test_dataset_y) = cifar100.load_data()
|
||||
|
||||
scaled_training_dataset_x = training_dataset_x / 255
|
||||
scaled_test_dataset_x = test_dataset_x / 255
|
||||
|
||||
from tensorflow.keras.utils import to_categorical
|
||||
|
||||
ohe_training_dataset_y = to_categorical(training_dataset_y)
|
||||
ohe_test_dataset_y = to_categorical(test_dataset_y)
|
||||
|
||||
class_names = [
|
||||
'apple', 'aquarium_fish', 'baby', 'bear', 'beaver', 'bed', 'bee', 'beetle',
|
||||
'bicycle', 'bottle', 'bowl', 'boy', 'bridge', 'bus', 'butterfly', 'camel',
|
||||
'can', 'castle', 'caterpillar', 'cattle', 'chair', 'chimpanzee', 'clock',
|
||||
'cloud', 'cockroach', 'couch', 'crab', 'crocodile', 'cup', 'dinosaur',
|
||||
'dolphin', 'elephant', 'flatfish', 'forest', 'fox', 'girl', 'hamster',
|
||||
'house', 'kangaroo', 'keyboard', 'lamp', 'lawn_mower', 'leopard', 'lion',
|
||||
'lizard', 'lobster', 'man', 'maple_tree', 'motorcycle', 'mountain', 'mouse',
|
||||
'mushroom', 'oak_tree', 'orange', 'orchid', 'otter', 'palm_tree', 'pear',
|
||||
'pickup_truck', 'pine_tree', 'plain', 'plate', 'poppy', 'porcupine',
|
||||
'possum', 'rabbit', 'raccoon', 'ray', 'road', 'rocket', 'rose',
|
||||
'sea', 'seal', 'shark', 'shrew', 'skunk', 'skyscraper', 'snail', 'snake',
|
||||
'spider', 'squirrel', 'streetcar', 'sunflower', 'sweet_pepper', 'table',
|
||||
'tank', 'telephone', 'television', 'tiger', 'tractor', 'train', 'trout',
|
||||
'tulip', 'turtle', 'wardrobe', 'whale', 'willow_tree', 'wolf', 'woman', 'worm'
|
||||
]
|
||||
|
||||
model = Sequential(name='ResNet121-Cifar-100')
|
||||
model.add(Input((32, 32, 3), name='Input'))
|
||||
model.add(DenseNet121(include_top=False, weights='imagenet', input_shape=(32, 32, 3), name='DenseModelTest'))
|
||||
model.add(Reshape((-1, )))
|
||||
model.add(Dense(128, activation='relu', name='Dense-1'))
|
||||
model.add(Dense(128, activation='relu', name='Dense-2'))
|
||||
model.add(Dense(100, activation='softmax', name='Output'))
|
||||
|
||||
model.summary()
|
||||
|
||||
model.compile('rmsprop', loss='categorical_crossentropy', metrics=['categorical_accuracy'])
|
||||
|
||||
hist = model.fit(scaled_training_dataset_x, ohe_training_dataset_y, batch_size=32, epochs=EPOCHS, validation_split=0.2)
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.figure(figsize=(14, 6))
|
||||
plt.title('Epoch - Loss Graph', fontsize=14, pad=10)
|
||||
plt.plot(hist.epoch, hist.history['loss'])
|
||||
plt.plot(hist.epoch, hist.history['val_loss'])
|
||||
plt.legend(['Loss', 'Validation Loss'])
|
||||
plt.show()
|
||||
|
||||
plt.figure(figsize=(14, 6))
|
||||
plt.title('Categorcal Accuracy - Validation Categorical Accuracy', fontsize=14, pad=10)
|
||||
plt.plot(hist.epoch, hist.history['categorical_accuracy'])
|
||||
plt.plot(hist.epoch, hist.history['val_categorical_accuracy'])
|
||||
plt.legend(['Categorical Accuracy', 'Validation Categorical Accuracy'])
|
||||
plt.show()
|
||||
|
||||
eval_result = model.evaluate(scaled_test_dataset_x , ohe_test_dataset_y, batch_size=32)
|
||||
for i in range(len(eval_result)):
|
||||
print(f'{model.metrics_names[i]}: {eval_result[i]}')
|
||||
|
||||
# prediction
|
||||
|
||||
import glob
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
count = 0
|
||||
hit_count = 0
|
||||
for path in glob.glob('Predict-Pictures/*.*'):
|
||||
image = plt.imread(path)
|
||||
scaled_image = image / 255
|
||||
model_result = model.predict(scaled_image.reshape(-1, 32, 32, 3), verbose=0)
|
||||
predict_result = np.argmax(model_result)
|
||||
fname = os.path.basename(path)
|
||||
real_class = fname[:fname.index('-')]
|
||||
predict_class = class_names[predict_result]
|
||||
print(f'Real class: {real_class}, Predicted Class: {predict_class}, Path: {path}')
|
||||
|
||||
if real_class == predict_class:
|
||||
hit_count += 1
|
||||
count += 1
|
||||
|
||||
print('-' * 20)
|
||||
print(f'Prediction accuracy: {hit_count / count}')
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
Şimdi de DenseNet121 sınıfının fonksiyonel bir modelde nasıl kullanılacağı üzerinde duralım. Önceki konularda da bellirttiğimiz
|
||||
gibi uygulamacılar aslında öncedne hazırlanmış bu modelleri genellikle fonksiyonel model içerisinde kullanmaktadır.
|
||||
Fonksiyonel modelde anımsayacağınız gibi sürekli çıktı girdiye verilerek katmanlar oluşturuluyordu:
|
||||
|
||||
inputs = Input((32, 32, 3), name='Input')
|
||||
x = DenseNet121(include_top=False, weights='imagenet', input_shape=(32, 32, 3), name='DenseModelTest')(inputs)
|
||||
x = Reshape((-1, ))(x)
|
||||
x = Dense(128, activation='relu', name='Dense-1')(x)
|
||||
x = Dense(128, activation='relu', name='Dense-2')(x)
|
||||
outputs = Dense(100, activation='softmax', name='Output')(x)
|
||||
|
||||
model = Model(inputs, outputs)
|
||||
|
||||
Burada Model nesnesinin kullanıldığına dikkat ediniz. Anımsanacağı gibi Model nesnesi oluşturulurken ona girdi ve çıktı
|
||||
katmanlarının verilmesi gerekiyordu. Geri kalan işlemler artık daha önce yaptığımız gibi devam ettirilebilir.
|
||||
|
||||
Aşağıda aynı ResNet121 CIFAR örneğinin fonksiyonel modelle gerçekleştirime ilişkin örnek verilmiştir.
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
from tensorflow.keras.applications.densenet import DenseNet121
|
||||
from tensorflow.keras import Model
|
||||
from tensorflow.keras.layers import Input, Reshape, Dense
|
||||
|
||||
EPOCHS = 10
|
||||
|
||||
from tensorflow.keras.datasets import cifar100
|
||||
|
||||
(training_dataset_x, training_dataset_y), (test_dataset_x, test_dataset_y) = cifar100.load_data()
|
||||
|
||||
scaled_training_dataset_x = training_dataset_x / 255
|
||||
scaled_test_dataset_x = test_dataset_x / 255
|
||||
|
||||
from tensorflow.keras.utils import to_categorical
|
||||
|
||||
ohe_training_dataset_y = to_categorical(training_dataset_y)
|
||||
ohe_test_dataset_y = to_categorical(test_dataset_y)
|
||||
|
||||
class_names = [
|
||||
'apple', 'aquarium_fish', 'baby', 'bear', 'beaver', 'bed', 'bee', 'beetle',
|
||||
'bicycle', 'bottle', 'bowl', 'boy', 'bridge', 'bus', 'butterfly', 'camel',
|
||||
'can', 'castle', 'caterpillar', 'cattle', 'chair', 'chimpanzee', 'clock',
|
||||
'cloud', 'cockroach', 'couch', 'crab', 'crocodile', 'cup', 'dinosaur',
|
||||
'dolphin', 'elephant', 'flatfish', 'forest', 'fox', 'girl', 'hamster',
|
||||
'house', 'kangaroo', 'keyboard', 'lamp', 'lawn_mower', 'leopard', 'lion',
|
||||
'lizard', 'lobster', 'man', 'maple_tree', 'motorcycle', 'mountain', 'mouse',
|
||||
'mushroom', 'oak_tree', 'orange', 'orchid', 'otter', 'palm_tree', 'pear',
|
||||
'pickup_truck', 'pine_tree', 'plain', 'plate', 'poppy', 'porcupine',
|
||||
'possum', 'rabbit', 'raccoon', 'ray', 'road', 'rocket', 'rose',
|
||||
'sea', 'seal', 'shark', 'shrew', 'skunk', 'skyscraper', 'snail', 'snake',
|
||||
'spider', 'squirrel', 'streetcar', 'sunflower', 'sweet_pepper', 'table',
|
||||
'tank', 'telephone', 'television', 'tiger', 'tractor', 'train', 'trout',
|
||||
'tulip', 'turtle', 'wardrobe', 'whale', 'willow_tree', 'wolf', 'woman', 'worm'
|
||||
]
|
||||
|
||||
inputs = Input((32, 32, 3), name='Input')
|
||||
x = DenseNet121(include_top=False, weights='imagenet', input_shape=(32, 32, 3), name='DenseModelTest')(inputs)
|
||||
x = Reshape((-1, ))(x)
|
||||
x = Dense(128, activation='relu', name='Dense-1')(x)
|
||||
x = Dense(128, activation='relu', name='Dense-2')(x)
|
||||
outputs = Dense(100, activation='softmax', name='Output')(x)
|
||||
|
||||
model = Model(inputs, outputs)
|
||||
|
||||
model.compile('rmsprop', loss='categorical_crossentropy', metrics=['categorical_accuracy'])
|
||||
|
||||
hist = model.fit(scaled_training_dataset_x, ohe_training_dataset_y, batch_size=32, epochs=EPOCHS, validation_split=0.2)
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.figure(figsize=(14, 6))
|
||||
plt.title('Epoch - Loss Graph', fontsize=14, pad=10)
|
||||
plt.plot(hist.epoch, hist.history['loss'])
|
||||
plt.plot(hist.epoch, hist.history['val_loss'])
|
||||
plt.legend(['Loss', 'Validation Loss'])
|
||||
plt.show()
|
||||
|
||||
plt.figure(figsize=(14, 6))
|
||||
plt.title('Categorcal Accuracy - Validation Categorical Accuracy', fontsize=14, pad=10)
|
||||
plt.plot(hist.epoch, hist.history['categorical_accuracy'])
|
||||
plt.plot(hist.epoch, hist.history['val_categorical_accuracy'])
|
||||
plt.legend(['Categorical Accuracy', 'Validation Categorical Accuracy'])
|
||||
plt.show()
|
||||
|
||||
eval_result = model.evaluate(scaled_test_dataset_x , ohe_test_dataset_y, batch_size=32)
|
||||
for i in range(len(eval_result)):
|
||||
print(f'{model.metrics_names[i]}: {eval_result[i]}')
|
||||
|
||||
# prediction
|
||||
|
||||
import glob
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
count = 0
|
||||
hit_count = 0
|
||||
for path in glob.glob('Predict-Pictures/*.*'):
|
||||
image = plt.imread(path)
|
||||
scaled_image = image / 255
|
||||
model_result = model.predict(scaled_image.reshape(-1, 32, 32, 3), verbose=0)
|
||||
predict_result = np.argmax(model_result)
|
||||
fname = os.path.basename(path)
|
||||
real_class = fname[:fname.index('-')]
|
||||
predict_class = class_names[predict_result]
|
||||
print(f'Real class: {real_class}, Predicted Class: {predict_class}, Path: {path}')
|
||||
|
||||
if real_class == predict_class:
|
||||
hit_count += 1
|
||||
count += 1
|
||||
|
||||
print('-' * 20)
|
||||
print(f'Prediction accuracy: {hit_count / count}')
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
Şimdi de başkaları tarafından hazırlanmış ve eğitilmiş olan modellerin kulanılmasına ilişkin örnekler verelim. Biz daha
|
||||
önce de bu amaçla çeşitli toplulukların oluşturuldupundan bahsetmiştik. Bunların en bilineni Kaggle denilen topluluktur.
|
||||
Ancak çeşitli framework'ler de "hub" adı altında Kaggle benzeri topluluklar oluşturmuştur. Örneğin Tensorflow için kişilerin
|
||||
modellerini paylaşabileceği "tensorflow hub" denilen bir topluluk vardır. Fakat daha önceden de belirttiğimiz gibi bu
|
||||
topluluk kendi sitesini kapatıp Kaggle'a geçmiştir. Bu tür topluluklardaki kişiler modellerini daha önce ele aldığımız
|
||||
formatlarda sunmaktadır. Ancak bu formattaki modellerde bazı sorunlar da ortaya çıkabilmektedir. Keras için önemli sorunalardan
|
||||
biri zamanında save edilmiş modllerin güncel versiyonlarla uyuşmamasından kaynaklanan sorunlardır.
|
||||
|
||||
Keras işin başında Tensorflow'dan bağımsız ve birden fazla backend'i destekleyecek biçimde tasarlanmıştı. Sonraları
|
||||
Tensorflow ekibi bu Keras arayüzünü bünyesine kattı ve yeniden Tensorflow ile bağlantılı bir biçimde Keras'ı yazdı. Böylece
|
||||
biribirine benzeyen iki farklı Keras gerçekleştirimi ortaya çıktı. Ancak daha sonraları eski Keras ekibi Tensorflow desteğini
|
||||
kuvvetlendirdi. Tensorflow da bu yeni Keras ile (Keras'ın 'lü versiyonları) kendisini senkronize etmeye başladı. Yani
|
||||
Tensorflow'daki son yıllardaki Keras kütüphanesi orijinal Keras ile aynı duruma gelmiştir. Ancak bir süre önce durum böyle
|
||||
değildi. İşte bu süreç içerisinde Tensorflow Keras le oluşturulup save edilmiş modeller yeni Tensorflow Keras'ta çalışmaz
|
||||
hale gelebilmektedir. Ancak Tensorflow'daki orijinal Keras ile senkronize edilmiş yeni Keras'ın yanı sıra eski Keras'ı da
|
||||
muhafaza etmektedir. Bu eski Keras'ta tf_keras ismiyle erişilebilmektedir. Bu KEras'ı aşağıdaki gibi yükleyebilirsiniz:
|
||||
|
||||
pip install tf_keras
|
||||
|
||||
Bu eski Keras'la yeni Keras'ın kullanımı büyük ölçüde aynıdır. Biz eski Keras'ı kullanacaksak tf_keras ismiyle, yeni
|
||||
Keras'ı kullanacaksak tensorflow.keras ismiyle import uygulamalıyız.
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
Tensorflow Hub içerisine yerleştirilmiş olan modeller için bir URL'de oluşturulmaktadır. Bu modellerin doğrudan URL eşliğinde
|
||||
yüklenenip bir katman nesnesi haline getirilmesi için KerasLayer isimli bir sınıf bulundurulmuştur. Bu sınıfın __init__
|
||||
metodunun parametrik yapısı şöyledir:
|
||||
|
||||
hub.KerasLayer(
|
||||
handle,
|
||||
trainable=False,
|
||||
arguments=None,
|
||||
_sentinel=None,
|
||||
tags=None,
|
||||
signature=None,
|
||||
signature_outputs_as_dict=None,
|
||||
output_key=None,
|
||||
output_shape=None,
|
||||
load_options=None,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
Buradaki en önemli iki iarametre handle ve trainable parametreleridir. handle parametresinde modelin URL'si girilmektedir.
|
||||
trainable parametresi de modelin katmanlarının eğitime dahil edilip edilmeyeceğini belirtmektedir. Tabii yukarıda da
|
||||
belirttiğimiz gibi Tensorflow Hub artık tümden Kaggle'a taşınmış durumdadır. Biz söz konusu modelleri bu yöntemle kullanmak
|
||||
yerine model dosyasını Kaggle'dan indirip load_model fonksiyonuyla da yükleyebiliriz.
|
||||
|
||||
Tensorflow Hub'ı modelleri kolay yüklemek amacıyla kullanabilmek için ona gözgü olan kütüphaneyi de yüklememi,z gerekir.
|
||||
Yukarıda açıkladığımız KerasLayer sınıfı da aslında bu kütüphane içerisindedir. Kütüphaneyi şöyle yükleyebiliriz:
|
||||
|
||||
pip install tensorflow_hub
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
DenseNet121 nesnesi kendi içerisinde modelin katmanlarını tutmaktadır. Bu modelde çıktı katmanı yoktur. Ancak girdi katmanı vardır. Dolayısıyla
|
||||
programcı bu modelin temel kısmına çıktı katmanlarını eklemelidir. Sınıfın input örnek özniteliği bize girdi tensörünü, output parametresi ise modelin çıktı
|
||||
tensörünü vermektedir. Uygulamacı tipik olarak nesnenin output örnek özniteliğinden faydalanarak çıktı katmanını fonksiyonel biçimde modele bağlar.
|
||||
Örneğin:
|
||||
|
||||
from tensorflow.keras.applications.densenet import DenseNet121
|
||||
|
|
Loading…
Add table
Reference in a new issue