Линейна регресия
Накратко: Линейната регресия предсказва число с права y = w · x + b, а средният квадрат на грешките (MSE) казва с едно число колко добре правата пасва на данните.
Проблем
Двама колеги, две прави
Складът иска да обещава часове, не дни. Ето 10 минали поръчки: разстоянието и за колко часа пристигнаха.
// минали поръчки: разстояние в km и часове до доставкатаconst KM = [30, 50, 80, 100, 130, 160, 200, 240, 270, 300];const HOURS = [10, 11, 17, 17, 23, 24, 31, 33, 40, 44];Двама колеги гледат точките и предлагат правило:
- права A: всеки километър добавя 0,08 часа, плюс 12 часа в склада;
- права B: всеки километър добавя 0,12 часа, плюс 6 часа в склада.
На графиката и двете изглеждат прилично. Колегите спорят цял следобед. Спорът няма да свърши, докато нямат едно число, което казва колко е добра правата.
Идеята
Правата като модел
Двете предложения са от един вид: часове = w · km + b.
- w е наклонът: колко часа добавя всеки километър;
- b е отместването: часовете при 0 km, тоест обработката в склада.
Модел, който предсказва число с права, се казва линейна регресия. Да научиш модела значи да избереш w и b.
// правата: часове = w · km + bfunction predict(w, b, km) { return w * km + b;}За разлика от k най-близки съседи, правата не пази поръчките. Тя е две числа, а прогнозата е едно умножение и едно събиране. И продължава наклона си: за 500 km казва 0,12 · 500 + 6 = 66 часа, докато съседите на 500 km са поръчките от 300 km.
Идеята
Грешката на всяка поръчка
За всяка поръчка правата казва някакво число. Грешката е разликата: колко казва правата минус колко е било наистина. Положителна е, когато правата обещава повече, отрицателна, когато обещава по-малко.
Права B, поръчка по поръчка:
const w = 0.12, b = 6;KM.forEach((km, i) => { const guess = predict(w, b, km); console.log(`${km} km: истина ${HOURS[i]} ч, правата ${guess.toFixed(2)} ч, грешка ${(guess - HOURS[i]).toFixed(2)}`);});console.log(`MSE: ${mse(w, b).toFixed(2)}`);30 km: истина 10 ч, правата 9.60 ч, грешка -0.4050 km: истина 11 ч, правата 12.00 ч, грешка 1.0080 km: истина 17 ч, правата 15.60 ч, грешка -1.40100 km: истина 17 ч, правата 18.00 ч, грешка 1.00130 km: истина 23 ч, правата 21.60 ч, грешка -1.40160 km: истина 24 ч, правата 25.20 ч, грешка 1.20200 km: истина 31 ч, правата 30.00 ч, грешка -1.00240 km: истина 33 ч, правата 34.80 ч, грешка 1.80270 km: истина 40 ч, правата 38.40 ч, грешка -1.60300 km: истина 44 ч, правата 42.00 ч, грешка -2.00MSE: 1.83Десет грешки, част с плюс, част с минус. Трябва ни едно число за цялата права.
Идеята
Едно число: средният квадрат
Всяка грешка се повдига на квадрат, квадратите се събират и се делят на броя на поръчките. Това е средният квадрат на грешките, MSE (mean squared error). Число, което казва колко е лош моделът, се казва функция на грешката (loss). По-малко е по-добре, а 0 значи, че правата минава през всяка точка.
// средният квадрат на грешките (MSE)function mse(w, b) { let sum = 0; for (let i = 0; i < KM.length; i++) { const error = predict(w, b, KM[i]) - HOURS[i]; sum += error * error; } return sum / KM.length;}Защо квадрат? Сравни трите прави. C е хоризонталната права през средното, 25 часа за всяка поръчка:
// средната грешка без квадратfunction meanError(w, b) { let sum = 0; for (let i = 0; i < KM.length; i++) { sum += predict(w, b, KM[i]) - HOURS[i]; } return sum / KM.length;}const lines = { A: [0.08, 12], B: [0.12, 6], C: [0, 25] };for (const [name, [w, b]] of Object.entries(lines)) { console.log(`права ${name}: MSE ${mse(w, b).toFixed(2)}, средна грешка ${meanError(w, b).toFixed(2)}`);}права A: MSE 17.35, средна грешка -0.52права B: MSE 1.83, средна грешка -0.28права C: MSE 124.00, средна грешка 0.00Без квадрата права C има средна грешка 0,00: колкото е над точките, толкова е под тях, и плюсовете унищожават минусите. Най-лошата права „печели“. С квадрата нищо не се унищожава: C е 124, A е 17,35, а B е 1,83. Колегата с права B печели спора.
Квадратът прави и още нещо: голямата грешка тежи повече. Грешка от 4 часа дава 16, а две грешки по 2 часа дават общо 8. Правата, която греши малко навсякъде, е по-добра от тази, която е точна на половината поръчки и греши много на другата половина.
Виж го
Грешките като квадрати
function predict(w, b, km) { return w * km + b;}
function mse(w, b) { let sum = 0; for (let i = 0; i < KM.length; i++) { const error = predict(w, b, KM[i]) - HOURS[i]; sum += error * error; } return sum / KM.length;}Стъпка по стъпка, с думи
- 10 минали поръчки: колко km и за колко часа пристигнаха.
- Права A: 0,08 часа на km плюс 12 часа.
- Грешката на всяка поръчка: колко казва правата минус колко беше. Едни точки са над правата, други под нея.
- Всяка грешка на квадрат е квадрат със страна грешката. Средната им площ е MSE: 17,35.
- Права B: 0,12 часа на km плюс 6 часа. Правата се завърта, а квадратите се свиват.
- MSE на B е 1,83 срещу 17,35 на A.
- Правата с по-малка MSE е по-близо до точките. Едно число вместо спор.
Всеки квадрат е една грешка на квадрат: страната му е разстоянието от точката до правата. MSE е средната им площ. Права B е по-добра, защото квадратите ѝ са по-малки, не защото някой така е преценил.
Пробвай
Предвиди изхода
Куриерът взима 2 € на килограм. Три пратки от 1, 2 и 3 kg са стрували 3, 5 и 7 €. Какво ще отпечата програмата?
const kg = [1, 2, 3];const price = [3, 5, 7];let sum = 0;for (let i = 0; i < 3; i++) { const error = 2 * kg[i] - price[i]; sum += error * error;}console.log((sum / 3).toFixed(2));Правата 2 · kg дава 2, 4 и 6 €, а истината е 3, 5 и 7 €. Всяка грешка е −1, а квадратът ѝ е 1. Средното на 1, 1 и 1 е 1,00. MSE не може да е отрицателна.
Пробвай
Попълни
Какво се добавя към сумата за всяка поръчка?
const error = predict(w, b, KM[i]) - HOURS[i];
sum += ;Квадратът на грешката: error * error. Умножението е по-ясно и по-бързо от степенуване, а резултатът е същият.
Пробвай
Намери грешката
Колега пише функцията на грешката и сравнява правите. Неговата програма казва, че най-добрата е хоризонталната права C. На кой ред е грешката?
Избери реда с грешката.
Грешката се добавя без квадрат. Плюсовете и минусите се унищожават, и правата през средното излиза с грешка 0. Трябва error * error.
Идеята
Кога правата лъже
Правата е най-простият модел и често е достатъчна. Но:
- Светът не е прав. Ако след 300 km поръчката минава през друг склад и чака още ден, точките правят стъпало. Правата минава между двете части и греши и на двете.
- Една странна точка. Поръчка, изгубена за седмица, е грешка от стотици часове, а квадратът ѝ тежи колкото всички останали заедно. Правата се накланя към нея и започва да лъже за нормалните поръчки. Такива точки се проверяват и махат или се мери със средната абсолютна грешка.
- Далеч извън данните. Поръчките са до 300 km. За 2 000 km правата казва 246 часа, но никой не е проверил дали изобщо има такава доставка.
- Връзка не значи причина. Правата казва, че по-далечните поръчки пътуват по-дълго. Тя не казва защо. Ако по-далечните градове просто се обслужват от по-бавен куриер, по-бърз куриер там ще счупи правата.
Правата, която колегите избраха на око, е само едно от безбройните w и b. Коя е най-добрата и как да я намериш без да гадаеш, е в урока за градиентното спускане.
По-точно, за любопитните
Формулата за най-добрата права. За една права има точна формула (метод на най-малките квадрати). w е колко се движат заедно km и часовете, делено на колко се движат самите km. b е такова, че правата минава през средната точка:
w = Σ (km − средно km) · (часове − средно часове) / Σ (km − средно km)², b = средно часове − w · средно km.
const n = KM.length;let meanKm = 0, meanHours = 0;for (let i = 0; i < n; i++) { meanKm += KM[i]; meanHours += HOURS[i];}meanKm /= n;meanHours /= n;let top = 0, bottom = 0;for (let i = 0; i < n; i++) { top += (KM[i] - meanKm) * (HOURS[i] - meanHours); bottom += (KM[i] - meanKm) * (KM[i] - meanKm);}const w = top / bottom;const b = meanHours - w * meanKm;console.log(`най-добрата права: ${w.toFixed(4)} ч/km, ${b.toFixed(2)} ч`);console.log(`MSE: ${mse(w, b).toFixed(2)}`);най-добрата права: 0.1241 ч/km, 5.64 чMSE: 1.62Най-добрата права е малко по-добра от B (1,62 срещу 1,83). Колегата е преценил добре.
Защо не винаги формулата? Тя е за правата с квадратна грешка. При много признаци, при друга функция на грешката и при невронните мрежи точна формула няма или е твърде бавна. Затова се ползва градиентното спускане.
Повече признаци. Правата може да има по едно w за всеки признак: часове = w₁ · km + w₂ · kg + w₃ · пратки + b. Идеята и MSE са същите.
Средната абсолютна грешка (MAE) събира |грешка| вместо квадрата. Тя не наказва толкова голямата грешка и е по-спокойна към странни точки. MSE е по-гладка и затова по-удобна за градиентното спускане.
В библиотеките: LinearRegression в scikit-learn смята точно тази формула. В ML.NET е Sdca или Ols регресия.
Упражнение
Цената на куриера
Реши задачата в редактора си.
Куриерът не публикува ценоразписа си, но магазинът пази колко е платил за осем пратки:
// пратки: тегло в kg и цена на куриера в €const KG = [0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 6.0];const EUR = [4.10, 4.60, 5.00, 5.60, 6.40, 7.50, 8.30, 9.40];Колегите предлагат три прави за цената: A е 0,90 € на kg плюс 3,80 €, B е 1,10 € на kg плюс 3,20 €, C е 0,70 € на kg плюс 4,60 €. Напиши функцията на грешката (MSE), отпечатай я за трите и кажи коя е най-добра.
права A (0.90 €/kg + 3.80 €): MSE 0.0144права B (1.10 €/kg + 3.20 €): MSE 0.0769права C (0.70 €/kg + 4.60 €): MSE 0.2869най-малка MSE: права AОще задачи
- Средната абсолютна грешка. Смени квадрата с абсолютна стойност. Сменя ли се победителят?
- Формулата. Сметни най-добрата права с формулата от „По-точно“. Колко е по-добра от A?
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: Функции · Параметри и return · static: общото за всички · Какво е машинно учене
- Продължава в: Градиентно спускане · Класификация: измама или не · Преобучение: моделът, който наизустява · Неврон
- Задачи по темата: Как учи модел