{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**In the notebook, We'll detect head of the wheat**\n\n<img src='https://i.ytimg.com/vi/yqvMuw-uedU/maxresdefault.jpg' height=500 width=800/>","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"# Firstly we'll preprocess the dataset in suitable format for YOLOv5.\nThe format should look like this:\n\n    - converter(main directory)\n        - val\n            - labels (contains all the box dimensions)\n            - images (contains images)\n        - train\n            - labels\n            - images","metadata":{}},{"cell_type":"code","source":"# Importing Required packages\nimport os\nimport torch\nimport numpy as np\nimport pandas as pd\n\nfrom tqdm.auto import tqdm\nimport shutil as sh\n\nimport matplotlib.pyplot as plt\nfrom IPython.display import Image, clear_output\n%matplotlib inline","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Reading the dataframe\ndf = pd.read_csv('/kaggle/input/human-faces-object-detection/faces.csv')\ndf.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# read the training data.\n\n# df = pd.read_csv('../input/global-wheat-detection/train.csv')\n# bboxs = np.stack(df['bbox'].apply(lambda x: np.fromstring(x[1:-1], sep=',')))\n# for i, column in enumerate(['x', 'y', 'w', 'h']):\n#     df[column] = bboxs[:,i]\n# df.drop(columns=['bbox'], inplace=True)\n# df['x_center'] = (df['x'] + df['w'])/2\n# df['y_center'] = (df['y'] + df['h'])/2\ndf['classes'] = 0\n\n# # Making new dataframe, suitable to make suitable dataset for yolov5\n# df = df[['image_id','x', 'y', 'w', 'h','x_center','y_center','classes']]\ndf.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"index = list(set(df.image_name))\nprint(\"Total Images: \",len(index))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# This cell will automatically make the dataset and save it to convertor folder\nsource = 'images'\nif True:\n    for fold in [0]:\n        val_index = index[len(index)*fold//5:len(index)*(fold+1)//5]\n        for name,mini in tqdm(df.groupby('image_name')):\n            if name in val_index:\n                path2save = 'val/'\n            else:\n                path2save = 'train/'\n            if not os.path.exists('convertor/fold{}/labels/'.format(fold)+path2save):\n                os.makedirs('convertor/fold{}/labels/'.format(fold)+path2save)\n            with open('convertor/fold{}/labels/'.format(fold)+path2save+name+\".txt\", 'w+') as f:\n                row = mini[['classes','x0','y0','width','height']].astype(float).values\n                row = row/1024\n                row = row.astype(str)\n                for j in range(len(row)):\n                    text = ' '.join(row[j])\n                    f.write(text)\n                    f.write(\"\\n\")\n            if not os.path.exists('convertor/fold{}/images/{}'.format(fold,path2save)):\n                os.makedirs('convertor/fold{}/images/{}'.format(fold,path2save))\n            sh.copy(\"/kaggle/input/human-faces-object-detection/{}/{}\".format(source,name),'convertor/fold{}/images/{}/{}'.format(fold,path2save,name))\n    \n# Bases on this notebook: https://www.kaggle.com/orkatz2/yolov5-train","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Now it's turn to train the dataset using YOLOv5\nOur stategy is to:\n1. Clone [yolov5](https://github.com/ultralytics/yolov5.git) directory from github\n\n2. Install the requirements for yolov5\n\n3. Make a [data.yml](https://github.com/ultralytics/yolov5/blob/master/data/coco.yaml) file indicating our training directory, validation directory, number of classes, and classname \n\n4. Chose a model bases on your requirement(YOLOv5s, YOLOv5m, YOLOv5l, YOLOv5x)\n\n5. Change the YOLOv5(..).yml acording to your dataset. As we have only one class, we'll use nc: 1\n\n6. Start the Training","metadata":{}},{"cell_type":"code","source":"# Cloning the repo\n!git clone https://github.com/ultralytics/yolov5.git\nclear_output()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Moving the folders to our working directory\n!mv ./yolov5/* ./","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Checking if the files correctly cloned and moved\n!ls","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# installing the requirements file\n!pip install -r requirements.txt\nclear_output()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#customize iPython writefile so we can write variables\n\nfrom IPython.core.magic import register_line_cell_magic\n@register_line_cell_magic\ndef writetemplate(line, cell):\n    with open(line, 'w') as f:\n        f.write(cell.format(**globals()))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Our image and annotation files are saved into this directory\n\nprint(os.listdir(\"./convertor/fold0\"))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Making a directory for storing our data.yaml and custom YOLOv5(..).yml files\n!mkdir DataFile","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writetemplate ./DataFile/data.yaml\n\ntrain: ./convertor/fold0/images/train # training directory\nval: ./convertor/fold0/images/val # validation directory\ntest: ./convertor/fold0/images/val # I'll use validation directory for test image\nnc: 1 # number of class\nnames: ['face'] # name of the class","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Our models structure files are save here. These are used for training [coco dataset](https://cocodataset.org/#home). But for using for our custom dataset, we have to change the nc parameter to 1, insted of 80.","metadata":{}},{"cell_type":"code","source":"print(os.listdir(\"./models\"))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# checking the yolov5s model architecture\n!cat ./models/yolov5.yaml","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Modifying the yolov5s model architecture for nc: 1","metadata":{}},{"cell_type":"code","source":"%%writetemplate ./DataFile/customYOLOv5x.yaml\n\n# parameters\nnc: 80  # number of classes\ndepth_multiple: 1.33  # model depth multiple\nwidth_multiple: 1.25  # layer channel multiple\n\n# anchors\nanchors:\n  - [10,13, 16,30, 33,23]  # P3/8\n  - [30,61, 62,45, 59,119]  # P4/16\n  - [116,90, 156,198, 373,326]  # P5/32\n\n# YOLOv5 backbone\nbackbone:\n  # [from, number, module, args]\n  [[-1, 1, Focus, [64, 3]],  # 0-P1/2\n   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4\n   [-1, 3, C3, [128]],\n   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8\n   [-1, 9, C3, [256]],\n   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16\n   [-1, 9, C3, [512]],\n   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32\n   [-1, 1, SPP, [1024, [5, 9, 13]]],\n   [-1, 3, C3, [1024, False]],  # 9\n  ]\n\n# YOLOv5 head\nhead:\n  [[-1, 1, Conv, [512, 1, 1]],\n   [-1, 1, nn.Upsample, [None, 2, 'nearest']],\n   [[-1, 6], 1, Concat, [1]],  # cat backbone P4\n   [-1, 3, C3, [512, False]],  # 13\n\n   [-1, 1, Conv, [256, 1, 1]],\n   [-1, 1, nn.Upsample, [None, 2, 'nearest']],\n   [[-1, 4], 1, Concat, [1]],  # cat backbone P3\n   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)\n\n   [-1, 1, Conv, [256, 3, 2]],\n   [[-1, 14], 1, Concat, [1]],  # cat head P4\n   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)\n\n   [-1, 1, Conv, [512, 3, 2]],\n   [[-1, 10], 1, Concat, [1]],  # cat head P5\n   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)\n\n   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)\n  ]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"With our __data.yaml__ and __custom_yolov5s.yaml__ files ready to go we are ready to train!\n\nTo kick off training we running the training command with the following options:\n\n- ```img```: define input image size\n- ```batch```: determine batch size\n- ```epochs```: define the number of training epochs.\n- ```data```: set the path to our yaml file\n- ```cfg```: specify our model configuration\n- ```weights```: specify a custom path to weights. (Note: you can download weights from the Ultralytics Google Drive folder)\n- ```name```: result names\n- ```cache```: cache images for faster training\n- ```evolve```: evolve hyperparameters","metadata":{}},{"cell_type":"code","source":"# You should skip this line\n!wandb off","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n!python train.py --img 640  --batch 16 --epochs 1 --data ./DataFile/data.yaml --cfg ./DataFile/customYOLOv5x.yaml --weights yolov5x.pt  --name Result --cache","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training is completed.\nResults are saved in ./runs/train directory","metadata":{}},{"cell_type":"code","source":"# let's check the training result directory.\n# Here model and result are saved\n!ls -R ./runs/train","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Analize the training and validation result\nImage('./runs/train/Result/results.png')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Analize the Confusion matrix\nImage('./runs/train/Result/confusion_matrix.png',width=400)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Let's run detection on test images\nParameters to consider:\n\n- ```img-size```: define input image size\n- ```conf```: Minimum threshold of confidence\n- ```source```: Location of the image/video file\n- ```weights```: specify our trined weight file\n- ```augment```: Augmentation of the images during detection for better result","metadata":{}},{"cell_type":"code","source":"!python detect.py --img-size 800  --conf 0.2 --source ../input/global-wheat-detection/test --weights ./runs/train/Result/weights/best.pt --augment ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Detected images/video are saved in this path\n!ls -R runs/detect","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Image('runs/detect/exp/2fd875eaa.jpg',width=400)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Techniques to push the training result\n\n* Experimenting with hyper parameters\n* increasing the image size","metadata":{}},{"cell_type":"code","source":"# hyper parameters data are saved in this location\n!ls ./data","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Let's train the model with better hyper parameter**","metadata":{}},{"cell_type":"code","source":"%cat ./data/hyp.finetune.yaml","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writetemplate ./DataFile/hyp.custom.yaml\n\nlr0: 0.0032\nlrf: 0.12\nmomentum: 0.843\nweight_decay: 0.00036\nwarmup_epochs: 2.0\nwarmup_momentum: 0.5\nwarmup_bias_lr: 0.05\nbox: 0.0296\ncls: 0.243\ncls_pw: 0.631\nobj: 0.301\nobj_pw: 0.911\niou_t: 0.2\nanchor_t: 2.91\n# anchors: 3.63\nfl_gamma: 0.0   # \nhsv_h: 0.0138\nhsv_s: 0.002\nhsv_v: 0.2\ndegrees: 0.2\ntranslate: 0.0\nscale: 0.3\nshear: 0.0\nperspective: 0.0\nflipud: 0.00856\nfliplr: 0.5\nmosaic: 1.0\nmixup: 0.5","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- ```--hyp```: Location of new custom hyper parameter","metadata":{}},{"cell_type":"markdown","source":"# I've used weight of previous trained model.So, this training will initilize from the best result(weight) of the previous model","metadata":{}},{"cell_type":"code","source":"%%time\n!python train.py --img 1024  --batch 4 --epochs 1 --data ./DataFile/data.yaml --cfg ./DataFile/customYOLOv5x.yaml --hyp ./DataFile/hyp.custom.yaml  --weights ./runs/train/Result/weights/best.pt  --name FineTuned","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# new training result\nos.listdir('./runs/train')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls -R ./runs/train/FineTuned","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Ensembling\nI've used two models during detection. It'll increase detection result.","metadata":{}},{"cell_type":"code","source":"!python detect.py  --img 800 --weights ./runs/train/Result/weights/best.pt ./runs/train/FineTuned/weights/best.pt --source ../input/global-wheat-detection/test","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls runs/detect","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls -R runs/detect/exp","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Image('runs/detect/exp/2fd875eaa.jpg', width=400)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# TTA(Test Time Augmentation) and Model Ensembling","metadata":{}},{"cell_type":"code","source":"!python test.py --weights ./runs/train/Result/weights/best.pt ./runs/train/FineTuned/weights/best.pt --data ./DataFile/data.yaml --img 800 --augment","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls -R runs/test/exp","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# That's all for today, I'll update the notebook for pseudo labelling soon.\n# Please let me know if you face any issue or want have any confusion","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}