这个月在实习,一些杂活挺多的,由此也锻炼了一下自己,平时在实验室忽略的一些杂活也总算上手做了一遍(苍天饶过谁,23333333)
任务是在CentOS下,将1000多万张图片提取深度特征,并入到数据库里,使用到了CnnExtract(ResNet)模块、Clickhouse等
电脑配置:
CPU:E5-2690 v4
RAM:512 DDR4
固态硬盘:8 TB PCI-E
GPU: dual Nvidia-2080Ti
即使是这个配置,还是需要3天的时间才能入完,期间出现过各种问题:
1、中断异常处理:首先入库的脚本运行后,会异常终止,查看log文件发现1000多万张图片有一些是损坏的,于是在for循环中添加了异常处理,把个别坏图片忽略掉继续执行:
import requests
import json
import glob
import random
import os, sys
import datetime
import threading
if __name__=="__main__":
print 'start extract feat'
featapi = Api(('127.0.0.1',9981),3500) # cnn port
print 'start ise'
api=Api( ('127.0.0.1', 2007),3500 ) # clickhouse & TCP
def action(api, featapi, folder_path, dbname, i):
startfold = i*1+1
endfold = i*1+2
for fold in range(startfold, endfold):
folder = '{}/{}'.format(folder_path,fold) # folder = /home/pic_data/car/car_1
webfolder = '{}/{}'.format(webpath,fold)
img_lists = os.listdir(folder) #/car/car_1
for file in img_lists:
try:
file_path = '{}/{}'.format(folder,file) # name1.jpg
name = 'http://10.18.221.150' + webfolder + '/' + file
spt = 20191114172611
loadimg = loadfile(file_path)
feat = featapi.rt_cnn_img_extract(loadimg)
api.rt_push_img_rec_v2(dbname, 1, feat, 'SmallImgUrl,SnapTime', "%s,%d"%(name, spt))
except Exception as e:
print str(e)
print file_path
pass
continue
for i in range(0, 8):
t=threading.Thread(target=action,args=(api, featapi, folder_path, dbname, i))
t.start()
其中添加了try···except 可以输出错误信息,可以从中发现哪些图片是有问题的,可以输出打印它们后期将它们删除,pass 则可以忽略这些问题,continue 则能在循环里继续提取图片深度特征以及入库。
2、多线程:在任务中,定义了action函数,下面使用for循环定义了8个线程将变量包裹,加快图像处理速度,因为单线程太浪费资源了,算了一下要10多天天才能将图片入库完毕,这下多线程 2天多一些就可以完成 。
3、多线程设置完成后,并没有那么简单,因为是原生centOS,对汉字很不友好,汉字入库会出现乱码,因为入库的Url字段是和后续的FTP有关的,可以通过网页访问,但是乱码了就无法访问了,因此又对所有图片进行了rename:
import os
import re
import sys
path = "/home/pic_data/car/car1"
fileList = os.listdir(path)
print("before:" + str(fileList))
os.chdir(path)
num = 1
for fileName in fileList:
pat = ".+\.(jpg|jpeg|JPG)"
pattern = re.findall(pat, fileName)
print('pattern[0]:', pattern)
print('num:', num, 'filename:', fileName)
os.rename(fileName, ('name' + str(num) +'_'+ '.' + pattern[0]))
num = num + 1
print("---------------------------------------------------")
sys.stdin.flush()
print("after:" + str(os.listdir(path)))
rename这里很简单,car目录下有24个文件夹,用for循环将所有图片进行 ‘name’+str(num)+图片格式 的重命名,一共完成这个类别的1300多万张重命名。
4、文件夹下的图片按比例移动到多个文件夹:你以为这样就结束了吗?不是的。。。在千万级别的图像入库中,会出现运行一段时间速度降低的情况,这个可能涉及到底层,catch以及数据库原理等等一系列问题,在不了解的无法排查的情况下,只能使用分阶段处理的方法,将24个图像文件下面细分多个文件夹,并继续使用多线程处理。下面是某文件夹下图片按比例移动到多个文件夹下的脚本:
import os
import random
import shutil
from shutil import move
datadir_normal = "/home/pic_data/cars/car8/8/"
print datadir_normal
all_data = os.listdir(datadir_normal) #Ttupian
num_all_data = len(all_data)
print( "num_all_data: " + str(num_all_data) )
index_list = list(range(num_all_data))
#print(index_list)
random.shuffle(index_list)
num = 0
ADir = "/home/pic_data/cars/car17/1/"#(A)
BDir = '/home/pic_data/cars/car17/2/'#(B)
CDir = '/home/pic_data/cars/car17/3/'#(C)
DDir = '/home/pic_data/cars/car17/4/'#(D)
EDir = '/home/pic_data/cars/car17/5/'#(E)
FDir = '/home/pic_data/cars/car17/6/'#(F)
GDir = '/home/pic_data/cars/car17/7/'#(G)
HDir = '/home/pic_data/cars/car17/8/'#(H)
for i in index_list:
fileName = os.path.join(datadir_normal, all_data[i])
if num < num_all_data*0.125:
# print(str(fileName))
move(fileName, ADir)
elif num>num_all_data*0.125 and num < num_all_data*0.25:
print(str(fileName))
move(fileName, BDir)
elif num>num_all_data*0.25 and num < num_all_data*0.375:
move(fileName, CDir)
elif num>num_all_data*0.375 and num < num_all_data*0.5:
move(fileName, DDir)
elif num>num_all_data*0.5 and num < num_all_data*0.625:
move(fileName, EDir)
elif num>num_all_data*0.625 and num < num_all_data*0.75:
move(fileName, FDir)
elif num>num_all_data*0.75 and num < num_all_data*0.875:
move(fileName, GDir)
else:
move(fileName, HDir)
num += 1
其中num_all_data*0.125就是按1/8的比例进行移动的,且是随机、平均移动
以上对以后深度学习制作训练集测试集、网络搭建以及中断异常处理等等都很有作用,干了杂活也学到了东西,锻炼了一下自己。