海华·垃圾分类AI挑战赛baseline分享,评测得分最高至0.85
點擊藍字關注biendata
導語
本篇baseline采用torchvision內置的目標檢測模型,LB可以達到76左右。如果為了進一步提高成績,可以把主辦方提供的簡單樣本也加入訓練,以及增加更多的圖像增強方式,預計LB可以達到85左右。
垃圾分類已經在全球范圍內成為一個非常重要和緊迫的社會問題,在我國垃圾分類的知識普及以及落實依然是個難題。
從人工智能行業角度來看,未來人工智能技術在城市運行領域將有廣泛應用。利用人工智能技術助力居民實現垃圾分類的“智慧分揀”也將會成為行業發展中的重要課題之一。實現“智慧分揀”需要一個龐大的垃圾智能分揀系統,其中涉及到很多環節。其中最重要的環節之一即是將攝像頭中捕捉到的垃圾物體自動識別出其所屬的垃圾類型。這一過程中需要利用計算機視覺中的目標檢測方法。
在此背景下,今年1月中關村海華信息技術前沿研究院與清華大學交叉信息研究院聯合主辦,中關村科技園區海淀園管理委員會與北京市海淀區城市管理委員會作為指導單位,開展了“2020海華AI挑戰賽·垃圾分類”。目前比賽還有1個月時間即進入復賽,陸續有選手開源了自己的baseline模型。
賽事承辦方biendata整理了一篇來自參賽選手Richard的開源baseline分享。本篇baseline采用torchvision內置的目標檢測模型,可以節省大量的用于搭建模型的時間和代碼,把注意力放在數據的分析和圖片增強上。根據主辦方提供的3000張復雜樣本,按照8:2的比例劃分訓練和測試樣本,通過簡單的圖片增強和torchvision發布的預訓練模型上進行finetune。Baseline的LB可以達到76左右。如果為了進一步提高成績,可以把主辦方提供的簡單樣本也加入訓練,以及增加更多的圖像增強方式,預計LB可以達到85左右。
本次比賽同步開放技術組與中學組雙賽道,總獎金高達30萬元。值得一提的是,本次比賽由華為網絡人工智能引擎NAIE云服務提供免費一站式開發環境,詳情請掃描下方二維碼查看。
比賽地址?
baseline原文鏈接
Baseline構建
1 | 環境要求 |
python==3.6.0
torch==1.1.0
torchvision==0.3.0
2 | 預訓練權重 |
預訓練權重來自torchvision官方,如果遇到網絡不通暢,也可以從百度網盤下載鏈接:
鏈接:https://pan.baidu.com/s/1mcVvzmRVZ4ey-Tp679YVhg
提取碼: hvwp
#?引入必要的庫 import?json import?pandas?as?pd import?time import?numpy?as?np import?matplotlib.pyplot?as?plt import?os,?sys,?glob from?data_aug.data_aug?import?* from?PIL?import?Image import?math from?tqdm?import?tqdm import?torch import?torchvision import?torchvision.transforms?as?T from?torchvision.models.detection.faster_rcnn?import?FastRCNNPredictor from?torch?import?autograd %matplotlib?inline #?定義全局變量 TRAIN_JSON_PATH?=?'train.json' TRAIN_IMAGE_PATH?=?'work/train/images'?#?訓練圖片的路徑 VAL_IMAGE_PATH?=?"work/val/images"?#?驗證圖片的路徑 CKP_PATH?=?'data/data22244/fasterrcnn.pth'?#?預訓練的權重 SAVE_PATH?=?"result.json"?#?提交結果的保存路徑 MODEL_PATH?=?"model.pth"?#?模型的保存路徑 device?=?torch.device("cuda"?if?torch.cuda.is_available()?else?"cpu")with?open(TRAIN_JSON_PATH)?as?f:train_json?=?json.load(f)NUM_CLASS?=?len(train_json["categories"])?+?1 id_to_label?=?dict([(item["id"],?item["name"])?for?item?in?train_json["categories"]])?#?將ID轉成中文名稱 image_paths?=?glob.glob(os.path.join(TRAIN_IMAGE_PATH,?"*.png"))[:100]?#?為了演示,只取前100張 imageid_of_annotation?=?list(set([i["image_id"]?for?i?in?train_json["annotations"]])) image_paths?=?[i?for?i?in?image_paths?if?os.path.basename(i).split(".")[0]?in?imageid_of_annotation ]?#?剔除image_id不在annotation里面的圖片len_train?=?int(0.8?*?len(image_paths))? train_image_paths?=?image_paths[:len_train] eval_image_paths?=?image_paths[len_train:] val_image_paths?=?glob.glob(os.path.join(VAL_IMAGE_PATH,?"*.png"))[:10]#?為了演示,只取前10張 #?將所有的標注信息整合到DataFrame里,方便后續分析 data?=?[] for?idx,?annotations?in?enumerate(train_json['annotations']):data.append([annotations['image_id'],annotations['category_id'],?idx])data?=?pd.DataFrame(data,?columns=['image_id','category_id',?'idx']) data['category_name']?=?data['category_id'].map(id_to_label) print('訓練集總共有多少張圖片??',?len(train_json['images'])) print('訓練集總共有多少個標注框??',len(train_json['annotations'])) print('總共有多少種類別??',len(train_json['categories'])) data.head() 訓練集總共有多少張圖片?? 2999 訓練集總共有多少個標注框?? 28160 總共有多少種類別?? 2043 | 分組顯示bounding box數量的圖片數量 |
每張圖片上最少有1個bounding box, 最多有20個。
擁有5個bounding box的圖片數量最多,平均每張圖片上有9個bounding box。
image_group?=?data.groupby('image_id').agg({'idx':'count'})['idx'] image_group.value_counts().sort_index().plot.bar() image_group.describe() count????2998.000000 mean????????9.392929 std?????????4.376932 min?????????1.000000 25%?????????5.000000 50%?????????9.000000 75%????????13.000000 max????????20.000000 Name:?idx,?dtype:?float644 | 顯示每個category出現的次數 |
每個category最少出現1次,最多出現897次,平均出現196次。出現次數最多的category分別為金屬工具、塑料包裝、藥瓶等。
category_group?=?data.groupby('category_name').agg({'idx':'count'})['idx']#?.value_counts() category_group.sort_index().plot.bar() print(category_group.describe()) category_group.sort_values(ascending=False).reset_index().head(5) count????143.000000 mean?????196.923077 std??????180.903331 min????????1.000000 25%???????60.000000 50%??????147.000000 75%??????287.000000 max??????897.000000 Name:?idx,?dtype:?float64palette?=?(2?**?11?-?1,?2?**?15?-?1,?2?**?20?-?1) #?顯示圖片及bounding?box的輔助函數 def?compute_color_for_labels(label):"""Simple?function?that?adds?fixed?color?depending?on?the?class"""color?=?[int((p?*?(label?**?2?-?label?+?1))?%?255)?for?p?in?palette]return?tuple(color)def?draw_boxes(img,?bbox,?identities=None,?offset=(0,0)):for?i,box?in?enumerate(bbox):x1,y1,x2,y2?=?[int(i)?for?i?in?box]x1?+=?offset[0]x2?+=?offset[0]y1?+=?offset[1]y2?+=?offset[1]#?box?text?and?barid?=?int(identities[i])?if?identities?is?not?None?else?0????color?=?compute_color_for_labels(id)label?=?'{}{:d}'.format("",?id)t_size?=?cv2.getTextSize(label,?cv2.FONT_HERSHEY_PLAIN,?2?,?2)[0]cv2.rectangle(img,(x1,?y1),(x2,y2),color,3)cv2.rectangle(img,(x1,?y1),(x1+t_size[0]+3,y1+t_size[1]+4),?color,-1)cv2.putText(img,label,(x1,y1+t_size[1]+4),?cv2.FONT_HERSHEY_PLAIN,?2,?[255,255,255],?2)return?img #?將coco的bounding?box格式從[x,y,width,height]轉成[x1,y1,x2,y2] def?xywh_to_xyxy(points):return?[points[0],?points[1],?points[0]?+?points[2],?points[1]+?points[3]]
5 | 圖像增強的方法 |
為了最大化的利用數據,對訓練數據進行增強是一個有效手段。以下代碼顯示原始的圖片,以及進行旋轉、平移后的圖片。圖像增強的關鍵在于圖片進行變換后,bounding box的坐標要跟著變換。除了介紹的這兩種,還有其他不同的圖像增強方法。
show_image_path?=?image_paths[0] show_image_id?=?os.path.basename(show_image_path).split('.')[0] show_annotation?=?[i?for?i?in?train_json['annotations']?if?i['image_id']?==?show_image_id]def?show_original_image(show_image_path,?show_annotation):show_bboxes?=?[xywh_to_xyxy(a['bbox'])?for?a?in?show_annotation]show_bboxes?=?np.array(show_bboxes).astype(np.float32)show_labels?=?[a['category_id']?for?a?in?show_annotation]show_iamge?=?cv2.imread(show_image_path)?#?[:,:,::-1]show_iamge?=?cv2.cvtColor(show_iamge,?cv2.COLOR_BGR2RGB)show_iamge?=?draw_boxes(show_iamge,?show_bboxes,?show_labels)print('顯示原始的圖片')plt.imshow(show_iamge)show_original_image(show_image_path,?show_annotation) 顯示原始的圖片def?show_translate_image(show_image_path,?show_annotation):show_bboxes?=?[xywh_to_xyxy(a['bbox'])?for?a?in?show_annotation]show_bboxes?=?np.array(show_bboxes).astype(np.float32)show_labels?=?[a['category_id']?for?a?in?show_annotation]show_iamge?=?cv2.imread(show_image_path)?#?[:,:,::-1]show_iamge?=?cv2.cvtColor(show_iamge,?cv2.COLOR_BGR2RGB)show_iamge,?show_bboxes?=?RandomTranslate(0.3,?diff?=?True)(show_iamge.copy(),?show_bboxes.copy())show_iamge?=?draw_boxes(show_iamge,?show_bboxes,?show_labels)print('顯示隨機平移的圖片')plt.imshow(show_iamge) show_translate_image(show_image_path,?show_annotation) 顯示隨機平移的圖片
def?show_rotate_image(show_image_path,?show_annotation):show_bboxes?=?[xywh_to_xyxy(a['bbox'])?for?a?in?show_annotation]show_bboxes?=?np.array(show_bboxes).astype(np.float32)show_labels?=?[a['category_id']?for?a?in?show_annotation]show_iamge?=?cv2.imread(show_image_path)?#?[:,:,::-1]show_iamge?=?cv2.cvtColor(show_iamge,?cv2.COLOR_BGR2RGB)show_iamge,?show_bboxes?=?RandomRotate(20)(show_iamge.copy(),?show_bboxes.copy())show_iamge?=?draw_boxes(show_iamge,?show_bboxes,?show_labels)print('顯示隨機旋轉的圖片')plt.imshow(show_iamge) show_rotate_image(show_image_path,?show_annotation) 顯示隨機旋轉的圖片
#?定義FasterRCNN的網絡結,主要是修改預測的類別數量 def?get_model(num_classes):#?load?an?instance?segmentation?model?pre-trained?pre-trained?on?COCOmodel?=?torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=False,?pretrained_backbone=False)model.load_state_dict(torch.load(CKP_PATH))#?get?number?of?input?features?for?the?classifierin_features?=?model.roi_heads.box_predictor.cls_score.in_features#?replace?the?pre-trained?head?with?a?new?onemodel.roi_heads.box_predictor?=?FastRCNNPredictor(in_features,?num_classes)return?model #?定義Pytorch支持的數據加載方式 def?get_transform():custom_transforms?=?[]custom_transforms.append(torchvision.transforms.ToTensor())return?torchvision.transforms.Compose(custom_transforms)def?cal_area(bbox):return?(bbox[2]?-?bbox[0])?*?(bbox[3]?-?bbox[1])class?CreateDataset(torch.utils.data.Dataset):def?__init__(self,?image_paths,?coco_json,?for_train=False):self.image_paths?=?image_pathsself.coco_json?=?coco_jsonself.for_train?=?for_train?#?是否需要圖像增強#?圖像增強的方法self.aug_seq?=?Sequence([RandomScale(0.1),RandomTranslate(0.1),])def?__getitem__(self,?index):image_id?=?os.path.basename(self.image_paths[index]).split(".")[0]image_path?=?self.image_paths[index]annotations?=?[i?for?i?in?self.coco_json["annotations"]?if?i["image_id"]?==?image_id]img?=?Image.open(image_path)transform?=?T.Compose([T.ToTensor()])??#?Defing?PyTorch?Transformnum_objs?=?len(annotations)bboxes?=?[xywh_to_xyxy(a["bbox"])?for?a?in?annotations]areas?=?[cal_area(bbox)?for?bbox?in?bboxes]bboxes?=?np.array(bboxes).astype(np.float32)labels?=?[a["category_id"]?for?a?in?annotations]if?self.for_train:img,?bboxes?=?self.aug_seq(np.array(img).copy(),?bboxes.copy())img?=?transform(img)??#?Apply?the?transform?to?the?imageboxes?=?torch.as_tensor(bboxes,?dtype=torch.float32)labels?=?torch.tensor(labels)img_id?=?torch.tensor([index])areas?=?torch.tensor(areas,?dtype=torch.float32)#?Iscrowdiscrowd?=?torch.zeros((num_objs,),?dtype=torch.int64)#?Annotation?is?in?dictionary?formatmy_annotation?=?{}my_annotation["boxes"]?=?boxesmy_annotation["labels"]?=?labelsmy_annotation["image_id"]?=?img_idmy_annotation["area"]?=?areasmy_annotation["iscrowd"]?=?iscrowdreturn?img,?my_annotationdef?__len__(self):return?len(self.image_paths)def?collate_fn(batch):return?tuple(zip(*batch))def?init_dataloader(image_paths,?train_json,?batch_size=2,?for_train=False):dataset?=?CreateDataset(image_paths,?train_json,?for_train)dataloader?=?torch.utils.data.DataLoader(dataset,batch_size=batch_size,shuffle=True,num_workers=0,collate_fn=collate_fn,)return?dataloadertrain_loader?=?init_dataloader(train_image_paths,?train_json,?for_train=True)?#?定義訓練集合 eval_loader?=?init_dataloader(eval_image_paths,?train_json)?#?定義驗證集合 #?訓練模型 def?train_epoch(model,?train_loader,?optimizer):model.to(device)pbar?=?tqdm(train_loader)with?autograd.detect_anomaly():loss_train,?loss_eval?=?[],?[]bg_time?=?time.time()model.train()for?batch_imgs,?batch_annotations?in?pbar:imgs?=?[img.to(device)?for?img?in?batch_imgs]annotations?=?[{k:?v.to(device)?for?k,?v?in?t.items()}?for?t?in?batch_annotations]loss_dict?=?model(imgs,?annotations)losses?=?sum(loss?for?loss?in?loss_dict.values())optimizer.zero_grad()losses.backward()optimizer.step()loss_train.append(losses.cpu().detach().numpy())desc?=?"%.3f"?%?(sum(loss_train)?/?len(loss_train))pbar.set_description(desc)print('training?loss?=?',?sum(loss_train)?/?len(loss_train))return?modeldef?train_process(train_loader,?eval_loader):model?=?get_model(num_classes=NUM_CLASS)if?os.path.exists(MODEL_PATH):model.load_state_dict(torch.load(MODEL_PATH))params?=?[p?for?p?in?model.parameters()?if?p.requires_grad]optimizer?=?torch.optim.SGD(params,?lr=0.005,?momentum=0.9,?weight_decay=0.0005)#?and?a?learning?rate?schedulerlr_scheduler?=?torch.optim.lr_scheduler.StepLR(optimizer,?step_size=3,?gamma=0.1)for?idx_epoch?in?range(1):print("training?%d?epoch"?%?idx_epoch)model?=?train_epoch(model,?train_loader,?optimizer)lr_scheduler.step()torch.save(model.state_dict(),?MODEL_PATH+'TEMP')return?model model?=?train_process(train_loader,?eval_loader) ??0%|??????????|?0/42?[00:00<?,??it/s]training?0?epoch1.715:?100%|██████████|?42/42?[00:23<00:00,??1.82it/s]training?loss?=??1.7150054659162248 #?生成驗證集的結果,并檢查驗證集上的預測效果 def?boxes_to_lines(preds,?image_id):r?=?[]for?bbox,?label,?score?in?zip(preds[0]["boxes"].cpu().detach().numpy(),preds[0]["labels"].cpu().detach().numpy(),preds[0]["scores"].cpu().detach().numpy(),):#?torchvision生成的bounding?box格式為xyxy,需要轉成xywhxyxy?=?list(bbox)xywh?=?[xyxy[0],?xyxy[1],?xyxy[2]?-?xyxy[0],?xyxy[3]-?xyxy[1]]r.append({"image_id":?image_id,"bbox":?xywh,"category_id":?label,"score":?score,})return?rdef?output_result(image_paths):result?=?[]model?=?get_model(num_classes=NUM_CLASS)if?os.path.exists(MODEL_PATH):model.load_state_dict(torch.load(MODEL_PATH))model.to(device)model.eval()for?image_path?in?tqdm(image_paths):img?=?Image.open(image_path)image_id?=?os.path.basename(image_path).split(".")[0]transform?=?T.Compose([T.ToTensor()])??#?Defing?PyTorch?Transformimg_tensor?=?transform(img)??#?Apply?the?transform?to?the?imagepreds?=?model([img_tensor.to(device)])result?+=?boxes_to_lines(preds,?image_id)bboxes,?labels?=?list(zip(*[(bbox,?label)for?bbox,?label?in?zip(preds[0]["boxes"].cpu().detach().numpy(),preds[0]["labels"].cpu().detach().numpy())?]))img?=?draw_boxes(np.array(img),?bboxes,?labels)plt.imshow(img)return?resultresult?=?output_result(val_image_paths) 100%|██████████|?10/10?[00:01<00:00,??7.11it/s]
#?將結果保存到本地JSON文件,由于帶有numpy的類型,需要自定義一個JSONEncoder class?MyEncoder(json.JSONEncoder):def?default(self,?obj):if?isinstance(obj,?np.integer):return?int(obj)elif?isinstance(obj,?np.floating):return?float(obj)elif?isinstance(obj,?np.ndarray):return?obj.tolist()else:return?super(MyEncoder,?self).default(obj)with?open(SAVE_PATH,?"w")?as?f:json.dump(result,?f,?cls=MyEncoder)
2020海華AI挑戰賽·垃圾分類
2020年1月15日,由中關村海華信息技術前沿研究院與清華大學交叉信息研究院聯合主辦,中關村科技園區海淀園管理委員會與北京市海淀區城市管理委員會作為指導單位,biendata競賽平臺承辦,華為NAIE云服務提供AI開發環境的“2020海華AI挑戰賽·垃圾分類”正式開賽。本次比賽旨在以算法賽選拔優秀的AI人才以及推動有潛力的項目孵化。
和一般的人工智能大賽相比,本次比賽特別設置了中學組賽道,重在培養初高中學生的對人工智能的熱情,能夠更早期的接觸AI技術,鍛煉他們的思維與實踐能力,成為“AI年輕人”參與藍圖建設,但凡在中學組賽程中提交算法模型且獲得達標分數的參賽者,均可獲得由賽事組委會頒發的電子版“參賽證書”。海華研究院將制作比賽配套的視頻培訓課程,并于比賽期間定期線上發布。并且對于比賽中展示出有潛力的優秀項目,海華研究院將優先予以孵化與投資。項目團隊也可獲得海華與叉院的技術科研崗位的面試直通機會。除了以上獎勵之外,大賽設置了總額高達30萬元的獎金,其中中學組賽道總獎金9萬元,技術組賽道總獎金21萬元。
掃描下方二維碼直達賽事頁面。
總結
以上是生活随笔為你收集整理的海华·垃圾分类AI挑战赛baseline分享,评测得分最高至0.85的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【白话机器学习】算法理论+实战之AdaB
- 下一篇: [PLM专题] 十分钟了解文本分类通用训