異常
相比起其他一些語言,在Python中我們可以更大膽地使用異常,因為異常在Python中是非常常見的存在,比如下面這種簡單的遍歷:
a = ['Why', 'so', 'serious', '?']
for x in a:
print(x)
當(dāng)用for進(jìn)行遍歷時,會對要遍歷的對象調(diào)用iter()。這需要給對象創(chuàng)建一個迭代器用來依次返回對象中的內(nèi)容。為了能成功調(diào)用iter(),該對象要么得支持迭代協(xié)議(定義__iter__()),要么得支持序列協(xié)議(定義__getitem__())。當(dāng)遍歷結(jié)束時,__iter__()或者_(dá)_getitem__()都需要拋出一個異常。__iter__()會拋出StopIteration,而__getitem__()會拋出IndexError,于是遍歷就會停止。
在深度學(xué)習(xí)中,尤其是數(shù)據(jù)準(zhǔn)備階段,常常遇到IO操作。這時候遇到異常的可能性很高,采用異常處理可以保證數(shù)據(jù)處理的過程不被中斷,并對有異常的情況進(jìn)行記錄或其他動作:
for filepath in filelist: # filelist中是文件路徑的列表
try:
with open(filepath, 'r') as f:
# 執(zhí)行數(shù)據(jù)處理的相關(guān)工作
...
print('{} is processed!'.format(filepath))
except IOError:
print('{} with IOError!'.format(filepath))
# 異常的相應(yīng)處理
...
多進(jìn)程(multiprocessing)
深度學(xué)習(xí)中對數(shù)據(jù)高效處理常常會需要并行,這時多進(jìn)程就派上了用場。考慮這樣一個場景,在數(shù)據(jù)準(zhǔn)備階段,有很多文件需要運行一定的預(yù)處理,正好有臺多核服務(wù)器,我們希望把這些文件分成32份,并行處理:
from multiprocessing import Process#, freeze_support
def process_data(filelist):
for filepath in filelist:
print('Processing {} ...'.format(filepath))
# 處理數(shù)據(jù)
...
if __name__ == '__main__':
# 如果是在Windows下,還需要加上freeze_support()
#freeze_support()
# full_list包含了要處理的全部文件列表
...
n_total = len(full_list) # 一個遠(yuǎn)大于32的數(shù)
n_processes = 32
# 每段子列表的平均長度
length = float(n_total) / float(n_processes)
# 計算下標(biāo),盡可能均勻地劃分輸入文件列表
indices = [int(round(i*length)) for i in range(n_processes+1)]
# 生成每個進(jìn)程要處理的子文件列表
sublists = [full_list[indices[i]:indices[i+1]] for i in range(n_processes)]
# 生成進(jìn)程
processes = [Process(target=process_data, args=(x,)) for x in sublists]
# 并行處理
for p in processes:
p.start()
for p in processes:
p.join()
其中if __name__ == ‘__main__’用來標(biāo)明在import時不包含,但是作為文件執(zhí)行時運行的語句塊。為什么不用多線程呢?簡單說就是Python中線程的并發(fā)無法有效利用多核,如果有興趣的讀者可以從下面這個鏈接看起:
GlobalInterpreterLock – Python Wiki
os模塊
深度學(xué)習(xí)中的數(shù)據(jù)多是文件,所以數(shù)據(jù)處理階段和文件相關(guān)的操作就非常重要。除了文件IO,Python中一些操作系統(tǒng)的相關(guān)功能也能夠非常方便地幫助數(shù)據(jù)處理。想象一下我們有一個文件夾叫做data,下邊有3個子文件夾叫做cat,dog和bat,里面分別是貓,狗和蝙蝠的照片。為了訓(xùn)練一個三分類模型,我們先要生成一個文件,里面每一行是文件的路徑和對應(yīng)的標(biāo)簽。定義cat是0,dog是1,bat是2,則可以通過如下腳本:
import os
# 定義文件夾名稱和標(biāo)簽的對應(yīng)關(guān)系
label_map = {
'cat': 0,
'dog': 1,
'bat': 2
}
with open('data.txt', 'w') as f:
# 遍歷所有文件,root為當(dāng)前文件夾,dirs是所有子文件夾名,files是所有文件名
for root, dirs, files in os.walk('data'):
for filename in files:
filepath = os.sep.join([root, filename]) # 獲得文件完整路徑
dirname = root.split(os.sep)[-1] # 獲取當(dāng)前文件夾名稱
label = label_map[dirname] # 得到標(biāo)簽
line = '{},{}\n'.format(filepath, label)
f.write(line)
其中,os.sep是當(dāng)前操作系統(tǒng)的路徑分隔符,在Unix/Linux中是’/’,Windows中是’\\’。有的時候我們已經(jīng)有了所有的文件在一個文件夾data下,希望獲取所有文件的名稱,則可以用os.listdir():
filenames = os.listdir('data')
os也提供了諸如拷貝,移動和修改文件名等操作。同時因為大部分深度學(xué)習(xí)框架最常見的都是在Unix/Linux下使用,并且Unix/Linux的shell已經(jīng)非常強大(比Windows好用太多),所以只需要用字符串格式化等方式生成shell命令的字符串,然后通過os.system()就能方便實現(xiàn)很多功能,有時比os,還有Python中另一個操作系統(tǒng)相關(guān)模塊shutil還要方便:
import os, shutil
filepath0 = 'data/bat/IMG_000001.jpg'
filepath1 = 'data/bat/IMG_000000.jpg'
# 修改文件名
os.system('mv {} {}'.format(filepath0, filepath1))
#os.rename(filepath0, filepath1)
# 創(chuàng)建文件夾
dirname = 'data_samples'
os.system('mkdir -p {}'.format(dirname))
#if not os.path.exists(dirname):
# os.mkdir(dirname)
# 拷貝文件
os.system('cp {} {}'.format(filepath1, dirname))
#shutil.copy(filepath1, dirname)
評論
查看更多