---
# System prepended metadata

title: CNN 學習筆記2

---

# 框架(Framework)
在開始架設模型之前，我們需要先選擇一個語言以及框架，目前主流的幾個框架包含了Keras、Tensorflow、及PyTorch，各框架之間都有各自的使用者以及優缺點，這邊就不一一贅述，而我之後的筆記我都會以PyTorch為主軸進行，一方面是目前工作上都是以PyTorch為主，另一方面則是我本人對PyTorch實在是不太熟(?，也想藉此機會正式的學習PyTorch的用法，轉換一下過往Keras加上Tensorflow的觀念。而語言的部分想當然爾就是以python為主了，編譯器則是使用jupyter notebook，比較可以階段式的分析code。

PS 不是工作混水摸魚，是專案都是上github找SOTA來做為模型進行訓練，頂多就是微調參數。


# 環境(Environment)
如果你是第一次接觸python，那我強烈建議直接安裝Anaconda，這對於新手來說省掉了相當多環境上的問題，並且他也有內建可創立多個虛擬環境，你可以根據每一個專案的要求去建立新的python版本。 下載連結: [點我](https://www.anaconda.com/download)
接著是CUDA跟cudnn，CUDA是由NVIDIA開發的一個軟硬體整合技術。透過CUDA，你可以使用GPU進行圖像處理以外的運算。
前一章節提到的，CNN中包含了非常多的節點，節點之間的運算其實非常簡單，但是對於一個8核心的CPU來說，他的平行運算能力是遠低於擁有上千個核心的GPU。所以前一張才會提到GPU的出現大幅加快了神經網路的發展。
扯遠了，我們回到安裝的問題，目前網路上已經有很多安裝的[教學文](https://medium.com/ching-i/win10-%E5%AE%89%E8%A3%9D-cuda-cudnn-%E6%95%99%E5%AD%B8-c617b3b76deb)了，作者也有寫linux的安裝方式，大家可以自行參考。
再來就是pytorch的安裝了，我們只需要去pytorch的[官網](https://pytorch.org/)選取我們的環境以及版本，就可以直接透過pip install 安裝我們的pytorch。

*選取當前電腦的配置後，將下面的command複製到命令視窗中*
![](https://hackmd.io/_uploads/rJ-z7Dnp3.png)

PS 要注意，使用anaconda建立虛擬環境後， pip install時需要轉換至指定的環境再安裝。

# 資料集(DataSet)
一個深度學習模型中，最為重要的就是資料集，一個資料集的好壞會直接影響模型準確度。
為了排除資料庫好壞的問題，我們就直接從kaggle中下載大家廣為人知的[貓狗大戰](https://www.kaggle.com/competitions/dogs-vs-cats-redux-kernels-edition/data)來作範例，一方面是大家對資料集應該都相當的了解，路上隨處可見，另一方面是kaggle應該能確保我們的資料集沒有奇怪的錯誤，像是分類錯誤或是混入錯誤資料。

下載完成後我們得到了一個壓縮檔，解壓縮後我們得到train,test與一個上傳用的csv檔
![](https://hackmd.io/_uploads/Byu1iV603.png)


# 模型(Model)
前面也有說過，我使用pytorch的經驗比較少，所以我就隨意從github中找了一篇[Pytorch-CNN-with-cats-and-dogs-](https://github.com/vashiegaran/Pytorch-CNN-with-cats-and-dogs-)來做為參考以及學習，我們也可以從這篇文章中的一些coding與細節來理解pytorch的架構與運作模式。

首先是import我們需要用到的library以及load data
```
import torch
import torch.nn as nn
from tqdm import tqdm
from sklearn.model_selection import train_test_split
from torchvision import transforms
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
from PIL import Image
import glob

# 這邊用以確定我們的裝置是否有GPU可以使用
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu' )
torch.manual_seed(42)

# 讀取train與test中的jpg檔
train_dir = './dataset/train'
test_dir = './dataset/test'
train_list = glob.glob(os.path.join(train_dir,'*.jpg'))
test_list = glob.glob(os.path.join(test_dir, '*.jpg'))
```

接著我們寫一個class，目的是對資料加上轉換為01的分類以及前處理(transforms)
PS 雙底線的method叫做 magic method，相關的使用方法這邊就不多贅述。

```
class dataset(torch.utils.data.Dataset):
    def __init__(self,file_list,transform = None):
        self.file_list=file_list
        self.transform=transform
    
    def __len__(self):
        self.filelength =len(self.file_list)
        return self.filelength
    
    def __getitem__(self,idx):
        img_path =self.file_list[idx]
        img = Image.open(img_path)
        img_transformed = self.transform(img)
        
        #這邊的split要注意是單斜線還是說斜線，這會因環境不同而有所差異
        #建議可以先測試產出的label會是甚麼
        
        label = img_path.split('/')[-1].split('.')[0]
        if label == 'dog':
            label=1
        elif label == 'cat':
            label=0
        return img_transformed,label

```
接著就是呼叫這個function來對我們先前讀取的data做處理
其中的一些參數大家可以自己去研究以及更動，看看模型會不會有更好的表現
```
# 將train 分為 train_list 與 val_list， 比例為8:2
train_list,val_list = train_test_split(train_list , test_size =0.2)

train_transforms =transforms.Compose([
    transforms.Resize((224,224)),
    transforms.TrivialAugmentWide(),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor()
])
# test 與 valid我們就不去做augmentation，會影響模型的驗證
# 所以我們只需要做resize跟轉換為tensor的形式即可。
test_transforms = transforms.Compose([
    transforms.Resize((224,224)),
    transforms.ToTensor()
])

# 接著使用我們剛剛建立的class，並輸入相關的變數，使變數train_data變為dataset。
train_data = dataset(train_list,transform=train_transforms)
test_data = dataset(test_list,transform=test_transforms)
val_data = dataset(val_list,transform=test_transforms)
train_loader = torch.utils.data.DataLoader(dataset = train_data, batch_size = batch_size,shuffle = True)
test_loader = torch.utils.data.DataLoader(dataset = test_data, batch_size = batch_size,shuffle = False)
val_loader = torch.utils.data.DataLoader(dataset = val_data, batch_size = batch_size,shuffle = False)
```

再來就是我們的CNN了，在pytorch我們是用一個nn.Module的Class所搭建，在init中建立我們會用到的sequential(可以當作 nn.Module 的容器)，之後再使用foward去決定我們資料的行進流程。

```
class Cnn(nn.Module):
    def __init__(self):
        super(Cnn,self).__init__()
        
        # Sequential 作為nn的基本容器
        self.layer1 = nn.Sequential(
            #使用64個過濾器(filter)對輸入資料進行卷積，通常層數月糕模醒表現越好
            #但也會影響模型運算的複雜度
            nn.Conv2d(3,64,kernel_size=3,padding=1),
            #激活函數，一般加在卷積層之後
            nn.ReLU(),
            #標準化
            nn.BatchNorm2d(64),
            #池化層
            nn.MaxPool2d(2)
        )
        
        self.layer2 = nn.Sequential(
            nn.Conv2d(64,512,kernel_size=3,padding=1),
            nn.ReLU(),
            nn.BatchNorm2d(512),
            nn.MaxPool2d(2)
        
        )
        self.layer3 = nn.Sequential(
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.ReLU(),
            nn.BatchNorm2d(512),
            nn.MaxPool2d(2)
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(in_features=512*3*3, out_features=2))
            # 這邊的512代表的是前面一層的通道數 後面的3*3則使目前的影像大小
        
    def forward(self,x):
        out =self.layer1(x)
        out =self.layer2(out)
        out =self.layer3(out)
        out =self.layer3(out)
        out =self.layer3(out)
        out =self.layer3(out)
        out =self.classifier(out)
        return out
    
model = Cnn().to(device)
```

接著我們用torchinfo中的summary看一下我們模型的架構

```
from torchinfo import summary
# do a test pass through of an example input size 
summary(model, input_size=[1, 3, 224 ,224]) 
```

到目前為止我們的前處理以及模型架構都已經完成了，下一章我們就要來寫training的function了。