【BERT】详解

BERT 简介

  • BERT 是谷歌在 2018 年时提出的一种基于 Transformer 的双向编码器的表示学习模型,它在多个 NLP 任务上刷新了记录。它利用了大量的无标注文本进行预训练,预训练任务有掩码语言模型和下一句预测,掩码语言模型指的是随机地替换文本中的一些词为掩码符号,并让它通过上下文信息来预测原来的词是什么。而下一句预测则是给定两个句子,然后让它预测第二个句子是不是第一个句子的下一句。

  • 由于 BERT 中编码器的强大学习能力,特别是上下文信息学习能力,使得它在预训练任务时学习到了大量通用的语言知识,而这些知识可以应用在下游任务中来提高性能。下游任务指的是句子对关系、文本分类、阅读理解、序列标注、语言翻译等任务。同时它也很容易适应不同的下游任务,只需在模型后面加上下游任务所需的输出层,然后使用少量的有标注文本数据来进行微调,这样可以节省大量的时间和资源,而且可以不用针对不同的任务重新设计模型结构,并从头开始训练模型。

  • BERT 的缺点在于需要大量的算力和无标注文本数据来进行预训练,从而使得下游任务只能在 BERT 的模型权重上进行微调。同时由于掩码符号的存在,使得预训练时的数据和微调、预测时的数据格式的不一致。

BERT 文本数据的 Mask 机制的规则如下

  • 随机选择一个样本中 15% 的词,然后有 80% 的概率替换为 [MASK] 符号,10 % 的概率替换为任意一个词,10% 的概率不替换。

Token 和 Token 化

  • Token

    将文本分割成一个个的最小单元,最小单元可以是字、词或者字符。Token 的目的是为了让模型能够理解和表示文本的语义和结构,同时也可以避免出现未登录词和新词无法识别的问题。

  • Token

    将文本分割成 Token,然后还会添加一些特殊的符号,例如 [CLS]、[SEP]、[PAD]、[UNK] 等,用来表示文本的开始、结束、填充、未知等含义。最后给每个 Token 分配一个唯一的 ID ,方便通过 Embedding 层来进行向量化。它的好处是可以减小词表的大小和解决未登录词和新词无法识别的问题,从而提高模型的泛化能力。

    一般不同的模型会有不同的 Token 化,而且同一个模型在面对不同语言时,也会有不同的 Token 化。

  • BERT 的 WordPiece Token

    WordPiece Token 化是 BERT 用来对文本进行 Token 化的过程。WordPiece Token 化是一个基于统计的 Token 化方法,它的基本思想是基于一个预先构建的词表,从最长的子词开始,逐步将单词分割成更小的子词,直到所有的子词都在词表中,或者达到最小的字符为止。

BERT 模型的结构

  • 结构图

  • 结构详解
    BERT 的网络结构主要是由 输入层编码器(Encoder)层输出层组成,其中:

    • 输入层是由 Token Embedding、句子 Embedding、位置 Embedding 组成的,一般是将它们的值进行相加来作为输入层的输出。

      • Token Embedding 会先将句子进行 Token 化,也就是将文本分割成一个个的最小单元 Token,然后再给它分配一个唯一的 ID,再经过 Embedding 层映射后得到一个对应的向量。

      • 句子(Sentence) Embedding 会分别给第一个句子的所有 Token 都分配 0 作为 ID,用来标记它们属于第一个句子。给第二个句子的所有 Token 都分配 1 作为 ID,用来标记它们属于第二个句子。

      • 位置(Position) Embedding :因为 BERT 的注意力层是并行化计算的,因此无法知道每个 Token 对应的位置是什么,所以需要输入一个位置信息给模型。BERT 会给每一个位置分配一个可学习的固定长度为 768 的向量,这些向量作为模型的参数,可以在训练的过程中进行更新。BERT 的最大的位置为 512,也就是最长可以输入的句子长度为 512.

      • 维度变化过程

        输入: [512]

        中间: [512]

          Token Embedding -> [512, 768]
          Sentence Embedding -> [512]         ->(相加)-> [512, 768]
          Position Embedding -> [512, 768]
        

        输出:[512, 768]

    • 编码器层 :编码器层由多个 Transformer 中的编码器堆叠组成,而每个编码器又包括了两个子层,分别是多头自注意力层和前馈神经网络层。每个子层后面都有残差连接和归一化层。它可以对输入的文本序列进行编码,学习序列中 Token 之间的关系,从而提取序列的上下文信息。

      • 多头自注意力层 :由多个自注意力组成,自注意力指的是计算注意力时的张量都是同一个输入经过乘以不同的矩阵得到的。每一个自注意力都可以独立地学习上下文信息,从而可以学习到不同的上下文信息,使得多头自注意力层可以捕捉到更丰富的上下文信息。计算自注意力的时候,使用的是缩放点乘注意力公式:

        S o f t m a x ( Q K T d k ) Softmax(\frac{QK^T}{\sqrt{d_k}}) Softmax(dk QKT)

        其中,张量 Q Q Q K K K 分别是输入 X 分别乘以矩阵 W q W_q Wq W k W_k Wk 得到的。而 K T K^T KT 则是张量 K K K 的转置。而 d k d_k dk 则是 Embedding 的维度。 S o f t m a x Softmax Softmax 则是将计算结果转换为概率,其公式为:
        S o f t m a x ( x i ) = exp ⁡ x i ∑ j = 1 N exp ⁡ x j Softmax(x_i) = \frac{\exp^{x_i}}{\sum_{j=1}^{N}\exp^{x_j}} Softmax(xi)=j=1Nexpxjexpxi

        Q K T {QK^T} QKT 除以 d k {\sqrt{d_k}} dk 的作用是可以将 Q K T {QK^T} QKT 的结果缩放到一定的范围,避免计算出来的结果太大或太小,从而在使用 S o f t m a x Softmax Softmax 计算概率时,出现概率太大和太小的问题,使得模型更容易学习。具体解释可以参看:在计算注意力时为什么要除以词向量维度的开方?

      • 前馈神经网络层 :由两个全连接层组成,作用是进一步提高网络的参数量,使得编码器具备更强大的学习能力。它的激活函数为 ReLU,也就是 m a x ( x , 0 ) max(x, 0) max(x,0)

    • 残差连接 :残差连接是将输入加到输出上作为新的输出,它的作用是让模型学习目标变成了学习输入和输出之间的差值,同时也减小了输入和输出之间的变化幅度,同时也可以避免梯度爆炸和梯度消失,这些作用都可以降低模型的学习难度。进而加速模型的收敛速度和减少训练时间,还能使得设计和训练更多的网络层数变得可能。

    • 层归一化 ( LayerNorm ) :层归一化可以对输入和输出进行缩放,使每一层的输入和输出服从相同的分布,降低模型的学习难度,提高模型的收敛速度。

      • 层归一化是按照最后一个维度来算的,也就是特征维度。这是因为在 BERT 模型中,每个输入的单词都会被表示为一个高维的向量,这个向量包含了单词的语义、语法、上下文等信息。如果我们按照其他的维度来进行归一化,比如句子维度或者批次维度,那么就会导致不同的单词之间的差异被消除,从而损失了单词的重要特征。而如果我们按照特征维度来进行归一化,那么就可以保证每个单词的特征向量都有相同的分布,从而提高模型的稳定性和泛化能力。

      • 层归一化的公式是:

      LayerNorm ( x ) = γ x − μ σ 2 + ϵ + β \text{LayerNorm}(x) = \gamma \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} + \beta LayerNorm(x)=γσ2+ϵ xμ+β

      • 参数说明: x x x 是输入张量, γ \gamma γ β \beta β 是缩放因子和偏移因子, μ \mu μ σ 2 \sigma^2 σ2 是沿着最后一个维度计算的均值和方差, ϵ \epsilon ϵ 是一个很小的正数,用于防止除以 0。

      • 计算过程:

        假设有张量 a ,它的维度为 [batch_size, sequence_len, embedding_dim],以下面的例子为例来计算。

        a = [
        [
        [1, 2, 3],
        [4, 5, 6]
        ]
        ]

        因此 a 的实际维度为 [1, 2, 3]。那么根据层归一化的计算规则,按照特征维度 embedding_dim 来计算均值和方差,以 [4, 5, 6] 来计算的过程如下:

          均值 = (4 + 5 + 6) / 3 = 5
          方差 = ((4-5)^2 + (5-5)^2 + (6-5)^2) / 3 = 2/3
        
    • 维度变化

      • 多头自注意力层:

        输入: [512, 768]
        输出:[512, 768]

      • 前馈神经网络层:

        输入: [512, 768]
        中间: [2048, 768]
        输出:[512, 768]

      输入: [512, 768]

      中间: [512, 768]

        多头自注意力 -> [512, 768]
        残差连接 -> [512, 768]
        层归一化 -> [512, 768]
        前馈神经网络:
            第一个全连接层:
                输入: [512, 768]
                输出: [2048, 768]
            第二个全连接层:
                输入: [2048, 768]
                输出: [512, 768]
        残差连接 -> [512, 768]
        层归一化 -> [512, 768]
      

      输出:[512, 768]

    • 输出层 :输出层一般是根据下游任务来决定的,例如文本分类则是一个全连接层。

在计算注意力时为什么要除以词向量维度的开方?

  • 在计算注意力时,要除以词向量维度的开方,是为了避免注意力分数过大或过小,导致梯度爆炸或消失的问题。这个方法是基于以下的数学原理:

    假设我们有两个词向量 q \mathbf{q} q k \mathbf{k} k,它们的维度是 d d d,它们的点积是 q ⋅ k \mathbf{q} \cdot \mathbf{k} qk,它们的范数是 ∥ q ∥ \|\mathbf{q}\| q ∥ k ∥ \|\mathbf{k}\| k。那么,我们可以得到以下的关系:

    q ⋅ k = ∥ q ∥ ∥ k ∥ cos ⁡ θ \mathbf{q} \cdot \mathbf{k} = \|\mathbf{q}\| \|\mathbf{k}\| \cos \theta qk=q∥∥kcosθ

    其中, θ \theta θ q \mathbf{q} q k \mathbf{k} k 之间的夹角。如果我们假设 q \mathbf{q} q k \mathbf{k} k 的每个元素都是从一个均值为 0,方差为 1 的正态分布中采样的随机变量,那么,我们可以得到以下的期望和方差:

    E [ q ⋅ k ] = 0 \mathbb{E}[\mathbf{q} \cdot \mathbf{k}] = 0 E[qk]=0

    V [ q ⋅ k ] = d \mathbb{V}[\mathbf{q} \cdot \mathbf{k}] = d V[qk]=d

    这意味着,当 d d d 很大时, q ⋅ k \mathbf{q} \cdot \mathbf{k} qk 的值也会很大,从而导致注意力分数的 softmax 函数的梯度接近于 0,这会影响模型的学习效率。为了解决这个问题,我们可以将 q ⋅ k \mathbf{q} \cdot \mathbf{k} qk 除以 d \sqrt{d} d ,这样就可以使得注意力分数的期望和方差都接近于 1,从而保持梯度的稳定性。这就是为什么要除以词向量维度的开方的原因。

BERT 的损失函数

  • 损失函数 是由两部分组成的,分别是掩码语言模型(MLM)的损失和下一句预测(NSP)的损失。这两个损失都是使用交叉熵(Cross Entropy)来计算的,但是具体的计算方式有所不同。而交叉熵的作用是用来衡量两个分布的差异程度,所以可以用来衡量真实值和预测值之间的差异程度。下面我将详细介绍 BERT 的损失函数的计算过程。

  • 掩码语言模型(MLM) 的损失是指模型在预测被掩码的词时产生的损失。具体来说,对于输入的每个词,模型会输出一个概率分布,表示该词是词表中每个词的可能性。然后,模型会根据真实的词和预测的概率分布来计算交叉熵损失。由于只有 15% 的词被掩码,所以只有这些词的损失会被计算,其他词的损失会被忽略。最后,模型会将所有被掩码的词的损失求平均,得到 MLM 的损失。MLM 的损失可以用下面的公式表示:

    L MLM = − 1 N ∑ i = 1 N log ⁡ P ( w i ∣ C i ) L_{\text{MLM}} = -\frac{1}{N}\sum_{i=1}^{N} \log P(w_i|C_i) LMLM=N1i=1NlogP(wiCi)

    其中, N N N 是被掩码的词的数量, w i w_i wi 是第 i i i 个被掩码的词, C i C_i Ci 是第 i i i 个被掩码的词的上下文, P ( w i ∣ C i ) P(w_i|C_i) P(wiCi) 是模型预测的概率分布。

  • 下一句预测(NSP) 的损失是指模型在判断两个句子是否连续时产生的损失。具体来说,对于输入的每个句子对,模型会输出一个二元概率分布,表示该句子对是连续的(IsNext)或者不连续的(NotNext)的可能性。然后,模型会根据真实的标签和预测的概率分布来计算交叉熵损失。最后,模型会将所有句子对的损失求平均,得到 NSP 的损失。NSP 的损失可以用下面的公式表示:

L NSP = − 1 M ∑ j = 1 M log ⁡ P ( y j ∣ S j ) L_{\text{NSP}} = -\frac{1}{M}\sum_{j=1}^{M} \log P(y_j|S_j) LNSP=M1j=1MlogP(yjSj)

其中, M M M 是句子对的数量, y j y_j yj 是第 j j j 个句子对的真实标签(0 表示 NotNext,1 表示 IsNext), S j S_j Sj 是第 j j j 个句子对, P ( y j ∣ S j ) P(y_j|S_j) P(yjSj) 是模型预测的概率分布。

BERT 的总损失是 MLM 的损失和 NSP 的损失的加权和,可以用下面的公式表示:

L BERT = L MLM + λ L NSP L_{\text{BERT}} = L_{\text{MLM}} + \lambda L_{\text{NSP}} LBERT=LMLM+λLNSP

其中, λ \lambda λ 是一个超参数,用来控制两个损失的相对重要性。在原始的 BERT 论文¹中, λ \lambda λ 被设置为 1,表示两个损失的权重相同。

BERT 的激活函数

  • ReLU :是一个计算简单的非线性函数,但是它可能会导致神经节点死亡和梯度消失,也就是当神经节点的输出为 0 之后,它之后的输出都将会一直是 0,无法再更新参数。它的公式为:
    m a x ( x , 0 ) max(x, 0) max(x,0)
    它的图像只在第一象限,且是一条 y=x 的直线。

  • GeLU :是一个基于高斯误差函数的激活函数,它的公式较为复杂,计算量也较大,但是它可以避免出现神经节点死亡和梯度消失的问题,而且它的非线性也比 ReLU 更好。它的公式为:

GELU ( x ) = x Φ ( x ) = x 1 2 [ 1 + erf ( x 2 ) ] \text{GELU}(x) = x \Phi(x) = x \frac{1}{2} \left[ 1 + \text{erf} \left( \frac{x}{\sqrt{2}} \right) \right] GELU(x)=xΦ(x)=x21[1+erf(2 x)]

其中, erf ( x ) \text{erf}(x) erf(x) 是高斯误差函数,它的定义是:

erf ( x ) = 2 π ∫ 0 x e − t 2 d t \text{erf}(x) = \frac{2}{\sqrt{\pi}} \int_{0}^{x} e^{-t^2} dt erf(x)=π 20xet2dt

GELU 激活函数的特点是,当 x x x 趋近于正无穷时,它的输出趋近于 x x x,当 x x x 趋近于负无穷时,它的输出趋近于 0,当 x x x 等于 0 时,它的输出等于 0。GELU 激活函数的图像如下:

GeLU

erf 函数的图像如下:

erf

BERT 的激活函数的使用情况是:

  • 在前馈神经网络中,BERT 使用了 GELU 激活函数,这是为了增加模型的非线性和复杂度,从而提高模型的表达能力。
  • 在自注意力机制中,BERT 使用了 ReLU 激活函数,这是为了减少模型的计算量,从而提高模型的运行速度。

BERT 的优化器

  • Adam:Adam 是一个自适应的优化器,它可以利用参数的梯度的均值和方差来动态地调整每个参数的学习率,从而实现一个平滑和稳定的优化过程。

    Adam 的公式是:

    m t = β 1 m t − 1 + ( 1 − β 1 ) g t \mathbf{m}_t = \beta_1 \mathbf{m}_{t-1} + (1 - \beta_1) \mathbf{g}_t mt=β1mt1+(1β1)gt

    v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 \mathbf{v}_t = \beta_2 \mathbf{v}_{t-1} + (1 - \beta_2) \mathbf{g}_t^2 vt=β2vt1+(1β2)gt2

    m ^ t = m t 1 − β 1 t \hat{\mathbf{m}}_t = \frac{\mathbf{m}_t}{1 - \beta_1^t} m^t=1β1tmt

    v ^ t = v t 1 − β 2 t \hat{\mathbf{v}}_t = \frac{\mathbf{v}_t}{1 - \beta_2^t} v^t=1β2tvt

    θ t + 1 = θ t − α m ^ t v ^ t + ϵ \mathbf{\theta}_{t+1} = \mathbf{\theta}_t - \alpha \frac{\hat{\mathbf{m}}_t}{\sqrt{\hat{\mathbf{v}}_t} + \epsilon} θt+1=θtαv^t +ϵm^t

    其中, g t \mathbf{g}_t gt 是第 t t t 步的梯度, m t \mathbf{m}_t mt v t \mathbf{v}_t vt 是第 t t t 步的一阶矩和二阶矩的估计, m ^ t \hat{\mathbf{m}}_t m^t v ^ t \hat{\mathbf{v}}_t v^t 是第 t t t 步的一阶矩和二阶矩的偏差修正, θ t \mathbf{\theta}_t θt 是第 t t t 步的参数, α \alpha α 是学习率, β 1 \beta_1 β1 β 2 \beta_2 β2 是一阶矩和二阶矩的衰减率, ϵ \epsilon ϵ 是一个很小的常数,用于防止除以零的错误。

    Adam 优化器的特点是,它可以自适应地调整每个参数的学习率,从而加速模型的收敛,同时也可以避免梯度的爆炸或消失的问题。Adam 优化器的优点是,它可以适用于各种类型的模型和数据,它也可以很容易地实现和使用。Adam 优化器的缺点是,它需要存储每个参数的一阶矩和二阶矩的估计,这会占用较多的内存空间,它也可能会导致一些参数的学习率过低,从而影响模型的性能。

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

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

相关文章

红外遥控,按键事件

问:android tv的遥控功能是如何实现的 答: Android TV 的遥控功能是通过红外遥控器或蓝牙遥控器来实现的。下面分别介绍这两种遥控器的工作原理: 红外遥控器: 红外遥控器是最常见的 Android TV 遥控器类型之一。 红外遥控器通…

Electron中苹果支付 Apple Pay inAppPurchase 内购支付

正在开发中,开发好了,写一个完整详细的过程,保证无脑集成即可 一、先创建一个App 一般情况下,在你看这篇文章的时候,说明你已经开发的app差不多了。 但是要上架app到Mac App Store,则要在appstoreconnect…

ROS第 9 课 编写简单的服务端 Server

文章目录 第 9 课 编写简单的服务端 Server1.创建服务器代码2.运行服务器节点 第 9 课 编写简单的服务端 Server 1.创建服务器代码 注意:在创建服务器代码之前,需要先创建工作空间和功能包,具体操作过程可前往目录“第4课 创建工作空间与功能…

14 STM32 - IIC (时序图+软件源码)

14.1 IIC简介 IIC(Inter-Integrated Circuit),中文集成电路总线,是一种串行通信总线,使用多主从架构。I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线…

VUE--组件的生命周期及其基本应用

VUE的生命周期 上图是实例生命周期的图表,需要注意以下几个重要时期: 创建期:beforeCreated、created 挂载期:beforeMount、mounted 更新期:beforeUpdate、updated 销毁期:beforeUnmount、unmounted 生命周…

云原生演进中的AI算力高效使用

0 1 云原生技术的普及与发展 云原生技术是一种基于容器技术的轻量级、高可用的应用架构,具有弹性扩展、快速部署、统一管理等特点。随着企业对敏捷开发和快速迭代的需求不断增加,云原生技术的普及与发展已成为不可逆转的趋势。 图1. 云原生技术发展之路…

软件测试阶段简介_单元测试、集成测试、配置项测试、系统测试

文章目录 前言一、软件测试“V”模型二、单元测试三、集成测试四、配置项测试五、系统测试总结 前言 一般来说,按照软件的研制阶段划分,软件测试可分为单元测试、集成测试、配置项测试、系统测试等。本文将对上述各测试阶段进行逐一介绍。 一、软件测试…

元宇宙:智慧城市建设的未来引擎与价值之源

在21世纪的技术大潮中,元宇宙的出现无疑是一场革命,其独特的概念与价值已经引发了全球范围内的关注。 作为新兴科技的前沿,元宇宙为智慧城市建设带来了无限的可能性和价值,有望成为未来城市发展的核心动力。 元宇宙,这…

【RPC】动态代理:面向接口编程,屏蔽RPC处理流程

一、背景 如果我问你,你知道动态代理吗? 你可能会如数家珍般地告诉我动态代理的作用以及好处。那我现在接着问你,你在项目中用过动态代理吗?这时候可能有些人就会犹豫了。那我再换一个方式问你,你在项目中有实现过统一…

【RT-DETR有效改进】轻量级网络ShuffleNetV2(附代码+修改教程)

前言 大家好,这里是RT-DETR有效涨点专栏。 本专栏的内容为根据ultralytics版本的RT-DETR进行改进,内容持续更新,每周更新文章数量3-10篇。 专栏以ResNet18、ResNet50为基础修改版本,同时修改内容也支持ResNet32、ResNet101和PP…

最新ChatGPT/GPT4科研应用与AI绘图及论文高效写作

详情点击链接:最新ChatGPT/GPT4科研应用与AI绘图及论文高效写作 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析,AI画图,图像识别,文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型Gemini以及大模型Clau…

详细介绍IP 地址、网络号和主机号、ABC三类、ip地址可分配问题、子网掩码、子网划分

1、 IP 地址: 网络之间互连的协议,是由4个字节(32位二进制)组成的逻辑上的地址。 将32位二进制进行分组,分成4组,每组8位(1个字节)。【ip地址通常使用十进制表示】ip地址分成四组之后,在逻辑上,分成网络号和主机号 2…

Educational Codeforces Round 161 (Rated for Div. 2)(A~E)

被教育咯 A - Tricky Template 题意: 思路:读题读了半天..可以发现,若对于第位而言,,那么c就一定与模板匹配。否则模板只需要取大写的即可。因此若所有的 ,都有,那么就不能构造,否…

gitgud.io+Sapphire注册账号教程

gitgud.io是一个仓库,地址 https://gitgud.io/,点进去之后会看到注册页面。 意思是需要通过注册这个Sapphire账户来登录。点击右边的Sapphire,就跳转到Sapphire的登陆页面,点击创建新账号,就进入注册页面。&#xff0…

中仕公考:国考进面后资格复审需要准备什么?

参加国考面试的考生在资格审核阶段需要准备以下材料: 1、本人身份证、学生证或工作证复印件。 2、公共科目笔试准考证复印件。 3、考试报名登记表。 4、本(专)科、研究生各阶段学历、学位证书(应届毕业生没有可以暂时不提供)。 5、报名资料上填写的各类证书材料…

【webrtc】GCC 7: call模块创建的ReceiveSideCongestionController

webrtc 代码学习&#xff08;三十二&#xff09; video RTT 作用笔记 从call模块说起 call模块创建的时候&#xff0c;会创建 src\call\call.h 线程&#xff1a; 统计 const std::unique_ptr<CallStats> call_stats_;SendDelayStats &#xff1a; 发送延迟统计 const…

统计学-R语言-6.1

文章目录 前言参数估计的原理总体、样本和统计量点估计区间估计评价估计量的标准有效性 总体均值的区间估计一个总体均值的估计&#xff08;大样本&#xff09;一个总体均值的估计&#xff08;小样本估计&#xff09; 练习 前言 本篇文章将开始介绍参数估计的相关知识。 参数估…

本地安装配置禅道BUG管理系统并结合内网穿透实现公网访问管理界面

文章目录 前言1. 本地安装配置BUG管理系统2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射本地服务3. 测试公网远程访问4. 配置固定二级子域名4.1 保留一个二级子域名5.1 配置二级子域名6. 使用固定二级子域名远程 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个…

《30天自制操作系统》学习笔记(七)

先体验一下编译仿真方法&#xff1a; 30天自制操作系统光盘代码在下面链接&#xff0c;但是没有编译仿真工具&#xff1a; https://gitee.com/zhanfei3000/30dayMakeOS 仿真工具在下面链接&#xff1a; https://gitee.com/909854136/nask-code-ide 这是一个集成的编译仿真工…

Docker五部曲之五:通过Docker和GitHub Action搭建个人CICD项目

文章目录 项目介绍Dockerfile解析compose.yml解析MySQL的准备工作Spring和环境变量的交互 GitHub Action解析项目测试结语 项目介绍 该项目是一个入门CICD-Demo&#xff0c;它由以下几部分组成&#xff1a; Dockerfile&#xff1a;用于构建自定义镜像compose.yml&#xff1a;…