Пример #1
0
    def __init__(self,
                 loss_function,
                 step_alpha=1,
                 step_beta=0,
                 tolerance=1e-5,
                 max_iter=1000,
                 **kwargs):
        """
        loss_function - строка, отвечающая за функцию потерь классификатора. 
        Может принимать значения:
        - 'binary_logistic' - бинарная логистическая регрессия
                
        step_alpha - float, параметр выбора шага из текста задания
        
        step_beta- float, параметр выбора шага из текста задания
        
        tolerance - точность, по достижении которой, необходимо прекратить оптимизацию.
        Необходимо использовать критерий выхода по модулю разности соседних значений функции:
        если |f(x_{k+1}) - f(x_{k})| < tolerance: то выход 
        
        max_iter - максимальное число итераций     
        
        **kwargs - аргументы, необходимые для инициализации   
        """
        self.step_alpha = step_alpha
        self.step_beta = step_beta
        self.tolerance = tolerance
        self.max_iter = max_iter

        if loss_function == 'binary_logistic':
            self.oracle = BinaryLogistic(**kwargs)
Пример #2
0
    def __init__(self,
                 loss_function,
                 batch_size,
                 step_alpha=1,
                 step_beta=0,
                 tolerance=1e-5,
                 max_iter=1000,
                 random_seed=153,
                 **kwargs):
        """
        loss_function - строка, отвечающая за функцию потерь классификатора. 
        Может принимать значения:
        - 'binary_logistic' - бинарная логистическая регрессия
        
        batch_size - размер подвыборки, по которой считается градиент
        
        step_alpha - float, параметр выбора шага из текста задания
        
        step_beta- float, параметр выбора шага из текста задания
        
        tolerance - точность, по достижении которой, необходимо прекратить оптимизацию
        Необходимо использовать критерий выхода по модулю разности соседних значений функции:
        если |f(x_{k+1}) - f(x_{k})| < tolerance: то выход 
        
        
        max_iter - максимальное число итераций (эпох)
        
        random_seed - в начале метода fit необходимо вызвать np.random.seed(random_seed).
        Этот параметр нужен для воспроизводимости результатов на разных машинах.
        
        **kwargs - аргументы, необходимые для инициализации
        """
        self.step_alpha = step_alpha
        self.step_beta = step_beta
        self.tolerance = tolerance
        self.max_epoch = max_iter
        self.batch_size = batch_size
        np.random.seed(random_seed)

        if loss_function == 'binary_logistic':
            self.oracle = BinaryLogistic(**kwargs)
Пример #3
0
 def fit(self,
         X,
         y,
         X_test=np.zeros(1),
         y_test=np.zeros(1),
         w_0=None,
         trace=False):
     if (self.loss_function == 'binary_logistic'):
         if (w_0 is None):
             w_0 = np.zeros(np.size(X, 1))
         self.lr = BinaryLogistic(**self.kwargs)
     elif (self.loss_function == 'multinomial_logistic'):
         if (w_0 is None):
             w_0 = np.zeros((np.size(np.unique(y)), np.size(X, 1)))
         self.lr = MulticlassLogistic(**self.kwargs)
     self.w = w_0.copy()
     last_func = self.lr.func(X, y, self.w)
     curr_func = last_func
     if (trace):
         self.history = dict()
         self.history['time'] = [0.0]
         self.history['func'] = [last_func]
         self.history['acc'] = [
             np.sum(np.equal(y_test, self.predict(X_test))) /
             np.size(y_test)
         ]
         start = time.time()
     num_iter = 0
     while (num_iter == 0
            or (np.abs(curr_func - last_func) >= self.tolerance
                and num_iter < self.max_iter)):
         num_iter += 1
         self.w -= self.lr.grad(X, y, self.w) * \
             self.step_alpha / num_iter ** self.step_beta
         last_func = curr_func
         curr_func = self.lr.func(X, y, self.w)
         if (trace):
             end = time.time()
             self.history['time'].append(end - start)
             self.history['func'].append(curr_func)
             self.history['acc'].append(
                 np.sum(np.equal(y_test, self.predict(X_test))) /
                 np.size(y_test))
     if (trace):
         return self.history
Пример #4
0
 def fit(self, X, y, X_test, y_test, w_0=None, trace=False, log_freq=1):
     np.random.seed(self.random_seed)
     if (self.loss_function == 'binary_logistic'):
         if (w_0 is None):
             w_0 = np.zeros(np.size(X, 1))
         self.lr = BinaryLogistic(**self.kwargs)
     elif (self.loss_function == 'multinomial_logistic'):
         if (w_0 is None):
             w_0 = np.zeros((np.size(np.unique(y)), np.size(X, 1)))
         self.lr = MulticlassLogistic(**self.kwargs)
     self.w = w_0.copy()
     last_func = self.lr.func(X, y, self.w)
     curr_func = last_func
     if (trace):
         self.history = dict()
         self.history['epoch_num'] = [0.0]
         self.history['time'] = [0.0]
         self.history['func'] = [last_func]
         self.history['weights_diff'] = [0.0]
         self.history['acc'] = [
             np.sum(np.equal(y_test, self.predict(X_test))) /
             np.size(y_test)
         ]
         start = time.time()
         last_epoch_num = 0
         curr_epoch_num = 0
         last_w = self.w.copy()
         curr_w = last_w.copy()
     num_iter = 0
     ind_list = np.arange(np.size(X, 0))
     np.random.shuffle(ind_list)
     curr_ind = 0
     while (num_iter == 0
            or (np.abs(curr_func - last_func) >= self.tolerance
                and num_iter < self.max_iter)):
         if (curr_ind >= np.size(X, 0)):
             np.random.shuffle(ind_list)
             curr_ind = 0
         num_iter += 1
         self.w -= self.lr.grad(
             X[curr_ind:curr_ind + self.batch_size, :],
             y[curr_ind:curr_ind + self.batch_size],
             self.w) * self.step_alpha / num_iter**self.step_beta
         last_func = curr_func.copy()
         curr_func = self.lr.func(X, y, self.w)
         if (trace):
             if (curr_ind + self.batch_size >= np.size(ind_list)):
                 curr_epoch_num += (np.size(ind_list) - curr_ind) / \
                     np.size(ind_list)
             else:
                 curr_epoch_num += self.batch_size / np.size(ind_list)
             if (curr_epoch_num - last_epoch_num >= log_freq):
                 end = time.time()
                 last_w = curr_w.copy()
                 curr_w = self.w
                 self.history['epoch_num'].append(curr_epoch_num)
                 self.history['time'].append(end - start)
                 self.history['func'].append(curr_func)
                 self.history['acc'].append(
                     np.sum(np.equal(y_test, self.predict(X_test))) /
                     np.size(y_test))
                 self.history['weights_diff'].append(
                     np.sum((last_w - curr_w)**2, axis=-1))
                 last_epoch_num = curr_epoch_num
         curr_ind += self.batch_size
     if (trace):
         return self.history
Пример #5
0
class GDClassifier:
    """
    Реализация метода градиентного спуска для произвольного
    оракула, соответствующего спецификации оракулов из модуля oracles.py
    """
    def __init__(self,
                 loss_function,
                 step_alpha=1,
                 step_beta=0,
                 tolerance=1e-5,
                 max_iter=1000,
                 **kwargs):
        """
        loss_function - строка, отвечающая за функцию потерь классификатора. 
        Может принимать значения:
        - 'binary_logistic' - бинарная логистическая регрессия
                
        step_alpha - float, параметр выбора шага из текста задания
        
        step_beta- float, параметр выбора шага из текста задания
        
        tolerance - точность, по достижении которой, необходимо прекратить оптимизацию.
        Необходимо использовать критерий выхода по модулю разности соседних значений функции:
        если |f(x_{k+1}) - f(x_{k})| < tolerance: то выход 
        
        max_iter - максимальное число итераций     
        
        **kwargs - аргументы, необходимые для инициализации   
        """
        self.step_alpha = step_alpha
        self.step_beta = step_beta
        self.tolerance = tolerance
        self.max_iter = max_iter

        if loss_function == 'binary_logistic':
            self.oracle = BinaryLogistic(**kwargs)

    def fit(self, X, y, w_0=None, trace=False):
        """
        Обучение метода по выборке X с ответами y
        
        X - scipy.sparse.csr_matrix или двумерный numpy.array
        
        y - одномерный numpy array

        ВАЖНО! Вектор y должен состоять из 1 и -1, а не 1 и 0.
        
        w_0 - начальное приближение в методе
        
        trace - переменная типа bool
      
        Если trace = True, то метод должен вернуть словарь history, содержащий информацию 
        о поведении метода. Длина словаря history = количество итераций + 1 (начальное приближение)
        
        history['time']: list of floats, содержит интервалы времени между двумя итерациями метода
        history['func']: list of floats, содержит значения функции на каждой итерации
        (0 для самой первой точки)
        """

        # initial value
        self.w = w_0 if not w_0 is None else np.zeros(X.shape[1])

        loss_value = self.oracle.func(X, y, self.w)
        history = {
            'time': [],
            'func': [loss_value],
            'accuracy': [(y == self.predict(X)).sum() / len(y)]
        }
        prev_time = time.time()

        for i in range(1, self.max_iter + 1):
            new_w = self.w - self.step_alpha / (
                i**self.step_beta) * self.oracle.grad(X, y, self.w)
            new_loss_value = self.oracle.func(X, y, new_w)

            if trace:
                history['func'].append(new_loss_value)
                history['time'].append(time.time() - prev_time)
                history['accuracy'].append(
                    (y == self.predict(X)).sum() / len(y))
                prev_time = time.time()

            if abs(loss_value - new_loss_value) < self.tolerance:
                self.w = new_w
                break

            loss_value = new_loss_value
            self.w = new_w

        if trace:
            return history

    def predict(self, X, threshold=0.5):
        """
        Получение меток ответов на выборке X
        
        X - scipy.sparse.csr_matrix или двумерный numpy.array
        
        return: одномерный numpy array с предсказаниями
        """
        result = (self.predict_proba(X) > threshold).astype('int')
        result[result == 0] = -1
        return result

    def predict_proba(self, X):
        """
        Получение вероятностей принадлежности X к классу k
        
        X - scipy.sparse.csr_matrix или двумерный numpy.array
        
        return: двумерной numpy array, [i, k] значение соответветствует вероятности
        принадлежности i-го объекта к классу k 
        """
        return expit(X.dot(self.w))

    def get_objective(self, X, y):
        """
        Получение значения целевой функции на выборке X с ответами y
        
        X - scipy.sparse.csr_matrix или двумерный numpy.array
        y - одномерный numpy array
        
        return: float
        """
        return self.oracle.func(X, y, self.w)

    def get_gradient(self, X, y):
        """
        Получение значения градиента функции на выборке X с ответами y
        
        X - scipy.sparse.csr_matrix или двумерный numpy.array
        y - одномерный numpy array
        
        return: numpy array, размерность зависит от задачи
        """
        return self.oracle.grad(X, y, self.w)

    def get_weights(self):
        """
        Получение значения весов функционала
        """
        return self.w
Пример #6
0
class SGDClassifier(GDClassifier):
    """
    Реализация метода стохастического градиентного спуска для произвольного
    оракула, соответствующего спецификации оракулов из модуля oracles.py
    """
    def __init__(self,
                 loss_function,
                 batch_size,
                 step_alpha=1,
                 step_beta=0,
                 tolerance=1e-5,
                 max_iter=1000,
                 random_seed=153,
                 **kwargs):
        """
        loss_function - строка, отвечающая за функцию потерь классификатора. 
        Может принимать значения:
        - 'binary_logistic' - бинарная логистическая регрессия
        
        batch_size - размер подвыборки, по которой считается градиент
        
        step_alpha - float, параметр выбора шага из текста задания
        
        step_beta- float, параметр выбора шага из текста задания
        
        tolerance - точность, по достижении которой, необходимо прекратить оптимизацию
        Необходимо использовать критерий выхода по модулю разности соседних значений функции:
        если |f(x_{k+1}) - f(x_{k})| < tolerance: то выход 
        
        
        max_iter - максимальное число итераций (эпох)
        
        random_seed - в начале метода fit необходимо вызвать np.random.seed(random_seed).
        Этот параметр нужен для воспроизводимости результатов на разных машинах.
        
        **kwargs - аргументы, необходимые для инициализации
        """
        self.step_alpha = step_alpha
        self.step_beta = step_beta
        self.tolerance = tolerance
        self.max_epoch = max_iter
        self.batch_size = batch_size
        np.random.seed(random_seed)

        if loss_function == 'binary_logistic':
            self.oracle = BinaryLogistic(**kwargs)

    def fit(self, X, y, w_0=None, trace=False, log_freq=1):
        """
        Обучение метода по выборке X с ответами y

        ВАЖНО! Вектор y должен состоять из 1 и -1, а не 1 и 0.
        
        X - scipy.sparse.csr_matrix или двумерный numpy.array
        
        y - одномерный numpy array
                
        w_0 - начальное приближение в методе
        
        Если trace = True, то метод должен вернуть словарь history, содержащий информацию 
        о поведении метода. Если обновлять history после каждой итерации, метод перестанет 
        превосходить в скорости метод GD. Поэтому, необходимо обновлять историю метода лишь
        после некоторого числа обработанных объектов в зависимости от приближённого номера эпохи.
        Приближённый номер эпохи:
            {количество объектов, обработанных методом SGD} / {количество объектов в выборке}
        
        log_freq - float от 0 до 1, параметр, отвечающий за частоту обновления. 
        Обновление должно проиходить каждый раз, когда разница между двумя значениями приближённого номера эпохи
        будет превосходить log_freq.
        
        history['epoch_num']: list of floats, в каждом элементе списка будет записан приближённый номер эпохи:
        history['time']: list of floats, содержит интервалы времени между двумя соседними замерами
        history['func']: list of floats, содержит значения функции после текущего приближённого номера эпохи
        history['weights_diff']: list of floats, содержит квадрат нормы разности векторов весов с соседних замеров
        (0 для самой первой точки)
        """

        # initial value
        self.w = w_0 if not w_0 is None else np.zeros(X.shape[1])

        if isinstance(X, scipy.sparse.coo.coo_matrix):
            X = X.tocsr()

        loss_value = self.oracle.func(X, y, self.w)
        history = {
            'epoch_num': [0],
            'time': [],
            'func': [loss_value],
            'weights_diff': [0],
            'accuracy': [(y == self.predict(X)).sum() / len(y)]
        }
        prev_time = time.time()
        iter_id = 1
        calc = time.time() - time.time()
        for epoch_i in range(1, self.max_epoch + 1):
            permutation = np.random.permutation(X.shape[0])

            X_shuffled = X[permutation]
            y_shuffled = y[permutation]

            self.w_prev = np.copy(self.w)
            for batch_i in range(int(np.ceil(X.shape[0] / self.batch_size))):

                X_batch = X_shuffled[batch_i * self.batch_size:(batch_i + 1) *
                                     self.batch_size]
                y_batch = y_shuffled[batch_i * self.batch_size:(batch_i + 1) *
                                     self.batch_size]

                self.w = self.w - self.step_alpha / (
                    iter_id**self.step_beta) * self.oracle.grad(
                        X_batch, y_batch, self.w)
                iter_id += 1

            new_loss_value = self.oracle.func(X, y, self.w)

            if trace:
                history['epoch_num'].append(epoch_i)
                history['time'].append(time.time() - prev_time)
                history['func'].append(new_loss_value)
                history['accuracy'].append(
                    (y == self.predict(X)).sum() / len(y))
                diff = self.w - self.w_prev
                history['weights_diff'].append(np.dot(diff, diff))
                prev_time = time.time()

            if abs(loss_value - new_loss_value) < self.tolerance:
                break
            loss_value = new_loss_value

        if trace:
            return history

    def predict(self, X, threshold=0.5):
        """
        Получение меток ответов на выборке X
        
        X - scipy.sparse.csr_matrix или двумерный numpy.array
        
        return: одномерный numpy array с предсказаниями
        """
        result = (self.predict_proba(X) > threshold).astype('int')
        result[result == 0] = -1
        return result

    def predict_proba(self, X):
        """
        Получение вероятностей принадлежности X к классу k
        
        X - scipy.sparse.csr_matrix или двумерный numpy.array
        
        return: двумерной numpy array, [i, k] значение соответветствует вероятности
        принадлежности i-го объекта к классу k 
        """
        return expit(X.dot(self.w))

    def get_weights(self):
        """
        Получение значения весов функционала
        """
        return self.w