🧠 Keras

Полный справочник Keras 3: бэкенды, Sequential и Functional API, слои, компиляция и обучение, колбэки, регуляризация, CNN и RNN и трансформеры, transfer learning, сохранение, KerasTuner и частые ошибки.

Шпаргалки · ИИ · #keras #deep-learning #python #tensorflow #neural-networks

Что это

Keras высокоуровневый API нейросетей. Keras 3 мультибэкенд: тот же код работает на TensorFlow, PyTorch или JAX.

pip install keras tensorflow            # или keras torch / keras jax
export KERAS_BACKEND=torch              # tensorflow | torch | jax  (до импорта keras)
import keras
from keras import layers, ops
keras.utils.set_random_seed(42)         # воспроизводимость

Операции над тензорами пишите через keras.ops (ops.matmul, ops.sum), чтобы код не зависел от бэкенда. Старый tf.keras остаётся как алиас в TensorFlow, но новый код лучше писать на keras.

Три способа построить модель

# 1. Sequential — цепочка слоёв
model = keras.Sequential([
    layers.Input(shape=(28, 28, 1)),
    layers.Conv2D(32, 3, activation="relu"),
    layers.MaxPooling2D(),
    layers.Flatten(),
    layers.Dense(10, activation="softmax"),
])

# 2. Functional API — ветвления, несколько входов и выходов, общие слои
inp = keras.Input(shape=(64,), name="features")
x = layers.Dense(128, activation="relu")(inp)
skip = layers.Dense(128)(inp)
x = layers.Add()([x, skip]); x = layers.LayerNormalization()(x)
out = layers.Dense(1, activation="sigmoid", name="p")(x)
model = keras.Model(inp, out)

# 3. Подклассы — полная свобода
class MLP(keras.Model):
    def __init__(self, n=10):
        super().__init__(); self.d1 = layers.Dense(64, activation="relu"); self.d2 = layers.Dense(n)
    def call(self, x, training=False): return self.d2(self.d1(x))

model.summary() показывает слои, формы и число параметров; keras.utils.plot_model(model, show_shapes=True) рисует схему.

Основные слои

Группа Слои
Полносвязные Dense, EinsumDense
Свёртки Conv1D, Conv2D, Conv3D, SeparableConv2D, DepthwiseConv2D, Conv2DTranspose
Пулинг MaxPooling2D, AveragePooling2D, GlobalAveragePooling2D, GlobalMaxPooling1D
Рекуррентные LSTM, GRU, SimpleRNN, Bidirectional(LSTM(64)), RNN
Внимание, трансформер MultiHeadAttention, LayerNormalization, Embedding, PositionEmbedding (KerasHub)
Нормализация BatchNormalization, LayerNormalization, GroupNormalization, UnitNormalization, Normalization
Регуляризация Dropout, SpatialDropout2D, GaussianNoise, ActivityRegularization
Форма Flatten, Reshape, Permute, RepeatVector, Concatenate, Add, Multiply, Lambda
Предобработка Rescaling, Resizing, Normalization, TextVectorization, StringLookup, IntegerLookup, Discretization, CategoryEncoding, Hashing
Аугментация RandomFlip, RandomRotation, RandomZoom, RandomContrast, RandomTranslation, RandomCrop

Активации: relu, gelu, swish (silu), leaky_relu, tanh, sigmoid, softmax, linear. Инициализаторы: glorot_uniform (по умолчанию), he_normal (для ReLU).

Компиляция

model.compile(
    optimizer=keras.optimizers.AdamW(learning_rate=1e-3, weight_decay=1e-4),
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=False),
    metrics=["accuracy", keras.metrics.AUC(name="auc")],
)
Задача Последний слой Потеря
Бинарная Dense(1, "sigmoid") binary_crossentropy
Многоклассовая (целые метки) Dense(N, "softmax") sparse_categorical_crossentropy
Многоклассовая (one-hot) Dense(N, "softmax") categorical_crossentropy
Мультилейбл Dense(N, "sigmoid") binary_crossentropy
Регрессия Dense(1) (без активации) mse, mae, huber

Если последний слой без softmax (логиты), задайте from_logits=True в потере. Скорость обучения можно менять расписанием: keras.optimizers.schedules.CosineDecay(1e-3, decay_steps); оптимизаторы: Adam, AdamW, SGD(momentum=0.9, nesterov=True), RMSprop, Lion; clipnorm=1.0 против взрыва градиентов.

Обучение и оценка

history = model.fit(
    X_train, y_train,                       # массивы NumPy, tf.data.Dataset, torch DataLoader, генераторы
    epochs=50, batch_size=64,
    validation_split=0.2,                   # или validation_data=(X_val, y_val)
    class_weight={0: 1.0, 1: 5.0},          # для несбалансированных классов
    callbacks=[...], verbose=2, shuffle=True,
)
model.evaluate(X_test, y_test); model.predict(X_new, batch_size=256); model.predict_on_batch(x)
history.history["val_loss"]                 # кривые для графика
model.train_on_batch(x, y); model.test_on_batch(x, y)

predict возвращает вероятности: для классов np.argmax(p, axis=1).

Колбэки

callbacks = [
    keras.callbacks.EarlyStopping(monitor="val_loss", patience=5, restore_best_weights=True),
    keras.callbacks.ModelCheckpoint("best.keras", monitor="val_loss", save_best_only=True),
    keras.callbacks.ReduceLROnPlateau(monitor="val_loss", factor=0.5, patience=2, min_lr=1e-6),
    keras.callbacks.TensorBoard(log_dir="logs"),
    keras.callbacks.CSVLogger("train.csv"),
    keras.callbacks.LearningRateScheduler(lambda epoch, lr: lr * 0.95),
    keras.callbacks.TerminateOnNaN(),
]
class MyCb(keras.callbacks.Callback):
    def on_epoch_end(self, epoch, logs=None): print(epoch, logs["val_accuracy"])

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

  • Dropout(0.2–0.5) между слоями, BatchNormalization.
  • kernel_regularizer=keras.regularizers.l2(1e-4) или l1.
  • Аугментация данных (особенно изображения), больше данных.
  • Меньше модель, ранняя остановка, weight_decay в AdamW.
  • Нормализуйте входы (Rescaling(1/255), Normalization() с adapt(X_train)).

Готовые архитектуры: примеры

# CNN для изображений
model = keras.Sequential([
    layers.Input((32, 32, 3)), layers.RandomFlip("horizontal"), layers.Rescaling(1/255),
    layers.Conv2D(32, 3, padding="same", activation="relu"), layers.BatchNormalization(), layers.MaxPooling2D(),
    layers.Conv2D(64, 3, padding="same", activation="relu"), layers.BatchNormalization(), layers.MaxPooling2D(),
    layers.GlobalAveragePooling2D(), layers.Dropout(0.3), layers.Dense(10, activation="softmax"),
])

# Текст: LSTM
vec = layers.TextVectorization(max_tokens=20000, output_sequence_length=200); vec.adapt(train_texts)
model = keras.Sequential([layers.Input((1,), dtype="string"), vec, layers.Embedding(20000, 128, mask_zero=True),
    layers.Bidirectional(layers.LSTM(64)), layers.Dropout(0.3), layers.Dense(1, activation="sigmoid")])

# Временной ряд
model = keras.Sequential([layers.Input((60, 5)), layers.LSTM(64, return_sequences=True), layers.LSTM(32), layers.Dense(1)])

# Блок трансформера
def block(x, heads=4, dim=128):
    a = layers.MultiHeadAttention(num_heads=heads, key_dim=dim // heads)(x, x)
    x = layers.LayerNormalization()(x + a)
    f = layers.Dense(dim * 4, activation="gelu")(x); f = layers.Dense(dim)(f)
    return layers.LayerNormalization()(x + f)

# Автоэнкодер
enc = keras.Sequential([layers.Input((784,)), layers.Dense(64, activation="relu"), layers.Dense(16)])
dec = keras.Sequential([layers.Input((16,)), layers.Dense(64, activation="relu"), layers.Dense(784, activation="sigmoid")])

Transfer learning

base = keras.applications.EfficientNetB0(weights="imagenet", include_top=False, input_shape=(224, 224, 3))
base.trainable = False                                # шаг 1: обучаем только голову
inp = keras.Input((224, 224, 3))
x = base(inp, training=False)                          # BatchNorm в режиме инференса
x = layers.GlobalAveragePooling2D()(x); x = layers.Dropout(0.3)(x)
out = layers.Dense(5, activation="softmax")(x)
model = keras.Model(inp, out)
model.compile(optimizer=keras.optimizers.Adam(1e-3), loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(train_ds, validation_data=val_ds, epochs=5)

base.trainable = True                                  # шаг 2: дообучение (fine-tuning) верхних слоёв с малым lr
for layer in base.layers[:-30]: layer.trainable = False
model.compile(optimizer=keras.optimizers.Adam(1e-5), loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(train_ds, validation_data=val_ds, epochs=10)

Каталог keras.applications: ResNet50, MobileNetV3, EfficientNetV2, ConvNeXt, VGG16. Предобученные текстовые и мультимодальные модели: KerasHub (keras_hub), BERT, Gemma, Llama.

Свои слои, потери, метрики и шаг обучения

@keras.saving.register_keras_serializable()
class Scale(layers.Layer):
    def __init__(self, factor=2.0, **kw): super().__init__(**kw); self.factor = factor
    def build(self, shape): self.w = self.add_weight(shape=(shape[-1],), initializer="ones", trainable=True)
    def call(self, x): return x * self.w * self.factor
    def get_config(self): return {**super().get_config(), "factor": self.factor}

def focal_loss(y_true, y_pred, gamma=2.0):
    p = ops.where(y_true == 1, y_pred, 1 - y_pred); return -ops.mean((1 - p) ** gamma * ops.log(p + 1e-7))

Для нестандартного обучения переопределите train_step в подклассе keras.Model (код зависит от бэкенда: GradientTape в TF, loss.backward() в PyTorch, jax.value_and_grad в JAX).

Данные и предобработка

train_ds = keras.utils.image_dataset_from_directory("data", validation_split=0.2, subset="training", seed=1, image_size=(224, 224), batch_size=32)
class_names = train_ds.class_names
norm = layers.Normalization(); norm.adapt(X_train)                      # статистики по обучающей выборке
keras.utils.to_categorical(y, num_classes=10)                           # one-hot

Предобработку включайте в модель: на инференсе вход тот же, что и на обучении.

Сохранение и загрузка

model.save("model.keras")                                # архитектура + веса + состояние оптимизатора
model = keras.saving.load_model("model.keras", custom_objects={"Scale": Scale})
model.save_weights("m.weights.h5"); model.load_weights("m.weights.h5")
model.export("serving_dir")                              # SavedModel (бэкенд TensorFlow): для TF Serving, TFLite

Потенциально опасно загружать чужие модели с Lambda-слоями: используйте safe_mode=True (по умолчанию).

Подбор гиперпараметров

import keras_tuner as kt
def build(hp):
    m = keras.Sequential([layers.Input((20,)), layers.Dense(hp.Int("units", 32, 256, step=32), activation="relu"), layers.Dense(1, activation="sigmoid")])
    m.compile(optimizer=keras.optimizers.Adam(hp.Choice("lr", [1e-2, 1e-3, 1e-4])), loss="binary_crossentropy", metrics=["accuracy"]); return m
tuner = kt.Hyperband(build, objective="val_accuracy", max_epochs=30); tuner.search(X, y, validation_split=0.2); tuner.get_best_hyperparameters(1)[0].values

Альтернативы: Optuna, Ray Tune.

Диагностика

Симптом Причина и действия
val_loss растёт, loss падает переобучение: Dropout, регуляризация, аугментация, ранняя остановка, меньше модель
Обе метрики плохие недообучение: больше слоёв и нейронов, дольше, выше lr, лучше признаки
Потеря nan lr слишком велик, нет нормализации, log(0), clipnorm, TerminateOnNaN
Точность на уровне случайной неверные метки или формат метки и потери, нет перемешивания, нет обучения (trainable=False у всего)
Ошибка формы сверьте model.summary() и X.shape; для CNN (H, W, C); метки (N,) или (N, C)
Нестабильные результаты keras.utils.set_random_seed, больше данных, ансамбли
Очень медленно батч больше, tf.data с prefetch, mixed_float16 (keras.mixed_precision.set_global_policy), GPU

Быстрая проверка кода: обучите на 10–100 примерах, потеря должна упасть почти до нуля.

Практика

  • Начинайте с простой модели и бейзлайна (линейная или дерево) и сравнивайте.
  • Делите данные заранее на train / val / test; тест трогайте один раз.
  • Нормализуйте входы, балансируйте классы, следите за утечками.
  • Для табличных данных нейросети часто уступают градиентному бустингу.
  • Фиксируйте версии keras, бэкенда и сохраняйте конфигурацию эксперимента (MLflow, Weights & Biases).
  • Экосистема: KerasHub (готовые модели), KerasCV / KerasNLP (теперь в KerasHub), KerasTuner, TensorBoard, TF Serving, TFLite, ONNX.