Merge pull request 'istyukov_timofey_lab_5 is ready' (#293) from istyukov_timofey_lab_5 into main

Reviewed-on: http://student.git.athene.tech/Alexey/IIS_2023_1/pulls/293
This commit is contained in:
Alexey 2024-01-13 09:44:13 +04:00
commit 81ec79f7fe
8 changed files with 50222 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,108 @@
# Лабораторная работа №5. Регрессия
## 12 вариант
___
### Задание:
Использовать регрессию по варианту для своих данных по варианту,
самостоятельно сформулировав задачу. Оценить, насколько хорошо она
подходит для решения сформулированной вами задачи.
### Вариант:
- Тип регрессии: **Логистическая регрессия**
### Вариант набора данных по курсовой работе:
- Прогнозирование музыкальных жанров ("Prediction of music genre")
___
### Запуск
- Запустить файл lab5.py
### Используемые технологии
- Язык программирования **Python**
- Среда разработки **PyCharm**
- Библиотеки:
* pandas
* sklearn
* matplotlib
* warnings
### Описание программы
**Набор данных (Kaggle):** Полный список жанров, включенных в CSV:
«Электронная музыка», «Аниме», «Джаз», «Альтернатива», «Кантри», «Рэп»,
«Блюз», «Рок», «Классика», «Хип-хоп».
**Задача, решаемая регрессией:**
Предсказание популярности нового музыкального трека на основе его
определённых характеристик. Регрессионная модель может предсказывать
числовую оценку популярности трека, что может быть полезно
для музыкальных платформ по типу Spotify.
**Задача оценки:**
- Вычисление 5 важных признаков и удаление из классификации ненужных
- Прогноз на тестовом на наборе и расчёт точности.
- Формирование матрицы путаницы для большего понимания.
- Формирование отчёта классификации
---
### Пример работы
*Датасет, сформированный из случайных строк csv-файла.*
![Graphics](1_dataset.jpg)
---
*Вычисленные характеристики признаков. Меткой True и рангом №1
указаны 5 важных для популярности музыкального трека признаков.*
*Например, danceability (танцевальность) трека оказалось важным признаком,
а duration_ms (длительность в милисекундах) — самый незначительный признак.*
![Graphics](2_features.jpg)
---
*Матрица путаницы — это табличное представление прогнозов, сделанных моделью
классификации, показывающее количество правильных и неправильных прогнозов
для каждого класса. На пересечениях n-строки и n-столба показаны верные прогнозы
признака с индексом i. На данной матрице видно, что на популярности уровня 0 и 3
не было ни одного верного предсказания.*
![Graphics](3_confusion_matrix.jpg)
---
*Оценка точности модели. По матрице путаницы можно было заметить, что оценка
по значению чуть больше 50%, так как количество верных и неверных прогнозов
не сильно отличается.*
![Graphics](4_score.jpg)
---
*На отчёте также можно заметить по нулям у уровней популярности 0 и 3,
что там ни одно значение не было верно предсказано.*
![Graphics](5_report.jpg)
### Вывод
Итак, можно сказать, что с поставленной задачей логистическая регрессия больше
справилась, чем не справилась. Но в то же время популярность трека — неоднозначный
признак. Нельзя по характеристикам музыкального трека точно сказать, насколько
он взлетит в чартах. Считаю, что именно поэтому программа не смогла предсказать
нулевую и высшую популярность, а назначила тестовой выборке лишь средние
значения популярности.
Логистическая регрессия выполняется быстро и относительно несложно, в ней удобно
интерпретировать результаты. Хотя по сути это метод бинарной классификации,
его также можно применять к задачам мультиклассов, что я и сделал (было бы
нелогично обозначать популярность лишь метками True и False, когда можно
её разделить на уровни).
Также логистическая регрессия не готова обрабатывать избыточное количество
категорических признаков. Она подвержена переподбору, что и было сделано в
данной лабораторной работе. Логистическая регрессия не будет хорошо работать
с независимыми признаки трека, которые не коррелируют с популярностью трека.

View File

@ -0,0 +1,108 @@
"""
Использовать регрессию по варианту для своих данных по варианту, самостоятельно сформулировав задачу.
Оценить, насколько хорошо она подходит для решения сформулированной вами задачи.
"""
"""
Задача, решаемая регрессией:
Предсказание популярности нового музыкального трека на основе его определённых характеристик.
Регрессионная модель может предсказывать числовую оценку популярности трека,
что может быть полезно для музыкальных платформ по типу Spotify.
"""
# 12 вариант
# Набор данных по курсовой: "Prediction of music genre"
# Тип регрессии: Логистическая регрессия
import pandas as pd
import warnings
import sklearn.exceptions
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, f1_score
from sklearn.feature_selection import RFE
DATASET_FILE = 'music_genre.csv'
def main():
df = open_dataset(DATASET_FILE)
df = df.sample(frac=.5) # отбираем 50% рандомных строк с набора данных, т.к. он большой
print("\033[92m[----------> Набор данных <----------]\033[00m")
print(df)
# Перевод ладов (минор/мажор) в числовые признаки
df_music = df.copy()
df_music['mode'] = df_music['mode'].apply(lambda x: 1 if x == 'Major' else 0)
# разделим проценты популярности на 4 уровня (от 0 до 3)
df_music['popularity'] = df_music['popularity'].apply(lambda x: int(x // 25))
X = df_music.drop(columns=['popularity']) # характеристики музыкального трека
y = df_music['popularity'] # уровень популярности
# Разделение датасета на тренировочные (95%) и тестовые данные (5%)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.05)
# Создание и обучение модели логистической регрессии
model = LogisticRegression(solver='lbfgs', max_iter=2000)
model.fit(X_train.values, y_train)
# Поиск 5 важных признаков
rfe = RFE(estimator=model, n_features_to_select=5)
rfe = rfe.fit(X_train.values, y_train)
features_ranks = pd.DataFrame({'Признак': X.columns.values, 'Выбор': rfe.support_, 'Ранг': rfe.ranking_})
print("\033[92m\n[----------> Оценка признаков, влияющих на популярность трека <----------]\033[00m")
print(features_ranks.sort_values(by=['Ранг']))
# Убираем незначительные признаки из датасета
del_features = features_ranks['Признак'][features_ranks['Ранг'] == True]
X_train = X_train[del_features]
X_test = X_test[del_features]
# Переобучаем на них модель
model.fit(X_train.values, y_train)
# Предсказание на тестовых данных уровня популярности трека
y_pred = model.predict(X_test.values)
# Формирование матрицы путаницы
cm = confusion_matrix(y_test, y_pred)
print("\033[92m\n[----------> Матрица путаницы <----------]\033[00m")
print(cm)
pred_true = sum(cm[i][i] for i in range(len(cm))) # верные прогнозы
pred_false = cm.sum() - pred_true # неверные прогнозы
print("\033[94mКол-во правильных предсказаний (главная диагональ) = {}\033[00m" .format(pred_true))
print("\033[91mКол-во неправильных предсказаний (остальное) = {}\033[00m".format(pred_false))
print("\033[92m\n[----------> Оценка модели <----------]\033[00m")
print('Точность модели на тестовых данных: {:.2f}'.format(model.score(X_test.values, y_test)))
print("\033[92m\n[----------> Отчёт о классификации по уровням <----------]\033[00m")
# игнор ошибки отсутствия некоторых классах при прогнозе (в данном случае 0 и 3)
warnings.filterwarnings("ignore", category=sklearn.exceptions.UndefinedMetricWarning)
print(classification_report(y_test, y_pred))
print("\033[95mМаксимально предсказанный уровень: {}\033[00m".format(y_pred.max()))
# Функция считывания и очищения csv-файла
def open_dataset(csv_file):
# открываем файл с указанием знака-отделителя
df = pd.read_csv(csv_file, delimiter=',')
# выбираем необходимые признаки
df = df[['duration_ms', 'mode', 'tempo', 'instrumentalness', 'acousticness', 'speechiness',
'danceability', 'energy', 'liveness', 'valence', 'loudness', 'popularity']]
# очищаем набор данных от пустых и неподходящих значений
df = df[df['tempo'] != '?']
df = df.dropna()
return df
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load Diff