From 1f99f399abe9afd4bfc6c8477b420759651f4b9d Mon Sep 17 00:00:00 2001 From: z1069614715 <1069614715@qq.com> Date: Wed, 28 Dec 2022 08:38:48 +0800 Subject: [PATCH] v1.3 --- README.md | 9 +- commad.txt | 19 + config/__pycache__/config.cpython-38.pyc | Bin 1099 -> 1099 bytes export.py | 3 +- metrice.py | 7 +- model/__init__.py | 3 +- model/__pycache__/__init__.cpython-38.pyc | Bin 466 -> 482 bytes model/__pycache__/convnext.cpython-38.pyc | Bin 9327 -> 9323 bytes model/__pycache__/cspnet.cpython-38.pyc | Bin 25129 -> 27163 bytes model/__pycache__/densenet.cpython-38.pyc | Bin 12329 -> 12416 bytes model/__pycache__/dpn.cpython-38.pyc | Bin 9915 -> 9911 bytes .../__pycache__/efficientnetv2.cpython-38.pyc | Bin 30994 -> 31851 bytes model/__pycache__/ghostnet.cpython-38.pyc | Bin 6895 -> 7899 bytes model/__pycache__/mnasnet.cpython-38.pyc | Bin 10030 -> 10663 bytes model/__pycache__/mobilenetv2.cpython-38.pyc | Bin 6937 -> 7648 bytes model/__pycache__/mobilenetv3.cpython-38.pyc | Bin 10162 -> 10891 bytes model/__pycache__/repghost.cpython-38.pyc | Bin 0 -> 14224 bytes model/__pycache__/repvgg.cpython-38.pyc | Bin 13001 -> 12997 bytes model/__pycache__/resnest.cpython-38.pyc | Bin 13035 -> 13947 bytes model/__pycache__/resnet.cpython-38.pyc | Bin 13285 -> 13873 bytes model/__pycache__/sequencer.cpython-38.pyc | Bin 15312 -> 15308 bytes model/__pycache__/shufflenetv2.cpython-38.pyc | Bin 8315 -> 8967 bytes model/__pycache__/vgg.cpython-38.pyc | Bin 9003 -> 9327 bytes model/__pycache__/vovnet.cpython-38.pyc | Bin 6791 -> 7733 bytes model/cspnet.py | 107 +++- model/densenet.py | 26 +- model/dpn.py | 1 - model/efficientnetv2.py | 35 +- model/ghostnet.py | 43 +- model/mnasnet.py | 25 +- model/mobilenetv2.py | 29 +- model/mobilenetv3.py | 23 +- model/repghost.py | 560 ++++++++++++++++++ model/resnest.py | 49 +- model/resnet.py | 46 +- model/shufflenetv2.py | 29 +- model/vgg.py | 11 +- model/vovnet.py | 40 +- predict.py | 8 +- processing.py | 1 - utils/__init__.py | 0 utils/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 140 bytes utils/__pycache__/utils.cpython-38.pyc | Bin 32957 -> 34789 bytes utils/__pycache__/utils_aug.cpython-38.pyc | Bin 6328 -> 6324 bytes .../__pycache__/utils_distill.cpython-38.pyc | Bin 5069 -> 5065 bytes utils/__pycache__/utils_fit.cpython-38.pyc | Bin 3398 -> 3394 bytes utils/__pycache__/utils_loss.cpython-38.pyc | Bin 3812 -> 3808 bytes utils/__pycache__/utils_model.cpython-38.pyc | Bin 2988 -> 3077 bytes utils/utils.py | 91 ++- utils/utils_model.py | 6 + v1.3-update_log.md | 6 + 51 files changed, 1102 insertions(+), 75 deletions(-) create mode 100644 commad.txt create mode 100644 model/__pycache__/repghost.cpython-38.pyc create mode 100644 model/repghost.py create mode 100644 utils/__init__.py create mode 100644 utils/__pycache__/__init__.cpython-38.pyc create mode 100644 v1.3-update_log.md diff --git a/README.md b/README.md index 2767e9b..4f00e49 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ image classifier implement in pytoch. type: string, default: acc, choices:['loss', 'acc', 'mean_acc'] 根据metrice选择的指标来进行保存best.pt. - **patience** - type: int, default:30 + type: int, default:30 早停法中的patience.(设置为0即为不使用早停法) - **imagenet_meanstd** default:False @@ -288,7 +288,7 @@ image classifier implement in pytoch. Example: transforms.Compose([transforms.RandomHorizontalFlip(p=0.5),transforms.RandomRotation(degrees=20),]) 自定义的数据增强. - **export.py** - 导出模型的文件.目前支持torchscript,onnx. + 导出模型的文件.目前支持torchscript,onnx,tensorrt. 参数解释: - **save_path** type: string, default: runs/exp @@ -334,7 +334,7 @@ image classifier implement in pytoch. | densenet | densenet121,densenet161,densenet169,densenet201 | | vgg | vgg11,vgg11_bn,vgg13,vgg13_bn,vgg16,vgg16_bn,vgg19,vgg19_bn | | efficientnet | efficientnet_b0,efficientnet_b1,efficientnet_b2,efficientnet_b3,efficientnet_b4,efficientnet_b5,efficientnet_b6,efficientnet_b7
efficientnet_v2_s,efficientnet_v2_m,efficientnet_v2_l | - | nasnet | mnasnet0_5,mnasnet1_0 | + | nasnet | mnasnet1_0 | | vovnet | vovnet39,vovnet59 | | convnext | convnext_tiny,convnext_small,convnext_base,convnext_large,convnext_xlarge | | ghostnet | ghostnet | @@ -343,6 +343,7 @@ image classifier implement in pytoch. | darknet | darknet53,darknetaa53 | | cspnet | cspresnet50,cspresnext50,cspdarknet53,cs3darknet_m,cs3darknet_l,cs3darknet_x,cs3darknet_focus_m,cs3darknet_focus_l
cs3sedarknet_l,cs3sedarknet_x,cs3edgenet_x,cs3se_edgenet_x | | dpn | dpn68,dpn68b,dpn92,dpn98,dpn107,dpn131 | + | repghost | repghostnet_0_5x,repghostnet_0_58x,repghostnet_0_8x,repghostnet_1_0x,repghostnet_1_11x
repghostnet_1_3x,repghostnet_1_5x,repghostnet_2_0x | @@ -610,7 +611,7 @@ image classifier implement in pytoch. - [ ] Accumulation Gradient - [ ] Model Ensembling - [ ] Freeze Training -- [ ] Support Fuse Conv and Bn +- [x] Support Fuse Conv and Bn - [x] Early Stop diff --git a/commad.txt b/commad.txt new file mode 100644 index 0000000..6f31bef --- /dev/null +++ b/commad.txt @@ -0,0 +1,19 @@ +python main.py --model_name efficientnet_v2_l --config config/config.py --batch_size 32 --Augment AutoAugment --save_path runs/efficientnet_v2_l --device 0 \ + --pretrained --amp --warmup --ema --imagenet_meanstd + +python main.py --model_name resnext50 --config config/config.py --batch_size 128 --Augment AutoAugment --save_path runs/resnext50 --device 1 \ + --pretrained --amp --warmup --ema --imagenet_meanstd + +python metrice.py --task fps --save_path runs/efficientnet_v2_l --batch_size 1 --device 0 +python metrice.py --task fps --save_path runs/efficientnet_v2_l --batch_size 1 --device 0 --half + +python metrice.py --task fps --save_path runs/resnext50 --batch_size 32 --device 0 +python metrice.py --task fps --save_path runs/resnext50 --batch_size 32 --device 0 --half + +python export.py --save_path runs/efficientnet_v2_l --export onnx --simplify --batch_size 1 +python metrice.py --task fps --save_path runs/efficientnet_v2_l --batch_size 1 --device 0 --model_type onnx + +python export.py --save_path runs/resnext50 --export onnx --simplify --batch_size 1 +python metrice.py --task fps --save_path runs/resnext50 --batch_size 1 --device 0 --model_type onnx + +python predict.py --source dataset/test/0000 --save_path runs/resnext50 --half --device 0 \ No newline at end of file diff --git a/config/__pycache__/config.cpython-38.pyc b/config/__pycache__/config.cpython-38.pyc index bcc2bcfe4a5c61e94d0f28343dbb6dbd84b2fa84..f6c4821f418903e48cd2a51701e8b9e8808113a7 100644 GIT binary patch delta 20 acmX@jahiiWl$V!_0SNA_ZQIE0zybg^@dY{n delta 20 acmX@jahiiWl$V!_0SJOROEz*lumAut`2>0Z diff --git a/export.py b/export.py index 38339be..8dbe92d 100644 --- a/export.py +++ b/export.py @@ -3,7 +3,7 @@ os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" import torch import torch.nn as nn -from utils.utils import select_device +from utils.utils import select_device, model_fuse def export_torchscript(opt, model, img, prefix='TorchScript'): print('Starting TorchScript export with pytorch %s...' % torch.__version__) @@ -104,6 +104,7 @@ def parse_opt(): assert not opt.dynamic, '--half not compatible with --dynamic' ckpt = torch.load(os.path.join(opt.save_path, 'best.pt')) model = ckpt['model'].float().to(DEVICE) + model_fuse(model) img = torch.rand((opt.batch_size, opt.image_channel, opt.image_size, opt.image_size)).to(DEVICE) return opt, (model.half() if opt.half else model), (img.half() if opt.half else img), DEVICE diff --git a/metrice.py b/metrice.py index 457091a..fe7aba9 100644 --- a/metrice.py +++ b/metrice.py @@ -6,7 +6,7 @@ os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" import numpy as np from utils import utils_aug -from utils.utils import classification_metrice, Metrice_Dataset, visual_predictions, visual_tsne, dict_to_PrettyTable, Model_Inference, select_device +from utils.utils import classification_metrice, Metrice_Dataset, visual_predictions, visual_tsne, dict_to_PrettyTable, Model_Inference, select_device, model_fuse torch.backends.cudnn.deterministic = True def set_seed(seed): @@ -56,7 +56,8 @@ def parse_opt(): fps_arr = [] for i in tqdm.tqdm(range(test_time + warm_up)): since = time.time() - model(inputs) + with torch.inference_mode(): + model(inputs) if i > warm_up: fps_arr.append(time.time() - since) fps = np.mean(fps_arr) @@ -85,7 +86,7 @@ def parse_opt(): if __name__ == '__main__': opt, model, test_dataset, DEVICE, CLASS_NUM, label, save_path = parse_opt() y_true, y_pred, y_score, y_feature, img_path = [], [], [], [], [] - with torch.no_grad(): + with torch.inference_mode(): for x, y, path in tqdm.tqdm(test_dataset, desc='Test Stage'): x = (x.half().to(DEVICE) if opt.half else x.to(DEVICE)) if opt.test_tta: diff --git a/model/__init__.py b/model/__init__.py index 0cbc7c7..ca792d1 100644 --- a/model/__init__.py +++ b/model/__init__.py @@ -13,4 +13,5 @@ from .repvgg import * from .sequencer import * from .cspnet import * -from .dpn import * \ No newline at end of file +from .dpn import * +from .repghost import * \ No newline at end of file diff --git a/model/__pycache__/__init__.cpython-38.pyc b/model/__pycache__/__init__.cpython-38.pyc index 242e622859af91c094728471fe3457ce8a7fe50b..177d5b3d08713780a783e58340ebb6b9b3581fb6 100644 GIT binary patch delta 102 zcmcb_{D_%1l$V!_0SIRJbtF%j$Scd(Gf}%miXnwDM<9eDg(;XplldhhP+pVq7NeG* zrr^Yba}}aEic$;GGxCc|Rx%Xv10})4FGKy@yb}H5l*Gvuj24UvlQ%LdGI1~h075bv AaR2}S delta 85 zcmaFFe2JMil$V!_0SJOROOk6Q^2#!{Ow?`>VhU!^WPZsA6xU?D#i-?{DKPQmT&a}| iMf^Y^F!9S&KO;XkRlhW;G_Ry|avP%squk{EjEVq{Y8IRT diff --git a/model/__pycache__/convnext.cpython-38.pyc b/model/__pycache__/convnext.cpython-38.pyc index ffc943e79c5efa36b6a2b5887c7c5bc049dd098b..37ce41d7ce2b1809ffef278a833a7688e0ffc689 100644 GIT binary patch delta 35 pcmaFw@!Eqsl$V!_0SNA_ZQIB_nT^v>KR2&Lzc?jv^J=yuvH;4T3%39O delta 39 tcmaFu@!o?wl$V!_0SJOROEz*(X5%&0&&bbB)h|sd%_}M0yqxWbECA}#48Q;Y diff --git a/model/__pycache__/cspnet.cpython-38.pyc b/model/__pycache__/cspnet.cpython-38.pyc index 88d3bccad3f0800167701c8cc365982ce2cc8cd5..aa6c3d8d8df7a0bd4c6600b388795a467eab7283 100644 GIT binary patch literal 27163 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 DBcjF9 diff --git a/model/__pycache__/densenet.cpython-38.pyc b/model/__pycache__/densenet.cpython-38.pyc index ee1bd52b7656883b9824931f3b13bc395a972a2d..448ea6bf3e79b981baf871b54132cd22404d527a 100644 GIT binary patch delta 3920 zcmai1O>i7X6`r1%{r}PGcfGP?{aLbBKP$nJOr-AofvZAQ%+|#f2)aT;M_#eKHgmU*G~95{mF%kF;7T7oCG(j8hDzuULmJY6KMh7rWv>BLYU^;<01WXHU1*R34 zE?~NWX`@MClH0R-@R#+nK1%3;4ZTWJwEeDb90sbNwZbF63wla{Rv&zuSr0w95u_co z6XXWyNR92@&}k11-1Qkp=;$7{mxh5Iq?tWz9}NPVrek$%Ri}q(2tsfGyZ4%EOc}>vMjB>J(V?Wmj>~!KIvFR*19I~j3=YHKWSzRtEI`Se zk{)-zte(%LRnbu>WvRK!@{2bd+g#wKqG>x>hnX~=b3}BZVly*WvR2IN*49IHj3m(c zJc1BKh`E1?wQT)Wv(?@X>~{i)Ksifk-df~`pqGvapIs?4&g1U7|I~C7iZ$baU+6X~ zEbsjpeY5J6#>diarPAi+_v!XthLB^9El?U>5ylv zw~%&EMj=X_z0YC)J6~^s+$>JB0L*MW@1nC;- z6|_0OjIgV4*V0$qPZIs)irdp@kOucoV+vm3JB=HYNthsXObF6`v;3eW%INm1&`hfw z<3gqZFr74R&b`@`oZ>^s52JYK^vdEJFykmqYu+nM!%`I3wjZ>%&G+-Bsp-9M=e?7B zlNatk2fFUTW2*o`!gH9W4$8uXtOdXngHXj#DKRwK=6TEK~jRVjL@3r>|huPs~eCVxe$ zy|DE20D!dDEboJ+@L45Z9Eadzh06FPF0JfCqmFxFs_j~pABG7KD*rPew?qAUp_E(h zr{I*Df!*JvIup_@OSlJV#Xi7|VHvxh!pHLi7!NbWE~G=r+V#De>@Gj$mcwFoQ} zvJRAzog7lzqzLD&a>X%G4HU;*w!l0^TAtqWj`{=CwQtk+-R?t`jt^i2iqJd?$nsJr z?LD3#J3o*1Q0c{3Zf=g5!?4 z4KSM~@~?sKJ^vzLnzWRI8gUUtdca%H&y6QgcUQT=nV_vk#gcx+&dPb@o zXQhMu2RQgc1Qf%I5(EghM7ZwKr7g2Hp+Pkk0W8GO+ZPC;L0;N+r~Dgq*=+!dPbcc9 zx*8=3sLVQPAyER^?};ogL7`{idgR@-YUfgf;RfLFY~F%$EH~v}Ud{4F8w%-M@0Y4F z9KaZ$oIe2&;bMuhf?44O8(!!~$R`kL8u^d0E6wl|Y{_@73sT_VUMFj@#hfJg+eYgs zp#_!pq| z!aocyaE17v$gH7j~x|+gGrM7nWld;Ty0J_d&djJ3c delta 3926 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 delta 174 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 delta 6843 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 BjPU>f delta 2306 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 delta 1882 zcmZ`(OK)366uxtPuj6+dKa<#b*?HZBrfE>q0wIq+lBPxBC4_ntYFy7GG4&&zYtsZ3 z6*WPH1t`_908wbNL9hU%27Uly!=e)V%O*R>iZx;Z=Zve~kP46QH*?RKbA0AI-+2GI z&&ETq1%o~XKCgUOE2x>fp)|R9cX;C#QK)x?m{IDbz9KPWa|-p-z?MP-tZCC_##!7< zz>@@fkcPk>V%l4+;AsO(GYx|!{K(Qy(<}ue#M)`}oaVo}blk214OM!3TP`3Z%7hjP(goqKfdv8g7 zMh%`mzFIA_0w2m-|6u~YyL;XzT6bbFMy1nF<0UN(@+5T&cS6C@$72jbz1UB9b zU}(G_CfmKjtMxU@ac~0WS%cWx)neWGD(6@rj{(@O zVx?y5kh3Conz~6`{M0mgrWM`22z>~cf)5~IIks=LNGYr2Xt8X&OZg4PEiOrrbVw$W zV#afs42sV^^F3o2cnZK!xlH^#8XW#Qh3-9>Fvx-t;9cnIM(9Bp6~FmXWAYd-e4j$d zAUNR;{di`q*NTZ_{-nyY;)1^;^f6c-(;pRD{J*yuCSV~fb95XbS#SAXf`dSL&{Gy7k0cX z3ehbxA^wOilS%Pnto70ruo}A4VcSz-jOLbEo-cyS_QR@H=gi727Y?-#kVTOkzI5Eu36VT7Df2N8+U}9*C*<-~C8Ce+l6n!X<=@0H8hW(jyRW zCX!@PtS8RSIf}%@wy!}3-aT8l3i%RiRPQR%bz7HWKlEJ4yy7c__Y3Ws{Qb1$aH!2jCw7K=#|>_tXF}L~}as8Ri@435%!F--Yd_ zTrOWK<#K!rjY08eIx{kjOR0V=AO7L0)e^^_ko?#hNT1qn*)og2FQz&Y7hgwj4Zzeu zaF*!?M8(z7{w~^GC`qS1awU7@nSkTfOKg(BHfuuM?}!ftRJW!o>OY!09MFj7iVR0? RCc-{~!viBi!Z6Y>{|gVlkm~>d diff --git a/model/__pycache__/mobilenetv3.cpython-38.pyc b/model/__pycache__/mobilenetv3.cpython-38.pyc index 8eb4b5e488a5c6042db54ba214ba4cd66ad4c22a..d37248acede99d28f5a894aecd58a874e990aad4 100644 GIT binary patch delta 3818 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 literal 0 HcmV?d00001 diff --git a/model/__pycache__/repvgg.cpython-38.pyc b/model/__pycache__/repvgg.cpython-38.pyc index 69592bb7361ed87efe7e6ac119ee950a580840ad..92a9b21c938d04dbb25f423f96f21acc71dde558 100644 GIT binary patch delta 35 pcmX?^dNh?gl$V!_0SNA_ZQIDbmy=UpKR2&Lzc?jv^Ht6-8UV}t3^)J) delta 39 tcmX?_dNP$el$V!_0SJOROEz-v<>WQi&&bbB)h|sd%_}M0e3A2u1_11m4M6|^ diff --git a/model/__pycache__/resnest.cpython-38.pyc b/model/__pycache__/resnest.cpython-38.pyc index cc468eb2f64012915d41ed76bcee31d32044bf0a..062bb5218c733222fafdfdfbff69955d8ca7165e 100644 GIT binary patch delta 6148 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 delta 5396 zcmbVQYj9gf6~23~-Y?6tY{!nBhu`umwqrYSXiEsGn)Kn4#7sdA6|(l)mMu%l-YYwG zrv ziH^^n-E;Qrp0nSc)xWd<&_kaM27L;AE;IunI5UMZ!0w6K|a7cxbqrIbnqDO>l2AaZA0dC1Qy|7&4^Cw%A$a3tgAh#BgE+RC+;WnD@0R`Uoki zsQ5IyQh>gG=o{Q)t~P=EJDX-qwyB3@Tz!@KpViu1{jl)@$p@3imh!9mvEqC&XBOp% zcJ`hyh+~eJn($0boJ^0;q*KC;A`nbWOpZ;>r%nh@Rd8ZzeEh`B*qHEE1!L*-$rI_x zlfoyr!%_JM?MGeB&?Q=c++dbBnM`L>hLKFt?4kktqVhS%n*khVIo2}zx@eIL&gu46 z6tJt7b3SFJm+bN6cY}P{dHHk{Rbx0NST&l~v)Nt4H1Gfpp(z@Hi@?`#0P(PZQ#6u) z3$BM)M84tb?55cqDY0l+u9bECyLU!36qO&k24)749YQipLL@XE@dK84!8Twbguv#CdQA2U0mVnSJgJBzO1W$N7R+U+m{CC3)qbmm~n zO{W>Ggi21XZL6D3aHFB*1XqCrSKowoc()C1ReLIp#JQk1QgUsxN>fP%7}UUqV-uvd zlYlN4&+YmNjV|)k^%0)B!Bce9b&I2JGl1B7O);agGkCDOd0T<>^3Dp(3ekZC^JzmH zADt4N*gPn|7(6vl%kG#a0wA`ulxbQ{qma#*d-A_Q{yG?+Z4q(Qya&lSBoyWckUI|q z)Rs~OT~M<1qQIX_uIa+a0oI_aaDB5WT>tAUR1sh$^A%XksOiNL#=*H zK+%t6K>jo|cC_ZHeew|@P}#r*D$K`R&o;!%F~2wtO)-gtI9xTHB5Y3qiO20NiMhr8 z6K}bbNuE!GpEUpyBdArMw+=gavijg_pmh&(sBi<|M}9Nhpo$5AgibnP`;b6|^B*8I z@Nh4nfz!SK6D~pnH-sxFKM40m&`k=~?9ORm<6`>ec;jDL8Rv?}RPhOy_fJ<90HTQ|K+^;~KZTK%_g`$wp;w zbRdG;Zx5dkcLOhGWHQ=QUO=&HUC%5onZki`%1R&4ujh=^Y9Xr&+7H~GSky*14}oY- zhuZ02gSVB3=nd=Ow;T$`68M$fbKCF z*j29tce$!-zlCw3nyi52FRLVgCE zf%&GuDfa=)&_6Z6eDxOa*p{9HJ>^))KDtD0qJwfgPJDb9e&R9DE%+nSFwFAmR|}~e z0_Oh%LwJ&KWd;@`07IHXKHt`<3U1eEZC;j@e`z~&EXo5s2(_#m%Ht4k0M5f(c$hZ= z=j9RJ1SPVMP{+NI}dH2nhs6_99`A zZ@~~MSqKDnsN~dc833q8SoA0u+W8U?5YRa!=@d#4VZm^j#B_{n=#^0bKj8kVYf!2j z2Q`AlMPTrA8muB@(yC%(RJ)3+C|Cnxc~WbOa0RUA#A7ICETIxdDaE-(-RH^OqI3I#pK1x0ZM#gJBtxQ67mb8IMH=%ze)OupaW{RovU-$R{s zAeM`wQ@vI8r>&sH6=77uk7;L@&W89t>aol%a3r1p@u{0v2FBS?1lX8ng%|PyUNk(p zpr_0tV43(9bckoL*I9)*`NvpqbU&~Wu3eF?!E+t&!qAc`HUNWbWj9S146;m%CU&0>s z>NtES;CmdtleD=9?JcGZ{s|0%-zXFVA4Y)n@|~eW^4+1J3=ZE_ru}9#8I)UOdHVDo6;vGRRRO<_7MDYIzDG zzX*o`5H-Do1sks6h8wUO2I7rrr;4^D|21~+@Hhx8Ct$FiMQBW!dXi_-`<_0iLbm0d z$1liN#vgc~?l|QFU5OM|ycg%<;+Lr6LGmjkZz6dcNj*HY6*%PPKC3$zww)dv`?0Nh zLHl*E`r<|ObLS9}F|_z9l8Yq?qlS13yD@}zP;i3;E{D#XR;6VzeGuvEK+t9|HhW1#A%{7mW?>qBs9%k$+W4XymzwW zfG!hAZCap7?QEO0jnhg+U-Zx-fzUn#1=@$AKwA`OfIeLx(jX{My|l?wk+y(4vqzDl zY8ydC-*9hsW_D+HzL}x_y7*qU`dm01RN!y_wQrEJzr3$H(Rq5F(ON~ zMlDgOPJPr*12jmhXo!Ys^)b+Oww69;RJxDXeNn6j;5q4_JN30!}M%`so00 z28x_E*3JSf#GRVdW{&PY;=8WLm>(qWh1M^ z*vH1{^!fzbx4wTv*&wULouMPE>T{YgX~aPy!6sM9DdqXz=Tu|LH~<}t=dp4J%$?Gh$c*(Ys3p}@!wCt44 zk~EXHozTLH#ggfqc`bR-+zM*%s^U-aKD{0Sk~fHtz3)BK0z=2QoT4;iF7hb8c`jyB z7H@^&rs}As>F6_HAWmam-h_(HNKj9TyMhYu1IE85CYDTl!lEhh4}G?I09Y~4UZpni zr0?`}Bh1alG~R)Bq_Hj>bprwJo^{Z+T!8nBKlob6p!hV@K}=B-ZY9;C%Rk$VpjlIl z@T-bPF{lrz1wM+Lu^l#-0(iuH90{H+4S(E$T*=W$Kg~ml$mzM zzTs%O72EZr5)7bUGPw49*%Sl!nF#tq${omrOj^=ci(b%I4@(VjF+H$9ed#-SGxyZ9IuO3c^0MXlbc%< zJvGzh5eb<)u+sklnG!4zG7?4-S{gu$1896DX!#|yz6L!11EJ;F1ucFKHWdrV^SZ!K z;#o-8?E%+WoIp`GXUyP8I$HJ?W91citX?3MSSn%4h&%{WkAU9i#gvruVm^^LgV6=Z zEb|I~42(d&xF6DZq3p`zAiCEJ4EFECV}*e1fUF3#QjC6Xpz$Zb$gU+IKx&I7DKZ9tg{uS_;Vi2krDs2&=+S3K-!2mBcG|DvQVP2*eTOU8e541y=HrrIhV<2C~XwcA-wOlf*w*&O_Wi>6Loj7wHpMW0soA|$ z8MeiRCsx7l8u}$GPTz7Vu`EbCP~m_6o5ozKC*o?rwj98J5ec z5}apOSFi-9vN9_>dTM!@nY3)j@$!_p$iRrK+og^0n`i_NgX1m^$s?7y$EjWB>{=$b zVnIilP1%{8DR09e#ZIMs-dWK6Sa_pfg#3FhxN{x~l zl)7SPkjy|+_8-yT`b=DA6~bRdU9G4KW4?x@QePHL9}z!kO%#3y(~yqc28om!q)ye#uYvEtO=_ipqg+%roJ{%1p zk?8m12pbCc{gKOMc>v#aBpBQLOGsok!ji$if&>HZsM|SVq`2)9I>lc{@-&iiu*mGt zk37t5KZ3_PByM#Kjvn@7%Dvk_R7F)hcSF&-syZzgm0~fU<4M6OyO=e^haJ6LZ6KXS zm7PD_I=X~jTKh$JXG3AkCN?ywQ6=x8nG}69r|wm7I0?I!Zs>RbM#Vt6l)cQp77R(T}R2`5ODYfAe*Yxl9#us z?NuzFxIApsIw(FF8r~}OJVD}NG}4Y_9>})5;P2w=_mJ$-Md6dE3!50Z;3y~hYrZolg?u*a6C|KsiSAi9EhKU;DfZAL{#xgVyJn#?o?et(N$7SbfUVS&q5LL%f#S+0l`g9 AQvd(} delta 4727 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 JMMZF;`@e6T@52B9 diff --git a/model/__pycache__/sequencer.cpython-38.pyc b/model/__pycache__/sequencer.cpython-38.pyc index 7f37a308bf1693d2f812c3703adb34428a354e32..6e0729ce27d45e0b424624c79b00c6674c2655ce 100644 GIT binary patch delta 35 pcmcamex{r|l$V!_0SNA_ZQIDL#KLK$pPN^rU!0P-*@|VFIRM1s3f=$! delta 39 tcmX?8exaN@l$V!_0SJOROEz*VvGAJdXXNLm>X#;!=9QFgHe;D)4gllL3+Dg; diff --git a/model/__pycache__/shufflenetv2.cpython-38.pyc b/model/__pycache__/shufflenetv2.cpython-38.pyc index 9033cb934bf376de6bdbe866e00942176edeed26..77d7ce96bf0b3c17cf1009a76d8fbed030ee9243 100644 GIT binary patch delta 3209 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 fvZYB>gKq@`!V}w{LBmX%W9CtDE8F)s^=vS( diff --git a/model/__pycache__/vgg.cpython-38.pyc b/model/__pycache__/vgg.cpython-38.pyc index e40147aaf73c3113443cd45232849bddf0228d33..be54e01fc4f3e110c1ffab74129a67120ea53808 100644 GIT binary patch delta 2534 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 diff --git a/model/__pycache__/vovnet.cpython-38.pyc b/model/__pycache__/vovnet.cpython-38.pyc index e788fe716600215f2936896cc447b96d2879a900..61e939ea8194a78c63390290e3f8ff7ddb9ad5ab 100644 GIT binary patch delta 2584 zcmb_dO>7&-6`q-0lFJoIQIsi(l5Ej({IiZD`d5Y;xq|#KQ~B z?&lG1@aWsjPVz^1jK|lgJ;3+!1n&oydWP^MA6O%NK(MQWEzRBmmJ}ZZ%OF_NU>O3- z4xR=}x;ti=5AoqOVvq2Be3XYFPKNL2V>|?Gl&>D(<9sL3F)_|}U1N8t{GK4~oqS@0 z+PlOkAK}?M)SeI%?d%4jL`;ZXEo$!;lYDo3kJ#OQWP@zb7VUU=ro~>;?7j9r7_ndM zZP9b&%H&JTK44ogV-jW@;N~z9RxppX=p-d~>Gi|l+yl;i4~6R`P;b*Sl&KHt#Afvt1dcoRo_^wEmnnh?9?_padNp>^t`(JrCe^IQvn;{@+unyDYQ$E_<0qbGb^@%eg$iIjwVgdc|dCD2r)}pq?bsmD! z>O9ON@E^KObYh2Rh!x%ZedsT2wLnpE-A9A}D73FYb3X=HAS=`(9&M7VP>G;cKV4y- zhW#eXkQVjytD$R#oNLl{*wfn~FS709G(&nE$Fg2e%a9GW0gAZ3n)+NY1A_ewf>5qM zm=05+f0}qv4$FXPDNMH=E(nyug*53KkkVnjg_zw%n0$KyCJ&OsD3g;Ja@7i5l(+@b z0N7#(_u;YnxbnFA*21b(*3(s6oeR?4F_Tpd-{|DH>xs zTJJ57JI~L2*{Rfcqb#OAAI75Rt%!Qpm}2s2^}ca9hOc8p$Y+TmBe^*k4O1qcQe)^x&m_AnQ3G z9%)k#<*0S_q4o5kp+364jsi6v>WWw6;hrxNs8i!c&ld$zgC-pfbhSz>^C4YHd{v1j zOMVMr3uUtvMu;F_tt|Z^Z3VKL0qZ+7kdn*-@4#+kPXqiGnwtbb;h=1~?X%_K*;;z` zqVWDpBw{q&0+jglrFjycOvFL0uZC zq|N}dm_&)l%LrwV_fGnO`rE+7^b6<&LBcO8zer=ae)%K z{+ZZsm}b;WO7s+PySAS`AlUyZGaw-~=ht11bdR1-tVpmL ziD$z%!7R#7LzdlhzFsc5p08gMH(Wm=;eh80Lh3Ng>htxyEEHt{I+7ziP_g;olOvbZ z*x&?x883_Jh|~GZd<4VYT>!8nlo58HF?LVabSOHzA7)891k9xJ6&P{;8|uyU6Pv#o zM))DZs|YCNj@mJq{1L)y2-zM4;ZgjAs`cTbcpaD@@4V^Jla5<1Rpg6c+ro-( zeHWn)AcG}f&eAOk-5_z0?tf>x12bZnY1op;eZz4o@OgI}IC8=K;9Lf8gkVv5Q~h|P zOb@DmjZDKuc`S1_8C>*!#BuWFvg61b>Q*NEHM}D`LckrBxMLFED@YUQ-q&Tm%KdPu xT5ou+yai5Q7mN9_uhkl!#QR7NAvDxqGncg_Gt`CA@yO$;Z0e$sR+Z7we*?LkTloM0 delta 1553 zcmaJ=T}&fY6rMZ%8wxF@(9%L%%0G2ix`2R7To!?_L6^-2)Wpf^SU5u|^l$G>0a;i` zN`gM@W;gfIjR7Cl_@WPu8WW?5i7!5B;!_{g=z}5ACu3qXM$ehL8#TsB=9@F$J?FdM zoO|ZoCyqwF&w4y=39rRh&6QI3U2lZ#+�j&m?ew6I^MV>IDVrzA!dlAsCvhZ6X8dEH;Osm;hVLklu3$h066Kqde|WP*R?1fdl|hmzU~eb5dr z)Coa9M8Jtr7zSWJ3Jwh0^md4C$afgu)TQGHbeu3Xs)r#A@moxd>9OOE6NyQBOplhC z8rM6a^SDcoA9tTfC#=kBE6`S!ui4ZdwHGt`^qw-Cm0sw&CaeAG09JHiML*QHO8S6R zCsSSg4!b^xi`}@``!~6V8;IB06l3CR)^XXhS=h|$=J2>?WHY&{QhOaa{~#J2-c=mG^J2Ms0KMC)kNg*Lqv=6!>OXHlifhPi^z(*x^e zz`%! zMSt_po<%H4D13n`pAm_c>3X10CrF7-f3sr+!*|5(mRyUKEAzCKi;!S^PBIq z;)_6N|D4H8c~=TcyG~%YKN7oEuvyo)+%~1LE^wtMe1& zjF9*-7`D3^H!Hr@`;1)`?}yHIFOf(c!YO%w3s>o3<3-|0i(j|8OketCB^LgGUARhY ziC~jpi(s4JWr7`o8w4~BoJNE166_J&w8UtTzY($UlGtw#PVdtK?anVFoRa+MO9Yk& zYd4b59576=_OO)yElLVMplGP4s%TmcyP;{=QY)7}ux6Cp;#?HPL?p`+;?2kyTN7VJ zrt7ONO-p67n#S|uTr_@#{+HT$P)9g@KPTTcr!+oQJ5S}I>dfSeCDY*CqV&yFwrVSs l%&McvGkKE-sb)jmi#~5_klo^!Xv8((kNam8zi5xO{Rsz|d2Ijy diff --git a/model/cspnet.py b/model/cspnet.py index 084ae5f..8ecff9f 100644 --- a/model/cspnet.py +++ b/model/cspnet.py @@ -7,7 +7,7 @@ from timm.models.helpers import named_apply from timm.models.layers import ConvNormAct, ConvNormActAa, DropPath, get_attn, create_act_layer, make_divisible from timm.models.registry import register_model -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn urls_dict = { 'cspresnet50': 'https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/cspresnet50_ra-d3e8d487.pth', @@ -343,6 +343,18 @@ def forward(self, x): x = self.act3(x) return x + def switch_to_deploy(self): + self.conv1 = nn.Sequential( + fuse_conv_bn(self.conv1.conv, self.conv1.bn), + self.conv1.bn.act, + ) + self.conv2 = nn.Sequential( + fuse_conv_bn(self.conv2.conv, self.conv2.bn), + self.conv2.bn.act, + ) + self.conv3 = nn.Sequential( + fuse_conv_bn(self.conv3.conv, self.conv3.bn), + ) class DarkBlock(nn.Module): """ DarkNet Block @@ -383,6 +395,15 @@ def forward(self, x): x = self.drop_path(x) + shortcut return x + def switch_to_deploy(self): + self.conv1 = nn.Sequential( + fuse_conv_bn(self.conv1.conv, self.conv1.bn), + self.conv1.bn.act, + ) + self.conv2 = nn.Sequential( + fuse_conv_bn(self.conv2.conv, self.conv2.bn), + self.conv2.bn.act, + ) class EdgeBlock(nn.Module): """ EdgeResidual / Fused-MBConv / MobileNetV1-like 3x3 + 1x1 block (w/ activated output) @@ -422,6 +443,16 @@ def forward(self, x): x = self.conv2(x) x = self.drop_path(x) + shortcut return x + + def switch_to_deploy(self): + self.conv1 = nn.Sequential( + fuse_conv_bn(self.conv1.conv, self.conv1.bn), + self.conv1.bn.act, + ) + self.conv2 = nn.Sequential( + fuse_conv_bn(self.conv2.conv, self.conv2.bn), + self.conv2.bn.act, + ) class CrossStage(nn.Module): @@ -500,6 +531,34 @@ def forward(self, x): out = self.conv_transition(torch.cat([xs, xb], dim=1)) return out + def switch_to_deploy(self): + if type(self.conv_down) is nn.Sequential: + self.conv_down = nn.Sequential( + self.conv_down[0], + fuse_conv_bn(self.conv_down[1].conv, self.conv_down[1].bn), + self.conv_down[1].bn.act, + self.conv_down[1].aa + ) + elif type(self.conv_down) is nn.Identity: + pass + else: + self.conv_down = nn.Sequential( + fuse_conv_bn(self.conv_down.conv, self.conv_down.bn), + self.conv_down.bn.act, + self.conv_down.aa + ) + self.conv_exp = nn.Sequential( + fuse_conv_bn(self.conv_exp.conv, self.conv_exp.bn), + self.conv_exp.bn.act, + ) + self.conv_transition_b = nn.Sequential( + fuse_conv_bn(self.conv_transition_b.conv, self.conv_transition_b.bn), + self.conv_transition_b.bn.act, + ) + self.conv_transition = nn.Sequential( + fuse_conv_bn(self.conv_transition.conv, self.conv_transition.bn), + self.conv_transition.bn.act, + ) class CrossStage3(nn.Module): """Cross Stage 3. @@ -575,6 +634,29 @@ def forward(self, x): out = self.conv_transition(torch.cat([x1, x2], dim=1)) return out + def switch_to_deploy(self): + if self.conv_down is not None: + if type(self.conv_down) is nn.Sequential: + self.conv_down = nn.Sequential( + self.conv_down[0], + fuse_conv_bn(self.conv_down[1].conv, self.conv_down[1].bn), + self.conv_down[1].bn.act, + self.conv_down[1].aa + ) + else: + self.conv_down = nn.Sequential( + fuse_conv_bn(self.conv_down.conv, self.conv_down.bn), + self.conv_down.bn.act, + self.conv_down.aa + ) + self.conv_exp = nn.Sequential( + fuse_conv_bn(self.conv_exp.conv, self.conv_exp.bn), + self.conv_exp.bn.act, + ) + self.conv_transition = nn.Sequential( + fuse_conv_bn(self.conv_transition.conv, self.conv_transition.bn), + self.conv_transition.bn.act, + ) class DarkStage(nn.Module): """DarkNet stage.""" @@ -630,6 +712,20 @@ def forward(self, x): x = self.blocks(x) return x + def switch_to_deploy(self): + if type(self.conv_down) is nn.Sequential: + self.conv_down = nn.Sequential( + self.conv_down[0], + fuse_conv_bn(self.conv_down[1].conv, self.conv_down[1].bn), + self.conv_down[1].bn.act, + self.conv_down[1].aa + ) + else: + self.conv_down = nn.Sequential( + fuse_conv_bn(self.conv_down.conv, self.conv_down.bn), + self.conv_down.bn.act, + self.conv_down.aa + ) def create_csp_stem( in_chans=3, @@ -834,11 +930,11 @@ def forward_features(self, x, need_fea=False): x = layer(x) features.append(x) x = self.avgpool(x) - return features, torch.flatten(x) + return features, torch.flatten(x, start_dim=1, end_dim=3) else: x = self.stages(x) x = self.avgpool(x) - return torch.flatten(x) + return torch.flatten(x, start_dim=1, end_dim=3) def forward_head(self, x): return self.head(x) @@ -992,8 +1088,9 @@ def cs3se_edgenet_x(pretrained=False, **kwargs): return _create_cspnet('cs3se_edgenet_x', pretrained=pretrained, **kwargs) if __name__ == '__main__': - inputs = torch.rand((1, 3, 224, 224)) - model = cs3se_edgenet_x(pretrained=False) + inputs = torch.rand((2, 3, 224, 224)) + model = cspresnet50(pretrained=False) + model.head = nn.Linear(model.head.in_features, 1) model.eval() out = model(inputs) print('out shape:{}'.format(out.size())) diff --git a/model/densenet.py b/model/densenet.py index 1876fd9..d8a9b94 100644 --- a/model/densenet.py +++ b/model/densenet.py @@ -8,7 +8,7 @@ from torchvision._internally_replaced_utils import load_state_dict_from_url from torch import Tensor from typing import Any, List, Tuple - +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = ['densenet121', 'densenet169', 'densenet201', 'densenet161'] @@ -246,7 +246,10 @@ def forward_features(self, x, need_fea=False): def cam_layer(self): return self.features[-1] - + + def switch_to_deploy(self): + self.features.conv0 = fuse_conv_bn(self.features.conv0, self.features.norm0) + del self.features.norm0 def load_state_dict(model: nn.Module, model_url: str, progress: bool) -> None: # '.'s are no longer allowed in module names, but previous _DenseLayer @@ -262,15 +265,16 @@ def load_state_dict(model: nn.Module, model_url: str, progress: bool) -> None: new_key = res.group(1) + res.group(2) state_dict[new_key] = state_dict[key] del state_dict[key] - model_dict = model.state_dict() - weight_dict = {} - for k, v in state_dict.items(): - if k in model_dict: - if np.shape(model_dict[k]) == np.shape(v): - weight_dict[k] = v - pretrained_dict = weight_dict - model_dict.update(pretrained_dict) - model.load_state_dict(model_dict) + load_weights_from_state_dict(model, state_dict) + # model_dict = model.state_dict() + # weight_dict = {} + # for k, v in state_dict.items(): + # if k in model_dict: + # if np.shape(model_dict[k]) == np.shape(v): + # weight_dict[k] = v + # pretrained_dict = weight_dict + # model_dict.update(pretrained_dict) + # model.load_state_dict(model_dict) def _densenet( arch: str, diff --git a/model/dpn.py b/model/dpn.py index 8b75748..d2f246f 100644 --- a/model/dpn.py +++ b/model/dpn.py @@ -51,7 +51,6 @@ def __init__(self, in_chs, out_chs, kernel_size, stride, groups=1, norm_layer=Ba def forward(self, x): return self.conv(self.bn(x)) - class DualPathBlock(nn.Module): def __init__( self, in_chs, num_1x1_a, num_3x3_b, num_1x1_c, inc, groups, block_type='normal', b=False): diff --git a/model/efficientnetv2.py b/model/efficientnetv2.py index 6d91d7c..acdbc76 100644 --- a/model/efficientnetv2.py +++ b/model/efficientnetv2.py @@ -15,7 +15,7 @@ from torchvision.models._api import WeightsEnum, Weights from torchvision.models._meta import _IMAGENET_CATEGORIES from torchvision.models._utils import handle_legacy_interface, _ovewrite_named_param, _make_divisible -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = [ "efficientnet_b0", @@ -155,6 +155,17 @@ def forward(self, input: Tensor) -> Tensor: result = self.stochastic_depth(result) result += input return result + + def switch_to_deploy(self): + new_block = [] + for layer in self.block: + if type(layer) is Conv2dNormActivation: + new_block.append(fuse_conv_bn(layer[0], layer[1])) + if len(layer) > 2: + new_block.append(layer[2]) + else: + new_block.append(layer) + self.block = nn.Sequential(*new_block) class FusedMBConv(nn.Module): @@ -216,6 +227,17 @@ def forward(self, input: Tensor) -> Tensor: result = self.stochastic_depth(result) result += input return result + + def switch_to_deploy(self): + new_block = [] + for layer in self.block: + if type(layer) is Conv2dNormActivation: + new_block.append(fuse_conv_bn(layer[0], layer[1])) + if len(layer) > 2: + new_block.append(layer[2]) + else: + new_block.append(layer) + self.block = nn.Sequential(*new_block) class EfficientNet(nn.Module): @@ -362,6 +384,17 @@ def forward_features(self, x, need_fea=False): def cam_layer(self): return self.features[-1] + + def switch_to_deploy(self): + new_block = [] + for layer in self.features: + if type(layer) is Conv2dNormActivation: + new_block.append(fuse_conv_bn(layer[0], layer[1])) + if len(layer) > 2: + new_block.append(layer[2]) + else: + new_block.append(layer) + self.features = nn.Sequential(*new_block) def _efficientnet( inverted_residual_setting: Sequence[Union[MBConvConfig, FusedMBConvConfig]], diff --git a/model/ghostnet.py b/model/ghostnet.py index d184c70..fc30863 100644 --- a/model/ghostnet.py +++ b/model/ghostnet.py @@ -3,7 +3,7 @@ import math import numpy as np from torch.hub import load_state_dict_from_url -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = ['ghostnet'] @@ -72,6 +72,15 @@ def forward(self, x): out = torch.cat([x1,x2], dim=1) return out[:,:self.oup,:,:] + def switch_to_deploy(self): + self.primary_conv = nn.Sequential( + fuse_conv_bn(self.primary_conv[0], self.primary_conv[1]), + self.primary_conv[2] + ) + self.cheap_operation = nn.Sequential( + fuse_conv_bn(self.cheap_operation[0], self.cheap_operation[1]), + self.cheap_operation[2] + ) class GhostBottleneck(nn.Module): def __init__(self, inp, hidden_dim, oup, kernel_size, stride, use_se): @@ -101,6 +110,27 @@ def __init__(self, inp, hidden_dim, oup, kernel_size, stride, use_se): def forward(self, x): return self.conv(x) + self.shortcut(x) + def switch_to_deploy(self): + if len(self.conv[1]) > 0: + self.conv = nn.Sequential( + self.conv[0], + fuse_conv_bn(self.conv[1][0], self.conv[1][1]), + self.conv[1][2], + self.conv[2], + self.conv[3], + ) + else: + self.conv = nn.Sequential( + self.conv[0], + self.conv[2], + self.conv[3], + ) + if len(self.shortcut) != 0: + self.shortcut = nn.Sequential( + fuse_conv_bn(self.shortcut[0][0], self.shortcut[0][1]), + self.shortcut[0][2], + fuse_conv_bn(self.shortcut[1], self.shortcut[2]) + ) class GhostNet(nn.Module): def __init__(self, cfgs, num_classes=1000, width_mult=1.): @@ -183,6 +213,17 @@ def _initialize_weights(self): def cam_layer(self): return self.features[-1] + + def switch_to_deploy(self): + self.features[0] = nn.Sequential( + fuse_conv_bn(self.features[0][0], self.features[0][1]), + self.features[0][2] + ) + self.squeeze = nn.Sequential( + fuse_conv_bn(self.squeeze[0], self.squeeze[1]), + self.squeeze[2], + self.squeeze[3] + ) def ghostnet(pretrained=False, **kwargs): """ diff --git a/model/mnasnet.py b/model/mnasnet.py index 9efa92b..60f8245 100644 --- a/model/mnasnet.py +++ b/model/mnasnet.py @@ -6,9 +6,9 @@ import numpy as np from torchvision._internally_replaced_utils import load_state_dict_from_url from typing import Any, Dict, List -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn -__all__ = ['mnasnet0_5', 'mnasnet0_75', 'mnasnet1_0', 'mnasnet1_3'] +__all__ = ['mnasnet1_0'] _MODEL_URLS = { "mnasnet0_5": @@ -59,6 +59,15 @@ def forward(self, input: Tensor) -> Tensor: return self.layers(input) + input else: return self.layers(input) + + def switch_to_deploy(self): + self.layers = nn.Sequential( + fuse_conv_bn(self.layers[0], self.layers[1]), + self.layers[2], + fuse_conv_bn(self.layers[3], self.layers[4]), + self.layers[5], + fuse_conv_bn(self.layers[6], self.layers[7]) + ) def _stack(in_ch: int, out_ch: int, kernel_size: int, stride: int, exp_factor: int, repeats: int, @@ -146,6 +155,18 @@ def __init__( nn.Linear(1280, num_classes)) self._initialize_weights() + def switch_to_deploy(self): + self.layers = nn.Sequential( + fuse_conv_bn(self.layers[0], self.layers[1]), + self.layers[2], + fuse_conv_bn(self.layers[3], self.layers[4]), + self.layers[5], + fuse_conv_bn(self.layers[6], self.layers[7]), + self.layers[8:14], + fuse_conv_bn(self.layers[14], self.layers[15]), + self.layers[16] + ) + def forward(self, x: Tensor, need_fea=False) -> Tensor: if need_fea: features, features_fc = self.forward_features(x, need_fea) diff --git a/model/mobilenetv2.py b/model/mobilenetv2.py index bbff475..63d7954 100644 --- a/model/mobilenetv2.py +++ b/model/mobilenetv2.py @@ -7,7 +7,7 @@ from torchvision._internally_replaced_utils import load_state_dict_from_url from torchvision.models._utils import _make_divisible from typing import Callable, Any, Optional, List -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = ['mobilenet_v2'] @@ -75,7 +75,22 @@ def forward(self, x: Tensor) -> Tensor: return x + self.conv(x) else: return self.conv(x) - + + def switch_to_deploy(self): + if len(self.conv) == 4: + self.conv = nn.Sequential( + fuse_conv_bn(self.conv[0][0], self.conv[0][1]), + self.conv[0][2], + fuse_conv_bn(self.conv[1][0], self.conv[1][1]), + self.conv[1][2], + fuse_conv_bn(self.conv[2], self.conv[3]), + ) + else: + self.conv = nn.Sequential( + fuse_conv_bn(self.conv[0][0], self.conv[0][1]), + self.conv[0][2], + fuse_conv_bn(self.conv[1], self.conv[2]), + ) class MobileNetV2(nn.Module): def __init__( @@ -199,6 +214,16 @@ def forward_features(self, x, need_fea=False): def cam_layer(self): return self.features[-1] + + def switch_to_deploy(self): + self.features[0] = nn.Sequential( + fuse_conv_bn(self.features[0][0], self.features[0][1]), + self.features[0][2] + ) + self.features[-1] = nn.Sequential( + fuse_conv_bn(self.features[-1][0], self.features[-1][1]), + self.features[-1][2] + ) def mobilenet_v2(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> MobileNetV2: diff --git a/model/mobilenetv3.py b/model/mobilenetv3.py index de05fcd..1a2cd4b 100644 --- a/model/mobilenetv3.py +++ b/model/mobilenetv3.py @@ -7,7 +7,7 @@ from torchvision.ops.misc import Conv2dNormActivation, SqueezeExcitation as SElayer from torchvision._internally_replaced_utils import load_state_dict_from_url from torchvision.models._utils import _make_divisible -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = ["mobilenetv3_large", "mobilenetv3_small"] @@ -90,6 +90,16 @@ def forward(self, input: Tensor) -> Tensor: result += input return result + def switch_to_deploy(self): + new_layers = [] + for i in range(len(self.block)): + if type(self.block[i]) is Conv2dNormActivation: + new_layers.append(fuse_conv_bn(self.block[i][0], self.block[i][1])) + if len(self.block[i]) == 3: + new_layers.append(self.block[i][2]) + else: + new_layers.append(self.block[i]) + self.block = nn.Sequential(*new_layers) class MobileNetV3(nn.Module): @@ -164,6 +174,17 @@ def __init__( nn.init.normal_(m.weight, 0, 0.01) nn.init.zeros_(m.bias) + def switch_to_deploy(self): + new_layers = [] + for i in range(len(self.features)): + if type(self.features[i]) is Conv2dNormActivation: + new_layers.append(fuse_conv_bn(self.features[i][0], self.features[i][1])) + if len(self.features[i]) == 3: + new_layers.append(self.features[i][2]) + else: + new_layers.append(self.features[i]) + self.features = nn.Sequential(*new_layers) + def _forward_impl(self, x: Tensor, need_fea=False) -> Tensor: if need_fea: features, features_fc = self.forward_features(x, need_fea) diff --git a/model/repghost.py b/model/repghost.py new file mode 100644 index 0000000..3076757 --- /dev/null +++ b/model/repghost.py @@ -0,0 +1,560 @@ +import copy +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.hub import load_state_dict_from_url +from utils.utils import load_weights_from_state_dict, fuse_conv_bn + +__all__ = [ + 'repghostnet_0_5x', + 'repghostnet_repid_0_5x', + 'repghostnet_norep_0_5x', + 'repghostnet_wo_0_5x', + 'repghostnet_0_58x', + 'repghostnet_0_8x', + 'repghostnet_1_0x', + 'repghostnet_1_11x', + 'repghostnet_1_3x', + 'repghostnet_1_5x', + 'repghostnet_2_0x', +] + +weights_dict = { + 'repghostnet_0_5x': 'https://github.com/z1069614715/pretrained-weights/releases/download/repghost_v1.0/repghostnet_0_5x_43M_66.95.pth.tar', + 'repghostnet_0_58x': 'https://github.com/z1069614715/pretrained-weights/releases/download/repghost_v1.0/repghostnet_0_58x_60M_68.94.pth.tar', + 'repghostnet_0_8x': 'https://github.com/z1069614715/pretrained-weights/releases/download/repghost_v1.0/repghostnet_0_8x_96M_72.24.pth.tar', + 'repghostnet_1_0x': 'https://github.com/z1069614715/pretrained-weights/releases/download/repghost_v1.0/repghostnet_1_0x_142M_74.22.pth.tar', + 'repghostnet_1_11x': 'https://github.com/z1069614715/pretrained-weights/releases/download/repghost_v1.0/repghostnet_1_11x_170M_75.07.pth.tar', + 'repghostnet_1_3x': 'https://github.com/z1069614715/pretrained-weights/releases/download/repghost_v1.0/repghostnet_1_3x_231M_76.37.pth.tar', + 'repghostnet_1_5x': 'https://github.com/z1069614715/pretrained-weights/releases/download/repghost_v1.0/repghostnet_1_5x_301M_77.45.pth.tar', + 'repghostnet_2_0x': 'https://github.com/z1069614715/pretrained-weights/releases/download/repghost_v1.0/repghostnet_2_0x_516M_78.81.pth.tar', +} + +def _make_divisible(v, divisor, min_value=None): + """ + This function is taken from the original tf repo. + It ensures that all layers have a channel number that is divisible by 8 + It can be seen here: + https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py + """ + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +def hard_sigmoid(x, inplace: bool = False): + if inplace: + return x.add_(3.0).clamp_(0.0, 6.0).div_(6.0) + else: + return F.relu6(x + 3.0) / 6.0 + + +class SqueezeExcite(nn.Module): + def __init__( + self, + in_chs, + se_ratio=0.25, + reduced_base_chs=None, + act_layer=nn.ReLU, + gate_fn=hard_sigmoid, + divisor=4, + **_, + ): + super(SqueezeExcite, self).__init__() + self.gate_fn = gate_fn + reduced_chs = _make_divisible( + (reduced_base_chs or in_chs) * se_ratio, divisor, + ) + self.avg_pool = nn.AdaptiveAvgPool2d(1) + self.conv_reduce = nn.Conv2d(in_chs, reduced_chs, 1, bias=True) + self.act1 = act_layer(inplace=True) + self.conv_expand = nn.Conv2d(reduced_chs, in_chs, 1, bias=True) + + def forward(self, x): + x_se = self.avg_pool(x) + x_se = self.conv_reduce(x_se) + x_se = self.act1(x_se) + x_se = self.conv_expand(x_se) + x = x * self.gate_fn(x_se) + return x + + +class ConvBnAct(nn.Module): + def __init__(self, in_chs, out_chs, kernel_size, stride=1, act_layer=nn.ReLU): + super(ConvBnAct, self).__init__() + self.conv = nn.Conv2d( + in_chs, out_chs, kernel_size, stride, kernel_size // 2, bias=False, + ) + self.bn1 = nn.BatchNorm2d(out_chs) + self.act1 = act_layer(inplace=True) + + def forward(self, x): + x = self.conv(x) + if hasattr(self, 'bn1'): + x = self.bn1(x) + x = self.act1(x) + return x + + def switch_to_deploy(self): + self.conv = fuse_conv_bn(self.conv, self.bn1) + del self.bn1 + +class RepGhostModule(nn.Module): + def __init__( + self, inp, oup, kernel_size=1, dw_size=3, stride=1, relu=True, deploy=False, reparam_bn=True, reparam_identity=False + ): + super(RepGhostModule, self).__init__() + init_channels = oup + new_channels = oup + self.deploy = deploy + + self.primary_conv = nn.Sequential( + nn.Conv2d( + inp, init_channels, kernel_size, stride, kernel_size // 2, bias=False, + ), + nn.BatchNorm2d(init_channels), + nn.ReLU(inplace=True) if relu else nn.Sequential(), + ) + fusion_conv = [] + fusion_bn = [] + if not deploy and reparam_bn: + fusion_conv.append(nn.Identity()) + fusion_bn.append(nn.BatchNorm2d(init_channels)) + if not deploy and reparam_identity: + fusion_conv.append(nn.Identity()) + fusion_bn.append(nn.Identity()) + + self.fusion_conv = nn.Sequential(*fusion_conv) + self.fusion_bn = nn.Sequential(*fusion_bn) + + self.cheap_operation = nn.Sequential( + nn.Conv2d( + init_channels, + new_channels, + dw_size, + 1, + dw_size // 2, + groups=init_channels, + bias=deploy, + ), + nn.BatchNorm2d(new_channels) if not deploy else nn.Sequential(), + # nn.ReLU(inplace=True) if relu else nn.Sequential(), + ) + if deploy: + self.cheap_operation = self.cheap_operation[0] + if relu: + self.relu = nn.ReLU(inplace=False) + else: + self.relu = nn.Sequential() + + def forward(self, x): + x1 = self.primary_conv(x) + x2 = self.cheap_operation(x1) + for conv, bn in zip(self.fusion_conv, self.fusion_bn): + x2 = x2 + bn(conv(x1)) + return self.relu(x2) + + def get_equivalent_kernel_bias(self): + kernel3x3, bias3x3 = self._fuse_bn_tensor(self.cheap_operation[0], self.cheap_operation[1]) + for conv, bn in zip(self.fusion_conv, self.fusion_bn): + kernel, bias = self._fuse_bn_tensor(conv, bn, kernel3x3.shape[0], kernel3x3.device) + kernel3x3 += self._pad_1x1_to_3x3_tensor(kernel) + bias3x3 += bias + return kernel3x3, bias3x3 + + @staticmethod + def _pad_1x1_to_3x3_tensor(kernel1x1): + if kernel1x1 is None: + return 0 + else: + return torch.nn.functional.pad(kernel1x1, [1, 1, 1, 1]) + + @staticmethod + def _fuse_bn_tensor(conv, bn, in_channels=None, device=None): + in_channels = in_channels if in_channels else bn.running_mean.shape[0] + device = device if device else bn.weight.device + if isinstance(conv, nn.Conv2d): + kernel = conv.weight + assert conv.bias is None + else: + assert isinstance(conv, nn.Identity) + kernel_value = np.zeros((in_channels, 1, 1, 1), dtype=np.float32) + for i in range(in_channels): + kernel_value[i, 0, 0, 0] = 1 + kernel = torch.from_numpy(kernel_value).to(device) + + if isinstance(bn, nn.BatchNorm2d): + running_mean = bn.running_mean + running_var = bn.running_var + gamma = bn.weight + beta = bn.bias + eps = bn.eps + std = (running_var + eps).sqrt() + t = (gamma / std).reshape(-1, 1, 1, 1) + return kernel * t, beta - running_mean * gamma / std + assert isinstance(bn, nn.Identity) + return kernel, torch.zeros(in_channels).to(kernel.device) + + def switch_to_deploy(self): + if len(self.fusion_conv) == 0 and len(self.fusion_bn) == 0: + return + kernel, bias = self.get_equivalent_kernel_bias() + self.cheap_operation = nn.Conv2d(in_channels=self.cheap_operation[0].in_channels, + out_channels=self.cheap_operation[0].out_channels, + kernel_size=self.cheap_operation[0].kernel_size, + padding=self.cheap_operation[0].padding, + dilation=self.cheap_operation[0].dilation, + groups=self.cheap_operation[0].groups, + bias=True) + self.cheap_operation.weight.data = kernel + self.cheap_operation.bias.data = bias + self.__delattr__('fusion_conv') + self.__delattr__('fusion_bn') + self.fusion_conv = [] + self.fusion_bn = [] + self.deploy = True + + self.primary_conv = nn.Sequential( + fuse_conv_bn(self.primary_conv[0], self.primary_conv[1]), + self.primary_conv[2] + ) + + +class RepGhostBottleneck(nn.Module): + """RepGhost bottleneck w/ optional SE""" + + def __init__( + self, + in_chs, + mid_chs, + out_chs, + dw_kernel_size=3, + stride=1, + se_ratio=0.0, + shortcut=True, + reparam=True, + reparam_bn=True, + reparam_identity=False, + deploy=False, + ): + super(RepGhostBottleneck, self).__init__() + has_se = se_ratio is not None and se_ratio > 0.0 + self.stride = stride + self.enable_shortcut = shortcut + self.in_chs = in_chs + self.out_chs = out_chs + + # Point-wise expansion + self.ghost1 = RepGhostModule( + in_chs, + mid_chs, + relu=True, + reparam_bn=reparam and reparam_bn, + reparam_identity=reparam and reparam_identity, + deploy=deploy, + ) + + # Depth-wise convolution + if self.stride > 1: + self.conv_dw = nn.Conv2d( + mid_chs, + mid_chs, + dw_kernel_size, + stride=stride, + padding=(dw_kernel_size - 1) // 2, + groups=mid_chs, + bias=False, + ) + self.bn_dw = nn.BatchNorm2d(mid_chs) + + # Squeeze-and-excitation + if has_se: + self.se = SqueezeExcite(mid_chs, se_ratio=se_ratio) + else: + self.se = None + + # Point-wise linear projection + self.ghost2 = RepGhostModule( + mid_chs, + out_chs, + relu=False, + reparam_bn=reparam and reparam_bn, + reparam_identity=reparam and reparam_identity, + deploy=deploy, + ) + + # shortcut + if in_chs == out_chs and self.stride == 1: + self.shortcut = nn.Sequential() + else: + self.shortcut = nn.Sequential( + nn.Conv2d( + in_chs, + in_chs, + dw_kernel_size, + stride=stride, + padding=(dw_kernel_size - 1) // 2, + groups=in_chs, + bias=False, + ), + nn.BatchNorm2d(in_chs), + nn.Conv2d( + in_chs, out_chs, 1, stride=1, + padding=0, bias=False, + ), + nn.BatchNorm2d(out_chs), + ) + + def forward(self, x): + residual = x + + # 1st repghost bottleneck + x1 = self.ghost1(x) + + # Depth-wise convolution + if self.stride > 1: + x = self.conv_dw(x1) + x = self.bn_dw(x) + else: + x = x1 + + # Squeeze-and-excitation + if self.se is not None: + x = self.se(x) + + # 2nd repghost bottleneck + x = self.ghost2(x) + if not self.enable_shortcut and self.in_chs == self.out_chs and self.stride == 1: + return x + return x + self.shortcut(residual) + + def switch_to_deploy(self): + if len(self.shortcut) != 0: + self.shortcut = nn.Sequential( + fuse_conv_bn(self.shortcut[0], self.shortcut[1]), + fuse_conv_bn(self.shortcut[2], self.shortcut[3]), + ) + + +class RepGhostNet(nn.Module): + def __init__( + self, + cfgs, + num_classes=1000, + width=1.0, + dropout=0.2, + shortcut=True, + reparam=True, + reparam_bn=True, + reparam_identity=False, + deploy=False, + ): + super(RepGhostNet, self).__init__() + # setting of inverted residual blocks + self.cfgs = cfgs + self.dropout = dropout + self.num_classes = num_classes + + # building first layer + output_channel = _make_divisible(16 * width, 4) + self.conv_stem = nn.Conv2d(3, output_channel, 3, 2, 1, bias=False) + self.bn1 = nn.BatchNorm2d(output_channel) + self.act1 = nn.ReLU(inplace=True) + input_channel = output_channel + + # building inverted residual blocks + stages = [] + block = RepGhostBottleneck + for cfg in self.cfgs: + layers = [] + for k, exp_size, c, se_ratio, s in cfg: + output_channel = _make_divisible(c * width, 4) + hidden_channel = _make_divisible(exp_size * width, 4) + layers.append( + block( + input_channel, + hidden_channel, + output_channel, + k, + s, + se_ratio=se_ratio, + shortcut=shortcut, + reparam=reparam, + reparam_bn=reparam_bn, + reparam_identity=reparam_identity, + deploy=deploy + ), + ) + input_channel = output_channel + stages.append(nn.Sequential(*layers)) + + output_channel = _make_divisible(exp_size * width * 2, 4) + stages.append( + nn.Sequential( + ConvBnAct(input_channel, output_channel, 1), + ), + ) + input_channel = output_channel + + self.blocks = nn.Sequential(*stages) + + # building last several layers + output_channel = 1280 + self.global_pool = nn.AdaptiveAvgPool2d((1, 1)) + self.conv_head = nn.Conv2d( + input_channel, output_channel, 1, 1, 0, bias=True, + ) + self.act2 = nn.ReLU(inplace=True) + self.classifier = nn.Linear(output_channel, num_classes) + + def forward(self, x, need_fea=False): + if need_fea: + features, features_fc = self.forward_features(x, need_fea) + x = self.classifier(features_fc) + return features, features_fc, x + else: + x = self.forward_features(x) + x = self.classifier(x) + return x + + def forward_features(self, x, need_fea=False): + input_size = x.size(2) + x = self.conv_stem(x) + x = self.bn1(x) + x = self.act1(x) + if need_fea: + scale = [4, 8, 16, 32] + features = [None, None, None, None] + for idx, layer in enumerate(self.blocks): + x = layer(x) + if input_size // x.size(2) in scale: + features[scale.index(input_size // x.size(2))] = x + x = self.global_pool(x) + x = self.conv_head(x) + x = self.act2(x) + return features, x.view(x.size(0), -1) + else: + x = self.blocks(x) + x = self.global_pool(x) + x = self.conv_head(x) + x = self.act2(x) + return x.view(x.size(0), -1) + + def convert_to_deploy(self): + repghost_model_convert(self, do_copy=False) + + +def repghost_model_convert(model:torch.nn.Module, save_path=None, do_copy=True): + """ + taken from from https://github.com/DingXiaoH/RepVGG/blob/main/repvgg.py + """ + if do_copy: + model = copy.deepcopy(model) + for module in model.modules(): + if hasattr(module, 'switch_to_deploy'): + module.switch_to_deploy() + if save_path is not None: + torch.save(model.state_dict(), save_path) + return model + + +def repghostnet(enable_se=True, pretrained=False, name=None, **kwargs): + """ + Constructs a RepGhostNet model + """ + cfgs = [ + # k, t, c, SE, s + # stage1 + [[3, 8, 16, 0, 1]], + # stage2 + [[3, 24, 24, 0, 2]], + [[3, 36, 24, 0, 1]], + # stage3 + [[5, 36, 40, 0.25 if enable_se else 0, 2]], + [[5, 60, 40, 0.25 if enable_se else 0, 1]], + # stage4 + [[3, 120, 80, 0, 2]], + [ + [3, 100, 80, 0, 1], + [3, 120, 80, 0, 1], + [3, 120, 80, 0, 1], + [3, 240, 112, 0.25 if enable_se else 0, 1], + [3, 336, 112, 0.25 if enable_se else 0, 1], + ], + # stage5 + [[5, 336, 160, 0.25 if enable_se else 0, 2]], + [ + [5, 480, 160, 0, 1], + [5, 480, 160, 0.25 if enable_se else 0, 1], + [5, 480, 160, 0, 1], + [5, 480, 160, 0.25 if enable_se else 0, 1], + ], + ] + model = RepGhostNet(cfgs, **kwargs) + if pretrained: + state_dict = load_state_dict_from_url(weights_dict[name], progress=True)['state_dict_ema'] + model = load_weights_from_state_dict(model, state_dict) + return model + + +def repghostnet_0_5x(**kwargs): + return repghostnet(width=0.5, name='repghostnet_0_5x', **kwargs) + + +def repghostnet_repid_0_5x(**kwargs): + return repghostnet(width=0.5, name='repghostnet_0_5x', reparam_bn=False, reparam_identity=True, **kwargs) + + +def repghostnet_norep_0_5x(**kwargs): + return repghostnet(width=0.5, name='repghostnet_0_5x', reparam=False, **kwargs) + + +def repghostnet_wo_0_5x(**kwargs): + return repghostnet(width=0.5, name='repghostnet_0_5x', shortcut=False, **kwargs) + + +def repghostnet_0_58x(**kwargs): + return repghostnet(width=0.58, name='repghostnet_0_58x', **kwargs) + + +def repghostnet_0_8x(**kwargs): + return repghostnet(width=0.8, name='repghostnet_0_8x', **kwargs) + + +def repghostnet_1_0x(**kwargs): + return repghostnet(width=1.0, name='repghostnet_1_0x', **kwargs) + + +def repghostnet_1_11x(**kwargs): + return repghostnet(width=1.11, name='repghostnet_1_11x', **kwargs) + + +def repghostnet_1_3x(**kwargs): + return repghostnet(width=1.3, name='repghostnet_1_3x', **kwargs) + + +def repghostnet_1_5x(**kwargs): + return repghostnet(width=1.5, name='repghostnet_1_5x', **kwargs) + + +def repghostnet_2_0x(**kwargs): + return repghostnet(width=2.0, name='repghostnet_2_0x', **kwargs) + +if __name__ == '__main__': + inputs = torch.rand((1, 3, 224, 224)) + model = repghostnet_0_5x(pretrained=True) + model.eval() + out = model(inputs) + print('out shape:{}'.format(out.size())) + feas, fea_fc, out = model(inputs, True) + for idx, fea in enumerate(feas): + print('feature {} shape:{}'.format(idx + 1, fea.size())) + print('fc shape:{}'.format(fea_fc.size())) + print('out shape:{}'.format(out.size())) + + model.convert_to_deploy() \ No newline at end of file diff --git a/model/resnest.py b/model/resnest.py index 75be5c9..dfdbb9a 100644 --- a/model/resnest.py +++ b/model/resnest.py @@ -4,7 +4,7 @@ from torch.nn.modules.utils import _pair import torch.nn.functional as F import numpy as np -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = ['resnest50', 'resnest101', 'resnest200', 'resnest269'] _url_format = 'https://github.com/zhanghang1989/ResNeSt/releases/download/weights_step1/{}-{}.pth' @@ -73,7 +73,7 @@ def __init__(self, in_channels, channels, kernel_size, stride=(1, 1), padding=(0 def forward(self, x): x = self.conv(x) - if self.use_bn: + if self.use_bn and hasattr(self, 'bn0'): x = self.bn0(x) if self.dropblock_prob > 0.0: x = self.dropblock(x) @@ -108,6 +108,14 @@ def forward(self, x): out = atten * x return out.contiguous() + def switch_to_deploy(self): + if self.use_bn: + try: + self.conv = fuse_conv_bn(self.conv, self.bn0) + del self.bn0 + except: + pass + class Bottleneck(nn.Module): """ResNet Bottleneck """ @@ -177,7 +185,8 @@ def forward(self, x): residual = x out = self.conv1(x) - out = self.bn1(out) + if hasattr(self, 'bn1'): + out = self.bn1(out) if self.dropblock_prob > 0.0: out = self.dropblock1(out) out = self.relu(out) @@ -187,7 +196,8 @@ def forward(self, x): out = self.conv2(out) if self.radix == 0: - out = self.bn2(out) + if hasattr(self, 'bn2'): + out = self.bn2(out) if self.dropblock_prob > 0.0: out = self.dropblock2(out) out = self.relu(out) @@ -196,7 +206,8 @@ def forward(self, x): out = self.avd_layer(out) out = self.conv3(out) - out = self.bn3(out) + if hasattr(self, 'bn3'): + out = self.bn3(out) if self.dropblock_prob > 0.0: out = self.dropblock3(out) @@ -207,6 +218,15 @@ def forward(self, x): out = self.relu(out) return out + + def switch_to_deploy(self): + self.conv1 = fuse_conv_bn(self.conv1, self.bn1) + del self.bn1 + if self.radix == 0: + self.conv2 = fuse_conv_bn(self.conv2, self.bn2) + del self.bn2 + self.conv3 = fuse_conv_bn(self.conv3, self.bn3) + del self.bn3 class ResNet(nn.Module): """ResNet Variants @@ -383,7 +403,8 @@ def forward(self, x, need_fea=False): def forward_features(self, x, need_fea=False): if need_fea: x = self.conv1(x) - x = self.bn1(x) + if hasattr(self, 'bn1'): + x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) @@ -397,7 +418,8 @@ def forward_features(self, x, need_fea=False): return [x1, x2, x3, x4], x else: x = self.conv1(x) - x = self.bn1(x) + if hasattr(self, 'bn1'): + x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) @@ -413,6 +435,19 @@ def forward_features(self, x, need_fea=False): def cam_layer(self): return self.layer4 + def switch_to_deploy(self): + if type(self.conv1) is nn.Conv2d: + self.conv1 = fuse_conv_bn(self.conv1, self.bn1) + else: + self.conv1 = nn.Sequential( + fuse_conv_bn(self.conv1[0], self.conv1[1]), + self.conv1[2], + fuse_conv_bn(self.conv1[3], self.conv1[4]), + self.conv1[5], + fuse_conv_bn(self.conv1[6], self.bn1), + ) + del self.bn1 + def short_hash(name): if name not in _model_sha256: raise ValueError('Pretrained model for {name} is not available.'.format(name=name)) diff --git a/model/resnet.py b/model/resnet.py index e4ae007..d348754 100644 --- a/model/resnet.py +++ b/model/resnet.py @@ -4,7 +4,7 @@ import numpy as np from torchvision._internally_replaced_utils import load_state_dict_from_url from typing import Type, Any, Callable, Union, List, Optional - +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = ['resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152', 'resnext50_32x4d', 'resnext101_32x8d', @@ -69,11 +69,13 @@ def forward(self, x: Tensor) -> Tensor: identity = x out = self.conv1(x) - out = self.bn1(out) + if hasattr(self, 'bn1'): + out = self.bn1(out) out = self.relu(out) out = self.conv2(out) - out = self.bn2(out) + if hasattr(self, 'bn2'): + out = self.bn2(out) if self.downsample is not None: identity = self.downsample(x) @@ -83,6 +85,11 @@ def forward(self, x: Tensor) -> Tensor: return out + def switch_to_deploy(self): + self.conv1 = fuse_conv_bn(self.conv1, self.bn1) + del self.bn1 + self.conv2 = fuse_conv_bn(self.conv2, self.bn2) + del self.bn2 class Bottleneck(nn.Module): # Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2) @@ -123,15 +130,18 @@ def forward(self, x: Tensor) -> Tensor: identity = x out = self.conv1(x) - out = self.bn1(out) + if hasattr(self, 'bn1'): + out = self.bn1(out) out = self.relu(out) out = self.conv2(out) - out = self.bn2(out) + if hasattr(self, 'bn2'): + out = self.bn2(out) out = self.relu(out) out = self.conv3(out) - out = self.bn3(out) + if hasattr(self, 'bn3'): + out = self.bn3(out) if self.downsample is not None: identity = self.downsample(x) @@ -141,6 +151,13 @@ def forward(self, x: Tensor) -> Tensor: return out + def switch_to_deploy(self): + self.conv1 = fuse_conv_bn(self.conv1, self.bn1) + del self.bn1 + self.conv2 = fuse_conv_bn(self.conv2, self.bn2) + del self.bn2 + self.conv3 = fuse_conv_bn(self.conv3, self.bn3) + del self.bn3 class ResNet(nn.Module): @@ -203,6 +220,10 @@ def __init__( elif isinstance(m, BasicBlock): nn.init.constant_(m.bn2.weight, 0) # type: ignore[arg-type] + def switch_to_deploy(self): + self.conv1 = fuse_conv_bn(self.conv1, self.bn1) + del self.bn1 + def _make_layer(self, block: Type[Union[BasicBlock, Bottleneck]], planes: int, blocks: int, stride: int = 1, dilate: bool = False) -> nn.Sequential: norm_layer = self._norm_layer @@ -244,7 +265,8 @@ def forward(self, x: Tensor, need_fea=False) -> Tensor: def forward_features(self, x, need_fea=False): x = self.conv1(x) - x = self.bn1(x) + if hasattr(self, 'bn1'): + x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) if need_fea: @@ -281,15 +303,7 @@ def _resnet( if pretrained: state_dict = load_state_dict_from_url(model_urls[arch], progress=progress) - model_dict = model.state_dict() - weight_dict = {} - for k, v in state_dict.items(): - if k in model_dict: - if np.shape(model_dict[k]) == np.shape(v): - weight_dict[k] = v - pretrained_dict = weight_dict - model_dict.update(pretrained_dict) - model.load_state_dict(model_dict) + load_weights_from_state_dict(model, state_dict) return model diff --git a/model/shufflenetv2.py b/model/shufflenetv2.py index c2e766e..df1986f 100644 --- a/model/shufflenetv2.py +++ b/model/shufflenetv2.py @@ -4,11 +4,10 @@ import torch.nn as nn from torchvision._internally_replaced_utils import load_state_dict_from_url from typing import Callable, Any, List -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = [ - 'shufflenet_v2_x0_5', 'shufflenet_v2_x1_0', - 'shufflenet_v2_x1_5', 'shufflenet_v2_x2_0' + 'shufflenet_v2_x0_5', 'shufflenet_v2_x1_0' ] model_urls = { @@ -96,6 +95,20 @@ def forward(self, x: Tensor) -> Tensor: return out + def switch_to_deploy(self): + if len(self.branch1) > 0: + self.branch1 = nn.Sequential( + fuse_conv_bn(self.branch1[0], self.branch1[1]), + fuse_conv_bn(self.branch1[2], self.branch1[3]), + self.branch1[4] + ) + self.branch2 = nn.Sequential( + fuse_conv_bn(self.branch2[0], self.branch2[1]), + self.branch2[2], + fuse_conv_bn(self.branch2[3], self.branch2[4]), + fuse_conv_bn(self.branch2[5], self.branch2[6]), + self.branch2[7] + ) class ShuffleNetV2(nn.Module): def __init__( @@ -146,6 +159,16 @@ def __init__( self.fc = nn.Linear(output_channels, num_classes) + def switch_to_deploy(self): + self.conv1 = nn.Sequential( + fuse_conv_bn(self.conv1[0], self.conv1[1]), + self.conv1[2] + ) + self.conv5 = nn.Sequential( + fuse_conv_bn(self.conv5[0], self.conv5[1]), + self.conv5[2] + ) + def _forward_impl(self, x: Tensor, need_fea=False) -> Tensor: if need_fea: features, features_fc = self.forward_features(x, need_fea) diff --git a/model/vgg.py b/model/vgg.py index 02372a6..5270b38 100644 --- a/model/vgg.py +++ b/model/vgg.py @@ -3,7 +3,7 @@ import numpy as np from torchvision._internally_replaced_utils import load_state_dict_from_url from typing import Union, List, Dict, Any, cast -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn __all__ = [ 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', @@ -88,6 +88,15 @@ def forward_features(self, x, need_fea=False): def cam_layer(self): return self.features[-1] + + def switch_to_deploy(self): + new_features = [] + for i in range(len(self.features)): + if type(self.features[i]) is nn.BatchNorm2d: + new_features[-1] = fuse_conv_bn(new_features[-1], self.features[i]) + else: + new_features.append(self.features[i]) + self.features = nn.Sequential(*new_features) def make_layers(cfg: List[Union[str, int]], batch_norm: bool = False) -> nn.Sequential: layers: List[nn.Module] = [] diff --git a/model/vovnet.py b/model/vovnet.py index a4f89d0..5714882 100644 --- a/model/vovnet.py +++ b/model/vovnet.py @@ -4,14 +4,14 @@ import numpy as np from torch.hub import load_state_dict_from_url from collections import OrderedDict -from utils.utils import load_weights_from_state_dict +from utils.utils import load_weights_from_state_dict, fuse_conv_bn -__all__ = ['vovnet27_slim', 'vovnet39', 'vovnet57'] +__all__ = ['vovnet39', 'vovnet57'] model_urls = { - 'vovnet39': 'https://dl.dropbox.com/s/1lnzsgnixd8gjra/vovnet39_torchvision.pth?dl=1', - 'vovnet57': 'https://dl.dropbox.com/s/6bfu9gstbwfw31m/vovnet57_torchvision.pth?dl=1' + 'vovnet39': 'https://github.com/z1069614715/pretrained-weights/releases/download/vovnet_v1.0/vovnet39_torchvision.pth', + 'vovnet57': 'https://github.com/z1069614715/pretrained-weights/releases/download/vovnet_v1.0/vovnet57_torchvision.pth' } @@ -90,6 +90,25 @@ def forward(self, x): return xt + def switch_to_deploy(self): + new_features = [] + for i in range(len(self.layers)): + if type(self.layers[i]) is nn.Sequential: + new_features.append(nn.Sequential( + fuse_conv_bn(self.layers[i][0], self.layers[i][1]), + self.layers[i][2] + )) + elif type(self.layers[i]) is nn.BatchNorm2d: + new_features[-1] = fuse_conv_bn(new_features[-1], self.layers[i]) + print(1) + else: + new_features.append(self.layers[i]) + self.layers = nn.Sequential(*new_features) + + self.concat = nn.Sequential( + fuse_conv_bn(self.concat[0], self.concat[1]), + self.concat[2] + ) class _OSA_stage(nn.Sequential): def __init__(self, @@ -163,6 +182,16 @@ def __init__(self, elif isinstance(m, nn.Linear): nn.init.constant_(m.bias, 0) + def switch_to_deploy(self): + self.stem = nn.Sequential( + fuse_conv_bn(self.stem[0], self.stem[1]), + self.stem[2], + fuse_conv_bn(self.stem[3], self.stem[4]), + self.stem[5], + fuse_conv_bn(self.stem[6], self.stem[7]), + self.stem[8], + ) + def forward(self, x, need_fea=False): if need_fea: features, features_fc = self.forward_features(x, need_fea) @@ -205,6 +234,9 @@ def _vovnet(arch, if pretrained: state_dict = load_state_dict_from_url(model_urls[arch], progress=progress) + for keys in list(state_dict.keys()): + state_dict[f'{keys.replace("module.", "")}'] = state_dict[keys] + del state_dict[keys] model = load_weights_from_state_dict(model, state_dict) return model diff --git a/predict.py b/predict.py index 920c34f..98ef390 100644 --- a/predict.py +++ b/predict.py @@ -7,7 +7,7 @@ import matplotlib.pyplot as plt import numpy as np from utils import utils_aug -from utils.utils import predict_single_image, cam_visual, dict_to_PrettyTable, select_device +from utils.utils import predict_single_image, cam_visual, dict_to_PrettyTable, select_device, model_fuse def set_seed(seed): random.seed(seed) @@ -24,7 +24,7 @@ def parse_opt(): parser.add_argument('--cam_visual', action="store_true", help='visual cam') parser.add_argument('--cam_type', type=str, choices=['GradCAM', 'HiResCAM', 'ScoreCAM', 'GradCAMPlusPlus', 'AblationCAM', 'XGradCAM', 'EigenCAM', 'FullGrad'], default='FullGrad', help='cam type') parser.add_argument('--half', action="store_true", help='use FP16 half-precision inference') - parser.add_argument('--device', type=str, default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') + parser.add_argument('--device', type=str, default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') opt = parser.parse_known_args()[0] if not os.path.exists(os.path.join(opt.save_path, 'best.pt')): @@ -35,7 +35,9 @@ def parse_opt(): raise Exception('half inference only supported GPU.') if opt.half and opt.cam_visual: raise Exception('cam visual only supported FP32.') - model = (ckpt['model'] if opt.half else ckpt['model'].float()) + model = ckpt['model'].float() + model_fuse(model) + model = (model.half() if opt.half else model) model.to(DEVICE) model.eval() train_opt = ckpt['opt'] diff --git a/processing.py b/processing.py index dd454ce..549a6f2 100644 --- a/processing.py +++ b/processing.py @@ -5,7 +5,6 @@ # set random seed np.random.seed(0) - ''' This file help us to split the dataset. It's going to be a training set, a validation set, a test set. diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/__pycache__/__init__.cpython-38.pyc b/utils/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..935f6cd527c41f85cf2294993f1a7bdfdef68aeb GIT binary patch literal 140 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 diff --git a/utils/__pycache__/utils_aug.cpython-38.pyc b/utils/__pycache__/utils_aug.cpython-38.pyc index 49b4b3f110c1eb866a4eb1d9202c04280529f92b..15166b0fa299fd5fb53b136c406312b9f035f97d 100644 GIT binary patch delta 797 zcmZwD&ubG=5C?Ey+-!Ce6C3lZjZ|o>m55T(n$ikhl$J=Y#D)DZ%*>lNyYG|llD>@J@7*Jx zFRveN`+SN%IZ?ChiDtp*jw!RQpySD}+Bq_8$Bkgk;hMTEQr({VLWpU$wUjz5Pg9ED zXv5+(f6@-d9Z$_{6$`l*B~h9LQ{W`K^m&oybNbxq3=%_z1Q5s94DE zZRdfxqe>2F|1T0=NhkdD}ZDmbyQ^_gx~xEq_NW@o&EoxFV}rkFi>=6)ii< z)xfwcKMRyZnSTYg#3kna?Z{<}$N*eVad4GK1~OyU5N5$YFN$ewwd!a$~EE>=7kI;tSM2P!d30b@s zKBZQWsq)A0F<%|wI_Pj?Fcu|rKap=!m1x4NXhAaNhP_b>U5_M=T3s`!+}!zy-CtSf zQ1t0M4pT#tP-zq~0jx`Rz+Ij{uV98aTz`MPr1XD*sR#t*?3bn xyp-?G#x70K6x!s^u_58`_t^F0DGW%11+WClU`67n_2o)2Z}-1j9%~Gy{{Wm4xQPG& delta 788 zcmZwD-)mA~7zglPJ3F4E=rnaYLjtLp5t!vQYupbs!QacoO)T49rSMQE(>t5hGJEtx?E!E!Jhn_v1#fk8F@xb z;AwiTMA&Kiq#O=w_GW#}s1($%MOU-y z-CEOVnM10%r$l?lU1bLSc0Xg6>B;zd;0i|M0Y1;e;2K3dh2sU3*TFyS8zSo=Ul80& zP|}mg$5mcLq=E>z366qW;5N`fNf4Jb)#Q4;Rcv=m9zcw3c?DRDmbx4Xkl$V!_0SNA_ZQIDbh?UbwKR2&Lzc?jv^DfpNb^x@G3fKSu delta 39 tcmX>kbxevol$V!_0SJOROEz*ZV&yf{&&bbB)h|sd%_}M0yp6So9RSis3*i6& diff --git a/utils/__pycache__/utils_loss.cpython-38.pyc b/utils/__pycache__/utils_loss.cpython-38.pyc index 41843542ff3ca62edcd184e9a056a101ad116358..040324f8ede3a1b2815efb13714f42e11b962954 100644 GIT binary patch delta 35 pcmaDN`#_dEl$V!_0SNA_ZQID*!Nh5-pPN^rU!0P-c>$9P4*X#;!=9QFgp2OtA0|3~>3#R}8 diff --git a/utils/__pycache__/utils_model.cpython-38.pyc b/utils/__pycache__/utils_model.cpython-38.pyc index 078909a741eebdeaaae33e83e093bce258629fdd..59c04d0f38b6fa7818096f567da50b5c858b33a7 100644 GIT binary patch delta 488 zcmYk3y-OTH7{=$Fncdsnb1!1JPwy@oKN5o<2#OvdTndXoz``JkglNdxGVr+DUheoyhf%p4{+Qn5+fwum~{ zrn5a%ER?{Ed4Gw!tEcPPLm-Y7l9K3V`5 zOLT-WXA}ea0{-HK(i6uV`s_WQYj}5EEjehfhiyJ2zZA)1MT^^GA$_E% z=JGq5Ty0weFYYJ2_{XH53W(~mqTQk5aR3!vHEGcynWQ3Z*-+ty25% zWn=+Fr|9~|-qMOuF}9C%FD~e00cpuCCRbc6Z!TtMom2Dz}PXFpV5>vD22U-A)Bpe)#OB0;mHY% zQj9W_s~N4Cne*;V-pOdmxMcEYAel8;m&uaRU~)Q>IWtq4l6FSc(I{Y#AiBEQnpy1azX@OKY$1v4$)N+={)Ns^rrZc6eHZws*WK+}@FxD_EWNc<+ zWGInMQLo_;V`v738i=D&!wKRrGSo2Cu*S>Pum&?|YHrSFD`edKmi-SSuZ4a_er~FM yX;NukN$KQIoc4^*CL3{WWaOCqgiB?z2={Ff0bn$7F!C^RFbXhn)dNXJ0TTczdVPEV diff --git a/utils/utils.py b/utils/utils.py index eb0d71b..d252f28 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -639,11 +639,12 @@ def predict_single_image(path, model, test_transform, DEVICE, half=False): pil_img = Image.open(path) tensor_img = test_transform(pil_img).unsqueeze(0).to(DEVICE) tensor_img = (tensor_img.half() if (half and torch.cuda.is_available()) else tensor_img) - if len(tensor_img.shape) == 5: - tensor_img = tensor_img.reshape((tensor_img.size(0) * tensor_img.size(1), tensor_img.size(2), tensor_img.size(3), tensor_img.size(4))) - output = model(tensor_img).mean(0) - else: - output = model(tensor_img)[0] + with torch.inference_mode(): + if len(tensor_img.shape) == 5: + tensor_img = tensor_img.reshape((tensor_img.size(0) * tensor_img.size(1), tensor_img.size(2), tensor_img.size(3), tensor_img.size(4))) + output = model(tensor_img).mean(0) + else: + output = model(tensor_img)[0] try: pred_result = torch.softmax(output, 0) @@ -766,7 +767,9 @@ def __init__(self, device, opt): if self.opt.model_type == 'torch': ckpt = torch.load(os.path.join(opt.save_path, 'best.pt')) - self.model = (ckpt['model'] if opt.half else ckpt['model'].float()) + self.model = ckpt['model'].float() + model_fuse(self.model) + self.model = (self.model.half() if opt.half else self.model) self.model.to(self.device) self.model.eval() elif self.opt.model_type == 'onnx': @@ -812,7 +815,8 @@ def __init__(self, device, opt): def __call__(self, inputs): if self.opt.model_type == 'torch': - return self.model(inputs) + with torch.inference_mode(): + return self.model(inputs) elif self.opt.model_type == 'onnx': inputs = inputs.cpu().numpy().astype(np.float16 if '16' in self.model.get_inputs()[0].type else np.float32) return self.model.run([self.model.get_outputs()[0].name], {self.model.get_inputs()[0].name: inputs})[0] @@ -871,3 +875,76 @@ def select_device(device='', batch_size=0): arg = 'cpu' print(print_str) return torch.device(arg) + +def fuse_conv_bn(conv, bn): + # Fuse convolution and batchnorm layers https://tehnokv.com/posts/fusing-batchnorm-and-conv/ + fusedconv = ( + nn.Conv2d( + conv.in_channels, + conv.out_channels, + kernel_size=conv.kernel_size, + stride=conv.stride, + padding=conv.padding, + groups=conv.groups, + bias=True, + ) + .requires_grad_(False) + .to(conv.weight.device) + ) + + # prepare filters + w_conv = conv.weight.clone().view(conv.out_channels, -1) + w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var))) + fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.shape)) + + # prepare spatial bias + b_conv = ( + torch.zeros(conv.weight.size(0), device=conv.weight.device) + if conv.bias is None + else conv.bias + ) + b_bn = bn.bias - bn.weight.mul(bn.running_mean).div( + torch.sqrt(bn.running_var + bn.eps) + ) + fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn) + return fusedconv + +def model_fuse(model): + before_fuse_layers = len(getLayers(model)) + for module in model.modules(): + if hasattr(module, 'switch_to_deploy'): + module.switch_to_deploy() + print(f'model fuse... {before_fuse_layers} layers to {len(getLayers(model))} layers') + +def getLayers(model): + """ + get each layer's name and its module + :param model: + :return: each layer's name and its module + """ + layers = [] + + def unfoldLayer(model): + """ + unfold each layer + :param model: the given model or a single layer + :param root: root name + :return: + """ + + # get all layers of the model + layer_list = list(model.named_children()) + for item in layer_list: + module = item[1] + sublayer = list(module.named_children()) + sublayer_num = len(sublayer) + + # if current layer contains sublayers, add current layer name on its sublayers + if sublayer_num == 0: + layers.append(module) + # if current layer contains sublayers, unfold them + elif isinstance(module, torch.nn.Module): + unfoldLayer(module) + + unfoldLayer(model) + return layers \ No newline at end of file diff --git a/utils/utils_model.py b/utils/utils_model.py index f807d85..d9fc7f6 100644 --- a/utils/utils_model.py +++ b/utils/utils_model.py @@ -109,6 +109,12 @@ def select_model(name, num_classes, input_shape, channels, pretrained=False): nn.Dropout(0.2), nn.Linear(in_features=model.classifier.in_features, out_features=num_classes) ) + elif name.startswith('repghostnet'): + model = eval('models.{}(pretrained={})'.format(name, pretrained)) + model.classifier = nn.Sequential( + nn.Dropout(0.2), + nn.Linear(in_features=model.classifier.in_features, out_features=num_classes) + ) else: raise 'Unsupported Model Name.' diff --git a/v1.3-update_log.md b/v1.3-update_log.md new file mode 100644 index 0000000..ba3c993 --- /dev/null +++ b/v1.3-update_log.md @@ -0,0 +1,6 @@ +# pytorch-classifier v1.3 更新日志 + +1. 增加[repghost](https://arxiv.org/abs/2211.06088)模型. +2. 推理阶段把模型中的conv和bn进行fuse. +3. 发现mnasnet0_5有点问题,暂停使用. +4. torch.no_grad()更换成torch.inference_mode(). \ No newline at end of file