source: orange/Orange/classification/neural.py @ 11559:05d6847d3185

Revision 11559:05d6847d3185, 10.3 KB checked in by Jure Zbontar <jure.zbontar@…>, 11 months ago (diff)

Add normalize flag for NNs

Line 
1"""
2.. index:: Neural Network Learner
3
4***************************************
5Neural Network Learner  (``neural``)
6***************************************
7
8
9.. index:: Neural Network Learner
10.. autoclass:: Orange.classification.neural.NeuralNetworkLearner
11    :members:
12    :show-inheritance:
13
14.. index:: Neural Network Classifier
15.. autoclass:: Orange.classification.neural.NeuralNetworkClassifier
16    :members:
17    :show-inheritance:
18
19"""
20
21
22import Orange
23import random
24import numpy as np
25np.seterr('ignore') # set to ignore to disable overflow errors
26import scipy.sparse
27from scipy.optimize import fmin_l_bfgs_b
28
29class _NeuralNetwork:
30    def __init__(self, layers,lambda_=1, callback=None, **fmin_args):
31        self.layers = layers
32        self.lambda_ = lambda_
33        self.callback = callback
34        self.fmin_args = fmin_args
35
36    def unfold_params(self, params):
37        i0, i1, i2 = self.layers
38
39        i = (i0 + 1) * i1
40
41        Theta1 = params[:i].reshape((i1, i0 + 1))
42        Theta2 = params[i:].reshape((i2, i1 + 1))
43
44        return Theta1, Theta2
45
46    def cost_grad(self, params):
47        Theta1, Theta2 = self.unfold_params(params)
48
49        if self.callback:
50            self.Theta1 = Theta1
51            self.Theta2 = Theta2
52            self.callback(self)
53
54        # Feedforward Propagation
55        m, n = self.X.shape
56
57        a1 = self.X
58        z2 = a1.dot(Theta1.T)
59        a2 = np.column_stack((np.ones(m), _sigmoid(z2)))
60        z3 = a2.dot(Theta2.T)
61        a3 = _sigmoid(z3)
62
63        # Cost
64        J = np.sum(-self.y * np.log(a3) - (1 - self.y) * np.log(1 - a3)) / m
65
66        t1 = Theta1.copy()
67        t1[:, 0] = 0
68        t2 = Theta2.copy()
69        t2[:, 0] = 0
70
71        # regularization
72        reg = np.dot(t1.flat, t1.flat)
73        reg += np.dot(t2.flat, t2.flat)
74        J += float(self.lambda_) * reg / (2.0 * m)
75
76        # Grad
77        d3 = a3 - self.y
78        d2 = d3.dot(Theta2)[:, 1:] * _sigmoid_gradient(z2)
79
80        D2 = a2.T.dot(d3).T / m
81        D1 = a1.T.dot(d2).T / m
82
83        # regularization
84        D2 += t2 * (float(self.lambda_) / m)
85        D1 += t1 * (float(self.lambda_) / m)
86
87        return J, np.hstack((D1.flat, D2.flat))
88
89    def fit(self, X, y):
90        i0, i1, i2 = self.layers
91
92        m, n = X.shape
93        n_params = i1 * (i0 + 1) + i2 * (i1 + 1)
94        eps = np.sqrt(6) / np.sqrt(i0 + i2)
95        initial_params = np.random.randn(n_params) * 2 * eps - eps
96
97        self.X = self.append_ones(X)
98        self.y = y
99
100        params, _, _ = fmin_l_bfgs_b(self.cost_grad, initial_params, **self.fmin_args)
101
102        self.Theta1, self.Theta2 = self.unfold_params(params)
103
104    def predict(self, X):
105        m, n = X.shape
106       
107        a2 = _sigmoid(self.append_ones(X).dot(self.Theta1.T))
108        a3 = _sigmoid(np.column_stack((np.ones(m), a2)).dot(self.Theta2.T))
109
110        return a3
111
112    def append_ones(self, X):
113        m, n = X.shape
114        if scipy.sparse.issparse(X):
115            return scipy.sparse.hstack((np.ones((m, 1)), X)).tocsr()
116        else:
117            return np.column_stack((np.ones(m), X))
118
119def _sigmoid(x):
120    return 1.0 / (1.0 + np.exp(-x))
121
122def _sigmoid_gradient(x):
123    sx = _sigmoid(x)
124    return sx * (1 - sx)
125
126
127class NeuralNetworkLearner(Orange.classification.Learner):
128    """
129    NeuralNetworkLearner implements a multilayer perceptron. Learning is performed by minimizing an L2-regularized
130    cost function with scipy's implementation of L-BFGS. The current implementations is limited to a single
131    hidden layer.
132
133    Regression is currently not supported.
134
135    :param name: learner name.
136    :type name: string
137
138    :param n_mid: Number of nodes in the hidden layer
139    :type n_mid: int
140
141    :param reg_fact: Regularization factor.
142    :type reg_fact: float
143
144    :param max_iter: Maximum number of iterations.
145    :type max_iter: int
146
147    :param normalize: Normalize the data prior to learning (subtract each column by the mean and divide by the standard deviation)
148    :type normalize: bool
149
150    :rtype: :class:`Orange.multitarget.neural.neuralNetworkLearner` or
151            :class:`Orange.multitarget.chain.NeuralNetworkClassifier`
152    """
153
154    def __new__(cls, data=None, weight = 0, **kwargs):
155        self = Orange.classification.Learner.__new__(cls, **kwargs)
156
157        if data is None:   
158            return self
159        else:
160            self.__init__(**kwargs)
161            return self(data,weight)
162
163    def __init__(self, name="NeuralNetwork", n_mid=10, reg_fact=1, max_iter=300, normalize=True, rand=None):
164        """
165        Current default values are the same as in the original implementation (neural_networks.py)
166        """
167        self.name = name
168        self.n_mid = n_mid
169        self.reg_fact = reg_fact
170        self.max_iter = max_iter
171        self.rand = rand
172        self.normalize = normalize
173
174        if not self.rand:
175            self.rand = random.Random(42)
176        np.random.seed(self.rand.randint(0,10000))
177
178    def __call__(self,data,weight=0):
179        """
180        Learn from the given table of data instances.
181       
182        :param instances: data for learning.
183        :type instances: class:`Orange.data.Table`
184
185        :param weight: weight.
186        :type weight: int
187
188        :param class_order: list of descriptors of class variables
189        :type class_order: list of :class:`Orange.feature.Descriptor`
190
191        :rtype: :class:`Orange.multitarget.chain.NeuralNetworkClassifier`
192        """
193
194        #converts attribute data
195        X = data.to_numpy()[0] 
196
197        mean = X.mean(axis=0)
198        std = X.std(axis=0)
199        if self.normalize:
200            X = (X - mean) / std
201
202        #converts multi-target or single-target classes to numpy
203        if data.domain.class_vars:
204            for cv in data.domain.class_vars:
205                if cv.var_type == Orange.feature.Continuous:
206                    raise ValueError("non-discrete classes not supported")
207        else:
208            if data.domain.class_var.var_type == Orange.feature.Continuous:
209                raise ValueError("non-discrete classes not supported")
210
211        if data.domain.class_vars:
212            cvals = [len(cv.values) if len(cv.values) > 2 else 1 for cv in data.domain.class_vars]
213            Y = np.zeros((len(data), sum(cvals)))
214            cvals = [0]+[sum(cvals[0:i+1]) for i in xrange(len(cvals))] 
215
216            for i in xrange(len(data)):
217                for j in xrange(len(cvals)-1):
218                    if cvals[j+1] - cvals[j] > 2:
219                        Y[i, cvals[j] + int(data[i].get_classes()[j])] = 1.0
220                    else:
221                        Y[i, cvals[j]] = float(data[i].get_classes()[j])
222        else:
223            y = np.array([int(d.get_class()) for d in data])
224            n_classes = len(data.domain.class_var.values)
225            if n_classes > 2:
226                Y = np.eye(n_classes)[y]
227            else:
228                Y = y[:,np.newaxis]
229       
230        #initializes neural networks
231        self.nn =  _NeuralNetwork([len(X[0]), self.n_mid,len(Y[0])], lambda_=self.reg_fact, maxfun=self.max_iter, iprint=-1)
232       
233        self.nn.fit(X,Y)
234               
235        return NeuralNetworkClassifier(classifier=self.nn.predict,
236            domain=data.domain, normalize=self.normalize, mean=mean, std=std)
237
238class NeuralNetworkClassifier():
239    """   
240    Uses the classifier induced by the :obj:`NeuralNetworkLearner`.
241 
242    :param name: name of the classifier.
243    :type name: string
244    """
245
246    def __init__(self,**kwargs):
247        self.__dict__.update(**kwargs)
248
249    def __call__(self,example, result_type=Orange.core.GetValue):
250        """
251        :param instance: instance to be classified.
252        :type instance: :class:`Orange.data.Instance`
253       
254        :param result_type: :class:`Orange.classification.Classifier.GetValue` or \
255              :class:`Orange.classification.Classifier.GetProbabilities` or
256              :class:`Orange.classification.Classifier.GetBoth`
257       
258        :rtype: :class:`Orange.data.Value`,
259              :class:`Orange.statistics.Distribution` or a tuple with both
260        """
261
262        # transform example to numpy
263        if not self.domain.class_vars: example = [example[i] for i in range(len(example)-1)]
264        input = np.array([[float(e) for e in example]])
265
266        if self.normalize:
267            input = (input - self.mean) / self.std
268
269        # transform results from numpy
270        results = self.classifier(input).tolist()[0]
271        if len(results) == 1:
272            prob_positive = results[0]
273            results = [1 - prob_positive, prob_positive]
274        mt_prob = []
275        mt_value = []
276         
277        if self.domain.class_vars:
278            cvals = [len(cv.values) if len(cv.values) > 2 else 1 for cv in self.domain.class_vars]
279            cvals = [0] + [sum(cvals[0:i]) for i in xrange(1, len(cvals) + 1)]
280
281            for cls in xrange(len(self.domain.class_vars)):
282                if cvals[cls+1]-cvals[cls] > 2:
283                    cprob = Orange.statistics.distribution.Discrete(results[cvals[cls]:cvals[cls+1]])
284                    cprob.normalize()
285                else:
286                    r = results[cvals[cls]]
287                    cprob = Orange.statistics.distribution.Discrete([1.0 - r, r])
288
289                mt_prob.append(cprob)
290                mt_value.append(Orange.data.Value(self.domain.class_vars[cls], cprob.values().index(max(cprob))))
291                                 
292        else:
293            cprob = Orange.statistics.distribution.Discrete(results)
294            cprob.normalize()
295
296            mt_prob = cprob
297            mt_value = Orange.data.Value(self.domain.class_var, cprob.values().index(max(cprob)))
298
299        if result_type == Orange.core.GetValue: return tuple(mt_value) if self.domain.class_vars else mt_value
300        elif result_type == Orange.core.GetProbabilities: return tuple(mt_prob) if self.domain.class_vars else mt_prob
301        else: 
302            return [tuple(mt_value), tuple(mt_prob)] if self.domain.class_vars else [mt_value, mt_prob] 
303
304if __name__ == '__main__':
305    import time
306    print "STARTED"
307    global_timer = time.time()
308
309    data = Orange.data.Table('wine')
310    l1 = NeuralNetworkLearner(n_mid=40, reg_fact=1, max_iter=200)
311
312#    c1 = l1(data)
313#    print c1(data[0], 3), data[0]
314
315    l2 = Orange.classification.bayes.NaiveLearner()
316    res = Orange.evaluation.testing.cross_validation([l1, l2],data, 5)
317    scores = Orange.evaluation.scoring.CA(res)
318    for i in range(len(scores)):
319        print res.classifierNames[i], scores[i]
320
321    print "--DONE %.2f --" % (time.time()-global_timer)
Note: See TracBrowser for help on using the repository browser.