Добавить данные как канал в модель keras - PullRequest
3 голосов
/ 09 июля 2020

У меня следующая модель:

model = Sequential()

model.add(Dense(units=X_train.shape[1],activation='tanh', activity_regularizer=regularizers.l2(1e-2)))
model.add(Dropout(0.5))

model.add(Dense(units=X_train.shape[1]/4,activation='tanh', activity_regularizer=regularizers.l2(1e-2)))
model.add(Dropout(0.5))
model.add(Dense(units=1,activation='sigmoid'))#, bias_initializer=output_bias))
optimzier = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.9, nesterov=True)
model.compile(loss=BinaryFocalLoss(gamma=2), optimizer=optimzier, metrics = [f1_m,Precision(name='precision')])

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

Если это имеет значение, мои данные:

X_train - это фрейм данных с 200 столбцами (функциями) - столбцы 1-10 являются категориальными, столбцы 11-100 - это канал №1, а столбцы 101-190 - канал №2 (так что столбцы 11 и 101 одна и та же функция и другой канал и т. д.).

Возможно ли это?

Ответы [ 2 ]

3 голосов
/ 13 июля 2020

Здесь мы использовали функциональный API Keras для создания моделей, которые более гибкие, чем tf.keras.Sequential API. Функциональный API может обрабатывать модели с нелинейной топологией, модели с общими слоями и модели с несколькими входами или выходами.

столбцы 1-10 являются категориальными

def prepare_inputs(X_train, X_test):
    from sklearn.preprocessing import OrdinalEncoder
    oe = OrdinalEncoder()
    oe.fit(X_train)
    X_train_enc = oe.transform(X_train)
    X_test_enc = oe.transform(X_test)
    return X_train_enc, X_test_enc
def prepare_inputs(X_train, X_test):
    from sklearn.preprocessing import OneHotEncoder
    ohe = OneHotEncoder()
    ohe.fit(X_train)
    X_train_enc = ohe.transform(X_train)
    X_test_enc = ohe.transform(X_test)
    return X_train_enc, X_test_enc

Общие слои - это экземпляры слоев, которые многократно используются в одной и той же модели - они изучают функции, соответствующие нескольким путям в графе слоев. Общие слои часто используются для кодирования входных данных из одинаковых пространств (скажем, двух разных фрагментов текста с одинаковым словарем). Здесь он используется для обработки этой части:

столбцы 11-100 - это канал №1, а столбцы 101-190 - канал №1 (так что столбцы 11 и 101 являются та же функция, другой канал и т. д.).

Примечание: В этом коде я использовал несколько пользовательских функций, чтобы воспроизвести ваш вариант использования. Не стесняйтесь менять его при необходимости.

Модель 1

import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.regularizers import l2
from tensorflow.keras.metrics import Precision
from tensorflow.keras import backend as K
from tensorflow.keras.utils import plot_model


# number of categories
categories = 10
# number of entries in data i.e. num of rows
entries = 1000

# Column 1-10: 10 categorical features
categorical = np.random.randint(categories, size=(entries, 10))
# Column 11-100: 90 numerical features
numerical1 = np.random.rand(entries, 90)
# Column 101-190: 90 numerical features
numerical2 = np.random.rand(entries, 90)
# output
output = np.random.rand(entries)


def f1_m(y_true, y_pred):
    precision = precision_m(y_true, y_pred)
    recall = recall_m(y_true, y_pred)
    return 2*((precision*recall)/(precision+recall+K.epsilon()))


def recall_m(y_true, y_pred):
    true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
    possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))
    recall = true_positives / (possible_positives + K.epsilon())
    return recall


def precision_m(y_true, y_pred):
    true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
    predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))
    precision = true_positives / (predicted_positives + K.epsilon())
    return precision


def focal_loss(gamma=2., alpha=.25):
    def focal_loss_fixed(y_true, y_pred):
        pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))
        pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))

        pt_1 = K.clip(pt_1, 1e-3, .999)
        pt_0 = K.clip(pt_0, 1e-3, .999)

        return -K.sum(alpha * K.pow(1. - pt_1, gamma) * K.log(pt_1))-K.sum((1-alpha) * K.pow( pt_0, gamma) * K.log(1. - pt_0))
    return focal_loss_fixed


def build(cat_shape, ch1_shape, ch2_shape):
    cat_in = Input(shape=cat_shape, name='input1')
    ch1_in = Input(shape=ch1_shape, name='input2')
    ch2_in = Input(shape=ch2_shape, name='input3')

    # model for categorical value inputs
    cat_op1 = Dense(32, activation='tanh', activity_regularizer=l2(1e-2), name='cat_dense_1')(cat_in)
    cat_op2 = Dense(16, activation='tanh', activity_regularizer=l2(1e-2), name='cat_dense_2')(cat_op1)

    # defining keras shared layers for common features
    shared_d1 = Dense(32, activation='tanh', activity_regularizer=l2(1e-2), name='shared_dense_1')
    ch1_op1 = shared_d1(ch1_in)
    ch2_op1 = shared_d1(ch2_in)

    shared_d2 = Dense(16, activation='tanh', activity_regularizer=l2(1e-2), name='shared_dense_2')
    ch1_op2 = shared_d2(ch1_op1)
    ch2_op2 = shared_d2(ch2_op1)

    # merging all the outputs
    concat = tf.keras.layers.concatenate([cat_op2, ch1_op2, ch2_op2], name='concatenate')
    pre = Dense(8, activation='tanh', activity_regularizer=l2(1e-2), name='dense_final_1')(concat)
    out = Dense(1, activation='sigmoid', name='output')(pre)
    
    # model with multiple inputs
    model = Model(inputs=[cat_in, ch1_in, ch2_in], 
                           outputs=out, name="EnhancedModel")

    optimzier = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.9, nesterov=True)
    model.compile(loss=focal_loss(gamma=2), optimizer=optimzier, metrics = [f1_m, Precision(name='precision')])
    
    return model

model = build(categorical.shape[1:], numerical1.shape[1:], numerical2.shape[1:])
model.fit([categorical, numerical1, numerical2], output, batch_size=4, epochs=5)
# plot_model(
#     model,
#     to_file='model.png',
#     show_shapes=True,
#     show_layer_names=True,
#     rankdir='TB',
#     expand_nested=True,
#     dpi=96
# )

Обзор

enter image description here

Edit 1: As asked in the comments, changed the input vectors by concatenating categorical and numeric columns.

Model 2

import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.regularizers import l2
from tensorflow.keras.metrics import Precision
from tensorflow.keras import backend as K
from tensorflow.keras.utils import plot_model


# number of categories
categories = 10
# number of entries in data i.e. num of rows
entries = 1000

# Column 1-10: 10 categorical features
categorical = np.random.randint(categories, size=(entries, 10))
# Column 11-100: 90 numerical features
numerical1 = np.random.rand(entries, 90)
# Column 101-190: 90 numerical features
numerical2 = np.random.rand(entries, 90)
# concatenating categorical and numeric vectors
cat_numerical1 = np.concatenate((categorical, numerical1), axis=-1)
cat_numerical2 = np.concatenate((categorical, numerical2), axis=-1)
# output
output = np.random.rand(entries)


def f1_m(y_true, y_pred):
    precision = precision_m(y_true, y_pred)
    recall = recall_m(y_true, y_pred)
    return 2*((precision*recall)/(precision+recall+K.epsilon()))


def recall_m(y_true, y_pred):
    true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
    possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))
    recall = true_positives / (possible_positives + K.epsilon())
    return recall


def precision_m(y_true, y_pred):
    true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
    predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))
    precision = true_positives / (predicted_positives + K.epsilon())
    return precision


def focal_loss(gamma=2., alpha=.25):
    def focal_loss_fixed(y_true, y_pred):
        pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))
        pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))

        pt_1 = K.clip(pt_1, 1e-3, .999)
        pt_0 = K.clip(pt_0, 1e-3, .999)

        return -K.sum(alpha * K.pow(1. - pt_1, gamma) * K.log(pt_1))-K.sum((1-alpha) * K.pow( pt_0, gamma) * K.log(1. - pt_0))
    return focal_loss_fixed


def build(ch1_shape, ch2_shape):
    ch1_in = Input(shape=ch1_shape, name='input1')
    ch2_in = Input(shape=ch2_shape, name='input2')

    # defining keras shared layers for common features
    shared_d1 = Dense(32, activation='tanh', activity_regularizer=l2(1e-2), name='shared_dense_1')
    ch1_op1 = shared_d1(ch1_in)
    ch2_op1 = shared_d1(ch2_in)

    shared_d2 = Dense(16, activation='tanh', activity_regularizer=l2(1e-2), name='shared_dense_2')
    ch1_op2 = shared_d2(ch1_op1)
    ch2_op2 = shared_d2(ch2_op1)

    # merging all the outputs
    concat = tf.keras.layers.concatenate([ch1_op2, ch2_op2], name='concatenate')
    pre = Dense(8, activation='tanh', activity_regularizer=l2(1e-2), name='dense_final_1')(concat)
    out = Dense(1, activation='sigmoid', name='output')(pre)
    
    # model with multiple inputs
    model = Model(inputs=[ch1_in, ch2_in], 
                           outputs=out, name="EnhancedModel")

    optimzier = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.9, nesterov=True)
    model.compile(loss=focal_loss(gamma=2), optimizer=optimzier, metrics = [f1_m, Precision(name='precision')])
    
    return model

model = build(cat_numerical1.shape[1:], cat_numerical2.shape[1:])
model.fit([cat_numerical1, cat_numerical2], output, batch_size=4, epochs=5)
plot_model(
    model,
    to_file='model.png',
    show_shapes=True,
    show_layer_names=True,
    rankdir='TB',
    expand_nested=True,
    dpi=96
)

Обзор

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

1 голос
/ 13 июля 2020

Я полагаю, вы пытаетесь заявить следующее, пожалуйста, дайте мне знать, если я что-то не так:

  1. Столбцы 1-10 из x_train содержат категориальные данные. Ради своего решения я думаю о категориях, определенных как целочисленные метки.
  2. Столбцы 11-100 содержат некатегориальные данные. Ради своего решения я думаю о них как о некоторых случайных значениях с плавающей запятой.
  3. Столбцы 101-190 также такие же, как указано в пункте 2.
  4. Вы рассматриваете пакет столбцов ( 11-100) и (101-190) как два канала. Я предполагаю, что вы ожидаете одинакового типа обработки (подмоделей) для обоих каналов.

Если мои предположения верны, вам следует разделить столбцы, указанные в пунктах 1, 2 и 3. Затем вы должны скормить их модели. Позвольте мне показать вам, что я разработал для этого случая с изображением модели Keras.

keras model

The model processes each of the same data types (categorical/non-categorical) and processes them differently based on the learned weights. Then the information is merged and it generates a final output. I am adding the code below.

import numpy as np
import tensorflow as tf

# number of categories
nb_category = 10
# number of rows in data
rows = 100

# Column 1 to 10 = 10-1+1 = 10 features
# The input is a plain array of integer categories wihtout one hot encoding
categories = np.random.randint(nb_category, size=(rows, 10))
# Column 11 to 100 = 100-11+1 = 90 features
channel1 = np.random.rand(rows, 90)
# Column 101 to 190 = 190-101+1 = 90 features
channel2 = np.random.rand(rows, 90)
# generating a random output for each of the rows
y_train = np.random.rand(rows)

def modelDef(cat_shape, ch1_shape, ch2_shape):
    cat_in = tf.keras.Input(shape=cat_shape)
    ch1_in = tf.keras.Input(shape=ch1_shape)
    ch2_in = tf.keras.Input(shape=ch2_shape)

    # shallow model for category inputs
    cat_x = tf.keras.layers.Dense(16)(cat_in)
    cat_x = tf.keras.layers.Dense(32)(cat_x)
    cat_x = tf.keras.layers.Dense(16)(cat_x)

    # shallow model for channel inputs
    ch1_x = tf.keras.layers.Dense(32)(ch1_in)
    ch1_x = tf.keras.layers.Dense(64)(ch1_x)
    ch1_x = tf.keras.layers.Dense(32)(ch1_x)

    # shallow model for channel inputs
    ch2_x = tf.keras.layers.Dense(32)(ch2_in)
    ch2_x = tf.keras.layers.Dense(64)(ch2_x)
    ch2_x = tf.keras.layers.Dense(32)(ch2_x)

    # merging all the outputs
    out = tf.keras.layers.concatenate([cat_x, ch1_x, ch2_x])
    out = tf.keras.layers.Dense(12)(out)
    out = tf.keras.layers.Dense(1)(out)
    
    # generating model
    model = tf.keras.Model(inputs=[cat_in, ch1_in, ch2_in], 
                           outputs=out, name="dummyModel")
    
    # Tune the loss, optimizer, metrics according to the data
    model.compile(loss='binary_crossentropy', optimizer='Adam',
                  metrics='accuracy')
    return model

model = modelDef(categories.shape[1:], channel1.shape[1:], channel2.shape[1:])

model.fit([categories, channel1, channel2], y_train, batch_size=32, epochs=10)

For the sake of my simplicity, I have generated random data. The categories define the data columns as stated in point 1. The channel1 and channel2 defies the data columns as stated in points 2 and 3, respectively. I tried to keep the code as simple as possible, therefore, I avoided declaring optional parameters.

Furthermore, as you are saying that the columns stated in point 2 and 3 contain the same feature, you may want them to go through the same training weights. To simplify my statement, I am adding a modified version of the previous architecture.

введите описание изображения здесь

Модель использует каналы 2 и 3 с одинаковыми заученными весами. Также уменьшены параметры обучения. Доработанная функция модели приведена ниже.

def modelDefMod(cat_shape, ch1_shape, ch2_shape):
    cat_in = tf.keras.Input(shape=cat_shape)
    ch1_in = tf.keras.Input(shape=ch1_shape)
    ch2_in = tf.keras.Input(shape=ch2_shape)

    # shallow model for category inputs
    cat_x = tf.keras.layers.Dense(16)(cat_in)
    cat_x = tf.keras.layers.Dense(32)(cat_x)
    cat_x = tf.keras.layers.Dense(16)(cat_x)

    # defining weights
    d1 = tf.keras.layers.Dense(32)
    d2 = tf.keras.layers.Dense(64)
    d3 = tf.keras.layers.Dense(32)

    # shallow model for channel inputs
    ch1_x = d1(ch1_in)
    ch1_x = d2(ch1_x)
    ch1_x = d3(ch1_x)

    # shallow model for channel inputs
    ch2_x = d1(ch2_in)
    ch2_x = d2(ch2_x)
    ch2_x = d3(ch2_x)

    # merging all the outputs
    out = tf.keras.layers.concatenate([cat_x, ch1_x, ch2_x])
    out = tf.keras.layers.Dense(12)(out)
    out = tf.keras.layers.Dense(1)(out)
    
    # generating model
    model = tf.keras.Model(inputs=[cat_in, ch1_in, ch2_in], 
                           outputs=out, name="dummyModel")
    
    # Tune the loss, optimizer, metrics according to the data
    model.compile(loss='binary_crossentropy', optimizer='Adam',
                  metrics='accuracy')
    return model
...