我正在尝试使用 TensorFlow 构建 CNN 来解决以下类型的回归任务。这个想法是有一些未知的函数,我们有兴趣知道该函数的一些参数。作为特征,我们有一个 x 值向量和一个向量 y,其中包含相应 x 值处的函数值。相应的标签就是函数参数。 CNN 将获取 x 和 y 值并预测参数值。但是,我的 CNN 模型无法达到足够的准确度。
更具体地说,请考虑以下简单示例。所有函数都是简单的线性函数 y = kx,任务是预测斜率 k。为了获取数据,我们可以使用以下Python代码:
N_data = 50000 # number of data points
X = [] # features (x and y values)
y = [] # labels (slopes)
for k in range(N_data):
# Randomly choose the x values:
x_min = 200*random.random()-100
xs = np.linspace(x_min, x_min + 10)
# Randomly choose the slope:
k = 200*random.random()-100
# Calculate the function values:
ys = k*xs
# Store the data:
X.append(xs.tolist() + ys.tolist())
y.append(k)
X = np.array(X)
y = np.array(y)
tf.random.set_seed(41)
# Split the data into training, validation and test sets:
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.2, random_state=41)
# Split the temporary set into validation and test sets:
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=41)
这是我的 CNN 模型:
model = models.Sequential()
# Data normalization layer
model.add(layers.InputLayer(input_shape=(X[0].shape[0], 1)))
model.add(layers.BatchNormalization())
# Convolutional block 1:
model.add(layers.Conv1D(32, 3, activation='relu'))
model.add(layers.AveragePooling1D(2))
# Convolutional block 2:
model.add(layers.Conv1D(64, 3, activation='relu'))
model.add(layers.AveragePooling1D(2))
# Convolutional block 3:
model.add(layers.Conv1D(128, 3, activation='relu'))
model.add(layers.MaxPooling1D(2))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(1))
以下代码适合该模型:
model.compile(optimizer=Adam(learning_rate=0.01),
loss='mean_squared_error',
metrics=['mae'])
history = model.fit(X_train, y_train, epochs=50, batch_size = 16,
validation_data=(X_val, y_val))
我不确定如何解释验证误差始终小于训练误差。对于测试集(总数据的 10%,即 5000 个样本),平均绝对误差为 3.37,对于这个简单的问题和如此大量的数据点来说,这个误差相当高。我可以做些什么来改进模型?我不确定问题是否出在数据点数量、CNN 架构或输入数据的格式化方式上。任何建议将不胜感激。
考虑输入为 [xi,yi] 和输出 [ki] 其中 xi*ki = yi。
用于标准输入/激活。
A(zi - (xi*wxj + yi*wyj))
你不能让这个激活函数的结果是 k。
不过你可以对 k 进行分类。这个想法是对 k 进行足够的分类,使得该值可以是标量。只考虑正 x、y 和 k。
sigmoid( - xi*10 + yi )
因此当 k > 10 时交叉。
有了这个想法,您可以构建足够的输出来将 k 分类为一系列值。
如果我们使用两个点,则不必进行除法。
(x0, y0, x0 + 1, y1)
现在我们可以看到斜率是 x[3] - x[1]。这是我们应该从您的示例中看到的,因为您始终使用相同的 linspec。我怀疑从你的池化/卷积中你已经以某种方式消除了这条训练路线的可能性。
我输入了:
X.append( np.array((xs[i], xs[i+1], ys[i], ys[i+1])) )
我改变了模型:
model = models.Sequential()
model.add(layers.InputLayer(input_shape=(X[0].shape[0], 1)))
nn = 32
nl = 4
act = 'relu'
model.add(layers.Flatten())
for i in range(nl):
model.add(layers.Dense(nn, activation=act))
model.add(layers.Dense(1, activation='linear'))
model.add(layers.Dense(1, activation='linear'))
模型和数据学习预测 k。