1. 程式人生 > >老衲這裡有七條煉丹經驗傳授與你

老衲這裡有七條煉丹經驗傳授與你

作者&編輯:李中樑

前言

用深度學習做影象分類任務也有近一年時間了,從最初模型的準確率只有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方的法,但是由於這個方法的計算量過大,我沒有在自己的訓練中使用過。他們還使用了有監督的資料增強的方法,有興趣的同學可以研究一下。

640?wx_fmt=png

  • 資料不平衡的處理
    如果訓練資料中各類樣本數目差距較大,很有可能會導致部分類別的準確率很低,從根本上解決樣本不平衡的問題就是要把樣本變平衡。
    一種是增加樣本少的類別的影象數目,可以用上述資料增強的方法。
    另一種就是直接將樣本多的類別影象數目減少,可以說是非常簡單粗暴了。
    當然,也有人提出類別權重的方法,增加少樣本在訓練時的權重,間接地增強了影象數目。

  • 自己的資料生成器

    當任務變得複雜,資料規模變大時,框架提供的介面不能滿足你的需求,這時你需要有自己的data generation function。例如,我使用keras時需要對輸入圖片進行多標籤任務的訓練,而keras本身不包含這樣的介面,所以需要自己實現一個data generation function。通過檢視官方文件和相關介面實現了一個多標籤資料生成器(寫這個資料生成器的時候被官方文件坑了一次,暫且不表,下次另起一文詳說),程式碼如下:

# 訓練集/測試集資料生成器,替換flow_from_directory()
def flow_from_2DList(directory=None, target_size=(256256)
    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

往期回顧之作者李中樑


機器學習演算法工程師

                            一個用心的公眾號

640?wx_fmt=jpeg

長按,識別,加關注

進群,學習,得幫助

你的關注,我們的熱度,

我們一定給你學習最大的幫助