# 框架(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了。