├── Ssh.md ├── README.md ├── Docker 深度学习配置.md ├── TensorRT_learning.md └── command for deeplearning.md /Ssh.md: -------------------------------------------------------------------------------- 1 | SSH 学习 2 | 3 | 4 | ssh-genkey 能够在~/.ssh/下建立 私有钥匙和公有钥匙 5 | 6 | id_rsa (private), id_rsa.pub(public) 7 | 8 | 9 | 10 | 将本地产生的key复制到服务器上的 authorized_keys 文件夹下,服务器上的每一个user都有自己的ssh key 11 | ``` 12 | cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys" 13 | ``` 14 | 以上command为一连串的, ssh到server, 然后mkdir .ssh文件夹,接着将key public的内容copy到authorized_keys文件中 15 | 16 | 也可以分开作业 17 | 比如手动复制key 18 | ``` 19 | cat ~/.ssh/id_rsa.pub #这时候会显示key,手动copy起来 20 | ``` 21 | 然后到服务器上touch一个key文件,用vim或者nano贴上复制的key 22 | 然后在 23 | ``` 24 | ssh-add 将刚刚新增的key, 新增到 ssh-agent 25 | ``` 26 | 27 | 28 | 新增私钥 29 | ``` 30 | ssh-add ~/.ssh/xxxxx 31 | ``` 32 | 删除私钥 33 | ``` 34 | ssh-add -d ~/.ssh/xxxx 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## command_for_deeplearning 2 | 3 | 创建本笔记主要的目的在于工程上可以快速查阅忘记的指令,要同时记住那么多指令除了常使用之外, 我认为要作为一个好的程序员最需要的就是做笔记的能力,Linux需要, git做版控也需要指令, docker来下镜像开启容器也需要,其中几个有的时候没有使用就忘记了, 但其实往往最常使用的就那几个主要的罢了, 如果在去百度又太慢了, 网上的笔记通常分成两种, 写的特别详细的, 但看起来很累, 因为我需要快速知道用法就行, 另一种又是写的太简单看起来不靠谱, 为此该笔记记录了一些常用的指令, 以便在需要的时候可以快速查阅, 并且尽量会在笔记旁边放上资料参考来源, 以便日后查阅 4 | 5 | 该笔记主要为深度学习领域较常使用到的, 其他领域暂时不涉及 6 | 7 | 查询指令时爱用 Ctrl + F 或者从目录中找 8 | 9 | #### 更新 10 | 11 | update date 2021.8.10 加入redis-py安装方法 12 | 13 | update date 2021.7.16 加入ffmpeg/nginx rtmp推流方法 、 Linux 指令 rsync 取代scp方法 14 | 15 | update date 2021.4.14 修改许多地方 16 | 17 | update date 2021.4.6 conda 打包方法新增 18 | 19 | update date 2021.3.17 新增vim shift 多行移动 20 | 21 | update date 2021.3.11 修改许多地方 22 | 23 | update date 2021.3.3 修改vim 新增搜索指令, 多行注释 24 | 25 | update date 2021.2.24 新增tmux终端插件指令 26 | 27 | update date 2021.2.19 修改vim 指令 28 | 29 | update date 2021.2.2 新增socket client 和server端 传送webcam图像配置 30 | 31 | update date 2021.1.18 修改ssh 32 | 33 | update date 2021.1.11 修改Ubuntu 终端快捷键 34 | 35 | update date 2021.1.6 conda修改 36 | 37 | update date 2021.1.5 单独拉出ssh 项目, git增加指令 38 | 39 | udpate date 2020.12.18 修改 ffmpeg 剪切指令 40 | 41 | update date 2020.12.17 ffmpeg 添加合成帧指令 42 | 43 | update date 2020.12.15 修改ffmpeg 指令 44 | 45 | update date 2020.12.9 添加conda clone 指令 46 | 47 | update date 2020.12.4 添加rename指令 48 | 49 | update date 2020.12.2 修改rm指令 50 | 51 | update date 2020.12.1 增加grep 筛选 / lsof 指令 52 | 53 | update date 2020.11.27 增加tensorboard 安装 54 | 55 | update date 2020.11.12 修正Ubuntu 安装python 56 | 57 | update date 2020.11.9 修改conda 加入安装教学/ Linux dd指令参考 58 | 59 | udpate date 2020.8.14 增加Ubuntu 安装Kolourpain4 60 | 61 | update date 2020.8.7 修改pip 修改源的部分 62 | 63 | update date 2020.8.4 加入新markdown文件 Docker深度学习配置, 添加crontab定时, 缓存清除脚本 64 | 65 | update date 2020.7.23 新增NVIDIA驱动安装, Ubuntu下安装v2ray翻墙 66 | 67 | update date 2020.7.13 将IDE重新拉出设置为独立篇章 68 | 69 | update date 2020.7.10 修正docker安装, 进入容器方法, 加速源更改 70 | 71 | update date 2020.7.6 添加vim :X 文件加密指令 72 | 73 | update date 2020.6.25 修正Linux nm指令 74 | 75 | update date 2020.6.10 修正CUDA安装 76 | 77 | update date 2020.6.1 新增Opencv 配置处理问题 78 | 79 | update date 2020.5.18 新增CUDA / CUDNN 安装, 移除, 共存 80 | 81 | update date 2020.5.16 新增Linux ldd命令 82 | 83 | update date 2020.5.9 新增Linux 程序安装(Ubuntu, opencv cmake) 84 | 85 | update date 2020.5.7 新增Linux 安装openCV的方法 86 | 87 | update date 2020.5.6 新增Linux OS安装方法连接 88 | 89 | update date 2020.4.24 新增Linux 其他 screen后台执行程序 90 | 91 | update date 2020.4.2 新增Linux 程序安装, g++/gcc 参数 92 | 93 | update date 2020.4.1 新增g++/gcc 升降版本方法, 修正查看版本格式 94 | 95 | update date 2020.3.31 新增Linux su指令 96 | 97 | update date 2020.3.11 新增unrar 解压缩指令 98 | 99 | update date 2020.3.5 新增other下 ffmpeg基础使用 100 | 101 | update date 2020.3.4 修正第8点解压缩 102 | 103 | update date 2020.2.20 修正git 部分 104 | 105 | update date 2020.2.15 修正第10点 In 106 | 107 | update date 2020.2.8 新增conda换源方式 108 | 109 | update date 2020.1.22 修正Linux 第15点 成其他 110 | 111 | update date 2020.1.19 更新linux find指令 112 | 113 | update date 2020.1.17 更新git, docker 114 | 115 | update date 2020.1.4 修正vim 部分 116 | 117 | udpate date 2019.12.27 新增git指令 branch 118 | 119 | update date 2019.12.24 修正others 查看版本openCV 120 | 121 | update date 2019.12.19 新增others 大项, 新增28.onnx 模型相关 29.PytorchToCaffe 122 | 123 | update date 2019.12.17 修正第23点为 python pip 包管理 124 | 125 | update date 2019.12.8 新增shell脚本基础, chmod权限设置, docker 修改(还需要修正) 126 | 127 | update date 2019.12.5 新增swapfile 新增虚拟内存 128 | 129 | update date 2019.10.24 新增Docker 指令 130 | 131 | update date 2019.10.7持续更新中 132 | 133 | 内容涵盖如下 134 | Linux 135 | vim 136 | anaconda3 137 | git 138 | Docker 139 | Shell script 140 | others 141 | cuda, pycharm SSH, 清华源加速, python安装 142 | -------------------------------------------------------------------------------- /Docker 深度学习配置.md: -------------------------------------------------------------------------------- 1 | update 20200804 2 | 3 | #### 注意 ! 本文档推荐使用typora 阅读 4 | 5 | 测试环境 : 6 | 7 | 1. Docker 19.03版本 8 | 2. GPU 2080Ti 9 | 10 | 11 | 12 | 13 | 14 | ### 注意 !1~7全部配置完在看 ! 15 | 16 | 启动容器的方式, 假设容器叫做nv_xxx 17 | 18 | ``` 19 | sudo docker run --gpus all -itd -p 23:22 nv_xxx:latest 20 | ``` 21 | 22 | 每次启动ssh需要重新开启, 这样才能远程连接 23 | 24 | ``` 25 | sudo /etc/init.d/ssh start 26 | ``` 27 | 28 | 如果要使用conda环境, 每次启动都要激活 29 | 30 | ``` 31 | cd /root/miniconda3/bin 32 | . ./activate #注意这边的确是两个点 33 | ``` 34 | 35 | 36 | 37 | 依照以下流程开始走 38 | 39 | ### 1. 采用nvidia官方镜像 nvidia/cuda-10.0:base 40 | 41 | 主要是让Docker支持GPU 42 | 43 | 现在本地安装**nvidia container tookit** 44 | 45 | 参考 https://github.com/NVIDIA/nvidia-docker 46 | 47 | ``` 48 | # Add the package repositories 49 | distribution=$(. /etc/os-release;echo $ID$VERSION_ID) 50 | curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - 51 | curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list 52 | 53 | sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit 54 | sudo systemctl restart docker 55 | ``` 56 | 57 | 然后启动 58 | 59 | ```shell 60 | #### Test nvidia-smi with the latest official CUDA image 61 | docker run --gpus all nvidia/cuda:10.0-base nvidia-smi 62 | #会自动下载nvidia这个镜像 63 | 64 | 65 | # Start a GPU enabled container on two GPUs 66 | docker run --gpus 2 nvidia/cuda:10.0-base nvidia-smi 67 | 68 | # Starting a GPU enabled container on specific GPUs 69 | docker run --gpus '"device=1,2"' nvidia/cuda:10.0-base nvidia-smi 70 | docker run --gpus '"device=UUID-ABCDEF,1"' nvidia/cuda:10.0-base nvidia-smi 71 | 72 | # Specifying a capability (graphics, compute, ...) for my container 73 | # Note this is rarely if ever used this way 74 | docker run --gpus all,capabilities=utility nvidia/cuda:10.0-base nvidia-smi 75 | ``` 76 | 77 | 78 | 79 | 上面指令会自动pull nvidia的镜像, `nvidia/cuda:10.0-base` 80 | 81 | 后续要启动的话, 82 | 83 | ``` 84 | sudo docker run --gpus all -itd nvidia/cuda:10.0-base 85 | ``` 86 | 87 | 88 | 89 | 接着以下开始配置镜像, 也就是安装自己需要的东西 90 | 91 | ### 2. 在容器内配置阿里 apt源 92 | 93 | 参考https://blog.csdn.net/u012308586/article/details/102953882 94 | 95 | 1. 备份原始源文件source.list 96 | 97 | ```shell 98 | sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 99 | ``` 100 | 101 | 2. 修改源文件sources.list 102 | 103 | ``` 104 | sudo chmod 777 /etc/apt/sources.list #更改文件权限使其可编辑 105 | sudo vim /etc/apt/sources.list #记得先安装vim 106 | ``` 107 | 108 | ```shell 109 | deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse 110 | 111 | deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse 112 | 113 | deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse 114 | 115 | deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse 116 | 117 | deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse 118 | 119 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse 120 | 121 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse 122 | 123 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse 124 | 125 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse 126 | 127 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse 128 | 129 | ``` 130 | 131 | 132 | 133 | 3. 最后更新一下源 134 | 135 | ```shell 136 | sudo apt update 137 | ``` 138 | 139 | 140 | 141 | ### 3. 安装杂七杂八gcc ..cmake等 142 | 143 | ``` 144 | apt-get gcc 145 | apt-get g++ 146 | apt-get install automake autoconf libtool make 147 | apt-get install cmake 148 | 149 | apt-get install git 150 | 151 | ``` 152 | 153 | 154 | 155 | ### 4. 安装python3.7 and pip3 156 | 157 | ``` 158 | #先按装 159 | apt-get install zlib* 160 | sudo apt-get install libffi-dev 161 | 162 | 163 | wget https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz 164 | tar -zxvf Python-3.7.1.tgz 165 | cd Python-3.7.1 166 | ./configure --prefix=/usr/local/python3.7.1 167 | make 168 | make test 169 | sudo make install 170 | 171 | ``` 172 | 173 | 可执行文件会在/usr /local/python3.7.1/bin 174 | 175 | ``` 176 | PATH=$PATH:$HOME/bin:/usr/local/python3.7.1/bin 177 | 178 | ``` 179 | 180 | 181 | 182 | ```shell 183 | vim ~/.bashrc 184 | 添加 export PATH=$PATH:$HOME/bin:/usr/local/python3.7.1/bin 185 | source ~/.bashrc 186 | 187 | python3 #测试一下有没有进入python的交互环境 188 | 189 | ``` 190 | 191 | 安装pip3 192 | 193 | ``` 194 | apt-get install python3-pip 195 | 196 | ``` 197 | 198 | 安装好直接修改pip源, 替换成阿里源 199 | 200 | ``` 201 | pip config set global.index-url http://mirrors.aliyun.com/pypi/simple 202 | 203 | ``` 204 | 205 | 206 | 207 | 208 | 209 | ### 5. 安装ssh, 为了让pycharm or 其他IDE可以连接到docker进行配置 210 | 211 | ``` 212 | apt-get install openssh-server 213 | ``` 214 | 215 | ``` 216 | passwd root #设置密码为了让ssh远程登入的时候 要输入验证 217 | #假设密码设置为xxx 218 | xxx 219 | xxx 220 | 221 | 222 | ``` 223 | 224 | ``` 225 | vim /etc/ssh/sshd_config #进行修改 226 | 227 | RSAAuthentication yes #启用 RSA 认证 228 | PubkeyAuthentication yes #启用公钥私钥配对认证方式 229 | AuthorizedKeysFile .ssh/authorized_keys #公钥文件路径(和上面生成的文件同) 230 | PermitRootLogin yes #root能使用ssh登录 231 | 232 | service ssh restart 233 | sudo /etc/init.d/ssh start 234 | 完成以上几步后,本机地址是127.0.0.1 235 | 236 | ``` 237 | 238 | 239 | 240 | 在container中安装好ssh之后, 下次启动容器的时候, 用如下指令 241 | 242 | ``` 243 | sudo docker run -itd -p 16022:22 xxx::latest 244 | ``` 245 | 246 | 也就是设置端口号 247 | 248 | 249 | 250 | 然后如下两种方式远程连接到docker中 251 | 252 | 第一种 本地电脑连接本地docker如下 253 | 254 | pycharm 连接的时候或者用ssh连接 255 | 256 | ``` 257 | ssh root@127.0.0.1 -p 16022 258 | 259 | ``` 260 | 261 | 然后输入使用者密码就能登入 262 | 263 | 264 | 265 | 第二种 局部网电脑远程(wifi)连接本地docker 266 | 267 | 在本机利用 268 | 269 | ``` 270 | ifconfig -a 271 | ``` 272 | 273 | 查看enp3s0下的 274 | 275 | Inet 地址是为何 比如是`192.168.1.8` 276 | 277 | 278 | 279 | 然后一样外部的电脑中的pycharm设置ssh 连线 280 | 281 | ``` 282 | SFTP 283 | root@192.168.1.8 端口设置16022 284 | 就能远程连线 285 | 286 | ``` 287 | 288 | 289 | 290 | 291 | 292 | 2375端口 293 | 294 | https://blog.csdn.net/qq_18881987/article/details/95441352?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase 295 | 296 | 297 | 298 | 299 | 300 | #### 6. CUDA 安装 301 | 302 | #### 安装CUDA 303 | 304 | 把cuda 跟 cudnn 安装压缩包放置到home下, 然后 305 | 306 | ``` 307 | sudo sh cuda_10.0.130_410.48_linux.run 308 | 309 | //注意 ! 如果遇到空间不足在用以下的方法 310 | sudo sh cuda_10.0.130_410.48_linux.run --tmpdir=/home 311 | 312 | #会遇到如下问题 313 | 1. EULA? accept 314 | 2. driver? no 315 | 3. cuda10.0 toolkit? yes 316 | 4. localtion /usr/local/cuda-10.0 ? 直接回车 317 | 5. symbolic yes 318 | 6. install samples ? no 319 | 320 | vim ~/.bashrc 321 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64 322 | export PATH=$PATH:/usr/local/cuda/bin 323 | export CUDA_HOME=$CUDA_HOME:/usr/local/cuda 324 | 325 | 326 | source ~/.bashrc //最后更新一下 327 | nvcc -V #如果有效果就表示安装成功 328 | 329 | ``` 330 | 331 | #### 安装CUDNN 332 | 333 | ``` 334 | 解压缩cu 335 | 336 | 得到cuda文件夹, 将里面的Include/ 以及 lib64/下的内容丢到/usr/local/cuda/下对应的文件夹下面就完成 337 | 338 | ``` 339 | 340 | 341 | 342 | #### 7. 安装conda 343 | 344 | ```d 345 | wget -c https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh 346 | chmod 777 Miniconda3-latest-Linux-x86_64.sh #给执行权限 347 | ./Miniconda3-latest-Linux-x86_64.sh 348 | 349 | ``` 350 | 351 | 安装好之后 直接输入conda 是command not found, 需要激活一下 352 | 353 | 找到安装路径 354 | 355 | `/root/miniconda3/bin` 356 | 357 | ``` 358 | chmod 777 activate 359 | . ./activate #注意有两个点 之间还有空格 360 | 361 | ``` 362 | 363 | 激活之后, 前面会出现base , 这就表示已经进入的conda环境 364 | 365 | ``` 366 | (base) root@xxxxxxx: 367 | 368 | ``` 369 | 370 | -------------------------------------------------------------------------------- /TensorRT_learning.md: -------------------------------------------------------------------------------- 1 |

模型加速

2 |
update 2021.5.10
3 | 4 | 鉴于没有一个好的教程, 网上简体中文教程多半机械式翻译, 跟垃圾差不多, 固自己记录一份并与官方文档及各博客进行整合, 在做模型转换之前, 请先确认你的onnx model 是那个版本torch进行转换的, 如果是1.3, 则TensorRT6并不支持, 5 | 6 | > ### 目录 7 | > 8 | > ### [TensorRT](1) 9 | > 10 | > 1. TensorRT 安装步骤 11 | > 2. TensorRT 基础使用(中文) 12 | > 3. TensorRT core library (官方内容) 13 | > 4. Building An Engine In C++ 14 | > 5. Adding A Custom Layer To Your Network In TensorRT 15 | > 6. Performing Inference In INT8 Using Custom Calibration 16 | > 7. TensorRT API 17 | > 1. 构建网路层及运行推断 18 | > 19 | > ### torch.onnx 20 | > 21 | > 1. 基础使用 22 | > 23 | > ### Libtorch 24 | > 25 | > 26 | > 27 | > ### Onnxruntime 28 | > 29 | > 30 | > 31 | > ### Onnx-TensorRT 32 | > 33 | > 1. Onnx-TensorRT 安装步骤 34 | > 2. onnx转tensorrt 自定义op 35 | > 36 | > 37 | > 38 | > ### Torch2Trt 39 | > 40 | > //TODO 41 | 42 | 43 | 44 | 45 | 46 |

TensorRT

47 | 48 | > ### TensorRT 安装步骤 49 | 50 | 依照CUDA版本下载相对应的CUDNN 51 | 52 | 1. 下载对应cuda版本的cudnn 53 | 54 | ``` 55 | tar -xvzf cudnn-10.0-linux-x64-v7.6.5.32.tgz -C 目的path 56 | #解压缩出来会是一个cuda资料夹 57 | cd 到cuda下 58 | 59 | sudo cp include/cudnn.h /usr/local/cuda/include/ 60 | sudo cp lib64/lib* /usr/local/cuda/lib64/ 61 | 62 | 63 | cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2 #测试 64 | ``` 65 | 66 | 67 | 68 | 2. 下载适合环境的TensorRT版本以及安装 69 | 70 | - 到官方下载适合环境的TensorRT https://developer.nvidia.com/tensorrt 71 | 72 | - 下载好之后 解压缩 73 | 74 | `tar -xzvf TensorRT-7.0.0.11.Ubuntu-16.04.x86_64-gnu.cuda-10.0.cudnn7.6.tar.gz -C /指定你要的路径` 75 | 76 | - 添加环境变量 `vim ~/.bashrc` 77 | 78 | 例如 `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/你安装的路径/TensorRT-7.0.0.11/lib` 79 | 80 | 之后记得`source ~/.bashrc`一下 81 | 82 | - `cd TensorRT-7.x.x.x/python ` 83 | 84 | - 依照python版本选择相应的安装, ex `pip install tensorrt-6.0.1.5-cp36-none-linux_x86_64.whl` 85 | 86 | 87 | 88 | > ### 中途遇见的问题 89 | 90 | **问题1** 91 | 92 | pip install tensorrt-6.0.1.5-cp36-none-linux_x86_64.whl 的时候出问题 93 | 94 | 报错如下 95 | 96 | ERROR: tensorrt-6.0.1.5-cp36-none-linux_x86_64.whl is not a supported wheel on this platform 97 | 98 | 这通常只会有两个原因, 但大部分是第一个原因, 也就是python环境选错了, 例如python3.5 则安装3.6的 99 | 100 | 注意如果使用conda可能会遇到该问题, 在conda下安装请使用pip install, 不要用pip3 101 | 102 | 103 | 104 | > ### TensorRT基础使用(中文) 105 | > 106 | > 各版本官方文档https://s0docs0nvidia0com.icopy.site/deeplearning/sdk/tensorrt-release-notes/index.html 107 | > 108 | > 内容大部分参考截取自 https://murphypei.github.io/blog/2019/09/trt-useage 109 | 110 | 111 | 112 | TRT 将模型结构和参数以及相应 kernel 计算方法都编译成一个二进制 engine,因此在部署之后大大加快了推理速度。为了能够使用 TRT 进行推理,需要创建一个 eninge。TRT 中 engine 的创建有两种方式 113 | 114 | - 通过网络模型结构和参数文件编译得到,比较慢。 115 | - 读取一个已有的 engine(gie 文件),因为跳过了模型解析等过程,速度更快。 116 | 117 | 118 | 119 | 现在假设我们是第一次用 TRT,所以就只能选择第一种方式来创建一个 engine。为了创建一个 engine,我们需要有模型结构和模型参数两个文件,同时需要能够解析这两个文件的方法。在 TRT 中,编译 engine 是通过 `IBuilder` 对象进行的,因此我们首先需要新键一个 `IBuilder` 对象: 120 | 121 | ``` 122 | nvinfer1::IBuilder *builder = createInferBuilder(gLogger); 123 | ``` 124 | 125 | > `gLogger` 是 TRT 中的日志接口 `ILogger` ,继承这个接口并创建自己的 logger 对象传入即可。 126 | 127 | 为了编译一个 engine,`builder` 首先需要创建一个 `INetworkDefinition` 作为模型的容器: 128 | 129 | ``` 130 | nvinfer1::INetworkDefinition *network = builder->createNetwork(); 131 | ``` 132 | 133 | 注意,**此时 network 是空的**,我们需要填充模型结构和参数,也就是解析我们自己的模型结构和参数文件,获取数据放到其中。 134 | 135 | TRT 官方给了三种主流框架模型格式的解析器(parser),分别是: 136 | 137 | - ONNX:`IOnnxParser parser = nvonnxparser::createParser(*network, gLogger);` 138 | - Caffe:`ICaffeParser parser = nvcaffeparser1::createCaffeParser();` 139 | - UFF:`IUffParser parser = nvuffparser::createUffParser();` 140 | 141 | 其中 UFF 是用于 TensorFlow 的格式。调用这三种解析器就可以解析相应的文件。以 `ICaffeParser` 为例,调用其 `parse` 方法来填充 `network`。 142 | 143 | ```C++ 144 | virtual const IBlobNameToTensor* nvcaffeparser1::ICaffeParser::parse( 145 | const char* deploy, 146 | const char * model, 147 | nvinfer1::INetworkDefinition &network, 148 | nvinfer1::DataType weightType) 149 | 150 | //Parameters 151 | //deploy The plain text, prototxt file used to define the network configuration. 152 | //model The binaryproto Caffe model that contains the weights associated with the network. 153 | //network Network in which the CaffeParser will fill the layers. 154 | //weightType The type to which the weights will transformed. 155 | ``` 156 | 157 | 这样就能得到一个填充好的 `network` ,就可以编译 engine 了,似乎一切都很美妙呢… 158 | 159 | 然而实际 TRT 并不完善,比如 TensorFlow 的很多操作并不支持,因此你传入的文件往往是根本就解析不了(深度学习框架最常见的困境之一)。因此我们需要自己去做填充 `network` 这件事,这就需要调用 TRT 中低级别的接口来创建模型结构,类似于你在 Caffe 或者 TensorFlow 中做的那样。 160 | 161 | 162 | 163 | TRT 提供了较为丰富的接口让你可以直接通过这些接口创建自己的网络,比如添加一个卷积层: 164 | 165 | ```C++ 166 | virtual IConvolutionLayer* nvinfer1::INetworkDefinition::addConvolution(ITensor &input, 167 | int nbOutputMaps, 168 | DimsHW kernelSize, 169 | Weights kernelWeights, 170 | Weights biasWeights) 171 | 172 | // Parameters 173 | // input The input tensor to the convolution. 174 | // nbOutputMaps The number of output feature maps for the convolution. 175 | // kernelSize The HW-dimensions of the convolution kernel. 176 | // kernelWeights The kernel weights for the convolution. 177 | // biasWeights The optional bias weights for the convolution. 178 | ``` 179 | 180 | 这里的参数基本上就是和其他深度学习框架类似的意思,没有什么好讲的。就是把数据封装成 TRT 中的数据结构即可。可能和平时构建训练网络不同的地方就是需要填充好模型的参数,因为 TRT 是推理框架,参数是已知确定的。这个过程一般是读取已经训练好的模型,构造 TRT 的数据结构类型放到其中,也就是需要你自己去解析模型参数文件。 181 | 182 | 之所以说 TRT 的网络构造接口是**较为丰富**,是因为即使使用这些低级接口这样,很多操作还是没办法完成,也就是没有相应的 `add*` 方法,更何况现实业务可能还会涉及很多自定义的功能层,因此 TRT 又有了 plugin 接口,允许你自己定义一个 `add*` 的操作。其流程就是继承 `nvinfer1::IPluginV2` 接口,利用 cuda 编写一个自定义层的功能,然后继承 `nvinfer1::IPluginCreator` 编写其创建类,需要重写其虚方法 `createPlugin`。最后调用 `REGISTER_TENSORRT_PLUGIN` 宏来注册这个 plugin 就可以用了。plugin 接口的成员函数介绍。 183 | 184 | 185 | 186 | ```C++ 187 | // 获得该自定义层的输出个数,比如 leaky relu 层的输出个数为1 188 | virtual int getNbOutputs() const = 0; 189 | 190 | // 得到输出 Tensor 的维数 191 | virtual Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) = 0; 192 | 193 | // 配置该层的参数。该函数在 initialize() 函数之前被构造器调用。它为该层提供了一个机会,可以根据其权重、尺寸和最大批量大小来做出算法选择。 194 | virtual void configure(const Dims* inputDims, int nbInputs, const Dims* outputDims, int nbOutputs, int maxBatchSize) = 0; 195 | 196 | // 对该层进行初始化,在 engine 创建时被调用。 197 | virtual int initialize() = 0; 198 | 199 | // 该函数在 engine 被摧毁时被调用 200 | virtual void terminate() = 0; 201 | 202 | // 获得该层所需的临时显存大小。 203 | virtual size_t getWorkspaceSize(int maxBatchSize) const = 0; 204 | 205 | // 执行该层 206 | virtual int enqueue(int batchSize, const void*const * inputs, void** outputs, void* workspace, cudaStream_t stream) = 0; 207 | 208 | // 获得该层进行 serialization 操作所需要的内存大小 209 | virtual size_t getSerializationSize() = 0; 210 | 211 | // 序列化该层,根据序列化大小 getSerializationSize(),将该类的参数和额外内存空间全都写入到序列化buffer中。 212 | virtual void serialize(void* buffer) = 0; 213 | ``` 214 | 215 | 我们需要根据自己层的功能,重写这里全部或者部分函数的实现,这里有很多细节,没办法一一展开,需要自定义的时候还是需要看官方 API。 216 | 217 | 构建好了网络模型,就可以执行 engine 的编译了,还需要对 engine 进行一些设置。比如计算精度,支持的 batch size 等等,因为这些设置不同,编译出来的 engine 也不同。 218 | 219 | TRT 支持 FP16 计算,也是官方推荐的计算精度,其设置也比简单,直接调用: 220 | 221 | 222 | 223 | ``` 224 | builder->setFp16Mode(true); 225 | ``` 226 | 227 | 另外在设置精度的时候,还有一个设置 strict 策略的接口: 228 | 229 | ``` 230 | builder->setStrictTypeConstraints(true); 231 | ``` 232 | 233 | 这个接口就是是否严格按照设置的精度进行类型转换,如果不设置 strict 策略,则 TRT 在某些计算中可能会选择更高精度(不影响性能)的计算类型。 234 | 235 | 除了精度,还需要设置好运行的 batch size 和 workspace size: 236 | 237 | ``` 238 | builder->setMaxBatchSize(batch_size); 239 | builder->setMaxWorkspaceSize(workspace_size); 240 | ``` 241 | 242 | 这里的 batch size 是运行时最大能够支持的 batch size,运行时可以选择比这个值小的 batch size,workspace 也是相对于这个最大 batch size 设置的。 243 | 244 | 设置好上述参数,就可以编译 engine 了。 245 | 246 | ``` 247 | nvinfer1::ICudaEngine *engine = builder->buildCudaEngine(*network); 248 | ``` 249 | 250 | 编译需要花较长时间,耐心等待。 251 | 252 | 253 | 254 | ### Engine 序列化和反序列化 255 | 256 | 编译 engine 需要较长时间,在模型和计算精度、batch size 等均保持不变的情况下,我们可以选择保存 engine 到本地,供下次运行使用,也就是 engine 序列化。TRT 提供了很方便的序列化方法: 257 | 258 | 259 | 260 | ``` 261 | nvinfer1::IHostMemory *modelStream = engine->serialize(); 262 | ``` 263 | 264 | 通过这个调用,得到的是一个二进制流,将这个流写入到一个文件中即可保存下来。 265 | 266 | 如果需要再次部署,可以直接反序列化保存好的文件,略过编译环节。 267 | 268 | ``` 269 | IRuntime* runtime = createInferRuntime(gLogger); 270 | ICudaEngine* engine = runtime->deserializeCudaEngine(modelData, modelSize, nullptr); 271 | ``` 272 | 273 | ### 使用 engine 进行预测 274 | 275 | 有了 engine 之后就可以使用它进行 inference 了。 276 | 277 | 首先创建一个 inference 的 context。这个 context 类似命名空间,用于保存一个 inference 任务的变量。 278 | 279 | ``` 280 | IExecutionContext *context = engine->createExecutionContext(); 281 | ``` 282 | 283 | **一个 engine 可以有多个 context**,也就是说一个 engine 可以同时进行多个预测任务。 284 | 285 | 然后就是绑定输入和输出的 index。这一步的原因在于 TRT 在 build engine 的过程中,将输入和输出映射为索引编号序列,因此我们只能通过索引编号来获取输入和输出层的信息。虽然 TRT 提供了通过名字获取索引编号的接口,但是本地保存可以方便后续操作。 286 | 287 | 我们可以先获取索引编号的数量: 288 | 289 | ``` 290 | int index_number = engine->getNbBindings(); 291 | ``` 292 | 293 | 我们可以判断这个编号数量是不是和我们网络的输入输出之和相同,比如你有一个输入和一个输出,那么编号的数量就是2。如果不是,则说明这个 engine 是有问题的;如果没问题,我们就可以通过名字获取输入输出对应的序号: 294 | 295 | ``` 296 | int input_index = engine->getBindingIndex(input_layer_name); 297 | int output_index = engine->getBindingIndex(output_layer_name); 298 | ``` 299 | 300 | 对于常见的一个输入和输出的网络,输入的索引编号就是 0,输出的索引编号就是 1,所以这一步也不是必须的。 301 | 302 | 接下来就需要为输入和输出层分配显存空间了。为了分配显存空间,我们需要知道输入输出的维度信息和存放的数据类型,TRT 中维度信息和数据类型的表示如下: 303 | 304 | ```c++ 305 | class Dims 306 | { 307 | public: 308 | static const int MAX_DIMS = 8; //!< The maximum number of dimensions supported for a tensor. 309 | int nbDims; //!< The number of dimensions. 310 | int d[MAX_DIMS]; //!< The extent of each dimension. 311 | DimensionType type[MAX_DIMS]; //!< The type of each dimension. 312 | }; 313 | 314 | enum class DataType : int 315 | { 316 | kFLOAT = 0, //!< FP32 format. 317 | kHALF = 1, //!< FP16 format. 318 | kINT8 = 2, //!< quantized INT8 format. 319 | kINT32 = 3 //!< INT32 format. 320 | }; 321 | ``` 322 | 323 | 我们通过索引编号获取输入和输出的数据维度(dims)和数据类型(dtype),然后为每个输出层开辟显存空间,存放输出结果: 324 | 325 | ``` 326 | for (int i = 0; i < index_number; ++i) 327 | { 328 | nvinfer1::Dims dims = engine->getBindingDimensions(i); 329 | nvinfer1::DataType dtype = engine->getBindingDataType(i); 330 | // 获取数据长度 331 | auto buff_len = std::accumulate(dims.d, dims.d + dims.nbDims, 1, std::multiplies()); 332 | // ... 333 | // 获取数据类型大小 334 | dtype_size = getTypeSize(dtype); // 自定义函数 335 | } 336 | 337 | // 为 output 分配显存空间 338 | for (auto &output_i : outputs) 339 | { 340 | cudaMalloc(buffer_len_i * dtype_size_i * batch_size); 341 | } 342 | ``` 343 | 344 | > 本文给出的是伪代码,仅表示逻辑,因此会涉及一些简单的自定义函数。 345 | 346 | 至此,我们已经做好了准备工作,现在就可以把数据塞进模型进行推理了。 347 | 348 | #### 前向预测 349 | 350 | TRT 的前向预测执行是异步的,context 通过一个 enqueue 调用来提交任务: 351 | 352 | ``` 353 | cudaStream_t stream; 354 | cudaStreamCreate(&stream); 355 | context->enqueue(batch_size, buffers, stream, nullptr); 356 | cudaStreamSynchronize(stream); 357 | ``` 358 | 359 | enqueue 是 TRT 的实际执行任务的函数,我们在写 plugin 的时候也需要实现这个函数接口。其中: 360 | 361 | - `batch_size`:engine 在 build 过程中传入的 `max_batch_size`。 362 | 363 | - `buffers`:是一个指针数组,其下标对应的就是输入输出层的索引编号,存放的就是输入的数据指针以及输出的数据存放地址(也就是开辟的显存地址)。 364 | 365 | - `stream`:stream 是 cuda 一系列顺序操作的概念。对于我们的模型来说就是将所有的模型操作按照(网络结构)指定的顺序在指定的设备上执行。 366 | 367 | > cuda stream 是指一堆异步的 cuda 操作,他们按照 host 代码调用的顺序执行在 device 上。stream 维护了这些操作的顺序,并在所有预处理完成后允许这些操作进入工作队列,同时也可以对这些操作进行一些查询操作。这些操作包括 host 到 device 的数据传输,launch kernel 以及其他的 host 发起由 device 执行的动作。这些操作的执行总是异步的,cuda runtime 会决定这些操作合适的执行时机。我们则可以使用相应的cuda api 来保证所取得结果是在所有操作完成后获得的。**同一个 stream 里的操作有严格的执行顺序**,不同的 stream 则没有此限制。 368 | 369 | 这里需要注意,输入数据和输出数据在 buffers 数组中都是在 GPU 上的,可以通过 `cudaMemcpy` 拷贝 CPU 上的输入数据到 GPU 中(需要提前开辟一块显存来存放)。同理,输出数据也需要从 GPU 中拷贝到 CPU 中。 370 | 371 | 前两句创建了一个 cuda stream,最后一句则是等待这个异步 stream 执行完毕,然后从显存中将数据拷贝出来即可。 372 | 373 | 至此,我们就完成了 TRT 一个基本的预测流程。 374 | 375 | ### 总结 376 | 377 | 本文仅仅是针对 TRT 的预测流程和一些常见调用进行了说明,并不涉及具体网络和具体实现,也没有太多编码的细节。不同网络不同操作需要一些扩展 plugin 的编写,而对于编码,包括内存和显存的开辟管理,TRT 的析构清理工作等等都不在本文叙述范围之内。 378 | 379 | #### 参考资料: 380 | 381 | - https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#c_topics 382 | - https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/c_api/index.html 383 | - https://www.cnblogs.com/1024incn/p/5891051.html 384 | - **本文作者:** murphypei 385 | - **本文链接:** https://murphypei.github.io/blog/2019/09/trt-useage.html 386 | - **版权声明:** 本博客所有文章除特别声明外,均采用 [BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/) 许可协议。转载请注明出处! 387 | 388 | 389 | 390 | 391 | 392 | ------ 393 | 394 | 以下为官方内容 395 | 396 | 397 | 398 | > ### TensorRT core library 399 | 400 | 主要分为下列三个部分 401 | 402 | - Network Definition 403 | 404 | The Network Definition interface provides methods for the application to specify the definition of a network. Input and output tensors can be specified, layers can be added, and there is an interface for configuring each supported layer type. As well as layer types, such as convolutional and recurrent layers, and a Plugin layer type allows the application to implement functionality not natively supported by TensorRT. For more information about the Network Definition, see [Network Definition API](http://docs.nvidia.com/deeplearning/sdk/tensorrt-api/c_api/classnvinfer1_1_1_i_network_definition.html). 405 | 406 | - Builder 407 | 408 | The Builder interface allows the creation of an optimized engine from a network definition. It allows the application to specify the maximum batch and workspace size, the minimum acceptable level of precision, timing iteration counts for autotuning, and an interface for quantizing networks to run in 8-bit precision. For more information about the Builder, see [Builder API](http://docs.nvidia.com/deeplearning/sdk/tensorrt-api/c_api/classnvinfer1_1_1_i_builder.html). 409 | 410 | - Engine 411 | 412 | The Engine interface allows the application to execute inference. It supports synchronous and asynchronous execution, profiling, and enumeration and querying of the bindings for the engine inputs and outputs. A single-engine can have multiple execution contexts, allowing a single set of trained parameters to be used for the simultaneous execution of multiple batches. For more information about the Engine, see [Execution API](https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/c_api/classnvinfer1_1_1_i_cuda_engine.html). 413 | 414 | > ### C++ VS python 415 | 416 | The C++ API should be used in any performance-critical scenarios, as well as in situations where safety is important, for example, in automotive. 417 | 418 | The main benefit of the Python API is that data preprocessing and postprocessing are easy to use because you’re able to use a variety of libraries like NumPy and SciPy. For more information about the Python API, see [Using The Python API](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#python_topics). 419 | 420 | 421 | 422 | 423 | 424 | > ### Building An Engine In C++ 425 | 426 | The next step is to invoke the TensorRT builder to create an optimized runtime. One of the functions of the builder is to search through its catalog of CUDA kernels for the fastest implementation available, and thus it is **necessary to use the same GPU for building like that on which the optimized engine will run.** 427 | 428 | 编译engine的以及使用的 必须在同一个GPU 429 | 430 | 431 | 432 | Two particularly important properties are the maximum batch size and the maximum workspace size. 433 | 434 | 435 | 436 | #### 1. Build the engine using the builder object: 437 | 438 | ``` 439 | builder->setMaxBatchSize(maxBatchSize); 440 | IBuilderConfig * config = builder->createBuilderConfig(); 441 | config->setMaxWorkspaceSize(1 << 20); 442 | ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config); 443 | ``` 444 | 445 | When the engine is built, TensorRT makes copies of the weights. 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | #### 2. Serializing A Model In C++ 序列化engine来存储 454 | 455 | It is not absolutely necessary to serialize and deserialize a model before using it for inference – if desirable, the engine object can be used for inference directly. 456 | 457 | To [serialize](https://en.wikipedia.org/wiki/Serialization), you are transforming the engine into a format to store and use at a later time for inference. To use for inference, you would simply deserialize the engine. Serializing and deserializing are optional. Since creating an engine from the Network Definition can be time consuming, you could avoid rebuilding the engine every time the application reruns by serializing it once and deserializing it while running inference. Therefore, after the engine is built, users typically want to serialize it for later use. 458 | 459 | **Note:** Serialized engines are not portable across platforms or TensorRT versions. Engines are specific to the exact GPU model they were built on (in addition to the platforms and the TensorRT version). 460 | 461 | 462 | 463 | 1. Run the builder as a prior offline step and then serialize: 464 | 465 | ``` 466 | IHostMemory *serializedModel = engine->serialize(); 467 | // store model to disk 468 | // <…> 469 | serializedModel->destroy(); 470 | ``` 471 | 472 | 2. Create a runtime object to deserialize: 473 | 474 | ``` 475 | IRuntime* runtime = createInferRuntime(gLogger); 476 | ICudaEngine* engine = runtime->deserializeCudaEngine(modelData, modelSize, nullptr); 477 | ``` 478 | 479 | The final argument is a plugin layer factory for applications using custom layers. For more information, see [Extending TensorRT With Custom Layers](https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#extending). 480 | 481 | 482 | 483 | **如果模型有包含plugin, 例如DCNv2** 484 | 485 | 记得如果输入的onnx, 是包含plugin的,必须先调用 createPluginFactory, 例如 486 | 487 | ```c++ 488 | //需要先创建PluginFactory 489 | mPlugins = nvonnxparser::createPluginFactory(gLogger); 490 | 491 | //接着desrializeCudaEngine 第三个参数需要加上 492 | mEngine= mRunTime->deserializeCudaEngine(modelStream->data(), modelStream->size(), mPlugins); 493 | ``` 494 | 495 | 否则在deserialized engine的过程会报以下错误 496 | 497 | ```shell 498 | ERROR: Serialized engine contains plugin, but no plugin factory was provided. To deserialize an engine without a factory, please use IPluginV2 instead. 499 | ``` 500 | 501 | 502 | 503 | 504 | 505 | #### 3. Performing Inference In C++ 506 | 507 | The following steps illustrate how to perform inference in C++ now that you have an engine. 508 | 509 | 510 | 511 | 1. Create some space to store intermediate activation values. Since the engine holds the network definition and trained parameters, additional space is necessary. These are held in an execution context: 512 | 513 | ``` 514 | IExecutionContext *context = engine->createExecutionContext(); 515 | ``` 516 | 517 | An engine can have multiple execution contexts, allowing one set of weights to be used for multiple overlapping inference tasks. For example, you can process images in parallel CUDA streams using one engine and one context per stream. Each context will be created on the same GPU as the engine. 518 | 519 | 2. Use the input and output blob names to get the corresponding input and output index: 520 | 521 | ``` 522 | int inputIndex = engine->getBindingIndex(INPUT_BLOB_NAME); 523 | int outputIndex = engine->getBindingIndex(OUTPUT_BLOB_NAME); 524 | ``` 525 | 526 | 527 | 528 | 529 | 530 | ------ 531 | 532 | > ### Adding A Custom Layer To Your Network In TensorRT 533 | > 534 | > 参考tensorrt 5.1 https://github.com/NVIDIA/TensorRT/tree/release/5.1/samples/opensource/samplePlugin#adding-a-custom-layer-to-your-network-in-tensorrt 535 | 536 | 该章节主要示范如何新增FC layer plugin到自己的网络中 537 | 538 | 539 | 540 | 流程 541 | 542 | - [Defines the network](https://github.com/NVIDIA/TensorRT/tree/release/5.1/samples/opensource/samplePlugin#defining-the-network) 543 | - [Enables custom layers](https://github.com/NVIDIA/TensorRT/tree/release/5.1/samples/opensource/samplePlugin#enabling-custom-layers-in-nvcaffeparser) 544 | - [Builds the engine](https://github.com/NVIDIA/TensorRT/tree/release/5.1/samples/opensource/samplePlugin#building-the-engine) 545 | - [Serialize and deserialize](https://github.com/NVIDIA/TensorRT/tree/release/5.1/samples/opensource/samplePlugin#serializing-and-deserializing4) 546 | - [Initializes the plugin and executes the custom layer](https://github.com/NVIDIA/TensorRT/tree/release/5.1/samples/opensource/samplePlugin#resource-management-and-execution) 547 | 548 | ### Defining the network 549 | 550 | The `FCPlugin` redefines the InnerProduct layer, which has a single output. Accordingly, `getNbOutputs` returns `1` and `getOutputDimensions` includes validation checks and returns the dimensions of the output: 551 | 552 | ```c++ 553 | Dims getOutputDimensions(int index, const Dims* inputDims, 554 | int nbInputDims) override 555 | { 556 | assert(index == 0 && nbInputDims == 1 && 557 | inputDims[0].nbDims == 3); 558 | assert(mNbInputChannels == inputDims[0].d[0] * 559 | inputDims[0].d[1] * 560 | inputDims[0].d[2]); 561 | return DimsCHW(mNbOutputChannels, 1, 1); 562 | } 563 | ``` 564 | 565 | 566 | 567 | //TODO 未完成 568 | 569 | ------ 570 | 571 | 572 | 573 | > ### Performing Inference In INT8 Using Custom Calibration 574 | 575 | 可参阅https://arleyzhang.github.io/articles/923e2c40/ 576 | 577 | 578 | 579 | 一般深度学习框架训练都以float32为主,为什么要使用低精度推理int8? 580 | 581 | - 降低计算量 582 | - 减低推断时间 583 | 584 | 585 | 586 | TensorRT只支持6.1以上的计算力, 587 | 588 | | | 动态范围 | 最小正数 | 589 | | ---- | ------------------------------------------ | --------------------- | 590 | | FP32 | $-3.4 \times 10^{38} + 3.4 \times 10^{38}$ | $1.4 \times 10^{-45}$ | 591 | | FP16 | $-65504 + 65504$ | $5.96 \times 10^{-8}$ | 592 | | INT8 | $-128 + 127$ | 1 | 593 | 594 | 原来一个tensor用32bit来表示, 现在打算用8bit来表示 595 | 596 | 而其实就是一种信息再编码的过程 597 | 598 | 599 | 600 | ![image-20191230164743898](/Users/stephenfang/Library/Application Support/typora-user-images/image-20191230164743898.png) 601 | 602 | 603 | 604 | - 这种做法不是将 ±|max| 映射为 ±127,而是存在一个 阈值 |T| ,将 ±|T| 映射为±127,显然这里 |T|<|max|。 605 | - 超出 阈值 ±|T| 外的直接映射为阈值 ±127。比如上图中的三个红色点,直接映射为-127。 606 | - 称这种映射关系为饱和的(Saturate ),不对称的。 607 | - 只要 阈值 选取得当,就能将分布散乱的较大的激活值舍弃掉,也就有可能使精度损失不至于降低太多。 608 | 609 | 610 | 611 | 对weights 的int8量化使用非饱和方式 612 | 613 | 对activation 使用饱和的方式 614 | 615 | 616 | 617 | FP32的情况下最能表达tensor的最佳分布, 而现在要用int8开表达, 随着阈值T的取值会有多种的情况产生, 而就是要将最符合FP32分布的情况给找出来 618 | 619 | 620 | 621 | 我们用一个衡量指标来衡量不同的int8分布与原来的FP32分布之间的差异程度, 该衡量指标为相对熵 relative entropy, 也叫做KL离散 622 | 623 | - 假设我们要给一个信息进行完美编码,那么最短平均编码长度就是信息熵。 624 | 625 | - 如果编码方案不一定完美(由于对概率分布的估计不一定正确),这时的平均编码长度就是交叉熵。 626 | 627 | 平均编码长度 = 最短平均编码长度 + 一个增量 628 | 629 | 交叉熵在深度学习中广泛使用,衡量了测试集标签分布和模型预测分布之间的差异程度。 630 | 631 | - 编码方法不一定完美时,平均编码长度相对于最小值的增加量(即上面那个增量)是相对熵。 632 | 633 | **交叉熵 = 信息熵 + 相对熵** 634 | 635 | 636 | 637 | 现在要用IN8来编码FP32的信息, 同时要求编码后的差异要尽可能的小, 638 | 639 | KL_divergence(P, Q): = SUM(P[i] * log(P[i]/Q[i], i) 640 | 641 | P = reference_distribution 642 | 643 | Q = quantize_distribution 644 | 645 | 每一层的tensor的阈值T都是不同的 646 | 647 | 确定每一层T的过程称之为**校准 calibration ** 648 | 649 | 650 | 651 | 為了找到合理的閾值, 也必須要有先驗知識, 也就是每一层在FP32精度下的激活值分布 652 | 653 | 654 | 655 | 1. FP32精度訓練好的模型 這個一般框架訓練出來的都是, 这个我们是知道的, 激活值分布如何得到? 656 | 657 | 658 | 659 | NVIDIA官方建议是 660 | 661 | 选取一个子集为校准集, 这个校准集必须代表性以及多样性, 最好是验证集的一个子集, 不应该只是分类类别的一个小部分 662 | 663 | 664 | 665 | 1. 首先在校准集上进行FP32 inference推理 666 | 2. 对于网络的每一层 667 | - 收集这一层的激活值,并做 直方图(histograms ),分成几个组别(bins)(官方给的一个说明使用的是2048组),分组是为了下面遍历 |T| 时,减少遍历次数; 668 | - 对于不同的 阈值 |T| 进行遍历,因为这里 |T|的取值肯定在 第128-2047 组之间,所以就选取每组的中间值进行遍历; 669 | - 选取使得 KL_divergence(ref_distr, quant_distr) 取得最小值的 |T|。 670 | 671 | 672 | 673 | 3. 返回一系列 |T|值, 每一层都有一个|T| 创建CalibrationTable 674 | 675 | 676 | 677 | 假设最终使得KL散度最小的|T|值是第200组的中间值, 那么就拿原来第0-200组的数值线性映射到0~128之间, 超出范围的直接映射到128 678 | 679 | 680 | 681 | 682 | 683 | 基本工作流程 from GTC2017 PPT 684 | 685 | - You will need: 686 | - Model trained in FP32. 687 | - Calibration dataset. 688 | - TensorRT will: 689 | - Run inference in FP32 on calibration dataset. 690 | - Collect required statistics. 691 | - Run calibration algorithm → optimal scaling factors. 692 | - Quantize FP32 weights → INT8. 693 | - Generate “CalibrationTable” and INT8 execution engine. 694 | 695 | 696 | 697 | 698 | 699 | ------ 700 | 701 | 702 | 703 | ------ 704 | 705 | > ### TensorRT API 706 | > 707 | > 该章节主要记录各个类的用法, 内容涵盖从创建engine到序列、反序列, 最后自定义plugin等 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | **用INetworkDefinition 构建网路层** 718 | 719 | 主要参考github tensorrtx 项目为主 720 | 721 | 主要涉及到以下几个大类, 均在**nvinfer1** namespace 底下 722 | 723 | 1. ITensor 724 | 725 | 2. Wights 726 | 727 | 3. IBuilder 728 | 729 | 1. setMaxBatchSize() 设置运行时候最大的batchsize 730 | 2. buildEngineWithConfig(INetworkDefinition& network, IBuilderConfig & config) : 从定义好的Network以及config来构建Engine 731 | 732 | 4. IBuilderConfig : 用来设置一些参数 733 | 734 | 1. setMaxWorkspaceSize(std::size_t workspaceSize): 设置engine在执行的时候,GPU最大临时分配内存, 这边size_t是byte, ex.如果填入 (1<<30) 1左位移30位( 2的30次3方), 也就是*1 Gigabyte* is equal to 1,073,741,824 bytes (1024x1024x1024) 735 | 736 | 5. INetworkDefinition 737 | 738 | 1. addInput 739 | 740 | 2. addShuffle 741 | 742 | 3. addConvolutionNd([ITensor](https://docs.nvidia.com/deeplearning/tensorrt/api/c_api/classnvinfer1_1_1_i_tensor.html) &input, int32_t nbOutputMaps, [Dims](https://docs.nvidia.com/deeplearning/tensorrt/api/c_api/namespacenvinfer1.html#af2bdf77382d189916a4e468f19298fec) kernelSize, [Weights](https://docs.nvidia.com/deeplearning/tensorrt/api/c_api/classnvinfer1_1_1_weights.html) kernelWeights, [Weights](https://docs.nvidia.com/deeplearning/tensorrt/api/c_api/classnvinfer1_1_1_weights.html) biasWeights) 743 | 744 | 4. addActivcation(ITensor & input, Activationtype type) : 激活函数类型依照官方定义枚举类进行输入 745 | 746 | ```c++ 747 | enum class ActivationType : int32_t 748 | { 749 | kRELU = 0, //!< Rectified linear activation. 750 | kSIGMOID = 1, //!< Sigmoid activation. 751 | kTANH = 2, //!< TanH activation. 752 | kLEAKY_RELU = 3, //!< LeakyRelu activation: x>=0 ? x : alpha * x. 753 | kELU = 4, //!< Elu activation: x>=0 ? x : alpha * (exp(x) - 1). 754 | kSELU = 5, //!< Selu activation: x>0 ? beta * x : beta * (alpha*exp(x) - alpha) 755 | kSOFTSIGN = 6, //!< Softsign activation: x / (1+|x|) 756 | kSOFTPLUS = 7, //!< Parametric softplus activation: alpha*log(exp(beta*x)+1) 757 | kCLIP = 8, //!< Clip activation: max(alpha, min(beta, x)) 758 | kHARD_SIGMOID = 9, //!< Hard sigmoid activation: max(0, min(1, alpha*x+beta)) 759 | kSCALED_TANH = 10, //!< Scaled tanh activation: alpha*tanh(beta*x) 760 | kTHRESHOLDED_RELU = 11 //!< Thresholded ReLU activation: x>alpha ? x : 0 761 | }; 762 | ``` 763 | 764 | 5. addElementWise(ITensor & input1. ITensor&input2, ElementWiseOperation op) 765 | 766 | ```c++ 767 | enum class ElementWiseOperation : int32_t 768 | { 769 | kSUM = 0, //!< Sum of the two elements. 770 | kPROD = 1, //!< Product of the two elements. 771 | kMAX = 2, //!< Maximum of the two elements. 772 | kMIN = 3, //!< Minimum of the two elements. 773 | kSUB = 4, //!< Substract the second element from the first. 774 | kDIV = 5, //!< Divide the first element by the second. 775 | kPOW = 6, //!< The first element to the power of the second element. 776 | kFLOOR_DIV = 7, //!< Floor division of the first element by the second. 777 | kAND = 8, //!< Logical AND of two elements. 778 | kOR = 9, //!< Logical OR of two elements. 779 | kXOR = 10, //!< Logical XOR of two elements. 780 | kEQUAL = 11, //!< Check if two elements are equal. 781 | kGREATER = 12, //!< Check if element in first tensor is greater than corresponding element in second tensor. 782 | kLESS = 13 //!< Check if element in first tensor is less than corresponding element in second tensor. 783 | }; 784 | ``` 785 | 786 | 6. addConcatenation(ITensor*const* inputs, int32_t nbInputs), nbInputs = number of inputs 787 | 788 | 7. addTopK ( ITensor & input, 789 | TopKOperation op, 790 | int32_t k, 791 | uint32_t reduceAxes 792 | ) 793 | 794 | ```c++ 795 | enum class TopKOperation : int32_t 796 | { 797 | kMAX = 0, //!< Maximum of the elements. 798 | kMIN = 1, //!< Minimum of the elements. 799 | }; 800 | ``` 801 | 8. addSlice(ITensor &input, Dims start, Dims size, Dims stride) 802 | 803 | 主要用来切分tensor, start是第几行第一列开始切, size 是切分需要的尺寸, stride是每次移动多少行,然后每行移动几列 804 | 805 | input={{0,2,4},{1,3,5}} start = {1,0}, size={1,2}, stride={1,2} 806 | 一开始是从第1行第0列开始移动,第一个为1, 然后移动一行两列(从1->5)就是5, 807 | output 就是{1,5} 808 | 809 | 810 | 811 | 6. ICudaEngine: 812 | 813 | 1. serialize:序列化一个网路到stream , 记得序列化完要用IHostMemory类型保存 814 | 2. createExecutionContext:创建一个executionContext 815 | 816 | 7. IRuntime: 817 | 818 | 1. deserializeCudaEngine() : 用来将输入stream反序列化, 返回得到engine 819 | 820 | 8. IExecutionContext : 执行inference时候使用 821 | 1. enqueue() : 传入batchsize, bindings, stream可执行异步的推断, bindings指的是input 和 output的buffers 822 | 823 | 824 | 825 | DataType 枚举类型 : 826 | 827 | ``` 828 | Enumerator 829 | kFLOAT :32-bit floating point format. 830 | 831 | kHALF : IEEE 16-bit floating-point format. 832 | 833 | kINT8 : 8-bit integer representing a quantized floating-point value. 834 | 835 | kINT32 : Signed 32-bit integer format. 836 | 837 | kBOOL : 8-bit boolean. 0 = false, 1 = true, other values undefined. 838 | ``` 839 | 840 | 841 | 842 | **大体流程** 843 | 844 | ``` 845 | //构建网路 846 | 1. create builder/builderconfig using logger 847 | 2. builder create Network 848 | 3. construct network layer using Network class 849 | 4. buildEngine 850 | 5. serialize engine to hostmemory(streaam) 851 | 6. destroy engine and builder 852 | ``` 853 | 854 | ``` 855 | //inference 856 | 可查阅 https://docs.nvidia.com/deeplearning/tensorrt/archives/tensorrt-723/developer-guide/index.html 857 | 858 | 1. create trtstream nullptr for pointing to engine 859 | 2. read engine file and set trtstream to point the extracted network 860 | 3. using cudaHostAlloc to map memory from host to device 861 | 3. create IRuntime Object 862 | 4. desrialize cuda engine from trtstream 863 | 5. create execution context and delete trtstream ptr 864 | 6. get input and output index using engine->getbindingIndex 865 | 7.create 2 buffer ptr point to input and output buffers on GPU 866 | 8. copy input_image.data to float* data 867 | 10. using cudaHostGetDevicePointer to get GPU pointer pointing to host memory 868 | 11.we have stream, buffer, batchsize, using context to do inferecnce 869 | ``` 870 | 871 | **具体步骤** 872 | 873 | ```c++ 874 | IBuilder *builder = createInferBuilder(gLogger); 875 | IBuilderConfig *config = builder->createBuilderConfig(); 876 | ``` 877 | 878 | 接着用builder 创建network 来构建网路层 879 | 880 | ```c++ 881 | INetworkDefinition *network = builder->createNetworkV2(0U); 882 | ``` 883 | 884 | 第一步需要先创建输入tensor, 然后增加一个shuffle layer 885 | 886 | ```c++ 887 | ITensor *data = network->addInput(INPUT_BLOB_NAME, dt, Dims3{INPUT_H, INPUT_W, 3}); 888 | assert(data); 889 | 890 | // hwc to chw 891 | auto ps = network->addShuffle(*data);底下 892 | ps->setFirstTranspose(nvinfer1::Permutation{2, 0, 1}); 893 | float mean[3] = {0.485, 0.456, 0.406}; 894 | float std[3] = {0.229, 0.224, 0.225}; 895 | ITensor *preinput = MeanStd(network, ps->getOutput(0), mean, std, true); 896 | ``` 897 | 898 | 为什么要创建, 官方文档如下没看太明白 899 | 900 | The name of the input tensor is used to find the index into the buffer array for an engine built from the network. The volume of the dimensions must be less than 2^30 elements. 901 | 902 | Shuffle layer 可查文档 **nvinfer1::IShuffleLayer** 主要可置换维度 903 | 904 | getOutput 是INetworkdefinition的一个成员, 输入index, 可得到输出的ITensor 905 | 906 | 907 | 908 | 909 | **读取engine file 并且 反序列化** 910 | ```c++ 911 | // deserialize the .engine and run inference 912 | char *trtModelStream{nullptr}; 913 | size_t size{0}; 914 | std::ifstream file(engine_name, std::ios::binary); 915 | if (file.good()) 916 | { 917 | file.seekg(0, file.end); 918 | size = file.tellg(); 919 | file.seekg(0, file.beg); 920 | trtModelStream = new char[size]; 921 | assert(trtModelStream); 922 | file.read(trtModelStream, size); //set trtModelStream point to file 923 | file.close(); 924 | } 925 | else 926 | { 927 | std::cerr << "could not open plan file" << std::endl; 928 | } 929 | 930 | // prepare input data --------------------------- 931 | cudaSetDeviceFlags(cudaDeviceMapHost); 932 | float *data; 933 | int *prob; // using int. output is index 934 | CHECK(cudaHostAlloc((void **)&data, BATCH_SIZE * 3 * INPUT_H * INPUT_W * sizeof(float), cudaHostAllocMapped)); 935 | CHECK(cudaHostAlloc((void **)&prob, BATCH_SIZE * OUTPUT_SIZE * sizeof(int), cudaHostAllocMapped)); 936 | 937 | IRuntime *runtime = createInferRuntime(gLogger); 938 | assert(runtime != nullptr); 939 | ICudaEngine *engine = runtime->deserializeCudaEngine(trtModelStream, size); 940 | assert(engine != nullptr); 941 | IExecutionContext *context = engine->createExecutionContext(); 942 | assert(context != nullptr); 943 | delete[] trtModelStream; 944 | void *buffers[2]; 945 | // In order to bind the buffers, we need to know the names of the input and output tensors. 946 | // Note that indices are guaranteed to be less than IEngine::getNbBindings() 947 | const int inputIndex = engine->getBindingIndex(INPUT_BLOB_NAME); 948 | const int outputIndex = engine->getBindingIndex(OUTPUT_BLOB_NAME); 949 | assert(inputIndex == 0); 950 | assert(outputIndex == 1); 951 | cudaStream_t stream; 952 | CHECK(cudaStreamCreate(&stream)); 953 | 954 | ``` 955 | 956 | --- 957 | 958 | 959 | 将提前从torch取到的wts格式的权重文件取出并以std::map 类型存储 960 | 961 | ```c++ 962 | std::map loadWeights(const std::string file) 963 | { 964 | std::cout << "Loading weights: " << file << std::endl; 965 | std::map weightMap; 966 | 967 | // Open weights file 968 | std::ifstream input(file); 969 | assert(input.is_open() && "Unable to load weight file."); 970 | 971 | // Read number of weight blobs 972 | int32_t count; 973 | input >> count; 974 | assert(count > 0 && "Invalid weight map file."); 975 | 976 | while (count--) 977 | { 978 | Weights wt{DataType::kFLOAT, nullptr, 0}; 979 | uint32_t size; 980 | 981 | // Read name and type of blob 982 | std::string name; 983 | input >> name >> std::dec >> size; 984 | wt.type = DataType::kFLOAT; 985 | 986 | // Load blob 987 | uint32_t *val = reinterpret_cast(malloc(sizeof(val) * size)); 988 | for (uint32_t x = 0, y = size; x < y; ++x) 989 | { 990 | input >> std::hex >> val[x]; 991 | } 992 | wt.values = val; 993 | 994 | wt.count = size; 995 | weightMap[name] = wt; 996 | } 997 | 998 | return weightMap; 999 | } 1000 | ``` 1001 | 1002 | 1003 | 1004 | **实现conv + Bn + relu** (from tensorrtx) 1005 | 1006 | 该函数主要创建convBnrelu模块, 其中调用到addConvolutionNd函数, 1007 | 1008 | ```c++ 1009 | ILayer *convBnRelu(INetworkDefinition *network, 1010 | std::map &weightMap, 1011 | ITensor &input, int outch, int ksize, int s, int p, 1012 | std::string convname, std::string bnname, 1013 | bool relu = true, 1014 | bool bias = false) 1015 | { 1016 | Weights emptywts{DataType::kFLOAT, nullptr, 0}; 1017 | IConvolutionLayer *conv1; 1018 | //Dims dim; 1019 | if (!bias) 1020 | { 1021 | conv1 = network->addConvolutionNd(input, outch, DimsHW{ksize, ksize}, weightMap[convname + ".weight"], emptywts); 1022 | } 1023 | else 1024 | { 1025 | conv1 = network->addConvolutionNd(input, outch, DimsHW{ksize, ksize}, weightMap[convname + ".weight"], weightMap[convname + ".bias"]); 1026 | } 1027 | assert(conv1); 1028 | conv1->setStrideNd(DimsHW{s, s}); 1029 | conv1->setPaddingNd(DimsHW{p, p}); 1030 | debug_print(conv1->getOutput(0), convname); 1031 | IScaleLayer *bn1 = addBatchNorm2d(network, weightMap, *conv1->getOutput(0), bnname, 1e-5); 1032 | debug_print(bn1->getOutput(0), bnname); 1033 | if (relu) 1034 | { 1035 | auto lr = network->addActivation(*bn1->getOutput(0), ActivationType::kRELU); 1036 | return lr; 1037 | } 1038 | return bn1; 1039 | } 1040 | 1041 | ``` 1042 | 1043 | 以下自定义batchnorm层 1044 | 1045 | ```c++ 1046 | IScaleLayer *addBatchNorm2d(INetworkDefinition *network, std::map &weightMap, ITensor &input, std::string lname, float eps) 1047 | { 1048 | float *gamma = (float *)weightMap[lname + ".weight"].values; 1049 | float *beta = (float *)weightMap[lname + ".bias"].values; 1050 | float *mean = (float *)weightMap[lname + ".running_mean"].values; 1051 | float *var = (float *)weightMap[lname + ".running_var"].values; 1052 | int len = weightMap[lname + ".running_var"].count; 1053 | //std::cout << "len " << len << std::endl; 1054 | 1055 | //scale 1056 | float *scval = reinterpret_cast(malloc(sizeof(float) * len)); 1057 | for (int i = 0; i < len; i++) 1058 | { 1059 | scval[i] = gamma[i] / sqrt(var[i] + eps); 1060 | } 1061 | Weights scale{DataType::kFLOAT, scval, len}; 1062 | 1063 | //shift 1064 | float *shval = reinterpret_cast(malloc(sizeof(float) * len)); 1065 | for (int i = 0; i < len; i++) 1066 | { 1067 | shval[i] = beta[i] - mean[i] * gamma[i] / sqrt(var[i] + eps); 1068 | } 1069 | Weights shift{DataType::kFLOAT, shval, len}; 1070 | 1071 | //power 1072 | float *pval = reinterpret_cast(malloc(sizeof(float) * len)); 1073 | for (int i = 0; i < len; i++) 1074 | { 1075 | pval[i] = 1.0; 1076 | } 1077 | Weights power{DataType::kFLOAT, pval, len}; 1078 | 1079 | weightMap[lname + ".scale"] = scale; 1080 | weightMap[lname + ".shift"] = shift; 1081 | weightMap[lname + ".power"] = power; 1082 | IScaleLayer *scale_1 = network->addScale(input, ScaleMode::kCHANNEL, shift, scale, power); 1083 | assert(scale_1); 1084 | return scale_1; 1085 | } 1086 | ``` 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | --- 1093 | 1094 | 1095 | 1096 | **创建Engine** 1097 | 1098 | ```c++ 1099 | virtual nvinfer1::ICudaEngine* nvinfer1::IBuilder::buildEngineWithConfig( 1100 | INetworkDefinition & network,IBuilderConfig & config ) 1101 | ``` 1102 | 1103 | 1104 | 1105 | ```c++ 1106 | virtual nvinfer1::IBuilder::buildCudaEngine([nvinfer1::INetworkDefinition] 1107 | ``` 1108 | 1109 | 该方法用来创建Engine, 旧方法 官网说未来会淘汰 1110 | 1111 | 1112 | 1113 | ``` 1114 | virtual void nvinfer1::IExecutionContext::setProfiler ( IProfiler * ) 1115 | ``` 1116 | 1117 | 1118 | 1119 | #### 反序列化Engine 1120 | 1121 | 该小节记录deserialize model等相关的类 1122 | 1123 | 1124 | 1125 | ##### deserializeCudaEngine 1126 | 1127 | ```c++ 1128 | virtual nvinfer1::ICudaEngine* nvinfer1::IRuntime::deserializeCudaEngine ( 1129 | const void * blob, 1130 | std::size_t size, 1131 | IPluginFactory * pluginFactory 1132 | ) 1133 | ``` 1134 | 1135 | - Parameters 1136 | 1137 | - blob: The memory that holds the serialized engine. 1138 | - size: The size of the memory. 1139 | - plugin:FactoryThe plugin factory, if any plugins are used by the network, otherwise nullptr. 1140 | 1141 | - Returns 1142 | 1143 | The engine, or nullptr if it could not be deserialized. 1144 | 1145 | 1146 | 1147 | #### Plugin 相关 1148 | 1149 | //TODO 1150 | 1151 | ------ 1152 | 1153 |

Torch.onnx

1154 | 1155 | 该function为torch框架内建 1156 | 1157 | 其余框架如果需要转换为onnx, 则参考工程链接 https://github.com/onnx/onnx 1158 | 1159 | 官方教程 https://pytorch.org/docs/stable/onnx.html 1160 | 1161 | > ### 基础使用 1162 | 1163 | ```python 1164 | import torch 1165 | import torchvision 1166 | 1167 | dummy_input = torch.randn(10, 3, 224, 224, device='cuda') 1168 | model = torchvision.models.alexnet(pretrained=True).cuda() 1169 | 1170 | 1171 | input_names = [ "actual_input_1" ] + [ "learned_%d" % i for i in range(16) ] 1172 | output_names = [ "output1" ] 1173 | 1174 | torch.onnx.export(model, dummy_input, "alexnet.onnx", verbose=True, input_names=input_names, output_names=output_names) 1175 | ``` 1176 | 1177 | 得到的 `alexnet.onnx` 是一个 protobuf 二值文件, 它包含所导出模型 ( 这里是 AlexNet )中网络架构和网络参数. 关键参数 `verbose=True` 会使导出过程中打印出的网络可读性较佳 1178 | 1179 | 1180 | 1181 | 你也可以使用 onnx 库来验证 输出的protobuf, 用 conda 安装 `onnx` 1182 | 1183 | ``` 1184 | conda install -c conda-forge onnx 1185 | ``` 1186 | 1187 | 然后运行: 1188 | 1189 | ```python 1190 | import onnx 1191 | 1192 | # 载入onnx模块 1193 | model = onnx.load("alexnet.onnx") 1194 | 1195 | #检查IR是否良好 1196 | onnx.checker.check_model(model) 1197 | 1198 | #输出一个图形的可读表示方式 1199 | onnx.helper.printable_graph(model.graph) 1200 | ``` 1201 | 1202 | 1203 | 1204 | > ### 使用onnxruntime来验证exported model 1205 | 1206 | 1207 | 1208 | 如果以及安装onnxruntime, 则可以用来测试转好的模型 1209 | 1210 | ```python 1211 | import onnxruntime as ort 1212 | 1213 | ort_session = ort.InferenceSession('alexnet.onnx') 1214 | 1215 | outputs = ort_session.run(None, {'actual_input_1': np.random.randn(10, 3, 224, 224).astype(np.float32)}) 1216 | 1217 | print(outputs[0]) 1218 | ``` 1219 | 1220 | > ### Tracing and scripting 1221 | 1222 | - Trace-based 1223 | - 表示执行模型一次, 并且输出实际在模型中运行的op, 这表示如果模型为动态的, 输出将会依据输入的不同而有所改变, 而输入的数据形式最好不变 1224 | 1225 | ```python 1226 | import torch 1227 | 1228 | # Trace-based only 1229 | 1230 | class LoopModel(torch.nn.Module): 1231 | def forward(self, x, y): 1232 | for i in range(y): 1233 | x = x + i 1234 | return x 1235 | 1236 | model = LoopModel() 1237 | dummy_input = torch.ones(2, 3, dtype=torch.long) 1238 | loop_count = torch.tensor(5, dtype=torch.long) 1239 | 1240 | torch.onnx.export(model, (dummy_input, loop_count), 'loop.onnx', verbose=True) 1241 | ``` 1242 | 1243 | ```python 1244 | graph(%0 : Long(2, 3), 1245 | %1 : Long()): 1246 | %2 : Tensor = onnx::Constant[value={1}]() 1247 | %3 : Tensor = onnx::Add(%0, %2) 1248 | %4 : Tensor = onnx::Constant[value={2}]() 1249 | %5 : Tensor = onnx::Add(%3, %4) 1250 | %6 : Tensor = onnx::Constant[value={3}]() 1251 | %7 : Tensor = onnx::Add(%5, %6) 1252 | %8 : Tensor = onnx::Constant[value={4}]() 1253 | %9 : Tensor = onnx::Add(%7, %8) 1254 | return (%9) 1255 | ``` 1256 | 1257 | 1258 | 1259 | - Script-based : 1260 | - 表示输出的模型为ScrptModule形式的, 是一种TorchScript的数据结构, Torchscript则从pytorch code中创建出可序列化及可优化的模型 1261 | 1262 | ```python 1263 | @torch.jit.script 1264 | def loop(x, y): 1265 | for i in range(int(y)): 1266 | x = x + i 1267 | return x 1268 | 1269 | class LoopModel2(torch.nn.Module): 1270 | def forward(self, x, y): 1271 | return loop(x, y) 1272 | 1273 | model = LoopModel2() 1274 | dummy_input = torch.ones(2, 3, dtype=torch.long) 1275 | loop_count = torch.tensor(5, dtype=torch.long) 1276 | torch.onnx.export(model, (dummy_input, loop_count), 'loop.onnx', verbose=True, 1277 | input_names=['input_data', 'loop_range']) 1278 | ``` 1279 | 1280 | 1281 | 1282 | ```python 1283 | graph(%input_data : Long(2, 3), 1284 | %loop_range : Long()): 1285 | %2 : Long() = onnx::Constant[value={1}](), scope: LoopModel2/loop 1286 | %3 : Tensor = onnx::Cast[to=9](%2) 1287 | %4 : Long(2, 3) = onnx::Loop(%loop_range, %3, %input_data), scope: LoopModel2/loop # custom_loop.py:240:5 1288 | block0(%i.1 : Long(), %cond : bool, %x.6 : Long(2, 3)): 1289 | %8 : Long(2, 3) = onnx::Add(%x.6, %i.1), scope: LoopModel2/loop # custom_loop.py:241:13 1290 | %9 : Tensor = onnx::Cast[to=9](%2) 1291 | -> (%9, %8) 1292 | return (%4) 1293 | ``` 1294 | 1295 | > ### Torch.onnx Functions 1296 | 1297 | `Torch.onnx.export` 1298 | 1299 | 该function主要用来将model转换为onnx类型, 首先需要执行model来取得执行轨迹, 1300 | 1301 | ``` 1302 | torch.onnx.export(model, args, f, export_params=True, verbose=False, training=False, input_names=None, output_names=None, aten=False, export_raw_ir=False, operator_export_type=None, opset_version=None, _retain_param_name=True, do_constant_folding=False, example_outputs=None, strip_doc_string=True, dynamic_axes=None, keep_initializers_as_inputs=None) 1303 | ``` 1304 | 1305 | - model :要转换的model 1306 | 1307 | - args : 1308 | 1309 | - f : 就是输出的filename包含扩展名 例如test.onnx 1310 | 1311 | - **export_params** : 默认开启, 将模型参数也输出, 如果设置为否, 则输出的模型为untrained 1312 | 1313 | - verbose : 如果开启, 则会输出被输出的trace的debug descrption 1314 | 1315 | - training : 一般不需要开启, 因为onnx 默认导向为输出 做inference only 1316 | 1317 | - input_names :字符串列表, 默认为空列表, 用来指定graph 的input node 1318 | 1319 | - output_names: 同上,相反 1320 | 1321 | 其余查阅[https://pytorch.org/docs/stable/onnx.html?highlight=onnx%20export#torch.onnx.export](https://pytorch.org/docs/stable/onnx.html?highlight=onnx export#torch.onnx.export) 1322 | 1323 | - **operator_export_type** : op输出的type 1324 | 1325 | - (*enum**,* *default OperatorExportTypes.ONNX*) – 1326 | 1327 | OperatorExportTypes.ONNX: all ops are exported as regular ONNX ops. OperatorExportTypes.ONNX_ATEN: all ops are exported as ATen ops. OperatorExportTypes.ONNX_ATEN_FALLBACK: if symbolic is missing, fall back on ATen op 1328 | 1329 | OperatorExportTypes.RAW: export raw ir. 1330 | 1331 | 1332 | 1333 | > ### onnx support operation 1334 | > 1335 | > onnx目前支持的operation 1336 | > 1337 | > https://github.com/onnx/onnx/blob/master/docs/Operators.md 1338 | 1339 | 1340 | 1341 | > ### Pytorch 实现自定义 operation, 并转换onnx 1342 | > 1343 | > https://blog.csdn.net/qq_33120609/article/details/99429967#pytorchop_9 1344 | > 1345 | > https://blog.csdn.net/qq_33120609/article/details/98850601 1346 | 1347 | 1348 | 1349 | 步骤如下 1350 | 1351 | 1. 自定义OP 1352 | 2. 在自定义OP中加入symbolic 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | #### 自定义OP C++ 实现 1359 | 1360 | 以下 截取自 [pytorch 添加c实现的自定义](http://www.shijinglei.com/2019/12/21/pytorch-添加c实现的自定义op/) 1361 | 1362 | pytorch已经基本实现了常见的各种op,然而,当想实现一个pytorch中没有的op时,有两种方式。一种方式是这个op可以由pytorch中已有的op进行组合而成,因此只需要使用python接口进行组合就可以了。反之,就必须使用c++或者cuda实现该op,然后添加到pytorch中。本文将介绍添加c++实现的自定义op,因为我还不会cuda : ( 1363 | 1364 | 本文介绍使用python的setuptools将c++实现的op添加到pytorch中。首先要用c++实现定义的op。比如想实现一个op为 z=3x-y 。头文件为my_op.h 1365 | 1366 | ```c++ 1367 | #include //这一句是无论要实现任何op都必须添加的 1368 | #include 1369 | 1370 | //前向传播 1371 | torch::Tensor my_op_forward(const torch::Tensor& x, const torch::Tensor& y); 1372 | //反向传播 1373 | std::vector my_op_backward(const torch::Tensor& gradOutput); 1374 | ``` 1375 | 1376 | 源文件为my_op.cpp 1377 | 1378 | ```C++ 1379 | #include "my_op.h" 1380 | 1381 | torch::Tensor my_op_forward(const torch::Tensor& x, const torch::Tensor& y) { 1382 | AT_ASSERTM(x.sizes() == y.sizes(), "x must be the same size as y"); 1383 | torch::Tensor z = torch::zeros(x.sizes()); 1384 | z = 3 * x - y; 1385 | return z; } 1386 | 1387 | std::vector my_op_backward(const torch::Tensor& gradOutput) { 1388 | torch::Tensor gradOutputX = 3 * gradOutput * torch::ones(gradOutput.sizes()); 1389 | torch::Tensor gradOutputY = -1 * gradOutput * torch::ones(gradOutput.sizes()); 1390 | return {gradOutputX, gradOutputY}; } 1391 | 1392 | // pybind11 绑定 1393 | PYBIND11_MODULE(my_op_api, m) { 1394 | m.def("forward", &my_op_forward, "MY_OP forward"); 1395 | m.def("backward", &my_op_backward, "MY_OP backward"); 1396 | } 1397 | ``` 1398 | 1399 | 其中最后的PYBIND11_MODULE是用来将C++函数绑定到python上的。其中第一个参数my_op_api为要生成的python模块名,以后import my_op_api就可以调用该op了。第二个参数固定为m 1400 | 函数体中的两个语句分别是绑定前向传播与反向传播到实现的两个函数上。 1401 | 1402 | 然后编写setup.py,用来构建pytorch的c++扩展。 1403 | 1404 | ``` 1405 | from setuptools import setup 1406 | from torch.utils.cpp_extension import BuildExtension, CppExtension 1407 | 1408 | setup(name='my_op_api', 1409 | version='0.l', 1410 | ext_modules=[CppExtension('my_op_api', sources=['my_op.cpp'], extra_compile_args=['-std=c++11'])], 1411 | cmdclass={'build_ext':BuildExtension}) 1412 | ``` 1413 | 1414 | 其中,setup中的name以及CppExtension中第一个参数(也是name)要和PYBIND11_MODULE里设的模块名保持一致,这里都是my_op_api。CppExtension中的extra_compile_args=[‘-std=c++11’]是趟坑发现的,不加的话gcc可能会报n多错(pytorch是用c++11编译的,因此这里用gcc编译的时候也要使用c++11) 1415 | 1416 | 然后运行python setup.py install,如果没有问题的话,就生成了所需的python模块。可以从输出的信息看到该模块在所在python环境下的site-packages文件夹下,以.egg结尾。另外,在当前目录下会有3个文件夹生成,build、dist、my_op_api.egg-info,其中dist下也有.egg文件,可以发布到其它python环境。 1417 | 1418 | 然后就可以在python中import my_op_api进行调用扩展的op了。这里需要注意一点的是,在import 自定义的op之前,必须先import torch。 但是,这样的op和我们日常使用的还是不太一样,这时需要将它包装为pytorch中的函数和模块,以便我们像使用其它模块一样使用自定义的op。要包装为模块,首先包装成函数。 1419 | 1420 | 包装成函数,需要继承torch.autograd.Function。然后包装成模块,需要继承torch.nn.Module 1421 | 1422 | ```python 1423 | import torch 1424 | from torch.autograd import Function 1425 | from torch.nn import Module 1426 | import my_op_api 1427 | 1428 | class MyOpFunction(Function): 1429 | @staticmethod 1430 | def forward(ctx, x, y): 1431 | #如果有一些信息,需要在梯度反向传播时用到,可以使用ctx.save_for_backward()进行保存 1432 | return my_op_api.forward(x, y) 1433 | @staticmethod 1434 | def backward(ctx, gradOutput): 1435 | #如果在forward中保存了信息,可以使用ctx.saved_tensors取回 1436 | grad_x, grad_y = my_op_api.backward(gradOutput) 1437 | return grad_x, grad_y 1438 | 1439 | class MyOpModule(Module): 1440 | def __init__(self): 1441 | super(MyOpModule, self).__init__() 1442 | def forward(self, input_x, input_y):#只需要定义forward的函数就可以了 1443 | return MyOpFunction.apply(input_x, input_y) 1444 | ``` 1445 | 1446 | #### 在自定义OP中加入symbolic函数 1447 | 1448 | ```python 1449 | import torch 1450 | from torch.autograd import Function 1451 | from torch.nn import Module 1452 | import my_op_api 1453 | 1454 | class MyOpFunction(Function): 1455 | @staticmethod 1456 | def symbolic(g, x, y): 1457 | return g.op("myfunction", x, y) 1458 | 1459 | 1460 | @staticmethod 1461 | def forward(ctx, x, y): 1462 | #如果有一些信息,需要在梯度反向传播时用到,可以使用ctx.save_for_backward()进行保存 1463 | return my_op_api.forward(x, y) 1464 | @staticmethod 1465 | def backward(ctx, gradOutput): 1466 | #如果在forward中保存了信息,可以使用ctx.saved_tensors取回 1467 | grad_x, grad_y = my_op_api.backward(gradOutput) 1468 | return grad_x, grad_y 1469 | 1470 | class MyOpModule(Module): 1471 | def __init__(self): 1472 | super(MyOpModule, self).__init__() 1473 | def forward(self, input_x, input_y):#只需要定义forward的函数就可以了 1474 | return MyOpFunction.apply(input_x, input_y) 1475 | ``` 1476 | 1477 | 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | ------ 1484 | 1485 |

Libtorch

1486 | 1487 | pytorch libtorch 文档 https://pytorch.org/cppdocs/ 1488 | 1489 | pytorch libtorch 安装 https://pytorch.org/cppdocs/installing.html 1490 | 1491 | //TODO 待新增 1492 | 1493 |

Onnx runtime

1494 | 1495 | onnxruntime 工程链接 https://github.com/microsoft/onnxruntime 1496 | 1497 | ONNX Runtime是将 ONNX 模型部署到生产环境的跨平台高性能运行引擎。 1498 | 适用于 Linux、Windows 和 Mac。编写C++,它还具有 C、Python 和C# api。 ONNX 运行时为所有 ONNX 规范提供支持,并与不同硬件(如 TensorRT 上的 NVidia Gpu)上的加速器集成 1499 | 1500 | 1501 | 1502 | 1503 | 1504 |

Onnx-TensorRT

1505 | 1506 | onnx-tensorrt工程主要为解析onnx的model并且转换为tensorRT 1507 | 1508 | 需要先依照官方需求安装指定的TensorRT版本以及protobuf 1509 | 1510 | 1511 | 1512 | 比如你有onnx模型结构比较复杂, 需要更多的自定义op, 或者是从torch版本比较高转换的, 就需要依赖TensorRT7, 那么onnx-tensorrt就选择7.0的版本 1513 | 1514 | 1515 | 1516 | 截至2020年1月 支持到tensorrt7 1517 | 1518 | 工程链接 https://github.com/onnx/onnx-tensorrt 1519 | 1520 | > ### Onnx-TensorRT 安装步骤 1521 | 1522 | 先确认环境, 示例中的环境为docker container 中, TensorRT版本为7 1523 | 1524 | 1525 | 1526 | 1. 依照以下官方步骤下载到本地 1527 | 1528 | - git clone --recurse-submodules https://github.com/onnx/onnx-tensorrt.git 1529 | 1530 | - `cd onnx-tensorrt` 1531 | 1532 | - `mkdir build` 1533 | 1534 | - `cmake .. -DTENSORRT_ROOT=` 1535 | 或是 1536 | `cmake .. -DTENSORRT_ROOT= -DGPU_ARCHS="61" ` 1537 | 1538 | 例如: `cmake .. -DTENSORRT_ROOT=/path/to/TensorRT-7.x.x.x -DGPU_ARCHS="61" ` 1539 | 1540 | 这个部分就是指定trt的路径联合构建编译 1541 | 1542 | - `make -j8` 进行编译(记得是在onnx-tensorrt目录下执行) 1543 | 1544 | - `sudo make install` 1545 | 1546 | - 安装完成后command指令输入 `onnx2trt` 确认一下指令是否可行 1547 | 1548 | 1549 | 1550 | #### 中途遇见的问题 1551 | 1552 | **问题1** 1553 | 1554 | Tensorrt 装在DL的conda环境中, 但是要编译onnx的时候遇到 `/usr/bin/cmake: 没有那个文件或目录` 1555 | 1556 | 必须退出当前的conda 环境才能使用cmake 但是用cmake的时候又找不到tensorrt环境,因为装在conda dl的环境中 1557 | 1558 | 1559 | 1560 | **问题2** 1561 | 1562 | cmake的时候会找不到TensorRT library 1563 | 1564 | 报错如下 1565 | 1566 | ```shell 1567 | -- Found TensorRT headers at /input/TensorRT-6.0.1.5/include 1568 | -- Find TensorRT libs at /input/TensorRT-6.0.1.5/lib/libnvinfer.so;/input/TensorRT-6.0.1.5/lib/libnvinfer_plugin.so;TENSORRT_LIBRARY_MYELIN-NOTFOUND 1569 | -- Could NOT find TENSORRT (missing: TENSORRT_LIBRARY) 1570 | ERRORCannot find TensorRT library. 1571 | CMake Error: The following variables are used in this project, but they are set to NOTFOUND. 1572 | Please set them or make sure they are set and tested correctly in the CMake files: 1573 | TENSORRT_LIBRARY_MYELIN 1574 | linked by target "nvonnxparser_static" in directory /input/onnx-tensorrt 1575 | linked by target "nvonnxparser" in directory /input/onnx-tensorrt 1576 | 1577 | -- Configuring incomplete, errors occurred! 1578 | See also "/input/onnx-tensorrt/build/CMakeFiles/CMakeOutput.log". 1579 | See also "/input/onnx-tensorrt/build/CMakeFiles/CMakeError.log". 1580 | ``` 1581 | 1582 | 1583 | 1584 | **解决2** 1585 | 1586 | 只是因为onnx1.6只支持tensorrt7的版本, 碰到这个问题是安装成6.0的TensorRT 1587 | 1588 | 1589 | 1590 | **问题3** 1591 | 1592 | make -j8 的时候 出现 onnx_utils.hpp文件的报错 1593 | 1594 | 报错如下 1595 | 1596 | ``` 1597 | numeric_limits’ is not a member of ‘std’ 1598 | ``` 1599 | 1600 | 表示出错的onnx_utils.hpp头文件没有包含 1601 | 1602 | ``` 1603 | #include 1604 | #include 1605 | ``` 1606 | 1607 | **解决** 1608 | 1609 | 只要vim 进去加上两个头文件就可以 1610 | 1611 | 1612 | 1613 | > ### onnx 转tensorrt 1614 | 1615 | 主要是基本的auto转换方式 1616 | 1617 | 1618 | 1619 | 以下参考 https://github.com/onnx/onnx-tensorrt 1620 | 1621 | 使用onnx2trt指令 指定要转换的onnx 模型, -o 表示输出为 xxxxx.trt 1622 | 1623 | ``` 1624 | onnx2trt my_model.onnx -o my_engine.trt 1625 | ``` 1626 | 1627 | 也可以将onnx 模型转换为可读txt文件 1628 | 1629 | ``` 1630 | onnx2trt my_model.onnx -t my_model.onnx.txt 1631 | ``` 1632 | 1633 | `-h` 可查看使用方法 1634 | 1635 | ``` 1636 | onnx2trt -h 1637 | ``` 1638 | 1639 | 1640 | 1641 | 1642 | 1643 | > ### onnx转tensorrt (自定义plugin) 1644 | > 1645 | > 以下参考 https://blog.csdn.net/qq_33120609/article/details/99429967#pytorchop_9 1646 | 1647 | 假设已经自定义了一个pytorch的DCNv2的op, 并且成功转换为onnx, 但因onnx转换成trt时还需要cuda的实现并且注册 1648 | 1649 | 注意一下流程实现环境为 **TensorRT 5.0 / onnx-tensorrt-5.0** 1650 | 1651 | #### 流程 1652 | 1653 | 1. 确认onnx-tensorrt已经下载 1654 | 2. 参考InstanceNormalization.cpp/.h ,写好自己DCN_v2.hpp和DCN_v2.cpp的实现。(同样可以参考FancyActivation,ResizeNearest等,都是官方写好的自定义op的示例,是op的逻辑) 1655 | 3. 在builtin_op_importers.cpp中使用`DEFINE_BUILTIN_OP_IMPORTER`添加对自己注册Op的使用。 1656 | 4. 在CMakeLists.txt中,`set(IMPORTER_SOURCES... `下面将自己的DCN_v2.cpp加进去。 1657 | 5. 按照官方,重新编译自己的onnx-tensorrt, 然后拿自己输出的onnx文件测试自定义的op层是否可以正常读取 1658 | 1659 | 1660 | 1661 | 可以看到onnx-tensorrt 中的 这4个OP都是注册在onnx-tensorrt中, 并非原先在tensorrt中就有, 所以可以用一样的方式注册, Split,FancyActivation,InstanceNormalization,ResizeNearest 1662 | 1663 | 官方repo master原始码中的InstanceInstanceNormalization 继承 onnx2trt::PluginV2 1664 | 1665 | ```c++ 1666 | class InstanceNormalizationPlugin final : public onnx2trt::PluginV2 1667 | ``` 1668 | 1669 | PluginCreator的部分则是继承nvinfer1::IPluginCreator 1670 | 1671 | ```c++ 1672 | class InstanceNormalizationPluginCreator : public nvinfer1::IPluginCreator 1673 | ``` 1674 | 1675 | 1676 | 1677 | 下面例子中为onnx-tensorrt 5.0, 继承的为Plugin而不是PluginV2 1678 | 1679 | onnx-tensorrt5.1之后都是继承PluginV2 1680 | 1681 | #### Plugin API 接口应用 1682 | 1683 | 1. 自定义DCNv2.hpp 1684 | - 继承onnx2trt::Plugin 1685 | - 定义参数(_in_channel, _out_channel_….) 1686 | - 定义protected函数, 包含反序列各种参数 1687 | - 定义getSerializationSize函数 1688 | - 定义Serialize 序列化函数 1689 | - 定义public函数 1690 | - 定义初始化方法 1691 | - 定义getPlugintype方法 1692 | - 定义getNbOutputs 1693 | - 定义initialize 1694 | - 定义terminate 1695 | - 定义enqueue 1696 | - 定义getWorkspaceSize 1697 | - 析构函数 1698 | 1699 | ```c++ 1700 | #ifndef ONNX2TRT_DCNV2_H 1701 | #define ONNX2TRT_DCNV2_H 1702 | 1703 | #pragma once 1704 | 1705 | #include "plugin.hpp" 1706 | #include "serialize.hpp" 1707 | #include 1708 | #include 1709 | #include 1710 | #include 1711 | 1712 | class DCNv2Plugin final : public onnx2trt::Plugin { 1713 | int _in_channel; 1714 | int _out_channel; 1715 | int _kernel_H; 1716 | int _kernel_W; 1717 | int _deformable_group; 1718 | int _dilation; 1719 | int _groups; // not use 1720 | int _padding; 1721 | int _stride; 1722 | std::vector _h_weight; 1723 | std::vector _h_bias; 1724 | float* _d_weight; 1725 | float* _d_bias; 1726 | float* _d_ones; 1727 | float *_d_columns; 1728 | 1729 | 1730 | bool _initialized; 1731 | 1732 | protected: 1733 | void deserialize(void const* serialData, size_t serialLength) { 1734 | deserializeBase(serialData, serialLength); 1735 | deserialize_value(&serialData, &serialLength, &_in_channel); 1736 | deserialize_value(&serialData, &serialLength, &_out_channel); 1737 | deserialize_value(&serialData, &serialLength, &_kernel_H); 1738 | deserialize_value(&serialData, &serialLength, &_kernel_W); 1739 | deserialize_value(&serialData, &serialLength, &_deformable_group); 1740 | deserialize_value(&serialData, &serialLength, &_dilation); 1741 | deserialize_value(&serialData, &serialLength, &_groups); 1742 | deserialize_value(&serialData, &serialLength, &_padding); 1743 | deserialize_value(&serialData, &serialLength, &_stride); 1744 | deserialize_value(&serialData, &serialLength, &_h_weight); 1745 | deserialize_value(&serialData, &serialLength, &_h_bias); 1746 | } 1747 | size_t getSerializationSize() override { 1748 | return (serialized_size(_in_channel) + 1749 | serialized_size(_out_channel) + 1750 | serialized_size(_kernel_H) + 1751 | serialized_size(_kernel_W) + 1752 | serialized_size(_deformable_group) + 1753 | serialized_size(_dilation) + 1754 | serialized_size(_groups) + 1755 | serialized_size(_padding) + 1756 | serialized_size(_stride) + 1757 | serialized_size(_h_weight) + 1758 | serialized_size(_h_bias) 1759 | ) + getBaseSerializationSize(); 1760 | } 1761 | void serialize(void *buffer) override { 1762 | serializeBase(buffer); 1763 | serialize_value(&buffer, _in_channel); 1764 | serialize_value(&buffer, _out_channel); 1765 | serialize_value(&buffer, _kernel_H); 1766 | serialize_value(&buffer, _kernel_W); 1767 | serialize_value(&buffer, _deformable_group); 1768 | serialize_value(&buffer, _dilation); 1769 | serialize_value(&buffer, _groups); 1770 | serialize_value(&buffer, _padding); 1771 | serialize_value(&buffer, _stride); 1772 | serialize_value(&buffer, _h_weight); 1773 | serialize_value(&buffer, _h_bias); 1774 | } 1775 | public: 1776 | DCNv2Plugin(int in_channel, 1777 | int out_channel, 1778 | int kernel_H, 1779 | int kernel_W, 1780 | int deformable_group, 1781 | int dilation, 1782 | int groups, 1783 | int padding, 1784 | int stride, 1785 | nvinfer1::Weights const& weight, 1786 | nvinfer1::Weights const& bias); 1787 | 1788 | DCNv2Plugin(void const* serialData, size_t serialLength) : _initialized(false) { 1789 | this->deserialize(serialData, serialLength); 1790 | } 1791 | 1792 | const char* getPluginType() const override { return "DCNv2"; } 1793 | bool supportsFormat(nvinfer1::DataType type, 1794 | nvinfer1::PluginFormat format) const override; 1795 | 1796 | int getNbOutputs() const override { return 1; } 1797 | nvinfer1::Dims getOutputDimensions(int index, 1798 | const nvinfer1::Dims *inputDims, 1799 | int nbInputs) override; 1800 | 1801 | int initialize() override; 1802 | void terminate() override; 1803 | int enqueue(int batchSize, 1804 | const void *const *inputs, void **outputs, 1805 | void *workspace, cudaStream_t stream) override; 1806 | size_t getWorkspaceSize(int maxBatchSize) const override; 1807 | ~DCNv2Plugin(); 1808 | }; 1809 | 1810 | 1811 | #endif //ONNX2TRT_DCNV2_H 1812 | ``` 1813 | 1814 | 1815 | 1816 | 2. 自定义DCNv2.cpp 1817 | 1818 | ```c++ 1819 | #include "DCNv2.hpp" 1820 | #include "dcn_v2_im2col_cuda.h" 1821 | 1822 | #define CHECK_CUDA(call) do { \ 1823 | cudaError_t status = call; \ 1824 | if( status != cudaSuccess ) { \ 1825 | return status; \ 1826 | } \ 1827 | } while(0) 1828 | 1829 | cublasHandle_t blas_handle() 1830 | { 1831 | static int init[16] = {0}; 1832 | static cublasHandle_t handle[16]; 1833 | int n = 0; 1834 | cudaError_t status = cudaGetDevice(&n); 1835 | if(!init[n]) { 1836 | cublasCreate(&handle[n]); 1837 | init[n] = 1; 1838 | } 1839 | return handle[n]; 1840 | } 1841 | inline bool is_CHW(nvinfer1::Dims const& dims) { 1842 | return (dims.nbDims == 3 && 1843 | dims.type[0] == nvinfer1::DimensionType::kCHANNEL && 1844 | dims.type[1] == nvinfer1::DimensionType::kSPATIAL && 1845 | dims.type[2] == nvinfer1::DimensionType::kSPATIAL); 1846 | } 1847 | 1848 | DCNv2Plugin::DCNv2Plugin(int in_channel, 1849 | int out_channel, 1850 | int kernel_H, 1851 | int kernel_W, 1852 | int deformable_group, 1853 | int dilation, 1854 | int groups, 1855 | int padding, 1856 | int stride, 1857 | nvinfer1::Weights const &weight, nvinfer1::Weights const &bias):_in_channel(in_channel), 1858 | _out_channel(out_channel),_kernel_H(kernel_H),_kernel_W(kernel_W),_deformable_group(deformable_group), 1859 | _dilation(dilation),_groups(groups),_padding(padding),_stride(stride),_initialized(false){ 1860 | 1861 | if (weight.type == nvinfer1::DataType::kFLOAT) 1862 | { 1863 | _h_weight.assign((float*)weight.values,(float*)weight.values+weight.count); 1864 | } else { throw std::runtime_error("Unsupported weight dtype");} 1865 | 1866 | if (bias.type == nvinfer1::DataType::kFLOAT) 1867 | { 1868 | _h_bias.assign((float*)bias.values,(float*)bias.values+bias.count); 1869 | } else { throw std::runtime_error("Unsupported bias dtype");} 1870 | 1871 | } 1872 | 1873 | 1874 | int DCNv2Plugin::initialize() { 1875 | if(_initialized) return 0; 1876 | auto _output_dims = this->getOutputDimensions(0, &this->getInputDims(0), 3); 1877 | assert(is_CHW(this->getInputDims(0))); 1878 | assert(is_CHW(_output_dims)); 1879 | size_t ones_size = _output_dims.d[1]*_output_dims.d[2]* sizeof(float); 1880 | size_t weight_size = _h_weight.size()* sizeof(float); 1881 | size_t bias_size = _h_bias.size()* sizeof(float); 1882 | float *ones_cpu = new float[ones_size/ sizeof(float)]; 1883 | for (int i = 0; i < ones_size/ sizeof(float); i++) { 1884 | ones_cpu[i] = 1.0; 1885 | } 1886 | CHECK_CUDA(cudaMalloc((void**)&_d_columns, _in_channel * _kernel_H * _kernel_W * ones_size);); 1887 | CHECK_CUDA(cudaMalloc((void**)&_d_ones, ones_size)); 1888 | CHECK_CUDA(cudaMalloc((void**)&_d_weight, weight_size)); 1889 | CHECK_CUDA(cudaMalloc((void**)&_d_bias, bias_size)); 1890 | CHECK_CUDA(cudaMemcpy(_d_ones, ones_cpu, ones_size, cudaMemcpyHostToDevice)); 1891 | CHECK_CUDA(cudaMemcpy(_d_weight, _h_weight.data(), weight_size, cudaMemcpyHostToDevice)); 1892 | CHECK_CUDA(cudaMemcpy(_d_bias, _h_bias.data(), bias_size, cudaMemcpyHostToDevice)); 1893 | delete[] ones_cpu; 1894 | _initialized = true; 1895 | 1896 | return 0; 1897 | } 1898 | void DCNv2Plugin::terminate() { 1899 | if (!_initialized) { 1900 | return; 1901 | } 1902 | cudaFree(_d_columns); 1903 | cudaFree(_d_bias); 1904 | cudaFree(_d_weight); 1905 | cudaFree(_d_ones); 1906 | _initialized = false; 1907 | } 1908 | 1909 | DCNv2Plugin::~DCNv2Plugin() { 1910 | terminate(); 1911 | } 1912 | bool DCNv2Plugin::supportsFormat(nvinfer1::DataType type, nvinfer1::PluginFormat format) const { 1913 | 1914 | return (type == nvinfer1::DataType::kFLOAT); 1915 | } 1916 | nvinfer1::Dims DCNv2Plugin::getOutputDimensions(int index, const nvinfer1::Dims *inputDims, int nbInputs) { 1917 | assert(index == 0); 1918 | assert(inputDims); 1919 | assert(nbInputs == 3); 1920 | nvinfer1::Dims const& input = inputDims[0]; 1921 | assert(is_CHW(input)); 1922 | nvinfer1::Dims output; 1923 | output.nbDims = input.nbDims; 1924 | for( int d=0; d(inputs[0]); 1944 | const float* offset = static_cast(inputs[1]); 1945 | const float* mask = static_cast(inputs[2]); 1946 | float * output = static_cast(outputs[0]); 1947 | nvinfer1::Dims input_dims = this->getInputDims(0); 1948 | assert(batchSize==1); 1949 | int h = input_dims.d[1]; 1950 | int w = input_dims.d[2]; 1951 | int height_out = (h + 2 * _padding - (_dilation * (_kernel_H - 1) + 1)) / _stride + 1; 1952 | int width_out = (w + 2 * _padding - (_dilation * (_kernel_W - 1) + 1)) / _stride + 1; 1953 | m = _out_channel; 1954 | n = height_out * width_out; 1955 | k = 1; 1956 | alpha = 1.0; 1957 | beta = 0.0; 1958 | /// output nxm 1959 | /// ones 1xn T ->> nx1 1960 | /// bias 1xm 1961 | /// ones x bias = nxm 1962 | // add bias 1963 | cublasSgemm(handle, 1964 | CUBLAS_OP_T, CUBLAS_OP_N, 1965 | n, m, k,&alpha, 1966 | _d_ones, k, 1967 | _d_bias, k,&beta, 1968 | output, n); 1969 | // im2col (offset and mask) 1970 | modulated_deformable_im2col_cuda(stream,input,offset,mask, 1971 | 1, _in_channel, h, w, 1972 | height_out, width_out, _kernel_H, _kernel_W, 1973 | _padding, _padding, _stride, _stride, _dilation, _dilation, 1974 | _deformable_group, _d_columns); 1975 | m = _out_channel; 1976 | n = height_out * width_out; 1977 | k = _in_channel * _kernel_H * _kernel_W; 1978 | alpha = 1.0; 1979 | beta = 1.0; 1980 | // im2col conv 1981 | cublasSgemm(handle, 1982 | CUBLAS_OP_N, CUBLAS_OP_N, 1983 | n, m, k,&alpha, 1984 | _d_columns, n, 1985 | _d_weight, k, 1986 | &beta, 1987 | output, n); 1988 | return 0; 1989 | } 1990 | ``` 1991 | 1992 | 1993 | 1994 | 1995 | 1996 | 1997 | 1998 | > onnx-tensorrt 5.1 继承PluginV2 范例 1999 | 2000 | //TODO 2001 | 2002 | #### PluginV2 API 接口应用 2003 | 2004 | 1. 自定义DCNv2.hpp 2005 | - 继承onnx2trt::Plugin 2006 | - 定义参数(_in_channel, _out_channel_….) 2007 | - 定义protected函数, 包含反序列各种参数 2008 | - 定义getSerializationSize函数 2009 | - 定义Serialize 序列化函数 2010 | - 定义public函数 2011 | - 定义初始化方法 2012 | - 定义getPlugintype方法 2013 | - 定义getNbOutputs 2014 | - 定义initialize 2015 | - 定义terminate 2016 | - 定义enqueue 2017 | - 定义getWorkspaceSize 2018 | - 析构函数 2019 | 2020 | 2021 | 2022 | ------ 2023 | 2024 |

Torch2Trt

2025 | 2026 | 官方项目地址 : https://github.com/NVIDIA-AI-IOT/torch2trt 2027 | 2028 | 2029 | 2030 | 关于讨论找出不支持layer的issue 2031 | 2032 | https://github.com/NVIDIA-AI-IOT/torch2trt/issues/130 2033 | -------------------------------------------------------------------------------- /command for deeplearning.md: -------------------------------------------------------------------------------- 1 |

Basic All You Need For Deep

2 |

update 2022.11.2

3 |

目錄

4 | 5 | > ### Linux 6 | 7 | 1. [移动到指定目录 cd](#1) 8 | 9 | 2. [查看文件下的资料 ls, du显示目录或文件大小 ](#2) 10 | 11 | 3. [創建文件夾 mkdir or rmdir](#3) 12 | 13 | 4. [创建文件夹 touch](#4) 14 | 15 | 5. [搜寻文件or文件夹](#5) 16 | 17 | 6. [查看当前目录下的文件数](#6) 18 | 19 | 7. [rm 删除指令](#7) 20 | 21 | 8. [解压缩及打包](#8) 22 | 23 | 9. [移動文件及复制文件移动, 修改文件名mv](#9) 24 | 25 | 10. [建立资料连接, 软连接 In](#10) 26 | 27 | 11. [chmod / chown](#11) 28 | 29 | 12. [查看CPU,内存占用率, 磁盘占用](#12) 30 | 31 | - swapfile 增加虚拟内存 32 | 33 | 13. [杀死用户进程 kill PID](#13) 34 | 35 | 14. [确认本机IP位置及端口查看](#14) 36 | 37 | 15. dd 拷贝文件, 刻录iso 38 | 39 | 16. dpkg 安装包指令 40 | 41 | 17. lsof 查询行程开启的文件列表 42 | 43 | 18. grep 筛选 44 | 45 | 19. rename 修改文件名, 后缀 46 | 47 | 20. Ubuntu终端Terminal快捷键(16.04) 48 | 49 | 21. SSH 使用方式 50 | 51 | 22. sftp 使用方式 52 | 53 | 23. Tmux 终端会话插件 54 | 55 | 24. Rsync 远程传输文件 56 | 57 | 25. [其他](#15) 58 | 59 | 1. 查看Ubuntu系统版本 60 | 61 | 2. Nm 目标文件格式分析 62 | 63 | 3. 使用者权限/创建删除用户 64 | 65 | 4. Screen 后台执行程序 66 | 67 | 5. ldd / readelf 指令查询程序或者依赖的共享库 68 | 69 | 6. Linux 查看装置 70 | 71 | 7. ldconfig 使用 72 | 73 | 8. 在桌面建立快捷图示 desktop 74 | 75 | 9. Ubuntu 下翻墙 安装v2ray 76 | 77 | 10. `/etc/crontab` 设定定时执行 78 | 79 | 11. Ubuntu 清楚缓存脚本 80 | 81 | 12. Ubuntu 安装类似win 绘图paint的软件 82 | 83 | 13. zsh 终端美化 84 | 85 | 14. 好用的rgb值提取插件 colour-pick 86 | 87 | ​ 88 | 89 | ------ 90 | 91 | > ### Vim 92 | 93 | 16. [VIM 编辑器常用操作](#16) 94 | 1. [編寫文件常用指令](#16-1) 95 | 2. [vim功能添加](#16-5) 96 | 3. [vim下一鍵生成編譯](#16-6) 97 | 4. [vim编辑时显示当前的文件名](#16-7) 98 | 17. [VIM 插件](#17) 99 | 1. ale 語法檢查 100 | 2. Vundle 插件管理器 101 | 3. youcompleteme 代碼補全 102 | 18. [g++ / gcc 编译器相关](#18) 103 | 104 | ------ 105 | 106 | > ### Anaconda3 107 | 108 | 19. [anaconda3 / mini-conda 安装/使用](#19) 109 | 110 | ------ 111 | 112 | > ### Git 113 | 114 | 20. [git 指令集](#20) 115 | 116 | ------ 117 | 118 | 21. [cuda使用](#21) 119 | 22. [python pip 包安装及管理](#23) 120 | 23. Jupyter-notebook SSH远程连接 (待完成) 121 | 24. Docker指令 ( 镜像, 容器操作 ) 122 | 25. Shell 脚本(Shell script) 语法与执行 123 | 26. python 安装 基于Linux 124 | 27. onnx 模型相关 125 | 28. PytorchToCaffe 126 | 29. TensorboardX 使用 127 | 128 | ------ 129 | 130 | > #### Linux 程序安装 131 | 132 | 汇整所有需要apt-get 安装的程序安装方法 133 | 134 | 1. 安装Ubuntu 135 | 2. 安装pip 136 | 3. 安装openCV 137 | 4. 安装cmake 138 | 5. Google Protocol buffer 139 | 140 | ------ 141 | 142 | > ### Others 143 | 144 | 1. 查看版本 145 | 2. 推荐好用 146 | 3. Nvidia 查看GPU使用率 147 | 4. ffmpeg 使用 148 | 5. pdb debug模式 149 | 150 | ------ 151 | 152 | > ### DeepLearning 153 | 154 | 1. 数据集格式 155 | 2. CUDA, CUDNN 安装,移除, 共存 156 | 157 | ------ 158 | 159 | 160 | 161 | > #### IDE 使用技巧(Pycharm, CLion…) 162 | 163 | 1. SSH 连接方式 164 | 165 | ------ 166 | 167 | > ### Socket 网路编程 168 | 169 | 1. 实现服务端与客户端webcam 实时传输图像 170 | 171 | 172 | 173 | --- 174 | 175 | > #### 使用nginx 本地实时推流rtmp 176 | 177 | --- 178 | 179 | > #### redis-py使用 180 | 181 |

目录结束

182 | 183 | 184 | 185 | 186 | 187 | 188 | # Linux 189 | 190 |

1. 移动到指定目录

191 | 192 | 1. 到指定目录 :`cd ./path` 193 | 194 | 2. 返回上级目录 :`cd .. ` 195 | 196 | 3. 如果要指定的目录在上面多层 可以用 ```../../```, 放回基层就放多少个```../``` 197 | 198 | Example: 199 | 200 | ``` 201 | ../../input/dataset 202 | ``` 203 | 204 | 4. 回到主目录 ```cd ~``` 205 | 206 | 5. 返回上一次所在的目录, 而不是上一级 `cd -` 207 | 208 | 6. `pwd`获取当前绝对路径 209 | 210 |

2. ls查看文件下的资料、du显示目录或文件大小, df 磁盘大小

211 | 212 | 213 | 214 | 215 | 216 | 217 | - `ls -a` 列出文件夹下所有文件 218 | 219 | - `ls -lah` 列出文件下的文件 以及 效果信息例如文件大小 220 | 221 | 222 | 223 | 224 | 225 | du 表示 disk usage 226 | 227 | Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的 228 | 229 | ``` 230 | du [选项][文件] 231 | ``` 232 | 233 | 查看一个目录下, 各个文件or文件夹的大小, -s表示 --summarize 仅显示总计,只列出最后加总的值。 234 | 235 | -h或--human-readable 以K,M,G为单位,提高信息的可读性。 236 | 237 | ``` 238 | du -sh * 239 | ``` 240 | 241 | —max-depth 可以设置查看目录的深度 242 | 243 | ``` 244 | du -lh --max-depth=1 #这就只会查看当前目录下的文件或者文件夹的大小 245 | ``` 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | Df 表示 disk free 254 | 255 | ```shell 256 | # df -h 257 | Filesystem Size Used Avail Use% Mounted on 258 | /dev/sda6 29G 4.2G 23G 16% / 259 | udev 1.5G 4.0K 1.5G 1% /dev 260 | tmpfs 604M 892K 603M 1% /run 261 | none 5.0M 0 5.0M 0% /run/lock 262 | none 1.5G 156K 1.5G 1% /run/shm 263 | ``` 264 | 265 | 266 | 267 | 268 | 269 | ------ 270 | 271 |

3. 創建文件夾及删除文件夹

272 | 273 | 274 | 275 | 276 | `mkdir test1` :創建一個空目錄 277 | 278 | `mkdir -p test1/test2`:递归创建多个目录 279 | 280 | `rmdir` 也就是remove empty directories 只能删除空目录 较少用 281 | 282 | ------ 283 | 284 |

4. 创建文件

285 | 286 | 先cd到你要的创建文件的路径,接着 287 | 288 | ```touch 文件名.扩展名``` 289 | 290 | ------ 291 | 292 |

5. 搜寻文件or文件夹 293 |

294 | 295 | 296 | 297 | 298 | 参考 https://blog.miniasp.com/post/2010/08/27/Linux-find-command-tips-and-notice 299 | 300 | 注意以下都是用 `/` 表示搜索所有路径, 也可以依照需求改变路径 301 | 302 | 303 | 304 | 一般最简单的方式可以用, 直接用grep过滤打印出来的文件 305 | 306 | ``` 307 | find / |grep "name" 308 | ``` 309 | 310 | 311 | 312 | 依照文件名找 313 | 314 | ```find / -iname "文件名"```: 315 | 316 | 找包含xxx字符的文件, 加单引号跟星号 317 | 318 | `find / -iname '*xxx*'` 319 | 320 | 321 | 322 | 依照目录找 323 | 324 | ```find / -iname "文件夹名" -type d``` 325 | 326 | 依照档案名找 327 | 328 | `find / -iname "档案" -type f` 329 | 330 | `find / -iname '*.log' -type f ` : 找副档名为log, 可以依照需求自己改 331 | 332 | 333 | 334 | **查找大文件** 335 | 336 | 参考 https://www.jb51.net/article/109696.htm 337 | 338 | ``` 339 | find . -type f -size +800M 340 | ``` 341 | 342 | 343 | 344 | 345 | 346 | ------ 347 | 348 |

6. 查看当前目录下的文件数

349 | ``` 350 | ls path |wc -l 351 | ``` 352 | 353 | 354 | 355 | 356 | 统计path directories下的文件个数 357 | 358 | wc表示wordcount 359 | 360 | 以上指令相当于先用ls将文件每行print出, 然后wc计算多少列 361 | 362 | 363 | 364 | ``` 365 | find path/ -type f |wc -l 366 | ``` 367 | 368 | 列出path下所有文件总共的数目 369 | 370 | 371 | 372 | 373 | 374 | ------ 375 | 376 |

7. rm 删除指令

377 | 378 | 379 | 380 | 381 | rm + 要删除的文件 一个也可以多个 382 | 383 | ``` 384 | rm file1 file2 385 | ``` 386 | 387 | rm -f 表示强制删除 388 | 389 | rm -r 表示递归删除, 可以删除包含文件夹下面的所有文件 390 | 391 | ``` 392 | rm -rf path/ 393 | ``` 394 | 395 | ps.使用时请千万谨慎 396 | 397 | 398 | 399 | 如果想在某个文件夹下, 保留特定文件不删除, 其他删除 400 | 401 | 参考 https://www.cnblogs.com/wjoyxt/p/10408423.html 402 | 403 | ```shell 404 | rm -rf !(file1 | file2) #删除除了file1, file2的文件rm -rf !(*jpg) #删除文件名不以jpg结尾的文件rm -rf *@(jpg|png) #删除文件名以jpg或png结尾的文件, 千万记得 | 中间不要有空格 405 | ``` 406 | 407 | 408 | 409 |

8. 解压缩及打包

410 | 411 | 412 | 413 | 414 | 415 | 416 | - ```tar xvf file.tar -C /dir you want``` 417 | - ```tar xvzf file.tgz -C /dir you want``` 418 | - ```tar xvzf file.tar.gz -C /dir you want``` 419 | - ```unzip file.zip -d /dir you want``` 420 | 421 | #### unrar 解壓 422 | 423 | ``` 424 | unrar e test.rar 解压文件到当前目录 425 | 426 | unrar x test.rar /path/to/extract 427 | 428 | unrar l test.rar 查看rar中的文件 429 | 430 | unrar v test.rar 更详细 431 | 432 | unrar t test.rar 测试是否可以成功解压 433 | ``` 434 | 435 | 436 | 437 | #### zip 打包 438 | 439 | 如果是直接压缩几个文件,那就可以直接使用命令 , 440 | 441 | ``` 442 | zip newfilename.zip filename1 filename2 443 | ``` 444 | 445 | 但是一般情况下都会压缩一个文件夹或者叫目录,使用命令 446 | 447 | ``` 448 | zip -r newfilename.zip file1 file2。 449 | ``` 450 | 451 | 如果文件夹下面包含太多的文件,可以使用-q选项,不现实压缩的过程,即zip -q -r newfilename.zip file1 file2。 452 | 一般来讲,-q选项和-r选项已经够用了 453 | 454 | EX. 455 | 将/root/test文件夹下的test1文件夹和test2.txt压缩到 aaa.zip 456 | 在/root/test目录下,执行命令 457 | 458 | ``` 459 | zip -q -r aaa.zip test1 test2.txt 460 | ``` 461 | 462 | OK!压缩完成! 463 | 464 | ------ 465 | 466 |

9. 移動文件及复制文件移动, 修改文件名

467 | 468 | `mv 文件名 方式 目的地` 469 | 470 | 471 | 472 | #### 复制文件下的内容移动 473 | 474 | ```CP [选项] 源文件或目录 目的文件或目录``` 475 | 476 | 例如要备份一个source.list文件 477 | 478 | `cp /etc/apt/sources.list /etc/apt/sources.list.backup` 479 | 480 | - -b 同名,备分原来的文件 481 | - -f 强制覆盖同名文件 482 | - -r 按递归方式保留原目录结构复制文件 483 | 484 | #### 复制文件夹下内容 485 | 486 | *号表示所有文件的意思, 将dir1文件夹中所有的东西全部复制都dstpath/dir2文件夹下面 487 | 488 | ```cp sourcepath/dir1/* dstpath/dir2``` 489 | 490 | #### 复制文件夹及里面的内容 491 | 492 | 将dir1文件夹 完整复制到path2底下 493 | 494 | ```cp -a path1/dir1 path2/``` 495 | 496 |

修改文件名 mv

497 | 498 | 将filename1 改成 filename2 499 | 500 | `mv filename1 filename2` 501 | 502 | 503 | 504 | #### 移动文件除了某个 505 | 506 | 除了xxxxx 之外, 其他移动到dst_path 507 | 508 | ``` 509 | mv !(xxxxx) dst_path 510 | ``` 511 | 512 | 513 | 514 | ------ 515 | 516 |

10. 建立资料连接, 软连接 In

517 | 518 | Linux ln命令是一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接。 519 | 520 | 当我们需要在不同的目录,用到相同的文件时,**我们不需要在每一个需要的目录下都放一个必须相同的文件**,我们只要在某个固定的目录,放上该文件,然后在 其它的目录下用ln命令链接(link)它就可以,**不必重复的占用磁盘空间。** 521 | 522 | **ln 的語法** 523 | 524 | ``` 525 | ln [选项] [源文件] [目标文件或目录] 526 | ``` 527 | 528 | 529 | 530 | **ln 必要参数**: 531 | 532 | - -b 删除,覆盖以前建立的链接 533 | - -d 允许超级用户制作目录的硬链接 534 | - -f 强制执行 535 | - -i 交互模式,文件存在则提示用户是否覆盖 536 | - -n 把符号链接视为一般目录 537 | - -s 软链接(符号链接) 538 | - -v 显示详细的处理过程 539 | 540 | 541 | 542 | **软连接 / 硬连接** 543 | 544 | 链接又可分为两种 : 硬链接(hard link)与软链接(symbolic link),硬链接的意思是一个档案可以有多个名称,而软链接的方式则是产生一个特殊的档案,该档案的内容是指向另一个档案的位置 545 | 546 | Example: 547 | 548 | 之前刚好遇到一个问题用上了软连接, Cmake编译的过程中, lib里面找不到libcudart.so这个动态库, 于是建立一个软连接将cuda文件夹中的libcudart.so 连接到 /usr/lib/下 549 | 550 | ```shell 551 | ln -s /usr/local/cuda/lib64/libcudart.so /usr/lib/libcudart.so 552 | ``` 553 | 554 | 555 | 556 | ------ 557 | 558 |

11. chmod / chown

559 | 560 | #### chmod 561 | 562 | > 参考http://linux.vbird.org/linux_basic/0210filepermission.php 563 | 564 | Linux 的文件可以分别为owner、groups、others三种身份并且有各自的read/wrtie/execute 权限, 一共3x3=9种 565 | 566 | 利用`ls -al`可以查看目录下文件的详细权限, 可看到类似-rwxrwxrwx 的字样, 这九个权限为三个三个为一组 567 | 568 | 可用分数表示各权限 569 | 570 | r : 2 571 | 572 | w : 4 573 | 574 | x : 1 575 | 576 | 所以权限数字为 577 | 578 | owner = rwx = 4+2+1 = 7 579 | group = rwx = 4+2+1 = 7 580 | others= --- = 0+0+0 = 0 581 | 582 | 如果要将一个文件的权限设定为所有对象都可以开启, 那么也就是-rwxrwxrwx, 也就是777 583 | 584 | `chmod 777 ./test.sh` 585 | 586 | 如果希望该档案不让人修改, 可以`chmod 755 ./test.sh` 587 | 588 | 589 | 590 | ##### 用符号类型改变权限 591 | 592 | `chmod u +x filename` +/-/= 593 | 594 | ``` 595 | u : user g :group o :othersa : all(所有身份) 596 | ``` 597 | 598 | ``` 599 | r : readw : writex : execute 600 | ``` 601 | 602 | 603 | 604 | ##### 将底下包含子文件夹都改变权限 605 | 606 | 加个-R 607 | 608 | ``` 609 | chmod -R xxx 610 | ``` 611 | 612 | 613 | 614 | #### chown 615 | 616 | Linux chown(英文全拼:**change owner**)命令用于设置文件所有者和文件关联组的命令 617 | 618 | 所有的文件皆有拥有者。利用 chown 将指定文件的拥有者改为指定的用户或组,用户可以是用户名或者用户 ID,组可以是组名或者组 ID,文件是以空格分开的要改变权限的文件列表 619 | 620 | chown 需要超级用户 **root** 的权限才能执行此命令 621 | 622 | 623 | 624 | 将文件 file1.txt 的拥有者设为 runoob,群体的使用者 runoobgroup : 625 | 626 | ``` 627 | chown runoob:runoobgroup file1.txt 628 | ``` 629 | 630 | 将当前前目录下的所有文件与子目录的拥有者皆设为 runoob,群体的使用者 runoobgroup: 631 | 632 | ``` 633 | chown -R runoob:runoobgroup * 634 | ``` 635 | 636 | 637 | 638 | 639 | 640 | ------ 641 | 642 |

12. 查看CPU、内存占用率

643 | 644 | 键入```top``` : 645 | 646 | - PID:进程的ID 647 | - USER:进程所有者 648 | - PR:进程的优先级别,越小越优先被执行 649 | - NInice:值 650 | - VIRT:进程占用的虚拟内存 651 | - RES:进程占用的物理内存 652 | - SHR:进程使用的共享内存 653 | - S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数 654 | - %CPU:进程占用CPU的使用率 655 | - %MEM:进程使用的物理内存和总内存的百分比 656 | - TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。 657 | 658 | 659 | 660 | 661 | 662 | #### swapfile 設置 增加虛擬內存 663 | 664 | 切割原磁盤空間, 例如需要4GB空間作為內存 665 | 666 | 1. `sudo fallocate -l 4G /swapfile` : l用來指定分配大小, /swapfile作為swap使用的檔案存放路徑 667 | 2. `. sudo chmod 600 /swapfile` :为设置好的档案添加root权限 668 | 3. `sudo mkswap /swapfile` : 告知系统将此档案作为swap使用 669 | 4. `sudo swapon /swapfile` :启用swap 670 | 671 | 将swap设定为开机时自动挂载(ubuntu 18之下) 672 | 673 | ``` 674 | sudo vim /etc/fstab添加以下到最下排/swapfile none swap sw 0 0 675 | ``` 676 | 677 | 678 | 679 | `swapon -s` : 查看当前的swap状态 680 | 681 | `swaoff /swapfile` :禁用swapfile 682 | 683 | `swaoff -a` :禁用所有的swapfile 684 | 685 | `swapon -a`: 启用所有swapfile 686 | 687 | 可以通过先禁用,再启动的方式,来擦除swap中已有的数据 688 | 689 | 690 | 691 | ------ 692 | 693 |

13. 杀死进程kill PID

694 | 695 | ```ps aux``` : 查看所有进程 696 | 697 | `top` : 查看所有进程 698 | 699 | ```kill pid``` : 找到对应的pid号, 例如2316是你要关闭的程序pid, 则```kill 2316``` 700 | 701 | `kill -9 pid` : 强制终止进程 702 | 703 | 删除指定程序所有进程: 704 | 705 | ```ps -ef | grep 程序名称| awk '{print $2}' | xargs kill -9``` 706 | 707 | 例如要kill所有vim进程则 708 | 709 | ```ps -ef | grep vim| awk '{print $2}' | xargs kill -9``` 710 | 711 | ------ 712 | 713 |

14. IP 内网及外网地址及端口查看

714 | 715 | `-a`: -a (all)显示所有选项,默认不显示LISTEN相关 716 | `-t`: (tcp)仅显示tcp相关选项 717 | `-u`: (udp)仅显示udp相关选项(all)显示所有选项,默认不显示LISTEN相关 718 | `-t`: (tcp)仅显示tcp相关选项 719 | `-u`: (udp)仅显示udp相关选项 720 | `-n`: 拒绝显示别名,能显示数字的全部转化成数字。 721 | `-l`: 仅列出有在 Listen (监听) 的服務状态 722 | `-p`: 显示建立相关链接的程序名 723 | `-r`: 显示路由信息,路由表 724 | `-e`: 显示扩展信息,例如uid等 725 | `-s`: 按各个协议进行统计 726 | `-c`: 每隔一个固定时间,执行该netstat命令。 727 | 728 | ```ifconfig -a``` : 确认内网IP位置,如果是连接wifi 请看wlan0底下inet的位置 729 | 730 | ```netstat -anptl``` : 确认端口 731 | 732 | `netstat -an |grep {portnum}` : 可查看portnum端口连接的情况 733 | 734 | ``` bash 735 | $ netstat -an |grep 8000 736 | >>> 737 | tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 738 | tcp6 0 0 :::8000 :::* LISTEN 739 | ``` 740 | 741 | `netstat -nultp` : 查看已经正在使用的端口 742 | 743 | ```bash 744 | $ netstat -nultp #主要是加了-l 745 | Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name 746 | tcp 0 0 0.0.0.0:8002 0.0.0.0:* LISTEN - 747 | tcp 0 0 0.0.0.0:10050 0.0.0.0:* LISTEN - 748 | tcp 0 0 127.0.0.1:3947 0.0.0.0:* LISTEN 2942980/python 749 | tcp 0 0 0.0.0.0:622 0.0.0.0:* LISTEN - 750 | tcp 0 0 127.0.0.1:31983 0.0.0.0:* LISTEN 2705969/node 751 | tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN - 752 | tcp 0 0 127.0.0.1:22705 0.0.0.0:* LISTEN 2942980/python 753 | tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - 754 | tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN - 755 | tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN - 756 | ``` 757 | 758 | 759 | 760 | `curl ifconfig.me` : 确认外网ip地址 761 | 762 | ------ 763 | 764 |

15. dd 拷贝文件, 刻录iso

765 | 766 | 767 | 参考 https://www.cnblogs.com/linuxde/p/8719253.html 768 | 769 | dpkg 是Debian package的简写,为”Debian“ 操作系统 专门开发的套件管理系统,用于软件的安装,更新和移除。 所有源自"Debian"的Linux的发行版都使用 dpkg, 例如"Ubuntu" 770 | 771 | ------ 772 | 773 |

17. lsof 查询行程开启的文件列表

774 | 775 | 776 | 777 | 778 | 参考 https://blog.gtwang.org/linux/linux-lsof-command-list-open-files-tutorial-examples/ 779 | 780 | 列出一位user或者多位 所开启的文件 781 | 782 | ``` 783 | lsof -u user1, user2, user3 784 | ``` 785 | 786 | 列出指定程式所开启的文件 787 | 788 | ``` 789 | lsof -c xxxx 790 | ``` 791 | 792 | 根据PID列出开启的文件 793 | 794 | ``` 795 | lsof -p 14662,14678,14979 796 | ``` 797 | 798 | 列出所以程序开启的文件列表 799 | 800 | ``` 801 | lsof 802 | ``` 803 | 804 | 依照上面, 可以用grep筛选 xxxx 关键字的文件 805 | 806 | ``` 807 | lsof | grep xxxxx 808 | ``` 809 | 810 | ------ 811 | 812 |

18. grep 筛选

813 | 814 | 815 | 816 | 817 | 参考https://blog.gtwang.org/linux/linux-grep-command-tutorial-examples/ 818 | 819 | 再指定的文件汇总搜寻关键字 820 | 821 | ``` 822 | grep 搜寻关键字 文件1 文件2 823 | ``` 824 | 825 | ex 826 | 827 | ```shell 828 | # 在 /etc/os-release 檔案中搜尋 Ubuntu 關鍵字grep Ubuntu /etc/os-release 829 | ``` 830 | 831 | ```shell 832 | # 在 /etc/*.conf 中搜尋 network 關鍵字grep network /etc/*.con 833 | ``` 834 | 835 | ```shell 836 | # 篩選含有 network 關鍵字的檔案名稱ls /etc/ | grep network 837 | ``` 838 | 839 | 840 | 841 | 不分大小写 -i 842 | 843 | ```shell 844 | # 不分大小寫grep -i Ubuntu /etc/os-release 845 | ``` 846 | 847 | 标示出行数 -n 848 | 849 | ```shell 850 | # 標示行號 851 | grep -n Ubuntu /etc/os-release 852 | ``` 853 | 854 | 排除关键字 -v 855 | 856 | ```shell 857 | # 顯示不包含 Ubuntu 關鍵字的行 858 | grep -v Ubuntu /etc/os-release 859 | ``` 860 | 861 | 递归搜寻 -r 862 | 863 | ```shell 864 | # 在 /etc/ 下所有檔案中搜尋 ubuntu 865 | grep -r ubuntu /etc/ 866 | ``` 867 | 868 | 更多用法查阅参考链接 869 | 870 | ------ 871 | 872 |

19. rename 修改文件名, 后缀

873 | 874 | 875 | 876 | 877 | 参考http://einverne.github.io/post/2018/01/rename-files-batch.html 878 | 879 | rename 可以用来批量重新命名 880 | 881 | 命令组成如下 882 | 883 | ``` 884 | rename [options] "s/oldname/newname/" file 885 | ``` 886 | 887 | 看选项options 部分,`rename` 支持以下的选项: 888 | 889 | - `-v` 将重命名的内容都打印到标准输出,v 可以看成 verbose 890 | - `-n` 测试会重命名的内容,将结果都打印,但是并不真正执行重命名的过程 891 | - `-f` force 会覆盖本地已经存在的文件 892 | - `-h` `-m` `-V` 分别为帮助,帮助,版本 893 | - `-e` 比较复杂,可以通过该选项,写一些脚本来做一些复杂的事情 894 | 895 | 896 | 897 | 当命令中最后 file 为 `*` 时表示,匹配当前文件夹下所有文件,如果为 `?` 时则匹配只有一个字符的文件名 898 | 899 | 900 | 901 | 修改文件名中特定的字段 902 | 903 | ``` 904 | rename "s/AA/aa/" * # 把文件名中的AA替换成aa 905 | ``` 906 | 907 | 修改文件后缀名 908 | 909 | ``` 910 | rename "s/.html/.php/" * # 把.html 后缀的改成 .php后缀 911 | rename "s/.png/.jpg/" * # 将 png 改为 jpg 912 | ``` 913 | 914 | 批量添加文件后缀 915 | 916 | ``` 917 | rename "s/$/.txt/" * # 把所有的文件名都以txt结尾 918 | ``` 919 | 920 | 批量删除文件名 921 | 922 | ``` 923 | rename "s/.txt//" * # 把所有以.txt结尾的文件名的.txt删掉 924 | ``` 925 | 926 | 927 | 928 | ------ 929 | 930 |

20. Ubuntu终端Terminal快捷键

931 | 932 | 933 | 934 | 935 | #### 版本16.04 936 | 937 | 参考https://blog.csdn.net/qq_15166261/article/details/104469606?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161032979616780277019548%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161032979616780277019548&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-2-104469606.first_rank_v2_pc_rank_v29&utm_term=Ubuntu%20%E7%BB%88%E7%AB%AF%E5%B8%B8%E7%94%A8%20%E5%BF%AB%E6%8D%B7&spm=1018.2226.3001.4187 938 | 939 | 画面中按下win, 可以跳出快捷提示 940 | 941 | ``` 942 | 常用的快捷键 943 | | 快捷键 |功能 | 944 | Tab 自动补全 945 | Ctrl+a 光标移动到开始位置 946 | Ctrl+e 光标移动到最末尾 947 | Ctrl+k 删除此处至末尾的所有内容 948 | Ctrl+u 删除此处至开始的所有内容 949 | Ctrl+d 删除当前字符 950 | Ctrl+h 删除当前字符前一个字符 951 | Ctrl+w 删除此处到左边的单词 952 | Ctrl+y 粘贴由Ctrl+u, Ctrl+d, Ctrl+w删除的单词 953 | Ctrl+l 相当于clear,即清屏 954 | Ctrl+r 查找历史命令 955 | Ctrl+b 向回移动光标 956 | Ctrl+f 向前移动光标 957 | Ctrl+t 将光标位置的字符和前一个字符进行位置交换 958 | Ctrl+& 恢复 ctrl+h 或者 ctrl+d 或者 ctrl+w 删除的内容 959 | Ctrl+S 暂停屏幕输出 960 | Ctrl+Q 继续屏幕输出 961 | Ctrl+Left-Arrow 光标移动到上一个单词的词首 962 | Ctrl+Right-Arrow 光标移动到下一个单词的词尾 963 | Ctrl+p 向上显示缓存命令 964 | Ctrl+n 向下显示缓存命令 965 | Ctrl+d 关闭终端 966 | Ctrl+xx 在EOL和当前光标位置移动 967 | Ctrl+x@ 显示可能hostname补全 968 | Ctrl+c 终止进程/命令 969 | Shift+上或下 终端上下滚动 970 | Shift+PgUp/PgDn 终端上下翻页滚动 971 | Ctrl+Shift+n 新终端 972 | alt+F2 输入gnome-terminal打开终端 973 | Shift+Ctrl+T 打开新的标签页 974 | Shift+Ctrl+W 关闭标签页 975 | Shift+Ctrl+C 复制 976 | Shift+Ctrl+V 粘贴 977 | Alt+数字 切换至对应的标签页 978 | Shift+Ctrl+N 打开新的终端窗口 979 | Shift+Ctrl+Q 管壁终端窗口 980 | Shift+Ctrl+PgUp/PgDn 左移右移标签页 981 | Ctrl+PgUp/PgDn 切换标签页 982 | F1 打开帮助指南 983 | F10 激活菜单栏 984 | F11 全屏切换 985 | Alt+F 打开 “文件” 菜单(file) 986 | Alt+E 打开 “编辑” 菜单(edit) 987 | Alt+V 打开 “查看” 菜单(view) 988 | Alt+S 打开 “搜索” 菜单(search) 989 | Alt+T 打开 “终端” 菜单(terminal) 990 | Alt+H 打开 “帮助” 菜单(help) 991 | 另外一些小技巧包括:在终端窗口命令提示符下,连续按两次 Tab 键、或者连续按三次 Esc 键、或者按 Ctrl+I 组合键,将显示所有的命令及工具名称。Application 键即位置在键盘上右 Ctrl 键左边的那个键,作用相当于单击鼠标右键。 992 | 993 | Terminal终端 994 | CTRL + ALT + T: 打开终端 995 | TAB: 自动补全命令或文件名 996 | CTRL + SHIFT + V: 粘贴(Linux中不需要复制的动作,文本被选择就自动被复制) 997 | CTRL + SHIFT + T: 新建标签页 998 | CTRL + D: 关闭标签页 999 | CTRL + L: 清楚屏幕 1000 | CTRL + R + 文本: 在输入历史中搜索 1001 | CTRL + A: 移动到行首 1002 | CTRL + E: 移动到行末 1003 | CTRL + C: 终止当前任务 1004 | CTRL + Z: 把当前任务放到后台运行(相当于运行命令时后面加&) 1005 | ~: 表示用户目录路径 1006 | ``` 1007 | 1008 | ------ 1009 | 1010 |

21. SSH 使用方式

1011 | 1012 | 1013 | 1014 | 1015 | #### 端口映射 1016 | 1017 | 例如将本地地址映射到服务器上, 假设服务器节点为g01n02, port为22 1018 | 1019 | ```shell 1020 | ssh -NfL 127.0.0.1:2222:g01n02:22 username@host 1021 | ``` 1022 | 1023 | 映射完之后, 可以直接 1024 | 1025 | ``` 1026 | ssh username@127.0.0.1 -p 2222 1027 | ``` 1028 | 1029 | 就能够访问g01n02 服务器节点 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | #### 透过ssh上传文件or下载文件到服务器 1036 | 1037 | 上传文件到服务器 1038 | 1039 | ``` 1040 | scp -P 端口号 要传输的文件 用户名@主机ip:路径 1041 | ``` 1042 | 1043 | Ex. 1044 | 1045 | ``` 1046 | scp -P 22 darkent.zip root@123.45.2.345:/home/username/workplace 1047 | ``` 1048 | 1049 | 1050 | 1051 | 从服务器下载文件到本地 1052 | 1053 | ``` 1054 | scp -P 端口号 用户名@主机ip:要下载文件的路径 空格 本地路径 1055 | ``` 1056 | 1057 | 如果要下载or上传整个目录 加上-r 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | #### 端口被占用 1066 | 1067 | 参考 https://blog.csdn.net/lsr40/article/details/77775858 1068 | 1069 | 1. 查找被占用的端口的pid进程, 最后一排就是pid 1070 | 1071 | ``` 1072 | netstat -apn | grep 2181 1073 | ``` 1074 | 1075 | 2. 通过kill -9 杀掉 1076 | 1077 | ``` 1078 | kill -9 pid 1079 | ``` 1080 | 1081 | 3. 也可以先用ps -ef | grep pid 查看哪一个用户创建该进程, 避免杀错 1082 | 1083 | ``` 1084 | ps -ef | grep pid 1085 | ``` 1086 | 1087 | ------ 1088 | 1089 |

22. sftp使用

1090 | 1091 | 1092 | 1093 | 1094 | 通常可以用来从服务器上下载数据 1095 | 1096 | 例如 `get` 1097 | 1098 | ``` 1099 | sftp username@host #先登入 1100 | 1101 | 然后可以cd到需要的地方下载比如 下载xxx文件 1102 | get xxxx.后缀 1103 | ``` 1104 | 1105 | 下载可以用`put` 1106 | 1107 | ------ 1108 | 1109 |

23. Tmux 终端会话插件

1110 | 1111 | 1112 | 1113 | 1114 | 启动会话 删除等操作 1115 | 1116 | ``` 1117 | tmux new-session -s "name" 启动会话 1118 | tmux at -t "name" 回复会话 1119 | tmux ls 列出会话 1120 | tmux kill-session -t 'name' 删除会话 1121 | crtl + B + D离开会话 1122 | ``` 1123 | 1124 | 启动控制台进行操作 1125 | 1126 | ``` 1127 | ctrl + B 启动控制台 1128 | p : previous bash 1129 | n : next bash 1130 | shift + & : close current bash 1131 | ``` 1132 | 1133 | 在一个窗格中进行分割 1134 | 1135 | ``` 1136 | % 垂直分割" 水平分割x : 关闭窗格[ : 在窗口中上下滚动, 支持直接按page up or page down上下, 因为在窗口中无法正常shift+up查看终端信息, 按q 可以退出模式ctrl + B + up or down : 调整窗口的高度 1137 | ``` 1138 | 1139 | 1140 | 1141 | #### 在tmux会话下 vim 功能失效 1142 | 1143 | 例如visual block模式下无法显示highlight的文字 1144 | 1145 | 到`~/.vimrc` 下添加 1146 | 1147 | ``` 1148 | set term=xterm-256colorset t_Co=256 1149 | ``` 1150 | 1151 | ------ 1152 | 1153 |

25. Rsync 传输文件

1154 | 1155 | 1156 | 1157 | ``` 1158 | rsync 参数 来源文件 目的文件 1159 | ``` 1160 | 1161 | -v : verbose 输出较详细文件 1162 | 1163 | -r : 递归子目录下所有文件 1164 | 1165 | -z : 启用压缩, 可将要传输的文件先打包, 然后到目的之后解压缩 1166 | 1167 | -avzh : 类似scp, 远程传输文件, 将上述命令综合输入 1168 | 1169 | -u :不会修改目标目录下已经有的文件 1170 | 1171 | ``` 1172 | rsync -avzh /path/xxx.xx root@192.168.xx.xx:/path/ 1173 | ``` 1174 | 1175 | `--include`: 仅传输固定的文件or目录 ex. `--include '*.py'` 1176 | 1177 | `--exclude`: 排除不上传的文件or目录 ex. `--exclude '*.py'` 1178 | 1179 | ``` 1180 | rsync -avzh /path/xxx.xx --exclude 'path/*.py' root@192.168.xx.xx:/path/ 1181 | rsync -avzh /path/xxx.xx --exclude={'*.txt', 'dir3', 'dir4'} root@192.168.xx.xx:/path/ #排除多个文件 1182 | ``` 1183 | 1184 | 1185 | 1186 | 如果是长期需要进行两地同步推荐使用 1187 | 1188 | `--exclude-from=` : 从txt文件中指定需要**排除**的文件 1189 | 1190 | `--files-from=` : 从txt文件中指定需要**传输**的文件 1191 | 1192 | **transfered_files.txt** 定义了需要传输的文件 1193 | 1194 | ``` 1195 | AlphaPose/ 1196 | yolov5/ 1197 | yolov5_4.0/ 1198 | spc_project.py 1199 | SPC_config.py 1200 | spc_run_server.py 1201 | models/ 1202 | ``` 1203 | 1204 | 使用--files-from=指定定义好的文件 1205 | 1206 | ``` 1207 | rsync -avzh ./ --files-from=transfered_files.txt user@127.0.0.1:/home/user/VScodeProjects/path/ 1208 | ``` 1209 | 1210 | 1211 | 1212 | --- 1213 | 1214 |

25. 其他

1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | #### 查Ubuntu系统版本 1224 | 1225 | 1. `cat /etc/issue (简单)` 1226 | 2. `cat /etc/lsb-release(具体)` 1227 | 3. `uname -a(内核)` 1228 | 1229 | #### nm 目标文件格式分析 1230 | 1231 | 主要用于列出目标的符号清单, 对于每一个符号,nm列出其值(the symbol value),类型(the symbol type)和其名字(the symbol name) 1232 | 1233 | 查看动态库 (so) :`nm -D /path/to/xxxx.so` 1234 | 1235 | 這樣會打印出全部內容, 可以用grep篩選 如 1236 | 1237 | `nm -D /path/to/xxxx.so | grep xxxx` 1238 | 1239 | 1240 | 1241 | | **符号类型** | **说明** | 1242 | | ------------ | ------------------------------------------------------------ | 1243 | | A | 该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。 | 1244 | | B | 该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM中 | 1245 | | C | 该符号为common。common symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B。 | 1246 | | D | 该符号位于初始话数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中。 | 1247 | | G | 该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。 | 1248 | | I | 该符号是对另一个符号的间接引用。 | 1249 | | N | 该符号是一个debugging符号。 | 1250 | | R | 该符号位于只读数据区。例如定义全局const int test[] = {123, 123};则test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,test在rodata section中,大小为4。 | 1251 | | S | 符号位于非初始化数据区,用于small object。 | 1252 | | T | 该符号位于代码区text section。 | 1253 | | U | 该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U。 | 1254 | | V | 该符号是一个weak object。 | 1255 | | W | The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. | 1256 | | - | 该符号是a.out格式文件中的stabs symbol。 | 1257 | | ? | 该符号类型没有定义 | 1258 | 1259 | 1260 | 1261 | #### 使用者权限/创建删除用户 1262 | 1263 | #### 创建用户 1264 | 1265 | 参考https://blog.gtwang.org/linux/linux-useradd-command-tutorial-examples/ 1266 | 1267 | ```shell 1268 | $ sudo useradd -m jason -s /bin/bash 1269 | $ sudo passwd jason 1270 | $ sudo adduser jason sudo 1271 | $ su jason 1272 | ``` 1273 | 1274 | - 创建了可以登录的meow用户并使用/bin/bash作为shell。 1275 | - 设置密码。 1276 | - 为jason用户增加管理员权限。 1277 | - 切换登录用户为jason。 1278 | 1279 | 如果要让用户只能访问一个文件夹可以加上 -d 1280 | 1281 | ``` 1282 | sudo useradd -m jason -s /bin/bash -d /home/jason 1283 | ``` 1284 | 1285 | 为用户指定id 1286 | 1287 | ``` 1288 | sudo useradd -m jason -s /bin/bash -d /home/jason -u 999 1289 | ``` 1290 | 1291 | 为用户加入既有的group, 除了主要group 一位user能同时在不同的group中 1292 | 1293 | ``` 1294 | sudo useradd -m jason -s /bin/bash -d /home/jason -g team 1295 | ``` 1296 | 1297 | 1298 | 1299 | 如果想为jason添加管理员权限可以 1300 | 1301 | ``` 1302 | sudo vi /etc/sudoers 1303 | ``` 1304 | 1305 | 找到 1306 | 1307 | root ALL=(ALL:ALL) ALL 1308 | 1309 | 下面添加 1310 | 1311 | jason ALL=(ALL:ALL) ALL 1312 | 1313 | 1314 | 1315 | #### 删除用户 1316 | 1317 | ``` 1318 | killall -u username 1319 | ``` 1320 | 1321 | 可以删除用户所有信息 1322 | 1323 | 如果碰到用户进程还在的时候, 可以直接杀光用户进程 1324 | 1325 | ``` 1326 | killall -u username 1327 | ``` 1328 | 1329 | 1330 | 1331 | #### su 指令 1332 | 1333 | 有些文件必须要root权限才能修改, 因此 1334 | 1335 | `su [-fmp] [-c command] [-s shell] [--help] [--version] [-] [USER [ARG]]` 1336 | 1337 | - 求换为root使用者: `su root`, 假如用户名为stephen则 `su stephen` 1338 | 1339 | - 初此设定需要`sudo passwd root` 设定密码, 设定好之后, 才能`su root` 1340 | 1341 | - 显示当前用户:`whoami` 1342 | 1343 | 如果以上出现`su: Authentication failure`, 就需要为root设定pwd 1344 | 1345 | `sudo passwd root`, 接着设定好密码 1346 | 1347 | - 退出当前用户 : `exit` / `logout` / `ctrl + d` 1348 | 1349 | 1350 | 1351 | 1352 | 1353 | #### screen 后台执行程序 1354 | 1355 | ref https://blog.csdn.net/weixin_42331537/article/details/89962801?depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2&utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2 1356 | 1357 | 这个绝对是常用云服务器执行任务的利器, 将程序放在后台运行即使登出或者与本地断开连线还是在运行 1358 | 1359 | 下面是几个常用的指令 1360 | 1361 | ```shell 1362 | screen -S session_name # 新建一个叫session_name的sessionscreen -ls(或者screen -list) # 列出当前所有的sessionscreen -r session_name # 回到session_name这个sessionscreen -d session_name # 远程detach某个sessionscreen -d -r session_name # 结束当前session并回到session_name这个session 1363 | ``` 1364 | 1365 | 通常先用 `screen -S xxxxx`(任意取名)创建session 并且进入 1366 | 1367 | 然后进入之后就可以执行需要在后台执行的程序, 比如开始训练模型 1368 | 1369 | 接着`ctrl + A + D`可以退出当前的session, 但是只是离开并不影响程序继续运行 1370 | 1371 | 如果想在进去观察的话可以`screen -r 你的session_name` , 如果进不去, 可以 1372 | 1373 | `screen -D -r session_name` 就可以 1374 | 1375 | 1376 | 1377 | 如果遇到任务没结束需要强制删除会话(比如占用显存的情况) 1378 | 1379 | `screen -S session_name -X quit` 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | #### ldd / readelf 指令查询程序或者依赖的共享库 1392 | 1393 | `ldd 选项 file` 1394 | 1395 | 一般可以查询程序或者库文件依赖的共享库列表 1396 | 1397 | 比如 有一个执行文件 test 1398 | 1399 | 可以 1400 | 1401 | ```shell 1402 | ldd test>>> 输出, 就可以看到连接许多共享库linux-vdso.so.1 => (0x00007ffe3dbc1000)libm.so.6 => /lib64/libm.so.6 (0x00007f17a5b55000)libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f17a592e000)libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f17a5704000) 1403 | ``` 1404 | 1405 | 选项 1406 | 1407 | `-v` : 看更多资讯包含版本信息 1408 | 1409 | #### readelf 1410 | 1411 | 参考 https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/readelf.html 1412 | 1413 | 1414 | 就是用于读取elf文件的,一般elf文件是 1415 | 1416 | 1417 | - 可重定位的对象文件(Relocatable file) 1418 | 由汇编器汇编生成的 .o 文件 1419 | 1420 | - 可执行的对象文件(Executable file) 1421 | 可执行应用程序 1422 | 1423 | - 可被共享的对象文件(Shared object file) 1424 | 动态库文件,也即 .so 文件 1425 | 1426 | 如果想知道一个程序可运行的架构平台 1427 | 1428 | `$readelf -h main| grep Machine` 1429 | 1430 | 想知道一个库需要什么其他的库支持 1431 | 1432 | `$readelf -d xxxxx.so` 1433 | 1434 | 1435 | #### alias 环境变量设置别名 1436 | 1437 | 在环境变量中添加 1438 | 1439 | ``` 1440 | alias 快捷指令="终端指令" 1441 | 1442 | EX. 1443 | alias wo="cd /home/workplace" 1444 | 则在终端输入wo, 就相当于 cd /home/workplace 1445 | ``` 1446 | 1447 | 1448 | 1449 | #### Linux查询装置 1450 | 1451 | 1. 查主机板 :`sudo dmidecode | more` 1452 | 1453 | 1454 | 1455 | #### ldconfig 1456 | 1457 | ref https://www.cnblogs.com/schips/p/10183111.html 1458 | 1459 | ldconfig是一个动态链接库管理命令,其目的为了让动态链接库为系统所共享。 1460 | 1461 | ldconfig的主要用途: 1462 | 1463 | **默认搜寻/lilb和/usr/lib,以及配置文件/etc/ld.so.conf内所列的目录下的库文件。** 1464 | 1465 | 搜索出可共享的动态链接库,库文件的格式为:lib***.so.**,进而创建出动态装入程序(ld.so)所需的连接和缓存文件。 1466 | 1467 | 缓存文件默认为/etc/ld.so.cache,该文件保存已排好序的动态链接库名字列表 1468 | 1469 | ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。 1470 | 1471 | ldconfig需要注意的地方: 1472 | 1473 | ``` 1474 | 1、往/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf文件的,但是添加完后需要调用下ldconfig,不然添加的library会找不到。 1475 | 1476 | 2、如果添加的library不在/lib和/usr/lib里面的话,就一定要修改/etc/ld.so.conf文件,往该文件追加library所在的路径,然后也需要重新调用下ldconfig命令。比如在安装[MySQL](http://lib.csdn.net/base/14)的时候,其库文件/usr/local/mysql/lib,就需要追加到/etc/ld.so.conf文件中。命令如下: 1477 | 1478 | \# echo "/usr/local/mysql/lib" >> /etc/ld.so.conf 1479 | 1480 | \# ldconfig -v | grep mysql 1481 | 1482 | 3、如果添加的library不在/lib或/usr/lib下,但是却没有权限操作写/etc/ld.so.conf文件的话,这时就需要往export里写一个全局变量LD_LIBRARY_PATH,就可以了。 1483 | ``` 1484 | 1485 | 1486 | 1487 | #### 桌面建立快捷图示 1488 | 1489 | 比如ubuntu下安装clion, 解压缩之后, 只有文件夹, 要到bin下 `./clion.sh`启动程序, 我们可以在桌面建立一个clion.desktop, 连接到这个sh进行快捷启动 1490 | 1491 | 1492 | 1493 | ``` 1494 | [Desktop Entry]Encoding=UTF-8Name=xxx//可执行文件Exec=sh /路径/你前面生成的可执行的shell文件.sh //.sh可执行文件的绝对路径, 前面的sh 命令不要丢哦Icon=/usr/local/share/icons/jesh.png //软件的图标文件路径 ico也可Info="Spark"Categories=GTK;Network;message; //可写可不写Comment="Gtk+ based like QQ" //提示性信息 ,可写可不写Terminal=falseType=ApplicationStartupNotify=true 1495 | ``` 1496 | 1497 | 1498 | 1499 | 1500 | 1501 | #### apt-file 寻找依赖 1502 | 1503 | 首先安装 1504 | 1505 | ``` 1506 | apt-get updateapt-get install apt-fileapt-file update 1507 | ``` 1508 | 1509 | 1510 | 1511 | 1512 | 1513 | 使用方法: 1514 | 1515 | 如果在运行过程中报错例如缺少 xxxx.so 1516 | 1517 | 则利用寻找文件 1518 | 1519 | ``` 1520 | apt-file search xxxx.so 1521 | ``` 1522 | 1523 | 接着就会先找这个文件在哪? 1524 | 1525 | 如果没有, 就会找这个库文件是属于哪个包的 1526 | 1527 | 1528 | 1529 | 如果想看这个包中有哪些文件可以 1530 | 1531 | ``` 1532 | apt-file list opencv-python 1533 | ``` 1534 | 1535 | 1536 | 1537 | #### Ubuntu 下翻墙 安装 v2ray 1538 | 1539 | 参考 https://mahongfei.com/1776.html 1540 | 1541 | 1. 下载`V2ray`客户端,这里以最简单的`AppImage`文件为例,下载链接: 1542 | 1543 | https://github.com/Qv2ray/Qv2ray/releases/download/v1.99.6/Qv2ray-refs.tags.v1.99.6-linux.AppImage 1544 | 1545 | 1546 | 1547 | 2. 下载核心文件,下载链接: 1548 | 1549 | https://github.com/v2ray/v2ray-core/releases/download/v4.22.1/v2ray-linux-64.zip 1550 | 1551 | 3. 3:进入v2ray下载的根目录,执行以下命令: 1552 | 1553 | ``` 1554 | sudo chmod +x ./Qv2ray-refs.tags.v1.99.6-linux.AppImage 1555 | ``` 1556 | 1557 | 4. 仍然在v2ray根目录下打开终端,输入以下命令, 会启动软件 1558 | 1559 | ``` 1560 | sudo ./Qv2ray-refs.tags.v1.99.6-linux.AppImage 1561 | ``` 1562 | 1563 | 执行4后会出现主界面,点击`首选项` preferences 1564 | 1565 | 然后在V2ray Settings 1566 | 1567 | ``` 1568 | -> Core Executable Path 设定核心文件中的v2ray-> V2ray Assets Directory 设定 核心文件路径 1569 | ``` 1570 | 1571 | 设置好之后按OK 1572 | 1573 | 1574 | 1575 | 5. 接着在订阅(subscription) 1576 | 1. 左下角新增 1577 | 1. 输入服务商名字 1578 | 2. 输入订阅url 1579 | 3. 然后update subscription Data 1580 | 6. 首选项(preference)中, 入站设置(Inbound Settings) 1581 | 1. 勾选 Set system Proxy 1582 | 7. 点击**连接 connect** , 开始上网 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | #### /etc/crontab 1589 | 1590 | 设置定时执行程序 1591 | 1592 | ``` 1593 | vim /etc/crontab 1594 | ``` 1595 | 1596 | 比如每隔3分钟执行一次xxx.sh脚本 1597 | 1598 | ```shell 1599 | #打开文件后在最下面添加 1600 | 1601 | #依序是min hour day month dayofwork user command 1602 | #这里只列出每隔几分钟执行的方法 1603 | 1604 | */3 * * * * root sh /home/path/to/xxx.sh 1605 | ``` 1606 | 1607 | 参考 https://blog.csdn.net/lxz978161079/article/details/80662346?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param 1608 | 1609 | 1610 | 1611 | 1612 | 1613 | 1614 | 1615 | #### Ubuntu 清除缓存脚本 1616 | 1617 | 以下内容复制到sh文件, 可以搭配crontab 定时使用 1618 | 1619 | ```shell 1620 | #!/bin/bash 1621 | sync; echo 1 > /proc/sys/vm/drop_caches 1622 | sync; echo 2 > /proc/sys/vm/drop_caches 1623 | sync; echo 3 > /proc/sys/vm/drop_caches 1624 | swapoff -a && swapon -a 1625 | ``` 1626 | 1627 | 1628 | 1629 | 1630 | 1631 | #### Ubuntu 安装类似win 绘图paint的软件 1632 | 1633 | 如下安装 1634 | 1635 | ``` 1636 | sudo apt-get install kolourpaint4 1637 | ``` 1638 | 1639 | 然后在左下角Show Applications中可以找到KolourPaint的图示 1640 | 1641 | 1642 | 1643 | 1644 | 1645 | #### Zsh 终端美化插件 1646 | 1647 | 项目地址 :https://github.com/ohmyzsh/ohmyzsh 1648 | 1649 | 1. Ubuntu安装步骤 1650 | 1651 | ```shell 1652 | sudo apt install zshsh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"#如果该步骤遇到ssl 无法建立问题, 尝试sh -c "$(wget -O- https://gitee.com/mcornella/ohmyzsh/raw/master/tools/install.sh)"#参考 https://github.com/ohmyzsh/ohmyzsh/issues/9528 1653 | ``` 1654 | 1655 | 2. 当前terminal 简单切换 1656 | 1657 | ```shell 1658 | exec bash或者 exec zsh 1659 | ``` 1660 | 1661 | 3. 设置default的shell样式, 设置完之后restart terminal就可以 1662 | 1663 | ```shell 1664 | chsh -s /bin/bash或者chsh -s /bin/zsh 1665 | ``` 1666 | 1667 | 1668 | 1669 | #### 好用的rbg 值提取插件 colour pick 1670 | 1671 | 项目参考 github.com/sturatlangridge/ColourPicker 1672 | 1673 | ubuntu下安装 1674 | 1675 | ```shell 1676 | sudo snap install pick-colour-picker 1677 | ``` 1678 | 1679 | 1680 | 1681 | ------ 1682 | 1683 |

16. VIM 编辑器常用操作

1684 | 1685 | 1686 | 1687 | 1688 |

編寫文件常用指令

1689 | 1690 | - `w` 向下行移動 1691 | - `b` 向上行移動 1692 | - ```i``` :insert 用來鍵入 1693 | - `d` : delete 删除所选的区域 1694 | - ```x``` : 游標的地方delete 1695 | - ```u``` : 返回上一步 1696 | - `ctrl + r`: 撤銷上一步 1697 | - ```:q!``` : 不保存強制退出 1698 | - ```:w ``` : 写入保存不退出 1699 | - ```:wq!```:写入保存后退出 1700 | - `y`:鼠标框起复制 1701 | - `yy`: 快速复制当前行 1702 | - `p`:贴上 1703 | - `pp` : 快速黏贴上yy复制的内容 1704 | - `:X` : 大写的X主要是用来加密文件 1705 | - 输入之后, 会要求输入二次密码, 两次都需要一样 1706 | - 下次开启就需要输入密码才能打开文件, 密码务必要记住, 否则解不开 1707 | - 如果需要重新设置为无密码, 只要`:X`之后, 直接回车两次就行, 就设置为无密码了 1708 | 1709 | - 多行注释的步骤 1710 | - 首先Ctrl + V 进行visual block模式 然后上下选择需要注释的行, 然后shift + i(insert), 之后输入注释符号 # or // 在按esc, 就能发现多行已经注释了, 如果解除注释一样多行选择要删去的注释地方, 然后按 `d` , 就能删除注释符号 1711 | - 搜索功能 1712 | - 首先:进行模式,然后输入`/` 然后在输入要搜索的名test 1713 | - 例如 `/test`, 就会搜索test这个字符 1714 | - `n` 表示搜索下一个, `N` 表示搜索上一个 1715 | 1716 | ------ 1717 | 1718 |

VIM 功能添加

1719 | 1720 | vim提供许多的功能可以依照需求在编辑文件中时添加, 也能直接在~/.vimrc文件中添加成永久功能 1721 | 1722 | #### 透过vimrc添加为永久功能 1723 | 1724 | 1. `vim ~/.vimrc` 开启or新建文件 (mac位置通常放在/User/yourname/.vimrc, linux 在etc/vimrc/.vimrc) 1725 | 2. 将需要的功能添加进文件并且保存退出 1726 | 1727 | #### 功能列表 1728 | 1729 | `: syntax on` 开启语法高亮 1730 | 1731 | `: set mouse=a` 可以开启功能 (鼠标mode下无法使用复制贴上) 1732 | 1733 | `: set mouse =` 关闭功能, 或是直接在vimrc中添加 : **set mouse=a**, 永久使用 1734 | 1735 | `: set number` 可以开启功能 1736 | 1737 | 1738 | 1739 | #### 单行Shift 移动 1740 | 1741 | 参考 http://blog.wildsky.cc/posts/vim-move-line/ 1742 | 1743 | 可以类似Pycharm 用shift 移动代码行的效果, 也能可视模式下选定多行 然后移动 1744 | 1745 | ```vim 1746 | "move line up/down" 1747 | nnoremap :m .+1== 1748 | nnoremap :m .-2== 1749 | inoremap :m .+1==gi 1750 | inoremap :m .-2==gi 1751 | vnoremap :m '>+1gv=gv 1752 | vnoremap :m '<-2gv=gv 1753 | ``` 1754 | 1755 | 1756 | 1757 | 1758 | 1759 | 1760 | 1761 | 1762 | 1763 | ------ 1764 | 1765 |

vim下一鍵生成編譯

1766 | 1767 | 將以下代碼添加到 ~/.vimrc中, vimrc 打開方式 ``` vi ~/.vimrc``` 1768 | 1769 | ``` 1770 | map :call CompileRunGcc() 1771 | func! CompileRunGcc() 1772 | exec "w" 1773 | if &filetype == 'c' 1774 | exec "!g++ % -o %<" 1775 | exec "!time ./%<" 1776 | elseif &filetype == 'cpp' 1777 | exec "!g++ % -o %<" 1778 | exec "!time ./%<" 1779 | elseif &filetype == 'java' 1780 | exec "!javac %" 1781 | exec "!time java %<" 1782 | elseif &filetype == 'sh' 1783 | :!time bash % 1784 | elseif &filetype == 'python' 1785 | exec "!time python2.7 %" 1786 | elseif &filetype == 'html' 1787 | exec "!firefox % &" 1788 | elseif &filetype == 'go' 1789 | exec "!go build %<" 1790 | exec "!time go run %" 1791 | elseif &filetype == 'mkd' 1792 | exec "!~/.vim/markdown.pl % > %.html &" 1793 | exec "!firefox %.html &" 1794 | endif 1795 | endfunc 1796 | 1797 | ``` 1798 | 1799 | 添加后保存,Fn+F5可一鍵編譯運行 1800 | 1801 | ------ 1802 | 1803 |

vim编辑时显示当前的文件名

1804 | 1805 | 命令vi .vimrc叫出vimrc 1806 | 1807 | 添加 1808 | ```set laststatus=2``` 在最底部 1809 | 1810 | ------ 1811 | 1812 |

17. vim 插件

1813 | 1814 | #### ale 語法檢查 1815 | 1816 | git hub地址 1817 | 1818 | > https://github.com/w0rp/ale 1819 | 1820 | install 1821 | 1822 | ``` 1823 | mkdir -p ~/.vim/pack/git-plugins/startgit clone --depth 1 https://github.com/w0rp/ale.git ~/.vim/pack/git-plugins/start/ale 1824 | ``` 1825 | 1826 | #### vundle 插件管理器 1827 | 1828 | 1. install 1829 | 1830 | ``` 1831 | git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim 1832 | ``` 1833 | 1834 | 2. 配置.vimrc 1835 | 1836 | ```vi .vimrc``` 1837 | 1838 | 进入后复制贴上 1839 | 1840 | ``` 1841 | set nocompatible " be iMproved, required 1842 | filetype off " required 1843 | 1844 | " set the runtime path to include Vundle and initialize 1845 | set rtp+=~/.vim/bundle/Vundle.vim 1846 | call vundle#begin() 1847 | " alternatively, pass a path where Vundle should install plugins 1848 | "call vundle#begin('~/some/path/here') 1849 | 1850 | " let Vundle manage Vundle, required 1851 | Plugin 'VundleVim/Vundle.vim' 1852 | 1853 | " The following are examples of different formats supported. 1854 | " Keep Plugin commands between vundle#begin/end. 1855 | " plugin on GitHub repo 1856 | Plugin 'tpope/vim-fugitive' 1857 | " plugin from http://vim-scripts.org/vim/scripts.html 1858 | " Plugin 'L9' 1859 | " Git plugin not hosted on GitHub 1860 | Plugin 'git://git.wincent.com/command-t.git' #可不加 1861 | " git repos on your local machine (i.e. when working on your own plugin) 1862 | Plugin 'file:///home/gmarik/path/to/plugin' #可不加 1863 | " The sparkup vim script is in a subdirectory of this repo called vim. 1864 | " Pass the path to set the runtimepath properly. 1865 | Plugin 'rstacruz/sparkup', {'rtp': 'vim/'} 1866 | " Install L9 and avoid a Naming conflict if you've already installed a 1867 | " different version somewhere else. 1868 | " Plugin 'ascenator/L9', {'name': 'newL9'} 1869 | 1870 | " All of your Plugins must be added before the following line 1871 | call vundle#end() " required 1872 | filetype plugin indent on " required 1873 | " To ignore plugin indent changes, instead use: 1874 | "filetype plugin on 1875 | " 1876 | " Brief help 1877 | " :PluginList - lists configured plugins 1878 | " :PluginInstall - installs plugins; append `!` to update or just :PluginUpdate 1879 | " :PluginSearch foo - searches for foo; append `!` to refresh local cache 1880 | " :PluginClean - confirms removal of unused plugins; append `!` to auto-approve removal 1881 | " 1882 | " see :h vundle for more details or wiki for FAQ 1883 | " Put your non-Plugin stuff after this line 1884 | ``` 1885 | 1886 | 1887 | 1888 | 之后任何要安装的插件放在#begin #end 之间 1889 | 1890 | 通常格式会是Plugin xxx/xxxxx 1891 | 1892 | 然后命令行输入 1893 | 1894 | ``` 1895 | vim +PluginIndstall +qall 1896 | ``` 1897 | 1898 | 1899 | 1900 | 1901 | 1902 | #### youcompleteme 代碼補全 1903 | 1904 | 直接利用Vundle插件管理器安装 1905 | 1906 | 1. vi .vimrc 进入vimrc配置 1907 | 2. 在call vundle#begin()以及call vundle#end() 之间 加入 1908 | 1909 | ```Plugin 'Valloric/YouCompleteMe'``` 1910 | 1911 | 3. :wq 保存跳出 1912 | 4. 进入vim 使用 :进行尾行命令 输入 PluginInstall 完成安装 1913 | 5. 执行 ```git submodule update --init --recursive``` 1914 | 1915 | 会开始下载细部文件到插件的各个文件夹中 1916 | 1917 | 6. 执行编译(完成下面两步骤) 1918 | 1919 | - ```cd ~/.vim/bundle/YouCompleteMe``` :到安装的目录下 1920 | - ```./install.py --clang-completer``` :执行install.py来进行编译安装 1921 | 1922 | 这个时候安装还没有完成, 打开cpp会出现 1923 | “No .ycm_extra_conf.py file detected, so no compile flags are available. Thus no semantic support for C/C++/ObjC/ObjC++” 1924 | 1925 | 我们要进行最后一步 1926 | 1927 | 7. 进入vimrc 下方添加 1928 | 1929 | ``` 1930 | let g:ycm_global_ycm_extra_conf='~/.vim/bundle/YouCompleteMe/third_party/ycmd/.ycm_extra_conf.py' 1931 | ``` 1932 | 1933 | 注意网上还有很多估计是旧版本的插件, 所以.ycm_extra_conf.py的档案位置不同, 新版本的路径请依照上面 1934 | 1935 | 8. 打开cpp档案, 补全功能正常运行 1936 | 1937 | 1938 | 1939 | #### vimplus 1940 | 1941 | 项目链接 https://github.com/chxuan/vimplus 1942 | 1943 | 安装介绍 https://www.cnblogs.com/highway-9/p/5984285.html 1944 | 1945 | ------ 1946 | 1947 |

18. g++ / gcc 编译器相关

1948 |

g++編譯/執行

1949 | 1950 | 1. 使用g++ 編譯cpp文件: 1951 | 1952 | - ```g++ filename.cpp``` : 自動生成a.out文件 1953 | - ```g++ filename.cpp``` -c 生成文件名 : 自動生成指定的文件名 1954 | 1955 | 2. ```./檔名``` :執行文件 1956 | 1957 | 1958 | 1959 | #### 搭配参数 1960 | 1961 | `-I` : 表示依照指定的路径搜索头文件, Ex. `-I./include/` 表示将./include/目录作为第一个寻找头文件的目录,寻找的顺序是: ./include/ --> /usr/include --> /usr/local/include 1962 | 1963 | `-L` : 指定库文件的搜索路径 Ex. `-L/lib/ -lapple` 表示到/lib/目录下寻找libapple.so库文件, 可以连接多个 1964 | 1965 | `-l` : 小写L , 指定要连接的库名称 1966 | 1967 | Ex. 输出compress 执行文件, 源文件compress.cpp 连接头文件在/home/include, -L连接库文件 libz.so, 1968 | 1969 | ```shell 1970 | g++ -o compress compress.cpp -I/home/include/ -L/lib/ -lz 1971 | ``` 1972 | 1973 | 1974 | 1975 | 1976 | 1977 | #### 连接头文件与源文件的编译方式 1978 | 1979 | 假设有三个文件包含 1980 | 1981 | **function.cpp** :源文件 1982 | 1983 | **function.h** : 头文件 1984 | 1985 | **main.cpp** : main 执行文件 1986 | 1987 | ```g++ -o out Function.cpp Function.h mian.cpp``` : 将三个文件一起编译 1988 | 1989 | 接着会生成out(这个out是可以指定名称的) 1990 | 1991 | 然后执行 ```./out``` 就能运行文件 1992 | 1993 | 1994 | 1995 | #### gcc / g++生降版本 1996 | 1997 | 以下用gcc举例, 记得gcc, g++需要同版本 1998 | 1999 | 2000 | 2001 | 1. 当前版本为7.3,需要降到4.8 2002 | 2003 | `sudo apt-get install gcc-4.8` (如果是g++ `g++-4.8`) 2004 | 2005 | 2. 装完后进入到/usr/bin目录下 2006 | 3. 输入`ls -l gcc*` 查看连接状况 2007 | 2008 | 发现gcc链接到gcc-7.0, 需要将它改为链接到gcc-4.8,方法如下: 2009 | 2010 | ```shell 2011 | sudo mv gcc gcc.backup #备份sudo ln -s gcc-4.8 gcc #利用软连接重新链接 2012 | ``` 2013 | 2014 | 完成, 可以在用`ls -l gcc*`检查一下 2015 | 2016 | 2017 | 2018 | 2019 | 2020 |

g++ 其它

2021 | 2022 | 查看g++ 版本 :`g++ -v` 2023 | 2024 | 版本安装, 例如4.8版 `sudo apt-get install gcc-4.8` 2025 | 2026 | 查看安装是否成功 `ls /usr/bin/gcc*` 2027 | 2028 | 设置优先级 `sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100` 2029 | 2030 | 查看设置结果 `sudo update-alternatives --config gcc` 2031 | 2032 | ------ 2033 | 2034 |

19. anaconda3/ Mini-Conda 安装/使用

2035 | 2036 | #### 安装miniconda3 2037 | 2038 | 下载 https://conda.io/en/latest/miniconda.html 下载完会是sh脚本 2039 | 2040 | sudo sh 安装之后 如果选择不开机自动激活 2041 | 2042 | 则需要去 /home/yourusername/miniconda3/bin/下 source activate激活一下之后就可以使用 2043 | 2044 | 2045 | 2046 | 2047 | 2048 | 2049 | 2050 | #### 虚拟环境 2051 | 2052 | 查看当前所有环境 : `conda info --env` or `conda env list` 2053 | 2054 | 创建虚拟环境 virtualEnv : `conda create -n 环境名 python=3.6 -y` 2055 | 2056 | 如果无法创建出现NotWritableError : The current user does not have write permissions to a required path. 2057 | 2058 | 2059 | 2060 | 可以改变目录owner 2061 | 2062 | ```shell 2063 | sudo chown -R 你的用户名 miniconda3///或者sudo chown -R 你的用户名 anaconda3 2064 | ``` 2065 | 2066 | 2067 | 2068 | 2069 | 2070 | 启动环境 :`source activate 环境名` 2071 | 2072 | 关闭环境 :`conda deactivate` 2073 | 2074 | 删除环境 :`conda env remove -n 环境名` 2075 | 2076 | 安装package :`conda install 包名` 用conda设置的源安装 2077 | 2078 | 安装package 方法二 : `pip install` 2079 | 2080 | 2081 | 2082 | #### 复制原来创建好的环境 2083 | 2084 | 假设有一个AAA环境, 要创建一个与AAA相同环境的BBB环境 2085 | 2086 | ```shell 2087 | conda create -n BBB --clone AAA 2088 | ``` 2089 | 2090 | 2091 | 2092 | #### 将conda 自创建的环境打包到其他服务器上使用 2093 | 2094 | 先安装conda-pack 2095 | 2096 | ``` 2097 | pip install conda-pack 2098 | ``` 2099 | 2100 | 利用这个包将想打包的环境打包 2101 | 2102 | ``` 2103 | conda pack -n 环境名 #输入之后会在当前目录下生成一个tar.gz文件 2104 | ``` 2105 | 2106 | 2107 | 2108 | 如果遇到CondaPackError:Cannot pack an environment with editable packages 2109 | 2110 | ```shell 2111 | conda pack -n 环境名 --ignore-editable-packages 2112 | ``` 2113 | 2114 | 2115 | 2116 | 2117 | 2118 | 然后在把这个文件放到需要使用环境的服务器下解压缩, 比如解压缩出来是haha文件 2119 | 2120 | ``` 2121 | source haha/bin/activate 2122 | ``` 2123 | 2124 | 激活后 环境就已经改变了 2125 | 2126 | 2127 | 2128 | #### 导出现在的环境为yaml文件 2129 | 2130 | 在想要导出的环境下 2131 | 2132 | conda导出已有环境: 2133 | `conda env export > environment.yaml` 2134 | 2135 | 环境会被保存在 environment.yaml文件中。当我们想再次创建该环境,或根据别人提供的.yaml文件复现环境时,可以: 2136 | 2137 | `conda env create -f environment.yaml` 2138 | 2139 | 就可以复现安装环境。移植过来的环境只是安装了你原来环境里用conda install等命令直接安装的包,你用pip之类装的东西没有移植过来,需要你重新安装。 2140 | 2141 | 2142 | 2143 | 2144 | 2145 | 2146 | 2147 | #### 虚拟环境路径 2148 | 2149 | `../anaconda3/envs/` 2150 | 2151 | #### 虚拟环境包的路径 2152 | 2153 | conda环境下, 查看安装的package `pip list` 2154 | 2155 | `/usr/local/anaconda3(或者是miniconda3)/envs/环境名(自己创建的)/lib/python3.x/site-packages/` 2156 | 2157 | 2158 | 2159 | #### jupyter notebook 添加conda环境到内核 2160 | 2161 | 将jupyter notebook添加 conda的虚拟环境内核, 也就是jupyter可以使用conda的环境 2162 | 2163 | 1. 启动虚拟环境 `source activate 环境名` 2164 | 2. 安装`conda install ipykernel` 2165 | 3. 将环境添加至juypter notebook中 2166 | 2167 | `python -m ipykernel install --user --name open-mmlab --display-name "python (open-mmlab)"` 2168 | 2169 | 4.启动juypter notebook > kernel > change kernel 就能看见新增的内核了 2170 | 2171 | 2172 | 2173 | #### conda 安装pytorch 2174 | 2175 | ``` 2176 | # CUDA 9.0conda install pytorch==1.0.1 torchvision==0.2.2 cudatoolkit=9.0 -c pytorch# CUDA 10.0conda install pytorch==1.0.1 torchvision==0.2.2 cudatoolkit=10.0 -c pytorch# CPU Onlyconda install pytorch-cpu==1.0.1 torchvision-cpu==0.2.2 cpuonly -c pytorch 2177 | ``` 2178 | 2179 | 参考Pytorch官方 [https://pytorch.org/get-started/previous-versions/](https://pytorch.org/get-started/previous-versions/) 2180 | 2181 | 2182 | 2183 | #### conda 换源 2184 | 2185 | 設置源的文件在 `~/.condarc` 2186 | 2187 | 所以用vim 進入編輯 2188 | 2189 | ```shell 2190 | #1 vim 进入 2191 | vim ~/.condarc 2192 | #2. 貼入清華源 2193 | 2194 | channels: 2195 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ 2196 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ 2197 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ 2198 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ 2199 | ssl_verify: true 2200 | 2201 | 2202 | #3. 如清華源失效, 可以試試中科大源 2203 | channels: 2204 | - https://mirrors.ustc.edu.cn/anaconda/pkgs/main/ 2205 | - https://mirrors.ustc.edu.cn/anaconda/pkgs/free/ 2206 | - https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/ 2207 | ssl_verify: true 2208 | 2209 | 2210 | #4. 上海交大源 2211 | channels: 2212 | - https://mirrors.sjtug.sjtu.edu.cn/anaconda/pkgs/main/ 2213 | - https://mirrors.sjtug.sjtu.edu.cn/anaconda/pkgs/free/ 2214 | - https://mirrors.sjtug.sjtu.edu.cn/anaconda/cloud/conda-forge/ 2215 | ssl_verify: true 2216 | 2217 | 2218 | #以上三个源, 泽一即可 2219 | ``` 2220 | 2221 | 经过实际测验, 以下速度飞快, 推荐使用 2222 | 2223 | ``` 2224 | channels: 2225 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ 2226 | - https://mirrors.sjtug.sjtu.edu.cn/anaconda/pkgs/main/ 2227 | - https://mirrors.sjtug.sjtu.edu.cn/anaconda/pkgs/free/ 2228 | - defaults 2229 | show_channel_urls: true 2230 | ``` 2231 | 2232 | 2233 | 2234 | 2235 | 2236 | 2237 | 2238 | ------ 2239 | 2240 |

20. git 操作指令

2241 | 2242 | 1. 配置指令如以下, Your Name 键入github名称, email键入注册github的邮箱 2243 | 2244 | ``` 2245 | git config --global user.name Your Name 2246 | git config --global user.email email@example.com 2247 | ``` 2248 | 2249 | git config命令的–global参数,用了这个参数,表示你这台机器上所有的 Git 仓库都会使用这个配置 2250 | 2251 | 2252 | 2253 | 2. 生成金钥SSH Key, 并且连敲三次回车 2254 | 2255 | ``` 2256 | ssh-keygen -t rsa -C youremail@example.com 2257 | 或者 2258 | ssh-keygen -t ed25519 -C youremail@example.com 2259 | #-t 表示用不一样的method加密 2260 | ``` 2261 | 2262 | 3. 到github上, 打开Account settings > SSH and GPG keys>点击Add SSH keys , 将id_rsa.pub或者id_ed25519.pub的内容复制并贴在空格内, title可以取名比如xxx's mac 之类的 2263 | 2264 | 2265 | 2266 | #### 建立本地仓库 2267 | 2268 | 创建一个存放程序的资料夹, 里面可以放各仓库, 例如以下 2269 | 2270 | ``` 2271 | cd 指定路径 2272 | mkdir myprogram 2273 | cd myprogram 2274 | mkdir example #这就是一个repo存在myprogram中 2275 | ``` 2276 | 2277 | 接着在example文件夹下执行git init 初始化本地仓库 就可成功创建repo仓库 2278 | 2279 | ``` 2280 | git init 2281 | ``` 2282 | 2283 | 然后将要存放的文件放进example资料夹中, 例如放了一个test.txt文件进去 2284 | 2285 | 接着我们需要对文件进行追踪, 键入, 这样如果之后档案修改过就可以追踪到 2286 | 2287 | ```git ad 2288 | git add test.txt 2289 | ``` 2290 | 2291 | 如果上传文件较多, 可以直接追踪全部 2292 | 2293 | ``` 2294 | git add --all 2295 | ``` 2296 | 2297 | 确认没有问题, 就可以commit到repo中, 例如”update the file“ 2298 | 2299 | ``` 2300 | git commit -m "updata the file" 2301 | ``` 2302 | 2303 | #### 过滤指定的文件不上传 使用.gitignore 2304 | 2305 | 在文件下新增.gitignore 2306 | 输入需要过滤的文件or目录 2307 | 2308 | ``` 2309 | test.pytest/*.log 2310 | ``` 2311 | 2312 | 2313 | 2314 | #### 把untracked file 删除 2315 | 2316 | 删除未追踪的文件 2317 | 2318 | ``` 2319 | git clean -f 2320 | ``` 2321 | 2322 | 删除未追踪的文件跟文件夹 2323 | 2324 | ``` 2325 | git clean -df 2326 | ``` 2327 | 2328 | 2329 | 2330 | 2331 | 2332 | #### 在github上创建仓库repo 2333 | 2334 | 建立好远程仓库之后, 就可以进行远程 - 本地连接 2335 | 2336 | ```git remote add origin 网址.git``` 2337 | 2338 | 2339 | 2340 | 接着就可以进行同步 push到remote repo, 注意下面的xxxx表示 branch名称 2341 | 2342 | 通常push需要输入username 和 password,就输入github的即可 2343 | 2344 | ``` 2345 | git push origin master #依照branch可替换名称 2346 | ``` 2347 | 2348 | 检查repo状态, 可以查看当前的状态 2349 | 2350 | ``` 2351 | git status 2352 | ``` 2353 | 2354 | 2355 | 2356 | 查看本地链接到远程哪一个仓库地址 2357 | 2358 | ``` 2359 | git remote show origin 2360 | ``` 2361 | 2362 | 2363 | 2364 | 2365 | 2366 | #### 克隆 clone 2367 | 2368 | 克隆可以将网上的开源库给download到自己的本地仓中例如向下面这样, 后面加上网址 2369 | 2370 | ``` 2371 | git clone https://github.com/Stephenfang51/Grad_CAM_Pytorch-1.01 2372 | ``` 2373 | 2374 | 2375 | 2376 | 克隆项目中特定的版本 2377 | 2378 | ``` 2379 | git clone -b --single-branch 2380 | ``` 2381 | 2382 | 2383 | 2384 | 更新子模块submodule 2385 | 2386 | ``` 2387 | git clone --recursive-submodule http://xxxxxxxxxxxxxx.git 2388 | ``` 2389 | 2390 | 2391 | 2392 | #### fork 原作者仓库后 如何更新 2393 | 2394 | 2395 | 2396 | #### 删除文件 2397 | 2398 | cd到repo的文件夹后, 一般的删除本地以及远程文件的方式 2399 | 2400 | ``` 2401 | git rm 文件名 2402 | ``` 2403 | 2404 | 2405 | 2406 | 如果只想删除远程而保留本地的方式 2407 | 2408 | ``` 2409 | git rm -r --cached 文件名 2410 | ``` 2411 | 2412 | 2413 | 2414 | #### 删除commit 2415 | 2416 | 参考https://www.cnblogs.com/lwcode6/p/11809973.html 2417 | 2418 | 2419 | 2420 | 2421 | 2422 | 记得最终如果要同步到remote repo都要git push一下 2423 | 2424 | #### 分支(branch) 2425 | 2426 | 部分参考http://gogojimmy.net/2012/01/21/how-to-use-git-2-basic-usage-and-worflow/ 2427 | 2428 | - 查看当前分支 `git branch` , 如果包含远程 加上`-a` 2429 | 2430 | - 开设分支 `Git branch ` 2431 | 2432 | - 切换分支 `git checkout branch-name` 2433 | 2434 | - 删除分支 `git branch -d branch-name` 2435 | 2436 | - 合并分支 2437 | 2438 | - 例如开发人员在A branch开发成功, 希望合并目前的master, 可以在A branch下输入 2439 | 2440 | `git rebase master` 该方法会基于master branch最新的commit内容在将自己A分支下新添加的内容加进去, **PS. commit 量较多建议使用rebase·** 2441 | 2442 | - `git merge` 不同于rebase, 是从master额外来条线 2443 | 2444 | - 删除远程分支 2445 | 2446 | - 先查看远程分支 `git branch -r` 找到要刪除的 2447 | 2448 | - `git branch -r -d origin/branch-name` , -r表示remote的意思, 該句主要将远程和本地的跟踪删除 2449 | 2450 | - `git push origin :branch-name` 正式删除远程分支 2451 | 2452 | 2453 | 2454 | #### 取消操作 2455 | 2456 | - 回到上一次commit时的状态 `git reset --hard HEAD `, 参数HEAD^表示目前版本的上个版本, HEAD~2则是在上一个 2457 | 2458 | 2459 | 2460 | #### 開啟Git GUI 2461 | 2462 | `gitk --all` : 可以開啟git的gui版本 2463 | 2464 | #### 更新 fetch / pull 2465 | 2466 | 本机如果有推送东西到server, 除了本地的分支, 远程分支也会记录一份在本机上, 一样也是有HEAD / master分支, 但会在前面加行远程节点origin, 变成`origin/HEAD` 以及 `origin/master` 2467 | 2468 | 如果线上有更新的部分, 但是本机并没有, 则执行fetch时候, 会自动从远程抓一份下来, 并且更新origin的部分 2469 | 2470 | ``` 2471 | git fetch 2472 | ``` 2473 | 2474 | 既然`origin/master`是从`master`分支出去且更新, 如果想要master更新`origin/master`, 就需要merge 2475 | 2476 | ``` 2477 | git merge origin/master 2478 | ``` 2479 | 2480 | 执行之后就会将master 来回到跟`origin/master`同个位置 2481 | 2482 | 2483 | 2484 | 而 git pull = git fetch + git merge 2485 | 2486 | ``` 2487 | git pull --rebase 2488 | ``` 2489 | 2490 | #### 克隆特定的子文件夹 2491 | ``` 2492 | 1. mkdir test 2493 | 2. cd test 2494 | 3. git init 2495 | 4. git remote add origin http:xxxx.git 2496 | 5. git config core.sparsecheckout true 2497 | 6. echo "文件夹路径" >> .git/info/sparse-checkout 2498 | 7. git pull origin master 2499 | ``` 2500 | 2501 | 2502 | 2503 | 2504 | #### 问题解决 2505 | 2506 | **错误信息 : error: failed to push some refs to** 2507 | 2508 | 问题在于远程版本比本地的还要新, 需要先pull回来做更新才能重新push 2509 | 2510 | 先进行更新, 然後合併 2511 | 2512 | 1. `git fetch ` 2513 | 2. `git merge ` 2514 | 2515 | 然后在push即可 2516 | 2517 | 2518 | 2519 | **错误信息 : Changes not staged for commit:** 2520 | 2521 | 因為要提交的提交的档案尚未track, 需要对该档案 git add 档案名, 然后在重新执行commit, push等 2522 | 2523 | 2524 | 2525 | **错误信息 : Git修改密码后命令行push代码报“fatal: Authentication failed for ** 2526 | 2527 | ```shell 2528 | git config --system --unset credential.helpergit config --global credential.helper store 2529 | ``` 2530 | 2531 | 然后在 git push 就会要求输入使用者的账号和密码 2532 | 2533 | 2534 | 2535 | 2536 | ------ 2537 | 2538 |

21. cuda 使用

2539 | 2540 | 查看cuda版本 2541 | 2542 | `cat /usr/local/cuda/version.txt` 2543 | 2544 | 查看NVCC版本 2545 | 2546 | `nvcc -V` 2547 | 2548 | 查看cudnn 版本 2549 | 2550 | `cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2` 2551 | 2552 | 2553 | 2554 | **cudnn 下载** : 依照对应的CUDA下载 2555 | 2556 | [https://developer.nvidia.com/rdp/cudnn-download](https://developer.nvidia.com/rdp/cudnn-download) 2557 | 2558 | 2559 | 2560 | ------ 2561 | 2562 |

22. python pip 安装及管理

2563 | 2564 | #### pip 包管理升级 2565 | 2566 | ``` 2567 | pip install --upgrade pip 2568 | ``` 2569 | 2570 | 如果是升级pip3 2571 | 2572 | ``` 2573 | pip3 install --upgrade pip 2574 | ``` 2575 | 2576 | #### 换源加速 (清华或者阿里) 2577 | 2578 | 推荐使用阿里源, 速度飞快 2579 | 2580 | 1. 清华源 2581 | 2582 | `pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple` 2583 | 2584 | 2. 阿里源 2585 | 2586 | ``` 2587 | pip config set global.index-url http://mirrors.aliyun.com/pypi/simple 2588 | ``` 2589 | 2590 | 在进行包的安装即可飞速 2591 | 2592 | 如果出现 找不到config 这个command , 说明pip的版本太旧了, 利用`pip install --upgrade pip` 来升级 2593 | 2594 | 2. 也可以找到pip.conf这个文件用vim修改源 2595 | 2596 | Pip.conf这个文件通常在 /home/yourname/.config/pip/pip.conf 2597 | 2598 | 2599 | 2600 | PS.如果有些比较不知名的包在清华源无法下载, 可以用 2601 | 2602 | `pip install [module] -i https://pypi.org/simple` 2603 | 2604 | 临时将链接替换回默认镜像 2605 | 2606 | 2607 | 2608 | #### 阿里镜像源加速 2609 | 2610 | 在install后面添加例如 2611 | 2612 | ``` 2613 | pip install torch==1.5 -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com 2614 | ``` 2615 | 2616 | 2617 | 2618 | 2619 | 2620 | #### ReadTimeoutError(“HTTPSConnectionPool) 2621 | 2622 | 遇到这样的问题通常就是国外源从国内访问太慢 2623 | 2624 | 建议可以直接下载包的whl 或者 tar.gz压缩包到本地进行安装 2625 | 2626 | ``` 2627 | sudo pip3 install xxxxx.whl 2628 | ``` 2629 | 2630 | 或者是将压缩包解压缩后进行安装 2631 | 2632 | ``` 2633 | tar -xvzf xxxxx.tar.gzcd xxxxxxsudo python3 setup.py install 2634 | ``` 2635 | 2636 | 即可完成安装 2637 | 2638 | 2639 | 2640 | #### Ubuntu下 安装常用的包 2641 | 2642 | ##### matplotlib 2643 | 2644 | ``` 2645 | sudo apt-get python3-matplotlib 2646 | ``` 2647 | 2648 | 2649 | 2650 | 2651 | 2652 | 2653 | 2654 | 2655 | 2656 | 2657 | 2658 | ------ 2659 | 2660 |

23. Jupyter notebook SSH 远程连接

2661 | 2662 | 1. 首先确保本机与远程主机都已经安装且可以正常使用jupyter notebook 2663 | 2. 待完成。。。 2664 | 2665 | ------ 2666 | 2667 |

24. Docker 安装及指令

2668 | 2669 | #### 安装 2670 | 2671 | 参考 https://segmentfault.com/a/1190000022374119 2672 | 2673 | 安装如下需要的包 2674 | 2675 | ``` 2676 | sudo apt-get update 2677 | sudo apt-get install docker-ce docker-ce-cli containerd.io 2678 | ``` 2679 | 2680 | 利用deb的方式安装 2681 | 2682 | 进入右侧网址 https://download.docker.com/linux/ubuntu/dists/ 2683 | 2684 | 进去之后点选Ubuntu版本, bionic 是ubuntu 18.04 2685 | 2686 | 然后pool/stable/amd64,选择docker 19.03的版本 (可支持GPU) 2687 | 2688 | 下载好之后安装 2689 | 2690 | ``` 2691 | sudo dpkg -i dokcer-ce_19.03.9_3-0-ubuntu-bionic_amd64.deb 2692 | ``` 2693 | 2694 | 执行以下进行测试看安装成功了 2695 | 2696 | ``` 2697 | sudo docker run hello-world 2698 | ``` 2699 | 2700 | 2701 | 2702 | 2703 | 2704 | #### 使用指南 2705 | 2706 | 需要了解三个概念 2707 | 2708 | 1. 镜像 images 2709 | 2. 容器 container 2710 | 3. 仓库 repository 2711 | 2712 | ##### 镜像images 2713 | 2714 | 镜像构建时, 是一层一层的构建, 前一层的镜像为当前层的基础, 任一层的改变只发生在当前层, 每一层只包含该层需要添加的东西, 任何额外的层都应该在构建结束前清除 2715 | 2716 | 官方的 [Docker hub](https://hub.docker.com/) 存储大量的images可以pull 2717 | 2718 | ##### 容器container 2719 | 2720 | 容器可以有自己root文件系统, 网络配置, 有自己独立的空间, 跟虚拟机类似, 每创建容器时是以镜像为基础添加一层 2721 | 2722 | 2723 | 2724 | ##### 镜像加速器 2725 | 2726 | 主要从官方的Docker hub pull镜像下来实在太慢, 所以需要其他厂商提供的加速镜像 2727 | 2728 | 主要为服务商提供类似于Docker Hub 2729 | 2730 | - Azure 中国镜像 https://dockerhub.azk8s.cn 2731 | - 网易云 https://hub-mirror.c.163.com 2732 | - 七牛云加速器 https://reg-mirror.qiniu.com 2733 | 2734 | 加速器地址 2735 | 2736 | ```json 2737 | { 2738 | "registry-mirrors": [ 2739 | "https://dockerhub.azk8s.cn", 2740 | "https://hub-mirror.c.163.com", 2741 | "https://reg-mirror.qiniu.com" 2742 | ] 2743 | } 2744 | ``` 2745 | 2746 | 2747 | 2748 | 1. mac 桌面版 添加加速器 2749 | 2750 | 在任务栏点击 Docker for mac 应用图标 -> Perferences... -> Daemon -> Registry mirrors。在列表中填写加速器地址即可。修改完成之后,点击 Apply & Restart 按钮,Docker 就会重启并应用配置的镜像地址了。 2751 | 2752 | 2753 | 2754 | 2. Linux 添加加速器(亲测有效) 2755 | 2756 | 推荐用阿里云镜像加速 2757 | 2758 | 登入阿里云(用支付宝登入)-> 搜寻镜像服务器 -> 控制台->左侧找到镜像加速器, 会看到属于自己的镜像地址 2759 | 2760 | ```shell 2761 | cd /etc/docker 2762 | vim daemon.json 2763 | 2764 | #添加下面内容, 文件必须符合json规范 2765 | { 2766 | "registry-mirrors": [ 2767 | "这边贴上阿里镜像加速器的地址" 2768 | ] 2769 | } 2770 | 2771 | #然后wq保存 2772 | 2773 | ``` 2774 | 2775 | reload一下docker 2776 | 2777 | ```shell 2778 | sudo systemctl daemon-reload 2779 | sudo systemctl restart docker 2780 | ``` 2781 | 2782 | 然后在pull images 就会发现速度提升非常多 2783 | 2784 | **从Dockerfile 创造镜像image** 2785 | 2786 | 与Dockerfile同一层目录下 依照 `docker build -t name:tag ` 2787 | 2788 | Ex. 例如建造onnx-tensorrt的镜像, 就先clone下项目的repo, 然后确认repo中有dockerfile 2789 | 2790 | ``` 2791 | #务必加上sudo 2792 | sudo docker build -t ubuntu/onnx2trt:v5.0 2793 | ``` 2794 | 2795 | 接着就会如下开始build 2796 | 2797 | ``` 2798 | Sending build context to Docker daemon 3.977MB 2799 | Step 1/16 : FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04 2800 | 9.0-cudnn7-devel-ubuntu16.04: Pulling from nvidia/cuda 2801 | 976a760c94fc: Pull complete 2802 | c58992f3c37b: Pull complete 2803 | 0ca0e5e7f12e: Pull complete 2804 | f2a274cc00ca: Pull complete 2805 | 708a53113e13: Pull complete 2806 | 371ddc2ca87b: Pull complete 2807 | f81888eb6932: Pull complete 2808 | 19dbd9dd59d6: Pull complete 2809 | e07d92c8415d: Extracting [======================> ] 276.3MB/615.8MB 2810 | aa4c26baf056: Download complete 2811 | ``` 2812 | 2813 | 2814 | 2815 | **Pull 镜像下来** 2816 | 2817 | ``` 2818 | docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] 2819 | ``` 2820 | 2821 | 假设从Docker hub pull一个ubuntu的镜像 则`docker pull ubuntu:18.04` 2822 | 2823 | 仓库名其实是两段式名称<用户名/软件名>, 如果没给用户名就是默认官方 library/ubuntu 2824 | 2825 | 预设的registry是Docker Hub, 所以指令等同于 2826 | 2827 | `docker pull registry.hub.docker.com/ubuntu:latest` 2828 | 2829 | 2830 | 2831 | 2832 | 2833 | **Run 运行容器** 2834 | 2835 | 例如启动bvlc/caffe:cpu这个镜像, cpu这tag一定要指定 2836 | 2837 | ``` 2838 | docker run -it bvlc/caffe:cpu 2839 | ``` 2840 | 2841 | 2842 | 2843 | 表示启动caffe cpu版本 并且使用ipython交互式 2844 | 2845 | ``` 2846 | docker run -it bvlc/caffe:cpu ipython 2847 | ``` 2848 | 2849 | 2850 | 2851 | docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 2852 | 2853 | 常用options 2854 | 2855 | - -i : 以交互模式运行容器,通常与 -t 同时使用; 2856 | - -t : 为容器重新分配一个伪输入终端,通常与 -i 同时使用; 2857 | 2858 | 2859 | 2860 | 2861 | 2862 | #### 镜像images操作篇 2863 | 2864 | `docker image ls` 查看目前本机有的images 2865 | 2866 | ``` 2867 | REPOSITORY TAG IMAGE ID CREATED SIZEbvlc/caffe cpu 0b577b836386 18 months ago 1.64GB 2868 | ``` 2869 | 2870 | `docker image ls -a` 可以看到中间层镜像, 也就是无标签镜像, 不需要删除 2871 | 2872 | `docker image rm xxxxx/xxxxxx` : 删除不需要的镜像 2873 | 2874 | `docker commit containerID newname` : 每次在容器中想要保存目前的内容,创建成新的镜像, 可以使用, newname可以自己定义 2875 | 2876 | #### container 容器常用指令 2877 | 2878 | **查看容器container** 2879 | 2880 | `docker ps -a`或是`docker container ls -a` : 可以查看到终止状态的容器,前者为旧的指令 2881 | 2882 | `docker container ls` :可以查看运行中的容器 2883 | 2884 | 2885 | 2886 | **跳出运行中的容器** 2887 | 2888 | `ctrl + D` : 可以跳出正在执行中的容器, 容器状态为EXIT终止, 但是并没有被删除 2889 | 2890 | 2891 | 2892 | **重新进入跳出的容器** 2893 | 2894 | 跳出容器之后, 容器会是exit的状态, 需要重新启动start 2895 | 2896 | 启动的方式 2897 | 2898 | `docker start ` : 启动停止的容器 2899 | 2900 | 接着进入容器 2901 | 2902 | `docker attach ` :进入容器, 离开后会终止 2903 | 2904 | `docker exec [option] bash` : 该方式进入容器, 离开后容器不会终止 2905 | 2906 | 2907 | 2908 | **保存镜像到本地(save)与 载入(load)** 2909 | 2910 | `docker save -o images-name.tar images-name:images tag` : 会将images 保存到本地 2911 | 2912 | Images_name.tar 可以自定义 2913 | 2914 | `docker load --input images-name.04.tar` : 将保存在本地的读取载入 2915 | 2916 | **导入与导出容器** 2917 | 2918 | `docker export /containerID/ > test.tar ` : 将某个容器导出为test.tar 2919 | 2920 | `cat test.tar | docker import - test/test:v1.0` : 透过Import将一个容器导入到镜像库 2921 | 2922 | `docker import https://www.xxxxx.tgz example/imagerepo` : 透过url导入 2923 | 2924 | 2925 | 2926 | **PS import 与 load区别在于容** 2927 | 2928 | 容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状 2929 | 态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入 2930 | 时可以重新指定标签等元数据信息。 2931 | 2932 | 2933 | 2934 | #### 本机传输文件到容器 2935 | 2936 | ``` 2937 | sudo docker cp 本地文件路径 容器ID:容器路径 2938 | ``` 2939 | 2940 | 2941 | 2942 | 2943 | 2944 | 2945 | 2946 | **其他常用** 2947 | 2948 | `docker ps -a` 查看正在运行中的container 2949 | 2950 | `docker stop ` : 停止指定的docker 2951 | 2952 | `docker stop $(docker ps -aq)` : 停止所有的容器 2953 | 2954 | `docker rm` 删除不需要的容器, 容器必须是停止状态 2955 | 2956 | `docker rm $(docker ps -q -a)` 删除所有已经停止的容器 2957 | 2958 | `docker container prune -f` 删除所有已经停止的容器 2959 | 2960 | `docker image rm ` 删除镜像, 确保删除之前容器已经移除, 通常image id 只需要取前三位就可以判别 2961 | 2962 | `docker image rm $(docker image ls -q xxxxx)` 可以删除所有仓库名为xxxxx的 2963 | 2964 | `docker system df`:查看docker 占用本地硬盘的状况 2965 | 2966 | `docker attach container-id`: 可以重新进入运行在后台的容器 2967 | 2968 | `dokcer tag 镜像ID 修改后的镜像名:修改后的tag` : 重新修改images 2969 | 2970 | `docker rename 容器ID 修改后的容器名:修改后的容器tag` : 重新修改containers 2971 | 2972 | `docker --version` : 查看docker 版本 2973 | 2974 | 2975 | 2976 | **从容器中复制文件到宿主机** 2977 | 2978 | docker cp <容器ID>:/文件路径/文件<这里有空格>/宿主机保存路径 2979 | 2980 | 2981 | 2982 | **保存修改后的镜像** 2983 | 2984 | 1. [root@xxxxxxxx /]# 中的xxxxxx是产生的容器ID 2985 | 2. 将容器打包成镜像` docker commit xxxxxxxxxxx 镜像名称`, 则会自动存储好镜像 2986 | 3. 如果要删除旧镜像, 会有依赖关系, 必须将新的镜像先save出来到本地, 然后将新与旧镜像删除之后, 在load进刚刚save到本地的镜像 2987 | 2988 | 2989 | 2990 | 参考https://my.oschina.net/u/2937605/blog/1797218 2991 | 2992 | 2993 | 2994 | 2995 | 2996 | **基于深度学习的docker image** 2997 | 2998 | Deepo: 2999 | 3000 | `docker pull ufoym/deepo:cpu` 3001 | 3002 | 3003 | 3004 | **在docker上安装jupyter 与本地端浏览器上使用** 3005 | 3006 | 1. 先在镜像上安装好jupyter notebook: `pip install jupyter` 3007 | 3008 | 然后执行`jupyter notebook --generate-config` 生成配置文件(不确定是否完全需要) 3009 | 3010 | 2. 安装好之后记得重新保存一个镜像 3011 | 3012 | 3. 启动刚刚保存好的镜像 启动容器的方式必须向下面这样 3013 | 3014 | `docker run -it -p 8888:8888 镜像名称` 3015 | 3016 | 加了一个 -p 表示将本机的port端口导入容器中的port端口, 左边为本机, 右边为容器的 3017 | 3018 | 4. 在容器中启动jupyter notebook 3019 | 3020 | ```py 3021 | jupyter notebook --ip 0.0.0.0 --port 8888 --no-browser --allow-root 3022 | ``` 3023 | 3024 | 终端会显示例如下面地址 3025 | 3026 | http://127.0.0.1:8888/?token=2dd74bc68a1a2b6aabab07b34c678f0b6ef0156f6afc13cf 3027 | 3028 | 在本地浏览器可打开 3029 | 3030 | ps.参考[https://medium.com/@jihung.mycena/docker-%E5%BB%BA%E7%AB%8B-jupyter-container-8084748e2f33 3031 | 3032 | 3033 | 3034 | **问题参考** 3035 | 3036 | 如果遇到Jupyter notebook启动kernel时 发生 3037 | 3038 | ``` 3039 | ImportError: cannot import name 'create_prompt_application' 3040 | #注意该报错会显示在终端, jupyter notebook的服务器无法成功连接 3041 | ``` 3042 | 3043 | 表示 ipython 和 prompt-toolkit 版本匹配有问题 3044 | 3045 | 解决如下 3046 | 3047 | 1.重新安装ipython 3048 | 3049 | ``` 3050 | sudo pip3 uninstall ipythonsudo pip3 install ipython 3051 | ``` 3052 | 3053 | 2. 重新强制安装prompt-toolkit 3054 | 3055 | ``` 3056 | sudo pip3 install 'prompt-toolkit<2.1.0,>=2.0.0' --force-reinstall 3057 | ``` 3058 | 3059 | 3. 执行完毕后记得存储镜像 3060 | 3061 | ------ 3062 | 3063 |

25. Shell 脚本语法与执行

3064 | 3065 | - 编写脚本时 第一行指定解释器 `#! /bin/bash` 3066 | - `chmod +x xxxxx.sh ` 加上x 表示为sh文件添加可执行(excute) 权限, 否则无法执行 3067 | 3068 | ##### 变量Variable 3069 | 3070 | - `variable=123 ` 变量赋值时, 等号左右没有空格 3071 | - `${variable}` 使用变量时可以加{} 3072 | - 变量 `$0` 表示sh脚本本身 3073 | - 变量`$#` 返回参数数量 3074 | 3075 | ##### 传参 3076 | 3077 | - `$1, $2, $3....$n` 表示传入的参数, 并且依照顺序, 例如 ./test.sh A B C, 则`$1`就是A `$2`就是B 3078 | 3079 | ##### 条件字句 if 3080 | 3081 | ```bash 3082 | if condition 3083 | 3084 | then 3085 | command1 3086 | command2 3087 | ... 3088 | commandN 3089 | fi 3090 | ``` 3091 | 3092 | ##### if else 3093 | 3094 | ```bash 3095 | if condition 3096 | then 3097 | command1 3098 | command2 3099 | ... 3100 | commandN 3101 | else 3102 | comandfi 3103 | ``` 3104 | 3105 | ##### 整数之间判断 3106 | 3107 | | -eq | 兩數值相等 (equal) | 3108 | | ---- | :------------------------------------- | 3109 | | -ne | 兩數值不等 (not equal) | 3110 | | -gt | n1 大於 n2 (greater than) | 3111 | | -lt | n1 小於 n2 (less than) | 3112 | | -ge | n1 大於等於 n2 (greater than or equal) | 3113 | | -le | n1 小於等於 n2 (less than or equal) | 3114 | 3115 | example: 3116 | 3117 | ```bash 3118 | if [ "$#" -ne 1 ]; then #表示如果输入的参数数量 不为1, 则echo..... 3119 | echo "Usage: $0 " 3120 | exit 3121 | ``` 3122 | 3123 | 3124 | 3125 | 3126 | 3127 | #### script的执行 3128 | 3129 | 1. `source` 3130 | 3131 | - Source 是在当前的shell中执行文件, 也就是在父程序中执行 3132 | - 可简写为 `.` , ex : `. test.sh`, 主要中间有空格 3133 | - 让某些指令写入 `~/.bashrc`时候, 就必须使用`source ~/.bashrc` 3134 | 3135 | 2. `bash` / `sh` / `./` 3136 | 3137 | 是在当前shell中的child shell中执行脚本, 并且不会影响到父shell 3138 | 3139 | `./`通常权限不够, 需要透过chmod添加 3140 | 3141 | 3142 | 3143 | 3144 | 3145 | ------ 3146 | 3147 |

26. python 安装基于Linux

3148 | 3149 | 参考https://blog.csdn.net/baidu_37973494/article/details/88324236?utm_medium=distribute.pc_relevant.none-task-blog-baidulandingword-7&spm=1001.2101.3001.4242 3150 | 3151 | ```cbash 3152 | # wget https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tgz 3153 | # tar -xzvf Python-3.7.2.tgz 3154 | # cd Python-3.7.2 3155 | # ./configure --enable-optimizations 3156 | # make 3157 | # make install 3158 | ``` 3159 | 3160 | 3161 | 3162 | 基于Ubuntu安装, 参考[这里](https://blog.csdn.net/u014775723/article/details/85213793?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase) 3163 | 3164 | **常见问题** 3165 | 3166 | 如果安装遇到zlib 问题 可以 3167 | 3168 | ```shell 3169 | sudo apt-get install zlib* 3170 | ``` 3171 | 3172 | 接着可能遇到 3173 | 3174 | ``` 3175 | ModuleNotFoundError: No module named '_ctypes' 3176 | make: *** [install] Error 1 3177 | ``` 3178 | 3179 | 可以 3180 | 3181 | ``` 3182 | sudo apt-get install --reinstall zlibc zlib1g zlib1g-dev sudo apt-get install libffi-dev libssl-dev libreadline-dev -y 3183 | ``` 3184 | 3185 | 然后重新configure, 编译 3186 | 3187 | ------ 3188 | 3189 |

27. onnx (Open Neural Network Exchange) 模型相关

3190 | 3191 | - Netron 3192 | - 在线导入onnx模型可视化 [lutzroeder](https://lutzroeder.github.io/netron/) 3193 | - [lutzroeder github](https://github.com/lutzroeder/netron) 3194 | - onnx - tensorrt : 将onnx转换为tensorrt 3195 | - [official onnx-tensorrt github](https://github.com/onnx/onnx-tensorrt) 3196 | 3197 | ------ 3198 | 3199 |

28. PytorchToCaffe

3200 | 3201 | - [PytorchToCaffe](https://github.com/xxradon/PytorchToCaffe) 3202 | 3203 | 3204 | 3205 |

29. TensorboardX使用

3206 | 3207 | 参考 https://stackoverflow.com/questions/37987839/how-can-i-run-tensorboard-on-a-remote-server 3208 | 3209 | 这边具体如何使用看官方文档 3210 | 3211 | 只教安装 3212 | 3213 | ``` 3214 | pip3 install tensorboardX 3215 | pip3 install tensorflow-gpu 3216 | ``` 3217 | 3218 | 如果要查看, terminal 输入 3219 | 3220 | ``` 3221 | tensorboard --logdir "这边输入记录的路径" 3222 | ``` 3223 | 3224 | 如果要在服务器上使用,在服务器上启动tensorboardX 并在本地显示的话 3225 | 3226 | 在本地用以下指令链接服务器 3227 | 3228 | ```shell 3229 | ssh -L 16006:127.0.0.1:6006 username@hot -p 端口 3230 | #(ssh -L 本地端口:本地IP:远程端口 远程服务器用户名@远程服务器ip -p 服务器端口) 3231 | ``` 3232 | 3233 | 然后在本地浏览器打开, 就能显示tensorboard界面 3234 | 3235 | ``` 3236 | http://localhost:16006 3237 | ``` 3238 | 3239 | 3240 | 3241 | ------ 3242 | 3243 |

Linux 程序安装

3244 | 3245 | #### 安装Ubuntu 教程 3246 | 3247 | https://blog.csdn.net/baidu_36602427/article/details/86548203?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2 3248 | 3249 | #### 换成清华源 3250 | 3251 | 安装完Ubuntu系统之后, 执行以下命令下载一个文件到本地 3252 | 3253 | ```shell 3254 | wget https://tuna.moe/oh-my-tuna/oh-my-tuna.py 3255 | ``` 3256 | 3257 | 下载完之后 python执行 3258 | 3259 | 接着pip 的源也默认变成清华的 3260 | 3261 | 3262 | 3263 | #### 将apt源换成阿里源 3264 | 3265 | 如果不用清华源, 可以如下方式改成阿里 3266 | 3267 | 参考https://blog.csdn.net/u012308586/article/details/102953882 3268 | 3269 | 1. 备份原始源文件source.list 3270 | 3271 | ```shell 3272 | sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 3273 | ``` 3274 | 3275 | 2. 修改源文件sources.list 3276 | 3277 | ``` 3278 | sudo chmod 777 /etc/apt/sources.list #更改文件权限使其可编辑 3279 | sudo vim /etc/apt/sources.list #记得先安装vim 3280 | ``` 3281 | 3282 | ```shell 3283 | deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse 3284 | 3285 | deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse 3286 | 3287 | deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse 3288 | 3289 | deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse 3290 | 3291 | deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse 3292 | 3293 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse 3294 | 3295 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse 3296 | 3297 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse 3298 | 3299 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse 3300 | 3301 | deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse 3302 | ``` 3303 | 3304 | 3305 | 3306 | 3. 最后更新一下源 3307 | 3308 | ```shell 3309 | sudo apt update 3310 | ``` 3311 | 3312 | 3313 | 3314 | 3315 | 3316 | #### Ubuntu 安装chrome 3317 | 3318 | 对于64位版本可以使用如下链接下载: 3319 | 3320 | wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 3321 | 3322 | 然后 `sudo dpkg -i google-chrome-stable_current_amd64.deb` 3323 | 3324 | 就安装好了 3325 | 3326 | 3327 | 3328 | #### Ubuntu 识别 移动硬盘 exFAT格式 3329 | 3330 | 刚装好的ubuntu必须按照插件才能识别移动硬盘 3331 | 3332 | ``` 3333 | sudo apt-get install exfat-utils 3334 | ``` 3335 | 3336 | 3337 | 3338 | #### 安装pip or pip3 3339 | 3340 | ```shell 3341 | sudo apt-get install python-pip 3342 | 3343 | sudo apt-get install python3-pip 3344 | ``` 3345 | 3346 | 3347 | 3348 | 3349 | 3350 | #### 安装openCV 3351 | 3352 | 先安装各种依赖 3353 | 3354 | ```shell 3355 | sudo apt-get install cmake libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev 3356 | sudo apt-get install libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff5-dev libdc1394-22-dev # 处理图像所需的包 3357 | sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev liblapacke-dev 3358 | 3359 | sudo apt-get install libxvidcore-dev libx264-dev # 处理视频所需的包 3360 | sudo apt-get install ffmpeg 3361 | 3362 | sudo apt-get install libcanberra-gtk-module 3363 | 3364 | 3365 | ``` 3366 | 3367 | clone github上的openCV 从源码编译安装 3368 | 3369 | git clone https://github.com/opencv/opencv.git 3370 | 3371 | 3372 | 3373 | 接着键入以下指令, 记得在第五行的地方, 最后是库文件的安装位置, 也可以是usr/local 这是默认的位置 3374 | 3375 | ```shell 3376 | cd opencv //切换到目录下 3377 | mkdir build 3378 | cd build //切换到该文件夹 3379 | //配置输出的参数 3380 | cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/user/local .. 3381 | sudo make //编译, 这一步会花比较长的时间 3382 | sudo make install //安装 3383 | sudo ldconfig //更新动态库 3384 | ``` 3385 | 3386 | 最终如果cmakelist中find_package(OpenCV REQUIRED)找不到opencv时候 3387 | 3388 | 要以下主动设置路径 3389 | 3390 | ``` 3391 | set(OpenCV_DIR /path/to/opencv-master/build) 3392 | ``` 3393 | 3394 | 3395 | 3396 | 移除opencv的方法 3397 | 3398 | 去原先build的文件夹中, 执行sudo make uninstall, 就会自动删除系统路径下lib里面的库文件 3399 | 3400 | 3401 | 3402 | 问题1 : 3403 | 3404 | 如果在cmake 的地方出现 3405 | 3406 | ``` 3407 | ICV: Failed to download ICV package: ippicv_linux_20151201.tgz. 3408 | ``` 3409 | 3410 | 到链接: https://pan.baidu.com/s/1tUn4so6BZc8MdVz0FbtWLA 提取码: sktn 3411 | 3412 | 下载ippicv_linux_20151201.tgz 3413 | 3414 | 并替换 3415 | 3416 | `./opencv-3.2.0/3rdparty/ippicv/downloads/linux-808b791a6eac9ed78d32a7666804320e`下的文件 3417 | 3418 | 3419 | 3420 | 问题2: 3421 | 3422 | 因为cuda9 不支持opencv3.2 所以参考以下做法 3423 | 3424 | https://blog.csdn.net/qq_39315153/article/details/103282762?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-1 3425 | 3426 | #### 安装Cmake 3427 | 3428 | [官网](https://cmake.org/download/)下载cmake-3.15.3.tar.gz 3429 | 3430 | 安装之前先安装 3431 | 3432 | ``` 3433 | sudo apt-get isntall libssl-dev 3434 | ``` 3435 | 3436 | 3437 | 3438 | ```shell 3439 | tar -xvzf cmake-3.15.3.tar.gz 3440 | cd cmake-3.15.3 3441 | ./bootstrap #执行引导文件 3442 | #该命令执行需要一定时间,请耐心等待。成功执行结束之后,末尾提示:CMake has bootstrapped. Now run make. 3443 | make 3444 | sudo make install 3445 | cmake --version 3446 | ``` 3447 | 3448 | 3449 | 3450 | #### 通用 3451 | 3452 | ld转载器 3453 | 3454 | Linux 使用这个ld-linux.so*(虚拟机x86的ubuntu 是使用ld-linux.so2)中的来装载(其实这只是一个链接)其他库。所以这个库必须放在 linux中/lib下。对于其他,通常我们共享库放在/lib这个路径下,而且也是系统默认的搜索路径。 3455 | 3456 | Linux共享库的搜索路径先后顺序: 3457 | 1、编译目标代码时指定的动态库搜索路径:在编译的时候指定-Wl,-rpath=路径 3458 | 2、环境变量LD_LIBRARY_PATH指定的动态库搜索路径 3459 | 3、配置文件/etc/ld.so.conf中指定的动态库搜索路径 3460 | 4、默认的动态库搜索路径/lib 3461 | 5、默认的动态库搜索路径 /usr/lib 3462 | 3463 | 3464 | 3465 | 安装完新的库之后, 要让程序搜寻库之前可以先 3466 | 3467 | ```shell 3468 | vim /etc/ld.so.conf #进入 3469 | /usr/local/lib #添加这行进去, 因为开源库安装后都会放到这个下面 3470 | sudo ldconfig -v #进行一下更新 3471 | ``` 3472 | 3473 | 3474 | 3475 | #### google protocol buffer 3476 | 3477 | 1. 安装参考 : https://www.codetd.com/article/5545049 3478 | 2. 去official github https://github.com/protocolbuffers/protobuf 从release中找到要的版本下载source code版本, zip 或者tar档案 3479 | 3. 注意安装3.8左右以上, 如果从源码安装, 需要额外下载googletest, 否则会报错 3480 | 1. 参考· https://www.twblogs.net/a/5c9bf5e2bd9eee73ef4b1238 3481 | 2. https://github.com/google/googletest/releases/tag/release-1.8.1 3482 | 3. 将下载好的googletest解压缩之后, 在丢进protobuf里面的thirdparty文件夹中, 并且重新将googletest..xxxx 命名为googletest就好 3483 | 4. `./autogen.sh` 重新开始接下来参考第一点安装参考继续就可以 3484 | 3485 | PS. 注意3.0.0以上版本需要用autogen.sh, 不能直接./configure 3486 | 3487 | 卸载 : 3488 | 3489 | ```shell 3490 | sudo apt-get remove libprotobuf-dev 3491 | 3492 | which protoc #找到路径 3493 | rm -rf /path/to/protoc #删除路径下的protoc 3494 | ``` 3495 | 3496 | 3497 | 3498 | 3499 | 3500 | 3501 | 3502 | 3503 | 3504 | 3505 | 3506 | ------ 3507 | 3508 |

others

3509 | 3510 | #### 查看版本 3511 | 3512 | | 程序 | Command | 3513 | | --------- | ------------------------------------------------------ | 3514 | | Ubuntu | `lsb_release -a` / `uname -a` / `cat /proc/version` | 3515 | | Cmake | `cmake --version` | 3516 | | openCV | `pkg-config --modversion opencv` | 3517 | | TensorRT | `dpkg -l grep TensorRT` | 3518 | | protobuf | `protoc --version` | 3519 | | g++ / gcc | `g++ --version` | 3520 | 3521 | 3522 | 3523 | #### 推荐好用 3524 | 3525 | Sourcegraph : chrome浏览器插件, 可以用来浏览github 项目代码 3526 | 3527 | 3528 | 3529 | #### 查看Nvidia GPU 使用率 3530 | 3531 | Nvidia自带了一个nvidia-smi的命令行工具,会显示显存使用情况 3532 | 3533 | ``` 3534 | nvidia-smi 3535 | ``` 3536 | 3537 | 如果想不间断持续监控可以使用watch 指令 3538 | 3539 | 基础用法: 3540 | 3541 | ``` 3542 | watch [options] command 3543 | ``` 3544 | 3545 | 最常用的参数是 -n, 后面指定是每多少秒来执行一次命令。 3546 | 3547 | 监视显存:我们设置为每 3s 显示一次显存的情况: 3548 | 3549 | ``` 3550 | watch -n 3 nvidia-smi 3551 | ``` 3552 | 3553 | 如果想释放显存 如下步骤 3554 | 3555 | 1. 先找到占用GPU的进程 3556 | 3557 | ``` 3558 | fuser -v /dev/nvidia* 3559 | ``` 3560 | 3561 | 2. 然后找到以后 比如是PID 12345 3562 | 3563 | ``` 3564 | kill -9 12345 3565 | ``` 3566 | 3567 | 3568 | 3569 | ### ffmpeg 使用 3570 | 3571 | 参考 https://c7sky.com/common-ffmpeg-commands.html 3572 | 3573 | 参考 http://www.mikewootc.com/wiki/sw_develop/multimedia/ffmpeg_app_param.html 3574 | 3575 | [FFmpeg](http://ffmpeg.org/) 是一个处理视频和音频内容的开源工具库,可以实现编码、解码、转码、流媒体和后处理等服务 3576 | 3577 | #### 转换格式 3578 | 3579 | Ex. 将mov转换为mp4 3580 | 3581 | ```shell 3582 | ffmpeg -i input.mov output.mp4 3583 | ``` 3584 | 3585 | - -i :表示输入文件 3586 | 3587 | 3588 | 3589 | #### 视频剪切 3590 | 3591 | Ex. 剪切前10秒 3592 | 3593 | ``` 3594 | ffmpeg -ss 0:0 -t 0:10 -i input.mov -c copy output.mp4 3595 | ``` 3596 | 3597 | - -ss : 表示视频开始时间 3598 | - -t : 表示持续时间 3599 | - -c : 表示复制出一份剪切好的视频 3600 | 3601 | PS. 把参数ss -t 放在参数-i 之后, 输入文件会逐帧解码, 直到-ss设置的时间点位置, 虽然时间点是精准的但是容易出现黑屏 3602 | 3603 | PS. 如果把-ss -t 放在-i之前, 会对输入文件执行seek, 直接找到-ss设置的时间点上, 不会出现黑屏 3604 | 3605 | 3606 | 3607 | Ex. 裁剪视频最后10秒 3608 | 3609 | ``` 3610 | ffmpeg -sseof -0:10 -i input.mov output.mp4 3611 | ``` 3612 | 3613 | - -sseof :表示视频最末尾的开始时间 3614 | 3615 | 3616 | 3617 | 3618 | 3619 | Ex. 剪切视频中 6分15秒到7分23秒 3620 | 3621 | ``` 3622 | ffmpeg -i xxx.mp4 -ss 06:15 -to 07:23 -strict -2 output.mp4 3623 | ``` 3624 | 3625 | - strict -2 是为了防止报错`the encoder 'aac' is experimental but experimental codecs are not enabled` 3626 | 3627 | 3628 | 3629 | #### 视频画面编辑 3630 | 3631 | EX.画面缩放 1080p - 480p 3632 | 3633 | ``` 3634 | ffmpeg -i input.mov -vf scale=854:480 -acodec aac -vcodec h264 out.mp4 3635 | ``` 3636 | 3637 | - -vf : 用来指定视频滤镜 3638 | - Scale : 缩放比例 ex. 853:480 为了将视频高度变为480px, 则宽度计算为 = 1920*480 / 1080 = 853 3639 | - -acodec = 指定音频使用acc编码 3640 | - -vcodec h264 = 指定视频使用h264编码 3641 | 3642 | EX.剪裁视频画面 3643 | 3644 | 假设目前input为1280*720的, 需要剪裁画面正中间640x640的部分则如下 3645 | 3646 | 因为xy默认为视频正中间, 所以不需要设置 3647 | 3648 | ``` 3649 | ffmpeg -i input.mov -strict -2 -vf crop=640:640:x:y out.mp4 3650 | ``` 3651 | 3652 | - crop : 表示剪裁视频的画面, 格式为width:height: x:y, width:height表示剪裁后的尺寸, x:y表示剪裁区域的左上角坐标 3653 | 3654 | 3655 | 3656 | #### 视频提取帧数 3657 | 3658 | Ex.从视频中提取帧数, 每秒提取24幅图 3659 | 3660 | ``` 3661 | ffmpeg -i twice_v2.mp4 -ss 00:00 -to 00:00 -q:v 1 -r 24 test/image-%05d.jpg 3662 | ``` 3663 | 3664 | - -q : v * : 表示抽取的图片质量, * 为1~5, 1通常是最高质量 3665 | - -r 指定抽取的帧率,即从视频中每秒钟抽取图片的数量。1代表每秒抽取一帧,5就表示一秒抽5张图 3666 | - image-%05d.jpg :表示存取的图像名称 05d表示00000的格式计数, 前面可以添加路劲 3667 | - -to : 表示抽帧的结束时间 跟-ss搭配 3668 | 3669 | 3670 | 3671 | 搭配`-start_number` 可以指定特定的开始数字, 比如设置 3672 | 3673 | 3674 | 3675 | #### 隔N秒取帧 3676 | 3677 | ``` 3678 | ffmpeg -i test.mp4 -q:v 1 -vf fps=25 test/image-%05d.jpg 3679 | ``` 3680 | 3681 | - -vf fps : 每秒取截取多少帧数 3682 | 3683 | 3684 | 3685 | 3686 | 3687 | #### 视频合成 3688 | 3689 | 参考 https://blog.csdn.net/u011636440/article/details/78031734?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control 3690 | 3691 | Ex.从抽出的帧 重新合成视频 3692 | 3693 | ``` 3694 | ffmpeg -i path/to/%d.jpg -vcodec libx264 -r 帧率 output.mp4 3695 | ``` 3696 | 3697 | 因为ffmpeg 默认图片编号从0开始, 如果图片第一张不是0开始, 则可pyhton脚本修改或者是添加 3698 | 3699 | -start_number 3700 | 3701 | 3702 | 3703 | #### 多视频合并 3704 | 3705 | 参考https://blog.csdn.net/u011086331/article/details/82966476 3706 | 3707 | 首先,把要合并的视频按顺序写到files.txt里,例如 3708 | 3709 | ``` 3710 | file '1.mp4' 3711 | 3712 | file '2.mp4' 3713 | ``` 3714 | 3715 | 注意必须单引号 3716 | 3717 | 然后 3718 | 3719 | ``` 3720 | ffmpeg -f concat -safe 0 -i files.txt -c copy output.mp4 3721 | ``` 3722 | 3723 | 注意:这一行指令使用了-c copy,说明他只适用于视频切割产生的分段,被合并的视频必须是相同的参数!!如果你需要合并参数不同的视频,把-c copy去掉或者自己写压制参数,参考参数:-c:v libx264 -crf 23 -profile:v high -level 5 -c:a aac -b:a 240k 3724 | 3725 | 3726 | 3727 | #### 設置幀率 3728 | 3729 | Ex. 設置視頻的幀率 3730 | 3731 | ``` 3732 | ffmpeg -i input.avi -codec:v mpeg4 -r 30 -qscale:v 2 -codec:a copy C.avi 3733 | ``` 3734 | 3735 | - `-codec:v mpeg4` : 使用mpeg4的encoder 3736 | - `-qscale:v n` : 或者`-q:v n`表示输出的video的quality, n从1~31, 1为最高级品质, 建议2~5为mp4 3737 | - `-qscale:a `: 或者 `-q:s ` 表示audio的quality, nn 3738 | - `-codec:a copy` : 複製 3739 | 3740 | 3741 | 3742 | #### 调整视频比特率(码率) -b 3743 | 3744 | 可以用来压缩视频大小, 将视频压缩到约2500kbps左右 3745 | 3746 | ```` 3747 | ffmpeg -i input.mp4 -b:v 2500k -b:a 2500k output.mp4 3748 | ```` 3749 | 3750 | 3751 | 3752 | 3753 | 3754 | #### 视频流录制 3755 | 3756 | ``` 3757 | ffmpeg -i rstp://user:password@ipaddress/Streams/Channels/101 -acodec copy -vcodec copy output/path/xxx.mp4 3758 | ``` 3759 | 3760 | 3761 | 3762 | #### 视频转换格式 3763 | 3764 | Dav2mp4 (通常只有大华摄像头才是dav格式) 3765 | 3766 | ``` 3767 | ffmpeg -i video.dav -c:v libx264 -crf 24 output.mp4 3768 | ``` 3769 | 3770 | - `-s` : 1280x720 3771 | 3772 | 3773 | 3774 | #### 编码格式 3775 | 3776 | H.264 : 3777 | 3778 | 3779 | 3780 | ### Pdb debug 模式 3781 | 3782 | ```python 3783 | import pdb #import 这个包 3784 | 在需要断点的地方 set_trace() 3785 | ``` 3786 | 3787 | 快捷键如下 3788 | 3789 | `n`: next step 下一步 3790 | 3791 | `c` : 执行下一个断点 3792 | 3793 | `l` : 查看当前代码段 3794 | 3795 | `step` : step in 进入 3796 | 3797 | `q`: quit debug 3798 | 3799 | `dir(类)` : 利用dir可以查看类的所有属性 3800 | 3801 | ------ 3802 | 3803 | # DeepLearning 3804 | 3805 | #### 标注工具 3806 | 3807 | Labellmg (https://github.com/tzutalin/labelImg) 3808 | 3809 | Labelme (https://github.com/wkentaro/labelme#ubuntu) 3810 | 3811 | #### 自定义数据集格式 //TODO 3812 | 3813 | 以下均以目标检测为例 3814 | 3815 | VOC 3816 | 3817 | ``` 3818 | VOC2007 | Annotations (存放目标在图片里的坐标信息) | ImageSets | Layout | Main | JPEGImages (存放图片, 按照顺序) | SegementationClass | SegementationObjects 3819 | ``` 3820 | 3821 | 3822 | 3823 | 3824 | 3825 | #### Segmentation 3826 | 3827 | 多边形分割标注推荐使用labelme, labelme 自带将json文件转换成png 3828 | 3829 | 3830 | 3831 | Labelme_json_to_dataset 可以处理单个文件, 编辑以下脚本可以批量处理多个json 3832 | 3833 | 在json目录下生成sh文件, 将以下内容复制进入, 然后保存执行就能批量产出png 3834 | 3835 | ```shell 3836 | #!/bin/bash 3837 | let i=1 3838 | path=./ 3839 | cd ${path} 3840 | for file in *.json 3841 | do 3842 | labelme_json_to_dataset ${file} 3843 | let i=i+1 3844 | done 3845 | ``` 3846 | 3847 | 3848 | 3849 | 3850 | 3851 | #### CUDA / CUDNN 安装与移除 3852 | 3853 | cuda版本需要与 Nvidia 显卡驱动匹配才能 3854 | 3855 | 参考 `docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html` 3856 | 3857 | 3858 | 3859 | #### 显卡驱动安装 3860 | 3861 | - 测试平台 Ubuntu 18.04.4 3862 | 3863 | 利用ppa 安装显卡驱动 3864 | 3865 | ```shell 3866 | sudo add-apt-repository ppa:graphics-drivers/ppa #添加入ppa 3867 | sudo apt-get update #更新一下 3868 | ubuntu-drivers devices #这会显示出哪些驱动可以安装 3869 | 3870 | #比如看到 3871 | driver : nvidia-driver-440 3872 | # 3873 | sudo apt install nvidia-driver-440 3874 | #如果出现软件包有未满足的依赖关系 3875 | sudo apt-get install libnvidia-compute-440 3876 | 3877 | #然后重新再一次 3878 | sudo apt install nvidia-driver-440 3879 | #装完nvidia-smi测试一下, 出现以下属于正常, 重启电脑就行了 3880 | Failed to initilize NVML:Driver/library version mismatch 3881 | 3882 | 3883 | 3884 | 3885 | sudo ubuntu-drivers autoinstall 3886 | ``` 3887 | 3888 | 上述执行完之后, 3889 | 3890 | ``` 3891 | sudo reboot //重启电脑 3892 | nvidia-smi //查看是否安装成功, 如果有会出现熟悉的界面 3893 | ``` 3894 | 3895 | 3896 | 3897 | #### CUDA安装 3898 | 3899 | 到官网 developer.nvidia.com/cuda-toolkit-archive 3900 | 3901 | 下载需要的版本,选择runfile格式 3902 | 3903 | 依照官方给的指令进行下载及安装 3904 | 3905 | 例如如下 3906 | 3907 | ```shell 3908 | wget http://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda_10.1.243_418.87.00_linux.run 3909 | sudo sh cuda_10.1.243_418.87.00_linux.run 3910 | ``` 3911 | 3912 | 过程会有一些协议需要accept 3913 | 3914 | **然后记得显卡安装驱动的地方可以取消, 如果之前已经安装过的话** 3915 | 3916 | 3917 | 3918 | 如果报错提示空间不足 3919 | 3920 | 可以将tmpdir改成, 如下重新执行 3921 | 3922 | ``` 3923 | sudo sh cuda_10.1.243_418.87.00_linux.run --tmpdir=/home 3924 | ``` 3925 | 3926 | 3927 | 3928 | 一般安装的路径位置会是在 /usr/local/cuda-10.x 3929 | 3930 | 然后会生成symbolic link 在/usr/local/cuda 3931 | 3932 | 3933 | 3934 | 然后在环境变量中设置路径 3935 | 3936 | ```bash 3937 | vim ~/.bashrc 3938 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64 3939 | export PATH=$PATH:/usr/local/cuda/bin 3940 | export CUDA_HOME=$CUDA_HOME:/usr/local/cuda 3941 | 3942 | 3943 | source ~/.bashrc //最后更新一下 3944 | ``` 3945 | 3946 | 以上这一步很重要如果没设置好, nvcc会找不到, 并且报错 3947 | 3948 | ``` 3949 | bash : /usr/bin/nvcc: No such file or directory 3950 | ``` 3951 | 3952 | 3953 | 3954 | 3955 | 3956 | #### CUDA共存版本 3957 | 3958 | 假设又想装一个cuda10.2 版本 3959 | 3960 | 一样记得驱动不要选择, 然后提示会跳出 已经有symbolic link, 就是原先已经有的/usr/local/cuda/ 3961 | 3962 | 如果选择Y, 则会覆盖之前旧版本的cuda, 如果选择N, 就不会覆盖, 只会装到/usr/local/cuda-10.2 3963 | 3964 | 要看安装了哪些版本就到/usr/local 下 3965 | 3966 | 要查看现在使用那一个版本的cuda可以查看 3967 | 3968 | ``` 3969 | stat cuda #查看当前符号链接的位置File: 'cuda' -> 'usr/local/cuda-10.1'Size : xxxx..etc 3970 | ``` 3971 | 3972 | 可以看到目前符号链接到 10.1的版本 3973 | 3974 | 如果想切换到 10.2 可以 3975 | 3976 | ```shell 3977 | sudo rm -rf cuda #删除之前的连接sudo In -s /usr/local/cuda-10.2 /usr/local/cuda # 也就是将10.2链接到cuda 3978 | ``` 3979 | 3980 | 3981 | 3982 | 3983 | 3984 | 3985 | 3986 | 3987 | 3988 | #### cudnn 安装 3989 | 3990 | 到 Developer.nvidia.com/rdp/cudnn-download (应该需要登入) 3991 | 3992 | 找到对应cuda的版本, 下载 cuDNN Library for Linux 这会是一个tgz包 3993 | 3994 | 解压缩后得到cuda文件夹, 将里面的Include/ 以及 lib64/下的内容丢到/usr/local/cuda/下对应的文件夹下面就完成 3995 | 3996 | 3997 | 3998 | #### CUDA 移除 3999 | 4000 | 1. 找到cuda自动的卸除脚本, 通常在`/usr/local/cuda/bin/`下面, 并且执行 4001 | 4002 | ``` 4003 | sudo find / -iname '*uninstall_cuda*' 4004 | ``` 4005 | 4006 | 2. 直接删除/usr/local/下 cuda版本的文件夹就可以 4007 | 4008 | 4009 | 4010 | 4011 | 4012 | 如果使用sudo apt-get 安装的nvidia-cuda-toolkit 4013 | 4014 | 可以 4015 | 4016 | ``` 4017 | sudo apt-get autoremove nvidia-cuda-toolkit 4018 | ``` 4019 | 4020 | 4021 | 4022 | 4023 | 4024 | 其他参考链接 4025 | 4026 | https://www.jianshu.com/p/fd0f84f858f8 4027 | 4028 | 4029 | 4030 | 4031 | 4032 | ------ 4033 | 4034 | # IDE 使用技巧(Pycharm / VScode / Clion) 4035 | 4036 | 4037 | 4038 | #### Pycharm SSH 远程链接 4039 | 4040 | 以下技巧pycharm clion都使用 4041 | 4042 | 1. 将pycharm IDE SSH连线到远程服务器段 4043 | 4044 | - 服务器需要提供host ip地址,port端口号, host name(root), 登入密码 4045 | - 例如远程服务器的为 test@192.168.3.2, test为hostname, 192.168.3.2为host 地址 4046 | - Pycharm setting中的project interpreter中可以新增服务器上的python解释器,找到SSH Interpreter地方, 输入以上服务器提供的资料进行连接 4047 | 4048 | 4049 | 4050 | 2. 实现资料互传 4051 | 4052 | - Pycharm>tools > deployment>configuation 的mapping可以设置映射 4053 | - Local path是本地项目的路径 4054 | - Deplotment path是服务器的路径 4055 | - Pycharm>tools > deployment>Brose remote host 可以访问远程的资料夹 4056 | 4057 | 3. 开启服务器的终端 4058 | 4059 | Pycharm > tools > start SSH session 可以在pycharm下方开启远程终端 4060 | 4061 | 4062 | 4063 | 更新远程解释器的包 4064 | 4065 | ``` 4066 | File -> Invalidate Caches / Restart #可以删除移除之前的cache然后重启 4067 | ``` 4068 | 4069 | 4070 | 4071 | 4072 | 4073 | **问题记录** 4074 | 4075 | **问题** 4076 | 4077 | 1. Unable to save settings, restart Pycharm 4078 | 2. Clion 直接open 文件会无法找到变量定义 cannot find declaration to go to 4079 | 4080 | **解决** 4081 | 4082 | 1. 问题发生通常是因为对project文件没有权限, 所以将权限改变一下就行 4083 | 2. 开启project需要用File->New CMake Project from Sources, 才会自动的找变量的定义 4084 | 4085 | 4086 | 4087 | #### VScode 4088 | 4089 | 快捷技巧: 4090 | 4091 | shift + Alt + 滚轮, 实现如pycharm多光标 下拉 4092 | 4093 | ##### 在vscode上编写 python 4094 | 4095 | 插件 : Python 、 jupyter 4096 | 4097 | 1. SSH 4098 | 4099 | 实现远程ssh, 从官方搜索ssh, 然后链接到本地下载Remote - SSH extension 4100 | 4101 | 并且选择远程python解释器的时候相当简单(前提先安装好python extension), 在最下方一条蓝色的地方可以选择python 解释器, 而且插件已经自动的帮你找到远程解释器的位置 4102 | 4103 | 4104 | 4105 | 2. 远程python解释器 4106 | 4107 | 在服务器上装python extension , 可以现在vscode中插件市场找到python, 然后右键copy 到浏览器贴上, 保留地址后面link的部分, 就能跳转到下载插件包的网站, 右侧找到download的地方把包下载下来丢到服务器,然后打开vscode 里面的终端 输入如下指令进行安装 4108 | 4109 | ``` 4110 | code --install-extension /path/to/extension.vsix 4111 | ``` 4112 | 4113 | **问题** 4114 | 4115 | 如果遇到无法识别插件的问题,可以将主目录下的`.vscode-server`文件夹删除或者改名 4116 | 4117 | 然后重新启动vscode让他自动建立一个新的`.vscode-server` 4118 | 4119 | 4120 | 4121 | 3. python extension 4122 | 4123 | 在extension 中搜索 python 并安装, 就能让IDE支持 快速查看函数定义等功能 4124 | 4125 | 4. 打开快捷键一览表 4126 | 4127 | ``` 4128 | ctrl K + ctrl S 4129 | ``` 4130 | 4131 | ##### 快捷 4132 | 4133 | ``` 4134 | Alt + up / down 移动上下行 4135 | 4136 | shift + Alt + up/down 可实现多行列选择 4137 | ``` 4138 | 4139 | 5. 安装智能补全插件 Kite 4140 | 4141 | ``` 4142 | Kite AutoComplete Al Code 4143 | ``` 4144 | 4145 | 记得去Kite 官方下载engine, 搭配vscode 的插件一起使用 4146 | 4147 | 6. **python**带参数的debug模式 4148 | 4149 | Run -> open configurations , 主要将args添加进去, 以下为示例 4150 | 4151 | ```json 4152 | { 4153 | // Use IntelliSense to learn about possible attributes. 4154 | // Hover to view descriptions of existing attributes. 4155 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 4156 | "version": "0.2.0", 4157 | "configurations": [ 4158 | { 4159 | "name": "Python: Current File", 4160 | "type": "python", 4161 | "request": "launch", 4162 | "program": "${file}", 4163 | "console": "integratedTerminal", 4164 | "args": ["-n","***", "-u", "**", "-p", "**", "-s", "***", "-k", "**", "-g", "acadci_prod"], 4165 | } 4166 | ] 4167 | } 4168 | 4169 | 4170 | #### torch 分布式训练debug, 主要将launch替换为torch底下的 4171 | { 4172 | // Use IntelliSense to learn about possible attributes. 4173 | // Hover to view descriptions of existing attributes. 4174 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 4175 | "version": "0.2.0", 4176 | "configurations": [ 4177 | { 4178 | "name": "Python: Current File", 4179 | "type": "python", 4180 | "request": "launch", 4181 | "program": "/path/to/site-packages/torch/distributed/launch.py", 4182 | "console": "integratedTerminal", 4183 | "args": ["--nproc_per_node=4", 4184 | "/path/train.py", 4185 | ], 4186 | } 4187 | ] 4188 | } 4189 | ``` 4190 | 4191 | 4192 | 4193 | #### 在vscode 上写C++ 4194 | 4195 | 插件:C++ 、 CMake、Cmake tools 4196 | 4197 | 修改`launch.json` configuration 4198 | 4199 | 以下为示例, 主要修改program 也就是程序运行的路径,然后args 为列表 4200 | 4201 | 详情参考 https://code.visualstudio.com/docs/cpp/launch-json-reference 4202 | 4203 | ```c++ 4204 | { 4205 | "name": "C++ Launch (Windows)", 4206 | "type": "cppvsdbg", 4207 | "request": "launch", 4208 | "program": "C:\\app1\\Debug\\app1.exe", 4209 | "symbolSearchPath": "C:\\Symbols;C:\\SymbolDir2", 4210 | "args":["-a", "-b"], 4211 | "externalConsole": true, 4212 | "logging": { 4213 | "moduleLoad": false, 4214 | "trace": true 4215 | }, 4216 | "visualizerFile": "${workspaceFolder}/my.natvis", 4217 | "showDisplayString": true 4218 | } 4219 | 4220 | ``` 4221 | 4222 | 4223 | 4224 | 4225 | 4226 | #### Vscode 必装插件 4227 | 4228 | 1. Markdown All in one : github.com/yzhang-gh/vscode-markdown 4229 | 4230 | 2. Remote-SSH 4231 | 4232 | 3. Todo-Tree 可以实现在代码中进行各种标记, 方便快速跳转 4233 | 4234 | 4235 | 4236 | 4237 | 4238 | #### 遇到问题 4239 | 4240 | 1. 如果从不同版本的vscode 登入remote server, 可能导致原先的extensions 无法正常加载, 解决方案是到.vscode_server/extensions/ms.python.python ms.jupyter 两个rm -rf 删除, 然后重新装 4241 | 4242 | 4243 | 4244 | 4245 | 4246 | ------ 4247 | 4248 |

Socket 网路编程

4249 | 4250 | #### 1. 实现服务端与客户端webcam 实时传输图像 4251 | 4252 | 参考 https://gist.github.com/kittinan/e7ecefddda5616eab2765fdb2affed1b 4253 | 4254 | 为实现两段进行connect, 需要知道host 以及 port端口 4255 | 4256 | 客户端例子如下 4257 | 4258 | ```python 4259 | import cv2 4260 | import io 4261 | import socket 4262 | import struct 4263 | import time 4264 | import pickle 4265 | import zlib 4266 | 4267 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4268 | client_socket.connect(('192.168.1.124', 8485)) 4269 | connection = client_socket.makefile('wb') 4270 | 4271 | cam = cv2.VideoCapture(0) 4272 | 4273 | cam.set(3, 320); 4274 | cam.set(4, 240); 4275 | 4276 | img_counter = 0 4277 | 4278 | encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90] 4279 | 4280 | while True: 4281 | ret, frame = cam.read() 4282 | result, frame = cv2.imencode('.jpg', frame, encode_param) 4283 | # data = zlib.compress(pickle.dumps(frame, 0)) 4284 | data = pickle.dumps(frame, 0) 4285 | size = len(data) 4286 | 4287 | 4288 | print("{}: {}".format(img_counter, size)) 4289 | client_socket.sendall(struct.pack(">L", size) + data) 4290 | img_counter += 1 4291 | 4292 | cam.release() 4293 | ``` 4294 | 4295 | 4296 | 4297 | 服务端例子如下 4298 | 4299 | ```python 4300 | import socket 4301 | import sys 4302 | import cv2 4303 | import pickle 4304 | import numpy as np 4305 | import struct ## new 4306 | import zlib 4307 | 4308 | HOST='' 4309 | PORT=8485 4310 | 4311 | s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4312 | print('Socket created') 4313 | 4314 | s.bind((HOST,PORT)) 4315 | print('Socket bind complete') 4316 | s.listen(10) 4317 | print('Socket now listening') 4318 | 4319 | conn,addr=s.accept() 4320 | 4321 | data = b"" 4322 | payload_size = struct.calcsize(">L") 4323 | print("payload_size: {}".format(payload_size)) 4324 | while True: 4325 | while len(data) < payload_size: 4326 | print("Recv: {}".format(len(data))) 4327 | data += conn.recv(4096) 4328 | 4329 | print("Done Recv: {}".format(len(data))) 4330 | packed_msg_size = data[:payload_size] 4331 | data = data[payload_size:] 4332 | msg_size = struct.unpack(">L", packed_msg_size)[0] 4333 | print("msg_size: {}".format(msg_size)) 4334 | while len(data) < msg_size: 4335 | data += conn.recv(4096) 4336 | frame_data = data[:msg_size] 4337 | data = data[msg_size:] 4338 | 4339 | frame=pickle.loads(frame_data, fix_imports=True, encoding="bytes") 4340 | frame = cv2.imdecode(frame, cv2.IMREAD_COLOR) 4341 | cv2.imshow('ImageWindow',frame) 4342 | cv2.waitKey(1) 4343 | ``` 4344 | 4345 | 4346 | 4347 | #### 2. 服务端与客户端之间信息通信 4348 | 4349 | python3中必须先将要send 的信息encode, 然后接收信息的一段需要decode才能得到接收的信息 4350 | 4351 | 参考https://stackoverflow.com/questions/33003498/typeerror-a-bytes-like-object-is-required-not-str 4352 | 4353 | ```python 4354 | Client Side: 4355 | >>> host='127.0.0.1' 4356 | >>> port=1337 4357 | >>> import socket 4358 | >>> s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4359 | >>> s.connect((host,port)) 4360 | >>> st='connection done' 4361 | >>> byt=st.encode() 4362 | >>> s.send(byt) 4363 | 15 4364 | >>> 4365 | Server Side: 4366 | 4367 | >>> host='' 4368 | >>> port=1337 4369 | >>> import socket 4370 | >>> s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4371 | >>> s.bind((host,port)) 4372 | >>> s.listen(1) 4373 | >>> conn ,addr=s.accept() 4374 | >>> data=conn.recv(2000) 4375 | >>> data.decode() 4376 | 'connection done' 4377 | >>> 4378 | ``` 4379 | 4380 | 如果要传送dict可以用json包的dump 4381 | 4382 | ```d 4383 | 4384 | ``` 4385 | 4386 | --- 4387 | 4388 |

使用nginx 本地实时推流rtmp

4389 | 4390 | 参考 : https://blog.csdn.net/kingroc/article/details/50839994 4391 | 4392 | ``` 4393 | sudo apt-get update 4394 | sudo apt-get install libpcre3 libpcre3-dev 4395 | sudo apt-get install openssl libssl-dev 4396 | ``` 4397 | 4398 | 找到合适的版本 先下载nginx 4399 | 4400 | ``` 4401 | nginx.org/download/ 4402 | ``` 4403 | 4404 | 将nginx的rtmp模块clone来 4405 | 4406 | ``` 4407 | git clone https://github.com/arut/nginx-rtmp-module 4408 | ``` 4409 | 4410 | 解压缩nginx, 然后 4411 | 4412 | ``` 4413 | ./configure --add-module=指定你rtmp包的位置 4414 | make 4415 | sudo make install 4416 | ``` 4417 | 4418 | 4419 | 4420 | 启动服务 4421 | 4422 | ``` 4423 | cd /usr/local/nginx./sbin/nginx 4424 | ``` 4425 | 4426 | 4427 | 4428 | 配置conf, 将rtmp添加到, application 名称可以随意输入 4429 | 4430 | ```json 4431 | worker_processes 1;events { worker_connections 1024;}rtmp { server { listen 1935; chunk_size 4096; application vod { play /opt/video/vod; } application live{ #第一处添加的直播字段 live on; } }}http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }} 4432 | ``` 4433 | 4434 | 4435 | 4436 | 接着利用ffmpeg 将python处理过的每一帧数进行推流 4437 | 4438 | ```python 4439 | rtmp_url = 'rtmp://localhost:1935/live/test'ffmpeg_command = [ 'ffmpeg', '-y', '-f', 'rawvideo', '-vcodec', 'rawvideo', '-pix_fmt', 'bgr24', '-s', "{}x{}".format(img_width, img_height), '-r', str(fps), '-i', '-', '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-tune', 'zerolatency', '-f', 'flv', rtmp_url]process = sp.Popen(ffmpeg_command, stdin=sp.PIPE)process.stdin.write(frame.string) 4440 | ``` 4441 | 4442 | 4443 | 4444 | --- 4445 | 4446 |

redis-py 使用

4447 | 4448 | Redis 为open source 的key-value数据存储方案 4449 | 4450 | 1. https://redis.io/download 下载stable 版本 4451 | 4452 | 2. ```shell 4453 | $ tar xzf redis-6.2.5.tar.gz 4454 | $ cd redis-6.2.5 4455 | $ sudo make 4456 | $ cd src 4457 | $ sudo make install 4458 | ``` 4459 | 4460 | 安装完毕后可以测试启动 4461 | 4462 | ```shell 4463 | src/redis-server 4464 | ``` 4465 | 4466 | 也能使用内建的客户端进行测试 4467 | 4468 | ```shell 4469 | src/redis-cli 4470 | redis> set foo bar 4471 | OK 4472 | redis> get foo 4473 | "bar" 4474 | ``` 4475 | 4476 | 我们可以在/usr/local/下建立redis目录存储必须的配置文件 4477 | 4478 | ```shell 4479 | mkdir -p /usr/local/redis/bin 4480 | mkdir -p /usr/local/redis/etc 4481 | 4482 | #复制src 下的文件到etc 4483 | cp redis.conf /usr/local/redis/etc 4484 | cp mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-sentinel /usr/local/redis/bin 4485 | ``` 4486 | 4487 | 4488 | 4489 | 遇到问题 4490 | 4491 | 1. redis failed opening .rdb for saving permission denied 4492 | 4493 | 解决: 4494 | 4495 | 主要是权限不够 4496 | 4497 | 1. vim 开启/usr/local/redis/etc , 搜索dbfilename dump.rdb 4498 | 2. 找到#Note that you must specify a directory here, not a file name 4499 | 4500 | ``` 4501 | dir ./ 4502 | ``` 4503 | 4504 | 就能找到指定文件夹, 然后chmod 777 开启最大权限就行 4505 | --------------------------------------------------------------------------------