def train_hmm(self,observation_list, iterations, quantities):
        """ Runs the Baum Welch Algorithm and finds the new model parameters

        **Arguments**:

        :param observation_list: A nested list, or a list of lists  
        :type observation_list: Contains a list  multiple observation sequences.

        :param iterations: Maximum number of iterations for the algorithm 
        :type iterations: An integer 

        :param quantities: Number of times, each corresponding item in  'observation_list' occurs.
        :type quantities: A list of integers

        :return: Returns the emission, transition and start probabilites as numpy matrices
        :rtype: Three numpy matices 
            
        **Features**:

        Scaling applied here. This ensures that no underflow error occurs.
            
        **Example**:

        >>> states = ('s', 't')
        >>> possible_observation = ('A','B' )
        >>> # Numpy arrays of the data
        >>> start_probability = np.matrix( '0.5 0.5 ')
        >>> transition_probability = np.matrix('0.6 0.4 ;  0.3 0.7 ')
        >>> emission_probability = np.matrix( '0.3 0.7 ; 0.4 0.6 ' )
        >>> # Initialize class object
        >>> test = hmm(states,possible_observation,start_probability,transition_probability,emission_probability)
        >>> 
        >>> observations = ('A', 'B','B','A')
        >>> obs4 = ('B', 'A','B')
        >>> observation_tuple = []
        >>> observation_tuple.extend( [observations,obs4] )
        >>> quantities_observations = [10, 20]
        >>> num_iter=1000
        >>> e,t,s = test.train_hmm(observation_tuple,num_iter,quantities_observations)
        >>> # e,t,s contain new emission transition and start probabilities

        """

        obs_size = len(observation_list)
        prob = float('inf')
        q = quantities

        # Train the model 'iteration' number of times
        # store em_prob and trans_prob copies since you should use same values for one loop
        for i in range(iterations):

            emProbNew = np.asmatrix(np.zeros((self.em_prob.shape)))
            transProbNew = np.asmatrix(np.zeros((self.trans_prob.shape)))
            startProbNew = np.asmatrix(np.zeros((self.start_prob.shape)))

            for j in range(obs_size):

                # re-assing values based on weight
                emProbNew= emProbNew + q[j] * self._train_emission(observation_list[j])
                transProbNew = transProbNew + q[j] * self._train_transition(observation_list[j])
                startProbNew = startProbNew + q[j] * self._train_start_prob(observation_list[j])


            # Normalizing
            em_norm = emProbNew.sum(axis = 1)
            trans_norm = transProbNew.sum(axis = 1)
            start_norm = startProbNew.sum(axis = 1)

            emProbNew = emProbNew/ em_norm.transpose()
            startProbNew = startProbNew/ start_norm.transpose()
            transProbNew = transProbNew/ trans_norm.transpose()


            self.em_prob,self.trans_prob = emProbNew,transProbNew
            self.start_prob = startProbNew

            if prob -  self.log_prob(observation_list,quantities)>0.0000001:
                prob = self.log_prob(observation_list,quantities)
            else:
                return self.em_prob, self.trans_prob , self.start_prob


        return self.em_prob, self.trans_prob , self.start_prob
Runs the Baum Welch Algorithm and finds the new model parameters

        **Arguments**:

        :param observation_list: A nested list, or a list of lists  
        :type observation_list: Contains a list  multiple observation sequences.

        :param iterations: Maximum number of iterations for the algorithm 
        :type iterations: An integer 

        :param quantities: Number of times, each corresponding item in  'observation_list' occurs.
        :type quantities: A list of integers

        :return: Returns the emission, transition and start probabilites as numpy matrices
        :rtype: Three numpy matices 
            
        **Features**:

        Scaling applied here. This ensures that no underflow error occurs.
            
        **Example**:

        >>> states = ('s', 't')
        >>> possible_observation = ('A','B' )
        >>> # Numpy arrays of the data
        >>> start_probability = np.matrix( '0.5 0.5 ')
        >>> transition_probability = np.matrix('0.6 0.4 ;  0.3 0.7 ')
        >>> emission_probability = np.matrix( '0.3 0.7 ; 0.4 0.6 ' )
        >>> # Initialize class object
        >>> test = hmm(states,possible_observation,start_probability,transition_probability,emission_probability)
        >>> 
        >>> observations = ('A', 'B','B','A')
        >>> obs4 = ('B', 'A','B')
        >>> observation_tuple = []
        >>> observation_tuple.extend( [observations,obs4] )
        >>> quantities_observations = [10, 20]
        >>> num_iter=1000
        >>> e,t,s = test.train_hmm(observation_tuple,num_iter,quantities_observations)
        >>> # e,t,s contain new emission transition and start probabilities