Вступ
Команда K-найближчі сусіди (KNN) алгоритм — це тип алгоритму керованого машинного навчання, який використовується для класифікації, регресії, а також виявлення викидів. Його надзвичайно легко реалізувати у найпростішій формі, але він може виконувати досить складні завдання. Це ледачий алгоритм навчання, оскільки він не має спеціальної фази навчання. Натомість він використовує всі дані для навчання під час класифікації (або регресії) нової точки даних або екземпляра.
KNN - це a непараметричний алгоритм навчання, що означає, що він нічого не передбачає щодо основних даних. Це надзвичайно корисна функція, оскільки більшість даних реального світу насправді не відповідають жодним теоретичним припущенням, наприклад лінійній роздільності, рівномірному розподілу тощо.
У цьому посібнику ми побачимо, як KNN можна реалізувати за допомогою бібліотеки Scikit-Learn Python. Перед цим ми спочатку дослідимо, як ми можемо використовувати KNN, і пояснимо теорію, що стоїть за цим. Після цього ми розглянемо Набір даних про житло в Каліфорнії ми будемо використовувати для ілюстрації алгоритму KNN та кількох його варіацій. Перш за все, ми розглянемо, як реалізувати алгоритм KNN для регресії, а потім запровадимо класифікацію KNN і виявлення викидів. Зрештою, ми зупинимося на деяких плюсах і мінусах алгоритму.
Коли слід використовувати KNN?
Припустимо, ви хотіли зняти квартиру і нещодавно дізналися, що сусідка вашого друга може здати свою квартиру через 2 тижні. Оскільки квартири ще немає на сайті оренди, як ви можете спробувати оцінити її орендну вартість?
Припустимо, ваш друг платить 1,200 доларів за оренду. Ваша орендна плата може бути приблизно такою, але квартири не зовсім однакові (орієнтація, площа, якість меблів тощо), тому було б добре мати більше даних про інші квартири.
Опитавши інших сусідів і переглянувши квартири в тому самому будинку, які були вказані на веб-сайті оренди, можна дізнатися, що орендна плата найближчих трьох сусідніх квартир становить 1,200, 1,210, 1,210 і 1,215 доларів. Ці квартири знаходяться на тому ж блоці й поверсі, що й квартира вашого друга.
Інші квартири, розташовані далі, на тому ж поверсі, але в іншому кварталі, мають орендну плату 1,400, 1,430, 1,500 і 1,470 доларів. Здається, вони дорожчі через те, що ввечері більше сонячного світла.
Враховуючи близькість квартири, здається, що ваша орендна плата становитиме близько 1,210 доларів США. Це загальне уявлення про те, що K-найближчі сусіди (KNN) алгоритм робить! Він класифікує або регресує нові дані на основі їх близькості до вже існуючих даних.
Переведіть приклад у теорію
Якщо оціночна вартість є безперервним числом, як-от вартість оренди, використовується KNN регресія. Але ми також могли б розділити квартири на категорії на основі, наприклад, мінімальної та максимальної орендної плати. Коли значення є дискретним, що робить його категорією, KNN використовується для класифікація.
Також є можливість оцінити, які сусіди настільки відрізняються від інших, що ймовірно перестануть платити орендну плату. Це те саме, що визначити, які точки даних знаходяться настільки далеко, що вони не підходять до жодного значення чи категорії, коли це відбувається, KNN використовується для виявлення викидів.
У нашому прикладі ми також уже знали орендну плату кожної квартири, що означає, що наші дані були позначені. KNN використовує раніше позначені дані, що робить його a керований алгоритм навчання.
KNN надзвичайно легко реалізувати у своїй найпростішій формі, але вона виконує досить складні завдання класифікації, регресії або виявлення викидів.
Щоразу, коли до даних додається нова точка, KNN використовує лише одну частину даних для визначення значення (регресії) або класу (класифікації) цієї доданої точки. Оскільки йому не потрібно знову переглядати всі точки, це робить його a ледачий алгоритм навчання.
KNN також не припускає нічого щодо базових характеристик даних, він не очікує, що дані підходять до певного типу розподілу, наприклад рівномірного, або бути лінійно роздільними. Це означає, що це a непараметричний алгоритм навчання. Це надзвичайно корисна функція, оскільки більшість даних реального світу насправді не відповідають жодним теоретичним припущенням.
Візуалізація різних видів використання KNN
Як було показано, інтуїція, що лежить в основі алгоритму KNN, є однією з найбільш прямих з усіх контрольованих алгоритмів машинного навчання. Алгоритм спочатку обчислює відстань нової точки даних до всіх інших точок даних навчання.
Примітка: Відстань можна виміряти різними способами. Ви можете використовувати Minkowski, Евклідова, формули Манхеттена, Махаланобіса чи Хеммінга, щоб назвати кілька показників. З даними великої розмірності евклідова відстань часто починає давати збій (висока розмірність — це… дивно), і замість неї використовується Манхеттенська відстань.
Після обчислення відстані KNN вибирає найближчі точки даних – 2, 3, 10 або будь-яке ціле число. Ця кількість балів (2, 3, 10 тощо) є K у K-Nearest Neighbours!
На останньому кроці, якщо це завдання регресії, KNN обчислить середньозважену суму K-найближчих точок для прогнозу. Якщо це завдання класифікації, нову точку даних буде присвоєно класу, до якого належить більшість вибраних K-найближчих точок.
Давайте візуалізуємо роботу алгоритму на простому прикладі. Розглянемо набір даних із двома змінними та K рівним 3.
При виконанні регресії завдання полягає в тому, щоб знайти значення нової точки даних на основі середньозваженої суми 3 найближчих точок.
КНН с K = 3
, Коли використовується для регресії:
Алгоритм KNN розпочнеться з обчислення відстані нової точки від усіх точок. Потім він знаходить 3 точки з найменшою відстанню до нової точки. Це показано на другому малюнку вище, де три найближчі точки, 47
, 58
та 79
були оточені. Після цього обчислюється зважена сума 47
, 58
та 79
– у цьому випадку ваги дорівнюють 1 – ми вважаємо всі точки рівними, але ми також можемо призначити різні ваги на основі відстані. Після обчислення зваженої суми нове значення балів є 61,33
.
А під час виконання класифікації завдання KNN класифікувати нову точку даних у "Purple"
or "Red"
клас.
КНН с K = 3
, Коли використовується для класифікації:
Алгоритм KNN розпочне роботу так само, як і раніше, обчислюючи відстань нової точки від усіх точок, знаходячи 3 найближчі точки з найменшою відстанню до нової точки, а потім, замість обчислення числа, він призначає нова точка до класу, до якого належить більшість із трьох найближчих точок, червоного класу. Тому нова точка даних буде класифікована як "Red"
.
Процес виявлення викидів відрізняється від обох описаних вище, ми поговоримо про нього більше під час його впровадження після впровадження регресії та класифікації.
примітки: код, наданий у цьому підручнику, було виконано та протестовано з наступним Блокнот Юпітера.
Набір даних Scikit-Learn California Housing
Ми збираємося використовувати Набір даних про житло в Каліфорнії щоб проілюструвати, як працює алгоритм KNN. Набір даних було отримано з перепису населення США 1990 року. Один рядок набору даних представляє перепис однієї групи блоків.
У цьому розділі ми детально розглянемо Каліфорнійський набір даних про житло, щоб ви могли отримати інтуїтивне розуміння даних, з якими ми працюватимемо. Дуже важливо ознайомитися зі своїми даними, перш ніж почати над ними працювати.
A блок група є найменшою географічною одиницею, для якої Бюро перепису населення США публікує вибіркові дані. Окрім блокової групи, використовується ще один термін – домогосподарство, домогосподарство – це група людей, які проживають у будинку.
Набір даних складається з дев'яти атрибутів:
MedInc
– середній дохід у блочній групіHouseAge
– середній вік будинку в блочній групіAveRooms
– середня кількість кімнат (надається на домогосподарство)AveBedrms
– середня кількість спалень (на домогосподарство)Population
– заповнення групи блоківAveOccup
– середня кількість членів домогосподарстваLatitude
– широта групи блоківLongitude
– довгота групи блоківMedHouseVal
– середня вартість будинку для округів Каліфорнії (сотні тисяч доларів)
Набір даних є вже є частиною бібліотеки Scikit-Learn, нам потрібно лише імпортувати його та завантажити як фрейм даних:
from sklearn.datasets import fetch_california_housing
california_housing = fetch_california_housing(as_frame=True)
df = california_housing.frame
Імпорт даних безпосередньо з Scikit-Learn імпортує більше, ніж лише стовпці та числа, і включає опис даних як Bunch
об’єкт – тому ми щойно вилучили frame
. Доступні додаткові відомості про набір даних тут.
Давайте імпортуємо Pandas і поглянемо на кілька перших рядків даних:
import pandas as pd
df.head()
Виконання коду відобразить перші п’ять рядків нашого набору даних:
MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude MedHouseVal
0 8.3252 41.0 6.984127 1.023810 322.0 2.555556 37.88 -122.23 4.526
1 8.3014 21.0 6.238137 0.971880 2401.0 2.109842 37.86 -122.22 3.585
2 7.2574 52.0 8.288136 1.073446 496.0 2.802260 37.85 -122.24 3.521
3 5.6431 52.0 5.817352 1.073059 558.0 2.547945 37.85 -122.25 3.413
4 3.8462 52.0 6.281853 1.081081 565.0 2.181467 37.85 -122.25 3.422
У цьому посібнику ми будемо використовувати MedInc
, HouseAge
, AveRooms
, AveBedrms
, Population
, AveOccup
, Latitude
, Longitude
передбачити MedHouseVal
. Щось схоже на наш мотиваційний наратив.
Тепер перейдемо безпосередньо до реалізації алгоритму KNN для регресії.
Регресія з K-найближчими сусідами за допомогою Scikit-Learn
Наразі ми ознайомилися з нашим набором даних і тепер можемо переходити до інших кроків у алгоритмі KNN.
Попередня обробка даних для регресії KNN
Під час попередньої обробки з’являються перші відмінності між завданнями регресії та класифікації. Оскільки в цьому розділі йдеться про регресію, ми відповідно підготуємо наш набір даних.
Для регресії нам потрібно передбачити інше середнє значення будинку. Для цього ми призначимо MedHouseVal
до y
і всі інші стовпці до X
просто скинувши MedHouseVal
:
y = df['MedHouseVal']
X = df.drop(['MedHouseVal'], axis = 1)
Дивлячись на наші описи змінних, ми можемо побачити, що у нас є відмінності у вимірюваннях. Щоб не гадати, скористаємося describe()
спосіб перевірки:
X.describe().T
Це призводить до:
count mean std min 25% 50% 75% max
MedInc 20640.0 3.870671 1.899822 0.499900 2.563400 3.534800 4.743250 15.000100
HouseAge 20640.0 28.639486 12.585558 1.000000 18.000000 29.000000 37.000000 52.000000
AveRooms 20640.0 5.429000 2.474173 0.846154 4.440716 5.229129 6.052381 141.909091
AveBedrms 20640.0 1.096675 0.473911 0.333333 1.006079 1.048780 1.099526 34.066667
Population 20640.0 1425.476744 1132.462122 3.000000 787.000000 1166.000000 1725.000000 35682.000000
AveOccup 20640.0 3.070655 10.386050 0.692308 2.429741 2.818116 3.282261 1243.333333
Latitude 20640.0 35.631861 2.135952 32.540000 33.930000 34.260000 37.710000 41.950000
Longitude 20640.0 -119.569704 2.003532 -124.350000 -121.800000 -118.490000 -118.010000 -114.310000
Тут ми бачимо, що mean
Значення MedInc
приблизно 3.87
і mean
Значення HouseAge
є про 28.64
, що робить його в 7.4 рази більшим, ніж MedInc
. Інші функції також мають відмінності в середньому та стандартному відхиленні – щоб побачити це, подивіться на mean
та std
значення та спостерігайте, наскільки вони віддалені одне від одного. для MedInc
std
приблизно 1.9
, Для HouseAge
, std
is 12.59
і те саме стосується інших функцій.
Ми використовуємо алгоритм, заснований на відстань і алгоритми, що базуються на відстані, сильно страждають від даних, які не мають такого ж масштабу, як-от ці дані. Шкала балів може (і на практиці майже завжди так) спотворювати реальну відстань між значеннями.
Щоб виконати масштабування функцій, ми будемо використовувати Scikit-Learn StandardScaler
клас пізніше. Якщо ми застосуємо масштабування прямо зараз (перед поділом тесту поїзда), розрахунок фактично включатиме тестові дані протікання інформацію про тестові дані в решту конвеєра. Такого роду витоку даних на жаль, зазвичай пропускається, що призводить до невідтворюваних або ілюзорних знахідок.
Розбиття даних на навчальні та тестові набори
Щоб мати можливість масштабувати наші дані без витоку, а також щоб оцінити наші результати та уникнути надмірної підгонки, ми розділимо наш набір даних на підготовку та тестування.
Простий спосіб створити тренувальний і тестовий розподіли - це train_test_split
метод від Scikit-Learn. Розподіл не розподіляється лінійно в певний момент, а вибірки X% і Y% випадковим чином. Щоб зробити цей процес відтворюваним (щоб метод завжди відбирав ті самі точки даних), ми встановимо random_state
аргумент до певного SEED
:
from sklearn.model_selection import train_test_split
SEED = 42
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=SEED)
Цей фрагмент коду вибирає 75% даних для навчання та 25% даних для тестування. Змінюючи test_size
до 0.3, наприклад, ви можете тренуватися з 70% даних і тестувати з 30%.
Використовуючи 75% даних для навчання та 25% для тестування, із 20640 записів навчальний набір містить 15480, а тестовий — 5160. Ми можемо швидко перевірити ці числа, надрукувавши довжину повного набору даних і розділених даних. :
len(X)
len(X_train)
len(X_test)
Чудово! Тепер ми можемо встановити масштабувальник даних на X_train
набір і масштаб обох X_train
та X_test
без витоку даних X_test
в X_train
.
Масштабування функцій для регресії KNN
Шляхом імпорту StandardScaler
, створюючи його екземпляр, підганяючи його відповідно до наших даних потягу (запобігаючи витоку), і перетворюючи як потяг, так і тестові набори даних, ми можемо виконати масштабування функції:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
Примітка: Оскільки ти будеш часто дзвонити scaler.fit(X_train)
подальшою scaler.transform(X_train)
– можна назвати єдиним scaler.fit_transform(X_train)
подальшою scaler.transform(X_test)
щоб зробити дзвінок коротшим!
Тепер наші дані масштабовані! Масштабувальник підтримує лише точки даних, а не назви стовпців, коли застосовується до a DataFrame
. Давайте знову організуємо дані у DataFrame з назвами стовпців і використанням describe()
спостерігати за змінами в mean
та std
:
col_names=['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude']
scaled_df = pd.DataFrame(X_train, columns=col_names)
scaled_df.describe().T
Це дасть нам:
count mean std min 25% 50% 75% max
MedInc 15480.0 2.074711e-16 1.000032 -1.774632 -0.688854 -0.175663 0.464450 5.842113
HouseAge 15480.0 -1.232434e-16 1.000032 -2.188261 -0.840224 0.032036 0.666407 1.855852
AveRooms 15480.0 -1.620294e-16 1.000032 -1.877586 -0.407008 -0.083940 0.257082 56.357392
AveBedrms 15480.0 7.435912e-17 1.000032 -1.740123 -0.205765 -0.108332 0.007435 55.925392
Population 15480.0 -8.996536e-17 1.000032 -1.246395 -0.558886 -0.227928 0.262056 29.971725
AveOccup 15480.0 1.055716e-17 1.000032 -0.201946 -0.056581 -0.024172 0.014501 103.737365
Latitude 15480.0 7.890329e-16 1.000032 -1.451215 -0.799820 -0.645172 0.971601 2.953905
Longitude 15480.0 2.206676e-15 1.000032 -2.380303 -1.106817 0.536231 0.785934 2.633738
Подивіться, як зараз виглядають усі стандартні відхилення 1
а коштів стало менше. Це те, що робить наші дані більш рівномірний! Давайте навчимо та оцінимо регресор на основі KNN.
Навчання та прогнозування регресії KNN
Інтуїтивно зрозумілий і стабільний API Scikit-Learn робить навчання регресорів і класифікаторів дуже простим. Давайте імпортуємо KNeighborsRegressor
клас з sklearn.neighbors
модуль, створіть його екземпляр і підберіть до даних нашого поїзда:
from sklearn.neighbors import KNeighborsRegressor
regressor = KNeighborsRegressor(n_neighbors=5)
regressor.fit(X_train, y_train)
У наведеному вище коді n_neighbors
є значенням для Kабо кількість сусідів, яку алгоритм враховуватиме для вибору нового середнього значення будинку. 5
є значенням за замовчуванням для KNeighborsRegressor()
. Немає ідеального значення для K, і його вибирають після тестування та оцінки, однак для початку, 5
є загальновживаним значенням для KNN і тому встановлено як значення за замовчуванням.
Останнім кроком є прогнозування даних наших тестів. Для цього виконайте такий сценарій:
y_pred = regressor.predict(X_test)
Тепер ми можемо оцінити, наскільки добре наша модель узагальнює нові дані, для яких у нас є мітки (основна правда) – тестовий набір!
Оцінка алгоритму регресії KNN
Найбільш часто використовуваними показниками регресії для оцінки алгоритму є середня абсолютна помилка (MAE), середня квадратична помилка (MSE), середня квадратична помилка (RMSE) і коефіцієнт детермінації (R2):
- Середня абсолютна помилка (MAE): коли ми віднімаємо прогнозовані значення від фактичних значень, отримуємо похибки, підсумовуємо абсолютні значення цих похибок і отримуємо їх середнє значення. Цей показник дає уявлення про загальну похибку для кожного передбачення моделі, чим менше (ближче до 0), тим краще:
$$
mae = (frac{1}{n})sum_{i=1}^{n}left | Фактичний – прогнозований правильний |
$$
Примітка: Ви також можете зіткнутися з y
та ŷ
(читається як y-hat) позначення в рівняннях. The y
відноситься до фактичних значень і ŷ
до прогнозованих значень.
- Середня квадратична помилка (MSE): подібний до метрики MAE, але зводить у квадрат абсолютні значення помилок. Крім того, як і з MAE, чим менше або ближче до 0, тим краще. Значення MSE зведено в квадрат, щоб зробити великі помилки ще більшими. Одна річ, на яку слід звернути пильну увагу, це те, що це показник, як правило, важко інтерпретувати через розмір його значень і той факт, що вони не в тому ж масштабі, що й дані.
$$
mse = sum_{i=1}^{D}(Фактичний – Прогнозований)^2
$$
- Середньоквадратична помилка (RMSE): намагається вирішити проблему інтерпретації, поставлену з MSE, отримавши квадратний корінь з його кінцевого значення, щоб повернути його до тих самих одиниць даних. Це легше інтерпретувати і добре, коли нам потрібно відобразити або показати фактичне значення даних з помилкою. Він показує, наскільки дані можуть відрізнятися, тому, якщо ми маємо RMSE 4.35, наша модель може зробити помилку або тому, що вона додала 4.35 до фактичного значення, або потрібна 4.35, щоб отримати фактичне значення. Чим ближче до 0, тим краще.
$$
rmse = sqrt{ sum_{i=1}^{D}(фактичний – прогнозований)^2}
$$
Команда mean_absolute_error()
та mean_squared_error()
методи sklearn.metrics
можна використовувати для обчислення цих показників, як видно з наступного фрагмента:
from sklearn.metrics import mean_absolute_error, mean_squared_error
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = mean_squared_error(y_test, y_pred, squared=False)
print(f'mae: {mae}')
print(f'mse: {mse}')
print(f'rmse: {rmse}')
Висновок наведеного вище сценарію виглядає так:
mae: 0.4460739527131783
mse: 0.4316907430948294
rmse: 0.6570317671884894
R2 можна розрахувати безпосередньо за допомогою score()
метод:
regressor.score(X_test, y_test)
Які результати:
0.6737569252627673
Результати показують, що загальна похибка та середня похибка нашого алгоритму KNN приблизно дорівнюють 0.44
та 0.43
. Крім того, RMSE показує, що ми можемо перевищити або знизити фактичне значення даних шляхом додавання 0.65
або віднімання 0.65
. Наскільки це добре?
Давайте перевіримо, як виглядають ціни:
y.describe()
count 20640.000000
mean 2.068558
std 1.153956
min 0.149990
25% 1.196000
50% 1.797000
75% 2.647250
max 5.000010
Name: MedHouseVal, dtype: float64
Середнє значення 2.06
а стандартне відхилення від середнього становить 1.15
тому наша оцінка ~0.44
не дуже зірковий, але не дуже поганий.
З Р2, що ближче до 1 ми отримаємо (або 100), то краще. Р2 повідомляє, скільки змін у даних або даних дисперсія розуміються або пояснені від KNN.
$$
R^2 = 1 – frac{сума(фактична – прогнозована)^2}{сума(фактична – фактична середня)^2}
$$
Зі значенням 0.67
, ми бачимо, що наша модель пояснює 67% дисперсії даних. Це вже більше 50%, що нормально, але не дуже добре. Чи можна якось зробити краще?
Ми використали попередньо визначене K зі значенням 5
, тому ми використовуємо 5 сусідів для прогнозування наших цілей, що не обов’язково є найкращим числом. Щоб зрозуміти, яка була б ідеальна кількість Ks, ми можемо проаналізувати помилки нашого алгоритму та вибрати K, який мінімізує втрати.
Пошук найкращого K для регресії KNN
В ідеалі ви побачите, яка метрика більше підходить до вашого контексту, але зазвичай цікаво перевірити всі метрики. Коли ви можете перевірити їх усі, зробіть це. Тут ми покажемо, як вибрати найкращий K, використовуючи лише середню абсолютну похибку, але ви можете змінити його на будь-який інший показник і порівняти результати.
Для цього ми створимо цикл for і запустимо моделі, які мають від 1 до X сусідів. Під час кожної взаємодії ми будемо обчислювати MAE і будувати графік кількості Ks разом із результатом MAE:
error = []
for i in range(1, 40):
knn = KNeighborsRegressor(n_neighbors=i)
knn.fit(X_train, y_train)
pred_i = knn.predict(X_test)
mae = mean_absolute_error(y_test, pred_i)
error.append(mae)
Тепер давайте побудуємо графік error
s:
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(range(1, 40), error, color='red',
linestyle='dashed', marker='o',
markerfacecolor='blue', markersize=10)
plt.title('K Value MAE')
plt.xlabel('K Value')
plt.ylabel('Mean Absolute Error')
Дивлячись на графік, здається, що найнижче значення MAE має значення K 12
. Давайте ближче подивимось на графік, щоб бути впевненими, відобразивши менше даних:
plt.figure(figsize=(12, 6))
plt.plot(range(1, 15), error[:14], color='red',
linestyle='dashed', marker='o',
markerfacecolor='blue', markersize=10)
plt.title('K Value MAE')
plt.xlabel('K Value')
plt.ylabel('Mean Absolute Error')
Ознайомтеся з нашим практичним практичним посібником із вивчення Git з передовими методами, прийнятими в галузі стандартами та включеною шпаргалкою. Припиніть гуглити команди Git і фактично вчитися це!
Ви також можете отримати найменшу помилку та індекс цієї точки за допомогою вбудованого min()
(працює зі списками) або перетворити список на масив NumPy і отримати argmin()
(індекс елемента з найменшим значенням):
import numpy as np
print(min(error))
print(np.array(error).argmin())
Ми почали рахувати сусідів з 1, тоді як масиви базуються на 0, тому 11-й індекс дорівнює 12 сусідам!
Це означає, що нам потрібно 12 сусідів, щоб мати можливість передбачити точку з найменшою помилкою MAE. Ми можемо знову виконати модель і метрику з 12 сусідами, щоб порівняти результати:
knn_reg12 = KNeighborsRegressor(n_neighbors=12)
knn_reg12.fit(X_train, y_train)
y_pred12 = knn_reg12.predict(X_test)
r2 = knn_reg12.score(X_test, y_test)
mae12 = mean_absolute_error(y_test, y_pred12)
mse12 = mean_squared_error(y_test, y_pred12)
rmse12 = mean_squared_error(y_test, y_pred12, squared=False)
print(f'r2: {r2}, nmae: {mae12} nmse: {mse12} nrmse: {rmse12}')
Виводить наступний код:
r2: 0.6887495617137436,
mae: 0.43631325936692505
mse: 0.4118522151025172
rmse: 0.6417571309323467
З 12 сусідами наша модель KNN тепер пояснює 69% дисперсії в даних і втратила трохи менше, починаючи з 0.44
до 0.43
, 0.43
до 0.41
та 0.65
до 0.64
з відповідними показниками. Це не дуже велике покращення, але все ж це покращення.
Примітка: Продовжуючи цей аналіз, проведення дослідницького аналізу даних (EDA) разом із аналізом залишків може допомогти вибрати функції та досягти кращих результатів.
Ми вже бачили, як використовувати KNN для регресії, але що, якби ми хотіли класифікувати точку замість прогнозування її значення? Тепер ми можемо розглянути, як використовувати KNN для класифікації.
Класифікація за допомогою K-Nearest Neighbors із Scikit-Learn
У цьому завданні замість передбачення постійного значення ми хочемо передбачити клас, до якого належать ці групи блоків. Для цього ми можемо розділити середню вартість будинку для районів на групи з різними діапазонами вартості будинку або бункерів.
Якщо ви хочете використовувати безперервне значення для класифікації, ви зазвичай можете об’єднати дані. Таким чином можна передбачати групи, а не значення.
Попередня обробка даних для класифікації
Давайте створимо контейнери даних, щоб перетворити наші безперервні значення в категорії:
df["MedHouseValCat"] = pd.qcut(df["MedHouseVal"], 4, retbins=False, labels=[1, 2, 3, 4])
Потім ми можемо розділити наш набір даних на його атрибути та мітки:
y = df['MedHouseValCat']
X = df.drop(['MedHouseVal', 'MedHouseValCat'], axis = 1)
Оскільки ми використовували MedHouseVal
для створення контейнерів, нам потрібно скинути стовпець MedHouseVal
стовпець і MedHouseValCat
стовпці з X
. Таким чином, DataFrame
міститиме перші 8 стовпців набору даних (тобто атрибути, функції), тоді як наш y
міститиме лише MedHouseValCat
призначена мітка.
Примітка: Ви також можете вибрати стовпці за допомогою .iloc()
замість того, щоб скинути їх. Під час відкидання майте на увазі, що вам потрібно призначити y
значення перед призначенням X
значення, тому що ви не можете призначити випущений стовпець a DataFrame
до іншого об’єкта в пам’яті.
Розбиття даних на навчальні та тестові набори
Як це було зроблено з регресією, ми також розділимо набір даних на тренувальну та тестову частини. Оскільки ми маємо різні дані, нам потрібно повторити цей процес:
from sklearn.model_selection import train_test_split
SEED = 42
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=SEED)
Ми знову використаємо стандартне значення Scikit-Learn із 75% тренувальних даних і 25% тестових даних. Це означає, що ми матимемо таку саму кількість записів, що й у регресії.
Масштабування ознак для класифікації
Оскільки ми маємо справу з тим самим необробленим набором даних і його змінними одиницями вимірювання, ми знову виконаємо масштабування функції таким же чином, як і для наших даних регресії:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
Навчання та прогнозування для класифікації
Після групування, поділу та масштабування даних ми нарешті можемо розмістити на них класифікатор. Для прогнозу ми знову використаємо 5 сусідів як базову лінію. Ви також можете створити екземпляр KNeighbors_
без будь-яких аргументів, і він автоматично використовуватиме 5 сусідів. Тут замість імпорту KNeighborsRegressor
, ми будемо імпортувати KNeighborsClassifier
, клас:
from sklearn.neighbors import KNeighborsClassifier
classifier = KNeighborsClassifier()
classifier.fit(X_train, y_train)
Після встановлення KNeighborsClassifier
, ми можемо передбачити класи тестових даних:
y_pred = classifier.predict(X_test)
Час оцінити прогнози! Чи прогнозування класів буде кращим підходом, ніж прогнозування значень у цьому випадку? Давайте оцінимо алгоритм, щоб побачити, що вийде.
Оцінка KNN для класифікації
Для оцінки класифікатора KNN ми також можемо використовувати score
метод, але він виконує інший показник, оскільки ми оцінюємо класифікатор, а не регресор. Основним показником класифікації є accuracy
– описує, скільки прогнозів наш класифікатор отримав правильно. Найнижче значення точності — 0, а найвище — 1. Зазвичай ми множимо це значення на 100, щоб отримати відсоток.
$$
точність = frac{текст{кількість правильних прогнозів}}{текст{загальна кількість прогнозів}}
$$
Примітка: Надзвичайно важко отримати 100% точність будь-яких реальних даних, якщо це станеться, майте на увазі, що може статися якийсь витік або щось не так – немає консенсусу щодо ідеального значення точності, і воно також залежить від контексту. Залежно від вартість помилки (як це погано, якщо ми довіряємо класифікатору, а він виявляється помилковим), прийнятний рівень помилок може становити 5%, 10% або навіть 30%.
Оцінимо наш класифікатор:
acc = classifier.score(X_test, y_test)
print(acc)
Подивившись на результат, ми можемо зробити висновок, що наш класифікатор отримав ~62% наших класів правильно. Це вже допомагає в аналізі, хоча, знаючи лише те, що класифікатор зробив правильно, важко його покращити.
У нашому наборі даних є 4 класи – що, якби наш класифікатор отримав 90% класів 1, 2 і 3 правильно, але лише 30% права 4 класу?
Системний збій певного класу, на відміну від збалансованого збою, розподіленого між класами, може давати 62% оцінку точності. Точність не є дуже хорошим показником для фактичної оцінки, але служить хорошим проксі. Найчастіше зі збалансованими наборами даних точність 62% розподіляється відносно рівномірно. Крім того, найчастіше набори даних не збалансовані, тож ми повертаємося на круги своя, а точність є недостатнім показником.
Ми можемо глибше розглянути результати за допомогою інших показників, щоб визначити це. Цей крок також відрізняється від регресії, тут ми будемо використовувати:
- Матриця плутанини: Щоб знати, скільки ми зробили правильно чи неправильно кожен клас. Значення, які були правильними і правильно передбачуваними, називаються справжні позитиви ті, які були передбачені як позитивні, але не були позитивними, називаються помилкові позитиви. Та сама номенклатура справжні негативи та помилкові негативи використовується для від’ємних значень;
- Точність: Щоб зрозуміти, які правильні значення прогнозу вважалися правильними нашим класифікатором. Точність розділить ці справжні позитивні значення на все, що було передбачено як позитивне;
$$
точність = frac{текст{істинний позитивний}}{текст{істинний позитивний} + текст{хибний позитивний}}
$$
- Згадувати: щоб зрозуміти, скільки справжніх позитивів виявив наш класифікатор. Відкликання розраховується шляхом ділення справжніх позитивних результатів на все, що мало бути передбачувано позитивним.
$$
recall = frac{текст{істинний позитивний}}{текст{істинний позитивний} + текст{хибний негативний}}
$$
- F1 бал: Чи є збалансованим або гармонійне середнє точності та відкликання. Найменше значення 0, а найвище 1. Коли
f1-score
дорівнює 1, це означає, що всі класи були правильно передбачені – це дуже важко отримати оцінку з реальними даними (майже завжди існують винятки).
$$
text{f1-score} = 2* frac{text{precision} * text{recall}}{text{precision} + text{recall}}
$$
Примітка: Зважена оцінка F1 також існує, і це просто F1, яка не застосовує однакову вагу до всіх класів. Вага зазвичай визначається класами підтримка – скільки примірників «підтримують» оцінку F1 (частка міток, що належать до певного класу). Чим нижча підтримка (чим менше екземплярів класу), тим нижчий зважений F1 для цього класу, оскільки він більш ненадійний.
Команда confusion_matrix()
та classification_report()
методи sklearn.metrics
модуль можна використовувати для обчислення та відображення всіх цих показників. The confusion_matrix
краще візуалізувати за допомогою теплової карти. Звіт про класифікацію вже дає нам accuracy
, precision
, recall
та f1-score
, але ви також можете імпортувати кожен із цих показників із sklearn.metrics
.
Щоб отримати показники, виконайте такий фрагмент коду:
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
classes_names = ['class 1','class 2','class 3', 'class 4']
cm = pd.DataFrame(confusion_matrix(yc_test, yc_pred),
columns=classes_names, index = classes_names)
sns.heatmap(cm, annot=True, fmt='d');
print(classification_report(y_test, y_pred))
Висновок наведеного вище сценарію виглядає так:
precision recall f1-score support
1 0.75 0.78 0.76 1292
2 0.49 0.56 0.53 1283
3 0.51 0.51 0.51 1292
4 0.76 0.62 0.69 1293
accuracy 0.62 5160
macro avg 0.63 0.62 0.62 5160
weighted avg 0.63 0.62 0.62 5160
Результати показують, що KNN вдалося класифікувати всі 5160 записів у тестовому наборі з точністю 62%, що вище середнього. Підтримки досить однакові (рівномірний розподіл класів у наборі даних), тому зважений F1 і незважений F1 будуть приблизно однаковими.
Ми також можемо побачити результат метрик для кожного з 4 класів. З цього ми можемо це помітити class 2
мав найнижчу точність, найнижчу recall
, і найнижчий f1-score
. Class 3
знаходиться прямо позаду class 2
за найнижчі бали, а потім маємо class 1
з найкращими результатами class 4
.
Подивившись на матрицю плутанини, ми можемо побачити, що:
class 1
здебільшого приймали заclass 2
у 238 випадкахclass 2
та цінностіclass 1
у 256 записах, а заclass 3
у 260 випадкахclass 3
здебільшого помилявсяclass 2
, 374 записи таclass 4
, у 193 випадкахclass 4
було помилково класифіковано якclass 3
на 339 записів, а якclass 2
в 130 випадках.
Крім того, зауважте, що діагональ відображає справжні позитивні значення, дивлячись на неї, це добре видно class 2
та class 3
мають найменш правильно передбачені значення.
Маючи ці результати, ми могли б заглибитися в аналіз, додатково перевіривши їх, щоб з’ясувати, чому це сталося, а також зрозуміти, чи 4 класи є найкращим способом групування даних. Можливо значення від class 2
та class 3
були надто близько один до одного, тому їх стало важко відрізнити.
Завжди намагайтеся перевірити дані з різною кількістю бункерів, щоб побачити, що станеться.
Крім довільної кількості бінів даних, є ще одне довільне число, яке ми обрали, кількість K сусідів. Той самий прийом, який ми застосували до задачі регресії, можна застосувати до класифікації під час визначення кількості K, які максимізують або мінімізують значення метрики.
Пошук найкращого K для класифікації KNN
Давайте повторимо те, що було зроблено для регресії, і побудуємо графік значень K і відповідної метрики для тестового набору. Ви також можете вибрати, який показник краще відповідає вашому контексту, тут ми виберемо f1-score
.
Таким чином ми побудуємо графік f1-score
для прогнозованих значень тестового набору для всіх значень K від 1 до 40.
По-перше, ми імпортуємо f1_score
від sklearn.metrics
а потім обчислити його значення для всіх передбачень класифікатора K-Nearest Neighbors, де K коливається від 1 до 40:
from sklearn.metrics import f1_score
f1s = []
for i in range(1, 40):
knn = KNeighborsClassifier(n_neighbors=i)
knn.fit(X_train, y_train)
pred_i = knn.predict(X_test)
f1s.append(f1_score(y_test, pred_i, average='weighted'))
Наступним кроком буде побудова графіка f1_score
значення проти значень K. Відмінність від регресії полягає в тому, що замість вибору значення K, яке мінімізує помилку, цього разу ми виберемо значення, яке максимізує f1-score
.
Виконайте такий сценарій, щоб створити графік:
plt.figure(figsize=(12, 6))
plt.plot(range(1, 40), f1s, color='red', linestyle='dashed', marker='o',
markerfacecolor='blue', markersize=10)
plt.title('F1 Score K Value')
plt.xlabel('K Value')
plt.ylabel('F1 Score')
Вихідний графік виглядає так:
З вихідних даних ми бачимо, що f1-score
є найвищим, коли значення K є 15
. Давайте перенавчимо наш класифікатор із 15 сусідами та подивимось, що він робить із результатами звіту про класифікацію:
classifier15 = KNeighborsClassifier(n_neighbors=15)
classifier15.fit(X_train, y_train)
y_pred15 = classifier15.predict(X_test)
print(classification_report(y_test, y_pred15))
Це виводить:
precision recall f1-score support
1 0.77 0.79 0.78 1292
2 0.52 0.58 0.55 1283
3 0.51 0.53 0.52 1292
4 0.77 0.64 0.70 1293
accuracy 0.63 5160
macro avg 0.64 0.63 0.64 5160
weighted avg 0.64 0.63 0.64 5160
Зверніть увагу, що наші показники покращилися з 15 сусідами, у нас точність 63% і вище precision
, recall
та f1-scores
, але нам все одно потрібно ще більше поглянути на контейнери, щоб спробувати зрозуміти, чому f1-score
для занять 2
та 3
все ще низька.
Окрім використання KNN для регресії та визначення значень блоків і для класифікації, для визначення класів блоків – ми також можемо використовувати KNN для виявлення, які середні значення блоків відрізняються від більшості – тих, які не відповідають тому, що робить більшість даних. Іншими словами, ми можемо використовувати KNN для виявлення викидів.
Впровадження KNN для виявлення викидів за допомогою Scikit-Learn
Виявлення викидів використовує інший метод, який відрізняється від того, що ми робили раніше для регресії та класифікації.
Тут ми побачимо, як далеко кожен із сусідів знаходиться від точки даних. Давайте використаємо 5 сусідів за замовчуванням. Для точки даних ми обчислимо відстань до кожного з K-найближчих сусідів. Для цього ми імпортуємо інший алгоритм KNN із Scikit-learn, який не є спеціальним ні для регресії, ні для класифікації, який називається просто NearestNeighbors
.
Після імпорту ми створимо екземпляр a NearestNeighbors
клас із 5 сусідами – ви також можете створити його екземпляр із 12 сусідами, щоб визначити викиди в нашому прикладі регресії, або з 15, щоб зробити те саме для прикладу класифікації. Потім ми підберемо дані нашого поїзда та використаємо kneighbors()
метод для визначення розрахованих відстаней для кожної точки даних і індексів сусідів:
from sklearn.neighbors import NearestNeighbors
nbrs = NearestNeighbors(n_neighbors = 5)
nbrs.fit(X_train)
distances, indexes = nbrs.kneighbors(X_train)
Тепер у нас є 5 відстаней для кожної точки даних – відстань між нею та її 5 сусідами та індекс, який їх ідентифікує. Давайте поглянемо на перші три результати та форму масиву, щоб краще уявити це.
Щоб переглянути перші три форми відстані, виконайте:
distances[:3], distances.shape
(array([[0. , 0.12998939, 0.15157687, 0.16543705, 0.17750354],
[0. , 0.25535314, 0.37100754, 0.39090243, 0.40619693],
[0. , 0.27149697, 0.28024623, 0.28112326, 0.30420656]]),
(3, 5))
Зверніть увагу, що є 3 ряди з 5 відстанями в кожному. Також можемо подивитися і індекси сусідів:
indexes[:3], indexes[:3].shape
Це призводить до:
(array([[ 0, 8608, 12831, 8298, 2482],
[ 1, 4966, 5786, 8568, 6759],
[ 2, 13326, 13936, 3618, 9756]]),
(3, 5))
У вихідних даних вище ми можемо побачити індекси кожного з 5 сусідів. Тепер ми можемо продовжити обчислювати середнє значення 5 відстаней і побудувати графік, який підраховує кожен рядок на осі X і відображає кожну середню відстань на осі Y:
dist_means = distances.mean(axis=1)
plt.plot(dist_means)
plt.title('Mean of the 5 neighbors distances for each data point')
plt.xlabel('Count')
plt.ylabel('Mean Distances')
Зверніть увагу, що є частина графіка, в якій середні відстані мають однакові значення. Точка осі Y, у якій середні значення не надто високі чи занизькі, є саме тією точкою, яку нам потрібно визначити, щоб відсікти викидні значення.
У цьому випадку середня відстань дорівнює 3. Давайте знову побудуємо графік горизонтальною пунктирною лінією, щоб побачити це:
dist_means = distances.mean(axis=1)
plt.plot(dist_means)
plt.title('Mean of the 5 neighbors distances for each data point with cut-off line')
plt.xlabel('Count')
plt.ylabel('Mean Distances')
plt.axhline(y = 3, color = 'r', linestyle = '--')
Ця лінія позначає середню відстань, для якої змінюються всі значення вище. Це означає, що всі точки з a mean
відстань вище 3
наші викиди. Ми можемо дізнатися індекси цих точок за допомогою np.where()
. Цей метод виведе будь-який True
or False
для кожного індексу щодо mean
вище 3 умова:
import numpy as np
outlier_index = np.where(dist_means > 3)
outlier_index
Наведені вище кодові виходи:
(array([ 564, 2167, 2415, 2902, 6607, 8047, 8243, 9029, 11892,
12127, 12226, 12353, 13534, 13795, 14292, 14707]),)
Тепер у нас є індекси викидів. Давайте знайдемо їх у фреймі даних:
outlier_values = df.iloc[outlier_index]
outlier_values
Це призводить до:
MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude MedHouseVal
564 4.8711 27.0 5.082811 0.944793 1499.0 1.880803 37.75 -122.24 2.86600
2167 2.8359 30.0 4.948357 1.001565 1660.0 2.597809 36.78 -119.83 0.80300
2415 2.8250 32.0 4.784232 0.979253 761.0 3.157676 36.59 -119.44 0.67600
2902 1.1875 48.0 5.492063 1.460317 129.0 2.047619 35.38 -119.02 0.63800
6607 3.5164 47.0 5.970639 1.074266 1700.0 2.936097 34.18 -118.14 2.26500
8047 2.7260 29.0 3.707547 1.078616 2515.0 1.977201 33.84 -118.17 2.08700
8243 2.0769 17.0 3.941667 1.211111 1300.0 3.611111 33.78 -118.18 1.00000
9029 6.8300 28.0 6.748744 1.080402 487.0 2.447236 34.05 -118.78 5.00001
11892 2.6071 45.0 4.225806 0.903226 89.0 2.870968 33.99 -117.35 1.12500
12127 4.1482 7.0 5.674957 1.106998 5595.0 3.235975 33.92 -117.25 1.24600
12226 2.8125 18.0 4.962500 1.112500 239.0 2.987500 33.63 -116.92 1.43800
12353 3.1493 24.0 7.307323 1.460984 1721.0 2.066026 33.81 -116.54 1.99400
13534 3.7949 13.0 5.832258 1.072581 2189.0 3.530645 34.17 -117.33 1.06300
13795 1.7567 8.0 4.485173 1.120264 3220.0 2.652389 34.59 -117.42 0.69500
14292 2.6250 50.0 4.742236 1.049689 728.0 2.260870 32.74 -117.13 2.03200
14707 3.7167 17.0 5.034130 1.051195 549.0 1.873720 32.80 -117.05 1.80400
Наше виявлення викидів завершено. Таким чином ми виявляємо кожну точку даних, яка відхиляється від загальної тенденції даних. Ми бачимо, що в даних нашого поїзда є 16 пунктів, які слід додатково переглянути, дослідити, можливо, вилікувати або навіть видалити з наших даних (якщо вони були введені помилково), щоб покращити результати. Ці моменти могли бути наслідком помилок друку, невідповідності середніх значень блоку або навіть обох.
Плюси і мінуси KNN
У цьому розділі ми представимо деякі переваги та недоліки використання алгоритму KNN.
профі
- Його легко реалізувати
- Це ледачий алгоритм навчання, тому він не потребує навчання для всіх точок даних (лише використання K-найближчих сусідів для прогнозування). Це робить алгоритм KNN набагато швидшим, ніж інші алгоритми, які вимагають навчання з усім набором даних, наприклад Підтримка векторних машин, лінійна регресія, І т.д.
- Оскільки KNN не потребує навчання перед прогнозуванням, нові дані можна додавати без проблем
- Для роботи з KNN необхідні лише два параметри, тобто значення K і функція відстані
мінуси
- Алгоритм KNN погано працює з даними великої розмірності, оскільки з великою кількістю вимірів відстань між точками стає «дивною», а метрики відстані, які ми використовуємо, не витримують
- Нарешті, алгоритм KNN погано працює з категоріальними ознаками, оскільки важко знайти відстань між вимірами з категоріальними ознаками
Йти далі – наскрізний ручний проект
У цьому керованому проекті ви дізнаєтесь, як створювати потужні традиційні моделі машинного навчання, а також моделі глибокого навчання, використовувати Ensemble Learning і навчати мета-навчальників прогнозувати ціни на житло за допомогою моделей Scikit-Learn і Keras.
Використовуючи Keras, API глибокого навчання, створений на основі Tensorflow, ми експериментуватимемо з архітектурами, створюватимемо ансамбль складених моделей і навчатимемо мета-учень нейронна мережа (модель рівня 1), щоб визначити ціну будинку.
Глибоке навчання дивовижне, але перш ніж вдаватися до нього, радимо також спробувати вирішити проблему за допомогою простіших методів, наприклад поверхневе навчання алгоритми. Наша базова продуктивність базуватиметься на a Регресія випадкового лісу алгоритм. Крім того, ми дослідимо створення ансамблів моделей за допомогою Scikit-Learn за допомогою таких методів, як мішок та голосування.
Це наскрізний проект, і, як і всі проекти машинного навчання, ми почнемо з нього Дослідницький аналіз даних, А потім Попередня обробка даних і, нарешті Будівля неглибока та Моделі глибокого навчання щоб відповідати даним, які ми дослідили та очистили раніше.
Висновок
KNN — простий, але потужний алгоритм. Його можна використовувати для багатьох завдань, таких як регресія, класифікація або виявлення викидів.
KNN широко використовується для пошуку схожості документів і розпізнавання шаблонів. Він також використовувався для розробки рекомендаційних систем і для зменшення розмірності та етапів попередньої обробки для комп’ютерного зору – зокрема завдань розпізнавання обличчя.
У цьому посібнику ми розглянули регресію, класифікацію та виявлення викидів, використовуючи реалізацію Scikit-Learn алгоритму K-Nearest Neighbor.