Day23 - 人工智慧 IV.

斜槓學習 – 零基礎成為 AI 解夢大師秘笈

本文作者:Ovien

系列文章簡介

自由團隊將從0到1 手把手教各位讀者學會(1)Python基礎語法、(2)Python Web 網頁開發框架 – Django 、(3)Python網頁爬蟲 – 周易解夢網、(4)Tensorflow AI語言模型基礎與訓練 – LSTM、(5)實際部屬AI解夢模型到Web框架上。

學習資源

AI . FREE Team 讀者專屬福利 → Python Basics 免費學習資源

上篇教學介紹了pytorch的基礎教學,這次我們會帶來更深層類神經網路實作與應用。

實作 (一)

導入套件

%matplotlib inline
%config Inline
Backend.figure_format = 'retina'
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
import warningswarnings.filterwarnings('ignore')

from datetime import datetime
from matplotlib.colors import ListedColormap
from sklearn.datasets import make_classification, make_moons, make_circles
from sklearn.metrics import confusion_matrix, classification_report, mean_squared_error, mean_absolute_error, r2_score
from sklearn.linear_model import LogisticRegression
from sklearn.utils import shuffle
from keras.utils.np_utils import to_categorical
from sklearn.preprocessing import StandardScaler, LabelEncoder, OneHotEncoder, MinMaxScaler
from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold, KFold
from tqdm import tqdm

這次實作的套件有點多,也有一些之前教學不曾看過的套件,待會實作過程會一一細講。

資料集(Dataset)

一開始先宣告我們的資料集,還有視覺化資料的function,可以把X跟y print 出來看一下,可以發現X其實就是座標,而y就是0跟1

def plot_data(X, y, figsize=None):  
    if not figsize:
        figsize = (8, 6)
    plt.figure(figsize=figsize)
    plt.plot(X[y==0, 0], X[y==0, 1], 'or', alpha=0.5, label=0)
    plt.plot(X[y==1, 0], X[y==1, 1], 'ob', alpha=0.5, label=1)
    plt.xlim((min(X[:, 0])-0.1, max(X[:, 0])+0.1))
    plt.ylim((min(X[:, 1])-0.1, max(X[:, 1])+0.1))
    plt.legend()X, y = make_classification(n_samples=1000, n_features=2, n_redundant=0, 
                           n_informative=2, random_state=50, n_clusters_per_class=1)
plot_data(X, y)

這是一個普通的binary classfication task,有很多的黃點與綠點分佈在上方圖中,若要透過機器學習做分類的判斷(羅吉斯回歸 - Logistic Regression) 如下:

lr = LogisticRegression()
lr.fit(X, y)
print('LR coefficients:', lr.coef_)
print('LR intercept:', lr.intercept_)

plot_data(X, y)

limits = np.array([-2, 2])
boundary = -(lr.coef_[0][0] * limits + lr.intercept_[0]) / lr.coef_[0][1]
print(boundary)
plt.plot(limits, boundary, "r-", linewidth=2)
plt.show()

模型 (Model)

我們宣告一個叫torchnetwork1繼承我們的pytorchnn.Moudle,下面的forward就是nn.Moudle中的向前傳播的部份。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
from torch.utils import data

class torchnetwork1(nn.Module):
  def __init__(self,input_size,output_size):
    super(torchnetwork1, self).__init__()
    self.layer1 = nn.Linear(input_size , output_size)
    self.activation = nn.Sigmoid()
  def forward(self,input):
    out = self.layer1(input)
    out = self.activation(out) 
    return out

這裡的模型向前傳播如果視覺化就會像這樣:

Epoch = 5
learning_rate = 0.01
nnmodel = torchnetwork1(2,1)
loss_fn = torch.nn.MSELoss()

X = torch.FloatTensor(X)
y = torch.FloatTensor(y)

因為我們的座標是 x,y,輸出 y 是1,為torchnetwork1(2,1),loss function使用的是之前使用過的MSELoss(),再來將我們的答案(y)與輸入(X)轉換為浮點數。

訓練 (Training)

def binary_acc(y_pred, y_test):
  y_pred_tag = [round(i) for i in y_pred]
  correct_results_sum = 0
  
  for i,j in zip(y_pred_tag,y_test):
    if i == j:
      correct_results_sum+=1
      
  acc = correct_results_sum/len(y_test)
  acc = acc * 100
  
  return acc

def optimize(learning_rate ,para_list,loss):
  for para in para_list:  
    if not para.grad is None:
      para.grad.zero_()
  loss.backward()  
  
  for i in para_list:
    i.data -= learning_rate * i.grad
    
def fit(Epoch,model,X,y,lr):
  loss_score = []
  acc_score = []
  
    for i in range(Epoch):
      print()
      print(f'Start the {i} epoch')
      print()
      y_pred_all = []
      y_true_all = []    
      
      for i,j in zip(X,y): 
          y_pred = model(i)
          loss = loss_fn(y_pred, j)

          y_pred_all.append(y_pred.item())
          y_true_all.append(j.item())
          para_list = [] 
          
          for para in model.parameters():
            para_list.append(para)
          optimize(lr ,para_list,loss)
          
      loss_score.append(loss.item())
      acc_score.append(binary_acc(y_pred_all,y_true_all))
      print(f'loss score : {loss.item()}')
      print(f'accuracy : {binary_acc(y_pred_all,y_true_all)}') 
  return acc_score,loss_score

先解釋第一個 function 的功能,把預測與答案做一個accuracy的計算,有 follow 我們人工智慧前兩篇文章的讀者們應該都對這個 function 相當熟悉,第二個 function 是優化器(optimizer),其實就是上次講的權重更新的部分,只是我把過程寫成一個 function;最後一個function 為:向前、向後傳播的部分。

這裡直接呼叫fit這個function

acc_score,loss_score = fit(Epoch,nnmodel,X,y,learning_rate)

查看結果

Start the 0 epoch
loss score : 0.014776031486690044
accuracy : 89.0

Start the 1 epoch
loss score : 0.005984860938042402
accuracy : 99.9

Start the 2 epoch
loss score : 0.0034086359664797783
accuracy : 99.9

Start the 3 epoch
loss score : 0.002259287517517805
accuracy : 99.9

Start the 4 epoch
loss score : 0.0016338723944500089
accuracy : 99.9

可以發現很輕鬆就可以到達99%了,剩下的那 1%應該是 noise

那我們視覺化一下預測的結果

def plot_decision_boundary(model, X, y, figsize=(9, 6)):
    amin, bmin = X.min(axis=0).values - 0.1
    amax, bmax = X.max(axis=0).values + 0.1
    hticks = np.linspace(amin, amax, 101)
    vticks = np.linspace(bmin, bmax, 101)
    
    aa, bb = np.meshgrid(hticks, vticks)    
    ab = np.c_[aa.ravel(), bb.ravel()]    
    ab = torch.FloatTensor(ab)    
    c = [[model(i).cpu().item()] for i in ab]    
    c = np.array(c)    
    cc = c.reshape(aa.shape)    
    cm = plt.cm.RdBu
    cm_bright = ListedColormap(['#FF0000', '#0000FF'])
    
    fig, ax = plt.subplots(figsize=figsize)
    contour = plt.contourf(aa, bb, cc, cmap=cm, alpha=0.8)
    
    ax_c = fig.colorbar(contour)
    ax_c.set_label("$P(y = 1)$")
    ax_c.set_ticks([0, 0.25, 0.5, 0.75, 1])
    
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cm_bright)
    plt.xlim(amin, amax)
    plt.ylim(bmin, bmax)

plot_decision_boundary(nnmodel, X, y)

觀察結果

這裡我們再用classification_report這個sklearn的套件來觀察數值

更複雜的資料集

X, y = make_moons(n_samples=1000, noise=0.05, random_state=2020)
plot_data(X, y)

使用剛剛的模型直接進訓練

Epoch = 5
moon_model = torchnetwork1(2,1)
loss_fn = torch.nn.MSELoss()

X = torch.FloatTensor(X)
y = torch.FloatTensor(y)
acc_score_moon,loss_score_moon = fit(Epoch,moon_model,X,y,0.001)

觀察accuracy

Start the 0 epoch
loss score : 0.14853742718696594
accuracy : 53.900000000000006

Start the 1 epoch
loss score : 0.1247144266963005
accuracy : 60.199999999999996

Start the 2 epoch
loss score : 0.10635500401258469
accuracy : 64.5

Start the 3 epoch
loss score : 0.09201527386903763
accuracy : 67.7

Start the 4 epoch
loss score : 0.08064766228199005
accuracy : 70.0

再看一下預測出的邊界

plot_decision_boundary(moon_model, X, y)

y_pred = [round(moon_model(i).cpu().item()) for i in X]
print(classification_report(y, y_pred))

會發現很多都預測錯誤,在同樣的模型條件下,此簡易的類神經模型在線性的 task 發揮很出色,在這個上下半月型的資料集(簡稱月亮資料) 卻明顯失去預測能力;因此我們可以得知,在越複雜的資料集,我們所要做的就是讓模型變得更複雜、更深層,讓模型能有更多的參數可以來作更深一層的判斷。

讓我們實際來感受更深度學習 (Deep Learning) 的威力吧!

月亮資料實作

模型

class moon_nn(nn.Module):
  def __init__(self,input_size,hidden_size,hidden_size2,output_size):  
    super(moon_nn, self).__init__()    
    self.layer1 = nn.Linear(input_size , hidden_size)    
    self.layer2 = nn.Linear(hidden_size , hidden_size2)    
    self.layer3 = nn.Linear(hidden_size2 , output_size)    
    self.tanh = nn.Tanh()    
    self.activation = nn.Sigmoid()
    
  def forward(self,input):  
    out = self.tanh(self.layer1(input))  
    out = self.tanh(self.layer2(out))    
    out = self.activation(self.layer3(out))    
    return out

超參數以及loss function

Epoch = 20
deeper_moon_model = moon_nn(2,4,2,1)
loss_fn = torch.nn.BCELoss()

X = torch.FloatTensor(X)
y = torch.FloatTensor(y)

Training

acc_score,loss_score = fit(Epoch,deeper_moon_model,X,y,learning_rate)

觀察輸出

Start the 0 epoch
loss score : 0.21858777105808258
accuracy : 75.2

Start the 1 epoch
loss score : 0.06843582540750504
accuracy : 87.0

Start the 2 epoch
loss score : 0.044113319367170334
accuracy : 88.2

Start the 3 epoch
loss score : 0.03773176297545433
accuracy : 88.3

Start the 4 epoch
loss score : 0.0352737158536911
accuracy : 88.4

Start the 5 epoch
loss score : 0.033938392996788025
accuracy : 88.4

.
.
.

Start the 29 epoch
loss score : 0.0010626595467329025
accuracy : 100.0

會發現其實要訓練比較多輪,要等損失函數收斂到一定的地步,就會精確了!

觀察決策邊界

plot_decision_boundary(deeper_moon_model, X, y)

太強大了!這個模型

y_pred = [round(deeper_moon_model(i).cpu().item()) for i in X]
print(classification_report(y, y_pred))

參數越多的模型、神經網路越深層,在訓練的時候也會耗比較多的時間,同時也會需要更多運算資源。

GPU

現在的人工智慧可以有那麼好的發展,有很大的原因要歸功於GPU的平行運算

國外這位高手使用了GPU參加了當時一個影像辨識的比賽,他只用了3GBRam的GPU就突破了當時許多主流如SVM、XGBoost 等強大機器學習演算法,從此一戰成名,開創了現在人工智慧的興起的契機。

這裡我們使用colab並且選取GPU

接下來使用!nvidia-smi來檢查被分配到的GPU規格以及容量。

Sun Oct  4 13:10:15 2020       +-----------------------------------------------------------------------------+| NVIDIA-SMI 455.23.05    Driver Version: 418.67       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |
| N/A   69C    P0    32W /  70W |    899MiB / 15079MiB |      0%      Default |
|                               |                      |                 ERR! |
+-------------------------------+----------------------+----------------------+
                                                                               +-----------------------------------------------------------------------------+| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

好了之後,我們必須將model宣告並丟到GPU,因為待會我們的向前傳播和向後傳播都要在GPU裡面做,因此包括data(待會兒也會做同樣的事)、model都必須一併丟到GPU。

from torch.utils.data import Dataset, DataLoader,TensorDataset
train_dataset = TensorDataset(X,y)
train_loader = data.DataLoader(train_dataset,batch_size=32, shuffle = True)
use_cuda = torch.cuda.is_available()
deeper_moon_model = moon_nn(2,4,2,1)
if use_cuda:
    print('use_cuda')
    deeper_moon_model = deeper_moon_model.cuda(0)

cudapytorch默認在GPU運行的指令只要看到.cuda()就代表這個變數將在GPU裡運行。

批次訓練集

data.DataLoader是一個Pytorch的一個很重要的API,主要功能是將資料集可以一組一組的順利丟進模型,因為資料集如果是一筆一筆的就無法平行,要將資料整理過後,如下圖:

因此,上面的程式碼batch_size = 32代表每32筆資料為一組

fit改成GPU運行的版本,這裡我們不和原本CPU的重疊,宣告為另一個名字fit_cuda

def fit_cuda(Epoch,model,loader,lr):
  loss_score = []
  acc_score = []  for i in range(Epoch):
      print()
      print(f'Start the {i} epoch')
      print()
      y_pred_all = []
      y_true_all = []      for i,j in loader: 
          optimize.zero_grad()
          i = i.cuda(0) if use_cuda else i
          j = j.cuda(0) if use_cuda else j
          y_pred = model(i)
          loss = loss_fn(y_pred, j)
          loss.backward()
          y_pred_all.extend(y_pred.squeeze(1).detach().cpu().numpy())
          y_true_all.extend(j.detach().cpu().numpy())
          optimize.step()
      loss_score.append(loss.cpu().item())
      acc_score.append(binary_acc(y_pred_all,y_true_all))
      print(f'loss score : {loss.cpu().item()}')
      print(f'accuracy : {binary_acc(y_pred_all,y_true_all)}')  
  return acc_score,loss_score

可以看到,除了向前、向後傳播外的東西,包括計算loss、accuracy我們都使用.cpu()將數據傳回CPU,因為GPU的資源很可貴,這種計算可以較快的通常我們都會丟回CPU。

optimize = torch.optim.Adam(deeper_moon_model.parameters(),lr=0.01)

這裡我們也使用pytorch更方便的API,torch.optim,之前的手動更新是一般的Gradient descent,而這裏我們使用強大的Adam,更新的公式如下:

下篇將使用Keras來建模,大家可以比較一下各自優缺點,再決定要走入哪一個 AI 的開發框架!

想更深入認識 AI . FREE Team ?

自由團隊 官方網站:https://aifreeblog.herokuapp.com/
自由團隊 Github:https://github.com/AI-FREE-Team/
自由團隊 粉絲專頁:https://www.facebook.com/AI.Free.Team/
自由團隊 IG:https://www.instagram.com/aifreeteam/
自由團隊 Youtube:https://www.youtube.com/channel/UCjw6Kuw3kwM_il39NTBJVTg/

文章同步發布於:第十二屆 IT 挑戰賽部落格

(繼續閱讀下一篇教學...)