TensorFlow

TensorFlow — фреймворк глубокого обучения от Google: построение и обучение нейронных сетей от простого перцептрона до трансформеров.

Уровень: средний 18 разделов ⏱ 4–5 часов

1. Что такое TensorFlow

TensorFlow работает с тензорами — многомерными массивами чисел (эволюция ndarray из NumPy). Поверх низкоуровневых операций построен высокоуровневый API Keras — именно с ним работают 99% задач.

Экосистема: TF Serving (сервер моделей), TF Lite (мобильные), TF.js (браузер), TensorBoard (визуализация).

2. Установка

pip install tensorflow
import tensorflow as tf
print(tf.__version__)

Версия для GPU требует CUDA с совместимым драйвером; для обучения видео на CPU слабее — проверяй tf.config.list_physical_devices('GPU').

3. Тензоры

a = tf.constant([[1., 2.], [3., 4.]])
print(a.shape)   # (2, 2)
b = tf.random.normal((3, 2))
c = a @ tf.linalg.transpose(a)   # умножение матриц

Операции окружены автодифференцированием: TF сам считает производные по всем параметрам при обратном распространении ошибки.

4. Первая модель: Keras Sequential

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Input(shape=(784,)),
    layers.Dense(128, activation='relu'),
    layers.Dense(10, activation='softmax')
])
model.summary()

5. Компиляция и обучение

model.compile(
    optimizer='adam',
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy']
)

history = model.fit(
    X_train, y_train,
    epochs=10,
    validation_data=(X_val, y_val)
)

epochs — проходы по данным; batch_size — размер порции (по умолчанию 32).

6. Обработка изображений (CNN)

model = keras.Sequential([
    layers.Input(shape=(28, 28, 1)),
    layers.Conv2D(32, 3, activation='relu'),
    layers.MaxPooling2D(2),
    layers.Conv2D(64, 3, activation='relu'),
    layers.Flatten(),
    layers.Dense(128, activation='relu'),
    layers.Dense(10, activation='softmax')
])

Свертка (Conv2D) ищет локальные паттерны, пулинг уменьшает размерность.

7. Оценка и TensorBoard

loss, acc = model.evaluate(X_test, y_test)
print(loss, acc)

tensorboard_cb = keras.callbacks.TensorBoard(log_dir='logs')
model.fit(X_train, y_train, epochs=10, callbacks=[tensorboard_cb])

Затем tensorboard --logdir logs — графики потерь и метрик в браузере.

8. Ранняя остановка и регуляризация

early = keras.callbacks.EarlyStopping(
    monitor='val_loss', patience=3, restore_best_weights=True)

model.fit(X_train, y_train,
          epochs=100,
          validation_split=0.2,
          callbacks=[early])

Без Dropout скрытые слои «запоминают» данные — слой Dropout отключает случайные нейроны и борется с переобучением.

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

model.save('model.keras')          # полный формат
model = keras.models.load_model('model.keras')

# для модели кода/продакшена
model.export('saved_model_path')   # SavedModel для продакшена

10. Трудности обучения

Не интерпретируй accuracy на несбалансированном наборе: если 90% — класс A, модель «всегда A» даст 0.9. Смотри F1 и матрицу ошибок (как в Scikit-Learn).

11. Деплой модели

12. Перенос обучения (Transfer Learning)

Обучать сеть с нуля для новой задачи дорого. Перенос обучения использует готовую модель (например, MobileNetV2 на ImageNet) как «веса-признаки» и обучает только последние слои под ваши классы.

from tensorflow.keras.applications import MobileNetV2

base = MobileNetV2(include_top=False, weights='imagenet',
                   input_shape=(224, 224, 3))
base.trainable = False

model = keras.Sequential([
    base,
    layers.GlobalAveragePooling2D(),
    layers.Dense(3, activation='softmax')
])

model.compile(optimizer='adam', loss='categorical_crossentropy')
model.fit(generator, epochs=5)

Сначала база заморожена (trainable=False) — обучается только классификатор. Затем можно разморозить несколько верхних слоёв для тонкой настройки с очень низким lr.

Если датасет маленький (сотни картинок на класс), берите предобученную базу и не размораживайте её: финальные слои быстро приспосабливаются. С ростом данных размораживайте по одному блоку сверху вниз и следите за val_loss после каждой разморозки.

В Keras для аугментации используй tf.keras.layers.RandomFlip и RandomRotation внутри самой модели — данные «дрожат» прямо во время обучения.

13. Пайплайны данных: tf.data

Собственный датасет редко влезает в память целиком. Класс tf.data.Dataset читает данные батчами, параллельно и вперемешку, не загружая всё сразу.

ds = tf.data.Dataset.from_tensor_slices((x, y))
ds = ds.shuffle(10_000) \
       .batch(32) \
       .map(augment_fn, num_parallel_calls=tf.data.AUTOTUNE) \
       .prefetch(tf.data.AUTOTUNE)

model.fit(ds, epochs=10)

Цепочка shuffle → batch → map → prefetch — стандарт: перемешивание до батчей, а подготовка образцов идёт в фоне, пока GPU занят.

МетодЗадача
shuffleперемешивание порядка
batchсборка батчей
mapаугментация каждой записи
prefetchподготовка следующего батча заранее

Для CSV и текстовых данных есть TextLineDataset и make_csv_dataset — они читают файлы построчно без ручного парсинга. Большие наборы держите в формате TFRecord и готовьте заранее в фоновом процессе, а не в памяти Python.

Аугментация обязательна для изображений и текста: без неё сеть быстро «запоминает» тренировочные данные и хуже обобщается на новые.

14. Воспроизводимость экспериментов

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

import random, numpy as np, tensorflow as tf

seed = 42
random.seed(seed)
np.random.seed(seed)
tf.random.set_seed(seed)

Фиксируйте и окружение: версии Python, CUDA и cuDNN меняют результат. Метка с датой запуска и бюджетом времени позволяет сравнивать эксперименты честно, а не «на глаз».

Полной детерминированности на GPU не гарантирует ни один фреймворк. Для честного сравнения запускайте эксперимент несколько раз с разными seed и сравнивайте распределения, а не одну цифру.
Версии предобработки данных (какие файлы, какая нормализация) — часть эксперимента. Храните их в конфиге, а не в комментарии к коду.

15. Текст и последовательности

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

vectorizer = tf.keras.layers.TextVectorization(
    max_tokens=10_000, output_sequence_length=200)
vectorizer.adapt(raw_texts)

model = tf.keras.Sequential([
    vectorizer,
    tf.keras.layers.Embedding(10_000, 128),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)),
    tf.keras.layers.Dense(1, activation="sigmoid")
])
На небольших датасетах помогут предобученные эмбеддинги (GloVe, fastText): стартовые векторы улучшают результат, если данных мало.
Токенизатор настраивайте на тех же данных, что и модель: сдвиг словаря между обучением и инференсом ломает предсказания тихо.

16. Распределённое обучение: tf.distribute

Когда одна видеокарта не справляется или данные не влезают в память, обучение раскидывают на несколько устройств. tf.distribute делает это стандартным API — без ручного обмена градиентами.

strategy = tf.distribute.MirroredStrategy()

with strategy.scope():
    model = create_model()
    model.compile(optimizer="adam",
                  loss="sparse_categorical_crossentropy",
                  metrics=["accuracy"])

model.fit(dataset, epochs=10)
Не включайте распределённое обучение «на всякий случай»: на одной карте оно медленнее из-за накладных расходов. Масштабируйтесь, когда упёрлись в предел.
Эффективный размер батча растёт с числом устройств: уменьшайте батч на устройство или корректируйте learning rate, иначе сходимость поедет.

17. Квантизация и развёртывание на мобильных (TFLite)

Модель в float32 весит много и требует ресурсов. Для смартфонов и встраиваемых устройств её конвертируют в TensorFlow Lite и сжимают квантизацией — веса из 32-бит в 8-бит.

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
open("model.tflite", "wb").write(tflite_model)
Держите baseline-метрики до и после квантизации: откатывать конвертацию нужно по числу, а не «на глаз».
Квантизация без калибровки на репрезентативных данных может «убить» редкие классы: давайте конвертеру выборку, похожую на боевую.

18. ML-модель в продакшене: мониторинг и дрейф

Задеплоенная модель — начало эксплуатации, а не финиш. Данные меняются, и качество медленно деградирует без видимой ошибки — это Concept/Data Drift.

  1. регистрируйте версии моделей и метаданные (MLflow, W&B): «какая модель сейчас в проде»;
  2. логируйте входные данные и прогнозы, чтобы сравнивать распределения;
  3. пересчитывайте метрики на отложенных данных или теневой доле трафика;
  4. настраивайте алерты: метрика упала ниже порога — это инцидент, а не «что-то странное».
# Простой индикатор дрейфа: смещение среднего прогноза
new_mean = model.predict(X_live).mean()
drift = abs(new_mean - train_mean)
Не обновляйте модель «молча»: свежие данные могут просесть на старых клиентах. Перед выкаткой сравнивайте кандидата с текущей версией на одном наборе.
Ведите базу «прогноз vs факт»: она кормит и мониторинг, и следующую версию модели.

19. Тонкая настройка: колбэки, расписания и Keras Tuner

Модель редко работает с первого запуска: приходится играть со скоростью обучения, сохранять лучшие веса и подбирать гиперпараметры. В Keras всё это — колбэки и Tuner.

callbacks = [
    ModelCheckpoint("best.weights.h5", save_best_only=True),
    EarlyStopping(patience=5, restore_best_weights=True),
    ReduceLROnPlateau(factor=0.5, patience=2),
]
model.fit(X, y, callbacks=callbacks)

Keras Tuner перебирает комбинации гиперпараметров (случайный поиск, Optuna/Bayesian): search() запускает трёх-четырёх «раундов» — вы получаете лучший набор значений с числами.

Сначала фиксируйте seed, архитектуру и процесс; Tuner — после того как базовая модель уже «поехала».
Дорого оценивать гипотезы «на глаз»: колбэки и Tuner делают это детерминированно и повторяемо.
Экономьте время: слишком долгий поиск гиберов стоит больше, чем выигрыш от пары лучших процентов. Ограничивайте раунды.

20. Автоэнкодеры и генеративные модели

Автоэнкодер сжимает данные до «узкого горлышка» (latent) и восстанавливает обратно — это один из кирпичей представлений. Генеративные техники (VAE, GAN) учатся создавать новые объекты по распределению входных данных.

# Простая архитектура VAE (фрагмент)
inputs = Input(shape=(28, 28, 1))
x = Conv2D(32, 3, activation="relu", padding="same")(inputs)
z_mean = Dense(2)(x_flat)      # латентный код
decoder = ...                  # восстановление

Для игр и дизайна это инструмент прототипирования: генерация вариаций текстур, персонажей, звуков — быстрее и дешевле ручной сборки.

Начните с маленького VAE на одной палитре данных (например, 64×64) — модель «мечты» на 1024×1024 растёт в цене экспоненциально.
GAN капризны: «битвы генератора и дискриминатора» без регуляризации могут расходиться. Не бойтесь перезапусков и фиксов архитектуры.
Качество генерации оценивается отдельными метриками (FID, IS), а не «на глаз» — глаз легко обмануть.

21. Functional API и Subclassing: гибкие архитектуры

Sequential подходит для линейных моделей; для ветвлений, двух входов, внимания или точного контроля жизни слоёв есть два более мощных способа — Functional API и Subclassing.

# Functional API: два входа
text_input = Input(shape=(100,))
img_input  = Input(shape=(32, 32, 3))
merged = Concatenate()([Dense(64)(text_input),
                        Flatten()(img_input)])
out = Dense(1, activation="sigmoid")(merged)
model = Model([text_input, img_input], out)

Subclassing удобен для исследовательских решений, но не для продуктового кода: динамический граф труднее инспектировать и фиксировать. Общее правило — начинать с Sequential/Functional.

Размечайте входы именами (name="text") — в продукте по ним проходит трейс, диагностика и отладка.
Оба подхода компилируются одинаково — выбор не влияет на обучение, только на гибкость описания.
В Subclassing «ручной» контроль — двойная ответственность: следите за инициализацией весов и бэтч-семантикой вручную.

22. Перенос модели: ONNX и запуск вне TensorFlow

Модель создана в TensorFlow, а запускать её нужно на сервере с PyTorch, в браузере или на микроконтроллере. Промежуточный стандарт — ONNX: единый формат, читаемый разными рантаймами.

import tf2onnx

tf2onnx.convert.from_keras(model,
    output_path="model.onnx")

# PyTorch-проект может грузить этот файл через onnxruntime
import onnxruntime as ort
session = ort.InferenceSession("model.onnx")
out = session.run(None, {"keras_input": x})

Альтернативы: TFLite (мобильные, из раздела 17), TensorFlow Serving (готовая серверная обвязка), Web платформы — js-рантайм. Выбор — требование платформы.

Проверяйте совместимость операторов: редкие кастомные слои могут не экспортироваться в ONNX — добавляйте после удачно конвертируемой «стандартной» модели.
ONNX — конвертация, а не «магия»: точность может измениться на тысячных долях. Сверяйте метрики, а не «вроде похожий».
Один раз упакованная модель живёт годами — держите в репо и версию позапрошлого .onnx («откат по кнопке»).

Что дальше

Если нужны динамические модели и исследовательская гибкость — смотри PyTorch, а удобный верхний уровень — Keras.

← Вернуться к курсам