A custom Convolutional Neural Network trained from scratch to classify music genres from audio, using Mel-Spectrograms as visual representations of sound.
This model treats audio classification as an image recognition problem. Audio clips are converted to Mel-Spectrograms and fed into a 4-block CNN classifier. No pretrained weights or transfer learning — trained entirely from scratch on the FMA Small dataset.
Input: (B, 1, 128, T)
→ ConvBlock(1→32) + MaxPool
→ ConvBlock(32→64) + MaxPool
→ ConvBlock(64→128) + MaxPool
→ ConvBlock(128→256)+ MaxPool
→ AdaptiveAvgPool2d(1)
→ Dropout(0.3) → Linear(256→128) → ReLU → Dropout(0.15) → Linear(128→8)
Output: (B, 8)
Each ConvBlock = Conv2d → BatchNorm → ReLU → Conv2d → BatchNorm → ReLU → MaxPool2d
1 import torch
2 import librosa
3 import numpy as np
4 from huggingface_hub import hf_hub_download
5
6 # Download model files
7 model_path = hf_hub_download ( repo_id = "jessitoi/genre-cnn-fma-small" , filename = "best_model.pth" )
8 config_path = hf_hub_download ( repo_id = "jessitoi/genre-cnn-fma-small" , filename = "config.yaml" )
9
10 # Load model architecture (download model.py from repo)
11 from model import GenreCNN
12
13 checkpoint = torch . load ( model_path , map_location = "cpu" )
14 model = GenreCNN ( num_classes = 8 , dropout = 0.3 )
15 model . load_state_dict ( checkpoint [ "model_state" ] )
16 model . eval ( )
17
18 # Preprocess audio
19 GENRES = [ "Electronic" , "Experimental" , "Folk" , "Hip-Hop" ,
20 "Instrumental" , "International" , "Pop" , "Rock" ]
21
22 def predict ( audio_path : str ) - > dict :
23 y , sr = librosa . load ( audio_path , sr = 22050 , duration = 30 , mono = True )
24 expected = 22050 * 30
25 if len ( y ) < expected :
26 y = np . pad ( y , ( 0 , expected - len ( y ) ) )
27
28 mel = librosa . feature . melspectrogram ( y = y , sr = sr , n_mels = 128 , n_fft = 2048 , hop_length = 512 )
29 mel_db = librosa . power_to_db ( mel , ref = np . max ) . astype ( np . float32 )
30 mel_db = ( mel_db - mel_db . mean ( ) ) / ( mel_db . std ( ) + 1e-8 )
31
32 x = torch . tensor ( mel_db ) . unsqueeze ( 0 ) . unsqueeze ( 0 ) # (1, 1, 128, T)
33
34 with torch . no_grad ( ) :
35 logits = model ( x )
36 probs = torch . softmax ( logits , dim = 1 ) . squeeze ( ) . numpy ( )
37
38 return { genre : float ( prob ) for genre , prob in zip ( GENRES , probs ) }
39
40 # Run inference
41 scores = predict ( "your_song.mp3" )
42 predicted = max ( scores , key = scores . get )
43 print ( f"Predicted genre: { predicted } ( { scores [ predicted ] : .2% } )" )
Full training code, FastAPI backend, and Next.js demo:
github.com/Jessitoii/cnn-based-genre-recognition-fma
1 @misc{ozer2026genrecnn,
2 author = {Özer, Alper Can},
3 title = {CNN-Based Music Genre Recognition on FMA},
4 year = {2026},
5 url = {https://huggingface.co/jessitoi/genre-cnn-fma-small}
6 }