# 框架(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複製到命令視窗中*

PS 要注意,使用anaconda建立虛擬環境後, pip install時需要轉換至指定的環境再安裝。
# 資料集(DataSet)
一個深度學習模型中,最為重要的就是資料集,一個資料集的好壞會直接影響模型準確度。
為了排除資料庫好壞的問題,我們就直接從kaggle中下載大家廣為人知的[貓狗大戰](https://www.kaggle.com/competitions/dogs-vs-cats-redux-kernels-edition/data)來作範例,一方面是大家對資料集應該都相當的了解,路上隨處可見,另一方面是kaggle應該能確保我們的資料集沒有奇怪的錯誤,像是分類錯誤或是混入錯誤資料。
下載完成後我們得到了一個壓縮檔,解壓縮後我們得到train,test與一個上傳用的csv檔

# 模型(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了。