【机器学习】深度学习概论(二)

五、受限玻尔兹曼机(Restricted Boltzmann Machine,RBM)

cd92844354fd82de0dda2e407a255bb3.jpeg

5.1 RBM介绍

ac3c3d62a55ab04d25874f23db8fd57b.jpeg

示例代码:

Python 编写了一个简单的 RBM 实现,并用一些假数据训练了它。然后,他展示了如何用 RBM 来解释用户的电影偏好,以及如何用 RBM 来生成电影推荐:

使用一些假数据训练了RBM。

  • 爱丽丝:(哈利波特 = 1,阿凡达 = 1,LOTR 3 = 1,角斗士 = 0,泰坦尼克号 = 0,闪光 = 0)。SF/奇幻大粉丝。

  • 鲍勃:(哈利波特 = 1,阿凡达 = 0,LOTR 3 = 1,角斗士 = 0,泰坦尼克号 = 0,闪光 = 0)。SF/奇幻迷,但不喜欢《阿凡达》。

  • 卡罗尔:(哈利波特 = 1,阿凡达 = 1,LOTR 3 = 1,角斗士 = 0,泰坦尼克号 = 0,闪光 = 0)。SF/奇幻大粉丝。

  • 大卫:(哈利波特 = 0,阿凡达 = 0,LOTR 3 = 1,角斗士 = 1,泰坦尼克号 = 1,闪光 = 0)。奥斯卡大奖得主的粉丝。

  • 埃里克:(哈利波特 = 0,阿凡达 = 0,LOTR 3 = 1,角斗士 = 1,泰坦尼克号 = 1,闪光 = 0)。奥斯卡奖得主的粉丝,泰坦尼克号除外。

  • 弗雷德:(哈利波特 = 0,阿凡达 = 0,LOTR 3 = 1,角斗士 = 1,泰坦尼克号 = 1,闪光 = 0)。奥斯卡大奖得主的粉丝。

该网络学习了以下权重:

548d3601f2c5346b051d114ea1b184f3.png

请注意,第一个隐藏单元似乎对应于奥斯卡奖得主,第二个隐藏单元似乎对应于 SF/奇幻电影,正如我们所希望的那样。

如果我们给 RBM 一个新用户 George,他将 (Harry Potter = 0, Avatar = 0, LOTR 3 = 0, Gladiator = 1, Titanic = 1, Glitter = 0) 作为他的偏好,会发生什么?它打开了奥斯卡奖得主单元(但不是 SF/奇幻单元),正确地猜测乔治可能喜欢奥斯卡奖得主的电影。

如果我们只激活 SF/幻想单元,并运行一系列不同的 RBM,会发生什么?在我的试验中,它打开了哈利波特、阿凡达和 LOTR 3 三次;它打开了《阿凡达》和《LOTR 3》,但没有打开《哈利波特》一次;它打开了哈利波特和 LOTR 3,但没有打开阿凡达,两次。请注意,根据我们的训练示例,这些生成的偏好确实符合我们期望真正的 SF/奇幻粉丝想要观看的内容。

# 导入未来模块,用于兼容不同版本的Python
from __future__ import print_function
# 导入numpy库,用于科学计算
import numpy as np


# 定义一个类,表示受限玻尔兹曼机
class RBM:
    # 定义初始化方法,接受可见层单元数和隐藏层单元数作为参数
    def __init__(self, num_visible, num_hidden):
        # 将隐藏层单元数和可见层单元数赋值给类的属性
        self.num_hidden = num_hidden
        self.num_visible = num_visible
        # 设置一个调试打印的标志,用于控制是否打印训练信息
        self.debug_print = True


        # 创建一个随机数生成器,指定随机种子为1234
        np_rng = np.random.RandomState(1234)


        # 创建一个权重矩阵,用于存储可见层和隐藏层之间的连接权重
        # 权重矩阵的形状为(num_visible, num_hidden),即每一列对应一个隐藏单元,每一行对应一个可见单元
        # 权重矩阵的初始值为均匀分布在[-0.1 * np.sqrt(6. / (num_hidden + num_visible)),
        # 0.1 * np.sqrt(6. / (num_hidden + num_visible))]之间的随机数,这个范围是根据论文中的建议选择的
        self.weights = np.asarray(np_rng.uniform(
                low=-0.1 * np.sqrt(6. / (num_hidden + num_visible)),
                            high=0.1 * np.sqrt(6. / (num_hidden + num_visible)),
                            size=(num_visible, num_hidden)))




        # 在权重矩阵的第一行和第一列插入零,用于表示偏置单元的权重
        # 偏置单元是一种特殊的单元,它的值始终为1,用于增加模型的灵活性
        # 第一行的权重表示隐藏层的偏置,第一列的权重表示可见层的偏置
        self.weights = np.insert(self.weights, 0, 0, axis = 0)
        self.weights = np.insert(self.weights, 0, 0, axis = 1)


    # 定义一个训练方法,接受数据,最大训练轮数,学习率等参数
    def train(self, data, max_epochs = 1000, learning_rate = 0.1):
        # 获取数据的样本数,即第一个维度的大小
        num_examples = data.shape[0]


        # 在数据的第一列插入1,用于表示偏置单元的值
        data = np.insert(data, 0, 1, axis = 1)


        # 遍历训练轮数
        for epoch in range(max_epochs):      
            # 将数据作为可见层的状态,计算隐藏层的激活值
            # 这是正向传播的过程,也称为正相对比散度阶段,或者现实阶段
            # 激活值等于数据与权重矩阵的点积,形状为(num_examples, num_hidden + 1)
            pos_hidden_activations = np.dot(data, self.weights)      
            # 计算隐藏层的激活概率,即隐藏层的单元以一定的概率被激活(取值为1)
            # 激活概率是通过逻辑斯蒂函数(或称为Sigmoid函数)计算的,它可以将任意值映射到(0,1)之间
            # 形状仍为(num_examples, num_hidden + 1)
            pos_hidden_probs = self._logistic(pos_hidden_activations)
            # 将第一列的激活概率设为1,用于表示偏置单元的值
            pos_hidden_probs[:,0] = 1 # Fix the bias unit.
            # 根据隐藏层的激活概率,生成隐藏层的状态
            # 隐藏层的状态是一个二值的矩阵,形状为(num_examples, num_hidden + 1)
            # 隐藏层的状态等于激活概率是否大于一个随机数,如果大于则为1,否则为0
            pos_hidden_states = pos_hidden_probs > np.random.rand(num_examples, self.num_hidden + 1)
            # 注意,我们在计算关联矩阵时,使用的是隐藏层的激活概率,而不是隐藏层的状态
            # 我们也可以使用状态,具体可以参考Hinton的论文《A Practical Guide to Training Restricted Boltzmann Machines》的第三节
            # 关联矩阵是可见层和隐藏层的状态的外积,形状为(num_visible + 1, num_hidden + 1)
            pos_associations = np.dot(data.T, pos_hidden_probs)


            # 从隐藏层的状态重构可见层的激活值
            # 这是反向传播的过程,也称为负相对比散度阶段,或者梦境阶段
            # 激活值等于隐藏层的状态与权重矩阵的转置的点积,形状为(num_examples, num_visible + 1)
            neg_visible_activations = np.dot(pos_hidden_states, self.weights.T)
            # 计算可见层的激活概率,即可见层的单元以一定的概率被激活(取值为1)
            # 激活概率是通过逻辑斯蒂函数(或称为Sigmoid函数)计算的,它可以将任意值映射到(0,1)之间
            # 形状仍为(num_examples, num_visible + 1)
            neg_visible_probs = self._logistic(neg_visible_activations)
            # 将第一列的激活概率设为1,用于表示偏置单元的值
            neg_visible_probs[:,0] = 1 # Fix the bias unit.
            # 从可见层的激活概率计算隐藏层的激活值
            # 激活值等于可见层的激活概率与权重矩阵的点积,形状为(num_examples, num_hidden + 1)
            neg_hidden_activations = np.dot(neg_visible_probs, self.weights)
            # 计算隐藏层的激活概率,即隐藏层的单元以一定的概率被激活(取值为1)
            # 激活概率是通过逻辑斯蒂函数(或称为Sigmoid函数)计算的,它可以将任意值映射到(0,1)之间
            # 形状仍为(num_examples, num_hidden + 1)
            neg_hidden_probs = self._logistic(neg_hidden_activations)
            # 注意,我们在计算关联矩阵时,使用的是可见层和隐藏层的激活概率,而不是状态
            # 关联矩阵是可见层和隐藏层的激活概率的外积,形状为(num_visible + 1, num_hidden + 1)
            neg_associations = np.dot(neg_visible_probs.T, neg_hidden_probs)


            # 更新权重矩阵
            # 权重矩阵的更新量等于学习率乘以正相关联矩阵减去负相关联矩阵,再除以样本数
            # 这样可以使得正相的概率增大,负相的概率减小,从而最大化数据的似然度
            # 更新权重矩阵,使用学习率、正相联和负相联的差值除以样本数作为增量
            self.weights += learning_rate * ((pos_associations - neg_associations) / num_examples)


            # 计算误差,使用数据和负可见概率的差的平方和
            error = np.sum((data - neg_visible_probs) ** 2)
            # 如果开启了调试打印,打印出每个迭代的误差
            if self.debug_print:
                print("Epoch %s: error is %s" % (epoch, error))




    # 定义一个方法,用于从可见层运行网络,得到隐藏层的状态
    def run_visible(self, data):
        # 获取样本数
        num_examples = data.shape[0]
        
        # 创建一个矩阵,每一行是一个训练样本对应的隐藏单元(加上一个偏置单元)
        hidden_states = np.ones((num_examples, self.num_hidden + 1))
        
        # 在数据的第一列插入偏置单元,值为1
        data = np.insert(data, 0, 1, axis = 1)


        # 计算隐藏单元的激活值
        hidden_activations = np.dot(data, self.weights)
        # 计算隐藏单元被激活的概率
        hidden_probs = self._logistic(hidden_activations)
        # 根据概率随机激活隐藏单元
        hidden_states[:,:] = hidden_probs > np.random.rand(num_examples, self.num_hidden + 1)
        # 始终将偏置单元设置为1
        # hidden_states[:,0] = 1


        # 忽略偏置单元
        hidden_states = hidden_states[:,1:]
        return hidden_states
    
    # 定义一个方法,用于从隐藏层运行网络,得到可见层的状态
    # TODO: 去除这个方法和`run_visible`之间的代码重复?
    def run_hidden(self, data):
        # 获取样本数
        num_examples = data.shape[0]


        # 创建一个矩阵,每一行是一个训练样本对应的可见单元(加上一个偏置单元)
        visible_states = np.ones((num_examples, self.num_visible + 1))


        # 在数据的第一列插入偏置单元,值为1
        data = np.insert(data, 0, 1, axis = 1)


        # 计算可见单元的激活值
        visible_activations = np.dot(data, self.weights.T)
        # 计算可见单元被激活的概率
        visible_probs = self._logistic(visible_activations)
        # 根据概率随机激活可见单元
        visible_states[:,:] = visible_probs > np.random.rand(num_examples, self.num_visible + 1)
        # 始终将偏置单元设置为1
        # visible_states[:,0] = 1


        # 忽略偏置单元
        visible_states = visible_states[:,1:]
        return visible_states
    
    # 定义一个方法,用于生成梦境样本,即从网络中随机抽取可见层的状态
    def daydream(self, num_samples):
        # 创建一个矩阵,每一行是一个可见单元(加上一个偏置单元)的样本
        samples = np.ones((num_samples, self.num_visible + 1))


        # 从均匀分布中取第一个样本
        samples[0,1:] = np.random.rand(self.num_visible)


        # 开始交替的吉布斯采样
        # 注意,我们保持隐藏单元的二进制状态,但是将可见单元作为实数概率
        # 参见 Hinton 的 "A Practical Guide to Training Restricted Boltzmann Machines" 的第三节
        # 了解更多原因
        for i in range(1, num_samples):
            visible = samples[i-1,:]


            # 计算隐藏单元的激活值
            hidden_activations = np.dot(visible, self.weights)      
            # 计算隐藏单元被激活的概率
            hidden_probs = self._logistic(hidden_activations)
            # 根据概率随机激活隐藏单元
            hidden_states = hidden_probs > np.random.rand(self.num_hidden + 1)
            # 始终将偏置单元设置为1
            hidden_states[0] = 1


            # 重新计算可见单元被激活的概率
            visible_activations = np.dot(hidden_states, self.weights.T)
            visible_probs = self._logistic(visible_activations)
            visible_states = visible_probs > np.random.rand(self.num_visible + 1)
            samples[i,:] = visible_states


        # 忽略偏置单元(第一列),因为它们总是被设置为1
        return samples[:,1:]                                         
                                       
# 判断是否是主模块,如果是,则执行以下代码
if __name__ == '__main__':
    # 创建一个受限玻尔兹曼机的实例,指定可见层单元数为6,隐藏层单元数为2
    r = RBM(num_visible = 6, num_hidden = 2)
    # 创建一个训练数据的数组,每一行是一个样本,每一列是一个特征
    # 这里的数据是一个二值的矩阵,表示6个特征的存在或缺失
    training_data = np.array([[1,1,1,0,0,0],[1,0,1,0,0,0],[1,1,1,0,0,0],[0,0,1,1,1,0], [0,0,1,1,0,0],[0,0,1,1,1,0]])
    # 调用训练方法,指定最大训练轮数为5000
    r.train(training_data, max_epochs = 5000)
    # 打印出训练后的权重矩阵
    print(r.weights)
    # 创建一个用户数据的数组,表示一个新的样本
    user = np.array([[0,0,0,1,1,0]])
    # 打印出从可见层运行网络得到的隐藏层的状态
    print(r.run_visible(user))

输出结果:

a02cf3b28e5c85960fe24bd6de98a550.png

5.2 深度玻尔兹曼机

深度玻尔兹曼机(Deep Boltzmann Machine,DBM)是一种基于能量的生成模型,它可以用来学习复杂数据的概率分布。DBM由多层隐变量组成,每层隐变量之间没有连接,但是每层隐变量都与下一层可见变量或上一层隐变量相连。DBM的最底层是可见层,它表示观测到的数据,例如图像、文本或音频。DBM的目标是最大化数据的对数似然,即让模型生成的数据尽可能接近真实数据。DBM的训练过程涉及到两个阶段:预训练和微调。预训练是使用贪婪逐层算法,将每两层隐变量视为一个受限玻尔兹曼机(Restricted Boltzmann Machine,RBM),并用对比散度(Contrastive Divergence,CD)算法进行无监督学习。微调是使用随机最大似然(Stochastic Maximum Likelihood,SML)算法,对整个模型进行联合优化,以提高模型的泛化能力。

DBM具有以下几个优点:

  • DBM可以从高维、非线性、非高斯的数据中学习出抽象的特征表示,从而实现数据的降维和特征提取。

  • DBM可以用于生成新的数据样本,例如生成新的图像或文本,从而实现数据的增强和创造。

  • DBM可以用于多种任务,例如分类、回归、聚类、协同过滤、推荐系统等,只需在模型的顶层添加一个适当的输出层即可。

DBM也有以下几个缺点:

  • DBM的训练过程比较复杂和耗时,需要大量的计算资源和数据量。

  • DBM的训练过程涉及到很多超参数的选择,例如学习率、批量大小、采样步数、正则化项等,这些超参数对模型的性能有很大的影响,但是很难确定最优的值。

  • DBM的理论分析比较困难,很多性质和定理还没有得到严格的证明,例如模型的收敛性、稳定性、可解释性等

5.3 深度置信网

深度置信网(Deep Belief Network,DBN)是一种基于图模型的生成模型,它由多层受限玻尔兹曼机(RBM)堆叠而成。DBN的最底层是可见层,它表示观测到的数据,例如图像、文本或音频。DBN的最顶层是一个无向图,它表示数据的高层抽象特征。DBN的中间层是有向图,它表示数据的中间层特征。DBN的目标是最大化数据的对数似然,即让模型生成的数据尽可能接近真实数据。DBN的训练过程涉及到两个阶段:预训练和微调。预训练是使用贪婪逐层算法,将每两层视为一个RBM,并用CD算法进行无监督学习。微调是使用反向传播(Backpropagation,BP)算法,对整个模型进行有监督学习,以提高模型的泛化能力。

DBN具有以下几个优点:

  • DBN可以从高维、非线性、非高斯的数据中学习出抽象的特征表示,从而实现数据的降维和特征提取。

  • DBN可以用于生成新的数据样本,例如生成新的图像或文本,从而实现数据的增强和创造。

  • DBN可以用于多种任务,例如分类、回归、聚类、协同过滤、推荐系统等,只需在模型的顶层添加一个适当的输出层即可。

DBN也有以下几个缺点:

  • DBN的训练过程比较复杂和耗时,需要大量的计算资源和数据量。

  • DBN的训练过程涉及到很多超参数的选择,例如学习率、批量大小、采样步数、正则化项等,这些超参数对模型的性能有很大的影响,但是很难确定最优的值。

  • DBN的理论分析比较困难,很多性质和定理还没有得到严格的证明,例如模型的收敛性、稳定性、可解释性等

附录:

受限玻尔兹曼机应用场景

a5beb9b5b283ab132f8a7c152b5b1ce6.png

各种激活函数的优缺点

e1100bf2fbb47f081b8343292fe5f731.png

各种激活函数各有优缺点,在深度学习中都有其适用场景。

  • Sigmoid和Tanh函数是传统的激活函数,具有输出范围有限、优化稳定等优点,但容易过饱和,梯度弥散。

  • ReLU函数是近年来流行的激活函数,具有计算速度快、容易训练等优点,但容易发生“死神经元”问题。

  • Leaky ReLU、ELU和SELU等函数是ReLU函数的改进版本,解决了“死神经元”问题。

  • softmax函数常用于多分类任务,可以用来输出概率分布

参考网址

https://blog.echen.me/2011/07/18/introduction-to-restricted-boltzmann-machines/

https://github.com/python-pillow/Pillow/ Python 图像库 

https://blog.echen.me/2011/07/18/introduction-to-restricted-boltzmann-machines/ 受限玻尔兹曼机简介 (echen.me)

The End

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Cocos3D项目中fbx模型转gITF模型和glb模型

1.npm安装:先按照npm哈 npm install --save fbx2gltf -g 2. 到指定目录 cd C:\Program Files\nodejs\node_global\node_modules\fbx2gltf\bin\Windows_NT cmd命令行界面进入node_modules\fbx2gltf文件下的bin文件,然后根据平台选择进入相应目录&#…

mac node基本操作

1 查看所有版本 npm view node versions输出 2 查看已经安装的版本 n list3 安装指定版本 sudo -E n 16.0.04 切换版本 sudo n 16.0.05 查看版本 node -v

ssm基于java的网上手机销售系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本网上手机销售系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息…

代码随想录刷题笔记(DAY3)

今日总结:虽然之前刷过链表,但这次做的是有些费力的,也有了更深的理解。整理完今天的 Vue 笔记就睡。。。 DAY 3 01. 移除链表元素(No. 203) 题目链接:https://leetcode.cn/problems/remove-linked-list-…

复数值神经网络可能是深度学习的未来

一、说明 复数这种东西,在人的头脑中似乎抽象、似乎复杂,然而,对于计算机来说,一点也不抽象,不复杂,那么,将复数概念推广到神经网络会是什么结果呢?本篇介绍国外的一些同行的尝试实践,请我们注意观察他们的进展。

详解“量子极限下运行的光学神经网络”——相干伊辛机

量子计算和量子启发计算可能成为解答复杂优化问题的新前沿,而经典计算机在历史上是无法解决这些问题的。 当今最快的计算机可能需要数千年才能完成高度复杂的计算,包括涉及许多变量的组合优化问题;研究人员正在努力将解决这些问题所需的时间缩…

大数据学习(29)-Spark Shuffle

&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦&#x1f91…

苹果电脑Dock栏优化软件 mac功能亮点

hyperdock mac是一款Dock优化软件,hyperdock支持使用窗口自动排列功能,您可以直接通过将窗口拖拉至屏幕上方来快速最大化至全屏,又或者拖动到左右来进行左分屏和右分屏。而且Dock优化软件还有一个特色便是对Dock的强大管理哪里能力&#xff0…

RISC Zero zkVM Host Guest 101

1. 引言 在RISC Zero zkVM应用程序中,host为运行RISC Zero zkVM的机器(或系统)。host为不可信agent,负责设置zkVM环境和处理执行过程中的输入输出。 host程序(代码),是指: zkVM应…

设计模式-Java版本

文章目录 前言设计原则单一职责原则开闭原则里氏替换原则迪米特法则接口隔离原则依赖倒置原则 设计模式构建类型工厂模式抽象工厂建造者模式原型模式单例模式 结构型适配器模式桥接模式组合模式装饰器模式代理模式外观模式享元模式 行为模式责任链模式命令模式迭代器模式中介模…

SONiC和ONL所依赖的Debian版本说明

Debian 的最新几个版本 下一代 Debian 正式发行版的代号为 trixie — 测试(testing)版 Debian 12 (bookworm) — 当前的稳定(stable)版 Debian 11 (bullseye) — 当前的旧的稳定(oldstable)版 Debian 10&a…

PPT录制视频的方法,轻松提升演示效果!

在现代工作和学习中,ppt是一种常见的演示工具,而将ppt转化为视频形式更能方便分享和传播。本文将介绍两种ppt录制视频的方法,每一种方法都将有详细的步骤和简要的介绍,通过这些方法,你可以轻松将ppt制作成视频&#xf…

IDEA/VScode + Git Blame

IDEA IDEA中支持查看每行代码的commit信息,这是靠git blame命令来完成的。 鼠标悬置在上面,可以看到更多信息。 VScode vscode中有相应插件完成类似的工作。 找到一个Git Blame插件,就是专门用来完成这项工作的。 安装完成后,下…

C语言rand函数,srand函数,time函数实现随机数,及猜数字小游戏

怀心之所爱,奔赴山河 前言 最近在复习c的知识,想起之前写过一个猜数字小游戏,所以今天就把自己关于随机数的使用经验分享一下,希望对大家有帮助。 一.rand函数 1.函数的声明如下 可以看到,返回值是int类型&#xff…

Elasticsearch 8.X进阶搜索之“图搜图”实战

Elasticsearch 8.X “图搜图”实战 1、什么是图搜图? "图搜图"指的是通过图像搜索的一种方法,用户可以通过上传一张图片,搜索引擎会返回类似或者相关的图片结果。这种搜索方式不需要用户输入文字,而是通过比较图片的视…

认识微服务---Spring Cloud

一、服务架构演变 1、单体架构:将业务的所有功能集中在一个项目开发,打包成一个部署。 优点: 架构简单部署成本低 缺点: 耦合度高不利于大型项目开发 2、分布式架构 :根据业务功能对系统进行拆分,每个…

自检服务器,无需服务器、不用编程。

自检服务器,无需服务器、不用编程。 大家好,我是JavaPub. 这几年自媒体原来热,很多人都知道了个人 IP 的重要性。连一个搞中医的朋友都要要做一个自己的网站,而且不想学编程、还不想花 RMB 租云服务。 老读者都知道&#xff0c…

Android : 使用GestureOverlayView进行手势识别—简单应用

示例图: GestureOverlayView介绍: GestureOverlayView 是 Android 开发中用于识别和显示手势的视图组件。它允许用户在屏幕上绘制手势,并且应用程序可以检测和响应这些手势。以下是关于 GestureOverlayView 的主要特点: 手势识别…

OpenHarmony南向之Camera简述

Camera驱动框架 该驱动框架模型内部分为三层,依次为HDI实现层、框架层和设备适配层: HDI实现层:实现OHOS(OpenHarmony Operation System)相机标准南向接口。框架层:对接HDI实现层的控制、流的转发&#x…

无需翻墙|Stable Diffusion WebUI 安装|AI绘画

前言 最近终于有机会从围墙里往外看,了解到外面的世界已经有了天翻地覆的变化,感叹万千,笔者在本地mac,windows,linux,docker部署了不下20遍后,整理出来的linux极简避坑安装方案,供…