AlgoC++第四课:逻辑回归

news2024/10/6 8:33:54

目录

  • 逻辑回归
    • 前言
    • 1. 鸢尾花分类问题
      • 1.1 基于线性回归的思考
      • 1.2 损失函数
    • 2. 伯努利分布
    • 3. 示例代码
      • 3.1 数据可视化
      • 3.2 日志信息打印
      • 3.3 数据读取和处理
      • 3.4 逻辑回归模型
      • 3.5 完整示例代码
      • 3.6 python实现
    • 4. 思考
    • 总结

逻辑回归

前言

手写AI推出的全新面向AI算法的C++课程 Algo C++,链接。记录下个人学习笔记,仅供自己参考。

本次课程主要讲解逻辑回归

课程大纲可看下面的思维导图

在这里插入图片描述

1. 鸢尾花分类问题

我们先从一个实际问题出发

假设现在我们想要对两种鸢尾花进行分类,现在我们可以观察到鸢尾花的两个特征:花瓣个数和直径大小。那么我们该如何构建一个自动分类器,让算法可以根据这两个特征识别出当前的鸢尾花属于哪个类别呢?我们不妨把已经获得的数据画出来看一下,假设如下图所示,横坐标表示花瓣个数特征,纵坐标表示直径大小特征。

在这里插入图片描述

1.1 基于线性回归的思考

上节课我们讲解了通过线性回归解决房价预测问题,那么我们是否可以训练一个线性回归模型,通过判断输出值的大小来决定是什么类别?

考虑二分类问题,假设我们认为线性回归模型输出值大于等于0.5时为1,小于0.5时为0,因此我们希望存在一个函数 h θ ( x ) h_{\theta}(x) hθ(x) 使得对于线性回归模型的值,能够映射到0-1范围内,表示如下:
f ( x ) = { 1 , h θ ( x ) ⩾ 0.5 0 , h θ ( x ) < 0.5 f(x)=\begin{cases}1,h_\theta(x)\geqslant0.5\\ 0,h_\theta(x)<0.5\end{cases} f(x)={1,hθ(x)0.50,hθ(x)<0.5
我们选择一个满足值域0-1特性的函数来作为映射函数:
g ( z ) = 1 1 + e − z g(z) = \frac{1}{1+e^{-z}} g(z)=1+ez1
函数(sigmoid)的图像如下:

在这里插入图片描述

函数的性质是,当 z z z 值为0时,函数值为0.5,当 z z z 趋于无穷大时,函数值趋于1,当 z z z 趋于无穷小时,函数值趋于0

我们回头看这个公式:
h θ ( x ) = 1 1 + e − ( k x + b ) h_\theta(x) =\frac{1}{1+e^{-(kx+b)}} hθ(x)=1+e(kx+b)1
注意 k x + b kx+b kx+b 是特指一元逻辑回归,对于多元,常用 θ T x \theta ^Tx θTx 表示,由上式可得:

ln ⁡ h θ ( x ) 1 − h θ ( x ) = k x + b \ln\dfrac{h_\theta(x)}{1-h_\theta(x)}=kx+b ln1hθ(x)hθ(x)=kx+b
模型预测的是正反例的对数概率,如果我们把 h θ ( x ) h_{\theta}(x) hθ(x) 视为样本 x x x 为正例的可能性,那么 1 − h θ ( x ) 1-h_{\theta}(x) 1hθ(x) 即为负例可能性,两者的比值的对数,称为对数几率,这也是为什么逻辑回归也称为对数几率回归。同样,反过来,我们通过对样本正反例可能性的伯努利分布推导(和指数族定义),也能得到上式的结果,因此,选择 sigmoid 的原因在于它符合描述样本 x 为正反例的可能性,而逻辑回归则是对该可能性建模,求解参数极大似然估计的过程

参考https://www.cnblogs.com/hustlx/p/5391772.html

参考https://blog.csdn.net/qq_31589695/article/details/79936938

1.2 损失函数

假设我们依旧按照线性回归的方式,代入下式:
L ( θ ) = 1 2 ∑ i = 1 m ( y ( i ) − h θ ( x ( i ) ) ) 2 L(\theta)=\dfrac{1}{2}\sum_{i=1}^m(y^{(i)}-h_\theta(x^{(i)}))^2 L(θ)=21i=1m(y(i)hθ(x(i)))2
其中, h θ ( x ) = 1 1 + e − ( k x + b ) h_\theta(x) =\frac{1}{1+e^{-(kx+b)}} hθ(x)=1+e(kx+b)1 , 其函数和导数图像如下:

在这里插入图片描述

这样的话,函数的导数值在多个区域接近0,使得在部分位置时难以迭代,不利于求解全局最优解,因此,直接采用线性回归的方式,可能在某些时候具备效果,但在理论上并不是合适的做法,也因此引出我们的主题,逻辑回归

2. 伯努利分布

对于上述分类问题中,y 取值 0,1 服从伯努利分布,则有:

y为1时的概率,表示为:
P ( y = 1 ∣ x ; θ ) = h θ ( x ) P(y=1|x;\theta)=h_\theta(x) P(y=1∣x;θ)=hθ(x)
y为0时的概率,表示为:
P ( y = 0 ∣ x ; θ ) = 1 − h θ ( x ) P(y=0|x;\theta)=1 - h_\theta(x) P(y=0∣x;θ)=1hθ(x)
简化表示为:
P ( y ∣ x ; θ ) = h θ ( x ) y ( 1 − h θ ( x ) ) 1 − y , y = 0 , 1 P(y|x;\theta)=h_\theta(x)^y(1-h_\theta(x))^{1-y},y=0,1 P(yx;θ)=hθ(x)y(1hθ(x))1y,y=0,1
为了估计参数 θ \theta θ

我们有似然函数L:
L ( θ ) = P ( y ⃗ ∣ x ; θ ) = ∏ i = 1 m P ( y ( i ) ∣ x ( i ) ; θ ) L(\theta)=P(\vec{y}|x;\theta)=\prod_{i=1}^{m}P(y^{(i)}|x^{(i)};\theta) L(θ)=P(y x;θ)=i=1mP(y(i)x(i);θ)
代入后有:
L ( θ ) = ∏ i = 1 m h θ ( x ( i ) ) y ( i ) ( 1 − h θ ( x ( i ) ) ) 1 − y ( i ) L(\theta)=\prod_{i=1}^{m}h_{\theta}(x^{(i)})^{y^{(i)}}(1-h_{\theta}(x^{(i)}))^{1-y^{(i)}} L(θ)=i=1mhθ(x(i))y(i)(1hθ(x(i)))1y(i)
我们要计算参数 θ \theta θ 的极大似然估计,找到参数 θ \theta θ 使得似然性函数 L ( θ ) L(\theta) L(θ)值最大化,即是为找到一组参数 θ \theta θ 使得:如果 y 为正类,模型预测 y 为1的概率最大,如果 y 为负类,模型预测 y 为0的概率最大

对于似然性最大化,可以使用似然性的对数最大化要比似然性最大化简单许多,我们有:
L ( θ ) = log ⁡ L ( θ ) L(\theta) = \log L(\theta) L(θ)=logL(θ)
展开后得:
L ( θ ) = ∑ i = 1 m y ( i ) log ⁡ h θ ( x ( i ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − h θ ( x ( i ) ) ) L(\theta)=\sum\limits_{i=1}^m y^{(i)}\log h_\theta(x^{(i)})+(1-y^{(i)})\log(1-h_\theta(x^{(i)})) L(θ)=i=1my(i)loghθ(x(i))+(1y(i))log(1hθ(x(i)))
上述式子也叫交叉熵(注意这里没有加负号),我们希望得到的是最大化值的参数估计,相当于是求 L 函数的最大值,对于求最大值,相比线性回归,我们唯一区别是这里使用了梯度上升法,仅仅是减改为加
θ + = θ + η ⋅ ∂ L ( θ ) ∂ θ \theta^{+}=\theta+\eta\cdot\frac{\partial L(\theta)}{\partial\theta} θ+=θ+ηθL(θ)
求导得
∂ L ( θ ) ∂ θ j = ( y − h θ ( x ) ) x j \dfrac{\partial L(\theta)}{\partial\theta_j} = (y-h_\theta(x)) x_j θjL(θ)=(yhθ(x))xj
我们得到每一个参数得更新公式如下:
θ j + = θ j + α ( y ( i ) − h θ ( x ( i ) ) ) x j ( i ) \theta_j^+=\theta_j+\alpha(y^{(i)}-h_\theta(x^{(i)}))x_j^{(i)} θj+=θj+α(y(i)hθ(x(i)))xj(i)
其中 j j j 是下标,相当于是 x x x 的第 j j j 个参数

将其与之前的 MSE 图像对比如下:

在这里插入图片描述

3. 示例代码

本次以居民幸福感和住房面积及离地铁距离为例进行一个二分类问题的讨论

3.1 数据可视化

我们通过下面的代码将数据进行可视化

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = pd.read_csv("workspace/shanghai.csv")
happiness   = data.query("happiness == 1")
unhappiness = data.query("happiness == 0")

plt.plot(happiness.area,   happiness.distance, "b*", label="happiness")
plt.plot(unhappiness.area, unhappiness.distance, "r*", label="unhappiness")
plt.legend()
plt.xlim([0, 500])
plt.ylim([0, 6000])
plt.xlabel("House Area")
plt.ylabel("Distance to subway")
plt.title("Are you living in happiness?")
plt.savefig("figure.jpg")

在这里插入图片描述

3.2 日志信息打印

日志信息打印的构建代码如下

namespace logger{

#define INFO(...)  Application::logger::__printf(__FILE__, __LINE__, __VA_ARGS__)

void __printf(const char* file, int line, const char* fmt, ...){

va_list vl;
va_start(vl, fmt);

// None   = 0,     // 无颜色配置
// Black  = 30,    // 黑色
// Red    = 31,    // 红色
// Green  = 32,    // 绿色
// Yellow = 33,    // 黄色
// Blue   = 34,    // 蓝色
// Rosein = 35,    // 品红
// Cyan   = 36,    // 青色
// White  = 37     // 白色
/* 格式是: \e[颜色号m文字\e[0m   */
printf("\e[32m[%s:%d]:\e[0m ", file, line);
vprintf(fmt, vl);
printf("\n");
}
};

上述示例代码通过宏定义实现了一个简单的日志输出功能,可以方便地输出包含文件名和行号的日志信息,方便程序调试和定位问题。

3.3 数据读取和处理

数据读取和处理代码如下

#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <tuple>
#include <iomanip>
#include <stdarg.h>

namespace io{

    /* 通过csv文件加载数据 */
    vector<Item> load_data(const string& file){

        vector<Item> output;
        fstream ifile(file, ios::binary | ios::in);

        string line;
        getline(ifile, line);

        while(getline(ifile, line)){
            int p0 = line.find(",");
            int p1 = line.find(",", p0 + 1);
            Item item;
            item.area     = atof(line.substr(0, p0).c_str());
            item.distance = atof(line.substr(p0+1, p1).c_str());
            item.label    = atof(line.substr(p1+1).c_str());

            // cout << item.area << ", " << item.distance << ", " << item.label << endl;
            output.emplace_back(item);
        }
        return output;
    }
};

namespace statistics{

    /* 计算数据的均值和标准差 */
    tuple<Item, Item> compute_mean_std(const vector<Item>& items){

        Item mean{0, 0}, stdval{0, 0};

        for(auto& item : items){
            mean.area  += item.area;
            mean.distance += item.distance;
        }
        mean.area  /= items.size();
        mean.distance /= items.size();

        for(auto& item : items){
            stdval.area  += std::pow(item.area - mean.area, 2.0f);
            stdval.distance += std::pow(item.distance - mean.distance, 2.0f);;
        }
        stdval.area  = std::sqrt(stdval.area / items.size());
        stdval.distance = std::sqrt(stdval.distance / items.size());
        return make_tuple(mean, stdval);
    }
};

上述示例代码中,在 io 命名空间下的 load_data 函数中,通过读取 csv 文件,将数据加载到一个 vector<Item> 类型的数组中,每个 Item 结构体包含住房面积、离地铁站距离两个特征以及是否幸福标签。然后使用 statistics 命名空间下的 compute_mean_std 函数计算数据的均值和标准差,对数据进行归一化处理,使其均值为0,标准差为1。

3.4 逻辑回归模型

逻辑回归模型的构建代码如下

int run(){

auto datas = io::load_data("shanghai.csv");

Item mean, stdval;
tie(mean, stdval) = statistics::compute_mean_std(datas);

/* 对数据进行减去均值除以标准差,使得均值为0,标准差为1 */
for(auto& item : datas){
item.area     = (item.area - mean.area) / stdval.area;
item.distance = (item.distance - mean.distance) / stdval.distance;
}

float k_area = 0.1;
float k_dist = 0.1;
float b = 0;
float lr = 0.1;

for(int iter = 0; iter < 1000; ++iter){

float loss = 0;
float delta_k_area = 0;
float delta_k_dist = 0;
float delta_b      = 0;

for(auto& item : datas){
float predict = k_area * item.area + k_dist * item.distance + b;
double logistic = nn::sigmoid(predict);
float L = -(std::log(logistic) * item.label + std::log(1 - logistic) * (1 - item.label));

delta_k_area += (logistic - item.label) * item.area;
delta_k_dist += (logistic - item.label) * item.distance;
delta_b      += (logistic - item.label);
loss += L;
}

if(iter % 100 == 0)
cout << "Iter " << iter << ", Loss: " << setprecision(3) << loss << endl;

k_area = k_area - lr * delta_k_area;
k_dist = k_dist - lr * delta_k_dist;
b      = b      - lr * delta_b;
}

INFO("模型参数: k_area = %f, k_dist = %f, b = %f", k_area, k_dist, b);
INFO("数据集: area_mean = %f, dist_mean = %f, area_std = %f, dist_std = %f", 
mean.area, mean.distance, stdval.area, stdval.distance
);

INFO(""
"k_area = %f\n"
"k_distance = %f\n"
"b = %f\n"
"area_mean = %f\n"
"area_std = %f\n"
"distance_mean = %f\n"
"distance_std = %f"
, k_area, k_dist, b, mean.area, stdval.area, mean.distance, stdval.distance);

float area          = 100;
float distance      = 2000;
float norm_area     = (area - mean.area) / stdval.area;
float norm_dist     = (distance - mean.distance) / stdval.distance;
float predict       = k_area * norm_area + k_dist * norm_dist + b;
float logistic      = nn::sigmoid(predict);

INFO("在上海,对于房屋面积 %.0f 平方米,距离地铁 %.0f 米的住户而言,他们觉得生活是 【%s】 的.",
area, distance, logistic > 0.5 ? "幸福" : "并不幸福"
);
return 0;
}

在上述示例代码中,我们采用逻辑回归模型来做幸福感二分类问题,其中 k_area、k_dist分别是权重系数,b 是偏置项。对每个数据项计算预测值 predict 和损失函数 L。然后分别计算权重系数和偏置项的梯度 delta_k_area、delta_k_dist、delata_b,用于更新权重和偏置项

3.5 完整示例代码

完整示例代码如下

#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <tuple>
#include <iomanip>
#include <stdarg.h>

using namespace std;

namespace Application{

    struct Item{
        float area;
        float distance;
        float label;
    };

    namespace logger{

        #define INFO(...)  Application::logger::__printf(__FILE__, __LINE__, __VA_ARGS__)

        void __printf(const char* file, int line, const char* fmt, ...){

            va_list vl;
            va_start(vl, fmt);

            // None   = 0,     // 无颜色配置
            // Black  = 30,    // 黑色
            // Red    = 31,    // 红色
            // Green  = 32,    // 绿色
            // Yellow = 33,    // 黄色
            // Blue   = 34,    // 蓝色
            // Rosein = 35,    // 品红
            // Cyan   = 36,    // 青色
            // White  = 37     // 白色
            /* 格式是: \e[颜色号m文字\e[0m   */
            printf("\e[32m[%s:%d]:\e[0m ", file, line);
            vprintf(fmt, vl);
            printf("\n");
        }
    };

    namespace io{

        /* 通过csv文件加载数据 */
        vector<Item> load_data(const string& file){

            vector<Item> output;
            fstream ifile(file, ios::binary | ios::in);

            string line;
            getline(ifile, line);

            while(getline(ifile, line)){
                int p0 = line.find(",");
                int p1 = line.find(",", p0 + 1);
                Item item;
                item.area     = atof(line.substr(0, p0).c_str());
                item.distance = atof(line.substr(p0+1, p1).c_str());
                item.label    = atof(line.substr(p1+1).c_str());

                // cout << item.area << ", " << item.distance << ", " << item.label << endl;
                output.emplace_back(item);
            }
            return output;
        }
    };

    namespace statistics{

        /* 计算数据的均值和标准差 */
        tuple<Item, Item> compute_mean_std(const vector<Item>& items){

            Item mean{0, 0}, stdval{0, 0};

            for(auto& item : items){
                mean.area  += item.area;
                mean.distance += item.distance;
            }
            mean.area  /= items.size();
            mean.distance /= items.size();

            for(auto& item : items){
                stdval.area  += std::pow(item.area - mean.area, 2.0f);
                stdval.distance += std::pow(item.distance - mean.distance, 2.0f);;
            }
            stdval.area  = std::sqrt(stdval.area / items.size());
            stdval.distance = std::sqrt(stdval.distance / items.size());
            return make_tuple(mean, stdval);
        }
    };

    namespace nn{

        double sigmoid(double x){
            return 1 / (1 + std::exp(-x));
        }
    };
    
    int run(){

        auto datas = io::load_data("shanghai.csv");

        Item mean, stdval;
        tie(mean, stdval) = statistics::compute_mean_std(datas);

        /* 对数据进行减去均值除以标准差,使得均值为0,标准差为1 */
        for(auto& item : datas){
            item.area     = (item.area - mean.area) / stdval.area;
            item.distance = (item.distance - mean.distance) / stdval.distance;
        }
        
        float k_area = 0.1;
        float k_dist = 0.1;
        float b = 0;
        float lr = 0.1;

        for(int iter = 0; iter < 1000; ++iter){

            float loss = 0;
            float delta_k_area = 0;
            float delta_k_dist = 0;
            float delta_b      = 0;

            for(auto& item : datas){
                float predict = k_area * item.area + k_dist * item.distance + b;
                double logistic = nn::sigmoid(predict);
                float L = -(std::log(logistic) * item.label + std::log(1 - logistic) * (1 - item.label));

                delta_k_area += (logistic - item.label) * item.area;
                delta_k_dist += (logistic - item.label) * item.distance;
                delta_b      += (logistic - item.label);
                loss += L;
            }

            if(iter % 100 == 0)
                cout << "Iter " << iter << ", Loss: " << setprecision(3) << loss << endl;

            k_area = k_area - lr * delta_k_area;
            k_dist = k_dist - lr * delta_k_dist;
            b      = b      - lr * delta_b;
        }
    
        INFO("模型参数: k_area = %f, k_dist = %f, b = %f", k_area, k_dist, b);
        INFO("数据集: area_mean = %f, dist_mean = %f, area_std = %f, dist_std = %f", 
            mean.area, mean.distance, stdval.area, stdval.distance
        );

        INFO(""
            "k_area = %f\n"
            "k_distance = %f\n"
            "b = %f\n"
            "area_mean = %f\n"
            "area_std = %f\n"
            "distance_mean = %f\n"
            "distance_std = %f"
        , k_area, k_dist, b, mean.area, stdval.area, mean.distance, stdval.distance);

        float area          = 100;
        float distance      = 2000;
        float norm_area     = (area - mean.area) / stdval.area;
        float norm_dist     = (distance - mean.distance) / stdval.distance;
        float predict       = k_area * norm_area + k_dist * norm_dist + b;
        float logistic      = nn::sigmoid(predict);

        INFO("在上海,对于房屋面积 %.0f 平方米,距离地铁 %.0f 米的住户而言,他们觉得生活是 【%s】 的.",
            area, distance, logistic > 0.5 ? "幸福" : "并不幸福"
        );
        return 0;
    }
};

int main(){
    return Application::run();
}

运行结果如下图所示:

在这里插入图片描述

3.6 python实现

根据 C++ 的示例代码分析,Python 版本的相对容易,完整的示例代码如下:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = pd.read_csv("workspace/shanghai.csv").to_numpy()
area_mean, distance_mean, _ = np.mean(data, axis=0)
area_std, distance_std, _   = np.std(data, axis=0)

area, distance, label = data.T
area     = (area     - area_mean)     / area_std
distance = (distance - distance_mean) / distance_std

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

############################# 训练流程 ####################################
k_area     = 0.1
k_distance = 0.1
b  = 0
lr = 0.1

for i in range(1000):
    predict  = area * k_area + distance * k_distance + b
    logistic = sigmoid(predict)
    loss     = -(label * np.log(logistic) + (1 - label) * np.log(1 - logistic)).sum()

    if i % 100 == 0:
        print(f"Iter: {i}, Loss: {loss:.3f}")

    dk_area     = ((logistic - label) * area).sum()
    dk_distance = ((logistic - label) * distance).sum()
    db          = (logistic - label).sum()
    k_area      = k_area - dk_area * lr
    k_distance  = k_distance - dk_distance * lr
    b           = b - db * lr

print(f"k_area = {k_area}\nk_distance = {k_distance}\nb = {b}")
print(f"area_mean = {area_mean:.6f}\narea_std = {area_std:.6f}\ndistance_mean = {distance_mean:.6f}\ndistance_std = {distance_std:.6f}")

运行效果如下:

在这里插入图片描述

我们可以将逻辑回归模型训练后的分类线进行可视化,代码如下:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = pd.read_csv("workspace/shanghai.csv")
happiness   = data.query("happiness == 1")
unhappiness = data.query("happiness == 0")

k_area = -10.555772
k_distance = -11.365233
b = 0.702190
area_mean = 225.294113
area_std = 133.112854
distance_mean = 2252.941162
distance_std = 1816.952026

# 分类线是
# k_area * x + k_distance * y + b = 0
# y = -(k_area * x + b) / k_distance
x      = np.array([0, 500])
norm_x = (x - area_mean) / area_std
norm_y = -(k_area * norm_x + b) / k_distance
y      = norm_y * distance_std + distance_mean

plt.plot(happiness.area,   happiness.distance, "b*", label="happiness")
plt.plot(unhappiness.area, unhappiness.distance, "r*", label="unhappiness")
plt.plot(x, y, "g-")
plt.legend()

plt.xlim([0, 500])
plt.ylim([0, 6000])
plt.xlabel("House Area")
plt.ylabel("Distance to subway")
plt.title("Are you living in happiness?")
plt.savefig("figure.jpg")

分类线如下图所示:

在这里插入图片描述

4. 思考

逻辑回归求解二分类问题时,为什么最大化似然函数?(from chatGPT)

在二分类问题中,我们假设一个样本的标签 y ∈ 0 , 1 y \in {0,1} y0,1 是由一个未知参数 θ \theta θ 和一个概率分布生成的,这个概率分布就是二项伯努利分布,其概率质量函数为:
P ( y ∣ θ ) = θ y ( 1 − θ ) 1 − y P(y|\theta) = \theta^y(1-\theta)^{1-y} P(yθ)=θy(1θ)1y
其中, θ \theta θ 是一个在 ( 0 , 1 ) (0,1) (0,1) 区间内的参数(在之前的分析中我们使用的是 sigmoid 函数),它表示 y = 1 y=1 y=1 的概率, 1 − θ 1-\theta 1θ 表示 y = 0 y=0 y=0 的概率。

现在给定 m m m 个样本 ( x ( 1 ) , y ( 1 ) ) , ( x ( 2 ) , y ( 2 ) ) , … , ( x ( m ) , y ( m ) ) (x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)}), \dots, (x^{(m)}, y^{(m)}) (x(1),y(1)),(x(2),y(2)),,(x(m),y(m)),它们独立同分布地满足上述二项伯努利分布,我们的目标是学习到参数 θ \theta θ,使得似然函数 L ( θ ) \mathcal{L}(\theta) L(θ) 最大(为什么呢?🤔)。
L ( θ ) = P ( y ( 1 ) , y ( 2 ) , … , y ( m ) ∣ x ( 2 ) , x ( 2 ) , … , x ( m ) ; θ ) = ∏ i = 1 m θ y ( i ) ( 1 − θ ) 1 − y ( i ) . \mathcal{L}(\theta)=P(y^{(1)},y^{(2)},\dots,y^{(m)}|x^{(2)},x^{(2)},\dots,x^{(m)};\theta)=\prod_{i=1}^{m}\theta^{y^{(i)}}(1-\theta)^{1-y^{(i)}}. L(θ)=P(y(1),y(2),,y(m)x(2),x(2),,x(m);θ)=i=1mθy(i)(1θ)1y(i).
这样做的原因是如果我们最大化了似然函数,那么就相当于最大化了训练集的可能性,也就是说,我们找到了一组能够最好地描述训练集的参数 θ \theta θ。再最大化似然函数地过程中,我们可以使用梯度上升等优化算法来求解最优参数。(还是不怎么好理解😂)

可以这样理解:假设我们有一个数据集,其中包含了一些样本和对应的标签,我们希望使用逻辑回归模型对这些数据进行拟合,从而能够对新的样本进行分类预测。而逻辑回归模型是由参数 θ \theta θ 来决定的,因此我们需要通过学习来确定最优的参数 θ \theta θ

最大化似然函数实际上是在寻找最优的参数 θ \theta θ使得模型对于训练集中已有的标签的预测结果尽可能接近真实的标签值。而似然函数 L ( θ ) \mathcal{L}(\theta) L(θ) 表示了在给定参数 θ \theta θ 的情况下,观察到当前数据的概率。我们通过最大化似然函数,就可以找到最优的参数 θ \theta θ,使得模型的预测结果更加接近真实标签,从而提高模型的准确率。

在二分类问题中,我们通常认为标签值 y y y 是服从伯努利分布的。假设样本 i i i 的真实标签为 y i y_i yi,模型预测的标签为 y ^ i \hat{y}_i y^i,则似然函数为:
L ( θ ) = ∏ i = 1 m ( y ^ i ) y i ( 1 − y ^ i ) 1 − y i \mathcal{L}(\theta)=\prod_{i=1}^{m}\left(\hat{y}_i\right)^{y_i}\left(1-\hat{y}_i\right)^{1-y_i} L(θ)=i=1m(y^i)yi(1y^i)1yi
我们的目标就是找到最优的参数 θ \theta θ,使得似然函数 L ( θ ) \mathcal{L}(\theta) L(θ) 最大,即:
θ ^ = arg ⁡ max ⁡ θ ∏ i = 1 m ( y ^ i ) y i ( 1 − y ^ i ) 1 − y i \hat{\theta}=\arg\max\limits_{\theta}\prod\limits_{i=1}^m(\hat{y}_i)^{y_i}\left(1-\hat{y}_i\right)^{1-y_i} θ^=argθmaxi=1m(y^i)yi(1y^i)1yi
对上式取对数并取负号,就可以得到逻辑回归的损失函数(交叉熵):
J ( θ ) = − 1 m ∑ i = 1 m [ y i log ⁡ y ^ i + ( 1 − y i ) log ⁡ ( 1 − y ^ i ) ] J(\theta)=-\dfrac{1}{m}\sum_{i=1}^{m}\left[y_i\log\hat{y}_i+(1-y_i)\log\left(1-\hat{y}_i\right)\right] J(θ)=m1i=1m[yilogy^i+(1yi)log(1y^i)]
我们通过最小化损失函数 J ( θ ) J(\theta) J(θ),也就是最小化预测值和真实值之间的差距,来找到最优的参数 θ \theta θ,从而提高模型的准确率。

下面是关于似然函数和最大似然估计法的定义(from book)

X 1 , X 2 , ⋅ ⋅ ⋅ , X n X_1,X_2, \cdot\cdot\cdot,X_n X1,X2,,Xn 是来自总体 X X X 的样本, x 1 , x 2 , ⋅ ⋅ ⋅ , x n x_1,x_2, \cdot\cdot\cdot,x_n x1,x2,,xn 是样本值, θ \theta θ 是待估参数。

似然函数

定义:对于离散型总体 X X X,设其概率分布为 P { X = a i } = p ( a i ; θ ) , i = 1 , 2 , ⋯   , P\left\{X=a_{i}\right\}=p\left(a_{i};\theta\right),i=1,2,\cdots, P{X=ai}=p(ai;θ),i=1,2,, 称函数
L ( θ ) = L ( X 1 , X 2 , ⋯   , X n ; θ ) = ∏ i = 1 n p ( X i ; θ ) L\left(\theta\right)=L\left(X_{1},X_{2},\cdots,X_{n};\theta\right)=\prod_{i=1}^{n}p\left(X_{i};\theta\right) L(θ)=L(X1,X2,,Xn;θ)=i=1np(Xi;θ)
为参数 θ \theta θ 的似然函数

​ 对于连续型总体 X X X,概率密度为 f ( x ; θ ) f(x;\theta) f(x;θ),则称函数
L ( θ ) = L ( X 1 , X 2 , ⋯   , X n ; θ ) = ∏ i = 1 n f ( X i ; θ ) L\left(\theta\right)=L\left(X_{1},X_{2},\cdots,X_{n};\theta\right)=\prod_{i=1}^{n}f\left(X_{i};\theta\right) L(θ)=L(X1,X2,,Xn;θ)=i=1nf(Xi;θ)
为参数 θ \theta θ 的似然函数。

最大似然估计法

定义:对于给定的样本值 x 1 , x 2 , ⋅ ⋅ ⋅ , x n x_1,x_2, \cdot\cdot\cdot,x_n x1,x2,,xn,使似然函数 L ( x 1 , x 2 , ⋅ ⋅ ⋅ , x n ; θ ) L(x_1,x_2, \cdot\cdot\cdot,x_n;\theta) L(x1,x2,,xn;θ) 达到最大值的参数值 θ ^ = θ ^ ( x 1 , x 2 , ⋯   , x n ) \widehat{\theta}=\widehat{\theta}\left(x_{1},x_{2},\cdots,x_{n}\right) θ =θ (x1,x2,,xn) 称为未知参数 θ \theta θ 的最大似然估计值,相应地使似然函数 L ( X 1 , X 2 , ⋅ ⋅ ⋅ , X n ; θ ) L(X_1,X_2, \cdot\cdot\cdot,X_n;\theta) L(X1,X2,,Xn;θ) 达到最大值地参数值 θ ^ = θ ^ ( X 1 , X 2 , ⋯   , X n ) \widehat{\theta}=\widehat{\theta}\left(X_{1},X_{2},\cdots,X_{n}\right) θ =θ (X1,X2,,Xn) 称为 θ \theta θ 的最大似然估计量,一般统称为 θ \theta θ 的最大似然估计。称这种估计法为最大似然估计法

线性回归和逻辑回归的区别

线性回归和逻辑回归虽然都是机器学习中的回归算法,但是它们解决的问题有很大的区别。

线性回归旨在建立一个线性方程来预测一个连续型变量的值,例如根据一个人的年龄、性别、收入等特征,来预测他们的房屋价格。线性回归模型通常用最小二乘法来拟合模型参数,并利用拟合好的模型来进行预测。其输出的结果是一个实数值,代表连续型变量的预测值。

逻辑回归则是用于分类问题的,它预测的是一个离散的标签或类别,如图像分类问题中的猫或狗。逻辑回归采用 sigmoid 函数将线性方程的结果映射到 (0,1) 之间,用来表示一个概率,其输出的结果是一个概率值,代表某个样本属于某个类别的概率。逻辑回归通常采用极大似然估计来拟合模型参数,并利用拟合好的模型来进行预测。

二分类使用 sigmoid 函数将分类器的输出转化为概率分布,那多分类呢?(from chatGPT)

通常在多分类任务中,使用 Softmax 函数将 C C C 个分类器的输出转化为概率分布。具体来说,对于样本 i i i,假设 z i , j z_{i,j} zi,j 表示样本 i i i 属于第 j j j 类的得分(例如经过线性变换后的输出),那么该样本属于第 j j j 类的概率可以通过 Softmax 函数计算:
p i , j = e z i , j ∑ k = 1 C e z i , k p_{i,j}=\dfrac{e^{z_i,j}}{\sum_{k=1}^C e^{z_i,k}} pi,j=k=1Cezi,kezi,j
在求解损失函数时,我们可以将 Softmax 函数和交叉熵损失函数结合起来,得到 Softmax Cross Entropy Loss。该损失函数的具体表达式为:
L ( θ ) = − 1 n ∑ i = 1 n ∑ j = 1 C 1 { n = j } log ⁡ e z i j ∑ k = 1 C e 2 t i k L(\theta)=-\dfrac{1}{n}\sum\limits_{i=1}^n\sum\limits_{j=1}^C1_{\{n=j\}}\log\dfrac{e^{z_ij}}{\sum_{k=1}^C e^{2tik}} L(θ)=n1i=1nj=1C1{n=j}logk=1Ce2tikezij
该式可以看作是对交叉熵损失函数和 Softmax 函数的组合,用于在多分类问题中优化模型参数 θ \theta θ

总结

本次课程主要学习了逻辑回归,通过二分类问题案例了解回归模型,并通过代码进行了相关实现。同时我们对逻辑回归展开了一些讨论,比如为什么最大化极大似然估计以及逻辑回归与前面提到过的线性回归的区别,期待下次课程😄

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/455997.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

InnoDB中必须要了解的几个关键特性

InnoDB引擎在设计中使用了很多技术思想。下面我们主要介绍一些InnoDB的关键特性&#xff0c;帮助你去更好了解 InnoDB。 关键特性 1.预读&#xff08;1&#xff09;预读的两种算法&#xff08;2&#xff09;在InnoDB中相关配置 2.插入缓冲2.1 Insert Buffer2.2 Change Buffer2…

跨域的五种最常见解决方案

这是一篇笔记 什么是跨域&#xff1f; 跨域不是问题&#xff0c;是一种安全机制。浏览器有一种策略名为同源策略&#xff0c;同源策略规定了部分请求不能被浏览器所接受。 值得一提的是&#xff1a;同源策略导致的跨域是浏览器单方面拒绝响应数据&#xff0c;服务器端是处理…

C#基于asp.net的企业人事管理系统的研究与实现

&#xff08;一&#xff09;任务 1.本课题的任务是对人事管理系统的管理过程进行分析&#xff0c;列出逻辑实现过程&#xff0c;对系统进行逻辑设计和数据库设计&#xff1b; 2.主要实现系统管理、档案管理、考勤管理、薪水管理等功能&#xff1b; 3.实现企业员工的相关信息&am…

一篇文章教你解决node-sass 4.12.0 安装失败,一劳永逸

已知&#xff1a; 使用mac电脑使用的node版本是v14.20.0 问题&#xff1a;在安装node-sass 4.12.0的时候报错如下 看到这一堆错误&#xff0c;千万不要立马复制粘贴到浏览器去搜&#xff0c;感觉像无头苍蝇乱撞&#xff0c;好歹稍微看一下什么意思。 显而易见是有一个文档40…

浅析低代码开发的典型应用构建场景v

在数字经济蓬勃发展的大势之下&#xff0c;企业软件开发人员供给不足、开发速度慢、开发成本高、数字化和智能化成效不明显等问题日益凸出&#xff0c;阻碍了企业的数字化转型。 而近年来&#xff0c;低代码的出现推动了经济社会的全面提效&#xff0c;也成为人才供求矛盾的润…

【Docker】限制已运行容器的Cpu和内存

docker限制已运行容器的Cpu和内存 本文首发于 慕雪的寒舍 1.问题描述 最近云服务器的内存经常不够用&#xff0c;而且是莫名其妙的增多&#xff0c;在腾讯云的控制台里面看&#xff0c;4g的内存占用了3.2g&#xff0c;就卡到连ssh都连不上了 PS: 已换过网络和设备&#xff0c…

只要10分钟,零代码基础搞定炫酷大屏设计(内附详细教程)

近几年可视化大屏再次被推上热搜&#xff0c;无论是已经结束的疫情时代指挥中心大屏&#xff0c;还是每年购物节的大屏数据成交额&#xff0c;或者是日常会议中的大屏分析&#xff0c;到处都是可视化大屏的身影。 有人会说&#xff0c;这种大屏一定需要大量的财力、技术顶端的…

开源构建系统Buck2发布

看来最近 Meta 的工程师是一点都没有闲着&#xff0c;前两天刚开源 AI 图像分割模型&#xff0c;这不就又发布了名为 Buck2 的开源构建系统。 Buck2 是一个已经在 Meta 内部使用了一段时间的大型构建系统&#xff0c;目前 Meta 有数千名开发人员正在使用该构建系统&#xff0c;…

IP地址配置

1.vi /etc/sysconfig/network-scripts/ifcfg-ens33 &#xff08;在配置文件中修改&#xff09; 配置文件内容为&#xff1a; IPADDR:配置IP地址 NETMASK:配置子网掩码 GATEWAY:配置网关 DNS:配置dns地址 BOOTPROTO&#xff1a;设置获取ip的方式\DHCP为动态获取\ static为…

C++(GCC)生成和使用静态库

C&#xff08;GCC&#xff09;生成和使用静态库 文章目录 C&#xff08;GCC&#xff09;生成和使用静态库1、前言1.1 什么是静态库1.2 静态库优缺点1.3 C使用静态库的方法1.4 注意事项 2、linux下C生成静态库.a3、链接使用静态库 更多精彩内容&#x1f449;个人内容分类汇总 &a…

可算是熬出头了,测试4年,费时8个月,入职阿里,涨薪14K

前言 你的努力&#xff0c;终将成就无可替代的自己。 本科毕业后就一直从事测试的工作&#xff0c;和多数人一样&#xff0c;最开始从事点点点的工作&#xff0c;看着自己的同学一步一步往上走&#xff0c;自己还是在原地踏步&#xff0c;说实话这不是自己想要的状态。 一年半…

蓝牙耳机接打电话哪个比较好?接打电话最好的蓝牙耳机

技术已经发展到如此程度&#xff0c;耳机可以淹没嘈杂环境中不断出现的杂音&#xff0c;同时还能让我们在通话、音乐和娱乐方面保持清晰&#xff0c;既然如此&#xff0c;我们就来整理一下2023年适合通话和娱乐的无线耳机清单。 一、南卡小音舱Lite2蓝牙耳机 参考价格&#x…

Ubuntu系统下Python的虚拟环境搭建方法

文章目录 参考链接&#xff1a;一、Python虚拟环境的作用及创建方法简介1. 创建虚拟环境的必要性2. venv、virtualenv、pipenv三种创建虚拟环境方法比较2.1 搭建Python虚拟环境的方法2.2 venv、virtualenv方法2.3 pipenv方法 二、 virtualenv搭建虚拟环境1. 环境准备1.1 安装vi…

AI数字人产品“世优BOTA”发布会落幕,世优科技纪智辉演讲回顾

4月20日&#xff0c;世优科技式发布了新一代AI数字人产品——“世优BOTA”。在会上&#xff0c;世优科技创始人纪智辉介绍了数字人技术、驱动形式、数字人应用、“世优BOTA”的前世今生及未来。 以下是纪智辉的演讲内容概要&#xff1a; 各位嘉宾、各位朋友&#xff1a;大家下…

Storm proxies动态SEO监测优化为什么要大量用到http代理ip?

SEO监测和优化通常需要大量使用HTTP代理IP来实现以下几个方面的需求&#xff1a; 模拟不同地区的搜索结果&#xff1a;搜索引擎通常会根据用户所在地区提供不同的搜索结果&#xff0c;因此需要使用HTTP代理IP模拟不同地区的搜索结果&#xff0c;以便监测和优化针对不同地区的SE…

Matlab 相机标定

详细的原理可以看这篇 计算机视觉-相机标定&#xff0c;写的很赞 Step1. 准备 首先准备打印好的黑白棋盘格图片&#xff0c;并且保证表面的平整&#xff0c;例如35*35的棋盘格&#xff0c;贴在亚克力板上。 Step2. 拍摄 用相机拍摄棋盘格分别在取景框左上角、右上角、左下…

openGauss数据库pg_xlog爆满问题解决

文章目录 问题现象问题定位解决办法总结 问题现象 最近有一个之前搭的环境登不上了&#xff0c;好久没用想拿来测试的时候发现启动不了。启动时报错&#xff1a; [Errno 28] No space left on device query也不行了&#xff0c;提示没有空间了。 查询磁盘使用情况 df -h …

车间设备能源管理系统作用有哪些?

由于能源消耗不断的增加&#xff0c;对于高耗能企业要实现节能转型&#xff0c;淘汰落后高耗能产业&#xff0c;提供能源的有效利用&#xff0c;实现节能减排&#xff0c;合理利用能源&#xff0c;从而促进企业的经营绩效。 车间设备能源管理系统优点 1.精细化管理 可以对车…

探索【Stable-Diffusion WEBUI】的附加功能:图片缩放抠图

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;附加功能&#xff08;图片处理&#xff09;&#xff08;1.1&#xff09;处理对象&#xff08;Source&#xff09;&#xff08;1.2&#xff09;缩放&#xff08;Scale&#xff09;&#xff08;1.2.1&#xff…

【论文精读】TNNLS 2022 - 基于深度学习的事件抽取研究综述

【论文精读】TNNLS 2022 - 基于深度学习的事件抽取研究综述 【论文原文】&#xff1a;A Survey on Deep Learning Event Extraction Approaches and Applications 【作者信息】&#xff1a;Li, Qian and Li, Jianxin and Sheng, Jiawei and Cui, Shiyao and Wu, Jia and Hei,…