{"cells":[{"metadata":{},"cell_type":"markdown","source":"I'm currently doing the fastai practical deep learning course. An option for lesson 3's homework was to try out a segmentation problem, so this seemed like a good opportunity to do that. To keep things simple I kept it to just the 27 main apparel items (no apparel parts or attributes), and I converted run-encoded pixels to png images to feed into the standard SegmentationItemList.\n\nImprovement areas:\n- Create a custom SegmentationItemList that takes the run length enconded masks directly (as is done in internueron's kernel)\n- Use more data, currently limited to ~10% of the training images (5k) to save time\n- Use larger images, currently using 224x224 images to save time\n\nI may work on the above, but for now am going to continue with the course. Maybe this kernel helps others."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# import required packages\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom fastai.vision import *\nfrom fastai.callbacks.hooks import *\nfrom fastai.utils.mem import *\nfrom progressbar import ProgressBar\nimport cv2\nimport os\nimport json","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# create a folder for the mask images\nif  not os.path.isdir('../labels'):\n    os.makedirs('../labels')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path = Path(\"../input\")\npath_img = path/'train'\npath_lbl = Path(\"../labels\")\n\n# only the 27 apparel items, plus 1 for background\n# model image size 224x224\ncategory_num = 27 + 1\nsize = 224\n\n# get and show categories\nwith open(path/\"label_descriptions.json\") as f:\n    label_descriptions = json.load(f)\n\nlabel_names = [x['name'] for x in label_descriptions['categories']]\nprint(label_names)\n\n# train dataframe\ndf = pd.read_csv(path/'train.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# training jpg images are in the train folder\nfnames = get_image_files(path_img)\nprint(fnames[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# need a function to turn the run encoded pixels from train.csv into an image mask\n# there are multiple rows per image for different apparel items, this groups them into one mask\ndef make_mask_img(segment_df):\n    seg_width = segment_df.at[0, \"Width\"]\n    seg_height = segment_df.at[0, \"Height\"]\n    seg_img = np.full(seg_width*seg_height, category_num-1, dtype=np.int32)\n    for encoded_pixels, class_id in zip(segment_df[\"EncodedPixels\"].values, segment_df[\"ClassId\"].values):\n        pixel_list = list(map(int, encoded_pixels.split(\" \")))\n        for i in range(0, len(pixel_list), 2):\n            start_index = pixel_list[i] - 1\n            index_len = pixel_list[i+1] - 1\n            if int(class_id.split(\"_\")[0]) < category_num - 1:\n                seg_img[start_index:start_index+index_len] = int(class_id.split(\"_\")[0])\n    seg_img = seg_img.reshape((seg_height, seg_width), order='F')\n    return seg_img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# we can look at an image to see how the processing works\n# the original image\nimg_file = fnames[500]\nimg = open_image(img_file)\nimg.show(figsize=(5,5))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# convert rows for this image into a numpy array mask \nimg_name = os.path.basename(img_file)\nimg_df = df[df.ImageId == img_name].reset_index()\n#img_df = img_df.iloc[0:1]\n#img_df = img_df[img_df.ClassId.astype(int) < category_num - 1].reset_index()\nimg_mask = make_mask_img(img_df)\nplt.imshow(img_mask)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# convert the numpy array into a three channel png that can be used in the standard SegmentationItemList\n# then write into the labels folder as png and show the image\n# all pixels have the category numbers, so it looks like a dark greyscale image\nimg_mask_3_chn = np.dstack((img_mask, img_mask, img_mask))\ncv2.imwrite('../labels/' + os.path.splitext(img_name)[0] + '_P.png', img_mask_3_chn)\npng = open_image('../labels/' + os.path.splitext(img_name)[0] + '_P.png')\npng.show(figsize=(5,5))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# use fastai's open_mask for an easier-to-view image (and check it works...)\nmask = open_mask('../labels/' + os.path.splitext(img_name)[0] + '_P.png')\nmask.show(figsize=(5,5), alpha=1)\nprint(mask.data)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# run the same procedure for a sample of first 5000 images in dataset\nimages = df.ImageId.unique()[:5000]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pbar = ProgressBar()\n\nfor img in pbar(images):\n    img_df = df[df.ImageId == img].reset_index()\n    img_mask = make_mask_img(img_df)\n    img_mask_3_chn = np.dstack((img_mask, img_mask, img_mask))\n    cv2.imwrite('../labels/' + os.path.splitext(img)[0] + '_P.png', img_mask_3_chn)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# before creating the databunch we need a function to find the mask images \n# also set the batch size, categories and wd\nget_y_fn = lambda x: path_lbl/f'{Path(x).stem}_P.png'\nbs = 32\n#classes = label_names\ncodes = list(range(category_num))\nwd = 1e-2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# create the databunch\nimages_df = pd.DataFrame(images)\n\nsrc = (SegmentationItemList.from_df(images_df, path_img)\n       .split_by_rand_pct()\n       .label_from_func(get_y_fn, classes=codes))\n\ndata = (src.transform(get_transforms(), size=size, tfm_y=True)\n       .databunch(bs=bs)\n       .normalize(imagenet_stats))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# look at a batch\ndata.show_batch(3, figsize=(10,10))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# I create an accuracy metric which excludes the background pixels\n# not sure if this is correct\ndef acc_fashion(input, target):\n    target = target.squeeze(1)\n    mask = target != category_num - 1\n    return (input.argmax(dim=1)==target).float().mean()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# learner, include where to save pre-trained weights (default is in non-write directory)\nlearn = unet_learner(data, models.resnet34, metrics=acc_fashion, wd=wd, model_dir=\"/kaggle/working/models\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# run learning rate finder\nlr_find(learn)\nlearn.recorder.plot()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# set learning rate based on roughly the steepest part of the curve\nlr=1e-4","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# train for 10 cycles frozen\nlearn.fit_one_cycle(10, slice(lr), pct_start=0.9)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# take a look at some results\nlearn.show_results()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# unfreeze earlier weights\nlearn.unfreeze()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# decrease the learning rate\nlrs = slice(lr/400,lr/4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# train for 10 more cycles unfrozen\nlearn.fit_one_cycle(10, lrs, pct_start=0.8)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# more results\nlearn.show_results()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}