Нейрон: шаг в будущее

⏱ 40 мин · 🧩 заданий: 5

Шаг в будущее: немного истории

Идея «искусственного нейрона» почти так же стара, как сами компьютеры.

  • 1943 — Уоррен Мак-Каллок и Уолтер Питтс описали математическую модель нейрона: он суммирует сигналы и «срабатывает», если сумма выше порога. Они показали, что из таких элементов можно собрать любую логическую схему.
  • 1958 — Фрэнк Розенблатт построил перцептрон — нейрон, который учится на примерах, подстраивая веса. Пресса писала, что скоро машины будут ходить, говорить и осознавать себя.
  • 1969 — Марвин Минский и Сеймур Пейперт доказали, что один перцептрон не может выучить даже простую функцию XOR. Интерес и финансирование надолго иссякли — началась первая «зима ИИ».
  • 1986 — Румельхарт, Хинтон и Уильямс популяризировали обратное распространение ошибки: сети из нескольких слоёв нейронов можно обучать градиентным спуском, и XOR им по силам.
  • 2012 — свёрточная сеть AlexNet с большим отрывом выиграла конкурс распознавания изображений ImageNet, обучившись на видеокартах. Началась эпоха глубокого обучения, которая привела к сегодняшним языковым моделям.

Всё это построено из одного элемента, который мы сейчас разберём.

Как устроен нейрон

Биологический нейрон получает сигналы от других нейронов через дендриты, «взвешивает» их (одни синапсы усиливают сигнал, другие ослабляют) и, если суммарное возбуждение превышает порог, посылает импульс дальше по аксону. Искусственный нейрон — сильно упрощённая математическая версия:

z = w_1 x_1 + w_2 x_2 + \dots + w_n x_n + b = w \cdot x + b, \qquad y = f(z)
  • x — входы (признаки или выходы других нейронов);
  • w — веса: насколько важен каждый вход и с каким знаком;
  • b — сдвиг (bias): порог срабатывания;
  • f — функция активации: ступенька, сигмоида, ReLU…
x₁ ──w₁──┐ x₂ ──w₂──┼──▶ Σ wᵢxᵢ + b = z ──▶ f(z) ──▶ y x₃ ──w₃──┘ ▲ b
import numpy as np

def neuron(x, w, b):
    z = w @ x + b
    return 1 / (1 + np.exp(-z))          # сигмоида

# «нейрон, который любит солнце и не любит ветер»: признаки (солнце, ветер, температура)
w = np.array([2.0, -3.0, 0.1])
b = -1.0
for day in ([1, 0, 20], [1, 1, 25], [0, 0, 15], [0, 1, 5]):
    print(day, f"→ желание гулять {neuron(np.array(day, float), w, b):.2f}")

Геометрия: нейрон проводит прямую

Нейрон с двумя входами и ступенькой (или сигмоидой с порогом 0.5) делит плоскость на две части прямой

w_1 x_1 + w_2 x_2 + b = 0

С одной стороны z > 0 (класс 1), с другой z < 0 (класс 0). Вектор весов w перпендикулярен прямой и указывает в сторону класса 1, а сдвиг b двигает прямую. В n измерениях это гиперплоскость. Модели с такой границей называют линейными классификаторами.

Попробуйте подобрать веса для логических функций. Точки — 4 комбинации входов, цвет — нужный ответ. Обведённые красным — ошибки.

  • И (AND): w_1 = w_2 = 1, b = -1.5 — прямая отсекает только угол (1, 1).
  • ИЛИ (OR): w_1 = w_2 = 1, b = -0.5.
  • XOR — попробуйте… и убедитесь, что ни одна прямая не отделяет (0, 1) и (1, 0) от (0, 0) и (1, 1).

Обучение перцептрона

Розенблатт предложил простое правило: показываем примеры по одному; если нейрон ошибся, сдвигаем веса в сторону правильного ответа:

w \leftarrow w + (y - \hat y)\,x, \qquad b \leftarrow b + (y - \hat y)

Если нейрон сказал 0, а надо 1, к весам прибавляется вход — в следующий раз w \cdot x на этом примере будет больше. Если сказал 1, а надо 0, — вычитается. Для линейно разделимых данных доказано, что перцептрон за конечное число шагов найдёт разделяющую прямую.

import numpy as np
import vis

X = np.array([[0.0, 0], [0, 1], [1, 0], [1, 1]])
for name, y in [("И", [0, 0, 0, 1]), ("ИЛИ", [0, 1, 1, 1]), ("XOR", [0, 1, 1, 0])]:
    w, b = np.zeros(2), 0.0
    for epoch in range(20):
        errors = 0
        for x, t in zip(X, y):
            p = 1 if w @ x + b > 0 else 0
            if p != t:
                w += (t - p) * x
                b += t - p
                errors += 1
    print(f"{name:4}: w = {w}, b = {b:+.0f}, ошибок в последней эпохе: {errors}")
    vis.scatter(X, labels=y, lines=[(w[0], w[1], b)], title=f"Перцептрон: {name}", equal=True)

На XOR перцептрон так и не сходится: правильной прямой просто нет.

Нейрон с градиентным спуском

Ступенька плоха для градиентного спуска: её производная везде 0. Если заменить её сигмоидой, а правило перцептрона — минимизацией бинарной кросс-энтропии, получится логистическая регрессия — нейрон, выдающий вероятность. Обучим его в nanotorch:

import numpy as np
import nanotorch as nt
import vis

X, y = nt.make_blobs(200, centers=((-1.5, -1), (1.5, 1)), std=1.0, seed=2)

nt.manual_seed(0)
neuron = nt.Linear(2, 1)                    # w·x + b; сигмоида встроена в функцию потерь
opt = nt.SGD(neuron.parameters(), lr=0.5)
for step in range(200):
    logits = neuron(nt.Tensor(X))
    loss = nt.binary_cross_entropy(logits, y)
    opt.zero_grad()
    loss.backward()
    opt.step()

w, b = neuron.W.data.ravel(), neuron.b.data[0]
acc = ((X @ w + b > 0) == (y == 1)).mean()
print(f"w = {w.round(2)}, b = {b:.2f}, loss = {loss.item():.3f}, точность = {acc:.1%}")
vis.boundary(lambda P: 1 / (1 + np.exp(-(P @ w + b))), X, y, title="Нейрон: вероятность класса 1 и граница p = 0.5")

Цвет фона — вероятность, которую выдаёт нейрон: у границы она около 0.5, вдали от неё — уверенные 0 или 1.

Что дальше

Один нейрон — линейный классификатор: он умеет только провести прямую. Чтобы решать XOR, спирали и распознавать цифры, нейроны объединяют:

  1. в слой — много нейронов, смотрящих на одни и те же входы (следующий урок);
  2. в сеть — слои один за другим, с нелинейностью между ними.

Итог

  • Нейрон: y = f(w \cdot x + b) — взвешенная сумма входов, сдвиг и функция активации.
  • Геометрически — гиперплоскость w \cdot x + b = 0, делящая пространство на два класса.
  • Один нейрон реализует И, ИЛИ, НЕ, но не XOR: классы XOR линейно неразделимы.
  • Правило перцептрона: w \mathrel{+}= (y - \hat y)x; с сигмоидой и кросс-энтропией нейрон обучается градиентным спуском (логистическая регрессия).

Практика решено 0 из 5

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Один вариант ответа

Нейрон: y = \text{step}(w_1 x_1 + w_2 x_2 + b), где step(z) = 1 при z > 0, иначе 0. Какие веса реализуют логическое И (AND) для входов 0/1?

Задание 2 Один вариант ответа

Почему один нейрон не может реализовать XOR?

Задание 3 Ввод ответа

Сколько параметров у нейрона с 10 входами (веса и сдвиг)?

Задание 4 Программа на Python

Нейрон. Напишите neuron(x, w, b, activation="sigmoid"): x и w — NumPy-векторы одной длины, b — число. Функция возвращает activation(w·x + b), где activation — "sigmoid", "relu" или "step" (1.0 при z > 0, иначе 0.0). Верните обычный float.

Какие тесты проверяют код (3)
assert neuron(np.array([1.0, 2]), np.array([0.5, -1]), 2.0, "step") == 1.0 and neuron(np.array([1.0, 2]), np.array([0.5, -1]), 1.5, "step") == 0.0
assert neuron(np.array([1.0, 2]), np.array([0.5, -1]), 1.0, "relu") == 0.0 and neuron(np.array([3.0]), np.array([2.0]), -1, "relu") == 5.0
assert abs(neuron(np.array([1.0, 1]), np.array([1.0, 1]), -2) - 0.5) < 1e-12
Задание 5 Программа на Python

Правило перцептрона. Напишите perceptron(X, y, epochs=20): X — (N, d), y — метки 0/1. Начните с w = 0 (вектор) и b = 0. В каждой эпохе пройдите по объектам по порядку: предсказание p = 1 if w @ x + b > 0 else 0; если p != y_i, обновите w += (y_i - p) * x, b += (y_i - p). Верните (w, b).

Тест проверит, что на линейно разделимых данных (функция ИЛИ и облака точек) ошибок не остаётся.

Какие тесты проверяют код (2)
X = np.array([[0.0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([0, 1, 1, 1])
w, b = perceptron(X, y)
assert all((w @ x + b > 0) == bool(t) for x, t in zip(X, y)), f"ИЛИ не выучено: w={w}, b={b}"

import nanotorch as nt
X, y = nt.make_blobs(100, centers=((-2, -2), (2, 2)), std=0.7, seed=0)
w, b = perceptron(X, y)
assert ((X @ w + b > 0) == (y == 1)).mean() == 1.0

Войдите, чтобы отмечать пройденные уроки.