Как определить пользовательский слой, функцию активации и функцию потерь в TensorFlow

Пошаговое объяснение и примеры с полным кодом

У меня есть несколько руководств по Tensorflow, где всегда использовались встроенные функции потерь и слои. Но Tensorflow намного динамичнее этого. Это позволяет нам писать собственные пользовательские функции потерь и создавать собственные пользовательские слои. Итак, есть много способов создавать высокоэффективные модели в Tensorflow.

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

Я предполагаю, что вы уже знакомы с основами анализа данных, очистки данных и Tensorflow. Итак, вначале мы будем двигаться немного быстрее.

Обработка данных

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

Вот ссылка на набор данных:



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



Давайте начнем.

Сначала импортируйте сюда все необходимые пакеты:

import numpy as np
import pandas as pd
import tensorflow as tf 
from tensorflow.keras import Sequential 
from tensorflow.keras.layers import Dense 
from tensorflow.keras.optimizers import Adam 
from tensorflow.keras import backend as K
from tensorflow.keras.layers import Layer

Вот набор данных:

df = pd.read_csv("auto_price.csv")

Хотя я сказал, что это чистый набор данных, в нем все еще есть два ненужных столбца, которые нужно удалить:

df = df.drop(columns=['Unnamed: 0', 'symboling'])

Мы разделим набор данных на три части. Один для обучения, один для тестирования и один для проверки.

from sklearn.model_selection import train_test_split
train, test = train_test_split(df, test_size=0.2, random_state=2)
train, val = train_test_split(train, test_size=0.2, random_state=23)

Чтобы нормализовать данные обучения с использованием метода z-оценки, нам нужно знать среднее значение и стандартное отклонение всех функций обучения. Вот как я это понял:

train_stats = train.describe()
train_stats= train_stats.transpose()
train_stats

Также есть дополнительная информация, но также есть среднее значение и стандартное отклонение.

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

def norm(x):
    return (x - train_stats['mean']) / train_stats['std']

Давайте нормализуем данные обучения, тестирования и проверки:

train_x = norm(train)
test_x = norm(test)
val_x = norm(val)

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

train_x = train_x.drop(columns='price')
test_x = test_x.drop(columns='price')
val_x=val_x.drop(columns='price')
train_y = train['price']
test_y = test['price']
val_y = val['price']

Обучающие и целевые переменные готовы.

Пользовательские потери и пользовательский слой

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

def rmse(y_true, y_pred):
    return K.sqrt(K.mean(K.square(y_pred - y_true)))

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

Теперь перейдем к пользовательскому слою. Для этого мы также будем использовать простую линейную формулу Y=WX+B в качестве формулы. Эта формула требует весов, которые являются коэффициентами X и Bias (обозначаются как «B» в формуле). Я объясню более подробно после того, как вы увидите код для этого:

class SimpleLinear(Layer):
def __init__(self, units=64, activation=None):
        super(SimpleLinear, self).__init__()
        self.units = units
        self.activation=tf.keras.activations.get(activation)
def weightsAndBias(self, input_shape):
        w_init = tf.random_normal_initializer()
        self.w = tf.Variable(name="kernel",
            initial_value=w_init(shape=(input_shape[-1], self.units),
                                 dtype='float32'),
            trainable=True)
b_init = tf.zeros_initializer()
        self.b = tf.Variable(name="bias",
            initial_value=b_init(shape=(self.units,), dtype='float32'),
            trainable=True)
def call(self, inputs):
        return self.activation(tf.matmul(inputs, self.w) + self.b)

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

В приведенном выше «weightsAndBias» мы инициируем веса и смещения, где веса инициируются как случайные числа, а смещения как нули.

В функции вызова мы умножаем наши входные данные и веса, используя матричное умножение (метод matmul выполняет матричное умножение) и добавляем к нему смещение (помните формулу wx+b)

Это самый основной. Пожалуйста, не стесняйтесь попробовать некоторые нелинейные слои, это могут быть квадратичные или кубические формулы.

Разработка модели

Разработка модели — более простая часть. У нас есть 24 переменных в качестве обучающих функций. Таким образом, входная форма (24, ). Вот полная модель:

model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(24,)),
    SimpleLinear(512, activation='relu'),
    tf.keras.layers.Dropout(0.2),
    SimpleLinear(256, activation='relu'),
    SimpleLinear(128, activation='relu'),
    tf.keras.layers.Dense(1, activation='relu')
])

Как видите, мы просто вызвали метод SimpleLinear, который мы определили ранее как слои. 512, 256 и 128 — это единицы измерения, а активация — «relu».

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

Давайте скомпилируем модель и воспользуемся функцией потерь «rmse», которую мы определили ранее:

model.compile(optimizer='adam',
              loss = rmse,
              metrics=tf.keras.metrics.RootMeanSquaredError())
h = model.fit(train_x, train_y, epochs=3)
model.evaluate(val_x, val_y)

Выход:

Epoch 1/3
4/4 [==============================] - 0s 3ms/step - loss: 13684.0762 - root_mean_squared_error: 13726.8496
Epoch 2/3
4/4 [==============================] - 0s 3ms/step - loss: 13669.2314 - root_mean_squared_error: 13726.8496
Epoch 3/3
4/4 [==============================] - 0s 3ms/step - loss: 13537.3682 - root_mean_squared_error: 13726.8496

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

Пользовательская функция активации

Здесь я объясню два способа использования пользовательской функции активации. Первый заключается в использовании лямбда-слоя. Лямбда-слой определяет функцию прямо в слое.

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

model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(24,)),
    SimpleLinear(512),
    tf.keras.layers.Lambda(lambda x: tf.abs(x)),
    tf.keras.layers.Dropout(0.2),
    SimpleLinear(256),
    tf.keras.layers.Lambda(lambda x: tf.abs(x)),
    tf.keras.layers.Dense(1),
    tf.keras.layers.Lambda(lambda x: tf.abs(x)),
])

Пожалуйста, не стесняйтесь пробовать любые другие виды операций на лямбда-слое.

Вам не нужно определять операцию в самом лямбда-слое. Его можно определить в функции и передать на уровень лямбда.

Вот функция, которая берет данные и возводит их в квадрат:

def active1(x):
    return x**2

Теперь эту функцию можно просто передать в лямбда-слой следующим образом:

model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(24,)),
    SimpleLinear(512),
    tf.keras.layers.Lambda(active1),
    tf.keras.layers.Dropout(0.2),
    SimpleLinear(256),
    tf.keras.layers.Lambda(active1),
    tf.keras.layers.Dense(1),
    tf.keras.layers.Lambda(active1),
])

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

Заключение

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

Не стесняйтесь подписываться на меня в Twitter и лайкать мою страницу Facebook.

Подробнее Чтение