机器学习算法-感知机代码实现
为了更深入的理解机器学习算法,最近阅读统计学习方法,打算把书上代码复现一遍,顺便把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得到标签。
鸢尾花数据集分布:
2.数据处理
取出数据中前100个样本,并且将特征1(“speal length”)和特征2(“speal width”)作为特征
特征数目=2。
用matplotlib中将样本可视化
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