- Je normaliseert een eenzijdig spectrum correct voor even en oneven framelengtes.
- Je onderscheidt frequenties, melgewichten en log-vermogenskenmerken zonder dB of MFCCs te claimen.
Een lage toon en een hoge toon kunnen ongeveer even luid zijn, terwijl hun golven heel anders bewegen. Een model dat alleen gemiddelde amplitude ziet, mist dat verschil. We zetten daarom ieder frame om naar kenmerken die de frequentie-inhoud beschrijven.
Denk aan een akkoord dat je uitsplitst in bijdragen van verschillende tonen. De Fouriertransformatie doet iets vergelijkbaars met een kort digitaal signaal. We gebruiken NumPy voor deze transformatie en bouwen zelf de venstering, vermogensnormalisatie en filterstappen eromheen.
1. Maak de randen zachter en verdeel vermogen correct
Een frame afknippen kan aan de randen een sprong maken. Het symmetrische Hann-venster geeft de randen minder gewicht en het midden meer. Voor vier samples zijn de twee buitenste gewichten nul.
window = np.hanning(4)
print(window.round(3).tolist())
# [0.0, 0.75, 0.75, 0.0]
Dat heeft een echte consequentie: een signaal dat uitsluitend op zo'n eindpunt niet nul is, verdwijnt na venstering. Het venster herkent geen geluid en kan geen informatie terughalen. Het verandert welke samples bijdragen aan de frequentieberekening.
Noem de FFT-uitvoer X_k en het venster w_n, bij framelengte M. Voor ons eenzijdige vermogen P_k gebruiken we de volgende normalisatie. De factor c_k is één bij de nulfrequentie en bij de Nyquistbin van een even M; bij overige positieve frequenties is hij twee.
De Nyquistfrequentie ligt op f_s/2: bij 8000 samples per seconde is dat 4000 Hz. Een even rfft heeft daar precies een laatste bin. Een oneven framelengte heeft geen bin precies op die frequentie.
De verdubbeling neemt de bijdrage van de weggelaten negatieve frequenties mee. Bij een oneven framelengte is er geen afzonderlijke Nyquistbin, dus verdubbelt ook de laatste positieve bin. De som van P is hierdoor het venstergewogen gemiddelde van de gekwadrateerde digitale amplitude.
spectrum = windowed_power(
np.ones((1,4)),8000)
print(spectrum['frequencies']
.tolist())
print(spectrum['power']
.round(3).tolist())
# [0.0, 2000.0, 4000.0]
# [[0.5, 0.5, 0.0]]
Bij vier constante enen is die som één. Door venstering wordt de getransformeerde rij [0;0,75;0,75;0], met ook een positieve frequentiecomponent. De berekening gebruikt dus een andere rij dan de oorspronkelijke constante enen.
Maak windowed_power met de juiste frequentieas en drie onafhankelijke uitvoerarrays. Het resultaat is vermogen per frequentiebin, geen gekalibreerde akoestische luidheid of vermogensdichtheid per hertz.
012. Combineer naburige frequenties met mel-filters
Een hele rij FFT-bins is niet altijd een handige modelinvoer. We combineren buurfrequenties met driehoekige gewichten en kiezen de randen op een mel-schaal. Onze expliciete HTK-conventie zet hertz f om naar melwaarde m.
Verdeel bands+2 punten gelijkmatig op die schaal en reken ze terug naar hertz. Elk filter stijgt tussen zijn linker- en middenrand en daalt daarna tot de rechterrand. De theoretische piek is één; een FFT-bin hoeft niet precies op die piek te vallen.
Wij gebruiken lineaire Hz-driehoeken zonder oppervlaktenormalisatie. Andere conventies kunnen dezelfde frequenties anders wegen. Vermeld daarom de gekozen instelling wanneer je kenmerken vergelijkt.
De inverse omzetting maakt van de gelijk verdeelde melpunten weer hertz. Eerst deel je m door 2595, daarna neem je de macht van tien. Zo krijg je de concrete linker-, midden- en rechterrand voor iedere driehoek.
bank = mel_filters(128,8000,6)
print(bank['weights'].shape)
print(bank['edges_hz'][:3]
.round(1).tolist())
# (6, 65)
# [0.0, 218.8, 506.1]
Er zijn 65 rfft-bins en zes filterrijen. Bij een te kleine FFT kunnen sommige driehoeken geen enkele positieve bin raken. Maak mel_filters zodat die configuratie een ValueError geeft, in plaats van ongemerkt een altijd leeg modelkenmerk te produceren.
02Vergelijk twee vensterlengtes en toonhoogtes. Bekijk het venstervermogen of één mel-driehoek op precies dezelfde frequentieas.
- Som venstervermogen
- 0,125
- Breedte van een FFT-bin
- 62,5 Hz
Melband M1 loopt van 0 tot 506,101 Hz en heeft haar theoretische piek bij 218,842 Hz. Een FFT-bin hoeft niet precies op die piek te liggen.
| Kenmerk | Waarde |
|---|---|
| M1 | 2,055 |
| M2 | 4,754 |
| M3 | 1,427 |
| M4 | 0 |
| M5 | 0 |
| M6 | 0 |
| E | 4,836 |
Verdubbeling van de amplitude verviervoudigt het vermogen vóór de logtransformatie. De melgewichten veranderen daardoor niet.
Dit zijn vaste toonvoorbeelden bij 8 kHz met een symmetrisch Hann-venster. De zeven kenmerken zijn ln(1+1000×vermogen): zes melbanden en totale energie. Het zijn geen dB, PSD of MFCCs.
3. Maak een begrensde logrepresentatie
Vermenigvuldig het vermogenspectrum met de getransponeerde filtermatrix. Voeg de totale binenergie als laatste kolom toe en pas log1p(1000×waarde) toe. De vaste factor maakt onze kleine digitale waarden leesbaar; dit is geen decibelschaal en geen MFCC-berekening.
Hier is u zo'n gewogen vermogenswaarde en z het modelkenmerk. Ln is de natuurlijke logaritme; log1p berekent dezelfde uitdrukking nauwkeurig bij kleine invoerwaarden.
Twee vermogenswaarden 0,001 en 0,003 met gewichten één en een half geven samen 0,0025. De logbewerking levert vervolgens ongeveer 1,253. Eerst wegen en optellen, daarna pas de logaritme nemen.
powers = np.array([.001,.003])
weights = np.array([1.,.5])
value = np.log1p(1000*(powers@weights))
print(round(float(value),3))
# 1.253
Maak spectral_features met de eerder gebouwde functies. Een volledig nulframe geeft zeven nulkenmerken: zes melbanden en de energie. Log1p maakt een nul invoer geldig; gewoon log(0) zou hier geen eindige waarde geven.
03Wat neem je mee?
- Venstering verandert de bijdrage van de samples.
- Eenzijdig vermogen vraagt aandacht voor nulfrequentie, pariteit en Nyquist.
- Log-melkenmerken zijn modelinvoer; ze zijn nog geen herkende transcriptie.
Hierna onderzoek je hoe framevoorspellingen in een kortere labelreeks veranderen.