[學習筆記] RBF神經網絡初探


RBF神經網絡初探

徑向基函數

徑向基函數是一種函數的取值僅僅與輸入的中心點有關的函數,具有這種性質的函數就稱為徑向基函數。

比如,高斯函數是一種徑向基函數,其輸出值的大小與距離中心點的距離有關,距離中心點越遠,函數值越小,距離中心點越近,函數值越大。

RBF神經網絡的結構

RBF神經網絡一般具有兩層結構,是一種前向神經網絡。第一層的作用是將輸入由非線性可分轉變為線性可分,第二層一般是感知機類型的神經元層或ADALINE類型的神經元層。

第一層計算

計算過程

對於只有一個兩維的輸入(p1, p2)而言,假定函數的中心點C1和C2已知(同樣假設只有兩個中心),利用高斯函數作為徑向基函數,如果高斯函數的標准差σ1和σ2已知,那么第一層的輸出即為:

計算過程為:

  1. 計算由樣本(p1,p2)組成的點到中心C1的距離r1,通過高斯徑向基函數投影為Φ1
  2. 同理計算到到C2的距離投影到Φ2.

中心C和標准差σ的確定

上述過程假定中心C和標准差σ是已知的,實際上很多任務中是需要學習這兩個量的,對於中心C,我們一般采用kmeans聚類算法來確定,因此聚類的中心點數量k也是一個超參。而σ的確定也非常簡單,即以到聚類中心點的距離(平方根距離)最近的前k個樣本的聚類的均值為σ,之后每個σ就都確定了。

還有一種方法來確定C和σ,就是認為C和σ是可學習的參數,利用梯度下降來更新學習C和σ。后面的代碼例子中將利用梯度來學習C和σ。

第二層計算

感知機和ADALINE

對於第一層輸出p(假設為5維向量),其輸出為線性映射:

\[a = hardlim(Wp + b) \]

其中W為1x5矩陣,b為1x1常量,harddim為sgn函數:

\[\begin{equation} sgn(x) = \begin{cases} 1,& x>0 \\ 0,& x=0\\ -1,& x<0 \end{cases} \end{equation} \]

其中感知機和ADALINE其實有一些不同點,雖然前向過程的purelin恆等函數看起來沒啥用(只是形式上要走個激活函數),但兩者在更新參數時使用的標簽是不同的,感知機是用離散的標簽作為gt來更新前面的參數,而ADALINE則是直接根據加權求和的結果,也就是連續值來更新前面的參數,具體可以見下圖:

Coding

本文僅以高斯徑向基函數,第二層為感知機模型,利用梯度下降算法更新參數為例來寫一個RBF神經網絡的Demo。(求梯度的公式感覺自己推就太麻煩了,有自動求導為啥不用.)

Loss:

測試擬合情況:

import torch
import torch.nn as nn
import numpy as np
import torch.optim as optim
from matplotlib import pyplot as plt
class RBF(nn.Module):
    def __init__(self,input_dim = 5,k = 3):
        super(RBF,self).__init__()
        self.k = k
        self.C = nn.Parameter(torch.randn(1,k,input_dim)) # (n,k,5)
        self.sigma = nn.Parameter(torch.randn(1,k)) # (n,k)
        self.w = nn.Parameter(torch.randn(1,1,k)) # (n,1,k)
        self.b = nn.Parameter(torch.randn(1,1,1)) # (n,1,1)
        self.tanh = nn.Tanh()
    def forward(self,x):#(n,5)
        r = torch.sqrt(torch.sum((x.view(-1,1,5) - self.C)**2,dim = -1)) # (n,k)
        phi = torch.exp(-r**2/(2*self.sigma**2)).unsqueeze(-1) # (n,k,1)
        return self.tanh(self.w @ phi + self.b).squeeze(-1)



if __name__ == "__main__":
    batch_size = 4
    k = 70
    input_dim = 5
    num_epochs = 300
    x_train = torch.rand(batch_size * 160,input_dim) * 2
    y_train = torch.FloatTensor(torch.sin(torch.sum(x_train,dim = -1,keepdim=True)))
    #y_train[(x_train[:,0] > 0.5) & (x_train[:,2] < 0.5)] = 0
    
    model = RBF(input_dim,k)
    optimizer = optim.Adam(model.parameters(),lr = 1e-3)
    criterion = nn.MSELoss()
    
    loss_curve = []
    for epoch in range(num_epochs):
        running_loss = 0.
        for i in range(160):
            x = x_train[i*batch_size:(i+1)*batch_size]
            y = y_train[i*batch_size:(i+1)*batch_size]
            
            pred = model(x)
            loss = criterion(pred,y)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            running_loss += loss
        print("loss: {}".format(running_loss / 160))
        loss_curve.append(running_loss / 160)
    x_test = torch.rand(160,input_dim)*2
    
    y_test = torch.FloatTensor(torch.sin(torch.sum(x_test,dim = -1,keepdim=True)))
    
    model.eval()
    with torch.no_grad():
        y_pred = model(x_test)
    y_test = y_test[indices]
    y_pred = y_pred[indices]
    plt.plot(y_test.squeeze(1).numpy())
    plt.plot(y_pred.squeeze(1).numpy())
    #plt.plot(loss_curve, label='train_loss')
    plt.show()


免責聲明!

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



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