LLM - GQA 之 Group Query Attention 论文与源码精读

目录

Abstract

1.Introduction

2.Method

2.1 UpTraining

2.2 Grouped-query attention

3.Experiments

3.1 Experimental setup

3.2 Main Results

3.3 Ablations

4.Related Work

5.Conclustion

6.Code

6.1 repeat_kv

6.2 Attention Arg Init

6.3 Attention Mat Init

6.4 Attentin Cache Init

6.5 Forward

7.Review


Abstract

多查询注意(MQA)只使用单个键值头,可大大加快解码器的推理速度。然而,MQA 可能会导致质量下降,而且仅仅为了加快推理速度而训练一个单独的模型可能并不可取。

  • 提出了一种将现有的多头语言模型检查点向上训练成带有 MQA 的模型的方法,只需使用原始预训练计算量的 5%;

  • 引入分组查询注意(GQA),这是一种多查询注意的广义化,它使用中间(多于一个,少于查询头数)键值头的数量。

我们的研究表明,经过向上训练的 GQA 可以达到接近多查询头注意力的质量,而且速度与 MQA 相当。

1.Introduction

自回归解码器推理是 Transformer 模型的一个严重瓶颈,这是因为在每个解码步骤中加载解码器权重和所有注意键与值会造成内存带宽开销(Shazeer,2019;Pope 等人,2022;de Jong 等人,2022)。通过多查询注意(Shazeer,2019 年),加载键和值所产生的内存带宽可以大幅降低,多查询注意使用多个查询头,而键和值头则使用单个。

这项工作包含两项贡献,旨在加快大型语言模型的推理速度。首先,我们对南加州大学做出了同等贡献。谷歌研究中心的工作表明,使用多头注意力(MHA)的语言模型检查点可以通过向上训练(Komatsuzaki 等人,2022 年)来使用 MQA,只需原始训练计算量的一小部分。这为获得快速多查询和高质量 MHA 检查点提供了一种经济有效的方法。其次,我们提出了分组查询注意(GQA),它是多查询头注意和多查询注意之间的一个插值,每个查询头子组只有一个键和值头。我们的研究表明,经过向上训练的 GQA 可以达到接近多头注意力的质量,同时速度几乎与多查询注意力一样快。

2.Method

2.1 UpTraining

从多头模型生成多查询模型分为两个步骤:首先是转换检查点,其次是额外的预训练,以使模型适应新的结构。图 1 显示了将多头检查点转换为多查询检查点的过程。键头和值头的投影矩阵平均汇集成单一投影矩阵,我们发现这比选择单一键头和值头或从头开始随机初始化新的键头和值头效果更好。

这里相当于把 Look Up 得到的 Keys 向量进行 Mean Pooling 转换为 1 个 Keys 即单头。然后,在相同的预训练配方上,对转换后的检查点进行原始训练步骤的一小部分 α 的预训练。

2.2 Grouped-query attention

分组查询注意将查询头分为 G 组,每组共享一个键头和值头。GQA-G 指的是有 G 个组的分组查询。GQA-1 只有一个组,因此也只有一个键头和值头,相当于 MQA,而 GQA-H 的组数等于查询头数,相当于 MHA。图 2 显示了分组查询注意和多头/多查询注意的比较。在将多头检查点转换为 GQA 检查点时,我们通过平均池化该组中的所有原始头来构建每个组的键和值头。

中间组的数量会导致插值模型的质量比 MQA 高,但速度比 MHA 快,正如我们将展示的那样,这代表了一种有利的权衡。从 MHA 到 MQA,可以将 H 个键和值头减少到单个键和值头,从而减少键值缓存的大小,并因此将需要加载的数据量减少 H 倍。GQA 可以让我们在模型规模增大时,保持相同的带宽和容量下降比例。此外,由于 KV 缓存会随着模型维度的增加而增加,而模型 FLOP 和参数则会随着模型维度的平方而增加,因此大型模型因注意力而产生的内存带宽开销相对较小。最后,大型模型的标准分片是按模型分区的数量复制单键和值头(Pope 等,2022 年);GQA 消除了这种分区带来的浪费。因此,我们希望 GQA 能为大型模型提供特别好的权衡。我们注意到,GQA 并未应用于编码器自注意层;编码器表示是并行计算的,因此内存带宽通常不是主要瓶颈。

3.Experiments

3.1 Experimental setup

Configurations

所有模型都基于 T5.1.1 架构(Raffel 等人,2020 年),使用 JAX(Bradbury 等人,2018 年)、Flax(Heek 等人,2020 年)和 Flaxformer1 实现。在我们的主要实验中,我们考虑了具有多头注意力的 T5 Large 和 XXL,以及具有多查询和分组查询注意力的 T5 XXL 的升级训练版本。我们使用 Adafactor 优化器,其超参数和学习率安排与 T5 相同(Raffel 等人,2020 年)。我们将 MQA 和 GQA 应用于解码器自注意和交叉注意,但不包括编码器自注意。

Uptraining

预训练。预训练模型从公开的 T5.1.1 检查点初始化。键头和值头被平均池化到适当的 MQA 或 GQA 结构中,然后使用原始预训练设置和数据集(Raffel et al.) 在 α = 0.05 的情况下,训练耗时约 600 TPUv3 芯片 / 日。

Data

我们在摘要数据集 CNN/每日邮报(Nallapati 等人,2016 年)、arXiv 和 PubMed(Cohan 等人,2018 年)、MediaSum(Zhu 等人,2021 年)和 Multi-News(Fabbri 等人,2019 年);翻译数据集 WMT 2014 English-to-German;以及问题解答数据集 TriviaQA(Joshi 等人,2017 年)上进行了评估。我们没有对 GLUE(Wang 等人,2019 年)等流行的分类基准进行评估,因为自回归推理不太适用于这些任务。

Fine-tuning

为了进行微调,我们在所有任务中使用 0.001 的恒定学习率、128 的批量大小和 0.1 的 Dropout Ratio。CNN/Daily Mail 和 WMT 的输入长度为 512,输出长度为 256。其他摘要数据集的输入长度为 2048,输出长度为 512。最后,TriviaQA 使用输入长度 2048 和输出长度 32。我们训练直到收敛,然后选择 dev 性能最高的检查点。我们使用贪婪解码进行推理。

Timing

我们报告了 xprof(谷歌,2020 年)测量的每个 TPUv4 芯片每个样本的时间。在时序实验中,我们使用 8 个 TPU,每个 TPU 最多可容纳 32 个批次,并对每个模型分别进行并行化优化。

3.2 Main Results

图 3 显示了 MHA T5-Large 和 T5-XXL,以及上训练比例为 α = 0.05 的 MQA 和 GQA-8 XXL 模型在所有数据集上的平均性能与平均推理时间的函数关系。我们发现,相对于 MHA 模型,较大的上训练 MQA 模型提供了有利的权衡,与 MHA-Large 模型相比,其推理质量更高、速度更快。此外,GQA 还能显著提高质量,性能接近 MHA-XXL,速度接近 MQA。表 1 包含了所有数据集的全部结果。

与 MHA-Large 具有更高的质量和更快的速度的 MHA 相比,Uptrained MQA 产生了有利的权衡,GQA 实现了更好的性能,速度增益相似,质量与 MHA-XXL 相当。所有任务的平均性能作为 T5-Large 和 T5-XXL 每个样本的平均推理时间的函数,多头注意力为 5% 的预训练 T5-XXL,MQA 和 GQA-8 注意力。

3.3 Ablations

本节通过实验来研究不同建模选择的效果。我们对具有代表性的任务子样本进行了性能评估: CNN/每日邮报(短篇摘要)、MultiNews(长篇摘要)和 TriviaQA(问题解答)。

Checkpoint conversion

图 4 比较了不同方法在检查点转换方面的性能。平均池化似乎效果最好,然后选择一个头部,然后随机初始化。直观地说,结果按从预训练模型中保留信息的程度排序。

T5-Large 的不同检查点转换方法的性能比较,比例为 α = 0.05。 “平均”均值池键和值头,“First”选择第一个头,“Random”从头开始初始化头。

Uptraining steps

图 5 显示了使用 MQA 和 GQA 的 T5 XXL 的性能随向上训练比例的变化情况。首先,我们注意到 GQA 在转换后已经达到了合理的性能,而 MQA 则需要向上训练才能发挥作用。MQA 和 GQA 都能从 5% 的上行训练中获益,而从 10% 的上行训练中收益递减。

Number of groups

图 6 展示了 GQA 组的数量对推理速度的影响。对于较大的模型,来自 KV 缓存的内存带宽开销限制较小(Shazeer,2019 年),而键值大小的减少则由于头部数量的增加而更加明显。因此,从 MQA 开始增加组数最初只会导致适度的减速,随着我们更接近 MHA,成本会越来越高。我们选择了 8 组作为有利的中间点。

4.Related Work

这项工作的重点是通过减少加载键和值带来的内存带宽开销(Williams 等人,2009 年),在解码器质量和推理时间之间实现更好的权衡。Shazeer (2019) 首次提出通过多查询关注来减少这种开销。后续工作表明,多查询关注对长输入特别有帮助(Pope 等人,2022 年;de Jong 等人,2022 年)。拉贝(2023 年)独立开发了 GQA,并公开实施。其他研究还探讨了如何分组注意力头以提高计算效率(Park 等人,2020 年;Luo 等人,2022 年;Ni 等人,2023 年),但没有特别关注键值头,因为键值头决定了内存带宽开销。

已经提出了许多其他方法来减少键和值的内存带宽开销,以及参数。Flash attention (Dao et al., 2022) 构建了注意力计算,以避免具体化二次注意力分数,减少内存和加速训练。量化(Dettmers 等人,2022;Frantar 等人,2022)通过降低精度来降低权重和激活的大小,包括键和值。模型蒸馏 (Hinton et al., 2015; Gou et al., 2021) 相反,使用从较大模型生成的数据来微调较小的模型,以给定精度降低模型大小。层稀疏交叉注意(de Jong et al., 2022)消除了大多数交叉注意层,这对较长的输入构成了主要费用。推测采样 (Chen et al., 2023; Levianathan et al., 2022) 通过提出多个具有较小模型的令牌来改进内存带宽瓶颈,然后由更大的模型并行评分。

最后,我们提出的上采样过程受到 Komatszaki 等人的启发。 (2022),它将标准 T5 检查点升级为稀疏激活的专家混合模型。

5.Conclustion

语言模型的推理成本很高,主要是由于加载键和值的内存带宽开销。多查询注意力以降低模型容量和质量为代价减少了这种开销。我们建议将多头注意力模型转换为具有少量原始预训练计算的多查询模型。此外,我们引入了分组查询注意力,这是一种多查询和多头注意力的插值,它以与多查询注意力相当的速度实现了接近多头的质量。

6.Code

6.1 repeat_kv

def repeat_kv(x: torch.Tensor, n_rep: int) -> torch.Tensor:"""torch.repeat_interleave(x, dim=2, repeats=n_rep)"""bs, slen, n_kv_heads, head_dim = x.shapeif n_rep == 1: # MHAreturn xreturn ( # MQA / GQAx[:, :, :, None, :].expand(bs, slen, n_kv_heads, n_rep, head_dim).reshape(bs, slen, n_kv_heads * n_rep, head_dim))

目的:这个函数用于在 n_kv_heads 小于 n_heads 时重复键/值头。n_rep 代表重复的次数,如果 n_rep 为 1,则直接返回 x,否则,沿着新维度扩展张量并重新调整形状以重复头。n_rep = 3 效果如下:

输入张量形状 => 输出张量形状 Bsz x SeqLen x KV_heads x HeadDim
torch.Size([2, 3, 2, 4]) => torch.Size([2, 3, 6, 4])

6.2 Attention Arg Init

class Attention(nn.Module):def __init__(self, args: ModelArgs):super().__init__()self.n_kv_heads = args.n_heads if args.n_kv_heads is None else args.n_kv_headsmodel_parallel_size = fs_init.get_model_parallel_world_size()self.n_local_heads = args.n_heads // model_parallel_sizeself.n_local_kv_heads = self.n_kv_heads // model_parallel_size # 此处 self.n_rep = self.n_local_heads // self.n_local_kv_heads # 此处 几个组self.head_dim = args.dim // args.n_heads
  • n_kv_heads: 如果 n_kv_heads 为 None 则退化为 MHA,否则根据 n_kv_heads 参数定义

  • model_parallel_size: 指定模型并行的规模,即模型被分割为多少个部分。local_heads 和 local_kv_heads 这些参数会受到该参数的影响,因为它们决定了每个设备上实际计算的头数。

  • n_rep: 代表 group 的数量,因为每一个分组共用向量,所以 repeak_kv 会就形状恢复

  • head_dim: 通过 embedding_dim 和 heads 计算

6.3 Attention Mat Init

        self.wq = ColumnParallelLinear(args.dim,args.n_heads * self.head_dim,bias=False,gather_output=False,init_method=lambda x: x,)self.wk = ColumnParallelLinear(args.dim,self.n_kv_heads * self.head_dim, # 初始化为单个组内的一份bias=False,gather_output=False,init_method=lambda x: x,)self.wv = ColumnParallelLinear(args.dim,self.n_kv_heads * self.head_dim, # 初始化为单个组内的一份bias=False,gather_output=False,init_method=lambda x: x,)self.wo = RowParallelLinear(args.n_heads * self.head_dim,args.dim,bias=False,input_is_parallel=True,init_method=lambda x: x,)

传统的模型 QKV 是同 Size 的,但是由于引入了 Group 的概念,Q 尺寸不变为 n_heads * head_dim,但是 KV 变成 n_kv_heads * head_dim,后续通过 ✖️ n_rep 即 Group 数复制为与 Q 同尺寸,从而继续执行传统的 QKV 计算,因此 GQA 在 Attention 的计算方式上还是一样的,区别是减少了读取 KV 的 IO,同时减少了 KV-Cahce 的换存量。

6.4 Attentin Cache Init

        self.cache_k = torch.zeros((args.max_batch_size,args.max_seq_len,self.n_local_kv_heads,self.head_dim,)).cuda()self.cache_v = torch.zeros((args.max_batch_size,args.max_seq_len,self.n_local_kv_heads,self.head_dim,)).cuda()

这里是为 KV Cache 预留的空间,BSZ x SeqLen x KV_HEADS x HeadDim 也是我们计算 KV_Cache 缓存量的方法。

6.5 Forward

    def forward(self,x: torch.Tensor,start_pos: int,freqs_cis: torch.Tensor,mask: Optional[torch.Tensor],):bsz, seqlen, _ = x.shapexq, xk, xv = self.wq(x), self.wk(x), self.wv(x)xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)xk = xk.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim)xv = xv.view(bsz, seqlen, self.n_local_kv_heads, self.head_dim)xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis)self.cache_k = self.cache_k.to(xq)self.cache_v = self.cache_v.to(xq)self.cache_k[:bsz, start_pos : start_pos + seqlen] = xkself.cache_v[:bsz, start_pos : start_pos + seqlen] = xvkeys = self.cache_k[:bsz, : start_pos + seqlen]values = self.cache_v[:bsz, : start_pos + seqlen]# repeat k/v heads if n_kv_heads < n_heads # 单个组扩展为完整headkeys = repeat_kv(keys, self.n_rep)  # (bs, seqlen, n_local_heads, head_dim)values = repeat_kv(values, self.n_rep)  # (bs, seqlen, n_local_heads, head_dim)xq = xq.transpose(1, 2)  # (bs, n_local_heads, seqlen, head_dim)keys = keys.transpose(1, 2)values = values.transpose(1, 2)scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim)if mask is not None:scores = scores + mask  # (bs, n_local_heads, seqlen, cache_len + seqlen)scores = F.softmax(scores.float(), dim=-1).type_as(xq)output = torch.matmul(scores, values)  # (bs, n_local_heads, seqlen, head_dim)output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1)return self.wo(output)

下面拆解下前向传播的过程:

  • 通过 QKV 矩阵获取对应的矩阵参数

通过 wq、wk、wv 获取 QKV 并通过 view 转换为 mulit_head 的形式,最后对 qk 应用 ROPE 旋转位置编码,引入 token 位置信息:

  • 更新 KV-Cache

首先通过 self.cache 获取原始的 cache,再将本次 lookup 得到的心得 k/v 添加值 cache 中用于下一次 token 生成。

  • Repeat KV

按照 Grouped-query 的图示维度是无法直接矩阵乘法的,因为维度不匹配,所以需要把对应的 keys、values 进行 repeak 操作,这也是最上面介绍的 repeak_kv 的作用。

  • QKV Attention

基础的 Attention 计算操作,包括 QK Matmal 计算、Scale 进行规划化、Softmax 归一化以及最终归一化权重与 Values 加权求和得到最终的输出,维度为 BSZ x SeqLen x EmbDim

7.Review

前两天分享了 Gemma-2 的技术报告,其中有同学问到了关于 QKV 维度的问题:

下面我们结合 Gemma-1、Gemma-2 的模型结构再细化一下 GQA 的概念:

从图中可以看到 Gemma-1 的 QKV 都是 [3072x4096] 的,因此通过 wq wk wv 转换后,其可以直接 matmul,因为尺寸是一致的,但是 Gemma-2 的 q 是 [3584x4096],而 k,v 是 [3584 x 2048] 的,这是因为 Num heads = 16,而 Num KV heads = 8,根据源码:

        self.n_rep = self.n_local_heads // self.n_local_kv_heads # 此处 几个组

我们可知 Gemma-2 的 n_rep = 16 / 8 = 2,即 Gemma-2 GQA 选取的 Group 数为 2,与 Gemma-2 报告中的匹配,结合报告中的实验可知,Gemma-2 在这里选择了极致的效率,即最小的 Group 换取最快的性能:

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IT服务质量管理攻略(至简)

质量管理、风险管理和信息安全管理是IT服务监督管理的重要内容&#xff0c;三者之间相对独立。IT服务质量管理是通过制订质量方针、质量目标和质量计划&#xff0c;实施质量控制、质量保证和质量改进活动&#xff0c;确保IT服务满足服务级别协议的要求&#xff0c;最终获得用户…

openvidu私有化部署

openvidu私有化部署 简介 OpenVidu 是一个允许您实施实时应用程序的平台。您可以从头开始构建全新的 OpenVidu 应用程序&#xff0c;但将 OpenVidu 集成到您现有的应用程序中也非常容易。 OpenVidu 基于 WebRTC 技术&#xff0c;允许开发您可以想象的任何类型的用例&#xf…

[算法]第一集 递归(未完待续)

递归啊递归&#xff0c;说简单简单&#xff0c;说难难。 首先我们要知道 一、什么是递归&#xff1f; 我们再C语言和数据结构里都用了不少递归&#xff0c;这里就不多详细介绍。 递归简单来说就是函数自己调用自己的情况 二、为什么要用递归呢&#xff1f; 本质来说其实就…

【WRF安装第四期(Ubuntu)】搭建WRF编译所需系统-WRF和WPS模型的安装

WRF安装第四期&#xff1a;搭建WRF编译所需系统-WRF和WPS模型的安装 1 WRF的编译安装&#xff08;Building WRF&#xff09;1.1 进入Build_WRF文件夹1.2 下载WRFV4.01.3 解压WRF安装包1.4 安装WRF选择#1&#xff1a;32选择#2&#xff1a;33选择#3&#xff1a;34 1.5 检查WRF是否…

Linux 中的信号处理

Linux 中的信号处理是操作系统中非常重要的一个概念&#xff0c;通过信号处理&#xff0c;进程之间可以进行通信、协调以及实现一些重要的功能。本文将从信号的概念、类型、生成、传递、处理、以及常见的信号处理函数等方面展开讨论&#xff0c;以帮助读者更深入地了解 Linux 中…

【机器学习】BP神经网络中的链式法则

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 BP神经网络中的链式法则1. 引言2. 链式法则基础2.1 什么是链式法则&#xff1f;…

29.Labview界面设计(下篇) --- 自定义控件库、界面布局与外观设计

摘要&#xff1a; 题主在上一篇文章中向大家讲解了前面板逻辑框架及结构的搭建和控件的类型介绍&#xff0c;那么本章主要围绕前面板的控件布局以及控件的自定义类型和背景等外观优化项中来讲解。 本篇文章讲解界面设计的下篇内容&#xff0c;上篇内容链接大家可以直接点击链接…

国家统计局中国主要城市面板数据(1990-2023年)

数据说明&#xff1a;数据来源于国家统计局&#xff0c;指标包含&#xff1a;城市、年份、第三产业增加值、第一产业增加值 地区生产总值、第二产业增加值、年末户籍人口、城镇非私营单位在岗职工平均工资 房地产开发投资额、房地产开发住宅投资额、房地产开发办公楼投资额、房…

Linux C 程序 【03】线程栈空间

1.开发背景 上一个篇章创建了线程&#xff0c;参考 FreeRTOS&#xff0c;每个线程都是有自己的内存空间&#xff0c;Linux上面也是一样的&#xff0c;这个篇章主要描述线程栈空间的设置。 2.开发需求 设计实验&#xff1a; 1&#xff09;创建线程&#xff0c;并配置线程内存大…

培训第二十二天(mysql数据库主从搭建)

上午 1、为mysql添加开机启动chkconfig [rootmysql1 ~]# chkconfig --list //列出系统服务在不同运行级别下的启动状态注&#xff1a;该输出结果只显示 SysV 服务&#xff0c;并不包含原生 systemd 服务。SysV 配置数据可能被原生 systemd 配置覆盖。 要列出 systemd 服务…

IEEE报告解读:存储技术发展趋势分析

1.引言 随着数据科学、物联网&#xff08;IoT&#xff09;和永久存储需求的快速增长&#xff0c;对大规模数据存储的需求正在迅速增加。存储技术的发展趋势直接关系到数据的可靠性和经济性。本文将根据IEEE最新发布的《2023年国际器件与系统路线图》&#xff0c;深入探讨各种存…

AnyGPT: Unified Multimodal LLM with Discrete Sequence Modeling

发表时间&#xff1a;arXiv 2024年2月26日 论文链接&#xff1a;https://arxiv.org/pdf/2402.12226 作者单位&#xff1a; Fudan University Motivation&#xff1a; LLM 在理解和生成人类语言方面表现出非凡的能力。但是&#xff0c;LLM 的能力仅限于针对文本的处理。而现…

详解Xilinx FPGA高速串行收发器GTX/GTP(2)--什么是GTX?

文章总目录点这里:《FPGA接口与协议》专栏的说明与导航 GTX本质上是基于SerDes技术的高速串行收发器,它是FPGA内部的底层电路,也叫做Gigabit Transceiver(吉比特收发器,简称为GT)。其中A7系列使用的GT叫GTP,K7系列使用的GT叫GTX,V7系列使用的GT叫GTH和GTZ,它们…

循环神经网络和自然语言处理一

目录 一.分词 1.分词工具 2.分词的方法 3.N-gram表示方法 二.向量化 1.one-hot编码 2.word embedding 3.word embedding API 4.数据形状改变 既然是自然语言&#xff0c;那么就有字&#xff0c;词&#xff0c;句了 一.分词 1.分词工具 tokenization&#xff0c;jie…

【数据结构】二叉搜索树(Java + 链表实现)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构、LeetCode专栏 &#x1f4da;本系…

【DOCKER】显示带UI的软件

1. Linux 1.1 宿主机开放X server权限 xhost 1.2 启动容器 docker run -it --rm --privilegedtrue --useru20 --workdir/home/u20 \ -e DISPLAYhost.docker.internal:0 u20:dev1.3 测试 # 安装测试软件 sudo apt-get -y install x11-apps# 显示测试程序 xclock2. Windows …

LearnOpenGL-光照章节学习笔记

LearnOpenGL-光照章节学习笔记 颜色创建一个光照场景 基础光照一、环境光照二、漫反射光照三、镜面反射 材质光照贴图一、漫反射贴图二、镜面光贴图三、放射光贴图 投光物一、平行光二、点光源衰减实现 三、聚光灯平滑边缘 多光源一、平行光&#xff08;定向光&#xff09;二、…

免费代理池是什么,如何使用代理IP进行网络爬虫?

互联网是一个庞大的数据集合体&#xff0c;网络信息资源丰富且繁杂&#xff0c;想要从中找到自己需要的信息要花费较多的时间。为了解决这个问题&#xff0c;网络爬虫技术应运而生&#xff0c;它的主要作用就是在海量的互联网信息中进行爬取&#xff0c;抓取有效信息并存储。然…

广州城市信息模型(CIM)白皮书学习

CIM平台定义 以建筑信息模型(BIM)、地理信息系统(GIS)、物联网(IoT)等技术为基础&#xff0c;整合城市地上地下、室内室外、历史现状未来多维多尺度信息模型数据和城市感知数据&#xff0c;构建起三维数字空间的城市信息有机综合体。 广州CIM平台建设历程 2019 年 6 月住房和…

动手学深度学习V2每日笔记(深度卷积神经网络AlexNet)

本文主要参考沐神的视频教程 https://www.bilibili.com/video/BV1h54y1L7oe/spm_id_from333.788.recommend_more_video.0&vd_sourcec7bfc6ce0ea0cbe43aa288ba2713e56d 文档教程 https://zh-v2.d2l.ai/ 本文的主要内容对沐神提供的代码中个人不太理解的内容进行笔记记录&…