Skip to content

Commit b8ce603

Browse files
committed
Added code for CNN music genre classifier.
1 parent 9a42311 commit b8ce603

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import json
2+
import numpy as np
3+
from sklearn.model_selection import train_test_split
4+
import tensorflow.keras as keras
5+
import matplotlib.pyplot as plt
6+
7+
DATA_PATH = "../13/data_10.json"
8+
9+
10+
def load_data(data_path):
11+
"""Loads training dataset from json file.
12+
13+
:param data_path (str): Path to json file containing data
14+
:return X (ndarray): Inputs
15+
:return y (ndarray): Targets
16+
"""
17+
18+
with open(data_path, "r") as fp:
19+
data = json.load(fp)
20+
21+
X = np.array(data["mfcc"])
22+
y = np.array(data["labels"])
23+
return X, y
24+
25+
26+
def plot_history(history):
27+
"""Plots accuracy/loss for training/validation set as a function of the epochs
28+
29+
:param history: Training history of model
30+
:return:
31+
"""
32+
33+
fig, axs = plt.subplots(2)
34+
35+
# create accuracy sublpot
36+
axs[0].plot(history.history["accuracy"], label="train accuracy")
37+
axs[0].plot(history.history["val_accuracy"], label="test accuracy")
38+
axs[0].set_ylabel("Accuracy")
39+
axs[0].legend(loc="lower right")
40+
axs[0].set_title("Accuracy eval")
41+
42+
# create error sublpot
43+
axs[1].plot(history.history["loss"], label="train error")
44+
axs[1].plot(history.history["val_loss"], label="test error")
45+
axs[1].set_ylabel("Error")
46+
axs[1].set_xlabel("Epoch")
47+
axs[1].legend(loc="upper right")
48+
axs[1].set_title("Error eval")
49+
50+
plt.show()
51+
52+
53+
def prepare_datasets(test_size, validation_size):
54+
"""Loads data and splits it into train, validation and test sets.
55+
56+
:param test_size (float): Value in [0, 1] indicating percentage of data set to allocate to test split
57+
:param validation_size (float): Value in [0, 1] indicating percentage of train set to allocate to validation split
58+
59+
:return X_train (ndarray): Input training set
60+
:return X_validation (ndarray): Input validation set
61+
:return X_test (ndarray): Input test set
62+
:return y_train (ndarray): Target training set
63+
:return y_validation (ndarray): Target validation set
64+
:return y_test (ndarray): Target test set
65+
"""
66+
67+
# load data
68+
X, y = load_data(DATA_PATH)
69+
70+
# create train, validation and test split
71+
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size)
72+
X_train, X_validation, y_train, y_validation = train_test_split(X_train, y_train, test_size=validation_size)
73+
74+
# add an axis to input sets
75+
X_train = X_train[..., np.newaxis]
76+
X_validation = X_validation[..., np.newaxis]
77+
X_test = X_test[..., np.newaxis]
78+
79+
return X_train, X_validation, X_test, y_train, y_validation, y_test
80+
81+
82+
def build_model(input_shape):
83+
"""Generates CNN model
84+
85+
:param input_shape (tuple): Shape of input set
86+
:return model: CNN model
87+
"""
88+
89+
# build network topology
90+
model = keras.Sequential()
91+
92+
# 1st conv layer
93+
model.add(keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=input_shape))
94+
model.add(keras.layers.MaxPooling2D((3, 3), strides=(2, 2), padding='same'))
95+
model.add(keras.layers.BatchNormalization())
96+
97+
# 2nd conv layer
98+
model.add(keras.layers.Conv2D(32, (3, 3), activation='relu'))
99+
model.add(keras.layers.MaxPooling2D((3, 3), strides=(2, 2), padding='same'))
100+
model.add(keras.layers.BatchNormalization())
101+
102+
# 3rd conv layer
103+
model.add(keras.layers.Conv2D(32, (2, 2), activation='relu'))
104+
model.add(keras.layers.MaxPooling2D((2, 2), strides=(2, 2), padding='same'))
105+
model.add(keras.layers.BatchNormalization())
106+
107+
# flatten output and feed it into dense layer
108+
model.add(keras.layers.Flatten())
109+
model.add(keras.layers.Dense(64, activation='relu'))
110+
model.add(keras.layers.Dropout(0.3))
111+
112+
# output layer
113+
model.add(keras.layers.Dense(10, activation='softmax'))
114+
115+
return model
116+
117+
118+
def predict(model, X, y):
119+
"""Predict a single sample using the trained model
120+
121+
:param model: Trained classifier
122+
:param X: Input data
123+
:param y (int): Target
124+
"""
125+
126+
# add a dimension to input data for sample - model.predict() expects a 4d array in this case
127+
X = X[np.newaxis, ...] # array shape (1, 130, 13, 1)
128+
129+
# perform prediction
130+
prediction = model.predict(X)
131+
132+
# get index with max value
133+
predicted_index = np.argmax(prediction, axis=1)
134+
135+
print("Target: {}, Predicted label: {}".format(y, predicted_index))
136+
137+
138+
if __name__ == "__main__":
139+
140+
# get train, validation, test splits
141+
X_train, X_validation, X_test, y_train, y_validation, y_test = prepare_datasets(0.25, 0.2)
142+
143+
# create network
144+
input_shape = (X_train.shape[1], X_train.shape[2], 1)
145+
model = build_model(input_shape)
146+
147+
# compile model
148+
optimiser = keras.optimizers.Adam(learning_rate=0.0001)
149+
model.compile(optimizer=optimiser,
150+
loss='sparse_categorical_crossentropy',
151+
metrics=['accuracy'])
152+
153+
model.summary()
154+
155+
# train model
156+
history = model.fit(X_train, y_train, validation_data=(X_validation, y_validation), batch_size=32, epochs=30)
157+
158+
# plot accuracy/error for training and validation
159+
plot_history(history)
160+
161+
# evaluate model on test set
162+
test_loss, test_acc = model.evaluate(X_test, y_test, verbose=2)
163+
print('\nTest accuracy:', test_acc)
164+
165+
# pick a sample to predict from the test set
166+
X_to_predict = X_test[100]
167+
y_to_predict = y_test[100]
168+
169+
# predict sample
170+
predict(model, X_to_predict, y_to_predict)
171+
172+

0 commit comments

Comments
 (0)