Привет!👋
Меня зовут Александр Куимов, я буду ревьюером твоего проекта. Ты можешь обращаться ко мне на "ты"😏 Надеюсь, тебя также не смутит, если я буду обращаться к тебе на "ты", но если это неудобно, обязательно скажи об этом!
Пожалуйста, не удаляй мои комментарии, они будут особенно полезны для нашей работы в случае повторной проверки проекта.
Ты также можешь реагировать на мои комментарии своими по шаблону, показанному чуть ниже. Это нужно, чтобы не создавалась путаница😉
Ты можешь найти мои комментарии, обозначенные зеленым, желтым и красным цветами, например:
Комментарий ревьюера
Все отлично!👍: В случае, если решение на отдельном шаге является полностью правильным.
Комментарий ревьюера
Некоторые замечания и рекомендации💡: В случае, когда решение на отдельном шаге станет еще лучше, если внести небольшие коррективы.
Комментарий ревьюера
На доработку🤔: В случае, когда решение на отдельном шаге требует существенной переработки и внесения правок. Напоминаю, что проект не может быть принят с первого раза, если ревью содержит комментарии, рекомендующие доработать шаги.
Комментарий студента:
👋: В такой цветовой ячейке я прошу тебя оставлять свои комментарии. Если исправляешь проект на второй итерации и выше, не забывай пожалуйста указывать номер итерации, например, "Комментарий студента v.2".
Увидев у тебя неточность, в первый раз я лишь укажу на ее наличие и дам тебе возможность самому найти и исправить ее. На реальной работе твой руководитель будет поступать также, и я пытаюсь подготовить тебя именно к работе датасаентистом. Но если ты пока не справишься с такой задачей - при следующей проверке я дам более точную подсказку!🤓
Рекомендация тарифов¶
В вашем распоряжении данные о поведении клиентов, которые уже перешли на эти тарифы (из проекта курса «Статистический анализ данных»). Нужно построить модель для задачи классификации, которая выберет подходящий тариф. Предобработка данных не понадобится — вы её уже сделали.
Постройте модель с максимально большим значением accuracy. Чтобы сдать проект успешно, нужно довести долю правильных ответов по крайней мере до 0.75. Проверьте accuracy на тестовой выборке самостоятельно.
Откройте и изучите файл¶
import pandas as pd
import random
import seaborn as sns
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, accuracy_score, mean_squared_error
from sklearn.tree import DecisionTreeClassifier
df = pd.read_csv('/datasets/users_behavior.csv')
print(df.info())
df.columns
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3214 entries, 0 to 3213 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 calls 3214 non-null float64 1 minutes 3214 non-null float64 2 messages 3214 non-null float64 3 mb_used 3214 non-null float64 4 is_ultra 3214 non-null int64 dtypes: float64(4), int64(1) memory usage: 125.7 KB None
Index(['calls', 'minutes', 'messages', 'mb_used', 'is_ultra'], dtype='object')
Комментарий ревьюера
Все отлично!👍:
Если хочешь сделать ещё лучше, соблюдай стандарт PEP-8, собери импорты в одной ячейке, раздели на группы и в каждой группе отсортируй импорты в алфавитном порядке. Ссылка для ознакомления: https://pythonworld.ru/osnovy/pep-8-rukovodstvo-po-napisaniyu-koda-na-python.html
df.head(10)
| calls | minutes | messages | mb_used | is_ultra | |
|---|---|---|---|---|---|
| 0 | 40.0 | 311.90 | 83.0 | 19915.42 | 0 |
| 1 | 85.0 | 516.75 | 56.0 | 22696.96 | 0 |
| 2 | 77.0 | 467.66 | 86.0 | 21060.45 | 0 |
| 3 | 106.0 | 745.53 | 81.0 | 8437.39 | 1 |
| 4 | 66.0 | 418.74 | 1.0 | 14502.75 | 0 |
| 5 | 58.0 | 344.56 | 21.0 | 15823.37 | 0 |
| 6 | 57.0 | 431.64 | 20.0 | 3738.90 | 1 |
| 7 | 15.0 | 132.40 | 6.0 | 21911.60 | 0 |
| 8 | 7.0 | 43.39 | 3.0 | 2538.67 | 1 |
| 9 | 90.0 | 665.41 | 38.0 | 17358.61 | 0 |
Вынес импорты в отдельную ячейку и отсортировал. head() теперь имеется.
Целевой признак находится в колонке "is_ultra", а признаки объектов находятся в других столбцах.
print(df['calls'].corr(df['minutes'],method='spearman'))
sns.pairplot(data = df , hue = 'is_ultra')
0.9786843069540279
<seaborn.axisgrid.PairGrid at 0x7f1d110988e0>
Учел советы (большое спасибо, обо всем этом не было упоминаний по ходу обучения).
Действительно корреляция данных в колонках "minutes" и "calls" имеется, и при этом связь положительная сильная.
Комментарий ревьюера
Все отлично!👍: С данными познакомились, проведен первичный аналитический осмотр!) Полученная информация поможет нам в дальнейшем с предобработкой данных.
Хочу тут затронуть термин мультиколлениарности, чтобы было представление о нем. В наших данных есть мультиколлинеарность в фичах calls и minutes. Желательно проверить это методом
.corr(method='spearman')В нашем проекте мы сосредоточены на предсказаниях, поэтому проблема мультиколлинеарности, если она и есть, для нас не страшна, на предсказаниях она не сказывается (она влияет на значения коэффициентов логистической регрессии
.coef_(это, кстати, о важности признаков), для скоррелированных признаков они смещены, поэтому их интерпретация становится невозможной, но для нас сейчас это не имеет значения).Рекомендую также тебе посмотреть метод
sns.pairplotс параметромhue, в который мы передаем целевой признак.
Разбейте данные на выборки¶
Поделим датасет в следующем соотношении: обучающая выборка 60%, валидационная выборка 25%, тестовая выборка 15%.
features =df.drop(['is_ultra'], axis=1) # признаки
target = df['is_ultra'] # целевой признак
features_train, features_between, target_train, target_between = train_test_split(
features, target, test_size=0.4, random_state=72)
features_valid, features_test, target_valid, target_test = train_test_split(
features_between, target_between, test_size=0.5, random_state=72)
features_train.info()
features_valid.info()
features_test.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 1928 entries, 2415 to 472 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 calls 1928 non-null float64 1 minutes 1928 non-null float64 2 messages 1928 non-null float64 3 mb_used 1928 non-null float64 dtypes: float64(4) memory usage: 75.3 KB <class 'pandas.core.frame.DataFrame'> Int64Index: 643 entries, 1021 to 340 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 calls 643 non-null float64 1 minutes 643 non-null float64 2 messages 643 non-null float64 3 mb_used 643 non-null float64 dtypes: float64(4) memory usage: 25.1 KB <class 'pandas.core.frame.DataFrame'> Int64Index: 643 entries, 274 to 1899 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 calls 643 non-null float64 1 minutes 643 non-null float64 2 messages 643 non-null float64 3 mb_used 643 non-null float64 dtypes: float64(4) memory usage: 25.1 KB
Исправил на классическое соотношение 60 / 20 / 20. Далее по коду все проверил
Исследуйте модели¶
Решающее дерево¶
Целевой признак у нас это или 0 или 1. Следовательно это задача классификации и нам потребуется два алгоритма по построению моделей: 1) Обучающие дерево 2) Случайный лес
1) Посмотрим обучающее дерево до 10 уровня.
2) Проверим линенйную регрессию
3) Посмотрим модель "случайный лес" с параметром максимального кол-ва деревьев 40.
Забегая вперед строил модели с большей глубиной и количеством деревьев. Но интересующий нас результат лежит в выше обозначенных параметрах.
for i in range(1,11):# < сделайте цикл для max_depth от 1 до 5 >
model = DecisionTreeClassifier(random_state=72, max_depth = i) # модель с заданной глубиной дерева
model.fit(features_train, target_train) # модель
predictions = model.predict(features_valid) # предсказания модели
result = accuracy_score(target_valid, predictions) # качество модели
print(f'max_depth = {i} : {result}')
max_depth = 1 : 0.76049766718507 max_depth = 2 : 0.7838258164852255 max_depth = 3 : 0.8009331259720062 max_depth = 4 : 0.80248833592535 max_depth = 5 : 0.8040435458786936 max_depth = 6 : 0.7962674961119751 max_depth = 7 : 0.7947122861586314 max_depth = 8 : 0.7916018662519441 max_depth = 9 : 0.7916018662519441 max_depth = 10 : 0.7791601866251944
Логистическая регрессия¶
model = LogisticRegression(random_state=72)
model. fit (features_train, target_train)
predictions = model. predict(features_valid)
print(" Accuracy:",accuracy_score(target_valid, predictions))
Accuracy: 0.7433903576982893
Случайный лес¶
best_model = None
best_result = 0
for est in range(10, 200, 10):
for i in range(1, 11):
model = RandomForestClassifier(random_state=72, n_estimators =est, max_depth = i ) # модель с заданным количеством деревьев
model.fit(features_train, target_train) # модель на тренировочной выборке
result = model.score(features_valid, target_valid) # качество модели на валидационной выборке
if result > best_result:
best_model = model# сохраняем наилучшую модель
best_result = result# сохраняем наилучшее значение метрики accuracy на валидационных данных
print("Accuracy наилучшей модели на валидационной выборке:", best_result)
Accuracy наилучшей модели на валидационной выборке: 0.8195956454121306
И "выигрывает" у нас случайный лес.
Комментарий ревьюера v.2
Все отлично!👍:
Часто при большом увеличении n_estimators качество на обучающей выборке может даже доходить до 100%, в то время как качество на тесте выходит на асимптоту, что сигнализирует о переобучении нашей модели. Лучший способ избежать этого – прикинуть, сколько деревьев нам достаточно, зафиксировав момент, когда качество теста еще не становится стабильно-неизменным, подробнее: https://proglib.io/p/mashinnoe-obuchenie-dlya-nachinayushchih-algoritm-sluchaynogo-lesa-random-forest-2021-08-12
Добавил:
регрессию
вывод
Добавил оглавление по моделям
Лучшая модель (дает большее кол-во верных предсказаний) это "Случайный лес" с вероятностью ~0.82.
Следом идет Решающее дерево с результатом ~0.8
Логистическая регрессия 0.74.
Прошу отметить ее в курсе не было и поверхностного знакомства я думаю не достаточно. Но спасибо за указание новых моментов.
Большое спасибо за помощь
Проверьте модель на тестовой выборке¶
predictions = best_model.predict(features_test) # получите предсказания модели
result = accuracy_score(target_test, predictions) # посчитайте качество модели
print(f'accuracy= {result}')
accuracy= 0.80248833592535
Добились мы этого меняя параметр random_state.
(бонус) Проверьте модели на адекватность¶
random_test = []
for i in range(len(target_test)):
random_test.append(random.randint(0,1))
print(accuracy_score(random_test, predictions))
0.4681181959564541
Результат в районе 50%. Что и предполагалось.
Комментарий ревьюера
Все отлично!👍:
Этап тестирования рекомендуется дополнять введением в работу baseline модели. Сравнивая результаты более сложных моделей с самой наивной, мы можем делать выводы о том, насколько далеко они вообще продвинулись в качестве предсказаний. Иногда может случиться такое, что константная модель оказывается не сильно хуже по качеству. Это может говорить либо о том, что еще есть смысл продолжить поиски "той самой" модели, либо структура данных такова, что ничего лучше константоной в принципе не найти, тогда в использовании более сложных ресурсозатратных моделей нет особого смысла. Создавать константные модели можно либо вручную, либо воспользоваться готовым алгоритмом DummyClassifier из пакета sklearn. В нашем случае подойдет модель DummyClassifier со стратегией most_frequent. Рекомендую также прочитать статью.
Итоговый комментарий ревьюера v.1
У меня сложилось хорошее впечатление о проекте. Молодец! Изучены все параметры, построено несколько моделей классификации и оценено их качество. Осмысленная аналитика и дельная модельная работа - многое удалось как надо)
Отмечу отдельные положительные моменты проекта🙂:
- тебе удалось добиться очень хорошего качества, поздравляю!
- при обучении моделей использована валидационная выборка и подбор гиперпараметров.
Есть несколько моментов всего, на которые стоит ещё раз взглянуть, я указал их в моих комментариях по ходу проекта - выводы. Предлагаю тебе доработать проект по моим комментариям, чтобы довести его до совершенства.
Если будут вопросы, обращайся, с удовольствием на них отвечу.
Итоговый комментарий ревьюера v.2
Теперь почти идеально, молодец! Принимаю твой проект)
Могу посоветовать посмотреть видеоразбор Как обучить модель по шагам в Data Science? Урок для начинающих на примере задачи регрессии
- Рекомендую тебе ознакомиться со статьями по интерпретации предсказания моделей: SHAP, + еще ссылка.
В этом проекте все шаги отработаны. С интересом читал твои размышления, код и выводы. Цель нашего проекта достигнута. Рад был тебе помочь)
Чек-лист готовности проекта¶
Поставьте 'x' в выполненных пунктах. Далее нажмите Shift+Enter.
- Jupyter Notebook открыт
- Весь код исполняется без ошибок
- Ячейки с кодом расположены в порядке исполнения
- Выполнено задание 1: данные загружены и изучены
- Выполнено задание 2: данные разбиты на три выборки
- Выполнено задание 3: проведено исследование моделей
- Рассмотрено больше одной модели
- Рассмотрено хотя бы 3 значения гипепараметров для какой-нибудь модели
- Написаны выводы по результатам исследования
- Выполнено задание 3: Проведено тестирование
- Удалось достичь accuracy не меньше 0.75