首页 > 编程语言 >机器学习算法原理实现——使用交叉熵、梯度下降求解逻辑回归

机器学习算法原理实现——使用交叉熵、梯度下降求解逻辑回归

时间:2023-09-07 19:45:00浏览次数:58  
标签:求解 梯度 train 算法 cost params pred np 模型

交叉熵的定义以及和熵的区别?

 

 

 

交叉熵是衡量两个概率分布之间的差异的一个度量。在机器学习和深度学习中,尤其是分类问题,交叉熵常被用作损失函数。交叉熵度量的是实际分布(标签)与模型预测之间的不一致程度。

 

这个值越小,模型的预测与真实分布越接近。完美的预测会有交叉熵为0,这是因为模型的预测概率分布与真实概率分布完全匹配。

在实际应用中,当我们使用交叉熵作为损失函数并利用优化算法(如梯度下降)来最小化这个损失时,模型的预测结果会逐渐地接近真实的标签。

 

交叉熵为什么可以衡量两个概率分布之间的差异?内在机理通俗解释。   

交叉熵背后的直觉和内在机制其实来源于信息论。在信息论中,熵是用来衡量一个随机变量不确定性的度量。交叉熵则可以被视为当你使用错误的概率分布去编码信息时,你平均需要的位数。

为了通俗解释这个概念,让我们用一个类比:

想象你和你的朋友在玩一个游戏。在这个游戏中,你们每次抽取一张带有数字的卡片。每张卡片上的数字都有一个与之关联的概率。你的任务是猜测每次抽取的数字,并且你已经知道每个数字的真实概率。

如果你知道真实的概率分布并根据这个概率分布做出决策,那么平均上你将达到最优的猜测效果。这个“最优效果”的平均不确定性(或你的平均惊讶程度)就是熵。

但假设你误解了这些概率,而是按照一个错误的概率分布来猜测(即你的模型的预测)。在这种情况下,你的平均惊讶程度会增加,因为你经常会被实际结果惊讶到。这个“增加的”惊讶度实际上是交叉熵。

更直接地说,交叉熵衡量的是,当我们使用模型的预测概率分布去解释真实的事件发生时,我们的平均惊讶程度是多少。如果模型的预测非常准确,那么交叉熵接近熵。如果模型的预测经常是错误的,那么交叉熵会远大于熵。

这就是为什么交叉熵可以作为两个概率分布之间差异的度量:它衡量了使用一个概率分布(模型的预测)去解释另一个概率分布(真实的标签)时的平均“惊讶度”。

 

应用:

Logistic Regression公式推导及其交叉熵损失函数

 

 

 再补上1/m(m为样本数):

 

好了,有了上面的知识就可以开始编码了!

import numpy as np


### 定义sigmoid函数
def sigmoid(x):
    '''
    输入:
    x:数组
    输出:
    z:经过sigmoid函数计算后的数组
    '''
    z = 1 / (1 + np.exp(-x))
    return z


### 定义参数初始化函数
def initialize_params(dims):
    '''
    输入:
    dims:参数维度
    输出:
    z:初始化后的参数向量W和参数值b
    '''
    # 将权重向量初始化为零向量
    W = np.zeros((dims, 1))
    # 将偏置初始化为零
    b = 0
    return W, b


### 定义对数几率回归模型主体
def logistic(X, y, W, b):
    '''
    输入:
    X: 输入特征矩阵
    y: 输出标签向量
    W: 权重系数
    b: 偏置参数
    输出:
    a: 对数几率回归模型输出
    cost: 损失
    dW: 权重梯度
    db: 偏置梯度
    '''
    # 训练样本量
    num_train = X.shape[0]
    # 训练特征数
    num_feature = X.shape[1]
    # 对数几率回归模型输出
    a = sigmoid(np.dot(X, W) + b)
    # 交叉熵损失
    cost = -1/num_train * np.sum(y*np.log(a) + (1-y)*np.log(1-a))
    # 权重梯度
    dW = np.dot(X.T, (a-y))/num_train
    # 偏置梯度
    db = np.sum(a-y)/num_train
    # 压缩损失数组维度
    cost = np.squeeze(cost)
    return a, cost, dW, db


### 定义对数几率回归模型训练过程
def logistic_train(X, y, learning_rate, epochs):
    '''
    输入:
    X: 输入特征矩阵
    y: 输出标签向量
    learning_rate: 学习率
    epochs: 训练轮数
    输出:
    cost_list: 损失列表
    params: 模型参数
    grads: 参数梯度
    '''
    # 初始化模型参数
    W, b = initialize_params(X.shape[1])
    # 初始化损失列表
    cost_list = []
    # 迭代训练
    for i in range(epochs):
        # 计算当前迭代的模型输出、损失和参数梯度
        a, cost, dW, db = logistic(X, y, W, b)
        # 参数更新
        W = W - learning_rate * dW
        b = b - learning_rate * db
        # 记录损失
        if i % 100 == 0:
            cost_list.append(cost)
        # 打印训练过程中的损失
        if i % 100 == 0:
            print('epoch %d cost %f' % (i, cost))
    # 保存参数
    params = {
        'W': W,
        'b': b
    }
    # 保存梯度
    grads = {
        'dW': dW,
        'db': db
    }
    return cost_list, params, grads


### 定义预测函数
def predict(X, params):
    '''
    输入:
    X: 输入特征矩阵
    params: 训练好的模型参数
    输出:
    y_pred: 转换后的模型预测值
    '''
    # 模型预测值
    y_pred = sigmoid(np.dot(X, params['W']) + params['b'])
    # 基于分类阈值对概率预测值进行类别转换
    for i in range(len(y_pred)):
        if y_pred[i] > 0.5:
            y_pred[i] = 1
        else:
            y_pred[i] = 0
    return y_pred


### 绘制对数几率回归分类决策边界
def plot_decision_boundary(X_train, y_train, params):
    '''
    输入:
    X_train: 训练集输入
    y_train: 训练集标签
    params:训练好的模型参数
    输出:
    分类决策边界图
    '''
    # 训练样本量
    n = X_train.shape[0]
    # 初始化类别坐标点列表
    xcord1 = []
    ycord1 = []
    xcord2 = []
    ycord2 = []
    # 获取两类坐标点并存入列表
    for i in range(n):
        if y_train[i] == 1:
            xcord1.append(X_train[i][0])
            ycord1.append(X_train[i][1])
        else:
            xcord2.append(X_train[i][0])
            ycord2.append(X_train[i][1])
    # 创建绘图
    fig = plt.figure()
    ax = fig.add_subplot(111)
    # 绘制两类散点,以不同颜色表示
    ax.scatter(xcord1, ycord1,s=32, c='red')
    ax.scatter(xcord2, ycord2, s=32, c='green')
    # 取值范围
    x = np.arange(-1.5, 3, 0.1)
    # 分类决策边界公式
    y = (-params['b'] - params['W'][0] * x) / params['W'][1]
    # 绘图
    ax.plot(x, y)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()


# 导入matplotlib绘图库
import matplotlib.pyplot as plt
# 导入生成分类数据函数
from sklearn.datasets import make_classification
# 生成100×2的模拟二分类数据集
X, labels = make_classification(
    n_samples=100,
    n_features=2,
    n_redundant=0,
    n_informative=2,
    random_state=1,
    n_clusters_per_class=2)
# 设置随机数种子
rng = np.random.RandomState(2)
# 对生成的特征数据添加一组均匀分布噪声
X += 2 * rng.uniform(size=X.shape)
# 标签类别数
unique_labels = set(labels)
# 根据标签类别数设置颜色
colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))
# 绘制模拟数据的散点图
for k,col in zip(unique_labels, colors):
    x_k = X[labels==k]
    plt.plot(x_k[:,0], x_k[:,1],'o',
             markerfacecolor=col,
             markeredgecolor='k',
             markersize=14)
plt.title('Simulated binary data set')
plt.show()

# 按9∶1简单划分训练集与测试集
offset = int(X.shape[0] * 0.9)
X_train, y_train = X[:offset], labels[:offset]
X_test, y_test = X[offset:], labels[offset:]
y_train = y_train.reshape((-1,1))
y_test = y_test.reshape((-1,1))
print('X_train =', X_train.shape)
print('X_test =', X_test.shape)
print('y_train =', y_train.shape)
print('y_test =', y_test.shape)



# 执行对数几率回归模型训练
cost_list, params, grads = logistic_train(X_train, y_train, 0.01, 1000)
# 打印训练好的模型参数
print(params)
# 基于训练参数对测试集进行预测
y_pred = predict(X_test, params)
print(y_pred)

# 导入classification_report模块
from sklearn.metrics import classification_report
# 打印测试集分类预测评估报告
print(classification_report(y_test, y_pred))
plot_decision_boundary(X_train, y_train, params)


# 导入对数几率回归模块
from sklearn.linear_model import LogisticRegression
# 拟合训练集
clf = LogisticRegression(random_state=0).fit(X_train, y_train)
# 预测测试集
y_pred = clf.predict(X_test)
# 打印预测结果
print(y_pred)

  

运行截图:

 

 

X_train = (90, 2)
X_test = (10, 2)
y_train = (90, 1)
y_test = (10, 1)
epoch 0 cost 0.693147
epoch 100 cost 0.554066
epoch 200 cost 0.480953
epoch 300 cost 0.434738
epoch 400 cost 0.402395
epoch 500 cost 0.378275
epoch 600 cost 0.359468
epoch 700 cost 0.344313
epoch 800 cost 0.331783
epoch 900 cost 0.321216
{'W': array([[ 1.55740577],
       [-0.46456883]]), 'b': -0.594451885315136}
[[0.]
 [1.]
 [1.]
 [0.]
 [1.]
 [1.]
 [0.]
 [0.]
 [1.]
 [0.]]
              precision    recall  f1-score   support

           0       1.00      1.00      1.00         5
           1       1.00      1.00      1.00         5

    accuracy                           1.00        10
   macro avg       1.00      1.00      1.00        10
weighted avg       1.00      1.00      1.00        10

 
[0 1 1 0 1 1 0 0 1 0]

  

可以看到预测结果和sklearn的完全一样。

 

补充:predict还可以这样写得更简洁:

def predict(X, params):
    ans = []
    y = sigmoid(np.dot(X, params['W']) + params['b'])
    return (y > 0.5).astype(int)
"""
    for i in range(X.shape[0]):
        if y[i][0] > 0.5:
            ans.append(1)
        else:
            ans.append(0)
    return ans
"""

### 定义预测函数
def predict2(X, params):
    '''
    输入:
    X: 输入特征矩阵
    params: 训练好的模型参数
    输出:
    y_pred: 转换后的模型预测值
    '''
    # 模型预测值
    y_pred = sigmoid(np.dot(X, params['W']) + params['b'])
    # 基于分类阈值对概率预测值进行类别转换
    for i in range(len(y_pred)):
        if y_pred[i] > 0.5: # y_pred[i][0] is more precise!!!
            y_pred[i] = 1
        else:
            y_pred[i] = 0
    return y_pred

  

 加一个flatten打平为[0,1,0,0,1]这种:

def predict(X, params):
    ans = []
    y = sigmoid(np.dot(X, params['W']) + params['b'])
    return (y > 0.5).astype(int).flatten()

  

 

 

 

 

标签:求解,梯度,train,算法,cost,params,pred,np,模型
From: https://www.cnblogs.com/bonelee/p/17684182.html

相关文章

  • KMP算法详解
    呼——终于看懂了KMP——磕了三天了。题目直达Q:KMP是干什么的?是查找字符串用的,可以查找到\(S2\)字符串在\(S1\)字符串中出现的位置(当然,你可以统计出次数)。Q:那复杂度是多少的?通常,我们认为他的复杂度是\(O(|S1|)\),虽然有点常数。常规的的比较方法是直接比较,枚......
  • 算法单元重启啦!
    开始跟着代码随想录重新学算法了,计划是按照它的目录一个专题一个专题地刷。我的文章目录会放在下面,按照自己的进度更新,整理出来一些有价值的基础知识和题解代码。使用语言是python,但知识点部分也会涉及C++。欢迎阅读点赞~目录数组链表......
  • LRUCache算法缓存策略(map+doubleLinkedList)
    packagearithmetic;importjava.util.HashMap;publicclassFaceTest81{//LRUcache缓存策略map+双向链表//get、update、put需要时间复杂度达到O1//map+双向链表结构publicFaceTest81(intcapacity){ cache=newMyCache(capacity);}privateMyCache<Integer,Intege......
  • 2023“钉耙编程”中国大学生算法设计超级联赛(4)
    1003SimpleSetProblem题意:分别从k个集合中选一个元素组成一个数组\((a_1,a_2,a_3,...,a_k)\),求max\((a_1,a_2,a_3,...,a_k)\)-min\((a_1,a_2,a_3,...,a_k)\)的最小值。分析:我们给每个集合中的元素添加一个id标识它属于哪个集合,然后将所有集合合并并按数值大小从......
  • day24 - 回溯算法part01
    回溯算法理论基础 77. 组合classSolution{public:vector<vector<int>>result;vector<int>path;voiddfs(intn,intk,intstart){if(path.size()==k){result.push_back(path);return;}......
  • Lnton羚通AI算法算力平台在海域可视化监管海域动态远程视频智能监管平台的构建方案
    一、方案背景随着科技的不断进步,智慧海域管理平台已成为海洋领域监管的关键工具。相比传统的视频监控方式,智慧海域管理平台通过建设近岸海域视频监控网、海洋环境监测网和海上目标探测网络等,实现了海洋管理的数字化转型。传统的监控方式需要大量人力物力,而智慧海域管理平台实现了......
  • 算法题---Jill旅行
     【问题描述】Jill要进行一次旅行,沿途中要经过若干个城市。对于每两个相邻城市之间的路程,他都可以选择骑自选车或是坐公车汽车。如果沿途风景怡人,则他更喜欢骑自选车来完成这段路程。Jill对每段路程都有评出了一个满意度,这是一个非零整数,所有他喜欢的路程标以正数,不喜欢的路程标......
  • Lnton羚通视频分析算法开发yolov8训练与rknn模型导出并在RK3588部署的详细步骤
    Lnton羚通的算法算力云平台以其突出的特点成为一款优秀的解决方案。它的高性能、高可靠性、高可扩展性和低成本使得用户能够高效地进行复杂的计算任务。同时,丰富的算法库和工具以及支持用户上传和部署自定义算法的功能进一步提升了平台的灵活性和个性化能力。一、环境验证(一)板端验......
  • Rendezvous hashing算法介绍
    RendezvoushashingRendezvoushashing用于解决分布式系统中的分布式哈希问题,该问题包括三部分:Keys:数据或负载的唯一标识Values:消耗资源的数据或负载Servers:管理数据或负载的实体例如,在一个分布式系统中,key可能是一个文件名,value是文件数据,servers是连接网络的数据服务器,......
  • 代码随想录算法第一天704
    代码随想录算法第一天|704.二分查找、27.移除元素学习(复习)数组理论基础:​ (https://programmercarl.com/数组理论基础.html)​ 新了解到Java中数组地址不是连续的。704.二分查找题目题目链接:https://leetcode.cn/problems/binary-search/文章讲解:https://programmercarl.......