{"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":"import torch\nfrom torch import nn\n\ntorch.__version__\n\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\ndevice\n\nimport os\ndef walk_through_dir(dir_path):\n  for dirpath, dirnames, filenames in os.walk(dir_path):\n    print(f\"There are {len(dirnames)} directories and {len(filenames)} images in '{dirpath}'.\")\n    \nimage_path = \"/kaggle/input/cat-and-dog\"\nwalk_through_dir(image_path)\n\ntrain_dir = \"/kaggle/input/cat-and-dog/training_set/training_set\"\ntest_dir = \"/kaggle/input/cat-and-dog/test_set/test_set\"\ntrain_dir, test_dir","metadata":{"id":"opMaq45sNwic","outputId":"3f1c70ff-d143-4a2d-90d3-57e2a2a2810b","execution":{"iopub.status.busy":"2023-07-14T09:49:47.293435Z","iopub.execute_input":"2023-07-14T09:49:47.293907Z","iopub.status.idle":"2023-07-14T09:49:56.008338Z","shell.execute_reply.started":"2023-07-14T09:49:47.293815Z","shell.execute_reply":"2023-07-14T09:49:56.007108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import random\nfrom PIL import Image\nimport glob\nfrom pathlib import Path\n\n# シード値 42に意味はない 西暦などでも用いられる場合がある\nrandom.seed(42) \n\n# 画像のパス\nimage_path_list= glob.glob(f\"{image_path}/*/*/*/*.jpg\")\n\n# ランダム画像のパス\nrandom_image_path = random.choice(image_path_list)\n\n# パス名から画像クラスを取得\nimage_class = Path(random_image_path).parent.stem\n\n# 画像\nimg = Image.open(random_image_path)\n\nprint(\"Random image path: {random_image_path}\")\nprint(\"Image class: {image_class}\")\nprint(\"Image height: {img.height}\") \nprint(\"Image width: {img.width}\")\nimg","metadata":{"id":"Dh5tUEeY-sWk","outputId":"dd5445b2-7fb3-4d66-cef5-8476ad7af591","execution":{"iopub.status.busy":"2023-07-14T09:49:56.010649Z","iopub.execute_input":"2023-07-14T09:49:56.011821Z","iopub.status.idle":"2023-07-14T09:49:56.106873Z","shell.execute_reply.started":"2023-07-14T09:49:56.011779Z","shell.execute_reply":"2023-07-14T09:49:56.105964Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nsns.set_theme()\n\n# 画像を配列にする\nimg_as_array = np.asarray(img)\n\n# matplotlibで画像をプロットする\nplt.figure(figsize=(8, 6))\nplt.imshow(img_as_array)\nplt.title(f\"Image class: {image_class} | Image shape: {img_as_array.shape} -> [height, width, color_channels]\")\nplt.axis(False);","metadata":{"id":"9yiV3Z_RDkAI","outputId":"6119a3ff-5ba9-42ef-b7b1-305d80eb8e95","execution":{"iopub.status.busy":"2023-07-14T09:49:56.108711Z","iopub.execute_input":"2023-07-14T09:49:56.109340Z","iopub.status.idle":"2023-07-14T09:49:57.249417Z","shell.execute_reply.started":"2023-07-14T09:49:56.109301Z","shell.execute_reply":"2023-07-14T09:49:57.248417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"Transforming-data\"></a>\n <p style=\"background-color:coral;font-family:newtimeroman;font-size:150%;color:white;text-align:center;border-radius:20px 20px;\"><b>データの変換</b></p>","metadata":{"id":"tXCiyY6vEUMs"}},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import DataLoader\nfrom torchvision import datasets, transforms\n\n# 画像をスクエアにする\nIMAGE_WIDTH=128\nIMAGE_HEIGHT=128\nIMAGE_SIZE=(IMAGE_WIDTH, IMAGE_HEIGHT)\n\n# 画像の変換を書き込む\ndata_transform = transforms.Compose([\n    # 画像のサイズをIMAGE_SIZE xIMAGE_SIZEに変更する。\n    transforms.Resize(size=IMAGE_SIZE),\n    # 画像を水平に50%の確率でランダムに反転させる\n    transforms.RandomHorizontalFlip(p=0.5),\n    # 0から255までのすべてのピクセル値を0.0から1.0の間に変換する。\n    transforms.ToTensor()\n])","metadata":{"id":"9Bwx1c5VEJeS","execution":{"iopub.status.busy":"2023-07-14T09:49:57.251771Z","iopub.execute_input":"2023-07-14T09:49:57.252091Z","iopub.status.idle":"2023-07-14T09:49:57.507430Z","shell.execute_reply.started":"2023-07-14T09:49:57.252056Z","shell.execute_reply":"2023-07-14T09:49:57.506450Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_transformed_images(image_paths, transform, n=3, seed=42):\n    random.seed(seed)\n    random_image_paths = random.sample(image_paths, k=n)\n    for image_path in random_image_paths:\n        with Image.open(image_path) as f:\n            fig, ax = plt.subplots(1, 2)\n            ax[0].imshow(f) \n            ax[0].set_title(f\"Original \\nSize: {f.size}\")\n            ax[0].axis(\"off\")\n\n            # 画像を変換してプロットする\n            # 注意: permute() は matplotlib に合うように画像の形を変えます。\n            # (PyTorch のデフォルトは [C, H, W] ですが、Matplotlib は [H, W, C] です)\n            transformed_image = transform(f).permute(1, 2, 0) \n            ax[1].imshow(transformed_image) \n            ax[1].set_title(f\"Transformed \\nSize: {transformed_image.shape}\")\n            ax[1].axis(\"off\")\n            fig.suptitle(f\"Class: {Path(image_path).parent.stem}\", fontsize=16)\n\nplot_transformed_images(image_path_list, transform=data_transform, n=3)","metadata":{"id":"_LY7nHI0FMeE","outputId":"943ef950-e112-4c8b-be58-6c3647f6d737","execution":{"iopub.status.busy":"2023-07-14T09:49:57.508846Z","iopub.execute_input":"2023-07-14T09:49:57.509232Z","iopub.status.idle":"2023-07-14T09:49:58.559332Z","shell.execute_reply.started":"2023-07-14T09:49:57.509193Z","shell.execute_reply":"2023-07-14T09:49:58.558406Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"Loading-image-data\"></a>\n<p style=\"background-color:coral;font-family:newtimeroman;font-size:150%;color:white;text-align:center;border-radius:20px 20px;\"><b>Loading Image Data</b></p>","metadata":{"id":"9LrpI_fKGX1H"}},{"cell_type":"code","source":"from torchvision import datasets\n\n# トレーニングセットの作成\ntrain_data = datasets.ImageFolder(root=train_dir, # 画像の保存先フォルダ\n                                  transform=data_transform, # データ（画像）に対して実行する変換\n                                  target_transform = None) # ラベルに対して実行する変換 (必要な場合)\n# テストセットの作成\ntest_data = datasets.ImageFolder(root=test_dir, transform=data_transform)\n\nprint(f\"Train data:\\n{train_data}\\nTest data:\\n{test_data}\")","metadata":{"id":"kYSJqf81GXb0","outputId":"ae2bcaba-26b8-4532-b2f3-4a546f492296","execution":{"iopub.status.busy":"2023-07-14T09:49:58.560745Z","iopub.execute_input":"2023-07-14T09:49:58.561529Z","iopub.status.idle":"2023-07-14T09:49:59.896999Z","shell.execute_reply.started":"2023-07-14T09:49:58.561485Z","shell.execute_reply":"2023-07-14T09:49:59.895812Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# クラス名をリストとして取得\nclass_names = train_data.classes\nprint(\"Class names: \",class_names)\n\n# クラス名をdictとして取得することもできる\nclass_dict = train_data.class_to_idx\nprint(\"Class names as a dict: \",class_dict)\n\n# 長さのチェック\nprint(\"The lengths of the training and test sets: \", len(train_data), len(test_data))","metadata":{"id":"TvLTj_T1G535","outputId":"0dc33d5b-dbca-4008-e9d4-83d029d5a607","execution":{"iopub.status.busy":"2023-07-14T09:49:59.898672Z","iopub.execute_input":"2023-07-14T09:49:59.899334Z","iopub.status.idle":"2023-07-14T09:49:59.906968Z","shell.execute_reply.started":"2023-07-14T09:49:59.899291Z","shell.execute_reply":"2023-07-14T09:49:59.905796Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img, label = train_data[0][0], train_data[0][1]\nprint(f\"Image tensor:\\n{img}\")\nprint(f\"Image shape: {img.shape}\")\nprint(f\"Image datatype: {img.dtype}\")\nprint(f\"Image label: {label}\")\nprint(f\"Label datatype: {type(label)}\")","metadata":{"id":"FRHcIRLBH5-C","outputId":"dd613725-38e6-408e-e783-a70afc7e7fe0","execution":{"iopub.status.busy":"2023-07-14T09:49:59.908364Z","iopub.execute_input":"2023-07-14T09:49:59.908673Z","iopub.status.idle":"2023-07-14T09:49:59.934019Z","shell.execute_reply.started":"2023-07-14T09:49:59.908629Z","shell.execute_reply":"2023-07-14T09:49:59.932927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 次元の順序を並べ替える\nimg_permute = img.permute(1, 2, 0)\n\nprint(f\"Original shape: {img.shape} -> [color_channels, height, width]\")\nprint(f\"Image permute shape: {img_permute.shape} -> [height, width, color_channels]\")\n\n# 画像をプロットする\nplt.figure(figsize=(10, 7))\nplt.imshow(img.permute(1, 2, 0))\nplt.axis(\"off\")\nplt.title(class_names[label], fontsize=14);","metadata":{"id":"VAYrBKA5IDh5","outputId":"2aca7fb1-6956-4e72-e68b-c0ba51220a21","execution":{"iopub.status.busy":"2023-07-14T09:49:59.937662Z","iopub.execute_input":"2023-07-14T09:49:59.937994Z","iopub.status.idle":"2023-07-14T09:50:00.314629Z","shell.execute_reply.started":"2023-07-14T09:49:59.937965Z","shell.execute_reply":"2023-07-14T09:50:00.313651Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 読み込んだ画像をDataLoaderのものにする\n\nPyTorchの`DataLoader`は、データセットオブジェクトから並列にデータをロードするためのユーティリティ。\nデータを一括して読み込むことができるので、ディープラーニングモデルを学習するときに便利です。さらに、データをシャッフルしてオーバーフィッティングを防ぐこともできる。\n\nDataLoaderはデータセットオブジェクトと、バッチサイズ、データのロードに使用するワーカースレッド数、データをシャッフルするかどうかのブール値フラグなどのオプションパラメータを受け取ります。DataLoaderは、バッチ内のデータを反復処理するために使用できるイテレータを返します。","metadata":{"id":"NRm0ua4DI9cc"}},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\n# データロードにいくつのサブプロセスを使用するか（高いほど多い）\nNUM_WORKERS = os.cpu_count()\n\n# 訓練データセットとテストデータセットをDataLoaderに変換する\ntrain_dataloader = DataLoader(dataset=train_data, \n                              batch_size=1, # バッチあたりのサンプル数\n                              num_workers=NUM_WORKERS,\n                              shuffle=True) # データをシャッフル\n\ntest_dataloader = DataLoader(dataset=test_data, \n                             batch_size=1, \n                             num_workers=NUM_WORKERS, \n                             shuffle=False)","metadata":{"id":"X6cgGISJI4PR","outputId":"02c649f4-c459-44a3-c7fc-aeed0372f512","execution":{"iopub.status.busy":"2023-07-14T09:50:00.319727Z","iopub.execute_input":"2023-07-14T09:50:00.320801Z","iopub.status.idle":"2023-07-14T09:50:00.328379Z","shell.execute_reply.started":"2023-07-14T09:50:00.320758Z","shell.execute_reply":"2023-07-14T09:50:00.327341Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img, label = next(iter(train_dataloader))\n\n# バッチサイズが1になることに注意。 \nprint(f\"Image shape: {img.shape} -> [batch_size, color_channels, height, width]\")\nprint(f\"Label shape: {label.shape}\")","metadata":{"id":"zSaEmqi_JMp5","outputId":"ee5667c0-5436-4268-b40d-6d71770a9f07","execution":{"iopub.status.busy":"2023-07-14T09:50:00.329892Z","iopub.execute_input":"2023-07-14T09:50:00.330344Z","iopub.status.idle":"2023-07-14T09:50:00.420077Z","shell.execute_reply.started":"2023-07-14T09:50:00.330305Z","shell.execute_reply":"2023-07-14T09:50:00.417848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"Model-building\"></a>\n<p style=\"background-color:coral;font-family:newtimeroman;font-size:150%;color:white;text-align:center;border-radius:20px 20px;\"><b>データのかさましによるモデル構築</b></p>\n\n一般的なデータ補強手法には、以下のようなものがある：\n\n- 画像のランダムな反転または回転\n- 画像のランダムな切り抜き\n- 明るさ、コントラスト、色のランダムな変更\n- 画像にノイズを加える\n- 画像の拡大縮小や平行移動","metadata":{"id":"_02nLX-NKrnj"}},{"cell_type":"markdown","source":"## トランスフォームの作成とデータの読み込み\n\nTrivialAugmentWideはPyTorchのデータかさましのテクニックで、画像にランダムなリサイズとトリミングを適用します。つまり、学習データの多様性を高めるために、画像に対して多数のランダムな変換を行います。これにより、機械学習モデルのロバスト性と汎化性を向上させることができます。","metadata":{"id":"fQtclsnSNEte"}},{"cell_type":"code","source":"IMAGE_WIDTH = 224\nIMAGE_HEIGHT = 224\nIMAGE_SIZE=(IMAGE_WIDTH, IMAGE_HEIGHT)\n\n# TrivialAugmentを使用してトレーニング変換を作成する。\ntrain_transform = transforms.Compose([\n    transforms.Resize(IMAGE_SIZE),\n    transforms.TrivialAugmentWide(),\n    transforms.ToTensor()])\n\n# テスト変換を作成する（データ拡張なし）\ntest_transform = transforms.Compose([\n    transforms.Resize(IMAGE_SIZE),\n    transforms.ToTensor()])","metadata":{"id":"RY-UzszvKrKW","execution":{"iopub.status.busy":"2023-07-14T09:50:00.421904Z","iopub.execute_input":"2023-07-14T09:50:00.423175Z","iopub.status.idle":"2023-07-14T09:50:00.430777Z","shell.execute_reply.started":"2023-07-14T09:50:00.423127Z","shell.execute_reply":"2023-07-14T09:50:00.429770Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 画像フォルダをデータセットにする\ntrain_data_augmented = datasets.ImageFolder(train_dir, transform=train_transform)\ntest_data_augmented = datasets.ImageFolder(test_dir, transform=test_transform)\n\ntrain_data_augmented, test_data_augmented","metadata":{"id":"mIdSIQORLd14","outputId":"776dbcf3-57cc-4419-b95e-5a767a826ec2","execution":{"iopub.status.busy":"2023-07-14T09:50:00.432329Z","iopub.execute_input":"2023-07-14T09:50:00.432809Z","iopub.status.idle":"2023-07-14T09:50:00.509352Z","shell.execute_reply.started":"2023-07-14T09:50:00.432769Z","shell.execute_reply":"2023-07-14T09:50:00.508279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# いくつかのパラメータを設定します。\nBATCH_SIZE = 32\ntorch.manual_seed(42)\n\ntrain_dataloader_augmented = DataLoader(train_data_augmented, \n                                        batch_size=BATCH_SIZE, \n                                        shuffle=True,\n                                        num_workers=NUM_WORKERS)\n\ntest_dataloader_augmented = DataLoader(test_data_augmented, \n                                       batch_size=BATCH_SIZE, \n                                       shuffle=False, \n                                       num_workers=NUM_WORKERS)","metadata":{"id":"O_rQ4PllMe04","outputId":"2a3ffefc-d582-4f28-b33d-2a0c2958db8d","execution":{"iopub.status.busy":"2023-07-14T09:50:00.511096Z","iopub.execute_input":"2023-07-14T09:50:00.511463Z","iopub.status.idle":"2023-07-14T09:50:00.520671Z","shell.execute_reply.started":"2023-07-14T09:50:00.511426Z","shell.execute_reply":"2023-07-14T09:50:00.519682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## CNN 画像分類器の作成","metadata":{"id":"cVDWp25FNRM_"}},{"cell_type":"markdown","source":"畳み込みニューラルネットワーク（CNN）はディープラーニング・ニューラルネットワークの一種で、主に画像や映像の認識タスクに使われる。CNNは、2Dグリッドに配置されたピクセルで構成される画像のような、グリッド状のトポロジーを持つデータを処理するように設計されている。CNNのアーキテクチャーは、畳み込み層、プーリング層、完全連結層など、いくつかの層から構成される。\n\n畳み込み層は、学習可能なフィルター・セットを画像に適用することで、入力画像の特徴を検出する。これらのフィルタは入力画像と畳み込まれ、一連の特徴マップを生成する。次に、特徴マップの空間次元を減らし、最も顕著な特徴のみを保持するために、プーリング層に渡される。次に完全接続層がプールされた特徴マップを処理し、分類ラベルなどの最終出力を生成する。\n\nCNNは、画像分類、物体検出、セマンティック・セグメンテーションなどの幅広いコンピュータ・ビジョン・タスクにおいて、最先端の結果を達成するために使用されている。","metadata":{}},{"cell_type":"code","source":"# CNNベースの画像分類器の作成。\nclass ImageClassifier(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.conv_layer_1 = nn.Sequential(\n          nn.Conv2d(3, 64, 3, padding=1),\n          nn.ReLU(),\n          nn.BatchNorm2d(64),\n          nn.MaxPool2d(2))\n        self.conv_layer_2 = nn.Sequential(\n          nn.Conv2d(64, 512, 3, padding=1),\n          nn.ReLU(),\n          nn.BatchNorm2d(512),\n          nn.MaxPool2d(2))\n        self.conv_layer_3 = nn.Sequential(\n          nn.Conv2d(512, 512, kernel_size=3, padding=1),\n          nn.ReLU(),\n          nn.BatchNorm2d(512),\n          nn.MaxPool2d(2)) \n        self.classifier = nn.Sequential(\n          nn.Flatten(),\n          nn.Linear(in_features=512*3*3, out_features=2))\n    def forward(self, x: torch.Tensor):\n        x = self.conv_layer_1(x)\n        x = self.conv_layer_2(x)\n        x = self.conv_layer_3(x)\n        x = self.conv_layer_3(x)\n        x = self.conv_layer_3(x)\n        x = self.conv_layer_3(x)\n        x = self.classifier(x)\n        return x\n# オブジェクトをインスタンス化します。\nmodel = ImageClassifier().to(device)","metadata":{"id":"j99yPcDCtnPG","outputId":"45bba9bf-3063-4ab7-c820-8b95a351b6e8","execution":{"iopub.status.busy":"2023-07-14T09:50:00.522034Z","iopub.execute_input":"2023-07-14T09:50:00.522472Z","iopub.status.idle":"2023-07-14T09:50:03.873937Z","shell.execute_reply.started":"2023-07-14T09:50:00.522435Z","shell.execute_reply":"2023-07-14T09:50:03.872900Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 単一の画像でフォワードパスを試す（モデルをテストするため）","metadata":{"id":"C4J-DUNXQbSB"}},{"cell_type":"code","source":"# DataLoaderから画像とラベルのバッチを取得する。\nimg_batch, label_batch = next(iter(train_dataloader_augmented))\n\n# バッチから1つの画像を取得し、その形状がモデルに合うように画像のスクイーズを解除する。\nimg_single, label_single = img_batch[0].unsqueeze(dim=0), label_batch[0]\nprint(f\"Single image shape: {img_single.shape}\\n\")\n\n# 単一の画像に対してフォワードパスを実行する\nmodel.eval()\nwith torch.inference_mode():\n    pred = model(img_single.to(device))\n    \n# 何が起きているかをプリントアウトし、モデルロジット→予測問題→予測ラベルに変換する。\nprint(f\"Output logits:\\n{pred}\\n\")\nprint(f\"Output prediction probabilities:\\n{torch.softmax(pred, dim=1)}\\n\")\nprint(f\"Output prediction label:\\n{torch.argmax(torch.softmax(pred, dim=1), dim=1)}\\n\")\nprint(f\"Actual label:\\n{label_single}\")","metadata":{"id":"W3QEE5CBQa7j","outputId":"2c2fa771-8ca4-4ca9-b643-27318a632e84","execution":{"iopub.status.busy":"2023-07-14T09:50:03.875909Z","iopub.execute_input":"2023-07-14T09:50:03.876368Z","iopub.status.idle":"2023-07-14T09:50:12.815120Z","shell.execute_reply.started":"2023-07-14T09:50:03.876315Z","shell.execute_reply":"2023-07-14T09:50:12.813818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## モデルを理解する\n\nモデルのアーキテクチャを理解することは重要である。幸い、`torchinfo`パッケージがあるので、モデルのアーキテクチャを見ることができる。見せてあげよう。","metadata":{"id":"2U_Kti1FRZUE"}},{"cell_type":"code","source":"# もしtorchinfoがなければインストールし、あればインポートする。\ntry: \n    import torchinfo\nexcept:\n    !pip install torchinfo\n    import torchinfo\n    \nfrom torchinfo import summary\n# 入力サイズの例をテストパススルーする \nsummary(model, input_size=[1, 3, IMAGE_WIDTH ,IMAGE_HEIGHT]) ","metadata":{"id":"iDdzcRfVRP2a","outputId":"078bff53-f872-4aff-bdbf-de6487d7fd4a","execution":{"iopub.status.busy":"2023-07-14T09:50:12.817031Z","iopub.execute_input":"2023-07-14T09:50:12.818030Z","iopub.status.idle":"2023-07-14T09:50:26.924114Z","shell.execute_reply.started":"2023-07-14T09:50:12.817983Z","shell.execute_reply":"2023-07-14T09:50:26.922045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 訓練とテストのループ関数を作成する","metadata":{"id":"K3n4unqvRxVW"}},{"cell_type":"code","source":"def train_step(model: torch.nn.Module, \n               dataloader: torch.utils.data.DataLoader, \n               loss_fn: torch.nn.Module, \n               optimizer: torch.optim.Optimizer):\n    # モデルをトレーニングモードに\n    model.train()\n    \n    # 訓練損失と訓練精度の値を設定する\n    train_loss, train_acc = 0, 0\n    \n    # データ・ローダーのデータ・バッチをループする\n    for batch, (X, y) in enumerate(dataloader):\n        # Send data to target device\n        X, y = X.to(device), y.to(device)\n        \n        # Forward pass\n        y_pred = model(X)\n\n        # 損失の計算と累積\n        loss = loss_fn(y_pred, y)\n        train_loss += loss.item() \n\n        # Optimizer zero grad\n        optimizer.zero_grad()\n\n        # Loss backward\n        loss.backward()\n\n        # Optimizer step\n        optimizer.step()\n\n        # 全バッチの精度指標を計算し、蓄積する。\n        y_pred_class = torch.argmax(torch.softmax(y_pred, dim=1), dim=1)\n        train_acc += (y_pred_class == y).sum().item()/len(y_pred)\n\n    # バッチごとの平均損失と精度を得るためにメトリクスを調整する。\n    train_loss = train_loss / len(dataloader)\n    train_acc = train_acc / len(dataloader)\n    return train_loss, train_acc","metadata":{"id":"K0cOVqstRwCZ","execution":{"iopub.status.busy":"2023-07-14T09:50:26.927500Z","iopub.execute_input":"2023-07-14T09:50:26.927990Z","iopub.status.idle":"2023-07-14T09:50:26.939211Z","shell.execute_reply.started":"2023-07-14T09:50:26.927928Z","shell.execute_reply":"2023-07-14T09:50:26.938004Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def test_step(model: torch.nn.Module, \n              dataloader: torch.utils.data.DataLoader, \n              loss_fn: torch.nn.Module):\n    # モデルを評価モードにする\n    model.eval() \n    \n    # 試験損失と試験精度値の設定\n    test_loss, test_acc = 0, 0\n    \n    # 推論コンテキスト・マネージャーをオンにする\n    with torch.inference_mode():\n        # DataLoaderバッチをループする\n        for batch, (X, y) in enumerate(dataloader):\n            # ターゲット・デバイスにデータを送信\n            X, y = X.to(device), y.to(device)\n    \n            # Forward pass\n            test_pred_logits = model(X)\n\n            # 損失の計算と累積\n            loss = loss_fn(test_pred_logits, y)\n            test_loss += loss.item()\n            \n            # 精度の計算と蓄積\n            test_pred_labels = test_pred_logits.argmax(dim=1)\n            test_acc += ((test_pred_labels == y).sum().item()/len(test_pred_labels))\n            \n    # メトリクスを調整し、バッチごとの平均損失と精度を得る \n    test_loss = test_loss / len(dataloader)\n    test_acc = test_acc / len(dataloader)\n    return test_loss, test_acc","metadata":{"id":"lAxEiSgWS3rv","execution":{"iopub.status.busy":"2023-07-14T09:50:26.940769Z","iopub.execute_input":"2023-07-14T09:50:26.941142Z","iopub.status.idle":"2023-07-14T09:50:26.953895Z","shell.execute_reply.started":"2023-07-14T09:50:26.941102Z","shell.execute_reply":"2023-07-14T09:50:26.952797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm.auto import tqdm\n\n# トレーニングおよびテストステップに必要な各種パラメーターを取り込む\ndef train(model: torch.nn.Module, \n          train_dataloader: torch.utils.data.DataLoader, \n          test_dataloader: torch.utils.data.DataLoader, \n          optimizer: torch.optim.Optimizer,\n          loss_fn: torch.nn.Module = nn.CrossEntropyLoss(),\n          epochs: int = 5):\n    \n    # 空の結果辞書を作成する\n    results = {\"train_loss\": [],\n        \"train_acc\": [],\n        \"test_loss\": [],\n        \"test_acc\": []\n    }\n    \n    # トレーニングステップとテストステップをエポック数分ループする。\n    for epoch in tqdm(range(epochs)):\n        train_loss, train_acc = train_step(model=model,\n                                           dataloader=train_dataloader,\n                                           loss_fn=loss_fn,\n                                           optimizer=optimizer)\n        test_loss, test_acc = test_step(model=model,\n            dataloader=test_dataloader,\n            loss_fn=loss_fn)\n        \n        print(\n            f\"Epoch: {epoch+1} | \"\n            f\"train_loss: {train_loss:.4f} | \"\n            f\"train_acc: {train_acc:.4f} | \"\n            f\"test_loss: {test_loss:.4f} | \"\n            f\"test_acc: {test_acc:.4f}\"\n        )\n\n        # 結果辞書の更新\n        results[\"train_loss\"].append(train_loss)\n        results[\"train_acc\"].append(train_acc)\n        results[\"test_loss\"].append(test_loss)\n        results[\"test_acc\"].append(test_acc)\n\n    # エポック終了時に塗りつぶした結果を返す\n    return results","metadata":{"id":"uhTD1INIS_UA","execution":{"iopub.status.busy":"2023-07-14T09:50:26.955811Z","iopub.execute_input":"2023-07-14T09:50:26.956320Z","iopub.status.idle":"2023-07-14T09:50:26.970290Z","shell.execute_reply.started":"2023-07-14T09:50:26.956281Z","shell.execute_reply":"2023-07-14T09:50:26.969233Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## モデルの訓練と評価\nここまでで、トレーニングステップとテストステップを作成した。これらのステップを使ってモデルをトレーニングする準備が整いました。時間を表示する：","metadata":{}},{"cell_type":"code","source":"torch.manual_seed(42) \ntorch.cuda.manual_seed(42)\n\n# エポック数の設定\nNUM_EPOCHS = 5\n\n# 損失関数とオプティマイザの設定\nloss_fn = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(params=model.parameters(), lr=1e-3)\n\n# タイマー開始\nfrom timeit import default_timer as timer \nstart_time = timer()\n\n# Train model_0 \nmodel_results = train(model=model,\n                      train_dataloader=train_dataloader_augmented,\n                      test_dataloader=test_dataloader_augmented,\n                      optimizer=optimizer,\n                      loss_fn=loss_fn,\n                      epochs=NUM_EPOCHS)\n\n# タイマーを終了し、かかった時間を出力する。\nend_time = timer()\nprint(f\"Total training time: {end_time-start_time:.3f} seconds\")","metadata":{"id":"n8K6e7DeTShp","outputId":"af4f43fc-5a21-4214-e655-91b99396e153","execution":{"iopub.status.busy":"2023-07-14T09:50:26.971975Z","iopub.execute_input":"2023-07-14T09:50:26.972428Z","iopub.status.idle":"2023-07-14T10:00:16.038046Z","shell.execute_reply.started":"2023-07-14T09:50:26.972389Z","shell.execute_reply":"2023-07-14T10:00:16.036658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## モデルの損失曲線をプロットする\n\nモデルの性能を理解するために、損失と精度の値を可視化してみましょう。","metadata":{"id":"PaUumQA9jZhh"}},{"cell_type":"code","source":"def plot_loss_curves(results):\n  \n    results = dict(list(model_results.items()))\n\n    # 結果辞書の損失値を取得する（トレーニングとテスト）\n    loss = results['train_loss']\n    test_loss = results['test_loss']\n\n    # 結果辞書（トレーニングとテスト）の精度値を取得する。\n    accuracy = results['train_acc']\n    test_accuracy = results['test_acc']\n\n    # Figure out how many epochs there were\n    epochs = range(len(results['train_loss']))\n\n    # Setup a plot \n    plt.figure(figsize=(15, 7))\n\n    # Plot loss\n    plt.subplot(1, 2, 1)\n    plt.plot(epochs, loss, label='train_loss')\n    plt.plot(epochs, test_loss, label='test_loss')\n    plt.title('Loss')\n    plt.xlabel('Epochs')\n    plt.legend()\n\n    # Plot accuracy\n    plt.subplot(1, 2, 2)\n    plt.plot(epochs, accuracy, label='train_accuracy')\n    plt.plot(epochs, test_accuracy, label='test_accuracy')\n    plt.title('Accuracy')\n    plt.xlabel('Epochs')\n    plt.legend();","metadata":{"id":"WevAWGI_kLf4","execution":{"iopub.status.busy":"2023-07-14T10:00:16.039962Z","iopub.execute_input":"2023-07-14T10:00:16.041094Z","iopub.status.idle":"2023-07-14T10:00:16.053687Z","shell.execute_reply.started":"2023-07-14T10:00:16.041045Z","shell.execute_reply":"2023-07-14T10:00:16.052553Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_loss_curves(model_results)","metadata":{"id":"3uS-wF6wmH0B","outputId":"aca58bb7-7f47-4965-ca12-b14d216ba74d","execution":{"iopub.status.busy":"2023-07-14T10:00:16.055229Z","iopub.execute_input":"2023-07-14T10:00:16.055653Z","iopub.status.idle":"2023-07-14T10:00:16.713102Z","shell.execute_reply.started":"2023-07-14T10:00:16.055611Z","shell.execute_reply":"2023-07-14T10:00:16.712087Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"Prediction\"></a>\n# <p style=\"background-color:coral;font-family:newtimeroman;font-size:150%;color:white;text-align:center;border-radius:20px 20px;\"><b>Make a Prediction</b></p>","metadata":{"id":"i4S-9BpBnhM6"}},{"cell_type":"code","source":"# Choose a image.\ncustom_image_path = \"/kaggle/input/cat-and-dog/test_set/test_set/dogs/dog.4001.jpg\"\n\nimport torchvision\n# Load in custom image and convert the tensor values to float32\ncustom_image = torchvision.io.read_image(str(custom_image_path)).type(torch.float32)\n\n# Divide the image pixel values by 255 to get them between [0, 1]\ncustom_image = custom_image / 255. \n\n# Print out image data\nprint(f\"Custom image tensor:\\n{custom_image}\\n\")\nprint(f\"Custom image shape: {custom_image.shape}\\n\")\nprint(f\"Custom image dtype: {custom_image.dtype}\")","metadata":{"id":"c2Cke99ToEZr","outputId":"bc388d6e-6d02-4811-c807-f7fe4036331f","execution":{"iopub.status.busy":"2023-07-14T10:00:16.715061Z","iopub.execute_input":"2023-07-14T10:00:16.715986Z","iopub.status.idle":"2023-07-14T10:00:16.737687Z","shell.execute_reply.started":"2023-07-14T10:00:16.715936Z","shell.execute_reply":"2023-07-14T10:00:16.736594Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"custom_image_transform = transforms.Compose([\n    transforms.Resize(IMAGE_SIZE),\n])\n\n# Transform target image\ncustom_image_transformed = custom_image_transform(custom_image)\n\n# Print out original shape and new shape\nprint(f\"Original shape: {custom_image.shape}\")\nprint(f\"New shape: {custom_image_transformed.shape}\")","metadata":{"id":"63uu57_JowXg","outputId":"f6d63ccf-b0cf-402d-a97f-bd14f0766112","execution":{"iopub.status.busy":"2023-07-14T10:00:16.740371Z","iopub.execute_input":"2023-07-14T10:00:16.741090Z","iopub.status.idle":"2023-07-14T10:00:16.758274Z","shell.execute_reply.started":"2023-07-14T10:00:16.741047Z","shell.execute_reply":"2023-07-14T10:00:16.756929Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval()\nwith torch.inference_mode():\n    # Add an extra dimension to image\n    custom_image_transformed_with_batch_size = custom_image_transformed.unsqueeze(dim=0)\n    \n    # Print out different shapes\n    print(f\"Custom image transformed shape: {custom_image_transformed.shape}\")\n    print(f\"Unsqueezed custom image shape: {custom_image_transformed_with_batch_size.shape}\")\n    \n    # Make a prediction on image with an extra dimension\n    custom_image_pred = model(custom_image_transformed.unsqueeze(dim=0).to(device))","metadata":{"id":"62tSSQHro-_V","outputId":"f44c1e06-cb55-436b-e235-fec914c0d9b9","execution":{"iopub.status.busy":"2023-07-14T10:00:16.760255Z","iopub.execute_input":"2023-07-14T10:00:16.760803Z","iopub.status.idle":"2023-07-14T10:00:16.772435Z","shell.execute_reply.started":"2023-07-14T10:00:16.760756Z","shell.execute_reply":"2023-07-14T10:00:16.771023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"custom_image_pred","metadata":{"id":"V8ie7MCupO3H","outputId":"7f0d8adb-3566-47af-fa4f-b84e8777e5fa","execution":{"iopub.status.busy":"2023-07-14T10:00:16.774221Z","iopub.execute_input":"2023-07-14T10:00:16.775369Z","iopub.status.idle":"2023-07-14T10:00:16.788331Z","shell.execute_reply.started":"2023-07-14T10:00:16.775177Z","shell.execute_reply":"2023-07-14T10:00:16.784928Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ロジット→予測確率→予測ラベルと変換しよう\n# 予測ロジットを出力する\nprint(f\"Prediction logits: {custom_image_pred}\")\n\n# ロジットを予測確率に変換（多クラス分類にはtorch.softmax()を使用）\ncustom_image_pred_probs = torch.softmax(custom_image_pred, dim=1)\nprint(f\"Prediction probabilities: {custom_image_pred_probs}\")\n\n# 予測確率を予測ラベルに変換する\ncustom_image_pred_label = torch.argmax(custom_image_pred_probs, dim=1)\nprint(f\"Prediction label: {custom_image_pred_label}\")","metadata":{"id":"g7K5cDOqpYqM","outputId":"1e004d6c-7338-46b5-a44b-e13bd6da7d1f","execution":{"iopub.status.busy":"2023-07-14T10:00:16.795040Z","iopub.execute_input":"2023-07-14T10:00:16.795466Z","iopub.status.idle":"2023-07-14T10:00:16.806762Z","shell.execute_reply.started":"2023-07-14T10:00:16.795433Z","shell.execute_reply":"2023-07-14T10:00:16.805192Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It is time to find the predicted label.","metadata":{}},{"cell_type":"code","source":"custom_image_pred_class = class_names[custom_image_pred_label.cpu()]\ncustom_image_pred_class","metadata":{"id":"BOS4bcrVpfej","outputId":"a7f17784-9d01-43ce-bd55-c3333e22206d","execution":{"iopub.status.busy":"2023-07-14T10:00:16.808977Z","iopub.execute_input":"2023-07-14T10:00:16.809704Z","iopub.status.idle":"2023-07-14T10:00:16.817915Z","shell.execute_reply.started":"2023-07-14T10:00:16.809657Z","shell.execute_reply":"2023-07-14T10:00:16.816781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot custom image\nplt.imshow(custom_image.permute(1, 2, 0)) # need to permute image dimensions from CHW -> HWC otherwise matplotlib will error\nplt.title(f\"Image shape: {custom_image.shape}\")\nplt.axis(False);","metadata":{"id":"XC8l1iqApxXX","outputId":"8bceeaf0-b2aa-42d1-cb6a-3fd01a40d702","execution":{"iopub.status.busy":"2023-07-14T10:00:16.819812Z","iopub.execute_input":"2023-07-14T10:00:16.820793Z","iopub.status.idle":"2023-07-14T10:00:17.084611Z","shell.execute_reply.started":"2023-07-14T10:00:16.820747Z","shell.execute_reply":"2023-07-14T10:00:17.083653Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"参考：https://www.kaggle.com/code/tirendazacademy/cats-dogs-classification-with-pytorch","metadata":{}}]}