source: orange/Orange/classification/neural.py @ 10984:935cd5530cde

Revision 10984:935cd5530cde, 9.7 KB checked in by Miran Levar <mlevar@…>, 19 months ago (diff)

Removed lingering multitarget files, made some fixes to scoring.

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 uses jzbontar's implementation of neural networks and wraps it in
130    an Orange compatible learner.
131
132    NeuralNetworkLearner supports all types of data and returns a classifier, regression is currently not supported.
133
134    More information about neural networks can be found at http://en.wikipedia.org/wiki/Artificial_neural_network.
135
136    :param name: learner name.
137    :type name: string
138
139    :param n_mid: Number of nodes in the hidden layer
140    :type n_mid: integer
141
142    :param reg_fact: Regularization factor.
143    :type reg_fact: float
144
145    :param max_iter: Maximum number of iterations.
146    :type max_iter: integer
147
148    :rtype: :class:`Orange.multitarget.neural.neuralNetworkLearner` or
149            :class:`Orange.multitarget.chain.NeuralNetworkClassifier`
150    """
151
152    def __new__(cls, data=None, weight = 0, **kwargs):
153        self = Orange.classification.Learner.__new__(cls, **kwargs)
154
155        if data is None:   
156            return self
157        else:
158            self.__init__(**kwargs)
159            return self(data,weight)
160
161    def __init__(self, name="NeuralNetwork", n_mid=10, reg_fact=1, max_iter=1000, rand=None):
162        """
163        Current default values are the same as in the original implementation (neural_networks.py)
164        """
165
166        self.name = name
167        self.n_mid = n_mid
168        self.reg_fact = reg_fact
169        self.max_iter = max_iter
170        self.rand = rand
171
172        if not self.rand:
173            self.rand = random.Random(42)
174        np.random.seed(self.rand.randint(0,10000))
175
176    def __call__(self,data,weight=0):
177        """
178        Learn from the given table of data instances.
179       
180        :param instances: data for learning.
181        :type instances: class:`Orange.data.Table`
182
183        :param weight: weight.
184        :type weight: int
185
186        :param class_order: list of descriptors of class variables
187        :type class_order: list of :class:`Orange.feature.Descriptor`
188
189        :rtype: :class:`Orange.multitarget.chain.NeuralNetworkClassifier`
190        """
191
192        #converts attribute data
193        X = data.to_numpy()[0] 
194
195        #converts multi-target or single-target classes to numpy
196
197
198        if data.domain.class_vars:
199            for cv in data.domain.class_vars:
200                if cv.var_type == Orange.feature.Continuous:
201                    raise ValueError("non-discrete classes not supported")
202        else:
203            if data.domain.class_var.var_type == Orange.feature.Continuous:
204                raise ValueError("non-discrete classes not supported")
205
206        if data.domain.class_vars:
207            cvals = [len(cv.values) if len(cv.values) > 2 else 1 for cv in data.domain.class_vars]
208            Y = np.zeros((len(data), sum(cvals)))
209            cvals = [0]+[sum(cvals[0:i+1]) for i in xrange(len(cvals))] 
210
211            for i in xrange(len(data)):
212                for j in xrange(len(cvals)-1):
213                    if cvals[j+1] - cvals[j] > 2:
214                        Y[i, cvals[j] + int(data[i].get_classes()[j])] = 1.0
215                    else:
216                        Y[i, cvals[j]] = float(data[i].get_classes()[j])
217        else:
218            y = np.array([int(d.get_class()) for d in data])
219            n_classes = len(data.domain.class_var.values)
220            if n_classes > 2:
221                Y = np.eye(n_classes)[y]
222            else:
223                Y = y[:,np.newaxis]
224       
225        #initializes neural networks
226        self.nn =  _NeuralNetwork([len(X[0]), self.n_mid,len(Y[0])], lambda_=self.reg_fact, maxfun=self.max_iter, iprint=-1)
227       
228        self.nn.fit(X,Y)
229               
230        return NeuralNetworkClassifier(classifier=self.nn.predict, domain = data.domain)
231
232class NeuralNetworkClassifier():
233    """   
234    Uses the classifier induced by the :obj:`NeuralNetworkLearner`.
235 
236    :param name: name of the classifier.
237    :type name: string
238    """
239
240    def __init__(self,**kwargs):
241        self.__dict__.update(**kwargs)
242
243    def __call__(self,example, result_type=Orange.core.GetValue):
244        """
245        :param instance: instance to be classified.
246        :type instance: :class:`Orange.data.Instance`
247       
248        :param result_type: :class:`Orange.classification.Classifier.GetValue` or \
249              :class:`Orange.classification.Classifier.GetProbabilities` or
250              :class:`Orange.classification.Classifier.GetBoth`
251       
252        :rtype: :class:`Orange.data.Value`,
253              :class:`Orange.statistics.Distribution` or a tuple with both
254        """
255
256        # transform example to numpy
257        if not self.domain.class_vars: example = [example[i] for i in range(len(example)-1)]
258        input = np.array([[float(e) for e in example]])
259
260        # transform results from numpy
261        results = self.classifier(input).tolist()[0]
262        mt_prob = []
263        mt_value = []
264         
265        if self.domain.class_vars:
266            cvals = [len(cv.values) if len(cv.values) > 2 else 1 for cv in self.domain.class_vars]
267            cvals = [0] + [sum(cvals[0:i]) for i in xrange(1, len(cvals) + 1)]
268
269            for cls in xrange(len(self.domain.class_vars)):
270                if cvals[cls+1]-cvals[cls] > 2:
271                    cprob = Orange.statistics.distribution.Discrete(results[cvals[cls]:cvals[cls+1]])
272                    cprob.normalize()
273                else:
274                    r = results[cvals[cls]]
275                    cprob = Orange.statistics.distribution.Discrete([1.0 - r, r])
276
277                mt_prob.append(cprob)
278                mt_value.append(Orange.data.Value(self.domain.class_vars[cls], cprob.values().index(max(cprob))))
279                                 
280        else:
281            cprob = Orange.statistics.distribution.Discrete(results)
282            cprob.normalize()
283
284            mt_prob = cprob
285            mt_value = Orange.data.Value(self.domain.class_var, cprob.values().index(max(cprob)))
286
287        if result_type == Orange.core.GetValue: return tuple(mt_value) if self.domain.class_vars else mt_value
288        elif result_type == Orange.core.GetProbabilities: return tuple(mt_prob) if self.domain.class_vars else mt_prob
289        else: 
290            return [tuple(mt_value), tuple(mt_prob)] if self.domain.class_vars else [mt_value, mt_prob] 
291
292if __name__ == '__main__':
293    import time
294    print "STARTED"
295    global_timer = time.time()
296
297    data = Orange.data.Table('iris')
298    l1 = NeuralNetworkLearner(n_mid=10, reg_fact=1, max_iter=1000)
299    res = Orange.evaluation.testing.cross_validation([l1],data, 3)
300    scores = Orange.evaluation.scoring.CA(res)
301
302    for i in range(len(scores)):
303        print res.classifierNames[i], scores[i]
304
305    print "--DONE %.2f --" % (time.time()-global_timer)
Note: See TracBrowser for help on using the repository browser.