Keras 二分类不同数据集结果相同的问题

4

我有两个预测标签的值,-1或1。使用 LSTMDense 进行学习效果良好,但对于不同的预测数据集,预测结果总是相同的,将层更改为 Dense 不会改变预测结果,也许我做错了什么。

以下是代码:

// set up data arrays
float[,,] training_data = new float[training.Count(), 12, 200];
float[,,] testing_data = new float[testing.Count(), 12, 200];
float[,,] predict_data = new float[1, 12, 200];

IList<float> training_labels = new List<float>();
IList<float> testing_labels = new List<float>();

// Load Data and add to arrays
...
...

/////////////////////////
NDarray train_y = np.array(training_labels.ToArray());
NDarray train_x = np.array(training_data);

NDarray test_y = np.array(testing_labels.ToArray());
NDarray test_x = np.array(testing_data);

NDarray predict_x = np.array(predict_data);

train_y = Util.ToCategorical(train_y, 2);
test_y = Util.ToCategorical(test_y, 2);

//Build functional model
var model = new Sequential();

model.Add(new Input(shape: new Keras.Shape(12, 200)));
model.Add(new BatchNormalization());

model.Add(new LSTM(128, activation: "tanh", recurrent_activation: "sigmoid", return_sequences: false));            
model.Add(new Dropout(0.2));
model.Add(new Dense(32, activation: "relu"));            
model.Add(new Dense(2, activation: "softmax"));

model.Compile(optimizer: new SGD(), loss: "binary_crossentropy", metrics: new string[] { "accuracy" });
model.Summary();

var history = model.Fit(train_x, train_y, batch_size: 1, epochs: 1, verbose: 1, validation_data: new NDarray[] { test_x, test_y });

var score = model.Evaluate(test_x, test_y, verbose: 2);
Console.WriteLine($"Test loss: {score[0]}");
Console.WriteLine($"Test accuracy: {score[1]}");

NDarray predicted=model.Predict(predict_x, verbose: 2);
                    
Console.WriteLine($"Prediction: {predicted[0][0]*100}");
Console.WriteLine($"Prediction: {predicted[0][1]*100}");  

这是输出结果

    483/483 [==============================] 
    - 9s 6ms/step - loss: 0.1989 - accuracy: 0.9633 - val_loss: 0.0416 - val_accuracy: 1.0000
      4/4 - 0s - loss: 0.0416 - accuracy: 1.0000
    Test loss: 0.04155446216464043
    Test accuracy: 1
    1/1 - 0s

    Prediction: 0.0010418787496746518
    Prediction: 99.99896287918091

在使用ML.Net时,相同的预测数据会产生不同的结果,但准确度仅为0.6,因此我需要深度学习。

1个回答

1

我没有设置来复制你的代码。但是我发现一个小问题,你可能需要考虑一下(不确定是否导致了麻烦)。根据你上面的代码设置,我认为你在训练时使用了错误的loss函数。就像你所设定的那样,

Util.ToCategorical(train_y, 2);
model.Add(new Dense(2, activation: "softmax"));

您的损失函数应该是'categorical_crossentropy',而不是'binary_crossentropy'。因为您将标签(-1,1)转换为独热编码向量,并在最后一层设置了softmax激活。但是,正如您所说,您的标签是-1和1;因此,如果您将问题视为二元分类问题,则设置应该如下所示:
# Util.ToCategorical(train_y, 2); # no transformation 
model.Add(new Dense(1, activation: "sigmoid"));
model.compile(..., loss: "binary_crossentropy" )

参考资料。

更新

这里我将提供一些工作演示代码以便更好地理解。但在此之前,有一个小提示。假设我们有一个训练数据集和标签从<0或负值开始,例如[-2, -1, 0, 1]。为了将这个整数值转换为一个独热编码向量,我们可以选择tf.keras.utils.to_categoricalpd.get_dummies函数。但这两种方法之间的一个小差异是,在tf..to_categorical中,我们的整数标签必须从0开始;而在pd.get_dummies中则不是这种情况,请查看我的其他答案。简而言之,

np.random.randint(-1, 1, size=(80))
array([-1, -1,  0,  0,  0 .. ]

pd.get_dummies(a).astype('float32').values[:5] 
array([[1., 0.],
       [1., 0.],
       [0., 1.],
       [0., 1.],
       [0., 1.]], dtype=float32)

tf.keras.utils.to_categorical(a+1, num_classes = 2)[:5]
array([[1., 0.],
       [1., 0.],
       [0., 1.],
       [0., 1.],
       [0., 1.]], dtype=float32)

好的,现在我提供一些工作演示代码。

img = tf.random.normal([80, 32], 0, 1, tf.float32)
tar = pd.get_dummies(np.random.randint(-1, 1,  # mine: [-1, 1) - yours: [-1, 1]
                                       size=80)).astype('float32').values 

model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(10, input_dim = 32, 
                       kernel_initializer ='normal', 
                       activation= 'relu'))
model.add(tf.keras.layers.Dense(2, activation='softmax'))

model.compile(loss='categorical_crossentropy', 
              optimizer='adam', metrics=['accuracy'])
model.fit(img, tar, epochs=10, verbose=2)

Epoch 1/10
3/3 - 0s - loss: 0.7610 - accuracy: 0.4375
Epoch 2/10
3/3 - 0s - loss: 0.7425 - accuracy: 0.4375
....
Epoch 8/10
3/3 - 0s - loss: 0.6694 - accuracy: 0.5125
Epoch 9/10
3/3 - 0s - loss: 0.6601 - accuracy: 0.5750
Epoch 10/10
3/3 - 0s - loss: 0.6511 - accuracy: 0.5750

Inference

loss, acc = model.evaluate(img, tar); print(loss, acc)
pred = model.predict(img); print(pred[:5])

3ms/step - loss: 0.6167 - accuracy: 0.7250
0.6166597604751587 0.7250000238418579

# probabilities of the predicted labels -1 and 0
[[0.35116166 0.64883834]
 [0.5542663  0.4457338 ]
 [0.28023133 0.71976864]
 [0.5024315  0.49756846]
 [0.41029742 0.5897026 ]]

现在,如果我们执行以下操作:
print(pred[0])
pred[0].argmax(-1) # expect: -1, 0 as our label 

[0.35116166 0.64883834]
1

它为目标标签-10分别给出0.35x0.64x。但是,当我们对概率预测的标签执行.argmax时,它返回零索引的最高值;(这是让训练标签从零索引开始的原因,所以我认为在您的情况下,将[-1,1]转换为[0,1]更好)。好的,最后,正如您提到的,您想要预测的标签和相应的置信度分数;为此,我们可以使用tf.math.top_kk = num_of_class
top_k_values, top_k_indices = tf.math.top_k(pred, k=2)
for values, indices in zip(top_k_values, top_k_indices):
    print(
        'For class {}, model confidence {:.2f}%'
        .format(indices.numpy()[0]-1, values.numpy()[0]*100)
        )
    
    print(
        'For class {}, model confidence {:.2f}%'
        .format(indices.numpy()[1]-1, values.numpy()[1]*100)
        )
    
    '''
    Note: above we substract -1 to match with 
          the target label (-1, 0)

    And it would not necessary if we initially -
    transform our label from (-1, 0) to (0, 1), i.e. start from zero 
    '''
    print()
    break # remove for full results 

For class 0, model confidence 64.88%
For class -1, model confidence 35.12%

验证分数顺序
# pick first samples: input and label
model(img)[0].numpy(), tar[0]

(array([0.35116166, 0.64883834], dtype=float32),
 array([0., 1.], dtype=float32))

Here, 
0: for -1
1: for 0

# Again, better to transform (-1, 0) to (0, 1) at initial.

谢谢,我按照您说的做了,现在我得到了不同的预测结果,但是只有一个值出现在predicted[0][0],predicted[0][1]为空。使用一组数据,我得到了20.3的结果,而使用另一组数据,我得到了12.2的结果,我该如何将这些数据解释为我的-1和1的结果?此外,即使不更改数据,结果也会发生变化。谢谢! - Mario
对于我的第一个评论,我也尝试了predicted[0]和predicted[1],第一个值相同,第二个值为空。 - Mario
我了解了。如果需要突破我的编码部分,请告诉我。我希望我能用C#来回答你。但是,基础应该是相同的,这是确定的。您可以在colab上轻松运行我的代码并验证每个方面。 - Innat
我不明白[:5]是什么意思?它出现在这行代码中:pd.get_dummies(a).astype('float32').values[:5],但在C#中不存在。 - Mario
1
[:5] 被称为 Python 中的数组切片。这对你来说不是很重要。我用它来进行检查目的。例如,当我使用 print(pred[:5]) 时,它意味着打印出前 5 个元素或者前 5 个样本的概率分数 - Innat
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接