{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-08-07T02:42:03.649732Z","iopub.execute_input":"2021-08-07T02:42:03.650112Z","iopub.status.idle":"2021-08-07T02:42:07.711481Z","shell.execute_reply.started":"2021-08-07T02:42:03.65008Z","shell.execute_reply":"2021-08-07T02:42:07.703264Z"},"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 安装\n按照YOLOX官网首先安装YOLOX，然后安装apex，如果没有支持半精度训练的显卡可以不需要安装，最后安装pycocotools","metadata":{}},{"cell_type":"code","source":"!git clone https://github.com/Megvii-BaseDetection/YOLOX.git\n!pip install -U pip && pip install -r YOLOX/requirements.txt\n!pip install -v -e YOLOX ","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:06:01.521916Z","iopub.execute_input":"2021-08-07T03:06:01.522404Z","iopub.status.idle":"2021-08-07T03:06:29.311497Z","shell.execute_reply.started":"2021-08-07T03:06:01.522356Z","shell.execute_reply":"2021-08-07T03:06:29.310332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!git clone https://github.com/NVIDIA/apex\n!pip install -v --disable-pip-version-check --no-cache-dir --global-option=\"--cpp_ext\" --global-option=\"--cuda_ext\" apex/","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:06:29.315895Z","iopub.execute_input":"2021-08-07T03:06:29.316222Z","iopub.status.idle":"2021-08-07T03:10:58.840275Z","shell.execute_reply.started":"2021-08-07T03:06:29.31617Z","shell.execute_reply":"2021-08-07T03:10:58.839174Z"},"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pip install cython; pip install 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:10:58.842188Z","iopub.execute_input":"2021-08-07T03:10:58.842535Z","iopub.status.idle":"2021-08-07T03:11:16.037311Z","shell.execute_reply.started":"2021-08-07T03:10:58.842475Z","shell.execute_reply":"2021-08-07T03:11:16.036363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!mkdir /kaggle/working/annotations","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 数据预处理\n\n讲一下思路，本来我是想直接把数据扔进去进行训练的，因为YOLOX直接支持coco格式的数据集，但是却会遇到一个很奇怪的bug，这个问题我还去YOLOX里面问了，想看报错信息的可以[点击这里](https://github.com/Megvii-BaseDetection/YOLOX/issues/388)\n\n报的错如下：\n```\nTypeError: can't convert np.ndarray of type numpy.ulonglong. The only supported types are: float64, float32, float16, complex64, complex128, int64, int32, int16, int8, uint8, and bool.\n```\n说的是无法转换ulong的数据，查了很久，发现是由于image_id过大导致的，简单的直接cast为float会造成精度损失，直接导致另一个问题，在对应annotation里面的id和image id的时候会报assertion error. 所以最终的办法是直接替换掉image_id，并且category_id也建立新的映射关系，变为1-5，代码如下","metadata":{}},{"cell_type":"code","source":"\nimport json\nfrom copy import deepcopy\n# data_list = ['merged_train.json',]\ndata_list = ['/kaggle/input/cowboyoutfits/train.json']\n# data_list = ['new_valid.json']\n\ncat = {87:1, 1034:5, 131:2, 318:3, 588:4}\n\ndict_list = []\nfor idx, data in enumerate(data_list):\n    with open(data) as f:\n        dict_list.append(json.load(f))\n\nnew_data = {}\nprint(dict_list[0].keys())\n\n\nnew_data['info'] = dict_list[0]['info']\n# new_data['licenses'] = dict_list[0]['licenses']\nnew_categories = []\nfor category in dict_list[0]['categories']:\n    new_category = deepcopy(category)\n    new_category['id'] = cat[category['id']]\n    new_categories.append(new_category)\nnew_data['categories'] = new_categories\nnew_data['annotations'] = []\nnew_data['images'] = []\nprint(new_data)\n\nanno_count = 1\nanno_id_dict = {}\ncount = 1\nanno_dict = {}\nfor data in dict_list:\n    annotations = []\n    for annotation in data['annotations']:\n        new_annotation = deepcopy(annotation)\n        new_annotation['category_id'] = cat[annotation['category_id']]\n        if annotation['image_id'] not in anno_dict:\n            new_annotation['image_id'] = anno_count\n            anno_dict[annotation['image_id']] = anno_count\n            anno_count += 1\n            anno_id_dict[anno_count] = 1\n        else:\n            new_annotation['image_id'] = anno_dict[annotation['image_id']]\n            anno_id_dict[anno_dict[annotation['image_id']]] += 1\n        new_annotation['id'] = count\n        count +=1\n        annotations.append(new_annotation)\n    \n    new_data['annotations'] = annotations\n\n    images = []\n    \n    for image in data['images']:\n        new_image = deepcopy(image)\n        new_image['id'] = anno_dict[image['id']]\n        images.append(new_image)\n    new_data['images'] = images\n\n    print(f'annotation size: {len(new_data[\"annotations\"])}')\n    print(f'image size: {len(new_data[\"images\"])}')\n\nwith open('/kaggle/working/annotations/new_train.json', 'w') as res:\n    json.dump(new_data, res)","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:12:47.963446Z","iopub.execute_input":"2021-08-07T03:12:47.963898Z","iopub.status.idle":"2021-08-07T03:12:49.171416Z","shell.execute_reply.started":"2021-08-07T03:12:47.963861Z","shell.execute_reply":"2021-08-07T03:12:49.170379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cp ../input/d/herunyu/cowboyoutfits/yolox_mm.py YOLOX/\n!cp ../input/d/herunyu/cowboyoutfits/predict.py YOLOX/","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:13:31.909446Z","iopub.execute_input":"2021-08-07T03:13:31.909887Z","iopub.status.idle":"2021-08-07T03:13:33.279319Z","shell.execute_reply.started":"2021-08-07T03:13:31.909849Z","shell.execute_reply":"2021-08-07T03:13:33.278162Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 模型选择\n具体训练可以参考YOLOX官方的文档，我这里使用的是YOLOX_M的模型，也就是中等的模型，自定义了exp文件来定义模型以及dataloader需要加载的数据路径，还有训练的epoch数量，以及自定义的dataloader","metadata":{}},{"cell_type":"code","source":"!head -n 50 YOLOX/yolox_mm.py","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:13:37.547249Z","iopub.execute_input":"2021-08-07T03:13:37.547646Z","iopub.status.idle":"2021-08-07T03:13:38.226194Z","shell.execute_reply.started":"2021-08-07T03:13:37.547605Z","shell.execute_reply":"2021-08-07T03:13:38.225171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cp -r /kaggle/input/cowboyoutfits/images /kaggle/working/","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:14:53.709989Z","iopub.execute_input":"2021-08-07T03:14:53.71037Z","iopub.status.idle":"2021-08-07T03:15:39.286496Z","shell.execute_reply.started":"2021-08-07T03:14:53.710332Z","shell.execute_reply":"2021-08-07T03:15:39.28537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 加载模型\n!wget https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_m.pth /kaggle/working/","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:16:52.645497Z","iopub.execute_input":"2021-08-07T03:16:52.645915Z","iopub.status.idle":"2021-08-07T03:17:02.931792Z","shell.execute_reply.started":"2021-08-07T03:16:52.645877Z","shell.execute_reply":"2021-08-07T03:17:02.93079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 训练\n由于kernel里训练时间较久，所以这里跑了10个epoch，会看到一开始就显示No mosaic aug now.实际上应该是最后15个epoch开始才不会加入augmentation，我训练了40个epoch左右得到的结果，按照设置，前5个epoch为warming up，后15个为no aug，中间20为加入aug的训练。下面的代码 -d 表示用一张显卡， -o 为预读数据到缓存中  --fp16是用了apex的半精度训练","metadata":{}},{"cell_type":"code","source":"!python YOLOX/tools/train.py -f YOLOX/yolox_mm.py -d 1 -b 8 -o --fp16 -c /kaggle/working/yolox_m.pth","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:17:53.721397Z","iopub.execute_input":"2021-08-07T03:17:53.721773Z","iopub.status.idle":"2021-08-07T03:27:05.255752Z","shell.execute_reply.started":"2021-08-07T03:17:53.721737Z","shell.execute_reply":"2021-08-07T03:27:05.253582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"在kernel里跑了10个epoch意思意思。提交的代码也写在predict.py里了，这里看下最后相关的代码","metadata":{}},{"cell_type":"code","source":"!tail -n 120 YOLOX/predict.py","metadata":{"execution":{"iopub.status.busy":"2021-08-07T03:27:47.300987Z","iopub.execute_input":"2021-08-07T03:27:47.301326Z","iopub.status.idle":"2021-08-07T03:27:47.946346Z","shell.execute_reply.started":"2021-08-07T03:27:47.301287Z","shell.execute_reply":"2021-08-07T03:27:47.94533Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 最后Test结果","metadata":{}},{"cell_type":"markdown","source":"![image.png](attachment:2ccf1aa2-78d1-4428-8314-1fbc239cfc63.png)","metadata":{},"attachments":{"2ccf1aa2-78d1-4428-8314-1fbc239cfc63.png":{"image/png":"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"}}}]}