我的机器是 nvidia T4 GPU + ubuntu22.04 我先通过下面的命令安装驱动 sudo apt install -y nvidia-driver-535-server 等电脑重启好了,输入 "nvidia-smi" 查看显卡信息 ╰─➤ nvidia-smi 130 ↵ Mon Sep 18 14:30:16 2023 +---------------------------------------------------------------------------------------+ | NVIDIA-SMI 535.54.03 Driver Version: 535.54.03 CUDA Version: 12.2 | |-----------------------------------------+----------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+======================+======================| | 0 Tesla T4 Off | 00000000:AF:00.0 Off | 0 | | N/A 47C P0 27W / 70W | 2MiB / 15360MiB | 6% Default | | | | N/A | +-----------------------------------------+----------------------+----------------------+ 然后在 "https://developer.nvidia.com/cuda-downloads?target_os=Linux&t..." (https://link.segmentfault.com/?enc=3lX4HpTDwbVmYUepMwiEig%3D%3D.hVEvpxzxrZ5nbln27HtjYX%2FZgdAL9yh2fVEKf%2BzBsut2FoxAl2GIprcrELn%2BXB3k4tuTTUNgH4yajNzLt5aX7MPvOenGOadnQHI0WBRAFLmmG6vebB5O0RH%2BJQ6JGCmbZ8nOl2AYWSzYeYL1qUJqUZfS29AFy55ZR5t1WOE1DY7jLNViJlaGZUKzxbx8L4omQCX1vFuTS5EcMlgK8i5cwQ%3D%3D) 下面 CUDA Toolkit 12.2 "图片.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241227/df6ec5e1bab5f00a3335f3e1a32d40fb.png) ╭─pon@T4GPU ~/Downloads ╰─➤ sudo sh cuda_12.2.2_535.104.05_linux.run [sudo] password for pon: 安装之后,还是没有 nvcc ╭─pon@T4GPU ~/Downloads ╰─➤ nvcc --version 127 ↵ zsh: command not found: nvcc ╭─pon@T4GPU ~/Downloads ╰─➤ cd / 127 ↵ ╭─pon@T4GPU / ╰─➤ fd -a -u nvcc /usr/share/cmake-3.22/Modules/FindCUDA/run_nvcc.cmake 我的期望是,安装这个 CUDA Toolkit 之后,就有 nvcc 命令
一、说明 神经网络是受人脑工作启发的计算模型,能够从复杂的非结构化数据(如图像、文本、音频和视频)中学习。然而,还有许多其他类型的数据无法用传统的神经网络轻松表示,例如那些具有图形结构的数据。图形是节点和边的集合,分别表示系统的实体和关系。图表无处不在:从社交网络、推荐系统、计算化学、分子生物学、网络安全等等。我们如何利用神经网络的力量来分析和学习这些图形数据? 答案是:使用图神经网络(GNN)。GNN是一类直接在图上运行的神经网络,利用节点和边的结构和语义信息。GNN 能够学习节点的向量表示(或嵌入),从而在图中捕获它们的特征和上下文。然后,这种表示可用于各种任务,例如节点分类、链接预测、图形生成、空间和时间推理等等。 GNN是一个非常活跃且发展迅速的研究领域,面临着许多挑战和机遇。在本文中,我想对 GNN 进行概述,说明它们的工作原理、应用、它们与传统神经网络的区别以及它们的关键概念和术语。特别是,我将重点介绍四种类型的 GNN:图卷积网络 (GCN)、图注意力网络 (GAT)、时态图网络 (TGN) 和内存增强图神经网络 (MA-GNN)。之所以选择这些类型的GNN,是因为它们代表了GNN领域中一些最具创新性和影响力的想法,并且因为它们涵盖了广泛的场景和应用。二、图卷积网络 (GCN) 卷积是将输入信号转换为输出信号的数学运算,同时保留原始信号的某些属性,例如局部性、平稳性和组合性。卷积在神经网络中广泛用于分析图像、文本、音频和视频,这些图像、文本、音频和视频可以看作是规则网格上的定义信号。然而,图形没有规则的结构,而是不规则和可变的。我们如何将卷积应用于图形? 图卷积网络 (GCN) 是一类用于图的神经网络,它使用卷积来学习图节点的向量表示。GCNs的基本思想是在图上定义一个卷积算子,它允许节点及其邻居的信息被高效且不变地聚合。有几种方法可以在图形上定义卷积算子,具体取决于如何对节点之间的关系进行建模、如何对邻居进行加权以及如何组合信息。图上卷积算子的一些示例包括:频谱、空间域、时域和频域。 GCN 能够学习节点的向量表示(或嵌入),从而在图中捕获它们的特征和上下文。然后,这种表示可用于各种任务,例如节点分类、链接预测、图形生成、空间和时间推理等等。基于 GCN 的算法的一些示例包括:GraphSage、PinSage、Graph Isomorphism Network、Graph U-Net 等。 你是对的,我忘了在我的文章中描述 PinSage。PinSage 是一种图神经网络 (GNN) 算法,它使用卷积来学习图节点的向量表示。PinSage 的独特之处在于它使用随机游走对节点的邻居进行采样,而不是使用均匀或加权采样。随机游走是一个随机过程,包括以随机方式沿着图形的边缘从一个节点移动到另一个节点。随机游走允许您探索图形中更大、更多样化的部分,并为与目标节点最相关的邻居分配更高的重要性。PinSage 旨在处理大型图表,例如 Pinterest 的图表,并为用户生成个性化推荐。三、图注意力网络 (GAT) 注意力是一种机制,允许您将注意力集中在输入信号的一部分上,而忽略其他不相关的部分。注意力在神经网络中被广泛用于分析文本、音频和视频,可以看作是元素的序列。然而,图不是序列,而是不规则和可变的结构。我们怎样才能把注意力放在图表上? 图注意力网络 (GAT) 是一类图神经网络,它使用注意力来学习图节点的向量表示。GAT 的基本思想是在图形上定义注意力机制,它允许您根据节点与目标节点的相关性为节点的邻居分配权重。通过这种方式,GAT能够有选择地和自适应地聚合节点及其邻居的信息,同时考虑到图的结构和内容。 GAT 使用空间方法聚合邻居信息,其中包括计算目标节点与其邻居之间的相似性函数,然后使用 softmax 函数对权重进行归一化。GAT 可以使用不同类型的相似函数,例如标量积、加性标量积或乘法标量积。GAT 还可以使用多个注意力头来学习节点的不同表示,然后将它们连接或调解以获得最终表示。 GAT 能够学习节点的向量表示(或嵌入),从而在图中捕获它们的特征和上下文。然后,这种表示可用于各种任务,例如节点分类、链接预测、图形生成、空间和时间推理等等。基于 GAT 的算法的一些示例包括:Graph Transformer、Graph Attention U-Net、Graph Attention Autoencoder 等。四、时态图网络 (TGN) 时态图是一种随时间变化的图,其结构和内容都发生了变化。时间图可以表示动态现象,例如社会互动、金融交易、化学反应、生物过程等等。我们如何从时间图中学习? 时态图网络 (TGN) 是一类神经图网络,它使用时间作为学习时态图节点的向量表示的基本维度。TGN 的基本思想是对节点和链接随时间变化的动态进行建模,同时考虑它们的属性和变化。TGN 能够将内存和时间嵌入到 GNN 中,以捕获时间依赖性以及节点和链路的演变。 TGN 使用编码模块根据节点的静态和动态特性来学习节点的向量表示。TGN 使用聚合模块根据节点与邻居随时间推移的交互来更新节点的表示。TGN 使用内存模块根据节点的时间相关性来存储和检索节点的表示。TGN 使用解码模块根据节点的表示和当前时间生成预测。 TGN 能够学习节点的向量表示(或嵌入),从而在时间图中捕获它们的特征和上下文。然后,这种表示可用于各种任务,例如节点分类、链接预测、图形生成、空间和时间推理等等。基于 TGN 的算法的一些示例包括:DyRep、JODIE、Know-Evolve 等。五、结论 在本文中,我概述了图神经网络 (GNN),这是一类直接在图上运行的神经网络,利用节点和边的结构和语义信息。我阐述了GNN的工作原理、应用、与传统神经网络的区别,以及关键概念和术语。特别是,我关注了四种类型的GNN:图卷积网络(GCN)、图注意力网络(GAT)、时态图网络(TGN)和内存增强图神经网络(MA-GNN)。 GNN是一个非常活跃且发展迅速的研究领域,面临着许多挑战和机遇。GNN 能够学习节点的向量表示,从而在图形中捕获它们的特征和上下文。然后,这种表示可用于各种任务,例如节点分类、链接预测、图形生成、空间和时间推理等等。GNN 已被证明在许多领域和场景中是有效和高效的,例如社交网络、推荐系统、计算化学、分子生物学、网络安全等。
前几天写了一个卷积神经网络(CNN)中,卷积和加法融合的文章。有同学发私信,希望写一个带代码版本的,方便更好的理解。我的第一反应是,代码版本的咋写,有那么多细节。后来一想,其实那位同学想知道的并不是那些细节,而是一个大致的流程。于是我说,行,那我写一个伪代码版的吧,把大致的代码思路写一下。至于具体卷积算法怎么实现的,建议chatGPT一下,或者看下开源深度学习仓库就行。如果没看之前的文章,可以看上一看:超简单的卷积和加法融合。开始分析还是以 resnet50 中的图为例,做一个卷积和加法的融合。正常情况下,上述网络片段在执行的时候大概是这样的:BatchNorm -> Relu -> Conv -| Add的左分支 | -> Add -> Conv -| Add的右分支 写出伪代码,实际上就是一种顺序调用逻辑,比如bn_out = Batch_normal(); relu_out = Relu(bn_out); conv_out_left = Conv2d(relu_out) conv_out_right = Conv2d(...) add_out = Add(conv_out_left, conv_out_right) 而一旦融合完之后,上图红框中的Conv 和 Add 就变成了一个算子,这里暂且称这个融合之后的算子为 ConvAdd 算子。于是,上述的图,就变成了如下的图:此时,整个网络片段的调用逻辑变成了:bn_out = Batch_normal(); relu_out = Relu(bn_out); conv_out_right = Conv2d(...) add_out = ConvAdd(relu_out, conv_out_right) 再把 ConvAdd 当做一个算子之后,便可以进行很多融合、拆图、流水并行操作。假设现在这个网络运行在一个Asic芯片上,芯片上卷积计算模块和加法计算模块是互相独立的,没有任何依赖。这里假设卷积输入的 Feature Map 的大小是 [n, hi, wi, ci],卷积核是[co, kh, kw, ci]。其余参数简化一下,将卷积 pad 简化为0,stride 简化为1,dilation简化为1。卷积的输出为[n, ho, wo, co]。那么卷积后面的加法,执行的两个tensor相加,也就变成了 [n, ho, wo, co] + [n, ho, wo, co] = [n, ho, wo, co]。那么,我们将卷积的输入(假设是下面的一张图),在H方向切成两份。那么计算完一整张图,需要调用两次卷积运算,第一次计算上半部分,第二次计算下半部分。两次计算中,大部分像素之间是没有关系的,仅仅在两部分交界的地方会有可能存在依赖。(存在依赖的条件为 kernel 大于1,或者 stride 大于1,这些情况先不考虑,暂时认为两部分像素没有关系)。那么第一次卷积计算,计算的输入是 [n, hi/2, wi, ci],计算输出结果是 [n, ho/2, wo, co]。此时计算的是前半部分的 hi。用红色表示。那么第二次卷积计算,计算的输入是 [n, hi/2, wi, ci],计算输出结果是 [n, ho/2, wo, co]。此时计算的是后半部分的 hi。用蓝色表示。同理,加法也会被分成两次计算,分别对应计算卷积的两次输出:第一次加法,计算的是第一次卷积的输出,即 [n, ho/2, wo, co]第一次加法,计算的是第一次卷积的输出,即 [n, ho/2, wo, co]那么,在两次计算的情况下,ConvAdd 这一个算子中,内部的实现逻辑大致应该是:conv_out_part1 = Conv2d(part1) conv_out_part2 = Conv2d(part2) add_out_part1 = Add(conv_out_part1) add_out_part2 = Add(conv_out_part2) 但是这样显然是不行的,因为这样写还是串行执行:执行完第一次卷积执行第二次卷积,执行完第二次卷积执行第一次加法...那怎么让 Conv 和 Add 并行起来呢?通过观察可以发现,第一次的Add并不依赖第二次的Conv,并且我们已经假设了Asic芯片上Conv运算模块和Add模块完全独立。那么让第二次Conv和第一次Add并行起来的方法就是:第一次Conv计算完之后,直接计算第一次Add,然后同时并行第二次Conv,这个时候,代码的实现大致是这样:conv1 = Conv2d(part1) ----------------------- add1 = Add(conv1) conv2 = Conv2d(part2) ----------------------- add2 = Add(conv2) 这个时候,Add 和 conv 在中间的一个流水级中并行起来了。所谓的一个流水级,指的是上面代码段中两个“ ------ ” 之间的代码,称之为在一个流水级中。那如果将图片拆成更多份,那可以并行的流水级就会更多。比如拆成4份,那可以有3个流水级中的Conv和Add并行起来。conv1 = Conv2d(part1) ----------------------- add1 = Add(conv1) conv2 = Conv2d(part2) ----------------------- add2 = Add(conv2) conv3 = Conv2d(part3) ----------------------- add3 = Add(conv3) conv4 = Conv2d(part4) ----------------------- add4 = Add(conv4) ----------------------- 需要说明一点的是,上面伪代码中,每一个 “-----” 其实都代表了一个同步点。在实际部署到硬件上运行时,需要在这些同步点上设置同步操作,用来使上一个流水级中的所有计算操作全部完成即可。常用的同步操作有一些同步指令或者barrier指令。假设我们使用barrier指令来进行同步,那么上述完整的伪代码便是:conv1 = Conv2d(part1) barrier() add1 = Add(conv1) conv2 = Conv2d(part2) barrier() add2 = Add(conv2) conv3 = Conv2d(part3) barrier() add3 = Add(conv3) conv4 = Conv2d(part4) barrier() add4 = Add(conv4) barrier() 当然上述代码看起来太长了,可以写成循环的形式,还是以将H方向拆分 4 份为例:conv1 = Conv2d(part1); barrier(); for i in range(1, 4): add_i = Add(convi) Conv_i+1 = Conv2d(part_i+1) barrier() add4 = Add(conv4) barrier() 伪代码的逻辑还是很简单的,关键是需要理解Conv和Add并行流水的思想。这种方法可以用到的融合场景很多,并不仅仅局限于Conv和Add这两个算子,也不局限于某一个神经网络。只要是在硬件上计算单元可以并行执行,并且在神经网络结构图上前后有依赖的层,几乎都可以这么进行融合来提升整体性能。
一、说明在各种类型的GNN中,图卷积网络(GCN)已成为最普遍和应用最广泛的模型。GCN具有创新性,因为它们能够利用节点的特征及其局部性进行预测,从而提供了一种处理图形结构数据的有效方法。在本文中,我们将在推荐系统的背景下概述图论和图神经网络(GNN)。二、经典矩阵补全方法 系统的一种流行技术是利用经典的机器学习方法进行矩阵补全,这是一种协同过滤方法。给定用户数 m 和数项 n,它旨在填充用户-项交互矩阵 R(维度为 mxn)中的缺失值。为了实现这一点,我们将每个用户和项目映射到大小为 k 的嵌入 — 向量空间中的抽象表示。这些嵌入可能会捕获电影类型或用户人口统计数据等特征,但很多时候是潜在的未知特征。生成用户嵌入矩阵 U(维度为 mxk)和项目嵌入矩阵 I(维度为 nxk)。 为了预测用户-项目对,我们计算转置项目矩阵和用户矩阵的点积。最初,潜在矩阵是随机初始化的,我们使用基于已知用户-项交互的损失函数优化嵌入。 图 1:此图显示了用户-项交互矩阵 R,以及我们如何在用户和项嵌入矩阵之间取点积来预测 R 矩阵中的特定值。 但是,此方法在处理稀疏矩阵时会遇到性能问题。在用户仅与数百万个可用项中的几个项交互的情况下,经典的矩阵完成方法可能是不够的,因为它们只考虑用户和项之间的直接连接。为了解决这一限制,基于图神经网络(GNN)的推荐系统已成为一种更有效的替代方案。 GNN 不仅通过考虑单个用户的偏好,还集成来自相邻用户的信息,在稀疏数据集中提供改进的性能。通过利用图形结构,GNN 可以更全面地捕获用户和项目之间的关系,从而实现更准确和个性化的推荐。让我们首先提醒自己一些关于图论的知识。三、图论概述3.1 什么是图表? 图形是一种数据结构,将实体集合表示为节点(顶点),并将其关系表示为边。它是建模和理解各种真实场景的强大工具。例如,图表可以表示银行交易,其中节点符号化银行账户,边表示它们之间的交易。同样,社交网络图以人为节点,边缘描绘个人之间的关系。图 2:图形示例。3.2 图表的类型 根据其特征,有不同类型的图形。 有向图具有具有特定方向的边。 例如,在银行交易图中,每条边表示从发送方到接收方的交易,从而建立明确的方向。另一方面,无向图不会为边分配方向。在社交网络中,无向边缘表示两个人之间的联系或相识,没有任何固有的方向性。 图形也可以分为同构或异构。 同类图具有单一类型的节点和边,而异构图可能包含多种类型。例如,在电子商务方案中,可能有两种类型的节点:一种表示可供销售的商品,另一种表示用户。不同类型的边缘可以表示不同的交互,例如用户单击项目或进行购买。图 3:有向图、无向图、齐次图和异构图的示例 二分图是一种特定类型的异构图,在建模推荐系统方面非常有用。它们涉及两组不同的节点,例如用户和项目,边缘专门连接来自不同集的节点。二分图有效地捕获用户-项目交互,并启用高效的推荐算法来利用丰富的网络结构。图 4:二分图示例。3.3 我们如何存储图形数据? 有多种方法可以存储图形数据。一种方法是使用邻接矩阵,表示为 A ∈ {0, 1}ⁿxⁿ,其中 n 是图中的节点数。矩阵的 (i, j) 条目 Ai,j 表示节点 vi 和 vj 之间的连通性,如果有连接 vi 和 vj 的边,则为 Ai,j = 1。对于无向图,邻接矩阵是对称的,即 Ai,j = Aj,i。但是,对于大型和稀疏图(如社交网络),邻接矩阵可能是内存密集型的。这是因为邻接矩阵随节点数而缩放。在拥有数百万个节点的社交网络中,大多数人彼此不认识。这将导致一个大矩阵,其中大多数单元格为空。 为了解决这个问题,邻接列表表示形式对内存效率更高。 它将节点之间的边描述为元组 (i,j),其中 (0,1) 表示节点 0 和 1 之间的边。例如,对于图 5 中的图形,邻接列表为 [(A,B)、(B,D)、(B,C)、(D,C)]。图 5a:图形示例 — 图 5b:图 4a 中图形的邻接矩阵。 邻接列表表示形式提供了更高的内存效率,特别是对于稀疏图,因为它仅存储有关连接节点的必要信息。这使其成为处理大规模图形数据(例如社交网络)的首选,其中连接数与节点总数相比通常有限。四、推荐系统中的图神经网络 与传统的矩阵完成方法类似,GNN 可以为用户和项目生成嵌入,以预测看不见的用户-项目交互。但是,它们提供了一种显式合并高阶图结构的方法,并且可以捕获数据本身中可能不可用的潜在或隐藏相关性。 给定一个图,我们的目标是将每个节点 v 映射到其自己的 d 维最终嵌入,其中基于其网络邻域特征以及自身特征的相似节点最终应在其最终嵌入空间中彼此接近。图 6:节点编码到嵌入空间。4.1 图形神经网络层 GNN 的一层在图中的所有直接邻居之间交换信息,为图中的每个节点生成新的节点嵌入。在 2 层 GNN 模型中,每个节点将根据其 2 跳邻域生成其第 2 层嵌入。 K-hop 邻域是指距离感兴趣节点 K 边的所有节点。这是一个迭代过程,其中邻居变量通过传递消息(一种消息传递方法)与每个变量“对话”。图 7:2 层 GNN 中特定目标节点的输入图和计算图 在此图像中,我们看到节点 A 的第 2 层表示是通过以某种方式聚合其直接邻居 [B,C,D] 的第 1 层嵌入并对其应用黑盒转换或神经网络生成的。这些嵌入又由其第 0 层 [X_A、X_B...X_F] 嵌入直接相邻要素,这是初始输入要素。每一层都会生成一个新的节点嵌入,节点的 K 层嵌入从距离自身的 K 跳节点获取信息。4.2 图神经网络的特点、优势和局限性 图神经网络(GNN)具有几个显着的特征和优势,使其与传统的矩阵补全方法区分开来。这些特征有助于它们在推荐系统中的有效性。让我们探索这些功能:阶次不变性: GNN 是顺序不变的,这意味着节点的标记顺序不会影响结果。计算图考虑节点连接而不是节点顺序,利用顺序不变的聚合函数(如平均值、最大/最小池化)进行消息传递。大小不变性: GNN 中的每个节点都有自己的计算图,这使得 GNN 的大小不变。这允许各个节点根据其本地邻域处理和集成信息,从而实现个性化和灵活的学习。下图显示了上图中每个节点的计算图。图 8:图 7 输入图中每个节点的计算图。处理稀疏矩阵:与经典的矩阵补全方法不同,GNN 擅长处理稀疏矩阵。它们超越了直接节点交互,并捕获了高阶图结构中存在的隐藏相关性。此功能增强了它们在交互受限的场景中的性能端到端学习: GNN 提供端到端学习,同时优化嵌入和预测任务。这减轻了对手动特征工程的需求,简化了推荐管道。此外,GNN 可以很好地适应不断发展的用户/项目功能,从而减少对重大代码修改的需求。尽管GNN具有优势,但它们也有应考虑的局限性:计算复杂性:GNN 可能是计算密集型的,特别是对于大型图形和深度架构。与更简单的模型相比,训练GNN可能需要大量的计算资源和更长的训练时间。可解释性:与传统方法相比,GNN的复杂性可能使它们的可解释性降低。了解基于 GNN 的建议背后的内部工作原理和推理可能具有挑战性。五、结论 在本文中,我们探讨了图神经网络(GNN)在推荐系统中的潜力,强调了它们相对于传统矩阵完成方法的优势。GNN为利用图论来改进推荐系统提供了一个强大的框架。 通过利用嵌入在图结构中的丰富信息,GNN可以捕获复杂的模式,发现潜在特征,并在推荐过程中考虑相邻用户的影响。这种方法增强了推荐系统做出准确预测的能力,即使在经典方法难以生存的稀疏数据集中也是如此。 随着推荐系统领域的不断发展,GNN已成为解决传统方法局限性的有前途的解决方案。 他们适应不同领域并自动从数据中学习的能力使他们非常适合在各种情况下提供相关和量身定制的建议。 在本系列的下一部分中,我们将深入研究GNN的数学基础,特别关注LightGCN在电影推荐系统中的应用。通过了解基本原理和算法,我们可以进一步了解GNN如何改变推荐系统的格局。
这是通俗易懂的神经网络推理加速入门第4篇。本篇介绍一个计算机中的部件——存储器,看完之后,你将了解存储器是什么以及存储器在AI计算中的作用。在介绍之前,先说一个我今天早晨的事儿。7点起来晨跑,突然发现已经到深秋了,凉飕飕的,感觉是时候换一波秋冬的衣服了。于是开始翻箱倒柜,花了好大一会儿,终于在衣柜的最深处,找到了去年冬季跑步的衣服,口袋里还装着去年的口罩。找到衣服之后,我突然盯着衣柜看了半天,发现:衣柜的设计确实是很讲究,只可惜我没用好,才导致费了九牛二虎之力才找到了衣服。衣柜讲究的设计最经常穿的衣服,应该是要用衣架撑好挂在衣柜横杆上的,方便随时取用;不经常穿的衣服,比如非当季的衣服,应该是要叠好放在最底层的柜子里,让他过冬。这种明显的功能区域划分,可以让我在需要某件衣服的时候,方便快捷的找到。这就和今天文章的主角——存储器有点像了。不知道是不是所有带有存储性质的产品都有类似的分层划分。但我知道,存储器这种储存数据和指令的东西,有着很明显的分层或分级划分的存储器的分层设计之前的一篇文章计算机存储和计算的分离说到,在计算机系统中,计算单元和存储器是分离的。而实际上,在计算机中,单看存储器,也是会分成很多层级。存储器最常见的就是内存。在买手机时,我们一定会关注一个参数,那就是内存大小。内存越大,手机可以存储的数据就越多,运行起来也就越流畅,手机性能就越好。但是在一个计算系统中,除了内存,还有其他的存储。下图是一个典型的存储器划分示意图。示意图越往上,代表存储器越靠近计算单元,其容量越小,相对造价就越高。这也是为什么,在计算机系统中,单位存储的内存价格很高,而外存(如硬盘)相对较为便宜。磁盘是离CPU最远的存储器。一般作为硬件外设存在。包括我们常见的硬盘、U盘等存储外设。磁盘的读写速度相比其他存储器慢,但是容量大,价格便宜。这个就相当于衣柜的最底层,存放着不经常穿的衣服(数据),像是一个大仓库。主存可以理解为电脑的内存条,用来存放程序运行时的指令和数据。程序运行时操作系统需要将程序和数据加载到内存中,它就相当于衣柜中搭衣服的横杆,随取随用。高速缓存(Cache)是比主存离CPU更近的一级存储,他会把程序需要的指令或者数据预先加载进来,在CPU进行运算时,会首先在缓存中查找数据或指令,如果找不到,就在去主存中寻找,找不到去主存中寻找的过程一般称为Cache Miss。预先加载怎么理解呢?打个比方,我们在冬天肯定有经常要穿的2-3件衣服,但不会每次衣服脏了都放回衣柜中,而是洗完放在阳台晾衣架上,这2-3件衣服轮换穿。CPU也是这样,它会频繁的从高速缓存中存取数据,找不到了,再去内存中找,就好像阳台上没衣服了,再去衣柜里找一样。寄存器(Register)是CPU最近的存储器。用来存放程序运行时需要的指令、地址、立即数等。类比于就是身上正在穿的衣服。有了这几级存储,在做AI加速时,就可以做很多事情。之前指令流水线 说过,由于计算和存储是分离的,那么可以将计算和存储指令排流水,实现性能的加速。同样,如果存储也有分层设计,并且开放给程序员的话,那么,单独的存储指令也可以进行流水设计,从而在带宽不变的前提下,提高数据的吞吐和程序的性能。GPU就是这么做的。熟悉GPU硬件架构的同学可能知道,GPU的编程模型中有DDR(显存,也就是最外层存储,可类比硬盘),Shared Memory(共享内存),当然还有其他的存储。单说 DDR 和 shared memory(SM)这两级存储,就可以排流水。比如——上表中每一行代表同一时刻,看不懂没关系,只需要知道在同一时刻,程序可以同时将数据从DDR load 到 SM(左侧的一例) 以及在SM上进行计算(右侧的一列)即可。这样就相当于在流水线上有两个工人一起工作,从而提高了性能。总结存储器的分层设计,一个好处之一就是,程序员可以通过编写存储指令(包括将数据从外存搬运到内存的 load 指令,将数据从内存加载到片上计算的 move 指令等),从而完成流水的排布。当然,存储器的分层设计肯定不单是这个原因,就不展开了。那么问题来了,这篇文章和AI加速有什么关系呢?其实,存储器作为一个偏计算机底层的部件,是根本不关心上层应用是什么的。我们可以让计算机进行AI计算,来完成AI加速,也可以让计算机运行一个游戏,完成游戏加速。只要了解了存储器的原理,不论是AI加速还是游戏加速,都能做到性能很好。政治基础决定上层建筑。——而且只要硬件支持指令流水级,并且编译器做的足够好,甚至都不需要程序员手动去排流水(手写汇编确实太枯燥了)就能自动实现。从而完成对于AI算法的加速计算,这一点对于编译器的要求很高,后面再介绍。今天就介绍到这,欢迎持续关注神经网络推理加速入门系列文章。
这篇简单介绍一个概念——异构编程。上一篇一个例子看懂流水——从指令到算法用一个生活中的小例子,介绍了流水这一概念。在计算资源有限的情况下,我们可以通过软件的流水技术来提升程序性能。但如果你是土豪,不想耗费太多精力去做软件优化,就想砸钱来提升程序性能,有办法么?当然有,性能不够,芯片来凑。正所谓“众人拾柴火焰高”,只要芯片足够多,性能就能飙到顶。异构芯片编程就是这样的一种方式。异构编程所谓异构编程,就是将不同厂家、不同架构的芯片放在一个统一的计算机系统中,通过软件的调度,来实现AI计算的一种方式。比如将x86的CPU和英伟达的GPU放在一起进行编程。人工智能的发展催生了异构编程的火热。主要是因为神经网络中有大量的稠密计算,如果用传统的CPU从头到尾训练一个神经网络,需要耗费大量的算力才能计算出来,就算把CPU累死,估计都算不出来。在神经网络中,矩阵和卷积运算这两种稠密运算,几乎占据了大部分神经网络90%的耗时。因此,有很多公司开发专用的AI芯片(也称ASIC 芯片,Application Specific Integrated Circuit,专用集成电路),专门围绕着卷积运算或者矩阵运算设计硬件单元,来完成运算加速。这就像,给CPU搞了个外挂。英伟达的GPU,谷歌的NPU,寒武纪的MLU等都是类似的ASIC芯片。它们通过PCIe总线和主机相连,从而作为协处理单元完成AI的加速。这和我们扩展电脑内存一个道理。内存不够了,买个内存条插上,内存就够了。算力不够了,买个显卡插上去,算力就够了。正是因为人工智能对于算力的需求,才出现了越来越多的AI芯片公司,并且使得异构编程这一技术越来越为人所知。其中,英伟达的 CUDA 编程就是最广为人知的一种异构编程方式。异构编程不熟悉的同学这时候可能会有些疑问,异构编程难么?首先说,不难。因为大部分做ASIC芯片的厂家都会提供异构编程所需要的驱动或者计算库。如英伟达有 cuDriver 库用来驱动显卡,有 cuDNN 库用来加速常见的深度学习算法,cuFFT 库用来就是FFT相关运算(里面都把算法写好了,我们自己调用即可),还有 TensorRT 专门用来做神经网络推理的优化和网络融合等等。再不济,如果你的神经网络中有个不常见的算法,像英伟达还提供了CUDA编程,这是一种类C语言的编程语言,只需要学会某些简单的标识符和核函数(kernel)的编写,就能写出远超CPU性能的代码。关于CUDA编程的资料网上有很多,感兴趣的同学可以自行去查找。除了英伟达,其他厂家也基本都是这个套路。对外提供提供加速库和驱动来辅助完成异构编程,从而实现AI的计算加速。一次与异构编程相关的面试记得有一次去某公司面试,面试官告诉我,他们公司是基于视觉做智能交通的解决方案的。所谓的解决方案,就是给他们的客户提供一整套的软件+硬件的产品,打包卖给他们。我当时比较好奇,你们也自己做芯片么?面试官说,我们自己不做,但我们会买。国内做AI芯片的企业开发的芯片产品我们都会买,当然GPU也会买,然后做二次开发,在这些硬件上部署我们自己的算法。那你们的解决方案中,卖给用户的硬件,是只有一家的产品,还是会多家混用?面试官:有可能多家的都有,看谁家的性能好用谁家的。...这家做解决方案的公司,会用到不同厂家的芯片,但核心的AI算法是自己的。这是一种典型的异构编程场景:在服务器主机上通过PCIe总线连接多张AI加速卡,实现AI算法计算加速,在云端实现交通场景下路人和车辆的识别。总结异构编程可以认为是一种使用专用芯片对神经网络进行加速的外挂方式。通过这种专用的加速卡,来完成神经网络中相关算法的加速运算。其实,异构编程并不是一个很新的概念。据一个从事手机开发的朋友讲,他们很早之前做手机,手机系统中会有很多不同的芯片,主处理器和协处理之间都会有通信,某些算法在主处理器上跑,某些算法在协处理器上跑,最终完成一个整体运算。这就是一种异构编程,只不过当时他们认为这是理所当然的。而随着人工智能的热潮,异构编程这一概念才越来越多的被人所熟知。从而也成为了AI加速中一个不可或缺的编程方式。好啦,异构编程就简单说到这。
前言刚毕业的时候在青岛某信任职,做嵌入式软件开发,需要调试电路板。公司有个要求是所有新入职的员工,都要去公司自己的加工厂上两周的班,亲自组装生产电路板,体验产品生产的过程。于是,刚毕业的我,光荣的成为了一名流水线工人,虽然只有两周的时间。这期间,我和同事一起,完成过一天组装3万片电路板的成就,也完成过一天往电路板上插装不计其数电阻、电容的操作,白班夜班倒,记忆犹新。当时的我,坐在流水线的椅子上,满脑子就一个想法:“我今天就是个螺丝钉”——真拧螺丝。也正是这一次流水线的工作体验,让我明白了一个道理——一个产线或团队的工作效率,取决于整条流水线上动作最慢的那个人,而不是最快的那个人。一旦动作慢导致产品在你那积压,最常见的就是带小红帽的组长过来噼里啪啦说一顿。不过好在我当时手脚勤快,没有拖后腿。在流水线上,人就是个机器,机械的执行着每一个动作。直到后来开始做芯片开发,开始给指令排流水,才恍然大悟,原来,每条指令都是流水线工人。指令流水上一篇计算和存储的分离说到,在冯诺依曼架构的计算体系中,计算和存储是分离的。分离的结果就是,我们可以将计算和存储分别看做两个工人,而不是一个,每个工人只负责干自己的事儿。—— 【计算】这名工人只负责计算,【存储】这名工人只负责存储。计算好比在厨房做菜,存储好比从厨房冰箱里将菜拿出来。这个比喻很重要,能帮你很通俗的理解流水。那么问题来了,【一个人从冰箱里拿菜洗菜然后去做菜】和【两个人,一个专门拿菜洗菜,一个专门做菜】,哪种做饭效率高?做过饭的同学肯定知道,当然两个人配合快了。比如做西红柿鸡蛋,一个人需要先从冰箱拿出鸡蛋,记需要花费 A 的时间,然后炒鸡蛋需要花费B的时间,然后回去拿出西红柿,需要花费C的时间,然后将西红柿和鸡蛋一起炒,需要花费D的时间。总共花费 A + B + C + D的时间。而两个人的话,第一个人从冰箱里拿出鸡蛋,记A的时间,第二个人炒鸡蛋,花费B的时间,而此时,在第二个人炒鸡蛋的同时,第一个人可以同时去拿出西红柿,需要花费C的时间,然后第二个人抄完鸡蛋后,接过第一个人的西红柿一起炒,需要花费D的时间。总共花费 A + MAX(B,C) + D 的时间。两个一比较,肯定两个人做饭时间花的少吧。而两个人做饭时,B,C同时进行,就像流水线上的工人一样,你负责你的事,我负责我的事,我做完就给下一个人,下一个人动作快的话,可以立刻接过去,动作慢的话,就会导致产品积压。在计算机的体系结构中,计算指令和存储指令,一般都是两条独立的指令,并且在硬件部件上,是两个独立的硬件部件,比如计算是ALU单元,存储是DMA或load/store单元等。独立的硬件部件和独立的指令设计,天然的保证了两条指令之间可以排流水,就像流水线上的工人一样工作。还是拿卷积算法举例子,我先计算上半张图片,同时你就可以搬运下半张图片的数据,整体的AI计算开销,就会少很多。从而起到了推理加速的作用。不仅如此,在指令集设计或硬件设计时,一般都会考虑流水线的设计,从而完成更高效的计算和访存操作。可进行流水操作的指令,也绝不仅仅只有计算指令和存储指令等,不同的计算单元之间,也是可以排流水的。比如卷积层后面接一个池化层,那么在计算第二部分卷积的同时,可以计算第一部分池化。流水线的发明对于制造业来说是一次技术变革。早期的手工制造业是靠大量人工完成的,这种工作方式要求每个工作者要了解整个成品的完整制造过程,要快速的制造出一个成品需要对整个工序、流程非常熟悉,无形中提高了对工人素质的要求。而流水线的出现,不需要工人了解整个流程,为手工制造业实现了半自动化,加快了加工速度。好啦,本篇就简单介绍了一种推理优化的常见方式,那就是通过对指令排流水的方式,可以完成指令加速,从而完成推理优化。
做优化有时候真的很头疼,绞尽脑汁的想怎么做算法等价,怎么把神经网络各层指令流水起来,在确保整网精度的同时,又有高性能。但有时做了半天,却发现流水根本就流不起来,总是莫名其妙地被卡住。真的是一顿操作猛如虎,回头一看原地杵。今天介绍一种神经网络的性能优化方法。它不需要懂特深奥的算法知识,就能做到整个优化系统,大到网络,小到算子的性能的成倍提升。而且绝对是成倍的性能提升,并且显而易见的算法等价。怎么做呢?很简单,只需要改一下算子的先后调用顺序就行。背景在做AI推理或者训练时,大部分情况下一个神经网络中的所有层(Layer)的计算数据类型是相同的。比如为了网络有更好的识别精度,神经网络中的运算可以使用高精度的浮点数,如 float32,简称 FP32。但有时为了性能,稍微损失一点识别精度也能接受,此时可能会使用 float16,简称FP16, 也就是半精度数据类型来做运算。FP32 和 FP16 的区别在于,前者数据位宽是后者的两倍,因此表示相同的数据的时候,前者的精度更高,但内存占用也更大。比如同时存储一张图片,如果使用 FP32的话,可能会占用1MB的内存,但如果使用FP16来存储,只占0.5MB的内存。我们可能听说过混合精度推理、混合精度训练。这里说的混合,指的就是精度混合。比如一个神经网络中存在多种数据类型。为什么可以做混合精度的推理或训练呢?一个神经网络就像是一个大厦,由一层一层的算法搭建而成,每一层的算法可能不同。不同的算法对数据精度的敏感程度不同。有很多算法对数据精度不敏感,比如 transpose, gather, scatter等,这类算法都是数据搬运操作,也就是纯IO操作。他们不需要进行数据计算,无需考虑数据在做加法时候的溢出处理等情况。而有些算法对数据精度很敏感,典型的比如conv2d算法,它需要做大量的乘累加操作,数据的累加很容易出现溢出,此时需要用更高位宽的数据来接收累加结果。如果把操作 FP32 比作需要搬运32块砖的话,那么 FP16 就是只需要搬运 16块砖。很明显,搬运16块砖比搬运32块砖,在其他条件不变的情况下,要省时省力。因此,在神经网络尤其是混合训练或推理的网络中,如果遇到了一些数据搬运算法搬运的是 FP32,那么是很有机会只让他搬16块砖(FP16)的。那么具体怎么做呢?首先简化一个神经网络,假设一个神经网络有如下结构:在这个假想的网络中,卷积层(conv2d)计算的输出是 FP32,然后送给transpose 层进行数据搬运,transpose由于是纯IO算法,因此它的输出也是FP32。transpose的输出送给下一层cast,cast负责将FP32的数据转换为FP16, 因此cast 的输出是FP16。然后FP16的数据送给接下来的层进行运算。不知有没有发现,在这个网络中,transpose 算法先搬运了FP32的数据,然后交给了 cast 进行数据类型转换,转换成了更低位宽的 FP16。但是由于 transpose 是纯IO运算,对数据类型不敏感,因此,我们完全可以将cast算子提前到 transpose 之前,如此的话,transpose 只需要做 FP16 的数据搬运。转换之后的网络如下:这样做的结果就是:整个网络的计算是等价的,但是 transpose 算子却由原来进行 FP32 的数据搬运,变成了 FP16 的数据搬运。对 transpose而言,其IO性能表现是成倍的提升。这只是举一个很简单的例子。而实际上,在真实的网络中,使用此方法可以优化成功的算法有时不仅仅是一个简单的 transpose,而是一个很大的网络片段。由此可见,仅仅将 cast 提前这一个简单的操作,就能使整网的性能提升一倍。这个方法很简单,很有效,也很容易实施。但是在实际进行网络优化的时候,有时却会被忽略。能够使用这一优化的网络必须满足以下两个条件:必须是混合精度的网络由高位宽转低位宽的cast 算子前存在 IO 型算子在我们绞尽脑汁使用一些高级的技巧,如模型并行、层层流水来做网络优化的同时,不妨放大视角,着眼全图,看看整网是否满足上面的条件,没准只一眼,就能发现这一最简单有效的优化点,从此百分比的提升网络性能,不是梦!
1、XGBoost算法XGBoost 是 eXtreme Gradient Boosting 的缩写称呼,它是一个非常强大的 Boosting 算法工具包,优秀的性能(效果与速度)让其在很长一段时间内霸屏数据科学比赛解决方案榜首,现在很多大厂的机器学习方案依旧会首选这个模型。XGBoost 在并行计算效率、缺失值处理、控制过拟合、预测泛化能力上都变现非常优秀。本文我们给大家详细展开介绍 XGBoost,包含「算法原理」和「工程实现」两个方面。2、算法原理可视化解读关于 XGBoost 的原理,其作者陈天奇本人有一个非常详尽的Slides做了系统性的介绍,我们在这里借助于这个资料给大家做展开讲解。2.1、监督学习核心要素符号(Notations):��∈��xi∈Rd表示训练集中的第i个样本。模型(Model):对于已知的��xi如何预测��^yi^?线性模型(Linear Model):�^=∑������y^=∑jwjxij(包括线性回归和逻辑回归),预测值��^yi^根据不同的任务有不同的解释:线性回归(Linear Regression): ��^yi^表示预测的分数。逻辑回归(Logistic Regression):1/(1+�−�^�)1/(1+e−y^i) 预测了实例为正的概率。其他:例如在排名任务中 ��^yi^可以是排名分数。2.2、监督学习进阶知识Ridge回归(L2正则化):• Ridge 是线性模型(Linear Model),用的是平方损失(Square Loss),正则化项是 L2 Norm。Lasso回归(L1正则化):• Lasso是线性模型(Linear Model),用的是平方损失(Square Loss),正则化项是L1 Norm。逻辑回归:• 逻辑回归是线性模型(Linear Model),用的是逻辑损失(Logistic Loss),正则化项是 L2 Norm。2.3、目标函数及偏差方差权衡优化训练损失函数(Training Loss)有助于建立预测模型,很好地拟合训练数据至少能让你更接近潜在的训练数据的分布。优化正则化项(Regularization)有助于建立简单的模型:模型越简单,未来预测的方差越小,预测越稳定。3、XGBoost算法原理案例:如上图所示,将左侧的数据输入到模型1中,会得到预测收入。预测收入和真实的收入之间的差值记做残差。由于这个模型1有一定的能力,但是能力比较弱,遗留了一些问题。这个残差就能表征这个遗留的问题。紧接着,再训练一个模型2去预测这些样本,只不过目标值改为刚刚得到的残差。上图所示,预测的结果不再是收入,而是模型1得到的残差。上图中的模型2还会得到残差,但是我们发现第一行样本的残差已经为零了。也就是说第一个样本,通过模型1和模型2能够预测对收入。但是除了第一行,其他的还是有残差的,这时候可以在这基础上训练一个模型3上图所示,在刚刚 模型2得到的残差(准确的说是模型1和模型2共同作用的结果) 的基础上去拟合,得到模型3。这时候的残差可以理解为是前两个模型遗留下来的问题。该模型去预测模型2的残差,我们发现通过前三个模型的预测,得到的残差是上图中最新的残差这一列。这时候最新的残差都是非常小了,如果能达到我们满意的标准,我们就可以停下。这样我们就得到了三个不同的模型。如下图所示,最终的预测就是三个模型预测的结果和。如下图:我们解决问题的步骤:如何构造目标函数 -> 目标函数直接优化难,如何近似? -> 如何把树的结构引入到目标函数?-> 仍然难优化,要不要使用贪心算法?3.1 构建目标函数首先举个例子,用多棵树来预测张三、李四的薪资。如下图所示,用年龄这个因素构建的树预测张三的值为12,用工作年限这个因素构建的树张三为2. 两个相加就是对张三薪资的预测:12+2=14。假设已经训练了�K颗树,则对于第�i个样本的最终预测值为:��xi是样本的特征,��(��)fk(xi)是用第�k颗树��xi样本进行预测。将结果加在一起就得到了最终的预测值�^�y^i,而该样本的真实label是��yi。这样我们就能构建损失函数了。构建的目标函数如下:损失函数计算模型预测值和真实值的 loss,其中�l是损失函数,可以是 MSE、Cross Entropy 等等。第二项是正则项,来控制模型的复杂度,防止过拟合。这个正则项可以类比 L2 正则。3.2叠加式的训练3.3用泰勒级数近似目标函数3.4如何用参数表示一颗树3.5、定义树的复杂度3.6、新的目标函数经过上面的一步步的简化,我们把最初的目标函数:简化为了:紧接着,看下图,假设第一个叶节点上(即 15 的地方)有样本[1, 3]落在这里 ,第二个节点有样本[2]落在这个地方,样本[4,5]落在了第三个叶子结点处这里 :所以:二次函数求最优解问题。知识回顾,典型的二次函数:最小点的值为:3.7、如何寻找树的形状?。所以,使用贪心的方式,选择新的树目标函数值较小的那颗树。比如下面这个例子,我们有样本[1、2、3、4、5、6、7、8],第一颗树把这些样本分为了两部分,左侧的叶子结点是[7,8],右侧节点是[1,2,3,4,5,6]。此时我们知道了树的结构,可以根据如下的公式计算出此时树的最小目标函数值:紧接着,我们根据新的特征对叶子结点再次进行了分割,得到了如下的树的形状:紧接着计算两颗树最小目标函数值的差:4.XGBoost工程优化4.1、并行列块设计XGBoost 将每一列特征提前进行排序,以块(Block)的形式储存在缓存中,并以索引将特征值和梯度统计量对应起来,每次节点分裂时会重复调用排好序的块。而且不同特征会分布在独立的块中,因此可以进行分布式或多线程的计算。4.2、缓存访问优化4.3、核外块计算数据量非常大的情形下,无法同时全部载入内存。XGBoost 将数据分为多个 blocks 储存在硬盘中,使用一个独立的线程专门从磁盘中读取数据到内存中,实现计算和读取数据的同时进行。为了进一步提高磁盘读取数据性能,XGBoost 还使用了两种方法:① 压缩 block,用解压缩的开销换取磁盘读取的开销。② 将 block 分散储存在多个磁盘中,提高磁盘吞吐量。5.XGBoost vs GBDTGBDT和这里的 XGBoost 做一个对比总结:GBDT 是机器学习算法,XGBoost 在算法基础上还有一些工程实现方面的优化。GBDT 使用的是损失函数一阶导数,相当于函数空间中的梯度下降;XGBoost 还使用了损失函数二阶导数,相当于函数空间中的牛顿法。正则化:XGBoost 显式地加入了正则项来控制模型的复杂度,能有效防止过拟合。列采样:XGBoost 采用了随机森林中的做法,每次节点分裂前进行列随机采样。缺失值:XGBoost 运用稀疏感知策略处理缺失值,GBDT无缺失值处理策略。并行高效:XGBoost 的列块设计能有效支持并行运算,效率更优。6、Codeimport xgboost as xgb from xgboost import plot_importance from matplotlib import pyplot as plt from sklearn.model_selection import train_test_split from sklearn.datasets import load_boston # 导入数据集 boston = load_boston() X ,y = boston.data,boston.target # Xgboost训练过程 X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=0) model = xgb.XGBRegressor(max_depth=5,learning_rate=0.1,n_estimators=160,silent=True,objective='reg:gamma') model.fit(X_train,y_train) # 对测试集进行预测 ans = model.predict(X_test) # 显示重要特征 plot_importance(model) plt.show() PythonCopy from sklearn.model_selection import train_test_split from sklearn import metrics from sklearn.datasets import make_hastie_10_2 from xgboost.sklearn import XGBClassifier X, y = make_hastie_10_2(random_state=0) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)##test_size测试集合所占比例 clf = XGBClassifier( silent=0 ,#设置成1则没有运行信息输出,最好是设置为0.是否在运行升级时打印消息。 #nthread=4,# cpu 线程数 默认最大 learning_rate= 0.3, # 如同学习率 min_child_weight=1, # 这个参数默认是 1,是每个叶子里面 h 的和至少是多少,对正负样本不均衡时的 0-1 分类而言 #,假设 h 在 0.01 附近,min_child_weight 为 1 意味着叶子节点中最少需要包含 100 个样本。 #这个参数非常影响结果,控制叶子节点中二阶导的和的最小值,该参数值越小,越容易 overfitting。 max_depth=6, # 构建树的深度,越大越容易过拟合 gamma=0, # 树的叶子节点上作进一步分区所需的最小损失减少,越大越保守,一般0.1、0.2这样子。 subsample=1, # 随机采样训练样本 训练实例的子采样比 max_delta_step=0,#最大增量步长,我们允许每个树的权重估计。 colsample_bytree=1, # 生成树时进行的列采样 reg_lambda=1, # 控制模型复杂度的权重值的L2正则化项参数,参数越大,模型越不容易过拟合。 #reg_alpha=0, # L1 正则项参数 #scale_pos_weight=1, #如果取值大于0的话,在类别样本不平衡的情况下有助于快速收敛。平衡正负权重 #objective= 'multi:softmax', #多分类的问题 指定学习任务和相应的学习目标 #num_class=10, # 类别数,多分类与 multisoftmax 并用 n_estimators=100, #树的个数 seed=1000 #随机种子 #eval_metric= 'auc' ) clf.fit(X_train,y_train,eval_metric='auc') #设置验证集合 verbose=False不打印过程 clf.fit(X_train, y_train,eval_set=[(X_train, y_train), (X_val, y_val)],eval_metric='auc',verbose=False) #获取验证集合结果 evals_result = clf.evals_result() y_true, y_pred = y_test, clf.predict(X_test) print"Accuracy : %.4g" % metrics.accuracy_score(y_true, y_pred) #回归 #m_regress = xgb.XGBRegressor(n_estimators=1000,seed=0) PythonCopy params = {'objective': 'reg:squarederror', # 默认reg:linear。 reg:squarederror,代表要解决的问题时分类还是回归,取值可以很多,一般只关心是分类还是回归, # 在回归问题objective一般使用reg:squarederror ,即MSE均方误差或者:reg:linear , reg:logistic , count:poisson 。 # 二分类问题一般使用binary:logistic,rank:pairwise # 多分类问题一般使用multi:softmax
1、什么是线性回归?线性:两个变量之间的关系是一次函数关系的非线性:两个变量之间的关系不是一次函数关系的2、应用场景房价预测流量预测票房预测....3、表达式&优化函数表达式: w是x的系数,b是偏置项目标函数(Loss Function) 利用梯度下降法求 J 的最小值,从而推导出w和b4、为什么需要正则化4.1、L1正则化(Lasso回归)L1正则化项:L1如何解决过拟合问题特征稀疏性4.2、L2正则化(岭回归)L2正则化项:L2如何解决过拟合问题拟合过程中通常都倾向于让权值尽可能小参数很小,数据偏移得多一点也不会对结果造成什么影响,鲁棒性更强参数:5、代码实现:房价回归预测-LinearRegression# encoding: utf-8 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression # 1.获取数据集 boston_data = load_boston() x = pd.DataFrame(boston_data.data) # 波士顿房价data y = boston_data.target # 波士顿房价真实值 x.columns = boston_data.feature_names # 特征赋值 #特征14个: #CRIM: 城镇人均犯罪率 #ZN: 住宅用地所占比例 #INDUS: 城镇中非住宅用地所占比例 #CHAS: 虚拟变量,用于回归分析 #NOX: 环保指数 #RM: 每栋住宅的房间数 #AGE: 1940 年以前建成的自住单位的比例 #DIS: 距离 5 个波士顿的就业中心的加权距离 #RAD: 距离高速公路的便利指数 #TAX: 每一万美元的不动产税率 #PTRATIO: 城镇中的教师学生比例 #B: 城镇中的黑人比例 #LSTAT: 地区中有多少房东属于低收入人群 #MEDV: 自住房屋房价中位数(也就是均价) #x.columns # 2.划分训练集、测试集 xtrain,xtest,ytrain,ytest = train_test_split(x,y,test_size=0.2,random_state=125) # 3.建立线性回归模型 model = LinearRegression().fit(xtrain,ytrain) # 4.1 获取预测值 y_pred = model.predict(xtest) # 4.2 获取回归系数 y_w = model.coef_ # 4.3 获取截距 y_w0 = model.intercept_ # 4.4 将回归系数与特征对应 compare_feature = [*zip(xtrain.columns,y_w)] compare_feature # 5.预测结果可视化 plt.rcParams['font.sans-serif'] = 'SimHei' fig = plt.figure(figsize=(10,6)) plt.plot(range(ytest.shape[0]),ytest,color='black',linestyle='-',linewidth=1.5) plt.plot(range(y_pred.shape[0]),y_pred,color='red',linestyle='-.',linewidth=1.5) plt.xlim((0,102)) plt.ylim((0,55)) plt.legend(['真实值','预测值']) plt.show() PythonCopy