《统计学习方法》啃书手册|第6章 逻辑斯谛回归与最大熵模型(教材全解 + 公式推导 + Python实现)

本文详细介绍了逻辑斯谛回归模型和最大熵模型的学习过程。逻辑斯谛回归通过求解对数似然函数极大化得到条件概率分布,用于分类问题,而最大熵模型同样最大化熵来选择分类模型,适用于特征选择灵活的情况。两模型的预测过程都是基于条件概率分布,并且都可以通过拉格朗日对偶问题求解。最大熵模型解决了线性回归预测值范围不匹配实际概率的问题。文章还探讨了两种模型的优化算法,如牛顿法和BFGS算法,并讨论了各自的优缺点。
摘要由CSDN通过智能技术生成

逻辑斯谛回归模型的学习过程:通过求解对数似然函数极大化,得到条件概率分布 P ( Y ∣ X ) P(Y|X) P(YX)

逻辑斯谛回归模型的预测过程:直接使用模型的条件概率分布 P ( Y ∣ X ) P(Y|X) P(YX) 进行预测。

逻辑斯谛回归模型的类别划分

  • 用于解决分类问题的监督学习模型
  • 概率模型&非概率模型:既可以看作是概率模型,也可以看作是非概率模型
  • 参数化模型:假设模型参数的维度固定
  • 判别模型:由数据直接学习决策函数 f ( Y ∣ X ) f(Y|X) f(YX)

逻辑斯谛回归模型的主要优点:直接对分类进行建模,无需事先假设数据分布;在预测类别的同时,还可以得到近似概率预测

逻辑斯谛回归模型的主要缺点:不太适合特征空间很大的情况。


最大熵模型的学习过程:通过求解对数似然函数极大化或对偶函数极大化,得到条件概率分布 P ( Y ∣ X ) P(Y|X) P(YX)

最大熵模型的预测过程:直接使用模型的条件概率分布 P ( Y ∣ X ) P(Y|X) P(YX) 进行预测。

最大熵模型的类别划分

  • 用于解决分类问题的监督学习模型
  • 判别模型:由数据直接学习决策函数 f ( Y ∣ X ) f(Y|X) f(YX)

最大熵模型的主要优点:可以灵活地选择特征,使用不同类型的特征。

最大熵模型的主要缺点:计算量巨大。


【补充说明】逻辑斯谛回归模型,也称逻辑回归模型、LR 模型。

【延伸阅读】改进的迭代尺度法(IIS)详细解析 | 统计学习方法学习笔记 | 数据分析 | 机器学习 - 舟晓南的文章 - 知乎

6.1 逻辑斯谛回归模型

【重点】逻辑斯谛回归模型的含义(来自《统计学习方法》P.93)

在逻辑斯蒂回归模型中,输出 Y = 1 Y=1 Y=1 的对数几率是输入 x 的线性函数。或者说,输出 Y = 1 Y=1 Y=1 的对数几率是由输入 x 的线性函数表示的模型,即逻辑斯蒂回归模型。

Sigmoid 函数的性质

f ( x ) = 1 1 + e − x f(x) = \frac{1}{1+e^{-x}} f(x)=1+ex1

其性质如下:

  • 定义域为 O ( − ∞ , + ∞ ) O(-\infty,+\infty) O(,+),值域为 ( 0 , 1 ) (0,1) (0,1),在定义域内连续,严格单调递增;
  • ( 0 , 1 2 ) (0,\frac{1}{2}) (0,21) 中心对称;
  • 其一阶导数为 f ′ ( x ) = f ( x ) ( 1 − f ( x ) ) f'(x) = f(x) (1-f(x)) f(x)=f(x)(1f(x))
  • 具有简单变形 f ( x ) = e x 1 + e x f(x)=\frac{e^x}{1+e^x} f(x)=1+exex(分子分母同乘 e x e^x ex)。
为什么要用 sigmoid 函数?

【延伸阅读】通过逻辑回归的 sigmoid 函数把线性回归转化到 [0, 1] 之间,这个值为什么可以代表概率? - 盛傢伟的回答 - 知乎

(结合个人理解,如有错误,还望指出)

对于二类分类问题,有输入变量 x ∈ R n x \in R^n xRn 和输出变量 y ∈ { 0 , 1 } y \in \{0,1\} y{0,1}。其基本的线性回归的形式为:

y = w T x + b y = w^T x + b y=wTx+b

其中 w ∈ R n w \in R^n wRn 是权值向量, b ∈ R b \in R bR 是偏置。显然,线性回归产生的预测值是一个连续变量。

很自然地,我们首先考虑通过线性回归直接预测正类的概率,即 P ( Y = 1 ∣ x ) P(Y=1|x) P(Y=1∣x)。但是线性回归的预测值的值域是 ( − ∞ , + ∞ ) (-\infty,+\infty) (,+),而我们希望这个预测值的值域是 ( 0 , 1 ) (0,1) (0,1),于是我们需要修改线性回归预测的内容。

首先,我们考虑将线性回归改为预测事件的几率,即该事件发生的概率与该事件不发生的概率的比值,即 P ( Y = 1 ∣ x ) 1 − P ( Y = 1 ∣ x ) \frac{P(Y=1|x)}{1-P(Y=1|x)} 1P(Y=1∣x)P(Y=1∣x)。此时,我们希望预测值的值域变为 ( 0 , + ∞ ) (0,+\infty) (0,+)

考虑到在进行分类时,我们是通过比较两个条件概率值的大小决定分类的;因此,在线性回归外套一个单调递增函数并不会影响分类结果;同时,为便于计算,我们还希望外套的这个函数任意阶可导。于是,可以选择外套底数大于 1 的指数函数,不妨先取底数为 e e e

P ( Y = 1 ∣ x ) 1 − P ( Y = 1 ∣ x ) = e w T x + b \frac{P(Y=1|x)}{1-P(Y=1|x)} = e^{w^T x + b} 1P(Y=1∣x)P(Y=1∣x)=ewTx+b

此时,线性回归拟合的就是事件对数几率,即

l o g P ( Y = 1 ∣ x ) 1 − P ( Y = 1 ∣ x ) = w T x + b log \frac{P(Y=1|x)}{1-P(Y=1|x)} = w^T x + b log1P(Y=1∣x)P(Y=1∣x)=wTx+b

综上所述,以上过程就是在寻找比正类概率更适合线性回归拟合的目标,而找到的结果,就是逻辑斯谛回归模型。

似然函数

【扩展阅读】如何理解似然函数? - Yeung Evan 的回答 - 知乎

【如何理解似然函数? - Yeung Evan 的回答 - 知乎】摘要

似然函数定义为给定样本值 x x x 关于未知参数 θ \theta θ 的函数,是样本集中各个样本的联合概率:

L ( θ ∣ x ) = f ( x ∣ θ ) L(\theta|x) = f(x|\theta) L(θx)=f(xθ)

其中, x x x 是输入变量 X X X 取到的值,即 X = x X=x X=x θ \theta θ 为未知参数, f ( x ∣ θ ) f(x|\theta) f(xθ) 是给定 θ \theta θ 的情况下 x x x 的联合密度函数。

似然函数 L ( θ ∣ x ) L(\theta|x) L(θx) 是关于 θ \theta θ 的函数,密度函数 f ( x ∣ θ ) f(x|\theta) f(xθ) 是关于 x x x 的函数,两者仅在函数值上相等,而定义域和对应关系并不相等。

似然函数 L ( θ ∣ x ) L(\theta|x) L(θx) 表示给定样本 X = x X=x X=x 下参数 θ \theta θ 为真实值的可能性;密度函数 f ( x ∣ θ ) f(x|\theta) f(xθ) 表示给定 θ \theta θ 下样本随机向量 X = x X=x X=x 的可能性。

6.2 最大熵模型

【重点】最大熵原理(来自《统计学习方法》P.95)

直观地,最大熵原理认为要选择的概率模型必须满足已有的事实,即约束条件。在没有更多信息的情况下,那么不确定的部分都是“等可能的”。最大熵原理通过熵的最大化来表示等可能性。“等可能”不容易操作,而熵则是一个可优化的数值指标。

单纯形

单纯形是三角形和四面体的一种泛化,即凸多面体; k k k 维单纯形是指包含 ( k + 1 ) (k+1) (k+1) 个结点的凸多面体。

经验分布

当分布由样本数据估计得到时,所对应的联合分布 P ( X , Y ) P(X,Y) P(X,Y) 和边缘分布 P ( X ) P(X) P(X) 就是联合分布 P ( X , Y ) P(X,Y) P(X,Y) 的经验分布和边缘分布 P ( X ) P(X) P(X) 的经验分布。

约束条件的含义是什么?

对于约束条件公式:

∑ x , y P ~ ( x ) P ( y ∣ x ) f ( x , y ) = ∑ x , y P ~ ( x , y ) f ( x , y ) \sum_{x,y} \tilde{P}(x) P(y|x) f(x,y) = \sum_{x,y} \tilde{P}(x,y) f(x,y) x,yP~(x)P(yx)f(x,y)=x,yP~(x,y)f(x,y)

我们可以将其理解为,模型必须完全服从所有 f ( x , y ) = 1 f(x,y)=1 f(x,y)=1 的实例点的概率总和,但对所有 f ( x , y ) = 1 f(x,y)=1 f(x,y)=1 的实例点之间不做区分。

【扩展阅读】如何理解最大熵模型里面的特征? - SleepyBag 的回答 - 知乎

最大熵模型为什么可以通过对偶问题求解?

关于拉格朗日对偶性,有如下定理:

《统计学习方法》定理 C.2 (P. 450)

考虑原始问题和对偶问题。假设函数 f ( x ) f(x) f(x) c i ( x ) c_i(x) ci(x) 是凸函数, h j ( x ) h_j(x) hj(x) 是仿射函数;并且假设不等式约束 c i ( x ) c_i(x) ci(x) 是严格可行的,即存在 x,对所有 i 有 c i ( x ) < 0 c_i(x)<0 ci(x)<0,则存在 x ∗ x^* x α ∗ \alpha^* α β ∗ \beta^* β,使 x ∗ x^* x 是原始问题的解, α ∗ \alpha^* α β ∗ \beta^* β 是对偶问题的解,并且

p ∗ = d ∗ = L ( x ∗ , α ∗ , β ∗ ) p ∗ = d ∗ = L ( x ∗ , α ∗ , β ∗ ) p^* = d^* = L(x^*,\alpha^*,\beta^*)p^* = d^* = L(x^*,\alpha^*,\beta^*) p=d=L(x,α,β)p=d=L(x,α,β)

根据上述定理,最大熵模型可以通过求解对偶问题来求解原始问题的条件如下:

  1. − H ( P ) = ∑ x , y P ~ ( x ) P ( y ∣ x ) l o g P ( y ∣ x ) -H(P)=\sum_{x,y} \tilde{P}(x) P(y|x) log P(y|x) H(P)=x,yP~(x)P(yx)logP(yx) 是凸函数;
  2. E p ( f i ) − E P ~ ( f i ) = 0 E_p(f_i) - E_{\tilde{P}}(f_i) = 0 Ep(fi)EP~(fi)=0 是仿射函数;
  3. ∑ y P ( y ∣ x ) = 1 \sum_y P(y|x) = 1 yP(yx)=1 是仿射函数。

第 3 个条件显然成立,下面依次证明第 1 个条件和第 2 个条件。

在最大熵模型中,分类模型是条件概率分布 P ( Y ∣ X ) P(Y|X) P(YX),学习的目标是用最大熵原理选择最好的分类模型,因此自变量就是条件概率分布 P ( Y ∣ X ) P(Y|X) P(YX)。根据训练数据集确定的联合分布的经验分布 P ~ ( X , Y ) \tilde{P}(X,Y) P~(X,Y) 和边缘分布的经验分布 P ~ ( X ) \tilde{P}(X) P~(X) 均应视为常量。

证明第 1 个条件

− H ( P ) -H(P) H(P) P ( y ∣ x ) P(y|x) P(yx) 的二阶偏导数

∂ ( − H ( P ) ) ∂ P ( y ∣ x ) = ∑ x , y P ~ ( x ) P ( y ∣ x ) \frac{\partial (-H(P))}{\partial P(y|x)} = \sum_{x,y} \frac{\tilde{P}(x)}{P(y|x)} P(yx)(H(P))=x,yP(yx)P~(x)

因为有 P ~ ( x ) = v ( X = x ) N > = 0 \tilde{P}(x) = \frac{v(X=x)}{N} >= 0 P~(x)=Nv(X=x)>=0 P ( y ∣ x ) > = 0 P(y|x) >= 0 P(yx)>=0,所以二阶偏导数恒非负。于是,第 1 个条件得证。

证明第 2 个条件

E p ( f i ) E_p(f_i) Ep(fi) E P ~ ( f i ) E_{\tilde{P}}(f_i) EP~(fi) 展开,于是有

E p ( f i ) − E P ~ ( f i ) = ∑ x , y P ~ ( x ) P ( y ∣ x ) f ( x , y ) − ∑ x , y P ~ ( x , y ) f ( x , y ) = ∑ x , y f ( x , y ) [ P ~ ( x ) P ( y ∣ x ) − P ~ ( x , y ) ] \begin{align} E_p(f_i) - E_{\tilde{P}}(f_i) & = \sum_{x,y} \tilde{P}(x) P(y|x) f(x,y) - \sum_{x,y} \tilde{P}(x,y) f(x,y) \\ & = \sum_{x,y} f(x,y) \big[ \tilde{P}(x) P(y|x) - \tilde{P}(x,y) \big] \end{align} Ep(fi)EP~(fi)=x,yP~(x)P(yx)f(x,y)x,yP~(x,y)f(x,y)=x,yf(x,y)[P~(x)P(yx)P~(x,y)]

于是, E p ( f i ) − E P ~ ( f i ) E_p(f_i) - E_{\tilde{P}}(f_i) Ep(fi)EP~(fi) 显然是关于 P ( Y ∣ X ) P(Y|X) P(YX) 的仿射函数。

最大熵模型对数似然函数中指数的含义?

因为在训练数据集中, ( x , y ) (x,y) (x,y) 的样本可能不止一个,而连乘符号中仅区分了不同的 ( x , y ) (x,y) (x,y)。如果没有指数的话,那么无论 ( x , y ) (x,y) (x,y) 的出现频数是多少,均只乘了一次。

P ~ ( x , y ) \tilde{P}(x,y) P~(x,y) 正是 ( x , y ) (x,y) (x,y) 的样本的频数,因此使用 P ~ ( x , y ) \tilde{P}(x,y) P~(x,y) 作为指数,以表示 ( x , y ) (x,y) (x,y) 的样本需要连乘的次数。

6.3 模型学习的最优化算法

Jensen 不等式(琴生不等式)

f ( ∑ i = 1 n λ i x i ) ≤ ∑ i = 1 n λ i f ( x i ) f(\sum_{i=1}^n \lambda_i x_i) \le \sum_{i=1}^n \lambda_i f(x_i) f(i=1nλixi)i=1nλif(xi)

在这里的使用中,将 ∑ i = 1 n f i ( x , y ) f # ( x , y ) = 1 \sum_{i=1}^n \frac{f_i(x,y)}{f^\#(x,y)}=1 i=1nf#(x,y)fi(x,y)=1 视作常量。

一维牛顿法求 f ( x ) = 0 f(x)=0 f(x)=0 的值(Python 实现)
from scipy.misc import derivative

def newton_method_linear(func, args=(), error=1e-6, dx=1e-6):
    """一维牛顿法求f(x)=0的值

    :param func: 目标函数
    :param args: 参数列表
    :param error: 容差
    :param dx: 计算导数时使用的dx
    :return:
    """
    x0, y0 = 0, func(0, *args)
    while True:
        d = derivative(func, x0, args=args, dx=dx)  # 计算一阶导数
        x1 = x0 - y0 / d
        if abs(x1 - x0) < error:
            return x1
        x0, y0 = x1, func(x1, *args)
>>> def f(x, k):
...     return (x - k) ** 3
>>> newton_method_linear(f, args=(2,))
1.999998535982025
改进的迭代尺度算法 IIS(Python 实现)
from math import e
from ._newton_method_linear import newton_method_linear

def improved_iterative_scaling(x, y, features, error=1e-6):
    """改进的迭代尺度法求最大熵模型

    :param x: 输入变量
    :param y: 输出变量
    :param features: 特征函数列表
    :return:
    """
    n_samples = len(x)  # 样本数量
    n_features = len(features)  # 特征函数数量

    # 坐标压缩(将可能存在的非数值的特征及类别转换为数值)
    y_list = list(set(y))
    y_mapping = {c: i for i, c in enumerate(y_list)}
    x_list = list(set(tuple(x[i]) for i in range(n_samples)))
    x_mapping = {c: i for i, c in enumerate(x_list)}

    n_x = len(x_list)  # 不同的x的数量
    n_y = len(y_list)  # 不同的y的数量
    n_total = n_x * n_y  # 不同样本的总数

    print(x_list, x_mapping)
    print(y_list, y_mapping)

    # 计算联合分布的经验分布:P(X,Y) (empirical_joint_distribution)
    d1 = [[0.0] * n_y for _ in range(n_x)]  # empirical_joint_distribution
    for i in range(n_samples):
        d1[x_mapping[tuple(x[i])]][y_mapping[y[i]]] += 1 / n_samples
    print("联合分布的经验分布:", d1)

    # 计算边缘分布的经验分布:P(X) (empirical_marginal_distribution)
    d2 = [0.0] * n_x  # empirical_marginal_distribution
    for i in range(n_samples):
        d2[x_mapping[tuple(x[i])]] += 1 / n_samples
    print("边缘分布的经验分布", d2)

    # 计算特征函数关于经验分布的期望值:EP(fi) (empirical_joint_distribution_each_feature)
    # 所有特征在(x,y)出现的次数:f#(x,y) (samples_n_features)
    d3 = [0.0] * n_features  # empirical_joint_distribution_each_feature
    nn = [[0.0] * n_y for _ in range(n_x)]  # samples_n_features
    for j in range(n_features):
        for xi in range(n_x):
            for yi in range(n_y):
                if features[j](list(x_list[xi]), y_list[yi]):
                    d3[j] += d1[xi][yi]
                    nn[xi][yi] += 1

    print("特征函数关于经验分布的期望值:", d3)
    print("所有特征在(x,y)出现的次数:", nn)

    # 定义w的初值和模型P(Y|X)的初值
    w0 = [0] * n_features  # w的初值:wi=0
    p0 = [[1 / n_total] * n_y for _ in range(n_x)]  # 当wi=0时,P(Y|X)的值

    change = True
    while change:
        change = False

        # 遍历各个特征条件以更新w
        for j in range(n_features):
            def func(d, jj):
                """目标方程"""
                res = 0
                for xxi in range(n_x):
                    for yyi in range(n_y):
                        if features[j](list(x_list[xxi]), y_list[yyi]):
                            res += d2[xxi] * p0[xxi][yyi] * pow(e, d * nn[xxi][yyi])
                res -= d3[jj]
                return res

            # 牛顿法求解目标方程的根
            dj = newton_method_linear(func, args=(j,))

            # 更新wi的值
            w0[j] += dj
            if abs(dj) >= error:
                change = True

        # 计算新的模型
        p1 = [[0.0] * n_y for _ in range(n_x)]
        for xi in range(n_x):
            for yi in range(n_y):
                for j in range(n_features):
                    if features[j](list(x_list[xi]), y_list[yi]):
                        p1[xi][yi] += w0[j]
                p1[xi][yi] = pow(e, p1[xi][yi])
            total = sum(p1[xi][yi] for yi in range(n_y))
            if total > 0:
                for yi in range(n_y):
                    p1[xi][yi] /= total

        if not change:
            ans = {}
            for xi in range(n_x):
                for yi in range(n_y):
                    ans[(tuple(x_list[xi]), y_list[yi])] = p1[xi][yi]
            return w0, ans

        p0 = p1
>>> from code.maximum_entropy_model import improved_iterative_scaling
>>> dataset = [[[1], [1], [1], [1], [2], [2], [2], [2]], [1, 2, 2, 3, 1, 1, 1, 1]]
>>> def f1(xx, yy):
...     return xx == [1] and yy == 1
>>> def f2(xx, yy):
...     return (xx == [1] and yy == 2) or (xx == [1] and yy == 3)
>>> improved_iterative_scaling(dataset[0], dataset[1], [f1])
[-0.40546481008458535],
{((1,), 1): 0.2500000558794252, ((1,), 2): 0.3749999720602874, ((1,), 3): 0.3749999720602874, ((2,), 1): 0.3333333333333333, ((2,), 2): 0.3333333333333333, ((2,), 3): 0.3333333333333333}
>>> improved_iterative_scaling(dataset[0], dataset[1], [f2])
[0.40546793651918606],
{((1,), 1): 0.24999946967330844, ((1,), 2): 0.3750002651633458, ((1,), 3): 0.3750002651633458, ((2,), 1): 0.3333333333333333, ((2,), 2): 0.3333333333333333, ((2,), 3): 0.3333333333333333}
最大熵模型学习的 BFGS 算法
from math import e
from math import log

import numpy as np

from ..gradient_descent import golden_section_for_line_search
from ..gradient_descent import partial_derivative


def bfgs_algorithm_for_maximum_entropy_model(x, y, features, error=1e-6, distance=20, maximum=1000):
    """最大熵模型学习的BFGS算法

    :param x: 输入变量
    :param y: 输出变量
    :param features: 特征函数列表
    :param error: [int/float] 学习精度
    :param distance: [int/float] 每次一维搜索的长度范围(distance倍梯度的模)
    :param maximum: [int] 最大学习次数
    :return: [list] 结果点坐标
    """
    n_samples = len(x)  # 样本数量
    n_features = len(features)  # 特征函数数量

    # 坐标压缩(将可能存在的非数值的特征及类别转换为数值)
    y_list = list(set(y))
    y_mapping = {c: i for i, c in enumerate(y_list)}
    x_list = list(set(tuple(x[i]) for i in range(n_samples)))
    x_mapping = {c: i for i, c in enumerate(x_list)}

    n_x = len(x_list)  # 不同的x的数量
    n_y = len(y_list)  # 不同的y的数量
    n_total = n_x * n_y  # 不同样本的总数

    print(x_list, x_mapping)
    print(y_list, y_mapping)

    # 计算联合分布的经验分布:P(X,Y) (empirical_joint_distribution)
    d1 = [[0.0] * n_y for _ in range(n_x)]  # empirical_joint_distribution
    for i in range(n_samples):
        d1[x_mapping[tuple(x[i])]][y_mapping[y[i]]] += 1 / n_samples
    print("联合分布的经验分布:", d1)

    # 计算边缘分布的经验分布:P(X) (empirical_marginal_distribution)
    d2 = [0.0] * n_x  # empirical_marginal_distribution
    for i in range(n_samples):
        d2[x_mapping[tuple(x[i])]] += 1 / n_samples
    print("边缘分布的经验分布", d2)

    # 计算特征函数关于经验分布的期望值:EP(fi) (empirical_joint_distribution_each_feature)
    # 所有特征在(x,y)出现的次数:f#(x,y) (samples_n_features)
    d3 = [0.0] * n_features  # empirical_joint_distribution_each_feature
    nn = [[0.0] * n_y for _ in range(n_x)]  # samples_n_features
    for j in range(n_features):
        for xi in range(n_x):
            for yi in range(n_y):
                if features[j](list(x_list[xi]), y_list[yi]):
                    d3[j] += d1[xi][yi]
                    nn[xi][yi] += 1

    print("特征函数关于经验分布的期望值:", d3)
    print("所有特征在(x,y)出现的次数:", nn)

    def func(ww):
        """目标函数"""
        res = 0
        for xxi in range(n_x):
            t1 = 0
            for yyi in range(n_y):
                t2 = 0
                for jj in range(n_features):
                    if features[jj](list(x_list[xxi]), y_list[yyi]):
                        t2 += ww[jj]
                t1 += pow(e, t2)
            res += d2[xxi] * log(t1, e)

        for xxi in range(n_x):
            for yyi in range(n_y):
                t3 = 0
                for jj in range(n_features):
                    if features[jj](list(x_list[xxi]), y_list[yyi]):
                        t3 += ww[jj]
                res -= d1[xxi][yyi] * t3

        return res

    # 定义w的初值和B0的初值
    w0 = [0] * n_features  # w的初值:wi=0
    B0 = np.identity(n_features)  # 构造初始矩阵G0(单位矩阵)

    for k in range(maximum):
        # 计算梯度 gk
        nabla = partial_derivative(func, w0)

        g0 = np.matrix([nabla]).T  # g0 = g_k

        # 当梯度的模长小于精度要求时,停止迭代
        if pow(sum([nabla[i] ** 2 for i in range(n_features)]), 0.5) < error:
            break

        # 计算pk
        if k == 0:
            pk = - B0 * g0  # 若numpy计算逆矩阵时有0,则对应位置会变为inf
        else:
            pk = - (B0 ** -1) * g0

        # 一维搜索求lambda_k
        def f(xx):
            """pk 方向的一维函数"""
            x2 = [w0[jj] + xx * float(pk[jj][0]) for jj in range(n_features)]
            return func(x2)

        lk = golden_section_for_line_search(f, 0, distance, epsilon=1e-6)  # lk = lambda_k

        # print(k, "lk:", lk)

        # 更新当前点坐标
        w1 = [w0[j] + lk * float(pk[j][0]) for j in range(n_features)]

        # print(k, "w1:", w1)

        # 计算g_{k+1},若模长小于精度要求时,则停止迭代
        # 计算新的模型

        nabla = partial_derivative(func, w1)

        g1 = np.matrix([nabla]).T  # g0 = g_{k+1}

        # 当梯度的模长小于精度要求时,停止迭代
        if pow(sum([nabla[i] ** 2 for i in range(n_features)]), 0.5) < error:
            w0 = w1
            break

        # 计算G_{k+1}
        yk = g1 - g0
        dk = np.matrix([[lk * float(pk[j][0]) for j in range(n_features)]]).T

        B1 = B0 + (yk * yk.T) / (yk.T * dk) + (B0 * dk * dk.T * B0) / (dk.T * B0 * dk)

        B0 = B1
        w0 = w1

    p1 = [[0.0] * n_y for _ in range(n_x)]
    for xi in range(n_x):
        for yi in range(n_y):
            for j in range(n_features):
                if features[j](list(x_list[xi]), y_list[yi]):
                    p1[xi][yi] += w0[j]
            p1[xi][yi] = pow(e, p1[xi][yi])
        total = sum(p1[xi][yi] for yi in range(n_y))
        if total > 0:
            for yi in range(n_y):
                p1[xi][yi] /= total

    ans = {}
    for xi in range(n_x):
        for yi in range(n_y):
            ans[(tuple(x_list[xi]), y_list[yi])] = p1[xi][yi]
    return w0, ans
>>> from code.maximum_entropy_model import bfgs_algorithm_for_maximum_entropy_model
>>> dataset = [[[1], [1], [1], [1], [2], [2], [2], [2]], [1, 2, 2, 3, 1, 1, 1, 1]]
>>> def f1(xx, yy):
...     return xx == [1] and yy == 1
>>> def f2(xx, yy):
...     return (xx == [1] and yy == 2) or (xx == [1] and yy == 3)
>>> bfgs_algorithm_for_maximum_entropy_model(dataset[0], dataset[1], [f1])
[-0.40546491364767995],
{((1,), 1): 0.2500000364613426, ((1,), 2): 0.3749999817693287, ((1,), 3): 0.3749999817693287, ((2,), 1): 0.3333333333333333, ((2,), 2): 0.3333333333333333, ((2,), 3): 0.3333333333333333}
>>> bfgs_algorithm_for_maximum_entropy_model(dataset[0], dataset[1], [f2])
[0.4054649114869288]
{((1,), 1): 0.2500000368664835, ((1,), 2): 0.3749999815667583, ((1,), 3): 0.3749999815667583, ((2,), 1): 0.3333333333333333, ((2,), 2): 0.3333333333333333, ((2,), 3): 0.3333333333333333}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值