博客專欄

EEPW首頁(yè) > 博客 > pytorch可視化教程:訓(xùn)練過(guò)程+網(wǎng)絡(luò)結(jié)構(gòu)(2)

pytorch可視化教程:訓(xùn)練過(guò)程+網(wǎng)絡(luò)結(jié)構(gòu)(2)

發(fā)布人:計(jì)算機(jī)視覺工坊 時(shí)間:2022-09-26 來(lái)源:工程師 發(fā)布文章
2.2 HiddenLayer可視化訓(xùn)練過(guò)程

tensorboard的圖像很華麗,但是使用過(guò)程相較于其他的工具包較為繁瑣,所以小網(wǎng)絡(luò)一般沒必要使用tensorboard。

 import hiddenlayer as hl
 import time
 
 # 記錄訓(xùn)練過(guò)程的指標(biāo)
 history = hl.History()
 # 使用canvas進(jìn)行可視化
 canvas = hl.Canvas()
 
 # 獲取優(yōu)化器和損失函數(shù)
 optimizer = torch.optim.Adam(MyConvNet.parameters(), lr=3e-4)
 loss_func = nn.CrossEntropyLoss()
 log_step_interval = 100      # 記錄的步數(shù)間隔
 
 for epoch in range(5):
     print("epoch:", epoch)
     # 每一輪都遍歷一遍數(shù)據(jù)加載器
     for step, (x, y) in enumerate(train_loader):
         # 前向計(jì)算->計(jì)算損失函數(shù)->(從損失函數(shù))反向傳播->更新網(wǎng)絡(luò)
         predict = MyConvNet(x)
         loss = loss_func(predict, y)
         optimizer.zero_grad()   # 清空梯度(可以不寫)
         loss.backward()     # 反向傳播計(jì)算梯度
         optimizer.step()    # 更新網(wǎng)絡(luò)
         global_iter_num = epoch * len(train_loader) + step + 1  # 計(jì)算當(dāng)前是從訓(xùn)練開始時(shí)的第幾步(全局迭代次數(shù))
         if global_iter_num % log_step_interval == 0:
             # 控制臺(tái)輸出一下
             print("global_step:{}, loss:{:.2}".format(global_iter_num, loss.item()))
             # 在測(cè)試集上預(yù)測(cè)并計(jì)算正確率
             test_predict = MyConvNet(test_data_x)
             _, predict_idx = torch.max(test_predict, 1)  # 計(jì)算softmax后的最大值的索引,即預(yù)測(cè)結(jié)果
             acc = accuracy_score(test_data_y, predict_idx)
 
             # 以epoch和step為索引,創(chuàng)建日志字典
             history.log((epoch, step),
                         train_loss=loss,
                         test_acc=acc,
                         hidden_weight=MyConvNet.fc[2].weight)
 
             # 可視化
             with canvas:
                 canvas.draw_plot(history["train_loss"])
                 canvas.draw_plot(history["test_acc"])
                 canvas.draw_image(history["hidden_weight"])

不同于tensorboard,hiddenlayer會(huì)在程序運(yùn)行的過(guò)程中動(dòng)態(tài)生成圖像,而不是模型訓(xùn)練完后

下面為模型訓(xùn)練的某一時(shí)刻的截圖:

圖片

三、使用Visdom進(jìn)行可視化

Visdom是Facebook為pytorch開發(fā)的一塊可視化工具。類似于tensorboard,visdom也是通過(guò)在本地啟動(dòng)前端服務(wù)器來(lái)實(shí)現(xiàn)可視化的,而在具體操作上,visdom又類似于matplotlib.pyplot。所以使用起來(lái)很靈活。

首先先安裝visdom庫(kù),然后補(bǔ)坑。由于啟動(dòng)前端服務(wù)器需要大量依賴項(xiàng),所以在第一次啟動(dòng)時(shí)可能會(huì)很慢(需要下載前端三板斧的依賴項(xiàng)),解決方法請(qǐng)見這里。

先導(dǎo)入需要的第三方庫(kù):

 from visdom import Visdom
 from sklearn.datasets import  load_iris
 import torch
 import numpy as np
 from PIL import Image

matplotlib里,用戶繪圖可以通過(guò)plt這個(gè)對(duì)象來(lái)繪圖,在visdom中,同樣需要一個(gè)繪圖對(duì)象,我們通過(guò)vis = Visdom()來(lái)獲取。具體繪制時(shí),由于我們會(huì)一次畫好幾張圖,所以visdom要求用戶在繪制時(shí)指定當(dāng)前繪制圖像的窗口名字(也就是win這個(gè)參數(shù));除此之外,為了到時(shí)候顯示的分塊,用戶還需要指定繪圖環(huán)境env,這個(gè)參數(shù)相同的圖像,最后會(huì)顯示在同一張頁(yè)面上。

繪制線圖(相當(dāng)于matplotlib中的plt.plot)

 # 繪制圖像需要的數(shù)據(jù)
 iris_x, iris_y = load_iris(return_X_y=True)
 
 # 獲取繪圖對(duì)象,相當(dāng)于plt
 vis = Visdom()
 
 # 添加折線圖
 x = torch.linspace(-66100).view([-11])
 sigmoid = torch.nn.Sigmoid()
 sigmoid_y = sigmoid(x)
 tanh = torch.nn.Tanh()
 tanh_y = tanh(x)
 relu = torch.nn.ReLU()
 relu_y = relu(x)
 # 連接三個(gè)張量
 plot_x = torch.cat([x, x, x], dim=1)
 plot_y = torch.cat([sigmoid_y, tanh_y, relu_y], dim=1)
 # 繪制線性圖
 vis.line(X=plot_x, Y=plot_y, win="line plot", env="main",
          opts={
              "dash" : np.array(["solid""dash""dashdot"]),
              "legend" : ["Sigmoid""Tanh""ReLU"]
          })

繪制散點(diǎn)圖:

 # 繪制2D和3D散點(diǎn)圖
 # 參數(shù)Y用來(lái)指定點(diǎn)的分布,win指定圖像的窗口名稱,env指定圖像所在的環(huán)境,opts通過(guò)字典來(lái)指定一些樣式
 vis.scatter(iris_x[ : , 0 : 2], Y=iris_y+1, win="windows1", env="main")
 vis.scatter(iris_x[ : , 0 : 3], Y=iris_y+1, win="3D scatter", env="main",
             opts={
                 "markersize" : 4,   # 點(diǎn)的大小
                 "xlabel" : "特征1",
                 "ylabel" : "特征2"
             })

繪制莖葉圖:

 # 添加莖葉圖
 x = torch.linspace(-66100).view([-11])
 y1 = torch.sin(x)
 y2 = torch.cos(x)
 
 # 連接張量
 plot_x = torch.cat([x, x], dim=1)
 plot_y = torch.cat([y1, y2], dim=1)
 # 繪制莖葉圖
 vis.stem(X=plot_x, Y=plot_y, win="stem plot", env="main",
          opts={
              "legend" : ["sin""cos"],
              "title" : "莖葉圖"
          })

繪制熱力圖:

 # 計(jì)算鳶尾花數(shù)據(jù)集特征向量的相關(guān)系數(shù)矩陣
 iris_corr = torch.from_numpy(np.corrcoef(iris_x, rowvar=False))
 # 繪制熱力圖
 vis.heatmap(iris_corr, win="heatmap", env="main",
             opts={
                 "rownames" : ["x1""x2""x3""x4"],
                 "columnnames" : ["x1""x2""x3""x4"],
                 "title" : "熱力圖"
             })

可視化圖片,這里我們使用自定義的env名MyPlotEnv

 # 可視化圖片
 img_Image = Image.open("./example.jpg")
 img_array = np.array(img_Image.convert("L"), dtype=np.float32)
 img_tensor = torch.from_numpy(img_array)
 print(img_tensor.shape)
 
 # 這次env自定義
 vis.image(img_tensor, win="one image", env="MyPlotEnv",
           opts={
               "title" : "一張圖像"
           })

可視化文本,同樣在MyPlotEnv中繪制:

 # 可視化文本
 text = "hello world"
 vis.text(text=text, win="text plot", env="MyPlotEnv",
          opts={
              "title" : "可視化文本"
          })
 

運(yùn)行上述代碼,再通過(guò)在終端中輸入python3 -m visdom.server啟動(dòng)服務(wù)器,然后根據(jù)終端返回的URL,在谷歌瀏覽器中訪問這個(gè)URL,就可以看到圖像了。

圖片圖片

在Environment中輸入不同的env參數(shù)可以看到我們?cè)诓煌h(huán)境下繪制的圖片。對(duì)于分類圖集特別有用。

在終端中按下Ctrl+C可以終止前端服務(wù)器。

進(jìn)一步

需要注意,如果你的前端服務(wù)器停掉了,那么所有的圖片都會(huì)丟失,因?yàn)榇藭r(shí)的圖像的數(shù)據(jù)都是駐留在內(nèi)存中,而并沒有dump到本地磁盤。那么如何保存當(dāng)前visdom中的可視化結(jié)果,并在將來(lái)復(fù)用呢?其實(shí)很簡(jiǎn)單,比如我現(xiàn)在有一堆來(lái)之不易的Mel頻譜圖:

圖片

點(diǎn)擊Manage Views

圖片

點(diǎn)擊fork->save:(此處我只保存名為normal的env)

圖片

接著,在你的User目錄下(Windows是C:\Users\賬戶.visdom文件夾,Linux是在~.visdom文件夾下),可以看到保存好的env:

圖片

它是以json文件格式保存的,那么如果你保存完后再shut down當(dāng)前的前端服務(wù)器,圖像數(shù)據(jù)便不會(huì)丟失。

好的,現(xiàn)在在保存完你珍貴的數(shù)據(jù)后,請(qǐng)關(guān)閉你的visdom前端服務(wù)器。然后再啟動(dòng)它。

如何查看保存的數(shù)據(jù)呢?很簡(jiǎn)答,下次打開visdom前端后,visdom會(huì)在.visdom文件夾下讀取所有的保存數(shù)據(jù)完成初始化,這意味著,你直接啟動(dòng)visdom,其他什么也不用做就可以看到之前保存的數(shù)據(jù)啦!

那么如何服用保存的數(shù)據(jù)呢?既然你都知道了visdom保存的數(shù)據(jù)在哪里,那么直接通過(guò)python的json包來(lái)讀取這個(gè)數(shù)據(jù)文件,然后做解析就可以了,這是方法一,演示如下:

import json

with open(r"...\.visdom\normal.json""r", encoding="utf-8"as f:
    dataset : dict = json.load(f)

jsons : dict = dataset["jsons"]      # 這里存著你想要恢復(fù)的數(shù)據(jù)
reload : dict = dataset["reload"]    # 這里存著有關(guān)窗口尺寸的數(shù)據(jù) 

print(jsons.keys())     # 查看所有的win

out:

dict_keys(['jsons''reload'])
dict_keys(['1.wav''2.wav''3.wav''4.wav''5.wav''6.wav''7.wav''8.wav''9.wav''10.wav''11.wav''12.wav''13.wav''14.wav'])

但這么做不是很優(yōu)雅,所以visdom封裝了第二種方法。你當(dāng)然可以通過(guò)訪問文件夾.visdom來(lái)查看當(dāng)前可用的env,但是也可以這么做:

from visdom import Visdom

vis = Visdom()
print(vis.get_env_list())

out:

Setting up a new session...
['main''normal']

在獲取了可用的環(huán)境名后,你可以通過(guò)get_window_data方法來(lái)獲取指定env、指定win下的圖像數(shù)據(jù)。請(qǐng)注意,該方法返回str,故需要通過(guò)json來(lái)解析:

from visdom import Visdom
import json

vis = Visdom()

window = vis.get_window_data(win="1.wav", env="normal")    
window = json.loads(window)         # window 是 str,需要解析為字典

content = window["content"]
data = content["data"][0]
print(data.keys())

out:

Setting up a new session...
dict_keys(['z''x''y''zmin''zmax''type''colorscale'])

通過(guò)索引這些keys,相信想復(fù)用原本的圖像數(shù)據(jù)并不困難。


本文僅做學(xué)術(shù)分享,如有侵權(quán),請(qǐng)聯(lián)系刪文。


*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。



關(guān)鍵詞: AI

相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉