1. 程式人生 > >Regularization(規則化)和model selection以及Python實現

Regularization(規則化)和model selection以及Python實現

這次以非線性轉換(Nonlinear Transformation)為例,分別通過對多項式次數的選擇和regularization避免過擬合,還通過model selection來提高識別能力。

(一)非線性模型

原來的學習模型大多假設兩個類別是線性可分的,所以找到了一條直線或一個線性空間平面可以將兩個類別進行分類,但是一些情況下會出現線性不可分的情況。或者進行迴歸預測的過程中,不能用一條直線來擬合數據。這時候就需要非線性模型來實現。下面兩個圖分別是Logistic 迴歸和迴歸過程線上性情況下欠擬合,到引數(這裡看的是多項式的階數)過多情況下過擬合的過程。選擇適當的非線性模型引數是分類或迴歸的效果最好是我們的目的。image

image

<span style="font-size:18px;">程式相對以前的改動如下</span>
# -*- coding: utf-8 -*-

import random
from numpy import *
from math import *
from sklearn.preprocessing import PolynomialFeatures


polynomial_features = PolynomialFeatures(2, False)

# 邏輯函式
def sigmoid(inX):#由於在計算過程中inX可能會超出Exp(-inX)計算的界限
	if inX > 1000:
		return 1
	elif inX< -500:
		return -1
	else:
		return 1.0/(1+exp(-inX))
#載入資料

#Logistic 迴歸
def logisticRegression(dataMat,labelMat,opts):
	numSamples,numFeature = shape(dataMat)
	alpha = opts['alpha'] ; maxIter = opts['maxIter']
	weight = ones((21,1))#由於2次多項式的係數是21,這個只要測試一下就可以了
	for j in range(maxIter):
		if opts['optimizeType'] == 'smoothStocGradDescent':# smooth stochastic gradient descent
			dataIndex = range(numSamples)
			for i in range(numSamples):
				randIndex = int(random.uniform(0,len(dataIndex)))#產生0到dataIndex之間的隨機數
				alpha = 4.0/(1.0+j+i)+0.01 #j是迭代次數,i是迭代時,第i個選出的樣本
				dataMatMul = polynomial_features.fit_transform(dataMat[randIndex]) #這個函式是sklearn帶的
				dataMatMul = mat(dataMatMul)
				h = sigmoid(sum(dataMatMul[0]*weight))
				error = labelMat[randIndex] - h
				weight = weight + alpha * dataMatMul[0].T* error
				del(dataIndex[randIndex])
		else:
			raise NameError('Not support optimize method type!')
	return weight

</pre></p><p></p><p><span style="font-size:18px"><span style="font-size:18px">主程式如下:</span></span></p><p><span style="font-size:18px"><span style="font-size:18px"></span></span><pre class="python" name="code"># -*- coding: utf-8 -*-

from logisticRegression import *
from numpy import *


#知道了Iris共有三種類別Iris-setosa,Iris-versicolor和Iris-virginica
def loadDataSet(filename):
	numFeat = len(open(filename).readline().split(','))-1
	dataMat = []; labelMat = []
	fr = open(filename)
	for line in fr.readlines():
		lineArr = []
		curLine = line.strip().split(',')
		for i in range(numFeat):
			lineArr.append(float(curLine[i]))
		dataMat.append([1]+lineArr)  #這裡是為了使 x0 等於 1
		labelMat.append(curLine[-1])
	return dataMat,labelMat

# voteResult = {'Iris-setosa':0,'Iris-versicolo':0,'Iris-virginica':0}#記錄投票情況
voteResult = [0,0,0]
categorylabels = ['Iris-setosa','Iris-versicolor','Iris-virginica']#類別標籤
opts = {'alpha': 0.01, 'maxIter': 100, 'optimizeType': 'smoothStocGradDescent'}
#訓練過程
dataMat,labelMat = loadDataSet('train.txt')

weight1 = []
for i in range(3):#三類
	labelMat1 = []
	for j in range(len(labelMat)):#把名稱變成0或1的數字
		if labelMat[j] == categorylabels[i]:
			labelMat1.append(1)
		else:
			labelMat1.append(0)
	dataMat = mat(dataMat);
	weight1.append(logisticRegression(dataMat,labelMat1,opts))
print weight1
#測試過程
dataMat,labelMat = loadDataSet('test.txt')
dataMat = mat(dataMat)

initial_value = 0
list_length = len(labelMat)
h = [initial_value]*list_length

for j in range(len(labelMat)):
	voteResult = [0,0,0]
	for i in range(3):
		dataMatMul = polynomial_features.fit_transform(dataMat[j])
		dataMatMul = mat(dataMatMul)
		h[j] = float(sigmoid(dataMatMul[0]*weight1[i]))#得到訓練結果
		if h[j] > 0.5 and h[j] <= 1:
			voteResult[i] = voteResult[i]+1+h[j]
		elif h[j] >= 0 and h[j] <= 0.5:
			voteResult[i] = voteResult[i]-1+h[j]
		else:
			print 'Properbility wrong!'
	h[j] = voteResult.index(max(voteResult))
print h
labelMat2 = []
for j in range(len(labelMat)):#把名稱變成0或1或2的數字
	for i in range(3):#三類
		if labelMat[j] == categorylabels[i]:
			labelMat2.append(i);break

#計算正確率
error = 0.0
for j in range(len(labelMat)):
	if h[j] != labelMat2[j]:
		error = error +1

pro = 1 - error / len(labelMat)#正確率
print pro

準確率達到了百分之96.666

(二)產生過擬合的原因

VC維變大時,函式的Ein變小但Eout變大(也就是假設函式的泛化能力很差),此種情況稱之為過擬合。相對於過擬合有著欠擬合,解決方法只要提高多項式次數就可以。而過擬合的處理方法相對複雜。

產生過擬合的原因主要有三種,使用過度的VC維、噪聲、資料量大小。

1、對於噪聲和VC維,有噪聲情況下,低次多項式假設比和目標函式同次的多項式假設表現更好。假設沒有噪聲,在更高次數的目標函式下,比如50次的多項式函式,用2次假設或10次假設都相當於一種含有噪聲的情況(由於兩者都無法做到50次的目標函式,相當於含有噪聲)。下圖為二次函式和10次函式的學習鴻溝(learning curves)可以看出,當資料量少時2次函式的Ein和Eout差距小,泛化能力強。


2、對於噪聲、複雜度(高次也相當於一種噪聲形式)和訓練資料量與過擬合的關係,就像教小孩學習,要從簡單開始(低複雜度),多次重複(資料量),減少其他事情的干擾(減少噪聲)。具體的內容可以參考林軒田的機器學習基石課程第13課。

而出現了過擬合問題,應該如何處理呢?主要有兩個辦法:特徵選擇和正則化

image

(三)regularization(正則化)

正則化中我們將保留所有的特徵變數,但是會減小特徵變數的數量級(引數數值的大小w(j))。這個方法非常有效,當我們有很多特徵變數時,其中每一個變數都能對預測產生一點影響。其本質就是給代價函式或Ein的係數加上一個限制。

利用拉格朗日函式可以得到下面的結果。(雖然符號不同,但是都講了同樣的事情)

下面的這項就是一個正則化項

image
並且 λ 在這裡我們稱做正則化引數。我們需要適當的選擇正則化引數。下圖表達了λ從小到大由過擬合到欠擬合的過程。引數的選擇方式主要是通過交叉驗證實現(model selection部分提到)。


在林教授的機器學習基石課程中講到了,若輸入Xn是一個[-1,1]之間的數,多項式的轉換函式在高次項的時候會變得非常小,所以為了和低次項對應的權值向量分量產生一致的影響力,則該項的權值Wq會非常大,但正則化的求解需要特別小的權值向量,因此需要轉換後的多項式各項線性無關,可以將轉換函式設為Legendre多項式。

除了上面講的Regularization方法還有L1norm方法(針對少數1多數為0的情況如在支援向量機中應用)。下面的文章寫的不錯。http://blog.csdn.net/zouxy09/article/details/24971995/

(四)模型選擇

模型選擇就是希望在有許多模型的情況下,能夠在偏差和方差之間達到平衡。對於不同的演算法,可能想選擇合適的多項式次數、區域性加權線性迴歸的 τ或者是SVM中的懲罰因子C。而只通過經驗風險(也就是訓練誤差)最小化方法一定會產生過擬合的現象,所以我們希望通過下面的方式來解決這一問題。

1、 從全部的訓練資料S中隨機選擇70%的樣例作為訓練集clip_image018,剩餘的30%作為測試集clip_image020

2、 在clip_image018[1]上訓練每一個clip_image010[1],得到假設函式clip_image012[1]

3、 在clip_image020[1]上測試每一個clip_image012[2],得到相應的經驗錯誤clip_image022

4、 選擇具有最小經驗錯誤clip_image022[1]clip_image012[3]作為最佳模型。

這種方法稱為保留交叉驗證(hold-out cross validation)。在資料量很小的情況下這個明顯不太適用。所以我們採用K重交叉驗證(k-fold cross validation),方法如下:


簡單說就是把一個數據集隨機分成K份,對於一個模型,首先用K-1份進行訓練,一份進行測試得到一個錯誤率,然後重複K次得到一個平均錯誤率。對所有的模型都進行這樣的訓練,然後取平均錯誤率最小的作為最終的模型。

(五)特徵選擇

嚴格來說,特徵選擇也是模型選擇的一種。特徵選擇主要針對一類問題,就是特徵的數量遠遠大於訓練樣本數時想要通過某種方式來剔除無用的特徵,n個特徵就有2^n種情況,顯然不能用交叉驗證注意考察每種模型的錯誤率。需要一些啟發式的搜尋方法。而有很多可以實現的方法,不逐一介紹了。可以參考下面的連結

http://www.cnblogs.com/XBWer/p/4563079.html

注:程式參考http://scikit-learn.org/stable/auto_examples/model_selection/plot_underfitting_overfitting.html