duclvQ commited on
Commit
64ef8a3
·
verified ·
1 Parent(s): 6100b73

Add Transformers load-by-id model files

Browse files
README.md ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ pipeline_tag: audio-classification
4
+ tags:
5
+ - audio
6
+ - music
7
+ - speech
8
+ - custom-code
9
+ license: mit
10
+ ---
11
+
12
+ # SMAD CRNN
13
+
14
+ SMAD classifies a 4-second mono audio clip into one of four labels:
15
+ `speech_noise`, `speech_music`, `singing_music`, or `none`.
16
+
17
+ The model is a small CNN + BiGRU trained from scratch on 80-bin log-mel
18
+ spectrograms. It has 834k parameters and reached 87.95% on the synthetic
19
+ held-out test split used by the project.
20
+
21
+ ## Load by model id
22
+
23
+ ```python
24
+ import torch
25
+ from transformers import AutoFeatureExtractor, AutoModelForAudioClassification
26
+
27
+ model_id = "duclvQ/smad"
28
+
29
+ feature_extractor = AutoFeatureExtractor.from_pretrained(
30
+ model_id,
31
+ trust_remote_code=True,
32
+ )
33
+ model = AutoModelForAudioClassification.from_pretrained(
34
+ model_id,
35
+ trust_remote_code=True,
36
+ ).eval()
37
+
38
+ # `audio` must be mono float32 audio sampled at 16 kHz. For longer files, run
39
+ # this over 4-second windows.
40
+ inputs = feature_extractor(audio, sampling_rate=16000, return_tensors="pt")
41
+ with torch.no_grad():
42
+ logits = model(**inputs).logits
43
+ probs = torch.softmax(logits / model.config.temperature, dim=-1)
44
+
45
+ label_id = int(probs.argmax(-1)[0])
46
+ label = model.config.id2label[label_id]
47
+ confidence = float(probs[0, label_id])
48
+ ```
49
+
50
+ For arbitrary file paths, load/resample with `librosa`:
51
+
52
+ ```python
53
+ import librosa
54
+
55
+ audio, _ = librosa.load("clip.mp3", sr=16000, mono=True)
56
+ ```
57
+
58
+ ## Caveat
59
+
60
+ The reported score is an upper bound on real-world performance: train and test
61
+ clips come from the same synthetic mixer, so they share its biases.
config.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "SmadForAudioClassification"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_smad.SmadConfig",
7
+ "AutoModel": "modeling_smad.SmadForAudioClassification",
8
+ "AutoModelForAudioClassification": "modeling_smad.SmadForAudioClassification"
9
+ },
10
+ "channels": [
11
+ 32,
12
+ 64,
13
+ 128,
14
+ 128
15
+ ],
16
+ "dropout": 0.2,
17
+ "id2label": {
18
+ "0": "speech_noise",
19
+ "1": "speech_music",
20
+ "2": "singing_music",
21
+ "3": "none"
22
+ },
23
+ "label2id": {
24
+ "speech_noise": 0,
25
+ "speech_music": 1,
26
+ "singing_music": 2,
27
+ "none": 3
28
+ },
29
+ "model_type": "smad_crnn",
30
+ "n_fft": 400,
31
+ "num_labels": 4,
32
+ "num_mels": 80,
33
+ "rnn_hidden": 128,
34
+ "rnn_type": "gru",
35
+ "sample_rate": 16000,
36
+ "segment_seconds": 4.0,
37
+ "temperature": 0.709507268312107
38
+ }
configuration_smad.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+
3
+
4
+ class SmadConfig(PretrainedConfig):
5
+ model_type = "smad_crnn"
6
+
7
+ def __init__(
8
+ self,
9
+ num_mels=80,
10
+ channels=(32, 64, 128, 128),
11
+ rnn_hidden=128,
12
+ dropout=0.2,
13
+ num_labels=4,
14
+ rnn_type="gru",
15
+ temperature=1.0,
16
+ sample_rate=16000,
17
+ segment_seconds=4.0,
18
+ n_fft=400,
19
+ hop_length=160,
20
+ label2id=None,
21
+ id2label=None,
22
+ **kwargs,
23
+ ):
24
+ label2id = label2id or {
25
+ "speech_noise": 0,
26
+ "speech_music": 1,
27
+ "singing_music": 2,
28
+ "none": 3,
29
+ }
30
+ id2label = id2label or {str(v): k for k, v in label2id.items()}
31
+ super().__init__(
32
+ num_labels=num_labels,
33
+ label2id=label2id,
34
+ id2label=id2label,
35
+ **kwargs,
36
+ )
37
+ self.num_mels = num_mels
38
+ self.channels = list(channels)
39
+ self.rnn_hidden = rnn_hidden
40
+ self.dropout = dropout
41
+ self.rnn_type = rnn_type
42
+ self.temperature = temperature
43
+ self.sample_rate = sample_rate
44
+ self.segment_seconds = segment_seconds
45
+ self.n_fft = n_fft
46
+ self.hop_length = hop_length
feature_extraction_smad.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from transformers import BatchFeature, SequenceFeatureExtractor
3
+
4
+
5
+ class SmadFeatureExtractor(SequenceFeatureExtractor):
6
+ model_input_names = ["input_features"]
7
+
8
+ def __init__(
9
+ self,
10
+ feature_size=80,
11
+ sampling_rate=16000,
12
+ padding_value=0.0,
13
+ segment_seconds=4.0,
14
+ n_fft=400,
15
+ hop_length=160,
16
+ **kwargs,
17
+ ):
18
+ super().__init__(
19
+ feature_size=feature_size,
20
+ sampling_rate=sampling_rate,
21
+ padding_value=padding_value,
22
+ **kwargs,
23
+ )
24
+ self.segment_seconds = segment_seconds
25
+ self.n_fft = n_fft
26
+ self.hop_length = hop_length
27
+
28
+ def waveform_to_mel(self, waveform, sampling_rate=None):
29
+ import librosa
30
+
31
+ sampling_rate = sampling_rate or self.sampling_rate
32
+ mel = librosa.feature.melspectrogram(
33
+ y=np.asarray(waveform, dtype=np.float32),
34
+ sr=sampling_rate,
35
+ n_fft=self.n_fft,
36
+ hop_length=self.hop_length,
37
+ n_mels=self.feature_size,
38
+ power=2.0,
39
+ )
40
+ return librosa.power_to_db(mel).T.astype(np.float32)
41
+
42
+ def __call__(self, raw_speech, sampling_rate=None, return_tensors=None, **kwargs):
43
+ sampling_rate = sampling_rate or self.sampling_rate
44
+ if sampling_rate != self.sampling_rate:
45
+ raise ValueError(
46
+ f"Expected {self.sampling_rate} Hz audio. Resample before calling "
47
+ f"the feature extractor; received {sampling_rate} Hz."
48
+ )
49
+
50
+ if isinstance(raw_speech, np.ndarray) and raw_speech.ndim == 1:
51
+ waves = [raw_speech]
52
+ else:
53
+ waves = [np.asarray(w, dtype=np.float32) for w in raw_speech]
54
+
55
+ target_len = int(round(self.segment_seconds * self.sampling_rate))
56
+ features = []
57
+ for wave in waves:
58
+ if wave.ndim != 1:
59
+ raise ValueError("Expected mono audio arrays with shape `(samples,)`.")
60
+ if wave.shape[0] < target_len:
61
+ wave = np.pad(wave, (0, target_len - wave.shape[0]))
62
+ elif wave.shape[0] > target_len:
63
+ wave = wave[:target_len]
64
+ features.append(self.waveform_to_mel(wave, sampling_rate=sampling_rate))
65
+
66
+ return BatchFeature({"input_features": features}, tensor_type=return_tensors)
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:63818291e401ca27616cb29ba3a224b780296ac4af87e6d6b70edb9daa40f5bc
3
+ size 3342832
modeling_smad.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from transformers import PreTrainedModel
4
+ from transformers.modeling_outputs import SequenceClassifierOutput
5
+
6
+ from .configuration_smad import SmadConfig
7
+
8
+
9
+ class ConvBlock(nn.Module):
10
+ def __init__(self, in_ch, out_ch, pool):
11
+ super().__init__()
12
+ self.block = nn.Sequential(
13
+ nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1, bias=False),
14
+ nn.BatchNorm2d(out_ch),
15
+ nn.ReLU(inplace=True),
16
+ nn.MaxPool2d(pool),
17
+ )
18
+
19
+ def forward(self, x):
20
+ return self.block(x)
21
+
22
+
23
+ class TinyAudioCRNN(nn.Module):
24
+ def __init__(
25
+ self,
26
+ n_mels=80,
27
+ channels=(32, 64, 128, 128),
28
+ rnn_hidden=128,
29
+ dropout=0.2,
30
+ num_classes=4,
31
+ rnn_type="gru",
32
+ ):
33
+ super().__init__()
34
+ self.register_buffer("feat_mean", torch.zeros(n_mels))
35
+ self.register_buffer("feat_std", torch.ones(n_mels))
36
+
37
+ pools = [(2, 2)] * (len(channels) - 1) + [(2, 1)]
38
+ blocks, in_ch = [], 1
39
+ for out_ch, pool in zip(channels, pools):
40
+ blocks.append(ConvBlock(in_ch, out_ch, pool))
41
+ in_ch = out_ch
42
+ self.conv = nn.Sequential(*blocks)
43
+
44
+ freq_out = n_mels
45
+ for freq_pool, _ in pools:
46
+ freq_out //= freq_pool
47
+ if freq_out < 1:
48
+ raise ValueError(f"{len(channels)} conv blocks pool {n_mels} mel bins down to nothing")
49
+ rnn_in = channels[-1] * freq_out
50
+
51
+ self.dropout = nn.Dropout(dropout)
52
+ rnn_cls = {"gru": nn.GRU, "lstm": nn.LSTM}[rnn_type.lower()]
53
+ self.rnn = rnn_cls(
54
+ rnn_in,
55
+ rnn_hidden,
56
+ num_layers=1,
57
+ batch_first=True,
58
+ bidirectional=True,
59
+ )
60
+ self.classifier = nn.Linear(rnn_hidden * 2 * 2, num_classes)
61
+
62
+ def forward(self, x):
63
+ x = (x - self.feat_mean) / self.feat_std
64
+ x = x.transpose(1, 2).unsqueeze(1)
65
+ x = self.conv(x)
66
+ b, c, f, t = x.shape
67
+ x = x.permute(0, 3, 1, 2).reshape(b, t, c * f)
68
+ x = self.dropout(x)
69
+ x, _ = self.rnn(x)
70
+ pooled = torch.cat([x.mean(dim=1), x.max(dim=1).values], dim=-1)
71
+ return self.classifier(self.dropout(pooled))
72
+
73
+
74
+ class SmadForAudioClassification(PreTrainedModel):
75
+ config_class = SmadConfig
76
+ base_model_prefix = "smad"
77
+ main_input_name = "input_features"
78
+ all_tied_weights_keys = {}
79
+
80
+ def __init__(self, config):
81
+ super().__init__(config)
82
+ self.smad = TinyAudioCRNN(
83
+ n_mels=config.num_mels,
84
+ channels=tuple(config.channels),
85
+ rnn_hidden=config.rnn_hidden,
86
+ dropout=config.dropout,
87
+ num_classes=config.num_labels,
88
+ rnn_type=config.rnn_type,
89
+ )
90
+
91
+ def forward(self, input_features=None, labels=None, return_dict=None, **kwargs):
92
+ if input_features is None:
93
+ input_features = kwargs.pop("inputs", None)
94
+ if input_features is None:
95
+ raise ValueError("Pass log-mel features as `input_features`.")
96
+
97
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
98
+ logits = self.smad(input_features)
99
+ loss = None
100
+ if labels is not None:
101
+ loss = nn.functional.cross_entropy(logits, labels)
102
+ if not return_dict:
103
+ output = (logits,)
104
+ return ((loss,) + output) if loss is not None else output
105
+ return SequenceClassifierOutput(loss=loss, logits=logits)
106
+
107
+ @torch.no_grad()
108
+ def predict_proba(self, input_features):
109
+ logits = self(input_features=input_features).logits
110
+ return torch.softmax(logits / float(self.config.temperature), dim=-1)
preprocessor_config.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "auto_map": {
3
+ "AutoFeatureExtractor": "feature_extraction_smad.SmadFeatureExtractor"
4
+ },
5
+ "feature_extractor_type": "SmadFeatureExtractor",
6
+ "feature_size": 80,
7
+ "hop_length": 160,
8
+ "n_fft": 400,
9
+ "padding_value": 0.0,
10
+ "sampling_rate": 16000,
11
+ "segment_seconds": 4.0
12
+ }