{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":4829,"databundleVersionId":44847,"sourceType":"competition"}],"dockerImageVersionId":30588,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# DSI206 Project\n\n\n<div style=\"pxborder-radius:10px; border:#80cbc4\n solid; padding: 15px; background-color: #e0f2f1; font-size:100%; text-align:left\">\n\n<h3 align=\"left\"><font color='#4db6ac'> 💻 Member</font></h3>\n\n1. พลพล จันทรอาภา        6524650063\n1. สิริชูพงษ์ จำปาศักดิ์       6524650097\n1. ชนากานต์ วีรเดชทวีวรรณ   6524651194\n1. ภัทรนิษฐ์ เจริญเนาวพัฒน์   6524651327\n1. ฤทธิ์ธิดา ยิ่งนัยเกียรติ     6524651350\n1. อินทิตา พิทย์พงศ์กุล      6524651475\n </div>  \n    \n### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:140%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\"> Yelp Restaurant Photo Classification </p>\n    \n  \n","metadata":{}},{"cell_type":"markdown","source":"# <div style= \"color:#FBF8BE;display:inline-block;border-radius:12px;background-color:#FBF8BE;font-block:Nexa;overflow:hidden;border:3px solid #000000\"><p style=\"padding:15px;color:#234E70;overflow:hidden;font-size:70%;letter-spacing:0.5px;margin:0\"><b> </b>Table of Contents📰</p></div>\n    \n* [Import for Data Exploration, Data Preparation and Model](#import)\n* [Data Loading](#load)\n* [Data Exploration & Preparation DataFrame](#exandprep)\n* [Data Preparation](#dataprep)\n* [About model](#model)\n* [Model Prediction](#modelpre)\n* [Submission File RestNet34 ](#submit)\n* [Reference](#ref)\n","metadata":{}},{"cell_type":"markdown","source":"### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:130%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\" id=\"import\"> Import for Data Exploration, Data Preparation and Model </p>","metadata":{}},{"cell_type":"code","source":"import numpy as np                                                    #ใช้ทำงานร่วมกับข้อมูลประเภทตัวเลข\nimport pandas as pd                                                   #ใช้ทำงานร่วมกับข้อมูลที่มีโครงสร้าง\nfrom IPython.display import display                                   #เพื่อแสดงรูปภาพ\nimport matplotlib.pyplot as plt                                       #ใช้สำหรับการพล็อตและแสดงกราฟ\nimport matplotlib.image as mpimg\nimport plotly                                                         #ใช้สร้างกราฟและแสดงผลข้อมูล\nimport plotly.graph_objs as go                                        #สร้าง interactive กราฟ\nfrom PIL import Image                                                 #เพื่อทำการประมวลผลรูปภาพ และทำงานกับภาพต่าง ๆ ใน Python ได้อย่างสะดวก\nfrom PIL import ImageFilter                                           #ใช้ปรับปรุง หรือเปลี่ยนแปลงลักษณะต่าง ๆ ของรูปภาพ\nimport cv2                                                            #เพื่อเรียกใช้คำสั่งการค้นหาที่ต้องการ\nimport random                                                         #สุ่มค่าต่างๆที่เรากำหนดไว้\nfrom collections import Counter                                       #นับจำนวนของอะไรบางอย่างว่ามีอะไรอยู่เท่าไหร่\nimport time                                                           #ใช้วัดเวลาการประมวลผลหรือทำงานที่เกี่ยวข้องกับเวลา\nimport os                                                             #ใช้ทำงานกับระบบไฟล์\nimport torch                                                          #เพื่อเข้าถึง tensor operations และ modules ที่มีใน PyTorch\nfrom torch.utils.data import Dataset, random_split, DataLoader        #Dataset ใช้สร้าง dataset, random_split ใช้แบ่ง dataset เป็นส่วน ๆ โดยสุ่ม, DataLoader ใช้โหลดข้อมูลจาก dataset เพื่อใช้ในการ train model\nimport torchvision.models as models                                   #เป็น library ที่มีโมเดล neural network ที่ถูกทำการ pre-trained ให้ใช้งานได้กับการทำนายภาพ\nimport torchvision.transforms as transforms                           #เป็นโมดูลที่มีคำสั่งต่าง ๆ สำหรับแปลงไฟล์รูปภาพ\nfrom sklearn.metrics import f1_score                                  #ใช้คำนวณ F1 Score สำหรับการจำแนกประเภทของโมเดลที่คุณฝึกอบรมแล้ว โดยเปรียบเทียบการทำนายกับข้อมูลจริง \nimport torch.nn.functional as F                                       #ใช้ในการคำนวณ Loss\nfrom torchvision.utils import make_grid                               #ใช้เพื่อสร้างกริดของรูปภาพ                                     \n\nfrom fastai import *                                                  #library Fatsai เอาเข้ามาทำ Computer Vision\nfrom fastai.vision import *                                           #ใช้สำหรับการประมวลผลข้อมูลที่เกี่ยวข้องกับ image processing                      \nfrom fastai.metrics import accuracy                                   #คำนวณค่า accuracy ของโมเดล\nfrom fastai.vision.all import *                                       #เข้าถึงทุกฟังก์ชันและคลาสของ fastai vision\nfrom fastai.metrics import error_rate                                 #ใช้คำนวณ error rate ของโมเดล\nfrom IPython.display import Image                                     #ใช้แสดงรูปภาพ\nfrom pathlib import Path                                              #เพื่อจัดการ path ของ file และ directory\nfrom tqdm.notebook import tqdm                                        #เพื่อแสดงความคืบหน้า(จำนวนครั้งที่ต้องทำงาน, เวลาที่ประมาณว่างานจะเสร็จ, ความเร็วในการทำงาน)ที่ terminal กำลังทำ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:44:16.638523Z","iopub.execute_input":"2023-11-27T13:44:16.638785Z","iopub.status.idle":"2023-11-27T13:44:24.421875Z","shell.execute_reply.started":"2023-11-27T13:44:16.638760Z","shell.execute_reply":"2023-11-27T13:44:24.420759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:140%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\" id=\"load\"> Data Loading </p>\n<div class=\"alert alert-success\">  \n<b>📍</b> ขั้นตอนการเตรียมความพร้อมในการใช้งานของไฟล์และตั้งตัวแปรต่าง ๆ  \n</div>\n\n\n<div style=\"pxborder-radius:10px; border:#DEB887 solid; padding: 15px; background-color: #FFFAF0; font-size:85%; text-align:left\">\n\n<h3 align=\"left\"><font color='#DEB887'>💡 นำเข้าข้อมูลจำนวน 6 ไฟล์ ดังนี้ และเตรียมความพร้อมของไฟล์พร้อมทั้งตั้งตัวแปรเพื่อใช้สำหรับการวิเคราะห์ข้อมูลต่อไป</font></h3>\n\n1. train_photos.tgz - รูปภาพของชุดฝึก \n1. test_photos.tgz- รูปภาพของชุดทดสอบ\n1. train_photo_to_biz_ids.csv - จาก photo_id ไป business_id\n1. test_photo_to_biz_ids.csv - จาก photo_id ไป business_id\n1. train.csv - ชุดข้อมูลฝึกหลัก รวมถึง business_id และ corresponding labels\n1. sample_submission.csv - ตัวอย่าง submission และ test dataset นี่คือรูปแบบที่ถูกต้องสำหรับการทำนาย\nควรประกอบด้วย business_id และ corresponding predicted labels","metadata":{}},{"cell_type":"code","source":"# บันทึกเวลาเริ่มการ run code\nstart_time = time.time()\n\n# แตกไฟล์ที่ถูกบีบอัดด้วย gzip และมีนามสกุล .tar.gz. โดยใช้ !tar -xzf จากนั้นสร้างไดเรกทอรีใหม่ที่มีชื่อเดียวกับไฟล์ .tar.gz และจะนำเนื้อหาทั้งหมดของไฟล์ไปวางไว้ในไดเรกทอรีนั้น\n!tar -xzf /kaggle/input/yelp-restaurant-photo-classification/sample_submission.csv.tgz\n!tar -xzf /kaggle/input/yelp-restaurant-photo-classification/test_photo_to_biz.csv.tgz\n!tar -xzf /kaggle/input/yelp-restaurant-photo-classification/test_photos.tgz\n!tar -xzf /kaggle/input/yelp-restaurant-photo-classification/train.csv.tgz\n!tar -xzf /kaggle/input/yelp-restaurant-photo-classification/train_photo_to_biz_ids.csv.tgz\n!tar -xzf /kaggle/input/yelp-restaurant-photo-classification/train_photos.tgz\n\n# บันทึกเวลาสิ้นสุดการ run code\nend_time = time.time()\n\n# แสดงระยะเวลาที่ใช้ในหน่วยวินาทีและนาที\nprint(\"Run time: {:.2f} seconds\".format(end_time - start_time))\nprint(\"Run time: {:.2f} minutes\".format((end_time - start_time)/60))","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:44:24.424163Z","iopub.execute_input":"2023-11-27T13:44:24.424793Z","iopub.status.idle":"2023-11-27T13:49:08.158862Z","shell.execute_reply.started":"2023-11-27T13:44:24.424753Z","shell.execute_reply":"2023-11-27T13:49:08.157582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# อ่านข้อมูลจากไฟล์ train_photo_to_biz_ids.csv, test_photo_to_biz.csv, train.csv, sample_submission.csv \n# จากนั้นเก็บไว้ในตัวแปร train_photos, test_photos, train_attr, sub ตามลำดับ\ntrain_photos = pd.read_csv(\"/kaggle/working/train_photo_to_biz_ids.csv\")\ntest_photos = pd.read_csv(\"/kaggle/working/test_photo_to_biz.csv\")\ntrain_attr = pd.read_csv(\"/kaggle/working/train.csv\") \nsub = pd.read_csv(\"/kaggle/working/sample_submission.csv\")","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:38.298028Z","iopub.execute_input":"2023-11-27T13:50:38.298472Z","iopub.status.idle":"2023-11-27T13:50:38.782201Z","shell.execute_reply.started":"2023-11-27T13:50:38.298439Z","shell.execute_reply":"2023-11-27T13:50:38.781203Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:140%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\" id=\"exandprep\"> Data Exploration & Preparation DataFrame </p>\n\n<div class=\"alert alert-success\">  \n<b>📍</b> Data Exploration Section \n</div>","metadata":{}},{"cell_type":"code","source":"display(train_attr.head(8))                                                 # แสดงแถวแรก 8 แถวของ DataFrame 'train_attr'\nprint('Shape of train data:', train_attr.shape)                             # แสดง DataFrame ทั้งหมด\nprint('Number of unique businesses:', train_attr.shape[0])                  # แสดงจำนวน Business ที่ไม่ซ้ำกัน","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:39.121881Z","iopub.execute_input":"2023-11-27T13:50:39.122750Z","iopub.status.idle":"2023-11-27T13:50:39.146414Z","shell.execute_reply.started":"2023-11-27T13:50:39.122715Z","shell.execute_reply":"2023-11-27T13:50:39.145351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(train_photos.head(8))                                              # แสดงแถวแรก 8 แถวของ DataFrame train_attr\nprint('Shape of train_photo_to_id:', train_photos.shape)                   # แสดง DataFrame ทั้งหมด\nprint('Number of images in training set:', train_photos.shape[0])          # แสดงจำนวน train รูปภาพใน model","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:39.623911Z","iopub.execute_input":"2023-11-27T13:50:39.624912Z","iopub.status.idle":"2023-11-27T13:50:39.637073Z","shell.execute_reply.started":"2023-11-27T13:50:39.624871Z","shell.execute_reply":"2023-11-27T13:50:39.635980Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(test_photos.head(8))                                               # แสดงแถวแรก 8 แถวของ DataFrame train_attr\nprint('Shape of test_photo_to_id:', test_photos.shape)                     # แสดง DataFrame ทั้งหมด\nprint('Number of images in test set:', test_photos.shape[0])               # แสดงจำนวน test","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:39.788329Z","iopub.execute_input":"2023-11-27T13:50:39.789026Z","iopub.status.idle":"2023-11-27T13:50:39.801081Z","shell.execute_reply.started":"2023-11-27T13:50:39.788989Z","shell.execute_reply":"2023-11-27T13:50:39.800021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> นับความถี่ของแต่ละ label และแสดงผล\n</div>","metadata":{}},{"cell_type":"code","source":"all_labels = ' '.join(list(train_attr['labels'].fillna('nan').values)).split()                # ทำการรวมและแยก labels\nfrom collections import Counter\nlabel_counts = Counter(all_labels)                                                            # นับความถี่ของแต่ละ label\nfor key in label_counts:                                                                      # วนลูปแล้วรับค่าจากทุก key ใน label_counts\n    print('Label {0} appears {1} times in training dataset'.format(key, label_counts[key]))   # แสดงผลลัพธ์จำนวน label ที่ปรากฎ ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:40.248392Z","iopub.execute_input":"2023-11-27T13:50:40.248739Z","iopub.status.idle":"2023-11-27T13:50:40.258904Z","shell.execute_reply.started":"2023-11-27T13:50:40.248711Z","shell.execute_reply":"2023-11-27T13:50:40.257731Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Number of duplicate photo IDs:', len(train_photos[train_photos['photo_id'].duplicated()]))    # แสดงค่าซ้ำ'photo_id' ของ DataFrame train_photos\nprint('Number of duplicate business IDS:', len(train_attr[train_attr['business_id'].duplicated()]))  # แสดงค่าซ้ำ 'business_id' ของ DataFrame train_attr","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:40.435901Z","iopub.execute_input":"2023-11-27T13:50:40.436782Z","iopub.status.idle":"2023-11-27T13:50:40.456971Z","shell.execute_reply.started":"2023-11-27T13:50:40.436751Z","shell.execute_reply":"2023-11-27T13:50:40.456106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Business id to labels dataframe\nprint('Total number of missing labels:', train_attr['labels'].isnull().sum())   # แสดงจำนวน labels ที่หายไป\ndisplay(train_attr[train_attr['labels'].isnull()])                              # แสดงแถวใน DataFrame ที่ labels หายไป","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:40.670844Z","iopub.execute_input":"2023-11-27T13:50:40.671180Z","iopub.status.idle":"2023-11-27T13:50:40.683625Z","shell.execute_reply.started":"2023-11-27T13:50:40.671153Z","shell.execute_reply":"2023-11-27T13:50:40.682568Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-success\">  \n<b>📍</b> Preparation DataFrame Section \n</div>","metadata":{}},{"cell_type":"code","source":"train_attr = train_attr.dropna(axis=0)                                          # Drop Missing Value (4 rows)\ndisplay(train_attr)\nprint('Shape of train data:', train_attr.shape)                                 # แสดงจำนวนแถวและจำนวนคอลัมน์ของ train_att\nprint('Number of unique businesses:', train_attr.shape[0])                      # ใช้ .shape เพื่อแสดงจำนวนธุรกิจที่ไม่ซ้ำกัน","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:41.122789Z","iopub.execute_input":"2023-11-27T13:50:41.123682Z","iopub.status.idle":"2023-11-27T13:50:41.139963Z","shell.execute_reply.started":"2023-11-27T13:50:41.123640Z","shell.execute_reply":"2023-11-27T13:50:41.138990Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_label = pd.merge(train_photos,train_attr,how = \"inner\")                  # Merge ข้อมูลของ 'train_photos' และ 'train_attr' แบบ inner join แล้วเก็บข้อมูลในชื่อ 'train_label'\ntrain_label = train_label.sample(n=30000)                                      # สุ่มเลือกข้อมูลจาก train_label มา 30,000 แถว\ndisplay(train_label)\nprint('Shape of train label:', train_label.shape)                              # แสดงจำนวนแถวและจำนวนคอลัมน์ของ train_label\ntrain_label.isnull().sum()                                                     # นับจำนวนค่าที่หายไป(NaN) ในแต่ละคอลัมน์","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:41.342217Z","iopub.execute_input":"2023-11-27T13:50:41.342536Z","iopub.status.idle":"2023-11-27T13:50:41.406487Z","shell.execute_reply.started":"2023-11-27T13:50:41.342509Z","shell.execute_reply":"2023-11-27T13:50:41.405478Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_csv = pd.merge(test_photos, sub, how = \"inner\")                         # Merge ข้อมูลของ 'test_photos' และ 'sub' แบบ outer join แล้วเก็บข้อมูลในชื่อ test_csv\ntest_csv = test_csv.groupby(['business_id'], as_index=False).first()         # Group ข้อมูลโดยใช้คอลัมน์ 'business_id' เป็นคีย์ โดยที่ไม่เป็น index และเลือกแถวแรกในแต่ละกลุ่ม\ndisplay(test_csv)\nprint('Shape of test csv:', test_csv.shape)                                  # แสดงจำนวนแถวและจำนวนคอลัมน์ของ test_csv","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:41.674666Z","iopub.execute_input":"2023-11-27T13:50:41.675631Z","iopub.status.idle":"2023-11-27T13:50:42.187462Z","shell.execute_reply.started":"2023-11-27T13:50:41.675589Z","shell.execute_reply":"2023-11-27T13:50:42.186446Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-success\">  \n<b>📍</b> Preparation DataFrame to CSV \n</div>","metadata":{}},{"cell_type":"code","source":"test_csv.to_csv('test_csv.csv', index=False)                                  # บันทึก DataFrame 'test_csv' เป็นไฟล์ CSV โดยใช้ชื่อไฟล์ 'test_csv.csv' และกำหนด index=False เพื่อไม่เก็บคอลัมน์ index ลงในไฟล์ CSV\ntrain_label.to_csv('train_label.csv', index=False)                            # บันทึก DataFrame 'train_label' เป็นไฟล์ CSV โดยใช้ชื่อไฟล์ 'train_label.csv' และกำหนด index=False เพื่อไม่เก็บคอลัมน์ index ลงในไฟล์ CSV","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:42.189381Z","iopub.execute_input":"2023-11-27T13:50:42.189745Z","iopub.status.idle":"2023-11-27T13:50:42.300318Z","shell.execute_reply.started":"2023-11-27T13:50:42.189712Z","shell.execute_reply":"2023-11-27T13:50:42.299471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_DIR = '/kaggle/working/'                                                 # กำหนดค่าตัวแปรสำหรับที่อยู่ของไดเรกทอรี\nTRAIN_DIR = DATA_DIR + 'train_photos'                                         # กำหนดค่าตัวแปรสำหรับที่อยู่ของไดเรกทอรี่ /kaggle/working/ ให้แสดงเป็น 'DATA_DIR' \nTEST_DIR = DATA_DIR + 'test_photos'                                           # กำหนดค่าตัวแปรของการรวม DATA_DIR และ test_photos ให้แสดงเป็น 'TEST_DIR'\nTRAIN_CSV = DATA_DIR + 'train_label.csv'                                      # กำหนดค่าตัวแปรของการรวม DATA_DIR และ train_label.csv ให้แสดงเป็น 'TRAIN_CSV'\nTEST_CSV = DATA_DIR + 'test_csv.csv'                                          # กำหนดค่าตัวแปรของการรวม DATA_DIR และ test_csv.csv ให้แสดงเป็น 'TEST_CSV'\nTEST_TO_BUS = DATA_DIR + 'test_photo_to_biz.csv'                              # กำหนดค่าตัวแปรของการรวม DATA_DIR และ test_photo_to_biz.csv ให้แสดงเป็น 'TEST_TO_BUS'","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:42.371478Z","iopub.execute_input":"2023-11-27T13:50:42.371753Z","iopub.status.idle":"2023-11-27T13:50:42.376967Z","shell.execute_reply.started":"2023-11-27T13:50:42.371728Z","shell.execute_reply":"2023-11-27T13:50:42.376091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(TRAIN_CSV)         # อ่านข้อมูลจาก TRAIN_CSV และจัดเก็บใน 'train_df'\ntrain_df                                  # แสดงข้อมูลของ 'train_df'","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:42.615467Z","iopub.execute_input":"2023-11-27T13:50:42.615810Z","iopub.status.idle":"2023-11-27T13:50:42.641921Z","shell.execute_reply.started":"2023-11-27T13:50:42.615781Z","shell.execute_reply":"2023-11-27T13:50:42.641079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:140%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\" id=\"dataprep\"> Data Preparation </p>","metadata":{}},{"cell_type":"markdown","source":"<div style=\"pxborder-radius:10px; border:#DEB887 solid; padding: 15px; background-color: #FFFAF0; font-size:80%; text-align:left\">\n\n<h3 align=\"left\"><font color='#DEB887'>🎞 การนำภาพที่เป็นส่วนนึงของธุระกิจต่างๆ มาทำนายว่าภาพนั้นหมายถึงธุรกิจอะไร โดยใ้ช้ 9 คุณลักษณะในการตัดสิน </font></h3>\n\n0.ดีสำหรับมื้อกลางวัน\n    \n1.ดีสำหรับมื้อค้ำ\n    \n2.Label รับการจองที่นั่ง\n    \n3.ที่นั่งกลางแจ้ง\n    \n4.ร้านอาหารมีราคาแพง\n    \n5.มีแอลกอฮอล์\n    \n6.มีบริการโต๊ะ\n    \n7.บรรยากาศที่หรูหรา\n    \n8.ดีสำหรับเด็ก\n\nคำอธิบายของไฟล์\n* train_photos.tgz - รูปภาพของชุดฝึก\n* test_photos.tgz- รูปภาพของชุดทดสอบ\n* train_photo_to_biz_ids.csv - จาก photo_id ไป business_id\n* test_photo_to_biz_ids.csv - จาก photo_id ไป business_id\n* train.csv - ชุดข้อมูลฝึกหลัก รวมถึง business_id และ corresponding labels\n* sample_submission.csv - ตัวอย่าง submission และ test dataset นี่คือรูปแบบที่ถูกต้องสำหรับการทำนาย ควรประกอบด้วย business_id และ corresponding predicted labels","metadata":{}},{"cell_type":"markdown","source":"<div style=\"pxborder-radius:10px; border:#DEB887 solid; padding: 15px; background-color: #FFFAF0; font-size:85%; text-align:left\">\n\n<h3 align=\"left\"><font color='#DEB887'>💡 List of Labels</font></h3>\n\n0: good_for_lunch\n\n1: good_for_dinner\n\n2: takes_reservations\n\n3: outdoor_seating\n\n4: restaurant_is_expensive\n\n5: has_alcohol\n\n6: has_table_service\n\n7: ambience_is_classy\n\n8: good_for_kids","metadata":{}},{"cell_type":"code","source":"#กำหนด Key:Value ของ Lable แต่ละประเภท\nlabels = {0: 'good_for_lunch', \n          1: 'good_for_dinner', \n          2: 'takes_reservations',  \n          3: 'outdoor_seating',\n          4: 'restaurant_is_expensive', \n          5: 'has_alcohol', \n          6: 'has_table_service', \n          7: 'ambience_is_classy',\n          8: 'good_for_kids'}","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:43.582523Z","iopub.execute_input":"2023-11-27T13:50:43.582967Z","iopub.status.idle":"2023-11-27T13:50:43.588619Z","shell.execute_reply.started":"2023-11-27T13:50:43.582931Z","shell.execute_reply":"2023-11-27T13:50:43.587637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from fastai import *\nfrom fastai.vision import *\nfrom fastai.vision.all import *\npath_images = '/kaggle/working/train_photos'                          # กำหนด path เก็บรูปภาพที่จะใช้\nfilenames = get_image_files(path_images)                              # กำหนดตัวแปรเพื่อใช้ function ดึงไฟล์รูปภาพทั้งหมดจาก File Paths\nfilenames[:10]                                                        # การแสดงผล10 รูปแรกจากในไฟล์","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:43.797330Z","iopub.execute_input":"2023-11-27T13:50:43.797766Z","iopub.status.idle":"2023-11-27T13:50:46.535341Z","shell.execute_reply.started":"2023-11-27T13:50:43.797732Z","shell.execute_reply":"2023-11-27T13:50:46.534307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> ฟังก์ชันสำหรับการแปลง label และ Tensor \n</div>","metadata":{}},{"cell_type":"code","source":"# ฟังก์ชันสำหรับการแปลง label ในรูปแบบของข้อความไปเป็น tensor และฟังก์ชันสำหรับการแปลง tensor กลับเป็น label \ndef encode_label(label):\n    target = torch.zeros(10)                                           # ฟังก์ชันรับข้อความ label และสร้าง tensor ขนาด 10 \n    for l in str(label).split(' '):                                    # วนลูปผ่านทุกตัวอักษรที่ได้จากการแปลง label เป็น string และใช้ช่องว่าง (' ') เป็นตัวแยก (delimiter) ระหว่างคำที่ต่อเนื่องกัน\n        target[int(l)] = 1.                                             # เปลี่ยนตัวอักษรที่ได้ในแต่ละรอบให้เป็นตัวเลขและนำตัวเลขนั้นไปใช้ในการกำหนดค่า index ของ target\n    return target\n\n# ฟังก์ชันแปลง Tensor ที่ได้จากการทำนายของโมเดลเป็น label หรือลำดับของ labels ได้อย่างยืดหยุ่นขึ้น, โดยใช้ threshold เป็นเงื่อนไข\ndef decode_target(target, text_labels=False, threshold=0.5):\n    result = []\n    for i, x in enumerate(target):                                    # กำหนดloopโดยมีเงื่อนไขว่าถ้า x มากกว่าหรือเท่ากับ threshold, จะเพิ่ม label ลงในlist ของ result\n        if (x >= threshold):\n            if text_labels:\n                result.append(labels[i] + \"(\" + str(i) + \")\")         # เพิ่ม label ที่อยู่ในตำแหน่ง i ของ labels พร้อมกับลำดับ i ลงใน list result\n            else:                                                     # ถ้า text_labels เป็น False ให้ทำต่อ\n                result.append(str(i))\n    return ' '.join(result)                                           # รวม list result เป็น string โดยใช้ช่องว่าง (' ') เป็นตัวคั่นระหว่างรายการ\n\ndisplay(encode_label('0 3 8'))\ndisplay(decode_target(torch.tensor([1., 0., 0., 1., 0., 0., 0., 0., 1., 0.]), text_labels=True))","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:46.537574Z","iopub.execute_input":"2023-11-27T13:50:46.538017Z","iopub.status.idle":"2023-11-27T13:50:46.652508Z","shell.execute_reply.started":"2023-11-27T13:50:46.537982Z","shell.execute_reply":"2023-11-27T13:50:46.651627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> สร้าง class เพื่อใช้โหลดข้อมูลรูปภาพจาก dataset ที่มีข้อมูลที่อยู่ในรูปแบบของไฟล์ CSV และรูปภาพที่ตั้งอยู่ในไดเรกทอรีที่กำหนด \n</div>","metadata":{}},{"cell_type":"code","source":"class YelpResPhoto(Dataset):\n    def __init__(self, csv_file, root_dir, transform=None):\n        self.df = pd.read_csv(csv_file)                                    # อ่านไฟล์ CSV \n        self.transform = transform                                         # ปรับปรุงภาพเพื่อให้เหมาะกับการฝึกโมเดล\n        self.root_dir = root_dir                                           # ไดเรกทอรีหลักที่เก็บรูปภาพของ dataset \n        \n    def __len__(self):                                                     # ระบุจำนวนรูปภาพ และ บอกขนาดของ dataset               \n        return len(self.df)                                                # คืนค่าจำนวนแถวของ DataFrame ที่เก็บข้อมูลจำนวนรูปภาพใน dataset  \n    \n    def __getitem__(self, idx):                                            # ฟังก์ชั่นโหลดข้อมูลสำหรับแต่ละตัวอย่างใน dataset\n        row = self.df.loc[idx]                                             # ดึงข้อมูลของตัวอย่างใน idx ใน DataFrame\n        img_id, img_label = row['photo_id'], row['labels']                 # ดึงข้อมูล photo_id และ labels จาก DataFrame    \n        img_fname = self.root_dir + \"/\" + str(img_id) + \".jpg\"             # สร้าง path โดยรวม root_dir, img_id, และกำหนดนามสกุลไฟล์เป็น .jpg\n        img = Image.open(img_fname)                                        # โหลดรูปภาพจากไฟล์ img_fname\n        \n        \n        if self.transform:\n            img = self.transform(img)                                      # ระบุการแปลงเมื่อสร้างอินสแตนซ์ของรูปภาพทุกภาพใน dataset \n        return img,encode_label(img_label)                                 # คืนค่า tuple ที่ประกอบไปด้วยรูปภาพที่ผ่านการแปลงและ label ที่ถูกแปลงจากข้อความเป็น tensor ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:46.654159Z","iopub.execute_input":"2023-11-27T13:50:46.654854Z","iopub.status.idle":"2023-11-27T13:50:46.663707Z","shell.execute_reply.started":"2023-11-27T13:50:46.654818Z","shell.execute_reply":"2023-11-27T13:50:46.662848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transform = transforms.Compose([transforms.ToTensor()])                      # เปลี่ยนแปลงภาพทั้งหมดและเก็บในรูปแบบของ list แล้วใช้ transforms.Compose เพื่อรวมทั้งหมดและนำไปใช้กับชุดข้อมูล\ndataset = YelpResPhoto(TRAIN_CSV, TRAIN_DIR, transform=transform)            # สร้าง DataLoader เพื่อให้ง่ายต่อการโหลดข้อมูลในแต่ละรอบของการฝึกโมเดล","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:46.665650Z","iopub.execute_input":"2023-11-27T13:50:46.665903Z","iopub.status.idle":"2023-11-27T13:50:46.691162Z","shell.execute_reply.started":"2023-11-27T13:50:46.665879Z","shell.execute_reply":"2023-11-27T13:50:46.690336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n<div class=\"alert alert-block alert-info\">  \n<b>📎</b> ฟังก์ชันแสดงตัวอย่างของภาพพร้อมกับlabels ที่ถูกถอดรหัสจาก tensor \n</div>","metadata":{}},{"cell_type":"code","source":" def show_sample(img, target, invert=False):                       \n    plt.imshow(img.permute(1, 2, 0))                                     # ใช้ Matplotlib เพื่อแสดงภาพบนกราฟ\n    print('Labels:', decode_target(target, text_labels=True))\n\nshow_sample(*dataset[0])                                                 # แสดงตัวอย่างของรูปภาพแรก และ lables ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:46.692135Z","iopub.execute_input":"2023-11-27T13:50:46.692385Z","iopub.status.idle":"2023-11-27T13:50:47.091776Z","shell.execute_reply.started":"2023-11-27T13:50:46.692363Z","shell.execute_reply":"2023-11-27T13:50:47.090842Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-success\">  \n<b>📍</b> Train and Validation Data Preparation \n</div>","metadata":{}},{"cell_type":"code","source":"val_pct = 0.1                             # แบ่งชุดข้อมูลเป็นชุด train กับ validation แบบสุ่มจาก dataset แล้วเก็บค่าใน train_ds (90%) และ val_ds (10%) จากนั้นแสดงจำนวนออกมา\nval_size = int(val_pct * len(dataset))      \ntrain_size = len(dataset) - val_size      ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:47.093510Z","iopub.execute_input":"2023-11-27T13:50:47.093796Z","iopub.status.idle":"2023-11-27T13:50:47.098453Z","shell.execute_reply.started":"2023-11-27T13:50:47.093771Z","shell.execute_reply":"2023-11-27T13:50:47.097593Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ds, val_ds = random_split(dataset, [train_size, val_size])    # แบ่งชุดข้อมูลเป็นชุด test และชุด train แบบสุ่มโดยรับเข้ามาทั้ง dataset \nlen(train_ds), len(val_ds)                                          # คำนวณขนาดของ train_ds และ val_ds เพื่อเช็กว่าแบ่งข้อมูลตามที่แบบที่ต้องการ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:47.196990Z","iopub.execute_input":"2023-11-27T13:50:47.197321Z","iopub.status.idle":"2023-11-27T13:50:47.210391Z","shell.execute_reply.started":"2023-11-27T13:50:47.197293Z","shell.execute_reply":"2023-11-27T13:50:47.209569Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dl = DataLoader(train_ds, item_tfms=Resize(460),                                                          # ปรับขนาดรูปภาพทั้งหมดของ train_ds ให้มีขนาดเดียวกัน เพื่อให้ง่ายต่อการนำเข้าข้อมูลเข้าสู่โมเดล\n                      batch_tfms=aug_transforms(size=224), shuffle=True, num_workers=2, pin_memory=True)\nval_dl = DataLoader(val_ds, item_tfms=Resize(460),                                                              # ปรับขนาดรูปภาพทั้งหมดของ val_ds ให้มีขนาดเดียวกัน เพื่อให้ง่ายต่อการนำเข้าข้อมูลเข้าสู่โมเดล\n                    batch_tfms=aug_transforms(size=224), num_workers=2, pin_memory=True)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:47.593653Z","iopub.execute_input":"2023-11-27T13:50:47.594114Z","iopub.status.idle":"2023-11-27T13:50:47.614536Z","shell.execute_reply.started":"2023-11-27T13:50:47.594075Z","shell.execute_reply":"2023-11-27T13:50:47.613554Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎 </b> ฟังก์ชัน show_batch สำหรับแสดงตัวอย่างรูปภาพ และ lables ที่รับค่ามาจาก DataLoader(d1)\n</div>","metadata":{}},{"cell_type":"code","source":"def show_batch(dl): \n    for images, labels in dl:\n        fig, ax = plt.subplots(figsize=(9, 4))                  # กำหนดขนาดของ figure ให้มีความกว้าง 9 นิ้วและความสูง 4 นิ้ว \n        ax.set_xticks([]); ax.set_yticks([])                    # set แกน x และแกน y ให้ไม่แสดงเลข\n        data = images                                           # เอารูปไปเก็บในตัวแปรชื่อ data\n        ax.imshow(make_grid(data, nrow=16).permute(1, 2, 0))    # ทำการรวมรูปภาพใน data ให้อยู่ในรูปแบบกริดโดยในแต่ละแถวจะแสดงรุปภาพ 16 รูป, ใช้ permute(1, 2, 0) เพื่อสลับแกนภาพเพื่อให้เข้ากับรูปแบบที่ Matplotlib ใช้แสดงรูป\n        break                                                   # เพื่อหยุด loop หลังแสดงรูปภาพ 1 batch\n\nshow_batch(train_dl)                                            # แสดง image และ lables ที่ถูกโหลดจาก train_d1 แบบสุ่ม","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:48.355763Z","iopub.execute_input":"2023-11-27T13:50:48.356508Z","iopub.status.idle":"2023-11-27T13:50:51.980061Z","shell.execute_reply.started":"2023-11-27T13:50:48.356461Z","shell.execute_reply":"2023-11-27T13:50:51.979077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:140%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\" id=\"model\"> About Model </p>\n\n","metadata":{}},{"cell_type":"markdown","source":"<div style=\"pxborder-radius:10px; border:#DEB887 solid; padding: 15px; background-color: #FFFAF0; font-size:85%; text-align:left\">\n\n<h3 align=\"left\"><font color='#DEB887'>💡 ResNet-34</font></h3>\nResNet-34 เป็นโมเดลเรียนรู้เชิงลึก (deep learning model) ที่ถูกพัฒนาขึ้นโดย Microsoft Research สำหรับการจำแนกภาพ (image classification).\"ResNet\" มาจากคำว่า \"Residual Network,\" ซึ่งแสดงถึงวิธีการที่ใช้ในการกำหนดโครงสร้างของโมเดล. โดย ResNet-34 นี้มีโครงสร้างทั้งหมด 34 layers, ประกอบด้วย convolutional layers, batch normalization, ReLU activation, residual blocks, max pooling, global average pooling, และ fully connected layers.\n\nขั้นตอนการทำงานของแต่ละชั้นของ ResNet-34 :\n\nInput Layer:\nรับภาพเป็นข้อมูลนำเข้า.\nConvolutional Layer 1 (7x7 Convolution):\nการสกัดคุณลักษณะด้วยการใช้ส่วนตัวสกัดคุณลักษณะขนาด 7x7.\nBatch Normalization 1:\n    \nปรับปรุงค่าคุณลักษณะด้วย batch normalization.\nReLU Activation 1:\n    \nใช้ Rectified Linear Unit (ReLU) เป็นฟังก์ชัน activation หลังการสกัดคุณลักษณะ.\n    \nMax Pooling 1:\n    \nการลดขนาดของรูปภาพด้วย max pooling ขนาด 3x3.\n    \nResidual Block 1 (3 Layers):\n    \nชุดของ 3 ชั้นที่ประกอบด้วย:\n    \nConvolutional Layer 2 (3x3 Convolution):\n    \nการสกัดคุณลักษณะด้วยส่วนตัวสกัดคุณลักษณะขนาด 3x3.\n    \nBatch Normalization 2:\n    \nปรับปรุงค่าคุณลักษณะด้วย batch normalization.\n    \nReLU Activation 2:\n    \nใช้ ReLU เป็นฟังก์ชัน activation.\n    \nConvolutional Layer 3 (3x3 Convolution):\n    \nการสกัดคุณลักษณะด้วยส่วนตัวสกัดคุณลักษณะขนาด 3x3.\n    \nBatch Normalization 3:\n    \nปรับปรุงค่าคุณลักษณะด้วย batch normalization.\n    \nSkip Connection (Identity Mapping):\n    \n    \nShortcut connection ที่ส่งข้อมูลตรงผ่านโดยไม่มีการทำซ้ำทางกลับ (skip connection).\nReLU Activation 3:\n    \nใช้ ReLU เป็นฟังก์ชัน activation ท้ายสุดของ residual block.\n    \nResidual Block 2 (3 Layers):\n    \nตามหลักการเดียวกันกับ Residual Block 1.\n    \nResidual Block 3 (3 Layers):\n    \nตามหลักการเดียวกันกับ Residual Block 1.\n    \nResidual Block 4 (3 Layers):\n    \nตามหลักการเดียวกันกับ Residual Block 1.\n    \nGlobal Average Pooling:\n    \nทำให้ข้อมูลเข้าสู่รูปแบบที่สามารถให้ข้อมูลขนาดแนวนอนเป็น 1 (global average pooling).\n    \nFully Connected Layer (Output Layer):\n    \nนำข้อมูลจาก global average pooling มาใช้ในการทำนายหมวดหมู่ของภาพ.\n    \nSoftmax Activation:\n    \nใช้ softmax activation เพื่อแปลงคะแนนของแต่ละหมวดหมู่ให้กลายเป็นความน่าจะเป็น.\n    \nResNet-34 นี้ออกแบบมาเพื่อลดปัญหาการหายไปหรือหายมิได้ที่พบในโมเดลที่มีความลึกมาก โดยใช้ residual blocks และ shortcut connections เพื่อช่วยในการส่งผ่านข้อมูลโดยตรงโดยไม่มีการทำซ้ำทางกลับ (skip connection).\n   \n ชั้น batch normalization ช่วยในการปรับปรุงความเร็วในการฝึกฝนและช่วยลดการกระทบจาก gradient exploding หรือ vanishing. \n    \n ชั้น global average pooling ช่วยในการลดขนาดของข้อมูลก่อนที่จะนำเข้า fully connected layer สุดท้ายสำหรับการทำนาย.","metadata":{}},{"cell_type":"markdown","source":"## RestNet34 Model","metadata":{}},{"cell_type":"markdown","source":"<div class=\"alert alert-success\">  \n<b>📍</b> train model\n</div>\n<div class=\"alert alert-block alert-info\">  \n<b>📎 </b> สร้าง class 'MultilabelImageClassificationBase' ที่เป็น subclass ของ 'nn.Module' (มี method ที่ใช้ในการทำนายและประเมินผลของโมเดลสำหรับงาน Multilabel Image Classification)\n</div>","metadata":{}},{"cell_type":"code","source":"# สร้าง class 'MultilabelImageClassificationBase' ที่เป็น subclass ของ 'nn.Module' (มี method ที่ใช้ในการทำนายและประเมินผลของโมเดลสำหรับงาน Multilabel Image Classification)\nclass MultilabelImageClassificationBase(nn.Module):\n    def training_step(self, batch):\n        images, targets = batch                                                                       # รับ batch ข้อมูล(images, targets) จาก DataLoader \n        \n        images = images.unsqueeze(0)                                                                  # เพิ่มมิติในแกนที่ 0 ของ images และ targets เพื่อให้มี batch dimension\n        targets = targets.unsqueeze(0)\n        \n        out = self(images)                                                                            # ทำนายผลลัพธ์จากโมเดล self โดยนำ images ผ่านฟังก์ชัน forward ของโมเดล                                 \n        loss = F.binary_cross_entropy(out, targets)                                                   # คำนวณ binary cross entropy loss ระหว่างผลลัพธ์ที่ได้จากผลลัพธ์ที่ได้(out) และ targets       \n        return loss                                                                                   # คืนค่า loss ที่ได้จากการคำนวณอbinary cross entropy loss นั้น ๆ เพื่อให้สามารถใช้ในการปรับปรุงและอัปเดตพารามิเตอร์ของโมเดลในการ training ต่อไป\n    \n    def validation_step(self, batch):\n        images, targets = batch                                                                       # แยกข้อมูลจาก batch เป็น images และ targets โดยใช้ tuple unpacking \n        images = images.unsqueeze(0)                                                                  # เพิ่มมิติในแกนที่ 0 ของ images และ targets เพื่อให้มี batch dimension\n        targets = targets.unsqueeze(0)\n        out = self(images)                                                                            # ทำนายผลลัพธ์จากโมเดล\n        loss = F.binary_cross_entropy(out, targets)                                                   # คำนวณค่า loss เพื่อใช้ปรับปรุงพารามิเตอร์ของ model\n        score = F_score(out, targets)                                                                 # คำนวณ F-score ระหว่างผลลัพธ์ที่ได้(out) กับ targets\n        return {'val_loss': loss.detach(), 'val_score': score.detach() }                              # คืนค่า val_loss และ val_score เอาไปใช้ประเมินความสำเร็จของโมเดล\n        \n    def validation_epoch_end(self, outputs):\n        batch_losses = [x['val_loss'] for x in outputs]                                               # สร้าง list ของค่า 'val_loss' จากทุกรอบใน 'outputs'\n        epoch_loss = torch.stack(batch_losses).mean()                                                 # รวมค่า 'val_loss' ทั้งหมดใน batch_losses โดยใช้ torch.stack เพื่อแปลง list เป็น tensor แล้วคำนวณค่าเฉลี่ย\n        batch_scores = [x['val_score'] for x in outputs]                                              # สร้าง list ของค่า 'val_score' จากทุกรอบใน 'outputs'\n        epoch_score = torch.stack(batch_scores).mean()                                                # รวมค่า 'val_score' ทั้งหมดใน batch_scores โดยใช้ torch.stack เพื่อแปลง list เป็น tensor แล้วคำนวณค่าเฉลี่ย\n        return {'val_loss': epoch_loss.item(), 'val_score': epoch_score.item()}                       # คืนค่า dictionary ที่มีค่า 'val_loss' เป็นค่าเฉลี่ยของ 'val_loss' ในทั้ง epoch และ 'val_score' เป็นค่าเฉลี่ยของ 'val_score' ในทั้ง epoch. '.item()' ใช้เพื่อดึงค่า scalar จาก tensor\n    \n    def epoch_end(self, epoch, result):\n        print(\"Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}, val_score: {:.4f}\".format(           # พิมพ์ผลลัพธ์สรุปของการฝึกโมเดลในแต่ละ epoch\n            epoch, result['train_loss'], result['val_loss'], result['val_score']))","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:51.982199Z","iopub.execute_input":"2023-11-27T13:50:51.982497Z","iopub.status.idle":"2023-11-27T13:50:51.995048Z","shell.execute_reply.started":"2023-11-27T13:50:51.982471Z","shell.execute_reply":"2023-11-27T13:50:51.994098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> โมเดลที่ถูกสร้างขึ้นโดยเป็นคลาสหลักสำหรับการสร้างโมเดลสำหรับงานการจำแนกภาพที่มีหลาย label\n</div>","metadata":{}},{"cell_type":"code","source":"class YelpRes34CnnModel(MultilabelImageClassificationBase):\n    def __init__(self):\n        super().__init__()                                                  # เรียกใช้เมธอด __init__ ของคลาสแม่ (super class) เพื่อให้ได้ความประโยชน์จากความสามารถและการกำหนดค่าที่ถูกนำมาจากคลาสหลัก\n        # Use a pretrained model\n        self.network = models.resnet34(pretrained=True)                     # ใช้โมเดล ResNet-34 ที่ถูกทำการฝึกสอนไว้ล่วงหน้าจาก PyTorch torchvision models, การใช้โมเดลที่ถูกฝึกสอนไว้ล่วงหน้านี้ช่วยให้โมเดลมีความสามารถในการจำแนกภาพได้ดีมากขึ้น \n        # Replace last layer\n        num_ftrs = self.network.fc.in_features                              # หาจำนวน features ที่อยู่ในเลเยอร์สุดท้ายของโมเดลโดยใช้ in_features ซึ่งเป็นค่าที่ใช้ในการกำหนดจำนวน input features ในเลเยอร์แรกของโมเดลที่จะถูกเพิ่มใหม่\n        self.network.fc = nn.Linear(num_ftrs, 10)                           # ทำการแทนที่เลเยอร์ที่เป็น fully connected สุดท้ายของโมเดลด้วย Linear layer ที่มี output features เป็น 10, นี่คือส่วนที่ทำให้โมเดลมีความสามารถในการจำแนกภาพอย่างแม่นยำตามจำนวน classes ที่กำหนด\n    \n    def forward(self, xb):                                                  # ทำการรับ batch ของ input data (xb) และทำการส่งผ่านโมเดล\n        return torch.sigmoid(self.network(xb))                              # ผลลัพธ์ที่ได้จากโมเดลจะถูกผ่านฟังก์ชัน sigmoid เพื่อให้ได้ค่าที่อยู่ในช่วง 0-1 ซึ่งเป็นที่พบบ่อยในงานการจำแนกหลาย label","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:51.996573Z","iopub.execute_input":"2023-11-27T13:50:51.996911Z","iopub.status.idle":"2023-11-27T13:50:52.011328Z","shell.execute_reply.started":"2023-11-27T13:50:51.996880Z","shell.execute_reply":"2023-11-27T13:50:52.010450Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-success\">  \n<b>📍</b> คำนวณค่า F-score ในการประเมินประสิทธิภาพของโมเดล\n</div>","metadata":{}},{"cell_type":"code","source":"def F_score(output, label, threshold=0.5, beta=1):\n    prob = output > threshold                                                         # บอกว่าค่าความเป็นไปได้ที่โมเดลทำนายมีค่ามากกว่า threshold หรือไม่\n    label = label > threshold                                                         # บอกว่าค่าป้ายกำกับมีค่ามากกว่า threshold หรือไม่\n\n    TP = (prob & label).sum(1).float()                                                # นับจำนวน True Positive(TP)\n    TN = ((~prob) & (~label)).sum(1).float()                                          # นับจำนวน True Negative(TN)\n    FP = (prob & (~label)).sum(1).float()                                             # นับจำนวน False Positive(FP) \n    FN = ((~prob) & label).sum(1).float()                                             # นับจำนวน False Negative(FN)\n\n    precision = torch.mean(TP / (TP + FP + 1e-12))                                    # คำนวณค่า precision เพื่อบอกความแม่นยำของโมเดล\n    recall = torch.mean(TP / (TP + FN + 1e-12))                                       # คำนวณค่า recall เพื่อบอกค่าความถูกต้องของโมเดล\n    F2 = (1 + beta**2) * precision * recall / (beta**2 * precision + recall + 1e-12)  # คำนวณค่า F-score\n    return F2.mean(0)  ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:52.013335Z","iopub.execute_input":"2023-11-27T13:50:52.013658Z","iopub.status.idle":"2023-11-27T13:50:52.025605Z","shell.execute_reply.started":"2023-11-27T13:50:52.013629Z","shell.execute_reply":"2023-11-27T13:50:52.024841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-success\">  \n<b>📍</b> คำนวณค่า F-score ในการประเมินประสิทธิภาพของโมเดล\n</div>","metadata":{}},{"cell_type":"code","source":"def get_default_device():\n    \"\"\"Pick GPU if available, else CPU\"\"\"\n    if torch.cuda.is_available():                                           # ถ้ามี GPU ที่สามารถใช้งานได้ (CUDA available), ฟังก์ชันจะคืนอุปกรณ์ที่เป็น GPU \n        return torch.device('cuda')\n    else:                                                                   # ถ้าไม่มี GPU ที่สามารถใช้งานได้, ฟังก์ชันจะคืนอุปกรณ์ที่เป็น CPU\n        return torch.device('cpu')\n\n# ฟังก์ชันย้าย Tensor หรือรายการของ Tensor ไปยังอุปกรณ์ที่กำหนด\ndef to_device(data, device):\n    \"\"\"Move tensor(s) to chosen device\"\"\"\n    if isinstance(data, (list,tuple)):                                      # ทุกรายการในlistหรือ tuple จะถูกย้ายไปยังอุปกรณ์ที่กำหนด\n        return [to_device(x, device) for x in data]                         # data เป็น Tensor เดียว, ฟังก์ชันจะใช้เมธอด to ของ Tensor เพื่อย้าย Tensor นั้นไปยังอุปกรณ์ที่กำหนด\n    return data.to(device, non_blocking=True)\n\n# สร้างขึ้นเพื่อwrap คลาส DataLoader และทำการย้ายข้อมูลในแต่ละ batch ไปยังอุปกรณ์ที่กำหนด\nclass DeviceDataLoader():\n    \"\"\"Wrap a dataloader to move data to a device\"\"\"\n    def __init__(self, dl, device):                                         # กำหนดค่า initial state ของคลาส เพื่อรับ DataLoader (dl) และอุปกรณ์ที่ต้องการย้ายข้อมูลไป\n        self.dl = dl                                                        # เก็บ DataLoader (dl) ที่รับเข้ามาเพื่อให้สามารถเข้าถึง DataLoader ได้ในทุกๆที่ในคลาส\n        self.device = device                                                # เก็บอุปกรณ์ (device) ที่รับเข้ามา เพื่อให้สามารถใช้งานในทุกๆ ที่ในคลาส DeviceDataLoader\n        \n    def __iter__(self):                                                     # generator method ที่ใช้ในการวนลูปผ่าน DataLoader (dl) และ yield แต่ละ batch หลังจากที่ย้ายข้อมูลใน batch นั้นไปยังอุปกรณ์ที่กำหนด\n        \"\"\"Yield a batch of data after moving it to device\"\"\"\n        for b in self.dl:                                                   # วนลูปผ่าน DataLoader (self.dl) ซึ่งจะได้รับ batch ทีละตัว \n            yield to_device(b, self.device)                                 # ย้ายข้อมูลใน batch นั้นไปยังอุปกรณ์ที่กำหนด \n\n    def __len__(self):                                                      # คืนค่าจำนวน batch ทั้งหมดใน DataLoader\n        \"\"\"Number of batches\"\"\"\n        return len(self.dl)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:52.098693Z","iopub.execute_input":"2023-11-27T13:50:52.098958Z","iopub.status.idle":"2023-11-27T13:50:52.108390Z","shell.execute_reply.started":"2023-11-27T13:50:52.098935Z","shell.execute_reply":"2023-11-27T13:50:52.107433Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-success\">  \n<b>📍</b> คำนวณค่า F-score ในการประเมินประสิทธิภาพของโมเดล\n</div>","metadata":{}},{"cell_type":"code","source":"device = get_default_device()               \ndevice                                                                     # ค่าที่จะได้จะช่วยบอกว่าควรใช้ GPU หรือ CPU ในการทำงาน","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:53.010495Z","iopub.execute_input":"2023-11-27T13:50:53.011252Z","iopub.status.idle":"2023-11-27T13:50:53.017667Z","shell.execute_reply.started":"2023-11-27T13:50:53.011216Z","shell.execute_reply":"2023-11-27T13:50:53.016496Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"modelres34 = YelpRes34CnnModel()                                                  # สร้างอินสแตนซ์ของคลาสและสร้างตัวแปร model เพื่อเก็บอินสแตนส์นี้                \nmodelres34                                                                       # แสดงข้อมูลของโมเดล","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:50:53.267763Z","iopub.execute_input":"2023-11-27T13:50:53.268088Z","iopub.status.idle":"2023-11-27T13:50:54.342987Z","shell.execute_reply.started":"2023-11-27T13:50:53.268056Z","shell.execute_reply":"2023-11-27T13:50:54.342104Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dl = DeviceDataLoader(train_dl, device)                            # โอน DataLoader ของชุดข้อมูลฝึกปยังอุปกรณ์ที่กำหนด เพื่อให้สามารถสทำงานในอุปกรณ์นั้นได้\nval_dl = DeviceDataLoader(val_dl, device)                                # ทำการโอน DataLoader ของชุดข้อมูลการตรวจสอบไปยังอุปกรณ์\nto_device(modelres34, device);                                                # ทำการโอนโมเดลปยังอุปกรณ์, to_device เป็นฟังก์ชันที่ถูกสร้างขึ้นเพื่อให้การโอนโมเดลไปยัง GPU หรือ CPU ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:51:05.184534Z","iopub.execute_input":"2023-11-27T13:51:05.184936Z","iopub.status.idle":"2023-11-27T13:51:05.218279Z","shell.execute_reply.started":"2023-11-27T13:51:05.184900Z","shell.execute_reply":"2023-11-27T13:51:05.217407Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> ทดสอบการผ่าน batch ข้อมูลผ่านโมเดล และแสดงรายละเอียดเกี่ยวกับมิติของรูปภาพและผลลัพธ์ที่ได้จากโมเดล\n</div>","metadata":{}},{"cell_type":"code","source":"def try_batch(dl):\n    for images, labels in dl:                                             # วนลูปผ่าน DataLoader เพื่อดึง batch ข้อมูล\n        images = images.unsqueeze(0)                                      # เพิ่มมิติในแกนที่ 0 ของ tensor เพื่อเตรียมข้อมูลให้สามารถใช้กับโมเดลที่อาศัยการรับข้อมูลในรูปแบบของ batch ที่มีมิติเพิ่มขึ้น\n        print('images.shape:', images.shape)                              # แสดงมิติของ tensor\n        out = modelres34(images)                                               # กำหนดตัวแปรเพื่อนำรูปภาพที่เตรียมไว้ผ่านโมเดลและทำนายผล\n        print('out.shape:', out.shape)                                    # แสดงมิติของ tensor out ที่ได้จากโมเดล\n        print('out[0]:', out[0])                                          # แสดงค่าในตำแหน่งแรกจากโมเดล\n        break\n\ntry_batch(train_dl)                                                       # แสดงผลลัพธ์ข้อมูล และแสดงรายละเอียดมิติของรูปภาพ\n","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:51:26.614704Z","iopub.execute_input":"2023-11-27T13:51:26.615078Z","iopub.status.idle":"2023-11-27T13:51:31.750194Z","shell.execute_reply.started":"2023-11-27T13:51:26.615049Z","shell.execute_reply":"2023-11-27T13:51:31.748983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> ฟังก์ชันทำนาย,ฝึกโมเดลและหาผลลัพธ์ของ epoch\n</div>","metadata":{}},{"cell_type":"code","source":"@torch.no_grad()                                                            # decorator ที่ใช้ปิดการคำนวณ gradient ขณะที่ทำนายหรือประเมิน\ndef evaluate_resnet34(modelres34, val_loader):                                            # นำโมเดลไปทำนายบนข้อมูลที่อยู่ใน DataLoader\n    modelres34.eval()\n    outputs = [modelres34.validation_step(batch) for batch in val_loader]        # สร้างตัวแปรเก็บ list ของการนับผลลัพธ์ของแต่ละ batch \n    return modelres34.validation_epoch_end(outputs)                              # รวมผลลัพธ์ทั้งหมดของ epoch และคืนค่า\n\n\ndef fit_34(epochs, lr, modelres34, train_loader, val_loader, opt_func=torch.optim.SGD):\n    torch.cuda.empty_cache()                                                # ลบข้อมูลที่ถูกเก็บไว้ในหน่วยความจำ GPU ที่ไม่ได้ใช้แล้ว \n    history_of_resnet34 = []                                                            # สร้าง List เก็บผลลัพธ์ของการฝึกโมเดลในแต่ละ epoch\n    optimizer = opt_func(modelres34.parameters(), lr)                            # สร้าง optimizer โดยใช้ฟังก์ชันการอัปเดต gradient \n    for epoch in range(epochs):\n        # Training Phase \n        modelres34.train()                                                       # กำหนดโมเดลที่จะทำการฝึก\n        train_losses_res34 = []                                                   # สร้าง List ก็บค่า loss ที่ได้จากแต่ละ batch ของชุดข้อมูลที่ฝึก\n        for batch in tqdm(train_loader):\n            loss = modelres34.training_step(batch)                               # คำนวณ loss จากข้อมูลใน batch ด้วยฟังก์ชัน model.training_step\n            train_losses_res34.append(loss)                                       # เพิ่มค่า loss ลงใน List \n            loss.backward()                                                 # คำนวณ gradient ของพารามิเตอร์\n            optimizer.step()                                                # ใช้ optimizer เพื่ออัปเดตพารามิเตอร์โมเดล\n            optimizer.zero_grad()                                           # เซ็ต gradient ทุกอันให้เป็นศูนย์ เพื่อเตรียมการคำนวณ gradient ใน batch ถัดไป\n        # Validation phase\n        result = evaluate_resnet34(modelres34, val_loader)                                # ประเมินโมเดลบนชุดข้อมูลการตรวจสอบโดยใช้ฟังก์ชัน evaluate\n        result['train_loss'] = torch.stack(train_losses_res34).mean().item()      # คำนวณค่าเฉลี่ยของ loss ในชุดข้อมูลที่ฝึกแล้วเก็บไว้ใน result\n        modelres34.epoch_end(epoch, result)                                      # เรียกใช้ฟังก์ชันเพื่อทำประมวลผลทุก epoch\n        history_of_resnet34.append(result)                                              # เพิ่ม result ลงใน List history\n    return history_of_resnet34                                                   # คืนค่า List history ซึ่งประกอบด้วยผลลัพธ์ของการฝึกโมเดลในแต่ละ epoch\n","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:54:33.662408Z","iopub.execute_input":"2023-11-27T14:54:33.662939Z","iopub.status.idle":"2023-11-27T14:54:33.681959Z","shell.execute_reply.started":"2023-11-27T14:54:33.662899Z","shell.execute_reply":"2023-11-27T14:54:33.680888Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# สร้าง YelpResCnnModel2 และย้ายโมเดลไปยังอุปกรณ์ที่กำหนดใน device โดยใช้ฟังก์ชัน to_device\nmodelres34 = to_device(YelpRes34CnnModel(), device)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:53:31.367627Z","iopub.execute_input":"2023-11-27T13:53:31.368123Z","iopub.status.idle":"2023-11-27T13:53:31.817481Z","shell.execute_reply.started":"2023-11-27T13:53:31.368085Z","shell.execute_reply":"2023-11-27T13:53:31.816573Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ให้โมเดลทำการประเมินบนชุดข้อมูลการตรวจสอบ \nevaluate_resnet34(modelres34, val_dl)                                                     # ฟังก์ชัน evaluate จะทำการวนลูปผ่าน DataLoader และทำการทำนายผลลัพธ์จากแต่ละ batch ของชุดข้อมูลการตรวจสอบ นับผลลัพธ์ทั้งหมด, และนำผลลัพธ์นี้มารวมผลลัพธ์ที่สำคัญสำหรับการประเมิน","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:53:33.207891Z","iopub.execute_input":"2023-11-27T13:53:33.208262Z","iopub.status.idle":"2023-11-27T13:53:58.492981Z","shell.execute_reply.started":"2023-11-27T13:53:33.208234Z","shell.execute_reply":"2023-11-27T13:53:58.491779Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_epochs = 5                                                             # กำหนดจำนวน epoch ที่ต้องการฝึกโมเดล\nopt_func = torch.optim.Adam                                                # กำหนดฟังก์ชัน optimizer ที่จะใช้ในการอัปเดตพารามิเตอร์ของโมเดลในแต่ละรอบฝึก, โดยใช้ Adam optimizerเป็นหนึ่งในอัลกอริทึมการอัปเดต gradient ที่มีประสิทธิภาพ\nlr = 1e-2                                                                  # กำหนดอัตราการเรียนรู้ที่จะใช้ในการคำนวณการอัปเดตพารามิเตอร์\n# กระบวนการฝึกโมเดลและการตรวจสอบบนชุดข้อมูลการตรวจสอบในแต่ละ epoch, ค่าผลลัพธ์ของแต่ละ epoch จะถูกเก็บไว้ใน history เพื่อให้สามารถดูผลลัพธ์หลังจากการฝึกโมเดลได้\nhistory_of_resnet34 = fit_34(num_epochs, lr, modelres34, train_dl, val_dl, opt_func)           # การฝึกโมเดลและประเมินผลของโมเดลในแต่ละ epoch บนชุดข้อมูลฝึกและชุดข้อมูลการตรวจสอบ       ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T13:54:11.632739Z","iopub.execute_input":"2023-11-27T13:54:11.633840Z","iopub.status.idle":"2023-11-27T13:54:19.593738Z","shell.execute_reply.started":"2023-11-27T13:54:11.633790Z","shell.execute_reply":"2023-11-27T13:54:19.592436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## RestNet50 Model","metadata":{}},{"cell_type":"markdown","source":"<div style=\"pxborder-radius:10px; border:#DEB887 solid; padding: 15px; background-color: #FFFAF0; font-size:85%; text-align:left\">\n\n<h3 align=\"left\"><font color='#DEB887'>💡 ResNet-50</font></h3>\nResNet-50 คือโมเดลที่เป็นส่วนขยายของ ResNet ที่มีโครงสร้างทั้งหมด 50 layers, มีจำนวนชั้นที่มากขึ้นเมื่อเปรียบเทียบกับ ResNet-34 และมีการใช้ block ที่ซับซ้อนขึ้นที่เรียกว่า bottleneck block ในการลดขนาดของโมเดล\n    \nขั้นตอนการทำงานของแต่ละชั้นของ ResNet-50 :\n\nInput Layer:\n    \nรับภาพเป็นข้อมูลนำเข้า.\n    \nConvolutional Layer 1 (7x7 Convolution, Stride 2):\n    \nการสกัดคุณลักษณะด้วยการใช้ส่วนตัวสกัดคุณลักษณะขนาด 7x7 ด้วยการกระโดดทีละ 2 พิกเซล.\n    \nBatch Normalization 1:\n    \nปรับปรุงค่าคุณลักษณะด้วย batch normalization.\n    \nReLU Activation 1:\n    \nใช้ Rectified Linear Unit (ReLU) เป็นฟังก์ชัน activation หลังการสกัดคุณลักษณะ.\n    \nMax Pooling 1:\n    \nการลดขนาดของรูปภาพด้วย max pooling ขนาด 3x3.\n    \nResidual Block 1 (3 Layers, Bottleneck Block):\n    \nชุดของ 3 ชั้นที่ประกอบด้วย:\n    \nConvolutional Layer 2 (1x1 Convolution):\n    \nการสกัดคุณลักษณะด้วยส่วนตัวสกัดคุณลักษณะขนาด 1x1 เพื่อลดมิติข้อมูล.\n    \nBatch Normalization 2:\n    \nปรับปรุงค่าคุณลักษณะด้วย batch normalization.\n    \nReLU Activation 2:\n    \nใช้ ReLU เป็นฟังก์ชัน activation.\n    \nConvolutional Layer 3 (3x3 Convolution):\n    \nการสกัดคุณลักษณะด้วยส่วนตัวสกัดคุณลักษณะขนาด 3x3.\n    \nBatch Normalization 3:\n    \nปรับปรุงค่าคุณลักษณะด้วย batch normalization.\n    \nReLU Activation 3:\n    \nใช้ ReLU เป็นฟังก์ชัน activation.\n    \nConvolutional Layer 4 (1x1 Convolution, Double Dimension):\n    \nการสกัดคุณลักษณะด้วยส่วนตัวสกัดคุณลักษณะขนาด 1x1 เพื่อเพิ่มมิติข้อมูล.\n    \nBatch Normalization 4:\n    \nปรับปรุงค่าคุณลักษณะด้วย batch normalization.\n    \nSkip Connection (Identity Mapping):\n    \nShortcut connection ที่ส่งข้อมูลตรงผ่านโดยไม่มีการทำซ้ำทางกลับ (skip connection).\n    \nReLU Activation 4:\n    \nใช้ ReLU เป็นฟังก์ชัน activation ท้ายสุดของ residual block.\n    \nResidual Block 2 (3 Layers, Bottleneck Block):\n    \nตามหลักการเดียวกันกับ Residual Block 1.\n    \nResidual Block 3 (3 Layers, Bottleneck Block):\n    \nตามหลักการเดียวกันกับ Residual Block 1.\n    \nResidual Block 4 (3 Layers, Bottleneck Block):\n    \nตามหลักการเดียวกันกับ Residual Block 1.\n    \nGlobal Average Pooling:\n    \nทำให้ข้อมูลเข้าสู่รูปแบบที่สามารถให้ข้อมูลขนาดแนวนอนเป็น 1 (global average pooling).\n    \nFully Connected Layer (Output Layer):\n    \nนำข้อมูลจาก global average pooling มาใช้ในการทำนายหมวดหมู่ของภาพ.\n    \nSoftmax Activation:\n    \nใช้ softmax activation เพื่อแปลงคะแนนของแต่ละหมวดหมู่ให้กลายเป็นความน่าจะเป็น.\n    \nResNet-50 นี้ออกแบบมาเพื่อให้สามารถจัดการกับโมเดลที่มีความลึกมากขึ้น โดยใช้ bottleneck block ที่ช่วยลดมิติข้อมูลในขณะที่ยังสามารถทำนายได้ดี. \n    \nShortcut connections ยังคงมีบทบาทสำคัญในการลดปัญหาการหายไปหรือหายมิได้.","metadata":{}},{"cell_type":"code","source":"class YelpRes50CnnModel(MultilabelImageClassificationBase):\n    def __init__(self):\n        super().__init__()                                                  # เรียกใช้เมธอด __init__ ของคลาสแม่ (super class) เพื่อให้ได้ความประโยชน์จากความสามารถและการกำหนดค่าที่ถูกนำมาจากคลาสหลัก\n        # Use a pretrained model\n        self.network = models.resnet50(pretrained=True)                     # ใช้โมเดล ResNet-50 ที่ถูกทำการฝึกสอนไว้ล่วงหน้าจาก PyTorch torchvision models, การใช้โมเดลที่ถูกฝึกสอนไว้ล่วงหน้านี้ช่วยให้โมเดลมีความสามารถในการจำแนกภาพได้ดีมากขึ้น \n        # Replace last layer\n        num_ftrs = self.network.fc.in_features                              # หาจำนวน features ที่อยู่ในเลเยอร์สุดท้ายของโมเดลโดยใช้ in_features ซึ่งเป็นค่าที่ใช้ในการกำหนดจำนวน input features ในเลเยอร์แรกของโมเดลที่จะถูกเพิ่มใหม่\n        self.network.fc = nn.Linear(num_ftrs, 10)                           # ทำการแทนที่เลเยอร์ที่เป็น fully connected สุดท้ายของโมเดลด้วย Linear layer ที่มี output features เป็น 10, นี่คือส่วนที่ทำให้โมเดลมีความสามารถในการจำแนกภาพอย่างแม่นยำตามจำนวน classes ที่กำหนด\n    \n    def forward(self, xb):                                                  # ทำการรับ batch ของ input data (xb) และทำการส่งผ่านโมเดล\n        return torch.sigmoid(self.network(xb)) ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> ฟังก์ชันทำนาย,ฝึกโมเดลและหาผลลัพธ์ของ epoch\n</div>","metadata":{}},{"cell_type":"code","source":"def evaluate_resnet50(modelres50, val_loader):                                            \n    modelres50.eval()\n    outputs = [modelres50.validation_step(batch) for batch in val_loader]         \n    return modelres50.validation_epoch_end(outputs)  \n\n                                                 \ndef fit_50(epochs, lr, modelres50, train_loader, val_loader, opt_func=torch.optim.SGD):\n    torch.cuda.empty_cache()                                                # ลบข้อมูลที่ถูกเก็บไว้ในหน่วยความจำ GPU ที่ไม่ได้ใช้แล้ว \n    history_of_resnet50 = []                                                            # สร้าง List เก็บผลลัพธ์ของการฝึกโมเดลในแต่ละ epoch\n    optimizer = opt_func(modelres50.parameters(), lr)                            # สร้าง optimizer โดยใช้ฟังก์ชันการอัปเดต gradient \n    for epoch in range(epochs):\n        # Training Phase \n        modelres50.train()                                                       # กำหนดโมเดลที่จะทำการฝึก\n        train_losses_res50 = []                                                   # สร้าง List ก็บค่า loss ที่ได้จากแต่ละ batch ของชุดข้อมูลที่ฝึก\n        for batch in tqdm(train_loader):\n            loss = modelres50.training_step(batch)                               # คำนวณ loss จากข้อมูลใน batch ด้วยฟังก์ชัน model.training_step\n            train_losses_res50.append(loss)                                       # เพิ่มค่า loss ลงใน List \n            loss.backward()                                                 # คำนวณ gradient ของพารามิเตอร์\n            optimizer.step()                                                # ใช้ optimizer เพื่ออัปเดตพารามิเตอร์โมเดล\n            optimizer.zero_grad()                                           # เซ็ต gradient ทุกอันให้เป็นศูนย์ เพื่อเตรียมการคำนวณ gradient ใน batch ถัดไป\n        # Validation phase\n        result = evaluate_resnet50(modelres50, val_loader)                                # ประเมินโมเดลบนชุดข้อมูลการตรวจสอบโดยใช้ฟังก์ชัน evaluate\n        result['train_loss'] = torch.stack(train_losses_res50).mean().item()      # คำนวณค่าเฉลี่ยของ loss ในชุดข้อมูลที่ฝึกแล้วเก็บไว้ใน result\n        modelres50.epoch_end(epoch, result)                                      # เรียกใช้ฟังก์ชันเพื่อทำประมวลผลทุก epoch\n        history_of_resnet50.append(result)                                              # เพิ่ม result ลงใน List history\n    return history_of_resnet50  ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#ทำนายผลลัพธ์ของรูปภาพ\nmodelres50 = YelpRes50CnnModel()\nmodelres50                      ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> โมเดลที่ถูกสร้างขึ้นโดยเป็นคลาสหลักสำหรับการสร้างโมเดลสำหรับงานการจำแนกภาพที่มีหลาย label\n</div>","metadata":{}},{"cell_type":"code","source":"train_dl = DeviceDataLoader(train_dl, device)                            # โอน DataLoader ของชุดข้อมูลฝึกปยังอุปกรณ์ที่กำหนด เพื่อให้สามารถสทำงานในอุปกรณ์นั้นได้\nval_dl = DeviceDataLoader(val_dl, device)                                # ทำการโอน DataLoader ของชุดข้อมูลการตรวจสอบไปยังอุปกรณ์\nto_device(modelres50, device);","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def try_batch50(dl):\n    for images, labels in dl:                                             # วนลูปผ่าน DataLoader เพื่อดึง batch ข้อมูล\n        images = images.unsqueeze(0)                                      # เพิ่มมิติในแกนที่ 0 ของ tensor เพื่อเตรียมข้อมูลให้สามารถใช้กับโมเดลที่อาศัยการรับข้อมูลในรูปแบบของ batch ที่มีมิติเพิ่มขึ้น\n        print('images.shape:', images.shape)                              # แสดงมิติของ tensor\n        out = modelres50(images)                                           # กำหนดตัวแปรเพื่อนำรูปภาพที่เตรียมไว้ผ่านโมเดลและทำนายผล\n        print('out.shape:', out.shape)                                    # แสดงมิติของ tensor out ที่ได้จากโมเดล\n        print('out[0]:', out[0])                                          # แสดงค่าในตำแหน่งแรกจากโมเดล\n        break\n\ntry_batch50(train_dl) ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#ฟังก์ชันจะใช้เมธอด to ของ PyTorch เพื่อย้าย tensor ไปยังอุปกรณ์ที่กำหนด\nto_device(modelres50, device);","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#ตรวจสอบว่า data เป็น tensor เดี่ยวหรือ list/tuple ของ tensor แล้วทำการย้ายไปยังอุปกรณ์ที่กำหนด\nmodelres50 = to_device(YelpRes50CnnModel(), device)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"evaluate_resnet50(modelres50, val_dl)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_epochs = 5                                                             # กำหนดจำนวน epoch ที่ต้องการฝึกโมเดล\nopt_func = torch.optim.Adam                                                # กำหนดฟังก์ชัน optimizer ที่จะใช้ในการอัปเดตพารามิเตอร์ของโมเดลในแต่ละรอบฝึก, โดยใช้ Adam optimizerเป็นหนึ่งในอัลกอริทึมการอัปเดต gradient ที่มีประสิทธิภาพ\nlr = 1e-2    \nhistory_of_resnet50 = fit_50(num_epochs, lr, modelres50, train_dl, val_dl, opt_func) ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:57:14.491241Z","iopub.execute_input":"2023-11-27T14:57:14.491679Z","iopub.status.idle":"2023-11-27T15:14:06.806371Z","shell.execute_reply.started":"2023-11-27T14:57:14.491643Z","shell.execute_reply":"2023-11-27T15:14:06.805295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:140%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\" id=\"modelpre\"> Model Prediction  </p>\n\n<div class=\"alert alert-success\">  \n<b>📍</b> ฟังก์ชันแสดงผลลัพธ์การทำนายของรูปภาพที่รับเข้ามา\n</div>","metadata":{}},{"cell_type":"code","source":"def predict_single_resnet34(image):\n    xb = image.unsqueeze(0)                                                # สร้างมิติแถวใหม่ตำแหน่งที่ 0 (แถวแรก) ของรูปภาพ\n    xb = to_device(xb, device)                                             # ย้ายรูปภาพที่ถูกแปลงไปเป็น Tensor ไปยังอุปกรณ์ที่กำหนด\n    preds = modelres34(xb)                                                 # ทำนายผลลัพธ์โดยใช้โมเดลบนรูปภาพที่ถูกแปลงและย้ายไปยังอุปกรณ์ที่ถูกกำหนด\n    prediction = preds[0]                                                  # ดึงผลลัพธ์จาก Tensor ผลลัพธ์ที่ได้มาในรูปแบบของ Tensor ที่มีมิติเดียว\n    print(\"Prediction: \", prediction)\n    show_sample(image, prediction)  \n    \ndef predict_single_resnet50(image):\n    xb = image.unsqueeze(0)                                                # สร้างมิติแถวใหม่ตำแหน่งที่ 0 (แถวแรก) ของรูปภาพ \n    xb = to_device(xb, device)                                             # ย้ายรูปภาพที่ถูกแปลงไปเป็น Tensor ไปยังอุปกรณ์ที่กำหนด\n    preds = modelres50(xb)                                                 #ทำนายผลลัพธ์จากรูปภาพที่ถูกนำเข้า\n    prediction = preds[0]                                                  # เลือกผลลัพธ์ที่อยู่ในตำแหน่งแรกของ batch (index 0)\n    print(\"Prediction: \", prediction)                                      #แสดงผลลัพธ์การทำนาย\n    show_sample(image, prediction)                                         #เรียกใช้ฟังก์ชัน show_sample เพื่อแสดงรูปภาพพร้อมกับผลลัพธ์การทำนาย","metadata":{"execution":{"iopub.status.busy":"2023-11-27T15:26:39.673622Z","iopub.execute_input":"2023-11-27T15:26:39.674061Z","iopub.status.idle":"2023-11-27T15:26:39.680997Z","shell.execute_reply.started":"2023-11-27T15:26:39.674007Z","shell.execute_reply":"2023-11-27T15:26:39.679969Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dataset = YelpResPhoto(TEST_CSV, TEST_DIR, transform = transform)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T15:24:17.842011Z","iopub.execute_input":"2023-11-27T15:24:17.842388Z","iopub.status.idle":"2023-11-27T15:24:17.861065Z","shell.execute_reply.started":"2023-11-27T15:24:17.842359Z","shell.execute_reply":"2023-11-27T15:24:17.860170Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dl = DeviceDataLoader(DataLoader(test_dataset, item_tfms=Resize(460), batch_tfms=aug_transforms(size=224), num_workers=2, pin_memory=True), device) # สร้าง PyTorch DataLoader สำหรับ test_dataset มีการแปลง item เช่น การเปลี่ยนขนาดเป็น 460, การแปลงข้อมูลในระดับแบทช์ (batch) ที่มีการเพิ่มเติมเช่นการเปลี่ยนขนาดเป็น 224, และระบุจำนวน worker processes สำหรับการโหลดข้อมูล","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:03:17.913111Z","iopub.execute_input":"2023-11-27T14:03:17.913869Z","iopub.status.idle":"2023-11-27T14:03:17.920494Z","shell.execute_reply.started":"2023-11-27T14:03:17.913837Z","shell.execute_reply":"2023-11-27T14:03:17.919369Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predict_single_resnet34(test_dataset[100][0]) # ใช้ฟังก์ชันที่ชื่อ predict_single มี test_datasetเพื่อทดสอบ โดย predict จาก model ที่ผ่านการ train","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:02:24.935167Z","iopub.execute_input":"2023-11-27T14:02:24.936116Z","iopub.status.idle":"2023-11-27T14:02:25.275231Z","shell.execute_reply.started":"2023-11-27T14:02:24.936084Z","shell.execute_reply":"2023-11-27T14:02:25.274190Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predict_single_resnet50(test_dataset[100][0])","metadata":{"execution":{"iopub.status.busy":"2023-11-27T15:24:20.021209Z","iopub.execute_input":"2023-11-27T15:24:20.021569Z","iopub.status.idle":"2023-11-27T15:24:20.416877Z","shell.execute_reply.started":"2023-11-27T15:24:20.021540Z","shell.execute_reply":"2023-11-27T15:24:20.415924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predict_single_resnet34(test_dataset[77][0]) # ใช้ฟังก์ชันที่ชื่อ predict_single มี test_datasetเพื่อทดสอบ โดย predict จาก model ที่ผ่านการ train","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:02:36.362433Z","iopub.execute_input":"2023-11-27T14:02:36.363305Z","iopub.status.idle":"2023-11-27T14:02:36.749804Z","shell.execute_reply.started":"2023-11-27T14:02:36.363261Z","shell.execute_reply":"2023-11-27T14:02:36.748851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predict_single_resnet50(test_dataset[77][0]) # ใช้ฟังก์ชันที่ชื่อ predict_single มี test_datasetเพื่อทดสอบ โดย predict จาก model ที่ผ่านการ train","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-info\">  \n<b>📎</b> ทำนายผลลัพธ์จากโมเดล deep learning เพื่อรับข้อมูลนำเข้าแบบชุดจากชุดข้อมูลทดสอบ และนำไปทำนายผลลัพธ์จากโมเดลที่กำหนด \n</div>","metadata":{}},{"cell_type":"code","source":"def predict_dl(dl, model):                                            # กำหนดฟังก์ชันที่ชื่อ predict_dl ซึ่งรับ dataloader (dl) และโมเดล deep learning (model) เป็นพารามิเตอร์\n    torch.cuda.empty_cache()                                          # ล้างแคชหน่วยความจำ GPU\n    model.eval()                                                      # กำหนดโมเดลให้เข้าสู่โหมดการประเมินใน PyTorch\n    batch_probs = []\n\n    for xb, _ in tqdm(dl):                                            # batch ใน dataloader (dl). นำข้อมูลนำเข้า (xb) และ target ที่เกี่ยวข้อง (ทำการละเว้น target ด้วย _)\n        # Add an extra dimension to the input tensor\n        xb = xb.unsqueeze(0)                                          # เพิ่มมิติเสริมให้กับเทนเซอร์ที่นำเข้ามา\n        \n        with torch.no_grad():                                         # ล็อก gradient computation ขณะทำนายโมเดลเพื่อประหยัดหน่วยความจำ\n            probs = modelres34(xb)                                         # predictผลลัพธ์โดยส่งข้อมูลเข้าโมเดล\n        \n        batch_probs.append(probs.cpu().detach())                      # เพิ่มผลpredictเข้าไปในlist\n\n    batch_probs = torch.cat(batch_probs)                              # Concatenates the list of predictions into a single tensor\n    return [decode_target(x) for x in batch_probs]","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:04:39.334112Z","iopub.execute_input":"2023-11-27T14:04:39.335101Z","iopub.status.idle":"2023-11-27T14:04:39.344025Z","shell.execute_reply.started":"2023-11-27T14:04:39.335059Z","shell.execute_reply":"2023-11-27T14:04:39.343042Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-success\">  \n<b>📍</b> ใช้ Model Predict ค่าแล้วจากนั้นเก็บในตัวแปรที่เตรียมไว้\n</div>","metadata":{}},{"cell_type":"code","source":"test_preds_resnet50 = predict_dl(test_dl, modelres50)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T15:24:26.649129Z","iopub.execute_input":"2023-11-27T15:24:26.649493Z","iopub.status.idle":"2023-11-27T15:25:56.721789Z","shell.execute_reply.started":"2023-11-27T15:24:26.649465Z","shell.execute_reply":"2023-11-27T15:25:56.720614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_preds_resnet34 = predict_dl(test_dl, modelres34)                               # predict modelกับข้อมูลทดสอบ และแสดงผลลัพ","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:04:40.672195Z","iopub.execute_input":"2023-11-27T14:04:40.673140Z","iopub.status.idle":"2023-11-27T14:06:09.996621Z","shell.execute_reply.started":"2023-11-27T14:04:40.673105Z","shell.execute_reply":"2023-11-27T14:06:09.995495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:130%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\" id=\"submit\">Submission File  </p>","metadata":{}},{"cell_type":"code","source":"submission_df_resnet34 = pd.read_csv(TEST_CSV)                                # อ่านข้อมูลจากไฟล์ CSV ที่ระบุโดยตัวแปร TEST_CSV ลงใน DataFrame ที่ชื่อว่า submission_df ด้วย pandas\nsubmission_df_resnet50 = pd.read_csv(TEST_CSV)                                \nsubmission_df_resnet34['labels'] = test_preds_resnet34                        # เพิ่มคอลัมน์ใหม่ที่ชื่อ 'labels' ใน DataFrame\nsubmission_df_resnet50['labels'] = test_preds_resnet50                               \nsubmission_df_resnet34 = submission_df_resnet34.drop('photo_id', axis=1)      # Drop คอลัมน์ photo_id จากนั้นเก็บค่าใน submission_df \nsubmission_df_resnet50 = submission_df_resnet50.drop('photo_id', axis=1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df_resnet34.sample(n=15)                                                    # แสดงตัวอย่างสุ่มของ 15 แถวจาก DataFrame submission_df","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df_resnet50.sample(n=15)                                                    # แสดงตัวอย่างสุ่มของ 15 แถวจาก DataFrame submission_df","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub34_fname = 'resnet34_submission.csv'                               # กำหนดตัวแปร\nsub50_fname = 'resnet50_submission.csv'\nsubmission_df_resnet34.to_csv(sub34_fname, index=False)                    #ใช้ในการบันทึก DataFrame ลงในไฟล์ CSV\nsubmission_df_resnet50.to_csv(sub50_fname, index=False)                    #ใช้ในการบันทึก DataFrame ลงในไฟล์ CSV","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <p style=\"font-family:JetBrains Mono; font-weight:bold; letter-spacing: 2px; color:#000000; font-size:130%; text-align:left;padding: 0px; border-bottom: 3px solid #003300\" id=\"ref\">Reference</p>\n🔑 Enerrio. (2018, July 29). Data exploration yelp classification. Kaggle. \n\nhttps://www.kaggle.com/code/enerrio/data-exploration-yelp-classification#Data-Exploration-for-Yelp-Image-Classification-Challenge \n\n🔑 Millerintllc. (2016, April 11). Imagine an Image1. Kaggle. \n\nhttps://www.kaggle.com/code/millerintllc/imagine-an-image1 \n\n🔑 muni04. (2020, July 2). Advanced transfer learning starter notebook. Kaggle.\n\nhttps://www.kaggle.com/code/muni04/advanced-transfer-learning-starter-notebook","metadata":{}}]}