Example #1
0
    def __check_params(self):
        pvs = self.pvs
        x_cv = self.x_cv
        y_cv = self.y_cv
        pruning = self.pruning

        if x_cv is not None and x_cv.shape[1] != len(pvs):
            raise DataNotMatchError('feature quantity mismatch')
        if (x_cv is not None and y_cv is None) or (x_cv is None
                                                   and y_cv is not None):
            raise ValueError('x_val and y_val must all be none or all exist')
        if (x_cv is not None
                and y_cv is not None) and (x_cv.shape[0] != y_cv.shape[0]):
            raise DataNotMatchError('number of samples does not match')
        if (x_cv is not None and y_cv is not None) and pruning is None:
            raise ValueError('pruning must be "post" or "pre"')
Example #2
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
Example #3
0
def __check_predict_actual_y(predict_y: ndarray, actual_y: ndarray, *, name: str = 'predict_y') \
        -> Tuple[ndarray, ndarray]:
    predict_y = _t.c2r(predict_y)
    actual_y = _t.c2r(actual_y)
    if predict_y.shape[0] != actual_y.shape[0]:
        raise DataNotMatchError('number of %s and actual_y do not match' %
                                name)

    return predict_y, actual_y
Example #4
0
def match_x_y(x_mat: ndarray,
              y_vec: ndarray,
              add_ones: bool = True) -> Tuple[ndarray, ndarray]:
    """
    检验 x_mat 和 y_vec 是否一致,不一致抛出异常。

    :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
    :param y_vec: 输出向量,可以是列向量也可以是行向量,每一个值代表 x_mat 中对应行的输出
    :param add_ones: 是否给 x_mat 增加截距列
    :return: 检验完后的 x_mat;检验完后的 y_vec
    """

    if add_ones:
        x_mat = addones(x_mat)
    else:
        x_mat = r2m(x_mat)
    y_vec = c2r(y_vec)
    if len(y_vec.shape) == 0:
        if x_mat.shape[0] != 1:
            raise DataNotMatchError('number of samples does not match')
    elif x_mat.shape[0] != y_vec.shape[0]:
        raise DataNotMatchError('number of samples does not match')

    return x_mat, y_vec
Example #5
0
    def predict(self, x_mat: ndarray) -> Union[ndarray, int]:
        """
        对新的数据进行预测。当 x_mat 中只有一个样本时返回一个数字,否则返回一个行向量。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :return: 预测值或向量
        """

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

        x_mat = _t.r2m(x_mat)
        if x_mat.shape[1] != len(self.pvs):
            raise DataNotMatchError('feature quantity mismatch')

        return self.__predict(self._root, x_mat)
Example #6
0
    def __match_theta_x(self, x_mat: ndarray) -> ndarray:
        """
        检查输入是否和参数匹配。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :return:
        """

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

        x_mat = _t.r2m(x_mat)
        if x_mat.shape[1] != self._theta.shape[0]:
            raise DataNotMatchError('feature quantity mismatch')

        return x_mat
Example #7
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())
Example #8
0
    def probability(self, x_mat: ndarray) -> Union[ndarray, float]:
        """
        返回对应于 x_mat 的预测概率。如果是二分类问题,那么返回一个行向量;如果是多分类问题,返回一个
        m*num_labels 的矩阵,其中每一行表示样本在每个类上的概率。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :return: 预测概率。
        """

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

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

        return _t.ret(_t.c2r(self.__feedforward(self._thetas, x_mat)[-1]))
Example #9
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)
Example #10
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)
Example #11
0
def match_theta_x(theta: ndarray, x_mat: ndarray) -> Tuple[ndarray, ndarray]:
    """
    检查 x_mat 特征数是否和 theta_vec 一致,不一致给 x_mat 加上一列截距列。还是不一致抛出异常。
    注意此方法只能用在线性回归和逻辑回归中。

    :param theta: 参数向量,可以是列向量、行向量或者矩阵
    :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
    :return: 检验完后的 x_mat;检验完后的 theta_vec
    """

    x_mat = r2m(x_mat)
    theta = c2r(theta)

    m = x_mat.shape[0]
    if x_mat.shape[1] + 1 == theta.shape[0]:
        x_mat = np.hstack((np.ones((m, 1)), x_mat))
    if x_mat.shape[1] != theta.shape[0]:
        raise DataNotMatchError('feature quantity mismatch')

    return theta, x_mat
Example #12
0
    def predict(self, x_mat: ndarray):
        """
        返回预测值,是对应于 x_mat 的标记。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :return: 预测标记
        """

        if self._theta is None:
            raise StateError('not trained yet')
        x_mat = _t.r2m(x_mat)
        if x_mat.shape[1] != self._theta.shape[0]:
            raise DataNotMatchError('feature quantity mismatch')

        if self.kernel == 'linear':
            pred = x_mat @ self._theta + self._b
        elif self.kernel == 'gauss':
            m = x_mat.shape[0]
            pred = np.empty((m, ))
            for i in range(m):
                for j in range(self._x_mat.shape[0]):
                    pred[i] = pred[i] + self._alphas[
                        j] * self._y_row[j] * gaussian_kernel(
                            x_mat[i], self._x_mat[j], self.gamma)
                pred[i] = pred[i] + self._b
        else:
            m = x_mat.shape[0]
            pred = np.empty((m, ))
            for i in range(m):
                for j in range(self._x_mat.shape[0]):
                    pred[i] = pred[i] + self._alphas[j] * self._y_row[
                        j] * self.kernel(x_mat[i], self._x_mat[j])
                pred[i] = pred[i] + self._b

        return _t.ret(
            _t.convert_y(self.labels, (pred >= 0).astype(dtype=np.int16),
                         to=False))
Example #13
0
    def predict(self, x_mat: ndarray) -> Union[ndarray, int]:
        """
        返回预测值,是对应于 x_mat 的标记。

        :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
        :return: 预测标记
        """

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

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

        a = self.__feedforward(self._thetas, x_mat)[-1]
        if len(self.labels) == 2:
            return _t.ret(
                _t.convert_y(self.labels,
                             _t.c2r(a >= self.threshold),
                             to=False))
        else:
            return _t.ret(self.labels[np.argmax(a, axis=1)])
Example #14
0
def feature_normalize(x_mat: ndarray, mean_row: ndarray = None, std_row: ndarray = None) \
        -> Tuple[ndarray, ndarray, ndarray]:
    """
    将 x_mat 的特征规范化和归一化,也就是缩放以使得不同的参数之间差距不要太大。
    如果参数 mean_col 和 std_col 都提供了的话,就使用它们进行规范;否则就计算新的 mean_col 和 std_col。
    可以用在线性回归、SVM、PCA 中。
    截距列不会被规范化。

    :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数
    :param mean_row: 每列特征值的平均值行向量
    :param std_row: 每列特征值的标准行向量
    :return: 规范化后的特征向量组,原来的特征向量组不会被影响;每个特征的平均值向量;每个特征的标准差向量
    """

    x_mat, has_ones = __t.delones(x_mat)
    if mean_row is not None:
        mean_row = __t.c2r(mean_row)
    if std_row is not None:
        std_row = __t.c2r(std_row)
    n = x_mat.shape[1]
    if mean_row is not None and std_row is not None and \
            n != mean_row.shape[0] and mean_row.shape[0] != std_row.shape[0]:
        raise DataNotMatchError(
            'x_mat\'s feature num does not match mean_row, std_row')

    # 转换数据类型防止计算精度丢失
    x_norm = __t.i2f_dtype(x_mat.copy())
    if mean_row is None and std_row is None:
        mean_row = np.mean(x_norm, axis=0)
        # ddof 设置为 1 表示计算样本方差,不设置表示计算总体方差
        std_row = np.std(x_norm, ddof=1, axis=0)
    x_norm = (x_norm - mean_row) / std_row
    if has_ones:
        x_norm = np.hstack((np.ones((x_norm.shape[0], 1)), x_norm))

    return x_norm, mean_row, std_row
Example #15
0
 def __check_params(self):
     if (len(self.labels) == 2 and self.layer_nodes[-1] != 1) or \
             (len(self.labels) != self.layer_nodes[-1]):
         raise DataNotMatchError(
             'number of labels does not match number of output layer nodes')