RBF Network
前面的一篇SVM中,最後的分割函數:
Gaussian函數還有另外一個叫法——徑向基函數,這是因爲這個base function的結果只和計算這個x和中心點xn的距離有關,與其他的無關。
從其他方面來看SVM,先構造一個函數:
g(x) = y_nexp(-γ|x - x_n|^2)指數求出來的其實就是x點和中心點的相似度,相似度越高,那麼=晚y這個方向投票的票數就會越多。不同的g(x)有不同的權重,他們的線性組合就成了SVM,g(x)函數稱爲是radial function。所以Gaussian SVM就是把一些radial function聯合起來做linear aggregation。
RBF Network就是SVM的延伸,目的就是找到所有radial hypotheses的linear aggregation,得到更好的網絡模型。
可以看到這兩種網絡其實很類似,Neural Network的隱藏層是權值和數據做內積非線性轉換再uniform的組合得到最後的輸出,而對於RBF Network隱藏層是求高斯距離在做aggregation的方法。比較大的不同點就在於hidden層的不同了。
β就是每一個radial function的權值,μ就是中心點,m爲中心點的個數,主要的,對比一下之前的SVM,β就是αy,μ就是支持向量。由於是一個分類問題,所以最後的output function就是sign函數了。
之前講過,一個核函數不是隨便亂選的,要滿足兩個條件:對稱,半正定。對於SVM裏面的核函數,其實ius把當前的數據提升到某一個很高很高的維度,然後做切片把數據分出來,polynomial function也是一樣的,只不過是有限維度的。而RBF其實就是在當前的空間做相似度處理,而那些kernel其實就是轉換到z空間來計算核函數以表徵兩個向量的相似度。所以RBF和kernel都是衡量相似度的方式。雖然SVM和RBF Network都很相似,甚至可以說最後的決策函數基本一致的,但是他們的學習過程是很不一樣的,一個是直接x空間,一個是轉換到z空間。
衡量相似性並不止一種RBF方法,餘弦相似度這些也可以衡量向量之間的相似度。
回過頭來思考一下SVM,其實支持向量機就是先通過凸優化的一些方法找到有投票權利的點,之後給出相應的權值,最後決策就是這些有投票權利的點進行決策;對於其他線性模型,其實主要的不同就是他們每一個點都有投票的權利,這就導致很遠的點都會干擾到邊界。而RBF Network事實上做的事情和SVM有點像,因爲RBF函數是指數增長,如果這個點很遠的話會非常小,近乎就是0了,所以也起到了弱化遠的點投票權,強化近的點投票權的能力。
RBF Network Learning
RBF Network的決策函數:
μ就是中心點,中心點是自己選擇的。有一種選擇中心點的方法,就是所有的點都作爲中心點,那麼每一個樣本對於預測都會有影響,β就是影響的程度。如果影響的程度都是一樣的,那麼就是1了,β = 1*y,最後相乘做uniform aggregation之後sign得到結果。這種我們稱爲full RBF Network。
這個時候,full RBF Network就可以表示爲:
這是一個指數函數,距離越遠,那麼衰減的就越快,x與中心點的距離越近那麼就越大,距離越遠就越小。也就是說,如果我們的樣本點是N個,那麼起了關鍵作用的一般就是最近的那個點而已,當然,不一定是最近的一個點,可以是最近的K個點,用這k個點來代替N個點,當前的點周圍最近的k個點哪個類別最多,那麼這個當前這個點就是屬於哪個類別的。這種算法就叫K近鄰算法。
k nearest neighbor通常比nearest neighbor model效果更好,計算量上也比full RBF Network要簡單一些。值得一提的是,k nearest neighbor與full RBF Network都是比較“偷懶”的方法。因爲它們在訓練模型的時候比較簡單,沒有太多的運算,但是在測試的時候卻要花費更多的力氣,甚至可以說是幾乎沒有運算在裏面,只需要做一些簡單的數據處理即可,找出最相近的中心點,計算相對複雜一些。
如果是做迴歸問題,我們就只需要去掉output:
很明顯,這樣就是一個線性迴歸的問題了,每一個RBF 其實可以看做就是一個矩陣比如第一個元素x1,那麼經過RBF的轉換之後:
z_1 = [RBF(x_1,x_1), RBF(x_1, x_2), RBF(x_1,x_3),RBF(x_1,x_3)...RBF(x_1,x_N)]
那麼Z就是z的按列排序了,按照線性迴歸的解公式:
β = (Z^TZ)^{-1}Z^Ty
上述矩陣Z是一個方陣,大小是N,有多少箇中心點那麼就有多少個N。如果每一個x都是不一樣的,那麼這個矩陣就是可以逆的矩陣了,畢竟x是訓練數據,一樣的就沒有意義了。
化簡一下:
β = Z^{-1}y
我們以x1爲例子,那麼解就是:
這個結果對於我們來說非常奇怪,如果這樣的話那麼對於所有的x都有:
g_{RBF}(x_n) = y_n所有Ein = 0,這樣對於機器學習來說並不是一個好事情,因爲這樣很大概率會出現過擬合。當然,某些情況下還是很有用的,比如函數擬合或者是做autoencode。
爲了避免過擬合,使用ridge regression的方法:
L2範式正則化。Z矩陣是由一系列Gaussian函數組成,每個Gaussian函數計算的是兩個樣本之間的distance similarity。這裏的Z與之前我們介紹的Gaussian SVM中的kernel K是一致的。當時使用ridge regression得到的解:
比較一下kernel ridgeregression與regularized full RBF Network的β解,形式上相似但不完全相同。這是因爲regularization不一樣,在kernel ridgeregression中,是對無限多維的特徵轉換做regularization,而在regularized full RBF Network中,是對有限維(N維度)的特徵轉換做regularization。因此,兩者的公式解有細微差別。
對於解決過擬合,還有另外的一種方法,可以選擇K個具有代表性的點來代表這N個點,這樣減少了中間點減少了權重的數量,VC維就減少了,可以起到regularization的作用。
原本的問題是求解中心點μ,β權重,現在β可以通過迴歸求解,那麼只需要求μ了。
K Mean Algorithm
選擇代表的原因,就是因爲這些點有很高的相似性,所以可以使用一箇中心點來代表,從所有的點中選擇幾個有代表性的點。
首先聚類算法是一種非監督的算法,不需要有label。需要確定的一般就是兩個變量,分羣值Sm,沒一個分類可以表示爲S1,S2,S3,S4...Sm,另一個就是中心值μm,μ1,μ2,μ3,μ4...μm,每一個分羣就對應着中心,要求的就是這個中心。對於這類問題的優化,就可以使用square error function了。優化的步驟也是一樣,求導,梯度下降或者梯度爲0求最優值解。
剛剛也說過了,既然是衰減的形式,那麼只需要取最大的就好了,最大也就意味着這需要求距離最近的即可。所以,表達式裏面只有屬於這個類別的數據求error。
最後就是求導做優化了:
兩個變量組合的優化問題,通常的方法就是對這兩個變量分別求。仔細觀察一下這兩個變量,可以發現,只要確定了μ,就可以確定S;或者只要確定了S,求個平均也可以得到μ。所以假設μ已經是固定的了,那麼可以依次迭代x,和哪個μ的距離最小就屬於哪個類別。
如果類別S是確定的,那麼目標就是找到對應的μ中心點,顯然這個,求個導的事,梯度下降就可以解決了。
所以這就成了一個雞生蛋蛋生雞的問題,所以一般一開始我們會選擇隨機的幾個點作爲中心μ,然後按照上訴步驟優化。
優化有結果嗎?
這個優化的過程好像有些不一樣,求導等於0應該是直接求出了最優的解,linear regression就是這個道理,但是爲什麼要一直這樣迭代呢?這是因爲求出來的這個μ並不是一個全局的μ,只是在當前對於每一個羣S的一個最優,但是這個S並不是最優的,之前也說了:這個S和μ是互相牽制的,雞生蛋蛋生雞的問題,S可以得到μ,μ也可以得到S。所以整個過程通俗點就是通過μ求局部最優的S,通過S有球局部的最優μ,不斷的迭代,慢慢的跑到全局。但是也沒有可以跑到局部呢?這個是有可能的,這於初值有關,所以Kmean均值算法也是一個初值敏感的算法。對於局部這個問題,有一種解法就是可以合併最近的幾個質心。事實上如果中心比較小,比如3個這樣,一般都不會有局部出現,因爲\sum_{m=1}^M(x_n - μ_m)^2不會這麼的彎曲。
停止是一定的,因爲無論是通過S優化μ還是μ優化S都朝Ein = 0爲目的,如果Ein增加了是不會繼續的,所以最後一定會到達一個平衡點。
The process of the RBF Network
既然中心有其他算法可以幫忙解決了,那麼整個算法也就清晰了:
求解優化的過程中,可以使用validation來求解最優的λ和M。
RBF Network and KMeans in action
k值的大小和初始位置的不同都會影響聚類的結果。
把這些機構k均值使用到RBF裏面:
對於正則化也有不同的影響。
coding
KMeans
接下來就是代碼實現KMeans算法了。KMeans算法其實很簡單,首先隨機得到幾個中心點,根據中心點預測做聚類算法即可。
def loadDataSet():
'''loading data......'''
data = dataSet.load_iris()
dataset = data.data
target = data.target
PCA = pca.PCA(n_components=2)
dataset = PCA.fit_transform(dataset)
return np.mat(dataset), np.mat(target)
pass
加載數據,iris數據集進行降維操作便於可視化。
def distEclud(vecA, vecB):
'''calculate the distance from vecA to vecB'''
return np.sqrt(np.sum(np.power(vecA - vecB, 2)))
pass
def randCent(dataSet, k):
'''create the center'''
n = np.shape(dataSet)[1]
centroids = np.mat(np.zeros((k, n)))
for j in range(n):
minJ = np.min(dataSet[:, j])
rangeJ = float(max(dataSet[:, j]) - minJ)
centroids[:, j] = minJ + rangeJ * np.random.rand(k, 1)
return centroids
計算距離,隨機選擇中心點。隨機選擇中心點這裏是先計算了每一個維度的範圍,之後在這個範圍裏面隨機選擇。
def KMeans(dataSet, k, distMeas = tool.distEclud, createCent = tool.randCent):
'''KMeans Algorithm is running......'''
m = np.shape(dataSet)[0]
clusterAssment = np.mat(np.zeros((m, 2)))
centroids = createCent(dataSet, k)
clusterChanged = True
while clusterChanged:
clusterChanged = False
for i in range(m):
minDist = np.inf
minIndex = -1
for j in range(k):
distJI = distMeas(centroids[j,:], dataSet[i,:])
if distJI < minDist:
minDist = distJI
minIndex = j
if clusterAssment[i, 0] != minIndex:
clusterChanged = True
clusterAssment[i, :] = minIndex, minDist**2
for cent in range(k):
ptsInClust = dataSet[np.nonzero(clusterAssment[:, 0].A == cent)[0]]
centroids[cent, :] = np.mean(ptsInClust, axis=0)
dataFrame = pd.DataFrame(data=np.hstack((dataSet, clusterAssment[:,0])))
return dataFrame, centroids
選擇和calculate distance的算法都是動態的方式,便於以後的改進。整個過程也比較簡單,離哪個近就屬於哪個類別。
RBF Network
①獲取中心點
_,center = kMeans.KMeans(np.mat(x_train), k)
剛剛寫好的KMeans就是這個作用。
②計算β值
beta = rbf(x_train, center, y_train, gama=gamma, lamda=lamda)
rbf函數的實現:
def rbf(x_train, center, y_train, gama = 0.001, lamda = 0.01):
M = center.shape[0]
N = len(x_train)
Z = np.zeros((M, N))
for i in range(M):
for j in range(N):
Z[i][j] = Gaussian(x_train[j], center[i], gama)
I1 = np.eye(N, k = 0)
beta = np.linalg.inv(np.dot(Z.T, Z) + lamda * I1)
beta = np.dot(beta, Z.T)
y_train = np.mat(y_train)
beta = np.dot(y_train, beta)
return beta
pass
def Gaussian(vecA, vecB, gama):
x_x = np.abs(np.sum(vecA - vecB))
x_x_2 = np.power(x_x, 2)
return np.exp(-1.0 * gama * x_x_2)
pass
首先是計算Z矩陣,就是φ(x)的矩陣。其實就是和上面步驟一模一樣的,使用的是線性迴歸,β = (Z^TZ + λI)^{-1}Z^Ty使用直接就可以算,要是邏輯迴歸也是一樣。
③預測
def predict(beta, x_train, center, gama):
result = []
for x in x_train:
x = np.mat(x)
sum = 0
for i, vecB in enumerate(center):
sum += beta[0,i]*Gaussian(x, vecB, gama)
result.append(sum)
return result
pass
爲了方便調用,整合成一個函數:
def RBF(y_test, x_test, y_train, x_train, gamma = 0.001, lamda = 0.01, k = 4):
Again = True
while Again == True:
_,center = kMeans.KMeans(np.mat(x_train), k)
beta = rbf(x_train, center, y_train, gama=gamma, lamda=lamda)
Again = False
for i in range(beta.shape[1]):
if np.isnan(beta[0, i]):
Again = True
result = predict(beta, x_train, center, gamma)
for i in range(len(result)):
if result[i] > 0:
result[i] = 1
else:
result[i] = -1
posibility = 0
for i in range(len(result)):
if result[i] == y_train[i]:
posibility += 1
train_accuracy = posibility/len(result)
result = predict(beta, x_test, center, gamma)
for i in range(len(result)):
if result[i] > 0:
result[i] = 1
else:
result[i] = -1
posibility = 0
for i in range(len(result)):
if result[i] == y_test[i]:
posibility += 1
test_accuracy = posibility/len(result)
return train_accuracy, test_accuracy
可以計算不同gamma,lamda,k的影響。
④獲取數據
def load_data():
data = dataset.load_breast_cancer().data
target = dataset.load_breast_cancer().target
for i in range(len(target)):
if target[i] == 0:
target[i] = -1
x_train, x_test, y_train, y_test = train_test_split(data, target, random_state=42, shuffle=True, test_size=0.4)
return x_train, x_test, y_train, y_test
pass
⑤啓動函數
if __name__ == '__main__':
x_train, x_test, y_train, y_test = load_data()
gamma = [1,0.1,0.01,0.001,0.0001]
lamda = gamma
train_accuracy = []
test_accutacy = []
c = ['red', 'blue', 'orange', 'green', 'yellow', 'black']
for n, i in enumerate(gamma):
for j in lamda:
train, text = RBF(x_test=x_test, y_test=y_test, x_train=x_train, y_train=y_train, gamma=i, lamda=j)
print('gama : ',i, ' lamda : ', j, ' train_accuracy : ', train, ' text_accuray : ', text)
train_accuracy.append(train)
test_accutacy.append(text)
plt.plot(lamda, train_accuracy, c = c[n], label = 'gamma:'+str(i) + ' (train)')
plt.plot(lamda, test_accutacy, c = c[n], linestyle='--', label = 'gamma:'+str(i) + ' (test)')
plt.xlabel('lambda')
plt.ylabel('accuracy')
plt.legend(loc = 'upper right')
train_accuracy = []
test_accutacy = []
plt.show()
for n, i in enumerate(lamda):
for j in gamma:
train, text = RBF(x_test=x_test, y_test=y_test, x_train=x_train, y_train=y_train, gamma=j, lamda=i)
print('lamda : ',i, ' gama : ', j, ' train_accuracy : ', train, ' text_accuray : ', text)
train_accuracy.append(train)
test_accutacy.append(text)
plt.plot(gamma, train_accuracy, c = c[n], label = 'lamda:'+str(i) + ' (train)')
plt.plot(gamma, test_accutacy, c = c[n], linestyle='--', label = 'lamda:'+str(i) + ' (test)')
plt.xlabel('gamma')
plt.ylabel('accuracy')
plt.legend(loc = 'upper right')
train_accuracy = []
test_accutacy = []
plt.show()
ks = [2,3,4,5,6,7]
train_accuracy = []
test_accutacy = []
for i in range(6):
for n, i in enumerate(ks):
train, text = RBF(x_test=x_test, y_test=y_test, x_train=x_train, y_train=y_train, gamma=0.0001, lamda=0.01, k=i)
print('k == ' + str(i))
train_accuracy.append(train)
test_accutacy.append(text)
plt.plot(ks, train_accuracy, c = c[n], label = 'train')
plt.plot(ks, test_accutacy, c = c[n], linestyle='--', label = 'test')
plt.xlabel('the number of k')
plt.ylabel('accuracy')
plt.legend(loc = 'upper left')
plt.show()
train_accuracy = []
test_accutacy = []
pass
效果的討論
在運行函數得到結果後進行分析。
①當γ固定了,看看lamda變化對於結果的影響。
其實還是很正常的,隨着gamma的減少,準確率慢慢提上去了,虛線的測試數據,直線是準確率。Gaussian函數:g(x) = exp(-γ|x_n - μ|^2)γ越小,就相當於σ變大,高斯函數的標準差變大那麼這個函數就會變的更加平滑,泛化能力會很強。其實就是regularization的過程。
觀察上圖是可以得到λ對於整體的影響不會太大。基本是平緩的。
②當λ固定了,看看γ變化對於結果的影響。
γ越小效果越好,但是如果γ非常小,那麼λ對於模型的影響幾乎是沒有影響。
③對於k的數量和準確率的討論
效果不太穩定,我多做了幾次。
效果非常非常不穩定,我之前懷疑是線性迴歸的解不穩定的緣故,之前學習到病態矩陣,也就是近似奇異矩陣的矩陣,而regularization L2正則化就是爲了使得病態矩陣轉換成正常矩陣,所以增大了λ,顯然並沒有什麼卵用。雖然整體效果不錯。上面的結果就已經是λ增大的結果了。
所以最後還有兩個問題沒有解決,一個就是λ爲什麼對於模型的影響很很小,另一個上面沒有提到,爲什麼測試數據的準確率會大於訓練數據的準確率?這還打的明顯,不知道是不是沒有做交叉驗證的結果,因爲懶。。。。之前看到的解釋是:
如果程序很好的實現了模型,那麼就是模型不適合你的數據,因爲這表明存在如下問題:每次訓練,都使得訓練之後的模型對測試的 1折效果很好,而對用於訓練的9折效果慘淡,也就是模型落入了局部極值點而非全局極值點。這很有可能是模型在具體數據下的失效問題。
但是這個問題已經重複過了很多次,一直在使用test_train_split區分,同時也有shuffle。事實上在γ比較小的時候,測試數據就超過了訓練數據的準確率,上面γ的變化可以看到。
最後附上GitHub代碼:https://github.com/GreenArrow2017/MachineLearning/tree/master/MachineLearning/RBFNetwork