欢迎光临散文网 会员登陆 & 注册

首个基于Transformer的分割检测+视觉大模型视频课程(23年新课+源码+课件)

2023-11-27 11:00 作者:bili_48219728313  | 我要投稿

学习资料地址1:https://pan.baidu.com/s/1mpYHRFi68lzNuA8neYI15w 提取码:pwjd 

学习资料地址2:https://share.weiyun.com/tnVNHGMD 密码:3fj7iy


自动驾驶是高安全型应用,需要高性能和高可靠的深度学习模型,Vision Transformer是理想的选摔。现在主流的自动驾驶感知算法基本都使用了Vision Transformer相关技术,比如分割、2D/3D检测,以及最近大火的大模型 (如SAM),Vision Transformer在自动驾驶领域的落地方面遍地开花。5一方面,在自动驾驶或图像处理相关算法岗位的面试题中,Vision Transformer是必考题,需要对其理论知识有深入理解,并且在项目中真实的使用过相关技术。


Transformer出自于Google于2017年发表的论文《Attention is all you need》,最开始是用于机器翻译,并且取得了非常好的效果。但是自提出以来,Transformer不仅仅在NLP领域大放异彩,并且在CV、RS等领域也取得了非常不错的表现。尤其是2020年,绝对称得上是Transformer的元年,比如在CV领域,基于Transformer的模型横扫各大榜单,完爆基于CNN的模型。为什么Transformer模型表现如此优异?它的原理是什么?它成功的关键又包含哪些?本文将简要地回答一下这些问题。


我们知道Transformer模型最初是用于机器翻译的,机器翻译应用的输入是某种语言的一个句子,输出是另外一种语言的句子。

var i *int = nil

fmt.Println("i.size:", unsafe.Sizeof(i)) //8


var i8 *int8 = nil

fmt.Println("i8.size:", unsafe.Sizeof(i8)) //8


var s *string = nil

fmt.Println("s.size:", unsafe.Sizeof(s)) //8


var ps *struct{} = nil

fmt.Println("ps.size:", unsafe.Sizeof(ps)) //8


var si []int = nil

var si1 []int = nil

fmt.Println("si.size:", unsafe.Sizeof(si)) //24


var ii interface{} = nil

fmt.Println("ii.size:", unsafe.Sizeof(ii)) //16

我们以生成我,爱,机器,学习,翻译成<bos>,i,love,machine,learning,<eos>这个例子做生成过程来解释。

训练:


把“我/爱/机器/学习”embedding后输入到encoder里去,最后一层的encoder最终输出的outputs [10, 512](假设我们采用的embedding长度为512,而且batch size = 1),此outputs 乘以新的参数矩阵,可以作为decoder里每一层用到的K和V;

将<bos>作为decoder的初始输入,将decoder的最大概率输出词向量A1和‘i’做cross entropy(交叉熵)计算error。

将<bos>,“i” 作为decoder的输入,将decoder的最大概率输出词 A2 和‘love’做cross entropy计算error。

将<bos>,“i”,“love” 作为decoder的输入,将decoder的最大概率输出词A3和’machine’ 做cross entropy计算error。

将<bos>,“i”,"love ",“machine” 作为decoder的输入,将decoder最大概率输出词A4和‘learning’做cross entropy计算error。

将<bos>,“i”,"love ",“machine”,“learning” 作为decoder的输入,将decoder最大概率输出词A5和终止符做cross entropy计算error。

那么并行的时候是怎么做的呢,我们会有一个mask矩阵在这叫seq mask,因为他起到的作用是在decoder编码我们的target seq的时候对每一个词的生成遮盖它之后的词的信息。

func main() {

s := []string{"a", "b", "c"}

fmt.Println("s:origin", s)

changes1(s)

fmt.Println("s:f1", s)


changes2(s)

fmt.Println("s:f2", s)


changes3(s)

fmt.Println("s:f3", s)

}


func changes1(s []string) {

var tmp = []string{"x", "y", "z"}

s = tmp

}


func changes2(s []string) {

// item只是一个副本,不能改变s中元素的值

for i, item := range s {

item = "d"

fmt.Printf("item=%s;s[%d]=%s", item, i, s[i])

}

}


func changes3(s []string) {

for i := range s {

s[i] = "d"

}

}


首先我们需要为每个输入向量(也就是词向量)创建3个向量,分别叫做Query、Key、Value。那么如何创建呢?我们可以对输入词向量分别乘上3个矩阵来得到Q、K、V向量,这3个矩阵的参数在训练的过程是可以训练的。注意Q、K、V向量的维度是一样的,但是它们的维度可以比输入词向量小一点,比如设置成64,其实这步也不是必要的,这样设置主要是为了与后面的Mulit-head注意力机制保持一致(当使用8头注意力时,单头所处理的词向量维度为512/8=64,此时Q、K、V向量与输入词向量就一致了)。我们假设输入序列为英文的"Thinking Machines"

想要深度理解Attention机制,就需要了解一下它产生的背景、在哪类问题下产生,以及最初是为了解决什么问题而产生。


首先回顾一下机器翻译领域的模型演进历史:


机器翻译是从RNN开始跨入神经网络机器翻译时代的,几个比较重要的阶段分别是: Simple RNN, Contextualize RNN,Contextualized RNN with attention, Transformer(2017),下面来一一介绍。


「Simple RNN」 :这个encoder-decoder模型结构中,encoder将整个源端序列(不论长度)压缩成一个向量(encoder output),源端信息和decoder之间唯一的联系只是: encoder output会作为decoder的initial states的输入。这样带来一个显而易见的问题就是,随着decoder长度的增加,encoder output的信息会衰减。

func main(){

var c = make(chan int)

fmt.Printf("c.pointer=%p\n", c) //c.pointer=0xc000022180

go func() {

c <- 1

addChannel(c)

close(c)

}()


for item := range c {

//item: 1

//item: 2

fmt.Println("item:", item)

}

}


func addChannel(done chan int) {

done <- 2

fmt.Printf("done.pointer=%p\n", done) //done.pointer=0xc000022180

}

在测试模型的时候,Test:decoder没有label,采用自回归一个词一个词的输出,要翻译的中文正常从encoder并行输入(和训练的时候一样)得到每个单词的embedding,然后decoder第一次先输入bos再此表中的id,得到翻译的第一个单词,然后自回归,如此循环直到预测达到eos停止标记

type visit struct {

a1  unsafe.Pointer

a2  unsafe.Pointer

typ Type

}


func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {

if !v1.IsValid() || !v2.IsValid() {

return v1.IsValid() == v2.IsValid()

}

if v1.Type() != v2.Type() {

return false

}


// We want to avoid putting more in the visited map than we need to.

// For any possible reference cycle that might be encountered,

// hard(v1, v2) needs to return true for at least one of the types in the cycle,

// and it's safe and valid to get Value's internal pointer.

hard := func(v1, v2 Value) bool {

switch v1.Kind() {

case Pointer:

if v1.typ.ptrdata == 0 {

// not-in-heap pointers can't be cyclic.

// At least, all of our current uses of runtime/internal/sys.NotInHeap

// have that property. The runtime ones aren't cyclic (and we don't use

// DeepEqual on them anyway), and the cgo-generated ones are

// all empty structs.

return false

}

fallthrough

case Map, Slice, Interface:

// Nil pointers cannot be cyclic. Avoid putting them in the visited map.

return !v1.IsNil() && !v2.IsNil()

}

return false

}


if hard(v1, v2) {

// For a Pointer or Map value, we need to check flagIndir,

// which we do by calling the pointer method.

// For Slice or Interface, flagIndir is always set,

// and using v.ptr suffices.

ptrval := func(v Value) unsafe.Pointer {

switch v.Kind() {

case Pointer, Map:

return v.pointer()

default:

return v.ptr

}

}

addr1 := ptrval(v1)

addr2 := ptrval(v2)

if uintptr(addr1) > uintptr(addr2) {

// Canonicalize order to reduce number of entries in visited.

// Assumes non-moving garbage collector.

addr1, addr2 = addr2, addr1

}


// Short circuit if references are already seen.

typ := v1.Type()

v := visit{addr1, addr2, typ}

if visited[v] {

return true

}


// Remember for later.

visited[v] = true

}


switch v1.Kind() {

case Array:

for i := 0; i < v1.Len(); i++ {

if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {

return false

}

}

return true

case Slice:

if v1.IsNil() != v2.IsNil() {

return false

}

if v1.Len() != v2.Len() {

return false

}

if v1.UnsafePointer() == v2.UnsafePointer() {

return true

}

// Special case for []byte, which is common.

if v1.Type().Elem().Kind() == Uint8 {

return bytealg.Equal(v1.Bytes(), v2.Bytes())

}

for i := 0; i < v1.Len(); i++ {

if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {

return false

}

}

return true

case Interface:

if v1.IsNil() || v2.IsNil() {

return v1.IsNil() == v2.IsNil()

}

return deepValueEqual(v1.Elem(), v2.Elem(), visited)

case Pointer:

if v1.UnsafePointer() == v2.UnsafePointer() {

return true

}

return deepValueEqual(v1.Elem(), v2.Elem(), visited)

case Struct:

for i, n := 0, v1.NumField(); i < n; i++ {

if !deepValueEqual(v1.Field(i), v2.Field(i), visited) {

return false

}

}

return true

case Map:

if v1.IsNil() != v2.IsNil() {

return false

}

if v1.Len() != v2.Len() {

return false

}

if v1.UnsafePointer() == v2.UnsafePointer() {

return true

}

for _, k := range v1.MapKeys() {

val1 := v1.MapIndex(k)

val2 := v2.MapIndex(k)

if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) {

return false

}

}

return true

case Func:

if v1.IsNil() && v2.IsNil() {

return true

}

// Can't do better than this:

return false

case Int, Int8, Int16, Int32, Int64:

return v1.Int() == v2.Int()

case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:

return v1.Uint() == v2.Uint()

case String:

return v1.String() == v2.String()

case Bool:

return v1.Bool() == v2.Bool()

case Float32, Float64:

return v1.Float() == v2.Float()

case Complex64, Complex128:

return v1.Complex() == v2.Complex()

default:

// Normal equality suffices

return valueInterface(v1, false) == valueInterface(v2, false)

}

}

这便是encoder的整体计算流程图了,Transformer模型中堆叠了多个这样的encoder,无非就是输出连接输入罢了,常规操作。

最后再附上一个Transformer的代码实现,读者有兴趣可以跟着自己复现一下Transformer模型的代码。

   package main


   import (

       "log"

       "sync"

   )


   func init() {

       log.SetFlags(log.Lshortfile)

   }

   func main() {

       lock := sync.Mutex{}


       //Go 1.18 新增,是一种非阻塞模式的取锁操作。当调用 TryLock() 时,

       //该函数仅简单地返回 true 或者 false,代表是否加锁成功

       //在某些情况下,如果我们希望在获取锁失败时,并不想停止执行,

       //而是可以进入其他的逻辑就可以使用TryLock()

       log.Println("TryLock:", lock.TryLock())

       //已经通过TryLock()加锁,不能再次加锁

       lock.Lock()


   }


首个基于Transformer的分割检测+视觉大模型视频课程(23年新课+源码+课件)的评论 (共 条)

分享到微博请遵守国家法律