0%

简介

  • 设计模式:可复用面向对象软件的基础 阅读笔记

第一章 引言

  • 设计面向对象软件比较困难,而设计可复用的面向对象软件就更加困难。你必须找到相关的对象,以适当的粒度将它们归类,再定义类的接口和继承层次,建立对象之间的基本关系

什么是设计模式

  • 一般而言,一个模式有四个基本要素:

    • 模式名称(pattern name),一个助记名,它用一两个词来描述模式的问题,解决方案和效果。
    • 问题(problem),描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题,例如怎样用对象表示算法等。也可能描述了导致了不灵活设计的类或对象结构。
    • 解决方案(solution),描述了设计的组成成分,它们之间的相互关系以及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题
    • 效果(consequences),描述了模式应用的效果以及使用模式应权衡的问题。尽管我们描述设计决策时,并不总提到模式效果,但它们对于评价设计选择和理解使用模式的代价和好处具有重要意义。软件效果大多关注对时间和空间的衡量,它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一,所以模式效果包括它对系统的灵活性,扩充性或者可移植性的映像。
  • 本书中的设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述

组织编目

  • 根据两条准则对模式进行分类。

  • 第一是目的准则,即模式是用来完成什么工作的。

    • 模式依据其目的可分为创建型(Creational),结构型(Structural),或行为型(Behavioral)三种。
      • 创建型模式与对象的创建有关;
      • 结构型模式处理类或对象的组合;
      • 行为型模式对类或对象怎样交互和怎样分配职责进行描述
  • 第二是范围准则,指定模式主要用作于类还是用作于对象。

    • 类模式处理类和子类之间的关系,这些关系通过继承建立,是静态的,在编译时刻便确定下来了。
    • 对象模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具有动态性。
    • 从某种意义上来说,几乎所有模式都使用继承机制,所以 类模式 只指那些集中于处理类间关系的模式,而大部分模式都属于对象模式的范畴

设计模式怎样解决设计问题

  • 设计模式采用多种方法解决面向对象设计者经常碰到的问题

寻找合适的对象

  • 面向对象程序由对象组成,对象包括数据和对数据进行操作的过程,过程通常称为方法或者操作。对象在收到客户的请求(或消息)后,执行相应的操作

  • 客户请求是使对象执行操作的唯一方法,操作又是对象改变内部数据的唯一方法。由于这些限制,对象的内部状态是被封装的,他不能被直接访问,它的表示对于对象外部是不可见的

  • 面向对象设计最困难的部分是将系统分解成对象集合。因为要考虑许多因素:封装,粒度,依赖关系,灵活性,性能,演化,复用等等,它们都影响着系统的分解,并且这些因素通常还是互相冲突的

  • 设计模式帮你确定并不明显的抽象和描述这些抽象的对象

决定对象的粒度

  • 对象在大小和数目上变化极大。它们能表示下自硬件或者上自整个应用的任何事物

指定对象接口

  • 对象声明的每一个操作指定操作名,作为参数的对象和返回值,这就是所谓的操作的型构(signature)。

  • 对象操作所定义的所有操作型构的集合被称为该对象的接口(interface)。

  • 对象接口描述了该对象所能接受的全部请求的集合,任何匹配对象接口中型构的请求都可以发送给该对象

  • 类型(type)是用来标识特定接口的一个名字。

  • 接口可以包含其他接口作为子集。当一个类型的接口包含另一个类型的接口时,我们就说它是另一个类型的子类型(subtype),另一个类型称之为它的超类型(supertype)。

  • 我们常说子类型继承了它的超类型的接口

  • 当给对象发送请求时,所引起的具体操作即与请求本身有关又与接收对象有关。支持相同请求的不同对象可能对请求激发的操作有不同的实现。发送给对象的请求和它的相应操作在运行时刻的连接就称之为动态绑定(dynamice binding)

  • 动态绑定是指发送的请求直到运行时刻才受到你的具体的实现的约束。

  • 进一步将,动态绑定允许你在运行时刻彼此替换有相同接口的对象。这种可替换性就称为多态(polymorphism),它是面向对象系统中的核心概念之一

描述对象的实现

  • 对象的实现是由它的类决定的,类指定了对象的内部数据和表示,也定义了对象所能够完成的操作。

  • 对象通过实例化类来创建,此时对象被称为该类的实例。当实例化类时,要给对象的内部数据(由实例变量组成)分配存储空间,并将操作与这些数据联系起来。

  • 新的类可以由已存在的类通过类继承(class inheritance)来定义。当子类(subclass)继承父类(parent class)时,子类包含了父类定义的所有数据和操作

  • 抽象类(abstrace class)的主要目的是为它的子类定义公共接口。一个抽象类将把它的部分或者全部操作的实现延迟到子类中,因此,一个抽象类不能被实例化。在抽象类中定义却没有实现的操作被称为抽象操作(abstract operation)。

  • 非抽象类称为具体类

  • 子类能够改进和重新定义它们的父类的操作。更具体的说,类能够重定义(override)父类定义的操作,重定义使得子类能够接管父类对请求的处理操作。

  • 混入类(mixin class)是给其他类提供可选择的接口或者功能的类。它与抽象类一样不能实例化。混入类要求多继承

类继承与接口继承的比较
  • 理解对象的类(class)与对象的类型(type)之间的差别非常重要

  • 一个对象的类定义了对象是怎样实现的,同时也定义了对象的内部状态和操作的实现。

  • 但是对象的类型只与它的接口有关,接口即对象能够响应的请求的集合。

  • 一个对象可以有多个类型,不同类的对象可以有相同的类型。

  • 理解类继承和接口继承(或子类型化)之间的差别也十分重要。

  • 类继承根据一个对象的实现定义了另一个对象的实现。简而言之,它是代码和表示的共享机制。

  • 然而,接口继承(或子类型化)描述了一个对象什么时候能被用来替代另一个对象。

  • 因为许多语言并不显式地区分这两个概念,所以容易被混淆。在C++ 和 Eiffel语言中,继承既指接口的继承又指实现的继承。

  • C++中接口继承的标准方法是公有继承一个含(纯)虚成员函数的类。

  • C++中纯接口继承接近于公有继承纯抽象类,纯实现继承或纯类继承接近于私有继承

对接口编程,而不是对实现编程
  • 类继承是一个通过复用父类功能而扩展应用功能的基本机制。

  • 然而,实现的复用只是成功的一半,继承所拥有的定义具有相同接口的对象族的能力也是很重要的(通常可以从抽象类来继承)。

  • 为什么?因为多态依赖于这种能力

  • 当继承被恰当使用时,所有从抽象类导出的类将共享该抽象类的接口。这意味着子类仅仅添加或重定义操作,而没有隐藏父类的操作。

  • 这时,所有的子类都能响应抽象类接口中的请求,从而子类的类型都是抽象类的子类型。

  • 只根据抽象类中定义的接口来操作对象有以下两个好处:

    • 客户无须知道它们使用对象的特定类型,只须对象有客户所期望的接口
    • 客户无须知道它们使用的对象是用什么类来实现的,它们只须知道定义接口的抽象类。
  • 这将极大地减少子系统实现之间的相互依赖关系,也产生了可复用的面向对象设计的如下原则:

    • 针对接口编程,而不是针对实现编程。
  • 不将变量声明为某个特定的具体类的实例对象,而是让它遵循从抽象类所定义的接口。这是本书设计模式的一个常见主题。

运用复用机制
  • 面向对象系统中功能复用的两种最常用技术是类继承和对象组合(object composition)

  • 类继承允许你根据其他类的实现来定义一个类的实现。这种通过生成子类的复用通常被称为白箱复用(white-box reuse)。

  • 术语 白箱 是相对可视性而言:在继承方式中,父类的内部细节对子类可见

  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。

  • 对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以 黑箱 的形式出现

  • 类继承的优点:

    • 在编译时刻静态定义的,且可直接使用,因为程序设计语言直接支持类继承。
    • 较方便地改变被复用的实现。
    • 当一个子类重定义一些而不是全部操作时,他也能影响他所继承的操作,只要在这些操作中调用了被重定义的操作。
  • 类继承的缺点:

    • 因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现
    • 父类通常至少定义了部分子类的具体表示
    • 因为继承对子类揭示了其父类的实现细节,所以继承常被认为破坏了封装性
    • 子类中的实现与他的父类有紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类的变化
  • 当你需要复用子类时,实现上的依赖性就会产生一些问题。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

  • 一个可用的解决方法就是只继承抽象类,因为抽象类通常提供较少的实现

  • 对象组合是通过获得对其他对象的引用而在运行时刻动态定义的。

  • 组合要求对象遵守彼此的接口约定,进而要求更仔细的定义接口,而这些接口并不妨碍你将一个对象和其他对象一起使用。

  • 这会产生良好的结果:

    • 因为对象只能通过接口访问,所以我们并不破坏封装性
    • 只要类型一致,运行时刻还可以用一个对象来替代另一个对象
    • 更进一步,因为对象的实现是基于接口写的,所以实现上存在较少的依赖关系
  • 对象组合对系统设计还有另一个作用,既优先使用对象组合有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

  • 另一方面,基于对象组合的设计会有更多的对象(而有较少的类),且系统的行为将依赖于对象间的关系而不是被定义在某个类中

  • 这导出了我们的面向对象设计的第二个原则:

    • 优先使用对象组合,而不是类继承。
  • 理想情况下,你不应该为获得复用而去创建新的构件。你应该能够只使用对象组合技术,通过组装已有的构建就能获得你需要的功能。

  • 但是事实很少如此,因为可用构建的集合实际上并不足够丰富。使用继承的复用使得创建新的构建要比组装旧的构建来的容易。

  • 这样,继承和对象组合常一起使用。

  • 委托(delegation)是一种组合方法,他使组合具有与继承同样的复用能力。在委托方式下,有两个对象参与处理一个请求,接受请求的对象将操作委托给他的代理者(delegate)

  • 这类似于子类将请求交给他的父类处理。使用继承时,被继承的操作总能引用接受请求的对象,C++中通过this成员变量

  • 委托是对象组合的特例。它告诉你对象组合作为一个代码复用机制可以代替继承

  • 另一种功能复用技术(并非严格的面向对象技术)是参数化类型(parameterized type),也就是类属(generic)或者模板(templates (C++))。他允许你在定义一个类型时并不指定该类型所用到的其他所有类型。未经指定的类型在使用时以参数形式提供。

  • 参数化类型给我们提供了除了类继承和对象组合外的第三种方法来组合面向对象系统中的行为。许多设计可以使用这三种技术中的任何一种来实现。

  • 实现一个以元素比较操作为可变元的排序例程,可有如下方法:

    • 通过子类实现该操作
    • 实现为传给排序例程的对象的职责
    • 作为C++模板或Ada类属的参数,以指定元素比较操作的名称

关联运行时刻和编译时刻的结构

  • 一个面向对象程序运行时刻的结构通常与他的代码结构相差很大。

  • 代码结构在编译时刻就被确定下来了,他由继承关系固定的类组成。而程序的运行时刻结构是由快速变化的通信对象网格组成。

  • 事实上,两个结构是彼此独立的,试图由一个区理解另一个就好像试图从静态的动植物分类去理解活生生的生态系统的动态性。反之亦然。

  • 聚合(aggregation),意味着一个对象拥有另一个对象或对另一个对象负责。

  • 一般我们称一个对象包含另一个对象或者另一个对象的一部分。聚合意味着聚合对象和其所有者具有相同的生命周期

设计应支持变化

  • 一些导致重新设计的一般原因,以及解决这些问题的设计模式:

    • 通过显示的指定一个类来创建对象 : 在创建对象时指定类名将使你受特定实现的约束而不是特定接口的约束。这会使未来的变化更复杂。要避免这种情况,应该间接地创建对象
      • 设计模式: Abstract Factory, Factory Method, Prototype
    • 对特殊操作的依赖 : 当你为请求指定一个特殊的操作时,完成该请求的方式就固定下来了。为避免把请求代码写死,你将可以在编译时刻或运行时刻很方便地改变响应请求的方式
      • 设计模式: Chain of Resposibility, Command
    • 对硬件和软件平台的依赖 : 外部的操作系统接口和应用编成接口(API)在不同的软硬件平台上是不同的。依赖于特定平台的软件将很难移植到其他平台上,甚至都很难跟上本地平台的更新。所以设计系统时限制其平台相关性就很重要了。
      • 设计模式: Abstract Factory, Bridge
    • 对对象表示或实现的依赖 : 知道对象怎样表示,保存,定位或实现的客户在对象发生变化时可能也需要变化。对客户隐藏这些信息能够阻止连锁变化
      • 设计模式: Abstract Factory, Bridge, Memento, Proxy
    • 算法依赖 : 算法在开发和复用时常常被扩展,优化和替代。依赖于某个特定算法的对象在算法发生变化时不得不变化。因此有可能发生变化的算法应该被孤立起来。
      • 设计模式: Builder, Iterator, Strategy, Template Method, Visitor
    • 紧耦合 : 紧耦合的类很难孤立的被复用,因为它们是互相依赖的。紧耦合产生单块的系统,要改变或者删掉一个类,你必须理解和改变其他许多类。这样的系统是一个很难学习移植和维护的密集体。松散耦合提高了一个类本身被复用的可能性,并且系统更易于学习,移植,修改和扩展。设计模式使用抽象耦合和分层技术来提高系统的松散耦合性。
      • 设计模式: Abstract Factory, Command, Facade, Mediator, Observer, Chain of Responsibility
    • 通过生成子类来扩充功能 : 通常很难通过定义子类来定制对象。每一个新类都有固定的实现开销(初始化,终止处理等)。子类方法会导致类爆炸,因为即使对于一个简单的扩充,你也不得不引入许多新的子类。一般的对象组合技术和具体的委托技术,是继承之外组合对象行为的另一种灵活方法。新的功能可以通过以新的方式组合已有对象,而不是通过定义已存在类的子类的方式加到应用中去。另一方面,过多使用对象组合会使设计难于理解。许多设计模式产生的设计中,你可以定义一个子类,且将它的实例和已存在实例进行组合来引入定制的功能。
      • 设计模式:Bridge, Chain of Responsibility, Composite, Decorator, Observer, Strategy
    • 不能方便地对类进行修改 : 有时你不得不改变一个难以修改的类。
      • 设计模式: Adapter, Decorator, Visitor
  • 设计模式在开发如下三类主要软件中所起作用:

    • 应用程序
    • 工具箱
    • 框架
  • 如果要建造像文档编辑器的应用程序(Application Program),那么它的内部复用性,可维护性和可扩充性是要优先考虑的。

  • 内部复用性确保你不会做多余的设计和实现

  • 设计模式通过减少依赖型来提高内部复用性。

  • 松散耦合也增强了一类对象与其他多个对象协作的可能性。

怎样选择设计模式

  • 考虑设计模式是怎样解决设计问题的

  • 浏览模式的意图部分

  • 研究模式怎样互相关联

  • 研究目的相似的模式

  • 检查重新设计的原因

  • 考虑你的设计中那些是可变的

怎样使用设计模式

  • 大致浏览一遍模式。

    • 特别注意其适用性部分和效果部分,确定它适合你的问题
  • 回头研究结构部分,参与者部分和协作部分

    • 确保你理解这个模式的类和对象以及他们是怎样关联的
  • 选择模式参与者的名字,使他们在应用上下文中有意义

  • 定义类

    • 声明它们的接口,建立他们的继承关系,定义代表数据和对象引用的实例变量。
    • 识别模式会影响的你的应用中存在的类,作出相应的修改
  • 定义模式中专用于应用的操作名称

    • 名字一般依赖于应用。使用与每一个操作相关联的责任和写作作为指导
    • 名字约定要一致
  • 实现执行模式中责任和写作的操作

第二章 实例研究: 设计一个文档编辑器

递归组合

  • 层次结构信息的表述通常是通过一种被称为递归组合(Recursive Composition)的技术来实现的。
  • 递归组合可以由较简单的元素逐渐建立复杂的元素,是我们通过简单图形元素构造文档的方法之一。

第三章 创建型模式

  • 创建型模式抽象了实例化过程。它们帮助一个系统独立于如何创建,组合和表示它的哪些对象。一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象。

  • 创建型模式在什么被创建,谁创建它,它是怎样被创建的,以及何时创建这方面给予很大的灵活性。它们允许你用结构和功能差别很大的产品对象配置一个系统。配置可以是静态的(即在编译时指定的),也可以是动态的(在运行时)

ABSTRACT FACTORY(抽象工厂) – 对象创建型模式

  • 意图:

    • 提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类
  • 别名:

    • Kit
  • 动机

    • 考虑一个支持多种视感(look-and-feel)标准的用户界面工具包。不同的视感风格,为诸如滚动条,窗口和按钮等用户界面”窗口组件”定义不同的外观和行为。
    • 为保证视感风格标准间的可移植性,一个应用不应该为一个特定的视感外观硬编码它的窗口组件。在整个应用中实例化特定视感风格的窗口组件类将使得以后很难改变视感风格

简介

  • gdb工具使用中常见的技巧

日志重定向

在使用GDB进行调试时,您可以将GDB的日志输出重定向到文件中。以下是一种方法:

  1. 打开终端并运行GDB。

  2. 在GDB提示符下,输入以下命令以启用日志记录功能并指定输出文件:

    1
    set logging file <file_path>

    <file_path>替换为您想要将日志记录写入的文件路径和文件名。

  3. 输入以下命令以开始记录GDB日志:

    1
    set logging on

    GDB将开始将日志记录写入指定的文件。

  4. 执行您的调试操作,进行需要的步骤和命令。

  5. 当您完成调试并想要停止日志记录时,输入以下命令:

    1
    set logging off

    GDB将停止将日志写入文件。

请注意,这种方法将GDB的整个会话记录到文件中,包括所有输入和输出。这对于调试复杂的问题或追踪程序执行流程非常有用。

希望这可以帮助您将GDB的日志输出到文件中。如有其他问题,请随时提问。


启动程序 输入 参数

  • 示例:
    1
    gdb --args ./mnc.exe --port=10008 --ip=0.0.0.0

gdb 查看断点

要查看在GDB中设置的所有断点,你可以使用以下命令:

1
info breakpoints

该命令会显示当前设置的所有断点的列表,包括断点编号、断点位置和断点条件(如果有设置条件的话)。例如:

1
2
3
4
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040051a in main at example.c:10
2 breakpoint keep y 0x00000000004004f6 in foo at example.c:5

在这个例子中,有两个断点,编号为1和2。第一个断点位于文件example.c的第10行的main函数处,第二个断点位于第5行的foo函数处。

如果你只想查看某个特定断点的详细信息,你可以使用以下命令,并将<breakpoint>替换为断点的编号:

1
info breakpoints <breakpoint>

例如,要查看断点1的详细信息,可以执行以下命令:

1
(gdb) info breakpoints 1

这将显示关于断点1的更多信息,如断点位置、断点条件等。


gdb 选项 详解

GDB(GNU Debugger)是一个功能强大的调试工具,用于在Linux和其他操作系统上调试程序。GDB 9.2版本引入了许多新的选项和功能,以下是一些常用的选项的详解:

  1. -ex "command":在启动GDB后执行指定的GDB命令。可以使用多个-ex选项来执行多个命令。

  2. -tui:启动GDB的文本用户界面(Text User Interface,TUI)模式。TUI模式提供了源代码和程序状态的交互式显示。

  3. -batch:以批处理模式运行GDB,不显示交互式命令提示符。适用于自动化脚本或批处理任务。

  4. -core <corefile>:指定要调试的核心转储文件。核心转储文件包含程序在崩溃或异常终止时的状态信息。

  5. -pid <pid>:调试指定进程标识符(PID)的正在运行的程序。通过此选项,可以附加到正在运行的进程进行调试。

  6. -symbols=<path>:指定要加载的调试符号文件的路径。调试符号文件包含程序的符号信息,如变量名、函数名等。

  7. -directory=<path>:指定要搜索源代码文件的目录。当源代码文件不在当前目录或GDB默认搜索路径中时,可以使用此选项指定源代码的位置。

  8. -quiet:以静默模式运行GDB,不显示任何冗余的信息。

  9. -ex=dir=<dir>:指定GDB的工作目录。GDB将在指定的目录中查找程序和其他文件。

  10. -command=<script_file>:指定要执行的GDB脚本文件。可以在脚本文件中定义自定义的GDB命令和操作。

这些选项只是GDB 9.2版本中的一部分,还有许多其他选项可供使用。你可以参考GDB的官方文档或运行gdb --help命令获取完整的选项列表和详细说明。通过熟悉和灵活运用这些选项,你可以根据具体需求使用GDB的功能来调试和分析程序。


gdb 9.2 保存崩溃时堆栈信息

在GDB 9.2中,你可以通过使用generate-core-file命令来保存崩溃时的堆栈信息。这个命令会生成一个核心转储文件,其中包含了程序在崩溃时的内存状态和堆栈信息。

以下是保存崩溃时堆栈信息的步骤:

  1. 在命令行中使用以下命令启动GDB并加载可执行文件:

    1
    gdb ./my_program
  2. 设置断点或者在崩溃发生的地方让程序停下来。你可以使用break命令设置断点,或者通过其他方式确保程序会在崩溃时停下来。

  3. 运行程序。在GDB命令行中,使用以下命令运行程序:

    1
    run
  4. 当程序崩溃时,GDB会自动停在断点处或者崩溃位置。

  5. 在GDB命令行中使用以下命令生成核心转储文件:

    1
    generate-core-file <corefile>

    其中<corefile>是你想要保存核心转储文件的路径和文件名。

  6. 此时,核心转储文件就保存了程序崩溃时的堆栈信息和内存状态。

通过保存核心转储文件,你可以在后续的调试过程中使用该文件进行分析。你可以加载核心转储文件并使用GDB命令查看堆栈、变量值等信息,以定位崩溃的原因和解决问题。

需要注意的是,生成核心转储文件可能需要对系统进行相应的配置。确保你的系统允许生成核心转储文件,并且具有足够的权限来保存文件。有些系统默认禁用了核心转储文件的生成,你可能需要修改相应的设置。

另外,记住在发布或共享代码时,避免包含核心转储文件,因为它可能包含敏感信息。核心转储文件主要用于调试和分析崩溃问题。


gdb 9.2 调试核心转储文件

在GDB 9.2中,你可以使用核心转储文件进行调试。核心转储文件是程序在崩溃或异常终止时保存的内存状态的快照,它包含了程序崩溃时的堆栈信息和内存数据。以下是使用GDB调试核心转储文件的步骤:

  1. 启动GDB,并加载可执行文件以及核心转储文件。在命令行中使用以下命令:

    1
    gdb ./my_program <corefile>

    其中<corefile>是核心转储文件的路径和文件名。

  2. 一旦GDB加载了核心转储文件,它会还原程序崩溃时的内存状态。你可以使用GDB命令来查看堆栈信息、变量值等。

  3. 使用backtracebt命令查看堆栈跟踪,了解程序崩溃的位置和调用链。

  4. 使用其他GDB命令进行调试。你可以使用printp命令查看变量值,使用steps命令单步执行程序,使用continuec命令让程序继续执行等。

  5. 分析堆栈信息和变量值,定位问题的原因,并进行修复。

需要注意的是,调试核心转储文件时,可执行文件和核心转储文件必须是对应的,即生成核心转储文件的程序版本与进行调试的程序版本相同。如果可执行文件和核心转储文件不匹配,可能会导致调试结果不准确或无法正常调试。

此外,调试核心转储文件还可以结合其他GDB功能和命令,例如条件断点、内存查看等,以进一步分析问题。GDB提供了丰富的调试功能,可以帮助你定位和解决程序崩溃问题。

简介

gdbserver使用方法,与PC机xxx-gdb搭配使用

  • 开发板:gdbserver <ip address> <listening port> <program>

    • 例如:gdbserver 192.167.66.112:9999 demo_02.exe
    • 需要注意的一点是:需要调试的程序在编译时记得加上-g参数
  • PC机:在终端中输入aarch64-linux-gnu-gdb之后进入gdb调试模式,然后输入target remote ip-address:port即可连接远程目标

  • 交叉编译,程序的参数是从gdbserver 传进来的,其原理就是将调试信息通过端口发送到远程

比特大陆智能盒子远程调试

  • 工具

    • vca里面的交叉编译工具链
    • gdbserver
    • VSCode
  • 大致流程

    • 通过使用vca中的交叉编译工具,在x86主机上编译带有调试符号的可执行程序
    • 将可执行程序拷贝到比特大陆盒子上,通过gdbserver启动程序,监听端口,例如:gdbserver 0.0.0.0:19999 /data/dagger/VideoProcess/bin/ice.exe --multi-task-zmq-listen tcp://*:9100
    • 在x86主机上,配置launch.json文件:
      • "program": "${workspaceFolder}/build/ice.exe",
      • "miDebuggerPath": "${workspaceFolder}/toolchains/aarch64-linux-gnu-6.3.1/bin/aarch64-linux-gnu-gdb",
      • "miDebuggerServerAddress": "192.169.5.46:19999"
    • 开始调试

简介

  • gdb工具的理论知识

gdb 常用命令

GDB(GNU Debugger)是一个功能强大的调试工具,提供了许多命令来帮助程序员进行调试。以下是GDB的全部命令及其详细解释:

常用命令

  1. run:运行程序。例如:run arg1 arg2
  2. break:设置断点。
    • break <function>:在指定函数的入口处设置断点。
    • break <line>:在指定行号处设置断点。
    • break <file:line>:在指定文件的指定行号处设置断点。
    • break <address>:在指定地址处设置断点。
  3. continue:继续执行程序,直到下一个断点或程序结束。
  4. next:执行下一行代码,但是不会进入函数内部。
  5. step:执行下一行代码,并进入函数内部。
  6. print(或者简写为p):打印变量的值。
    • print <variable>:打印指定变量的值。
    • print <expression>:计算并打印指定表达式的值。
  7. backtrace(或者简写为bt):显示当前的函数调用堆栈。
  8. info locals:显示当前函数的局部变量。
  9. info breakpoints:显示当前设置的所有断点。
  10. delete:删除指定的断点。
  • delete <breakpoint>:删除指定编号的断点。
  • delete breakpoints:删除所有断点。
  1. watch:设置观察点。
  • watch <expression>:当指定表达式的值发生变化时中断程序。
  1. finish:执行完当前函数并停止。
  2. quit:退出GDB。

其他常用命令

  1. list:显示源代码。
    • list:显示当前位置附近的源代码。
    • list <function>:显示指定函数的源代码。
    • list <line>:显示指定行号附近的源代码。
  2. info registers:显示寄存器的值。
  3. set:设置变量的值。
    • set <variable>=<value>:将指定变量的值设置为指定值。
  4. display:持续显示表达式的值。
    • display <expression>:每次停止时显示指定表达式的值。
  5. x:显示内存内容。
    • x/<count><format> <address>:以指定格式显示指定地址开始的内存内容。
  6. info threads:显示当前线程信息。
  7. **thread **:切换到指定线程。
  8. set args:设置运行程序时的命令行参数。
  9. set environment:设置运行程序时的环境变量。
  10. directory:指定源代码文件的搜索路径。
  11. **run **:运行程序并传递命令行参数。

以上只是GDB命令的一小部分,GDB还有更多的命令和选项可供使用。你可以在GDB的命令行界面中输入help命令来查看完整的命令列表和使用说明。


gdb 是什么

GDB(GNU Debugger)是一个功能强大的调试工具,用于在Linux和其他操作系统上调试程序。它提供了许多功能和命令,用于追踪程序的执行、查看变量的值、检测内存错误等。下面是对GDB的一些详解:

  1. 启动程序:使用gdb命令后跟要调试的可执行文件的路径来启动GDB。例如:gdb ./my_program

  2. 设置断点:使用breakb命令在代码中设置断点。可以使用行号、函数名或文件名来指定断点的位置。例如:b mainb file.cpp:10

  3. 运行程序:使用runr命令来运行程序。可以带上命令行参数。例如:r arg1 arg2

  4. 单步执行:使用nextn命令进行逐行单步执行。可以逐过程地执行函数体内部的语句。

  5. 进入函数:使用steps命令进入函数内部并逐语句单步执行。

  6. 继续执行:使用continuec命令继续程序的执行直到下一个断点或程序结束。

  7. 打印变量值:使用printp命令打印变量的值。可以打印局部变量、全局变量、表达式等。例如:p my_var

  8. 查看堆栈:使用backtracebt命令查看函数调用的堆栈信息,显示调用链。

  9. 查看源代码:使用listl命令查看源代码,显示当前位置附近的代码片段。

  10. 修改变量值:使用set命令修改变量的值。例如:set my_var = 42

  11. 监视变量:使用watch命令设置监视点,当变量的值发生变化时,程序会停在相应的位置。

  12. 内存调试:使用GDB的内存调试功能,可以检测和分析内存错误、内存泄漏等问题。

  13. 调试核心转储文件:使用core命令调试核心转储文件,用于分析程序崩溃或异常终止的情况。

  14. 断点条件和命令:可以在设置断点时指定条件和命令,以控制程序的执行流程。

  15. 定义自定义命令:可以使用GDB的Python扩展或GDB脚本语言来定义自定义命令,以实现更复杂的调试操作。

  16. 调试多线程程序:GDB支持调试多线程程序,可以追踪和控制每个线程的执行。

这只是GDB的一小部分功能和命令,GDB还有很多

其他强大的功能,如条件断点、反汇编、寄存器查看等。你可以查阅GDB的官方文档或其他资源,深入了解和掌握更多关于GDB的知识和技巧。调试是开发过程中的重要环节,GDB可以帮助你更好地理解程序的执行过程,定位和解决问题。

简介

  • 目标检测相关笔记
  • 除了图像分类,图像生成,图像去噪,目标检测也是计算机视觉领域非常常见的一类问题。其在人脸检测,行人检测,图像检索和视频监控等方面有广泛应用。

目标检测是什么

  • 目标检测,英文为Object Detection,有时候也称之为物体检测,物体识别等。简单来讲,目标检测是与计算机视觉和图像处理有关的计算机技术,其涉及在数字图像和视频中检测特定类(例如人,建筑物或汽车)的语义对象的实例。目标检测在人脸检测,行人检测,图像检索和视频监控等计算机视觉领域有广泛的应用。动态物体检测与识别也是自动驾驶中需要攻克的一项关键技术。
  • 目标检测实际上就主要做一件事情。识别图像中指定存在的所有对象及其位置,并标示出来

目标检测的前处理

  • 目标检测的前处理技术主要包括图像预处理、特征提取、数据增强等。图像预处理包括图像缩放、归一化、旋转、剪裁等操作,这能够对原始图像进行一定的优化和处理,提高模型的鲁棒性和准确率。特征提取是从原始图像中提取目标特征的过程,常用的提取特征的方式有卷积神经网络(CNN)、霍夫变换、边缘检测等方法。数据增强则是通过对原始图像进行变换,如随机裁剪、随机翻转、亮度调节、添加噪声等操作,增加训练数据量,提高模型的泛化能力。

  • 通过OpenCV读取图片,对图片进行缩放

机器学习和深度学习中关于图像数据输入的 Shape

  • 在机器学习的图片输入数据中,只需要将图片数据的4维(图片的多少,图片的长,图片的宽,图片的通道数)转换为2维即可

    1
    train_data = train_data.reshape(train_data.shape[0], train_data.shape[1] * train_data.shape[2] * train_data.shape[3])
  • 深度学习,读取图片阶段:

    1
    2
    item_image = cv2.imread(img_path)
    item_image = cv2.resize(item_image, (224,224), interpolation=cv2.INTER_AREA)
    • cv2.imread()是为了读取图片上的数据,也就是原始图像数据的信息。
    • 在cv2.resize()中,可以指定输出图片的大小,也就是图片的长和宽。
  • 把读取到数据转换成ndarray

    1
    train_data = np.array(train_data, dtype='float32')
  • 此时,如果打印train_data的维度,会显示一个4个维度的数据,(图片的数量,图片的长,图片的宽,图片的通道数)

  • shape

    • 图像形状,行数,列数,通道数
  • size

    • 图像大小,行数 * 列数 * 通道数
  • dtype

    • 图像数据类型,通常为uint8

目标检测的后处理

  • 目标检测的后处理技术主要是指对目标检测算法的输出进行调整和优化,以提高检测结果的准确性和稳定性。

  • 后处理技术一般包括非极大值抑制(NMS)、筛选、矫正等方法。

  • 非极大值抑制是指对检测框进行去重和筛选,只保留探测到的目标中得分最高的那一个,并抑制周围高度重叠的框。

  • 筛选技术是指判断目标是否符合检测条件,去掉检测到的误检目标或不符合条件的目标。

  • 矫正技术是指对检测框的位置和倾斜角度进行矫正,使得目标在图像中的位置更为精确,提高识别准确性。这些技术通常在目标检测的训练和测试过程中都会使用。

  • 目标检测并不一定需要后处理,但通常情况下会进行后处理来进一步提高模型的准确性和稳定性。后处理技术能够对目标检测的结果进行优化和调整,例如去除重复检测框、筛选、矫正等等

  • 在目标检测中,由于前景和背景的比例极为不平衡,因此检测框往往会出现大量的误检和漏检。后处理技术能够对这些问题进行针对性的处理,提高最终的检测准确率

  • 常用的后处理技术包括非极大值抑制(NMS)、筛选、矫正等。这些技术不仅可以提高目标检测的准确性,还能够缩短检测时间、减少处理数据的复杂度,从而优化算法的整体性能。因此,目标检测中通常会使用后处理技术来优化模型的表现

NMS(Non-Maximum Suppression)

  • NMS(Non-Maximum Suppression)是目标检测中常用的一种后处理技术,用于去除重复的检测结果,只保留最佳的目标框,从而提高模型精度和效率

  • NMS的原理是基于阈值和重叠率进行筛选。假设模型输出了多个待筛选的检测框,每个检测框都有一个置信度分数,表示该框中包含目标的概率。NMS需要分别对每个类别的检测框进行处理

  • 详细流程如下

    • 首先,按照置信度分数从高到低对检测框进行排序。
    • 取分数最高的检测框A,作为起始框,并从剩余的框里删除A。
    • 按照重叠率的大小判断剩余框中是否与A重叠,若存在则计算它们的IoU值(交并比)。
    • 若IoU值大于预设阈值(一般设为0.5),则将该框从剩余框中删除;若IoU值小于阈值,则保留该框。
    • 重复第2-4步,直到剩余框中没有可删除的框,即所有检测框都被筛选过了。
    • 将剩余的保留框和置信度分数返回。
  • 下面是一个简单的Python实现:

    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
    def nms(detections, threshold=0.5):
    # detections: 待筛选的检测框列表,每个元素为(x1, y1, x2, y2, score)
    # threshold: 阈值,设定IoU值小于阈值的框为非极大值框

    if len(detections) == 0:
    return []

    # 按照置信度分数从高到低排序
    detections = sorted(detections, key=lambda x: x[4], reverse=True)

    # 用于保存最终保留下来的检测框
    keep = []

    while len(detections) > 0:
    # 取分数最高的框作为起始框
    A = detections[0]
    keep.append(A)

    # 计算该起始框和剩余框的IoU值
    B = detections[1:]
    overlaps = [iou(A, b) for b in B]

    # 将IoU值大于阈值的框删除
    indices = [i+1 for i, overlap in enumerate(overlaps) if overlap > threshold]
    detections = [b for i, b in enumerate(detections) if i not in indices]

    return keep
  • 其中,iou函数用于计算两个框之间的IoU值。

  • 目前有很多对NMS算法的改进方法。下面是几种常见的NMS改进方法:

    • Soft-NMS:传统的NMS是直接抑制掉重叠框中候选框中分数较低的框,而Soft-NMS通过修改分数的方式,将重叠的框的分数逐渐降低。例如,重叠的框的分数会随着IoU值的增加而逐渐减少,而不是直接变为0。这样做可以让模型更加关注可靠的检测结果,避免错判。
    • Adaptive NMS:Adaptive NMS主要是为了解决NMS不同的IoU阈值不适用于不同的物体大小和密度的问题。它引入了一个Adaptive Parameter,该参数会根据候选框大小和样例分布自适应地调整IoU阈值,从而更好地适应不同尺寸的物体目标
    • Soft-NMS with Learnable Weight Scheme:Soft-NMS技术引入了一个权重系数,确定哪些重叠框应该被抑制,而Soft-NMS-LWS则引入了一个学习的权重计算任务,让模型自己学习如何更好地选择重叠框。
  • 这种方法通常会在训练期间学习最佳的权重,并且适用于NMS抑制的具有不同尺度粒度的多个检测结果

  • 这些改进方法的核心思想都是为了更好地处理较为复杂的目标检测情况,例如遮挡和噪声干扰等。同时,这些改进方法还可以进一步提高检测精度并减少误检率,相对于传统的NMS方法表现更出色

  • 目标检测算法中,有些方法可以不需要显式的NMS后处理技术,例如YOLO和SSD(Single Shot MultiBox Detector)。这些方法具有以下特点

    • YOLO:YOLO(You Only Look Once)是一种实时的目标检测算法,它的目标检测过程是在单个神经网络中并行进行的。YOLO将整个图像分成多个网格,每个网格预测出N个边界框及它们的置信度,然后根据置信度和类别得分,筛选出最佳的框。在选择框时,YOLO使用了阈值(置信度分数),减少了重叠框的存在,从而取代了传统的NMS技术。这种方式不仅能够实现快速检测,还能避免高阈值造成的误检和低阈值造成的漏检
    • SSD:SSD(Single Shot MultiBox Detector)也是一种实时目标检测算法,与YOLO类似,它也是将整个图像分成多个网格进行处理。但SSD采用的是先验框机制,通过预设的先验框,让模型更加有效的学到各个物体的形状和大小等特征。在每个网格上,SSD预测出每个先验框的位置和它们所属的类别,然后通过非最大抑制的方式删除一些冗余的框。相比其他的检测算法,SSD是一种速度较快,精度较高的算法。

图像处理(维基百科)

  • 图像处理是指对图像进行分析,加工和处理,使其满足视觉,心理或其他要求的技术。图像处理是信号处理在图像领域上的一个应用。目前大多数的图像均是以数字形式存储,因而图像处理很多情况下指数字图像处理。此外,基于光学理论的处理方法依然占有重要的地位。

  • 图像处理是信号处理的子类,另外与计算机科学、人工智能等领域也有密切的关系

  • 传统的一维信号处理的方法和概念很多仍然可以直接应用在图像处理上,比如降噪、量化等。然而,图像属于二维信号,和一维信号相比,它有自己特殊的一面,处理的方式和角度也有所不同

  • 常用的信号处理技术

    • 大多数用于一维信号处理的概念都有其在二维图像信号领域的延伸,它们之中的一部分在二维情形下变得十分复杂。 同时图像处理自身也具有一些新的概念, 例如,连通性、旋转不变性,等等。这些概念仅对二维或更高维的情况下才有非平凡的意义。
    • 图像处理中常用到快速傅立叶变换,因为它可以减小数据处理量和处理时间。
  • 从一维信号处理扩展来的技术和概念

    • 分辨率
    • 动态范围
    • 带宽
    • 滤波器设计
    • 微分算子
    • 边缘检测
    • Domain modulation
    • 降噪(Noise reduction)
  • 专用于二维(或更高维)的技术和概念

    • 连通性
    • 旋转不变性
  • 典型问题

    • 几何变换(geometric transformations): 包括放大,缩小,旋转等
    • 颜色处理(color): 颜色空间的转化,亮度以及对比度的调节,颜色修正等
    • 图像融合(image composite): 多个图像的加,减,组合,拼接等
    • 降噪(image denoising): 研究各种针对二维图像的去噪滤波器或者信号处理技术
    • 边缘检测: 进行边缘或者其他局部特征提取
    • 分割: 依据不同标准,把二维图像分割成不同区域
    • 图像编辑: 和计算机图形学有一定交叉
    • 图像配准: 比较或集成不同条件下获取的图像
    • 图像增强(image enhancement)
    • 图像数字水印: 研究图像域的数据隐藏,加密或者认证
    • 图像压缩: 研究图像压缩
  • 应用

    • 摄影以及印刷
    • 卫星图像处理(Statellite image processing)
    • 医学图像处理(Medical image processing)
    • 面孔识别,特征识别(Face detection, feature detection, face identification)
    • 显微图像处理(Microscope image processing)
    • 汽车障碍识别(Car barrier detection)

简介

  • gdbserver使用过程中遇到的理论知识

gdbserver 常用 选项

GDBserver是一个用于远程调试的程序,它与GDB一起使用,允许你在目标系统上进行远程调试。以下是一些常用的GDBserver选项的详解:

  1. --multi:启动GDBserver以支持多个GDB客户端连接。这允许多个调试器同时连接到同一个目标系统进行并行调试。

  2. --attach <pid>:将GDBserver附加到指定进程的PID上进行调试。通过这个选项,你可以在目标系统上调试正在运行的进程。

  3. --once:让GDBserver只调试一个进程,并在该进程退出后自动停止。这对于单次调试会话很有用。

  4. --remote-debug:启用远程调试支持,允许GDB通过网络与GDBserver通信进行远程调试。通常与--port选项一起使用。

  5. --port <port>:指定GDBserver监听的端口号,以便GDB通过网络连接到GDBserver进行远程调试。

  6. --attach-command <command>:在附加到进程时执行指定的GDB命令。这对于在附加时自动执行特定的调试命令非常有用。

  7. --wrapper <wrapper>:在启动目标程序之前运行指定的包装器程序。这对于在目标系统上执行额外的初始化步骤或设置环境变量非常有用。

  8. --localhost:GDBserver仅允许本地主机连接。这可以增加调试的安全性,限制远程访问。

  9. --persistent:使GDBserver保持持久运行,即使GDB客户端断开连接或退出。这样,当GDB客户端重新连接时,调试会话可以继续。

  10. --separate:将每个连接的GDB客户端隔离开,使它们无法相互干扰。这在并行调试多个目标时很有用。

这些选项只是GDBserver的一部分,还有其他选项可供使用。你可以参考GDBserver的文档或运行gdbserver --help命令获取完整的选项列表和详细说明。GDBserver是一个强大的工具,可用于在目标系统上进行远程调试,帮助你调试和分析嵌入式系统或其他远程目标。

简介

  • NVIDIA 硬件平台下深度学习相关的理论基础

TensorRT txtexec 工具

txtexec 是 NVIDIA TensorRT 中的一个实用工具,用于从文本文件加载和执行优化后的推理引擎(.engine 文件)。这个工具可以用于在命令行中加载和运行已经通过 TensorRT 优化的模型,而无需编写额外的代码。下面我会对 txtexec 进行详细解释:

  1. 加载和执行引擎txtexec 工具允许你通过命令行加载和执行优化过的引擎文件(.engine 文件)。这意味着你可以使用这个工具在没有额外编程的情况下运行经过优化的深度学习模型。

  2. 命令行语法txtexec 的命令行语法如下:

    1
    2
    txtexec [-h] --loadEngine=<string> [--iterations=<int>] [--avgRuns=<int>]
    [--warmUp=<int>] [--useDLA=<int>] [--useSpinWait] [--threads=<int>]
    • --loadEngine:指定要加载的 .engine 文件的路径。
    • --iterations:指定进行推理的迭代次数(默认为 1)。
    • --avgRuns:指定多次推理运行的平均次数(默认为 1)。
    • --warmUp:指定预热运行的次数,这些运行不会计入统计(默认为 0)。
    • --useDLA:指定是否使用深度学习加速器(DLA),0 表示不使用,1 表示使用(默认为 0)。
    • --useSpinWait:指定是否使用自旋等待进行同步。
    • --threads:指定并行线程数(默认为 1)。
  3. 使用示例:以下是一个示例命令行使用 txtexec 加载并执行 .engine 文件的命令:

    1
    txtexec --loadEngine=my_model.engine --iterations=100 --avgRuns=10 --useDLA=0

    这个命令将加载名为 my_model.engine 的引擎文件,进行 100 次迭代的推理,每次推理进行 10 次运行取平均,并且不使用深度学习加速器。

请注意,txtexec 工具是 TensorRT 的一个辅助工具,主要用于快速测试和验证模型的推理性能。对于更复杂的应用和场景,通常需要在代码中使用 TensorRT 的 API 进行推理。详细的用法和配置可以在 NVIDIA 的 TensorRT 文档中找到。

简介

  • Caffe,全称Convolutional Architecture for Fast Feature Embedding。是一种常用的深度学习框架,主要应用在视频、图像处理方面的应用上。
  • caffe是一个清晰,可读性高,快速的深度学习框架

网络结构层参数

  • prototxt文件是caffe的配置文件,用于保存CNN的网络结构和配置信息。

  • prototxt文件有三种,分别是deploy.prototxttrain_val.prototxtsolver.prototxt

  • solver.prototxt

    • solver.prototxt是caffe的配置文件。里面定义了网络训练时候的各种参数,比如学习率、权重衰减、迭代次数等等。
    • solver.prototxt文件只在网络进行训练的时候需要载入。是网络训练的一个整体的参数配置文件。
  • deploy.prototxttrain_val.prototx

    • 这两个文件是caffe的网络结构文件。
    • train_val.prototx是训练时候的网络结构,deploy.prototxt用于发布(即测试时候的网络结构)。
    • 这两个文件中内容基本一致,但是存在一些细微区别:
      • train_val.prototx中网络结构的data层有两种,分别为TRAINTEST。顾名思义,TRAIN是网络训练时后的数据结构,TEST是网络做验证时候的数据结构。一般来说TRAIN中的batchSizeTEST中的要大一些。
      • train_val.prototx中的卷积层(Convolution)中存在学习率和权重衰减的参数,而deploy.prototxt文件中则没有这些参数(有些deploy.prototxt中仍然有这些参数,但是对测试不起任何作用)。

简介

  • 深度学习 模型 相关理论基础

关于深度学习和模型的一些只言片语

  • 模型就像数学公式,推理就是去套公式
  • 数学公式需要数学家花很长时间才能推导出来,但是公式出来之后,小学生都能够套用公式快速算出结论
  • 所以模型需要性能极强的设备花很多时间才能训练好,但是很便宜的设备就能使用成熟的模型,快速得到想要的结果。

Coco(Common Objects in Context)

COCO(Common Objects in Context)是一个广泛使用的计算机视觉数据集,旨在推动物体检测、图像分割和图像理解等任务的研究和发展。COCO数据集由微软公司创建,是一个包含图像和标注的大规模数据集。

COCO数据集包含超过330,000张图像,涵盖80个常见的物体类别,例如人、动物、交通工具、家具等。每个图像都标注了物体的边界框、物体类别和可选的语义分割掩码。此外,COCO数据集还包含了图像关系和场景分类等附加任务的标注信息。

COCO数据集的目标是提供一个具有挑战性的视觉理解任务,以促进计算机视觉算法的发展。它广泛应用于物体检测、图像分割、姿态估计、图像生成和场景理解等领域的研究和评估。

许多深度学习模型和算法都使用COCO数据集进行训练和评估,以提高物体检测和图像分割的性能。此外,COCO数据集还举办了每年一次的COCO挑战赛,鼓励研究者提交他们的算法并进行比较和竞争。

总而言之,COCO是一个大规模的计算机视觉数据集,提供了丰富的图像和标注信息,用于推动物体检测、图像分割和图像理解等任务的研究和发展。

.engine 文件

关于深度学习、NVIDIA 和 .engine 文件的问题,我可以为你提供一些相关信息。

  1. 深度学习:深度学习是机器学习的一个分支,致力于模拟人脑神经网络的工作原理,通过构建多层神经网络来学习和解决复杂的模式识别和特征提取任务。深度学习在计算机视觉、自然语言处理、语音识别等领域取得了重大突破。

  2. NVIDIA:NVIDIA 是一家专门从事图形处理器(GPU)和计算平台的技术公司。NVIDIA 的 GPU 在深度学习领域得到了广泛的应用,因为它们在并行计算和加速深度神经网络方面表现出色。NVIDIA 提供了深度学习框架、GPU 加速库等工具,用于训练和部署深度学习模型。

  3. .engine 文件:在 NVIDIA 的上下文中,.engine 文件通常指的是 TensorRT 的引擎文件。TensorRT(Tensor Runtime)是 NVIDIA 开发的用于优化深度学习推理(inference)的库,它可以将训练好的深度学习模型进行优化,以便在生产环境中更快地执行推理。.engine 文件是 TensorRT 编译优化后的模型表示,其中包含了针对特定硬件和优化选项的模型表示,以提高推理性能。

    TensorRT 使用过程中,通常的步骤是:

    • 从深度学习框架(如 TensorFlow 或 PyTorch)中导出训练好的模型。
    • 使用 TensorRT 的 API 将模型转换为 .engine 文件,这个文件经过了优化以适应特定硬件。
    • 在推理时,加载 .engine 文件,使得模型能够在 GPU 上进行高效推理。

    这种方式可以显著提高深度学习模型的推理速度,并减少所需的资源。

需要注意的是,.engine 文件是 NVIDIA TensorRT 特定的文件格式,不同于其他深度学习模型的保存格式。如果你要使用 .engine 文件,你需要使用 TensorRT 的 API 进行加载和执行。详细的操作和使用方法可以参考 NVIDIA 的 TensorRT 文档和示例。

什么是深度学习

  • 深度学习是一种人工智能(AI)方法,用于教计算机以受人脑启发的方式处理数据。深度学习模型可以识别图片,文本,声音和其他数据中的复杂模式,从而生成准确的见解和预测。

为什么深度学习很重要

  • 人工智能(AI)试图训练计算机像人类一样思考和学习。深度学习技术推动了日常产品中使用的许多AI应用程序的发展,例如

    • 数字助理
    • 声控电视遥控器
    • 欺诈检测
    • 自动面部识别
  • 它也是自动驾驶汽车,虚拟现实等新兴技术的重要组成部分

  • 深度学习模型,是一些计算机文件,数据科学家训练这些文件,以使用算法或一组预定义步骤来执行任务。企业使用深度学习模型在各种应用程序中分析数据并作出预测。

深度学习的作用

  • 深度学习在汽车、航空航天、制造、电子、医学研究和其他领域有很多使用场景。以下是深度学习的一些示例:

    • 自动驾驶汽车使用深度学习模型自动检测路标和行人。
    • 国防系统使用深度学习在卫星图像中自动标记感兴趣的区域。
    • 医学图像分析使用深度学习自动检测癌细胞以进行医学诊断。
    • 工厂使用深度学习应用程序自动检测人员或物体何时位于机器的不安全距离内。
  • 可以将这些不同的深度学习使用场景分为四大类:计算机视觉、语音识别、自然语言处理(NLP)和推荐引擎。

  • 计算机视觉,是指计算机从图像和视频中提取信息及见解的能力。计算机可以使用深度学习技术来理解图像,就像人类一样。计算机视觉具有多种应用,如下所示

    • 内容审核,用于从图像和视频归档中自动删除不安全或不适当的内容
    • 面部识别,用于识别面部和多项属性,如睁开的眼睛、眼镜以及面部毛发
    • 图像分类,用于识别品牌徽标、服装、安全装备和其他图像细节
  • 语音识别,深度学习模型可以分析人类语音,尽管说话模式、音调、语气、语言和口音不尽相同。虚拟助手(如 Amazon Alexa)和自动转录软件使用语音识别执行以下任务:

    • 帮助呼叫中心座席并对呼叫进行自动分类。
    • 将临床对话实时转换为文档。
    • 为视频和会议记录添加准确的字幕以实现更广泛的内容覆盖范围
  • 自然语言处理,计算机使用深度学习算法从文本数据和文档中收集见解和意义。这种处理自然的、人工创建的文本的能力有几个使用场景,包括在以下功能中:

    • 自动虚拟座席和聊天机器人
    • 自动总结文件或新闻文章
    • 长格式文档(如电子邮件和表格)的业务情报分析
    • 用于表示情绪(如社交媒体上的正面和负面评论)的关键短语索引
  • 推荐引擎,应用程序可以使用深度学习方法来跟踪用户活动并开发个性化推荐。它们可以分析各种用户的行为,并帮助他们发现新产品或服务。例如,许多媒体和娱乐公司,例如 Netflix、Fox 和 Peacock,都使用深度学习来提供个性化的视频推荐

深度学习网络有哪些组成部分?

  • 深度神经网络的组成部分如下:
    • 输入层,人工神经网络有几个向其输入数据的节点。这些节点构成了系统的输入层
    • 隐藏层,输入层处理数据并将其传递到神经网络中更远的层。这些隐藏层在不同层级处理信息,在接收新信息时调整其行为。深度学习网络有数百个隐藏层,可用于从多个不同角度分析问题
    • 输出层,输出层由输出数据的节点组成。输出是或否的答案的深度学习模型在输出层中只有两个节点。那些输出更广泛答案的模型有更多的节点。

机器学习背景下的深度学习是什么

  • 深度学习是机器学习的子集。深度学习算法的出现是为了提高传统的机器学习技术的效率。传统的机器学习方法需要大量的人力来训练软件。
  • 例如,在动物图像识别中,需要执行以下操作:
    • 手动标记数十万张动物图像
    • 让机器学习算法处理这些图像
    • 在一组未知图像上测试这些算法
    • 找出某些结果不准确的原因
    • 通过标注新图像来改进数据集,以提高结果准确性。
  • 这个过程称为有监督学习。在有监督学习中,只有当您拥有广泛且充分多样化的数据集中,结果准确性才会提高。
  • 例如,该算法可能可以准确识别黑猫,但是不能准确识别白猫,因为训练数据集包含更多黑猫图像。在这种情况下,需要标记更多的白猫图像并再次训练机器学习模型。

深度学习相对于机器学习有什么好处?

  • 与传统的机器学习相比,深度学习网络具有以下优势:
    • 高效处理非结构化数据
      • 机器学习方法发现非结构化数据(如文本文档)难以处理,因为训练数据集可能有无限种变化。另一方面,深度学习模型可以理解非结构化数据并进行一般观察,而无需手动提取特征
    • 隐藏的关系和模式发现
      • 深度学习应用程序可以更深入地分析大量数据,并揭示可能尚未对其进行过训练的新见解。例如,考虑一个经过训练以分析消费者购买的深度学习模型。该模型仅包含您已购买的商品的数据。但是,通过将您的购买模式与其他类似客户的购买模式进行比较,人工神经网络可以向您推荐您尚未购买的新物品
    • 无监督学习
      • 深度学习可以根据用户行为进行学习,并随时间推移进行改进。它们不需要大量不同版本的标注数据集。例如,考虑通过分析您的键入行为自动更正或推荐词汇的神经网络。假设该模型是使用英语训练的,因此可对英语词汇进行拼写检查。但是,如果您经常键入非英语词汇,如 danke,神经网络也可以自动学习和更正这些词汇
    • 易失性数据处理
      • 易失性数据集具有各种不同的版本。银行的贷款还款额就是其中的一个例子。深度学习神经网络也可以对这些数据进行分类和排序,例如通过分析金融交易并标记其中一些交易以进行欺诈检测

模型训练和模型部署

  • 模型训练重点关注的是如何通过训练策略来得到一个性能更好的模型。整个流程包含从训练样本的获取(包括数据采集和标注),模型结构的确定,损失函数和评价指标的确定,到模型参数的训练,这部分更多是业务方去承接相关工作。

  • 一旦训练得到了一个指标不错的模型,如何将这个模型赋能到实际业务中,充分发挥其能力,这就是部署方需要承接的工作。

  • 因此,一般来说,学术界负责各种 SOTA(State of the Art) 模型的训练和结构探索,而工业界负责将这些 SOTA 模型应用落地,赋能百业

  • 模型部署一般无需再考虑如何修改训练方式或者修改网络结构以提高模型精度,更多的是需要明确部署的场景、部署方式(中心服务化还是本地终端部署)、模型的优化指标,以及如何提高吞吐率和减少延迟等。

简介

  1. 深度学习(deep learning)是机器学习的分支,是一种以人工神经网络为架构,对资料进行表征学习的算法。
  2. 深度学习的好处是用非监督式或半监督式的特征学习和分层提取高效算法来替代手工获取特征。
  3. 表征学习的目标是寻求更好的表示方法并建立更好的模型来从大规模未标记数据中学习这些表示方法。表示方法来自神经科学,并松散地建立在类似神经系统中的信息处理和对通信模式的理解上
  4. 至今已有深度学习框架,如
    1. 深度神经网络
    2. 卷积神经网络
    3. 深度置信网络
    4. 循环神经网络
  5. 深度学习的基础是机器学习中的分散表示(distributed representation)。分散表示假定观测值是由不同因子相互作用生成。在此基础上,深度学习进一步假定这一相互作用的过程可分为多个层次,代表对观测值的多层抽象。不同的层数和层的规模可用于不同程度的抽象。

神经网络(西瓜书)

  1. 神经元模型
    1. 神经网络是由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所做出的交互反应。[Kohonen, 1988]
    2. 神经网络中最基本的成分是神经元(neuron)模型,即上述定义中的“简单单元”
    3. 感知机与多层网络
      1. 感知机(Perceptron)由两层神经元组成,输入层接受外界输入信号后传递给输出层,输出层是M-P神经元,亦称“阈值逻辑单元”(threshold logic unit)
      2. 输出层与输入层之间的一层神经元,被称为隐层或隐含层(hidden layer),隐含层和输出层神经元都是拥有激活函数的功能神经元
      3. 常见的神经网络,每层神经元与下一层神经元全互连,神经元之间不存在同层连接,也不存在跨层连接,这样的神经网络结构通常称为“多层前馈神经网络”(multi-layer feedforward neural networks),其中输入层神经元接受外界输入,隐层与输出层神经元对信号进行加工,最终结果由输出层神经元输出;换言之,输入层神经元仅是接受输入,不进行函数处理,隐层与输出层包含功能神经元。
        1. “前馈”并不意味着网络中信号不能向后传,而是指网络拓扑结构上不存在环或回路
        2. 只需要包含隐层,即可称为多层网络。
        3. 神经网络的学习过程,就是根据训练数据来调整神经元之间的“连接权”(connection weight)以及每个功能神经元的阈值;换言之,神经网络“学”到的东西,蕴涵在连接权与阈值中。
    4. 误差逆传播算法(亦称“反向传播算法”)
      1. 多层网络的学习能力比单层感知机强得多,想要训练多层网络,简单感知机学习规则显然不够,需要更加强大的学习算法,误差逆传播算法(errorBackPropagation, BP)就是其中最杰出的代码,它是迄今为止最成功的神经网络学习算法。
      2. BP算法不仅可用于多层前馈神经网络,还可用于其他类型的神经网路,例如训练递归神经网络,但是通常说“BP网络”时,一般是指用BP算法训练的多层前馈神经网络。
    5. 深度学习
      1. 理论上来说,参数越多的模型复杂度越高、容量越大,这意味着它能够完成更复杂的学习任务。但是在一般情形下,复杂模型的训练效率低,易陷入过拟合,因此难以受到人们青睐
      2. 我们可以从另外一个角度来理解深度学习。无论是DBN还是CNN,其多隐层堆叠、每层对上一层的输出进行处理的机制,可看作是在对输入信号进行逐层加工,从而把初始的、与输出目标之间联系不太密切的输入表示,转化成与输出目标联系更密切的表示,使得原来仅基于最后一层输出映射难以完成的任务成为可能。换言之,通过多层处理,逐渐将初始的“低层”特征表示转化为“高层”特征表示后,用“简单模型”即可完成复杂的分类等学习任务,由此可将深度学习理解为“特征学习”(feature learning)或“表示学习”(representation learning)

深度神经网络(Deep Neural Network, DNN)

  1. 深度神经网络是一种具备至少一个隐层的神经网络。与浅层神经网络类似,深度神经网络也能够为复杂非线性系统提供建模,但多出的层次为模型提供了更高的抽象层次,因而提高了模型的能力。深度神经网络通常都是前馈神经网络,但也有语言建模方面的研究将其拓展到循环神经网络。卷积神经网络在计算机视觉领域得到了成功的应用。
  2. 深度神经网络是一种判别模型,可以使用反向传播算法进行训练。权重更新可以使用下式进行随机梯度下降法求解。
  3. 与其它神经网络模型类似,如果仅仅是简单地训练,深度神经网络可能会存在很多问题。常见的两类问题是过拟合过长的运算时间

Cmake & VScode

Create a CMake project

  1. makdir cmakeQuickStart
  2. cd cmakeQuickStart
  3. code .
  4. Open the Command Palette and run the CMake:Quick Start command
  5. main.cpp , CMakeLists.txt(which tells the CMake tools how to build your project)
  6. CMake Tools 插件能够给C/C++插件提供信息,实现IntelliSense、代码补全、注释浏览、文件跳转等功能

Opencv DNN

框架

  1. 在深度学习初始阶段,每个深度学习研究者都需要写大量的重复代码。为了提高工作效率,这些研究者就将这些代码写成了一个框架放到网上让所有研究者一起使用。接着,网上就出现了不同的框架。随着时间的推移,最为好用的几个框架被大量的人使用从而流行了起来。全世界最为流行的深度学习框架有
    1. PaddlePaddle(Baidu)
      1. PaddlePaddle是Baidu研发的开源开放的深度学习平台,是国内最早开源、也是当前唯一一个功能完备的深度学习平台。
    2. Tensorflow(Google)
    3. Caffe
    4. Theano
    5. MXNet
    6. Torch(NYU/Facebook)
      1. Torch是Facebook的开源机器学习库、科学计算框架和基于Lua编程语言的脚本语言。
    7. PyTorch

PyTorch

  1. PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序

TensorFlow(符号数学系统)

  1. TensorFlow是一个基于数据流变成(dataflow programming)的符号数学系统,被广泛应用于各类机器学习(machine learning)算法的编程实现,其前身是谷歌的神经网络算法库DistBelief。

Caffe

  1. Caffe(Convolutional Architecture for Fast Feature Embedding),卷积神经网络框架,是一个兼具表达性、速度和四维模块化的深度学习框架。
  2. 结构:
    1. 简单来讲,Caffe中的数据结构是以Blobs-layers-Net形式存在。其中,Blobs是通过4维向量形式(num, channel, height, width)存储网络中所有权重,激活值以及正向反向的数据。
    2. 作为Caffe的标准数据格式,Blob提供了统一内存借口。Layers表示的是神经网络中具体层,例如卷积层等,是Caffe模型的本质内容和执行计算的基本单元。layer层接受底层输入的Blobs,向高层输出Blobs。在每层会实现前向传播,后向传播。Net是由多个层连接在一起,组成的有向无环图。一个网络讲最初的data数据层加载数据开始到最后的loss层组合为整体

ONNX

  1. 了解Open Neural Network Exchange(ONNX)来帮助优化机器学习模型的推理。推理或模型评分是将部署的模型用于预测(通常针对生产数据)的阶段。
  2. 什么是ONNX?
    1. ONNX(Open Neural Network Exchange),开放神经网络交换,是一种模型IR,用于在各种深度学习训练和推理框架转换的一个中间表示格式。在实际业务中,可以使用Pytorch或者TensorFlow训练模型,导出ONNX格式,然后在转换成目标设备上支撑的模型格式,比如TensorRT Engine、 NCNN、MNN等格式。ONNX定义了一组和环境、平台均无关的标准格式,来增强各种AI模型的可交互性,开放性较强。
    2. 换句话说,无论你使用何种训练框架训练模型(例如TensorFlow/Pytorch/OneFlow/Paddle),在训练完毕后你都可以将这些框架的模型统一转换为ONNX这种统一的格式进行存储,转为ONNX模型,然后就可以放在其他框架上面去推理(inference)。
  3. ProtoBuf简介
    1. 在分析ONNX组织格式前,需要了解Protobuf。ONNX作为一个文件格式,自然需要一定的规则去读取想要的信息或者写入需要保存的信息。ONNX使用的是Protobuf这个序列化数据结构去存储神经网络的圈中信息。熟悉Caffe(深度学习框架)或者Caffe2的应该知道,它们的模型存储数据结构协议也是Protobuf。
    2. Protobuf是一种轻便高校的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了C++、Java、Python三种语言的API
  4. ONNX中最核心的部分是onnx.proto。它定义了ONNX这个数据协议的规则和一些其他信息,其中最核心的几个对象:
    1. ModelProto
      1. 包含了一些版本信息,生产者信息和一个GraphProto
    2. GraphProto
      1. 包含了四个repeated数组,分别是
        1. node(NodeProto类型)
          1. 存放模型中所有的计算节点
        2. input(ValueInfoProto类型
          1. 存放模型的输入节点
        3. output(ValueInfoProto类型)
          1. 存放模型中所有的输出节点
        4. initializer(TensorProto类型)
          1. 存放模型的所有权重参数
    3. NodeProto
    4. ValueInfoProto
    5. TensorProto
    6. AttributeProto

使用Google inception-5h实现图像分类

  1. DNN模块介绍:
    1. OpenCV的DNN模块是在OpenCV3.3合并到OpenCV release中的,它最早是在扩展模块中的,可以导入caffe、tensorflow、pytorch等深度学习框架训练生成的模型文件,从而正向传递实现预测功能
  2. 加载模型读取网络信息
    1. 模型可以使用readNet API来加载 Net cv::dnn::readNet(const String& model, const String& config = String(), const String& framework = String());
      1. model是训练好的二进制网络权重文件,支持多种框架训练出来的模型
      2. config是二进制模型的描述文件,不同的框架配置文件有不同的扩展名
      3. framework则声明模型对应框架名称
    2. 也可以使用
      1. Net readNetFromTensorflow(const String& model, const String& config = String());
      2. Net readNetFromCaffe(const String& prototxt, const String& caffeModel = String());
      3. 其他等API直接加载对应框架训练出来的模型
  3. 模型正向传递预测
    1. 使用模型实现预测的时候,需要读取图像作为输入,网络模型支持的输入数据是四维的输入,所以要把读取到的Mat对象转换为四维张量,OpenCV提供的API如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      {
      Mat blobFromImage(
      InputArray image,
      double scalefactor = 1.0,
      const Size& size = Size(),
      const Scalar& mean = Scalar(),
      bool swapRB = false,
      bool crop = false,
      int ddepth = CV_32F
      )
      }
      1. image:输入图像
      2. scalefactor:默认是1.0
      3. size表示网络接受的数据大小
      4. mean表示训练时数据集的均值
      5. swapRB是否互换Red与Blur通道
      6. crop是剪切
      7. ddepth是数据类型
    2. 模型的输入和正向传递则使用如下两个API

      1. void setInput(InputArray blob, const String& name = "");
      2. Mat forward(const String& outputName = String());
  4. 输出预测结果
    1. 首先读取标签文件,定义一个读取文件的额函数read_class_names();

模块(DNN)的应用–图像分类

训练(training)和推理(inference)

  1. 训练(training)包含了前向传播和后向传播两个阶段,针对的是训练集,训练时通过误差反向传播来不断修改网络权值(weights)
    1. 训练一个神经网络时,训练数据被输入该网络的第一层,同时一个神经元会基于所执行的任务为该输入分配一个权重–即该输入正确与否的程度
    2. 在一个用于图像识别的网络中,第一层可能是用来寻找图像的边缘。第二层可能是寻找这些边所构成的形状–矩形或者是圆形。第三层可能是寻找特定的特征–比如闪亮的眼睛或者按钮式的鼻子。每一层都会将图像传递给下一层,直到最后一层;最后的输出由该网络所产生的所有这些权重总体决定。
  2. 推理(inference)只包含前向传播一个阶段,针对的是除了训练集之外的新数据。可以是测试机,但不完全是,更多的是整个数据集之外的数据。其实就是针对新数据进行预测,预测时,速度是一个很重要的因素。
    1. 神经网络训练好了,已经为推理做好了准备
    2. 准确分配加权了的神经网络本质上是一个笨重的巨型数据库。此前,为了让它学习而准备的各种东西已经远远多余它在完成某一特定任务时所需要的内容
    3. 在现实世界中,如果有人想要使用这些训练,真正所需要的是一种可以保持学习并能将其学习成果应用于其从未见过的数据的快速应用,这就是推理:只需要少的多的真实数据,就能快速得出正确答案(实际上是对什么是正确的预测)
      1. 训练是为了让“猫”这个线索得以抽象,推理就是利用这个抽象在应用场景下给出是不是“猫”这个结论。

数据预处理与名字解释

  1. 深度学习中的数据预处理有哪些方式?
    1. 数据归一化。包括高斯归一化、最大最小值归一化等
    2. 白化。许多深度学习算法都以来于白化来获得更好的特征。所谓的白化,以PCA白化来说,就是对PCA降维后的数据的每一列除以其特征值的根号。
  2. 为什么需要图像预处理?
    1. 图像预处理的主要目的是消除图像中无关的信息,恢复有用的真实信息,增强有关信息的可检测性和最大限度地简化数据,从而改进特征抽取,图像分隔,匹配和识别的可靠性。
  3. 为什么需要对数据进行归一化处理,归一化的方式有哪些?
    1. 为了后面数据处理的方便,归一化的确可以避免一些不必要的数值问题
    2. 为了程序运行时收敛加快
    3. 同一量纲。样本数据的评价标准不一样,需要对其量纲化,统一评价标准。这算是应用层面的需求
    4. 避免神经元饱和。当神经元的激活在接近0或者1时会饱和,在这些区域,梯度几乎为0,这样,再反向传播过程中,局部梯度就会接近0,这样会有效地“杀死”梯度
    5. 保证输出数据中数值小的不被吞食
    6. 归一化的方式主要有:线性归一化、标准差归一化、非线性归一化
  4. 数据增强的方式有哪些?
    1. 翻转、旋转、缩放、裁剪、平移、尺度变化、添加噪声、色彩抖动、亮度调整、对比度增强
    2. 数据增强可以分为两类:离线增强和在线增强
      1. 离线增强:直接对数据集进行处理,数据的数目会变成增强因子乘以原数据集的数目,这种方法常常用于数据集很小的时候。
      2. 在线增强:这种增强的方法用于,获得batch数据之后,对这个batch的数据进行增强,如旋转、平移、翻转等相应的变化,由于有些数据集不能接受线性级别的增长,这种方法用于大的数据集

后处理

整体上说,后处理的CPU实现分为三部分:

  1. 框信息的预处理,类似一个decode过程
  2. NMS过程,实现框的选择和合并
    • NMS过程是后处理的重点.基本原理是针对每一个类,每次循环选取这个类中概率最高的框与其他所有框计算IOU.如果某个框与最大概率框的IOU大于nms_thresh.这个框对应的概率被设为-1,表示被过滤掉了.
    • IOU,为两个框重叠面积与总面积的比值
  3. 框排序,当选出的结果框数量大于maxBoxNum时选取score最大的maxBoxNum个

吴恩达 深度学习课程

  1. Binary Classification(二分分类)
    1. 在二分分类问题中,目标是训练处一个分类器,它以图片的特征向量x作为输入,预测输出的结果标签y是1还是0,也就是预测图片中是否有猫
  2. logistic regression(logistic 回归)
    1. 它是一个学习算法,用在监督学习问题中输出标签是0还是1时,二元分类问题

雷锋网

  • https://www.leiphone.com/category/ai/DZeAwe2qgx8JhbU8.html

大部分深度学习框架都包含以下五个核心组件

  • 张量 (Tensor
  • 基于张量的各种操作
  • 计算图(Computation Graph
  • 自动微分工具(Automatic Differentiation)工具
  • BLAS, cuBLAS, cuDNN等扩展包

张量

  • 张量是所有深度学习框架中最核心的组件,因为后续的所有运算和优化算法都是基于张量进行的

  • 几何代数中定义的张量是, 基于向量和矩阵的推广,通俗一点理解的话,可以将标量视为零阶张量,矢量视为一阶张量,那么据栈就是二阶张量

  • 举例来说,可以将任意一张RGB彩色图片表示称一个三阶张量, 三个纬度分别是图片的高度,宽度和色彩数据。

  • 将这一定义进行扩展,我们也可以用四阶张量表示一个包含多张图片的数据集,其中的四个纬度分别是 – 图片在数据集中的编号,图片高度,宽度,以及色彩数据。

  • 将各种各样的数据抽象成张量表示,然后再输入神经网络模型进行后续处理是一种非常必要且高效的策略

  • 因为,如果没有这一步骤,我们就需要根据各种不同类型的数据组织形式定义各种不同类型的数据操作,这会浪费大量的开发这精力。更关键的是,当数据处理完成后,我们还可以方便地将张量再转回想要的格式。

基于张量的各种操作

  • 整个神经网络都可以简单视为为了达到某种目的,针对输入张量进行的一系列操作过程。而所谓的“学习”就是不断纠正神经网络的实际输出结果和预期结果之间的误差的过程。

  • 这里的一系列操作包含的范围很宽,可以是简单的矩阵乘法,也可以是卷积,池化和LSTM等稍复杂的运算。而且各框架支持的张量操作通常也不尽相同。

  • 需要指出的是,大部分的张量操作都是基于类实现的(而且是抽象类),而并不是函数(这一点可能要归功于大部分的深度学习框架都是用面向对象的编程语言实现的)。

  • 这种实现思路一方面允许开发这将各种类似的操作汇总在一起,方便组织管理。另一方面也保证了整个代码的复用性,扩展性和对外接口的统一。总体上让整个框架更灵活和易于扩展,为将来的发展预留了空间。

计算图(Computation Graph)

  • 有了张量和基于张量的各种操作之后,下一步就是将各种操作整合起来,输出需要的结果

  • 但不幸的是,随着操作种类和数量的增多,有可能引发各种意想不到的问题,包括多个操作之间应该并行还是顺次执行,如何协同各种不同的底层设备,以及如何避免各种类型的冗余操作等。这些问题有可能拉低整个深度学习网络的运行效率或者引入不必要的Bug,而计算图正是为解决这一问题产生的。

  • 计算图首次被引入人工智能领域是2009年的论文<Learning Deep Architectures for AI>。作者用不同的占位符构成操作结点,以字母构成变量结点,再以有向线段将这些结点连接起来,组成一个表征运算逻辑关系的清晰明了的图型数据结构,这就是最初的计算图

  • 后来随着技术的不断演进,加上脚本语言和低级语言各自不同的特点(概括地说,脚本语言建模方便但执行缓慢,低级语言正好相反),因此业界逐渐形成了一种开发框架:前端用Python等脚本语言建模,后端用C++等低级语言执行(这里低级是就应用层而言),以此综合了两者的优点

  • 这种开发框架大大降低了传统框架做跨设备计算时的代码耦合度,也避免了每次后端变动都需要修改前端的维护开销。而这里,在前端和后端之间起到的关键耦合作用的就是计算图。

  • 将计算图作为前后端之间的中间表示(Intermediate Representations)可以带来良好的交互性,开发这可以将Tensor对象作为数据结构,函数/方法作为操作类型,将特定的操作类型应用于特定的数据结构,从而定义出类似MATLAB的强大建模语言。

  • 因为计算图的引入,开发者得以从宏观上俯瞰整个神经网络的内部结构,就好像编译器可以从整个代码的角度决定如何分配寄存器那样,计算图也可以从宏观上决定代码运行时的GPU内存分配,以及分布式环境中不同底层设备间的相互协作方式。除此之外,现在也有许多深度学习框架将计算图应用于模型调试,可以实时输出当前某一操作类型的文本描述。

自动微分(Automatic Differentiation)工具

  • 计算图带来的另一个好处是让模型训练阶段的梯度计算变得模块化且更为便捷,也就是自动微分法。

  • 我们可以将神经网络视为由许多非线性过程组成的一个复杂的函数体,而计算图则以模块化的方式完整表征了这一函数体的内部逻辑关系,因此微分这一复杂函数体,即求取模型梯度的方法就变成了在计算图中简单地从输入到输出进行一次完整遍历的过程。与自动微分对应,业内更传统的做法是符号微分。

BLAS, CUBLAS, CUDNN等扩展包

  • 通过上述所有模块,已经可以搭建一个全功能的深度学习框架:

    • 将待处理数据转换为张量,针对张量施加各种需要的操作,通过自动微分对模型展开训练,然后得到输出结果开始测试
    • 这个时候还缺 – 运算效率
  • 由于此前的大部分实现都是基于高级语言的,而即时是执行最简单的操作,高级语言也会比低级语言消耗更多的CPU周期,更何况是结构复杂的深度神经网络,因此运算缓慢就成了高级语言的一个天然的缺陷。

  • 目前针对这一个问题有两种解决方案

    • 第一种方法是模拟传统的编译器。就好像传统编译器会把高级语言编译成特定平台的汇编语言实现高效运行一样,这种方法将高级语言转换为C语言,然后在C语言基础上编译,执行。为了实现这种转换,每一种张量操作的实现代码就会预先加入C语言的转换部分,然后由编译器在编译阶段将这些由C语言实现的张量操作综合在一起。目前pyCUDACpython等编译器都已经实现了这一功能
    • 第二种方法就是,利用脚本语言实现前端建模,用低级语言实现后端运行,这意味着高级语言和低级语言之间的交互都发生在框架内部,因此每次的后端变动都不需要修改前端,也不需要完整编译(只需要通过修改编译参数进行部分编译),因此整体速度也就更快
  • 除此之外,由于低级语言的最优化编程难度很高,而且大部分的基础操作其实也都有公开的最优解决方案,因此另一个显著的加速手段就是利用现成的扩展包。

  • 例如,最初用Fortran实现的BLAS(基础线形代数子程序)就是一个非常优秀的基本矩阵(张量)运算库,此外,还有英特尔的MKL(Math Kernel Library)等

  • 为了向开发者提供尽量简单的接口,大部分深度学习框架通常都会将普通的概念抽象化,这可能是造成许多用户感知不到上述五点核心组件的重要原因。

sigmoid 归一化

  • The sigmoid function is a mathematical function that has a characteristic that can take any real value and map it to between 0 to 1 shaped like the letter “S”.

  • Sigmoid功能在机器学习中扮演激活函数的作用,该功能用于在机器学习模型中添加非线性。基本上,该函数确定要传递的值作为输出以及不通过输出传递的值。机器学习和深度学习中使用了7种类型的激活功能

  • Sigmoid神经元本质上是深神经网络的基础。这些sigmoid神经元与感知性相似,但是它们恰好被稍微修饰,因此来自Sigmoid神经元的输出比Perceptron的步骤功能输出更光滑

简介

  • onnx相关笔记

TensorRT, OpenVino, ONNXRuntime, OpenCV dnn

  • 无论用什么框架训练的模型,推荐转为onnx格式,方便部署
  • 支持onnx模型的框架如下
    • TensorRT: 英伟达的,用于 GPU 推理加速。注意需要英伟达 GPU 硬件的支持
    • OpenVino: 英特尔的,用于 CPU 推理加速。注意需要英特尔 CPU 硬件的支持
    • ONNXRuntime: 微软,亚马逊,Facebook和IBM等公司共同开发的,可用于CPU,GPU
    • OpenCV dnn: OpenCV的调用模型的模块
  • pt格式的模型,可以用Pytorch框架部署
  • 推理效率上:TensorRT > OpenVino > ONNXRuntime > OpenCV dnn > Pytorch

onnx推理

  • ONNX的推理分别需要装两种框架
    1
    2
    pip install onnxruntime       # CPU 版本
    pip install onnxruntime-gpu # GPU 版本
  • 而且需要在保存pytorch模型的时候也需要指定cpu和gpu。
  • 这里推理就不需要pytorch框架来支持了,尤其是在容器部署的时候pytorch框架安装包非常大这会导致部署的镜像也很大,如果推理去掉pytorch框架的情况下镜像大小也会减少很多,大大减少部署难度。

onnx概述

  • 深度学习算法大多通过计算数据流图来完成神经网络的深度学习过程。 一些框架(例如CNTK,Caffe2,Theano和TensorFlow)使用静态图形,而其他框架(例如 PyTorch 和 Chainer)使用动态图形。

  • 但是这些框架都提供了接口,使开发人员可以轻松构建计算图和运行时,以优化的方式处理图。 这些图用作中间表示(IR),捕获开发人员源代码的特定意图,有助于优化和转换在特定设备(CPU,GPU,FPGA等)上运行

  • ONNX 的本质只是一套开放的 ML 模型标准,模型文件存储的只是网络的拓扑结构和权重(其实每个深度学习框架最后保存的模型都是类似的),脱离开框架是没办法对模型直接进行 inference 的

  • 为什么使用通用IR

    • 现在很多的深度学习框架提供的功能都是类似的,但是在 API、计算图和 runtime 方面却是独立的,这就给 AI 开发者在不同平台部署不同模型带来了很多困难和挑战,ONNX 的目的在于提供一个跨框架的模型中间表达框架,用于模型转换和部署。ONNX 提供的计算图是通用的,格式也是开源的。

简介

yolov3算法思想

  • yolo的作者将目标检测问题视为回归问题:首先将整幅图划分为S*S的网格,如果目标框的中心店落在这个网格中,那么这个网格就负责预测这个目标
  • 每一个网格都会预测bounding box(边界框), confidence(置信度)以及class probability map(类概率图)
    • bounding box, 包含四个值:x, y, w, h其中(x, y)代表预测框的中心点, (w, h)代表预测框的宽和高
    • confidence, 表示预测框包含目标的可能性,训练时的真值为预测框和真值框的IOU
    • class probability map, 表明这个目标所属类别的置信度

yolov3网络结构

  • yolov3在3个scale的特征图上分别预测不同大小的目标,即在8倍,16倍和32倍的特征图上进行预测,也就是说如果我们的输入为416416,那么yolov3预测时采用的特征图的大小分别为5252, 3232, 1313
  • 总之,yolov3在3中不同尺度的特征图上进行检测,因此如果我们输入416*416大小的图像,它将产生三种不同的输出形状张量: 13*13*255, 26*26*255和52*52*255

残差模块

  • 残差模块(Residual module)最显著的特点是使用了一种shortcut机制,以缓解因增加神经网络中的深度而导致的梯度消失的问题,从而使神经网络更易于训练
  • 它主要使用identity mapping在输入和输出之间建立连接

特征图分析

  • 要详细了解yolo的输出含义,首先需要了解什么是特征图
  • 在讨论CNN网络结构的时候,总经常使用的一个词汇叫做feature map,简单地说,输入图像与卷积核进行卷积操作后就可以获得图像特征.
  • 一般来说,当输入图像经过CNN提取特征时,特征图的数量(卷积核的数量)将增加,同时空间信息将减少,当然提取到的特征也会越来越抽象.随着网络变深,特征图的空间尺寸越来越小,但是通道数目越来越大,这是CNN的特性
  • 当CNN网络自下而上从输入图像中提取特征时,生成的特征图通常在空间尺寸上越来越小,在通道数目上越来越深.这个特性与ROI(感兴趣区)到特征图的映射有关.
  • 将原始图像中的ROI映射到CNN网络空间后,特征图上的空间会变小,甚至成为一个点,但是该点的通道信息会非常丰富.该信息是CNN网络上映射的ROI区域中图像信息的特征表示
  • 由于图像中的像素在空间上紧密相连,这导致了空间上的巨大冗余.因此,通常通过减少空间维度和增加通道维度来消除这种冗余,并尝试在最小维度中获得其最重要的特征.举例:图像的ROI经过CNN映射,在特征图上仅获得一个点,但是该点有85个通道线,因此,ROI的维度已经从原来的[32, 32, 3]变为当前的85维.
  • 这实际上是yolo网络对ROI执行特征提取后获得的85维特征向量.该特征向量的前四个维度表示候选框信息,中间维度表示判断对象是否存在的概率,接下来的80个维度表示80个类别的分类概率信息.

理解输出

  • yolov3网络的输入尺寸为(m, 416, 416, 3),其中m代表每个batch中图像数目.m=1,代表每个batch处理1张输入图像
  • yolov3分3个尺度进行预测,3个尺度的特征图的大小依次为13 x 13, 26 x 26, 52 x 52
  • yolov3中每个cell预测3个bounding box,每个bounding box可以表示为6元组:(tx, ty, yw, th, pc, c)
  • 在COCO数据集中一共有80个类别,此时我们将c扩展成80维向量,这样每个bounding box可以用85维向量进行表示
  • 输入图像尺寸为416*416,将采样缩小32倍,得到特征图的大小为13*13,也就是说,将输入图像划分成13*13的网格,每个cell对应输入图像中对应32*32的区域
  • 如果每个cell在原图中包含物体真实框的中心点时,那么这个cell负责预测该目标,

理解bounding box

  • yolov3网络结构中有3个分支(3个不同尺度的特征图)被送到decode函数来进一步进行解析

NMS后处理

  • 从候选框中选择置信度最高的box
  • 计算当前框和其他框的IOU,如果IOU>iou_threshold,则移除对应的box
  • 重复上述步骤进行迭代,知道剩余框中没有和当前挑选出的box重叠iou大于阈值的框
  • 上述过程主要是为了过滤和当前框具有很大重叠度的框,针对每个目标仅保留网络预测置信度最高的那个框

yolov3的输出

  • 9个anchor会被三个输出张量平分,根据大中小三种size各自取自己的anchor
  • 每个输出y在自己的网络上都会输出3个预测框,这三个框是9除以3得到的,这是作者设置的,我们可以从输出张量的维度来看
  • 13 * 13 * 255:
    • 13 * 13 : 表示网格的数量,将图片分成13 * 13个网格
    • 255 : 3 * (5 + 80)
      • 80:表示能够识别80个物品类别
      • 5:表示位置信息和置信度(x, y, w, h, confidence)
      • 3:表示要输出3个prediction;从代码上来看,3是由num_anchors得到的

作者使用了logistics回归来对每个anchor包围的内容进行了一个目标性平分(objectness score),根据目标性平分来选择anchor prior进行predict,而不是所有的anchor prior都会有输出.

yolov3输出张量解码过程

  • yolo网络输出是一个元组,包含三个张量,代表三个不同尺度,大小是1*255*13*13, 1*255*26*261*255*52*52.(255:表示每个格点输出三个预测框,每个预测框包含85个元素,一共是255个元素)
  • yolov3解码过程包括五个阶段:
    1. 缩小先验框
    2. 生成网格
    3. 生成预测框
    4. 非极大值抑制
    5. 显示预测框
  • 解码过程包含的尺寸变换:
    1. 原尺寸变换为416*416
    2. 为了在特征图上确定预测框的大小和位置,将先验框缩小(416/13, 416/26, 416/52)倍
    3. 在特征图上生成网格,根据输出张量,确定预测框的位置和大小;然后将坐标和宽高信息放大(416/13, 416/26, 416/52)倍,在原图中显示;最后将原图恢复至原来的大小.

opencv预处理:

  • blobFromImage():
    • Mat cv::dnn::blobFromImage(+
      InputArray image, // 输入图像
      double scalefactor = 1.0, // 图像值的乘数
      const Size& size = Size(), // 输出图像的空间大小
      const Scalar& mean = Scalar(),// 带有从通道中减去的平均值的标量.如果图像具有BGR排序且swapRB为真,则值应按(mean-R, mean-G, mean-B)顺序排列
      bool swapRB = false, // 表示需要交换3通道图像中的第一个和最后一个通道的标志
      bool crop = false, // 指示调整大小后是否裁剪图像的标志
      int ddepth = CV_32F // 输出blob的深度.选择CV_32F或者CV_8U
      )
    • 返回格式:NCHW
    • 从图像创建4维blob.可选择从中心调整图像大小和裁剪图像,减去平均值,按比例因子缩放值,交换蓝色和红色通道

opencv后处理:

  • minMaxLoc():

    • void cv::minMaxLoc(
      InputArray src, // 输入的单通道数组
      double* minVal // 指针返回的最小值的指针;如果不需要,则使用NULL
      double* maxVal = 0, // 指针返回的最大值的指针,如果不需要,则使用NULL
      Point* minLoc = 0, // 指向返回的最小位置的指针(在2D情况下),如果不需要,则使用NULL
      Point* maxLoc = 0, // 指向返回的最大位置的指针(在2D情况下),如果不需要,则使用NULL
      InputArray mask = noArray() // 用于选择子数组的可选掩码
      )
    • 功能:查找数组中的全局最小值和最大值(和位置).
  • NMSBoxes():

    • void cv::dnn::NMSBoxes(
      const std::vector& bboxes, // 一组边界框来应用NMS
      const std::vector& scores, // 一组对应的置信度
      const float score_threshold, // 用于按分数过滤的阈值
      const float nms_threshold, // 用于非最大抑制的阈值
      std::vector& indices, // NMS后保存的bbox索引
      const flat eta = 1.f, // 自适应阈值公式中的一个系数
      const int top_k = 0 // 如果>0,则最多保留top_k个选择的索引
      )
    • 功能:在给定框和相应分数的情况下执行非最大抑制

yaml:用来写配置文件的语言

  • YAML,是一个可读性高,用来表达数据序列化的格式
  • YAML,是YAML Ain't a Markup Language的递归缩写
  • 使用场景:
    • 由于实现简单,解析成本很低,YAML特别适合在脚本语言中使用
    • YAML,比较适合做序列化,因为它是宿主语言数据类型直转的
    • YAML做配置文件也不错.写YAML要比写XML快得多,无需关注标签或引号,并且比ini文档功能更强

简介

摘要

  • 讲解神经网络基本的训练和工作原理
  • 讲解反向传播和梯度下降
  • 梯度下降是神经网络的基本学习方法
    讲解损失函数

人工智能的定义

  1. 第一个层面,人们对人工智能的期待可以分为:

    • 智能地把某件特定的事情做好,在某个领域增强人类的智慧,这种方式又叫做智能增强,例如搜索引擎,智能助手等,帮助人类完成某种特定任务,又称为”弱人工智能”,或者”狭义人工智能”.
    • 像人类一样能认知,思考,判断:模拟人类的智能,这样的智能也称为”通用人工智能“(Artificial General Intelligence, AGI),或”强人工智能”.
  2. 第二个层面,从技术的特点来看

    • 机器学习:如果一个程序解决任务(T)的效能(用P表示)随着经验(E)得到了提高,那么这个程序就能从经验(E)中学到了关于任务(T)的知识,并让衡量值(P)得到提高.

      1. 选择一个模型结构(例如逻辑回归,决策树等),这就是上面所说的程序
      2. 用训练数据(输入和输出) 输入模型,这就是上面的经验(E)
      3. 通过不断执行任务(T)并衡量结果(P),让P不断提高,直到达到一个满意的值.
    • 机器学习的各种方法是如何从经验中学习?其大致可分为三种类型:

      1. 监督学习(Supervised Learning) :通过标注的数据来学习
      2. 无监督学习(Unsupervised Learning):通过没有标注的数据来学习,这种算法可以发现数据中自然形成的共同特性(聚类),可以用来发现不同数据之间的联系
      3. 强化学习(Reinforcement Learning):让程序选择和它的环境互动,环境给程序反馈
    • 机器学习领域出现了各种模型,其中,神经网络模型是一个重要的方法

      • 它的原型在1943年就出现了,在生物神经网络中,每个神经元与其他神经元相连,当它兴奋时,就会向相邻的神经元发送化学物质,从而改变这些神经元内的电位;如果某神经元的电位超过了一个阈值,那么它就会被激活(兴奋) ,向其他神经元发送化学物质.
      • 把许多这样的神经元按照一定的层次结构连接起来,我们就构建了一个神经网络
      • 随着数据的丰富和机器算力的增强,人们不断增加神经网络的层次数目,相邻层次之间的输入输出由非线性函数来控制,这就产生了DNN(深度神经网络).
      • 随着人们不断的调整网络结构,DNN也演变成许多不同的网络拓扑结构,例如 CNN(卷积神经网络), RNN(循环神经网络), LSTM(长期短期记忆), GAN(生成对抗网络), Transfer Learning(迁移学习)等,这些模型还在不断演化中
      • 训练AI模型,需要一系列专门的工具,业界有不少成熟的训练平台(TensorFlow, PyTorch, MXNet等),这些平台也在不断演化,支持新的模型,提高训练的效率,改进易用性等.
  3. 第三个层面,从应用的角度来看,狭义人工智能在各个领域都取得了很大的成果

    • 一种是标杆式的任务,例如ImageNet,考察AI模型能否识别图像的类别
    • 另一种是AI技术和各种其他技术结合,解决政府,企业,个人用户的需求.在政府方面,把所有计算,数据,云端和物联网终端的设备联系起来,搭建一个能支持智能决定的系统,现代社会的城市管理,金融,医疗,物流和交通管理等等都运行在这样的系统上.专家称之为智能基础建设

模型的生成与应用

  • 首先要设计一个模型,然后用已标注过的数据来训练这个模型,在训练过程中,模型的各个参数在多次训练中不断得到调整,最后得到了一个达到要求的模型.
  • 这个模型会被用于一个推理模型中,和其他程序模块一起组成一个应用程序或者是服务,能处理新的数据,满足用户的需求.

范式的演化

范式演化的四个阶段:

  1. 第一阶段:经验

    • 人类最早的科学研究,主要以记录和描述自然现象为特征,可以称之为”经验归纳”(第一范式)
  2. 第二阶段:理论

    • 科学家们开始明确定义,速度是什么,质量是什么等等
  3. 第三阶段:计算仿真

    • 从二十世纪中期开始,利用电子计算机对科学实验进行模拟仿真的模式得到迅速普及
  4. 第四阶段:数据探索

    • 在这个阶段,科学家收集数据,分析数据,探索新的规律.
    • 在深度学习的浪潮中出现的许多结果就是基于海量数据学习得来的.有些数据并不是从现实世界中收集而来的,而是由计算机程序自己生成的

数据探索

  • 当人类探索客观世界的时候,大部分情况下,我们是不了解新环境的运行规则.这个时候,我们可以观察自己的行动和客观世界的反馈,判断得失,再总结出规律.这种学习方法,叫强化学习(Reinforcement Learning),可以使用这种方法来找出适合的策略

神经网络的基本工作原理简介

神经元计算模型:

  1. 输入 input:

    • (x1, x2, x3)是外界输入信号,一般是一个训练数据样本的多个属性. 例如:(x1, x2, x3)分别代表了(红, 绿, 蓝)三种颜色,而此神经元用于识别输入的信号是暖色还是冷色
  2. 权重 weights:

    • (w1, w2, w3)是每个输入信号的权重值,以上面的(x1, x2, x3)的例子来说,x1的权重可能是0.92, x2的权重可能是0.2, x3的权重可能是0.03. 权重值相加之后可以不是1
  3. 偏移 bias:

    • 一般的书或者博客上会说:那是因为y = w*x + b, b是偏移值,使得直线能够沿Y轴上下移动,这是用结果来解释原因,并非b存在的真实原因
    • 从生物学上解释,在脑神经细胞中,一定是输入信号的电平/电流大于某个临界值时,神经元细胞才会处于兴奋状态,这个b实际就是那个临界值. 即当: w1 * x1 + w2 * x2 + w3 * x3 >= t时,该神经 细胞才会兴奋
  4. 激活函数 activation:

    • 求和之后,神经细胞已经处于兴奋状态了,已经决定要向下一个神经元传递信号了,但是要传递多强烈的信号,要由激活函数来确定.
    • 如果激活函数是一个阶跃信号的话,会像继电器开合一样咔咔的开启和闭合; 但是在生物体中是不可能有这种装置的,而是一个渐渐变化的过程.所以激活函数都是有一个渐变的过程,也就是是个曲线

神经网络的主要功能

  1. 回归(Regression)或者叫做拟合(Fitting)

    • 单层的神经网络能够模拟一条二维平面上的直线,从而可以完成线性分割任务
    • 所谓回归或者拟合,其实就是给出x值输出y值的过程,并且让y值与样本数据形成的曲线的距离可能的小,可以理解为是对样本数据的一种骨架式的抽象.
  2. 分类(Classification)

    • 使用一个两层的神经网络可以得到一个非常近似的结果,使得分类误差在满意的范围之内.
    • 分类可以理解为对两类或多类样本数据的边界的抽象
    • 对于复杂的函数,一个两层的神经网络是如何做到的?其实从输入层到隐藏层的矩阵计算,就是对输入数据进行了空间变换,使其可以被线性可分,然后在输出层画出一个分界线.而训练的过程,就是确定那个空间变换矩阵的过程.因此,多层神经网络的本质就是对复杂函数的拟合.

激活函数的作用

  1. 激活函数相当于人类的关节
  2. 如果不运用激活函数,输出信号将仅仅是一个简单的线性函数,线性函数是一个一级多项式.线性方程是很容易解决的,但是它们的复杂性有限,并且从数据中学习复杂函数映射的能力更小.一个没有激活函数的神经网络将只不过是一个线性回归模型罢了,不能解决现实世界中的大多数非线性问题.
  3. 没有激活函数,神经网络将无法学习和模拟其他复杂类型的数据,例如图像,视频,音频,语音等.这就是为什么要使用人工神经网络技术,诸如深度学习等来理解一些复杂的事情,一些相互之间具有很多隐藏层的非线性问题

神经网络中的三个基本概念:反向传播, 梯度下降, 损失函数

  • 神经网络训练的最基本的思想就是:先”猜”一个结果,称为预测结果a,看看这个预测结果和事先标记好的训练集中的真是结果y之间的差距,然后调整策略,再试一次,这一次就不是”猜”了,而是有依据地向正确的方向靠近.如此反复多次,一直到预测结果和真实结果之间相差无几,即|a - y| 趋近于 0 ,就结束训练.在神经网络训练中,我们把”猜”叫做初始化,可以随机,也可以根据以前的经验给定初始值

  • 简单总结反向传播和梯度下降的基本工作原理:

    1. 初始化;
    2. 正向计算;
    3. 损失函数提供计算损失的方法;
    4. 梯度下降是在损失函数基础上向着损失最小的点靠近而指引了网络权重调整的方向;
    5. 反向传播把损失值反向传给神经网络的每一层,让每一层都根据损失值反向调整权重;
    6. Go to 2,直到精度足够好

梯度下降

  • 梯度下降三要素:

    1. 当前点;
    2. 方向;
    3. 步长.
  • 为什么说是”梯度下降”?

    1. 梯度:函数当前位置的最快上升点
    2. 下降:与导数相反的方向,用数学语言描述就是那个减号; 亦即与上升相反的方向运动,就是下降.

损失函数

  • 概念:

    • 在各种材料中经常看到的中英文词汇有:误差,偏差,Error,Cost,Loss,损失,代价等,意思都差不多
    • 使用”损失函数”和”Loss Function”这两个词汇,具体的损失函数符号用J来表示,误差值用loss表示
    • “损失”就是所有样本的”误差”的总和
    • 在黑盒子的例子中,如果说”某个样本的损失”是不对的,只能说”某个样本的误差”,因为样本是一个一个计算的.
    • 如果我们把神经网络的参数调整到完全满足独立样本的输出误差为0,通常会令其它样本的误差变得更大,这样作为误差之和的损失函数值,就会变得更大.所以,我们通常会根据某个样本的误差调整权重后,计算一下整体样本的损失函数值,来判定网络是不是已经训练到了可接受的状态.
  • 损失函数的作用:

    • 损失函数的作用,就是计算神经网络每次迭代的前向计算结果与真实值的差距,从而指导下一步的训练正确的方向进行.
    • 如何使用损失函数?具体步骤:
      1. 用随机值初始化前向计算公式的参数;
      2. 代入样本,计算输出的预测值;
      3. 用损失函数计算预测值和标签值(真实值)的误差;
      4. 根据损失函数的导数,沿梯度最小方向将误差回传,修正前向计算公式中的各个权重值;
      5. 进入第二步重复,指导损失函数值达到一个满意的值就停止迭代
  • 神经网络中常用的损失函数:

    • 均方差函数,主要用于回归
    • 交叉熵函数,主要用于分类
    • 二者都是非负函数,极值在底部,用梯度下降法可以求解.

第二步:线性回归

神经网络法

  • 定义神经网络结构:

    1. 输入层
    2. 权重w,b
    3. 输出层
    4. 损失函数
  • 训练过程:

    1. 读取一个样本数据
    2. 前向计算
    3. 反向传播
    4. 更新梯度
  • 推理预测:

    • 推理过程,实际上就是一个前向计算过程,把它单独拿出来,方便对外接口的设计,所以这个方法被设计成了公开的方法

样本特征数据标准化

  • 数据标准化(Normalization),又可以叫做数据归一化

  • 为什么要做标准化?

  • 理论层面上,神经网络是以样本在事件中的统计分布概率为基础进行训练和预测的,所以它对样本数据的要求比较苛刻

    1. 样本的各个特征的取值要符合概率分布,即[0,1]
    2. 样本的度量单位要相同
    3. 神经网络假设所有的输入输出数据都是标准差为1,均值为0,包括权重值的初始化,激活函数的选择,以及优化算法的设计
    4. 数值问题
    5. 梯度更新,如果输出层的数量级很大,会引起损失函数的数量级很大,这样做反向传播时的梯度也就很大,这时会给梯度的更新带来数值问题
    6. 学习率
  • 标准化的常用方法:

    • Min-Max标准化(离差标准化),将数据映射到[0,1]区间
    • 平均值标准化,将数据映射到[-1,1]区间
    • 对数转换
    • 反正切转换
    • Z-Score法(标准差标准化/零均值标准化):把每个特征值中的所有数据,变成平均值为0,标准差为1的数据,最后正态分布
    • 中心化,平均值为0,无标准差要求
    • 比例法,要求数据全是正值

总结:

  1. 标准如果不做标准化,网络发散,训练无法进行;
  2. 训练样本标准化后,网络训练可以得到结果,但是预测结果有问题;
  3. 还原参数值后,预测结果正确,但是此还原方法并不普遍使用;
  4. 标准化测试样本,而不需要还原参数值,可以保证普遍使用;
  5. 标准化标签纸,可以使得网络训练收敛快,但是在预测时需要把结果反标准化,以便得到真实值.

第三步:线性分类

摘要

  • 分类问题在很多资料中都称之为逻辑回归, Logistic Regression, 其原因是使用了线性回归中的线性模型,加上一个Logistic二分类函数,共同构造了一个分类器.
  • 神经网络的一个重要功能就是分类,现实世界中的分类任务复杂多样,但是万变不离其宗,我们都可以用同一种模式的神经网络来处理

逻辑回归模型

  • 回归问题可以分为两类:线性回归和逻辑回归
    1. 逻辑回归(Logistic Regression), 回归给出的结果是事件成功或失败的概率.当因变量的类型属于二值(1/0, 真/假, 是/否)变量时,我们就应该使用逻辑回归
    2. 线性回归使用一条直线拟合样本数据,而逻辑回归的目标是”拟合”0或1两个数值,而不是具体连续数值,所以成为广义线性模型.
    3. 逻辑回归又称Logistic回归分析,常用于数据挖掘,疾病自动诊断,经济预测等领域
    4. 逻辑回归的另外一个名字叫做分类器,分为线性分类器和非线性分类器,然后无论是线性还是非线性分类器,又分为两种:二分类问题和多分类问题.
  • 回归问题->逻辑回归问题->线性逻辑回归即分类问题->线性二分类问题

二分类函数

  • 对率函数Logistic Function,即可以作为激活函数使用,又可以作为二分类函数使用.在二分类任务中,叫做Logistic函数;在作为激活函数时,叫做Sigmoid函数

实现逻辑与或非门

  • 单层神经网络,又叫做感知机,它可以轻松实现逻辑与,或,非门.由于逻辑与,或门,需要有两个变量输入,而逻辑非门只有一个变量输入.但是它们共同的特点是输入为0或1,可以看做是正负两个类别

第四步:非线性回归

第五步:非线性分类

第六步:模型部署

摘要

  • 如果已经用神经网络训练出来了一套权重矩阵,那么在实际生产环境中如何使用这些权重矩阵?
  • 如果需要将训练好的网络用到后台服务中:搭建网络->加载权重矩阵->调用前向计算
  • 如果是在桌面端或手机端应用中使用这些权重矩阵,这就是一个跨平台,跨语言的需求.引出了模型文件的概念,后面也直接简称为模型.
  • 模型文件中不仅包含了所有的权重矩阵,还记录了该神经网络的数据流图.这样不同平台,不同语言都可以根据模型文件里的信息构建网络,加载权重矩阵,执行前向计算.

模型的推理与部署

  • 在经过几天或者几个月的训练之后,得到了一系列的能够满足需求的权重矩阵.将输入数据按一定的顺序与权重矩阵进行运算,就可以得到对应的输出,这个过程就是推理的过程.
  • 如果没有保存这些权重矩阵,那么每次使用之前,需要重新训练,这在计算资源有限的情况下是难以接受的.另外一个选择是将训练好的权重矩阵保存下来,需要使用的时候重新加载权重矩阵.
  • 几乎所有的训练平台都会把网络结构,权重矩阵等信息保存在文件中,这就是我们常说的模型文件,后面也直接简称为模型.
  • 模型文件快问快答:
    1. 为什么需要模型文件?
      • 模型,可以想象为一个”黑盒”,输入是需要处理的一张图像,输出是一个它的类别信息或是一些特征,模型文件也因此保存了能完成这一过程的所有重要信息,并且还能用来再次训练,推理等,方便了模型的传播与发展.
    2. 模型文件描述的是什么?
      • 首先,目前绝大部分的深度学习框架都将整个AI模型的计算过程抽象成数据流图(Data Flow Graphs),用户写的模型构建代码都由框架组建出一个数据流图(也可以简单理解为神经网络的结构),而当程序开始运行时,框架的执行器会根据调度策略一次执行数据流图,完成整个计算.
      • 有了上面的背景知识,很容易想到:为了方便地重用AI模型的计算过程,需要将它运行的数据流图,相应的运行参数(Parameters)和训练出来的权重(Weights)保存下来,这就是AI模型文件主要描述的内容.
    3. AI模型的作用是什么?
      • 以视觉处理为例,人通过眼睛捕获光线,传递给大脑处理,返回图像的一些信息,例如:这是花,那是动物.AI模型的作用就相当于大脑的处理,能根据输入的数据给予一定的判断.
      • 使用封装好的AI模型,那么设计者只需要考虑把输入的数据处理成合适的格式(类似于感光细胞的作用),然后传递给AI模型(大脑),之后就可以得到一个想要的数据.
    4. 模型文件有哪些类型?TensorFlow和其他框架有什么区别?
      • 由于每个深度学习框架都有自己的设计理念和工具链,对数据流图的定义和粒度都不一样,所以每家的AI模型文件都有些区别,几乎不能通用.
      • TensorFlow在设计之处,就考虑了从训练,预测,部署等复杂的需求,所以它的数据流图几乎涵盖了整个过程可能涉及到的操作,例如:初始化,后向求导以及优化算法,设备部署(Device Placement)和分布式化,量化压缩等,所以只需要通过TensorFlow的模型文件就能获取模型完整的运行逻辑,所以很容易迁移到各种平台使用.
    5. 拿到一个模型文件,自己有一些新的数据,就能继续训练AI吗?如果不能,还需要做什么 ?
    • 训练模型的时候,除了网络架构和权重,还有训练时所使用的各种超参,比如:使用的优化器(Optimizer),批量大小(Batch size),学习率(Learning rate),冲量(Momentum)等,这些都会影响我们再训练的效果,需要格外注意.
    1. ONNX文件是什么,如果保存为ONNX文件?
      • 开放式神经网络交换(Open Neural Network Exchange,简称ONNX),是由微软,FaceBook,亚马逊等多个公司一起推出的,针对机器学习设计的开放式文件格式,可以用来存储训练好的模型.它使得不同的人工智能框架可以采用相同的格式存储模型数据并交互.
      • 目前很多机器学习框架都支持ONNX格式,如Pytorch,Caffe2,CNTK,ML,NET,MXNet等,它们都有专门的export_to_onnx方法,通过遍历它们原生的数据流图,转换为ONNX标准的数据流图.而对于TensorFlow这样并不原生支持ONNX的框架,通常会使用图匹配(Graph Matching)的方法转化数据流图
    2. 转化的来的模型文件有什么信息丢失?
      • 由于模型文件仅仅描述了数据流和和权重,并不包含操作符的具体实现,所以不同框架对于”同名”的操作符理解和实现也会有所不同,最终可能得到不完全一致的推理结果
    3. 模型文件是如何与应用程序一起工作的?
      • 应用程序使用模型文件,本质也是要执行模型文件的数据流图.一般有两种方式实现模型文件和应用程序的协作:
        1. 如果有可以独立执行模型文件的运行时(Runtime),例如:系统级别的CoreML,WinML和软件级别的Caffe,DarkNet等,我们就可以在程序中动态链接直接使用
        2. 除此之外,也可以将数据流图和执行数据流图的程序(一般称为Op Kernel)编译在一起,从而脱离运行时,由于单一模型涉及到的操作有限,这样可以极大减少框架所占用的资源.
      • 在将模型集成到应用程序中之前,应该先使用模型查看工具(例如Netron等)查看模型的接口,输入输出的格式和对应的范围,然后对程序中传入模型的输入作对应的预处理工作,否则可能无法得到预期的效果.
    4. 本地机器有GPU,在运行推理模型的时候,怎么能利用上GPU资源?
      • 首先需要安装匹配的显卡驱动,CUDA和GPU版的框架,然后根据框架进行代码调整:
      • 对于TensorFlow这样能够自动做设备部署(Device Placement)的框架,它会尽量把GPU支持的操作自动分配给GPU计算,不太需要额外的适配;
      • 对于PyTorch,MXNet这样不具有自动设备部署功能的框架,可能需要进行一个额外的操作将模型,张量(tensor)从CPU部署到GPU上.

ONNX模型文件

  • ONNX是一个开放式的规范,定义了可扩展的计算图模型,标准数据类型以及内置的运算符.该文件在存储结构上可以理解为是一种层级的结构
    • 最顶层结构是模型(Model):模型记录了该模型文件的基本数据,例如:使用的ONNX标准的版本,使用的运算符集版本,制造商的名字和版本等信息.除此以外,模型中记录着最主要的信息是图(Graph)
    • 图(Graph):可以理解为是计算图的一种描述,是由输入,输出以及节点(Node)组成的,它们之间通过寻找相同的名字实现连接,也就是说,相同名字的变量会被认为是同一个变量,如果一个节点的输出名字和另一个节点的输入名字相同,这两个节点会被认为是连在一起的
    • 节点(Node):就是要调用的运算符,多个节点(Node)以列表的形式在图(Graph)中存储

第七步:深度神经网络

第八步:卷及神经网络

卷积神经网络概述

  • 卷积神经网络(CNN,Convolutional Neural Net)是神经网络的类型之一,在图像识别和分类领域中取得了非常好的效果,比如识别人脸、物体、交通标识等,这就为机器人、自动驾驶等应用提供了坚实的技术基础。

  • 一个典型的卷积神经网络中,会至少包含以下几个层:

    • 卷积层
    • 激活函数层
    • 池化层
    • 全连接分类层
  • 卷积核, 就是一个小矩阵,

  • 在卷积层中,我们会用输入数据与卷积核相乘,得到输出数据,就类似全连接层中的Weights一样,所以卷积核里的数值,也是通过反向传播的方法学习到的

  • 各种卷积核的作用

    • 锐化(sharpness) : 如果一个像素点比周围像素点亮,则此算子会令其更亮
    • 检测竖边(vertical edge) : 检测出了十字线中的竖线,由于是左侧和右侧分别检查一次,所以得到两条颜色不一样的竖线
    • 周边(surround) : 把周边增强,把同色的区域变弱,形成大色块
    • Sobel-Y(sobel y) : 纵向亮度差分可以检测出横边,与横边检测不同的是,它可以使得两条横线具有相同的颜色,具有分割线的效果
    • Identity : 中心为1四周为0的过滤器,卷积后与原图相同
    • 横边检测(horizontal edge) : 检测出了十字线中的横线,由于是上侧和下侧分别检查一次,所以得到两条颜色不一样的横线
    • 模糊(blur) : 通过把周围的点做平均值计算而“杀富济贫”造成模糊效果
    • Sobel-X(sobel x) : 横向亮度差分可以检测出竖边,与竖边检测不同的是,它可以使得两条竖线具有相同的颜色,具有分割线的效果
    • 浮雕(embossing) : 形成大理石浮雕般的效果
  • 卷积神经网络通过反向传播而令卷积核自我学习,找到分布在图片中的不同的feature,最后形成的卷积核中的数据。

  • 但是如果想达到这种效果,只有卷积层的话是不够的,还需要激活函数、池化等操作的配合

  • 我们实现的卷积操作不是原始数学含义的卷积,而是工程上的卷积,可以简称为卷积

  • 在实现卷积操作时,并不会反转卷积核

  • 在传统的图像处理中,卷积操作多用来进行滤波,锐化或者边缘检测啥的。我们可以认为卷积是利用某些设计好的参数组合(卷积核)去提取图像空域上相邻的信息

  • 多入单出的降维卷积

    • 一张图片,通常是彩色的,具有红绿蓝三个通道。我们可以有两个选择来处理
      • 变成灰度的,每个像素只剩下一个值,就可以用二维卷积
      • 对于三个通道,每个通道都使用一个卷积核,分别处理红绿蓝三种颜色的信息
    • 显然第2种方法可以从图中学习到更多的特征,于是出现了三维卷积,即有三个卷积核分别对应输入的三个通道,三个子核的尺寸是一样的,比如都是2x2,这样的话,这三个卷积核就是一个3x2x2的立体核,称为过滤器Filter,所以称为三维卷积
  • 每一个卷积核对应着左侧相同颜色的输入通道,三个过滤器的值并不一定相同。对三个通道各自做卷积后,得到右侧的三张特征图,然后再按照原始值不加权地相加在一起,得到最右侧的白色特征图,这张图里面已经把三种颜色的特征混在一起了,所以画成了白色,表示没有颜色特征了

  • 虽然输入图片是多个通道的,或者说是三维的,但是在相同数量的过滤器的计算后,相加在一起的结果是一个通道,即2维数据,所以称为降维。这当然简化了对多通道数据的计算难度,但同时也会损失多通道数据自带的颜色信息

  • 卷积编程模型, 五个概念

    • 输入 – Input Channel
    • 卷积核组 – WeightsBias
    • 过滤器 – Filter
    • 卷积核 – Kernel
    • 输出 – Feature Map
  • 输入是三维数据(3x32x32),经过2x3x5x5的卷积后,输出为三维(2x28x28),维数并没有变化,只是每一维内部的尺寸有了变化,一般都是要向更小的尺寸变化,以便于简化计算

  • 三维卷积的特点:

    • 预先定义输出的feature map的数量,而不是根据前向计算自动计算出来
    • 对于每个输出,都有一个对应的过滤器Filter
    • 每个Filter内都有一个或多个卷积核Kernal,对应每个输入通道(Input Channel),
    • 每个Filter只有一个Bias值,Filter-1对应b1,Filter-2对应b2
    • 卷积核Kernal的大小一般是奇数如:1x1, 3x3, 5x5, 7x7等
  • 步长:

    • 每次计算后,卷积核会向右或者向下移动一个单元,即步长stride = 1
    • 卷积核每次向右或向下移动两个单元,即stride = 2
  • 一般情况下,我们用正方形的卷积核,且为奇数

  • 如果计算出的输出图片尺寸为小数,则取整,不做四舍五入

卷积神经网络应用

  • 卷积神经网络是现在深度学习领域中最有用的网络类型,尤其在计算机视觉领域更是一枝独秀。卷积神经网络从90年代的LeNet开始,沉寂了10年,也孵化了10年,直到2012年AlexNet开始再次崛起,后续的ZF Net、VGG、GoogLeNet、ResNet、DenseNet,网络越来越深,架构越来越复杂,解决反向传播时梯度消失的方法也越来越巧妙。下面让我们一起学习一下这些经典的网络模型

  • LeNet是卷积神经网络的开创者LeCun在1998年提出,用于解决手写数字识别的视觉任务。自那时起,卷积神经网络的最基本的架构就定下来了:卷积层、池化层、全连接层

第九步:循环神经网络

简介

  • pytorch相关笔记

pytorch生态部署方案

  • Pytorch虽然社区生态好,各种功能模块都是非常好用,但是部署生态一直饱受诟病。
  • ONNX的规范及代码主要由微软,亚马逊,Facebook和IBM等公司共同开发,也主要是为了来对抗谷歌的Tensorflow框架的部署生态。

Pytorch保存模型转化为ONNX模型

  • 通过Pytorch保存下来的模型pth,因为pth部署无法跨平台移植,我们必须将模型转化成通用格式
  • 优化由于推理(或模型评分)的机器学习模型非常困难,因为需要调整模型和推理库,充分利用硬件功能。如果想要在不同类型的平台(云/Edge、CPU/GPU 等)上获得最佳性能,实现起来会异常困难,因为每个平台都有不同的功能和特性。 如果模型来自需要在各种平台上运行的多种框架,会极大增加复杂性。 优化框架和硬件的所有不同组合非常耗时。此时ONNX的作用就显现出来的,专注于模型推理。

简介

  • tensorflow serving库相关笔记

TensorFlow Serving是什么

  • TensorFlow Serving 是一个适用于机器学习模型的灵活、高性能应用系统,专为生产环境而设计。借助 TensorFlow Serving,您可以轻松部署新算法和实验,同时保留相同的服务器架构和 API。TensorFlow Serving 提供与 TensorFlow 模型的开箱即用型集成,但也可以轻松扩展以应用其他类型的模型和数据。

C++ tensorflow serving库 详解

TensorFlow Serving 是一个用于部署机器学习模型的开源库,它提供了一个高性能、灵活的方式来将训练好的 TensorFlow 模型部署到生产环境中,以便进行实时预测。虽然 TensorFlow Serving 主要是用 C++ 实现的,但它也提供了 Python API 以方便用户使用。

下面是对 TensorFlow Serving C++ 库的详细介绍:

  1. 核心功能

    • 加载和管理模型:TensorFlow Serving 能够加载和管理训练好的 TensorFlow 模型,包括 SavedModel 格式。
    • 提供预测服务:一旦模型被加载,TensorFlow Serving 可以为客户端提供实时的预测服务,支持单个或批量请求。
    • 高性能和可扩展性:TensorFlow Serving 在设计上注重性能和可扩展性,能够处理高并发的请求并保持低延迟。
  2. 架构

    • TensorFlow Serving 的架构主要由三个组件组成:Servable、Loader 和 Servable Manager。
    • Servable 是指 TensorFlow 模型的一种抽象,它代表了一个可部署的模型或模型的版本。
    • Loader 负责加载 Servable,并在需要时将其初始化或卸载。
    • Servable Manager 负责管理 Servable 的生命周期,包括加载、卸载和路由请求等。
  3. 部署流程

    • 准备模型:首先需要使用 TensorFlow 训练模型,并将其保存为 SavedModel 格式。
    • 配置 TensorFlow Serving:创建一个模型配置文件,指定模型的路径、输入和输出等信息。
    • 启动 TensorFlow Serving:运行 TensorFlow Serving 服务器,并指定模型配置文件。
    • 发送请求:客户端向 TensorFlow Serving 发送预测请求,服务器返回预测结果。
  4. 使用示例
    下面是一个简单的 TensorFlow Serving C++ 应用示例,假设我们已经有一个名为 my_model 的 SavedModel:

    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
    #include <iostream>
    #include <grpcpp/grpcpp.h>
    #include "tensorflow_serving/apis/prediction_service.grpc.pb.h"

    using grpc::Channel;
    using grpc::ClientContext;
    using grpc::Status;
    using tensorflow::serving::PredictRequest;
    using tensorflow::serving::PredictResponse;
    using tensorflow::serving::PredictionService;

    class TensorFlowServingClient {
    public:
    TensorFlowServingClient(std::shared_ptr<Channel> channel)
    : stub_(PredictionService::NewStub(channel)) {}

    // 发送预测请求
    void Predict() {
    PredictRequest request;
    PredictResponse response;
    ClientContext context;

    // 设置输入数据
    // request.mutable_model_spec()->set_name("my_model");
    // 添加输入数据
    // request.mutable_inputs()->...;

    // 发送预测请求
    Status status = stub_->Predict(&context, request, &response);

    // 处理预测结果
    if (status.ok()) {
    // 处理 response 中的输出结果
    } else {
    std::cerr << "RPC failed: " << status.error_message() << std::endl;
    }
    }

    private:
    std::unique_ptr<PredictionService::Stub> stub_;
    };

    int main() {
    // 创建 TensorFlow Serving 客户端
    TensorFlowServingClient client(grpc::CreateChannel("localhost:8500", grpc::InsecureChannelCredentials()));

    // 发送预测请求
    client.Predict();

    return 0;
    }

    该示例演示了如何创建一个 TensorFlow Serving 客户端,并发送预测请求。需要注意的是,你需要根据实际情况修改模型的名称、输入数据等信息。

总的来说,TensorFlow Serving 提供了一个功能强大的 C++ 库,用于部署 TensorFlow 模型并提供实时预测服务。通过了解其核心功能、架构和部署流程,你可以更好地使用 TensorFlow Serving 来满足你的部署需求。

SavedModel格式是什么

SavedModel 是 TensorFlow 中用于保存和加载模型的标准格式。它是一种可移植、可恢复的模型表示形式,可以轻松地在不同的平台上进行部署和共享。SavedModel 以文件夹的形式存储,其中包含了模型的结构、权重参数、计算图以及其他相关的元数据。

SavedModel 主要由两个部分组成:

  1. 模型图(GraphDef):模型的计算图以 Protocol Buffers 格式保存在名为 saved_model.pb 的文件中。计算图定义了模型的结构,包括输入、输出、各种操作(如张量运算、变量等)以及操作之间的连接关系。

  2. 变量和资源:模型的权重参数、变量和其他资源以二进制格式保存在名为 variables/ 的文件夹中。这些文件包含了模型的具体参数值,用于在加载模型时恢复模型的状态。

除了这两个基本部分之外,SavedModel 还可以包含其他的元数据信息,例如模型的签名信息(用于描述模型的输入和输出)、运行配置、版本信息等。

SavedModel 的优点在于它具有跨平台和版本控制的能力,可以在不同的 TensorFlow 版本和不同的硬件平台上进行部署和加载。这使得 SavedModel 成为了 TensorFlow 模型部署和共享的标准格式,被广泛应用于生产环境中。

C++ tensorflow serving加载onnx模型

TensorFlow Serving 最初设计用于加载和提供 TensorFlow 模型的预测服务,但它也支持加载其他格式的模型,包括 ONNX(Open Neural Network Exchange)格式的模型。ONNX 是一种开放的标准,用于表示深度学习模型,它允许用户在不同的深度学习框架之间交换模型,并在不同的硬件平台上运行这些模型。

要在 TensorFlow Serving 中加载 ONNX 模型,你需要先将 ONNX 模型转换为 TensorFlow 的 SavedModel 格式,然后再使用 TensorFlow Serving 加载 SavedModel。以下是一个大致的步骤:

  1. 将 ONNX 模型转换为 TensorFlow SavedModel

    • 使用 ONNX-TensorFlow 或 onnx-tf 工具将 ONNX 模型转换为 TensorFlow 的 SavedModel 格式。这些工具可以帮助你将 ONNX 模型导入到 TensorFlow 中,并将其保存为 SavedModel 格式。
  2. 使用 TensorFlow Serving 加载 SavedModel

    • 使用 TensorFlow Serving 的配置文件(通常是 TensorFlow Serving 的 Docker 镜像中的配置文件),将转换后的 SavedModel 添加到配置中。
    • 启动 TensorFlow Serving 服务器,并指定模型配置文件。

下面是一个大致的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 将 ONNX 模型转换为 TensorFlow SavedModel
onnx-tf convert -i model.onnx -o saved_model/

# 2. 配置 TensorFlow Serving
# 创建一个模型配置文件,例如 model_config.json,指定模型的路径和类型
{
"model_version_policy": { "specific": { "versions": { "1": {} } } },
"model_platform": "tensorflow",
"name": "onnx_model",
"base_path": "/path/to/saved_model"
}

# 3. 启动 TensorFlow Serving
docker run -p 8500:8500 -p 8501:8501 --mount type=bind,source=/path/to/model_config.json,target=/models/onnx_model/config.json -t tensorflow/serving

这里的关键步骤是将 ONNX 模型转换为 TensorFlow 的 SavedModel 格式,并通过 TensorFlow Serving 的配置文件将 SavedModel 添加到 TensorFlow Serving 中。这样就可以在 TensorFlow Serving 中加载和提供 ONNX 模型的预测服务了。

docker方式部署(官方示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Download the TensorFlow Serving Docker image and repo
docker pull tensorflow/serving

git clone https://github.com/tensorflow/serving
# Location of demo models
TESTDATA="$(pwd)/serving/tensorflow_serving/servables/tensorflow/testdata"

# Start TensorFlow Serving container and open the REST API port
docker run -t --rm -p 8501:8501 \
-v "$TESTDATA/saved_model_half_plus_two_cpu:/models/half_plus_two" \
-e MODEL_NAME=half_plus_two \
tensorflow/serving &

# Query the model using the predict API
curl -d '{"instances": [1.0, 2.0, 5.0]}' \
-X POST http://localhost:8501/v1/models/half_plus_two:predict

# Returns => { "predictions": [2.5, 3.0, 4.5] }

拉取serving docker image

从serving docker image 启动一个容器

  • serving images有以下属性
    • 8500 端口服务于gRPC
    • 8501 端口服务于 REST API
    • 可选的环境变量 MODEL_NAME(默认为 model)
    • 可选的环境变量 MODEL_BASE_PATH(默认为 /models)
  • 当 serving image运行一个容器时,其内部实际运行的命令是
    1
    2
    tensorflow_model_server --port=8500 --rest_api_port=8501 \
    --model_name=${MODEL_NAME} --model_base_path=${MODEL_BASE_PATH}/${MODEL_NAME}

简介

  • 计算机视觉相关笔记

计算机视觉(维基百科)

  • 计算机视觉(Computer vision)是一门研究如何使机器“看”的科学,更进一步的说,就是指用摄影机和计算机代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图像处理,用计算机处理成为更适合人眼观察或传送给仪器检测的图像

  • 作为一门科学学科,计算机视觉研究相关的理论和技术,试图建立能够从图像或者多维数据中获取“信息”的人工智能系统。这里所指的信息指香农定义的,可以用来帮助做一个“决定”的信息。因为感知可以看作是从感官信号中提取信息,所以计算机视觉也可以看作是研究如何使人工系统从图像或多维数据中“感知”的科学

  • 作为一个工程学科,计算机视觉寻求基于相关理论与模型来建立计算机视觉系统。这类系统的组成部分包括:

    • 过程控制(例如工业机器人和无人驾驶汽车)
    • 事件监测(例如图像监测)
    • 信息组织(例如图像数据库和图像序列的索引建立)
    • 物体与环境建模(例如工业检查,医学图像分析和拓扑建模)
    • 交感互动(例如人机互动的输入设备)
  • 计算机视觉同样可以被看作是生物视觉的一个补充。在生物视觉领域中,人类和各种动物的视觉都得到了研究,从而建立了这些视觉系统感知信息过程中所使用的物理模型。另一方面,在计算机视觉中,靠软件和硬件实现的人工智能系统得到了研究与描述。生物视觉与计算机视觉进行的学科间交流为彼此都带来了巨大价值。

  • 计算机视觉的发展现状

    • 在计算机视觉的大多数实际应用当中,计算机被预设为解决特定的任务,然而基于机器学习的方法正日渐普及,一旦机器学习的研究进一步发展,未来“泛用型”的电脑视觉应用或许可以成真。
    • 人工智能所研究的一个主要问题是:如何让系统具备“计划”和“决策能力”?从而使之完成特定的技术动作(例如:移动一个机器人通过某种特定环境)。这一问题便与计算机视觉问题息息相关。在这里,计算机视觉系统作为一个感知器,为决策提供信息。另外一些研究方向包括模式识别和机器学习(这也隶属于人工智能领域,但与计算机视觉有着重要联系),也由此,计算机视觉时常被看作人工智能与计算机科学的一个分支。
    • 物理是与计算机视觉有着重要联系的另一领域。
    • 计算机视觉关注的目标在于充分理解电磁波——主要是可见光与红外线部分——遇到物体表面被反射所形成的图像,而这一过程便是基于光学物理和固态物理,一些尖端的图像传感器甚至会应用到量子力学理论,来解析影像所表示的真实世界。同时,物理学中的很多测量难题也可以通过计算机视觉得到解决,例如流体运动。也由此,计算机视觉同样可以被看作是物理学的拓展
    • 另一个具有重要意义的领域是神经生物学,尤其是其中生物视觉系统的部分
    • 计算机视觉的另一个相关领域是信号处理。很多有关单元变量信号的处理方法,尤其对是时变信号的处理,都可以很自然的被扩展为计算机视觉中对二元变量信号或者多元变量信号的处理方法。但由于图像数据的特有属性,很多计算机视觉中发展起来的方法,在单元信号的处理方法中却找不到对应版本。这类方法的一个主要特征,便是他们的非线性以及图像信息的多维性,以上二点作为计算机视觉的一部分,在信号处理学中形成了一个特殊的研究方向。
    • 除了上面提到的领域,很多研究课题同样可被当作纯粹的数学问题。例如,计算机视觉中的很多问题,其理论基础便是统计学,最优化理论以及几何学。
    • 如何使既有方法通过各种软硬件实现,或说如何对这些方法加以修改,而使之获得合理的执行速度而又不损失足够精度,是现今计算机视觉领域的主要课题。
  • 相邻领域的异同

    • 计算机视觉,图像处理,图像分析,机器人视觉和机器视觉是彼此紧密关联的学科。如果你翻开带有上面这些名字的教材,你会发现在技术和应用领域上他们都有着相当大部分的重叠。这表明这些学科的基础理论大致是相同的,甚至让人怀疑他们是同一学科被冠以不同的名称。
    • 计算机视觉的研究对象主要是映射到单幅或多幅图像上的三维场景,例如三维场景的重建。计算机视觉的研究很大程度上针对图像的内容。
    • 图像处理与图像分析的研究对象主要是二维图像,实现图像的转化,尤其针对像素级的操作,例如提高图像对比度,边缘提取,去噪声和几何变换如图像旋转。这一特征表明无论是图像处理还是图像分析其研究内容都和图像的具体内容无关。
    • 机器视觉主要是指工业领域的视觉研究,例如自主机器人的视觉,用于检测和测量的视觉。这表明在这一领域通过软件硬件,图像感知与控制理论往往与图像处理得到紧密结合来实现高效的机器人控制或各种实时操作。
    • 模式识别使用各种方法从信号中提取信息,主要运用统计学的理论。此领域的一个主要方向便是从图像数据中提取信息。
    • 还有一个领域被称为成像技术。这一领域最初的研究内容主要是制作图像,但有时也涉及到图像分析和处理。例如,医学成像就包含大量的医学领域的图像分析。
  • 计算机视觉的经典问题

    • 几乎在每个计算机视觉技术的具体应用都要解决一系列相同的问题。这些经典的问题包括:
    • 识别
      • 一个计算机视觉,图像处理和机器视觉所共有的经典问题便是判定一组图像数据中是否包含某个特定的物体,图像特征或运动状态。这一问题通常可以通过机器自动解决,但是到目前为止,还没有某个单一的方法能够广泛的对各种情况进行判定:在任意环境中识别任意物体。现有技术能够也只能够很好地解决特定目标的识别,比如简单几何图形识别,人脸识别,印刷或手写文件识别或者车辆识别。而且这些识别需要在特定的环境中,具有指定的光照,背景和目标姿态要求。
      • 广义的识别在不同的场合又演化成了几个略有差异的概念:
        • 识别(狭义的):对一个或多个经过预先定义或学习的物体或物类进行辨识,通常在辨识过程中还要提供他们的二维位置或三维姿态。
        • 鉴别:识别辨认单一物体本身。例如:某一人脸的识别,某一指纹的识别。
        • 监测:从图像中发现特定的情况内容。例如:医学中对细胞或组织不正常技能的发现,交通监视仪器对过往车辆的发现。监测往往是通过简单的图象处理发现图像中的特殊区域,为后继更复杂的操作提供起点
      • 识别的几个具体应用方向:
        • 基于内容的图像提取:在巨大的图像集合中寻找包含指定内容的所有图片。被指定的内容可以是多种形式,比如一个红色的大致是圆形的图案,或者一辆自行车。在这里对后一种内容的寻找显然要比前一种更复杂,因为前一种描述的是一个低级直观的视觉特征,而后者则涉及一个抽象概念(也可以说是高级的视觉特征),即‘自行车’,显然的一点就是自行车的外观并不是固定的。
        • 姿态评估:对某一物体相对于摄像机的位置或者方向的评估。例如:对机器臂姿态和位置的评估。
        • 光学字符识别对图像中的印刷或手写文字进行识别鉴别,通常的输出是将之转化成易于编辑的文档形式。
    • 运动
      • 基于序列图像的对物体运动的监测包含多种类型,诸如:
        • 自体运动:监测摄像机的三维刚性运动
        • 图像跟踪:跟踪运动的物体
    • 场景重建
      • 给定一个场景的二或多幅图像或者一段录像,场景重建寻求为该场景建立一个三维模型。最简单的情况便是生成一组三维空间中的点。更复杂的情况下会建立起完整的三维表面模型。
    • 图像恢复
      • 图像恢复的目标在于移除图像中的噪声,例如仪器噪声、动态模糊等
  • 计算机视觉系统

    • 计算机视觉系统的结构形式很大程度上依赖于其具体应用方向。有些是独立工作的,用于解决具体的测量或检测问题;也有些作为某个大型复杂系统的组成部分出现,比如和机械控制系统,数据库系统,人机接口设备协同工作。计算机视觉系统的具体实现方法同时也由其功能决定——是预先固定的抑或是在运行过程中自动学习调整。尽管如此,有些功能却几乎是每个计算机系统都需要具备的:
    • 图像获取:一幅数字图像是由一个或多个图像传感器产生,这里的传感器可以是各种光敏摄像机,包括遥感设备,X射线断层摄影仪,雷达,超声波接收器等。取决于不同的传感器,产生的图片可以是普通的二维图像,三维图组或者一个图像序列。图片的像素值往往对应于光在一个或多个光谱段上的强度(灰度图或彩色图),但也可以是相关的各种物理数据,如声波,电磁波或核磁共振的深度,吸收度或反射度。
    • 预处理:在对图像实施具体的计算机视觉方法来提取某种特定的信息前,一种或一些预处理往往被采用来使图像满足后继方法的要求。例如:
      • 二次取样保证图像坐标的正确
      • 平滑去噪来滤除传感器引入的设备噪声
      • 提高对比度来保证实现相关信息可以被检测到
      • 调整尺度空间使图像结构适合局部应用
    • 特征提取:从图像中提取各种复杂度的特征。例如
      • 线、边缘提取和脊侦测
      • 局部化的特征点检测如边角检测、斑点检测
      • 更复杂的特征可能与图像中的纹理形状或运动有关
    • 检测/分割:在图像处理过程中,有时会需要对图像进行分割来提取有价值的用于后继处理的部分,例如:
      • 筛选特征点
      • 分割一或多幅图片中含有特定目标的部分
    • 高级处理:到了这一步,数据往往具有很小的数量,例如图像中经先前处理被认为含有目标物体的部分。这时的处理包括:
      • 验证得到的数据是否符合前提要求
      • 估测特定系数,比如目标的姿态、体积
      • 对目标进行分类

简介

  • docker相关的理论基础

理解Docker镜像和容器的存储路径

  • 执行以下命令可以查看Docker的配置信息

    1
    docker info
  • 输出内容包含了存储驱动和docker根目录的信息

  • docker镜像和容器的存储路径

    • Docker容器由网络文件,卷和镜像组成。Docker文件的存储路径取决于你的操作系统。
    • 常用操作系统中的路径如下
      • Ubuntu: /var/lib/docker/
      • Fedora: /var/lib/docker
      • Debian: /var/lib/docker
      • Windows: C:\ProgramData\DockerDesktop
      • MacOS: /Library/Containers/com.docker.docker/Data/vms/0/
    • 在MacOS和Windows系统中,Docker在一个虚拟机中运行Linux容器
  • 查看常规镜像的信息,会得到linux系统中的路径

    1
    2
    3
    4
    docker insepct nginx

    "UpperDir":"/var/lib/docker/overlay2/585...9eb/diff"

  • Docker镜像。最大的文件通常是镜像。如果使用默认的overlay2存储驱动,Docker镜像会保存在/var/lib/docker/overlay2目录

  • LowerDir包含镜像的只读层,表示变更的读写层包含在UpperDir中。

  • MergeDir表示UppeDir和LowerDir合并的结果,Docker用它来运行容器

  • WorkDir是overlay2的内部目录,应该是空的

docker 一般使用什么方式设置自启动服务

在 Docker 中,通常使用以下两种方式之一来设置容器的自启动服务:

  1. 使用 Docker Compose:
    Docker Compose 是一个用于定义和管理多容器应用程序的工具。在 Docker Compose 配置文件中,你可以定义容器的启动顺序和依赖关系。当使用 docker-compose up 启动容器组时,它们将按照定义的顺序自动启动,并在 Docker 守护进程启动时自动启动。

    在 Docker Compose 配置文件中,可以使用 restart 字段来设置容器的自启动行为。以下是一个示例 Docker Compose 配置文件的部分内容:

    1
    2
    3
    4
    5
    version: '3'
    services:
    web:
    image: nginx:latest
    restart: always

    在上述示例中,restart: always 指示 Docker 在容器退出时自动重新启动该容器。

  2. 使用 systemd 或其他宿主机的服务管理工具:
    如果你的 Docker 容器运行在一个宿主机上,并且宿主机使用 systemd 或其他服务管理工具(如 Upstart 或 SysV init),你可以在宿主机上创建一个服务单元文件来管理 Docker 容器的启动和自启动行为。

    在宿主机上,你可以创建一个自定义的 systemd 服务单元文件(通常位于 /etc/systemd/system/ 目录下),并使用 docker run 命令来启动容器。然后,通过配置 systemd 服务单元文件,将 Docker 容器与宿主机的服务管理工具集成在一起,以实现容器的自启动。

    以下是一个示例 systemd 服务单元文件的部分内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [Unit]
    Description=Docker Container: My Service
    Requires=docker.service
    After=docker.service

    [Service]
    ExecStart=/usr/bin/docker run --name my-container my-image
    Restart=always

    [Install]
    WantedBy=default.target

    在上述示例中,ExecStart 字段指定了启动容器的命令。Restart=always 字段指示 systemd 在容器退出时自动重新启动。

无论你选择使用 Docker Compose 还是宿主机的服务管理工具,都可以根据具体需求来设置容器的自启动服务。请注意,在设置自启动服务时,要确保 Docker 服务已经正确安装和启动,并且容器的镜像已经准备好。


docker systemctl 无效

在 Docker 容器中,systemctl 命令无效,因为容器中并没有运行 Systemd 进程,而 systemctl 命令是 Systemd 的服务管理工具。

Docker 容器是基于轻量级的容器化技术构建的,它们通常以单个进程运行,并不包含完整的操作系统。因此,与在主机操作系统上一样使用 systemctl 命令是不可行的。

在 Docker 中,你可以通过以下方式来管理容器内的服务或进程:

  1. 启动和停止容器:使用 docker startdocker stop 命令来启动和停止容器,例如:

    1
    2
    docker start container_name
    docker stop container_name
  2. 运行服务进程:在容器中直接运行服务进程,可以通过 Dockerfile 或者启动容器时指定要运行的命令。

  3. 使用 Docker Compose:如果你在 Docker Compose 中定义了服务,可以使用 docker-compose updocker-compose down 命令来启动和停止整个服务组。

请记住,Docker 容器是设计用于运行单个进程或服务的,而不是一个完整的操作系统环境。因此,与在传统的虚拟机或物理机上管理服务的方式有所不同。你可以通过容器的启动命令和配置来管理容器内的服务和进程。


目录映射

  • -p:指定端口映射,格式为:主机(宿主)端口:容器端口
  • --privileged=true:关闭安全权限,否则容器操作文件没有权限
  • -v:挂在目录为:主机目录:容器目录,在创建前容器是没有指定目录时,docker容器会自己创建
  • 端口映射:-p 8083:8083 -p 5678:5678
  • 目录映射以宿主机为主,换句话说,容器中原本存在的文件一旦进过挂载,就一定会被宿主机覆盖

如何处理overlay2

  • 真正要处理的不是这两个文件,是要检查正在跑的容器内的服务,有没有往容器本地写文件的行为,如果有,则要把写文件的路径挂载出来,或者直接在容器中删除文件

docker 语言支持

  • node.js
  • python
  • java
  • go
  • c#

docker中的overlay2

  • 在安装的docker系统中,删除了容器和镜像,但是docker/overlay2还是占用很大的磁盘空间

docker/overlay2下的文件都是些什么

  • docker/overlay2目录下的文件名基本都是md5编码
  • overlay2是docker使用的文件存储驱动,也就是说,该目录下的文件都是docker使用的存储
    • overlay2是分层存储,每一层通过本层的md5作为文件夹名来命名,如果要存储的两个东西,例如两个镜像的底层中的几层内容是一样的,那么它们的md5也就是一样的,通过md5检验,确认它们这几层是一样的,那么在overlay2实际存储的时候,这几层就可以只存储一份,然后由这两个镜像共同使用,来达到节省空间的目的

docker底层原理

  • 第一次接触docker概念,都会见到或者听见一句话:docker技术比虚拟技术更为方便,快捷,docker容器本质上是进程

  • 所有容器共享宿主机的cpu,磁盘,网络,内存等:

    • 实现了进程隔离,每个服务独立运行
    • 文件系统隔离,容器目录修改不影响主机目录
    • 资源隔离,CPU内存,磁盘,网络资源相互独立
  • Docker容器的实现原理就是通过Namespace命名空间进行进程隔离,Unionfilesystem联合文件系统实现文件系统隔离,ControlGroups控制组实现资源隔离。

  • 其底层原理涉及到linux namespace,Linux Namespace 是Linux提供的一种内核级别环境隔离的方法。 Unix有chroot,提供了一种简单的模式:chroot内部的文件系统无法访问外部的内容。Linux Namespace在此基础上,提供了对UTS, IPC, mount, PID, network, User等的隔离机制

    • https://lwn.net/Articles/531114/
  • Linux Namespace, 有几个种类:Mount namespaces, UTS namespaces, IPC namespaces, PID namespaces, Network namespaces, User namespaces

  • 主要是三个系统调用:

    • clone(), 实现线程的系统调用,用来创建一个新的进程,并且可以通过设计上述参数达到隔离
    • unshare(), 使某个进程脱离某个namespace
    • setns(), 把某进程加入到某个namespace

Docker exec 的实现原理

  • Linux Namespace创建的隔离空间是虚拟的,一个进程的Namespace信息在宿主机上是真实存在的,并且是以一个文件的方式存在。
  • 一个进程,可以选择加入到某个进程已有的Namespace当中,从而达到“进入”这个进程所在容器的目的,这正是docker exec的实现原理
  • 通过setns()可以将当前进程加入到已有的namespace中。

镜像启动容器的本质

  • 首先,需要明确Docker内的文件系统是如何工作的,Docker镜像被存储在一系列的只读层
  • Docker镜像是由多个文件系统(只读层)叠加而成,当启动一个容器的时候,Docker会加载只读层并在其上(镜像栈顶部)添加一个读写层
  • 如果运行中的容器修改了现有的一个已经存在的文件,那么该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除Docker容器,并通过该镜像重启的时候,之前的修改将会丢失
  • 在Docker中,只读层和顶部的读写层的组合被称为联合文件系统(Union File System)
  • Docker镜像可以理解成多个只读文件叠加而成,因此镜像是只读的,当镜像运行起来时,就相当于在只读的镜像外面包裹了一层读写层变成了容器。
  • 当删除容器之后,使用镜像重新创建一个容器时,此时的镜像的只读层和原来一样,只是在读写层的修改会全部丢失
  • 所以,docker的数据持久化说的就是:数据不随容器的删除而消失。

数据卷Volume

  • Docker的数据卷Volume能够让容器从宿主机中读取文件或持久化数据到宿主机内,让容器与容器产生的数据分离开来。
  • 可以简单的把Volume理解为Linux服务器上的挂载点,一个容器可以挂载多个不同的目录
  • Volume的生命周期是独立于容器的声明周期之外的,即使容器删除,Volume也会保留下来,Docker也不会因为这个Volume没有被容器使用而回收。在容器中,添加或修改这个文件夹中的文件也不会影响容器的联合文件系统。
  • Volume数据卷不是用分层文件系统,这对经常读取和写入的数据很有用。在开发过程中,可以将代码目录挂载到容器中,这样如果更改代码容器会实时地得到文件修改的返回文件。

Docker数据管理-数据库容器化并持久化:数据卷概念,创建数据卷的两种方式,docker volume用法

Docker数据管理

  • 在生产环境中使用Docker的过程,往往需要对数据进行持久化,或者需要在多个容器之间进行数据共享,这必然涉及容器的数据管理操作
  • 所谓Docker的数据持久化,即:数据不随着容器的结束而结束。在Docker中,要想实现数据的持久化,需要将数据从宿主机挂载到容器中
  • 容器中管理数据主要有两种方式:
    • 数据卷(Data Volumes):容器内数据直接映射到本地主机环境
    • 数据卷容器(Data Volume Containers):使用特定容器维护数据卷

Docker核心概念:镜像,容器,仓库,架构核心设计理念

Docker核心概念

  • 镜像,通俗地讲,它是一个只读的文件和文件夹组合

  • 它包含了容器运行时所需要的所有基础文件和配置信息,是容器启动的基础。所以想启动一个容器,那首先必须要有一个镜像。镜像是Docker容器启动的先决条件

  • 如果想要使用一个镜像,一般有两种方式:

    • 自己创建镜像。通常情况下,一个镜像是基于一个基础镜像构建的,可以在基础镜像上添加一些用户自定义的内容。形成业务镜像。
    • 从功能镜像仓库拉取别人制作好的镜像
  • 容器,是Docker的另一个核心概念。

  • 通俗地讲,容器是镜像的运行实体。镜像是静态的只读文件,而容器带有运行时需要的可写文件层,并且容器中的进程属于运行状态。即容器运行着真正的应用进程

  • 虽然容器的本质上是主机上运行的一个进程,但是容器有自己独立的命名空间隔离和资源限制。也就是说,在容器内部,无法看到主机上的进程,环境变量,网络等信息,这是容器于直接运行在主机上进程的本质区别。

  • 仓库,Docker的镜像仓库类似于代码仓库,用来存储和分发Docker镜像。

  • 镜像仓库分为公有镜像仓库和私有镜像仓库

Docker架构

  • 相关背景知识—-容器的发展史

    • 容器技术随着Docker的出现变得炙手可热,所有公司都在积极拥抱容器技术。此时市场上除了有Docker容器,还有很多其他的容器技术,例如:CoresOS的rkt, lxc等。容器技术百花齐放是好事,但是也出现了很多问题,比如容器技术的标准到底是什么?
    • 可能会说,Docker已经成为了事实标准,把Docker作为容器技术的标准不可以吗?事实并没有想象的那么简单。因为那个时候不仅有容器标准之争,编排技术之争也十分激烈。当时的编排技术有三大主力,分别是:Docker Swarm, Kubernetes, Mesos。在这样的背景下,为了形成统一的标准,OCI应运而生
    • OCI全称为开放容器标准(Open Container Initiative),它是一个轻量级,开放的治理结构。OCI组织在Linux基金会的大力支持下,于2015年6月分正式注册成立。基金会旨在为用户围绕工业化容器的格式和镜像运行时,制定一个开放的容器标准。目前主要有两个标准文档:**容器运行时标准(runtime spec)和容器镜像标准(image spec)
    • 正是由于容器的战争,才导致Docker不得不改变了一些技术架构。最终,Docker整体架构采用C/S(客户端/服务器)模式,主要由客户端和服务端两大部分组成。客户端负责发送操作指令,服务端负责接受和处理指令。客户端和服务端通信有多种方式,既可以在通一台机器上通过UNIX套接字通信,也可以通过网络连接远程通信。
  • Docker客户端

    • Docker客户端其实是一种泛称。其中docker命令是Docker用户与Docker服务端交互的主要方式。
    • 除了使用docker命令的方式,还可以使用直接请求REST API的方式与Docker服务端交互,甚至还可以使用各种语言的SDK与Docker服务端交互
  • Docker服务端

    • Docker服务端是Docker所有后台服务的统称。
    • 其中dockerd是一个非常重要的后台管理进程,它负责相应和处理来自Docker客户端的请求,然后将客户端的请求转换为Docker的具体操作。例如镜像,容器,网络和挂载卷等具体对象的管理和操作。
    • Docker从诞生到现在,服务端经历了多次架构重构。起初,服务端的组件是全部集成在docker二进制里,但是从1.11版本开始,dockerd已经成了独立的二进制,此时的容器也不是直接由dockerd来启动了,而是继承了containerd, runC等多个组件
    • 虽然Docker的架构在不停重构,但是各个模块的基本功能和定位并没有变化。它和一般的C/S架构系统一样,Docker服务端模块负责和Docker客户端交互,并管理Docker的容器,镜像,网络等资源。
  • Docker重要组件
    • Docker目前已经有了非常多的组件和工具。Docker的两个至关重要的组件:**runCcontainerd**
      • runC是Docker官方按照OCI容器运行时标准的一个实现。通俗地讲,**runC是一个用来运行容器的轻量级工具,是真正用来运行容器的。**
      • containerd是Docker服务端的一个核心组件,它是从dockerd中剥离出来的,它的诞生完全遵循OCI标准,是容器标准化后的产物。**containerd通过containerd-shim启动并管理runC,可以说containerd真正管理了容器的生命周期**。
    • dockerd通过gRPCcontainerd通信,由于dockerd与真正的容器运行时,runC中间有了containerd这一OCI标准层,使的dockerd可以确保接口向下兼容
      • gRPC是一种远程服务调用
    • containerd-shim的意思是垫片,类似于拧螺丝时夹在螺丝和螺母之间的垫片。containerd-shim的主要作用是:**将containerd和真正的容器进程解耦,使用containerd-shim作为容器进程的父进程,从而实现重启dockerd不影响已经启动的容器进程
    • 事实上,dockerd启动的时候,containerd就随之启动了,dockerdcontainerd一直存在。当执行docker run命令时,containerd会创建containerd-shim充当”垫片”进程,然后启动容器的真正进程。(**containerd-shim是真正容器的进程的父进程,这么做为了不让真正的容器进程作为containerd的子进程,从而可以实现重启containerd而不影响已经运行的容器**)

简介

  • docker常用技巧

docker 启动容器并分离到后台

1
docker run -id -p 4000:4000 jekyll/ubuntu:v1 /bin/bash

docker 镜像存储位置

  • Docker 镜像存储位置取决于你在安装 Docker 时所选择的存储驱动程序。Docker 默认使用“overlay2”存储驱动程序,它会将镜像存储在 /var/lib/docker/overlay2 目录下。但是,如果你使用的是其他存储驱动程序,那么镜像存储位置可能会有所不同。

  • 你可以通过以下命令查看 Docker 镜像存储位置:

    1
    docker info | grep -i 'docker root dir'
  • 在输出结果中,你将看到存储路径的具体位置。例如:

    1
    Docker Root Dir: /var/lib/docker
  • 如果你想将 Docker 镜像存储到不同的位置,可以通过以下步骤进行设置:

  • 停止 Docker 服务:

    1
    sudo systemctl stop docker
  • 移动 /var/lib/docker 目录到新的位置,例如 /new/docker/path:

    1
    sudo mv /var/lib/docker /new/docker/path
  • 创建一个指向新目录的符号链接:

    1
    sudo ln -s /new/docker/path /var/lib/docker
  • 启动 Docker 服务:

    1
    sudo systemctl start docker
  • 现在,Docker 镜像将被存储在新的位置 /new/docker/path 中。请注意,这种更改可能会导致现有的 Docker 容器无法正常运行,因此请谨慎操作。

网络模式设置

在 Docker 中,-net=host 是一种网络模式选项,用于指定容器的网络命名空间。当你使用该选项时,容器将与宿主机共享网络命名空间,这意味着容器将使用宿主机的网络栈,与宿主机具有相同的网络接口和IP地址。

具体来说,使用-net=host选项,容器内的网络配置与宿主机完全一致,容器可以直接访问宿主机上的网络接口和端口。这样的配置有一些优势和限制:

优势:

  1. 简化网络配置:使用该选项,不需要再单独设置容器的网络配置,容器直接继承宿主机的网络配置。
  2. 容器与宿主机共享IP地址:可以让容器直接使用宿主机的IP地址,方便某些网络应用场景。

限制:

  1. 安全性:容器与宿主机共享网络命名空间,容器内的进程可以直接访问宿主机的网络,这可能会增加潜在的安全风险,因此需要格外小心。
  2. 端口冲突:由于容器与宿主机共享网络,如果宿主机上已经在使用的端口被容器尝试使用,可能会导致端口冲突。
  3. 主机网络的限制:由于容器使用宿主机网络栈,容器将受到宿主机网络设置和限制的影响。

在大多数情况下,推荐使用默认的网络模式,Docker会为容器分配独立的网络命名空间,从而保持隔离和安全性。只有在特定的使用场景下,才应该考虑使用-net=host选项。

请注意,Docker在不同版本中可能会有一些变化,因此建议查阅最新的Docker文档以获得最准确的信息。

保存镜像

  • 使用命令:tar cvf image.tar --exclude=/proc --exclude=/image.tar /, 在容器里打包系统所需要的文件,
    • 记录以下:tar cvf image.tar --exclude=/system --exclude=/sys --exclude=/proc --exclude=/image.tar /
  • sudo docker cp containerID:/image.tar ./ , 将容器中打包的文件拷贝到主机中
  • cat image.tar | sudo docker import - image:v1, 导入镜像

linux下重启,启动,关闭docker服务

  • sudo systemctl start docker : 启动
  • sudo systemctl daemon-reload: 守护进程重启
  • sudo systemctl restart docker or sudo service docker restart : 重启docker服务
  • sudo service docker stop or sudo systemctl stop docker : 关闭docker

以非root用户身份管理

  • Docker守护程序绑定到Unix套接字而不是TCP端口,默认情况下,Unix套接字的用户有root,其它用户只能使用sudo来访问。 Docker守护程序始终以root用户身份运行
  • 不想使用sudo,创建docker的Unix组,将用户添加到改组
    • sudo groupadd docker
    • sudo usermod -aG docker $USER
    • newgrp docker

镜像的上传和拉取

  • 登录docker

    • docker login
  • 修改镜像命名

    • 修改镜像的repository,使之与Docker Hub账号匹配
    • Docker Hub为了区分不同用户的同名镜像,镜像的registry中要包含用户名,完整的格式为:[username]/xxx:tag
    • Docker官方自己维护的镜像没有用户名
  • 镜像上传

    • 通过docker push将镜像上传到Docker Hub
  • 镜像的拉取

    • 上传的是公共镜像仓库,任何人都可以下载使用

根据这些md5目录名检查是哪个容器

  • docker inspect xxx | grep merge

查看容器日志

  • docker logs <container-id>,查看日志
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    user@computer:test-docker$ docker logs 172
    root@1722c360945f:/# ls
    bin boot dev etc home lib lib64 media mnt opt proc root run sbin src srv sys tmp usr var
    root@1722c360945f:/# cd src
    root@1722c360945f:/src# ks
    bash: ks: command not found
    root@1722c360945f:/src# ls
    one.txt
    root@1722c360945f:/src# ls
    one.txt two.txt
    root@1722c360945f:/src# cat two.txt
    root@1722c360945f:/src# ls
    two.txt
    root@1722c360945f:/src# ls
    two.txt
    root@1722c360945f:/src# pwd
    /src
    user@computer:test-docker$

mount

  • docker run -it --mount type=bind,src="$(pwd)",target=/src ubuntu:18.04 bash

简介

  • docker基础语法

docker inspect 详解

docker inspect 命令用于查看 Docker 中的各种对象的详细信息,包括容器、镜像、网络、卷等。这个命令以 JSON 格式输出对象的配置和状态信息,可用于调试、监视和了解 Docker 对象的各个方面。下面是有关 docker inspect 命令的详细解释:

语法:

1
docker inspect [OPTIONS] OBJECT [OBJECT...]
  • OPTIONS:可选,可以附加一些选项来控制输出格式和内容。
  • OBJECT:必需,指定要检查的 Docker 对象的名称或 ID。

以下是一些常见的用法和选项:

  1. 查看容器详细信息:

    1
    docker inspect container_name_or_id

    这将输出指定容器的详细信息,包括容器的配置、状态、网络设置等等。

  2. 查看镜像详细信息:

    1
    docker inspect image_name_or_id

    这将输出指定镜像的详细信息,包括镜像的元数据、层信息等。

  3. 输出格式选项:

    默认情况下,docker inspect 输出 JSON 格式的信息,但你可以使用 --format 选项来指定自定义输出格式。例如:

    1
    docker inspect --format container_name_or_id

    这将仅输出容器的状态(运行、停止等)。

  4. 查看多个对象的信息:

    你可以一次查看多个 Docker 对象的信息。例如,要查看多个容器的详细信息,可以列出它们的名称或 ID 并用空格分隔:

    1
    docker inspect container1 container2
  5. 使用 --type 选项过滤对象类型:

    你可以使用 --type 选项来过滤要检查的 Docker 对象的类型。例如,要查看所有网络的详细信息,可以运行:

    1
    docker inspect --type network

    这将列出所有网络的详细信息。

  6. 导出信息到文件:

    如果你想将对象的详细信息保存到文件中,可以使用重定向操作符(>)将输出重定向到文件。例如:

    1
    docker inspect container_name_or_id > container_info.json

    这将容器的详细信息保存到 container_info.json 文件中。

docker inspect 命令是一个非常有用的工具,可用于深入了解 Docker 对象的各个方面,从容器的配置到网络设置,以及镜像的元数据等等。通过查看这些信息,你可以更好地理解和管理你的 Docker 环境。

docker –network(net) 详解

在 Docker 中,网络是容器和其他容器或宿主机之间进行通信的基础。Docker 提供了多种网络模式和选项,可以根据不同的需求选择适当的网络配置。以下是 Docker 中一些常见的网络选项:

  1. --network:这是一个全局选项,用于指定容器所属的网络。可以通过以下几种预定义的网络进行选择:

    • bridge:默认网络模式,Docker 在主机上创建一个名为 bridge 的虚拟网络,容器将连接到该网络,容器可以通过它与宿主机和其他容器通信。

    • host:使用宿主机的网络命名空间,容器与宿主机共享网络栈,与宿主机拥有相同的网络接口和IP地址。适用于需要容器与宿主机共享网络的场景。

    • none:不使用任何网络,容器将没有网络接口,只适用于特殊情况。

    • container:<name|id>:使容器与指定名称或ID的另一个容器共享网络命名空间,从而实现容器之间的网络通信。

    • 自定义网络:可以使用 docker network create 命令创建自定义网络,并将容器连接到该网络。

  2. --publish-p:这个选项用于将容器内部的端口映射到宿主机上,允许外部主机通过宿主机的IP和端口访问容器内部的服务。

    例如:docker run -p 8080:80 nginx 将容器内部的80端口映射到宿主机的8080端口。

  3. --expose:用于将容器内部的端口暴露给与容器连接的其他容器,但不映射到宿主机。这只是一种文档工具,实际上并不会进行端口映射。

  4. --link:已经过时,不推荐使用。用于在容器之间创建链接,允许容器使用对方的环境变量进行通信。

  5. --hostname:指定容器的主机名。

这些选项可以通过 docker run 命令进行设置,例如:

1
docker run --network=bridge -p 8080:80 --name my_container nginx

上述命令将创建一个名为 my_container 的容器,连接到默认的 bridge 网络,并将容器内部的80端口映射到宿主机的8080端口,让外部可以通过宿主机的IP和端口访问容器内的NGINX服务。

请注意,Docker 的网络功能在不同版本之间可能会有一些变化,建议查阅最新的 Docker 文档以获得最准确的信息。

docker compose

Docker Compose 是一个用于定义和管理多容器应用程序的工具,它使用一个单独的 YAML 文件来配置应用程序的服务、网络和卷等。

以下是一些常用的 Docker Compose 命令及其功能:

  1. 启动和停止容器组:

    • 启动容器组:docker-compose updocker-compose start 命令可以启动 Docker Compose 配置文件中定义的所有服务和容器。
    • 停止容器组:docker-compose stop 命令可以停止 Docker Compose 配置文件中定义的所有服务和容器。
  2. 构建和重建容器:

    • 构建容器:docker-compose build 命令可以根据 Docker Compose 配置文件中的定义,构建容器的镜像。
    • 重建容器:docker-compose up --build 命令可以重建所有容器的镜像,并重新启动容器组。
  3. 查看容器组状态:

    • 查看容器组状态:docker-compose ps 命令可以列出 Docker Compose 配置文件中定义的所有服务和容器的状态信息。
  4. 执行命令:

    • 在容器内执行命令:docker-compose exec <service> <command> 命令可以在指定的服务容器内执行命令。
  5. 清理容器和资源:

    • 删除容器:docker-compose rm 命令可以删除停止的容器。
    • 清理资源:docker-compose down 命令可以停止并删除所有容器、网络和卷。
  6. 查看日志:

    • 查看容器日志:docker-compose logs 命令可以查看 Docker Compose 配置文件中定义的所有服务和容器的日志。

这些是一些常见的 Docker Compose 命令及其功能。你可以根据需要使用这些命令来管理和操作 Docker Compose 配置文件中定义的容器组。

请注意,在使用 Docker Compose 命令之前,确保已经安装了 Docker Compose 工具,并在当前工作目录下存在正确的 Docker Compose 配置文件。


docker save

  • 简介:

    • docker save 命令用于将 Docker 镜像保存为一个 tar 归档文件。这个归档文件可以方便地传输、备份或在其他 Docker 主机上加载和使用
  • 语法:

    1
    docker save [OPTIONS] IMAGE [IMAGE...]
  • 参数:

    • OPTIONS:可选参数,用于指定额外的选项,如压缩级别等。
    • IMAGE:要保存为归档文件的 Docker 镜像名称或镜像 ID
  • 示例:

    1
    docker save -o myimage.tar myimage:latest
  • 注:

    • 上述示例将名为 myimage:latest 的 Docker 镜像保存为 myimage.tar 归档文件。
    • 要了解更多关于 docker save 命令的详细信息,你可以使用 docker save –help 查看帮助文档
    • 使用 docker save 命令保存的归档文件可以使用 docker load 命令进行加载和导入。这样可以在其他 Docker 主机上使用相同的镜像,或者进行备份和共享镜像

docker system

docker system 命令用于管理和查看 Docker 系统资源的相关信息。它提供了一组子命令,可以用于获取关于 Docker 守护进程(Docker daemon)的状态、清理无用资源以及查看 Docker 系统的使用情况。下面是对 docker system 命令的一些常用子命令的详细解释:

  1. **docker system df**:显示 Docker 系统的磁盘使用情况,包括镜像、容器和卷的使用情况。

  2. **docker system events**:实时显示 Docker 系统的事件流,包括容器的创建、启动、停止、删除等事件。

  3. **docker system info**:显示 Docker 系统的详细信息,包括容器、镜像、存储驱动、内核版本等。

  4. **docker system prune**:清理无用的 Docker 资源,包括未使用的镜像、停止的容器、未使用的网络等。

  5. **docker system resize**:调整 Docker 容器终端的大小。

这只是 docker system 命令的一些常见用法。你可以通过运行 docker system --helpdocker system <子命令> --help 查看完整的帮助文档,以了解更多关于各个子命令的详细信息和使用方式。

请注意,docker system 命令需要在具有足够权限的环境中执行,例如在管理员或超级用户模式下执行。

docker system prune

docker system prune 命令用于清理无用的 Docker 资源,以释放磁盘空间。它会删除未使用的镜像、停止的容器、未使用的网络和未被任何容器引用的卷。这可以帮助你清理系统中的不必要资源,以避免占用过多的磁盘空间。

用法:

1
docker system prune [OPTIONS]

参数:

  • OPTIONS:可选参数,用于指定额外的选项。常见的选项包括:
    • -a, --all:清理所有未使用的资源,包括未使用的镜像、容器、网络和卷。
    • -f, --force:强制执行清理操作,不需要用户进行确认。
    • --filter <FILTER>:按照指定的条件过滤要清理的资源。
    • -v, --volumes:同时清理未被容器引用的卷。

示例:

1
docker system prune -a

上述示例将清理所有未使用的 Docker 资源,包括未使用的镜像、停止的容器、未使用的网络和未被任何容器引用的卷。

请注意,清理操作是不可逆的,一旦执行将无法恢复被删除的资源。在执行 docker system prune 命令之前,请确保你了解将要删除的资源,并确认没有重要的数据会被删除。

使用 docker system prune 命令可以帮助你管理 Docker 系统资源,提供更高效和规整的环境。

docker system df

docker system df 命令用于显示 Docker 系统的磁盘使用情况,包括镜像、容器和卷的使用情况。它可以帮助你了解 Docker 系统在磁盘上占用的空间,以便更好地管理和优化资源。

用法:

1
docker system df [OPTIONS]

参数:

  • OPTIONS:可选参数,用于指定额外的选项。常见的选项包括:
    • --no-trunc:不截断输出的镜像和容器名称。
    • -v, --volumes:同时显示卷的使用情况。

示例:

1
docker system df

上述示例将显示 Docker 系统的磁盘使用情况,包括镜像、容器和卷的使用情况。

输出内容包括以下几个部分:

  • IMAGE TYPE:镜像类型(例如,镜像、容器或卷)。
  • TOTAL:总大小。
  • USED:已使用的大小。
  • SHARED:共享的大小。
  • CACHE:缓存的大小。
  • RECLAIMABLE:可回收的大小。
  • PARENT:父镜像的 ID。
  • TAG:镜像的标签或容器的名称。
  • VIRTUAL SIZE:虚拟大小。

使用 docker system df 命令可以方便地查看 Docker 系统的磁盘使用情况,帮助你了解镜像和容器占用的空间。这有助于进行资源管理和优化,以确保磁盘空间得到合理利用。

docker system info

docker system info 命令用于显示 Docker 系统的详细信息,包括容器、镜像、存储驱动、内核版本等。它提供了有关 Docker 守护进程(Docker daemon)和运行环境的相关信息。

用法:

1
docker system info

示例输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Client:
Context: default
Debug Mode: false
Plugins:
app: Docker App (Docker Inc., v0.9.1-beta3)
buildx: Build with BuildKit (Docker Inc., v0.5.1-docker)

Server:
Containers: 3
Running: 2
Paused: 0
Stopped: 1
Images: 15
Server Version: 20.10.6
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
...

输出内容包括以下几个部分:

  • Client:客户端相关信息,如上下文和调试模式。
  • Plugins:安装的插件列表。
  • Server:Docker 守护进程相关信息,如容器数量、镜像数量、服务器版本等。
  • Storage Driver:存储驱动信息,如使用的驱动类型、底层文件系统等。
  • 其他相关信息,如操作系统类型、内核版本等。

通过运行 docker system info 命令,你可以获得有关 Docker 系统的详细信息,帮助你了解 Docker 运行环境和配置。这对于故障排查、性能优化和了解 Docker 系统的状态非常有用。

docker stats

docker stats 命令用于实时监视 Docker 容器的资源使用情况,包括 CPU 使用率、内存使用量、网络 I/O 等。它提供了一个实时的流式输出,可以帮助你了解正在运行的容器的性能和资源消耗情况。

用法:

1
docker stats [OPTIONS] [CONTAINER...]

参数:

  • OPTIONS:可选参数,用于指定额外的选项。常见的选项包括:
    • --all, -a:显示所有的容器(包括停止的容器)。
    • --format:自定义输出格式。
    • --no-stream:只显示一次统计结果,然后退出。
    • --no-trunc:不截断输出的容器名称。
  • CONTAINER:要监视的容器名称或容器 ID。如果不指定容器,则会监视所有正在运行的容器。

示例:

1
docker stats container1 container2

上述示例将实时监视名为 container1container2 的容器的资源使用情况。

输出内容包括以下几个部分:

  • CONTAINER ID:容器的 ID。
  • NAME:容器的名称。
  • CPU %:CPU 使用率。
  • MEM USAGE / LIMIT:内存使用量和限制。
  • MEM %:内存使用率。
  • NET I/O:网络输入/输出。
  • BLOCK I/O:块设备输入/输出。
  • PIDS:进程 ID 数量。

使用 docker stats 命令可以实时监视 Docker 容器的资源使用情况,帮助你了解容器的性能和资源消耗情况。这对于监控和优化容器的运行非常有用。请注意,docker stats 命令会持续输出监视结果,可以通过 Ctrl+C 终止命令的执行。

docker history

docker history 命令用于查看 Docker 镜像的历史记录,包括每个镜像层的创建步骤和相关信息。它可以帮助你了解镜像是如何构建的,每个镜像层的来源和变更,以及各个镜像层的大小。

用法:

1
docker history [OPTIONS] IMAGE

参数:

  • OPTIONS:可选参数,用于指定额外的选项。常见的选项包括:
    • --no-trunc:显示完整的命令信息,不截断输出。
    • --quiet, -q:仅显示镜像层的 ID。

示例:

1
docker history myimage:latest

上述示例将显示名为 myimage:latest 的 Docker 镜像的历史记录。

输出内容包括以下几个部分:

  • IMAGE ID:镜像层的 ID。
  • CREATED:镜像层的创建时间。
  • CREATED BY:创建镜像层的命令或操作。
  • SIZE:镜像层的大小。

docker history 命令按照镜像层的构建顺序列出了镜像的历史记录。每个镜像层都代表一个变更,它可以是一个 Dockerfile 中的一条指令或者基于其他镜像层的修改。通过查看镜像的历史记录,你可以了解镜像是如何构建的,每个步骤的细节和影响,以及镜像层的大小情况。

请注意,镜像历史记录中的每一行都会添加一个新的镜像层,并且每个镜像层都会占用磁盘空间。因此,构建过多的镜像层可能会增加镜像的大小和磁盘消耗。

docker kill

docker kill 命令用于强制停止正在运行的 Docker 容器。它发送一个 SIGKILL 信号给容器的主进程,使容器立即停止运行,类似于使用 kill -9 命令终止进程。

用法:

1
docker kill [OPTIONS] CONTAINER [CONTAINER...]

参数:

  • OPTIONS:可选参数,用于指定额外的选项。常见的选项包括:
    • -s, --signal:指定要发送的信号。默认为 SIGKILL。
  • CONTAINER:要停止的容器名称或容器 ID。

示例:

1
docker kill mycontainer

上述示例将停止名为 mycontainer 的 Docker 容器。

注意事项:

  • 使用 docker kill 命令会立即停止容器,不会触发容器的停止信号或执行容器的停止脚本。
  • 如果你希望优雅地停止容器并触发容器的停止信号和脚本,可以使用 docker stop 命令。
  • 使用 docker kill 命令强制停止容器可能导致数据丢失或不正常的容器状态,请谨慎使用。

docker kill 命令用于强制停止正在运行的容器,对于无法通过正常方式停止的容器,这是一种有效的方法。然而,它不是优雅停止容器的首选方法,因为容器没有机会进行清理操作。建议在必要时使用,并确保了解可能的副作用。

docker pause

docker pause 命令用于暂停正在运行的 Docker 容器。它会暂停容器内所有的进程,使其停止在当前状态,直到恢复为止。暂停后的容器不会消耗 CPU 资源,但内存和磁盘资源仍然保留。

用法:

1
docker pause CONTAINER [CONTAINER...]

参数:

  • CONTAINER:要暂停的容器名称或容器 ID。

示例:

1
docker pause mycontainer

上述示例将暂停名为 mycontainer 的 Docker 容器。

注意事项:

  • 暂停容器会暂停容器内的所有进程,包括正在运行的和后台运行的。
  • 暂停后的容器可以使用 docker unpause 命令来恢复运行。
  • 暂停容器并不会影响容器的网络连接,因此容器仍然可以接收网络流量。
  • 只有在支持容器暂停功能的操作系统上才能使用 docker pause 命令。

使用 docker pause 命令可以方便地暂停正在运行的容器,以便进行调试、维护或其他需要容器暂停的操作。请确保在使用此命令之前了解其影响,并谨慎操作以避免不必要的问题。

docker unpause

docker unpause 命令用于恢复被暂停的 Docker 容器。它会使之前被暂停的容器继续运行,并恢复容器内的所有进程的执行。

用法:

1
docker unpause CONTAINER [CONTAINER...]

参数:

  • CONTAINER:要恢复的容器名称或容器 ID。

示例:

1
docker unpause mycontainer

上述示例将恢复名为 mycontainer 的 Docker 容器。

注意事项:

  • 只有被暂停的容器才能使用 docker unpause 命令进行恢复。
  • 恢复容器后,之前被暂停的进程会继续执行。
  • 容器的网络连接不会受到 docker unpause 命令的影响。

使用 docker unpause 命令可以恢复之前被暂停的容器,使其继续运行。这对于从暂停状态中恢复容器非常有用,例如在进行调试或维护操作后重新启动容器。请注意,只能对已暂停的容器执行 docker unpause 命令。

docker rename

docker rename 命令用于为 Docker 容器重命名。它可以修改容器的名称标识符,使其更符合你的需求或命名约定。

用法:

1
docker rename CONTAINER NEW_NAME

参数:

  • CONTAINER:要重命名的容器名称或容器 ID。
  • NEW_NAME:新的容器名称。

示例:

1
docker rename mycontainer newname

上述示例将名为 mycontainer 的 Docker 容器重命名为 newname

注意事项:

  • 重命名容器只会修改容器的名称标识符,不会对容器内的运行进程或状态造成影响。
  • 容器的新名称必须是唯一的,不得与现有容器名称重复。

使用 docker rename 命令可以轻松地为 Docker 容器重新命名,以便更好地管理容器或与命名约定保持一致。请记住,在容器被重命名后,你需要使用新的名称来执行其他容器相关的操作。

builder

  • manage builds

build

  • 命令:docker builder build [OPTIONS] PATH | URL | -
  • 简介:从Dockerfile创建一个镜像
  • 选项:
    • --add-host : 添加一个自定义的主机到IP的映射(host:ip)
    • --build-arg : 设置构建时的变量
    • --cache-from : 考虑作为缓存来源的镜像
    • --cgroup-parent : 容器的可选父级c组
    • --compress : 使用gzip压缩构建环境
    • --cpu-period
    • --cpu-quota
    • --cpu-shares,-c
    • --cpuset-cpus
    • --cpuset-mems
    • --disable-content-true : 跳过镜像验证,默认为true
    • --file,-f : Dockerfile的名称,默认为PATH/Dockerfile
    • --force-rm : 始终移除中间的容器
    • --iidfile : 将镜像的ID写入到文件中
    • --isolation : 容器隔离技术
    • --label : 设置镜像的元数据
    • --memory,-m : 内存限制
    • --memory-swap : 交换限制等于内存加交换:-1表示启用无限制交换。
    • --network : 在构建过程中为RUN指令设置网络模式
    • --no-cache : 在构建镜像的时候不要使用缓存
    • --platform : 如果服务器是多平台的,则设置平台
    • --pull : 始终尝试拉取一个较新版本的镜像
    • --quiet,-q : 过滤构建镜像的输出,仅在成功时输出镜像的ID
    • --rm : 构建成功后删除中间容器
    • --security-opt : 安全选项
    • --shm-size : /dev/shm的大小
    • --squash : 将新建的层压成一个新的层
    • --tag,-t : 名称和可选的标签,格式为name:tag
    • --target : 设置目标构建阶段为构建。
    • --ulimit : 限定选项

run

  • 命令:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
  • 简介:docker run命令首先在指定的镜像上创建一个可写的容器层,然后用指定的命令启动它。也就是说,docker run相当于API中的/containers/create然后/containers/(id)/start。一个停止的容器可以用docker start来重新启动,其之前的所有变化都是完整的。
  • 选项:
    • --add-host : 添加一个自定义的主机到IP的映射(host:ip)
    • --attach,-a : 附加到STDIN、STDOUT或STDERR上
    • --blkio-weight : 区块IO(相对权重),在10和1000之间,或0表示禁用(默认为0)。
    • --blkio-weight-device : 块状IO重量(相对设备重量)
    • --cap-add : 增加Linux功能
    • --cap-drop : 降低Linux的能力
    • --cgroup-parent : 容器的可选父级c组
    • --cgroupns
    • --cidfile : 将容器ID写到文件中
    • --cpu-count : CPU计数,仅支持Windows
    • --cpu-percent :
    • --cpu-period
    • --cpu-quota
    • --cpu-rt-period
    • --cpu-rt-runtime
    • --cpu-shared,-c
    • --cpus : cpu的数量
    • --cpuset-cpus
    • --cpuset-mems
    • --detach,-d : 在后台运行容器并打印容器ID
    • --detach-keys : 覆盖脱离容器的按键顺序
    • --device : 在容器中添加一个主机设备
    • --device-cgroup-rule : 在cgroup允许的设备列表中添加一条规则
    • --device-read-bps : 限制从一个设备上的读取速率(每秒字节数)。
    • --device-read-iops : 限制从一个设备的读取率(每秒的IO)。
    • --device-write-bps
    • --device-write-iops
    • --disable-content-trust : 跳过镜像验证,默认为true
    • --dns : 设置自定义的DNS服务器
    • --dns-option : 设置DNS选项
    • --dns-search : 设置自定义DNS搜索域
    • --domainname : 容器NIS域名
    • --entrypoint : 覆盖镜像的默认ENTRYPOINT
    • --env,-e : 设置环境变量
    • --env-file : 读取环境变量的文件
    • --expose : 暴露一个端口或一系列的端口
    • --gpus : 要添加到容器中的GPU设备(’全部’用于传递所有GPU)。
    • --group-add
    • --health-cmd : 运行命令以检查健康状况
    • --health-interval : 运行检查的间隔时间(ms|s|m|h)(默认为0s)。
    • --health-retries
    • --health-start-period
    • --health-timeout
    • --help : 输出帮助信息
    • --hostname,-h : 容器主机名称
    • --init : 在容器内运行一个init,转发信号并收割进程
    • --interactive,-i : 保持STDIN开放,即使没有连接
    • --io-maxbandwidth : 系统驱动器的最大IO带宽限制(仅Windows)。
    • --io-maxiops : 系统驱动器的最大IOps限制(仅Windows)。

常用命令

docker images, docker image ls

  • 列出本地主机上的镜像
    • repository : 表示镜像的仓库源
    • tag : 镜像的标签
    • image id : 镜像id
    • created : 镜像创建时间
    • size : 镜像大小

docker rmi helloworld

  • 删除镜像

docker run -it ubuntu /bin/bash

  • 使用ubuntu镜像启动一个容器,参数为以命令模式进入该容器
    • -i : 交互操作
    • -t : 终端
    • ubuntu : ubuntu镜像
    • /bin/bash : 放在镜像名后的是命令,这里是希望有个交互式shell

docker run -itd --name ubuntu-test ubuntu /bin/bash

  • 后台运行容器
    • -d : 默认不会进入容器,想要进入容器需要使用指令docker exec

docker ps

  • 语法:docker ps [options]
  • 作用:列出容器
  • [options]
    • --all, -a : 显示所有容器(默认仅显示正在运行的)。(Show all containers(default shows just running))
    • --filter, -f : 基于指定的条件过滤输出信息。(Filter output based on conditions provided)
    • --format : 使用go模板输出
    • --last, -n : 显示n个最后创建的容器。(Show n last created containers(includes all states))
    • --no-trunc : 不截断输出。(Don’t truncate output)
    • --quiet, -q : 仅显示容器ID。(Only display container IDs)
    • --size, -s : 显示总文件大小。(Display total file sizes)

docker start id

  • 启动一个已停止的容器

docker stop

  • 语法:docker stop [options] container [container...]
  • 作用:停止一个容器
  • [options]:
    • --time, -t : 在杀死容器之前等待停止的时间,默认为10秒

docker restart id

  • 重启一个容器

docker attach, docker exec

  • 进入容器:在使用-d参数时,容器启动后会进入后台,此时想要进入容器,通过以下指令:
    • docker attach: docker attach id
    • docker exec:推荐使用此命令,因为使用它退出容器终端,但是不会导致容器的停止,docker exec -it id /bin/bash

docker export, docker import, docker commit

docker export

  • docker export命令不会导出与容器关联的卷的内容。如果卷安装在容器中现有目录的顶部,则docker export将导出底层目录的内容,而不是卷的内容
  • 功能:将容器的文件系统导出为tar存档
  • docker export 容器id > 容器名称.tar , docker export 容器id --output 容器名称.tar
    • 导出本地某个容器到tar包
  • 参数
    • --output, -o : 写入一个文件,而不是stdout

docker import

  • 语法:docker import [options] file|URL|- [REPOSITORY[:TAG]]
  • 作用:从一个tar包导入文件数据并创建一个文件系统镜像
  • [options] :
    • --change, -c : 应用于创建镜像的dockerfile 指示
    • --message, -m : 为导入的镜像设置提交信息
  • cat docker/ubuntu.tar | docker import - test/ubuntu:v1
    • 通过管道和标准输入的方式导入容器快照

docker load

  • 导入镜像文件:docker load --input imagePath

docker commit

  • 更新镜像:通过命令docker commit来提交容器副本,Warning:使用此命令保存镜像,会以层的概念保存,每一次保存都会增加一层,镜像会越来越大
    • 示例:docker commit -m="has update" -a="runoob" e218edb10161 runoob/ubuntu:v2
    • 参数说明:
      • -m:提交的描述信息
      • -a:指定镜像作者
      • e218edb10161 : 容器ID
      • runoob/ubuntu:v2 : 指定要创建的目标镜像名

docker tag

  • 修改镜像标签:docker tag image-id name:tag