def probability(self, x_mat: ndarray) -> Union[ndarray, float]: """ 返回对应于 x_mat 的预测概率。如果是二分类问题,那么返回一个行向量;如果是多分类问题,返回一个 m*num_labels 的矩阵,其中每一行表示样本在每个类上的概率。 :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数 :return: 预测概率。 """ if self._theta is None: raise StateError('not trained yet') self._theta, x_mat = _t.match_theta_x(self._theta, x_mat) return _mf.sigmoid(x_mat @ self._theta)
def __cost(self, theta_row: ndarray, x_mat: ndarray, y_row: ndarray) -> float: """ 计算代价值。 :param theta_row: 参数 :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数 :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出 :return: 代价值 """ m = x_mat.shape[0] hx = _mf.sigmoid(x_mat @ theta_row) thetan = theta_row[1:] # FIXME: 当 hx 中包含 0 或 1 时,会导致 log 出现警告并终止迭代 cost = -(y_row @ np.log(hx) + (1 - y_row) @ np.log(1 - hx)) / m + self.lamb * sum( thetan**2) / (2 * m) return cost
def __feedforward(thetas: Union[List[ndarray], Tuple[ndarray]], x_mat: ndarray) -> list: """ 正向传播,计算分类问题的预测概率。 :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数 :param thetas: 参数向量组。每一个 theta 表示对应层的参数 :return: 一个 list,存有每一层计算后的 ai 和 zi(除了 an,其他 ai 都加上了一列偏置列) """ r = [] a = x_mat for i, theta in enumerate(thetas): z = a @ theta.T r.append(z) a = _mf.sigmoid(z) if i != len(thetas) - 1: a = np.hstack((np.ones((a.shape[0], 1)), a)) r.append(a) return r
def __gradient(self, theta_row: ndarray, x_mat: ndarray, y_row: ndarray) -> ndarray: """ 计算梯度。 :param theta_row: 参数 :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数 :param y_row: 输出行向量,每一个值代表 x_mat 中对应行的输出 :return: 梯度,是一个行向量 """ m = x_mat.shape[0] thetan = theta_row[1:] xn = x_mat[:, 1:] hx = _mf.sigmoid(x_mat @ theta_row) # 如果不展开的话,就是个只有一个元素的二维数组 grad0 = ((x_mat[:, :1].T @ (hx - y_row)) / m).ravel() gradn = ((xn.T @ (hx - y_row)) / m + self.lamb * thetan / m).ravel() return np.hstack((grad0, gradn))
def predict(self, x_mat: ndarray) -> Union[ndarray, int]: """ 返回预测值,是对应于 x_mat 的标记。 :param x_mat: 特征向量组,行数 m 表示样本数,列数 n 表示特征数 :return: 预测标记 """ if self._theta is None: raise StateError('not trained yet') self._theta, x_mat = _t.match_theta_x(self._theta, x_mat) prob = x_mat @ self._theta if len(self.labels) == 2: return _t.ret( _t.convert_y(self.labels, _mf.sigmoid(prob) >= self.threshold, to=False)) else: return _t.ret(self.labels[np.argmax(prob, axis=1)])