# 导入必要的库,类和方法
#机器学习第一个实验:感知机模型,自己按照算法编写程序
import pandas as pd #导入 pandas 库并定义一个别名 pd
import numpy as np #导入 numpy 库并定义一个别名 np
from matplotlib.projections import projection_registry
from sklearn.datasets import load_iris #从 sklearn 库的 datasets 类中导入 load_iris方法
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt #从 matplotlib 中导入 pyplot 类并定义一个别名 plt
# 从 sklearn 库中导入 iris 数据集
iris = load_iris() # 导入鸢尾花数据
# iris 是一个字典,包含'data','target','target_names'等键
# 键'data'对应的值是一个二维 array,其每一个一维 array 对应着一个实例(特征向量)
# 'target'对应的值是一个一维 array,表示样本标签,共有 0,1,2 三种取值
# 'target_names'对应的值是一个一维 array,共有'setosa', 'versicolor', 'virginica' 三种取值,表示三种鸢尾花的名称
df = pd.DataFrame(iris.data, columns=iris.feature_names)
# 将鸢尾花的'data'键对应的值(实例(特征向量))转化成数据框,查看数据更直观
# print(df.describe())
# print(iris.DESCR)
df['label'] = iris.target
# 在数据框中添加一列,列名称为‘label’,该列存储的是样本标签
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
# 对列名进行重命名
df.label.value_counts()
# 返回数据中每种标签的数据个数
# 删除数据框中标签为 2 的数据
df.drop(index=df[df.label == 2].index, axis=0, inplace=True)
# 使用.drop 方法,其中.index 表示提取数据框中某些行的 index(行号),axis=0 表示删除行,axis=1 表示删除列
# inplace=True 表示删除某些行(列)后,用删除后的数据框替换原有数据框,如果没有inplace=True,则原数据框不会发生变化
# df = df[df.label!=2]
# 删除数据框中标签为 2 的数据的第二种方式,即提取出标签不为 2 的数据构成一个新的数据框
# 从四个特征中选取三个作为横、纵和竖坐标对数据进行可视化,
# 以三个特征为例构建感知机模型,便于可视化超平面
fig = plt.figure() #d打开一个图像窗口
ax = fig.add_subplot(111, projection='3d') # 创建一个三维的绘图工程
ax.scatter(df[df.label == 0]['sepal length'], df[df.label == 0]['sepal width'],
df[df.label == 0]['petal length'], c='b', label='0')
# c='b'是设置颜色为蓝色
ax.scatter(df[df.label == 1]['sepal length'], df[df.label == 1]['sepal width'],
df[df.label == 1]['petal length'], c='r', label='1')
ax.set_xlabel('sepal length') # 添加 x 轴名称
ax.set_ylabel('sepal width') # 添加 y 轴名称
ax.set_zlabel('petal length') # 添加 z 轴名称
ax.legend(loc='upper right')
loc='upper right'#表示放在右上角
# plt.show()
# (7)数据格式转换便于后面计算
Feture = np.array(df.iloc[:, :3])
# 提取前三个特征构成一个新的数据框 同时转化数据格式便于后面计算
Label = df.iloc[:, -1]
Label = np.array(Label.apply(lambda x: -1 if x == 0 else 1))
# 将标签 0 转化为-1,因为感知机的输出只有-1 和+1,同时转化数据格式便于后面计算
# (8)定义预测函数 y =sign(WX+b)
def pred_sign(x, w, b):
y_pred = np.sign( np.dot(x, w) + b )
# Numpy 中 dot()函数主要功能有两个:向量点积和矩阵乘法。
# 格式:x.dot(y) 等价于 np.dot(x,y) ———x 是 m*n 矩阵 ,y 是 n*m 矩阵,则x.dot(y)得到m * m矩阵
# np.sign 是符号函数,若 x>0,则 np.sign(x)=1,若 x==0,则 np.sign(x)=0,若 x<0,则np.sign(x) = -1
return y_pred
# 定义随机选择误分类点的函数
def rand_selc(mis_class):
# 该函数的作用是从误分类点构成的集合中随机选择一个点,用其更新 w 和 b
num_mis = len(mis_class) # 误分类点个数
mis_selc = mis_class[np.random.randint(0, num_mis)]
# np.random.randint(0, num_mis)是产生一个[0,num_mis-1]的随机整数
return mis_selc
# 定义使用随机梯度下降法对 W 和 b 更新的函数
def fit(x_train, y_train): # 参数拟合 X_train 为特征向量矩阵 y_train 输出向量
w = np.zeros(x_train.shape[1], dtype=np.float32)
b = 0
l_rate = 0.1
max_iter = 1000
flag_train = 0
for i in range(max_iter):
mis_class = []
for j in range(len(y_train)):
if y_train[j] * pred_sign(x_train[j, :], w, b) <= 0:
mis_class.append(j)
if len(mis_class) > 0:
mis_selc = rand_selc(mis_class)
w = w + l_rate * y_train[mis_selc] * x_train[mis_selc]
b = b + l_rate * y_train[mis_selc]
else:
flag_train = 1
break
return w, b, flag_train
def CrossVali(k_cv, state_rand, Feture, Label): # 自定义交叉验证函数
from sklearn.model_selection import KFold # 用于 K 折交叉验证时对数据的划分
kf = KFold(n_splits=k_cv, random_state=state_rand, shuffle=True)
# k_cv 是 k 折交叉验证的 k
# state_rand 随机数的 seed,是为了可重现,运行两次交叉验证时,如果 state_rand设置为同一个值
# 则对数据集的划分是相同的,因此得到的结果也是相同的
# Feture 是样本的实例(特征向量构成的 array)
# Label 是样本的标签构成 array
score_acc = [] # 准确率
score_p = [] # 精确率(查准率)
score_recall = [] # 召回率(查全率)
score_f1 = [] # F1 值
for train_index, valid_index in kf.split(Feture):
w, b, flag_train = fit(Feture[train_index, :], Label[train_index])
y_pred = pred_sign(Feture[valid_index, :], w, b)
# 计算准确率
acc = sum(y_pred == Label[valid_index]) / len(y_pred)
score_acc.append(acc)
# 计算精确率、召回率和 F1 值
TP = np.sum((y_pred == 1) & (Label[valid_index] == 1))
FP = np.sum((y_pred == 1) & (Label[valid_index] == -1))
TN = np.sum((y_pred == -1) & (Label[valid_index] == -1))
FN = np.sum((y_pred == -1) & (Label[valid_index] == 1))
if TP + FP > 0:
precis = TP / (TP + FP)
else:
precis = 0
if TP + FN > 0:
recall = TP / (TP + FN)
else:
recall = 0
if precis + recall > 0:
F1 = 2 * precis * recall / (precis + recall)
else:
F1 = 0
score_p.append(precis)
score_recall.append(recall)
score_f1.append(F1)
# 打印交叉验证结果
print(f"交叉验证结果 (k={k_cv}):")
print(f"平均准确率: {np.mean(score_acc):.4f}")
print(f"平均精确率: {np.mean(score_p):.4f}")
print(f"平均召回率: {np.mean(score_recall):.4f}")
print(f"平均 F1 值: {np.mean(score_f1):.4f}")
return np.mean(score_acc), np.mean(score_p), np.mean(score_recall),np.mean(score_f1)
# (12)使用所有数据构建感知机模型
w, b, flag_train = fit(Feture, Label)
# (13)画出分离超平面
if flag_train == 1:
print('The Perceptron Model is successfully trained!')
# 二元函数定义域平面
x = np.linspace(min(0, np.min(Feture[:, 0])), np.max(Feture[:, 0]), 10)
# np.linspace(a, b, m)生成一个 array,其元素是[a,b]之间的等间隔的 m 个数
y = np.linspace(min(0, np.min(Feture[:, 1])), np.max(Feture[:, 1]), 10)
X, Y = np.meshgrid(x, y)
# 超级平面的一般方程为:w[0]*x+w[1]*y+w[2]*z+b=0
# 移项就是 z=-(w[0]*x+w[1]*y+b)/w[2]
if w[2] != 0:
Z = -(w[0] * X + w[1] * Y + b) / w[2]
else:
Z = np.zeros_like(X)
ax.plot_surface(X, Y, Z, color='g', alpha=0.6)
plt.show()
else:
print('警告:感知机模型在迭代次数内没有收敛!')
# 执行交叉验证
cv_acc, cv_precis, cv_recall, cv_f1 = CrossVali(5, 42, Feture, Label)
最新发布