1. 讀取數據集
def load_data(filename,dataType): return np.loadtxt(filename,delimiter=",",dtype = dataType) def read_data(): data = load_data("data2.txt",np.float64) X = data[:,0:-1] y = data[:,-1] return X,y
2. 查看原始數據的分布
def plot_data(x,y): pos = np.where(y==1) # 找到標簽為1的位置 neg = np.where(y==0) #找到標簽為0的位置 plt.figure(figsize=(8,6)) plt.plot(x[pos,0],x[pos,1],'ro') plt.plot(x[neg,0],x[neg,1],'bo') plt.title("raw data") plt.show() X,y = read_data() plot_data(X,y)
結果:
3. 將數據映射為多項式
由原圖數據分布可知,數據的分布是非線性的,這里將數據變為多項式的形式,使其變得可分類。
映射為二次方的形式:
def mapFeature(x1,x2): degree = 2; #映射的最高次方 out = np.ones((x1.shape[0],1)) # 映射后的結果數組(取代X) for i in np.arange(1,degree+1): for j in range(i+1): temp = x1 ** (i-j) * (x2**j) out = np.hstack((out,temp.reshape(-1,1))) return out
4. 定義交叉熵損失函數
可以綜合起來為:
其中:
為了防止過擬合,加入正則化技術:
注意j是重1開始的,因為theta(0)為一個常數項,X中最前面一列會加上1列1,所以乘積還是theta(0),feature沒有關系,沒有必要正則化
def sigmoid(x): return 1.0 / (1.0+np.exp(-x)) def CrossEntropy_loss(initial_theta,X,y,inital_lambda): #定義交叉熵損失函數 m = len(y) h = sigmoid(np.dot(X,initial_theta)) theta1 = initial_theta.copy() # 因為正則化j=1從1開始,不包含0,所以復制一份,前theta(0)值為0 theta1[0] = 0 temp = np.dot(np.transpose(theta1),theta1) loss = (-np.dot(np.transpose(y),np.log(h)) - np.dot(np.transpose(1-y),np.log(1-h)) + temp*inital_lambda/2) / m return loss
5. 計算梯度
對上述的交叉熵損失函數求偏導:
利用梯度下降法進行優化:
def gradientDescent(initial_theta,X,y,initial_lambda,lr,num_iters): m = len(y) theta1 = initial_theta.copy() theta1[0] = 0 J_history = np.zeros((num_iters,1)) for i in range(num_iters): h = sigmoid(np.dot(X,theta1)) grad = np.dot(np.transpose(X),h-y)/m + initial_lambda * theta1/m theta1 = theta1 - lr*grad #print(theta1) J_history[i] = CrossEntropy_loss(theta1,X,y,initial_lambda) return theta1,J_history
6. 繪制損失值隨迭代次數的變化曲線
def plotLoss(J_history,num_iters): x = np.arange(1,num_iters+1) plt.plot(x,J_history) plt.xlabel("num_iters") plt.ylabel("loss") plt.title("Loss value changes with the number of iterations") plt.show()
7. 繪制決策邊界
def plotDecisionBoundary(theta,x,y): pos = np.where(y==1) #找到標簽為1的位置 neg = np.where(y==0) #找到標簽為2的位置 plt.figure(figsize=(8,6)) plt.plot(x[pos,0],x[pos,1],'ro') plt.plot(x[neg,0],x[neg,1],'bo') plt.title("Decision Boundary") #生成和原數據類似的數據 u = np.linspace(-1,1.5,50) v = np.linspace(-1,1.5,50) z = np.zeros((len(u),len(v))) #利用訓練好的參數做預測 for i in range(len(u)): for j in range(len(v)): z[i,j] = np.dot(mapFeature(u[i].reshape(1,-1),v[j].reshape(1,-1)),theta) z = np.transpose(z) plt.contour(u,v,z,[0,0.01],linewidth=2.0) # 畫等高線,范圍在[0,0.01],即近似為決策邊界 plt.legend() plt.show()
8.主函數
if __name__ == "__main__": #數據的加載 x,y = read_data() X = mapFeature(x[:,0],x[:,1]) Y = y.reshape((-1,1)) #參數的初始化 num_iters = 400 lr = 0.1 initial_theta = np.zeros((X.shape[1],1)) #初始化參數theta initial_lambda = 0.1 #初始化正則化系數 #迭代優化 theta,loss = gradientDescent(initial_theta,X,Y,initial_lambda,lr,num_iters) plotLoss(loss,num_iters) plotDecisionBoundary(theta,x,y)
9.結果