多序列对齐数据训练Variational Autoencoder报错求助及训练方法咨询
问题排查与解决方案
错误原因解析
报错Input 0 of layer "encoder" is incompatible with the layer: expected shape=(None, 28, 28, 1), found shape=(None, 13, 1)本质是输入数据形状与模型预期不匹配,具体问题有以下几点:
- 模型适配错误:你用的是针对MNIST手写数字(28x28的2D图像)的VAE结构,而蛋白质多序列对齐是1D序列数据,两者形状完全不同。
- One-hot编码逻辑错误:直接将整个DataFrame传入
OneHotEncoder,没有对蛋白质序列做字符拆分处理,导致编码结果形状混乱。 - 训练数据引用错误:代码中出现了
train、test、mnist_digits这些未定义的变量,完全没有用到自己的蛋白质序列数据。 - 损失计算逻辑错误:损失函数里直接用原始DataFrame
datax计算,而非模型输入的张量数据。
修正步骤与完整代码
步骤1:正确处理蛋白质序列的One-hot编码
需要将每个序列拆分为单个氨基酸字符,再对每个位置的字符做One-hot编码,最终得到形状为(样本数, 序列长度, 氨基酸种类)的张量。
步骤2:重构VAE模型适配1D序列数据
放弃2D卷积层,改用1D卷积或全连接层构建编码器和解码器,匹配序列数据的形状。
步骤3:修正训练逻辑
替换未定义的MNIST数据,用自己的编码后数据训练,同时修正损失计算的张量引用。
以下是完整可运行的修正代码:
from sklearn.preprocessing import OneHotEncoder import numpy as np import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import pandas as pd # 原始数据 datax = pd.DataFrame({'sequence':['ACKIENIKYKGKEVESKLGSQLIDIFNDLDRAKEEYDKLSSPEFIAKFGDWINDEVERNVNEDGEPLLIQDVRQDSSKHYFFILKNGERFDLLTR','---------TGKEVQSSLFDQILELVKDPQLAQDAYAQIRTPEFIEKFGDWINDPYESKLDDNGEPILIE-------------------------','-------PNTGKEVQSSLFDQIIELIKDPKLAQDAYEQVRTPEFIEKFGDWINDPYAAKLDDNGEPILVERD-----------------------','-------PNQGKEVESKLYNDLVNLTGSEKAADEAYEQVHHPNFLRWFGDWINNKVSNVVDENGEPLIV--------------------------','ACRIIISLNKGKEVKSKLFDSLLDLTQSEAKAEEAYKKVFSEEFINWFGDWINTPASKVVDENGEPLMVYRFTQEE-------------------','-------PNKGKEVRSKLYDDLLELTGDDNAALEAYKKVHHPNFLRWFGDWINNKVSKVVDENGEPLIVY-------------------------','-------PNKGKEVESKLYNDLVNLTGSEKAADEAYTKVHHPNFLRWFGDWMTNPSSKVVDENGEPKIV--------------------------','-------PNKGQRVDSILYNDLLSLTGNEKSADKAYTKAHTSSFLNWFGDWINTNIQDNVDQNGEPKV---------------------------','-----------------------------NLTSEQYKLVRTPEFIAWFGNWMDDPNASVIDENGEPLVCFH-GTDNSFHIF--------------','---------------------------------KQWVQVRTPAFIEWFGDWMNDPASKGVDENGEPLVVYHGTENKFTQYDFD------------','-------------------------------TPKQYKLVRTLEFKAWFGDWENDPASKVVDENGEPLVVYH--GTDSKHNVFSYEQ---------','--------------GSSLYEQYVVIIDSSNLTTEQYKLVRTPEFKKWFGDWENNPSEAVVDENGEPLVVYH------------------------','------------------------------LTPEQYKLVRTPEFKAWFGDWENNPSSKVVDDNGEPMVVY---HGSRKKAFTEFK----------']}) # 步骤1:正确处理One-hot编码 # 将每个序列拆分为单个字符的列表 sequences = datax['sequence'].apply(list).tolist() # 转换为2D数组(样本数×序列长度) seq_array = np.array(sequences) # 初始化OneHotEncoder,处理所有字符(包括-和G等) encoder = OneHotEncoder(sparse=False, handle_unknown='ignore') # 对每个位置的字符编码,最终形状为(样本数, 序列长度×特征数),再reshape为(样本数, 序列长度, 特征数) onehot = encoder.fit_transform(seq_array.reshape(-1, 1)).reshape(seq_array.shape[0], seq_array.shape[1], -1) # 获取序列长度和特征数(氨基酸种类+空位) seq_len = onehot.shape[1] n_features = onehot.shape[2] # 步骤2:定义适配序列的VAE模型 class Sampling(layers.Layer): def call(self, inputs): z_mean, z_log_var = inputs batch = tf.shape(z_mean)[0] dim = tf.shape(z_mean)[1] epsilon = tf.keras.backend.random_normal(shape=(batch, dim)) return z_mean + tf.exp(0.5 * z_log_var) * epsilon latent_dim = 10 # 编码器:用1D卷积处理序列 encoder_inputs = keras.Input(shape=(seq_len, n_features)) x = layers.Conv1D(32, 3, activation="relu", strides=1, padding="same")(encoder_inputs) x = layers.Conv1D(64, 3, activation="relu", strides=1, padding="same")(x) x = layers.Flatten()(x) x = layers.Dense(64, activation="relu")(x) z_mean = layers.Dense(latent_dim, name="z_mean")(x) z_log_var = layers.Dense(latent_dim, name="z_log_var")(x) z = Sampling()([z_mean, z_log_var]) encoder = keras.Model(encoder_inputs, [z_mean, z_log_var, z], name="encoder") encoder.summary() # 解码器:从隐空间还原序列 latent_inputs = keras.Input(shape=(latent_dim,)) x = layers.Dense(seq_len * 32, activation="relu")(latent_inputs) x = layers.Reshape((seq_len, 32))(x) x = layers.Conv1DTranspose(64, 3, activation="relu", strides=1, padding="same")(x) x = layers.Conv1DTranspose(32, 3, activation="relu", strides=1, padding="same")(x) decoder_outputs = layers.Conv1DTranspose(n_features, 3, activation="sigmoid", padding="same")(x) decoder = keras.Model(latent_inputs, decoder_outputs, name="decoder") decoder.summary() # 定义VAE训练逻辑 class VAE(keras.Model): def __init__(self, encoder, decoder, **kwargs): super(VAE, self).__init__(**kwargs) self.encoder = encoder self.decoder = decoder self.total_loss_tracker = keras.metrics.Mean(name="total_loss") self.reconstruction_loss_tracker = keras.metrics.Mean(name="reconstruction_loss") self.kl_loss_tracker = keras.metrics.Mean(name="kl_loss") @property def metrics(self): return [self.total_loss_tracker, self.reconstruction_loss_tracker, self.kl_loss_tracker] def train_step(self, data): with tf.GradientTape() as tape: z_mean, z_log_var, z = self.encoder(data) reconstruction = self.decoder(z) # 修正损失计算:用输入的data张量,而非原始DataFrame reconstruction_loss = tf.reduce_mean( tf.reduce_sum(keras.losses.binary_crossentropy(data, reconstruction), axis=(1, 2)) ) kl_loss = -0.5 * (1 + z_log_var - tf.square(z_mean) - tf.exp(z_log_var)) kl_loss = tf.reduce_mean(tf.reduce_sum(kl_loss, axis=1)) total_loss = reconstruction_loss + kl_loss grads = tape.gradient(total_loss, self.trainable_weights) self.optimizer.apply_gradients(zip(grads, self.trainable_weights)) self.total_loss_tracker.update_state(total_loss) self.reconstruction_loss_tracker.update_state(reconstruction_loss) self.kl_loss_tracker.update_state(kl_loss) return { "loss": self.total_loss_tracker.result(), "reconstruction_loss": self.reconstruction_loss_tracker.result(), "kl_loss": self.kl_loss_tracker.result(), } # 步骤3:训练模型 vae = VAE(encoder, decoder) vae.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001)) # 用onehot编码后的数据训练 vae.fit(onehot, epochs=50, batch_size=4)
关键改动说明
- 编码部分:将每个序列拆分为单个字符,确保One-hot编码后是
(样本数, 序列长度, 特征数)的3D张量,符合序列数据的结构。 - 模型结构:把2D卷积替换为1D卷积层,适配1D序列的特征提取和还原。
- 训练逻辑:移除了未定义的MNIST相关变量,用自己的蛋白质序列编码数据训练,同时修正了损失计算中的张量引用错误。
内容的提问来源于stack exchange,提问作者Ahmet
相关产品推荐
相关产品推荐

