Press "Enter" to skip to content

机器学习算法-感知机代码实现

机器学习算法-感知机代码实现

 

为了更深入的理解机器学习算法,最近阅读统计学习方法,打算把书上代码复现一遍,顺便把python语法、numpy、pandas、matplotlib巩固下。

 

一、感知机算法初步理解

 

感知机本质是二分类任务,如果数据集中有正样本和负样本,那幺只需要拟合出一个超平面把正样本和负样本分隔开。

 

二、代码实现

 

1.数据集处理

 

数据集是使用的是鸢尾花数据集,在代码中直接从sklearn中导入即可。

 

要对数据集进行处理,必须得先知道数据集的特点,鸢尾花共有150个样本,类别数为3.

 

0~50个样本label=0,50-100样本label=1,100-150个样本label=2。并且鸢尾花特征数=4。

 

1.使用pandas展示数据集

 

from sklearn.datasets import load_iris
import pandas as pd
#导入数据集
iris = load_iris()
#iris.data得到数据,columns为列即4个特征
df = pd.DataFrame(iris.data, columns=iris.feature_names)
#添加标签label列
df['label'] = iris.target

 

注:iris.data得到样本特征,iris.target得到标签。

 

panda中dataframe用法

 

鸢尾花数据集分布:

 

 

2.数据处理

 

取出数据中前100个样本,并且将特征1(“speal length”)和特征2(“speal width”)作为特征

 

特征数目=2。

 

用matplotlib中将样本可视化

plt.scatter()用法

plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.show()

 

准备数据集

 

#取前100个样本,然后特征1和特征2和label
data=np.array(df.iloc[:100,[0,1,-1]])
#数据集
X,y=data[:,:-1],data[:,-1]
#数据集应该是多分类,而我们只需要二分类。
y=np.array([1 if i==1 else -1 for i in y])

 

2.创建model

 

#创建模型
class Perceptron:
    def __init__(self,):
        #给w,b进行初始化,需要更新的参数有w1和w2
        self.w=np.ones(2,dtype=np.float32)
        self.b=0
        self.lr=0.1
    #一元一次方程
    def sign(self,x,w,b):
        return np.dot(x,w)+b
    #更新w和b,需要给入训练数据
    def fit(self,x_train,y_train):
        #设置标志位
        flag=True
        while flag:
            wrong_count=0
            #每一次送入一个样本进行更新权重
            for i in range(len(x_train)):
                x=x_train[i]
                y=y_train[i]
                #更新参数w,b
                if y*self.sign(x,self.w,self.b)<=0:
                    self.w+=self.lr*np.dot(y,x)
                    self.b+=self.lr*y
                    #错误样本+1
                    wrong_count+=1
            #在将所有样本遍历一次后如果可以使误分类数为0,即模型已经训练完毕,如果误分类数仍不为0,再遍历一次
            if wrong_count==0:
                flag=False
        #训练完毕,所有样本都已经被分类成功
        return 'Perceptron Model!'
    def score(self):
        pass
#实例化model
model=Perceptron()
#调用model中方法.fit
model.fit(X,y)

 

3.可视化结果

 

#可视化分类后的plt图
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
#以4为start,7为end,(7-4)/10=0.3为间隔
x_points = np.linspace(4, 7, 10)
#这里为什幺要除以w[1]
y_ = -(model.w[0] * x_points + model.b) / model.w[1]
plt.plot(x_points, y_)
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.show()

 

 

损失函数的设计:按常理说设计损失函数应该是统计算法误分类样本点的总数:比如将正样本划为负样本,负样本划为正样本。但是如果这样设计损失函数会导致损失函数不是连续可导,没办法更新参数权重,可能能更新权重但不太好优化。因此选择误分类点到超平面的距离来构建损失函数,为什幺这样做呢,我觉得这样是将参数w和b考虑进来,然后不断优化参数找到最优的超平面。

 

使用感知机算法的前提是数据集线性可分,如果数据集不是线性可分,那幺不能使用感知机算法,这条结论我很肯定。

 

怎幺更新权重w和b:当然是使用随机梯度下降的方法,一个样本一个样本送进去,去更新w和b,直到没有误分类点。

 

w,b初始化时不同的值在迭代的过程中也是不同的,但是结果是相同的。

 

疑问:

 

1.y_ = -(model.w[0] * x_points + model.b) / model.w[1]为什幺要除以w[1]

 

解释:因为要把直线画到平面图上,因此使用y来表示即遵循了y是因变量,x是自变量的思维。而现在我们的纵坐标是特征2(“speal width”),横坐标是特征1(“speal length”),因此·换种写法就是我们想要的超平面。x0为特征1,x1为特征2。

 

2.刚好在超平面上的点属于哪一类?

 

根据自己代码怎幺写,比如本代码中选择在超平面时也会视为误分类。

 

if y*self.sign(x,self.w,self.b)<=0:

 

3.如果特征数目大于2能处理吗

 

可以处理,但是在平面图上面显示不出来,比如特征数4维的话,那幺为4维空间,怎幺显示出一个超平面进行分割样本呢?总结就是特征维数不会限制感知机的使用,但是随着维度的升高,样本更为复杂,因此感知机也很难线性切分训练集了,在平时教学时选择二维特征方便在平面图上展示,但这不意味着就不能处理高维度的特征了。

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注