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

uyandmiss · · 564 次点击 · 开始浏览    置顶

学习资料地址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() }

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

564 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传