【机器学习】逻辑回归的C++实现

代码框架与感知机一致,区别仅在于train和test函数上。

train

逻辑回归和感知机一样,也是来解决二分类问题。但输出的为当前标签为1的概率,所以和感知机只训练错误样例不同,我们需要对每个样本进行训练。

我们需要的输出范围为[0, 1],但是线性方程\(f(x) = wx + b\)的范围为负无穷到正无穷,所以我们要对输出进行变化(感知机的变换函数为符号函数)。于是我们使用sigmoid函数,sigmoid除了能够将输入调整到0到1的功能外,还具备很好的导数性质。sigmoid函数表示为:

\[f(z)=\frac{1}{1 + e^{-z}} \]

则逻辑回归的整体数学形式为:

\[y = \frac{1}{1 + e^{-wx}} \]

输出的值为样本为1的概率,为了方便后续计算,我们将其设为\(p\),即:

\[P(y=1 \mid x)=p=\frac{1}{1 + e^{-wx}} \]

上式对\(w\)求导,可以得到\(p'=p(1-p)x\),其导数形式也非常简单,有利于后续的参数更新。

在当前\(w\)参数下,对于任意输入样本,逻辑回归的预测结果概率可以表示为:

\[P(y \mid x)=\left\{\begin{array}{r} p, \quad y=1 \\ 1-p, \quad y=0 \end{array}\right. \]

综合起来可以写为:

\[P(y \mid x)=p^{y}(1-p)^{1-y} \]

我们要求得一组\(w\)参数,使当前抽样样本发生的概率最大,也就是取极大似然估计,再加上负号就可以定义为逻辑回归的损失函数。所以我们对所有概率连乘,两边取log变成连加,再取负号,得到逻辑回归的损失函数:

\[F(x)=-\sum{ylogp + (1-y)log(1-p)} \]

得到目标函数后,我们对\(w\)求偏导,得\(w\_delta[i] = -(y[i]-p[i])x[i]\)。我们使用随机梯度下降的方法,每次输入样本后都能更新\(w = w - h * w\_delta\)

void train(int itera) {
    int m = train_set.size();
    int n = train_set[0].size();
    w.assign(n + 1, 0); // w与b合并,维度+1
    double h = 0.0001;
    for (int i = 0; i < itera; i++) { // 开始迭代
        for (int j = 0; j < m; j++) {
            Vec xi = train_set[j]; // 当前训练数据,一维向量
            double yi = train_label[j]; // 当前label
            double exp_wx = exp(mul_vv(w, xi)); // 先计算好,避免重复运算
            Vec w_delta = mul_vd(xi, h * (yi - exp_wx / (1 + exp_wx))); // 计算w更新值
            w = add_vv(w, w_delta); // 更新w
        }
    }
}

test

测试时,只需要计算预测为1的概率p即可。p>=0.5则预测为1,否则预测为0。

double test() {
    int m = test_set.size();
    int n = test_set[0].size();
    double err_cnt = 0;
    for (int i = 0; i < m; i++) {
        Vec xi = test_set[i];
        double yi = test_label[i];
        double exp_wx = exp(mul_vv(w, xi));
        double predict = exp_wx / (1 + exp_wx);
        double res = predict >= 0.5 ? 1 : 0;
        if (res != yi) err_cnt++;
    }
    double acc_rate = 1 - (err_cnt / m);
    return acc_rate;
}
posted @ 2021-03-16 19:15  tmpUser  阅读(424)  评论(0编辑  收藏  举报