Пример #1
0
    def reset(self, **kwargs):
        IterativeAlgorithm.reset(self)
        # memberships = [self.normalize([random() for j in range(self.n_clusters)]) for i in range(len(self.sample))]
        # self.model = FuzzyCMeansClusterer(self.sample, memberships) 

        if self.initializer:
            self.n_clusters = len(self.initializer)
            centroids = self.initializer
            data = array([elem.pattern for elem in self.sample])
            memberships = [self.normalize([random() for j in range(self.n_clusters)]) for i in range(len(self.sample))]
            for j in range(len(centroids)):
                for i in range(len(data)):
                    den = sum([(linalg.norm(data[i] - centroids[j])/
                        linalg.norm(data[i]-centroids[k]))**(2/(self.m-1)) 
                        for k in range(self.n_clusters)])
                    memberships[i][j] = 1/den
        else:
            memberships = [self.normalize([random() for j in range(self.n_clusters)]) for i in range(len(self.sample))]
        
        self.model = FuzzyCMeansClusterer(self.sample, memberships) 
            
            
        # 10 means simply a big value, so that we don't run the risk of erronously stop
        # if we check convergence before first iteration
        
        self.memberships_old = [[10 for j in range(self.n_clusters)] for i in range(len(self.sample))]
Пример #2
0
    def __init__(self, sample, **kwargs):
        r"""
        See ``RosenblattPerceptronAlgorithm`` for full definition.

        """

        PerceptronAlgorithm.__init__(self, sample, **kwargs)
        IterativeAlgorithm.__init__(self, sample)
        PerceptronAlgorithm.reset(self, **kwargs)
Пример #3
0
    def __init__(self, sample, **kwargs):
        r"""
        See ``GradientPerceptronAlgorithm`` for full documentation.

        """

        PerceptronAlgorithm.__init__(self, sample, **kwargs)
        IterativeAlgorithm.__init__(self, sample)
        # This calms down pylint
        self.beta = None
        self.reset(**kwargs)
Пример #4
0
    def __init__(self, sample, n_clusters = 2, m = 2,  **kwargs ):
        
        IterativeAlgorithm.__init__(self, sample)

        try:
            self.initializer = kwargs['initializer']
        except KeyError:
            self.initializer = False
        
        self.n_clusters = n_clusters
        self.m = m
        self.distance = 1.0
        #self.reset(**kwargs)

        self.memberships_old = None
        self.reset(**kwargs)
Пример #5
0
    def run(self, **kwargs):
        IterativeAlgorithm.run(self, **kwargs)
        data = array([elem.pattern for elem in self.sample])
        while self.stop_criterion.stop() == False:
            self.memberships_old = self.matrix_copy(self.model.memberships)
            #print self.model.memberships
            centroids = self.model.getCentroids(m= self.m)
            #print centroids
            #exit()

            for j in range(len(centroids)):
                for i in range(len(data)):
                    den = sum([(linalg.norm(data[i] - centroids[j])/
                        linalg.norm(data[i]-centroids[k]))**(2/(self.m-1)) 
                        for k in range(self.n_clusters)])
                    self.model.memberships[i][j] = 1/den
            
        self.notify_observers()
Пример #6
0
    def __init__(self, sample, dimensions=None, **kwargs):
        r"""
        See ``BackpropagationAlgorithm`` for full documentation.

        """

        IterativeAlgorithm.__init__(self, sample)

        # dimensions needs to be a named argument in order to be able to
        # cross-validate on it. The default value will correspond to three
        # layers, with the input and output one automatically sized in order
        # to fit the provided data set, and the hidden one containing half of
        # the biggest value between number of input and output units.

        if dimensions is not None:
            self.dimensions = dimensions
        else:
            n_in = len(sample[0].pattern)
            n_out = len(sample[0].label)
            self.dimensions = (n_in, int(max(n_in, n_out) / 2), n_out)

        num_input = self.dimensions[0]
        for example in sample:
            if len(example.pattern) != num_input:
                raise ValueError('Sample incompatible with number of units')

        try:
            self.activations = kwargs['activations']
            if len(self.activations) != len(dimensions):
                raise ValueError(\
                    'Activations incompatible with number of layers')
        except KeyError:
            self.activations = SigmoidActivationFunction()
        except TypeError:
            pass
            # Raised by len if the argument is assigned a single activation
        # this calms down pylint
        self.threshold = None

        self.reset(**kwargs)
Пример #7
0
    def run(self, **kwargs):
        r"""
        Run the learning algorithm.

        INPUT:

        - ``self`` -- object on which the function is invoked.

        - ``stopping_criterion`` -- ``StoppingCriterion`` instance (default:
          ``FixedIterationsStoppingCriterion()``, amounting to the execution of
          one learning step) describing the criterion to be fulfilled in order
          to stop the training phase.

        - ``batch`` -- boolean (default: ``False``, amounting to online
          learning mode) flag setting batch learning, i.e. model update after
          the presentation of all examples, instead of online learning, i.e.
          model update at each example presentation.

        - ``selector`` -- iterator (default: ``sequential_selector``, amounting
          to cycling through the available examples) selecting the next sample
          to be fed to the learnin algorithm.

        - ``learning_rate`` -- float (default: 0.1) value to be used as
          learning rate.

        - ``momentum_term`` -- float (default: 0) value tu be used as momentum
          term.

        - ``min_error`` -- float (default: 0, which means connections
          and thresholds will always be updated) error value under which no
          update will occur on connections and thresholds.

        OUTPUT:

        No output. After the invocation the inferred model is available through
        the ``model`` field, in form of a ``Perceptron`` instance.

        EXAMPLES:

        Consider the following data set summarizing the binary XOR function,
        and a ``BackpropagationAlgorithm`` instance for it:

        ::

            >>> from yaplf.data import LabeledExample
            >>> xor_sample = [LabeledExample((0, 0), (0,)),
            ... LabeledExample((0, 1), (1,)), LabeledExample((1, 0), (1,)),
            ... LabeledExample((1, 1), (0,))]
            >>> from yaplf.utility import SigmoidActivationFunction
            >>> from yaplf.algorithms.neural import BackpropagationAlgorithm
            >>> alg = BackpropagationAlgorithm(xor_sample, (2, 2, 1),
            ... threshold = True, activations = SigmoidActivationFunction(10))

        In order to actually run the algorithm it is necessary to specify a
        stopping criterion (the default behaviour would only execute a learning
        iteration, probably not going so far). In order to keep it simple, one
        can chose ``FixedIterationsStoppingCriterion`` so as to run a fixed
        number of iterations, say `5000`:

        ::

            >>> from yaplf.utility.stopping import \
            ... FixedIterationsStoppingCriterion
            >>> alg.run(stopping_criterion = \
            ... FixedIterationsStoppingCriterion(5000), learning_rate = .1)

        The inferred model can be inspected through the ``model`` field in the
        ``LearningAlgorithm`` object:

        ::

            >>> alg.model # random
            MultilayerPerceptron((2, 2, 1), [array([[-0.49748418, -0.48592928],
            [-0.72052151, -0.69609958]]), array([[ 0.78258238, -0.84286672]])],
            thresholds = [array([ 0.71869556,  0.24337534]),
            array([-0.36203957])], activations = SigmoidActivationFunction(10))

        One of the ways of assessing the performance of the algorithm is that
        of invoking the ``test`` function inherited from the ``Model`` class in
        order to see how each example has been classified:

        ::

            >>> alg.model.test(xor_sample, verbose = True) # random
            (0, 0) mapped to 0.0279358676426, label is (0,), error
            [ 0.00078041]
            (0, 1) mapped to 0.968320708961, label is (1,), error [ 0.00100358]
            (1, 0) mapped to 0.966511649371, label is (1,), error [ 0.00112147]
            (1, 1) mapped to 0.0429967333857, label is (0,), error
            [ 0.00184872]
            MSE 0.00118854472284
            0.0011885447228408164

        The named argument ``verbose`` activates the verbose output detailing
        how error spreads on each example. Note that the output of ``test`` is
        likely to be different on each run, as when the class constructor is
        called the initial weights are chosen at random.

        Another solution is that of running the algorithm until the error on
        its training set is below a given threshold. This can be easily
        attained using ``TrainErrorStoppingCriterion``:

        ::

            >>> from yaplf.utility.stopping import TrainErrorStoppingCriterion
            >>> alg = BackpropagationAlgorithm(xor_sample, (2, 2, 1),
            ... learning_rate = .1,
            ... activations = SigmoidActivationFunction(10))
            >>> alg.run(stopping_criterion = TrainErrorStoppingCriterion(0.01))

        The argument in the constructor of ``TrainErrorStoppingCriterion`` sets
        the above mentioned threshold. It is also possible (and, besides, more
        correct) to run the learning algorithm using a training set and
        stopping the process when the test error on another data set goes
        below a given threshold. This can be attained using
        ``TestErrorStoppingCriterion`` in package ``yaplf.utility.stopping``.

        Another way of evaluating the inferred model is in this case that of
        graphically visualizing it. As the perceptron has two inputs it is
        indeed possible to call its ``plot`` method specifying a region
        containing all the pattern supplied to the learning algorithm:

        ::

            >>> alg.model.plot((0, 1), (0, 1), shading = True)

        Learning can be also monitored using the class ``ErrorTrajectory`` in
        package ``yaplf.graph.trajectory`` as follows:

       ::

            >>> alg = BackpropagationAlgorithm(xor_sample, (2, 2, 1),
            ... activations = SigmoidActivationFunction(10))
            >>> errObs = ErrorTrajectory(alg)
            >>> alg.run(stopping_criterion = \
            ... FixedIterationsStoppingCriterion(1500))
            >>> errObs.get_trajectory(color='red', joined = True)

        In this way, at each learning iteration the inferred model is tested
        against the training set, so that the ``get_trajectory`` function
        returns a graph of the related error versus the iteration number.

        The ``run`` function has a number of named argument allowing to
        tune how the backpropagation algorithm selects its output, and whose
        meaning requires a bit more information about how the algorithm works:
        basically, during the initialization of ``BackpropagationAlgorithm``
        and at each invokation of ``reset`` a multilayer perceptron is created
        picking its connection weights and its threshold values at random;
        subsequently at each iteration an example is selected and fed to this
        perceptron. The obtained output is compared to the expected one and
        an error is computed. This error, together with other information,
        the computation of a quantity `\Delta w` which will in turn be used
        in order to modify connections and thresholds. More precisely at a
        given time `t`, for each connection weight, say `w_{ij}(t)`, a
        corresponding `\Delta w_{ij}(t)` is computed and the perceptron is
        updated so that `w_{ij}(t+1) = w_{ij}(t) - \eta \Delta w_{ij}(t) +
        \alpha \Delta w_{ij}(t-1)`. This rule implements a local descent in
        the error space so that eventually the obtained minimizes locally the
        training error. The values `\eta` and `\alpha` can be chosen through
        the following named arguments:

        - ``learning_rate`` corresponds to`\eta`, the so-called *learning
          rate*, with a default value of 0.1. The higher this value, the more
          extended will be the local steps of the algorithm. This will improve
          convergence but will also raise the risk of outrunning an optimum
          and starting to oscillate around it.

        - ``momentum_term`` corresponds to `\alpha`, the so-called *momentum
          term* as it features a momentum which increases the actual step
          when the surface is smooth and tends to decrement it otherwise. Its
          default value is 0, corresponding to the original version of the
          backpropagation algorithm.

        The following named argument also affect the learning behaviour:

        - ``selector`` sets an iterator selecting the next example to be fed to
          the learnin algorithm. Its default value cycles through the provided
          sample.

        - ``batch`` -- boolean flag setting batch learning mode, in which the
          update values `\Delta w_{ij}` are cumulated for all example in the
          sample and subsequently used in order to modify the perceptron,
          rather than the standard online mode where the perceptron is updated
          after each single example presentation. Its default value is
          ``False``, corresponding to the online mode previously illustrated.
          It is worth noting that when the algorithm is run for a fixed number
          of iterations (i.e. through ``FixedIterationsStoppingCriterion``),
          an iteration corresponds to one example presentation for online mode
          and to the presentation of the whole sample for batch mode.

        - ``min_error`` -- error value under which no update will occur on
          connections and thresholds. This argument can be used in order to
          avoid that some examples are overlearnt at the expense of the
          remaining ones. The default value is 0, leading to updating the
          perceptron regardless of how small is the error on an example.

        AUTHORS:

        - Dario Malchiodi (2010-03-31)

        """

        IterativeAlgorithm.run(self, **kwargs)
        try:
            # batch or online learning
            batch = kwargs['batch']
        except KeyError:
            batch = False

        try:
            learning_rate = kwargs['learning_rate']
        except KeyError:
            learning_rate = 0.1

        try:
            momentum_term = kwargs['momentum_term']
        except KeyError:
            momentum_term = 0

        try:
            min_error = kwargs['min_error']
        except KeyError:
            min_error = 0

        dims = transpose((self.dimensions[1:], self.dimensions[:-1]))
        last_delta = [zeros(shape) for shape in dims]
        if self.threshold:
            last_delta_threshold = [zeros(shape) \
                for shape in self.dimensions[1:]]
        while self.stop_criterion.stop() == False:
            if batch:
                cumul_delta = last_delta = [zeros(shape) for shape in dims]
                if self.threshold:
                    cumul_delta_thresholds = [zeros(shape) \
                        for shape in self.dimensions[1:]]
                for elem in self.sample:
                    answer = self.model.compute(elem.pattern,
                        full_state=True, show_net=True, no_unbox=True)

                    delta = [[]] * (len(self.dimensions) - 1)

                    error = transpose(answer[-1])[0] - elem.label
                    if abs(error) < min_error:
                        continue
                    derivative = [\
                        self.model.get_activation(-1).compute_derivative(net,
                        func_value=val)
                        for (val, net)  in answer[-1]]

                    delta[-1] = error * derivative

                    for lev in range(1, len(self.dimensions) - 1):
                        derivatives = [self.model.get_activation(-lev - \
                            1).compute_derivative(net, func_value=val) \
                            for (val, net)  in answer[-lev - 1]]

                        prop_delta = \
                            dot(transpose(self.model.connections[-lev]),
                            delta[-lev])
                        delta[-lev - 1] = array(derivatives * prop_delta)

                    for lev in range(1, len(self.dimensions)):
                        new_delta = -learning_rate * outer(delta[-lev],
                           transpose(answer[-lev - 1])[0]) + \
                           momentum_term * last_delta[-lev]
                        cumul_delta[-lev] += new_delta
                        last_delta[-lev] = new_delta

                        if self.threshold:
                            new_delta = -learning_rate * delta[-lev] +\
                                momentum_term * last_delta_threshold[-lev]
                            cumul_delta_thresholds[-lev] += new_delta
                            last_delta_threshold[-lev] = new_delta

                for lev in range(1, len(self.dimensions)):
                    self.model.connections[-lev] += cumul_delta[-lev]

                    if self.threshold:
                        self.model.thresholds[-lev] += \
                            cumul_delta_thresholds[lev]

            else:
                elem = self.sample_selector.next()
                answer = self.model.compute(elem.pattern,
                    full_state=True, show_net=True, no_unbox=True)

                delta = [[]] * (len(self.dimensions) - 1)

                error = transpose(answer[-1])[0] - elem.label
                if abs(error) < min_error:
                    continue
                derivative = [\
                    self.model.get_activation(-1).compute_derivative(net,
                    func_value=val)
                    for (val, net)  in answer[-1]]

                delta[-1] = error * derivative

                for lev in range(1, len(self.dimensions) - 1):
                    derivatives = [self.model.get_activation(-lev - \
                        1).compute_derivative(net, func_value=val) \
                        for (val, net)  in answer[-lev - 1]]

                    prop_delta = dot(transpose(self.model.connections[-lev]),
                        delta[-lev])
                    delta[-lev - 1] = array(derivatives * prop_delta)

                for lev in range(1, len(self.dimensions)):
                    new_delta = -learning_rate * outer(delta[-lev],
                        transpose(answer[-lev - 1])[0]) + \
                        momentum_term * last_delta[-lev]
                    self.model.connections[-lev] += new_delta
                    last_delta[-lev] = new_delta

                    if self.threshold:
                        new_delta = -learning_rate * delta[-lev] +\
                            momentum_term * last_delta_threshold[-lev]
                        self.model.thresholds[-lev] += new_delta
                        last_delta_threshold[-lev] = new_delta

            self.notify_observers()
Пример #8
0
    def run(self, **kwargs):
        r"""
        Run the learning algorithm.

        INPUT:

        - ``self`` -- object on which the function is invoked.

        - ``stopping_criterion`` -- ``StoppingCriterion`` instance (default:
          ``FixedIterationsStoppingCriterion()``, amounting to the execution of
          one learning step) describing the criterion to be fulfilled in order
          to stop the training phase.

        - ``batch`` -- boolean (default: ``False``, amounting to online
          learning mode) flag setting batch learning, i.e. model update after
          the presentation of all examples, instead of online learning, i.e.
          model update at each example presentation.

        - ``selector`` -- iterator (default: ``sequential_selector``, amounting
          to cycling through the available examples) selecting the next sample
          to be fed to the learnin algorithm.

        - ``learning_rate`` -- float (default: 0.1) value to be used as
          learning rate.

        OUTPUT:

        No output. After the invocation the inferred model is available through
        the ``model`` field, in form of a ``Perceptron`` instance.

        EXAMPLES:

        Consider the following data set summarizing the binary AND function,
        and an instance of ``GradientPerceptronAlgorithm``:

        ::

            >>> from yaplf.data import LabeledExample
            >>> and_sample = [LabeledExample((1, 1), (1,)),
            ... LabeledExample((0, 0), (0,)), LabeledExample((0, 1), (0,)),
            ... LabeledExample((1, 0), (0,))]
            >>> from yaplf.algorithms.neural import GradientPerceptronAlgorithm
            >>> alg = GradientPerceptronAlgorithm(and_sample, threshold = True,
            ... weight_bound = 0.1, beta = 0.8)

        In order to actually run the algorithm it is necessary to specify a
        stopping criterion (the default behaviour would only execute a learning
        iteration, probably not going so far). In order to keep it simple, one
        can chose ``FixedIterationsStoppingCriterion`` so as to run a fixed
        number of iterations, say `5000`:

        ::

            >>> from yaplf.utility.stopping import \
            ... FixedIterationsStoppingCriterion
            >>> alg.run(stopping_criterion = \
            .... FixedIterationsStoppingCriterion(5000), batch = False,
            ... learning_rate = .1)

        The inferred model can be inspected through the ``model`` field in the
        ``LearningAlgorithm`` object:

        ::

             >>> alg.model # random
            Perceptron([array([ 4.01133491,  4.00742238])], threshold =
            [6.1595362999239365], activation =
            SigmoidActivationFunction(0.800000000000000))

        A better way of assessing the performance of the algorithm is that of
        invoking the ``test`` function inherited from the ``Model`` class in
        order to see how each example has been classified:

        ::

            >>> from yaplf.utility.error import MaxError
            >>> alg.model.test(and_sample, MaxError(), verbose = True) # random
            (1, 1) mapped to 0.81568421764, label is (1,), error 0.033972307625
            (0, 0) mapped to 0.00719156413, label is (0,), error 5.17185946e-05
            (0, 1) mapped to 0.15165346334, label is (0,), error 0.022998772945
            (1, 0) mapped to 0.1520565945, label is (0,), error 0.0231212079
            Maximum error: 0.0339723076259
            0.033972307625870855

        The second argument in ``test`` is an object subclassing
        ``ErrorModel``, allowing to specify a given metric in order to compute
        the distance between expected and actual output. The argument's
        default, amounting to the mean square error, has been in this case
        overridden to ``MaxError()``, thus computing the maximum error.

        As a final remark, it should be highlighted that these examples are
        shown for illustrative purpose. The suitable way of assessing a learnt
        model performance involves more complex techniques involving the use of
        a test set possibly coupled with a cross validation procedure (see
        function ``cross_validation`` in package ``yaplf.utility.validation``.)

        AUTHORS:

        - Dario Malchiodi (2010-02-22)

        """

        IterativeAlgorithm.run(self, **kwargs)
        try:
            # batch or online learning
            batch = kwargs["batch"]
        except KeyError:
            batch = False

        try:
            learning_rate = kwargs["learning_rate"]
        except KeyError:
            learning_rate = 0.1

        while self.stop_criterion.stop() == False:
            if batch:
                delta = array([[0.0] * len(self.sample[0].pattern) + 1] * len(self.sample[0].label))
                for elem in self.sample:
                    answer = self.model.compute(elem.pattern)
                    if type(answer) != type(()):
                        answer = (answer,)
                    delta += [
                        2
                        * self.beta
                        * learning_rate
                        * array(
                            [
                                (answer[s] - elem.label[s])
                                * answer[s]
                                * (1 - answer[s])
                                * hstack((elem.pattern, (-1 if self.threshold else 0)))[t]
                                for t in range(len(self.sample[0].pattern) + 1)
                            ]
                        )
                        for s in range(len(self.sample[0].label))
                    ]
                delta = delta.tolist()
            else:
                elem = self.sample_selector.next()
                answer = self.model.compute(elem.pattern)
                if type(answer) != type(()):
                    answer = (answer,)
                delta = [
                    2
                    * self.beta
                    * learning_rate
                    * array(
                        [
                            (answer[s] - elem.label[s])
                            * answer[s]
                            * (1 - answer[s])
                            * hstack((elem.pattern, (-1 if self.threshold else 0)))[t]
                            for t in range(len(self.sample[0].pattern) + 1)
                        ]
                    )
                    for s in range(len(self.sample[0].label))
                ]
            self.model.weights = [self.model.weights[i] - delta[i] for i in range(len(delta))]
            self.notify_observers()
Пример #9
0
    def run(self, **kwargs):
        r"""
        Run the Rosenblatt learning algorithm.

        INPUT:

        - ``self`` -- object on which the function is invoked.

        - ``stopping_criterion`` -- ``StoppingCriterion`` object (default:
          ``FixedIterationsStoppingCriterion()``, amounting to the execution of
          one iteration step) to be used in order to decide when learning
          should be stopped.

        - ``selector`` -- iterator (default: ``sequential_selector``) yielding
          the examples to be fed to the learning algorithm.

        OUTPUT:

        No output. After the invocation the inferred model is available through
        the ``model`` field, in form of a ``Perceptron`` instance.

        EXAMPLES:

        Consider the following linearly separable data set summarizing the
        binary AND function, and a ``RosenblattPerceptronAlgorithm`` instance:

        ::

            >>> from yaplf.data import LabeledExample
            >>> and_sample = [LabeledExample((1, 1), (1,)),
            ... LabeledExample((0, 0), (0,)), LabeledExample((0, 1), (0,)),
            ... LabeledExample((1, 0), (0,))]
            >>> from yaplf.algorithms.neural import \
            ... RosenblattPerceptronAlgorithm
            >>> alg = RosenblattPerceptronAlgorithm(and_sample,
            ... threshold = True, weight_bound = 0.1)

        In order to actually run the algorithm it is necessary to specify a
        stopping criterion (the default behaviour would only execute a learning
        iteration, probably not going so far). In order to keep it simple, one
        can chose ``FixedIterationsStoppingCriterion`` so as to run a fixed
        number of iterations, say `100`:

        ::

            >>> from yaplf.utility.stopping import \
            ... FixedIterationsStoppingCriterion
            >>> alg.run(stopping_criterion = \
            ... FixedIterationsStoppingCriterion(100))

        Another possible variation is that modifying the way examples are
        chosen and fed to the learning algorithm at each iteration. The default
        choice cycles between available examples, but it possible for instance
        to pick examples at random. This requires to specify a value for the
        ``selector`` named argument:

        ::

            >>> alg.reset()
            >>> from yaplf.utility.selection import random_selector
            >>> alg.run(stopping_criterion = \
            ... FixedIterationsStoppingCriterion(100),
            ... selector = random_selector)

        Note how, in order to restart the learning rocedure from fresh, the
        ``reset`` function was called. In this way the inferred model is
        initialized using randomly picked values.

        The inferred model is available through the ``model`` field. In order
        to get some information about its the performance is that of invoking
        the ``test`` function inherited from the ``Model`` class so as to get
        the mean approximation error, evaluated on all fed examples:

        ::

            >>> alg.model.test(and_sample)
            0.0

        As a final remark, it should be highlighted that these examples are
        shown for illustrative purpose. The suitable way of assessing a learnt
        model performance involves more complex techniques involving the use of
        a test set possibly coupled with a cross validation procedure (see
        function ``cross_validation`` in package ``yaplf.utility.validation``.)

        AUTHORS:

        - Dario Malchiodi (2010-02-22)

        """

        IterativeAlgorithm.run(self, **kwargs)
        while self.stop_criterion.stop() == False:
            elem = self.sample_selector.next()
            answer = self.model.compute(elem.pattern)

            if type(answer) != type(()):
                answer = (answer,)

            #            for i in range(len(self.sample[0].label)):
            #                if answer[i] == 1 and elem.label[i] == 0:
            #                    self.model.weights[i] -= hstack((elem.pattern,
            #                        (-1 if self.threshold else 0)))
            #
            #                if answer[i] == 0 and elem.label[i] == 1:
            #                    self.model.weights[i] += hstack((elem.pattern,
            #                        (-1 if self.threshold else 0)))

            temporary_weights = self.model.weights
            temporary_threshold = self.model.threshold
            for i in range(len(self.sample[0].label)):
                if answer[i] != elem.label[i]:
                    temporary_weights[i] += [(elem.label[i] - answer[i]) * p for p in elem.pattern]
                    temporary_threshold[i] += (elem.label[i] - answer[i]) * (-1 if self.threshold else 0)

            self.model.set_weights_and_threshold(temporary_weights, temporary_threshold)

            self.notify_observers()