以Python撰寫 AI模型框架


 以Python撰寫 AI模型框架

 by 高煥堂

 

1. 前言:

  在AI(人工智慧)方面,由於當今的機器學習本質是一種<大數據相關性支撐的>歸納性推理。軟體框架的復用(Reuse)性愈高,對於應用開發的幫助愈大。因此,在AI領域里,軟體框架魅力將會大放異彩。在本文里,是基於最簡單的Perceptron模型來闡述如何分析、設計及實作一個框架和API。在本節里,將優化這個AI模型,讓它從線性分類,提升到非線性分類,可以展現更高的智慧,也適用於更廣的范圍。而且將把最典型的Sigmoid激勵函數,添加到上一節所撰寫的Percentron基類里。由於這Sigmoid激勵函數適合於二元分類(Binary classification)的情境,包括線性和非線性二元分類問題。所以將這框架取名為:BCModelFarmework。期待充分發揮框架威力、支援您的商業模式,邁向輝煌騰達之道。

2.  Python框架設計:從需求到實作

     大家都知道,人們的需求都是善變的,所以API的內涵也是隨時會改變的。在上一篇文章里,其API里只定義了一個getLR()函數。在本節里,就來替API增添多函數。

2.1  親自演練:需求分析  

   一旦客人的需求有所改變了,可能會不斷擴充API。例如,當客人來了之后,才會告知下述5項資料:學習率(Learning Rate)、要訓練幾回合(Epoch)、訓練資料集X[] ,以及期望值T[]等。就能依據框架的需求時間軸概念,來繪出下圖:

     

   1、框架需求分析

   從這圖可以看出來,這5項資料都是寫在子類里,而框架必須透過API來向子類索取這5項資料值。一旦框架取得了這些資料,就能展開AI模型的訓練工作了。現在,就依據上圖的思維而繪制出類別圖,如下所示:

    

    2、此范例的類別圖

   在這PerFramewor02框架里,含有一個Perceptron基類,讓myNN子類來繼承之。也就是由子類來實作Perceptron里的抽象函數。

2.2  親自演練:實現框架  

  現在,就以Python來實現這個框架,如下:

 # PerFramework02.py

import numpy as np
from abc import ABC, abstractmethod
class Perceptron(ABC):
    def __init__(self):
        self.learningRate = self.getLR()
        self.epoches = self.getEpoch()
        self.B = 0;
        self.W = 0;
        self.correctRate = np.zeros([30])
    def train(self):
        X = self.getX()
        T = self.getT()
        len = X.size
 
       for i in range(self.epoches):

           errorCount = 0;

            for j in range(len):

                error = T[j] - self.predict(X[j])
                update = self.learningRate * error
                # 修正W和B
                self.W += update * X[j]

                self.B += update
                # 累積錯誤次數
                if (error != 0):

                     errorCount = errorCount + 1
         # 算出正確率
         self.correctRate[i] = 1 - errorCount * 1.0 / len

    def predict(self, x):
        y = x * self.W + self.B
        z = self.getZ(y)
        return z
    def printCR(self, idx):
        print("#:", idx, " ", self.correctRate[idx])

   @abstractmethod

   def getLR():    pass
  @abstractmethod

   def getEpoch():  pass
  @abstractmethod

   def getZ(y):  pass
  @abstractmethod

   def getX():  pass
@abstractmethod

  def getT(): pass

這個時候,API已經擴大了,總共包含了5個抽
象函數:getLR()、getEpoch()、getZ()、getX()和getT()。在程式執行時,基類就會透過抽象函數的機制,來呼叫子類的函數,來取得上述的5項資料。接着,就來撰寫App如下:

# Ex1-01.py

import numpy as np
from abc import ABC, abstractmethod
from PerFramework02 import Perceptron
class myNN(Perceptron):
    def __init__(self):
        super().__init__()
    def getLR(self):
        return 0.1
    def getEpoch(self):
        return 30
    def getZ(self, y):
        if (y >= 0):
            return 1
        else:
            return 0
    def getX(self):
        dx = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
        return dx
    def getT(self):
        dt = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0])
        return dt
# ----主程式部分---------------------------------------
p = myNN()

p.train()
p.printCR(0)
p.printCR(1)
p.printCR(2)
p.printCR(21)
p.printCR(22)
p.printCR(23)
p.printCR(24)
p.printCR(25)
p.printCR(26)

  這是一支能在Python環境里執行的App,內含兩個類別,其中的myNN繼承框架里的Perceptron基類。其中的主程式(部分)里有個指令:

        p = myNN()

   誕生一個myNN的物件,並且呼叫到Perceptron基類的建構式(Constructor):

     class Perceptron(ABC):

         def __init__(self):
            self.learningRate = self.getLR()
            self.epoches = self.getEpoch()
      # …………..

       # ………….

   然后,開始執行這建構式里的指令,首先呼叫到getLR()函數,轉而呼叫子類myNN里的getLR()函數,要求它傳回來<學習率>的值。接着,就呼叫到getEpoch()函數,轉而呼叫子類myNN里的getEpoch()函數,要求它傳回來訓練的回合數。然后返回主程式部分,執行下一個指令:

     p.train()

  於是,這指令就呼叫Perceptron基類的train()函數,並開始執行train()函數里的指令:

    def train(self):

        X = self.getX()

        T = self.getT()

就呼叫到getX()函數了,並轉而呼叫myNN里的getX()函數,要求它傳回來訓練資料X[]的內容。接着呼叫getT()函數了,然而這getT()是抽象函數,其指令是實作於myNN子類里,於是就轉而呼叫myNN里的getT()函數,要求它傳回來期望資料T[]的內容。然后展開訓練的動作,並輸出結果如下:

 

    一開始,在第#0~#22回合,其預測的正確率比較低。然而愈多回合的訓練,其正確率就逐漸上升了。到了第#23回合之后,其預測的正確率就接近於1.0(即達到100%)了。以上展現了框架API的不斷成長過程,而框架與App之間的互動也更頻繁了。

 

3.  優化模型:使用Sigmoid激勵函數 

 3.1  寫出一支App:使用Sigmoid函數  

   在上一節里,是基於最簡單的Perceptron模型來闡述如何分析、設計及實作一個框架和API。在本節里,將優化這個AI模型,讓它從線性分類,提升到非線性分類,可以展現更高的智慧,也適用於更廣的范圍。本節的范例里,將把最典型的Sigmoid激勵函數,添加到上一節所撰寫的Percentron基類里。由於這Sigmoid激勵函數適合於二元分類(Binary classification)的情境,包括線性和非線性二元分類問題。所以將這框架取名為:BCModelFarmework。如下述Python程式碼:

   #BCModelFramework.py

  from abc import ABC, abstractmethod

import numpy as np
class
Perceptron(ABC):

    def __init__(self):
        self.learningRate = self.onLearningRate()
        self.epoches = self.onEpoch()
        dw = self.onW()
        self.W = dw[0]
        self.B = dw[1]
        self.correctRate = np.zeros(self.epoches)

    def train(self):
        X = self.onX()
        T = self.onT()
        epoches = self.correctRate.size
        len = np.size(X, 0)
        for i in range(epoches):
            errorCount = 0
            for j in range(len):
                z = self.predict(X[j])
                loss = T[j] - z
                delta = 2 * self.deriv(z) * loss
                update = self.learningRate * delta
                # 修正W和B
                self.W += update * X[j]
                self.B += update
                # 累計錯誤次數
                if (z >= 0.5):
                    v = 1
                else:
                    v = 0
                if ((T[j] - v) != 0):
                    errorCount = errorCount + 1
            # 算出正確率
            self.correctRate[i] = 1 - errorCount * 1.0 / len
    def predict(self, X):
        y = np.dot(X, self.W) + self.B
        z = self.sigmoid(y)
        return z
    def sigmoid(self, y):
        z = float(1 / (1 + np.exp(-y)))
        return z
    def deriv(self, z):
        d = z * (1 - z)
        return d
    @abstractmethod
    def onLearningRate():   pass
   @abstractmethod
    def onEpoch():    pass
   @abstractmethod
     def onX():    pass
   @abstractmethod
    def onT():     pass
   @abstractmethod
    def onW():     pass
  def getW(self):
        return self.W
    def getCR(self):
          return self.correctRate

這個BCModelFramework框架里,含由一個Perceptron基類,它提供的API總共包含5個抽象函數:onLearningRate()、onEpoch()、onX()、onT()和onW()。接着,就可基於這個框架來快速開發App了,如下述的程式碼:

# Ex12-07.py

import numpy as np
from abc import ABC, abstractmethod
from BCModelFramework import Perceptron
class myNN(Perceptron):
    def __init__(self):
        super().__init__()
    def onLearningRate(self):
        return 0.1
    def onEpoch(self):
        return 80
    def onX(self):
        dx = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
        return dx
    def onT(self):
        dt = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0])
        return dt
    def onW(self):
        self.dw = np.array([[0.0], 0.0])
        return self.dw
    def printWeight(self):
        print(self.getW())
    def printCR(self, idx):
        cr = self.correctRate
        print(", idx, "回合:  ", str(int(cr[idx] * 100)), "%")
# -----------------主程式部分-------------------------------------------
p = myNN()
p.train()
print(正確率:")
p.printCR(0)
p.printCR(1)
p.printCR(10)
p.printCR(20)
p.printCR(30)
p.printCR(40)
p.printCR(50)
p.printCR(60)
p.printCR(70)

 這是一支能在Python環境里執行的App,內含兩個類別。其中的myNN繼承框架里的Perceptron基類,如下:

  

     圖3、框架支援App的快速開發

 

這個簡單范例里,輸出層神經元的主要計算公式是:y= X*W+B。表現於指令:

   def predict(self, X):

       y = np.dot(X, self.W) + self.B
       z = self.sigmoid(y)
       return z

計算出y值之后,再經由Sigmoid()激勵函數,轉換出z值,才成為這神經元的輸出值(即z值)。有了z值之后,就能進行「反向傳播(Backpropagation)」來更新權重(Weight)值。表現於指令:

       loss = T[j] - z

       delta = 2 * self.deriv(z) * loss
       update = self.learningRate * delta

  其中的loss值,還要乘以Sigmoid的導數(Derivation)值,來決定修正的幅度。這樣子,讓模型的適用范圍更廣了。除了可以應用於線性分類(Linear classification)問題上,也適用於非線性分類(Nonlinear classification)的情境

  至於主程式(部分)里有個指令:

        p = myNN() 

誕生一個myNN的物件,並且呼叫到基類Perceptron的建構式:

    class Perceptron(ABC):

      def __init__(self):
         self.learningRate = self.onLearningRate()
         self.epoches = self.onEpoch()
         dw = self.onW()
            # …………

            # …………

  然后,開始執行建構式里的指令,呼叫到了onLearningRate()函數,轉而呼叫子類myNN里的onLearningRate()函數,要求它傳回來<學習率>的值。接着,就呼叫到onEpoch()函數,轉而呼叫子類myNN里的onEpoch()函數,要求它傳回來訓練的回合數。接着,就呼叫到onW()函數,轉而呼叫子類myNN里的onW()函數,要求它傳回來權重的初期值。然后返回主程式部分,執行下一個指令:

      p.train()

   於是,這指令就呼叫基類Perceptron里的train()函數,執行到train()函數里的指令:

      def train(self):

           X = self.onX()
           T = self.onT() 

  就呼叫到onX()函數了。然而這onX()是抽象函數,其指令是實作於myNN子類里。於是就轉而呼叫myNN里的onX()函數,要求它傳回來訓練資料X[]的內容。接着呼叫onT()函數了,就轉而呼叫myNN里的onT()函數,要求它傳回來期望資料T[]的內容。然后展開訓練的動作,並輸出結果如下:

  

   其結果與上一個范例是一致的,一開始的預測正確率比較低。然而愈多回合的訓練,其正確率就逐漸上升了。到了第#50回合之后,其預測的正確率就接近於1.0(即達到100%)了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM