前言
代碼倉庫地址:https://github.com/Oneflow-Inc/one-yolov5歡迎star one-yolov5項(xiàng)目 獲取最新的動(dòng)態(tài)。如果您有問題,歡迎在倉庫給我們提出寶貴的意見。如果對(duì)您有幫助,歡迎來給我Star呀~
源碼解讀:https://github.com/Oneflow-Inc/one-yolov5/blob/main/utils/callbacks.py 。文章里面的超鏈接可能被公眾號(hào)吃掉,可以考慮到我們的文檔網(wǎng)站閱讀獲得更好的體驗(yàn):https://start.oneflow.org/oneflow-yolo-doc/source_code_interpretation/callbacks_py.html
這個(gè)文件是yolov5的Callback utils
鉤子
Hookhook(鉤子)是一個(gè)編程機(jī)制,與語言無關(guān),通常用于在不修改原始代碼的情況下,捕獲或替換程序的一些函數(shù)或API調(diào)用。
個(gè)人觀點(diǎn):鉤子是指將代碼插入到其他代碼的執(zhí)行流程中的技術(shù),從而實(shí)現(xiàn)在執(zhí)行原有代碼之前或之后執(zhí)行額外代碼的目的,下面是一個(gè)簡(jiǎn)單demo。
defhook_function(original_function):
#定義鉤子函數(shù)
defnew_function(*args,**kwargs):
print("Beforeoriginalfunction")
result=original_function(*args,**kwargs)
print("Afteroriginalfunction")
returnresult
returnnew_function
@hook_function
deforiginal_function():
#@hook_function(python語法)等價(jià)于hook_function(original_function)
print("Originalfunction")
if__name__=="__main__":
original_function()
輸出
Beforeoriginalfunction
Originalfunction
Afteroriginalfunction
回調(diào)函數(shù)
來源網(wǎng)絡(luò)的例子,有一家旅館提供叫醒服務(wù),但是要求旅客自己決定叫醒的方法。可以是打客房電話,也可以是派服務(wù)員去敲門,睡得死怕耽誤事的,還可以要求往自己頭上澆盆水。這里,“叫醒”這個(gè)行為是旅館提供的,相當(dāng)于庫函數(shù),但是叫醒的方式是由旅客決定并告訴旅館的,也就是回調(diào)函數(shù)。而旅客告訴旅館怎么叫醒自己的動(dòng)作,也就是把回調(diào)函數(shù)傳入庫函數(shù)的動(dòng)作,稱為登記回調(diào)函數(shù)(to register a callback function)。如下圖所示(圖片來源:維基百科):
callback從上圖可以看到,回調(diào)函數(shù)通常和應(yīng)用處于同一抽象層(因?yàn)閭魅胧裁礃拥幕卣{(diào)函數(shù)是在應(yīng)用級(jí)別決定的)。而回調(diào)就成了一個(gè)高層調(diào)用底層,底層再回過頭來調(diào)用高層的過程。
簡(jiǎn)單來說:
- 一般函數(shù):function a(int a, String b),接收的參數(shù)是一般類型。
- 特殊函數(shù):function b(function c),接收的參數(shù)是一個(gè)函數(shù),c這個(gè)函數(shù)就叫回調(diào)函數(shù)。
個(gè)人觀點(diǎn):回調(diào)函數(shù)是指在代碼中被調(diào)用的一個(gè)函數(shù),它會(huì)對(duì)其他代碼的執(zhí)行造成影響,并在適當(dāng)?shù)臅r(shí)間進(jìn)行回調(diào),下面是一個(gè)簡(jiǎn)單demo。
defcallback_function(input_data):
#在回調(diào)函數(shù)中處理輸入數(shù)據(jù)
print("Inputdata:",input_data)
defmain(callback):
#調(diào)用回調(diào)函數(shù)
callback("HelloWorld")
if__name__=="__main__":
main(callback_function)
輸出
Inputdata:HelloWorld
總之,鉤子和回調(diào)函數(shù)是實(shí)現(xiàn)代碼間通信和協(xié)作的不同技術(shù),它們都可以用于實(shí)現(xiàn)代碼級(jí)別的自定義行為,只是函數(shù)的觸發(fā)時(shí)機(jī)有差異。
hook實(shí)現(xiàn)例子
hook函數(shù)是程序中預(yù)定義好的函數(shù),這個(gè)函數(shù)處于原有程序流程當(dāng)中(暴露一個(gè)鉤子出來)。我們需要再在有流程中鉤子定義的函數(shù)塊中實(shí)現(xiàn)某個(gè)具體的細(xì)節(jié),需要把我們的實(shí)現(xiàn),掛接或者注冊(cè)(register)到鉤子里,使得hook函數(shù)對(duì)目標(biāo)可用。
hook函數(shù)最常使用在某種流程處理當(dāng)中。這個(gè)流程往往有很多步驟。hook函數(shù)常常掛載在這些步驟中,為增加額外的一些操作,提供靈活性。
下面舉一個(gè)簡(jiǎn)單的例子,這個(gè)例子的目的是實(shí)現(xiàn)一個(gè)通過鉤子調(diào)用函數(shù)判斷字符串是否是"good"
#YOLOv5byUltralytics,GPL-3.0license
"""
Callbackutils
"""
classCallbacks:
""""
HandlesallregisteredcallbacksforYOLOv5Hooks
"""
def__init__(self):
#Definetheavailablecallbacks
self._callbacks={
"on_pretrain_routine_start":[],
}
self.stop_training=False#setTruetointerrupttraining
defregister_action(self,hook,name="",callback=None):
"""
Registeranewactiontoacallbackhook
Args:
hook:Thecallbackhooknametoregistertheactionto要向其注冊(cè)操作的回調(diào)鉤子名稱
name:Thenameoftheactionforlaterreference動(dòng)作的名稱,供以后參考
callback:Thecallbacktofire對(duì)fire的回調(diào)
"""
asserthookinself._callbacks,f"hook'{hook}'notfoundincallbacks{self._callbacks}"
assertcallable(callback),f"callback'{callback}'isnotcallable"
self._callbacks[hook].append({"name":name,"callback":callback})
defget_registered_actions(self,hook=None):
""""
Returnsalltheregisteredactionsbycallbackhook
Args:
hook:Thenameofthehooktocheck,defaultstoall
"""
returnself._callbacks[hook]ifhookelseself._callbacks
defrun(self,hook,*args,**kwargs):
"""
Loopthroughtheregisteredactionsandfireallcallbacks
Args:
hook:Thenameofthehooktocheck,defaultstoall
args:ArgumentstoreceivefromYOLOv5
kwargs:KeywordArgumentstoreceivefromYOLOv5
"""
asserthookinself._callbacks,f"hook'{hook}'notfoundincallbacks{self._callbacks}"
forloggerinself._callbacks[hook]:
logger["callback"](*args,**kwargs)
defon_pretrain_routine_start(good:str):
ifgood=="good":
print("isgood!")
else:
print("isbad!")
#初始化Callbacks對(duì)象
callbacks=Callbacks()
#要向其注冊(cè)操作的回調(diào)鉤子名稱
callbacks.register_action(hook="on_pretrain_routine_start",name="ss",callback=on_pretrain_routine_start)
#調(diào)用hook
callbacks.run("on_pretrain_routine_start","good")
#打印hook信息
callbacks.get_registered_actions("on_pretrain_routine_start")
is good
[{'name': 'ss',
'callback': }]
yolov5項(xiàng)目中
在yolov5訓(xùn)練流程中,hook函數(shù)體現(xiàn)在一個(gè)訓(xùn)練過程(不包括數(shù)據(jù)準(zhǔn)備),會(huì)輪詢多次訓(xùn)練集,每次稱為一個(gè)epoch,每個(gè)epoch又分為多個(gè)batch來訓(xùn)練。流程先后拆解成:
- 開始訓(xùn)練
- 訓(xùn)練一個(gè)epoch前
- 訓(xùn)練一個(gè)batch前
- 訓(xùn)練一個(gè)batch后
- 訓(xùn)練一個(gè)epoch后。
- 評(píng)估驗(yàn)證集
- 結(jié)束訓(xùn)練
這些步驟是穿插在訓(xùn)練一個(gè)batch數(shù)據(jù)的過程中,這些可以理解成是鉤子函數(shù),我們可能需要在這些鉤子函數(shù)中實(shí)現(xiàn)一些定制化的東西,比如在訓(xùn)練一個(gè)epoch后我們要保存下訓(xùn)練的損失。
#在train.py中hook注冊(cè)操作代碼
#Registeractions
forkinmethods(loggers):
callbacks.register_action(k,callback=getattr(loggers,k))
#YOLOv5byUltralytics,GPL-3.0license
"""
Callbackutils
"""
classCallbacks:
""""
HandlesallregisteredcallbacksforYOLOv5Hooks
"""
def__init__(self):
#Definetheavailablecallbacks
#定義些回調(diào)函數(shù),函數(shù)實(shí)現(xiàn)在utils/loggers/__init__.py
#github鏈接:https://github.com/Oneflow-Inc/one-yolov5/blob/main/utils/loggers/__init__.py
self._callbacks={
"on_pretrain_routine_start":[],
#https://github.com/Oneflow-Inc/one-yolov5/blob/88864544cd9fa9ddcbe35a28a0bcf2c674daeb97/utils/loggers/__init__.py#L118
"on_pretrain_routine_end":[],
"on_train_start":[],
"on_train_epoch_start":[],
"on_train_batch_start":[],
"optimizer_step":[],
"on_before_zero_grad":[],
"on_train_batch_end":[],
"on_train_epoch_end":[],
"on_val_start":[],
"on_val_batch_start":[],
"on_val_image_end":[],
"on_val_batch_end":[],
"on_val_end":[],
"on_fit_epoch_end":[],#fit=train+val
"on_model_save":[],
"on_train_end":[],
"on_params_update":[],
"teardown":[],
}
self.stop_training=False#setTruetointerrupttraining
defregister_action(self,hook,name="",callback=None):
"""
Registeranewactiontoacallbackhook
Args:
hook:Thecallbackhooknametoregistertheactionto
name:Thenameoftheactionforlaterreference
callback:Thecallbacktofire
"""
asserthookinself._callbacks,f"hook'{hook}'notfoundincallbacks{self._callbacks}"
assertcallable(callback),f"callback'{callback}'isnotcallable"
self._callbacks[hook].append({"name":name,"callback":callback})
defget_registered_actions(self,hook=None):
""""
Returnsalltheregisteredactionsbycallbackhook
Args:
hook:Thenameofthehooktocheck,defaultstoall
"""
returnself._callbacks[hook]ifhookelseself._callbacks
defrun(self,hook,*args,**kwargs):
"""
Loopthroughtheregisteredactionsandfireallcallbacks
Args:
hook:Thenameofthehooktocheck,defaultstoall
args:ArgumentstoreceivefromYOLOv5
kwargs:KeywordArgumentstoreceivefromYOLOv5
"""
asserthookinself._callbacks,f"hook'{hook}'notfoundincallbacks{self._callbacks}"
forloggerinself._callbacks[hook]:
logger["callback"](*args,**kwargs)
審核編輯 :李倩
-
函數(shù)
+關(guān)注
關(guān)注
3文章
4346瀏覽量
62973 -
代碼
+關(guān)注
關(guān)注
30文章
4827瀏覽量
69054 -
HOOK
+關(guān)注
關(guān)注
0文章
15瀏覽量
8417
原文標(biāo)題:《YOLOv5全面解析教程》?十五,YOLOv5 Callback機(jī)制解讀
文章出處:【微信號(hào):GiantPandaCV,微信公眾號(hào):GiantPandaCV】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論