DeepLearning: Conv/Deconv/Relu/Loss/BatchNorm 的关系(非理论探究)

卷积与反卷积层的正确使用顺序
本文探讨了卷积层(ConvLayers)、激活层(ActivationLayers)及反卷积层(DeconvLayers)在神经网络中的正确使用顺序。强调了在上采样操作中DeconvLayer的重要性,并提供了Caffe框架下具体的参数设置,同时指出了连接损失层(loss layer)时的注意事项。
Contact Me:
王雪豪 xuehaowang@buaa.edu.cn

It is generally known that the order is important when you use the Conv Layers and Activation Layers.

We usually use them as follow:

conv -> relu
conv -> batchnorm -> scale -> relu

This order shown above can work well. However, how to add into a Deconv Layer?

We use Deconv Layers to upsample the input data and the paras can be set as this(caffe framwork):

layer {
  name: "deconv"
  type: "Deconvolution"
  bottom: "conv"
  top: "deconv"
  param {lr_mult: 0 decay_mult: 0}
  param {lr_mult: 0 decay_mult: 0}
  convolution_param {
    num_output: 1
    pad: 4
    kernel_size: 16
    stride: 8 #扩大的倍数
    weight_filler {
      type:"bilinear" #差值填充
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

This is a part of caffe network designed to upsample the input 'conv'. The lr is set as 0 to avoid changing the paras during training phase.

So if we want to combine the Conv layer and Deconv layer, we could follow this order:

Conv -> Deconv -> Relu
Conv -> Deconv -> batchnorm -> scale -> Relu

Essentially the Deconv layer can be treat as a post-operate of the Conv layer when you just want to upsample input data and Do Not Need a Activation Layer to active the Conv layer additionally.

Meanwhile if your network has a Loss layer to deal with the data from Conv and Deconv, you must directly connect the loss layer with Deconv layer instead of Conv layer, unless your data from Deconv and Conv layer are both single channel.

The follow order can work well and if you conect the loss layer with conv layer which is output by the deconv layer, you will get an unstable loss value.

Conv(维度下降) -> Deconv(上采样) -> loss
INSTEAD OF
Deconv(上采样) -> Conv(维度下降) -> loss

 

from .model_utils import Model, SiameseAccuracy import tensorflow as tf from tensorflow.keras import layers, models, metrics, losses from tensorflow.keras.callbacks import LambdaCallback import tensorflow_addons as tfa class UpscalingLayer(layers.Layer): def __init__(self, out_shape): super(UpscalingLayer, self).__init__() self.out_shape = out_shape def call(self, inputs, **kwargs): output = tf.repeat(inputs, 2, axis=1) return tf.ensure_shape(output, self.out_shape) def get_config(self): config = super().get_config().copy() config.update({ 'out_shape': self.out_shape, }) return config """ A simple siamese model with convolutional layers, followed by a dense layer and a difference layer. Model architecture based on https://github.com/ssloxford/auto-phy-fingerprint/blob/main/code/fingerprinting/adsb-siamese/train.py """ class SimpleSiameseConvModel(Model): def build_model(self, input_len, feature_count, conv_layers, dense_size, learning_rate): """ Build the siamese model. Args: input_len (int): Length of the input vector. feature_count (int): Number of features in the input vector. conv_layers (list): List of tuples (filters, kernel_size) for each convolutional layer. dense_size (int): Size of the dense layer. learning_rate (float): Learning rate for the optimizer. Returns: tf.keras.Model: The siamese model. """ input_left = layers.Input(shape=(input_len, feature_count), name="input_left") input_right = layers.Input(shape=(input_len, feature_count), name="input_right") input_dummy = layers.Input(shape=(input_len, feature_count), name="input_dummy") input_norm = layers.BatchNormalization()(input_dummy) input_padded = layers.ZeroPadding1D(padding=2)(input_norm) conv = [input_padded] for i, (filters, kernel_size) in enumerate(conv_layers): conv.append(layers.Conv1D(filters, kernel_size, activation='relu', name="conv_{}".format(i))(conv[-1])) conv.append(layers.MaxPooling1D()(conv[-1])) flat = layers.Flatten()(conv[-1]) dense = layers.Dense(dense_size, activation='sigmoid', name="dense")(flat) extractor = models.Model(inputs=[input_dummy], outputs=[dense], name="extractor") encoded_left = models.Model(inputs=[input_left], outputs=[extractor.call(input_left)], name="encoded_left") encoded_right = models.Model(inputs=[input_right], outputs=[extractor.call(input_right)], name="encoded_right") difference = layers.Subtract(name="difference")([encoded_left.output, encoded_right.output]) difference_abs = layers.Lambda(lambda x: tf.abs(x), name="difference_abs")(difference) prediction = layers.Dense(1, activation='sigmoid', name="output")(difference_abs) model = models.Model(inputs=[input_left, input_right], outputs=[prediction], name="siamese") model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate), loss='binary_crossentropy', metrics=['accuracy'] ) return model def __init__(self, name, input_len, feature_count, conv_layers, dense_size, learning_rate, save_dir=None): """ Initialize the siamese model. Args: name (str): Name of the model. input_len (int): Length of the input vector. feature_count (int): Number of features in the input vector. conv_layers (list): List of tuples (filters, kernel_size) for each convolutional layer. dense_size (int): Size of the dense layer. learning_rate (float): Learning rate for the optimizer. save_dir (str): Directory to save the model to. """ self.model = self.build_model(input_len, feature_count, conv_layers, dense_size, learning_rate) super().__init__(self.model, name, save_dir=save_dir) """ A simple siamese model with convolutional layers (split into separate IQ layers), followed by a dense layer and a difference layer. Model architecture based on https://github.com/ssloxford/auto-phy-fingerprint/blob/main/code/fingerprinting/adsb-siamese/train.py """ class SimpleSiameseSplitConvModel(Model): def build_model(self, input_len, feature_count, conv_layers, dense_size, learning_rate): """ Build the siamese model. Args: input_len (int): Length of the input vector. feature_count (int): Number of features in the input vector. conv_layers (list): List of tuples (filters, kernel_size) for each convolutional layer. dense_size (int): Size of the dense layer. learning_rate (float): Learning rate for the optimizer. Returns: tf.keras.Model: The siamese model. """ input_left = layers.Input(shape=(input_len, feature_count), name="input_left") input_right = layers.Input(shape=(input_len, feature_count), name="input_right") input_dummy = layers.Input(shape=(input_len, feature_count), name="input_dummy") input_norm = layers.BatchNormalization()(input_dummy) input_padded = layers.ZeroPadding1D(padding=2)(input_norm) input_i, input_q = tf.split(input_padded, feature_count, axis=2) conv_i = [input_i] conv_q = [input_q] for i, (filters, kernel_size) in enumerate(conv_layers): conv_i.append(layers.Conv1D(filters, kernel_size, activation='relu', name="conv_i_{}".format(i))(conv_i[-1])) conv_i.append(layers.MaxPooling1D()(conv_i[-1])) conv_q.append(layers.Conv1D(filters, kernel_size, activation='relu', name="conv_{}".format(i))(conv_q[-1])) conv_q.append(layers.MaxPooling1D()(conv_q[-1])) conv_out = layers.Concatenate(axis=2, name="conv_out")([conv_i[-1], conv_q[-1]]) flat = layers.Flatten()(conv_out) dense = layers.Dense(dense_size, activation='sigmoid', name="dense")(flat) extractor = models.Model(inputs=[input_dummy], outputs=[dense], name="extractor") encoded_left = models.Model(inputs=[input_left], outputs=[extractor.call(input_left)], name="encoded_left") encoded_right = models.Model(inputs=[input_right], outputs=[extractor.call(input_right)], name="encoded_right") difference = layers.Subtract(name="difference")([encoded_left.output, encoded_right.output]) difference_abs = layers.Lambda(lambda x: tf.abs(x), name="difference_abs")(difference) prediction = layers.Dense(1, activation='sigmoid', name="output")(difference_abs) model = models.Model(inputs=[input_left, input_right], outputs=[prediction], name="siamese") model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate), loss='binary_crossentropy', metrics=['accuracy'] ) return model def __init__(self, name, input_len, feature_count, conv_layers, dense_size, learning_rate, save_dir=None): """ Initialize the siamese model. Args: name (str): Name of the model. input_len (int): Length of the input vector. feature_count (int): Number of features in the input vector. conv_layers (list): List of tuples (filters, kernel_size) for each convolutional layer. dense_size (int): Size of the dense layer. learning_rate (float): Learning rate for the optimizer. save_dir (str): Directory to save the model to. """ if feature_count != 2: raise ValueError("This model currently only supports 2D input vectors (IQ samples).") self.model = self.build_model(input_len, feature_count, conv_layers, dense_size, learning_rate) super().__init__(self.model, name, save_dir=save_dir) """ A simple siamese model with convolutional layers (split into separate IQ layers), followed by a dense layer. Triplet loss is used. Model architecture based on https://github.com/ssloxford/auto-phy-fingerprint/blob/main/code/fingerprinting/adsb-siamese/train.py """ class SimpleTripletSplitConvModel(Model): def siamese_accuracy(self, y_true, y_pred): """ Calculate the accuracy of the siamese model. Args: y_true (tf.Tensor): True labels. y_pred (tf.Tensor): Embeddings. """ label_equality = tf.equal(y_true[:, None], y_true[None, :]) distances = tf.linalg.norm(y_pred[:, None, :] - y_pred[None, :, :], axis=-1) predicted_matches = distances < self.accuracy_threshold correct_matches = tf.logical_and(label_equality, predicted_matches) accuracy = tf.reduce_mean(tf.cast(correct_matches, tf.float32)) return accuracy def siamese_tpr(self, y_true, y_pred): """ Calculate the TPR of the siamese model. Args: y_true (tf.Tensor): True labels. y_pred (tf.Tensor): Embeddings. """ label_equality = tf.equal(y_true[:, None], y_true[None, :]) distances = tf.linalg.norm(y_pred[:, None, :] - y_pred[None, :, :], axis=-1) predicted_matches = distances < self.accuracy_threshold #true_positives = tf.logical_and(label_equality, predicted_matches) #tpr = tf.reduce_sum(tf.cast(true_positives, tf.float32)) / tf.reduce_sum(tf.cast(label_equality, tf.float32)) true_positives = tf.logical_and(label_equality, predicted_matches) tp = tf.reduce_sum(tf.cast(true_positives, tf.float32)) fn = tf.reduce_sum(tf.cast(tf.logical_and(tf.logical_not(label_equality), predicted_matches), tf.float32)) tpr = tp / (tp + fn) return tpr def siamese_fpr(self, y_true, y_pred): """ Calculate the FPR of the siamese model. Args: y_true (tf.Tensor): True labels. y_pred (tf.Tensor): Embeddings. """ label_equality = tf.equal(y_true[:, None], y_true[None, :]) distances = tf.linalg.norm(y_pred[:, None, :] - y_pred[None, :, :], axis=-1) predicted_matches = distances < self.accuracy_threshold #false_positives = tf.logical_and(tf.logical_not(label_equality), predicted_matches) #fpr = tf.reduce_sum(tf.cast(false_positives, tf.float32)) / tf.reduce_sum(tf.cast(tf.logical_not(label_equality), tf.float32)) false_positives = tf.logical_and(tf.logical_not(label_equality), predicted_matches) fp = tf.reduce_sum(tf.cast(false_positives, tf.float32)) tn = tf.reduce_sum(tf.cast(tf.logical_and(tf.logical_not(label_equality), tf.logical_not(predicted_matches)), tf.float32)) fpr = fp / (fp + tn) return fpr def siamese_fscore(self, y_true, y_pred): """ Calculate the F-score of the siamese model. Args: y_true (tf.Tensor): True labels. y_pred (tf.Tensor): Embeddings. """ label_equality = tf.equal(y_true[:, None], y_true[None, :]) distances = tf.linalg.norm(y_pred[:, None, :] - y_pred[None, :, :], axis=-1) predicted_matches = distances < self.accuracy_threshold true_positives = tf.logical_and(label_equality, predicted_matches) false_positives = tf.logical_and(tf.logical_not(label_equality), predicted_matches) false_negatives = tf.logical_and(label_equality, tf.logical_not(predicted_matches)) tp = tf.reduce_sum(tf.cast(true_positives, tf.float32)) fp = tf.reduce_sum(tf.cast(false_positives, tf.float32)) fn = tf.reduce_sum(tf.cast(false_negatives, tf.float32)) precision = tp / (tp + fp) recall = tp / (tp + fn) f_score = 2 * precision * recall / (precision + recall) return f_score def build_model(self, input_len, feature_count, conv_layers, dense_size, learning_rate, triplet_margin, triplet_distance_metric, normalization): """ Build the siamese model. Args: input_len (int): Length of the input vector. feature_count (int): Number of features in the input vector. conv_layers (list): List of tuples (filters, kernel_size) for each convolutional layer. dense_size (int): Size of the dense layer. learning_rate (float): Learning rate for the optimizer. triplet_margin (float): Margin for the triplet loss. triplet_distance_metric (str): Distance metric to use for the triplet loss. One of 'L2', 'squared-L2', 'angular', or a callable. normalization (str): Normalization to use for the embeddings. One of 'L1', 'L2', None. Returns: tf.keras.Model: The siamese model. """ input = layers.Input(shape=(input_len, feature_count), name="input") input_norm = layers.BatchNormalization()(input) input_padded = layers.ZeroPadding1D(padding=2)(input_norm) input_i, input_q = tf.split(input_padded, feature_count, axis=2) conv_i = [input_i] conv_q = [input_q] for i, (filters, kernel_size) in enumerate(conv_layers): conv_i.append(layers.Conv1D(filters, kernel_size, activation='relu', name="conv_i_{}".format(i))(conv_i[-1])) conv_i.append(layers.MaxPooling1D()(conv_i[-1])) conv_q.append(layers.Conv1D(filters, kernel_size, activation='relu', name="conv_{}".format(i))(conv_q[-1])) conv_q.append(layers.MaxPooling1D()(conv_q[-1])) conv_out = layers.Concatenate(axis=2, name="conv_out")([conv_i[-1], conv_q[-1]]) flat = layers.Flatten()(conv_out) dense = layers.Dense(dense_size, activation=None, name="dense")(flat) norm = None if normalization == 'l1': norm = layers.Lambda(lambda x: tf.linalg.normalize(x, ord=1, axis=1)[0], name="l1_norm")(dense) elif normalization == 'l2': norm = layers.Lambda(lambda x: tf.math.l2_normalize(x, axis=1), name="l2_norm")(dense) elif normalization is None: norm = dense model = models.Model(inputs=[input], outputs=[norm], name="model") model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate), loss=tfa.losses.TripletSemiHardLoss( margin=triplet_margin, distance_metric=triplet_distance_metric, ), metrics=[ #self.siamese_accuracy, #self.siamese_tpr, #self.siamese_fpr, #self.siamese_fscore, ] ) return model def __init__(self, name, input_len, feature_count, conv_layers, dense_size, learning_rate, triplet_margin=1.0, triplet_distance_metric='L2', normalization='L2', accuracy_threshold=0.5, save_dir=None): """ Initialize the siamese model. Args: name (str): Name of the model. input_len (int): Length of the input vector. feature_count (int): Number of features in the input vector. conv_layers (list): List of tuples (filters, kernel_size) for each convolutional layer. dense_size (int): Size of the dense layer. learning_rate (float): Learning rate for the optimizer. triplet_margin (float): Margin for the triplet loss. triplet_distance_metric (str): Distance metric to use for the triplet loss. One of 'L2', 'squared-L2', 'angular', or a callable. normalization (str): Normalization to use for the embeddings. One of 'L1', 'L2', None. accuracy_threshold (float): Accuracy threshold for the model's SiameseAccuracy metric. save_dir (str): Directory to save the model to. """ self.accuracy_threshold = accuracy_threshold if feature_count != 2: raise ValueError("This model currently only supports 2D input vectors (IQ samples).") if normalization is not None: normalization = normalization.lower() if normalization not in ['l1', 'l2']: raise ValueError("Invalid normalization: {}".format(normalization)) self.model = self.build_model(input_len, feature_count, conv_layers, dense_size, learning_rate, triplet_margin, triplet_distance_metric, normalization) super().__init__(self.model, name, save_dir=save_dir) """ A simple siamese model with convolutional layers (split into separate IQ layers), followed by a dense layer. Triplet loss is used. """ class AETripletSplitConvModel(Model): def build_model(self, input_len, feature_count, conv_layers, dense_size, learning_rate, triplet_margin, triplet_distance_metric, normalization): """ Build the siamese model. Args: input_len (int): Length of the input vector. feature_count (int): Number of features in the input vector. conv_layers (list): List of tuples (filters, kernel_size) for each convolutional layer. dense_size (int): Size of the dense layer. learning_rate (float): Learning rate for the optimizer. triplet_margin (float): Margin for the triplet loss. triplet_distance_metric (str): Distance metric to use for the triplet loss. One of 'L2', 'squared-L2', 'angular', or a callable. normalization (str): Normalization to use for the embeddings. One of 'L1', 'L2', None. Returns: tf.keras.Model: The siamese model. """ input = layers.Input(shape=(input_len, feature_count), name="input") input_norm = layers.BatchNormalization()(input) input_padded = layers.ZeroPadding1D(padding=2)(input_norm) input_i, input_q = tf.split(input_padded, feature_count, axis=2) conv_shapes = [] conv_i = [input_i] conv_q = [input_q] for i, (filters, kernel_size) in enumerate(conv_layers): conv_shapes.append(conv_i[-1].get_shape().as_list()[1]) conv_i.append(layers.Conv1D(filters, kernel_size, activation='relu', name="conv_i_{}".format(i))(conv_i[-1])) conv_i.append(layers.MaxPooling1D()(conv_i[-1])) conv_q.append(layers.Conv1D(filters, kernel_size, activation='relu', name="conv_q_{}".format(i))(conv_q[-1])) conv_q.append(layers.MaxPooling1D()(conv_q[-1])) conv_out = layers.Concatenate(axis=2, name="conv_out")([conv_i[-1], conv_q[-1]]) flat = layers.Flatten()(conv_out) dense = layers.Dense(dense_size, activation=None, name="dense")(flat) norm = None if normalization == 'l1': norm = layers.Lambda(lambda x: tf.linalg.normalize(x, ord=1, axis=1)[0], name="embedding")(dense) elif normalization == 'l2': norm = layers.Lambda(lambda x: tf.math.l2_normalize(x, axis=1), name="embedding")(dense) elif normalization is None: norm = layers.Identity(name="embedding")(dense) # Build the decoder decoder_dense = layers.Dense(flat.get_shape().as_list()[1], activation='relu', name="decoder_dense")(norm) deconv_in = layers.Reshape(conv_out.get_shape().as_list()[1:])(decoder_dense) deconv_i, deconv_q = tf.split(deconv_in, feature_count, axis=2) deconv_i = [deconv_i] deconv_q = [deconv_q] for i, (filters, kernel_size) in enumerate(reversed(conv_layers)): upscaling_shape = deconv_i[-1].get_shape().as_list() upscaling_shape[1] *= 2 #deconv_i.append(layers.UpSampling1D()(deconv_i[-1])) #deconv_i.append(layers.Conv1DTranspose(filters, kernel_size, activation='relu', name="deconv_i_{}".format(i))(deconv_i[-1])) deconv_i.append(UpscalingLayer(upscaling_shape)(deconv_i[-1])) deconv_i.append(layers.Conv1D(filters, kernel_size, activation='relu', name="deconv_i_{}".format(i))(deconv_i[-1])) #deconv_q.append(layers.UpSampling1D()(deconv_q[-1])) #deconv_q.append(layers.Conv1DTranspose(filters, kernel_size, activation='relu', name="deconv_{}".format(i))(deconv_q[-1])) deconv_q.append(UpscalingLayer(upscaling_shape)(deconv_q[-1])) deconv_q.append(layers.Conv1D(filters, kernel_size, activation='relu', name="deconv_q_{}".format(i))(deconv_q[-1])) conv_shape = list(reversed(conv_shapes))[i] if deconv_i[-1].get_shape().as_list()[1] != conv_shape: deconv_i.append(layers.ZeroPadding1D(padding=(0, conv_shape - deconv_i[-1].get_shape().as_list()[1]))(deconv_i[-1])) deconv_q.append(layers.ZeroPadding1D(padding=(0, conv_shape - deconv_q[-1].get_shape().as_list()[1]))(deconv_q[-1])) output_i = layers.Conv1DTranspose(1, 5, activation='linear', padding='valid', name="output_i")(deconv_i[-1]) output_q = layers.Conv1DTranspose(1, 5, activation='linear', padding='valid', name="output_q")(deconv_q[-1]) output_concatenate = layers.Concatenate(axis=2, name="output_concatenate")([output_i, output_q]) cropping = (output_concatenate.get_shape().as_list()[1] - input_len) // 2 output = layers.Cropping1D(cropping=cropping, name="output")(output_concatenate) model = models.Model(inputs=[input], outputs=[norm, output], name="model") #model.add_loss(tfa.losses.TripletSemiHardLoss( # margin=triplet_margin, # distance_metric=triplet_distance_metric, #)(norm), name="embedding_loss") mse = tf.keras.losses.MeanSquaredError()(input, output) model.add_loss(mse) model.add_metric(mse, name="reconstruction_loss") losses = { "embedding": tfa.losses.TripletSemiHardLoss( margin=triplet_margin, distance_metric=triplet_distance_metric, ), # MSE between input and output #"output": tf.keras.losses.MeanSquaredError()(input, output), } loss_weights = { "embedding": 1.0, #"output": 1.0, } model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate), loss=losses, loss_weights=loss_weights, #loss=None, metrics=[ ] ) return model def __init__(self, name, input_len, feature_count, conv_layers, dense_size, learning_rate, triplet_margin=1.0, triplet_distance_metric='L2', normalization='L2', accuracy_threshold=0.5, save_dir=None): """ Initialize the siamese model. Args: name (str): Name of the model. input_len (int): Length of the input vector. feature_count (int): Number of features in the input vector. conv_layers (list): List of tuples (filters, kernel_size) for each convolutional layer. dense_size (int): Size of the dense layer. learning_rate (float): Learning rate for the optimizer. triplet_margin (float): Margin for the triplet loss. triplet_distance_metric (str): Distance metric to use for the triplet loss. One of 'L2', 'squared-L2', 'angular', or a callable. normalization (str): Normalization to use for the embeddings. One of 'L1', 'L2', None. accuracy_threshold (float): Accuracy threshold for the model's SiameseAccuracy metric. save_dir (str): Directory to save the model to. """ self.accuracy_threshold = accuracy_threshold if feature_count != 2: raise ValueError("This model currently only supports 2D input vectors (IQ samples).") if normalization is not None: normalization = normalization.lower() if normalization not in ['l1', 'l2']: raise ValueError("Invalid normalization: {}".format(normalization)) self.model = self.build_model(input_len, feature_count, conv_layers, dense_size, learning_rate, triplet_margin, triplet_distance_metric, normalization) super().__init__(self.model, name, save_dir=save_dir) 这是模型的脚本
07-04
这段代码定义了多个基于卷积神经网络(CNN)的**孪生网络(Siamese Network)**和**自动编码器+三元组损失(Autoencoder + Triplet Loss)**模型,用于处理卫星指纹识别任务。这些模型利用 IQ 数据(In-phase and Quadrature)进行设备身份识别。 --- ## 代码结构与功能详解: ### ✅ `UpscalingLayer` 一个自定义层,用于对输入进行上采样(放大),通过重复操作实现。它替代了常见的 `UpSampling1D` 或 `Conv1DTranspose`。 ```python class UpscalingLayer(layers.Layer): def __init__(self, out_shape): super(UpscalingLayer, self).__init__() self.out_shape = out_shape def call(self, inputs, **kwargs): output = tf.repeat(inputs, 2, axis=1) return tf.ensure_shape(output, self.out_shape) def get_config(self): config = super().get_config().copy() config.update({'out_shape': self.out_shape}) return config ``` > 用途:用于解码器部分恢复原始信号长度。 --- ### ✅ `SimpleSiameseConvModel` 基础孪生网络模型,使用共享权重的 CNN 提取特征,并通过差值比较判断是否为同一设备。 #### 结构: - 输入 → BatchNorm → ZeroPadding → Conv1D × N → Flatten → Dense → Subtract → Abs → Sigmoid #### 损失函数: - 使用二分类交叉熵(Binary Crossentropy) --- ### ✅ `SimpleSiameseSplitConvModel` 与上一模型类似,但将 IQ 信号拆分为两个通道分别处理,最后合并。 #### 特点: - 分别处理 In-phase 和 Quadrature 信号。 - 更适合处理通信信号中的相位信息。 --- ### ✅ `SimpleTripletSplitConvModel` 使用**三元组损失(Triplet Loss)**训练嵌入空间,使得相同设备的样本更接近,不同设备更远。 #### 损失函数: - `tfa.losses.TripletSemiHardLoss`:半难例挖掘的三元组损失。 #### 嵌入归一化: - 支持 L1、L2 归一化或无归一化。 #### 自定义指标: - 包括准确率、TPR、FPR、F-score 等评估指标。 --- ### ✅ `AETripletSplitConvModel` 结合了自动编码器(AE)和三元组损失,同时优化重构能力和嵌入空间距离。 #### 编码器部分: - 与前几个模型一致,提取嵌入向量(Embedding)。 #### 解码器部分: - 将嵌入向量还原回原始信号,使用 MSE 衡量重构误差。 #### 损失函数: - 主要损失:Triplet Semi-Hard Loss - 辅助损失:MSE(重建损失) #### 输出: - 两个输出头:`embedding` 和 `output`(重建信号) --- ## 示例总结图示: ``` [Input] ───┐ ├─ Encoder CNNs ─ Dense ─ Embedding (Normalized) ─┐ [Input] ───┘ ├─ Concatenate │ Decoder CNNs ─ Deconv1D ─ Output ``` --- ## 关键技术点说明: | 技术 | 说明 | |------|------| | **IQ Splitting** | 将信号分为实部和虚部处理,保留信号完整性 | | **Triplet Loss** | 强制学习类内相似、类间差异的嵌入空间 | | **Batch Norm & Padding** | 提升训练稳定性和梯度传播 | | **Reconstruction Loss (MSE)** | AE 部分确保信号可逆性 | | **Custom Metrics** | TPR/FPR/F-score 等帮助分析模型性能 | --- ## 相关问题建议: ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值