New version

This commit is contained in:
C ve Sistem Programcıları Derneği 2024-11-07 13:00:04 +03:00
parent c798b8e4e0
commit 7bd9db3454
3 changed files with 3550 additions and 1685 deletions

View file

@ -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

View file

@ -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)
ı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