Читать книгу «Нейросети: создание и оптимизация будущего» онлайн полностью📖 — Джеймса Девиса — MyBook.
image
cover









Искусственная нейронная сеть (ИНС) работает исключительно с числовыми значениями, что позволяет ей легко обрабатывать и передавать информацию через узлы и слои. Каждая входная характеристика объекта, будь то пиксели изображения, слова в тексте или другие числовые данные, представлена числовым значением, которое затем передается по сети для дальнейших преобразований. Это числовое представление данных является основой для всех вычислений, которые выполняет ИНС, и позволяет ей выявлять закономерности и делать прогнозы.

Числовые данные могут быть представлены как одномерные векторы, если речь идет о простых характеристиках, или как многомерные матрицы, если данные более сложные и содержат пространственные или временные взаимосвязи. Например, изображение, которое представляет собой двумерное пространство, обычно преобразуется в матрицу значений, где каждый элемент матрицы соответствует интенсивности цвета пикселя. Аналогично, в текстовой обработке слова могут быть закодированы в виде векторов, где каждый элемент вектора отражает одну характеристику слова, такую как его частота или взаимосвязь с другими словами в предложении.

Векторы: представление одномерных данных

Вектор – это одномерный массив чисел, который может представлять простой набор характеристик объекта. Например, чтобы сеть могла работать с изображением, его нужно представить как последовательность пикселей, каждый из которых преобразуется в числовое значение, отражающее его яркость или цвет. В случае обработки текста вектор может представлять каждое слово, закодированное через числовые значения, которые отражают его смысловое значение или позицию. Каждый элемент вектора соответствует одной входной характеристике, позволяя сети «видеть» и учитывать её при обработке данных. Векторное представление особенно удобно, когда характеристики объекта независимы друг от друга и могут быть переданы сети без дополнительной пространственной информации.

Использование векторов также актуально для задач, где входные данные поступают из измерений или количественных характеристик. Например, при обработке финансовых данных вектор может содержать числовые значения, представляющие ежедневные цены акций, доходы или другие метрики. Векторное представление делает возможным быстрые и эффективные вычисления, необходимые для классификации и других задач, где важны значения характеристик, но не их пространственные взаимосвязи.

Матрицы: двумерное представление для сложных данных

Матрицы представляют собой двумерные массивы, где данные хранятся в виде строк и столбцов. Этот формат особенно полезен для более сложных данных, таких как изображения, временные ряды или любые данные, требующие сохранения пространственной или временной структуры. В случае изображения каждая ячейка матрицы будет соответствовать одному пикселю, а значение в ней – интенсивности или цвету пикселя. Это позволяет сети видеть изображение как единое целое, сохраняя его структуру и особенности, такие как границы, текстуры и формы. Это важно для задач распознавания изображений, где пространственные отношения между элементами изображения имеют решающее значение.

Временные ряды также могут быть представлены в виде матриц, где строки могут соответствовать временным меткам, а столбцы – различным параметрам или характеристикам данных. Например, временные данные о погоде могут быть организованы как матрица, где каждая строка представляет определённый день или час, а каждый столбец – температуру, влажность и другие параметры. Матрицы позволяют сохранить связи и последовательность между данными, что помогает нейронной сети понимать их взаимосвязи и лучше справляться с задачами прогнозирования.

Преобразование данных для нейронной сети

Перед подачей в нейронную сеть данные обычно проходят предварительную обработку, включающую преобразование в числовой формат, нормализацию и масштабирование. Нормализация, например, может быть полезной, чтобы значения входных данных находились в одном диапазоне, что помогает модели обучаться быстрее и избегать проблем, связанных с сильно различающимися масштабами характеристик. После нормализации данные превращаются в векторы или матрицы, подходящие для обработки в сети, где каждый элемент легко интерпретируется узлами сети. Эти преобразования делают данные совместимыми с архитектурой ИНС, которая затем может анализировать их на каждом слое, выявляя закономерности и закономерности.

Таким образом, преобразование данных в числовые векторы и матрицы является критически важным шагом, который делает информацию доступной для ИНС, позволяя ей эффективно работать с разнообразными типами входных данных, будь то изображения, текст или временные ряды.

Когда вектор или матрица поступает в сеть, каждый элемент умножается на веса и проходит через функцию активации. Эти операции продолжаются через слои сети, пока модель не выведет результат на выходе.

1.3. Принципы работы нейронных сетей

Нейронные сети – это алгоритмы, которые пытаются имитировать процесс принятия решений в мозге, обрабатывая данные, используя ряд искусственных «нейронов». Каждый нейрон выполняет простые операции, но при объединении в многослойную структуру сеть может решать сложные задачи. Основной принцип нейронной сети – это прохождение данных через сеть нейронов, которые организованы в слои (входной, скрытые и выходной). На каждом этапе информация преобразуется, и сеть обучается корректировать свои внутренние параметры, чтобы уменьшить ошибки на выходе.

Функции активации: сигмоид, ReLU, tanh и их особенности

– Сигмоидная функция: Сигмоидная функция активации сжимает входные значения в диапазон от 0 до 1, что удобно для задач, где требуется вероятностная интерпретация результата (например, бинарная классификация). Она имеет плавный S-образный вид. Однако, когда значения на входе очень большие или маленькие, сигмоид сильно сглаживает значения, делая градиент почти равным нулю. Это приводит к проблеме затухающих градиентов, что замедляет обучение.

– ReLU (Rectified Linear Unit): ReLU активируется только при положительных входных значениях, а при отрицательных обнуляется. Она значительно ускоряет обучение по сравнению с сигмоидом и помогает преодолеть проблему затухающих градиентов. Однако ReLU имеет свою проблему: если значение на входе слишком велико или слишком мало, нейрон может «вылететь» в область, где он всегда отдает ноль, так называемая проблема «умирающих нейронов».

– tanh (гиперболический тангенс): tanh работает похоже на сигмоид, но сжимает значения в диапазон от -1 до 1. Это помогает справляться с отрицательными входами, что полезно для задач, где такие значения играют важную роль. Tanh также подвержена затуханию градиентов, но меньше, чем сигмоид. Он помогает в задачах, где важно учитывать знаки выходных данных, так как диапазон шире, чем у сигмоида.

Каждая функция активации выбирается в зависимости от конкретной задачи и структуры сети. Например, ReLU предпочтителен для глубоких сетей, так как он обеспечивает более быстрый и стабильный процесс обучения.

Примеры задач для различных функций активации

Сигмоидная функция

Задача: Определение, является ли пациент здоровым (0) или больным (1) на основе анализа его медицинских данных.

Решение: В этой задаче бинарной классификации нужно построить нейросеть, которая на основе различных показателей (возраст, давление, уровень холестерина и пр.) предскажет вероятность того, что пациент болен.

Для этого:

1. На вход подаются числовые значения параметров.

2. Нейроны скрытого слоя обрабатывают эти данные и передают в выходной нейрон.

3. Сигмоидная функция активации применяется на выходном слое, сжимая итоговое значение между 0 и 1. Если значение близко к 1, сеть «уверена», что пациент болен; если близко к 0 – здоров.

Особенность: Сигмоид удобен, поскольку интерпретируется как вероятность. Однако, если сеть получает очень большие значения на входе (например, значение здоровья больше 10 или меньше -10), сигмоид сильно сглаживает выход, давая почти 0 или почти 1. Из-за этого нейроны начинают "глохнуть" и сеть обучается медленнее – проблема затухающих градиентов.

ReLU (Rectified Linear Unit)

Задача: Распознавание объектов на изображениях (например, классификация, что на картинке – собака или кошка).

Решение: Эта задача требует глубокую сверточную нейросеть, в которой обработка изображения должна проходить через множество слоев.

1. Изображение пропускается через сверточные и полносвязные слои.

2. На каждом из этих слоев используются нейроны с функцией активации ReLU. ReLU активирует нейроны для всех положительных значений, а все отрицательные преобразует в ноль, ускоряя вычисления и обучение.

3. После ряда слоев сеть дает прогноз по объекту, показанному на изображении.

Особенность: ReLU хорошо справляется с глубокими сетями, позволяя избежать затухания градиентов, так как не сглаживает значения. Однако если нейроны получают очень большие или слишком маленькие значения, они могут "умирать", становясь всегда равными нулю и отключаясь от дальнейшего обучения. Поэтому для глубоких сетей иногда используют его модификацию – Leaky ReLU, которая сохраняет небольшие отрицательные значения, предотвращая «умирание» нейронов.

tanh (гиперболический тангенс)

Задача: Предсказание изменения цены акций в зависимости от рыночных факторов (например, макроэкономических показателей).

Решение: Для этой задачи строится нейросеть, которая оценивает разностные данные (рост или падение) – то есть она должна различать положительные и отрицательные значения.

1. Данные об изменении рынка подаются на входные нейроны.

2. Нейроны скрытых слоев используют функцию активации tanh, которая нормирует выходные значения от -1 до 1. Благодаря этому сеть может выдать как положительные, так и отрицательные значения, полезные для предсказания роста или падения.

3. На выходе сеть дает прогноз по изменению цены.

Особенность: Поскольку tanh учитывает знак значений, он подходит для задач, где важно различать положительные и отрицательные выходные данные, например, изменение цен, температуры или других разностных характеристик. Однако tanh также подвержен затуханию градиентов, но его диапазон шире, чем у сигмоида, и он лучше подходит для данных, которые изменяются в обе стороны.

Каждая функция активации помогает сети обрабатывать данные по-разному, обеспечивая подходящий баланс между скоростью, стабильностью обучения и интерпретируемостью вывода. Выбор функции зависит от сложности задачи, глубины сети и особенностей данных.

Обратное распространение ошибки и процесс обучения сети

Обратное распространение ошибки (backpropagation) – ключевой процесс, используемый для обучения нейронных сетей. Он начинается с того, что сеть выдает какой-то результат, который затем сравнивается с эталонным (правильным) значением. После этого сеть рассчитывает, насколько велика ошибка между предсказанием и реальным результатом.

Процесс обратного распространения заключается в том, чтобы передать эту ошибку обратно через сеть, корректируя веса каждого нейрона, чтобы в следующий раз ошибка была меньше. Это делается постепенно, слой за слоем, начиная с последнего (выходного) слоя и продвигаясь к входному. Цель этого процесса – минимизировать суммарную ошибку сети, заставляя её «учиться» лучше соответствовать эталонным данным.

Для понимания процесса обратного распространения ошибки и обучения сети создадим простую нейронную сеть с использованием библиотеки PyTorch. Эта сеть будет решать задачу бинарной классификации, предсказывая принадлежность к одному из двух классов.

Задача

Рассмотрим задачу, где мы классифицируем точки на плоскости в зависимости от их положения относительно заданной прямой. Наша цель – обучить нейронную сеть, которая сможет определить, в какой из двух классов (0 или 1) попадает каждая точка.

Решение с использованием обратного распространения и процесса обучения

В этом примере:

1. Мы создаем простую сеть с двумя полносвязными слоями.

2. Определяем функцию ошибки (Binary Cross Entropy).

3. Используем метод обратного распространения для корректировки весов сети на каждом этапе обучения.

Код решения

```python

import torch

import torch.nn as nn

import torch.optim as optim

import numpy as np

import matplotlib.pyplot as plt

# Генерация данных

np.random.seed(0)

torch.manual_seed(0)

data_size = 100

X = np.random.rand(data_size, 2) * 2 – 1 # точки на плоскости от -1 до 1

Y = (X[:, 0] + X[:, 1] > 0).astype(int) # класс 1, если сумма координат > 0, иначе 0

# Преобразование в тензоры

X_tensor = torch.FloatTensor(X)

Y_tensor = torch.FloatTensor(Y).reshape(-1, 1)

# Определение нейронной сети

class SimpleNet(nn.Module):

def __init__(self):

super(SimpleNet, self).__init__()

self.fc1 = nn.Linear(2, 4) # первый полносвязный слой

self.fc2 = nn.Linear(4, 1) # выходной слой для предсказания класса

def forward(self, x):

x = torch.relu(self.fc1(x))

x = torch.sigmoid(self.fc2(x))

return x

# Инициализация сети, функции потерь и оптимизатора

model = SimpleNet()

criterion = nn.BCELoss() # Binary Cross Entropy Loss

optimizer = optim.SGD(model.parameters(), lr=0.1)

# Обучение

epochs = 1000

losses = []

for epoch in range(epochs):

# Прямой проход

outputs = model(X_tensor)

loss = criterion(outputs, Y_tensor)

# Обратное распространение и оптимизация

optimizer.zero_grad() # очистка градиентов

loss.backward() # вычисление градиентов

optimizer.step() # обновление весов

losses.append(loss.item())

if (epoch+1) % 100 == 0:

print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')

# График функции потерь

plt.plot(losses)

plt.xlabel('Epoch')

plt.ylabel('Loss')

plt.title('Процесс обучения')

plt.show()

# Визуализация результатов

with torch.no_grad():

predictions = model(X_tensor).round()

plt.scatter(X[:, 0], X[:, 1], c=predictions.reshape(-1), cmap='coolwarm', marker='o', edgecolors='k')

plt.title("Классификация точек")

plt.show()

```

Пояснение к коду

1. Генерация данных: Мы создали случайные точки и разделили их на два класса в зависимости от их положения относительно прямой (x + y = 0).

2. Модель: Простая нейросеть с двумя слоями – входной слой с 2 нейронами (для двух координат) и один скрытый слой на 4 нейрона. На выходе – один нейрон с функцией активации `sigmoid`, который предсказывает вероятность принадлежности к классу.

3. Функция ошибки: Используем `BCELoss` (Binary Cross Entropy), поскольку это подходящий критерий для задач бинарной классификации.

4. Оптимизация: Параметры сети оптимизируются методом стохастического градиентного спуска (SGD).

5. Обучение: На каждом шаге выполняется прямой проход для вычисления ошибки, затем вычисляются градиенты и обновляются веса, что и является сутью обратного распространения ошибки.

6. Визуализация: Построение графика потерь для наблюдения за процессом обучения и визуализация предсказаний.

Результат

График потерь демонстрирует снижение ошибки, а классификация точек показывает, что сеть успешно научилась разделять классы, корректируя веса с каждым циклом обучения, основываясь на ошибках.

Этот процесс и есть результат обратного распространения ошибки: сеть обучается на ошибках, постепенно улучшая свои предсказания.

Градиентный спуск и оптимизация параметров

Градиентный спуск – это метод, который используется для минимизации функции ошибки в процессе обучения. Принцип градиентного спуска заключается в том, чтобы двигаться в направлении самого быстрого уменьшения ошибки, находя точки, где ошибка минимальна. Представьте это как спуск по склону горы: каждый шаг направлен туда, где рельеф понижается, с целью оказаться в самой низкой точке.

Градиентный спуск бывает нескольких типов:

– Обычный (batch) градиентный спуск: Он учитывает весь набор данных на каждом шаге и обновляет веса, основываясь на средней ошибке, что может быть вычислительно затратным.

– Стохастический градиентный спуск (SGD): Здесь обновление весов происходит на каждом отдельном примере, что делает обучение более быстрым, но с шумом, так как веса могут изменяться от случая к случаю.

– Мини-batch градиентный спуск: Здесь данные разделяются на небольшие группы (мини-батчи), на основе которых рассчитывается ошибка и корректируются веса, что позволяет использовать преимущества обоих методов.

Оптимизация параметров сети включает выбор скорости обучения и других гиперпараметров, таких как момент, чтобы корректировка весов происходила достаточно быстро, но без переборов. Существуют также адаптивные методы оптимизации, такие как Adam и RMSprop, которые динамически настраивают скорость обучения для каждого веса, учитывая историю изменений, что позволяет избежать проблем с оптимизацией и улучшает эффективность обучения.

Этот процесс позволяет сети с каждым шагом приближаться к решению задачи, становясь все более точной.

Примеры для разных типов градиентного спуска с использованием библиотеки PyTorch

Для каждого типа градиентного спуска мы решим простую задачу регрессии: предскажем значение (y) для каждого (x) по уравнению ( y = 2x + 3 ) с добавлением небольшого шума.

1. Обычный (Batch) Градиентный Спуск

В этом случае мы используем весь набор данных для вычисления средней ошибки на каждом этапе, после чего обновляем веса.

```python

import torch

import torch.nn as nn

import torch.optim as optim

import numpy as np

import matplotlib.pyplot as plt

# Генерация данных

np.random.seed(0)

X = np.linspace(-1, 1, 100)

Y = 2 * X + 3 + 0.1 * np.random.randn(100)

# Преобразование в тензоры

X_tensor = torch.FloatTensor(X).view(-1, 1)

Y_tensor = torch.FloatTensor(Y).view(-1, 1)

# Простая линейная модель

model = nn.Linear(1, 1)

criterion = nn.MSELoss()

optimizer = optim.SGD(model.parameters(), lr=0.01)

# Обучение с использованием batch градиентного спуска

epochs = 1000

losses = []

for epoch in range(epochs):

optimizer.zero_grad()

predictions = model(X_tensor)

loss = criterion(predictions, Y_tensor)

loss.backward()

optimizer.step()

losses.append(loss.item())

# График ошибки

plt.plot(losses)

plt.xlabel("Epoch")

plt.ylabel("Loss")

plt.title("Batch Gradient Descent")

plt.show()

```

Пояснение: Модель обновляет веса, используя весь набор данных для каждого шага. Такой подход может быть более точным, но затратен по времени на больших данных.

2. Стохастический Градиентный Спуск (SGD)

Теперь каждый пример обновляет веса независимо, что делает обучение более быстрым, но и более "шумным".

```python

# Новая модель

model = nn.Linear(1, 1)

optimizer = optim.SGD(model.parameters(), lr=0.01)

# Обучение с использованием стохастического градиентного спуска

losses = []

for epoch in range(epochs):

for x, y in zip(X_tensor, Y_tensor):

optimizer.zero_grad()

prediction = model(x.view(-1, 1))

loss = criterion(prediction, y.view(-1, 1))

loss.backward()

optimizer.step()

losses.append(loss.item())

# График ошибки

plt.plot(losses)

plt.xlabel("Step")

plt.ylabel("Loss")

plt.title("Stochastic Gradient Descent")

plt.show()

```