sklean SVM / SVC always predicting the same class for any given input

Viewed 108

I am trying to train a SVM with speech data, here with the RAVDESS data set which you can find here: https://www.kaggle.com/uwrfkaggler/ravdess-emotional-speech-audio

This dataset has 24 directories, but I am only using the first 20 directories.

First, I am navigating and saving the directory paths of all files inside these 20 directories into an array.

Then I am iterating over the array, saving the classes into another array and extracting the features of the audio files. I am saving the features in a dataframe.

I can see that the classes are somehow balanced:

>>from collections import Counter
>>print(Counter(klass).keys()) # equals to list(set(words))
>>print(Counter(klass).values()) # counts the elements' frequency

dict_keys(['neutral', 'calm', 'happy', 'sad', 'angry', 'fearful', 'disgust', 'surprised'])
dict_values([76, 152, 152, 152, 152, 152, 152, 152])

Next, I am preprocessing and normalizing the dataframe:

from sklearn.preprocessing import StandardScaler

x2 = x.copy()
x2.drop(["file","start","end"], axis = 1,inplace = True)

scaler = StandardScaler()
print(scaler.fit(x2))
x2 = scaler.transform(x2)
x2 = pd.DataFrame(x2)

Now I start training the SVM with this:

from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(x2, klass, test_size = 0.20,random_state = 1)

and of course optimizing the hyperparameter (gamma, C, etc) before.

Afterwards I am checking the best params and score which looks quite good.

Accuracy Training:  0.9857456140350878
Accuracy Test:  0.6271929824561403

So far, so good. I have extracted the features and trained the SVM with Emotion classification. Now I want to predict the emotion of a new audio file which is unknown to the model. Here I am randomly selecting an audio file from the 24th directory of the dataset. And here is somehow the problem:

import random
Xnew = pd.DataFrame()
EvalFile = glob.glob('{}\**\*24.wav'.format(PATH_RAVDESS),recursive=True)

Xnew = Xnew.append(smile.process_file(EvalFile[random.randint(0, len(EvalFile)-1)]))
Xnew = Xnew.reset_index()
Xnew.drop(["file","start","end"], axis = 1,inplace=True)

#Standardising is skipped because it makes no sense with one file

Ynew = grid.predict(Xnew)
print("File=%s \nPredicted=%s" % (rdfile, Ynew))

It doesnt matter which file is picked. The model is always predicting the same class 'angry'.

What am I doing wrong?

2 Answers

You comment in the code:

#Standardising is skipped because it makes no sense with one file

Standardization not only does makes sense (even with a single file), it is also not optional; since your model has been trained with standardized data, it expects such standardized data for any future predictions. Failure to do so can lead to behaviors such as the one you describe.

So, before predicting your Xnew, you should transform it with your already fitted scaler:

Xnew = scaler.transform(Xnew)
Ynew = grid.predict(Xnew)

Solved the problem! Xnew must be standardized again with scaler.transform(Xnew) only. But scaler.fit must not be used again. That was my mistake.

Related