首页 > 解决方案 > 为什么我在循环中使用 Keras 顺序神经网络得到不同的预测?

问题描述

我遇到了 keras model.fit() 和 sklearn model.fit() 函数之间的奇怪区别。当在循环内调用 model.fit() 时,我使用 Keras 顺序模型得到不一致的预测。使用 sklearn 模型时并非如此。请参阅示例代码以重现该现象。

from numpy.random import seed
seed(1337)
import tensorflow as tf
tf.random.set_seed(1337)

from sklearn.linear_model import LogisticRegression

from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.layers import InputLayer

from sklearn.datasets import make_blobs
from sklearn.preprocessing import MinMaxScaler
import numpy as np

def get_sequential_dnn(NUM_COLS, NUM_ROWS):
   # code for model

if __name__ == "__main__":

    input_size = 10
    X, y = make_blobs(n_samples=100, centers=2, n_features=input_size,
                      random_state=1
                      )

    scalar = MinMaxScaler()
    scalar.fit(X)
    X = scalar.transform(X)

    model = get_sequential_dnn(X.shape[1], X.shape[0])
    # print(model.summary())
    # model = LogisticRegression()

    for i in range(2):
        model.fit(X, y, epochs=100, verbose=0, shuffle=False)
        # model.fit(X, y)
    
        Xnew, _ = make_blobs(n_samples=3, centers=2, n_features=10, random_state=1)
        Xnew = scalar.transform(Xnew)

        # make a prediction  
        # ynew = model.predict_proba(Xnew)[:, 1]
        ynew = model.predict_proba(Xnew)
        ynew = np.array(ynew)
    
        # show the inputs and predicted outputs
        print('--------------')
        for i in range(len(Xnew)):
            print("X=%s \n Predicted=%s" % (Xnew[i], ynew[i]))

这个的输出是

--------------
X=[0.32799209 0.32682211 0.62699485 0.89987274 0.59894281 0.94662653
 0.77125788 0.73345369 0.2153754  0.35317172] 
 Predicted=[0.9931685]
X=[0.60876924 0.33208319 0.24770841 0.11435312 0.66211608 0.17361879
 0.12891829 0.25729677 0.69975833 0.73165292] 
 Predicted=[0.35249507]
X=[0.65154993 0.26153846 0.2416324  0.11793901 0.7047334  0.17706289
 0.07761879 0.45189967 0.8481064  0.85092378] 
 Predicted=[0.35249507]
--------------
X=[0.32799209 0.32682211 0.62699485 0.89987274 0.59894281 0.94662653
 0.77125788 0.73345369 0.2153754  0.35317172] 
 Predicted=[1.]
X=[0.60876924 0.33208319 0.24770841 0.11435312 0.66211608 0.17361879
 0.12891829 0.25729677 0.69975833 0.73165292] 
 Predicted=[0.17942095]
X=[0.65154993 0.26153846 0.2416324  0.11793901 0.7047334  0.17706289
 0.07761879 0.45189967 0.8481064  0.85092378] 
 Predicted=[0.17942095]

如果我使用逻辑回归(取消注释注释行),则预测是一致的:

--------------
X=[0.32799209 0.32682211 0.62699485 0.89987274 0.59894281 0.94662653
 0.77125788 0.73345369 0.2153754  0.35317172] 
 Predicted=0.929209043999009
X=[0.60876924 0.33208319 0.24770841 0.11435312 0.66211608 0.17361879
 0.12891829 0.25729677 0.69975833 0.73165292] 
 Predicted=0.04643513037543502
X=[0.65154993 0.26153846 0.2416324  0.11793901 0.7047334  0.17706289
 0.07761879 0.45189967 0.8481064  0.85092378] 
 Predicted=0.038716408758471876
--------------
X=[0.32799209 0.32682211 0.62699485 0.89987274 0.59894281 0.94662653
 0.77125788 0.73345369 0.2153754  0.35317172] 
 Predicted=0.929209043999009
X=[0.60876924 0.33208319 0.24770841 0.11435312 0.66211608 0.17361879
 0.12891829 0.25729677 0.69975833 0.73165292] 
 Predicted=0.04643513037543502
X=[0.65154993 0.26153846 0.2416324  0.11793901 0.7047334  0.17706289
 0.07761879 0.45189967 0.8481064  0.85092378] 
 Predicted=0.038716408758471876

我知道对此的明显解决方案是在循环之前拟合模型,Keras 模型如何将数据拟合到标签可能存在很强的随机性,但在某些情况下,您需要有一个循环来获得预测分数. 例如,如果您要执行 10 倍交叉验证以获得训练数据的 AUC、敏感性、特异性值。在这些情况下,这种随机性是不可接受的。

是什么导致了这种不一致,解决方法是什么?

标签: pythontensorflowmachine-learningkerasscikit-learn

解决方案


您尝试使用 keras 获得可重现结果的方式存在几个问题。

  1. 您在已经拟合的模型(when )上调用(when fit)。所以优化器在这两种情况下都会看到不同的初始权重集,所以你最终会得到两个不同的模型。解决方案:每次都获得一个新模型。sklearn 的情况并非如此,它每次调用 fit 时都会从新的初始化权重开始。i==1i==0
  2. model.fit内部可能会使用当前阶段的随机数生成器。fit您将其播种在循环之外,因此第二次调用时状态会有所不同。解决方案:在循环内播种。

有问题的示例代码

# Issue 2 here
tf.random.set_seed(1337)

def get_model():
  model = Sequential()
  model.add(Dense(4, input_dim=8, activation='relu'))
  model.add(Dense(1, activation='sigmoid'))
  model.compile(loss='binary_crossentropy', optimizer='adam')
  return model

X = np.random.randn(10,8)
y = np.random.randn(10,1)

# Issue 1 here
model = get_model()

results = []
for i in range(10):
  model.fit(X, y, epochs=5, verbose=0, shuffle=False)
  results.append(np.sum(model.predict(X)))

assert np.all(np.isclose(results, results[0]))

如您所见,断言失败

更正的代码

results = []
for i in range(10):
  tf.random.set_seed(1337)
  model = get_model()
  model.fit(X, y, epochs=5, verbose=0, shuffle=False)
  results.append(np.sum(model.predict(X)))

assert np.all(np.isclose(results, results[0]))

推荐阅读