Нейросети на языке Python: практическое руководство
Введение в нейросети на Python
Нейронные сети стали неотъемлемой частью машинного обучения. Python предоставляет несколько подходов для их реализации: от низкоуровневых вычислений до высокоуровневых фреймворков. В этой статье рассмотрим основные варианты, их цели и типичные проблемы.
Основное решение: быстрая нейросеть с Keras
Как создать простую нейросеть для классификации всего за несколько строк кода?
Keras (входит в TensorFlow) позволяет определить модель последовательно. Пример для задачи классификации ирисов Фишера:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
import numpy as np
data = load_iris()
X = data.data
y = data.target.reshape(-1, 1)
encoder = OneHotEncoder(sparse=False)
y_enc = encoder.fit_transform(y)
X_train, X_test, y_train, y_test = train_test_split(X, y_enc, test_size=0.2, random_state=42)
model = Sequential([
Dense(10, activation='relu', input_shape=(4,)),
Dense(10, activation='relu'),
Dense(3, activation='softmax')
])
model.compile(optimizer=Adam(0.001), loss='categorical_crossentropy', metrics=['accuracy'])
history = model.fit(X_train, y_train, epochs=50, validation_split=0.2, verbose=0)
loss, acc = model.evaluate(X_test, y_test, verbose=0)
print(f'Test accuracy: {acc:.3f}')библиотеки для машинного обучения python (библиотеки для машинного обучения в python (scikit-learn, tensorflow, pytorch))
Этот код создает двухслойный перцептрон. Функция активации ReLU скрытых слоев и Softmax на выходе. Оптимизатор Adam и категориальная кросс-энтропия подходят для многоклассовой классификации.
Типичные ошибки:
- Неправильная форма входных данных (ожидается (None, 4), но передаётся (4,))
- Забыть нормировать признаки – может замедлить сходимость
- Выбор неподходящей функции потерь (например, binary_crossentropy для 3 классов)
Вариант 1: нейросеть с нуля на NumPy
Как реализовать полносвязную нейросеть без фреймворков, чтобы глубже понять механизмы обучения?
Этот вариант подходит для изучения обратного распространения ошибки и градиентного спуска. Пример для задачи XOR:
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x):
return x * (1 - x)
# Данные XOR
X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([[0],[1],[1],[0]])
# Инициализация весов
np.random.seed(42)
w1 = np.random.randn(2, 4)
w2 = np.random.randn(4, 1)
b1 = np.zeros((1,4))
b2 = np.zeros((1,1))
lr = 1.0
for epoch in range(10000):
# Прямой проход
z1 = X @ w1 + b1
a1 = sigmoid(z1)
z2 = a1 @ w2 + b2
a2 = sigmoid(z2)
# Обратный проход
error = y - a2
d2 = error * sigmoid_derivative(a2)
d1 = (d2 @ w2.T) * sigmoid_derivative(a1)
# Обновление
w2 += a1.T @ d2 * lr
b2 += np.sum(d2, axis=0, keepdims=True) * lr
w1 += X.T @ d1 * lr
b1 += np.sum(d1, axis=0, keepdims=True) * lr
print('Predictions after training:')
print(np.round(a2))Feature names python (имена признаков в python)
Проблемы и решения:
- Градиенты могут затухать при глубоких сетях – используйте сигмоиду с осторожностью
- Выбор скорости обучения критичен: слишком большая – расходится, слишком малая – медленно
- Неправильная инициализация весов (нули) приводит к симметрии
Вариант 2: нейросеть на PyTorch для гибких архитектур
Как создать нейросеть с поддержкой динамического графа и автоматического дифференцирования?
PyTorch часто используют в исследованиях и для сложных моделей. Пример простого классификатора на MNIST:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# Загрузка MNIST
X, y = fetch_openml('mnist_784', version=1, return_X_y=True, as_frame=False, parser='auto')
y = y.astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# Преобразование в тензоры
X_train_t = torch.tensor(X_train, dtype=torch.float32)
y_train_t = torch.tensor(y_train, dtype=torch.long)
X_test_t = torch.tensor(X_test, dtype=torch.float32)
y_test_t = torch.tensor(y_test, dtype=torch.long)
train_dataset = TensorDataset(X_train_t, y_train_t)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
# Определение модели
class Net(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 128)
self.fc2 = nn.Linear(128, 64)
self.fc3 = nn.Linear(64, 10)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
return self.fc3(x)
model = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# Обучение
for epoch in range(5):
for batch_x, batch_y in train_loader:
optimizer.zero_grad()
outputs = model(batch_x)
loss = criterion(outputs, batch_y)
loss.backward()
optimizer.step()
# Оценка
with torch.no_grad():
outputs = model(X_test_t)
_, predicted = torch.max(outputs, 1)
accuracy = (predicted == y_test_t).float().mean()
print(f'Test accuracy: {accuracy:.3f}')Частые проблемы в PyTorch:
- Забыть обнулить градиенты (optimizer.zero_grad()) – градиенты накапливаются
- Не перевести данные в нужный тип (float32) или не указать requires_grad
- Перепутать оси при работе с изображениями (PyTorch ожидает (batch, channels, height, width))
Каждый из подходов имеет свою область применения. Keras оптимален для быстрого прототипирования и продакшена, NumPy – для обучения, PyTorch – для исследований и динамических архитектур. Выбор зависит от задач и опыта команды.
Расширенные примеры с подробным кодом и результатами
Пример 1: Многослойный перцептрон на Keras с регуляризацией и Early Stopping
Этот пример показывает, как улучшить обобщение, добавив Dropout и L2-регуляризацию, а также раннюю остановку для предотвращения переобучения.
import numpy as np
from tensorflow.keras.datasets import fashion_mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.regularizers import l2
from tensorflow.keras.utils import to_categorical
# Загрузка данных Fashion-MNIST
(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()
X_train = X_train.reshape(-1, 784).astype('float32') / 255.0
X_test = X_test.reshape(-1, 784).astype('float32') / 255.0
y_train_cat = to_categorical(y_train, 10)
y_test_cat = to_categorical(y_test, 10)
# Модель с регуляризацией
model = Sequential([
Dense(256, activation='relu', input_shape=(784,), kernel_regularizer=l2(0.001)),
Dropout(0.3),
Dense(128, activation='relu', kernel_regularizer=l2(0.001)),
Dropout(0.3),
Dense(10, activation='softmax')
])
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
# Early Stopping
early_stop = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
history = model.fit(X_train, y_train_cat,
validation_split=0.2,
epochs=100,
batch_size=128,
callbacks=[early_stop],
verbose=1)
# Оценка
loss, acc = model.evaluate(X_test, y_test_cat, verbose=0)
print(f'Test accuracy: {acc:.4f}')Epoch 1/100 375/375 - 2s - loss: 1.0101 - accuracy: 0.6913 - val_loss: 0.6420 - val_accuracy: 0.7938 ... Epoch 20/100 375/375 - 2s - loss: 0.5659 - accuracy: 0.8122 - val_loss: 0.4870 - val_accuracy: 0.8364 Test accuracy: 0.8452
В результате модель достигает точности около 0.84 на тестовом наборе. Ранняя остановка остановила обучение до переобучения.
Возможные проблемы:
- Слишком сильная регуляризация может привести к недообучению
- Необходимо подбирать p для Dropout и коэффициент L2
Пример 2: Сверточная нейросеть на PyTorch для CIFAR-10
Сверточные сети лучше подходят для изображений. Реализация на PyTorch с использованием nn.Conv2d и пулинга.
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))
])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False, num_workers=2)
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
self.fc1 = nn.Linear(64 * 8 * 8, 512)
self.fc2 = nn.Linear(512, 10)
self.dropout = nn.Dropout(0.5)
def forward(self, x):
x = self.pool(torch.relu(self.conv1(x)))
x = self.pool(torch.relu(self.conv2(x)))
x = x.view(-1, 64 * 8 * 8)
x = torch.relu(self.fc1(x))
x = self.dropout(x)
return self.fc2(x)
model = CNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
for epoch in range(10):
running_loss = 0.0
for inputs, labels in trainloader:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
print(f'Epoch {epoch+1}, loss: {running_loss/len(trainloader):.3f}')
# Тест
correct = 0
total = 0
with torch.no_grad():
for inputs, labels in testloader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Test accuracy: {100*correct/total:.2f}%')Epoch 1, loss: 1.568 Epoch 2, loss: 1.143 ... Epoch 10, loss: 0.602 Test accuracy: 72.34%
Точность 72% на CIFAR-10 достигнута за 10 эпох. Можно улучшить аугментацией и увеличением глубины.
Пример 3: Нейросеть на NumPy с пакетным обучением и функцией потерь MSE
Более продвинутый пример на чистом NumPy, включающий пакетную обработку (batch) для регрессии.
import numpy as np
def relu(x):
return np.maximum(0, x)
def relu_derivative(x):
return (x > 0).astype(float)
# Данные: синусоида с шумом
np.random.seed(42)
X = np.linspace(-3, 3, 300).reshape(-1, 1)
y = np.sin(X) + 0.1 * np.random.randn(300, 1)
# Архитектура: 1-32-32-1
layer_sizes = [1, 32, 32, 1]
L = len(layer_sizes) - 1
weights = [np.random.randn(layer_sizes[i], layer_sizes[i+1]) * 0.1 for i in range(L)]
biases = [np.zeros((1, layer_sizes[i+1])) for i in range(L)]
lr = 0.01
batch_size = 32
num_epochs = 1000
for epoch in range(num_epochs):
# Перемешивание
perm = np.random.permutation(X.shape[0])
X_shuff = X[perm]
y_shuff = y[perm]
for i in range(0, X.shape[0], batch_size):
X_batch = X_shuff[i:i+batch_size]
y_batch = y_shuff[i:i+batch_size]
# Прямой проход (сохраняем все слои)
activations = [X_batch]
zs = []
a = X_batch
for w, b in zip(weights, biases):
z = a @ w + b
zs.append(z)
a = relu(z)
activations.append(a)
# Выходной слой – линейный, поэтому a[-1] – это выход
# Обратное распространение
delta = (activations[-1] - y_batch) # для MSE grad = (out - y)
for l in range(L-1, -1, -1):
# Обновление весов и смещений
grad_w = activations[l].T @ delta
grad_b = np.sum(delta, axis=0, keepdims=True)
weights[l] -= lr * grad_w
biases[l] -= lr * grad_b
if l > 0:
delta = (delta @ weights[l].T) * relu_derivative(activations[l])
# Оценка каждые 100 эпох
if epoch % 100 == 0:
preds = activations[-1] if 'activations' in dir() else None
loss = np.mean((preds - y_batch)**2)
print(f'Epoch {epoch}, batch loss: {loss:.4f}')
# Финальный проход по всем данным
activations = [X]
for w, b in zip(weights, biases):
activations.append(relu(activations[-1] @ w + b))
y_pred = activations[-1]
mse = np.mean((y_pred - y)**2)
print(f'Final MSE: {mse:.4f}')Epoch 0, batch loss: 0.6375 Epoch 100, batch loss: 0.0215 ... Epoch 900, batch loss: 0.0104 Final MSE: 0.0108
Модель успешно аппроксимирует синусоиду. Пакетное обучение ускоряет сходимость.