InternImage實戰(zhàn):使用InternImage實現(xiàn)圖像分類任務(一)
@
“書生2.5”的應用
1. 圖像模態(tài)任務性能
2. 圖文跨模態(tài)任務性能
核心技術(shù)
安裝timm
安裝 grad-cam
安裝DCNV3
摘要
論文翻譯:https://wanghao.blog.csdn.net/article/details/129379410 官方源碼: https://github.com/OpenGVLab/InternImage
他來了!他來了!他帶著氬弧焊的光芒過來了!作為CV的大模型,InternImage的光芒太強了。
2023年3月14日: ?? “書生2.5”發(fā)布!
2023年2月28日: ?? InternImage 被CVPR 2023接收!
2022年11月18日: ?? 基于 InternImage-XL 主干網(wǎng)絡,BEVFormer v2 在nuScenes的純視覺3D檢測任務上取得了最佳性能
63.4 NDS
!2022年11月10日: ?? InternImage-H 在COCO目標檢測任務上以
65.4 mAP
斬獲冠軍,是唯一突破65.0 mAP
的超強物體檢測模型!2022年11月10日: ?? InternImage-H 在ADE20K語義分割數(shù)據(jù)集上取得
62.9 mIoU
的SOTA性能!
“書生2.5”的應用
1. 圖像模態(tài)任務性能
在圖像分類標桿數(shù)據(jù)集ImageNet上,“書生2.5”僅基于公開數(shù)據(jù)便達到了 90.1% 的Top-1準確率。這是除谷歌與微軟兩個未公開模型及額外數(shù)據(jù)集外,唯一準確率超過90.0%的模型,同時也是世界上開源模型中ImageNet準確度最高,規(guī)模最大的模型;
在物體檢測標桿數(shù)據(jù)集COCO上,“書生2.5” 取得了 65.5 的 mAP,是世界上唯一超過65 mAP的模型;
在另外16個重要的視覺基礎(chǔ)數(shù)據(jù)集(覆蓋分類、檢測和分割任務)上取得世界最好性能。
分類任務
圖像分類場景分類長尾分類ImageNetPlaces365Places 205iNaturalist 201890.161.271.792.3
檢測任務
常規(guī)物體檢測長尾物體檢測自動駕駛物體檢測密集物體檢測COCOVOC 2007VOC 2012OpenImageLVIS minivalLVIS valBDD100KnuScenesCrowdHuman65.594.097.274.165.863.238.864.897.2
分割任務
語義分割街景分割RGBD分割ADE20KCOCO Stuff-10KPascal ContextCityScapesNYU Depth V262.959.670.386.169.7
2. 圖文跨模態(tài)任務性能
圖文檢索
“書生2.5”可根據(jù)文本內(nèi)容需求快速定位檢索出語義最相關(guān)的圖像。這一能力既可應用于視頻和圖像集合,也可進一步結(jié)合物體檢測框,具有豐富的應用模式,幫助用戶更便捷、快速地找到所需圖像資源, 例如可在相冊中返回文本所指定的相關(guān)圖像。
以圖生文
“書生2.5”的“以圖生文”在圖像描述、視覺問答、視覺推理和文字識別等多個方面均擁有強大的理解能力。例如在自動駕駛場景下,可以提升場景感知理解能力,輔助車輛判斷交通信號燈狀態(tài)、道路標志牌等信息,為車輛的決策規(guī)劃提供有效的感知信息支持。
圖文多模態(tài)任務
圖像描述微調(diào)圖文檢索零樣本圖文檢索COCO CaptionCOCO CaptionFlickr30kFlickr30k148.276.494.889.1
核心技術(shù)
“書生2.5”在圖文跨模態(tài)領(lǐng)域卓越的性能表現(xiàn),源自于在多模態(tài)多任務通用模型技術(shù)核心方面的多項創(chuàng)新,實現(xiàn)了視覺核心視覺感知大模型主干網(wǎng)絡(InternImage)、用于文本核心的超大規(guī)模文本預訓練網(wǎng)絡(LLM)和用于多任務的兼容解碼建模(Uni-Perceiver)的創(chuàng)新組合。 ?視覺主干網(wǎng)絡InternImage參數(shù)量高達30億,能夠基于動態(tài)稀疏卷積算子自適應地調(diào)整卷積的位置和組合方式,從而為多功能視覺感知提供強大的表示。Uni-Perceiver通才任務解碼建模通過將不同模態(tài)的數(shù)據(jù)編碼到統(tǒng)一的表示空間,并將不同任務統(tǒng)一為相同的任務范式,從而能夠以相同的任務架構(gòu)和共享的模型參數(shù)同時處理各種模態(tài)和任務。

這篇文章主要講解如何使用InternImage完成圖像分類任務,接下來我們一起完成項目的實戰(zhàn)。本例選用的模型是InternImageNet_s,在植物幼苗數(shù)據(jù)集上實現(xiàn)了97%的準確率。


通過這篇文章能讓你學到:
如何使用數(shù)據(jù)增強,包括transforms的增強、CutOut、MixUp、CutMix等增強手段?
如何實現(xiàn)InternImage模型實現(xiàn)訓練?
如何使用pytorch自帶混合精度?
如何使用梯度裁剪防止梯度爆炸?
如何使用DP多顯卡訓練?
如何繪制loss和acc曲線?
如何生成val的測評報告?
如何編寫測試腳本測試測試集?
如何使用余弦退火策略調(diào)整學習率?
如何使用AverageMeter類統(tǒng)計ACC和loss等自定義變量?
如何理解和統(tǒng)計ACC1和ACC5?
如何使用EMA?
如果使用Grad-CAM 實現(xiàn)熱力圖可視化?
如果基礎(chǔ)薄弱,對上面的這些功能難以理解可以看我的專欄:經(jīng)典主干網(wǎng)絡精講與實戰(zhàn)這個專欄,從零開始時,一步一步的講解這些,讓大家更容易接受。
安裝包
安裝timm
使用pip就行,命令:
pip?install?timm
本文實戰(zhàn)用的timm里面的模型。
安裝 grad-cam
pip?install?grad-cam
安裝DCNV3
從https://github.com/OpenGVLab/InternImage
下載源碼,然后進入ops_dcnv3,如果是Ubuntu系統(tǒng),
則可以執(zhí)行:
sh?./make.sh
通用的方式:
python?setup.py?install?
注意:本機要有顯卡,并安裝CUDA環(huán)境。
數(shù)據(jù)增強Cutout和Mixup
為了提高成績我在代碼中加入Cutout和Mixup這兩種增強方式。實現(xiàn)這兩種增強需要安裝torchtoolbox。安裝命令:
pip?install?torchtoolbox
Cutout實現(xiàn),在transforms中。
from?torchtoolbox.transform?import?Cutout
#?數(shù)據(jù)預處理
transform?=?transforms.Compose([
????transforms.Resize((224,?224)),
????Cutout(),
????transforms.ToTensor(),
????transforms.Normalize([0.5,?0.5,?0.5],?[0.5,?0.5,?0.5])
])
需要導入包:from timm.data.mixup import Mixup,
定義Mixup,和SoftTargetCrossEntropy
??mixup_fn?=?Mixup(
????mixup_alpha=0.8,?cutmix_alpha=1.0,?cutmix_minmax=None,
????prob=0.1,?switch_prob=0.5,?mode='batch',
????label_smoothing=0.1,?num_classes=12)
?criterion_train?=?SoftTargetCrossEntropy()
參數(shù)詳解:
★mixup_alpha (float): mixup alpha 值,如果 > 0,則 mixup 處于活動狀態(tài)。
cutmix_alpha (float):cutmix alpha 值,如果 > 0,cutmix 處于活動狀態(tài)。
cutmix_minmax (List[float]):cutmix 最小/最大圖像比率,cutmix 處于活動狀態(tài),如果不是 None,則使用這個 vs alpha。
如果設(shè)置了 cutmix_minmax 則cutmix_alpha 默認為1.0
prob (float): 每批次或元素應用 mixup 或 cutmix 的概率。
switch_prob (float): 當兩者都處于活動狀態(tài)時切換cutmix 和mixup 的概率 。
mode (str): 如何應用 mixup/cutmix 參數(shù)(每個'batch','pair'(元素對),'elem'(元素)。
correct_lam (bool): 當 cutmix bbox 被圖像邊框剪裁時應用。 lambda 校正
label_smoothing (float):將標簽平滑應用于混合目標張量。
num_classes (int): 目標的類數(shù)。
”
EMA
EMA(Exponential Moving Average)是指數(shù)移動平均值。在深度學習中的做法是保存歷史的一份參數(shù),在一定訓練階段后,拿歷史的參數(shù)給目前學習的參數(shù)做一次平滑。具體實現(xiàn)如下:
import?logging
from?collections?import?OrderedDict
from?copy?import?deepcopy
import?torch
import?torch.nn?as?nn
_logger?=?logging.getLogger(__name__)
class?ModelEma:
????def?__init__(self,?model,?decay=0.9999,?device='',?resume=''):
????????#?make?a?copy?of?the?model?for?accumulating?moving?average?of?weights
????????self.ema?=?deepcopy(model)
????????self.ema.eval()
????????self.decay?=?decay
????????self.device?=?device??#?perform?ema?on?different?device?from?model?if?set
????????if?device:
????????????self.ema.to(device=device)
????????self.ema_has_module?=?hasattr(self.ema,?'module')
????????if?resume:
????????????self._load_checkpoint(resume)
????????for?p?in?self.ema.parameters():
????????????p.requires_grad_(False)
????def?_load_checkpoint(self,?checkpoint_path):
????????checkpoint?=?torch.load(checkpoint_path,?map_location='cpu')
????????assert?isinstance(checkpoint,?dict)
????????if?'state_dict_ema'?in?checkpoint:
????????????new_state_dict?=?OrderedDict()
????????????for?k,?v?in?checkpoint['state_dict_ema'].items():
????????????????#?ema?model?may?have?been?wrapped?by?DataParallel,?and?need?module?prefix
????????????????if?self.ema_has_module:
????????????????????name?=?'module.'?+?k?if?not?k.startswith('module')?else?k
????????????????else:
????????????????????name?=?k
????????????????new_state_dict[name]?=?v
????????????self.ema.load_state_dict(new_state_dict)
????????????_logger.info("Loaded?state_dict_ema")
????????else:
????????????_logger.warning("Failed?to?find?state_dict_ema,?starting?from?loaded?model?weights")
????def?update(self,?model):
????????#?correct?a?mismatch?in?state?dict?keys
????????needs_module?=?hasattr(model,?'module')?and?not?self.ema_has_module
????????with?torch.no_grad():
????????????msd?=?model.state_dict()
????????????for?k,?ema_v?in?self.ema.state_dict().items():
????????????????if?needs_module:
????????????????????k?=?'module.'?+?k
????????????????model_v?=?msd[k].detach()
????????????????if?self.device:
????????????????????model_v?=?model_v.to(device=self.device)
????????????????ema_v.copy_(ema_v?*?self.decay?+?(1.?-?self.decay)?*?model_v)
加入到模型中。
#初始化
if?use_ema:
?????model_ema?=?ModelEma(
????????????model_ft,
????????????decay=model_ema_decay,
????????????device='cpu',
????????????resume=resume)
#?訓練過程中,更新完參數(shù)后,同步update?shadow?weights
def?train():
????optimizer.step()
????if?model_ema?is?not?None:
????????model_ema.update(model)
#?將model_ema傳入驗證函數(shù)中
val(model_ema.ema,?DEVICE,?test_loader)
針對沒有預訓練的模型,容易出現(xiàn)EMA不上分的情況,這點大家要注意?。?/p>
項目結(jié)構(gòu)
InternImage_Demo
├─data1
│??├─Black-grass
│??├─Charlock
│??├─Cleavers
│??├─Common?Chickweed
│??├─Common?wheat
│??├─Fat?Hen
│??├─Loose?Silky-bent
│??├─Maize
│??├─Scentless?Mayweed
│??├─Shepherds?Purse
│??├─Small-flowered?Cranesbill
│??└─Sugar?beet
├─models
│??├─__init__.py
│??└─intern_image.py
├─ops_dcnv3
├─mean_std.py
├─makedata.py
├─train.py
├─cam_image.py
└─test.py
ops_dcnv3:來著官方的文件夾,存放dcnv3的文件。 models:來源官方代碼,對面的代碼做了一些適應性修改。增加了一些加載預訓練,調(diào)用模型的邏輯。 mean_std.py:計算mean和std的值。 makedata.py:生成數(shù)據(jù)集。 ema.py:EMA腳本 train.py:訓練PoolFormer模型 cam_image.py:熱力圖可視化
計算mean和std
為了使模型更加快速的收斂,我們需要計算出mean和std的值,新建mean_std.py,插入代碼:
from?torchvision.datasets?import?ImageFolder
import?torch
from?torchvision?import?transforms
def?get_mean_and_std(train_data):
????train_loader?=?torch.utils.data.DataLoader(
????????train_data,?batch_size=1,?shuffle=False,?num_workers=0,
????????pin_memory=True)
????mean?=?torch.zeros(3)
????std?=?torch.zeros(3)
????for?X,?_?in?train_loader:
????????for?d?in?range(3):
????????????mean[d]?+=?X[:,?d,?:,?:].mean()
????????????std[d]?+=?X[:,?d,?:,?:].std()
????mean.div_(len(train_data))
????std.div_(len(train_data))
????return?list(mean.numpy()),?list(std.numpy())
if?__name__?==?'__main__':
????train_dataset?=?ImageFolder(root=r'data1',?transform=transforms.ToTensor())
????print(get_mean_and_std(train_dataset))
數(shù)據(jù)集結(jié)構(gòu):

運行結(jié)果:
([0.3281186,?0.28937867,?0.20702125],?[0.09407319,?0.09732835,?0.106712654])
把這個結(jié)果記錄下來,后面要用!
生成數(shù)據(jù)集
我們整理還的圖像分類的數(shù)據(jù)集結(jié)構(gòu)是這樣的
data
├─Black-grass
├─Charlock
├─Cleavers
├─Common?Chickweed
├─Common?wheat
├─Fat?Hen
├─Loose?Silky-bent
├─Maize
├─Scentless?Mayweed
├─Shepherds?Purse
├─Small-flowered?Cranesbill
└─Sugar?beet
pytorch和keras默認加載方式是ImageNet數(shù)據(jù)集格式,格式是
├─data
│??├─val
│??│???├─Black-grass
│??│???├─Charlock
│??│???├─Cleavers
│??│???├─Common?Chickweed
│??│???├─Common?wheat
│??│???├─Fat?Hen
│??│???├─Loose?Silky-bent
│??│???├─Maize
│??│???├─Scentless?Mayweed
│??│???├─Shepherds?Purse
│??│???├─Small-flowered?Cranesbill
│??│???└─Sugar?beet
│??└─train
│??????├─Black-grass
│??????├─Charlock
│??????├─Cleavers
│??????├─Common?Chickweed
│??????├─Common?wheat
│??????├─Fat?Hen
│??????├─Loose?Silky-bent
│??????├─Maize
│??????├─Scentless?Mayweed
│??????├─Shepherds?Purse
│??????├─Small-flowered?Cranesbill
│??????└─Sugar?beet
新增格式轉(zhuǎn)化腳本makedata.py,插入代碼:
import?glob
import?os
import?shutil
image_list=glob.glob('data1/*/*.png')
print(image_list)
file_dir='data'
if?os.path.exists(file_dir):
????print('true')
????#os.rmdir(file_dir)
????shutil.rmtree(file_dir)#刪除再建立
????os.makedirs(file_dir)
else:
????os.makedirs(file_dir)
from?sklearn.model_selection?import?train_test_split
trainval_files,?val_files?=?train_test_split(image_list,?test_size=0.3,?random_state=42)
train_dir='train'
val_dir='val'
train_root=os.path.join(file_dir,train_dir)
val_root=os.path.join(file_dir,val_dir)
for?file?in?trainval_files:
????file_class=file.replace("\\","/").split('/')[-2]
????file_name=file.replace("\\","/").split('/')[-1]
????file_class=os.path.join(train_root,file_class)
????if?not?os.path.isdir(file_class):
????????os.makedirs(file_class)
????shutil.copy(file,?file_class?+?'/'?+?file_name)
for?file?in?val_files:
????file_class=file.replace("\\","/").split('/')[-2]
????file_name=file.replace("\\","/").split('/')[-1]
????file_class=os.path.join(val_root,file_class)
????if?not?os.path.isdir(file_class):
????????os.makedirs(file_class)
????shutil.copy(file,?file_class?+?'/'?+?file_name)
完成上面的內(nèi)容就可以開啟訓練和測試了。