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