在过去几个小时里,我一直在处理我的Keras/TensorFlow代码,试图通过为每个使用的随机生成器设定种子来获得可重复的结果。现在我的解决方案确实有效,但奇怪的是,只有当我使用单线程运行代码时才有效,使用的代码如下:
from keras import backend as Kconfig = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)sess = tf.Session(graph=tf.get_default_graph(), config=config)K.set_session(sess)
我无法解释这种行为,因此我想了解你们对此的看法。为了进一步理解,我将在下面发布我的完整代码:
import tensorflow as tfrandom.seed(seed_value)np.random.seed(seed_value)tf.set_random_seed(seed_value)from keras import backend as Kconfig = tf.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1)sess = tf.Session(graph=tf.get_default_graph(), config=config)K.set_session(sess)""""""import pandas as pd # 数据处理,CSV文件I/O(例如pd.read_csv)import matplotlib.pyplot as pltfrom sklearn import preprocessing, model_selectionfrom sklearn.decomposition import PCAfrom keras.models import load_modelfrom keras.utils import np_utilsfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.preprocessing import LabelEncoder, StandardScalerfrom keras.utils.np_utils import to_categoricalfrom sklearn.utils import shufflefrom sklearn.metrics import confusion_matrixfrom TimingCallback import TimeHistorydef train(): files = ['RAW_combined_shuffled.csv'] # select = ['html_tag_script', 'js_max_value_assignments', 'url_found_scripttags', 'url_param_count_"', 'label'] # read_files = (pd.read_csv(f, usecols=select) for f in files) # 只读取选定的特征 read_files = (pd.read_csv(f) for f in files) # 读取所有特征 data = pd.concat(read_files, ignore_index=True) data = data.drop(['data'], axis=1) # 删除单个列 // 通过KNIME完成 # data = shuffle(data) # 随机化数据集的顺序 //通过KNIME完成 i = 100 data_to_predict = data[:i].reset_index(drop=True) # 分割用于测试模型的数据(从开始到i) real_label = data_to_predict.label real_label = np.array(real_label) prediction = np.array(data_to_predict.drop(['label'], axis=1)) data = data[i:].reset_index(drop=True) # 分割用于训练和测试的数据(从i到结束) X = data.drop(['label'], axis=1) # X 是除输出标签外的所有列 X = np.array(X) Y = data['label'] # Y 是包含输出标签的列 # 将名称种类转换为数值 encoder = LabelEncoder() encoder.fit(Y) Y = encoder.transform(Y) Y = np_utils.to_categorical(Y) # 我们有类别:输出看起来像: # 0,1 : 类别1 # 1,0 : 类别2 # 分割训练和测试数据;random_state 是用于生成随机数的种子,用于确定哪些数据集用于训练,哪些用于测试 train_x, test_x, train_y, test_y = model_selection.train_test_split(X, Y, test_size=0.3, random_state=5, shuffle=False) input_dim = len(data.columns) - 1 print(input_dim) callback = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=10) time_callback = TimeHistory() model = tf.keras.Sequential() model.add(tf.keras.layers.Dense(8, input_dim=input_dim, activation='sigmoid')) model.add(tf.keras.layers.Dense(10, activation='sigmoid')) model.add(tf.keras.layers.Dense(10, activation='sigmoid')) model.add(tf.keras.layers.Dense(10, activation='sigmoid')) model.add(tf.keras.layers.Dense(2, activation='softmax')) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) # 开始训练若干轮次 # validation_split 取训练数据的一部分用于验证(用于在每轮结束时确定val_acc和val_loss) # verbose 控制显示;0=无显示,1=显示一个进度条,2=每轮显示一个进度条 history = model.fit(train_x, train_y, validation_split=0.33, epochs=1000, batch_size=1000, verbose=1, callbacks=[callback, time_callback], shuffle=False) scores = model.evaluate(test_x, test_y)
如您所见,我还在model.fit方法和train_test_split中禁用了洗牌选项。因为我想进一步提高训练性能,我通常希望使用多个线程和因此更多的CPU核心。
回答:
是的,这是有道理的,这个问题由两部分组成:
-
浮点数只是对实数的近似,特别是因为不是所有数字都能被表示,并且加法不是结合律的。例如 (a + b) + c != a + (b + c),因为每次加法都可能四舍五入到最接近的浮点数,从而产生略有不同的结果。例如在Python中:
(0.1 + 0.2) + 0.3 的结果是0.6000000000000001。
0.1 + (0.2 + 0.3) 的结果是0.6。
-
使用多线程进行并行计算会给过程引入更多的随机性,因为现在涉及到了调度器和其他进程。问题发生在从多个线程中添加变量时,例如当合并来自多个线程的结果时,通常使用锁并写入同一个变量,但每个线程执行此操作的顺序是未定义的,并且会改变结果。
这也发生在GPU内部,所以不幸的是,如果你想要可重复的结果,你需要尽量减少跨线程的并行使用(因此不使用多线程)。