深度学习炼丹常见操作及问题(持续更新)

众所周知,炼丹需要强大的计算资源,通常都是在服务器(集群)上进行炼丹。
但是,对于新手来说,Linux命令不熟悉,远程登录软件不会用,python环境不会管理等等,分分钟劝退。曾经的我就是这样一个小小白,跳了许多坑,慢慢摸索着才上了道。
所以就写一篇博客,随手记录一下常见的一些操作,会一直更新,免得自己忘记。这些是我个人使用的一些操作或者解决方案,不是放之四海而皆准的,请酌情参考哈!
我的基本环境是:

  • CentOS
  • VSCode
  • Anaconda(python3.6.2)

配置远程登录

远程我使用的是VSCode+Remote SSH拓展+OpenSSH
VSCode远程登录服务器

服务器基本操作

服务器结点间跳转
ssh node2    #当前结点跳转到结点node2
查看服务器GPU使用情况

在终端输入以下命令

nvidia-smi    
查看CUDA版本
#终端输入
nvcc -V
#或者
cat /usr/local/cuda/version.txt
打包文件

1、简单打包

tar -zcvf mypackage.tar.gz file1 file2  #file1,file2以tar.gz方式打包到mypackage.tar.gz包内

解包

tar -zxvf mypackage.tar.gz #解压到当前目录
tar -zxvf mypackage.tar.gz -C dir #解压到dir目录

2、打包除指定文件外的文件夹

tar -zcvf mypackage.tar.gz --exclude=aa/b --exclude=aa/c aa/  #打包aa文件夹内除了aa/b和aa/c之外的所有文件及文件夹

3、去除绝对路径打包

默认情况下打包可能会将文件的绝对路径打包进去,这样解压出来的文件就会有一长串的路径,可以使用-C选项去除绝对路径

#打包/public/home/user/路径下的myfiles文件夹,-C选项user/后面加个空格即可去掉/public/home/user/路径
tar -zcvf myfile.tar.gz -C /public/home/user/ myfiles  

选项意义

选项意义
-c创建打包文件
-t查看打包文件内有哪些文件
-z通过gzip进行压缩或解压,文件明最好为*.tar.gz
-j通过bzip2进行压缩或者解压,文件名最好为*.tar.bz2
-v在压缩解压过程中显示处理的文件名
-f filename压缩或者解压的包的路径,-f必须放在选项组合的最后
-C将文件解压到指定目录
-p保留文件的原本权限和属性,用于备份数据
–exclude=FILE压缩时排除指定文件或者文件夹
删除文件

Linux下删除文件
rm [-选项] 文件或目录
-f:不做确认提示,忽略不存在的文件,不会出现任何警告
-i:进行确认提示,删除前询问是否确认删除
-r:递归删除,可以删除目录
-v:在删除文件之前打印文件名称

rm -f a.o

删除目录

rm -r dir  #dir是指定目录

删除特定开头的文件,只需要在特定开头后面加上*

rm -r P*  #删除以P开头的所有文件
统计文件个数
find -type f | wc -l 
#如果要统计文件夹个数,将f改成d即可
find -type d | wc -l
将程序放到后台运行

这里有两种方法:1、使用nohup命令将程序放入后台运行。 2、使用tmux将窗口与会话进行分离,这样退出终端时程序依然能在后台运行。

1、nohup

nohup python train.py >train.log 2>&1 &

将train.py运行输出的内容重定向到train.log中,最后一个&表示将程序放入后台运行。

2、使用tmux将会话与窗口分离。
比如,我要将一个python程序放到后台运行,大致步骤如下:

  • 新建一个tmux会话: tmux new -s name 这样就新建了一个名为name的tmux会话。
  • 在tmux会话中运行程序: python example.py 。
  • 然后在tmux中输入: ctrl+b 然后摁下 d 或者输入 tmux detach 将会话与窗口分离,这样程序就能在后台运行了。
  • 如果中途想要查看程序运行的状态,需要重新连接到刚刚新建的tmux会话中,使用 tmux a -s name 这里的name时刚刚新建的tmux会话的名称。
  • 最后程序运行完后,不再需要该tmux会话,那么使用 tmux kill-session name结束该会话。
    更多tmux操作,see tmux操作
查看程序运行时间

先通过nvidia-smi或者top指令找到想查看的程序的pid号。
使用ps指令,按指定格式输出想要查看的信息。

ps -eo pid, tty, user, lstart, etime | grep 25542

其中-eo与|之间的内容表示希望输出的格式,pid表示这个进程的pid号,tty表示终端机连接标准输入设备的文件名称,user表示这个程序属于哪个用户,lstart表示程序开始时间,etime表示程序运行时间,25542表示想要查询的程序的pid。

Python及框架使用

指定GPU训练模型

在你的训练的python文件中加入,指定使用序号为2的GPU

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "2"  #2代表GPU的序号,可以是一个,也可以是'0,1,2,3'这样的多个
统计程序每个函数运行时间
python -m cProfile -s cumtime yourscript.py  #按cumtime时间排序

结果如下

2118852 function calls (2074450 primitive calls) in 52.258 seconds

Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   2317/1    0.033    0.000   53.050   53.050 {built-in method builtins.exec}
        1    2.264    2.264   53.050   53.050 dataset.py:7(<module>)
      626    0.002    0.000   45.739    0.073 dataloader.py:344(__next__)
      626    0.012    0.000   45.737    0.073 dataloader.py:812(_next_data)
      625    0.003    0.000   45.648    0.073 dataloader.py:779(_get_data)
      625    0.005    0.000   45.646    0.073 dataloader.py:748(_try_get_data)
      625    0.015    0.000   45.641    0.073 queues.py:91(get)
      625    0.003    0.000   39.723    0.064 connection.py:253(poll)
      625    0.005    0.000   39.719    0.064 connection.py:413(_poll)
      625    0.014    0.000   39.714    0.064 connection.py:897(wait)
      625    0.014    0.000   39.668    0.063 selectors.py:365(select)
      625   39.651    0.063   39.651    0.063 {method 'poll' of 'select.poll' objects}

其中参数意义为:
ncalls:表示函数调用的次数;
tottime:表示指定函数的总的运行时间,除掉函数中调用子函数的运行时间;
percall:(第一个percall)等于 tottime/ncalls;
cumtime:表示该函数及其所有子函数的调用运行的时间,即函数开始调用到返回的时间;
percall:(第二个percall)即函数运行一次的平均时间,等于 cumtime/ncalls;
filename:lineno(function):每个函数调用的具体信息;

conda安装第三方库
conda install numpy     # 最基本的情况将numpy替换成要安装的库即可
conda install numpy==1.14.3   #安装指定的版本

安装指定较老版本的pytorch和torchvision,GPU版。

conda install pytorch==0.4.0 torchvision cuda92  #去掉cuda92安装cpu版本

安装最新的pytorch可以到pytorch官网直接安装。

conda install pytorch torchvision cudatoolkit=10.2 -c pytorch

对于安装cv2,conda好像没有???

#用
conda install opencv
#安装的包在import cv2时会报
ModuleNotFoundError: No module named 'cv2'

说明opencv这个包和cv2是不同的包。
然后我用

conda install opencv-python

发现在conda的channels里面没有找到这个包。

最后,使用pip安装opencv-python成功,能够使用cv2库。
所以安装cv2推荐使用pip

pip install opencv-python
conda管理虚拟环境

创建一个名为pytorch10,python版本为3.6的新环境

conda create -n pytorch10 python=3.6

查看你创建了多少个虚拟环境

conda env list   #或者
conda info -e 

激活你创建的环境,这样就可以使用你新建的环境了,可以正常滴安装需要的库。

conda activate pytorch10 #Linux Windows
source activate pytorch10  #Linux下,若上面命令不行,使用这个命令

退出虚拟环境

conda deactivate pytorch10  #退出pytorch10
source deactivate pytorch10  #Linux下

删除不想要的虚拟环境

conda remove -n pytorch10 --all

安装指定版本pytorch,GPU版本的pytorch要和CUDA版本对应。

# CUDA 9.2
conda install pytorch==1.2.0 torchvision==0.4.0 cudatoolkit=9.2 -c pytorch
# CUDA 10.0
conda install pytorch==1.2.0 torchvision==0.4.0 cudatoolkit=10.0 -c pytorch
# CPU Only
conda install pytorch==1.2.0 torchvision==0.4.0 cpuonly -c pytorch
pytorch需要注意的坑

torchvision.transforms.ToTensor()

在加载数据的时候,经常需要用到这个类。其输入可以是PIL Image或者np.ndarray格式(包括cv2)。它会先将图像由HWC格式转换成CHW格式。

对于ndarray,如果输入的数据类型是int型,那么ToTensor之后输出的将会是[0,1]归一化的结果。如果输入的数据类型是 float的,其输出数据范围为[0,255]。很多时候我们的模型输入是归一化的,如果不注意这一点,有可能会使模型loss很大,甚至出现nan。

>>> from torchvision import transforms
>>> import torch
>>> import numpy as np
>>> transform = transforms.Compose([transforms.ToTensor()])
>>>#整型
>>> img1 = np.array([[1,2,3],[4,5,6]], dtype=np.uint8)
>>>#float型
>>> img2 = np.array([[1,2,3],[4,5,6]], dtype=np.float32)
>>>
>>> img1 = transform(img1)
>>> img1     #其数据范围[0-1]
tensor([[[0.0039, 0.0078, 0.0118],
         [0.0157, 0.0196, 0.0235]]])
>>>
>>> img2 = transform(img2)
>>> img2 #其数据范围[0-255.]
tensor([[[1., 2., 3.],
         [4., 5., 6.]]])
>>>   

对于PILImage转化的Tensor,其数据类型是torch.FloatTensor。具体参考博文1博文2

THCudaCheck FAIL file=/opt/conda/conda-bld/pytorch_1579022034529/work/aten/src/THC/THCGeneral.cpp line=313 error=98 : invalid device function

新版本pytorch运行旧版本的代码时遇到的问题,我的解决方案是将cuda从9.2更新到10.1 。不知道大家还有什么好办法??
解决方案

torch.load()加载训练好的模型时报错:CUDA out of memory

情况是训练时显存是充足的,但是在测试时加载模型就显示显存不够。
这个问题是torch.load()函数在加载模型参数时会默认加载在服务器的GPU0上,即使你使用了os.environ[“CUDA_VISIBLE_DEVICES”]指定使用了别的显卡也会这样。
解决方法:

net = torch,load(model_path, map_location={'cuda:0' : 'cuda:1'})#映射到别的显卡上

pytorch的坑

常见报错

RuntimeError: cuda runtime error (59) : device-side assert triggered

Traceback (most recent call last):
  File "train_RFB_MLP.py", line 326, in <module>
    train()
  File "train_RFB_MLP.py", line 283, in train
    loss.backward()
  File "/lustre/home/jmtang/anaconda3/envs/pytorch12/lib/python3.6/site-packages/torch/tensor.py", line 118, in backward
    torch.autograd.backward(self, gradient, retain_graph, create_graph)
  File "/lustre/home/jmtang/anaconda3/envs/pytorch12/lib/python3.6/site-packages/torch/autograd/__init__.py", line 93, in backward
    allow_unreachable=True)  # allow_unreachable flag
RuntimeError: cuda runtime error (59) : device-side assert triggered at /tmp/pip-req-build-p5q91txh/aten/src/THC/generic/THCTensorMath.cu:26

出现这个异常大概率是类别标签的问题,例如数据集共100类,对应的标签是0-99,而在全连接的最后输出维度或者目标检测的num_classes只指定为99,那就会造成类别索引溢出,从而报错。
pytorch训练模型LOSS出现NAN
造成损失值NAN爆炸的情况很多,可能有:

  • 数据有问题,例如检测框的位置超出图像范围或者左上角坐标大于右下角坐标。检查数据,剔除脏数据。
  • 学习率过大,可以调小学习率。
  • 优化器的问题,比如某些模型使用Adam训没问题,相同情况下用SGD可能也会出现NAN。可以更换优化器。
  • 计算的数组可能越界,或者除数可能为0 。

参考1参考2参考3
To Be Continue…


版权声明:本文为weixin_40313940原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。