Kurs içeriği (7 / 10)
  1. 1 Raspberry Pi ile Tanışma ve Kurulum 80 dk
  2. 2 Makine Öğrenmesi Nedir? İlk Modelimiz 80 dk
  3. 3 Kamera ve Görüntü İşlemenin Temelleri 80 dk
  4. 4 Görüntü Veri Seti Hazırlama 80 dk
  5. 5 Görüntü Tanıma Modeli Eğitip Pi’de Çalıştırma 120 dk
  6. 6 Mikrofon ve Sesin Temelleri 80 dk
  7. 7 Ses Veri Seti Hazırlama 80 dk
  8. 8 Ses Tanıma Modeli ile Sesli Komutlar 120 dk
  9. 9 Yapay Zekâyı Donanıma Bağlama: LED, Buzzer ve Servo 80 dk
  10. 10 Mini Proje: Akıllı Geri Dönüşüm Kutusu 160 dk

Raspberry Pi ile Makine Öğrenmesi: Görüntü ve Ses Tanıma · Ders 7 / 10

Ses Veri Seti Hazırlama

Uygulama ⏱ 80 dk

Sesli komut modelimiz için “aç”, “kapat”, arka plan gürültüsü ve bilinmeyen kelimelerden oluşan bir ses veri seti topluyoruz. Bir saniyelik kayıtları etiketli dosya adlarıyla kaydeden bir program yazıyor, sessiz, kırpılmış ya da yanlış biçimli kayıtları bulan bir kontrol programı çalıştırıyor ve veri setini Edge Impulse’a yüklüyoruz.

Bu derste

  • Sesli komut tanıma için sınıfları (komutlar, gürültü, bilinmeyen) gerekçesiyle belirler.
  • Bir saniyelik ses kayıtlarını etiketli dosya adlarıyla kaydeden bir program kullanır.
  • İyi bir ses veri setinin özelliklerini (konuşmacı çeşitliliği, denge, ortam) açıklar.
  • Sessiz, kırpılmış ve yanlış biçimli kayıtları bir Python programıyla bulur.
  • Veri setini Edge Impulse’a etiketleriyle birlikte yükler.

Malzemeler

  • Raspberry Pi 5 veya 4
  • USB mikrofon
  • Thonny ve yzenv sanal ortamı
  • Edge Impulse hesabı (studio.edgeimpulse.com)
  • Gönüllü konuşmacılar (sınıf arkadaşları)

Görüntü tanımada fotoğraflarla yaptığımız işi şimdi seslerle yapıyoruz. Kutumuz “aç” ve “kapat” komutlarını anlayacak. Bunun için önce modelin öğreneceği ses örneklerini toplamalıyız. İyi bir ses veri seti hazırlamak, iyi bir fotoğraf veri seti hazırlamaktan bile daha fazla özen ister.

Dersin Amacı ve Kazanımlar

Bu dersin sonunda:

  1. Sesli komut tanıma için sınıfları gerekçesiyle belirleyebileceksiniz.
  2. Bir saniyelik kayıtları etiketli dosya adlarıyla kaydeden bir program kullanabileceksiniz.
  3. İyi bir ses veri setinin özelliklerini açıklayabileceksiniz.
  4. Sessiz, kırpılmış ve yanlış biçimli kayıtları bir programla bulabileceksiniz.
  5. Veri setini Edge Impulse’a etiketleriyle birlikte yükleyebileceksiniz.

Gerekli Malzemeler

Malzeme Açıklama
Raspberry Pi ve USB mikrofon Ders 6’da test ettiğimiz düzenek. Modeli hangi mikrofonla kullanacaksak kayıtları da onunla yapıyoruz.
Edge Impulse hesabı studio.edgeimpulse.com adresinde ücretsiz hesap. Sınıf için öğretmenin açtığı bir proje kullanılabilir.
Konuşmacılar Farklı ses tonlarına sahip en az 5 gönüllü
Sessiz bir köşe ve gürültü kaynakları Komut kayıtları için sakin bir yer; gürültü sınıfı için sınıfın doğal sesleri, vantilatör, sandalye sesleri

💡 İpucu

Kayıtları modelin çalışacağı mikrofonla yapın. Bilgisayarın dahili mikrofonuyla kaydedilen seslerle eğitilen bir model, Pi’deki USB mikrofonla daha kötü çalışır; çünkü her mikrofon sesi biraz farklı “duyar”.

Hangi Sınıflara İhtiyacımız Var?

Yalnızca “ac” ve “kapat” sınıfları olsaydı model her sese bu ikisinden biriyle cevap vermek zorunda kalırdı. Sınıfta biri öksürse kutu açılabilirdi. Bu yüzden dört sınıf kullanıyoruz:

  • ac ve kapat: Komut kelimeleri.
  • gurultu: Kimse komut vermezken ortamın sesi: sınıf uğultusu, vantilatör, sandalye gıcırtısı, uzaktan gelen konuşmalar.
  • bilinmeyen: Komut olmayan başka kelimeler: “masa”, “kitap”, “merhaba”, “aç” kelimesine benzeyen “ağaç”, “saç” gibi.

“Bilinmeyen” sınıfı modele “her kelime komut değildir” demeyi öğretir. Görüntüdeki “bos” sınıfı neyse, seste “gurultu” ve “bilinmeyen” sınıfları odur.

Ses veri setinin klasör yapısı ve dosya adları

İyi Bir Ses Veri Setinin Kuralları

  1. Konuşmacı çeşitliliği: Kayıtların hepsi sizin sesinizden olursa model yalnızca sizi anlar. En az 5 farklı kişiden, kalın ve ince seslerden örnek toplayın.
  2. Söyleyiş çeşitliliği: Hızlı, yavaş, yüksek, alçak sesle; mikrofona yakın ve uzak söyleyin.
  3. Denge: Her sınıfta yaklaşık aynı sayıda kayıt olsun. Başlangıç için sınıf başına 50–100 kayıt hedefleyin.
  4. Sabit biçim: Bütün kayıtlar 16 000 Hz, mono ve 1 saniye olsun. Kelime bu bir saniyenin içine tam sığmalı; başı ya da sonu kesilmemeli.
  5. Gerçek ortam: Kutu sınıfta duracaksa kayıtların bir kısmını sınıfın normal gürültüsü içinde yapın.
  6. İzin ve gizlilik: Ses de kişisel bir veridir. Arkadaşlarınızın sesini kaydetmeden önce izin alın; kayıtlarda isim ya da kişisel bilgi söylenmesin.

Kayıt Programı

Program etiketi ve kayıt sayısını sorar. Her kayıttan önce Enter’a basmanızı bekler, sonra tam bir saniye kayıt yapar. Kaydın tepe değerine bakarak çok sessiz ya da kırpılmış kayıtları anında haber verir; böylece kötü bir kaydı hemen tekrarlayabilirsiniz.

# dosya: ses_topla.py  (1 saniyelik etiketli kayıtlar)
import sounddevice as sd
import numpy as np
import wave
import os

ORNEKLEME = 16000
SURE = 1.0
KLASOR = "veri_ses"
CIHAZ = None          # gerekirse Ders 6'daki cihaz numarası

etiket = input("Etiket (ac, kapat, gurultu, bilinmeyen): ").strip().lower()
adet = int(input("Kaç kayıt yapılacak? "))
hedef = os.path.join(KLASOR, etiket)
os.makedirs(hedef, exist_ok=True)
mevcut = len([f for f in os.listdir(hedef) if f.endswith(".wav")])

for i in range(adet):
    input(f"[{i + 1}/{adet}] Enter'a basın ve hemen söyleyin...")
    kayit = sd.rec(int(SURE * ORNEKLEME), samplerate=ORNEKLEME,
                   channels=1, dtype="int16", device=CIHAZ)
    sd.wait()
    dosya = os.path.join(hedef, f"{etiket}.{mevcut + i + 1:03d}.wav")
    with wave.open(dosya, "wb") as f:
        f.setnchannels(1)
        f.setsampwidth(2)
        f.setframerate(ORNEKLEME)
        f.writeframes(kayit.tobytes())
    tepe = int(np.abs(kayit).max())
    if tepe < 1000:
        durum = "çok sessiz, tekrar deneyin"
    elif tepe >= 32000:
        durum = "kırpılmış, biraz uzaklaşın"
    else:
        durum = "iyi"
    print(f"    {dosya} (tepe {tepe}: {durum})")

Örnek çıktı: (Raspberry Pi 5 ve USB mikrofonla örnek çalıştırma; kullanıcının yazdığı değerler de görünüyor.)

Etiket (ac, kapat, gurultu, bilinmeyen): ac
Kaç kayıt yapılacak? 3
[1/3] Enter'a basın ve hemen söyleyin...
    veri_ses/ac/ac.001.wav (tepe 12051: iyi)
[2/3] Enter'a basın ve hemen söyleyin...
    veri_ses/ac/ac.002.wav (tepe 640: çok sessiz, tekrar deneyin)
[3/3] Enter'a basın ve hemen söyleyin...
    veri_ses/ac/ac.003.wav (tepe 15893: iyi)

Dosya adlarına dikkat edin: ac.001.wav. Etiket ile numara arasında alt çizgi değil nokta var. Edge Impulse, dosyaları yüklerken etiketi dosya adının ilk noktasına kadar olan kısımdan okuyabilir. Bu küçük ayrıntı sayesinde yüzlerce dosyayı tek tek etiketlemek zorunda kalmayacağız.

“gurultu” sınıfı için bir şey söylemeyin; yalnızca Enter’a basıp ortamın sesini kaydedin. Kayıt alırken vantilatörü açmak, sandalye çekmek gibi farklı gürültüler de ekleyin.

Veri Setini Kontrol Etme

Kayıtları dinleyerek tek tek kontrol etmek çok uzun sürer. Önce mikrofonunuz yoksa da çalışabilmeniz için yapay bir deneme seti üretelim. İçine bilerek dört sorunlu kayıt koyuyoruz:

# dosya: deneme_ses_seti.py  (mikrofonunuz yoksa kontrol programını denemek için)
import numpy as np
import wave
import os

rng = np.random.default_rng(3)

def kaydet(yol, ses, ornekleme=16000):
    veri = (np.clip(ses, -1, 1) * 32767).astype(np.int16)
    with wave.open(yol, "wb") as f:
        f.setnchannels(1)
        f.setsampwidth(2)
        f.setframerate(ornekleme)
        f.writeframes(veri.tobytes())

def kelime(f1, f2, sure=1.0, ornekleme=16000):
    """İki tonlu, ortası yüksek bir 'kelime' benzeri ses üretir."""
    t = np.arange(int(sure * ornekleme)) / ornekleme
    zarf = np.exp(-((t - 0.5) / 0.15) ** 2)               # sesin ortada yükselmesi
    ses = np.sin(2 * np.pi * f1 * t) + 0.5 * np.sin(2 * np.pi * f2 * t)
    return 0.5 * zarf * ses + 0.01 * rng.normal(0, 1, len(t))

plan = {"ac": (40, 700, 1200), "kapat": (40, 500, 1800),
        "gurultu": (40, None, None), "bilinmeyen": (22, 350, 900)}
for etiket, (adet, f1, f2) in plan.items():
    os.makedirs(f"veri_ses/{etiket}", exist_ok=True)
    for i in range(1, adet + 1):
        if etiket == "gurultu":
            ses = 0.05 * rng.normal(0, 1, 16000)
        else:
            ses = kelime(f1 * rng.uniform(0.9, 1.1), f2 * rng.uniform(0.9, 1.1))
        kaydet(f"veri_ses/{etiket}/{etiket}.{i:03d}.wav", ses)

# Bilerek eklenen sorunlu kayıtlar
kaydet("veri_ses/ac/ac.041.wav", 0.002 * rng.normal(0, 1, 16000))          # sessiz
kaydet("veri_ses/kapat/kapat.041.wav", 3 * kelime(500, 1800))              # kırpılmış
kaydet("veri_ses/bilinmeyen/bilinmeyen.023.wav", kelime(350, 900, sure=2.0))  # 2 saniye
kaydet("veri_ses/bilinmeyen/bilinmeyen.024.wav", kelime(350, 900, ornekleme=44100), 44100)
print("Deneme ses seti hazır.")

Beklenen çıktı:

Deneme ses seti hazır.

Şimdi kontrol programı. Kendi veri_ses klasörünüzde de aynen çalışır:

# dosya: ses_kontrol.py  (ses veri setindeki sorunlu kayıtları bulur)
import wave
import os
import numpy as np

KLASOR = "veri_ses"
BEKLENEN_ORNEKLEME = 16000
BEKLENEN_SURE = 1.0

ozet = {}
for etiket in sorted(os.listdir(KLASOR)):
    yol = os.path.join(KLASOR, etiket)
    if not os.path.isdir(yol):
        continue
    duzeyler = []
    for ad in sorted(os.listdir(yol)):
        if not ad.endswith(".wav"):
            continue
        tam = os.path.join(yol, ad)
        if not ad.startswith(etiket + "."):
            print(f"Ad uyumsuz: {tam} ('{etiket}.' ile başlamalı)")
        with wave.open(tam, "rb") as f:
            orn, kanal = f.getframerate(), f.getnchannels()
            ses = np.frombuffer(f.readframes(f.getnframes()), dtype=np.int16)
        sure = len(ses) / orn
        tepe = int(np.abs(ses).max())
        rms = np.sqrt(np.mean((ses / 32768.0) ** 2))
        dbfs = 20 * np.log10(rms + 1e-9)       # 0 dBFS en yüksek düzeydir

        if orn != BEKLENEN_ORNEKLEME or kanal != 1:
            print(f"Biçim hatalı: {tam} ({orn} Hz, {kanal} kanal)")
        if abs(sure - BEKLENEN_SURE) > 0.05:
            print(f"Süre hatalı: {tam} ({sure:.2f} s)")
        if tepe >= 32000:
            print(f"Kırpılmış: {tam} (tepe {tepe})")
        if etiket != "gurultu" and tepe < 1000:
            print(f"Çok sessiz: {tam} (tepe {tepe})")
        duzeyler.append(dbfs)
    ozet[etiket] = duzeyler

print()
print(f"{'etiket':<12}{'kayıt':>6}{'ort. düzey':>12}")
for etiket, d in ozet.items():
    print(f"{etiket:<12}{len(d):>6}{np.mean(d):>9.1f} dB")

Beklenen çıktı:

Çok sessiz: veri_ses/ac/ac.041.wav (tepe 280)
Süre hatalı: veri_ses/bilinmeyen/bilinmeyen.023.wav (2.00 s)
Biçim hatalı: veri_ses/bilinmeyen/bilinmeyen.024.wav (44100 Hz, 1 kanal)
Kırpılmış: veri_ses/kapat/kapat.041.wav (tepe 32767)

etiket       kayıt  ort. düzey
ac              41    -16.2 dB
bilinmeyen      24    -15.4 dB
gurultu         40    -26.0 dB
kapat           41    -15.1 dB

Program her kaydın biçimini, süresini, tepe değerini ve ortalama ses düzeyini kontrol eder. Ses düzeyini dBFS biriminde yazıyoruz: 0 dB en yüksek düzeydir, değer eksiye gittikçe ses kısılır. Çıktıda bilerek eklediğimiz dört sorun da yakalandı. Özet tablosu da önemli bilgiler veriyor: “bilinmeyen” sınıfında diğerlerinden belirgin biçimde az kayıt var ve gürültü kayıtlarının düzeyi komutlardan belirgin biçimde düşük. Gerçek bir veri setinde “bilinmeyen” sınıfına daha fazla kayıt eklememiz gerekirdi.

Her sınıftan bir örnek kaydın dalga formu

Veri Setini Edge Impulse’a Yükleme

Ses modelimizi bir sonraki derste Edge Impulse ile eğiteceğiz (nedenini orada ayrıntılı anlatacağız). Hazırlık olarak veri setini şimdiden yükleyelim:

  1. studio.edgeimpulse.com adresinde giriş yapın ve yeni bir proje oluşturun (ör. geri-donusum-sesli-komut).
  2. Soldaki menüden Data acquisition sayfasını açın ve Add data → Upload data seçeneğini seçin.
  3. Bir klasördeki dosyaların hepsini seçin.
  4. Kategori olarak Automatically split between training and testing, etiket olarak Infer from filename seçin ve yüklemeyi başlatın.
  5. Diğer klasörler için de aynısını yapın. Yükleme bitince her sınıfın eğitim ve test verisine yaklaşık %80/%20 oranında bölündüğünü göreceksiniz.

⚠️ Önemli Not

Aynı kişinin aynı anda kaydettiği birbirine çok benzeyen kayıtlar hem eğitim hem test verisine düşerse modelin başarısı olduğundan yüksek görünür. Gerçek başarıyı görmek için bir iki arkadaşınızın kayıtlarını eğitime hiç katmayın; bunları test için saklayın. Bir de uyarı: Komut kelimesi bir saniyelik pencereden taşarsa model kelimenin yalnızca bir parçasını duyar. Kayıtta Enter’a bastıktan hemen sonra söylemeye alışın ve kontrol programının uyarılarını ciddiye alın.

Sıra Sizde

  1. Kolay: Gruptaki her öğrenci her komut için en az 10 kayıt yapsın. ses_kontrol.py ile kontrol edin ve sorunlu kayıtları yeniden alın.
  2. Orta: “bilinmeyen” sınıfı için en az 10 farklı kelimeden oluşan bir liste hazırlayın. “aç” ve “kapat” kelimelerine benzeyen kelimeleri listeye neden eklemeliyiz?
  3. Zor: ses_kontrol.py programını, sorunlu kayıtların adlarını sorunlu.txt dosyasına yazacak şekilde geliştirin.

Kendini Dene

  1. Veri setine neden “gurultu” ve “bilinmeyen” sınıflarını ekledik?
  2. kapat.017.wav dosyasını Edge Impulse’a “Infer from filename” seçeneğiyle yüklersek etiketi ne olur?
  3. Bütün kayıtlar tek bir öğrencinin sesinden olursa ne olabilir?

Cevaplar

  1. Model her sesi bir komut sanmasın diye. Bu sınıflar modele ortam sesini ve komut olmayan kelimeleri tanımayı öğretir.
  2. kapat. Dosya adının ilk noktaya kadar olan kısmı etiket olarak okunur.
  3. Model o öğrencinin ses tonuna alışır; başka kişilerin, özellikle farklı ses tonundaki kişilerin komutlarını tanıyamayabilir.