繪制loss曲線


第一步保存日志文件,用重定向即可:

$TOOLS/caffe train --solver=$SOLVERFILE 2>&1 |tee out.log

第二步直接繪制:

python plot_training_log.py 2 testloss.png out.log

這個plot_training_log.py在這個目錄下caffe-fast-rcnn/tools/extra

2是選擇畫哪種類型的圖片,具體數字是代表哪個類型可以查看幫助信息看到:

0: Test accuracy vs. Iters

1: Test accuracy vs. Seconds

2: Test loss vs. Iters

3: Test loss vs. Seconds

4: Train learning rate vs. Iters

5: Train learning rate vs. Seconds

6: Train loss vs. Iters

7: Train loss vs. Seconds

testloss.png是生成圖片的名字,要求必須是png類型的文件

out.log是之前生成的日志文件

 

有個教程讓你先生成解析日志文件:

python parse_log.py out.log ./

注意最后一個是./,是保存的路徑,最后會生成.train和.test兩個文件。

實際上我覺得沒有必要執行這一步,直接繪制曲線就好,繪制曲線中間也會生成這兩個文件,因為plot_training_log.py本身要調用parse_log.py的shell腳本。並且生成的文件第一行是自帶'#',但是用這個解析生成的反而是不帶的。

 

跑項目代碼時,生成的日志文件有一點問題,一個正常的日志文件應該是這樣:

而我的日志文件是這樣;

即在Iteration前我的日志文件沒有I0619 10:29:45.757735  8944 solver.cpp:280] Solving deeplab_largeFOV 這句話,在parse_log.sh里有這樣一句:grep '] Solving ' $1 > aux3.txt,要尋找 '] Solving ',如果沒有,生成的aux3.txt就為空,

因為aux4.txt是由aux3.txt來的,這樣就無法生成aux4.txt,也就報錯說不能paste和rm aux4.txt。在extract_seconds.py中也是通過尋找sovling來確定開始時間的。如果單獨用parse_log.py生成日志文件,不會報aux4.txt的錯誤,但會報extract_seconds.py

的錯誤。所以在Iteration 0前面一行加上沒有這句話,就能解決問題。

 

在parse_log.sh中兩行代碼:

grep '] Solving ' $1 > aux3.txt
# grep 'Testing net' $1 >> aux3.txt
grep 'Train net' $1 >> aux3.txt

這兩行代碼都是搜索含有字符串的行然后寫入aux3.txt。因為我的日志中沒有] Solving,並且我的是訓練日志,也沒有Testing net,所以在沒有添加] Solving的時候去運行腳本就報了:無法paste,rm aux4.txt的錯誤。實際上是因為沒有任何東西寫進aux3.txt,

aux3.txt是空的,所以運行$DIR/extract_seconds.py aux3.txt aux4.txt就不會生成aux4.txt。當然也就沒辦法paste,rm。修改的方法可以把Testing net改成Train net,這樣可以在日志文件中找到行寫入aux3.txt。或者在日志中添加] Solving讓能有東西寫進

aux3.txt。其實兩種改法都可以,反正這個腳本之后要刪除這些臨時文件,然后再取生成.train的東西,這樣改只是為了讓程序不報錯,能正常運行。

 

 

plot_training_log.py.example里的load_data函數也需要改一下,原本的代碼是:

def load_data(data_file, field_idx0, field_idx1):
    data = [[], []]
    with open(data_file, 'r') as f:
        for line in f:
            line = line.strip()
            if line[0] != '#':
                fields = line.split()
                data[0].append(float(fields[field_idx0].strip()))
                data[1].append(float(fields[field_idx1].strip()))
return data

這段代碼是從中間生成的日志文件讀取你需要項的數據,!= '#'其實就是不讀取第一行的項信息,這是中間文件:

直接用會報字符串無法轉換成float的錯誤,原代碼里對每一行split空格后生成的list,不是只含這4個數字的list,而是含有許多空格的list,所以當然float無法轉換空格字符。需要做的就是split掉所有的空格,生成一個大小為4只包含4個數字的list。

這里需要注意個問題,日志文件中兩個數字間的間隔的空格數是不一樣的,有的是4個,有的是5個,代碼需要實現無論多少個空格,都split掉。

修改代碼:

def load_data(data_file, field_idx0, field_idx1):
    data = [[], []]
    with open(data_file, 'r') as f:
        for line in f:
            line = line.strip()
            if line[0] != '#':
                line=','.join(filter(lambda x: x, line.split(' ')))
                print line
                fields = line.split(',')
                print fields
                data[0].append(float(fields[field_idx0].strip()))
                data[1].append(float(fields[field_idx1].strip()))
    return data

這個的filter實現了功能。filter先split生成了一個每個空格占一個位置的list。

以下這張圖大概模擬了一下過程:

可以看到split之后一個空格占一個位置。實際上我發現,這個filter函數,要想完成我需要的功能,只能處理list中任何帶空格的位置都只能有一個空格,如果包含兩個或其他多個,就不能實現過濾掉空格的功能,下圖做了演示:

實際上,這樣做很麻煩,split函數里面什么都不加,就會處理掉所有空格,無論空格多少個,即split(),如下圖:

 

 

我最初繪制的loss曲線是將日志中每個loss都顯示,但曲線誤差大,不平滑,不便於分析:

造成這種原因是,在項目中,batch_size大小是2,即每次處理兩張圖片,在終端每20個迭代期顯示一次loss,也就是每個loss是40張圖片的,有可能某幾張圖片的loss比較大,就造成這一段迭代期的誤差大。基於此,我將圖像顯示換成連續10個loss的平均,相當於200個迭代期的平均loss,這樣下來波動就小了很多,方便分析:

 

生成的日志文件,無論是40000迭代期,還是80000,在20000時都要進入ipython,造成日志的格式不對,導致畫不出圖像,在后面的迭代期也會顯示一些奇怪的東西,反正都需要刪除掉,之后就能正常顯示圖像:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM