[深度学习] RCNNs系列1 Ubuntu下Faster RCNN配置及训练和测试自己的数据方法

最近用到Faster RCNN进行目标检测,前前后后两周把RCNN,SPPNet,Fast RCNN和Faster RCNN大体调查了一遍,准备写一个RCNNs系列,后面还要加上今年最新的Mask RCNN。

要想开个头,知道RCNNs在目标检测方向的优势,那就先用用作者的代码,跑跑自己的代码,下面就是在Ubuntu下进行Faster RCNN配置的方法

一、Faster RCNN环境配置及demo运行

虽然Faster RCNN中作者加入了很多新的东西,比如怎么选Anchar,怎么计算多任务的loss等等,所幸的是作者开源了代码,让我们很容易就能够用他的算法实现我们自己的任务。在运行自己的任务之前,我们首先要做的就是确保我们已经配置好源代码的运行环境。
本文是在Caffe已经配置好的条件下进行的,如果Caffe的需求库,比如opencv等等还没有弄好,建议先去把Caffe配置好。
(1)首先使用git把Faster RCNN的源码下载到本地:
  1. gitclone--recursivehttps://github.com/rbgirshick/py-faster-rcnn.git
(2)安装cpython和python-opencv
  1. pipinstallcpython
  2. apt-getinstallpython-opencv
(3)下载Faster RCNN并安装好cpython以后,进入py-faster-rcnn/lib中使用命令编译一下
  1. make
(4)进入py-faster-rcnn/caffe-fast-rcnn,配置Makefile.config文件,目录中有Makefile.config.example文件,可以按照这个文件进行修改,或者使用你配置caffe时的Makefile.config文件。我这里有一个 配置文件,可以参考。我的配置文件用到了cuDNN,开始我使用cuDNN的时候总会在编译时报错,后来在 在windows上配置py-faster-rcnn和调参经验中找到了解决办法,解决办法如下:
  • 把Caffe中的include/caffe/layers/cudnn_relu_layer.hpp,/include/caffe/layers/cudnn_sigmoid_layer.hpp,/include/caffe/layers/cudnn_tanh_layer.hpp和/include/caffe/util/cudnn.hpp替换py-faster-rcnn/caffe-fast-rcnn中对应的文件
  • 把Caffe中的src/caffe/layers/cudnn_relu_layer.cpp,src/caffe/layers/cudnn_relu_layer.cu,src/caffe/layers/cudnn_sigmoid_layer.cpp,src/caffe/layers/cudnn_sigmoid_layer.cu,src/caffe/layer/cudnn_tanh_layer.cpp,src/caffe/layers/cudnn_tanh_layer.cu替换掉py-faster-rcnn/caffe-fast-rcnn中对应的文件
  • 把py-faster-rcnn/caffe-fast-rcnn中src/caffe/layers/cudnn_conv_layer.cu文件中的cudnnConvolutionBackwardData_v3全部换为cudnnConvolutionBackwardData,把cudnnConvolutionBackwardFilter_v3全部换为cudnnConvolutionBackwardFilter。
如果自己的显卡配置足够的话, 强烈建议开启cudnn,cudnn不仅可以起到加速的作用,而且我跑实验的时候发现加入cudnn以后 可以明显的降低显存的使用。如下图所示:
开启cudnn之前:

开启cudnn之后:

可以看到显存减少了将近2G,非常可观,强烈建议开启。
(5)配置好Makefile以后,在caffe-fast-rcnn中执行命令进行编译
  1. make-j8&&makepycaffe
(6)这个时候,基本上faster RCNN也就配置好了,让我们看一下是否真的能够运行。首先我们下载几个作者已经训练好的caffemodel,如果现在直接运行demo.py的话会提示你没有caffemodel文件,然后询问是否运行过py-faster-rcnn/data/scripts/fetch_faster_rcnn_models.sh文件,我们可以找到该文件中的下载链接,用迅雷一会就可以下载好,如果直接运行该文件可能要不少时间。把下载好的文件中VGG16_faster_rcnn_final.caffemodel文件复制到py-faster-rcnn/data/faster_rcnn_models中
(7)运行py-faster-rcnn/experiments/tools/demo.py文件,不出意外的话,会出现如下错误

修改py-faster-rcnn/lib/fast_rcnn/train.py文件,在文件中引入text_format
  1. importgoogle.protobuf.text_format
再次运行demo.py文件,这次应该可以运行成功!

二、Faster RCNN训练自己的数据

既然已经把Faster RCNN配置好,下一步我们来训练自己的数据吧。Faster RCNN论文中采用的训练方法分为几个阶段,训练起来比较麻烦,我们这里采用源码中的end to end的训练方式,更简便一些。

2.1 建立数据集

为了让我们的训练更简单些,我们不去改动源码中读写数据的方式,而是把我们的数据集改成Pascal VOC的数据集格式,Pascal VOC数据集主要分为三个部分:Annotations,JPEGImages和imagesets。其中JPEGImages中存放的是训练和测试时需要的图像;Annotations存放的是每个图像中所有目标的bounding Box信息,每个图像对应一个xml文件imageset文件中存放的Main目录,而Main目录中就是训练和测试时需要的文件列表,主要分为train.txt,test.txt,trainval.txt,val.txt可以根据文件名就知道哪些是训练数据列表,哪些是测试数据列表。
这里如果我们有这样结构的数据集当然最好了,如果没有这样的数据集的话就需要自己建立。建立数据集的话可以参考: 将数据集做成VOC2007格式用于Faster-RCNN训练——小咸鱼_的博客
这里解说一下我的获取方法,因为我的数据比较特殊,是由在线数据转换过来的,所以我很容易的就获得了各个目标的bounding Box信息,所以没有使用参考博客方法。我最终获得的bounding Box信息文件如下所示:
  1. Train_IMG\000001.jpg976174202305
  2. Train_IMG\000001.jpg856198282162
  3. ......省略若干行
  4. Train_IMG\000001.jpg90537194699314
其中每个图像对应一个bounding Box文件,每个文件中的每一行表示一个目标的bounding Box,每一行由6列数据组成,第1列数据为图像的路径,第2列为该目标的分类,第3列为bounding Box左上角的x(对应图像的列,即mincol),第4列为bounding Box左上角的y(对应图像的行,即minrow),第5列为bounding Box的右下角的x(对应图像的列,即maxcol),第6列为bounding Box右下角的y(对应图像的行,即maxrow)。
然后我们接下来要做的就是把这些bounding Box文件转换为VOC数据集格式的xml文件,我这里是从github上找到的一个Python开源代码(但是作者的原址找不到了,所以这里没能给出参考链接,如果有人有原址欢迎告知,我会把原址贴上)上进行的改动,源码如下:
  1. #-*-coding:utf-8-*-
  2. __author__="peic"
  3. importxml.dom
  4. importxml.dom.minidom
  5. importos
  6. fromPILimportImage
  7. '''''
  8. 根据下面的路径和文件,将output.txt制作成xml的标注
  9. '''
  10. #xml文件规范定义
  11. _INDENT=''*4
  12. _NEW_LINE='\n'
  13. _FOLDER_NODE='VOC2007'
  14. _ROOT_NODE='annotation'
  15. _DATABASE_NAME='CROHMEofflineMEDataset'
  16. _CLASS='person'
  17. _ANNOTATION='PASCALVOC2007'
  18. _AUTHOR='hanchao'
  19. _SEGMENTED='0'
  20. _DIFFICULT='0'
  21. _TruncATED='0'
  22. _POSE='Unspecified'
  23. #需要注意,这一项是存放boundingBox对应图像的目录
  24. _IMAGE_PATH='Train_IMG'
  25. #这一项是保存生成xml文件的目录
  26. _ANNOTATION_SAVE_PATH='Annotations'
  27. _IMAGE_CHANNEL=3
  28. #封装创建节点的过程
  29. defcreateElementNode(doc,tag,attr):
  30. #创建一个元素节点
  31. element_node=doc.createElement(tag)
  32. #创建一个文本节点
  33. text_node=doc.createTextNode(attr)
  34. #将文本节点作为元素节点的子节点
  35. element_node.appendChild(text_node)
  36. returnelement_node
  37. #封装添加一个子节点的过程
  38. defcreateChildNode(doc,attr,parent_node):
  39. child_node=createElementNode(doc,attr)
  40. parent_node.appendChild(child_node)
  41. #object节点比较特殊
  42. defcreateObjectNode(doc,attrs):
  43. object_node=doc.createElement('object')
  44. createChildNode(doc,'name',attrs['classification'],object_node)
  45. createChildNode(doc,'pose',_POSE,object_node)
  46. createChildNode(doc,'truncated',_TruncATED,'difficult',_DIFFICULT,object_node)
  47. bndBox_node=doc.createElement('bndBox')
  48. createChildNode(doc,'xmin',attrs['xmin'],bndBox_node)
  49. createChildNode(doc,'ymin',attrs['ymin'],bndBox_node)
  50. createChildNode(doc,'xmax',attrs['xmax'],'ymax',attrs['ymax'],bndBox_node)
  51. object_node.appendChild(bndBox_node)
  52. returnobject_node
  53. #将documentElement写入XML文件
  54. defwriteXMLFile(doc,filename):
  55. tmpfile=open('tmp.xml','w')
  56. doc.writexml(tmpfile,addindent=''*4,newl='\n',encoding='utf-8')
  57. tmpfile.close()
  58. #删除第一行添加标记
  59. fin=open('tmp.xml')
  60. fout=open(filename,'w')
  61. lines=fin.readlines()
  62. forlineinlines[1:]:
  63. ifline.split():
  64. fout.writelines(line)
  65. #new_lines=''.join(lines[1:])
  66. #fout.write(new_lines)
  67. fin.close()
  68. fout.close()
  69. #创建XML文档并写入节点信息
  70. defcreateXMLFile(attrs,width,height,filename):
  71. #创建文档对象,文档对象用于创建各种节点
  72. my_dom=xml.dom.getDOMImplementation()
  73. doc=my_dom.createDocument(None,_ROOT_NODE,None)
  74. #获得根节点
  75. root_node=doc.documentElement
  76. #folder节点
  77. createChildNode(doc,'folder',_FOLDER_NODE,root_node)
  78. #filename节点
  79. createChildNode(doc,'filename',attrs['name'],root_node)
  80. #source节点
  81. source_node=doc.createElement('source')
  82. #source的子节点
  83. createChildNode(doc,'database',_DATABASE_NAME,source_node)
  84. createChildNode(doc,'annotation',_ANNOTATION,source_node)
  85. createChildNode(doc,'image','flickr','flickrid','NULL',source_node)
  86. root_node.appendChild(source_node)
  87. #owner节点
  88. owner_node=doc.createElement('owner')
  89. #owner的子节点
  90. createChildNode(doc,owner_node)
  91. createChildNode(doc,_AUTHOR,owner_node)
  92. root_node.appendChild(owner_node)
  93. #size节点
  94. size_node=doc.createElement('size')
  95. createChildNode(doc,'width',str(width),size_node)
  96. createChildNode(doc,'height',str(height),size_node)
  97. createChildNode(doc,'depth',str(_IMAGE_CHANNEL),size_node)
  98. root_node.appendChild(size_node)
  99. #segmented节点
  100. createChildNode(doc,'segmented',_SEGMENTED,root_node)
  101. #object节点
  102. object_node=createObjectNode(doc,attrs)
  103. root_node.appendChild(object_node)
  104. #写入文件
  105. writeXMLFile(doc,filename)
  106. defgenerate_xml(txt_filename):
  107. #注意,这里的txt_filename文件是待转换的boundingBox文件
  108. ouput_file=open(txt_filename)
  109. current_dirpath=os.path.dirname(os.path.abspath('__file__'))
  110. ifnotos.path.exists(_ANNOTATION_SAVE_PATH):
  111. os.mkdir(_ANNOTATION_SAVE_PATH)
  112. lines=ouput_file.readlines()
  113. forlineinlines:
  114. s=line.rstrip()
  115. array=s.split('')
  116. #printlen(array)
  117. attrs=dict()
  118. attrs['name']=array[0].split('\\')[1]
  119. attrs['classification']=array[1]
  120. attrs['xmin']=array[2]
  121. attrs['ymin']=array[3]
  122. attrs['xmax']=array[4]
  123. attrs['ymax']=array[5]
  124. #构建XML文件名称
  125. xml_file_name=os.path.join(_ANNOTATION_SAVE_PATH,(attrs['name'].split('.'))[0]+'.xml')
  126. #printxml_file_name
  127. ifos.path.exists(xml_file_name):
  128. #print('doexists')
  129. existed_doc=xml.dom.minidom.parse(xml_file_name)
  130. root_node=existed_doc.documentElement
  131. #如果XML存在了,添加object节点信息即可
  132. object_node=createObjectNode(existed_doc,attrs)
  133. root_node.appendChild(object_node)
  134. #写入文件
  135. writeXMLFile(existed_doc,xml_file_name)
  136. else:
  137. #print('notexists')
  138. #如果XML文件不存在,创建文件并写入节点信息
  139. img_name=attrs['name']
  140. img_path=os.path.join(current_dirpath,_IMAGE_PATH,img_name)
  141. #获取图片信息
  142. img=Image.open(img_path)
  143. width,height=img.size
  144. img.close()
  145. #创建XML文件
  146. createXMLFile(attrs,xml_file_name)
执行的Main程序如下:
  1. #coding=utf-8
  2. '''''
  3. Createdon2017年4月18日
  4. @author:hanchao
  5. '''
  6. importgenerate_xml
  7. importos.path
  8. importcv2
  9. #
  10. if__name__=="__main__":
  11. fordir,path,filenamesinos.walk('Train_BB'):#Train_BB是存放boundingBox文件的目录
  12. forfilenameinfilenames:
  13. printdir+'/'+filename
  14. generate_xml.generate_xml(dir+'/'+filename)
最终生成的xml文件会保存在Annotations目录中。
对于imagesets/Main中的几个文件就比较好建立了,只需要把训练、验证和测试集中图像名称写入到对应的文件中即可。
接下来,可以自己在py-faster-rcnn/data中建立一个VOCdevkit2007目录,在该目录中建立VOC2007子目录,然后把JPEGImages,Annotaions和imagesets目录复制到该目录中,即准备好了自己的数据。
注意: Faster RCNN对图像和目标的大小以及长宽比是有一定要求的,具体的讨论见 用ImageNet的数据集(ILSVRC2014)训练Faster R-CNN——Jiajun的博客
在这里我有深痛的教训T_T,我的数据集中设置目标的长宽比在0.1~10之间,图像的长宽比在0.3~7之间

2.2 训练自己的数据

经过前面的铺垫,我们终于可以训练自己的数据了。
接下来我们需要下载几个模型,看过论文的都知道,作者的训练首先使用在ImageNet上训练好的模型对网络结构进行初始化,然后再训练的网络,我们现在下载的模型就是进行初始化的模型,下载地址: https://pan.baidu.com/s/1c2tfkRm下载完成后,把文件加压把模型放在py-faster-rcnn/data/imagenet_models中即可。
接下来,我们只需要更改几个文件即可。(注意,我这里使用的是端到端的训练方式,所以改动全部为end to end的,如果使用论文中的分阶段训练的方式,则需要改动alt_opt对应的文件
(1)更改py-faster-rcnn/experiments/scripts/faster_rcnn_end2end.sh文件,该文件中的问题在于没有设置各个文件绝对路径,所以运行起来可能有问题,把两个time后的路径设置加上py-faster-rcnn的绝对路径即可,我更改后示例如下:
  1. time/home/hanchao/py-faster-rcnn/tools/train_net.py--gpu${GPU_ID}\
  2. --solver/home/hanchao/py-faster-rcnn/models/${PT_DIR}/${NET}/faster_rcnn_end2end/solver.prototxt\
  3. --weights/home/hanchao/py-faster-rcnn/data/imagenet_models/${NET}.v2.caffemodel\
  4. --imdb${TRAIN_IMDB}\
  5. --iters${ITERS}\
  6. --cfg/home/hanchao/py-faster-rcnn/experiments/cfgs/faster_rcnn_end2end.yml\
  7. ${EXTRA_ARGS}
(2)更改py-faster-rcnn/models/pascal_voc/VGG16/faster_rcnn_end2end目录中的train,test和solver文件(我采用的是VGG16网络,如果使用ZF网络,去修改对应目录下文件即可)。把train和test中所有的21和84改成 你的分类类型数+1(你的分类类型数+1)×4即可。在solver文件中加入绝对路径
(3)更改py-faster-rcnn/lib/datasets/pascal_voc.py文件,更改self._classes中标签换成自己的标签,即
  1. self._classes=('__background__',#alwaysindex0
  2. '你的标签1','你的标签2','你的标签3')
注意这里的标签不要有大写符号。
(4)文章中还采用了horizontal flip以扩充数据, 我的数据是字符,因此不能扩充,所以我把py-faster-rcnn/lib/datasets/imdb.py中append_flipped_images(self)函数改为
  1. defappend_flipped_images(self):
  2. #num_images=self.num_images
  3. #widths=self._get_widths()
  4. #foriinxrange(num_images):
  5. #Boxes=self.roidb[i]['Boxes'].copy()
  6. #oldx1=Boxes[:,0].copy()
  7. #oldx2=Boxes[:,2].copy()
  8. #Boxes[:,0]=widths[i]-oldx2-1
  9. #Boxes[:,2]=widths[i]-oldx1-1
  10. #assert(Boxes[:,2]>=Boxes[:,0]).all()
  11. #entry={'Boxes':Boxes,
  12. #'gt_overlaps':self.roidb[i]['gt_overlaps'],
  13. #'gt_classes':self.roidb[i]['gt_classes'],
  14. #'flipped':True}
  15. #self.roidb.append(entry)
  16. #self._image_index=self._image_index*2
  17. self._image_index=self._image_index
如果你的数据比较多的话同样也可以去掉这个函数,该函数也可能会引发一些问题,具体更改和解决方法参见 Faster-RCNN+ZF用自己的数据集训练模型(Python版本)——小咸鱼_的博客
好啦,接下来应该就可以愉快的训练自己的数据了,进入py-faster-rcnn/experiments/scripts目录,使用命令
  1. faster_rcnn_end2end.sh0VGG16pascal_voc
其中0表示你训练时使用的gpu标号,VGG16是模型类型,具体的内容可以去读faster_rcnn_end2end.sh的源码。
(注意:如果更改数据以后,再次训练以前一定要把py-faster-rcnn/data中的cache目录删掉,否则训练时用的还是以前的数据)
训练结束后,把训练好的模型放入到py-faster-rcnn/data/faster_rcnn_models中,然后调用把demo.py中的测试图片换成自己的测试图像,运行进行测试,如果有需要,也可以自己修改demo文件,程序还是很容易看懂的。

这就是训练的全部过程了,因为配置起来也有一定的时间了,有些错误也记不太清了,我把我还记得的坑都写在上面了,如果配置过程中有其他的一些错误,欢迎在评论里一起交流。

附录

————————————————————————
1. 在测试的时候,可能会出现IndexError: too many indices for array,出现这个错误的原因是你的测试集中有的类别没有出现,在这里只需要在py-faster-rcnn/lib/datasets/voc_eval.py中BB = BB[sorted_ind,:]前一句加上if len(BB) != 0:即可。
2. 在测试时还有可能出现KeyError: '某样本名',这是因为你数据更改了,但是程序有cache文件,把py-faster-rcnn/data/VOCdevkit2007/annotations_cache目录删除即可。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


目录前言一、创建Hadoop用户二、更新apt和安装Vim编辑器三、安装SSH和配置SSH无密码登录四、安装Java环境1. 安装JDK2. 配置JDK环境3. 检验安装五、安装单机Hadoop1. 下载安装Hadoop2. 运行示例总结前言本文安装的 Hadoop 及 Java 环境基于林子雨老师的《大数据技术原理与应用(第3版)》中所要求,其中Java 版本为1.8.0_301,Hadoop 版本为3.3.1,其他版本的安装请参考其他博客。..
原文连接:https://www.cnblogs.com/yasmi/p/5192694.html  运行django出现错误信息:[2016-02-16 14:33:24,476 pyinotify ERROR] add_watch: cannot watch /usr/local/lib/python2.7/dist-packages/django/contrib/sessio...
电脑重启后,打开VirtualBox,发现一直用的虚拟机莫名的消失了,如下:别着急,以下教你如何找回之前的虚拟机:1、点击控制,然后选择注册,找到虚拟机的安装目录,比如:C:UserstxVirtualBox VMs,然后选择需要找回的虚拟机vbox,点击打开按钮即可:2、如果打开后报错,则执行第三步:3、删除ubuntu.vbox,然后将ubuntu.vbox-prev重命名为ubuntu.vbox,然后再执行第二步即可...
参见:https://blog.csdn.net/weixin_38883338/article/details/82153933 https://blog.csdn.net/github_39533414/article/details/85211012
Ubuntu 18.04 LTS 已切换到 Netplan 来配置网络接口。Netplan 基于 YAML 的配置系统,使得配置过程非常简单。Netplan 替换了我们之前在 Ubuntu 中用于配置网络接口的旧配置文件/etc/network/interfaces。在本文中,我们将学习如何使用 Netplan 在 Ubuntu 中配置网络。我们将看到静态和动态 IP 配置。我将使用 Ubuntu 18.04 LTS 来描述本文中提到的过程。使用 Netplan 配置网络您可以在/etc
介绍每个 Web 服务都可以通过特定的 URL 在 Internet 上访问,该 URL 代表一种“替代名称”,用于标识提供该服务的服务器的 IP 地址和端口。同一台机器可以同时在不同的端口上提供不同的服务。出于安全原因,可能需要屏蔽 Web 服务的端口号,从而在外部显示与服务实际侦听的端口号不对应的端口号。感谢本教程,您将能够管理您的服务器端口,配置集成在 Ubuntu 中的 UFW 防火墙。特别是,按照指南的说明,您将学习将来自某个端口的请求转发到另一个端口(端口转发),同时使用后者提供的.
Observium 是一个免费和开源的 sa 网络管理和监控系统工具。我们可以使用 SNMP 收集数据,它允许监控所有网络设备。它提供了一个简单易用的 Web 界面。它基于 PHP 并使用 MySQL 数据库来存储数据。在 ubuntu 上设置 Observium 有几个步骤:第 1 步:更新系统。apt-get update第 2 步:安装 PHP 和模块。apt install wget apache2 php php-{pear,cgi,common,curl,mbstring,g
从 20.04 开始,Ubuntu 决定更新实时服务器安装程序以实现自动安装规范,以便能够仅使用 Subiquity 完全自动化安装过程。Subiquity 是新的服务器安装程序(又名“服务器无处不在”),旨在取代之前基于 debian-installer 的经典系统。本文说明了如何使用 Packer 和 Proxmox 上的 Subiquity 生成 Ubuntu Server 20.04 图像模板。介绍Subiquity 仅在live图像文件版本中可用(例如ubuntu-20.0...
Ubuntu 将停止支持 Debian 安装程序(预置)。Ubuntu Server 20.04 附带了一种新的自动化操作系统安装方法,称为带有 subiquity 服务器安装程序的自动安装。这篇文章展示了使用新安装程序构建的打包程序。此设置仅适用于 Ubuntu-20.04 live-server 而不是旧版。SubiquitySubiquity 是 Ubuntu 服务器的新自动安装程序,它是在 18.04 中引入的。自动安装的设置由 cloud-init 配置提供。如果设置,将从配置文件.
此页面的目的是提供在您机器上的 VM 中执行自动安装的简单说明。此页面假设您使用的是 amd64 架构。s390x也有一个版本。通过网络提供自动安装数据这种方法是最容易推广到完全基于网络的安装的方法,在这种安装中,机器会进行网络引导,然后自动安装。下载 ISO转到20.04 ISO 下载页面并下载最新的 Ubuntu 20.04 实时服务器 ISO。挂载 ISOsudo mount -r ~/Downloads/ubuntu-20.04-live-server-amd64...
FTP 或文件传输协议是一种非常古老的知名协议,用于在客户端和服务器之间传输文件。它也是一个不安全的协议,仅在没有 TLS 的情况下使用。在本教程中,我们将为 TLS 配置 vsftpd,它允许我们安全地使用 FTP。如今,FTP 经常被更安全的协议所取代,例如 SFTP 或 SCP。但是,如果您需要在服务器上使用 FTP,vsftpd(Very Secure FTP Daemon)是一个完美的选择。在本教程中,我们将学习如何在基于 Ubuntu 20.04 的服务器上使用 vsft
使用cloud-init快速启动来宾操作系统的能力通常与在 EC2 或 Azure 等 IaaS 中部署虚拟机相关。但是 cloud-init 不仅仅适用于远程云提供商,将 cloud-init 用于可以快速部署在 KVM 中的本地映像非常适合本地开发和测试。本文将逐步测试 KVM 上的来宾 Ubuntu 仿生云映像。先决条件作为本文的先决条件,您必须按照此处所述安装 KVM 和 libvirt。还要安装管理云图像所需的附加包:sudo apt-get install -y
Window Linux双系统安装历程今天下午搞了几个小时,可谓是困难重重,但是实际上只是被一个很小的问题困住了,其它地方都很简单。接下来简单讲一下安装的步骤以及遇到的问题。先讲一下设备状况:一台12年买的i3 window7 待报废的lenovo 笔记本一个32g的usb3.0另一台新的win10笔记本电脑磁盘分区首先要在安装双系统的电脑上,划分硬盘中的一个分区来装linxu。点击我的电脑(此电脑)->管理-> 磁盘管理比如这里我们的e盘原本有98.55g,然后还有
https://pinyin.sogou.com/linux/help.php 安装指南 Ubuntu搜狗输入法安装指南 搜狗输入法已支持Ubuntu1604、1804、1910、2004、2010
在home主文件夹里面。
格式:rm -rf 目录名字 -r 就是向下递归,不管有多少级目录,一并删除 -f 就是直接强行删除,不作任何提示的意思 名称 概述 描述 选项
https://blog.csdn.net/qq_29985391/article/details/78008864 https://blog.csdn.net/wangxin0205/article
echo $ROS_PACKAGE_PATH
https://blog.csdn.net/weixin_37657720/article/details/80645991 :q! 不保存强制推出。 :wq 保存并退出。
https://blog.csdn.net/xin_101/article/details/88624726 https://blog.csdn.net/qq_15192373/article/det