CodeLab
145 subscribers
538 photos
20 videos
159 links
Говорим просто о сложном

Обсуждение, новости, материал и сплетни — все здесь https://t.me/CodeLabMLChat
Download Telegram
👨‍💻 Всем ку! Продолжаем разбирать 📈 логистическую регрессию. Намутил для вас классный дизайн для поста, а вот кстати прошлый пост

7⃣📊 Мы остановились на стандартизации (создали объект 🟰Z-score🟰 и обучили + применили стандартизацию).

⚙️ Теперь создадим модель логистической регрессии:

model = LogisticRegression(max_iter = 1000)


🛍 По умолчанию стоит L2 регуляризация, а max_iter отвечает за число итераций для градиентного спуска. (Про регуляризацию мы поговорим попозже, тоже очень важная тема).

👨‍💻 Вам нужно понимать одно: регуляризация штука интересная, нужна для 'защиты от переобучения модельки', и ,грубо говоря, она 'штрафует большие веса модели' — это все про L2, еще есть L1 (но обо всем потом)


📊8⃣ Обучаем модель:

model.fit(X_train_scaled, y_train)


📉Кароч ну тут мы по сути ищем такие веса (w), чтобы минимизировать 🟰log loss🟰 (это метрика для логистической регрессии) предсказаниями и 🟰y_train🟰

🧑‍💻 Чтобы вам понять, что делает model.fit():

🔵Получает X_train_scaled и y_train (наши обучающие данные и соответствующие метки, предсказания)

🔵Начинает с рандомных весов w

🔵Делает предсказания ŷ (вероятности через функцию сигмоиды)

🔵Сравнивает их с 🟰y_train🟰 с помощью 🟰log loss🟰

🔵Считает ошибку (loss)

🔵Обновляет веса, чтобы ошибка стала меньше

🔵Повторяет это до сходимости (или до max_iter)

➡️0⃣ Получаем предсказания y_pred:

y_pred = model.predict(X_test_scaled) #выдаст 0 или 1


1⃣0⃣ Выводим метрики качества:

print("Classification Report:\n", classification_report(y_test, y_pred))


🧑‍💻 classification_report — это функция для оценки эффективности алгоритмов классификации (в нем сразу содержится несколько + метрик качества) — тут тоже нужен отдельный пост, на выводе покажу что получится.

print("Confusion Matrix:\n", confusion_matrix(y_test, y_pred))


🧑‍💻 Выдает матрицу ошибок (показывает, где модель ошиблась, а где угадала )

🧑‍💻 Вот такой получится вывод для двух функций сверху

precision recall f1-score support

0 0.98 0.95 0.96 43
1 0.97 0.99 0.98 71

accuracy 0.97 114
macro avg 0.97 0.97 0.97 114
weighted avg 0.97 0.97 0.97 114


⚙️ Остается визуал:

sns.heatmap(confusion_matrix(y_test, y_pred), annot=True, fmt='d', cmap='Blues')
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.title("Confusion Matrix")
plt.show()


🧑‍💻 sns.heatmap(confusion_matrix...)как раз отвечает за цветную таблицу в выводе (в прошлом посте я ее кидал, в этом будет в комментах). В функцию мы передали матрицу ошибок.

🧑‍💻 annot = True аргумент вписывает числа внутрь ячеек.

Что мы видим на графике

🔵 41 — модель правильно предсказала класс 0 (у пациента рак)

🔵 70 — правильно предсказала класс 1 (у пациента рака нет)

🔵 1, 2 это ошибки модели в каждой из 2 ситуаций
Please open Telegram to view this post
VIEW IN TELEGRAM
5221
🧑‍💻 Сегодня крутецкий пост
Please open Telegram to view this post
VIEW IN TELEGRAM
722
👨‍💻 Йоу, воссап друзья, предлагаю продолжить бинарную классификацию, тем более, вы ничего не можете предложить.

import numpy as np

x_test = [(5, -3), (-3, 8), (3, 6), (0,0), (5, 3), (-3, -1), (-3, 3)]

w = np.array([-33, 9, 13])
a_sign = lambda x, w: -1 if np.dot(x, w) < 0 else 1
x_test_new = np.array([[1, x1, x2] for x1, x2 in x_test])
predict = [a_sign(x, w) for x in x_test_new]


⚙️ Мы разобрали код выше ТУТ и ТУТ, теперь можно попробовать его улучшить.

📇 Напоминаю, нам нужно было найти вектор параметров w 🟰w = [w₀, w₁, w₂]🟰 для разделяющей линии в соответствии с выражением: 🟰w * x₁ + w₂ * x₂ + w₀ = 0 🟰

📊 В прошлом посте ТУТ мы нашли этот самый вектор параметров 🟰 w = [-33, 9, 13] 🟰и получили такое уравнение прямой: 🟰9 * x₁ +13 * x₂ − 33 = 0🟰

📊 Но, но, помимо вектора параметров нам также нужно было сформировать список 🟰predict из значений меток {-1, +1}🟰 (поскольку у нас как раз 2 класса — C₁ = +1, C₂ = -1). То есть, если

wᵀ * x ≥ 0
📐 +1

wᵀ * x < 0
📐 -1

🧠 Эти самые метки мы искали по формуле 🟰 a(x) = sign(wᵀ * x) 🟰, здесь x = [1, x₁, x₂] — вектор признаков (координат) объекта выборки, дополненный первой единицей для параметра w₀.

📈 Короче, чтобы вам было проще, давайте на примере:

Мы уже имеем метки по ТЗ:

x_test = [(5, -3), (-3, 8), (3, 6), (0, 0), (5, 3), (-3, -1), (-3, 3)]


🟰 x = [1, 5, −3] 🟰— добавляем 1, чтобы учесть смещение w (со всеми примерами также)

🟰 wᵀ = [-33, 9, 13] 🟰

🟰 wᵀ * x 🟰

🟰 wᵀ * x = −33 * 1 + 9 * 5 + 13 * (−3) = −33 + 45 − 39 = −27 🟰Получаем wᵀ * x < 0, следовательно метка -1

🔜 Можете сами посчитать остальные метки, получаем такую штуку:

predict=[−1, +1, +1, −1, +1, −1, −1]


🧑‍💻 Позже продолжим
Please open Telegram to view this post
VIEW IN TELEGRAM
15542
3221
Друзья, у админа сессия, так что пока держите мем 👨‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
6321
CodeLab
Друзья, у админа сессия, так что пока держите мем 👨‍💻
Media is too big
VIEW IN TELEGRAM
👨‍💻Всем ку, решаем задачку с margin (это у нас так отступ называется). Кстати, уже задачки с отступами были, можете поискать через поиск канала, не менее интересные 👍

Фотка задания в комментах ⬇️

0⃣📱 Итак, условие не отличается от прошлых заданий: у нас есть разделяющая линия, она определяется параметрами

🟰w = [w₀, w₁, w₂]🟰

⚙️ Значения параметров также известны:

🟰w = [15/7, -9/7, -1]🟰

1⃣ Параметры задают положение линии в соответствие с выражением:

🟰w * x₁ + w₂ * x₂ + w₀ = 0 🟰

🔤🔤🌹🔤🔤🔤

2⃣ Так, теперь про отступ, что это и зачем оно нужно

Отступ
(или margin или γ или M) — все это, так или иначе, относится к отступу.

🔵Показывает margin насколько уверенно модель классифицировала точку а также, с какой стороны от разделяющей прямой она находится.


Формулы:

🟰Q(a, X) = Σ[a(x) ≠ y]🟰 — Число неверных классификаций

🟰[a(x) ≠ y]🟰 — нотация Айверсона, важная формула, вощвращает 1 или 0, щас поясню:

🟰y🟰 в нашей скобе это целевое значение, исходя из прошлых задач на бинарную классицикацию, y может быть -1 и +1 (номер класса)

‼️ Так вот, если модель a(x) в скобках [a(x) ≠ y] относит объект не к тому классу, [a(x) ≠ y] — выдает True. В соответствие с нотацией Айверсона 🟰[a(x) ≠ y]🟰 1 (True), 0 (False)

‼️🟰Q(a, X)🟰 — получается, этот функционал подсчитывает число неверных классификаций (сколько раз модель ошиблась)

🟰y ∈{-1, +1} 🟰— как раз, наши целевые

🟰Q(a, X) = Σ[ y * a(x) < 0]🟰 — немного переписываем число неверных классификаций теперь добавили y - целевые {-1, +1} и умножили их на ответ модели a(x) — это тоже, либо -1, либо +1. Если это произведение будет отрицательным — значит, мы неверно классифицируем X. Если y и a(X) будут совпадать (оба по -1; +1)получаем положительные значения.

‼️ Добрались до сути — 🟰M = a(X) * y — отступ (margin)🟰

🟰a(X) = {w, x}🟰 — если мы оставим только скалярное произведение, то 🟰a(X) = {w, x}🟰 будет давать нам не только {+1; -1}, но и величину, на которую отстоит наш образ X от разделяющей прямой (если отступ положительный, образ классифицируется верно)

🟰a(X) = sign(wᵀ * x)🟰— классификация в линейных моделях. В линейных моделях классификатор просто смотрит, с какой стороны от границы находится точка. Если результат положительный — класс +1, если отрицательный — -1
Please open Telegram to view this post
VIEW IN TELEGRAM
3111
3⃣📊 Хотел еще добавить формулу:

🟰γᵢ = yᵢ ⋅ Mᵢ = yᵢ ⋅ (wᵀ * xᵢ)🟰 — это у нас формула для отступа (margin)

🧑‍💻 Mᵢ = wᵀ * xᵢ — скалярное произведение весов и признаков

🧑‍💻 yᵢ ∈ {−1, +1} — целевые метки

🧑‍💻 Умножаем y * M и получаем отступ (насколько правильно мы классифицировали, если y > 0, модель предсказала правильно образ X

👨‍💻 Теперь, что касаемо кода:

4⃣ Импортируем numpy и прописываем веса модели:

import numpy as np

w = np.array([15/7, -9/7, -1]) # [w0, w1, w2]


5⃣ Теперь определяем корды (надо ток добавить единицу для w₀)

x_test = np.array([
(1, -8, -4),
(1, -2, 2),
(1, 4, 8),
(1, 6, 3)
])


6⃣ Теперь нам нужно как-нибудь определить метки (или знаки +1, -1), для этого воспользуемся функцией 🟰np.sign()🟰

y_test = np.sign(x_test @ w.T)


🧑‍💻 Умножаем матрицу из 4 векторов-образов (X) на вектор w, получаем вектор из 4 скалярных произведений:

🔵 x₁ * w ... x₄ * w

🔵w — вектор весов: w = [w₀, w₁, w₂]

🔵xᵢ — вектор признаков: xᵢ = [1, x₁, x₂]

🔵w * xᵢ — это то, насколько объект далеко от прямой


🟰M = a(X) * y🟰

🟰M = {w, x} * y🟰

7⃣ Самое главное, margin:

margin = x_test @ w.T * y_test #Все по формуле


import numpy as np

w = np.array([15/7, -9/7, -1])

x_test = np.array([
(1, -8, -4),
(1, -2, 2),
(1, 4, 8),
(1, 6, 3),
])

y_test = np.sign(x_test @ w.T)

margin = x_test @ w.T * y_test
Please open Telegram to view this post
VIEW IN TELEGRAM
422
🤔 Как вам последние посты? Все ли понятно?
Please open Telegram to view this post
VIEW IN TELEGRAM
522
👨‍💻 Друзья, новое задание! Оно не сильно отличается от прошлого, но разобрать стоит:

📈Снова имеем разделяющую прямую, а также два признака — x₁, x₂, определяется параметрами🟰w = [w₀, w₁, w₂]🟰

⚙️ Только в отличие от прошлого задания, эти параметры нам неизвестны.

0⃣‼️ А вычислить нам нужно значения отступов (напоминаю, margin) для образов 1-6 и сохранить в список 🟰margin = []🟰

1⃣ Начнем с параметров, пример вычисления смотримтут

Скажу сразу, что получилось у меня:

🔵Точки — (-4, 0) ; (5, 4)

🔵k (угловой коэффициент) = y₂ - y₁/ x₂ - x₁ (у меня получилось 4/9)

🔵b (коэффициент смещения, просто подставляем одну из точек и угловой коэффициент) (16/9)

🔵y = 16/9 + 4/9 * x — получили уравнение

🔵Переносим y и вычисляем коэффициенты

🔵-16 -4 * x + 9 = 0 ➡️получили [-16, -4, 9]

2⃣ Теперь прописываем это в коде + пишем корды точек (добавляем единицу для w₀)

import numpy as np
w = np.array([-16, -4, 9])

x_test = np.array([
[1, -5, 2],
[1, -4, 6],
[1, 3, 2],
[1, 3, -3],
[1, 5, 6],
[1, 9, 2]
])


3⃣ Метки классов определяем тоже сами:

y_test = np.array([1, 1, 1, -1, -1])


Вообще, тут у меня возник вопрос, вроде визуально понятно, что точки 1, 2 и 5 выше разделющей прямой, но они идут не по порядку. Если вычислять через np.sign, то получаем

y_test = np.sign(x_test @ w.T)

#y_test = np.array([1, 1, -1, -1, 1]) — А такой ответ не подойдет


4⃣ Вычисляем отступ:

margin = x_test @ w * y_test #Скалярное произведение
print(margin)


‼️ Чем больше отступ, тем увереннее классификация.

🔵В нашем случае, в точках 3 и 5 наша модель опростоволосилась


‼️ Сама формула 🟰margin — Mᵢ = wᵀ * xᵢ🟰 — это просто скалярное произведение между весами (это наши параметры в начале кода) и x-объектами (это и есть наши 6 точек).

🔵 y_test — целевые значения. Говорит нам о том, каким должен быть знак margin, если классификация верная.


‼️ Вот как работает главная формула нашего кода:

w = np.array([-16, -4, 9])
x_test[0] = [1, -5, 2]

M₁ = (-16) * 1 + (-4) * (-5) + 9 * 2 = -16 + 20 + 18 = 22
y₁ = 1

margin₁ = 22 * 1 = 22



5⃣🧑‍💻 Наш код:

import numpy as np

w = np.array([-16, -4, 9])

x_test = np.array([
[1, -5, 2],
[1, -4, 6],
[1, 3, 2],
[1, 3, -3],
[1, 5, 6],
[1, 9, 2]
])


y_test = np.array([1, 1, 1, -1, -1, -1])
# y_test = np.sign(x_test @ w.T)

# print(y_test)

margin = x_test @ w * y_test


🔗 Ссылка на курс 🔗
Please open Telegram to view this post
VIEW IN TELEGRAM
3321
Сегодня крутецкий пост 🧑‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
4221
👨‍💻 Итак, новое задание:

0⃣ Че у нас есть? Мы имеем тестовую выборку в виде набора образов с двумя признаками (x₁ и x₂) — первый элемент это значение x₁, второй — x₂. ‼️ Также мы имеем метки двух классов — (+1, -1)

🔵Вектор параметров у нас тоже имеется — [-8/3, -2/3, 1]

‼️ А вычислить нам нужно Q (это такой показатель качества, который вычисляется как сумма нотации Айверсона (M) для всех точек тестовой выборки). ⚙️ Проще говоря, найденный Q даст нам понять, сколько точек в тестовой выборке имеют отступ.

🟰 Qᵢ = Σ[Mᵢ < 0] 🟰, где 🟰 Mᵢ = y * {w, xᵢ} 🟰

🟰 [Mᵢ < 0] 🟰функция нотации Айверсона (возвращает 1, если Mᵢ < 0, и 0б если Mᵢ ≥ 0).

import numpy as np

x_test = np.array([(-5, 2), (-4, 6), (3, 2), (3, -3), (5, 5), (5, 2), (-1, 3)])
y_test = np.array([1, 1, 1, -1, -1, -1, -1])
w = np.array([-8/3, -2/3, 1])


1⃣⚙️ Для начала, добавляем столбец из единиц:

X = np.column_stack((np.ones(len(x_test)), x_test))


📊 Теперь наш вывод X будет выглядеть как-то так:

[[ 1. -5.  2.] 
[ 1. -4. 6.]
[ 1. 3. 2.]
...


2⃣🧠 Вычисляем отступы по привычной формуле:

🟰 Mᵢ = wᵀ * xᵢ 🟰 скалярное произведение ⟨w,xᵢ)

margin = y_test * (X @ w)


3⃣‼️ Самое главное — считаем количество M [Mᵢ < 0]

Q = np.sum(margin < 0)
print(int(Q))


Q = 2 ➡️ следовательно модель допустила 2 ошибки в выборке из 7 точек (покатит)
Please open Telegram to view this post
VIEW IN TELEGRAM
5332
🤝
Please open Telegram to view this post
VIEW IN TELEGRAM
6331
Постараюсь выкатить пост завтра 🧑‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
5321
🧑‍💻Разбираем код

🌷🔤🔤🔤

‼️ Ситуация примерно та же: нужно посчитать количество неправильно классифицированных объектов. Помимо этого, докрутим для кода визуал.

1⃣ Библиотеки:
import numpy as np
import matplotlib.pyplot as plt


2⃣ Теперь наши данные:
x_train = [
[10, 50], [20, 30], [25, 30], [20, 60], [15, 75],
[40, 40], [30, 45], [20, 45], [40, 30], [7, 36]
]


🔵Будем учитывать свободный член w₀, поэтому добавим единицу в каждую скобку:

x_train = [x + [1] for x in x_train]


🔵Теперь каждая точка будет иметь вид [x₁, x₂, 1]. Сделаем x_train NumPy - массивом вида (10, 3) — 10 строк и 3 столбца. Зачем мы преобразовали наш список? 📊 Теперь мы тупа можем делать больше операций, по типу транспонирования, всяких векторных
умножений и тд.
x_train = np.array(x_train)


🔵Метки класса (наши целевые значения):
y_train = np.array([-1, 1, 1, -1, -1, 1, 1, -1, 1, 1])


2⃣ ‼️ Важный момент: все, что мы написали сверху, было нам ДАНО, а вот теперь можно вычислить отступы (margin):
sumvec = np.sum([x * y for x, y in zip(x_train, y_train)], axis = 0)


🔵Это сумма векторов 🟰xᵢ * yᵢ🟰, вычисляем по формуле:

🟰 sumvec = Σ (xᵢ * yᵢ) 🟰Но, откуда мы взяли формулу

📊 Как вы уже знаете, существует немало функций потерь при бинарной классификации (). Наша цель — сделать так, чтобы классификатор давал результат максимально близко к y.‼️ Воспользуемся КВАДРАТИЧНОЙ функцией потерь:

🟰 Q(ω) = (1- M)² 🟰— Квадратичная функция потерь, Если M = 1, то ошибка = 0, если M = 0, то ошибка = 1. M — это что-то вроде оценки нашего классификатора (показывает насколько сильно модель верит в правильность классификации )

🟰 L(ω) = (1- M)² = (1 - ωᵀ * xᵢ * yᵢ)² 🟰отступ (margin) является линейным классификатором

🟰 Q(ω) = ∑(1 - ωᵀ * xᵢ * y)² → min(ω) 🟰Минимизируем сумму потерь (сумму отступов)

🟰 ωᵀ = ( ∑ xᵢ ᵀ * yᵢ ) * (∑ xᵢ * xᵢ ᵀ )⁻ ¹🟰— Берем производную по ω — это и будет нашим решением

🟰 ( ∑ xᵢ ᵀ * yᵢ ) 🟰— Взвешенная сумма всех признаков. Если yᵢ =+1, то xᵢ добавляется. Если yᵢ =−1, то xᵢ вычитается| (левая часть — вектор движения классов)

🟰 (∑ xᵢ * xᵢ ᵀ ) 🟰Матрица (ковариации) или разброса объектов |(правая часть — разброс(плотность точек))

💤 PART 2 SOON 💤
Please open Telegram to view this post
VIEW IN TELEGRAM
3321
🧑‍💻 Настоящему программисту не нужен макбук (но мне нужен)


https://t.me/boost/pywithCodeLab
5422
👨‍💻 Всем ку, прошлый пост — ТУТ, а мы продолжаем:

🟰 ωᵀ = ( ∑ xᵢ ᵀ * yᵢ ) * (∑ xᵢ * xᵢ ᵀ )⁻ ¹🟰 — закончили мы на минимизации суммы квадратов ошибок (это аналитическое решение)

🟰 Q(ω) = ∑(1 - ωᵀ * x * yᵢ)² 🟰 — формула минимизации квадратичной функции потерь

⚙️Так, теперь код:

🔵 Распишу все с формулами, чтобы вы смогли увидеть, как они применяются:

3⃣🟰 ( ∑ xᵢ ᵀ * yᵢ ) 🟰— Здесь мы каждую строку xᵢ умножаем на метку yᵢ (Допустим, имеем строку [10, 50, 1], умножаем на метку (-1) и получаем [-10, -50, -1])
sumvec = np.sum([x * y for x, y in zip(x_train, y_train)], axis=0)


4⃣🟰 (∑ xᵢ * xᵢ ᵀ ) 🟰 — outer(x, x) создаёт матрицу xx
xxt = np.sum([np.outer(x, x) for x in x_train], axis=0)


🔵Например:

x = [10, 50, 1]
np.outer(x, x) =
[[100, 500, 10],
[500, 2500, 50],
[10, 50, 1]]


5⃣🟰 ωᵀ = ( ∑ xᵢ ᵀ * yᵢ ) * (∑ xᵢ * xᵢ ᵀ )⁻ ¹ 🟰 Теперь реализация формулы через производную градиента и минимум функции потерь (получаем на выходе вектор весов ω = [ω₀, ω₁, ω₂]):
w = np.dot(sumvec, np.linalg.inv(xxt))
print("Вектор весов w:", w.round(3))


6⃣ Строим разделяющую прямую (ω₀ * x + ω₁ * y + ω₂ = 0) и выражаем y:

🟰 y = - ω₀x + ω₂ / ω₂ 🟰

line_x = np.linspace(0, 45, 100)
line_y = -(w[0] * line_x + w[2]) / w[1]


7⃣ Разделение точек по классам:

x_pos = x_train[y_train == 1]  
x_neg = x_train[y_train == -1]


Визуализацию продолжим позже (PART 3 SOON)💤
Please open Telegram to view this post
VIEW IN TELEGRAM
522
👨‍💻 Йоу, мы закончили на разделении точек по классам, теперь заканчиваем визуалом:

8⃣:

🔵 x_train — это матрица с данными (у каждой точки 2 признака)

🔵 x_neg — вектор ответов: [+1, -1, -1, +1, ...]
x_pos = x_train[y_train == 1]  # класс 1
x_neg = x_train[y_train == -1] # класс -1


0⃣ Теперь визуал, но, в этот раз мы поговорим про функции matplotlib более подробно:

🟢Объект .figure создает окно графика, figsize — размер в дюймах (у нас, 9 — ширина, 7 — высота)
plt.figure(figsize=(9, 7))


🔵Если хотим нарисовать точки на графике.scatter(x,y,sz,c) тут задаем x,y (корды), sz (размер), с (color). Верхняя строка — все точки класса +1, нижняя — все точки класса -1.

🟢x_pos[:, 0] — значения по оси X, x_pos[:, 1] — значения по оси Y
plt.scatter(x_pos[:, 0], x_pos[:, 1], color='blue', label='Класс +1', s=100, edgecolors='k')
plt.scatter(x_neg[:, 0], x_neg[:, 1], color='red', label='Класс -1', s=100, edgecolors='k')


🟢zip — объединяет два массива (x_train (корды), y_train (метки) )
for i, (x, y) in enumerate(zip(x_train, y_train)):
plt.text(x[0] + 0.5, x[1] + 0.5, f'{i+1}', fontsize=10)


🔵Пример работы zip:
x_train = [[10, 20], [15, 25], [30, 40]]
y_train = [+1, -1, +1]

zip(x_train, y_train) →
→ ([10, 20], +1), ([15, 25], -1), ([30, 40], +1)


🟢 enumerate() — добавляет номер к каждой паре, например:

🔵 БЫЛО: ([10, 20], +1), ([15, 25], -1), ([30, 40], +1)

🔵СТАЛО: (0, ([10, 20], +1)), (1, ([15, 25], -1)), (2, ([30, 40], +1))

🟢Ну и теперь базовые функции matplotlib:
plt.xlabel('$x_1$') # Подпись оси X как x1
plt.ylabel('$x_2$') # Аналогично но с Y и x2
plt.title('Классификация с разделяющей прямой')
plt.legend()
plt.grid(True) # Сетка
plt.xlim(0, 45) # Диапазон оси X (от 0 до 45)
plt.ylim(20, 80) # Аналогично но с Y
plt.show()


🔟 Теперь, самое основное, подытожим логикой кода:

⚙️В самом начале мы имеем x_train (корды), y_train (метки классов) 🔜

🔜 Добавляем столбец единиц в x_train (к кордам добавился столбец единиц, чтобы учесть свободный член) 🔜

🔜 Считаем sumvec по формуле (см. PART 2) — проходимся по парам (x, y), умножаем каждый вектор x на y, суммируем все произведения. в результате получаем вектор sumvec (он будет показывать направление, в котором модель будет тянуть нашу разделяющую прямую, туда, где классы лучше отделяются) 🔜

🔜 Считаем матрицу (3 на 3), для каждой точки делаем матрицу и складываем их все вместе, по итогу получаем суммарную матрицу Грамма. Матрица показывает, как точки расположены в пространстве (чем больше разброс, тем сильнее матрица влияет на обратную матрицу при вычислении весов). Кароч, чтобы вам было понятно: у нас есть 🟢обычная матрица (сумма внешних произведений точек, показывает, как точки разбросаны), а есть 🟢обратная матрица (что-то вроде коррекции для наших данных, помогает найти лучшее направление). 🔜

🔜 Вычисляем вектор весов ω (задает уравнение разделяющей прямой, влияет на положение границу между классами) 🔜

🔜Строим разделяющую прямую (Берем 100 точек x от 0 до 45, для каждой точки считаем y )🔜

🔜Разделяем точки по классам🔜Визуализация
Please open Telegram to view this post
VIEW IN TELEGRAM
4221
👨‍💻 Забыл совсем вам скинуть результат и код:

import numpy as np
import matplotlib.pyplot as plt

#обучающие данные (10 точек, по 2 признака)

x_train = [
[10, 50], [20, 30], [25, 30], [20, 60], [15, 75],
[40, 40], [30, 45], [20, 45], [40, 30], [7, 36]
]

#точки только с 2 признаками [x1, #x2]
#учитываем свободный член: #свободный член w₀ в модели
x_train = [x + [1] for x in x_train] # теперь каждая точка вида [x1, x2, 1]
x_train = np.array(x_train) # (10, 3)

# Метки классов: -1 или +1
y_train = np.array([-1, 1, 1, -1, -1, 1, 1, -1, 1, 1])

#Считаем вектор pt = Σ (x_i * y_i)
# ДО: нет признака y
# ПОСЛЕ: каждый x умножается на соответствующее y, потом суммируются
sumvec = np.sum([x * y for x, y in zip(x_train, y_train)], axis=0)

# считаем xxt = Σ (x_i * x_i^T)
# сумма матриц Грама: x^T * x для каждой точки
xxt = np.sum([np.outer(x, x) for x in x_train], axis=0)

# w = pt · (X^T X)^(-1)
w = np.dot(sumvec, np.linalg.inv(xxt))
print("Вектор весов w:", w.round(3)) # округлим для удобства

# разделяющая прямая: w0 * x + w1 * y + w2 = 0
# Выразим y: y = -(w0 * x + w2) / w1
line_x = np.linspace(0, 45, 100)
line_y = -(w[0] * line_x + w[2]) / w[1]

# разделение точек по классам
x_pos = x_train[y_train == 1] # класс 1
x_neg = x_train[y_train == -1] # класс -1

# Визуал
plt.figure(figsize=(9, 7))

# Отрисовка точек классов
plt.scatter(x_pos[:, 0], x_pos[:, 1], color='blue', label='Класс +1', s=100, edgecolors='k')
plt.scatter(x_neg[:, 0], x_neg[:, 1], color='red', label='Класс -1', s=100, edgecolors='k')

# Подпишем точки
for i, (x, y) in enumerate(zip(x_train, y_train)):
plt.text(x[0] + 0.5, x[1] + 0.5, f'{i+1}', fontsize=10)

# Отрисовка разделяющей прямой
plt.plot(line_x, line_y, 'g--', label='Разделяющая граница')

plt.xlabel('$x_1$')
plt.ylabel('$x_2$')
plt.title('Классификация с разделяющей прямой')
plt.legend()
plt.grid(True)
plt.xlim(0, 45)
plt.ylim(20, 80)
plt.show()
Please open Telegram to view this post
VIEW IN TELEGRAM
2111
👨‍💻 Н О В Ы Й П О С Т

⬇️ Задание в комментах ⬇️

🟢Скажем кратко: У нас уже есть готовая функция f(x), а нам нужно обучить свою модель так, чтобы она эту функцию имитировала

1⃣ Первое, у нас объявлена функция func(), которая возвращает значения функции:

🟰 f(x) = 0.1 * x**2 - sin(x) + 0.1 * cos(5x) + 1 🟰

2⃣ Второе, у нас есть вектор отсчетов coord_x по оси абсцисс (Ox) в диапазоне [-5; 5] с шагом 0,1 и вектор coord_y значений функции f(x) по оси ординат в соответствующих отсчетах coord_x.

🔵Но, давайте немного облегчим: coord_x — это просто множество значений в диапазоне от -5 до 5 с шагом 0.1 (всего 100 точек)

coord_x = np.arange(-5.0, 5.0, 0.1)


🔵coord_y = func(coord_x) — это мы для каждого x из coord_x вычислили значение функции:

 0.1 * x**2 - np.sin(x) + 0.1 * np.cos(x * 5) + 1 # Это не сам код, скорее блок для понимания


🔵По итогу, coord_y — это массив из 100 значений y (высота O(y) графика при каждом x)

‼️ Вместе эти два значения будут являться графиком функции f(x), график будет показывать как ведет себя функция в диапазоне от -5 до 5. Именно по этим данным будут считаться ошибки и средний эмпирический риск Q ‼️


3⃣ Третье, у нас есть моделька с параметрами и квадратической функцией потерь:

a(x, w) = w₀ + w₁ * x + w₂ * x² + w₃ * x³ + w₄ * x⁴

w = [1.11, -0.26, 0.061, 0.0226, 0.00178]

L_i(w, x_i) = (a(x_i, w) - f(x_i))² # Если что, это все есть в комментах в условии задачи


4⃣ Четвертое, НАМ НЕОБХОДИМО найти Q (средний эмпирический риск)

Q(a, X) = (1/n) * ∑[i=1 to n] L_i(w, x_i)


5⃣ Нам уже дан по условию блок кода, который необходимо продолжить:

import numpy as np

def func(x):
return 0.1 * x**2 - np.sin(x) + 0.1 * np.cos(x * 5) + 1.

coord_x = np.arange(-5.0, 5.0, 0.1) #Значения X
coord_y = func(coord_x) # Ральные значения

sz = len(coord_x) # общее число отсчетов
w = [1.11, -0.26, 0.061, 0.0226, 0.00178] #Параметры модельки


6⃣ Вычислим предсказания :

model_y = (
w[0] +
w[1] * coord_x +
w[2] * coord_x**2 +
w[3] * coord_x**3 +
w[4] * coord_x**4
)


7⃣ Ошибка модели:

losses = (model_y - coord_y)**2 # Сравниваем нашу модель с 'идеальными значениями'


8⃣ Средний эмпирический риск (чем он будет меньше, тем лучше модель приближает настоящую функцию и предсказания будут ближе к реальным значениям):

Q = np.mean(losses)


‼️ Короче, для каждой из 100 точек (coord_x) у нас есть идеальное значение (coord_y) ➡️ мы строим модельку a(x, ω) с параметрами ω ➡️ сравниваем предсказания модели с настоящими (идеальными) значениями ➡️считаем среднюю ошибку Q ‼️

⬇️ ГРАФИКИ И ВЫВОДЫ В КОММЕНТАХ ⬇️
Please open Telegram to view this post
VIEW IN TELEGRAM
3221