首页 > 编程语言 >机器学习算法原理实现——最大熵模型

机器学习算法原理实现——最大熵模型

时间:2023-11-16 12:00:59浏览次数:38  
标签:机器 no 模型 get self 算法 xy 特征函数

【写在前面】

在sklearn库中,没有直接称为"最大熵模型"的类,但是有一个与之非常相似的模型,那就是LogisticRegression。逻辑回归模型可以被视为最大熵模型的一个特例,当问题是二分类问题,且特征函数是输入和输出的线性函数时,最大熵模型就等价于逻辑回归模型。

【最大熵模型的原理】

最大熵模型是一种概率模型,它的基本思想是在满足给定约束条件下,使模型的熵最大。熵是一个衡量不确定性的量,熵越大,不确定性越大,模型越平滑,对未知数据的适应性越强。

最大熵模型的基本原理可以从以下几个方面来理解:

1. 特征函数:最大熵模型通过特征函数来描述输入和输出之间的关系。特征函数是一个二元函数,如果输入和输出满足某种关系,则特征函数的值为1,否则为0。

2. 模型的形式:最大熵模型是一个对数线性模型,它的形式为:P(Y|X) = exp(∑λifi(X,Y)) / Z(X),其中,λi是特征函数fi对应的权重,Z(X)是归一化因子,保证概率之和为1。

3. 训练目标:最大熵模型的训练目标是最大化训练数据的对数似然函数,这等价于最小化交叉熵。通过优化算法(如梯度下降、牛顿法等)来求解最优的权重参数。

4. 约束条件:最大熵模型的约束条件通常是特征函数关于输入的期望等于特征函数关于模型的期望,这保证了模型能够尽可能地符合训练数据的统计特性。

最大熵模型广泛应用于自然语言处理、信息检索等领域,如文本分类、词性标注、命名实体识别等任务。

 

【如何理解其中提到的对数线性模型?】

最大熵模型是一个对数线性模型,这是因为它的形式可以写成一个线性函数的指数形式,并且这个线性函数的参数是特征函数的权重。具体来说,最大熵模型的形式可以写成:

P(Y|X) = exp(∑λifi(X,Y)) / Z(X)

其中,λi是特征函数fi对应的权重,Z(X)是归一化因子,保证概率之和为1。这个模型是线性的,因为对数概率log P(Y|X)是特征函数和权重的线性组合:

log P(Y|X) = ∑λi fi(X,Y) - log Z(X)

这就是为什么最大熵模型被称为对数线性模型。

举一个实际的例子,假设我们有一个二分类问题,输入X是一个人的年龄,输出Y是这个人是否喜欢吃冰淇淋。我们可以定义两个特征函数:f1(X,Y) = X (Y == 0)表示年龄和不喜欢吃冰淇淋的关系,f2(X,Y) = X (Y == 1)表示年龄和喜欢吃冰淇淋的关系。然后,我们可以通过训练数据来学习特征函数的权重λ1和λ2,得到最大熵模型:

P(Y=0|X) = exp(λ1X) / (exp(λ1X) + exp(λ2X))
P(Y=1|X) = exp(λ2X) / (exp(λ1X) + exp(λ2X))

这个模型就是一个对数线性模型,因为对数概率是特征函数和权重的线性组合。

【最大熵模型和softmax函数关系】

最大熵模型和Softmax函数都是处理多分类问题的常用工具,它们之间有一些关联,但也有一些关键的区别。

1. 最大熵模型:最大熵模型是一种概率模型,它的目标是在满足给定约束条件下,使模型的熵最大。在最大熵模型中,我们通常定义一组特征函数和对应的权重,然后使用这些特征函数和权重来计算每个类别的概率。最大熵模型可以用于多分类问题,其模型形式为P(Y|X) = exp(w·f(X,Y)) / Z(X),其中Z(X)是归一化因子。

2. Softmax函数:Softmax函数是一种将一组实数映射到概率分布的函数。给定一组实数,Softmax函数可以计算出每个实数对应的概率,使得所有概率之和为1。Softmax函数常用于多分类问题的最后一层,用于输出每个类别的概率。

最大熵模型和Softmax函数的关系在于,最大熵模型的输出形式和Softmax函数的形式非常相似。实际上,如果我们将最大熵模型的特征函数定义为输入和类别的线性函数,那么最大熵模型的输出就等价于Softmax函数的输出。

总的来说,最大熵模型和Softmax函数都是处理多分类问题的工具,它们在形式上有一些相似之处,但在定义和使用方式上有一些关键的区别。

 

【推导最大熵模型的实现步骤最大熵模型的求解步骤主要包括以下几个步骤

1. 定义特征函数:特征函数是一个二元函数,如果输入和输出满足某种关系,则特征函数的值为1,否则为0。

2. 定义模型:最大熵模型是一个对数线性模型,它的形式为:P(Y|X) = exp(∑λifi(X,Y)) / Z(X),其中,λi是特征函数fi对应的权重,Z(X)是归一化因子,保证概率之和为1。

3. 定义约束条件:最大熵模型的约束条件通常是特征函数关于输入的期望等于特征函数关于模型的期望,这保证了模型能够尽可能地符合训练数据的统计特性。

4. 定义目标函数:最大熵模型的目标函数是最大化训练数据的对数似然函数,这等价于最小化交叉熵。

5. 求解模型:通过优化算法(如梯度下降、牛顿法等)来求解最优的权重参数。

以下是最大熵模型的求解步骤的详细推导:

首先,我们定义模型的对数似然函数为:

L(λ) = ∑y,x]∈D P̂(x,y) log P(y|x;λ)

其中,P̂(x,y)是训练数据中(x,y)的经验分布,P(y|x;λ)是模型的概率分布。我们的目标是最大化对数似然函数,即:

max L(λ)

然后,我们定义模型的约束条件为:

∑y P̂(x,y)fi(x,y) = ∑y P(y|x;λ)fi(x,y)

这个约束条件保证了模型的期望等于训练数据的期望。

最后,我们使用拉格朗日乘子法和KKT条件,可以得到最优解的条件:

∑x∈D P̂(x)P(y|x;λ)fi(x,y) = ∑x∈D P̂(x,y)fi(x,y)

这个条件可以通过迭代算法(如改进的迭代尺度法、拟牛顿法等)来求解。

 

然后:

机器学习算法原理实现——最大熵模型_数据

 

后续的推导见:

总之,就是梯度下降的迭代算法:

机器学习算法原理实现——最大熵模型_数据_02

 


python实现

import numpy as np


class MaxEntropy(object):
    def __init__(self, lr=0.01,epoch = 1000):
        self.lr = lr # 学习率
        self.N = None  # 数据个数
        self.n = None # Xy对
        self.hat_Ep = None
        # self.sampleXY = []
        self.labels = None
        self.xy_couple = {}
        self.xy_id = {}
        self.id_xy = {}
        self.epoch = epoch

    def _rebuild_X(self,X):
        X_result = []
        for x in X:
            print(x,self.X_columns)
            X_result.append([y_s + '_' + x_s for x_s, y_s in zip(x, self.X_columns)])
        return X_result
        
    def build_data(self,X,y,X_columns):
        self.X_columns = X_columns
        self.y = y
        
        self.X = self._rebuild_X(X)
        self.N = len(X)
        self.labels = set(y)
        for x_i,y_i in zip(self.X,y):
            for f in  x_i:
                self.xy_couple[(f,y_i)]  = self.xy_couple.get((f,y_i),0) + 1
        self.n = len(self.xy_couple.items())

    def fit(self,X,y,X_columns):
        self.build_data(X,y,X_columns)
        self.w = [0] * self.n
        for _ in range(self.epoch):
            for i in range(self.n):
                # self.w[i]  += 1/self.n * np.log(self.get_hat_Ep(i) / self.get_Ep(i) )  # 此处乘1/self.n,或者乘一个较小的学习率
                self.w[i]  += self.lr * np.log(self.get_hat_Ep(i) / self.get_Ep(i) )  # 此处乘1/self.n,或者乘一个较小的学习率
                # print(_,np.log(self.get_hat_Ep(i) / self.get_Ep(i) ) )
    
    def predict(self,X):
        print(X)
        X = self._rebuild_X(X)
        print(X)
        
        result = [{} for _ in range(len(X))] 
        for i,x_i in enumerate (X):
            for y in self.labels:
                # print(x_i)
                result[i][y] = self.get_Pyx(x_i,y)
        return result
       
    def get_hat_Ep(self,index):
        self.hat_Ep = [0]*(self.n)
        for i,xy in enumerate(self.xy_couple):
            self.hat_Ep[i] = self.xy_couple[xy] / self.N
            self.xy_id[xy] = i
            self.id_xy[i] = xy
        return self.hat_Ep[index]

    def get_Zx(self,x_i):
        Zx = 0
        for y in self.labels:
            count = 0
            for f in x_i :
                if (f,y) in self.xy_couple:
                    count += self.w[self.xy_id[(f,y)]]
            Zx +=  np.exp(count)
        return  Zx

    def get_Pyx(self,x_i,y):
        count = 0
        for f in x_i :
            if (f,y) in self.xy_couple:
                count += self.w[self.xy_id[(f,y)]]
        return np.exp(count) / self.get_Zx(x_i)

    def get_Ep(self,index):
        f,y = self.id_xy[index]
        # print(f,y)
        ans = 0
        # print(self.X)
        for x_i in self.X:
            if f not in x_i:
                continue
            pyx = self.get_Pyx(x_i,y)
            ans += pyx / self.N
            # print("ans",ans,pyx)
        return ans
        
data_set = [['youth', 'no', 'no', '1', 'refuse'],
               ['youth', 'no', 'no', '2', 'refuse'],
               ['youth', 'yes', 'no', '2', 'agree'],
               ['youth', 'yes', 'yes', '1', 'agree'],
               ['youth', 'no', 'no', '1', 'refuse'],
               ['mid', 'no', 'no', '1', 'refuse'],
               ['mid', 'no', 'no', '2', 'refuse'],
               ['mid', 'yes', 'yes', '2', 'agree'],
               ['mid', 'no', 'yes', '3', 'agree'],
               ['mid', 'no', 'yes', '3', 'agree'],
               ['elder', 'no', 'yes', '3', 'agree'],
               ['elder', 'no', 'yes', '2', 'agree'],
               ['elder', 'yes', 'no', '2', 'agree'],
               ['elder', 'yes', 'no', '3', 'agree'],
               ['elder', 'no', 'no', '1', 'refuse'],
               ]
columns = ['age', 'working', 'house', 'credit_situation','labels']
X = [i[:-1] for i in data_set]
X_columns = columns[:-1]
Y = [i[-1] for i in data_set]

train_X = X[:12]
test_X = X[12:]
train_Y = Y[:12]
test_Y = Y[12:]
 

X_columns = columns[:-1]



mae = MaxEntropy()
mae.fit(train_X,train_Y,X_columns)

mae.predict(test_X)

  


注意:

def get_hat_Ep(self,index):
        self.hat_Ep = [0]*(self.n)
        for i,xy in enumerate(self.xy_couple):
            self.hat_Ep[i] = self.xy_couple[xy] / self.N
            self.xy_id[xy] = i
            self.id_xy[i] = xy
        return self.hat_Ep[index]

  

get_hat_Ep函数的功能是计算特征函数关于经验分布的期望值。在最大熵模型中,这是一个重要的步骤,因为我们需要确保模型的期望等于训练数据的期望。

在给定的代码中,get_hat_Ep函数通过遍历self.xy_couple(即特征函数和类别的组合)来计算每个特征函数关于经验分布的期望值。具体来说,对于每个特征函数和类别的组合,它计算该组合在训练数据中出现的次数(即self.xy_couple[xy]),然后除以总的数据个数(即self.N),得到该组合的经验概率。然后,它将这个经验概率存储在self.hat_Ep中,以便后续使用。

总的来说,get_hat_Ep函数的功能是计算并存储每个特征函数关于经验分布的期望值,这是最大熵模型训练过程中的一个重要步骤。

 

def get_Ep(self,index):
        f,y = self.id_xy[index]
        # print(f,y)
        ans = 0
        # print(self.X)
        for x_i in self.X:
            if f not in x_i:
                continue
            pyx = self.get_Pyx(x_i,y)
            ans += pyx / self.N
            # print("ans",ans,pyx)
        return ans

  

get_Ep函数的功能是计算特征函数关于模型分布的期望值。在最大熵模型中,这是一个重要的步骤,因为我们需要确保模型的期望等于训练数据的期望。

在给定的代码中,get_Ep函数通过遍历self.X(即输入数据)来计算每个特征函数关于模型分布的期望值。具体来说,对于每个输入数据点,它首先检查该数据点是否包含特征函数对应的特征。如果包含,它则计算该数据点对应的类别的条件概率(即self.get_Pyx(x_i,y)),然后将这个条件概率累加到ans中。最后,它将ans除以总的数据个数(即self.N),得到该特征函数关于模型分布的期望值。

总的来说,get_Ep函数的功能是计算并返回每个特征函数关于模型分布的期望值,这是最大熵模型训练过程中的一个重要步骤。

 

代码输出:

['youth', 'no', 'no', '1'] ['age', 'working', 'house', 'credit_situation']

['youth', 'no', 'no', '2'] ['age', 'working', 'house', 'credit_situation']

['youth', 'yes', 'no', '2'] ['age', 'working', 'house', 'credit_situation']

['youth', 'yes', 'yes', '1'] ['age', 'working', 'house', 'credit_situation']

['youth', 'no', 'no', '1'] ['age', 'working', 'house', 'credit_situation']

['mid', 'no', 'no', '1'] ['age', 'working', 'house', 'credit_situation']

['mid', 'no', 'no', '2'] ['age', 'working', 'house', 'credit_situation']

['mid', 'yes', 'yes', '2'] ['age', 'working', 'house', 'credit_situation']

['mid', 'no', 'yes', '3'] ['age', 'working', 'house', 'credit_situation']

['mid', 'no', 'yes', '3'] ['age', 'working', 'house', 'credit_situation']

['elder', 'yes', 'no', '2'] ['age', 'working', 'house', 'credit_situation']

['elder', 'yes', 'no', '3'] ['age', 'working', 'house', 'credit_situation']

['elder', 'no', 'no', '1'] ['age', 'working', 'house', 'credit_situation']

[['age_elder', 'working_yes', 'house_no', 'credit_situation_2'], ['age_elder', 'working_yes', 'house_no', 'credit_situation_3'], ['age_elder', 'working_no', 'house_no', 'credit_situation_1']]

 

 

【最大熵模型和逻辑回归的区别】

最大熵模型和逻辑回归确实都可以被视为线性指数族函数,但它们之间还是存在一些关键的区别:

1. 特征选择:在逻辑回归中,特征通常是输入变量的函数,而在最大熵模型中,特征可以是输入和输出变量的联合函数。这使得最大熵模型可以更灵活地建模输入和输出之间的复杂关系。

2. 模型形式:虽然两者都可以被视为线性指数族函数,但它们的具体形式有所不同。逻辑回归通常用于二分类问题,其模型形式为P(Y=1|X) = 1 / (1 + exp(-w·X))。而最大熵模型可以用于多分类问题,其模型形式为P(Y|X) = exp(w·f(X,Y)) / Z(X),其中Z(X)是归一化因子。

3. 训练目标:逻辑回归的训练目标是最大化对数似然函数,而最大熵模型的训练目标是在满足特征函数期望等于经验期望的约束条件下,最大化模型的熵。

4. 应用领域:逻辑回归广泛应用于各种分类问题,如信用评分、疾病预测等。而最大熵模型主要应用于自然语言处理领域,如文本分类、词性标注、命名实体识别等。

总的来说,虽然最大熵模型和逻辑回归在形式上有一些相似之处,但它们在特征选择、模型形式、训练目标和应用领域等方面都有各自的特点。

 

 



标签:机器,no,模型,get,self,算法,xy,特征函数
From: https://blog.51cto.com/u_11908275/8416163

相关文章

  • 机器学习——注意力汇聚:Nadaraya-Watson 核回归
    上节介绍了框架下的注意力机制的主要成分 图10.1.3:查询(自主提示)和键(非自主提示)之间的交互形成了注意力汇聚;注意力汇聚有选择地聚合了值(感官输入)以生成最终的输出。本节将介绍注意力汇聚的更多细节,以便从宏观上了解注意力机制在实践中的运作方式。具体来说,1964年提出的Nadara......
  • 算法刷题记录-哈希表
    算法刷题记录-哈希表有效的字母异位词给定两个字符串*s*和*t*,编写一个函数来判断*t*是否是*s*的字母异位词。注意:若*s*和*t*中每个字符出现的次数都相同,则称*s*和*t*互为字母异位词。示例1:输入:s="anagram",t="nagaram"输出:true示例2:输入:s......
  • 大语言模型量化方法对比:GPTQ、GGUF、AWQ
    在过去的一年里,大型语言模型(llm)有了飞速的发展,在本文中,我们将探讨几种(量化)的方式,除此以外,还会介绍分片及不同的保存和压缩策略。说明:每次加载LLM示例后,建议清除缓存,以防止出现OutOfMemory错误。delmodel,tokenizer,pipeimporttorchtorch.cuda.empty_cache()如......
  • 机器学习-小样本情况下如何机器学习
    交叉验证是在机器学习建立模型和验证模型参数时常用的办法。交叉验证,顾名思义,就是重复的使用数据,把得到的样本数据进行切分,组合为不同的训练集和测试集,用训练集来训练模型,用测试集来评估模型预测的好坏。在此基础上可以得到多组不同的训练集和测试集,某次训练集中的某样本在下次可......
  • KMeans算法全面解析与应用案例
    本文深入探讨了KMeans聚类算法的核心原理、实际应用、优缺点以及在文本聚类中的特殊用途,为您在聚类分析和自然语言处理方面提供有价值的见解和指导。关注TechLead,分享AI全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验......
  • 测试开发常见算法题
    1.冒泡排序deffaet_sort(test:list)->list:"""冒泡排序"""foriinrange(len(test)):forjinrange(len(test)-i-1):iftest[j]>test[j+1]:test[j],test[j+1]=test[j+1],te......
  • 算法~base64算法理解
    base64Base64是一种用于将二进制数据编码成ASCII字符的编码方式。它主要用于在文字环境中传输或存储二进制数据,如在电子邮件、XML文件、URL参数等。Base64编码不是一种加密算法,而是一种编码方式,其主要作用是将二进制数据转换为文本数据,以便更容易在文本协议中处理。Base64......
  • 倾斜摄影三维模型根节点合并的纹理压缩与抽稀关键技术分析
    倾斜摄影三维模型根节点合并的纹理压缩与抽稀关键技术分析 倾斜摄影三维模型的根节点合并、纹理压缩和抽稀是关键的技术,可以有效地减少模型数据的大小,提高渲染效率和加载速度。在本文中,我们将对这三个技术进行详细分析。1、根节点合并:倾斜摄影生成的三维模型往往由多个子节......
  • 新火种AI | 字节跳动低调踏上AI之路:大厂纷纷入局大模型,未来将何去何从?
    作者:小岩最近一段时间,字节跳动的中层员工们可能都在思考一个问题:是否应该出售一部分公司股票?众所周知,字节跳动是极具影响力的公司巨头,更被评为“全球最具价值的独角兽公司”。不过,字节跳动尚未上市,对自己的财务状况始终处于严格保密的状态,从不对外界披露任何业绩。可就在最近,字节跳......
  • 对匈牙利算法的一些解释
    首先看蓝书上的代码为什么即将开始dfs时,没有一开始就把vis[i]标记了?其实dfs的流程是从左部的一个节点出发,考察右部的一个节点,如果右部的节点已经匹配了,下次dfs直接从这个右部节点的匹配点开始计算,所以vis的标记都是标记的右部节点,左部节点是不用标记的(因为是匹配二分图,只会被访......