[深度学习] fast-reid入门教程
fast-reid入門教程
ReID,全拼為Re-identification,目的是利用各種智能算法在圖像數據庫中找到與要搜索的目標相似的對象。ReID是圖像檢索的一個子任務,本質上是圖像檢索而不是圖像分類。fast-reid是一個強悍的目標重識別Reid開源庫,由京東開源管理。本文主要是介紹fast-reid的使用,隨著技術的發展,對于cv從業人員有必要了解不同智能算法技術的應用。而且ReID是相對下游的任務,了解ReID的相關技術應用能學到很多東西。
文章目錄
- fast-reid入門教程
- 1 fast-reid介紹
- 1.1 fast-reid安裝與項目結構
- 1.2 數據集和預訓練模型
- 1.2.1 數據集介紹
- 1.2.2 預訓練模型
- 2 fast-reid基礎使用
- 3 參考
- 3.1 代碼庫
- 3.2 文檔
以行人重識別Person re-identification為例,行人重識別主要目的是針對出現在監控攝像頭內的某個目標行人,準確快速地從監控網絡其他攝像頭內的大量行人中將這個目標行人標識出來。如下圖所示(圖片來自網絡)。
工程上,最簡單的行人重識別的技術流程如下所示。
行人檢測(目標識別) --> 特征提取 --> 行人跟蹤(目標跟蹤)--> 跨鏡頭行人跟蹤 --> 向量存儲與檢索簡單的一個技術解決方案為:
在以上步驟中,特征提取是最關鍵的一環,它的作用是將輸入的行人圖片轉化為固定維度的特征向量,以用于后續的目標跟蹤和向量檢索。好的特征需要具備良好的相似度保持性,即在特征空間中,相似度高的圖片之間的向量距離比較近,而相似度低的圖片對的向量距離比較遠。通常用于訓練這種模型的方式叫做度量學習,度量學習很簡單可以自己查查。
fast-reid是一個面向學術界和工業界的ReID工具箱,是京東的開源項目之一。如果想要了解更多關于fast-reid的信息,可以直接去看作者的論文FastReID: A Pytorch Toolbox for Real-world Person Re-identification。fast-reid基于python和pytorch實現各種模型,同時提供一些腳本將pytorch訓練的模型轉到caffe和TensorRT上。所以非常推薦使用fast-reid進行學習。
fast-reid是一個很不錯的ReID工具箱,提供了豐富的代碼接口,但是代碼有許多小bug,使用的時候要多注意。本文只介紹了fast-reid的基礎使用,沒有進一步的介紹fast-red的工程項目,以及相關的理論知識。關于fast-reid的使用,最好多單步調試進入源代碼,可以學到很多的東西。fast-reid項目中提供的工程示例代碼也是值得一看的。
結合行人重識別和目標檢測與跟蹤的項目,可以看看下面的文章:
行人重識別 ReID
詳細介紹FastReID各部分代碼結構的文章,可以看看:
詳解ReID的各部分組成及Trick——基于FastReID
本文所有代碼見:
github: Python-Study-Notes
1 fast-reid介紹
1.1 fast-reid安裝與項目結構
本文主要介紹fast-reid的基礎使用,度量學習和ReID最新技術建議學習相關論文。本文的項目運行環境為Ubuntu18.01,Python3.8,Pytorch1.8.1+cu102。
對于fast-reid首先去官方倉庫下載對應的代碼到本地,倉庫地址:fast-reid,然后安裝對應的Python庫。具體代碼如下:
關于fast-reid開源項目結構如下圖所示:
其中最主要的是configs文件夾,fastreid文件夾,projects文件夾,tools文件夾和MODEL_ZOO.md。configs文件夾提供了不同模型的結構和訓練實現腳本。fastreid文件夾提供了fast-reid的源代碼實現。projects提供了一些基于fast-reid的項目代碼,里面所有的項目代碼非常有用,建議都跑跑。tools文件夾提供了模型訓練和部署代碼。MODEL_ZOO.md提供了不同數據集下的預訓練模型,可以down下來跑一跑。
此外為了加速索引速度,進入fast-reid/fastreid/evaluation/rank_cylib/目錄,輸入make all編譯文件以加速查詢。如果發現編譯所使用的python版本不是系統默認版本,比如我用的是python3.8,需要修改Makefile文件。如下所示:
all:# python3 setup.py build_ext --inplacepython3.8 setup.py build_ext --inplacerm -rf build clean:rm -rf buildrm -f rank_cy.c *.so1.2 數據集和預訓練模型
1.2.1 數據集介紹
在fast-reid/datasets/目錄提供了不同數據集的信息。可以自行下載。這里介紹其中最常用的Market-1501數據集。
Market-1501是用于行人重識別的大規模公共基準數據集。它包含由6個不同的攝像機捕獲的1501個行人,以及32,668個行人圖像邊界框。數據集分為兩部分:其中750人的圖像用于訓練,其余751人的圖像用于測試。在官方測試協議中,選擇3,368個查詢圖像作為查詢集query,以在包含19,732張參考圖像的gallery圖像集中找到正確匹配。
Market-1501├── bounding_box_test (750人的19732張圖像用于測試)├── -1_c1s1_000401_03.jpg├── 0071_c6s2_072893_01.jpg├── 0071_c6s2_072918_02.jpg├── bounding_box_train (751人的12936張圖像用于訓練)├── 0002_c1s1_000451_03.jpg├── 0002_c1s1_000801_01.jpg├── 0430_c5s1_109673_01.jpg├── gt_bbox (25259張圖像手動標注)├── 0001_c1s1_001051_00.jpg├── 0001_c1s2_041171_00.jpg├── 0933_c6s2_110943_00.jpg├── gt_query (matlab格式,用于判斷一個query的哪些圖片是好的匹配和不好的匹配)├── 0001_c1s1_001051_00_good.mat├── 0794_c2s2_086182_00_good.mat├── 0001_c1s1_001051_00_junk.mat├── query (750人的3368張圖像用于查詢)├── 0001_c1s1_001051_00.jpg├── 0001_c2s1_000301_00.jpg├── 0001_c3s1_000551_00.jpg└── readme.txt圖像命名規則
以0071_c6s2_072893_01.jpg 為例
- 0071 表示當前行人的編號,編號范圍為-1到1501,-1表示不包含在這1501人中的行人,0000表示背景;
- c6 表示當前攝像機的編號,共有6個攝像機;
- s2 表示當前攝像機的第幾個片段,每個攝像機都有多個錄像片段;
- 072893 表示c6s2的第072893幀圖片,視頻幀率為25fps;
- 01 表示0071_c6s2_072893這一幀上的第1個檢測框,00表示手工標注框。
數據集使用
通常都是用度量學習的方式來使用Market-1501數據集。一般使用bounding_box_train,bounding_box_tes和query數據集中的圖像進行模型訓練和測試。
- bounding_box_train:用來訓練模型,使模型能夠學習該集合的圖像特征。
- bounding_box_test:用來提供度量學習中的gallery數據。
- query:與gallery中的數據進行距離匹配以測試模型的好壞。
1.2.2 預訓練模型
在fast-reid/MODEL_ZOO.md文件下提供了不同數據集下不同方法得到的sota模型。以最簡單的Bot在Market1501中訓練ResNet50模型為例。點擊Method下的鏈接會轉到模型配置文件路徑,點擊download會下載對應的預訓練模型(大概300MB)。
對于對應的config路徑位于fast-reid/configs目錄下,所用到的文件有兩個:
configs├── Market1501├── bagtricks_R50.yml├── Base-bagtricks.yml代碼運行時會把Base-bagtricks.yml和bagtricks_R50.yml合并在一起。模型訓練測試推理就是靠這兩個文件,當然你可以手動把這兩個文件并在一起。具體文件修改可以后續看看不同的config文件和官方代碼,自己摸索摸索就可以入手。
Base-bagtricks.yml
MODEL:META_ARCHITECTURE: BaselineBACKBONE: # 模型骨干結構NAME: build_resnet_backboneNORM: BNDEPTH: 50xLAST_STRIDE: 1FEAT_DIM: 2048WITH_IBN: FalsePRETRAIN: TrueHEADS: # 模型頭NAME: EmbeddingHeadNORM: BNWITH_BNNECK: TruePOOL_LAYER: GlobalAvgPoolNECK_FEAT: beforeCLS_LAYER: LinearLOSSES: # 訓練lossNAME: ("CrossEntropyLoss", "TripletLoss",)CE:EPSILON: 0.1SCALE: 1.TRI:MARGIN: 0.3HARD_MINING: TrueNORM_FEAT: FalseSCALE: 1.INPUT: # 模型輸入圖像處理方式SIZE_TRAIN: [ 256, 128 ]SIZE_TEST: [ 256, 128 ]REA:ENABLED: TruePROB: 0.5FLIP:ENABLED: TruePADDING:ENABLED: TrueDATALOADER: # 模型讀取圖像方式SAMPLER_TRAIN: NaiveIdentitySamplerNUM_INSTANCE: 4NUM_WORKERS: 8SOLVER: # 模型訓練配置文件AMP:ENABLED: TrueOPT: AdamMAX_EPOCH: 120BASE_LR: 0.00035WEIGHT_DECAY: 0.0005WEIGHT_DECAY_NORM: 0.0005IMS_PER_BATCH: 64SCHED: MultiStepLRSTEPS: [ 40, 90 ]GAMMA: 0.1WARMUP_FACTOR: 0.1WARMUP_ITERS: 2000CHECKPOINT_PERIOD: 30TEST: # 模型測試配置EVAL_PERIOD: 30IMS_PER_BATCH: 128CUDNN_BENCHMARK: True MODEL:META_ARCHITECTURE: BaselineBACKBONE: # 模型骨干結構NAME: build_resnet_backboneNORM: BNDEPTH: 50xLAST_STRIDE: 1FEAT_DIM: 2048WITH_IBN: FalsePRETRAIN: TrueHEADS: # 模型頭NAME: EmbeddingHeadNORM: BNWITH_BNNECK: TruePOOL_LAYER: GlobalAvgPoolNECK_FEAT: beforeCLS_LAYER: LinearLOSSES: # 訓練lossNAME: ("CrossEntropyLoss", "TripletLoss",)CE:EPSILON: 0.1SCALE: 1.TRI:MARGIN: 0.3HARD_MINING: TrueNORM_FEAT: FalseSCALE: 1.INPUT: # 模型輸入圖像處理方式SIZE_TRAIN: [ 256, 128 ]SIZE_TEST: [ 256, 128 ]REA:ENABLED: TruePROB: 0.5FLIP:ENABLED: TruePADDING:ENABLED: TrueDATALOADER: # 模型讀取圖像方式SAMPLER_TRAIN: NaiveIdentitySamplerNUM_INSTANCE: 4NUM_WORKERS: 8SOLVER: # 模型訓練配置文件AMP:ENABLED: TrueOPT: AdamMAX_EPOCH: 120BASE_LR: 0.00035WEIGHT_DECAY: 0.0005WEIGHT_DECAY_NORM: 0.0005IMS_PER_BATCH: 64SCHED: MultiStepLRSTEPS: [ 40, 90 ]GAMMA: 0.1WARMUP_FACTOR: 0.1WARMUP_ITERS: 2000CHECKPOINT_PERIOD: 30TEST: # 模型測試配置EVAL_PERIOD: 30IMS_PER_BATCH: 128CUDNN_BENCHMARK: Truebagtricks_R50.yml
注意我加了預訓練模型路徑。
_BASE_: ../Base-bagtricks.yml # 鏈接父目錄下的Base-bagtricks.ymlDATASETS:NAMES: ("Market1501",) # 數據集路徑TESTS: ("Market1501",) # 測試集路徑OUTPUT_DIR: logs/market1501/bagtricks_R50 # 輸出結果路徑MODEL:WEIGHTS: model/market_bot_R50.pth # 預訓練模型路徑,這句是我自己加的2 fast-reid基礎使用
這里我的示例代碼結構如下所示,個人習慣為了方便調試和后續接口使用,和官方倉庫不一樣,實際可以不這樣用。
├── configs(配置文件路徑)├── Market1501├── bagtricks_R50.yml├── Base-bagtricks.yml├── datasets(數據集目錄)├── Market-1501-v15.09.15 (這個數據集名不要改)├── bounding_box_test (750人的19732張圖像用于測試)├── bounding_box_train (751人的12936張圖像用于訓練)├── query (750人的3368張圖像用于查詢)├── fastreid├── model(預訓練模型目錄),下載好的預訓練模型存放在這├── demo.py(提取圖像的特征,并保存),來自原來的demo目錄├── predictor.py (模型加載文件),來自原來的demo目錄├── train_net.py (模型訓練與測試封裝版代碼),來自原來的tools目錄├── visualize_result.py (可視化特征提取結果),來自原來的demo目錄重點關注幾個py文件,我直接挪到根目錄下了。還有模型文件的保存路徑,config預訓練模型地址,數據集的名字也要注意的。各個文件具體使用可以看看下面介紹,都有代碼注釋。
???特別注意,py文件為了方便調試,我直接在代碼里面設置了args的參數,實際使用要特別注意。
demo.py
這個代碼就是加載模型(調用predictor.py),提取查詢圖像的特征,并保存為npy文件。保存在demo_output文件夾下,一張圖像對一個npy文件。這些包含特征向量的npy文件可供后續向量檢索使用。
# encoding: utf-8 """ @author: liaoxingyu @contact: sherlockliao01@gmail.com 提取圖像的特征,并保存 """import argparse import glob import os import sysimport torch.nn.functional as F import cv2 import numpy as np import tqdm from torch.backends import cudnnsys.path.append('.')from fastreid.config import get_cfg from fastreid.utils.logger import setup_logger from fastreid.utils.file_io import PathManagerfrom predictor import FeatureExtractionDemo# import some modules added in project like this below # sys.path.append("projects/PartialReID") # from partialreid import *cudnn.benchmark = True setup_logger(name="fastreid")# 讀取配置文件 def setup_cfg(args):# load config from file and command-line argumentscfg = get_cfg()# add_partialreid_config(cfg)cfg.merge_from_file(args.config_file)cfg.merge_from_list(args.opts)cfg.freeze()return cfgdef get_parser():parser = argparse.ArgumentParser(description="Feature extraction with reid models")parser.add_argument("--config-file", # config路徑,通常包含模型配置文件metavar="FILE",help="path to config file",)parser.add_argument("--parallel", # 是否并行action='store_true',help='If use multiprocess for feature extraction.')parser.add_argument("--input", # 輸入圖像路徑nargs="+",help="A list of space separated input images; ""or a single glob pattern such as 'directory/*.jpg'",)parser.add_argument("--output", # 輸出結果路徑default='demo_output',help='path to save features')parser.add_argument("--opts",help="Modify config options using the command-line 'KEY VALUE' pairs",default=[],nargs=argparse.REMAINDER,)return parserdef postprocess(features):# Normalize feature to compute cosine distancefeatures = F.normalize(features) # 特征歸一化features = features.cpu().data.numpy()return featuresif __name__ == '__main__':args = get_parser().parse_args() # 解析輸入參數# 調試使用,使用的時候刪除下面代碼# ---args.config_file = "./configs/Market1501/bagtricks_R50.yml" # config路徑args.input = "./datasets/Market-1501-v15.09.15/query/*.jpg" # 圖像路徑# ---cfg = setup_cfg(args) # 讀取cfg文件demo = FeatureExtractionDemo(cfg, parallel=args.parallel) # 加載特征提取器,也就是加載模型PathManager.mkdirs(args.output) # 創建輸出路徑if args.input:if PathManager.isdir(args.input[0]): # 判斷輸入的是否為路徑# args.input = glob.glob(os.path.expanduser(args.input[0])) # 原來的代碼有問題args.input = glob.glob(os.path.expanduser(args.input)) # 獲取輸入路徑下所有的文件路徑assert args.input, "The input path(s) was not found"for path in tqdm.tqdm(args.input): # 逐張處理img = cv2.imread(path)feat = demo.run_on_image(img) # 提取圖像特征feat = postprocess(feat) # 后處理主要是特征歸一化np.save(os.path.join(args.output, os.path.basename(path).split('.')[0] + '.npy'), feat) # 保存圖像對應的特征,以便下次使用visualize_result.py
這個代碼就是加載模型(調用predictor.py),提取查詢圖像的特征,計算模型的各個精度指標。輸出模型的ROC結果圖,以及某張圖像的匹配結果圖像。輸出目錄為vis_rank_list。
ROC結果圖如下圖所示,ROC曲線下的面積AUC越大,表示模型效果越好。top1精度93.37左右。
某張圖像的匹配結果圖像如下所示。每張圖有1張查詢圖和5張查詢結果圖,左1為查詢圖像,其他為查詢結果圖。藍色框表示查詢結果錯誤,紅色框表示查詢結果正確。在查詢結果圖上有標題,比如0.976/false/cam1,表示當前查詢結果圖像和查詢圖像特征距離為0.976,查詢結果為false(查詢錯誤),該查詢結果來自cam1攝像頭。查詢圖像上的標題,如0.9967/cam2,這里0.9967表示查詢圖像的查詢結果精度指標,cam2表示查詢圖像來自cam2攝像頭。
# encoding: utf-8 """ @author: xingyu liao @contact: sherlockliao01@gmail.com 可視化特征提取結果 """import argparse import logging import sysimport numpy as np import torch import tqdm from torch.backends import cudnnsys.path.append('.')import torch.nn.functional as F from fastreid.evaluation.rank import evaluate_rank from fastreid.config import get_cfg from fastreid.utils.logger import setup_logger from fastreid.data import build_reid_test_loader from predictor import FeatureExtractionDemo from fastreid.utils.visualizer import Visualizer# import some modules added in project # for example, add partial reid like this below # sys.path.append("projects/PartialReID") # from partialreid import *cudnn.benchmark = True setup_logger(name="fastreid")logger = logging.getLogger('fastreid.visualize_result')# 讀取配置文件 def setup_cfg(args):# load config from file and command-line argumentscfg = get_cfg()# add_partialreid_config(cfg)cfg.merge_from_file(args.config_file)cfg.merge_from_list(args.opts)cfg.freeze()return cfgdef get_parser():parser = argparse.ArgumentParser(description="Feature extraction with reid models")parser.add_argument("--config-file", # config路徑,通常包含模型配置文件metavar="FILE",help="path to config file",)parser.add_argument('--parallel', # 是否并行action='store_true',help='if use multiprocess for feature extraction.')parser.add_argument("--dataset-name", # 數據集名字help="a test dataset name for visualizing ranking list.")parser.add_argument("--output", # 輸出結果路徑default="./vis_rank_list",help="a file or directory to save rankling list result.",)parser.add_argument("--vis-label", # 輸出結果是否查看action='store_true',help="if visualize label of query instance")parser.add_argument("--num-vis", # 挑選多少張圖像用于結果展示default=1000,help="number of query images to be visualized",)parser.add_argument("--rank-sort", # 結果展示是相似度排序方式,默認從小到大排序default="ascending",help="rank order of visualization images by AP metric",)parser.add_argument("--label-sort", # label結果展示是相似度排序方式,默認從小到大排序default="ascending",help="label order of visualization images by cosine similarity metric",)parser.add_argument("--max-rank", # 顯示topk的結果,默認顯示前10個結果default=5,help="maximum number of rank list to be visualized",)parser.add_argument("--opts",help="Modify config options using the command-line 'KEY VALUE' pairs",default=[],nargs=argparse.REMAINDER,)return parserif __name__ == '__main__':args = get_parser().parse_args()# 調試使用,使用的時候刪除下面代碼# ---args.config_file = "./configs/Market1501/bagtricks_R50.yml" # config路徑args.dataset_name = 'Market1501' # 數據集名字args.vis_label = False # 是否顯示正確label結果args.rank_sort = 'descending' # 從大到小顯示關聯結果args.label_sort = 'descending' # 從大到小顯示關聯結果# ---cfg = setup_cfg(args)# 可以直接在代碼中設置cfg中設置模型路徑# cfg["MODEL"]["WEIGHTS"] = './configs/Market1501/bagtricks_R50.yml'test_loader, num_query = build_reid_test_loader(cfg, dataset_name=args.dataset_name) # 創建測試數據集demo = FeatureExtractionDemo(cfg, parallel=args.parallel) # 加載特征提取器,也就是加載模型logger.info("Start extracting image features")feats = [] # 圖像特征,用于保存每個行人的圖像特征pids = [] # 行人id,用于保存每個行人的idcamids = [] # 拍攝的攝像頭,行人出現的攝像頭id# 逐張保存讀入行人圖像,并保存相關信息for (feat, pid, camid) in tqdm.tqdm(demo.run_on_loader(test_loader), total=len(test_loader)):feats.append(feat)pids.extend(pid)camids.extend(camid)feats = torch.cat(feats, dim=0) # 將feats轉換為tensor的二維向量,向量維度為[圖像數,特征維度]# 這里把query和gallery數據放在一起了,需要切分query和gallery的數據q_feat = feats[:num_query]g_feat = feats[num_query:]q_pids = np.asarray(pids[:num_query])g_pids = np.asarray(pids[num_query:])q_camids = np.asarray(camids[:num_query])g_camids = np.asarray(camids[num_query:])# compute cosine distance 計算余弦距離q_feat = F.normalize(q_feat, p=2, dim=1)g_feat = F.normalize(g_feat, p=2, dim=1)distmat = 1 - torch.mm(q_feat, g_feat.t()) # 這里distmat表示兩張圖像的距離,越小越接近distmat = distmat.numpy()# 計算各種評價指標 cmc[0]就是top1精度,應該是93%左右,這里精度會有波動logger.info("Computing APs for all query images ...")cmc, all_ap, all_inp = evaluate_rank(distmat, q_pids, g_pids, q_camids, g_camids)logger.info("Finish computing APs for all query images!")visualizer = Visualizer(test_loader.dataset) # 創建Visualizer類visualizer.get_model_output(all_ap, distmat, q_pids, g_pids, q_camids, g_camids) # 保存結果logger.info("Start saving ROC curve ...") # 保存ROC曲線fpr, tpr, pos, neg = visualizer.vis_roc_curve(args.output)visualizer.save_roc_info(args.output, fpr, tpr, pos, neg)logger.info("Finish saving ROC curve!")logger.info("Saving rank list result ...") # 保存部分查詢圖像的關聯結果,按照順序排列query_indices = visualizer.vis_rank_list(args.output, args.vis_label, args.num_vis,args.rank_sort, args.label_sort, args.max_rank)logger.info("Finish saving rank list results!")train_net.py
這段代碼調用config文件,訓練或者測試模型。訓練模型設置args.eval_only = False,反之為測試模型。測試模型結果如下圖所示。代碼封裝的很不錯,把該有的測試指標都貼上去了。
另外這是封裝過多的代碼,如果想知道清晰的訓練代碼查看fast-reid/tools/plain_train_net.py,這個文件提供了詳細沒有封裝過多的訓練代碼。
#!/usr/bin/env python # encoding: utf-8 """ @author: sherlock @contact: sherlockliao01@gmail.com 模型訓練與測試封裝版代碼 """import syssys.path.append('.')from fastreid.config import get_cfg from fastreid.engine import DefaultTrainer, default_argument_parser, default_setup, launch from fastreid.utils.checkpoint import Checkpointer# 讀取配置文件 def setup(args):"""Create configs and perform basic setups."""cfg = get_cfg()cfg.merge_from_file(args.config_file)cfg.merge_from_list(args.opts)cfg.freeze()default_setup(cfg, args)return cfgdef main(args):cfg = setup(args)# 模型測試if args.eval_only:cfg.defrost()cfg.MODEL.BACKBONE.PRETRAIN = Falsemodel = DefaultTrainer.build_model(cfg)# 加載預訓練模型Checkpointer(model).load(cfg.MODEL.WEIGHTS) # load trained modelres = DefaultTrainer.test(cfg, model)return res# 模型訓練trainer = DefaultTrainer(cfg)trainer.resume_or_load(resume=args.resume)return trainer.train()if __name__ == "__main__":args = default_argument_parser().parse_args()# 調試使用,使用的時候刪除下面代碼# ---args.config_file = "./configs/Market1501/bagtricks_R50.yml" # config路徑args.eval_only = True # 是否測試模型,False表示訓練模型,True表示測試模型# ---print("Command Line Args:", args)launch(main,args.num_gpus,num_machines=args.num_machines,machine_rank=args.machine_rank,dist_url=args.dist_url,args=(args,),)3 參考
3.1 代碼庫
- fast-reid
- faiss
- Yolov5_deepsort
3.2 文檔
- FastReID: A Pytorch Toolbox for Real-world Person Re-identification
- 行人重識別 ReID
- 詳解ReID的各部分組成及Trick——基于FastReID
總結
以上是生活随笔為你收集整理的[深度学习] fast-reid入门教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端项目——当当图书网(javaScri
- 下一篇: js校验营业执照