예제 #1
0
    def train(self, x_mat: ndarray, y_row: ndarray) -> List[ndarray]:
        """
        训练前馈神经网络,既可以训练二分类问题,也可以训练多类分类问题。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 每层的权值(输出层没有)。
        """

        self.__check_params()
        x_mat, y_row = _t.match_x_y(x_mat, y_row)
        if x_mat.shape[1] - 1 != self.layer_nodes[0]:
            raise DataNotMatchError(
                'feature number and input layer node number mismatch')
        y_row = _t.convert_y(self.labels, y_row)

        init_theta = np.empty((0, ))
        for i in range(len(self.layer_nodes) - 1):
            theta = self.__rand_thetas(self.layer_nodes[i],
                                       self.layer_nodes[i + 1])
            init_theta = np.hstack((init_theta, theta.ravel()))

        self._thetas = self.__extract_thetas(
            op.fmin_cg(
                f=lambda t, x, y: self.__cost(self.__extract_thetas(t), x, y),
                x0=init_theta,
                args=(x_mat, y_row),
                maxiter=self.max_iter,
                fprime=lambda t, x, y: self.__gradient(
                    self.__extract_thetas(t), x, y)))

        return self._thetas
예제 #2
0
파일: linear.py 프로젝트: taowu750/wtml
    def train(self, x_mat: ndarray, y_row: ndarray) -> ndarray:
        """
        进行训练。可以使用梯度下降或正规方程算法进行训练,其中正规方程法对于特征不是很多的情况下,
        比如 n <= 10000,会取得很好的效果。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return 训练的来的参数
        """

        x_mat, y_row = _t.match_x_y(x_mat, y_row)
        n = x_mat.shape[1]

        if self.method == 'gradient':
            self._theta_row = opt.fmin_cg(
                f=lambda t, x, y: self.__cost(t, x, y),
                x0=np.zeros((n, )),
                args=(x_mat, y_row),
                maxiter=self.max_iter,
                fprime=lambda t, x, y: self.__gradient(t, x, y))
        elif self.method == 'normal':
            self._theta_row = self.__normal_eqn(x_mat, y_row)
        else:
            raise ValueError('parameter method must be "gradient" or "normal')

        self.x_mat = x_mat
        self.y_row = y_row

        return self._theta_row
예제 #3
0
파일: linear.py 프로젝트: taowu750/wtml
    def cost(self, x_mat: ndarray, y_row: ndarray) -> float:
        """
        计算在 x_mat 和 y_row 上的代价。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 代价值。
        """

        if self._theta is None:
            raise StateError('not trained yet')

        self._theta, x_mat = _t.match_theta_x(self._theta, x_mat)
        x_mat, y_row = _t.match_x_y(x_mat, y_row)

        if len(self.labels) == 2:
            return self.__cost(self._theta, x_mat, y_row)
        else:
            m = x_mat.shape[0]
            cost_sum = 0
            for i, label in enumerate(self.labels):
                y = y_row == label
                cost_sum = cost_sum + np.sum(y) * self.__cost(
                    self._theta[:, i], x_mat, y) / m

            return cost_sum
예제 #4
0
파일: linear.py 프로젝트: taowu750/wtml
    def train(self, x_mat: ndarray, y_row: ndarray) -> ndarray:
        """
        训练逻辑回归,既可以训练二分类问题,也可以训练多类分类问题。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 参数。二分类问题返回行向量。多类分类问题返回 n*num_labels 的矩阵,每一列表示对应类别的参数。
        """

        x_mat, y_row = _t.match_x_y(x_mat, y_row)
        y_row = _t.convert_y(self.labels, y_row)
        n = x_mat.shape[1]

        if len(self.labels) == 2:
            self._theta = opt.fmin_cg(
                f=lambda t, x, y: self.__cost(t, x, y),
                x0=np.zeros((n, )),
                args=(x_mat, y_row),
                maxiter=self.max_iter,
                fprime=lambda t, x, y: self.__gradient(t, x, y))
        else:
            self._theta = np.zeros((n, len(self.labels)))

            for i, label in enumerate(self.labels):
                self._theta[:, i] = opt.fmin_cg(
                    f=lambda t, x, y: self.__cost(t, x, y == label),
                    x0=np.zeros((n, )),
                    args=(x_mat, y_row),
                    maxiter=self.max_iter,
                    fprime=lambda t, x, y: self.__gradient(t, x, y == label))

        return self._theta
예제 #5
0
파일: linear.py 프로젝트: taowu750/wtml
    def train(self, x_mat: ndarray, y_row: ndarray):
        """
        使用给定的数据对 LDA 学习算法进行训练。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 学习得来的参数
        """

        x_mat, y_row = _t.match_x_y(x_mat, y_row, add_ones=False)
        self._center_projections.clear()

        if len(self.labels) == 2:
            # 两种类别的样本
            x0 = x_mat[y_row == self.labels[0], :]
            x1 = x_mat[y_row == self.labels[1], :]

            # 样本中心点
            u0 = np.mean(x0, axis=0)
            u1 = np.mean(x1, axis=0)

            sw = (x0 - u0).T @ (x0 - u0) + (x1 - u1).T @ (x1 - u1)  # 类内散度矩阵

            self._theta = np.linalg.pinv(sw) @ (u0 - u1)
            self._center_projections[self.labels[0]] = self._theta @ u0
            self._center_projections[self.labels[1]] = self._theta @ u1
        else:
            xn = []
            un = OrderedDict()
            u_mean = 0
            for label in self.labels:
                xn.append(x_mat[y_row == label, :])
                un[label] = np.mean(xn[-1], axis=0)
                u_mean = u_mean + un[label]
            u_mean = u_mean / x_mat.shape[0]

            sw = 0
            sb = 0
            n = u_mean.shape[0]
            for x, u in zip(xn, un.values()):
                sw = sw + (x - u).T @ (x - u)
                sb = sb + x.shape[0] * (u - u_mean).reshape(
                    (n, 1)) @ (u - u_mean).reshape((1, n))

            ev, fv = np.linalg.eig(np.linalg.pinv(sw) @ sb)
            # 去掉 0 特征
            fv = fv[:, np.isclose(ev, 0) == False]
            ev = ev[np.isclose(ev, 0) == False]
            # 从大到小排序
            # TODO: 考虑是否只在压缩的时候进行排序
            indices = np.argsort(ev)[::-1]
            fv = fv[:, indices]

            self._theta = np.real(fv)
            for label in self.labels:
                self._center_projections[label] = self._theta.T @ un[label]

        return self._theta
예제 #6
0
def cost_curve(x_mat: ndarray, y_vec: ndarray, x_cv: ndarray, y_cv: ndarray,
               learner: Callable[[], ISuperviseLearner],
               *, ran: Union[int, Tuple[int], List[int], ndarray] = 0, add_ones: bool = True) \
        -> Tuple[ndarray, ndarray]:
    """
    获取学习曲线值,诊断算法是否有高偏差或高方差问题。

    :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
    :param y_vec: 输出向量,可以是列向量也可以是行向量,每一个值代表 x_mat 中对应行的输出
    :param x_cv: 交叉验证集特征向量组
    :param y_cv: 交叉验证集输出向量
    :param learner: 学习器生成函数,用以生成一个学习器,这个学习器必须有 train 方法用以训练,还要有 cost 方法,
                             用以计算代价
    :param ran: 需要检验的训练集样本范围,默认为 0,表示选取所有的训练集样本。
    :param add_ones: 是否增加截距项
    :return: 训练集代价行向量;验证集代价行向量
    """

    x_mat, y_vec = _t.match_x_y(x_mat, y_vec, add_ones=add_ones)
    x_cv, y_cv = _t.match_x_y(x_cv, y_cv, add_ones=add_ones)

    m = x_mat.shape[0]
    if isinstance(ran, int):
        if ran <= 0 or ran > m:
            ran = np.arange(1, m + 1)
        else:
            ran = np.arange(1, ran + 1)

    cost_train = np.empty((len(ran), ))
    cost_cv = np.empty((len(ran), ))
    for i, interval in enumerate(ran):
        if interval <= 0 or interval > m:
            continue
        xi = x_mat[:interval, :]
        yi = y_vec[:interval]
        lm = learner()
        lm.train(xi, yi)

        cost_train[i] = lm.cost(xi, yi)
        cost_cv[i] = lm.cost(x_cv, y_cv)

    return cost_train, cost_cv
예제 #7
0
    def train(self, x_mat: ndarray, y_row: ndarray):
        """
        进行训练。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        """

        if self.positive_label is not None and self.negative_label is not None and \
                self.positive_label == self.negative_label:
            raise ValueError(
                'positive_label and negative_label cannot be the same')

        x_mat, y_row = _t.match_x_y(x_mat, y_row, add_ones=False)
        self._trained = True
        self._learners.clear()

        if self.strategy == 'ovr':
            for label in self._labels:
                positive_label = label if self.positive_label is None else self.positive_label
                negative_label = label + 1 if self.negative_label is None else self.negative_label
                learner = self.learner(positive_label, negative_label)
                self._learners[label] = learner

                y = y_row.copy()
                # 记录更改之前的坐标,防止 positive_label 和 negative 相等或 negative_label 和 positive 相等这种情况
                positive_idx = y == label
                negative_idx = y != label
                if self.positive_label is not None:
                    y[positive_idx] = self.positive_label
                y[negative_idx] = negative_label
                learner.train(x_mat, y)
        else:
            for positive in self.labels:
                for negative in self.labels:
                    if positive == negative:
                        continue
                    positive_label = positive if self.positive_label is None else self.positive_label
                    negative_label = negative if self.negative_label is None else self.negative_label
                    learner = self.learner(positive_label, negative_label)
                    self._learners[(positive, negative)] = learner

                    y = y_row[(y_row == positive) | (y_row == negative)]
                    x = x_mat[(y_row == positive) | (y_row == negative)]
                    # 记录更改之前的坐标,防止 positive_label 和 negative 相等或 negative_label 和 positive 相等这种情况
                    positive_idx = y == positive
                    negative_idx = y == negative
                    if self.positive_label is not None:
                        y[positive_idx] = self.positive_label
                    if self.negative_label is not None:
                        y[negative_idx] = self.negative_label
                    learner.train(x, y)
예제 #8
0
    def cost(self, x_mat: ndarray, y_row: ndarray) -> float:
        """
        计算在 x_mat 和 y_row 上的代价。此方法只计算参与了分类结果的学习器。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 代价值。
        """

        if not self._trained:
            raise StateError('not trained yet')

        x_mat, y_row = _t.match_x_y(x_mat, y_row, add_ones=False)
        pred = self.__predict(x_mat)
        cost_sum = 0
        m = x_mat.shape[0]

        if self.strategy == 'ovr':
            for i, label in enumerate(pred):
                y = y_row[i]
                if y == label:
                    y = self.positive_label if self.positive_label is not None else label
                else:
                    y = self.negative_label if self.negative_label is not None else label + 1
                cost_sum = cost_sum + self._learners[label].cost(
                    x_mat[i, :], np.array([y]))
        else:
            result = np.empty((m, ))
            for i in range(m):
                r = pred[:, i]
                result[i] = sorted([(np.sum(r == label), label)
                                    for label in set(r)])[-1][1]

            for i in range(m):
                sub_cost_sum = 0
                count = 0
                for j, ((positive, negative),
                        learner) in enumerate(self._learners.items()):
                    if pred[j][i] == result[i]:
                        y = y_row[i]
                        if y == positive:
                            y = self.positive_label if self.positive_label is not None else positive
                        else:
                            y = self.negative_label if self.negative_label is not None else negative
                        sub_cost_sum = sub_cost_sum + learner.cost(
                            x_mat[i, :], np.array([y]))
                        count = count + 1
                if count != 0:
                    cost_sum = cost_sum + sub_cost_sum / count

        return cost_sum / m
예제 #9
0
def regularization_curve(x_mat: ndarray,
                         y_vec: ndarray,
                         x_cv: ndarray,
                         y_cv: ndarray,
                         learner: Callable[[Union[int, float]],
                                           ISuperviseLearner],
                         *,
                         lambs: Union[tuple, list,
                                      ndarray] = (0, 0.001, 0.003, 0.01, 0.03,
                                                  0.1, 0.3, 1, 3, 10)):
    """
    不断选取不同的正则化参数,验证在不同正则化参数下的误差。

    :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
    :param y_vec: 输出向量,可以是列向量也可以是行向量,每一个值代表 x_mat 中对应行的输出
    :param x_cv: 交叉验证集特征向量组
    :param y_cv: 交叉验证集输出向量
    :param learner: 学习器生成函数,参数是正则化参数,用以生成一个学习器,这个学习器必须有 train 方法用以训练,
                             还要有 cost 方法,用以计算代价
    :param lambs: lamb 参数集
    :return: 训练集代价行向量;验证集代价行向量;lambs 参数
    """

    x_mat, y_vec = _t.match_x_y(x_mat, y_vec)
    x_cv, y_cv = _t.match_x_y(x_cv, y_cv)

    lambs_len = len(lambs)
    cost_train = np.empty((lambs_len, ))
    cost_cv = np.empty((lambs_len, ))

    for i, lamb in enumerate(lambs):
        lm = learner(lamb)
        lm.train(x_mat, y_vec)
        cost_train[i] = lm.cost(x_mat, y_vec)
        cost_cv[i] = lm.cost(x_cv, y_cv)

    return cost_train, cost_cv, lambs
예제 #10
0
파일: linear.py 프로젝트: taowu750/wtml
    def cost(self, x_mat: ndarray, y_row: ndarray) -> float:
        """
        计算在 x_mat 和 y_row 上的代价。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 代价值。
        """

        x_mat = self.__match_theta_x(x_mat)
        x_mat, y_row = _t.match_x_y(x_mat, y_row, add_ones=False)

        if len(self.labels) == 2:
            # 两种类别的样本
            x0 = x_mat[y_row == self.labels[0], :]
            x1 = x_mat[y_row == self.labels[1], :]

            # 样本中心点
            u0 = np.mean(x0, axis=0)
            u1 = np.mean(x1, axis=0)

            n = u0.shape[0]
            sw = (x0 - u0).T @ (x0 - u0) + (x1 - u1).T @ (x1 - u1)  # 类内散度矩阵
            sb = (u0 - u1).reshape((n, 1)) @ (u0 - u1).reshape(
                (1, n))  # 类间散度矩阵

            # 和书上分子分母相反,只是因为约定代价值越小越好,而原来的公式是越大越好,故取反
            return (self._theta @ sw @ self._theta) / (
                self._theta @ sb @ self._theta)
        else:
            xn = []
            un = OrderedDict()
            u_mean = 0
            for label in self.labels:
                xn.append(x_mat[y_row == label, :])
                un[label] = np.mean(xn[-1], axis=0)
                u_mean = u_mean + un[label]
            u_mean = u_mean / x_mat.shape[0]

            sw = 0
            sb = 0
            n = u_mean.shape[0]
            for x, u in zip(xn, un.values()):
                sw = sw + (x - u).T @ (x - u)
                sb = sb + x.shape[0] * (u - u_mean).reshape(
                    (n, 1)) @ (u - u_mean).reshape((1, n))

            return np.trace(self._theta.T @ sw @ self._theta) / np.trace(
                self._theta.T @ sb @ self._theta)
예제 #11
0
파일: linear.py 프로젝트: taowu750/wtml
    def cost(self, x_mat: ndarray, y_row: ndarray):
        """
        计算在 x_mat 和 y_row 上的代价。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 代价值
        """

        if self._theta_row is None:
            raise StateError('not trained yet')

        self._theta_row, x_mat = _t.match_theta_x(self._theta_row, x_mat)
        x_mat, y_row = _t.match_x_y(x_mat, y_row)

        return self.__cost(self._theta_row, x_mat, y_row)
예제 #12
0
    def train(self, x_mat: ndarray, y_row: ndarray):
        """
        使用给定的数据对 LDA 学习算法进行训练。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        """

        self.__check_params()
        x_mat, y_row = _t.match_x_y(x_mat, y_row, add_ones=False)
        if x_mat.shape[1] != len(self.pvs):
            raise DataNotMatchError('feature quantity mismatch')

        is_pruning = self.x_cv is not None and self.y_cv is not None
        if is_pruning:
            self._node0 = None
            self._pruning_accuracy = 0
            if self.pruning == 'pre':
                # 预剪枝先考虑只有一个叶结点的情况
                self._root = _DecisionTreeNode()
                self._root.label = self.__find_most_label(y_row)
                self.__pruning_test(self._root)
                # 递归地进行预剪枝
                self._root = self.__generate(x_mat,
                                             y_row,
                                             self.pvs.copy(),
                                             cur_layer=0,
                                             max_layer=1)
            else:
                # 后剪枝需要先生成整棵树,然后从底层的非叶节点开始进行剪枝,比较是否得到了性能提升
                self._stack = []
                self._root = self.__generate(x_mat, y_row.ravel(),
                                             self.pvs.copy())
                self.__pruning_test(self._root)
                while len(self._stack) > 0:
                    node, label = self._stack.pop(-1)
                    children_num = node.children_num()
                    node.sl_prop()
                    node.label = label
                    # 如果剪枝后性能得到提升,就剪枝,否则就保留
                    if self.__pruning_test(self._root):
                        self._size = self._size - children_num
                        node.sl_prop(delete=True)
                    else:
                        node.sl_prop(False)
        else:
            self._root = self.__generate(x_mat, y_row.ravel(), self.pvs.copy())
예제 #13
0
    def cost(self, x_mat: ndarray, y_row: ndarray) -> float:
        """
        计算在 x_mat 和 y_row 上的代价。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 代价值。
        """

        if self._thetas is None:
            raise StateError('not trained yet')

        x_mat, y_row = _t.match_x_y(x_mat, y_row)
        if x_mat.shape[1] - 1 != self.layer_nodes[0]:
            raise DataNotMatchError(
                'feature number and input layer node number mismatch')

        return self.__cost(self._thetas, x_mat, y_row)
예제 #14
0
    def cost(self, x_mat: ndarray, y_row: ndarray) -> float:
        """
        计算在 x_mat 和 y_row 上的代价。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出
        :return: 代价值。
        """

        if self._root is None:
            raise StateError('not trained yet')

        x_mat, y_row = _t.match_x_y(x_mat, y_row, add_ones=False)
        if x_mat.shape[1] != len(self.pvs):
            raise DataNotMatchError('feature quantity mismatch')

        # TODO: 暂时先用错误率作为代价值,以后想想有什么更好的方法
        return 1 - _ms.accuracy(self.__predict(self._root, x_mat), y_row)
예제 #15
0
    def train(self, x_mat: ndarray, y_row: ndarray):
        """


        :param x_mat:
        :param y_row:
        :return:
        """

        x_mat, y_row = _t.match_x_y(x_mat, y_row, add_ones=False)
        y_row = _t.u2i_dtype(y_row)
        y_row = _t.convert_y(self.labels, y_row, to_labels=np.array([1, -1]))

        m, n = x_mat.shape
        if self.kernel == 'linear':
            k_mat = x_mat @ x_mat.T
        elif self.kernel == 'gauss':
            if not self.gamma:
                self.gamma = 1 / n
            k_mat = np.empty((m, m), dtype=np.float)
            for i, xi in enumerate(x_mat):
                for j in range(i, m):
                    k_mat[i, j] = gaussian_kernel(xi, x_mat[j], self.gamma)
                    k_mat[j, i] = k_mat[i, j]
        else:
            k_mat = np.empty((m, m), dtype=np.float)
            for i, xi in enumerate(x_mat):
                for j in range(i, m):
                    k_mat[i, j] = self.kernel(xi, x_mat[j])
                    k_mat[j, i] = k_mat[i, j]
        self._alphas = np.zeros((m, ), dtype=np.float)
        self._b = 0
        self._errors = self._calc_errors(k_mat, y_row)
        print('params initialized')

        iter_num = 0
        iter_all = True
        changed_num = 0
        while (iter_num < self.max_iter) and (changed_num > 0 or iter_all):
            changed_num = 0
            # 在整个样本集和非边界样本集之间切换
            if iter_all:
                for i1 in range(m):
                    # 运行 smo 过程
                    changed_num = changed_num + self._smo(k_mat, y_row, i1)
            else:
                invalid_list = np.argwhere((self._alphas <= 0) | (self._alphas >= self.c)).ravel() if self._c else \
                    np.argwhere(self._alphas <= 0).ravel()
                for i1 in invalid_list:
                    # 运行 smo 过程
                    changed_num = changed_num + self._smo(k_mat, y_row, i1)
            if iter_all:
                iter_all = False
            elif changed_num == 0:
                iter_all = True
            iter_num = iter_num + 1
            print(iter_num, ',', changed_num, ':',
                  self._alphas[self._alphas > 0])
        # 只使用支持向量构造划分超平面法向量和其他参数
        idx = self._alphas > 0
        self._x_mat = x_mat[idx, :]
        self._y_row = y_row[idx]
        self._alphas = self._alphas[idx]
        self._theta = self._alphas * self._y_row @ self._x_mat