机器学习入门-python实现感知器算法

2023-10-21 11:17:01 浏览数 (1)

感知器学习规则

感知器算法可以总结为以下步骤

  • 把权重初始化为0或者小的随机数
  • 分别对每个训练样本x(i)计算输出值y`(i),更新权重。

输出值为预先定义好的单位阶跃函数预测的分类标签,同时更新权重w的每个值w(j):

代码语言:javascript复制
w(j): = w(j)   △w(j)

其中△w(j)用于更新w(j)的值,该值计算(eta为学习速率,一般为0-1之间的常数):

代码语言:javascript复制
△w(j) = eta*(y(i) - y`(i))*x(ij)

更具体的描述可以看Python机器学习(原书第三版本)。

面向对象的感知机API

代码语言:javascript复制
import numpy as np
import os
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap


# 感知器对象
class Perceptron(object):

    def __init__(self, eta=0.01, n_iter=50, random_state=1):
        self.eta = eta
        self.n_iter = n_iter
        self.random_state = random_state

    def fit(self, X, y):
        rgen = np.random.RandomState(self.random_state)
        self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1  
                              X.shape[1])  # 产生标准差为0.01的正态分布
        self.errors_ = []

        # 训练数据集样本,更新权重
        for _ in range(self.n_iter):
            errors = 0
            for xi, target in zip(X, y):
                update = self.eta * (target - self.predict(xi))
                self.w_[1:]  = update * xi
                self.w_[0]  = update
                errors  = int(update != 0.0)
            self.errors_.append(errors)
        return self

eta为学习速率,n_iter为学习次数(遍历数据集的次数)来初始化新的感知器对象。用fit()方法初始化权重向量,该向量来源于标准差为0.01的正态分布的小随机数。error_数组用于存储学习过程中的预测错误。之后遍历数据集样本,调用predict()函数预测分类标签,然后用我们上面提到的方式来更新权重向量。predict()函数调用net_input()函数计算样本特征向量与权重向量的点积,与阈值进行比较,对样本数据进行预测。

代码语言:javascript复制
def net_input(self, X):
        return np.dot(X, self.w_[1:])   self.w_[0]  # 计算向量点积
def predict(self, X):
        return np.where(self.net_input(X) >= 0.0, 1, -1)  # 当第一个参数成立时返回1,否则返回-1

在鸢尾花数据集上训练感知器模型

首先,用pandas库从UCI机器学习库中把鸢尾花数据集直接加载到DataFrame对象

代码语言:javascript复制
# 获取数据集
s = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
print('URL:', s)

df = pd.read_csv(s,
                 header=None,
                 encoding='utf-8')

df.tail()

接下来提取50朵山鸢尾花和50朵变色鸢尾花对应的100个分类标签

代码语言:javascript复制
# 截取前一百个品种
y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)

# 截取前一百个数据的第一和第三个数据
X = df.iloc[0:100, [0, 2]].values

然后绘制这100组数据的散点图。

代码语言:javascript复制
# 绘制散点图
plt.scatter(X[:50, 0], X[:50, 1],
            color='red', marker='o', label='setosa')
plt.scatter(X[50:100, 0], X[50:100, 1],
            color='blue', marker='x', label='versicolor')

plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')

# 展示散点图
plt.show()

执行前面的代码可以看到如下图所示的二维散点图,从这个二维特征子空间可以看出一个线性的决策边界足以把山鸢尾花和变色鸢尾花区分开来。因此,像感知器这样线性分类器一个能够完美地对数据集中的花朵进行分类、

接下来,我们需要使用鸢尾花数据集训练感知器,迭代更新权重向量。并绘制每次迭代的分类错误,用以检查算法是否收敛,并找到决策分类边界。

实例化感知器对象,调用fit()函数训练数据集更新权重向量。

代码语言:javascript复制
ppn = Perceptron(eta=0.1, n_iter=20)
ppn.fit(X, y)

然后绘制分类错误与迭代次数的折线图,

代码语言:javascript复制
plt.plot(range(1, len(ppn.errors_) 1), ppn.errors_, marker='o')
plt.xlabel("epochs")
plt.ylabel("number of updates")
plt.show()

根据绘制的折线图,感知器在第六次迭代后开始收敛,这样我们就能完美地对训练样本集进行分类了。

接下来我们通过ListedColormap根据颜色列表来定义一些颜色、标记并创建色度图。然后确定两个特征值的最大值和最小值。通过调用Numpy的meshgrid函数,利用特征向量创建网格数组对xx1和xx2,相当于两个n*n矩阵。

xx1矩阵每一行都为从x1_min到x1_max步距为resolution的向量,相当于x轴在y轴每一层的映射。xx2矩阵每一列都为从x2_min到x2_max步距为resolution的向量,相当于y轴在x轴每一层的映射。

一个简单的例子,假设为3*3的矩阵。

然后使用ravel()函数将两个矩阵降维,变成两个n*n维向量,然后组合成一个n^2*2的矩阵,最后转置。

然后调用predict()对这个n^2*2的矩阵的样本集预测,得到n*n维向量,再将预测结果升维成xx1的n*n矩阵,这样就得到坐标系的预测结果了。

下面是转化的简单例子。

代码语言:javascript复制
[
1 3
2 3
3 3
1 2
2 2
3 2
1 1
2 1
3 1
]
预测变为
[
1
-1
1
-1
1
1
-1
1
-1
]
升维
[
1 -1 1
-1 1 1
-1 1 -1
]

最后使用plt.contourf()绘制决策边界并填充色彩。

代码语言:javascript复制
def plot_decision_regions(X, y, classifier, resolution=0.02):

    # 定义一些颜色和标记并创建色度图
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # 确定两个特征的最小值和最大值
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max()   1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max()   1
    # 创建网格数组对,步距为resolution
    # 生成数据点
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    # 预测数据点类型,对不同数据点进行标记
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    # 绘制决策边界
    plt.contourf(xx1, xx2, Z, alpha=0.3, cmap="cool")
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    # plot class examples
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0],
                    y=X[y == cl, 1],
                    alpha=0.8,
                    c=colors[idx],
                    marker=markers[idx],
                    label=cl,
                    edgecolor='black')

plot_decision_regions(X, y, classifier=ppn)
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')

#展示决策区域
plt.show()

决策边界图绘制如图所示

测试

这样我们的感知器就训练完毕了,我们可以输入萼片长度和花瓣长度然后使用这个感知器模型来预测鸢尾花的品种了。

完整代码

代码语言:javascript复制
import numpy as np
import os
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap


# 感知器对象
class Perceptron(object):

    def __init__(self, eta=0.01, n_iter=50, random_state=1):
        self.eta = eta
        self.n_iter = n_iter
        self.random_state = random_state

    def fit(self, X, y):
        rgen = np.random.RandomState(self.random_state)
        self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1  
                              X.shape[1])  # 产生标准差为0.01的正态分布
        self.errors_ = []

        # 训练数据集样本,更新权重
        for _ in range(self.n_iter):
            errors = 0
            for xi, target in zip(X, y):
                update = self.eta * (target - self.predict(xi))
                self.w_[1:]  = update * xi
                self.w_[0]  = update
                errors  = int(update != 0.0)
            self.errors_.append(errors)
        return self

    def net_input(self, X):
        return np.dot(X, self.w_[1:])   self.w_[0]  # 计算向量点积

    def predict(self, X):
        return np.where(self.net_input(X) >= 0.0, 1, -1)  # 当第一个参数成立时返回1,否则返回-1


# 获取数据集
s = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
print('URL:', s)

df = pd.read_csv(s,
                 header=None,
                 encoding='utf-8')

df.tail()

# 截取前一百个品种
y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)

# 截取前一百个数据的第一和第三个数据
X = df.iloc[0:100, [0, 2]].values

# 绘制散点图
plt.scatter(X[:50, 0], X[:50, 1],
            color='red', marker='o', label='setosa')
plt.scatter(X[50:100, 0], X[50:100, 1],
            color='blue', marker='x', label='versicolor')

plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')

# 展示散点图
plt.show()


# 使用鸢尾花数据集训练感知机,绘制每次迭代的分类错误
ppn = Perceptron(eta=0.1, n_iter=20)
ppn.fit(X, y)
plt.plot(range(1, len(ppn.errors_) 1), ppn.errors_, marker='o')
plt.xlabel("epochs")
plt.ylabel("number of updates")
plt.show()
# 在第六次迭代后开始收敛

# 完成二维数据集的决策边界的可视化


def plot_decision_regions(X, y, classifier, resolution=0.02):

    # 定义一些颜色和标记并创建色度图
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # 确定两个特征的最小值和最大值
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max()   1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max()   1
    # 创建网格数组对,步距为resolution
    # 生成数据点
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    # 预测数据点类型,对不同数据点进行标记
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T) #.T对矩阵转置
    print(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    # 绘制决策边界
    plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    # plot class examples
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0],
                    y=X[y == cl, 1],
                    alpha=0.8,
                    c=colors[idx],
                    marker=markers[idx],
                    label=cl,
                    edgecolor='blue')


plot_decision_regions(X, y, classifier=ppn)
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')


#展示决策区域
plt.show()
while(True):
    myDate = []
    val1 = float(input("请输入萼片长度:"))
    myDate.append(val1)
    val2 = float(input("请输入花瓣长度:"))
    myDate.append(val2)
    if(ppn.predict(myDate) == 1):
        print("品种为变色鸢尾花")
    else:
        print("品种为山鸢尾花")

备忘录

代码语言:javascript复制
#np.meshgrid的用法
如: x = [1, 2, 3, 4]
      y = [7, 8, 9]
   
x和y中的每一个元素组合生成
[[[1, 7], [2, 7], [3, 7], [4, 7]],
 [[1, 8], [2, 8], [3, 8], [4, 8]],
 [[1, 9], [2, 9], [3, 9], [4, 9]]]
 
然后
再分别放入X和Y中
X = [[1, 2, 3, 4],
	 [1, 2, 3, 4],
	 [1, 2, 3, 4]]

Y = [[7, 7, 7, 7],
	 [8, 8, 8, 8],
	 [9, 9, 9, 9],]
代码语言:javascript复制
np.dot(vec1, vec2) #计算向量点积

np.where(a, 1, -1)  # 当第一个参数成立时返回1,否则返回-1
代码语言:javascript复制
#np.arange()的用法

#一个参数 默认起点0,步长为1 输出:[0 1 2]
a = np.arange(3)

#两个参数 默认步长为1 输出[3 4 5 6 7 8]
a = np.arange(3,9)

#三个参数 起点为0,终点为3,步长为0.1 输出[ 0.   0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1.   1.1  1.2  1.3  1.4 1.5  1.6  1.7  1.8  1.9  2.   2.1  2.2  2.3  2.4  2.5  2.6  2.7  2.8  2.9]
a = np.arange(0, 3, 0.1)
代码语言:javascript复制
#ravel()函数的用法

a.ravel()#将数组a降维成一维数组

0 人点赞