是否可以将Keras的Scikit-Learn API与fit_generator()
方法一起使用?还是使用另一种方法来批量进行培训?我使用的是Scipy的稀疏矩阵,在输入到Keras之前,必须将其转换为Numpy阵列,但是由于高内存消耗,我无法同时转换它们。这是我产生批次的功能:
def batch_generator(X, y, batch_size):
n_splits = len(X) // (batch_size - 1)
X = np.array_split(X, n_splits)
y = np.array_split(y, n_splits)
while True:
for i in range(len(X)):
X_batch = []
y_batch = []
for ii in range(len(X[i])):
X_batch.append(X[i][ii].toarray().astype(np.int8)) # conversion sparse matrix -> np.array
y_batch.append(y[i][ii])
yield (np.array(X_batch), np.array(y_batch))
和带有交叉验证的示例代码:
from sklearn.model_selection import StratifiedKFold, GridSearchCV
from sklearn import datasets
from keras.models import Sequential
from keras.layers import Activation, Dense
from keras.wrappers.scikit_learn import KerasClassifier
import numpy as np
def build_model(n_hidden=32):
model = Sequential([
Dense(n_hidden, input_dim=4),
Activation("relu"),
Dense(n_hidden),
Activation("relu"),
Dense(3),
Activation("sigmoid")
])
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
return model
iris = datasets.load_iris()
X = iris["data"]
y = iris["target"].flatten()
param_grid = {
"n_hidden": np.array([4, 8, 16]),
"nb_epoch": np.array(range(50, 61, 5))
}
model = KerasClassifier(build_fn=build_model, verbose=0)
skf = StratifiedKFold(n_splits=5).split(X, y) # this yields (train_indices, test_indices)
grid = GridSearchCV(model, param_grid, cv=skf, verbose=2, n_jobs=4)
grid.fit(X, y)
print(grid.best_score_)
print(grid.cv_results_["params"][grid.best_index_])
为了更多的解释,它使用param_grid
中超参数的所有可能组合来构建模型。然后,在StratifiedKFold
提供的火车测试数据拆分( folds )上对每个模型进行训练并一一测试。然后给定模型的最终分数是所有折叠的平均分数。
因此,是否可以在上面的代码上插入一些预处理取代,以在实际拟合之前转换数据(稀疏矩阵)?
我知道我可以编写自己的交叉验证生成器,但是它必须产生索引,而不是真实的数据!
实际上,您可以使用稀疏矩阵用作发电机的keras输入。这是我的版本,用于以前的项目:
> class KerasClassifier(KerasClassifier):
> """ adds sparse matrix handling using batch generator
> """
>
> def fit(self, x, y, **kwargs):
> """ adds sparse matrix handling """
> if not issparse(x):
> return super().fit(x, y, **kwargs)
>
> ############ adapted from KerasClassifier.fit ######################
> if self.build_fn is None:
> self.model = self.__call__(**self.filter_sk_params(self.__call__))
> elif not isinstance(self.build_fn, types.FunctionType):
> self.model = self.build_fn(
> **self.filter_sk_params(self.build_fn.__call__))
> else:
> self.model = self.build_fn(**self.filter_sk_params(self.build_fn))
>
> loss_name = self.model.loss
> if hasattr(loss_name, '__name__'):
> loss_name = loss_name.__name__
> if loss_name == 'categorical_crossentropy' and len(y.shape) != 2:
> y = to_categorical(y)
> ### fit => fit_generator
> fit_args = copy.deepcopy(self.filter_sk_params(Sequential.fit_generator))
> fit_args.update(kwargs)
> ############################################################
> self.model.fit_generator(
> self.get_batch(x, y, self.sk_params["batch_size"]),
> samples_per_epoch=x.shape[0],
> **fit_args)
> return self
>
> def get_batch(self, x, y=None, batch_size=32):
> """ batch generator to enable sparse input """
> index = np.arange(x.shape[0])
> start = 0
> while True:
> if start == 0 and y is not None:
> np.random.shuffle(index)
> batch = index[start:start+batch_size]
> if y is not None:
> yield x[batch].toarray(), y[batch]
> else:
> yield x[batch].toarray()
> start += batch_size
> if start >= x.shape[0]:
> start = 0
>
> def predict_proba(self, x):
> """ adds sparse matrix handling """
> if not issparse(x):
> return super().predict_proba(x)
>
> preds = self.model.predict_generator(
> self.get_batch(x, None, self.sk_params["batch_size"]),
> val_samples=x.shape[0])
> return preds