[算法前沿]--004-transformer的前世今生

news2024/11/25 14:31:46

文章目录

  • 1.transformer介绍
    • 1.1 注意力机制
    • 1.2 Transformer架构
      • 1.2.1编码器
      • 1.2.2解码器
  • 2. Transformer中的模块
    • 2.1 注意模块
      • 2.1.1 缩放点积注意事项
      • 2.1.2 多头注意
    • 2.2 Transformer中的注意事项
      • 2.2.1 自注意
      • 2.2.2 掩蔽的自注意(自回归或因果注意)
      • 2.2.3 交叉注意

1.transformer介绍

  1. Transformer被认为是一种新型的深度前馈人工神经网络架构,它利用了自注意机制,可以处理输入序列项之间的长期相关性。
  2. 在大量领域中采用,如自然语言处理(NLP)、计算机视觉(CV)、,音频和语音处理、化学和生命科学;他们可以在前面提到的学科中实现SOTA性能。
  3. TransformerX库存储库

1.1 注意力机制

  1. 注意力是一种处理能力有限的认知资源分配方案
  2. 它同时生成源标记(单词)的翻译,1)这些相关位置的上下文向量和2)先前生成的单词。
  3. 注意力的特性
    1.软 2.硬 3.局部 4.全局
  4. 输入特征的形式
    1. Item-wise 2. Location-wise
  5. 输入表示
    1.Co-attention 2. Self-attention 3. Distinctive attention 4. Hierarchical attention
  6. 输出表示
    1. 多头 2.单输出 3.多维

1.2 Transformer架构

  1. 基本Transformer架构由两个主要构建块组成,即编码器和解码器块.(与序列到序列模型类似,Transformer使用编码器-解码器架构)
  2. 编码器从输入表示序列 (𝒙₁ , …, 𝒙ₙ) 生成嵌入向量𝒁 = (𝒛₁ , …, 𝒛ₙ),并将其传递给解码器以生成输出序列 (𝒚₁ , …, 𝒚ₘ).
    在每一步生成输出之前,𝒁 向量被送入解码器,因此该模型是自回归的。
  3. Sv9q4J

1.2.1编码器

  1. 编码器只是多个组件或层的堆栈-𝑵 在原始论文中是6。它们本身是两个子层,即多头自注意块和简单FC FFN(全连接的前馈网络)。
  2. 为了实现更深入的模型,研究人员通过包裹两个子层,然后进行层归一化,并实现残差连接。因此,每个子层的输出都是LayerNorm( 𝒙 + Sublayer( 𝒙 )) ,Sublayer(* 𝒙 *)*是在其内部实现的函数。所有子层以及嵌入的输出维度为𝒅 _model=512。
import tensorflow as tf

from transformerx.layers.positional_encoding import SinePositionalEncoding
from transformerx.layers.transformer_encoder_block import TransformerEncoderBlock


class TransformerEncoder(tf.keras.layers.Layer):
    def __init__(self,vocab_size,depth,norm_shape,ffn_num_hiddens,
        num_heads,
        n_blocks,
        dropout,
        bias=False,
    ):
        super().__init__()
        self.depth = depth
        self.n_blocks = n_blocks
        self.embedding = tf.keras.layers.Embedding(vocab_size, depth)
        self.pos_encoding = SinePositionalEncoding(depth, dropout)
        self.blocks = [
            TransformerEncoderBlock(
                depth,
                norm_shape,
                ffn_num_hiddens,
                num_heads,
                dropout,
                bias,
            )
            for _ in range(self.n_blocks)
        ]

    def call(self, X, valid_lens, **kwargs):
     X = self.pos_encoding(
            self.embedding(X) * tf.math.sqrt(tf.cast(self.depth, dtype=tf.float32)),
            **kwargs,
        )
        self.attention_weights = [None] * len(self.blocks)
        for i, blk in enumerate(self.blocks):
            X = blk(X, valid_lens, **kwargs)
            self.attention_weights[i] = blk.attention.attention.attention_weights
        return X

1.2.2解码器

  1. 除了编码器中使用的子层之外,解码器对编码器组件的输出应用多头注意。与编码器一样,残差连接连接到子层,然后进行层规范化。保证对该位置的预测𝒊 可以仅依赖于先前已知的位置,对自注意子层应用另一种修改以防止位置伴随着将输出嵌入偏移一个位置而注意其他位置。
import tensorflow as tf

from transformerx.layers.positional_encoding import SinePositionalEncoding
from transformerx.layers.transformer_decoder_block import TransformerDecoderBlock


class TransformerDecoder(tf.keras.layers.Layer):
    def __init__(self,vocab_size,depth,norm_shape,ffn_num_hiddens,num_heads,n_blocks,dropout,):
        super().__init__()
        self.depth = depth
        self.n_blocks = n_blocks
        self.embedding = tf.keras.layers.Embedding(vocab_size, depth)
        self.pos_encoding = SinePositionalEncoding(depth, dropout)
        self.blocks = [
            TransformerDecoderBlock(
                depth,
                norm_shape,
                ffn_num_hiddens,
                num_heads,
                dropout,
                i,
            )
            for i in range(n_blocks)
        ]
        self.dense = tf.keras.layers.Dense(vocab_size)

    def init_state(self, enc_outputs, enc_valid_lens):
        return [enc_outputs, enc_valid_lens, [None] * self.n_blocks]

    def call(self, X, state, **kwargs):
         X = self.pos_encoding(
            self.embedding(X) * tf.math.sqrt(tf.cast(self.depth, dtype=tf.float32)),
            **kwargs,
        )
        # 2 attention layers in decoder
        self._attention_weights = [[None] * len(self.blocks) for _ in range(2)]
        for i, blk in enumerate(self.blocks):
            X, state = blk(X, state, **kwargs)
            # Decoder self-attention weights
            self._attention_weights[0][i] = blk.attention1.attention.attention_weights
            # Encoder-decoder attention weights
            self._attention_weights[1][i] = blk.attention2.attention.attention_weights
        return self.dense(X), state

    @property
    def attention_weights(self):
        return self._attention_weights

2. Transformer中的模块

2.1 注意模块

该Transformer将信息检索中的查询键值(QKV)概念与注意力机制相结合

  1. 缩放的点积注意
  2. 多头注意力

2.1.1 缩放点积注意事项

  • 2ZAAJS
  • 3ktfQq

矩阵𝑨 在等式1中,通常称为注意力矩阵。他们使用点积注意力而不是加法注意力(使用具有单个隐藏层的前馈网络来计算兼容性函数)的原因是,由于矩阵乘法优化技术,速度和空间效率更快。

尽管如此,对于较大值的𝐷𝑘 这将softmax函数的梯度推到极小的梯度。为了抑制softmax函数的梯度消失问题,将键和查询的点积除以𝐷𝑘, 由于这个事实,它被称为缩放点积。

import os

import numpy as np
import tensorflow as tf

from transformerx.utils import masked_softmax


class DotProductAttention(tf.keras.layers.Layer):
    def __init__(
        self,
        dropout_rate: float = 0,
        scaled: bool = True,
        normalize: bool = False,
        kernel_initializer: str = "ones",
        kernel_regularizer: str = None,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.dropout_rate = dropout_rate
        self.dropout = tf.keras.layers.Dropout(self.dropout_rate)
        self.scaled = scaled
        self.normalize = normalize
        self.attention_weights = None
        self.kernel_initializer = kernel_initializer
        self.kernel_regularizer = kernel_regularizer

    def build(self, input_shape):
        super().build(input_shape)

    # Shape of queries: (batch_size, no. of queries, d)
    # Shape of keys: (batch_size, no. of key-value pairs, d)
    # Shape of values: (batch_size, no. of key-value pairs, value dimension)
    # Shape of attention_mask: (batch_size,) or (batch_size, no. of queries)
    def call(
        self,
        queries: tf.Tensor,
        keys: tf.Tensor,
        values: tf.Tensor,
        attention_mask: tf.Tensor = None,
        causal_mask: bool = None,
        training=None,
        **kwargs
    ) -> tf.Tensor:
        scores = tf.matmul(queries, keys, transpose_b=True)
        if self.scaled:
            # self.scale = self.add_weight(
            #     name="scale",
            #     shape=(scores.shape),
            #     initializer=self.kernel_initializer,
            #     regularizer=self.kernel_regularizer,
            #     trainable=True,
            # )
            depth = queries.shape[-1]
            # print(self.scale, scores.shape)
            # self.scale = tf.broadcast_to(scores.shape)
            # self.scale = tf.broadcast_to(
            #     tf.expand_dims(tf.expand_dims(self.scale, -1), -1), scores.shape
            # )
            scores = (
                scores
                / tf.math.sqrt(tf.cast(depth, dtype=tf.float32))
                # * self.scale
            )

        # apply causal mask
        if causal_mask:
            seq_len = tf.shape(queries)[2]
            heads = tf.shape(queries)[1]
            causal_mask = tf.ones((heads, seq_len)) * -1e9
            causal_mask = tf.linalg.LinearOperatorLowerTriangular(
                causal_mask
            ).to_dense()
            causal_mask = tf.expand_dims(causal_mask, axis=0)  # add batch dimension
            scores += tf.broadcast_to(
                tf.expand_dims(causal_mask, -1), scores.shape
            )  # broadcast across batch dimension

        self.attention_weights = masked_softmax(scores, attention_mask)
        # self.attention_weights = tf.nn.softmax(scores, axis=-1, mask=attention_mask)
        scores = tf.matmul(self.dropout(self.attention_weights, **kwargs), values)
        if self.normalize:
            depth = tf.cast(tf.shape(keys)[-1], tf.float32)
            scores /= tf.sqrt(depth)
        return scores

    def get_attention_weights(self):
        return self.attention_weights

2.1.2 多头注意

  • 引入多个注意力头而不是单个注意力函数,Transformer 将 𝐷𝑚 维的原始查询、键和值线性投影到 𝐷𝑘、𝐷𝑘 和 𝐷𝑣 维度,分别使用不同的线性投影 h次;通过它,可以并行计算这些投影上的注意力函数(等式 1),产生 𝐷𝑣 维输出值。然后该模型将它们连接起来并生成 𝐷𝑚 维表示。
import numpy as np
import tensorflow as tf
from einops import rearrange

from transformerx.layers.dot_product_attention import DotProductAttention


class MultiHeadAttention(tf.keras.layers.Layer):
    def __init__(
        self,
        d_model: int = 512,
        num_heads: int = 8,
        dropout_rate: float = 0,
        bias: bool = False,
        attention: str = "scaled_dotproduct",
        **kwargs,
    ):
        super(MultiHeadAttention, self).__init__(**kwargs)
        self.d_model = d_model
        self.num_heads = num_heads
        self.dropout_rate = dropout_rate
        self.bias = bias
        if attention == "scaled_dotproduct" or attention == None:
            self.attention = DotProductAttention(self.dropout_rate, scaled=True)
        elif attention == "dotproduct":
            self.attention = DotProductAttention(self.dropout_rate, scaled=False)
        self.W_q = tf.keras.layers.Dense(self.d_model, use_bias=self.bias)
        self.W_k = tf.keras.layers.Dense(self.d_model, use_bias=self.bias)
        self.W_v = tf.keras.layers.Dense(self.d_model, use_bias=self.bias)
        self.W_o = tf.keras.layers.Dense(self.d_model, use_bias=self.bias)

    def split_heads(self, X: tf.Tensor) -> tf.Tensor:
        # x = tf.reshape(x, shape=(x.shape[0], x.shape[1], self.num_heads, -1))
        X = rearrange(X, "b l (h dk) -> b l h dk", h=self.num_heads)
        # x = tf.transpose(x, perm=(0, 2, 1, 3))
        X = rearrange(X, "b l h dk -> b h l dk")
        # return tf.reshape(x, shape=(-1, x.shape[2], x.shape[3]))
        # X = rearrange(X, "b h l dk -> (b h) l dk")
        return X

    def inverse_transpose_qkv(self, X: tf.Tensor) -> tf.Tensor:

        # transpose back to original shape: (batch_size, seq_len, num_heads, head_dim)
        X = rearrange(X, "b h l d -> b l h d")

        # concatenate num_heads dimension with head_dim dimension:
        X = rearrange(X, "b l h d -> b l (h d)")
        return X

    def call(
        self,
        queries: tf.Tensor,
        values: tf.Tensor,
        keys: tf.Tensor,
        attention_mask: tf.Tensor = None,
        causal_mask: bool = False,
        **kwargs,
    ) -> tf.Tensor:
        
        queries = self.split_heads(self.W_q(queries))
        keys = self.split_heads(self.W_k(keys))
        values = self.split_heads(self.W_v(values))

        if attention_mask is not None:
            # On axis 0, copy the first item (scalar or vector) for num_heads
            # times, then copy the next item, and so on
            attention_mask = tf.repeat(attention_mask, repeats=self.num_heads, axis=0)

        # Shape of output: (batch_size * num_heads, no. of queries,
        # depth / num_heads)
        output = self.attention(
            queries, keys, values, attention_mask, causal_mask, **kwargs
        )

        # Shape of output_concat: (batch_size, no. of queries, depth)
        output_concat = self.inverse_transpose_qkv(output)
        return self.W_o(output_concat)

2.2 Transformer中的注意事项

  • Transformer论文中采用了三种不同的使用注意力的方法,它们在键、查询和值被输入注意力函数的方式方面是不同的。

2.2.1 自注意

  • 所有键、查询和值向量来自相同的序列,在Transformer的情况下,编码器的前一步输出,允许编码器同时注意其自身前一层中的所有位置,即。𝑸 = 𝑲 = 𝑽 = 𝑿 (以前的编码器输出)。

2.2.2 掩蔽的自注意(自回归或因果注意)

  • 尽管有编码器层,但在解码器的自注意中,查询被限制在它们之前的键值对位置以及它们的当前位置,以便保持自回归特性。这可以通过屏蔽无效位置并将其设置为负无限来实现,即𝑨 𝒊𝒋 = −∞ 如果𝒊 < 𝒋.

2.2.3 交叉注意

  • 这种类型的注意力从先前的解码器层获得其查询,而键和值是从编码器产量获得的。这基本上是在序列到序列模型中的编码器-解码器注意机制中使用的注意。换句话说,交叉注意力将两个不同的嵌入序列相结合,这些维度从一个序列中导出其查询,从另一个序列导出其键和值。
  • 假设S1和S2是两个嵌入序列,交叉注意力从S1获得其键和值,从S2获得其查询,然后计算注意力得分并生成长度为S2的结果序列。在Transformer的情况下,键和值是从编码器和前一步解码器输出的查询中导出的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

027:Mapbox GL加载circle样式图层,用data-driven风格绘制圆形

第027个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载circle样式图层。圆形样式图层在地图上呈现一个或多个实心圆。 您可以使用圆形图层来配置矢量切片中点或点集合要素的视觉外观。 圆形层渲染其半径以屏幕单位测量的圆形。 直接复制下面的 vue+mapbox源…

HTML5 <label> 标签、HTML5 <map> 标签

HTML5 <label> 标签 实例 HTML5 <label>标签用于为 input 元素做出标记。 带有两个输入字段和相关标记的简单 HTML 表单&#xff1a; <form action"demo_form.asp"><label for"male">Male</label><input type"ra…

【libuv】入门:queue work 的跨线程异步通信

通过阅读2012年的uv book 入门。有中文版 Handles and Requests libuv works by the user expressing interest in particular events. This is usually done by creating a handle to an I/O device, timer or process. Handles are opaque structs named as uv_TYPE_t where…

【分布式搜索引擎ES01】

分布式搜索引擎ES 分布式搜索引擎ES1.elasticsearch概念1.1.ES起源1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引 1.3.es的一些概念1.3.1.文档和字段1.3.2.索引和映射1.3.3.mysql与elasticsearch 1.4.1安装es、kibana、IK分词器1.4.2扩展词词典与停用词词典 2.索引库操作2.1.mappi…

设置网格旋转轴心【Babylonjs】

推荐&#xff1a;用 NSDT场景设计器 快速搭建3D场景。 Babylon.js 中的轴心&#xff08;Pivot Point&#xff09;是使用父节点设置网格变换中心的替代方法&#xff0c;即用作旋转中心或放大中心的点。 注意&#xff1a;使用 setPivotPoint 产生的行为不同于在 3DS Max 和 Maya …

vue-cli的使用和单页面应用程序、使用vue-cli脚手架创建vue项目步骤

1.vue-cli的使用 vue-cli是Vue.js开发的标准工具。它简化了程序员基于webpack创建工程化的Vue项目的过程。 引用自vue-cli官网上的一句话: 程序员可以专注在撰写应用上&#xff0c;而不必花好几天去纠结webpack配置的问题。 中文官网: https://cli.vuejs.org/zh/ 1.1 安装 …

WTI纽约原油CFD是什么?交易技巧有哪些?

WTI常称为美国原油或纽约原油&#xff0c;WTI是West Texas Intermediate 的简称&#xff0c;代表西德州中级原油(West Texas Intermediate)&#xff0c;偶尔称为德州轻甜原油(Texas Light Sweet)&#xff0c;它是大宗商品交易中核心的石油基准。那么本文就来具体的聊聊&#xf…

接口自动化【四】(在接口自动化【三】上的优化_加入了类前置,表格中替换数据,断言)

前言 一、使用 unittest框架结合setUpClass前置条件上传图片 二、一个类里面同时有类方法和实例方法----补充知识点&#xff08;需要引用类方法中的变量&#xff09; 三、结合类前置setUpClass&#xff0c;ddt&#xff0c;Excel表格数据&#xff0c;进行上传图片 四、加入l…

铁路应答器传输系统介绍

应答器传输系统 应答器传输系统是安全点式信息传输系统&#xff0c;通过应答器实现地面设备向车载设备传输信息。 应答器可根据应用需求向车载设备传输固定的&#xff08;通过无源应答器&#xff09;或可变的&#xff08;通过有源应答器&#xff09;上行链路数据。 当天线单…

【gitee】安装依赖报错

gitee地址 安装依赖时报错 Error while executing: npm ERR! D:\gongju\Git\cmd\git.EXE ls-remote -h -t git://github.com/adobe-web npm ERR! Error while executing: npm ERR! D:\git\Git\cmd\git.EXE ls-remote -h -t https://github.com/nhn/raphael.git npm ERR! npm…

数据分析实战 205 :项目分析思路 —— 某在线教育机构经营分析

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 文章目录 一、思路分析1.1 教育行业营收转化模型1.2 某教育机构利润结构1.3 问题确认与指标拆解&#xff1a;业务逻辑图1.3.1 确认毛利额数据异常的问题1.3.2 提升毛利额的方案 1.4 问题解决思路 一…

vue大屏开发系列—使用echart开发省市地图数据,并点击省获取市地图数据

1. 本文在基础上进行改进&#xff0c;后端使用若依后端 IofTV-Screen: &#x1f525;一个基于 vue、datav、Echart 框架的物联网可视化&#xff08;大屏展示&#xff09;模板&#xff0c;提供数据动态刷新渲染、屏幕适应、数据滚动配置&#xff0c;内部图表自由替换、Mixins注入…

JS Array数组常用方法(附上相应的用法及示例)

会改变原数组的方法 1、array.push(需要在末尾添加的数据)【给数组末尾添加一个元素】 2、array.unshift(需要在首位添加的数据)【给数组首位添加一个元素】 3、array.pop()【从数组末尾删除元素,不需要传参】 4、array.shift()【从数组首位开始删除元素,不需要传参】 5、arra…

【算法与数据结构】5 常见的时间复杂度,你知道吗?

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于算法与数据结构体系专栏,本专栏对于0基础者极为友好,欢迎与我一起完成算法与数据结构的从0到1的跨越 时间复杂度与空间复杂度 一、前情回顾二、常见的时间复杂度1.常见的…

Day954.以增量演进为手段 -遗留系统现代化实战

以增量演进为手段 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于以增量演进为手段的内容。 遗留系统现代化中的 HOW&#xff0c;也就是第三个原则&#xff0c;以增量演进为手段。 很多团队在一阵大张旗鼓的遗留系统改造后&#xff0c;终于迎来了最终的“梭哈”时…

【线程同步】

一个大佬的笔记&#xff0c;比较详细 一、线程概述 1.线程概述 与进程&#xff08;process&#xff09;类似&#xff0c;线程&#xff08;thread&#xff09;是允许应用程序并发执行多个任务的一种机 制。一个进程可以包含多个线程。同一个程序中的所有线程均会独立执行相同…

supervisor安装

说明 Supervisor翻译过来是监管人&#xff0c;在Linux中Supervisor是一个进程管理工具&#xff0c;当进程中断的时候Supervisor能自动重新启动它。可以运行在各种类Linux/unix的机器上&#xff0c;supervisor就是用Python开发的一套通用的进程管理程序&#xff0c;能将一个普通…

【别再困扰于LeetCode接雨水问题了 | 从暴力法=>动态规划=>单调栈】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Spring使用总结

Spring框架使用 前言处理事务管理声明式事务&#xff1a;编程式事务&#xff1a; 框架核心常见注解 AOP&#xff08; 面向切面编程&#xff09;切面和通知有哪些类型&#xff1f;切面的类型通知类型AOP实现使用场景 IOC(管理所有的JavaBean)依赖注入&#xff08;DI&#xff09;…

道氏转02,水羊转债,超达转债,晓鸣转债,中旗转债上市价格预测

道氏转02 基本信息 转债名称&#xff1a;道氏转02&#xff0c;评级&#xff1a;AA-&#xff0c;发行规模&#xff1a;26.0亿元。 正股名称&#xff1a;道氏技术&#xff0c;今日收盘价&#xff1a;13.41元&#xff0c;转股价格&#xff1a;15.46元。 当前转股价值 转债面值 / …