几天前我遇到了一个问题,现在仍然困扰着我。我正在按照Daniel Nouri的深度学习教程进行学习:http://danielnouri.org/notes/category/deep-learning/,并尝试将他的示例应用到一个分类数据集上。我的问题是,如果我将数据集视为回归问题,它可以正常工作,但如果我尝试进行分类,它就会失败。我尝试编写了两个可复现的示例。
1) 回归(运行良好)
import lasagnefrom sklearn import datasetsimport numpy as npfrom lasagne import layersfrom lasagne.updates import nesterov_momentumfrom nolearn.lasagne import NeuralNetfrom sklearn.preprocessing import StandardScaleriris = datasets.load_iris()X = iris.data[iris.target<2] # 我们只取前两个特征。Y = iris.target[iris.target<2]stdscaler = StandardScaler(copy=True, with_mean=True, with_std=True)X = stdscaler.fit_transform(X).astype(np.float32)y = np.asmatrix((Y-0.5)*2).T.astype(np.float32)print X.shape, type(X)print y.shape, type(y)net1 = NeuralNet( layers=[ # 三层:一个隐藏层 ('input', layers.InputLayer), ('hidden', layers.DenseLayer), ('output', layers.DenseLayer), ], # 层参数: input_shape=(None, 4), # 每批次96x96输入像素 hidden_num_units=10, # 隐藏层单元数 output_nonlinearity=None, # 输出层使用恒等函数 output_num_units=1, # 1个目标值 # 优化方法: update=nesterov_momentum, update_learning_rate=0.01, update_momentum=0.9, regression=True, # 标志表示我们处理的是回归问题 max_epochs=400, # 我们希望训练这么多轮 verbose=1, )net1.fit(X, y)
2) 分类(引发矩阵维度错误;我将错误信息粘贴在下面)
import lasagnefrom sklearn import datasetsimport numpy as npfrom lasagne import layersfrom lasagne.nonlinearities import softmaxfrom lasagne.updates import nesterov_momentumfrom nolearn.lasagne import NeuralNetfrom sklearn.preprocessing import StandardScaleriris = datasets.load_iris()X = iris.data[iris.target<2] # 我们只取前两个特征。Y = iris.target[iris.target<2]stdscaler = StandardScaler(copy=True, with_mean=True, with_std=True)X = stdscaler.fit_transform(X).astype(np.float32)y = np.asmatrix((Y-0.5)*2).T.astype(np.int32)print X.shape, type(X)print y.shape, type(y)net1 = NeuralNet( layers=[ # 三层:一个隐藏层 ('input', layers.InputLayer), ('hidden', layers.DenseLayer), ('output', layers.DenseLayer), ], # 层参数: input_shape=(None, 4), # 每批次96x96输入像素 hidden_num_units=10, # 隐藏层单元数 output_nonlinearity=softmax, # 输出层使用恒等函数 output_num_units=1, # 1个目标值 # 优化方法: update=nesterov_momentum, update_learning_rate=0.01, update_momentum=0.9, regression=False, # 标志表示我们处理的是分类问题 max_epochs=400, # 我们希望训练这么多轮 verbose=1, )net1.fit(X, y)
使用代码2时,我得到的失败输出如下:
(100, 4) <type 'numpy.ndarray'>(100, 1) <type 'numpy.ndarray'> input (None, 4) produces 4 outputs hidden (None, 10) produces 10 outputs output (None, 1) produces 1 outputs---------------------------------------------------------------------------IndexError Traceback (most recent call last)<ipython-input-13-184a45e5abaa> in <module>() 40 ) 41 ---> 42 net1.fit(X, y)/Users/ivanvallesperez/anaconda/lib/python2.7/site-packages/nolearn/lasagne/base.pyc in fit(self, X, y) 291 292 try:--> 293 self.train_loop(X, y) 294 except KeyboardInterrupt: 295 pass/Users/ivanvallesperez/anaconda/lib/python2.7/site-packages/nolearn/lasagne/base.pyc in train_loop(self, X, y) 298 def train_loop(self, X, y): 299 X_train, X_valid, y_train, y_valid = self.train_test_split(--> 300 X, y, self.eval_size) 301 302 on_epoch_finished = self.on_epoch_finished/Users/ivanvallesperez/anaconda/lib/python2.7/site-packages/nolearn/lasagne/base.pyc in train_test_split(self, X, y, eval_size) 399 kf = KFold(y.shape[0], round(1. / eval_size)) 400 else:--> 401 kf = StratifiedKFold(y, round(1. / eval_size)) 402 403 train_indices, valid_indices = next(iter(kf))/Users/ivanvallesperez/anaconda/lib/python2.7/site-packages/sklearn/cross_validation.pyc in __init__(self, y, n_folds, shuffle, random_state) 531 for test_fold_idx, per_label_splits in enumerate(zip(*per_label_cvs)): 532 for label, (_, test_split) in zip(unique_labels, per_label_splits):--> 533 label_test_folds = test_folds[y == label] 534 # the test split can be too big because we used 535 # KFold(max(c, self.n_folds), self.n_folds) instead ofIndexError: too many indices for array
这是怎么回事?我做错了什么吗?我觉得我已经尝试了一切,但还是无法弄清楚发生了什么。
请注意,我今天刚刚使用命令更新了我的lasagne和依赖项:pip install -r https://raw.githubusercontent.com/dnouri/kfkd-tutorial/master/requirements.txt
提前感谢
编辑
通过进行以下更改,我成功地使其工作了,但我仍然有一些疑问:
-
我将Y定义为包含0/1值的一维向量,如下所示:
y = Y.astype(np.int32)
,但我仍然有一些疑问 -
我不得不将参数
output_num_units=1
更改为output_num_units=2
,我不是很理解这一点,因为我正在处理一个二元分类问题,我认为这个多层感知器应该只有1个输出神经元,而不是2个…我错了么?
我还尝试将成本函数更改为ROC-AUC。我知道有一个名为objective_loss_function
的参数,默认定义为objective_loss_function=lasagne.objectives.categorical_crossentropy
,但是…我如何使用ROC AUC作为成本函数而不是分类交叉熵呢?
谢谢
回答:
在nolearn中,如果你进行分类任务,output_num_units
表示你有多少个类。虽然可以用一个输出单元实现两类分类,但在nolearn中并没有这样特殊处理,这一点可以从[1]中看出:
if not self.regression: predict = predict_proba.argmax(axis=1)
请注意,无论你有多少个类,预测总是使用argmax(这意味着两类分类有两个输出,而不是一个)。
所以你的更改是正确的:output_num_units
应该始终是你拥有的类数,即使你有两个类,Y
应该具有(num_samples)
或(num_samples, 1)
的形状,包含表示类别的整数值,而不是例如具有每个类别一个位的向量,形状为(num_samples, num_categories)
。
回答你的另一个问题,Lasagne似乎没有ROC-AUC
目标函数,所以你需要自己实现。请注意,你不能使用scikit-learn中的实现,因为Lasagne要求目标函数接受theano张量作为参数,而不是列表或ndarray。要了解Lasagne中如何实现目标函数,你可以查看现有的目标函数[2]。其中许多引用了theano内部的函数,你可以在[3]中查看它们的实现(它会自动滚动到binary_crossentropy
,这是一个目标函数的好例子)。
[1] https://github.com/dnouri/nolearn/blob/master/nolearn/lasagne/base.py#L414
[2] https://github.com/Lasagne/Lasagne/blob/master/lasagne/objectives.py
[3] https://github.com/Theano/Theano/blob/master/theano/tensor/nnet/nnet.py#L1809