Музыка повсюду вокруг нас. Воздействие музыки на человека невозможно выразить словами. На протяжении своего вечного путешествия лицо музыки видело много разнообразия и изменений в своей форме. Окружающий нас мир неоднороден и существует огромное разнообразие в образе жизни людей, их мышлении и их культуре. С этим разнообразием очевидно, что музыка тоже будет другой и будет иметь разнообразие, соответствующее интересам разных людей. Помня об этом разнообразии, у компаний, занимающихся потоковой передачей аудио и мультимедийных услуг, есть огромные возможности для автоматической классификации жанра конкретной песни, чтобы обслуживать похожие песни для потенциальных слушателей. С нынешним бумом практики Data Science и прогнозного моделирования, а также с доступностью структурированных/неструктурированных данных в Интернете такие задачи стали удобными и находятся в авангарде крупных компаний.

В этом онлайн-проекте мы будем стремиться классифицировать песни по соответствующим категориям, а именно. (электроника, рэп, хип-хоп, поп) на основе определенных параметров или признаков, заданных в табличном формате данных, на основе которых мы классифицируем, к какой категории относятся песни.

Первоочередной задачей будет импорт всех необходимых библиотек.

# Standard Data Manipulation Libraries
import pandas as pd
import numpy as np
from pathlib import Path
import os
path = Path('../input/prediction-of-music-genre')
​
# Profile Report and Statistical analysis 
from pandas_profiling import profile_report
​
# Preprocessing Data Libraries
from sklearn.preprocessing import LabelEncoder
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import label_binarize
from sklearn.preprocessing import StandardScaler
​
# Data Splitting Libraries
from sklearn.model_selection import train_test_split
from sklearn.model_selection import StratifiedKFold, KFold
​
# Performance Metrics Libraries
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.metrics import roc_curve, classification_report
​
# Models
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from mlxtend.classifier import StackingCVClassifier
​
# Plotting
import plotly.express as px
import plotly.graph_objects as go
from plotly.offline import plot, iplot, init_notebook_mode
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('darkgrid')  # sets the background of plots to a darkgrid
plt.rcParams['figure.figsize'] = [12.0, 8.0] # Defaults the size of the figures to specified value
init_notebook_mode(connected=True)
dataframe = pd.read_csv(path / 'music_genre.csv')

Основная идея машинного обучения заключается в том, что на основе определенного числового ввода с помощью методов статистической/математической аппроксимации мы изучаем сопоставление этих входных данных с заданным выходным значением. Узнав, как представлять особенности модели на обучающих данных — аннотированные пары (X, Y), мы затем вычисляем эффективность модели на вновь увиденных тестовых данных, которые являются смоделированным представителем данных в реальном слове. Чтобы модель точно моделировала поведение входных данных, абсолютно необходимо, чтобы входные данные были чистыми, непротиворечивыми и достоверными.

Итак, давайте посмотрим на имеющиеся данные.

print(f'Number of Columns in the Dataset : {len(dataframe.columns)}')
col_df = {}
for col in dataframe.columns:
    col_df[col] = dataframe[col].dtype
pd.DataFrame(col_df.items(), columns = ['Column Name', 'Data Type']).head(18)

Столбцы набора данных состоят из 18 полей, представляющих конкретную песню в наборе данных. Столбцы следующие:\

  1. instance_id: серийный номер песни в наборе данных.
  2. artist_name : имя исполнителя песни.
  3. track_name : название песни.
  4. популярность . Произвольная оценка, присваиваемая песне в диапазоне от 0 до 100, где 100 — самая популярная, а 0 — наименьшая.
  5. acousticness: это значение описывает, насколько акустична песня. Оценка 1,0 означает, что песня, скорее всего, будет акустической.
  6. танцевальность. Танцевальность описывает, насколько трек подходит для танцев на основе сочетания музыкальных элементов. Значение 0.0 соответствует наименее танцевальному, а значение 1.0 наиболее танцевальному.
  7. duration_ms : продолжительность песни в миллисекундах.
  8. энергия: показывает, насколько энергична песня. Диапазон этого поля находится между [0–1], где 1 соответствует песне с самой высокой энергией, а 0 — с самой низкой.
  9. Инструментальность: это значение представляет количество вокала в песне. Чем он ближе к 1.0, тем инструментальнее песня.
  10. тональность . Тональность произведения — это группа тонов или ладов, составляющих основу музыкальной композиции.
  11. liveness: это значение описывает вероятность того, что песня была записана вживую.
  12. loudness: столбец, показывающий, насколько громко звучит песня.
  13. mode: мажорные и минорные гаммы, на которых основана песня.
  14. speechiness: Speechiness определяет наличие произнесенных слов в дорожке.
  15. tempo: скорость воспроизведения песни.
  16. дата_получения: дата получения метаданных песни.
  17. валентность : показатель от 0,0 до 1,0, описывающий позитивность музыки, передаваемую треком. Треки с высокой валентностью звучат более позитивно.
  18. music_genre: фактическая категория, к которой принадлежит песня. Это наша целевая переменная.
dataframe.head()

Давайте визуализируем, сколько песен в каждом жанре. Мы рассчитаем количество значений каждого жанра и построим их, используя plotly. Обратите внимание, что в кадре данных есть 5 отсутствующих значений или значений «нан», поэтому мы исключаем их при построении графика.

genre_names = [col for col in dataframe['music_genre'].unique() if type(col)!=float]
colors = [
    '#1f77b4',  # muted blue
    '#ff7f0e',  # safety orange
    '#2ca02c',  # cooked asparagus green
    '#d62728',  # brick red
    '#9467bd',  # muted purple
    '#8c564b',  # chestnut brown
    '#e377c2',  # raspberry yogurt pink
    '#7f7f7f',  # middle gray
    '#bcbd22',  # curry yellow-green
    '#17becf'   # blue-teal
]
fig = px.bar(x=genre_names, 
             y=dataframe['music_genre'].value_counts().values,
             labels={'x': 'Genre', 'y': 'Value Counts'},
             color = colors,
            title='Count of Songs belonging to each Genre')
fig.show()

Таким образом, мы можем заметить, что набор данных довольно сбалансирован, то есть в каждой категории равное количество образцов, что делает нашу работу немного проще. Несбалансированный набор данных является проблематичным в том, что один класс, который сравнительно присутствует в большем количестве, становится более представленным по сравнению с другими классами, что приводит к предвзятости в отношении этой конкретной задачи. В таких случаях точность становится неточным показателем производительности модели, поскольку вероятность попадания в один класс не равна, поэтому даже случайный классификатор будет хорошо работать с набором данных (т. Е. Показывать высокую точность).

fig = px.histogram(dataframe, x="popularity", nbins = 20, title = 'Distribution of Popularity Values')
fig.update_layout(bargap = 0.2)
fig.show()

Мы можем видеть распределение значений популярности песен. Режим гистограммы указывает на диапазон 50–54, что означает, что большинство песен находятся в этой области популярности. Песни с высоким/чрезвычайно высоким значением популярности — [80–100] довольно низкие, что логично, поскольку лишь несколько песен попадают в верхние чарты рекламных щитов. Значения этого конкретного столбца симметричны относительно медианы, поэтому этот нормально распределенный столбец может быть очень важной особенностью при определении жанра конкретной песни.

fig = px.histogram(dataframe, x="danceability", nbins = 20, title = 'Distribution of Danceability Values', color_discrete_sequence = ['darksalmon'])
fig.update_layout(bargap = 0.2)
fig.show()

Подобно популярности, танцевальность также является симметричным распределением вдоль медианы, что может быть потенциально хорошим индикатором для классификации жанров. Интуиция, стоящая за этой колонкой, может быть истолкована так: некоторые песни спокойные и, следовательно, имеют низкую танцевальную ценность по сравнению с некоторыми песнями, которые являются тяжелыми песнями для вечеринок и имеют высокую танцевальную ценность.

fig = px.histogram(dataframe, x="energy", nbins = 20, title = 'Distribution of Energy Values', color_discrete_sequence = ['seagreen'])
fig.update_layout(bargap = 0.2)
fig.show()
fig = px.histogram(dataframe, x="acousticness", nbins = 20, title = 'Distribution of Acousticness Values', color_discrete_sequence=['indianred'])
fig.update_layout(bargap = 0.2)
fig.show()
fig = px.histogram(dataframe, x="instrumentalness", nbins = 20, title = 'Distribution of Instrumentalness Values', color_discrete_sequence=['darkturquoise'])
fig.update_layout(bargap = 0.2)
fig.show()
fig = px.histogram(dataframe, x="liveness", nbins = 20, title = 'Distribution of Acousticness Values', color_discrete_sequence=['purple'])
fig.update_layout(bargap = 0.2)
fig.show()
key_names = [col for col in dataframe['key'].unique() if type(col)!=float]
fig = px.bar(x=key_names, 
             y=dataframe['key'].value_counts().values,
             labels={'x': 'Key', 'y': 'Value Counts'},
             color = colors + ['purple', 'orange'] ,
            title='Count of Key types')
fig.show()
dataframe.isnull().sum()
n = len(dataframe)
for col in dataframe.columns:
    missing = sum(dataframe[col].isnull())
    print(f'Percentage of Missing values in {col} column : {(missing / n) * 100} %')

Набор данных представляет собой набор данных хорошего качества с почти незначительными отсутствующими значениями в каждом столбце. Тем не менее мы отбросим столбцы с отсутствующими значениями, чтобы наши модели не столкнулись с проблемами для значений nan во время обучения.

dataframe = dataframe.dropna()
dataframe.isnull().sum()
duplicatedRows = dataframe[dataframe.duplicated()]
print('Duplicated Rows in the Dataset are :')
print(len(duplicatedRows))

Набор данных также не содержит повторяющихся значений, о которых нужно позаботиться вручную.

profile = dataframe.profile_report()
profile
profile.to_file(output_file="Dataset_Profile_Report.html")
dataframe.info()

После этого краткого EDA пришло время создать модели для прогнозирования. Что касается инженерной части, мы должны позаботиться о следующих вещах:

  1. Удаление столбцов, которые не вносят или могут не вносить большой вклад в предсказание.
  2. Работа с пропущенными значениями или значениями Nan и приписывание им среднего значения для непрерывных переменных и режима для категориальных переменных.
dataframe.head()
train = dataframe.drop(columns = ['instance_id', 'artist_name', 'track_name', 'obtained_date'])
continuous_columns = [col for col in train.columns if dataframe[col].dtype == float]
categorical_columns = [col for col in train.columns if dataframe[col].dtype == object]
continuous_columns, categorical_columns
impute_sum = 0
for val in train['tempo']:
    if val!= '?':
        impute_sum += float(val)
impute_val = impute_sum // len(train['tempo'])

Некоторые значения из класса «tempo» ошибочно включали токен «?», что делало тип данных столбца объектом. Мы можем заменить теги «?» средним значением всех других значений в столбце.

train['tempo'].replace(to_replace = '?', value = impute_val, inplace=True)
train['tempo'] = train['tempo'].astype(float)

Некоторые значения из столбца «duration_ms» ошибочно включали токен «-1». Это неверная интерпретация, поскольку если название песни и имя исполнителя существуют, то они должны иметь продолжительность во времени, которая не является отрицательной (что невозможно). Сделаем аналогичное вменение для этого поля.

train['duration_ms'].replace(to_replace = -1, value = train['duration_ms'].mean(), inplace=True)

В наших входных функциях есть несколько категориальных переменных, а наша целевая переменная — это категориальная функция, представленная строками. Чтобы передать их в нашу модель, нам нужно преобразовать их в закодированное представление. Мы будем использовать класс LabelEncoder из sklearn, который кодирует целевые метки со значением от 0 до n_classes-1.

le = LabelEncoder()
for col in categorical_columns:
    train[col] = le.fit_transform(train[col])
    print(le.classes_)
train.columns
# Training with all columns included
X = train.drop(columns = ['music_genre'])
Y = train['music_genre'].values
​
temp_X = train.drop(columns = ['tempo', 'duration_ms', 'music_genre'])
temp_Y = train['music_genre'].values
from sklearn.model_selection import train_test_split
xtrain, xtest, ytrain, ytest = train_test_split(X, Y, test_size = 0.2, random_state = 786)
for ds in [xtrain, xtest, ytrain, ytest]:
    print(f'Shape : {ds.shape}')
model1 = 'Logistic Regression'
lr = LogisticRegression(solver='liblinear')
model = lr.fit(xtrain, ytrain)
ypred = lr.predict(xtest)
lr_cm = confusion_matrix(ytest, ypred)
lr_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(lr_cm)
print('\n')
print(f'Accuracy of {model1} : {lr_acc *100} \n')
print(classification_report(ytest, ypred))
from sklearn.model_selection import train_test_split
xtrain, xtest, ytrain, ytest = train_test_split(temp_X, temp_Y, test_size = 0.2, random_state = 786)
model1 = 'Logistic Regression'
lr = LogisticRegression(solver='liblinear')
model = lr.fit(xtrain, ytrain)
ypred = lr.predict(xtest)
lr_cm = confusion_matrix(ytest, ypred)
lr_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(lr_cm)
print('\n')
print(f'Accuracy of {model1} : {lr_acc *100} \n')
print(classification_report(ytest, ypred))
model2 = 'Naive Bayes'
nb = GaussianNB()
nb.fit(xtrain, ytrain)
ypred = nb.predict(xtest)
nb_cm = confusion_matrix(ytest, ypred)
nb_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(nb_cm)
print('\n')
print(f'Accuracy of {model2} : {nb_acc * 100} \n')
print(classification_report(ytest, ypred))
model3 = 'Random Forest Classifer'
rf = RandomForestClassifier(n_estimators = 20, random_state = 2, max_depth = 5)
rf.fit(xtrain,ytrain)
ypred = rf.predict(xtest)
rf_cm = confusion_matrix(ytest, ypred)
rf_acc = accuracy_score(ytest, ypred)
print("confussion matrix")
print(rf_cm)
print("\n")
print(f"Accuracy of {model3} : {rf_acc*100}\n")
print(classification_report(ytest,ypred))
model4 = 'K Neighbors Classifier'
knn = KNeighborsClassifier(n_neighbors = 10)
knn.fit(xtrain, ytrain)
ypred = knn.predict(xtest)
knn_cm = confusion_matrix(ytest, ypred)
knn_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(knn_cm)
print('\n')
print(f'Accuracy of {model4} : {knn_acc * 100} \n')
print(classification_report(ytest, ypred))
model5 = 'DecisionTreeClassifier'
dt = DecisionTreeClassifier(criterion = 'entropy', random_state = 0, max_depth = 6)
dt.fit(xtrain, ytrain)
ypred = dt.predict(xtest)
dt_cm = confusion_matrix(ytest, ypred)
dt_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(dt_cm)
print('\n')
print(f'Accuracy of {model5} : {dt_acc * 100} \n')
print(classification_report(ytest, ypred))
model6 = 'Support Vector Classifier'
svc = SVC(kernel = 'rbf', C = 2)
svc.fit(xtrain, ytrain)
ypred = svc.predict(xtest)
svc_cm = confusion_matrix(ytest, ypred)
svc_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(svc_cm)
print('\n')
print(f'Accuracy of {model6} : {svc_acc * 100} \n')
print(classification_report(ytest, ypred))
model7 = 'Extreme Gradient Boosting'
xgb = XGBClassifier()
xgb.fit(xtrain, ytrain)
ypred = xgb.predict(xtest)
xgb_cm = confusion_matrix(ytest, ypred)
xgb_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(xgb_cm)
print('\n')
print(f'Accuracy of {model7} : {xgb_acc * 100} \n')
print(classification_report(ytest, ypred))
model8 = 'Light Gradient Boosting Machine'
lgb = LGBMClassifier()
lgb.fit(xtrain, ytrain)
ypred = lgb.predict(xtest)
lgb_cm = confusion_matrix(ytest, ypred)
lgb_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(lgb_cm)
print('\n')
print(f'Accuracy of {model7} : {lgb_acc * 100} \n')
print(classification_report(ytest, ypred))
from sklearn.feature_selection import RFE
selector = RFE(lgb, n_features_to_select=1)
selector.fit(xtrain, ytrain)
print(f"Model's Feature Importance")
for i in range(len(selector.ranking_)):
    print(f"#{i+1}: {temp_X.columns[selector.ranking_[i]-1]} ")
from sklearn.model_selection import cross_val_score
import warnings
warnings.filterwarnings('ignore')

Перекрестная проверка в основном используется в прикладном машинном обучении для оценки навыков модели машинного обучения на невидимых данных. То есть использовать ограниченную выборку, чтобы оценить, как модель, как ожидается, будет работать в целом, когда она используется для прогнозирования данных, не используемых во время обучения модели.

kf =KFold(n_splits=10, shuffle=True, random_state=24)
​
accuracy_logr = cross_val_score(lr, xtrain, ytrain, scoring="accuracy", cv=kf)
accuracy_dtree = cross_val_score(xgb, xtrain, ytrain, scoring="accuracy", cv=kf)
accuracy_nb = cross_val_score(lgb, xtrain, ytrain, scoring="accuracy", cv=kf)
​
accuracy_logr = accuracy_logr.mean()
accuracy_dtree = accuracy_dtree.mean()
accuracy_nb = accuracy_nb.mean()
​
print('k-fold Cross Validation Accuracy Outputs:')
print('-')
print("Logistic Regression:", round(accuracy_logr*100,2),"%")
print("Xtreme Gradient Boosting:", round(accuracy_dtree*100,2),"%")
print("Light GBM:", round(accuracy_nb*100,2),"%")

Использование Optuna для автоматической настройки гиперпараметров модели LGBM.

import optuna.integration.lightgbm as lgb
dtrain = lgb.Dataset(xtrain, label = ytrain)
dtest = lgb.Dataset(xtest, label = ytest)
params = {'objective' : 'multiclass', 'num_class' : 10,  'metric' : 'multi_logloss', 'verbosity' : -1, 'boosting_type' : 'gbdt'}
model = lgb.train(params, dtrain, valid_sets = [dtest], verbose_eval = 100, early_stopping_rounds = 100)
params = model.params
params
from lightgbm import LGBMClassifier
model = LGBMClassifier(**params)
model.fit(xtrain, ytrain, eval_set = ((xtest, ytest)), early_stopping_rounds = 50, verbose = 0)
ypred = model.predict(xtest)
lgb_cm = confusion_matrix(ytest, ypred)
lgb_acc = accuracy_score(ytest, ypred)
print('Confusion Matrix')
print(lgb_cm)
print('\n')
print(f'Accuracy of {model7} : {lgb_acc * 100} \n')
print(classification_report(ytest, ypred))

Большое спасибо за выполнение. Для начинающего блоггера очень важно, что вы приложили усилия, чтобы пролистать всю страницу, неважно, понравилось вам это или нет. Я благодарен.

Ознакомьтесь со всей записной книжкой в ​​моем профиле Kaggle с выводом визуализаций по этой ссылке:

Предсказание музыкального жанра

Свяжитесь со мной по адресу:

Линкедин

Каггле.