IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> Transfomer -> 正文阅读

[人工智能]Transfomer

1. Transformer 模型

? 参考链接 - ShusenWang - Transformer 模型

? transformer 模型完全基于 attention,attention 原本是用在 RNN 上的,这里我们把 RNN 去掉,只保留attention。

? transformer 模型是 Ashish Vaswani 等人在 NIPS 2017 中论文 Attention is all you need 提出。transformer 是一种 Seq2Seq 模型,它有一个 encoder 和一个 decoder,很适合做机器翻译。transformer 不是循环神经网络网络,transformer 没有循环的结构,transformer 只有 attention全连接层

? transformer 的实验效果非常惊人,可以完爆最好的 RNN 加 attention。机器翻译问题已经没有人用 RNN ,业界都用 transformer 加 bert。

? 思考一个问题,如果把 RNN 去掉,只保留 attention,然后用 attention 来搭一个深度神经网络来代替 RNN,该怎么做?

1.1 Attention for Seq2Seq Model

? 这一章从零开始起搭一个基于attention 的神经网络,从前面的 RNN 加 attention 模型入手,剥离 RNN ,保留 attention,然后搭建 attention 层与 self-attention 层。后面章节会把这些层组装起来,搭一个深度的 Seq2Seq 模型,搭出来的模型就是 transformer

? Seq2Seq 模型有一个 encoder 和一个 decoder。encoder 的输入是m 个向量 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? encoder 把这些输入的信息压缩到状态向量 h h h 中,最后一个状态 h m h_m hm? 是对所有输入的概括。

? decoder 是一个文本生成器,依次生成状态 s s s ,然后根据状态 s s s 生成单词,把新生成的单词作为下一个输入 x ′ \mathbf{x}^{\prime} x 。如果有 attention 的话,还需要计算 Context vector c c c ,每算出一个状态 s s s ,就算一个 c c c

在这里插入图片描述
? Context vector c c c 的计算如下,首先把 decoder 当前状态 s j s_j sj? ,与 encoder 所有 m 个状态 h 1 h_1 h1? h m h_m hm? 做对比。用 align 函数计算它们的相关性,把算出的数字 α i j \alpha_{ij} αij? 作为权重,这里的 i i i 是 encoder 状态 h h h 的下标,j是decoder 状态 s s s 的下标。

? 每算一个 Context vector c c c ,就要计算出 m 个权重 α \alpha α α 1 j \alpha_{1j} α1j? 一直到 α m j \alpha_{mj} αmj? ,每个 α \alpha α 对应一个状态 h h h

  • 权重 α \alpha α 的计算,权重是 h i h_i hi? s j s_j sj? 的函数,把向量 h i h_i hi? 乘到矩阵 W K \mathbf{W}_{K} WK? 上,得到向量 k : i \mathbf{k}_{:i} k:i? ;把向量 s j s_j sj? 乘到矩阵 W Q \mathbf{W}_{Q} WQ? 上,得到向量 q : j \mathbf{q}_{:j} q:j? 。这里的矩阵 W K \mathbf{W}_{K} WK? W Q \mathbf{W}_{Q} WQ?align 函数的参数,参数需要从训练数据里学习。

  • 我们要把 s j s_j sj? 这一个向量,与 encoder 所有 m 个状态向量 h h h 做对比,有 m m m h i h_i hi? 向量,所以有 m m m k : i \mathbf{k}_{:i} k:i? 向量。用这些 k : i \mathbf{k}_{:i} k:i? 向量组成 K \mathbf{K} K 矩阵,每个 k : i \mathbf{k}_{:i} k:i? 向量都是矩阵 K \mathbf{K} K 的列。

  • 计算矩阵 K T \mathbf{K}^{T} KT 与向量 q : j \mathbf{q}_{:j} q:j? 的乘积,结果是个 m 维的向量,然后用 Softmax 函数,输出一个 m m m 维的向量 α : j \alpha_{:j} α:j? 。把 α : j \alpha_{:j} α:j?m 个元素记为 α 1 j \alpha_{1j} α1j? α 2 j \alpha_{2j} α2j? 一直到 α m j \alpha_{mj} αmj?,这些元素全都介于 0~1 之间,而且它们相加等于1,这样我们就得到 m m m 个权重。

在这里插入图片描述
? 刚才把 decoder 状态 s j s_j sj? 还有 encoder 状态 h h h , 分别做线性变换,得到向量 q : j \mathbf{q}_{:j} q:j? k : i \mathbf{k}_{:i} k:i? ,它们被称为 Query Key Query 的意思是用来匹配 Key 值, Key 的意思是用来被 Query 匹配。

? 拿一个 Query 向量 q : j \mathbf{q}_{:j} q:j? 去对比所有m Key 向量,算出 m m m 个权重 α : j \alpha_{:j} α:j?,这 m m m α \alpha α 就说明 Query 和每个 Key 的匹配程度,匹配程度越高,权重 α \alpha α 就越大。

在这里插入图片描述? 除此之外,还要算 Value 向量 v : i \mathbf{v}_{:i} v:i? ,把 h i h_i hi? 乘到矩阵 W V \mathbf{W}_{V} WV? 上,得到向量 v : i \mathbf{v}_{:i} v:i? ,矩阵 W V \mathbf{W}_{V} WV? 也是个参数。attention 中一共有三个参数矩阵 W Q \mathbf{W}_{Q} WQ? W K \mathbf{W}_{K} WK? 以及 W V \mathbf{W}_{V} WV? ,他们都要从训练数据中学习。

? 上面说明了 Query Key Value 三种向量是怎么计算出来的,下面回过头来看这个例子。我们先把 decoder 当前状态 s j s_j sj? 映射到 Query 向量。具体是这么做的,拿参数矩阵 W Q \mathbf{W}_{Q} WQ? 与 decoder 状态 s j s_j sj? 相乘,得到 Query 向量 q : j \mathbf{q}_{:j} q:j?

? 然后把 encoder 所有 m 个状态, h 1 h_1 h1? h m h_m hm? 映射到 m m m Key 向量。具体这么做,拿参数矩阵 W K \mathbf{W}_{K} WK? 与 第 i i i 的状态 h i h_i hi? 相乘得到 Key 向量 k : i \mathbf{k}_{:i} k:i? 。对 h 1 h_1 h1? h m h_m hm? ,所有 m 个状态向量都做这种变换,得到 m m m Key 向量,把这 m m m Key 向量表示为矩阵 K \mathbf{K} K ,向量 k : i \mathbf{k}_{:i} k:i? 是矩阵 K \mathbf{K} K 的第 i i i 列。

? 用矩阵 K \mathbf{K} K 与向量 q : j \mathbf{q}_{:j} q:j? 计算出 m 维的权重向量 α : j \alpha_{:j} α:j?。向量 α : j \alpha_{:j} α:j? 的m 个元素分别是 α 1 j \alpha_{1j} α1j? α 2 j \alpha_{2j} α2j? 一直到 α m j \alpha_{mj} αmj?,每一个元素对应一个 h h h 向量。

? 接下来计算 Value 向量 v : i \mathbf{v}_{:i} v:i? ,那 encoder 的第 i i i 个状态向量 h i h_i hi? 与参数矩阵 W V \mathbf{W}_{V} WV? 相乘,得到一个 Value 向量 v : i \mathbf{v}_{:i} v:i? 。把所有 m 个状态 h 1 h_1 h1? h m h_m hm? 都做这种变换,得到 mValue 向量叫做 v : 1 \mathbf{v}_{:1} v:1? v : 2 \mathbf{v}_{:2} v:2? v : 3 \mathbf{v}_{:3} v:3? 一直到 v : m \mathbf{v}_{:m} v:m? ,每个
v : i \mathbf{v}_{:i} v:i? 对应一个 encoder 状态 h i h_i hi?

在这里插入图片描述
? 现在我们有了 m 个权重值 α \alpha α,以及 m m mValue 向量 v \mathbf{v} v 。 现在用 α \alpha α 做权重,把这 mValue 向量 v \mathbf{v} v 做加权平均,把结果作为新的 Context vector c j c_{j} cj? Context vector c j c_{j} cj? = = = α 1 j \alpha_{1j} α1j? × \times × v : 1 \mathbf{v}_{:1} v:1? + ? + +\cdots+ +?+ α m j \alpha_{mj} αmj? × \times × v : m \mathbf{v}_{:m} v:m? ,这种计算权重 α \alpha αContext vector c j c_{j} cj? 的方法就是 transformer 里面用的。

1.2 Attention without RNN

? attention 原本是用在 RNN 上的,思考如何才能剥离 RNN ,只保留 attention ?这样可以得到一个attention 层与 self-attention 层,transformer 就是由 attention 层与 self-attention 层组成的。

1.2.1 Attention Layer

? 我们先设计一个 attention 层,用于 Seq2Seq 模型。我们移除了 RNN 现在开始搭建 attention,考虑 Seq2Seq 模型,它有一个 encoder 和一个 decoder,encoder 的输入向量是 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? decoder 的输入是 x 1 ′ \mathbf{x}_1^{\prime} x1? x t ′ \mathbf{x}_t^{\prime} xt?

? 举个例子,把英语翻译成德语,英语句子里有 m 个单词变成了词向量 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? decoder 一次生成德语单词,把当前生成的德语单词作为下一轮的输入,现在已经生成了 t 个德语单词,把它们作为输入来生成下一个单词,新生成的德语单词会作为第 t + 1 t+1 t+1 个输入。

? 我们不用 RNN ,只用 attention。首先 encoder 的输入 x 1 \mathbf{x}_1 x1? x 2 \mathbf{x}_2 x2? x m \mathbf{x}_m xm? 来计算 Key Value 向量。用矩阵 W K \mathbf{W}_{K} WK? 把向量 x i \mathbf{x}_i xi? 变成 k : i \mathbf{k}_{:i} k:i? ,用矩阵 W V \mathbf{W}_{V} WV? 把向量 x i \mathbf{x}_i xi? 变成 v : i \mathbf{v}_{:i} v:i? 。于是 x 1 \mathbf{x}_1 x1? 就被映射成了向量 k : 1 \mathbf{k}_{:1} k:1? v : 1 \mathbf{v}_{:1} v:1? x 2 \mathbf{x}_2 x2? 就被映射成了 k : 2 \mathbf{k}_{:2} k:2? v : 2 \mathbf{v}_{:2} v:2? …… 同样道理,我们可以得到 m m m k \mathbf{k} k 向量和 v \mathbf{v} v 向量。

在这里插入图片描述
? 然后把 decoder 的输入 x 1 ′ \mathbf{x}_1^{\prime} x1? x 2 ′ \mathbf{x}_2^{\prime} x2? 一直到 x t ′ \mathbf{x}_t^{\prime} xt? 做线性变换,用矩阵 W Q \mathbf{W}_{Q} WQ? x j ′ \mathbf{x}_j^{\prime} xj? 映射到 Query 向量 q : j \mathbf{q}_{:j} q:j? 。decoder 有 t 个 输入向量,所以得到 t t t Query 向量 q : 1 \mathbf{q}_{:1} q:1? q : 2 \mathbf{q}_{:2} q:2? q : 3 \mathbf{q}_{:3} q:3? 一直到 q : t \mathbf{q}_{:t} q:t?

? 注意,一共有 3 个参数矩阵,encoder 中有两个,分别是矩阵 W K \mathbf{W}_{K} WK? W V \mathbf{W}_{V} WV? ,用来计算 Key Value
decoder 中有一个 W Q \mathbf{W}_{Q} WQ? ,用来计算 Query


? 现在开始计算权重 α \alpha α ,拿第一个 Query 向量 q : 1 \mathbf{q}_{:1} q:1? 与所有 m Key 向量做对比,通过比较 q : 1 \mathbf{q}_{:1} q:1? k : 1 \mathbf{k}_{:1} k:1? k : 2 \mathbf{k}_{:2} k:2? k : 3 \mathbf{k}_{:3} k:3? 一直到 k : m \mathbf{k}_{:m} k:m? m m m 个向量的相关性,算出 m m m 个权重值,记为这个 m m m 维 的向量 α : 1 \alpha_{:1} α:1?

在这里插入图片描述
? 具体是用这个公式计算出来的,把所有 m m m Key 向量表示为矩阵 K \mathbf{K} K K T \mathbf{K}^T KT 乘以向量 q : 1 \mathbf{q}_{:1} q:1? ,再做 Softmax 变换,得到这个 m m m 维 的向量 α : 1 \alpha_{:1} α:1?

? 然后计算 Context vector c : 1 \mathbf{c}_{:1} c:1? ,需要用到权重向量 α : 1 \alpha_{:1} α:1? 与所有 m Value 向量 v : 1 \mathbf{v}_{:1} v:1? v : 2 \mathbf{v}_{:2} v:2? v : 3 \mathbf{v}_{:3} v:3? 一直到 v : m \mathbf{v}_{:m} v:m? c : 1 \mathbf{c}_{:1} c:1? 是指 m m m v \mathbf{v} v 向量的加权平均,权重就是向量 α : 1 \alpha_{:1} α:1? m m m 个元素分别是 α 11 \alpha_{11} α11? α 21 \alpha_{21} α21?一直到 α m 1 \alpha_{m1} αm1?。把向量 v : 1 \mathbf{v}_{:1} v:1? v : m \mathbf{v}_{:m} v:m? 作为矩阵 V \mathbf{V} V 的列,那么这个加权平均就可以写成矩阵 V \mathbf{V} V 乘以向量 α : 1 \alpha_{:1} α:1?

在这里插入图片描述
? 现在计算权重向量 α : 2 \alpha_{:2} α:2?, 要用到第二个 Query 向量 q : 2 \mathbf{q}_{:2} q:2? ,以及所有 m 个k 向量 k : 1 \mathbf{k}_{:1} k:1? k : 2 \mathbf{k}_{:2} k:2? , k : 3 \mathbf{k}_{:3} k:3? 一直到 k : m \mathbf{k}_{:m} k:m?

在这里插入图片描述
? 然后计算Context vector c : 2 \mathbf{c}_{:2} c:2? , 要用到权重 α : 2 \alpha_{:2} α:2? 以及所有 m Value 向量 v : 1 \mathbf{v}_{:1} v:1? v : 2 \mathbf{v}_{:2} v:2? v : 3 \mathbf{v}_{:3} v:3? 一直到 v : m \mathbf{v}_{:m} v:m? c : 2 \mathbf{c}_{:2} c:2? m m m v \mathbf{v} v 向量的加权平均,权重就是向量 α : 2 \alpha_{:2} α:2? m m m 个元素。

在这里插入图片描述
? 用类似的方法计算出所有的 Context vector c \mathbf{c} c 。每个 c \mathbf{c} c 对应一个 x ′ \mathbf{x}^{\prime} x c : 1 \mathbf{c}_{:1} c:1? 对应 x 1 ′ \mathbf{x}_1^{\prime} x1? c : 2 \mathbf{c}_{:2} c:2? 对应 x 2 ′ \mathbf{x}_2^{\prime} x2? ,以此类推,decoder 的输入一共有 t x ′ \mathbf{x}^{\prime} x 向量,所以计算出 t t t c \mathbf{c} c 向量,这 t t tContext vector c \mathbf{c} c 就是最终的输出。可以用矩阵 C \mathbf{C} C 来表示这些向量,这些 c \mathbf{c} c 向量 c : 1 \mathbf{c}_{:1} c:1? c : 2 \mathbf{c}_{:2} c:2? c : 3 \mathbf{c}_{:3} c:3? 一直到 c : t \mathbf{c}_{:t} c:t? 都是矩阵 C \mathbf{C} C 的列。

在这里插入图片描述
? 上图中计算 c : j \mathbf{c}_{:j} c:j? 公式表明,想要计算一个向量 c : j \mathbf{c}_{:j} c:j? ,要用到所有的 v \mathbf{v} v 向量,(即 V \mathbf{V} V );所有的 k \mathbf{k} k 向量,(即 K \mathbf{K} K );以及一个 q : j \mathbf{q}_{:j} q:j? 向量。比如向量 c : 2 \mathbf{c}_{:2} c:2? 依赖于 q : 2 \mathbf{q}_{:2} q:2? 以及所有的 v \mathbf{v} v 向量 和 k \mathbf{k} k 向量,所以 c : 2 \mathbf{c}_{:2} c:2? 依赖于 decoder 一个输入 x 2 ′ \mathbf{x}_2^{\prime} x2? 以及 encoder 全部输入 x 1 \mathbf{x}_1 x1? x 2 \mathbf{x}_2 x2? x 3 \mathbf{x}_3 x3? 一直到 x m \mathbf{x}_m xm?

  • 举个例子,假如我们想把英语翻译成德语,可以把 m 个英文单词输入encoder,然后用 decoder 来依次产生德语单词。第二个Context vector c : 2 \mathbf{c}_{:2} c:2? 可以通过 Key Value 看到所有 m 个英文单词, c : 2 \mathbf{c}_{:2} c:2? 还能看到 x 2 ′ \mathbf{x}_2^{\prime} x2? ,也就是当前输入的德语单词。

  • 我们可以把 c : 2 \mathbf{c}_{:2} c:2? 作为特征向量,输入 Softmax 分类器,计算出一个概率分布 p 2 \mathbf{p}_{2} p2? ,然后通过 p 2 \mathbf{p}_{2} p2? 抽样得到第 3 个德语单词,编码成 x 3 ′ \mathbf{x}_3^{\prime} x3? 作为下一轮的输入。

在这里插入图片描述
? 不难看出,用 Attention Layer 做机器翻译跟用 RNN 非常类似,用 RNN 会把状态 h h h 作为特征向量,用 attention 的话会把Context vector c \mathbf{c} c 作为特征向量。不论是用 attention ,还是用 RNN 来搭一个 Seq2Seq 模型,输入与输出的大小都是一样的,所以显然可以用 attention layer 来代替 RNN。Attention layer 的好处是不会遗忘。向量 c : 2 \mathbf{c}_{:2} c:2? 是直接用所有 m 个英文词向量 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? 算出来的,所以 c : 2 \mathbf{c}_{:2} c:2? 知道整句英语。

? 把 Attention Layer 记为函数 Attn, 输入是矩阵 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X X \mathbf{X} X 的列是 encoder m m m 个输入向量 x 1 \mathbf{x}_1 x1? x 2 \mathbf{x}_2 x2? 一直到 x m \mathbf{x}_m xm? X ′ \mathbf{X}^{\prime} X 的列是 decoder t t t 个输入向量 x 1 ′ \mathbf{x}_1^{\prime} x1? x 2 ′ \mathbf{x}_2^{\prime} x2? 一直到 x t ′ \mathbf{x}_t^{\prime} xt? Attention 层有 3 个参数矩阵, W Q \mathbf{W}_{Q} WQ? W K \mathbf{W}_{K} WK? W V \mathbf{W}_{V} WV? ,它们要靠训练数据来学习。attention 层的输出是矩阵 C \mathbf{C} C ,它的列向量是 c : 1 \mathbf{c}_{:1} c:1? c : 2 \mathbf{c}_{:2} c:2? c : 3 \mathbf{c}_{:3} c:3? 一直到 c : t \mathbf{c}_{:t} c:t? t t t 个向量。

在这里插入图片描述
? 可以这样表示 attention 层,有两个输入序列 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X ,有一个输出序列 C \mathbf{C} C ,每个 c \mathbf{c} c 向量对应一个 x ′ \mathbf{x}^{\prime} x 向量。

1.3 Self-Attention without RNN

? 前面我们研究了 Seq2Seq 模型,我们删掉了 RNN,并且搭建了一个 attention 层,可以用 attention 层来做机器翻译。接下来搭建一个 Self-Attention 层,原理完全一样,仍可以用 self-attention 来取代 RNN。

? 前面介绍搭的 Attention Layer,attention 层用于 Seq2Seq,它有两个输入序列,比如左边是英语,右边是翻译出来的德语。我们用函数 Attn 来表示 attention 层,函数有 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X 两个输入。

? Self-attention 层不是 Seq2Seq,它只有一个输入序列,这就像是普通的 RNN 一样。self-attention 层也可以用 Attn 函数表示,这个函数跟前面的 Attn 完全一样,区别在于函数的输入,attention 层的输入是 X \mathbf{X} X X ′ \mathbf{X}^{\prime} X 两个矩阵。

? 用 self-attention 层,Attn 函数的两个输入都是 X \mathbf{X} X ,输出序列是 c : 1 \mathbf{c}_{:1} c:1? c : m \mathbf{c}_{:m} c:m? ,输出序列的长度跟输入是一样的,都是 m 。每个 c \mathbf{c} c 向量都对应一个 x \mathbf{x} x 向量,但是要 注意 c : i \mathbf{c}_{:i} c:i? 并非只依赖于 x i \mathbf{x}_i xi? ,而是依赖于所有 m x \mathbf{x} x 向量。改变任何一个 x \mathbf{x} x c : i \mathbf{c}_{:i} c:i? 就会发生变化。

在这里插入图片描述
? Self-Attention Layer 的原理跟前面的 Attention Layer 完全一样,只是输入不一样。Self-Attention Layer 只有一个输入序列 x 1 \mathbf{x}_1 x1? x 2 \mathbf{x}_2 x2? 一直到 x m \mathbf{x}_m xm?

? 1?? 第一步是做三种变换,把 x i \mathbf{x}_i xi? 映射到 q : i \mathbf{q}_{:i} q:i? k : i \mathbf{k}_{:i} k:i? v : i \mathbf{v}_{:i} v:i? 三个向量,参数矩阵还是 W Q \mathbf{W}_{Q} WQ? W K \mathbf{W}_{K} WK? W V \mathbf{W}_{V} WV?。线性变换之后, x 1 \mathbf{x}_1 x1? 被映射到 q : 1 \mathbf{q}_{:1} q:1? k : 1 \mathbf{k}_{:1} k:1? v : 1 \mathbf{v}_{:1} v:1? x 2 \mathbf{x}_2 x2? 被映射到 q : 2 \mathbf{q}_{:2} q:2? k : 2 \mathbf{k}_{:2} k:2? v : 2 \mathbf{v}_{:2} v:2? ,每个 x \mathbf{x} x 向量都会被映射成 q \mathbf{q} q k \mathbf{k} k v \mathbf{v} v 三个向量。

在这里插入图片描述
? 2?? 然后是计算权重向量 α \alpha α,公式还是一样。矩阵 K T \mathbf{K}^T KT 乘以 q : j \mathbf{q}_{:j} q:j? 向量,然后做 Softmax,得到 m 维向量 α : j \alpha_{:j} α:j?。下面这张图, α : 1 \alpha_{:1} α:1? 依赖于 q : 1 \mathbf{q}_{:1} q:1? ,以及所有 k \mathbf{k} k 向量 k : 1 \mathbf{k}_{:1} k:1? k : 2 \mathbf{k}_{:2} k:2? k : 3 \mathbf{k}_{:3} k:3? 一直到 k : m \mathbf{k}_{:m} k:m?

在这里插入图片描述
? 权重向量 α : 2 \alpha_{:2} α:2? 依赖于 q : 2 \mathbf{q}_{:2} q:2? ,以及所有 k \mathbf{k} k 向量, k : 1 \mathbf{k}_{:1} k:1? k : 2 \mathbf{k}_{:2} k:2? k : 3 \mathbf{k}_{:3} k:3? 一直到 k : m \mathbf{k}_{:m} k:m? 。用同样的公式计算出所有权重向量 α \alpha α,一共有 m α \alpha α 向量,每个向量都是 m m m 维 的。

在这里插入图片描述
? 3?? 现在可以开始计算 Context vector c \mathbf{c} c c : 1 \mathbf{c}_{:1} c:1? 是所有m v \mathbf{v} v 向量的加权平均,权重都是 α \alpha α。下面图中显示 c : 1 \mathbf{c}_{:1} c:1? 依赖于权重向量 α : 1 \alpha_{:1} α:1?,以及所有 m m m v \mathbf{v} v 向量 v : 1 \mathbf{v}_{:1} v:1? v : 2 \mathbf{v}_{:2} v:2? v : 3 \mathbf{v}_{:3} v:3? 一直到 v : m \mathbf{v}_{:m} v:m?

在这里插入图片描述
? 同样的方法计算 c : 2 \mathbf{c}_{:2} c:2? ,把所有的 m v \mathbf{v} v 向量 v : 1 \mathbf{v}_{:1} v:1? v : 2 \mathbf{v}_{:2} v:2? v : 3 \mathbf{v}_{:3} v:3? 一直到 v : m \mathbf{v}_{:m} v:m? 做加权平均,权重是向量 α : 2 \alpha_{:2} α:2? m m m 个元素。

在这里插入图片描述
? 做同样的操作,计算出所有m c \mathbf{c} c 向量,得到 c : 1 \mathbf{c}_{:1} c:1? c : 2 \mathbf{c}_{:2} c:2? c : 3 \mathbf{c}_{:3} c:3? 一直到 c : m \mathbf{c}_{:m} c:m? ,这 m m m c \mathbf{c} c 向量就是 self-attention 层的输出。

? j j j 个输出 c : j \mathbf{c}_{:j} c:j? 是这样算出来的,它依赖于矩阵 V \mathbf{V} V 矩阵 K \mathbf{K} K 以及向量 q : j \mathbf{q}_{:j} q:j? 。因为 c : j \mathbf{c}_{:j} c:j? 依赖于所有的 k \mathbf{k} k 和所有的 v \mathbf{v} v ,所以 c : j \mathbf{c}_{:j} c:j? 依赖于所有 m x \mathbf{x} x 向量, x 1 \mathbf{x}_1 x1? 一直到 x m \mathbf{x}_m xm?

在这里插入图片描述
? 上图里面,每个输入 x i \mathbf{x}_i xi? 的位置上都对应一个输出 c : i \mathbf{c}_{:i} c:i? 注意 c : i \mathbf{c}_{:i} c:i? 并非只依赖于 x i \mathbf{x}_i xi? ,而是依赖于所有的 x \mathbf{x} x 。改变任何一个 x \mathbf{x} x ,输出的 c : i \mathbf{c}_{:i} c:i? 都会发生变化。

? Self-attention Layer 的构造,输入是一个序列,从 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? 这个 m 这个向量,这一层有 3 个参数矩阵 W Q \mathbf{W}_{Q} WQ? W K \mathbf{W}_{K} WK? W V \mathbf{W}_{V} WV? ,这 3 个矩阵可以把每个 x \mathbf{x} x 映射到 q \mathbf{q} q k \mathbf{k} k v \mathbf{v} v 三个向量。输出也是一个序列,从 c : 1 \mathbf{c}_{:1} c:1? c : m \mathbf{c}_{:m} c:m? m 个向量,每个 x \mathbf{x} x 的位置上都有一个对应的 c \mathbf{c} c

? attentionself-attention 都用 Attn 这个函数来表示,这个函数有两个输入矩阵,attention 层的两个输入是 不同 的矩阵,self-attention 层的两个输入是 相同 的矩阵,都是 X \mathbf{X} X

在这里插入图片描述
总结
? attention 最早用于改进 Seq2Seq 模型;后来发现 attention 并不局限于 Seq2Seq 模型,而是可以用在所有的 RNN 上。如果只有一个 RNN 网络,那么 attention 就叫做 self-attention。后来有人发现根本不需要 RNN ,直接单独用 attention,反而效果更好,就是这篇论文《attention is all you need 》提出的 transformer 模型。

1.4 Multi-Head Attention

? 下面用构造的 attention layer 和 self-attention layer 来搭建一个深度神经网络。首先使用 attention 来组建 multi-head attention。

? self-attention 层输入是一个序列, x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? ,self-attention 层有 3 个参数矩阵 W Q \mathbf{W}_{Q} WQ? W K \mathbf{W}_{K} WK? W V \mathbf{W}_{V} WV? ,输出是一个序列 c : 1 \mathbf{c}_{:1} c:1? c : m \mathbf{c}_{:m} c:m? 。这样的 self-attention 层被称为 single-head self-attention

? multi-head self-attention 是用 l l l 个 single-head self-attention 组成的,它们各自有各自的参数,不共享参数。每个 single-head self-attention 有 3 个参数矩阵,所以 multi-head self attention 一共有 3 l 3l 3l 个参数矩阵。

? 所有 single-head self-attention 都有相同的输入,输入都是 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? 序列。但是他们的参数矩阵各不相同,所以输出的 c \mathbf{c} c 序列也各不相同。把 l l l 个single-head self-attention 输出的序列做 concatnation 堆叠起来,作为 multi-head self-attention 的最终输出,堆叠起来的 c \mathbf{c} c 向量变得更高。如果每个单头的输出都是 d × m d\times m d×m 的矩阵,那么多头的输出就是 l d × m ld\times m ld×m 的矩阵。

在这里插入图片描述
? 上面用 l l l 个单头 self-attention 构造出 多头self-attention。同样可以用 l l l 个 single-head attention 构造multi-head attention 。所有单头 attention 的输入都是这两个序列, x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? 以及 x 1 ′ \mathbf{x}_1^{\prime} x1? x m ′ \mathbf{x}_m^{\prime} xm? ,每个单头 attention 都有各自的参数矩阵。它们不共享参数,每个单头都有自己的输出序列 c \mathbf{c} c ,把单头输出的 c \mathbf{c} c 给堆叠起来,就是多头的输出。

在这里插入图片描述

1.4.1 Stacked Self-Attention Layers

? 已经构造出了 multi-head self-attentionmulti-head attention,接下来要用这两种层搭建一个深度神经网络。

? 首先用 multi-head self-attention全连接层 来搭建一个 encoder 的网络。多头 self-attention 层输入是序列 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? ,输出是序列 c : 1 \mathbf{c}_{:1} c:1? c : m \mathbf{c}_{:m} c:m? 。然后搭一个全连接层,把向量 c : 1 \mathbf{c}_{:1} c:1? 作为输入,全连接层把 c : 1 \mathbf{c}_{:1} c:1? 乘到参数矩阵 W U \mathbf{W}_{U} WU?上,然后再用 ReLU 或其他激活函数得到输出向量 u : 1 \mathbf{u}_{:1} u:1?

? 把同一个全连接层用到 c : 2 \mathbf{c}_{:2} c:2? 上,得到输出 u : 2 \mathbf{u}_{:2} u:2? u : 2 \mathbf{u}_{:2} u:2? 的计算方法完全相同。注意 两个全连接层是 完全相同 的,它们的参数矩阵都是 W U \mathbf{W}_{U} WU?

? 然后把全连接层用到 c : 3 \mathbf{c}_{:3} c:3? 上,输出 u : 3 \mathbf{u}_{:3} u:3? 。以此类推,得到 m m m 个输出向量 u \mathbf{u} u 。这些全连接层都是完全一样的,有同一个参数矩阵 W U \mathbf{W}_{U} WU?

在这里插入图片描述
? 刚才搭了两层,一个multi-head self-attention,一个全连接层,输入是 m 个向量 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? ,输出也是 m m m 个向量 u : 1 \mathbf{u}_{:1} u:1? u : m \mathbf{u}_{:m} u:m? u : i \mathbf{u}_{:i} u:i? 向量在 x i \mathbf{x}_i xi? 向量的位置上,但是 u : i \mathbf{u}_{:i} u:i? 不仅仅依赖于 x i \mathbf{x}_i xi? u : i \mathbf{u}_{:i} u:i? 依赖于所有 m m m x \mathbf{x} x 向量。改变任何一个 x \mathbf{x} x 向量, u : i \mathbf{u}_{:i} u:i? 都会发生变化。当然对 u : i \mathbf{u}_{:i} u:i? 影响最大的还是 x i \mathbf{x}_i xi?

在这里插入图片描述
? 可以继续搭更多层,可以再搭一个multi-head attention 层和一个全连接层。想搭多少层都可以,这样就可以搭建出一个深度神经网络,道理跟 多层 RNN 是一样的。

在这里插入图片描述
? 现在来搭建 transformer 模型的 encoder 网络。一个 block 有两层,一个multi-head attention 层和一个全连接层。输入是 512 × m 512\times m 512×m 的矩阵,输出也是 512 × m 512\times m 512×m 的矩阵。这里的m 是输入序列 x \mathbf{x} x 的长度,每个 x \mathbf{x} x 向量都是 512 维的。

在这里插入图片描述
? 左边是 encoder 网络的结构,输入是 512 × m 512\times m 512×m 的矩阵 X \mathbf{X} X X \mathbf{X} X 的每一列都是 512 维的词向量。右边是先前定义的一个block,它有两层,一个 self-attention 层和一个 Dense 层,它的输出也是 512 × m 512\times m 512×m 的矩阵。输入和输出的大小一样,所以可以用 ResNet 那种 skip-connection,把输入加到输出上。

? 然后搭第二个block ,输出还是 512 × m 512\times m 512×m 的矩阵,想搭多少个 block 都可以。transformerencoder 网络一共有 6 个blocks,每个 block 有两层,每个 block 都有自己的参数,blocks 之间不共享参数。最终的输出是 512 × m 512\times m 512×m 的矩阵,输出与输入的大小是一样的。

在这里插入图片描述
? 上面通过堆叠 multi-head self-attention 与全连接层搭建出 encoder 网络,encoder 网络有 6 个blocks,每个 block 有两层,现在进一步用到 attention 层,从而搭建 transformer 的 decoder 网络。

? transformer 是一个 Seq2Seq 模型,它有一个 encoder 和一个 decoder,输入是两个序列。如果我们想要把英语翻译成德语。那么 x \mathbf{x} x 序列是英语的词向量, x ′ \mathbf{x}^{\prime} x 是德语的词向量。

? 刚才已经搭建的 encoder 网络,有 6 个blocks,每个block 有两层。encoder 的输入和输出都是 512 维的向量,输入序列有 m 个词向量, x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? ,输出序列也有 m 个向量 u : 1 \mathbf{u}_{:1} u:1? u : m \mathbf{u}_{:m} u:m?

在这里插入图片描述
? 现在我们开始搭建 decoder 网络的一个 block。1?? block 的第一层是个 multi-head self-attention ,输入是序列 x 1 ′ \mathbf{x}_1^{\prime} x1? x t ′ \mathbf{x}_t^{\prime} xt? ,输出是序列 c : 1 \mathbf{c}_{:1} c:1? c : t \mathbf{c}_{:t} c:t? ,它们全都是 512 维的向量。

在这里插入图片描述

? 2?? 第二层是multi-head attention,这一层的输入是两个序列,一个序列是 u : 1 \mathbf{u}_{:1} u:1? u : m \mathbf{u}_{:m} u:m? ,它们是 encoder 网络输出;另一个序列是 c : 1 \mathbf{c}_{:1} c:1? c : t \mathbf{c}_{:t} c:t? 。多头 attention 层的输出是 z : 1 \mathbf{z}_{:1} z:1? z : t \mathbf{z}_{:t} z:t? ,它们也都是 512 维的向量。

在这里插入图片描述
? 3?? 最后再搭一个全连接层,输入是 512 维的向量 z : 1 \mathbf{z}_{:1} z:1? ,输出是 512 维的向量 s : 1 \mathbf{s}_{:1} s:1? 。全连接层都一样,都是把参数矩阵 W S \mathbf{W}_{S} WS? 与输入的 z \mathbf{z} z 向量相乘,然后用 ReLU 激活函数得到向量 s \mathbf{s} s

? 把 z : 2 \mathbf{z}_{:2} z:2? 作为输入全连接层输出 s : 2 \mathbf{s}_{:2} s:2? 。用同样的方法,把所有的 z \mathbf{z} z 向量都映射到 s \mathbf{s} s 向量。

在这里插入图片描述
? 我们已经搭建了decoder 网络的一个block,这个block 有三层,分别是 self-attention 层、attention 层以及全连接层。这三层组成 decoder 的一个 block

在这里插入图片描述
? 这个 block 需要两个输入序列,两个序列都是 512 维的向量,两个序列的长度分别是 mt。如果是把英语翻译成德语,那么 m m m 是英语句子的长度, t t t 是已经生成德语单词的数量。 这个block 的输出序列长度是 t ,每个向量都是 512 维的。把这个block 简化成右图所示,它有两个输入序列,一个输出序列。

在这里插入图片描述

1.5 Put Everything Together

? 现在我们已经有了所有模块,可以把这些模块拼起来,得到最终的 transformer 模型。

? 我们已经搭建好了encoder 网络,encoder 网络很简单,依次叠加 6 个blocks,每个 block 有两层,encoder 网络的输入是矩阵 X \mathbf{X} X , 它有 m 列,每列都是 512 维的词向量,输出是矩阵 U \mathbf{U} U ,它跟输入矩阵 X \mathbf{X} X 的大小完全一样,都是 512 × m 512 \times m 512×m

? 前面我们已经搭好了 decoder 网络的一个 block,它的输入是两个序列,输出是一个序列。图中 decoder 网络最底层的一个模块 Block 1 ,左边输入序列是 512 × m 512 \times m 512×m 的矩阵 U \mathbf{U} U ,也就是 encoder 网络的输出,右边的输入序列是 512 × t 512 \times t 512×t 的矩阵 X ′ \mathbf{X}^{\prime} X

在这里插入图片描述
? 在英语翻译德语的例子中, X ′ \mathbf{X}^{\prime} X 包含 t 个德语词向量,这个模块输出一个 512 × t 512 \times t 512×t 的矩阵,大小跟右边的输入 X ′ \mathbf{X}^{\prime} X 一样。

? 再来一个 block,左边的输入序列还是矩阵 U \mathbf{U} U , 也就是 encoder 网络的输出,右边输入序列是 decoder 上一个模块的输出。第二个block 的输出还是 512 × t 512 \times t 512×t 的矩阵。

? 一共搭了 6 个blocks,组成了 decoder 网络,每个block 有 3 层,分别是 self-attention 层、attention 层以及全连接层。每个block 有两个输入序列,一个输出序列。左边输入序列都是矩阵 U \mathbf{U} U ,也就是 encoder 网络的输出,右边输入序列是前一个 block 的输出。

在这里插入图片描述
? 左边 encoder 网络和右边 decoder 网络组合起来就是 transformer 模型,最终输出的序列是t个向量,每个向量都是 512 维的。


总结

? RNN Seq2Seq 模型有两个输入序列,encoder 的输入序列是 x 1 \mathbf{x}_1 x1? x m \mathbf{x}_m xm? decoder 输入序列是 x 1 ′ \mathbf{x}_1^{\prime} x1? x t ′ \mathbf{x}_t^{\prime} xt?

? transformer 模型也有这两个输入序列,decoder 输出 t 个状态,向量 s : 1 \mathbf{s}_{:1} s:1? s : t \mathbf{s}_{:t} s:t? RNN Seq2Seq 模型与 transformer 模型这两者的输入大小完全一样,输出大小也完全一样。

? 所以怎么样用 RNN 的,现在就可以怎么样用 transformer ,所有 RNN Seq2Seq 模型能做的,transformer 模型也同样能做。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. BERT 模型

? 参考链接 - ShusenWang - Transformer 模型

? Bidirectional Encoder Representations from Transformers (BERT) 是用来预训练 transformer 模型,BERT 在 2018 年提出,文章 BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 在 ACL 2019 上发表。BERT 的目的,数据训练transformer 模型的 encoder 的网络,从而大幅提高准确率。

? BERT 的基本想法有两个,一个想法是随机遮挡一个或者多个单词,让 encoder 网络根据上下文来预测被遮挡的单词,第二个想法是把两个句子放在一起,让 encoder 网络判断两句话是不是原文里相邻的两句话。BERT 用这两个任务来预训练 transformer 模型中的 encoder 网络。

2.1 Task 1: Predict Masked Words

? transformer 的 encoder 网络,输入是一句话,被分成很多个单词。Embedding 层把每一个单词映射到一个词向量,然后是 encoder 网络,它是由多个blocks 垒起来,每个 block 有两层,分别是 self-attention 层和 全连接层。

? the cat set on the mat 这句输入有 6 个单词,Embedding 层把这 6 个单词映射成 6 个词向量 x 1 \mathbf{x}_1 x1? x 6 \mathbf{x}_6 x6? encoder 网络的输入是 6 个词向量,所以最终输出 6 个向量 u : 1 \mathbf{u}_{:1} u:1? u : m \mathbf{u}_{:m} u:m?

在这里插入图片描述
? BERT 的第一个任务是预测被遮挡的单词,随机遮挡一个或者多个单词,然后问神经网络被遮住的单词是什么?比如下面这个例子里,第二个单词被遮住,于是让神经网络来预测第二个单词。

在这里插入图片描述

? 具体这么做,把输入的第二个单词替换成 [mask] 符号,[mask] 符号会被embedding 层编码成词向量到 x M \mathbf{x}_{\mathbf{M}} xM? 。把 [mask] 位置的输出记作向量 u M \mathbf{u}_{\mathbf{M}} uM? 注意 transformer 的 encoder 网络不是一对一映射,而是多对一。

? u M \mathbf{u}_{\mathbf{M}} uM? 向量不仅依赖于 x M \mathbf{x}_{\mathbf{M}} xM? ,而且依赖于所有 x \mathbf{x} x 向量, u M \mathbf{u}_{\mathbf{M}} uM? 在mask 位置上,但是 u M \mathbf{u}_{\mathbf{M}} uM? 知道整句话的信息。 u M \mathbf{u}_{\mathbf{M}} uM? 包含上下文信息,所以可以用 u M \mathbf{u}_{\mathbf{M}} uM? 来预测被遮挡的单词。

? 把 u M \mathbf{u}_{\mathbf{M}} uM? 作为特征向量,输入一个 Softmax 分类器,分类器的输出是一个概率分布 p \mathbf{p} p ,字典里每个单词都有一个概率值,通过概率值就能判断被遮挡单词是什么。

在这里插入图片描述
? 这个例子里遮住了 cat 这个单词。训练的时候,希望分类器的输出 p \mathbf{p} p 向量,接近单词 cat 的 one-hot 向量。把 cat 的 one-hot 向量记作 e \mathbf{e} e ,e 是 ground truth。先前得到的向量 p ,是分类器输出的概率分布。我们希望预测接近 ground truth,也就是让 p 接近 e。把 e \mathbf{e} e p \mathbf{p} p CrossEntropy 作为损失函数,用反向传播算出损失函数,关于模型参数的梯度,然后梯度下降来更新模型参数。

在这里插入图片描述
? BERT 会随机遮挡单词,把遮住的单词作为标签,BERT 预训练不需要人工标注的数据集,可以用任何书籍或者维基百科作为训练数据,可以自动生成标签。这样一来,训练数据要多少有多少,足以训练出一个非常大的模型。

2.2 Task 2: Predict the Next Sentence

? 第一句话是 “ calculus is a branch of math ” ,“微积分是数学的一个分支”。再告诉你第二句话是“ it was developed by newton and leibniz ”,“它是由牛顿和莱布尼兹发展起来的”。现在让你做一个判断,这两句话是否是原文中相邻的两句话?

? 很有可能是,因为微积分与牛顿、莱布尼兹的相关性非常大。神经网络可以从海量的训练数据中学出这种相关性。所以神经网络有能力做出正确的判断。

? 假如第一句话不变,还是 “ calculus is a branch of math ” ,第二句话换成 “ panda is native to south central china ” ,这两句话是否是原文中相邻的两句话呢?

? 很可能不是,第一句话讲的是微积分和数学,第二句话讲的是熊猫,这两句话之间没有任何关联。


? 可以这样准备训练数据,把两句话给拼接起来,两句话之间用 [SEP] 符号给分开,在最前面放一个[CLS] 符号占一个位置。[CLS] 符号的意思是 classification 分类。

? 生成训练数据的时候,有 50% 是原文里真实相邻的两句话,另外 50% 的第二句话是从训练数据中随机抽取的句子。

? “ calculus is a branch of math ” “ it was developed by newton and leibniz ” 这两句话是真实的原文,所以标签设置 为 True。第二句话也可以是随机选取的句子。这里的第二句话“ panda is native to south central china ” 是随机选取的,所以标签是False

在这里插入图片描述
? 一条训练数据包含两句话,两句话都被分割成很多个符号。在最前面放了一个 [CLS] 符号,占一个位置,这个位置上的输出叫做向量 c \mathbf{c} c。向量 c \mathbf{c} c[CLS] 的位置上,但是 c \mathbf{c} c 并不只依赖于 [CLS] 符号,向量 c \mathbf{c} c 包含两句话的全部信息,所以靠 c \mathbf{c} c 向量就能判断出两句话是否真实的相邻。

? 把 c \mathbf{c} c 作为特征向量,输入一个分类器,分类器的输出是介于 0 ~ 1 之间的值 f f f 1代表 True,分类器认为两句话是从原文里拿出来的,0代表 False, 分类器认为第二句话是假的,两句话不相关。可以用 CrossEntropy 作为损失函数来衡量预测 f f f 与真实标签之间的区别,有了损失函数就可以计算梯度,然后用梯度下降来更新模型参数。

在这里插入图片描述
? 这样做预训练有什么用呢? 相邻两句话通常有关联。这样做 二分类 可以强化这种关联,让 Embedding 词向量包含这种关联,比如微积分和牛顿的词向量就应该有某种关联。

? encoder 网络中有self-attention 层,self-attention 的作用就是找相关性。这种分类任务可以训练 self-attention 找到正确的相关性。

2.3 Combining the two methods

? 前面介绍了两个任务,一个是预测遮住的单词,另一个是判断两句话是否在原文里真实相邻。BERT 把两个任务结合起来,用来预训练 transformer。

? 把两句话给拼接起来,然后随机遮挡 15% 的单词,这条训练数据里碰巧有 2 个被遮挡的单词,一共有三个任务,第一个任务是判断两句话是否真的相邻?由于这两句话是从原文里取出来的,所以标签设置为True。另外两个任务是预测 2 个被遮住的单词,标签是真实的单词 branchwas

在这里插入图片描述
? 下面这条训练数据里碰巧只有 1个被遮挡的单词,所以一共有两个任务。由于第二句话是随机抽样得到的,所以是假的,标签设置为 False。被遮挡的单词是 south,所以标签是单词 south

在这里插入图片描述
? 假如有两个单词被遮挡,那么就有三个任务,就需要定义三个损失函数。第一个任务是 二分类,所以第一个损失函数就是 binary CrossEntropy。第二、三个任务是 预测单词,这是多分类任务,所以损失函数是 CrossEntropy。目标函数是 3 个损失函数的加和,把目标函数关于模型参数求梯度,然后做梯度下降来更新模型参数。

在这里插入图片描述
? BERT 的好处是不需要人工标注数据,人工标注数据非常贵,得花几十万甚至上百万才能标注一个比较大的数据集。BERT 两种任务的标签都是自动生成的,这是个非常好的性质,可以用书,用论文,用网页等等数据来做 预训练。反正标签都是自动生成的,无需人工标注,BERT 论文里用了英文维基百科,长度是二十五亿个单词。

? BERT 的想法简单,而且非常有效,但是计算代价超级大。论文里有两种模型,小模型有 1.1 亿个参数,其实它一点也不小。大模型更大,有 2.35 亿个参数,训练小模型用 16 块 TPU ,花了四天时间,这只是跑一遍的时间,还不算调参数,要是调参数的话,时间还要多很多倍。训练大模型代价要大 4 倍更夸张。不过训练出来的模型参数都是公开的。想要用 transformer 可以直接去下载 BERT 预训练的模型和参数就好了。


参考链接 - ShusenWang - Transformer 模型
参考链接 - ShusenWang’s github - DeepLearning - 课件

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-08-15 15:32:20  更:2021-08-15 15:35:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/12 0:59:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码