Перцептрон или дедушка нейросетей
Что такое перцептрон, зачем он нужен и как мы передали ответственность за алгоритмы на машину.
Откуда он взялся
До того как появились всякие заумные концепции машинного обучения, нейросети, глубокое обучение, люди писали код четко формулируя условия его выполнения.
Сейчас подобный код был бы похож на сборище иф элсов или того хуже перебора занчений в одном ифе:
if a == 1 and b == 1 and c != 1 and d == 1 and e == 1:
y = 1
else:
y = 0
То есть разработчик придумывает какое-то правило или набор правил, описывает его. А компьютер выполняет это правило.
На самом деле это ок, но что делать если условий так много, что код становится нечитабельным?
А что делать если мы научились что-то определять интуитивно, но нам тяжело сформировать какие-то четкие правила?
Эту проблему может решить перцептрон.
Совсем немного истории
В 1957 году Фрэнк Розенблатт создал концепцию перцептрона.

Однако из-за критики современников (Минского и Паперта в 1969 году), которые не безосновательно указали на фунадментальные ограничения однослойного перцептрона, произошло падение интереса к нейросетям почти на два десятилетия.
Да чем же так интересен этот перцептрон?
Смена парадигмы мышления.
Перцептрон как идеальный работник, может заявить нам:
Не говори мне правило, дай мне примеры. Я сам найду решение.
Действительно, бывает так, что задачи, которые перед нами стоят:
- не имеют никаких правил
- имеют слишком много правил
- имеют правила, которые часто меняются
- имеют правила, которые нам непонятны.
Например, всегда ли можно четко понять:
- плохой или хороший пользователь
- спам или нормальное сообщение
- опасна операция или не опасна
Простой вопрос, а как много факторов
Примеров много, но давайте разберем самый простой, который будет близок всем нам.
Пойти гулять сегодня или нет?
Представляете сколько всяких факторов может помешать нам выйти на улицу, а сколько факторов может заставить нас. Приведу в пример малую часть:
- Хорошая ли сегодня погода?
- Запланирована ли у меня встреча на улице?
- Болею ли я сейчас?
- Надо ли мне выгуливать собаку?
- Прошел ли я сегодня свою норму по шагам?
- Идет ли на улице дождь?
- Есть ли на улице солнце?
- Вчера я гулял?
- Много ли я гулял за последнюю неделю?
- Надо ли мне сбросить вес?
Уверен, что вы помимо этих примеров смогли бы назвать еще 10, а может и 100 примеров вопросов, которые могли бы определить судьбу вашей прогулки.
Было бы непросто закинуть все эти условия в код для поиска идеального алгоритма.
Да и идеального алгоритма даже в таком случае не будет. Тут ведь не просто сочетание ифов и элсов. У каждого фактора (вопроса) есть свой вес.
Ведь иногда даже если есть дождь, мы все равно пойдем на улицу, например, если у нас запланирована встреча. А иногда мы не пойдем на встречу, например, если заболели.
Исходя из этого мы можем сделать вывод, что факторов для принятия нами решений может быть много, но не все они имеют равную силу. У каждого фактора есть свой вес.
Магия перцептрона
Магия перцептрона заключается в том, что он умеет высчитывать эти веса из-за чего мы получаем довольно четкий ответ на вопрос, пойдем мы гулять сегодня или нет.
Для того, чтобы нам было легче разобраться с тем, как перцептрон это делает, мы предположим, что есть всего два фактора, в зависимости от которого, мы отвечаем на вопрос "Идем мы гулять сегодня, или нет?"
- Есть ли на улице солнце?
- Идет ли на улице дождь?
Каждый из этих признаков имеет свой вес.
Предположим, что мы идем на улицу только если есть солнце и нет дождя.
Солнце Дождь → Гуляем?
1 0 1 ✅
1 1 0 ❌
0 1 0 ❌
0 0 0 ❌
Очевидно, что солнце – это аргумента "ЗА", дождь — аргумент "ПРОТИВ". Это наши аргументы, у них есть свой вес.
Помимо аргументов, почти всегда еще общее настроение (для зумеров вайб).
Например, наша лень.
Общее настроение называют смещением (bias). Это то, насколько мы строги или ленивы в целом, даже если аргументов нет.
В чем смысл работы перецептрона?
Он делает всего одну вещь.
Перцептрон считает результат умножения и сложения и сравнивает результат с нулем. Если предсказание не совпадает с правильным ответом, мы корректируем веса и смещение и пробуем еще раз.
Вернемся к нашей погоде. Давай попробуем сделать выражение по правилам перцептрона, но пока без цифр.

Как мы можем определить пойдем на улицу или нет:
результат = ☀️ * вес солнца + 🌧 * вес дождя + моя лень(смещение)
Сравниваем наш результат с нулем, как это делает перцептрон.
если результат > 0
итог = 1
если результат <= 0
итог = 0
Что-то подобное происходит в нашей голове каждый раз при принятии решений. Просто Фрэнк Розенблатт, как психолог, смог это представить в виде умножений и сложений 🤯
Итог, который мы получили, мы сравниваем с конечным результатом, который был дан изначально.
Если итог совпадает, то мы сохраняем веса аргументов и смещение. Перцептрон сработал как надо. Когда к нам придут с такими же данными, мы уже будем знать, какой аргумент сколько весит, насколько мы ленивы, и мы без проблем сможем дать правильный ответ.
Если итог не совпадает, то мы корректируем веса аргументов и смещение. И пробуем заново посчитать результат. Так мы делаем, пока не найдем те самые веса и смещения, которые дадут нам спокойствие и умиротворение.
Теперь давайте попробуем добавить хотя бы немного цифр. Напишем маленький сниппет на питоне:
Вводные данные
# Солнце, Дождь, Правильный ответ (Гуляем?)
data = [
(1, 0, 1), # есть солнце, нет дождя → идем
(1, 1, 0), # солнце + дождь → не идем
(0, 1, 0), # нет солнца, дождь → не идем
(0, 0, 0), # ни солнца, ни дождя → не идем
]
- Первые два числа — аргументы.
- Последнее — правильный ответ.
Веса и смещение
Чтобы не тыкать пальцем в небо, давайте начнем с нулей:
w_sun = 0.0 # вес солнца
w_rain = 0.0 # вес дождя
Перцептрон ничего не знает, он пока нейтрален ко всему.
Однако мы знаем, что лень нам больше мешает, поэтому дадим ей произвольное отрицательное значение (никто не мешает нам поставить 0, перцептрон умный, он сам найдет все нужные значения).
lazy = -0.5 # моя лень (bias)
Функция перцептрона
Это сердце всей модели.
def perceptron(sun, rain):
result = sun * w_sun + rain * w_rain + lazy
return 1 if result > 0 else 0
Это как раз та самая формула, которую мы уже видели:
результат = ☀️ * вес солнца + 🌧 * вес дождя + моя лень(смещение)
если результат > 0
итог = 1
если результат< 0
итог = 0
Обучение: пробуем, ошибаемся, корректируем веса и смещение
«Если результат не совпадает — корректируем веса и лень». Это надо зафиксировать.
learning_rate = 0.1
Добавим переменую learning_rate. Это насколько сильно мы готовы менять веса и смещение.
Пока просто добавим, эта переменная нам понадобится позже.
Давайте попробуем сравнить ответы данных и предсказания перцептрона.
for sun, rain, correct_answer in data:
prediction = perceptron(sun, rain)
error = correct_answer - prediction
Тут мы понимаем ошиблись, или нет. Если ошиблись, то сможем понять в какую сторону:
- error = 0 → мы угадали
- error = 1 → надо было идти, но мы не пошли
- error = -1 → пошли, когда не надо было
Тут давайте чуть подробнее, а то когда я показывал эту часть своей жене, именно тут возникли вопросы.
error = correct_answer - prediction
Напоминаю, что correct_answer (правильный ответ) это последнее значение внутри списков (кортежей):
data = [
(1, 0, 1), # есть солнце, нет дождя → идем
(1, 1, 0), # солнце + дождь → не идем
(0, 1, 0), # нет солнца, дождь → не идем
(0, 0, 0), # ни солнца, ни дождя → не идем
]
То есть:
- correct_answer (правильный ответ) может быть, что мы идем или не идем. 1 или 0.
- prediction (предсказание перцептрона) может быть, что мы идем или нет, то есть тоже 1 или 0.
Когда мы вычитаем из правильного ответа предсказание перцептрона, у нас могут возникнуть следующие ситуации:
error = 0 - 0 # 0 - ошибок нет, предсказание перцептрона является верным.
error = 1 - 1 # 0 - тоже ошибок нет, предсказание перцептрона тоже верное.
Оба варианта подразумевают, что предсказания перцептрона верные. Ответы совпадают, у нас нет необходимости, что-либо корректировать.
Другая история получается если error становится равным 1:
error = 1 - 0 # 1 - мы должны были получить 1 (что идем на улицу),
# а поучили 0 от перцептрона (что не идем на улицу)
Это означает, что предсказания перцептрона оказались слишком пессимистичные.
То есть мы занизили веса аргументов, которые участвовали в решении. Например, важность того, что солнце есть на самом деле намного значительнее, вдруг мы в Норвегии, и солнце для нас особенно сильный аргумент, чтобы выйти на улицу. Необходимо повысить вес.
А что делать, если error стал равен -1?
error = 0 - 1 # -1 мы должны были получить 0 (что не выходим на улицу),
# а получили 1, от перцептрона (что выходим на улицу)
Это означает, что предсказания перцептрона оказались слишком оптимистичные.
То есть мы завысили веса аругментов, которые участвовали в решении. Например, солнце для нас не имеет такого большого значения, мы живем в Калифорнии, солнце круглогодично, и мне в принципе не так важно есть оно или нет. Необходимо понизить вес.
А сейчас будет вообще магия математики:
Корректировка весов (самое важное место)
Мы понимаем, что в зависимости от значения error, мы должны:
- Либо ничего не менять (когда error = 0)
- Либо повысить веса (когда error = 1)
- Либо понизить веса (когда error = -1)
При этом мы должны помнить, что меняем мы веса только тех аргументов, которые участвовали в принятии решения перцептроном.
Можно воспользоваться любимыми иф элсами. Тогда получится что-то подобное:
# проверяем что sun == 1, что есть что есть солнце и при этом есть ошибка
if error == 1 and sun == 1:
w_sun = w_sun + learning_rate
elif error == -1 and sun == 1:
w_sun = w_sun - learning_rate
# проверяем что rain == 1, то есть что дождь был, и при этом все равно ошибка
if error == 1 and rain == 1:
w_rain = w_rain + learning_rate
elif error == -1 and rain == 1:
w_rain = w_rain - learning_rate
# в остальных случаях мы веса дождя или солнца вообще не трогаем.
Но это выглядит не так элегантно, для того чтобы назвать это "алгоритмом" или "дедушкой нейросетей", поэтому пришли математики и дали красивое решение (которое по своей сути делает абсолютно то же самое):
w_sun = w_sun + learning_rate * error * sun
w_rain = w_rain + learning_rate * error * rain
Это не магия: если sun = 0, вес солнца не меняется, потому что этот признак не участвовал в принятии решения.
То же самое справедливо и для дождя.
А что делать с ленью?
Лень мы будем двигать всегда, если есть ошибка.
lazy = lazy + learning_rate * error
Интуитивно:
- если солнце помогло ошибиться → увеличиваем его вес
- если дождь помешал → уменьшаем
- лень двигается всегда
Почему bias двигается всегда?
На самом деле это довольно логично.
Представьте, если у вас на вход приходит, что нет дождя и нет солнца. То есть 0 и 0. И мы при этом получаем ошибку.
Поменять вес солнца и дождя, как мы уже знаем, нельзя. Ведь в данном случае они не участвовали в принятии решения перецептрона.
Значит, что единственное что может поменять исход это bias. То есть общее настроение. Например, если у нас нет дождя, нет солнца, а мы все равно идем гулять. Значит общее настроение это скорее всего не лень, а наша проактивность (или большая любовь к прогулке).
Исходя из этого мы можем сделать вывод, что bias может быть и положительным числом.
При этом важно запомнить, что
bias в любом случае будет меняться в случае ошибки перцептрона!
Прогоним обучение несколько раз
Один проход — мало. Повторим несколько эпох:
for epoch in range(10):
for sun, rain, correct_answer in data:
prediction = perceptron(sun, rain)
error = correct_answer - prediction
w_sun = w_sun + learning_rate * error * sun
w_rain = w_rain + learning_rate * error * rain
lazy = lazy + learning_rate * error
Посмотрим, чему мы научились
print("Вес солнца:", w_sun)
print("Вес дождя:", w_rain)
print("Лень:", lazy)
А теперь проверим себя:
for sun, rain, _ in data:
print(sun, rain, "→", perceptron(sun, rain))
Вывод в консоль:
Вес солнца: 0.3
Вес дождя: -0.1
Лень: -0.2
1 0 → 1
1 1 → 0
0 1 → 0
0 0 → 0
Перцептрон научился нашему правилу. Мы дали ему лишь данные, он сам определил веса аргументов и силу нашей лени.
Вот полный сниппет:
# Солнце, Дождь, Правильный ответ (Гуляем?)
data = [
(1, 0, 1), # есть солнце, нет дождя → идем
(1, 1, 0), # солнце + дождь → не идем
(0, 1, 0), # нет солнца, дождь → не идем
(0, 0, 0), # ни солнца, ни дождя → не идем
]
# Перцептрон ничего не знает, он пока нейтрален ко всему.
w_sun = 0.0 # вес солнца
w_rain = 0.0 # вес дождя
lazy = -0.5 # моя лень (bias)
def perceptron(sun, rain):
result = sun * w_sun + rain * w_rain + lazy
return 1 if result > 0 else 0
# Насколько сильно мы корректируем веса
learning_rate = 0.1
# Обучение, корректировка весов
# Прогоним несколько раз
for epoch in range(10):
for sun, rain, correct_answer in data:
prediction = perceptron(sun, rain)
error = correct_answer - prediction
w_sun = w_sun + learning_rate * error * sun
w_rain = w_rain + learning_rate * error * rain
lazy = lazy + learning_rate * error
# Посмотрим итоговые результаты в конце:
print("Вес солнца:", w_sun)
print("Вес дождя:", w_rain)
print("Лень:", lazy)
# Сравним результат, с тем, что нам давалось изначально:
for sun, rain, _ in data:
print(sun, rain, "→", perceptron(sun, rain))
И как это можно использовать в реальной жизни?
Далее это можно использовать как обычную функцию, которая ответит на вопрос. Пойдем мы сегодня гулять или нет.
Она будет выглядеть как-то так.
def should_go_outside(sun: int, rain: int) -> bool:
# вес солнца, вес дождя, минус bias(лень)
prediction = sun * 0.3 + rain * -0.1 - 0.2
return True if prediction > 0 else False
А если данные опять поменяются, то мы сможем заново прогнать все через функцию перцептрона, получив новые веса.
И че?
Зачем вы это прочитали? Ну был и был, че бубнить то про какой-то перцептрон. Его все равно там раскритиковали потом.
Ограничения перцептрона:
Да, его раскритиковали, вот основные ограничения перцептрона:
- Он не умеет справляться с XOR:
x1 x2 → y
0 0 → 0
0 1 → 1
1 0 → 1
1 1 → 0
x2 ↑
1 | ● ○
|
0 | ○ ●
+----------------→ x1
0 1
● = 1
○ = 0
Нет ни одной прямой линии, которая их разделит.
Один перцептрон в таком случае бесполезен. Именно эта проблема позже привела к появлению многослойных сетей.
- Перцептрон не знает вероятностей.
Он может выдавать только жесткое бинарное решение. 1 или 0.
Это проблема, если:
- нужен риск
- нужна калибровка
- нужны пороги
Поэтому в реальности чаще используют логистическую регрессию.
- Перцептрон не умеет строить сложные зависимости:
Если есть солнце ИЛИ (есть дождь И запланирована встреча)
Перцептрон не умеет скобки.
Он складывает всё линейно.
- Плохо работает с реальными данными:
Реальный мир:
- шумный
- противоречивый
- неполный
Перцептрон:
- ожидает чёткие границы
- плохо переносит ошибки разметки
- ломается при перекосе классов
И зачем я это прочитал
Не работает твой перцептрон! Вон там что нейросети делают, а я тут свое время на твое старье трачу.
Как бы не так!
Про перцептрон нужно и важно знать. Ведь он:
- Объясняет идею обучения. Ml не сразу появилось, надо чтить память наших дедов!
- Учит нас думать в категории признаков и весов.
- Учит думать не “как запрограммировать правило”, а как позволить модели найти правило.
В следующий раз, когда вы попробуете снова изучить как работают нейросети. И например, решите посмотреть видео от 3Blue1Brown
Вы обязательно наткнетесь на многослойный перцептрон.
Надеюсь, что на одну загадку в вашей жизни стало меньше.
C любовью к перцептрону ❤️
Сергей Усынин