keras 使用变换器模型的多示例分类

sg3maiej  于 2023-06-23  发布在  其他
关注(0)|答案(2)|浏览(113)

我使用这个Keras documentation example中的转换器进行多示例分类。每个示例的类依赖于一个包中的其他示例。我使用Transformer模型是因为:
它不对数据之间的时间/空间关系做出任何假设。这对于处理一组对象是理想的
例如,每个包可以具有最多5个示例,并且每个示例有3个特征。

# Generate data
max_length = 5
x_lst = []
y_lst = []
for _ in range(10):
    num_instances = np.random.randint(2, max_length + 1)
    x_bag = np.random.randint(0, 9, size=(num_instances, 3))
    y_bag = np.random.randint(0, 2, size=num_instances)
    
    x_lst.append(x_bag)
    y_lst.append(y_bag)

前2个袋的特征和标签(包括5和2个示例):

x_lst[:2]

[array([[8, 0, 3],
        [8, 1, 0],
        [4, 6, 8],
        [1, 6, 4],
        [7, 4, 6]]),
 array([[5, 8, 4],
        [2, 1, 1]])]

y_lst[:2]

[array([0, 1, 1, 1, 0]), array([0, 0])]

接下来,用零填充特征,用-1填充目标:

x_padded = []
y_padded = []

for x, y in zip(x_lst, y_lst):
    x_p = np.zeros((max_length, 3))
    x_p[:x.shape[0], :x.shape[1]] = x
    x_padded.append(x_p)

    y_p = np.negative(np.ones(max_length))
    y_p[:y.shape[0]] = y
    y_padded.append(y_p)

X = np.stack(x_padded)
y = np.stack(y_padded)

其中X.shape等于(10, 5, 3)y.shape等于(10, 5)
我对原始模型做了两个改动:在输入层之后添加掩蔽层,并将最后一个致密层中的单元数设置为袋的最大尺寸(加上“S形”激活):

def transformer_encoder(inputs, head_size, num_heads, ff_dim, dropout=0):
    # Attention and Normalization
    x = layers.MultiHeadAttention(
        key_dim=head_size, num_heads=num_heads, dropout=dropout
    )(inputs, inputs)
    x = layers.Dropout(dropout)(x)
    x = layers.LayerNormalization(epsilon=1e-6)(x)
    res = x + inputs

    # Feed Forward Part
    x = layers.Conv1D(filters=ff_dim, kernel_size=1, activation="relu")(res)
    x = layers.Dropout(dropout)(x)
    x = layers.Conv1D(filters=inputs.shape[-1], kernel_size=1)(x)
    x = layers.LayerNormalization(epsilon=1e-6)(x)
    return x + res

def build_model(
    input_shape,
    head_size,
    num_heads,
    ff_dim,
    num_transformer_blocks,
    mlp_units,
    dropout=0,
    mlp_dropout=0,
):
    inputs = keras.Input(shape=input_shape)
    inputs = keras.layers.Masking(mask_value=0)(inputs) # ADDED MASKING LAYER
    x = inputs
    for _ in range(num_transformer_blocks):
        x = transformer_encoder(x, head_size, num_heads, ff_dim, dropout)

    x = layers.GlobalAveragePooling1D(data_format="channels_first")(x)
    for dim in mlp_units:
        x = layers.Dense(dim, activation="relu")(x)
        x = layers.Dropout(mlp_dropout)(x)
    outputs = layers.Dense(5, activation='sigmoid')(x) # CHANGED ACCORDING TO MY OUTPUT
    return keras.Model(inputs, outputs)

input_shape = (5, 3)

model = build_model(
    input_shape,
    head_size=256,
    num_heads=4,
    ff_dim=4,
    num_transformer_blocks=4,
    mlp_units=[128],
    mlp_dropout=0.4,
    dropout=0.25,
)

model.compile(
    loss="binary_crossentropy",
    optimizer=keras.optimizers.Adam(learning_rate=1e-4),
    metrics=["binary_accuracy"],
)
model.summary()

看起来我的模特学不多。如果我使用每个包的真值的数量(y.sum(axis=1)Dense(1))作为目标,而不是每个示例的分类,模型学习得很好。我的错误在哪里?在这种情况下,我应该如何构建输出层?我是否需要自定义丢失函数?
更新:我做了一个自定义的损失函数:

def my_loss_fn(y_true, y_pred):
    mask = tf.cast(tf.math.not_equal(y_true, tf.constant(-1.)), tf.float32)
    y_true, y_pred = tf.expand_dims(y_true, axis=-1), tf.expand_dims(y_pred, axis=-1)
    bce = tf.keras.losses.BinaryCrossentropy(reduction='none')
    return tf.reduce_sum(tf.cast(bce(y_true, y_pred), tf.float32) * mask)

mask = (y_test != -1).astype(int)
pd.DataFrame({'n_labels': mask.sum(axis=1), 'preds': ((preds * mask) >= .5).sum(axis=1)}).plot(figsize=(20, 5))

看起来模型学习:

但它预测所有非掩蔽标签为1。

@thushv89这是my problem。我需要两个时间点:t1和t2,并查找在时间t1处于维护中的所有车辆和计划在时间t2处于维护中的所有车辆。这是我的物品袋。然后,我计算诸如t1车辆已经花费了多少时间进行维护,t2车辆从t1到计划开始的时间等特征。如果我试图预测在时间t2时维修车辆的数量,我的模型学习得很好,但我想预测哪些车辆会离开,哪些车辆会进来(对于包中的4辆车辆,3 vs [True,False,True,True])。

k4aesqcs

k4aesqcs1#

有三个重要的改进:
1.将GlobalAveragePooling1D图层替换为Flatten图层。
1.添加一个自定义损失函数,以排除计算中的目标填充(已经添加到我的问题中)和一个自定义度量函数,如果你想看到真实的的度量。
1.将attention_mask添加到MultiHeadAttention(而不是Masking层)以掩蔽填充。

nzrxty8p

nzrxty8p2#

只是@Mykola_Zotko的一个简单的附加组件,它可以帮助那些正在使用kerastensorflow学习深度学习的新用户。
删除GlobalAveragePooling 1D
对于上下文,这个GlobalAveragePooling1D基本上是时态数据的全局平均池化操作。所以基本上,当你删除这个方法调用时,你删除了“池化”操作,或者用更简单的术语,通过@Mykola_Zotko:
……你得到了一个二维Tensor,它在第一维度(在我的例子中是5个)中免费输出
别名为:

tf.keras.layers.GlobalAvgPool1D

这个方法的代码:

tf.keras.layers.GlobalAveragePooling1D (
    data_format = "channels_last", **kwargs
)

可以在以下网站找到此来源:

添加自定义损失函数
损失函数所做的只是“生成模型在训练时间内应该寻求最小化的量”。Source
或者换句话说:
在数学优化、统计学、机器学习和深度学习中,Loss Function(也称为成本函数或误差函数)是定义一系列值与真实的之间相关性的函数。该数字在概念上表示与事件或一组值相关联的成本。通常,优化过程的目标是使损失函数最小化。Towardsdatascience - custom loss function in tensorflow
将attention_mask添加到MultiHeadAttention
别名:

tf.keras.layers.MultiHeadAttention

方法代码:

tf.keras.layers.MultiHeadAttention(
    num_heads,
    key_dim,
    value_dim=None,
    dropout=0.0,
    use_bias=True,
    output_shape=None,
    attention_axes=None,
    kernel_initializer='glorot_uniform',
    bias_initializer='zeros',
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    **kwargs
)

来源:

以前对代码进行的改进:

  • metrics=["accuracy"]metrics=["binary_accuracy"]
model.compile(
    loss="binary_crossentropy",
    optimizer=keras.optimizers.Adam(learning_rate=1e-4),
    metrics=["binary_accuracy"],
)
  • 在自定义损失函数中使用Crossentropy

相关问题