Модел: учене от примери
Накратко: Моделът за машинно обучение учи правилото от примери (признаци X и цел y) с fit, прогнозира с predict и се проверява на теста срещу базова линия.
Проблем
„Колко хора да сложа на склада?“
Управителят прави графика на склада за следващата седмица: колко души да приемат и опаковат поръчките всеки ден. Досега гадае по усет. Иска число за всеки ден. Ето поръчките от последната година, по дни:
Дните са 365, а средно идват 30,45 поръчки на ден. Броим всички поръчки, и отказаните: въпросът е колко пристигат, а и отказаната се приема и обработва. До всеки ден са денят от седмицата (0 е понеделник, 6 е неделя) и месецът. Как от тези примери да получиш число за петък, 6 ноември?
Идеята
Първо въпросът
Преди модела питаш какво решение ще вземеш с прогнозата. Тук то е колко хора да има всеки ден. От него следват три неща:
- Какво предсказваш. Броят поръчки в деня, а не оборотът: опаковането зависи от поръчките.
- Какво знаеш предварително. Графикът се прави в неделя. Тогава знаеш деня от седмицата и месеца на всеки ден. Колко поръчки ще дойдат до обяд в сряда не знаеш.
- Колко точно трябва. Колкото да не смениш решението: ако прогнозата греши с по-малко, отколкото поема един човек, графикът е същият.
Ако графикът не се мени, каквото и да покаже прогнозата, модел не ти трябва.
Идеята
Модел е функция, научена от примери
Модел е функция: даваш ѝ деня и тя връща прогноза. Правилото не го пишеш ти. Даваш примери, дни с известния им брой поръчки, и моделът сам намира правило, което пасва на тях. Входът се казва признаци (features) и се пише X. Това, което предсказваш, е целта (target), y.
Денят от седмицата е число от 0 до 6, но не е количество: неделята (6) не е „три пъти сряда“ (2). Затова всяка стойност става своя колона с 0 и 1. Това е one-hot кодиране, а pd.get_dummies го прави:
Колоните са 19: 7 за дните и 12 за месеците. 1 ноември 2025 е събота, затова в реда му е 1 само в weekday_5 (и в month_11, която е по-вдясно).
Идеята
fit, predict и тестът
Моделите в scikit-learn имат едни и същи две стъпки:
fit(X, y)учи от примерите;predict(X)дава прогноза за нови редове.
Дали моделът е добър, проверяваш на дни, които не е виждал. train_test_split разбърква редовете и дели: 75% са за обучение (train), а 25% за тест. С random_state=42 делението е едно и също при всяко пускане. Първото зареждане на scikit-learn (заедно със scipy) тегли около 18 MB и отнема няколко секунди:
Моделът учи от 273 дни, а 92 дни са за теста. На тях греши средно с 4,38 поръчки на ден: средното на разликите между факта и прогнозата, без знака. На дните за обучение грешката е 4,09. Там моделът винаги изглежда по-добре, защото е виждал отговорите. Затова числото, което казваш, е от теста.
LinearRegression е модел, който събира по едно число за всяка колона. Как ги намира, е в следващия урок. Тук важат само fit и predict, а те са същите за всеки модел.
По-точно, за любопитните
Дните са подредени във времето, а train_test_split ги разбърква. Тук това е опростяване: моделът учи за септември и от дни след тестовия ден. В следващия урок ще видиш защо за прогноза е по-честно да делиш по време: учиш на по-ранните месеци и проверяваш на по-късните. Как учи модел на ръка, без библиотека, има в урока Линейна регресия от курса по AI.
Идеята
Първо базова линия
4,38 поръчки грешка добре ли е? Без сравнение не знаеш. Сравняваш с базова линия (baseline): прогноза без модел, с която всеки може да излезе. Например „винаги средното“. DummyRegressor е точно това, със същите fit и predict:
Базовата линия казва 30,34 за всеки ден: средното на дните за обучение. Тя греши с 5,76 поръчки, а моделът с 4,38. Моделът е с 1,38 поръчки по-точен, с 24% по-малка грешка. Има смисъл.
Модел, който не бие базовата линия, не струва нищо. Той е код, данни и поддръжка, а дава колкото „винаги 30“. Затова базовата линия идва първа. Друга добра базова линия е това, което хората правят сега, например „колкото в същия ден миналата седмица“.
Пробвай
Правилото „като миналата седмица“
Сега управителят планира „колкото в същия ден миналата седмица“. Колко греши това правило на тестовите дни? Запиши в last_week поръчките отпреди 7 дни за всеки ден, а в err средната грешка без знака на дните от y_test.
Решение
import numpy as np
import pandas as pd
orders = pd.read_csv('year_orders.csv')
orders['ordered_at'] = pd.to_datetime(orders['ordered_at'])
daily = orders.set_index('ordered_at').resample('D').size().rename('orders').to_frame()
daily['weekday'] = daily.index.dayofweek
daily['month'] = daily.index.month
X = pd.get_dummies(daily[['weekday', 'month']], columns=['weekday', 'month'], dtype=int)
y = daily['orders']
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
model = LinearRegression().fit(X_train, y_train)
last_week = y.shift(7)
err = (y_test - last_week[y_test.index]).abs().mean()
print(round(err, 2))Правилото греши средно с 6,16 поръчки на тестовите дни: повече и от „винаги средното“ (5,76). Един ден от миналата седмица носи своя шум, а средното от 273 дни го изглажда. shift(7) взима стойността отпреди 7 реда, тоест отпреди седмица. Три от тестовите дни са в първата седмица и нямат миналата седмица, а mean() прескача празните. shift(-7) би взел следващата седмица: бъдеще, което при правенето на графика не знаеш.
Упражнение
Стига ли денят? И планът за следващата седмица
Стига ли само денят от седмицата, без месеца? Учи LinearRegression само на колоните weekday от X_train. В err запиши средната грешка на тестовите дни, в base грешката на „винаги средното от обучението“, а в beats дали моделът я бие.
Решение
import numpy as np
import pandas as pd
orders = pd.read_csv('year_orders.csv')
orders['ordered_at'] = pd.to_datetime(orders['ordered_at'])
daily = orders.set_index('ordered_at').resample('D').size().rename('orders').to_frame()
daily['weekday'] = daily.index.dayofweek
daily['month'] = daily.index.month
X = pd.get_dummies(daily[['weekday', 'month']], columns=['weekday', 'month'], dtype=int)
y = daily['orders']
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
model = LinearRegression().fit(X_train, y_train)
week_cols = [c for c in X.columns if c.startswith('weekday')]
week_model = LinearRegression().fit(X_train[week_cols], y_train)
err = np.abs(y_test - week_model.predict(X_test[week_cols])).mean()
base = np.abs(y_test - y_train.mean()).mean()
beats = err < base
print(round(err, 2), round(base, 2), beats)Само с деня моделът греши с 5,52 поръчки, а базовата линия с 5,76. Бие я, но с 0,24 поръчки: на косъм. Месецът носи повече: с него грешката е 4,38. Мерено на дните за обучение, моделът изглежда по-добре (5,00), но тези дни ги е виждал. А средното на y_test като базова линия е отговор, който уж не знаеш.
Тестът каза, че моделът с деня и месеца бие базовата линия. Сега прогнозата за истинската седмица, от понеделник 2.11 до неделя 8.11.2026. Учи модела наново на всички 365 дни, направи X_new с колоните на X и запиши в plan прогнозата за всеки ден (Series с датите като индекс).
Решение
import numpy as np
import pandas as pd
orders = pd.read_csv('year_orders.csv')
orders['ordered_at'] = pd.to_datetime(orders['ordered_at'])
daily = orders.set_index('ordered_at').resample('D').size().rename('orders').to_frame()
daily['weekday'] = daily.index.dayofweek
daily['month'] = daily.index.month
X = pd.get_dummies(daily[['weekday', 'month']], columns=['weekday', 'month'], dtype=int)
y = daily['orders']
from sklearn.linear_model import LinearRegression
final = LinearRegression().fit(X, y)
dates = pd.date_range('2026-11-02', periods=7)
week = pd.DataFrame({'weekday': dates.dayofweek, 'month': dates.month}, index=dates)
X_new = pd.get_dummies(week, columns=['weekday', 'month'], dtype=int).reindex(columns=X.columns, fill_value=0)
plan = pd.Series(final.predict(X_new), index=dates).round(1)
print(plan)Прогнозата е 32,9, 32,0, 31,6, 33,1, 37,9, 29,6 и 26,0 поръчки от понеделник до неделя. Петъкът е най-силен, а неделята най-слаба. След проверката моделът се учи на всички дни: последните месеци са най-близо до това, което предстои. get_dummies на седемте дни дава само month_11, а reindex(columns=X.columns, fill_value=0) добавя липсващите колони с 0 в реда на X. Ако дадеш на модела деня и месеца като числа, прогнозата пада равномерно от 35,6 в понеделник до 31,3 в неделя: моделът вижда деня като количество.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: Избиране: [], loc и iloc · Нови колони · Типове и липсващи стойности · Дати и времеви редове
- Продължава в: Линейна регресия · Класификация · Изтичане на данни (data leakage) · Кръстосана проверка (cross-validation)
- Задачи по темата: Модели със scikit-learn