【統計學習方法by李航】第二章實踐——感知機


 
鏈接:[ 全文章目錄 ]

一、代碼:


!!!第二部分代碼無法直接在python中運行,要在jupyter notebook等環境中打開。
Github鏈接:代碼(打開無代碼,換個瀏覽器試試)
 
 

二、感知機,代碼解釋(有部分刪改):


數據使用說明:
這裏使用的是sklearn中的iris數據集。該數據集的x是150行4列的矩陣;y是150行1列的矩陣,數值選擇爲(0,1,2)將y分爲三類。

下標從0 ~ 49是類0,下標從50 ~ 99是類1,下標從100 ~ 149是類2。

爲了簡單起見,對於x,我們只取第一列和第二列的數據,即[sepal length,sepal width];對於y,我們只選擇類0與類1(即下標從0~99)。

import pandas as pd #數據分析常用庫
import numpy as np #數學計算常用庫
from sklearn.datasets import load_iris #導入iris數據集
import matplotlib.pyplot as plt	#導入畫圖功能
%matplotlib inline #畫圖功能初始化

###################################################################
# 數據加載與預處理
iris = load_iris() #load_iris()函數返回一個可供操作的對象(類型是sklearn.utils.Bunch)
df = pd.DataFrame(iris.data, columns=['sepal length', 'sepal width', 'petal length', 'petal width']) #iris.data從就是感知機的x,columns是每列的名字
df['label'] = iris.target #iris.target是感知機的y;這句話的意思是,增加一列,列的名字叫label,列的數據從iris.target裏取

data = np.array(df.iloc[:100, [0, 1, -1]])
	'''這句話是一個數據拆分,
	np.array():將括號裏面的東西轉化成一個存儲單一數據類型的多維數組(這	裏就代表二維數組)
	df.iloc[:100, [0, 1, -1]]:逗號左邊,下標從0到99;逗號右邊,取下標爲0,爲1,以及最後一列。
	總的來說,就是把第一二和最後一列的數據拿出來,當作一個新的二維數組'''
X, y = data[:,:-1], data[:,-1] #把第一二列的數據給X,把最後一列的數據給y;逗號前面的 “:” 代表那一列全部元素
y = np.array([1 if i == 1 else -1 for i in y]) #把y中的元素換成1或-1(本來是0和1的)

###################################################################
#感知機模型
class Model:
    def __init__(self): #構造函數
        self.w = np.ones(len(data[0])-1, dtype=np.float32) #創建一個等於X長度、全爲1、類型爲float32的矩陣,在這裏w是[ 1.  1.]
        self.b = 0 #b
        self.l_rate = 0.1 #學習率
    
    def sign(self, x, w, b):
        y = np.dot(x, w) + b #np.dot(x, w),求x與w的內積(點積)
        return y
    
    # 隨機梯度下降法
    def fit(self, X_train, y_train):
        is_wrong = True #假設一開始有誤分類點(這裏我改了一下變量,方便理解)
        while is_wrong: #如果有誤分類點,就繼續
            wrong_count = 0 #誤分類點數目初始化爲0
            for d in range(len(X_train)): #對每一行向量進行判斷
                X = X_train[d] #取出當前行的x
                y = y_train[d] #取出當前行的y
                if y * self.sign(X, self.w, self.b) <= 0: #如果經驗損失函數小於0就說明其誤分類(可以等於0是因爲數據絕對線性可分)
                    self.w = self.w + self.l_rate*np.dot(y, X) #更新w
                    self.b = self.b + self.l_rate*y #更新b
                    wrong_count += 1 #誤分類點數+1
            if wrong_count == 0: #如果沒有誤分類點
                is_wrong = False

###################################################################
#主程序
perceptron = Model() #創建一個對象
perceptron.fit(X, y) #通過梯度下降擬合

x_points = np.linspace(4, 7,10) #這個是標準線
y_ = -(perceptron.w[0]*x_points + perceptron.b)/perceptron.w[1] #這個是算出來的線
plt.plot(x_points, y_)

plt.plot(data[:50, 0], data[:50, 1], 'bo', color='blue', label='0')#畫出類0的點
plt.plot(data[50:100, 0], data[50:100, 1], 'bo', color='orange', label='1')#畫出類1的點
plt.xlabel('sepal length')#寫出橫座標意思
plt.ylabel('sepal width')#寫出縱座標意思
plt.legend()#畫出圖例

在jupyter notebook中的輸出是這樣的:
在這裏插入圖片描述

三、自己寫的代碼,求出的w和b是差不多的,沒有用pandas和numpy


from sklearn.datasets import load_iris

def l(iris, w, b, a):#經驗損失函數,以哪兩項爲判斷依據(下標從0開始)
    i=0
    while(i<100):#數據行數爲150,但是0~49是1類,50~99是一類,100~149是一類;這裏我們只用兩類來進行分類就可以了
        
        temp = 0#求向量w和向量x的內積
        for j in range(2):
            temp += w[j]*iris.data[i][j]
            
        if(kind_y(iris.target[i]) * (temp+b)<0):#如果損失函數對某個點的值小於0,那麼這個點就是誤分類點
            #l(data, change_w(w, a, change_w(w, a, kind_y(iris.target[i]), iris.data[i]), change_b(b, a, kind_y(iris.target[i])), a)#如果用遞歸,會超出遞歸上限的
            w = change_w(w, a, kind_y(iris.target[i]), iris.data[i])
            b = change_b(b, a, kind_y(iris.target[i]))
            i=0
        else:
            i+=1
    return w, b

def change_w(w, a, y, x):#更新w
    for i in range(2):#iris數據集裏面每一行是一個四維向量,我們只取前2個
        w[i] += a*y*x[i]
    return w

def change_b(b, a, y):#更新b
    return b + a*y

def kind_y(y):#iris數據集是三分類的,類型爲0,1,2.
    if y==0:
        return -1
    else:
        return 1


iris = load_iris()
w = [1, 1]
w, b = l(iris, w, 0, 0.1)
print(w,b)

 


感知機結束… …
 
鏈接:[ 全文章目錄 ]

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