CMAC小腦模型神經網絡與Python實現

CMAC 小腦模型神經網絡

簡述

CMAC是cerebellar model articulation controller,一種模擬小腦的模型。小腦對肢體動作的操作是近似“反射”的,CMAC的理念就是讓訓練後的網絡在應用時對輸入的計算越快越好。很容易想到,最快的方法自然是把所有可能的輸入情況和它們對應的輸出存在表裏,在使用時直接查表。而且爲了更快的速度,即使是平衡搜索樹也不夠,哈希散列技術是一種更有效的技術,只需要預留足夠的內存空間,並設計優秀的散列函數就能只花費散列函數計算的時間就查詢到結果。

模型搭建

在這裏插入圖片描述
如上圖所示,首先對輸入的模擬信號進行量化分級,分級的數目根據自己設置的採樣率而定。每個級別的量化信號會激活一個區間內的感知器,被激活的感知器把自帶的權值做加和成爲輸出。
其中爲了減少感知機的數目,一般使用隨機的哈希映射,把量化激活的感知機虛地址映射到實地址。這一點可以有效節約感知器數目,在硬件實現時至關重要。

訓練步驟

  1. 輸入向量的維度有可能很高,因此先把向量做量化是必不可少的。用一定的分辨率採樣模擬信號,得到離散的向量。
  2. 做合適的感知機激活,爲了泛化性能,相似的感知機將被同時激活。體現在模型中就是設置鄰域,把輸入向量及其附近的感知機都激活。這個激活使用的是一個滾動激活方式,因爲多維情形下,每一維的變量都對應一個激活區間。爲了理解這一點我們不妨舉個例子。
    在二維輸入的情形下,兩個維度激活的感知機編號分別爲1234、2345、3456和abcd、bcde、cdef。
    使用簡單的1234、2345、3456的順序的話,第一個維度的1234和第二個維度的abcd、bcde、cdef結合產生的地址將是
    1a2b3c4d、1b2c3d4e、1c2d3e4f
    而如果我們考慮使用滾動編碼的順序,即1234、5234、5634和abcd、ebcd、efcd。則結合的地址將是
    1a2b3c4d、1e2b3c4d、1e2f3c4d
    可以看出,比起前者,後者讓相鄰的感知機地址更爲接近(相鄰的地址只有一位是不同的)。
  3. 把第二步中可供選擇的感知機都存儲下來是不明智的,因爲分辨率不能設置的很低,而維度有可能很高,如果爲每種情況預留空間,需要的內存將是指數增長的。考慮到維度間的相關性,很多向量情況完全不可能,或者很小概率出現。爲此,我們可以考慮把所有地址散列到一個實地址空間內。
  4. 輸出。網絡的最終輸出是簡單的,使用被激活的幾個神經元的權值做疊加。
  5. 學習。使用類似前饋網絡的學習方法,因爲每次激活的神經元數目很少,所以在反饋傳播誤差時,也只需要做很少的,簡單的加減法即可。

綜上,CMAC實際上是一種智能查表技術。使用簡單的局部性原理,用多個超平面擬合輸出超曲面。雖然CMAC做不到RBF和BPN的完全非線性逼近,但CMAC勝在前饋計算速度快,適合智能應用中需要即時反射的場景,而且網絡模型更簡單,參數更少,計算速度更快。

CMAC實踐

CMAC最早的出現是爲了解決一個機器人手臂協調的問題,機器人的傳感器會返回速度、角度和加速度等信號,它們和力矩滿足一個方程,而想從這些信號中計算出應該施加的力矩需要求解反函數,但這個反函數是未知的。爲了學習這個反函數,並能滿足機器人需要的快速反射,就設計了這種查表型的算法。
在這裏插入圖片描述
這裏我們也用CMAC來學習一個二維輸入一維輸出的函數,y = sin(x1)cos(x2), 驗證本算法的性能。

import numpy as np
import random
import matplotlib.pyplot as plt

def prime(x):
    top = int(x**0.5)+1
    for i in range(2,top):
        if x%i==0:
            return False
    return True

def getprime(x):
    for i in range(x,x**2):
        if prime(i):
            return i
    return False

k = getprime(random.randint(0,1000))
b = getprime(random.randint(0,1000))

def hashing(x,size):
    return (k*x+b)%size



class CMAC:
    def __init__(self,resolution,steps,bottom,C,mem):
        '''
        resolution:模型在各個維度的分辨率
        steps:模型在各個維度採樣的步長
        bottom:模型在各個維度採樣的起點
        
        C:模型的泛化範圍
        mem:模型被分配的實地址數目,也是神經元數目
        爲了哈希函數的應用,會在給定的mem向上取到質數
        '''
        self.dim = len(resolution)
        self.C = C
        self.resolution = resolution
        self.steps = steps
        self.bottom = bottom
        #向上取到質數
        self.size = getprime(mem)
        #設置size個權值,用於描述結果
        self.W = np.zeros(self.size)
        #爲每個維度的每個可能的量化取值,計算感知器激活情況
        L = max(resolution)
        self.activate = np.zeros((self.dim,L,C))
        for d in range(self.dim):
            self.activate[d][0] = np.array([x for x in range(C)])
            for i in range(1,resolution[d]):
                self.activate[d][i] = self.activate[d][i-1]
                self.activate[d][i][(i-1)%C]+=C
        
        
        
    def forward(self, x):
        #首先解析x,確定每個維度的虛地址
        assert len(x)==self.dim
        dim = self.dim
        address = np.zeros(self.C)#被激活的C個神經元的虛地址
        radix = 1# 由於地址是直接拼接的,每上升一個維度進制要乘以一個值
        for d in range(dim):
            #根據採樣步長得到x在該維度的量化取值
            num = int((x[d]-self.bottom[d])//self.steps[d])
            assert num<self.resolution[d]
            #查表找到量化取值num對應的感知器激活情況
            acti = self.activate[d][num]
            address += radix*acti
            radix *= self.resolution[d]+self.C-1
        #使用哈希函數,虛地址轉實地址
        address = hashing(address,self.size)
        self.work = address.astype(np.int)
        #實地址對應的神經元權值相加
        self.out = sum(self.W[i] for i in self.work)
        return self.out
    
    def backward(self, yhat, lr):
        total = len(self.work)
        delta = yhat-self.out
        for i in self.work:
            self.W[i] += delta*lr/total
        return
    
    def fit(self, X, Y, lr=0.1, iterations = 1000):
        n = len(X)
        for t in range(iterations):
            i = random.randint(0,n-1)
            x,y = X[i],Y[i]
            self.forward(x)
            self.backward(y,lr)
        return

首先我們定義CMAC的基本架構,代碼的註釋比較詳細,需要知道的都在裏面。最上面的隨機數生成和質數判斷是用於哈希散列的,因爲如果使用隨機質數係數的線性函數和取模來散列能取得比較好的效果。另外,哈希表的長度,也就是網絡裏神經元的數目也需要是質數。

#希望模型學習一個函數, f(x1,x2) = sin(x1)cos(x2)
#生成僞數據集
row = np.linspace(0,6.2,300)
col = np.linspace(0,6.2,300)
x1,x2 = np.meshgrid(row,col)
X = np.concatenate((x1.reshape(1,-1),x2.reshape(1,-1)),axis=0).T
Y = np.zeros(90000)
for i in range(len(X)):
    Y[i] = np.sin(X[i][0])*np.cos(X[i][1])

我們在三維空間上製造九萬個點用於訓練。

model = CMAC([65,65],[0.1,0.1],[0,0],5,5000)
model.fit(X,Y,iterations=20000)
#用3dplot打印model生成的曲面
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib import pyplot as plt
xx = np.linspace(0,6,101)
yy = np.linspace(0,6,101)
X,Y = np.meshgrid(xx,yy)
Z = np.zeros(X.shape)
for i in range(len(X)):
    for j in range(len(X[0])):
        x = np.array((X[i][j],Y[i][j]))
        Z[i][j] = model.forward(x)

fig = plt.figure()
axes3d = Axes3D(fig)
axes3d.plot_surface(X,Y,Z,color='grey')

這裏建立一個0.1步長,泛化範圍(感知器的激活區間)爲5,總存儲單元5000(會自動向上取到質數)的CMAC網絡,經過20000次隨機學習,打印出模型學習到的曲面如下。
在這裏插入圖片描述
曲面是不光滑的,因爲模型實質上是用很多平面去近似目標函數的曲面。這也是它的最大特點,局部近似。

總結

CMAC是一種智能查表技術,說它是神經網絡的一種並沒錯,但似乎有些勉強。
CMAC和前饋網絡,RBF徑向基網絡一樣可以逼近非線性函數,但是它是局部逼近。
CMAC的訓練速度和響應速度都很快,因爲CMAC每次訓練只需要更新被激活的幾個神經元的權值。而在前饋響應時,只需要計算一下需要激活的神經元的地址,做一個哈希映射,再求和就能得到輸出。比起其他深度網絡,它需要的計算量非常小,適合用於機器人、證券等對響應時間要求高的領域。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章