机器学习编译第2讲:张量程序抽象

02 张量程序抽象 【MLC-机器学习编译中文版】

课程主页:https://mlc.ai/summer22-zh/

文章目录

      • 2.1 元张量函数
      • 2.2 张量程序抽象
        • 2.2.1 张量程序抽象中的其它结构
      • 2.3 张量程序变换实践
        • 2.3.1 安装相关的包
        • 2.3.2 构造张量程序
        • 2.3.3 编译与运行
        • 2.3.4 张量程序变换
        • 2.3.5 通过张量表达式(Tensor Expression,TE)构造张量程序
        • 2.3.6 变换一个矩阵乘法程序
      • 2.4 总结

在本章中,我们将讨论对单个单元计算步骤的抽象以及在机器学习编译中对这些抽象的可能的变换。

2.1 元张量函数

在上一章的概述中,我们介绍到机器学习编译的过程可以被看作张量函数之间的变换。一个典型的机器学习模型的执行包含许多步将输入张量之间转化为最终预测的计算步骤,其中的每一步都被称为元张量函数 (primitive tensor function)

在这里插入图片描述
在上面这张图中,张量算子 linear, add, relu 和 softmax 均为元张量函数。特别的是,许多不同的抽象能够表示(和实现)同样的元张量函数(正如下图所示)。我们可以选择调用已经预先编译的框架库(如 torch.add 和 numpy.add)并利用在 Python 中的实现。在实践中,元张量函数被例如 C 或 C++ 的低级语言所实现,并且在一些时候会包含一些汇编代码。

在这里插入图片描述
许多机器学习框架都提供机器学习模型的编译过程,以将元张量函数变换为更加专门的、针对特定工作和部署环境的函数。

在这里插入图片描述
上面这张图展示了一个元张量函数 add 的实现被变换至另一个不同实现的例子,其中在右侧的代码是一段表示可能的组合优化的伪代码:左侧代码中的循环被拆分出长度为 4 的单元,f32x4.add 对应的是一个特殊的执行向量加法计算的函数。


2.2 张量程序抽象

上一节谈到了对元张量函数变换的需要。为了让我们能够更有效地变换元张量函数,我们需要一个有效的抽象来表示这些函数。

通常来说,一个典型的元张量函数实现的抽象包含了一下成分:存储数据的多维数组驱动张量计算的循环嵌套以及计算部分本身的语句。

在这里插入图片描述
我们称这类抽象为 张量程序抽象。张量程序抽象的一个重要性质是,他们能够被一系列有效的程序变换所改变

在这里插入图片描述
例如,我们能够通过一组变换操作(如循环拆分、并行和向量化)将上图左侧的一个初始循环程序变换为右侧的程序。

2.2.1 张量程序抽象中的其它结构

重要的是,我们不能任意地对程序进行变换,比方说这可能是因为一些计算会依赖于循环之间的顺序。但幸运的是,我们所感兴趣的大多数元张量函数都具有良好的属性(例如循环迭代之间的独立性)。

张量程序可以将这些额外的信息合并为程序的一部分,以使程序变换更加便利。

在这里插入图片描述
举个例子,上面图中的程序包含额外的 T.axis.spatial 标注,表明 vi 这个特定的变量被映射到循环变量 i,并且所有的迭代都是独立的。这个信息对于执行这个程序而言并非必要,但会使得我们在变换这个程序时更加方便。在这个例子中,我们知道我们可以安全地并行或者重新排序所有与 vi 有关的循环,只要实际执行中 vi 的值按照从 0 到 128 的顺序变化。


2.3 张量程序变换实践

2.3.1 安装相关的包

为了本课程的目标,我们会使用 TVM (一个开源的机器学习编译框架)中一些正在持续开发的部分。我们提供了下面的命令用于为 MLC 课程安装一个包装好的版本。

python3 -m  pip install mlc-ai-nightly -f https://mlc.ai/wheels

2.3.2 构造张量程序

让我们首先构造一个执行两向量加法的张量程序。

import numpy as np
import tvm
from tvm.ir.module import IRModule
from tvm.script import tir as T
@tvm.script.ir_module
class MyModule:@T.prim_funcdef main(A: T.Buffer[128, "float32"],B: T.Buffer[128, "float32"],C: T.Buffer[128, "float32"]):# extra annotations for the functionT.func_attr({"global_symbol": "main", "tir.noalias": True})for i in range(128):with T.block("C"):# declare a data parallel iterator on spatial domainvi = T.axis.spatial(128, i)C[vi] = A[vi] + B[vi]

TVMScript 是一种让我们能以 Python 抽象语法树的形式来表示张量程序的方式。注意到这段代码并不实际对应一个 Python 程序,而是对应一个机器学习编译过程中的张量程序。TVMScript 的语言设计是为了与 Python 语法所对应,并在 Python 语法的基础上增加了能够帮助程序分析与变换的额外结构。

type(MyModule)
tvm.ir.module.IRModule

MyModule 是 IRModule 数据结构的一个实例,是一组张量函数的集合。

我们可以通过 script 函数得到这个 IRModule 的 TVMScript 表示。这个函数对于在一步步程序变换间检查 IRModule 而言非常有帮助。

print(MyModule.script())
@tvm.script.ir_module
class Module:@tir.prim_funcdef func(A: tir.Buffer[128, "float32"], B: tir.Buffer[128, "float32"], C: tir.Buffer[128, "float32"]) -> None:# function attr dicttir.func_attr({"global_symbol": "main", "tir.noalias": True})# body# with tir.block("root")for i in tir.serial(128):with tir.block("C"):vi = tir.axis.spatial(128, i)tir.reads(A[vi], B[vi])tir.writes(C[vi])C[vi] = A[vi] + B[vi]

2.3.3 编译与运行

在任何时刻,我们都可以通过 build 将一个 IRModule 转化为可以执行的函数。

rt_mod = tvm.build(MyModule, target="llvm")  # The module for CPU backends.
type(rt_mod)
tvm.driver.build_module.OperatorModule

在编译后,mod 包含了一组可以执行的函数。我们可以通过这些函数的名字拿到对应的函数。

func = rt_mod["main"]
func
<tvm.runtime.packed_func.PackedFunc at 0x7fd5ad30aa90>
a = tvm.nd.array(np.arange(128, dtype="float32"))
b = tvm.nd.array(np.ones(128, dtype="float32"))
c = tvm.nd.empty((128,), dtype="float32")

要执行这个函数,我们在 TVM runtime 中创建三个 NDArray,然后执行调用这个函数。

func(a, b, c)
print(a)
[  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  12.  13.14.  15.  16.  17.  18.  19.  20.  21.  22.  23.  24.  25.  26.  27.28.  29.  30.  31.  32.  33.  34.  35.  36.  37.  38.  39.  40.  41.42.  43.  44.  45.  46.  47.  48.  49.  50.  51.  52.  53.  54.  55.56.  57.  58.  59.  60.  61.  62.  63.  64.  65.  66.  67.  68.  69.70.  71.  72.  73.  74.  75.  76.  77.  78.  79.  80.  81.  82.  83.84.  85.  86.  87.  88.  89.  90.  91.  92.  93.  94.  95.  96.  97.98.  99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111.112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125.126. 127.]
print(b)
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1. 1. 1.]
print(c)
[  1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  12.  13.  14.15.  16.  17.  18.  19.  20.  21.  22.  23.  24.  25.  26.  27.  28.29.  30.  31.  32.  33.  34.  35.  36.  37.  38.  39.  40.  41.  42.43.  44.  45.  46.  47.  48.  49.  50.  51.  52.  53.  54.  55.  56.57.  58.  59.  60.  61.  62.  63.  64.  65.  66.  67.  68.  69.  70.71.  72.  73.  74.  75.  76.  77.  78.  79.  80.  81.  82.  83.  84.85.  86.  87.  88.  89.  90.  91.  92.  93.  94.  95.  96.  97.  98.99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112.113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126.127. 128.]

2.3.4 张量程序变换

现在我们开始变换张量程序。一个张量程序可以通过一个辅助的名为调度(schedule)的数据结构得到变换。

sch = tvm.tir.Schedule(MyModule)
type(sch)
tvm.tir.schedule.schedule.Schedule

我们首先尝试拆分循环。

# Get block by its name
block_c = sch.get_block("C")
# Get loops surrounding the block
(i,) = sch.get_loops(block_c)
# Tile the loop nesting.
i_0, i_1, i_2 = sch.split(i, factors=[None, 4, 4])
print(sch.mod.script())
@tvm.script.ir_module
class Module:@tir.prim_funcdef func(A: tir.Buffer[128, "float32"], B: tir.Buffer[128, "float32"], C: tir.Buffer[128, "float32"]) -> None:# function attr dicttir.func_attr({"global_symbol": "main", "tir.noalias": True})# body# with tir.block("root")for i_0, i_1, i_2 in tir.grid(8, 4, 4):with tir.block("C"):vi = tir.axis.spatial(128, i_0 * 16 + i_1 * 4 + i_2)tir.reads(A[vi], B[vi])tir.writes(C[vi])C[vi] = A[vi] + B[vi]

我们可以对这些循环重新排序。现在我们将 i_2 移动到 i_1 的外侧。

sch.reorder(i_0, i_2, i_1)
print(sch.mod.script())
@tvm.script.ir_module
class Module:@tir.prim_funcdef func(A: tir.Buffer[128, "float32"], B: tir.Buffer[128, "float32"], C: tir.Buffer[128, "float32"]) -> None:# function attr dicttir.func_attr({"global_symbol": "main", "tir.noalias": True})# body# with tir.block("root")for i_0, i_2, i_1 in tir.grid(8, 4, 4):with tir.block("C"):vi = tir.axis.spatial(128, i_0 * 16 + i_1 * 4 + i_2)tir.reads(A[vi], B[vi])tir.writes(C[vi])C[vi] = A[vi] + B[vi]

最后,我们可以标注我们想要并行最外层的循环。

sch.parallel(i_0)
print(sch.mod.script())
@tvm.script.ir_module
class Module:@tir.prim_funcdef func(A: tir.Buffer[128, "float32"], B: tir.Buffer[128, "float32"], C: tir.Buffer[128, "float32"]) -> None:# function attr dicttir.func_attr({"global_symbol": "main", "tir.noalias": True})# body# with tir.block("root")for i_0 in tir.parallel(8):for i_2, i_1 in tir.grid(4, 4):with tir.block("C"):vi = tir.axis.spatial(128, i_0 * 16 + i_1 * 4 + i_2)tir.reads(A[vi], B[vi])tir.writes(C[vi])C[vi] = A[vi] + B[vi]

我们能够编译并运行变换后的程序。

transformed_mod = tvm.build(sch.mod, target="llvm")  # The module for CPU backends.
transformed_mod["main"](a, b, c)
print(c)
[  1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  12.  13.  14.15.  16.  17.  18.  19.  20.  21.  22.  23.  24.  25.  26.  27.  28.29.  30.  31.  32.  33.  34.  35.  36.  37.  38.  39.  40.  41.  42.43.  44.  45.  46.  47.  48.  49.  50.  51.  52.  53.  54.  55.  56.57.  58.  59.  60.  61.  62.  63.  64.  65.  66.  67.  68.  69.  70.71.  72.  73.  74.  75.  76.  77.  78.  79.  80.  81.  82.  83.  84.85.  86.  87.  88.  89.  90.  91.  92.  93.  94.  95.  96.  97.  98.99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112.113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126.127. 128.]

2.3.5 通过张量表达式(Tensor Expression,TE)构造张量程序

在之前的例子中,我们直接使用 TVMScript 构造张量程序。在实际中,通过现有的定义方便地构造这些函数是很有帮助的。张量表达式(tensor expression)是一个帮助我们将一些可以通过表达式表示的张量计算转化为张量程序的 API。

# namespace for tensor expression utility
from tvm import te# declare the computation using the expression API
A = te.placeholder((128, ), name="A")
B = te.placeholder((128, ), name="B")
C = te.compute((128,), lambda i: A[i] + B[i], name="C")# create a function with the specified list of arguments.
func = te.create_prim_func([A, B, C])
# mark that the function name is main
func = func.with_attr("global_symbol", "main")
ir_mod_from_te = IRModule({"main": func})print(ir_mod_from_te.script())
@tvm.script.ir_module
class Module:@tir.prim_funcdef func(A: tir.Buffer[128, "float32"], B: tir.Buffer[128, "float32"], C: tir.Buffer[128, "float32"]) -> None:# function attr dicttir.func_attr({"global_symbol": "main", "tir.noalias": True})# body# with tir.block("root")for i0 in tir.serial(128):with tir.block("C"):i = tir.axis.spatial(128, i0)tir.reads(A[i], B[i])tir.writes(C[i])C[i] = A[i] + B[i]

2.3.6 变换一个矩阵乘法程序

在上面的例子中,我们展示了如何变换一个向量加法程序。现在我们尝试应用一些变换到一个稍微更复杂的的程序——矩阵乘法程序。我们首先使用张量表达式 API 构造初始的张量程序,并编译执行它。

from tvm import teM = 1024
K = 1024
N = 1024# The default tensor type in tvm
dtype = "float32"target = "llvm"
dev = tvm.device(target, 0)# Algorithm
k = te.reduce_axis((0, K), "k")
A = te.placeholder((M, K), name="A")
B = te.placeholder((K, N), name="B")
C = te.compute((M, N), lambda m, n: te.sum(A[m, k] * B[k, n], axis=k), name="C")# Default schedule
func = te.create_prim_func([A, B, C])
func = func.with_attr("global_symbol", "main")
ir_module = IRModule({"main": func})
print(ir_module.script())func = tvm.build(ir_module, target="llvm")  # The module for CPU backends.a = tvm.nd.array(np.random.rand(M, K).astype(dtype), dev)
b = tvm.nd.array(np.random.rand(K, N).astype(dtype), dev)
c = tvm.nd.array(np.zeros((M, N), dtype=dtype), dev)
func(a, b, c)evaluator = func.time_evaluator(func.entry_name, dev, number=1)
print("Baseline: %f" % evaluator(a, b, c).mean)
@tvm.script.ir_module
class Module:@tir.prim_funcdef func(A: tir.Buffer[(1024, 1024), "float32"], B: tir.Buffer[(1024, 1024), "float32"], C: tir.Buffer[(1024, 1024), "float32"]) -> None:# function attr dicttir.func_attr({"global_symbol": "main", "tir.noalias": True})# body# with tir.block("root")for i0, i1, i2 in tir.grid(1024, 1024, 1024):with tir.block("C"):m, n, k = tir.axis.remap("SSR", [i0, i1, i2])tir.reads(A[m, k], B[k, n])tir.writes(C[m, n])with tir.init():C[m, n] = tir.float32(0)C[m, n] = C[m, n] + A[m, k] * B[k, n]Baseline: 2.967772

我们可以变换张量程序中的循环,使得在新循环下内存访问的模式对缓存更加友好。我们尝试下面的调度。

sch = tvm.tir.Schedule(ir_module)
type(sch)
block_c = sch.get_block("C")
# Get loops surrounding the block
(y, x, k) = sch.get_loops(block_c)
block_size = 32
yo, yi = sch.split(y, [None, block_size])
xo, xi = sch.split(x, [None, block_size])sch.reorder(yo, xo, k, yi, xi)
print(sch.mod.script())func = tvm.build(sch.mod, target="llvm")  # The module for CPU backends.c = tvm.nd.array(np.zeros((M, N), dtype=dtype), dev)
func(a, b, c)evaluator = func.time_evaluator(func.entry_name, dev, number=1)
print("after transformation: %f" % evaluator(a, b, c).mean)
@tvm.script.ir_module
class Module:@tir.prim_funcdef func(A: tir.Buffer[(1024, 1024), "float32"], B: tir.Buffer[(1024, 1024), "float32"], C: tir.Buffer[(1024, 1024), "float32"]) -> None:# function attr dicttir.func_attr({"global_symbol": "main", "tir.noalias": True})# body# with tir.block("root")for i0_0, i1_0, i2, i0_1, i1_1 in tir.grid(32, 32, 1024, 32, 32):with tir.block("C"):m = tir.axis.spatial(1024, i0_0 * 32 + i0_1)n = tir.axis.spatial(1024, i1_0 * 32 + i1_1)k = tir.axis.reduce(1024, i2)tir.reads(A[m, k], B[k, n])tir.writes(C[m, n])with tir.init():C[m, n] = tir.float32(0)C[m, n] = C[m, n] + A[m, k] * B[k, n]after transformation: 0.296419

尝试改变 batch_size 的值,看看你能得到怎样的性能。在实际情况中,我们会利用一个自动化的系统在一个可能的变换空间中搜索找到最优的程序变换。


2.4 总结

  • 元张量函数表示机器学习模型计算中的单个单元计算。一个机器学习编译过程可以有选择地转换元张量函数的实现。

  • 张量程序是一个表示元张量函数的有效抽象。关键成分包括: 多维数组,循环嵌套,计算语句。程序变换可以被用于加速张量程序的执行。张量程序中额外的结构能够为程序变换提供更多的信息。

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

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

相关文章

迁移TFS 2012服务至新的电脑硬件

迁移TFS 2012的时候碰到一些问题, 中文记录很少, 英文的记录也比较零散. 这里记录最直接和简单的方法. 环境: 1. 公司域环境, 所有TFS用户都是公司域帐户. 2. TFS从一台服务器转移至另一台服务器. 都加入了公司域. 机器名分别为PC1和PC2. 域内不能有同名的电脑。 准备两台…

【PAT - 甲级1005】Spell It Right (20分) (递归输出,水题)

题干&#xff1a; Given a non-negative integer N, your task is to compute the sum of all the digits of N, and output every digit of the sum in English. Input Specification: Each input file contains one test case. Each case occupies one line which contain…

详解自动驾驶仿真数据集 SHIFT:A Synthetic Driving Dataset for Continuous Multi-Task Domain Adaptation

SHIFT&#xff1a;A Synthetic Driving Dataset for Continuous Multi-Task Domain Adaptation本文介绍一个新的自动驾驶仿真数据集&#xff1a;SHIFT&#xff0c;论文收录于 CVPR2022。适应连续变化的环境是自动驾驶系统一直以来要面临的挑战。然而&#xff0c;目前现有的图像…

TFS下的源代码控制

以下主要描述了&#xff1a; TFS源代码控制系统的基本场景如何把一个项目添加到源代码管理中如何与服务器同步如何做Check-In如何做分支与合并什么是上架与下架 我们知道工作项是项目管理的基本元素&#xff0c;但是一个项目的成功&#xff0c;光有工作项还是不够的。工作项说…

【PAT - 甲级1004】Counting Leaves (30分) (dfs,递归)

题干&#xff1a; A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child. Input Specification: Each input file contains one test case. Each case starts with a line containing 0<N<100,…

地平线:面向规模化量产的智能驾驶系统和软件开发

导读 7月27日&#xff0c;地平线在智东西公开课开设的「地平线自动驾驶技术专场」第3讲顺利完结&#xff0c;地平线智能驾驶应用软件部负责人宋巍围绕 《面向规模化量产的智能驾驶系统和软件开发》这一主题进行了直播讲解。本次分享主要分为以下4个部分&#xff1a; 1、智能驾驶…

【转】TFS上分支和标签的用法

什么时候用分支: 例如为某个客户定制的专用版本,和主干的特性有很大差别.不具通用性的需求. 大的版本修改,例如2.0 到3.0 加了很多特性,但2.0 还有维护.改bug 什么时候用标签: 小版本的发布, 如2.1.1到2.1.2. 分支的优点: 清晰,容易操作,程序员只要get latest/checkin latest就…

【PAT - 甲级1034】Head of a Gang (30分)(并查集)

题干&#xff1a; One way that the police finds the head of a gang is to check peoples phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone c…

重读经典(CLIP上):《Learning Transferable Visual Models From Natural Language Supervision》

CLIP 论文逐段精读【论文精读】这一次朱毅博士给大家精读的论文是 CLIP&#xff0c;来自于 OpenAI&#xff0c;是图像文本多模态领域一个里程碑式的工作。 CLIP 的影响力可见一斑&#xff0c;如果按照沐神之前讲的如何判断一个工作的价值来说&#xff0c;CLIP 应该就是 1001001…

TFS准备(一)

一、TFS概念&#xff1a; TFS全称Team FoundationServer&#xff0c;是应用程序生命周期管理的服务端&#xff0c;功能包括如图功能&#xff1a;源代码管理&#xff0c;版本控制&#xff0c;团队开发协作&#xff0c;统一集成&#xff0c;测试管理等。 二、TFS安装要求&#…

【PAT - 甲级1094】The Largest Generation (25分)(dfs建树)

题干&#xff1a; A family hierarchy is usually presented by a pedigree tree where all the nodes on the same level belong to the same generation. Your task is to find the generation with the largest population. Input Specification: Each input file contai…

重读经典(CLIP下):《Learning Transferable Visual Models From Natural Language Supervision》

上文链接&#xff1a;重读经典&#xff08;CLIP上&#xff09;&#xff1a;《Learning Transferable Visual Models From Natural Language Supervision》 5. 实验 现在我们已经知道 CLIP 是如何进行预训练的以及作者为什么选用对比学习来预训练 CLIP&#xff0c;接下来我们就…

TFS创建团队项目(三)

打开Visual Studio 2013&#xff0c;视图-团队资源管理器-连接图标&#xff08;插头图标&#xff09; 当前是没有TFS服务器&#xff0c;点击服务器按钮 添加&#xff0c;并在URL地址栏里输入装有TFS的服务器IP地址&#xff08;配置完TFS后有这个URL&#xff1a;http://tfs-serv…

【PAT - 甲级1007】Maximum Subsequence Sum (25分)(前缀和)

题干&#xff1a; Given a sequence of K integers { N​1​​, N​2​​, ..., N​K​​ }. A continuous subsequence is defined to be { N​i​​, N​i1​​, ..., N​j​​ } where 1≤i≤j≤K. The Maximum Subsequence is the continuous subsequence which has the l…

详解4D毫米波雷达数据集(VOD)Multi-class Road User Detection with 3+1D Radar in the View-of-Delft Dataset

Multi-class Road User Detection with 31D Radar in the View-of-Delft Dataset本文介绍一个新的自动驾驶数据集&#xff1a;VOD&#xff0c;论文收录于 ICRA2022。下一代毫米波雷达除了提供距离、方位和多普勒速度外&#xff0c;还会提供高度信息。 在本文中&#xff0c;作者…

自动驾驶之心:毫米波雷达-视觉融合感知方法(前融合/特征级融合/数据级融合)

毫米波雷达-视觉融合感知方法&#xff08;前融合/特征级融合/数据级融合&#xff09;分享一个自动驾驶之心的报告&#xff1a;毫米波雷达与视觉融合目标检测。 作者主页为&#xff1a;https://www.zhihu.com/people/nacayu 文章目录1. 毫米波雷达与相机融合检测背景2. 主流融合…

【PAT - 甲级1095】Cars on Campus (30分)(模拟)

题干&#xff1a; Zhejiang University has 8 campuses and a lot of gates. From each gate we can collect the in/out times and the plate numbers of the cars crossing the gate. Now with all the information available, you are supposed to tell, at any specific t…

TFS中的迭代(五)

从团队资源管理器中打开迭代选项。 TFS在新建完团队项目后会自动为本团队项目新建迭代子项&#xff0c;包含发布和冲刺。第一级为团队项目TestProject&#xff0c;第二层为发布&#xff0c;第三层为冲刺&#xff0c;这样的层次一共可以建14层。 这些选项可以编辑&#xff0c;添…

2022百度ApolloDay技术开放日:文心大模型在自动驾驶感知中的落地应用

2数据处理大模型技术是自动驾驶行业近年的热议趋势&#xff0c;但能否落地应用、能否用好是关键难题。百度自动驾驶依托文心大模型特色优势&#xff0c;率先实现技术应用突破。百度自动驾驶技术专家王井东表示&#xff1a;文心大模型-图文弱监督预训练模型&#xff0c;背靠文心…

【PAT - 甲级1155】Heap Paths (30分)(栈,dfs,二叉树)

题干&#xff1a; In computer science, a heap is a specialized tree-based data structure that satisfies the heap property: if P is a parent node of C, then the key (the value) of P is either greater than or equal to (in a max heap) or less than or equal to…