TensorFlow можно установить с помощью стандартного менеджера пакетов pip. Библиотека, на момент написания статьи, работает начиная с версии python 3.7 и выше. Для установки нужно ввести следующие команды:
1pip install --upgrade pip
2pip install tensorflow
TensorFlow выполняет операции над многомерными массивами или тензорами, которые представлены экземплярами класса tf.Tensor
. Вот как выглядит пример создания трех размерного массива.
1import tensorflow as tf
2
3t = tf.constant([
4 [
5 [1],
6 [2],
7 [3],
8 ],
9 [
10 [4],
11 [1],
12 [6],
13 ],
14 [
15 [7],
16 [8],
17 [9],
18 ],
19])
20
21print(t)
Вывод у данного кода будет следующий:
1tf.Tensor(
2[[[1]
3 [2]
4 [3]]
5
6 [[4]
7 [1]
8 [6]]
9
10 [[7]
11 [8]
12 [9]]], shape=(3, 3, 1), dtype=int32)
Наиболее важными атрибутами tf.Tensor
являются shape
и dtype
:
Tensor.shape
показывает размерность массива.Tensor.dtype
говорит о типе элементов в тензоре.В результате выполнения кода ниже, можно получить вот такой вывод:
1print(t.shape)
2print(t.dtype)
Вывод в консоль:
1(3, 3, 1)
2<dtype: 'int32'>
TensorFlow также позволяет выполнять стандартные математические операции с тензорами, так же как и множество других операций связанных с машинным обучением. Рассмотрим несколько примеров:
1t + t
Вывод в консоль:
1<tf.Tensor: shape=(3, 3, 1), dtype=int32, numpy=
2array([[[ 2],
3 [ 4],
4 [ 6]],
5 [[ 8],
6 [ 2],
7 [12]],
8 [[14],
9 [16],
10 [18]]], dtype=int32)>
Пример:
110 * t
Вывод в консоль:
1<tf.Tensor: shape=(3, 3, 1), dtype=int32, numpy=
2array([[[10],
3 [20],
4 [30]],
5
6 [[40],
7 [10],
8 [60]],
9
10 [[70],
11 [80],
12 [90]]], dtype=int32)>
Пример:
1tf.reduce_sum(t)
Вывод в консоль:
1<tf.Tensor: shape=(), dtype=int32, numpy=41>
Пример:
1tf.concat([t, t, t], axis=0)
Вывод в консоль:
1<tf.Tensor: shape=(6, 3, 1), dtype=int32, numpy=
2array([[[1],
3 [2],
4 [3]],
5 [[4],
6 [1],
7 [6]],
8 [[7],
9 [8],
10 [9]],
11 [[1],
12 [2],
13 [3]],
14 [[4],
15 [1],
16 [6]],
17 [[7],
18 [8],
19 [9]]], dtype=int32)>
Обычно объекты tf.Tensor
являются иммутабельными, т.е. не изменяемыми. Для хранения весов модели или других мутабельных состояний в TensorFlow обычно используется tf.Variable
. Создавать и изменять переменные можно следующими способами:
1var = tf.Variable([0.0, 0.0, 0.0])
2print(var)
3var.assign([1, 2, 3])
4print(var)
5var.assign_add([1, 1, 1])
6print(var)
Вывод в консоль:
1<tf.Variable 'Variable:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>
2<tf.Variable 'Variable:0' shape=(3,) dtype=float32, numpy=array([1., 2., 3.], dtype=float32)>
3<tf.Variable 'Variable:0' shape=(3,) dtype=float32, numpy=array([2., 3., 4.], dtype=float32)>
TensorFlow предоставляет инструменты для:
Для этого необходимо использовать tf.function
, чтобы отделить чистый TensorFlow код от Python. Рассмотрим на следующем примере: создадим функцию, которая будет возвращать сумму всех элементов тензора.
1@tf.function
2def reduce(t):
3 print('Execute reduce function...')
4 return tf.reduce_sum(t)
После того как мы первый раз запустим эту функцию на выполнение, то она выполнится на Python, но при этом создастся и запишется полный оптимизированный граф вычислений TensorFlow, выполняемых внутри функции.
1t1 = tf.constant([1, 2, 3])
2reduce(t1)
Вывод в консоль:
1Execute reduce function...
2<tf.Tensor: shape=(), dtype=int32, numpy=6>
При последующих вызовах TensorFlow будет выполнять только оптимизированный граф вычислений, пропуская любые шаги, не относящиеся к TensorFlow. Ниже в примере можно увидеть, что при повторном вызове функция уже не будет выводить print
в консоль.
1t2 = tf.constant([3, 3, 3])
2reduce(t2)
Вывод в консоль:
1<tf.Tensor: shape=(), dtype=int32, numpy=9>
Одна граф вычислений нельзя будет переиспользовать, если у входных параметров функции изменится сигнатура (размерность shape
или тип dtype
). В этом случае будет сгенерирован новый граф.
1t3 = tf.constant([3., 3., 3.])
2reduce(t3)
Вывод в консоль:
1Execute reduce function...
2<tf.Tensor: shape=(), dtype=float32, numpy=9.0>
Оптимизированные графы приносят следующую выгоду:
tf.saved_model
, для запуска их на других системах вроде сервера или мобильного устройства, без обязательной установки python.tf.Module
это класс для управления экземплярами tf.Variable
и объектами tf.function
. Класс tf.Module
необходим для поддержания двух важных функций:
tf.train.Checkpoint
. Это полезно в процессе тренировки модели, позволяет быстро сохранить и восстановить ее состояние.tf.Variable
и вычислительные графы tf.function
с помощью tf.saved_model
. Это позволяет запускать модель независимо от python программы, с помощью которой она была создана.Рассмотрим простейший пример создания модуля, с помощью tf.Module
:
1class OperationModule(tf.Module):
2 def __init__(self, weight: float):
3 self.weight = tf.Variable(weight)
4
5 @tf.function
6 def multiply(self, multiplier: tf.constant) -> float:
7 return self.weight * multiplier
8
9op_module = OperationModule(5)
10op_module.multiply(tf.constant([1, 2, 3]))
Вывод в консоль:
1<tf.Tensor: shape=(3,), dtype=int32, numpy=array([ 5, 10, 15], dtype=int32)>
Сохраним модуль для его последующего переиспользования:
1save_path = './saved'
2tf.saved_model.save(op_module, save_path)
Теперь сохраненная модель не зависит от кода, в котором она создавалась. Попробуем восстановить ее в другую переменную и вызвать функцию multiply
:
1reloaded = tf.saved_model.load(save_path)
2reloaded.multiply(tf.constant([5, 15, 20]))
Вывод в консоль:
1<tf.Tensor: shape=(3,), dtype=int32, numpy=array([ 25, 75, 100], dtype=int32)>
tf.keras.layers.Layer
и tf.keras.Model
– классы, построенные на базе tf.Module
, но предоставляющие дополнительную функциональность и удобство при построении, тренировке и сохранении моделей.
Теперь, объединяя полученные знания попробуем создать простейшую модель. Прежде всего сгенерируем данные для обучения и проверки.
1from matplotlib import pyplot as plt
2
3x = tf.linspace(-2, 2, 201) # Создаются равномерно расположенные значения в интервале
4x = tf.cast(x, tf.float32) # Приведение тензора к новому типу
5
6def f(x):
7 y = x**2 + 2*x - 5
8 return y
9
10y = f(x) + tf.random.normal(shape=[201]) # Применение функции f к тензору с добавлением случайных значений из нормального распределения
11plt.plot(x.numpy(), y.numpy(), '.', label='Данные')
12plt.plot(x, f(x), label='Функция f')
13plt.legend()
Определим квадратичную функцию f и применим ее к набору из 201 значения по оси х, расположенных в интервале [-2.0, 2.0]. Добавим также дополнительно небольшой случайный шум. В результате получим следующий график, на котором синими точками представлены данные для обучения, а оранжевая линия – это представление функции f от x.
Создаем модель описывающую квадратичную функцию.
1class Model(tf.Module):
2 def __init__(self):
3 # Генерация трех случайных весов в интервале от 0 до 5
4 rand_init = tf.random.uniform(shape=[3], minval=0., maxval=5., seed=22)
5 # Инициализация весов модели случайными
6 self.w_q = tf.Variable(rand_init[0])
7 self.w_l = tf.Variable(rand_init[1])
8 self.b = tf.Variable(rand_init[2])
9
10 @tf.function
11 def __call__(self, x):
12 # Квадратична функция модели: quadratic_weight * x^2 + linear_weight * x + bias
13 return self.w_q * (x**2) + self.w_l * x + self.b
14
15quad_model = Model()
Добавляем функцию, которая поможет отобразить предсказанные значения модели вместе с исходными и отображаем их:
1def plot_preds(x, y, f, model, title):
2 plt.figure()
3 plt.plot(x, y, '.', label='Данные')
4 plt.plot(x, f(x), label='Функция f')
5 plt.plot(x, model(x), label='Прогноз модели')
6 plt.title(title)
7 plt.legend()
8
9plot_preds(x, y, f, quad_model, 'До тренировки')
Теперь определим функцию потерь для модели. С учетом того, что она предназначена для прогнозирования непрерывных значений наиболее подходящей функцией расчета потерь может являться среднеквадратичная ошибка. Определим ее:
1def mse_loss(y_pred, y):
2 return tf.reduce_mean(tf.square(y_pred - y))
Напишем базовый тренировочный цикл для модели. Цикл будет использовать функцию потерь и ее градиенты по отношению к входным данным для итеративного обновления параметров модели. Использование мини батчей для обучения обеспечит эффективность использования памяти и быструю сходимость. API tf.data.Dataset
имеет полезные функции для работы с батчами и перемешивания.
1batch_size = 32 # Задаем размер батча
2dataset = tf.data.Dataset.from_tensor_slices((x, y)) # Создаем датасет из тензоров x и y
3dataset = dataset.shuffle(buffer_size=x.shape[0]).batch(batch_size) # Перемешиваем записи в датасете
4
5# Задаем тренировочные параметры
6epochs = 100
7learning_rate = 0.01
8losses = []
9
10for epoch in range(epochs):
11 for x_batch, y_batch in dataset:
12 with tf.GradientTape() as tape:
13 batch_loss = mse_loss(quad_model(x_batch), y_batch)
14 # Обновляем веса модели в соответствии с вычислениями градиента
15 grads = tape.gradient(batch_loss, quad_model.variables)
16 for g,v in zip(grads, quad_model.variables):
17 v.assign_sub(learning_rate*g)
18 # Сохраняем значения потерь модели на каждой эпохе
19 loss = mse_loss(quad_model(x), y)
20 losses.append(loss)
21 if epoch % 10 == 0:
22 print(f'Среднеквадратичная ошибка для шага {epoch}: {loss.numpy():0.3f}')
23
24# Plot model results
25print("\n")
26plt.plot(range(epochs), losses)
27plt.xlabel("Эпоха")
28plt.ylabel("Среднеквадратичная ошибка (MSE)")
29plt.title('MSE по эпохам')
В результате получим следующий график:
Оценим прогноз модели после обучения:
1plot_preds(x, y, f, quad_model, 'После обучения')
Модель обучена и работает. Но следует помнить, что реализации обучающих функций доступны в модуле tf.keras
, при написании модели лучше рассмотреть сначала эти модули, прежде чем писать свои. Рассмотрим использование модуля tf.keras
в нашем случае.
Начнем с создания последовательной модели keras с использованием tf.keras.Sequential
. Один из простейших слоев в keras – это dense слой. Он способен обучаться многомерным линейным связям вида Y = W*X + b. Слой lambda необходим для выполнения трансформации данных на вход dense слою.
1new_model = tf.keras.Sequential([
2 tf.keras.layers.Lambda(lambda x: tf.stack([x, x**2], axis=1)),
3 tf.keras.layers.Dense(units=1, kernel_initializer=tf.random.normal)])
4
5new_model.compile(
6 loss=tf.keras.losses.MSE,
7 optimizer=tf.keras.optimizers.SGD(learning_rate=0.01))
8
9history = new_model.fit(x, y,
10 epochs=100,
11 batch_size=32,
12 verbose=0)
13
14new_model.save('./new_model.keras')
15
16plt.plot(history.history['loss'])
17plt.xlabel('Эпоха')
18plt.ylim([0, max(plt.ylim())])
19plt.ylabel('Потери [MSE]')
20plt.title('Keras тренировочный прогресс')
21
22plot_preds(x, y, f, new_model, 'После тренировки: Keras')
После выполнения этого кода будут отображены два графика:
На этом завершаем базовое рассмотрение основных элементов библиотеки TensorFlow. Jupyter ноутбук доступен в нашем репозитории по ссылке.