机器学习编译第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,一经查实,立即删除!

相关文章

详解自动驾驶仿真数据集 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;光有工作项还是不够的。工作项说…

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

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

重读经典(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安装要求&#…

重读经典(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…

详解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. 主流融合…

TFS中的迭代(五)

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

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

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

TFS中的工作项(六)

TFS中的工作项 在VS中打开团队资源管理器 工作项有功能&#xff0c;产品积压工作项&#xff0c;任务&#xff0c;测试用例&#xff0c;Bug&#xff0c;障碍&#xff0c;它们之间的关系是&#xff1a;一个产品或应用&#xff0c;有一系列功能&#xff0c;功能最大&#xff0c;可…

单目3D物体检测模型(CaDDN): Categorical Depth Distribution Network for Monocular 3D Object Detection

本文介绍一篇单目3D物体检测模型&#xff1a;CaDDN&#xff0c;论文收录于 CVPR2021。 单目3D物体检测的主要挑战在于准确预测物体深度&#xff0c;由于缺乏直接的距离测量&#xff0c;因此必须根据物体和场景线索来推断物体深度。过去许多方法试图直接估计深度来辅助3D检测&am…

抠图、换背景、正装图证件照制作方法

本篇灵感是最近又要使用别的底色的正装照的图片。上学的时候&#xff0c;要求证件照的底色是蓝底、党员档案里要求图片的底色是红底、 将来上班的证件照要求是白底&#xff0c;并且无论是考研还是找工作都是制作简历的时候&#xff0c;根据简历的样板不同需要更换不同的底色。 …

TFS的Web门户工作项(七)

在TFS的web门户中有更丰富和更全面的设置和数据展示。 Web门户如图有A&#xff0c;B&#xff0c;C&#xff0c;D四部分组成。A部分为团队项目和项目切换&#xff0c;登录人员和登录人员切换。B部分是Web门户主要功能的菜单&#xff0c;C部分是当前团队项的一些信息的展示&#…

详解多视角3D检测模型 BEVDepth: Acquisition of Reliable Depth for Multi-view 3D Object Detection

本文介绍一个多视角的3D检测模型&#xff1a;BEVDepth&#xff0c;论文收录于 AAAI2023。在这篇文章中&#xff0c;作者提出了一种新的具有可信深度估计的三维物体检测器。本文提出的BEVDepth通过利用激光雷达显式深度监督来提高图像深度估计的可信度。作者引入了摄像机感知深度…

TFS版本管理(八)

目前TFS支持两种版本管理方式&#xff0c;一种是自带的TFS版本控制&#xff0c;一种是Git&#xff0c;后一种是分布式&#xff0c;并且分支功能相对强大&#xff0c;关于Git可以找更详细的资源学习。 如果没有创建项&#xff0c;在web门户中代码下的资源管理器是空的。 团队资…

Python开发常用工具库

本文汇总了在使用Python开发时常用到的第三方库和工具。 1.Python 官网地址&#xff1a;https://www.python.org/doc/ 博文地址&#xff1a;《Python编程&#xff1a;从入门到实践》速查表 2.OpenCV&#xff08;图像处理&#xff09; 官网地址&#xff1a;https://opencv.o…

TFS中的统一集成(九)

首先要在TFS服务器上配置生成配置&#xff0c;操作如下&#xff1a; 现在可以创建一个生成定义了&#xff0c;单击团队资源管理器的生成。 新建生成定义&#xff1a; 这里选择的是手动。触发这个生成有很多条件&#xff0c;我们选择手动主式。 在要服务器上共享一个文件夹&…

常用工具整理:数学,论文,代码等

1. Mathpix&#xff08;图片转数学公式工具&#xff09; 官网地址&#xff1a;Mathpix Snip在线LaTeX公式编辑&#xff1a;在线LaTeX公式编辑器-编辑器 (latexlive.com)推荐另一个开源的工具&#xff1a;Image to LaTex Converter&#xff0c;官网地址&#xff1a;GitHub - ki…