老衲這裡有七條煉丹經驗傳授與你
作者&編輯:李中樑
前言
用深度學習做影象分類任務也有近一年時間了,從最初模型的準確率只有60%到後來訓練到80%,再到最後的90%+的準確率,摸索中踩了很多坑,也總結出了一些經驗。現在將一些自己覺得非常實用的模型訓練經驗寫下來作為記錄,也方便後來者借鑑驗證。
調參經驗
模型選擇
通常我會使用一個簡單的CNN模型(這個模型一般包含5個卷積層)將資料扔進去訓練跑出一個baseline,這一步工作主要是為了驗證資料集的質量。如果這個模型訓練結果很差就不要先除錯模型,需要檢查一下你的訓練集資料,看看影象的質量,影象標籤是否正確,模型的程式碼是否正確等等,否則就是在做無用功,畢竟:garbage in,garbage out超引數的選擇
調參是項技術活,調得好CVPR,調不好下海搬磚。
通常要選的超引數有卷積核大小和數目,批訓練(batch size)大小,優化函式(optimizer),學習率等等,一般來說卷積核用3*3或者5*5,batch szie 用16或者32不會過擬合,optimizer用Adam(學習率建議用論文中預設的,我試過調整Adam的學習率,效果或都沒有預設的好),啟用函式用relu這個應該是大家的共識吧。還有就是先跑幾百個epoch看loss的變化趨勢。資料預處理
訓練資料對模型的影響是決定性的,提高訓練資料的質量,就是在提高模型的準確率。
影象預處理的時候一般我會抽出部分影象觀察,對影象中的噪聲進行濾波,影象標籤要驗證一下,其他的預處理就結合實際情況來看了。一般來說,資料清洗的工作佔比是多於寫模型的工作(通常是7:3)。資料增強
資料增強已經是訓練深度網路的常規操作了,這味丹藥有利於增加訓練資料量,減少網路過擬合程度,男女老少,居家旅行必備良藥。
常用的資料增強方法包括:影象縮放,影象翻轉,影象裁剪,影象色彩的飽和度、亮度和對比度變換。
海康威視在ImageNet上曾經用過PCA Jittering方的法,但是由於這個方法的計算量過大,我沒有在自己的訓練中使用過。他們還使用了有監督的資料增強的方法,有興趣的同學可以研究一下。
資料不平衡的處理
如果訓練資料中各類樣本數目差距較大,很有可能會導致部分類別的準確率很低,從根本上解決樣本不平衡的問題就是要把樣本變平衡。
一種是增加樣本少的類別的影象數目,可以用上述資料增強的方法。
另一種就是直接將樣本多的類別影象數目減少,可以說是非常簡單粗暴了。
當然,也有人提出類別權重的方法,增加少樣本在訓練時的權重,間接地增強了影象數目。自己的資料生成器
當任務變得複雜,資料規模變大時,框架提供的介面不能滿足你的需求,這時你需要有自己的data generation function。例如,我使用keras時需要對輸入圖片進行多標籤任務的訓練,而keras本身不包含這樣的介面,所以需要自己實現一個data generation function。通過檢視官方文件和相關介面實現了一個多標籤資料生成器(寫這個資料生成器的時候被官方文件坑了一次,暫且不表,下次另起一文詳說),程式碼如下:
# 訓練集/測試集資料生成器,替換flow_from_directory()
def flow_from_2DList(directory=None, target_size=(256, 256),
color_mode='rgb', classes=None, class_mode='categorical',
batch_size=1, shuffle=True, seed=None, save_to_dir=None,
save_prefix='', save_format='png', follow_links=False,
subset=None, interpolation='nearest'):
"""
A DirectoryIterator yielding tuples of (x, y)
where x is a numpy array containing a batch of images
with shape (batch_size, *target_size, channels) and
y is a numpy array of corresponding labels.
"""
# 每個epoch都要shuffle資料集
random.shuffle(directory)
# 引數初始化
if directory is None: # python函式的預設引數如果是list這種可變型別,
# 需要在函式體內進行初始化,
# 否則會在上次的結果後繼續使用list
directory = [ [ 99999 for x in range(4) ] for y in range(batch_size) ]
list_len = len(directory)
print('\nlength of directory:', list_len, '\n\n')
print('\nbatch_size:', batch_size, '\n\n')
step = list_len//batch_size # 向下取整得到一個epoch需要多少個step
print('\nsetp:',step,'\n\n')
for i in range(step):
# 每行一個記錄讀取訓練/測試資料,返回(x,[y1,y2,y3])
batch_images = []
y_label_age = np.zeros((batch_size, 100))
y_label_sex = np.zeros((batch_size, 2))
y_label_sick = np.zeros((batch_size, 2))
batch_directory = directory[i*batch_size : (i+1)*batch_size].copy()
batch_size_num = 0 # 迴圈計數器
for record in batch_directory:
file_path = record[0]
image = cv2.imread(file_path)
image = cv2.resize(image, target_size)
batch_images.append(image)
age = record[1]
sex = record[2]
sick = record[3]
# 將age,sex,sick轉換成one-hot編碼
if age != 0:
age -= 1
age = to_categorical(age, num_classes = 100)
sex = to_categorical(sex-1, num_classes = 2)
sick = to_categorical(sick-1, num_classes = 2)
y_label_age[batch_size_num,:] = age
y_label_sex[batch_size_num,:] = sex
y_label_sick[batch_size_num,:] = sick
batch_size_num += 1
batch_images = np.array(batch_images)
y_labels = [y_label_age, y_label_sex, y_label_sick]
data = (batch_images, y_labels)
yield data
其他提示
當然,具體任務不同可能某些經驗不能適用,實踐是檢驗真理的唯一標準,祝大家煉丹愉快~
與我交流
github: https://github.com/keloli
blog: https://www.jianshu.com/u/d055ee434e59
往期回顧之作者李中樑
機器學習演算法工程師
一個用心的公眾號
長按,識別,加關注
進群,學習,得幫助
你的關注,我們的熱度,
我們一定給你學習最大的幫助