本次目标:
- 整理运行代码产生的内容
- 下载KITTI数据集和LSVH数据集
- 修改数据集样式为VOC2007
- 在新的数据集上训测
2019.4.13
一、准备工作
-
备份之前训练生成的文件
models,output
,以及没有用的qsub
相关的.sh
文件和文件夹(虽然这个备份似乎没有什么用) -
下载KITTI:
数据集: http://www.cvlibs.net/download.php?file=data_object_image_2.zip
标注文件: http://www.cvlibs.net/download.php?file=data_object_label_2.zip
或者KITTI官网
-
下载LSVH
在作者的GitHubhttps://github.com/xw-hu/SINet上找到:https://drive.google.com/file/d/1yHeuZia3pbcbn8OLkotJGJGhczI7gM3e/view -
下载用时很久,特别是那个12G的
-
删除data下的软链接
rm -rf VOCdevkit2007
- 删除data下的cache文件夹
- 备份原版的
VOC2007
文件夹到我的备份用文件夹/backup
mv ./VOC2007/ /home/jingge/hdd2T/backup/
- 删除
__pycache__
文件夹 - 删除
_init_paths.pyc
文件
二、操作新的文件夹
- 在移走
VOC2007
的位置重建一个VOC2007
文件夹:
mkdir VOC2007
- 参考文章
https://blog.youkuaiyun.com/u014256231/article/details/79801665
使用mkdir
命令新建文件夹:
最后结构如下:
- 解压之前下载的两个zip文件,图片放入JPEGImages里;标注信息解压后的label_2文件夹在VOC2007下,与Annotations等平行
unzip test.zip
会默认将文件解压到当前目录,如果要解压到指定目录,可以加上 -d 选项,即unzip test.zip -d /root/
。故:
unzip data_object_image_2.zip -d /home/jingge/hdd2T/faster-rcnn.pytorch/data/VOCdevkit/VOC2007/JPEGImages/
- 解压图片数据需要一点时间,结果目录如下:
- 再解压标签:
unzip data_object_label_2.zip -d /home/jingge/hdd2T/faster-rcnn.pytorch/data/VOCdevkit/VOC2007/
三、转换KITTI中标签的类别——对标注信息的txt文件进行操作
https://blog.youkuaiyun.com/u014256231/article/details/79801665
- 新建py文件
/data/VOCdevkit/VOC2007/training$ vim modify_annotations_txt.py
- 写入:
(忽略了’DontCare’和’Misc’类,也忽略了’Cyclist’类,因为图片中这一类过于小,标注信息也不准确,肉眼都难以分辨。还合并’Person_sitting’和’Pedestrian’统一为’Pedestrian’)
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# modify_annotations_txt.py
import glob
import string
txt_list = glob.glob('./label_2/*.txt') # 存储Labels文件夹所有txt文件路径
def show_category(txt_list):
category_list= []
for item in txt_list:
try:
with open(item) as tdf:
for each_line in tdf:
labeldata = each_line.strip().split(' ') # 去掉前后多余的字符并把其分开
category_list.append(labeldata[0]) # 只要第一个字段,即类别
except IOError as ioerr:
print('File error:'+str(ioerr))
print(set(category_list)) # 输出集合
def merge(line):
each_line=''
for i in range(len(line)):
if i!= (len(line)-1):
each_line=each_line+line[i]+' '
else:
each_line=each_line+line[i] # 最后一条字段后面不加空格
each_line=each_line+'\n'
return (each_line)
print('before modify categories are:\n')
show_category(txt_list)
for item in txt_list:
new_txt=[]
try:
with open(item, 'r') as r_tdf:
for each_line in r_tdf:
labeldata = each_line.strip().split(' ')
'''if labeldata[0] in ['Truck','Van','Tram','Car']: # 合并汽车类
labeldata[0] = labeldata[0].replace(labeldata[0],'car')
if labeldata[0] in ['Person_sitting','Cyclist','Pedestrian']: # 合并行人类
labeldata[0] = labeldata[0].replace(labeldata[0],'pedestrian')'''
#print type(labeldata[4])
if labeldata[4] == '0.00':
labeldata[4] = labeldata[4].replace(labeldata[4],'1.00')
if labeldata[5] == '0.00':
labeldata[5] = labeldata[5].replace(labeldata[5],'1.00')
if labeldata[0] == 'Truck':
labeldata[0] = labeldata[0].replace(labeldata[0],'truck')
if labeldata[0] == 'Van':
labeldata[0] = labeldata[0].replace(labeldata[0],'van')
if labeldata[0] == 'Tram':
labeldata[0] = labeldata[0].replace(labeldata[0],'tram')
if labeldata[0] == 'Car':
labeldata[0] = labeldata[0].replace(labeldata[0],'car')
#if labeldata[0] == 'Cyclist':
#labeldata[0] = labeldata[0].replace(labeldata[0],'cyclist')
if labeldata[0] in ['Person_sitting','Pedestrian']: # 合并行人类
labeldata[0] = labeldata[0].replace(labeldata[0],'pedestrian')
if labeldata[0] == 'Cyclist':
continue
if labeldata[0] == 'DontCare': # 忽略Dontcare类
continue
if labeldata[0] == 'Misc': # 忽略Misc类
continue
new_txt.append(merge(labeldata)) # 重新写入新的txt文件
with open(item,'w+') as w_tdf: # w+是打开原文件将内容删除,另写新内容进去
for temp in new_txt:
w_tdf.write(temp)
except IOError as ioerr:
print('File error:'+str(ioerr))
print('\nafter modify categories are:\n')
show_category(txt_list)
四、转换标注信息格式:txt到xml
- 对原始txt文件进行上述处理后,接下来需要将标注文件从txt转化为xml,并去掉标注信息中用不上的部分,只留下3类,还有把坐标值从float型转化为int型,最后所有生成的xml文件要存放在Annotations文件夹中。
AttributeError: 'NoneType' object has no attribute 'shape'
因为这个错误折腾了一晚上,就是读图片的路径的问题
以后凡是碰到路径的问题一定再三check, check, check!!!
另外我跟参考作者文章的似乎不太一样,我下下来的KITTI数据集的图片解压出来都是png格式的,用了一个rename 's/\.png/.jpg/' *.png
命令,把\faster-rcnn.pytorch\data\VOCdevkit\VOC2007\JPEGImages\training\image_2\
目录下的png图片全都转换成jpg。上面说到的路径问题,也是因为JPEGImages文件夹下面分了training和testing两个文件夹,而不是图片直接在下面。
- 新建py文件
/data/VOCdevkit/VOC2007/training$ vim txt_to_xml.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# txt_to_xml.py
# 根据一个给定的XML Schema,使用DOM树的形式从空白文件生成一个XML
from xml.dom.minidom import Document
import cv2
import os
def generate_xml(name,split_lines,img_size,class_ind):
doc = Document() # 创建DOM文档对象
annotation = doc.createElement('annotation')
doc.appendChild(annotation)
title = doc.createElement('folder')
title_text = doc.createTextNode('VOC2007')#这里修改了文件夹名
title.appendChild(title_text)
annotation.appendChild(title)
img_name=name+'.jpg'#要用jpg格式
title = doc.createElement('filename')
title_text = doc.createTextNode(img_name)
title.appendChild(title_text)
annotation.appendChild(title)
source = doc.createElement('source')
annotation.appendChild(source)
title = doc.createElement('database')
title_text = doc.createTextNode('The VOC2007 Database')#修改为VOC
title.appendChild(title_text)
source.appendChild(title)
title = doc.createElement('annotation')
title_text = doc.createTextNode('PASCAL VOC2007')#修改为VOC
title.appendChild(title_text)
source.appendChild(title)
size = doc.createElement('size')
annotation.appendChild(size)
title = doc.createElement('width')
title_text = doc.createTextNode(str(img_size[1]))
title.appendChild(title_text)
size.appendChild(title)
title = doc.createElement('height')
title_text = doc.createTextNode(str(img_size[0]))
title.appendChild(title_text)
size.appendChild(title)
title = doc.createElement('depth')
title_text = doc.createTextNode(str(img_size[2]))
title.appendChild(title_text)
size.appendChild(title)
for split_line in split_lines:
line=split_line.strip().split()
if line[0] in class_ind:
object = doc.createElement('object')
annotation.appendChild(object)
title = doc.createElement('name')
title_text = doc.createTextNode(line[0])
title.appendChild(title_text)
object.appendChild(title)
title = doc.createElement('difficult')
title_text = doc.createTextNode('0')
title.appendChild(title_text)
object.appendChild(title)
bndbox = doc.createElement('bndbox')
object.appendChild(bndbox)
title = doc.createElement('xmin')
title_text = doc.createTextNode(str(int(float(line[4]))))
title.appendChild(title_text)
bndbox.appendChild(title)
title = doc.createElement('ymin')
title_text = doc.createTextNode(str(int(float(line[5]))))
title.appendChild(title_text)
bndbox.appendChild(title)
title = doc.createElement('xmax')
title_text = doc.createTextNode(str(int(float(line[6]))))
title.appendChild(title_text)
bndbox.appendChild(title)
title = doc.createElement('ymax')
title_text = doc.createTextNode(str(int(float(line[7]))))
title.appendChild(title_text)
bndbox.appendChild(title)
# 将DOM对象doc写入文件
f = open('/home/jingge/hdd2T/faster-rcnn.pytorch/data/VOCdevkit/VOC2007/Annotations/'+name+'.xml','w') # Annotations/
#f = open('Annotations/'+name+'.xml','w')
f.write(doc.toprettyxml(indent = ''))
f.close()
if __name__ == '__main__':
class_ind=('van', 'tram', 'car', 'pedestrian', 'truck')#修改为了5类
cur_dir=os.getcwd()
print(cur_dir) #wjg
labels_dir=os.path.join(cur_dir,'label_2')
print(labels_dir) #wjg
for parent, dirnames, filenames in os.walk(labels_dir): # 分别得到根目录,子目录和根目录下文件
for file_name in filenames:
full_path=os.path.join(parent, file_name) # 获取文件全路径
#print full_path
print(full_path) # wjg
f=open(full_path)
split_lines = f.readlines()
name= file_name[:-4] # 后四位是扩展名.txt,只取前面的文件名
#print name
img_name=name+'.jpg'
img_path=os.path.join('/home/jingge/hdd2T/faster-rcnn.pytorch/data/VOCdevkit/VOC2007/JPEGImages',img_name) # 路径需要自行修改
#print img_path
# wjg
print("img_path",img_path)
img_size=cv2.imread(img_path).shape
generate_xml(name,split_lines,img_size,class_ind)
print('all txts has converted into xmls')
- 运行py文件
/data/VOCdevkit/VOC2007/training$ python txt_to_xml.py
五、生成训练验证集和测试集列表
这个工具是用Python3写的,所以执行的时候要进入文件目录(我把这个py放在了/hdd2T/faster-rcnn.pytorch/data/VOCdevkit/VOC2007/training
里面,和前两个py文件一样)后,执行:
python3 create_train_test_txt.py
# create_train_test_txt.py
# encoding:utf-8
import pdb
import glob
import os
import random
import math
def get_sample_value(txt_name, category_name):
label_path = './label_2/'
txt_path = label_path + txt_name+'.txt'
try:
with open(txt_path) as r_tdf:
if category_name in r_tdf.read():
return ' 1'
else:
return '-1'
except IOError as ioerr:
print('File error:'+str(ioerr))
txt_list_path = glob.glob('./label_2/*.txt')
txt_list = []
for item in txt_list_path:
temp1,temp2 = os.path.splitext(os.path.basename(item))
txt_list.append(temp1)
txt_list.sort()
print(txt_list, end = '\n\n')
# 有博客建议train:val:test=8:1:1,先尝试用一下
num_trainval = random.sample(txt_list, math.floor(len(txt_list)*9/10.0)) # 可修改百分比
num_trainval.sort()
print(num_trainval, end = '\n\n')
num_train = random.sample(num_trainval,math.floor(len(num_trainval)*8/9.0)) # 可修改百分比
num_train.sort()
print(num_train, end = '\n\n')
num_val = list(set(num_trainval).difference(set(num_train)))
num_val.sort()
print(num_val, end = '\n\n')
num_test = list(set(txt_list).difference(set(num_trainval)))
num_test.sort()
print(num_test, end = '\n\n')
# pdb.set_trace() # wjg
Main_path = '/home/jingge/hdd2T/faster-rcnn.pytorch/data/VOCdevkit/VOC2007/ImageSets/Main/'
train_test_name = ['trainval','train','val','test']
category_name = ['van', 'tram', 'car', 'pedestrian', 'truck']#修改类别
# 循环写trainvl train val test
for item_train_test_name in train_test_name:
list_name = 'num_'
list_name += item_train_test_name
train_test_txt_name = Main_path + item_train_test_name + '.txt'
try:
# 写单个文件
with open(train_test_txt_name, 'w') as w_tdf:
# 一行一行写
for item in eval(list_name):
w_tdf.write(item+'\n')
# 循环写Car Pedestrian Cyclist
for item_category_name in category_name:
category_txt_name = Main_path + item_category_name + '_' + item_train_test_name + '.txt'
with open(category_txt_name, 'w') as w_tdf:
# 一行一行写
for item in eval(list_name):
w_tdf.write(item+' '+ get_sample_value(item, item_category_name)+'\n')
except IOError as ioerr:
print('File error:'+str(ioerr))
六、代码的修改
数据集准备完毕,接下来该改一下运行训练的代码了。
参考:
https://blog.youkuaiyun.com/weixin_43380510/article/details/83004127
2019.4.14
昨天晚上加今天早上又作了如下几个修改:
- 将
data/VOCdevkit/VOC2007/JPEGImages/training/image_2
下的jpg图片全部都转到了./JPEGImages/
下面,这是参考原VOC2007数据集发现的,如下图。另外这样修改了之后在txt_to_xml.py中的图片路径要相应修改。所以昨天凌晨没有跑出来,今天上午改了之后,其实重新把三个数据处理的py文件又重新跑了一次,即重新生成了xml文件和区分训测的txt。
- **注意每次重新训练之前一定要删除data/cache!**这是因为在trainval_net.py引用的combiled_roidb函数中,有引用roidb.py下的一个prepare_roidb函数,而这个函数呢,有这么一段:
def prepare_roidb(imdb):
"""Enrich the imdb's roidb by adding some derived quantities that
are useful for training. This function precomputes the maximum
overlap, taken over ground-truth boxes, between each ROI and
each ground-truth box. The class with maximum overlap is also
recorded.
"""
roidb = imdb.roidb
if not (imdb.name.startswith('coco')):
cache_file = os.path.join(imdb.cache_path, imdb.name + '_sizes.pkl')
if os.path.exists(cache_file):
print('Image sizes loaded from %s' % cache_file)
with open(cache_file, 'rb') as f:
sizes = pickle.load(f)
else:
print('Extracting image sizes... (It may take long time)')
sizes = [PIL.Image.open(imdb.image_path_at(i)).size
for i in range(imdb.num_images)]
with open(cache_file, 'wb') as f:
pickle.dump(sizes, f)
print('Done!!')
就是说只有cache里面没有东西,才会去提取图片,否则会提取cache路径下的内容!博客https://blog.youkuaiyun.com/yutingzhaomeng/article/details/80328422 中有特别强调!
- 然后再进行代码的修改,也就是faster-rcnn.pytorch/lib/datasets/pascal_voc.py文件中的类别:**在pascal_voc.py中修改自己要检测的类别的时候,不要把backgroud删掉了!**https://blog.youkuaiyun.com/yutingzhaomeng/article/details/80328422 的说法是,保留background类,用于训练RPN网络,视作RPN网络的负样本。
''' wjg
self._classes = ('__background__', # always index 0
'aeroplane', 'bicycle', 'bird', 'boat',
'bottle', 'bus', 'car', 'cat', 'chair',
'cow', 'diningtable', 'dog', 'horse',
'motorbike', 'person', 'pottedplant',
'sheep', 'sofa', 'train', 'tvmonitor')
'''
self._classes = ('__background__','pedestrian', 'car', 'truck', 'tram', 'van')
其他我没有动。
七、训练
【5.3】试一试断电时能不能不删除cache直接接着读取cache继续训练
- 先跑一个epoch试试看,
batchsize=4,然后lr和lr_decay都用默认的就是作者给出的第一个结果的参数,先试试看所以不用默认的epoch=20。见下图:
CUDA_VISIBLE_DEVICES=1 python trainval_net.py --dataset pascal_voc --net vgg16 --epochs 1 --bs 4 --nw 1 --cuda
然后出错了:
- 修改参数batch4为1,删除cache,重新跑
CUDA_VISIBLE_DEVICES=1 python trainval_net.py --dataset pascal_voc --net vgg16 --epochs 1 --bs 1 --nw 1 --cuda
这次似乎没有问题:
跑了1个epoch,生成的结果在models文件夹下,如下所示:
八、测试
- 跑一下测试,checksession和checkepoch都用了默认的1
python test_net.py --dataset pascal_voc --net vgg16 --checkpoint 10021 --cuda --load_dir ./data/pretrained_model/
- 报错:
- 参考文章 https://blog.youkuaiyun.com/lvsehaiyang1993/article/details/80910104 的做法,根据提示去找了/lib/datasets/voc_eval.py文件,然后看到上面报错的pose,然后注释掉了;再跑,就会出现truncated的报错,然后把它也注释掉。然后跑竟然出来了:
上面修改的一段放在下面,以防之后因为我的修改出现问题:
# /lib/datasets/voc_eval.py
def parse_rec(filename):
""" Parse a PASCAL VOC xml file """
tree = ET.parse(filename)
objects = []
for obj in tree.findall('object'):
obj_struct = {}
obj_struct['name'] = obj.find('name').text
# wjg obj_struct['pose'] = obj.find('pose').text
# wjg obj_struct['truncated'] = int(obj.find('truncated').text)
obj_struct['difficult'] = int(obj.find('difficult').text)
bbox = obj.find('bndbox')
obj_struct['bbox'] = [int(bbox.find('xmin').text),
int(bbox.find('ymin').text),
int(bbox.find('xmax').text),
int(bbox.find('ymax').text)]
objects.append(obj_struct)
return objects
九、demo效果
- 修改demo.py中类别
# wjg 19.4.14
'''
pascal_classes = np.asarray(['__background__',
'aeroplane', 'bicycle', 'bird', 'boat',
'bottle', 'bus', 'car', 'cat', 'chair',
'cow', 'diningtable', 'dog', 'horse',
'motorbike', 'person', 'pottedplant',
'sheep', 'sofa', 'train', 'tvmonitor'])
'''
pascal_classes = np.asarray(['__background__','pedestrian', 'car', 'truck', 'tram', 'van']) # wjg
- 修改要测试的图片
保留一张原来VOC2007中的图片
再去KITTI挖两张——无奈系统资源不足?打不开图片文件夹?
先测一张吧那就。 - 修改读取模型的路径
parser.add_argument('--load_dir', dest='load_dir',
help='directory to load models',
default="/home/jingge/hdd2T/faster-rcnn.pytorch/models") #wjg 4.14 for KITTI
- 运行
python demo.py --net vgg16 --checksession 1 --checkepoch 1 --checkpoint 13463 --cuda
- 结果
- 跟之前那个直接用作者的第一个上传模型相比,检测出了那个遮挡的车!
- 晚上回寝室,可以开始跑多几轮的KITTI了,一晚上训练
- 或者先看看多尺度?
参考文章 https://blog.youkuaiyun.com/lkl3346/article/details/42268805 用命令实现只要主机(也就是目的电脑)在运行,程序就会在运行,直到命令执行结束。
nohup command & #后台主机运行程序command
命令是否运行,可使用下面命令看看里面的内容是否变化
vim nohup.out
- 搞了一张其他地方的图来跑demo,一轮训练的结果果然很差:
- 晚上后台跑个epoch 20的训练试试看:)
2019.4.15
昨晚运行下面命令,没有指定gpu,然后6个epoch,让他在后台运行:
#CUDA_VISIBLE_DEVICES=1 python trainval_net.py --dataset pascal_voc --net vgg16 --bs 1 --nw 1 --cuda
nohup python trainval_net.py --dataset pascal_voc --net vgg16 --epochs 6 --bs 1 --nw 1 --cuda &
上午起来发现昨天训练的晚上0点20就停止了,出现了跟前面一样的问题:
- 删除所有不想关的生成的内容重来
python trainval_net.py --dataset pascal_voc --net vgg16 --epochs 6 --bs 1 --nw 1 --cuda &
- 结果一样在上次的地方出错
- 删除data/cache文件夹,删除_init_paths.pyc,删除output文件夹,删除models文件夹,删除data/pretrained/vgg16/pascal_voc文件夹下之前下载的作者训练好的1个g的pth文件(是训练好的模型)
- 发现没有加GPU,这次加上GPU试试下面的:
CUDA_VISIBLE_DEVICES=1 python trainval_net.py --dataset pascal_voc --net vgg16 --epochs 6 --bs 1 --nw 1 --cuda > output.log 2>&1 &
使用上面的命令,可以在后台进行运行:
可以看到它越过了之前出错的5300的迭代次数:
试了一下断开网络,重开一个终端,看到训练还在继续进行,到了6400:
再试一次断网重连,nozuonodie,然后我惊喜地看到,迭代到了6800,仍在默默地继续中:
- 所以这次应该可以晚上好好睡觉让它训练了。总结一下之前出错的原因点,估计是两点:
- 清理不需要的内容,重点是cache文件夹
- 指定GPU号码来运行,看一下我的程序在1号gpu的运行情况:
- 另外注意到,在我没有手动关闭或者exit最初那个执行&后台程序的putty终端的情况下,在断网两次之后,它依然是保持的active的状态,而后来两个打开检查断网情况的终端都变成了inactive:
然后。。。第一epoch快完的时候还是出现了这个问题:
- 尝试epoch=2, bs=8
- 没有用
2019.4.16 训测KITTI数据集
- 训练:
参考 https://github.com/jwyang/faster-rcnn.pytorch/issues/136
等相关问题修改,然后运行:
CUDA_VISIBLE_DEVICES=0 python trainval_net.py --dataset pascal_voc --net vgg16 --epochs 6 --bs 1 --nw 1 --cuda >output.log 2>&1 &
早上起来普天同庆:
十一点训练完了thank god:
可以看到,生成一个epoch的模型用了两个小时,跟之前的一样:
- 测试:
python test_net.py --dataset pascal_voc --net vgg16 --checkepoch 6 --checkpoint 13463 --cuda
mAP=0,8428约等于0.843哈哈哈哈
- demo:
先从KITTI搞几张新图片,复制到images里面重命名
# 复制
faster-rcnn.pytorch/data/VOCdevkit/VOC2007/JPEGImages/testing/image_2$ cp -i 000000.jpg /home/jingge/hdd2T/faster-rcnn.pytorch/images/
# 重命名
faster-rcnn.pytorch/images$ mv 000000.jpg img3.jpg
# 直接复制加重命名
faster-rcnn.pytorch/data/VOCdevkit/VOC2007/JPEGImages/testing/image_2$ cp -i 000001.jpg /home/jingge/hdd2T/faster-rcnn.pytorch/images/img4.jpg
faster-rcnn.pytorch/data/VOCdevkit/VOC2007/JPEGImages/testing/image_2$ cp -i 000002.jpg /home/jingge/hdd2T/faster-rcnn.pytorch/images/img5.jpg
运行:
python demo.py --net vgg16 --checksession 1 --checkepoch 6 --checkpoint 13463 --cuda
看看可视化的结果:
① 右边是新的,左边是epoch=1的结果,科技看到检测出了一个最左边遮挡的车,一共八个框,比左边多一个。
②这张结果还是不好
③这张没有检测出来:
④这张还可以:
⑤这张很多小车没检测出来啊啊啊啊:
- 备份images文件夹下的结果备查,删除images下产生的det照片以便之后再检测不会被当成原图片
faster-rcnn.pytorch$ cp -r ./images /home/jingge/hdd2T/backup/images0416/
- 后来还是觉得检测出来框有问题,太少了,参考文章里面的说法增加了数量:
结果还真是变多了,右边是从10变成20:
再改成50,其实问题还是挺多的,很多检测不出来:
所以说还是算法本身的问题,不是这个参数设置的问题。
参考下面的文章进行后台运行程序和查询程序进程的操作
https://blog.youkuaiyun.com/Dby_freedom/article/details/80212107
https://blog.youkuaiyun.com/qq_16583687/article/details/77330722
nohup 命令 > output.log 2>&1 &
让命令在后台执行
我用到的是命令 > output.log 2>&1 &
cat output.log
查看日志
按照https://blog.youkuaiyun.com/qq_16583687/article/details/77330722 一文的说法,nohup命令可以让你的shell命令忽略SIGHUP信号,即可以使之脱离终端运行;“&”可以让你的命令在后台运行。以脱离终端的方式在后台运行shell命令有这样几个好处:只要你执行过了命令,那么你的网络中断不会对你有任何影响,并且你就可以关闭终端软件了。注意这个时候要关putty最好用exit的方式,而不是直接×
我猜我只用了&没有用nohup可能只能断网不能exit开putty软件。
每次如果需要更换数据集
先删除原来数据集的软连接
替换掉数据集里面的内容之后
记得把软连接补回来!