绘制分类图

绘制分类图,第1张

逻辑回归是一种线性分类器,能够把线性可分的数据集划分为两类。

1、绘制网格线


这条绿色的直线称为决策边界。
也可以通过下面这种分区图更加清晰地展现分类的结果。

这个图是这样实现的:先把这个平面分为很多小的网格,然后将分类直线上面的网格都是用粉色填充,分类直线下面的网格用绿色填充。

在Python中,生成网格坐标矩阵可以使用 Numpy 中的 meshgrid 函数,
填充网格可以使用plt中的 pcolomesh 函数。

举例如下:

import numpy as np
import matplotlib.pyplot as plt

n = 10

x = np.linspace(-10, 10, n)  # 生成一个一维数组,10个元素
y = np.linspace(-10, 10, n)
print(x)
"""
[-10.          -7.77777778  -5.55555556  -3.33333333  -1.11111111
   1.11111111   3.33333333   5.55555556   7.77777778  10.        ]
"""
print(y)
"""
[-10.          -7.77777778  -5.55555556  -3.33333333  -1.11111111
   1.11111111   3.33333333   5.55555556   7.77777778  10.        ]
"""

X, Y = np.meshgrid(x, y)  # 生成网格坐标矩阵
Z = X+Y  # 利用每个点的坐标来计算Z,这个函数

plt.pcolormesh(X, Y, Z, cmap="rainbow")  # 使用色彩方案"rainbow"来填充网格,这个函数的前两个参数用来确定网格位置,第三个参数的值决定网格的颜色。第四个参数是指定所使用的颜色方案。它通过色彩的变化展现出Z的变化。

plt.show()

运行代码,

如果划分出更多的网格,就可以得到更加细腻的色彩过渡。
下图为n = 200时候的运行结果:

这个rainbow色彩方案不仅包括了彩虹中的七种基本颜色,还有各种颜色之间的过渡颜色。因此看起来色彩过渡非常的平滑自然。

也可以使用自己定义的色彩方案:

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

n = 200

x = np.linspace(-10, 10, n)  # 生成一个一维数组,10个元素
y = np.linspace(-10, 10, n)

X, Y = np.meshgrid(x, y)
Z = X+Y

cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0"])
plt.pcolormesh(X, Y, Z, cmap=cm_bg)

plt.show()

因为只有两种颜色,所以就按照 Z 的值把绘图区域平均划分为两个部分。
运行结果如下:

如果色彩方案中定义了三种颜色:

cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0", "#660000"])

那么就按照 Z 的值平均分为三部分。

2、绘制轮廓线
plt.contour()

它可以根据 Z 的取值绘制出相同取值的边界。

例如,

import numpy as np
import matplotlib.pyplot as plt

n = 10

x = np.linspace(-10, 10, n)  # 生成一个一维数组,10个元素
y = np.linspace(-10, 10, n)

X, Y = np.meshgrid(x, y)  # 生成网格坐标矩阵
Z = X+Y  # 利用每个点的坐标来计算Z,这个函数

plt.contour(X, Y, Z, cmap="rainbow")
# 使用色彩方案"rainbow"来填充网格,这个函数的前两个参数用来确定网格位置,
# 第三个参数的值决定网格的颜色。第四个参数是指定所使用的颜色方案。
# 它通过色彩的变化展现出Z的变化。
plt.show()

运行结果如下:

在没一条线上 Z 的取值都是相同的。可以理解为三维模型中的等高线。
例如:这里将 Z =X2 + Y2

import numpy as np
import matplotlib.pyplot as plt

n = 200

x = np.linspace(-10, 10, n)  # 生成一个一维数组,10个元素
y = np.linspace(-10, 10, n)

X, Y = np.meshgrid(x, y)  # 生成网格坐标矩阵
Z = X**2+Y**2  # 利用每个点的坐标来计算Z,这个函数

plt.contour(X, Y, Z, cmap="rainbow")
# 使用色彩方案"rainbow"来填充网格,这个函数的前两个参数用来确定网格位置,
# 第三个参数的值决定网格的颜色。第四个参数是指定所使用的颜色方案。
# 它通过色彩的变化展现出Z的变化。
plt.show()

运行结果如下:

如果要给等高线之间的区域填充颜色,可以使用 contourf 函数。这个增加的 ’ f ’ 表示的就是填充的意思。

plt.contourf(X, Y, Z, cmap="rainbow")


还可以在这个函数中添加参数,来指定颜色细分的数量。

plt.contourf(X, Y, Z, 20, cmap="rainbow")

下图为绘图的结果

可以看到图中分区的数量增加了。此外,还可以使用这个函数来代替填充网格时使用的plt的 pcolomesh 函数。

3、展示分类的效果

机器学习的分类任务中,经常使用下面这些函数以可视化的形式来展示分类的效果。

生成网格坐标矩阵:np.meshgrid() 
绘制分类图:pcolormesh()/plt.contourf()

例如,下面这个示例,我们要根据 Z 的取值把样本分为两类。

import numpy as np
import matplotlib as mpl
import tensorflow as tf
import matplotlib.pyplot as plt

n = 200

x = np.linspace(-10, 10, n)  # 生成一个一维数组,10个元素
y = np.linspace(-10, 10, n)

X, Y = np.meshgrid(x, y)  # 生成网格坐标矩阵
Z = X+Y  # 利用每个点的坐标来计算Z,这个函数

cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0"])
Z = tf.where(Z < 0, 0, 1)  # 根据Z的取值将 Z 的转化为 01
plt.pcolormesh(X, Y, Z, cmap=cm_bg)
plt.show()

运行结果如下:

4、根据鸢尾花分类模型,绘制训练集的分类图

在模型训练好后,根据鸢尾花分类模型,绘制训练集的分类图。

import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib as mpl
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False

# 第一步:加载数据集
TRAIN_URL = "http://download.tensorflow.org/data/iris_training.csv"
train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL)
df_iris_train = pd.read_csv(train_path, header=0)  # 表示第一行数据作为列标题

iris_train = np.array(df_iris_train)  # 将二维数据表转换为 Numpy 数组, (120, 5), iris的训练集中有120条样本,

train_x = iris_train[:, 0:2]  # 取出鸢尾花训练数据集的前两列属性值
train_y = iris_train[:, 4]  # 取出最后一列作为标签值, (120,)

# 2.3 提取山鸢尾和变色鸢尾 (标签值——品种, 0 —— 山鸢尾、1 —— 变色鸢尾、2 —— 维吉尼亚鸢尾)
x_train = train_x[train_y < 2]  # 提取出标签值为 01 的样本
y_train = train_y[train_y < 2]
print(x_train.shape, y_train.shape)  # (78, 2) (78,)

num_train = len(x_train)  # 78

# 2.6 数据归一化
print(np.mean(x_train, axis=0))  # axis=0, 计算数组中每一列的均值
# [5.42692308 3.1025641 ]

# 可以看出这两个属性的尺寸相同,因此不需要进行归一化,可以直接对其进行中心化处理
# 对每个属性进行中心化, 也就是按列中心化, 所以使用下面这种方式
x_train = x_train-np.mean(x_train, axis=0)

# 2.8 生成多元模型的属性矩阵和标签列向量
x0_train = np.ones(num_train).reshape(-1, 1)  # (78, 1)
# 改变张量中元素的数据类型函数 tf.cast()
# 拼接就是将多个张量在某个维度上合并,在TensorFlow中使
# 用tf.concat()函数来拼接张量, 拼接并不会产生新的维度。
X_train = tf.cast(tf.concat((x0_train, x_train), axis=1), tf.float32)
Y_train = tf.cast(y_train.reshape(-1, 1), tf.float32)

# 第三步:设置超参数和显示间隔
learn_rate = 0.2
itar = 120

display_step = 30

# 第四步:设置模型参数初始值
np.random.seed(612)
# 这里的W是一个列向量
W = tf.Variable(np.random.randn(3, 1), dtype=tf.float32)

# 第五步:训练模型
cross_train = []  # 列表cross_train用来保存每一次迭代的交叉熵损失
acc_train = []  # 用来存放训练集的分类准确率

cross_test = []  # 列表cross_test用来保存每一次迭代的交叉熵损失
acc_test = []  # 用来存放测试集的分类准确率

for i in range(0, itar + 1):

    with tf.GradientTape() as tape:
        # Sigmoid 函数
        # 属性矩阵X和参数向量W相乘的结果是一个列向量
        # X - (78, 3), W - (3, 1) , 所以 Pred_train - (78, 1), 是每个样本的预测概率
        Pred_train = 1 / (1 + tf.exp(-tf.matmul(X_train, W)))
        # 计算平均交叉熵损失函数
        Loss_train = -tf.reduce_mean(Y_train * tf.math.log(Pred_train) + (1 - Y_train) * tf.math.log(1 - Pred_train))

    # 计算准确率函数 -- 因为不需要对其进行求导运算, 因此也可以把这条语句写在 with 语句的外面
    Accuarcy_train = tf.reduce_mean(tf.cast(tf.equal(tf.where(Pred_train.numpy() < 0.5, 0., 1.), Y_train), tf.float32))

    # 记录每一次迭代的交叉熵损失和准确率
    cross_train.append(Loss_train)
    acc_train.append(Accuarcy_train)

    # 对交叉熵损失函数分别对 w 和 b 求偏导
    dL_dW = tape.gradient(Loss_train, W)
    # 更新模型参数
    W.assign_sub(learn_rate * dL_dW)

    if i % display_step == 0:
        print("i: %i, TrainLoss: %f, TrainAccuracy: %f"
              % (i, Loss_train, Accuarcy_train))

M = 300

x1_min, x2_min = x_train.min(axis=0)
x1_max, x2_max = x_train.max(axis=0)

t1 = np.linspace(x1_min, x1_max, M)  # 使用花萼的长度的取值范围作为横坐标的取值范围
t2 = np.linspace(x2_min, x2_max, M)  # 使用花萼的宽度的取值范围作为纵坐标的取值范围

m1, m2 = np.meshgrid(t1, t2)  # 使用它们来生成网格坐标矩阵
print(m1.shape)  # (300, 300)
print(m2.shape)  # (300, 300)

# 生成多元线性模型需要的属性矩阵
m0 = np.ones(M*M)  # 生成元素全为1的一位数组

# 在TensorFlow中使用tf.stack函数来实现张量的堆叠。
# tf.stack(values,axis)
# 合并张量时,创建一个新的维度。
X_mesh = tf.cast(np.stack((m0, m1.reshape(-1), m2.reshape(-1)), axis=1), dtype=tf.float32)
print(X_mesh.shape)  # (90000, 3)
Y_mesh = tf.cast(1/(1+tf.exp(-tf.matmul(X_mesh, W))), dtype=tf.float32)
# 根据Sigmoid公式计算所有网格点对应的函数值
Y_mesh = tf.where(Y_mesh < 0.5, 0, 1)
# 把它们转化为分类结果01,作为填充依据

n = tf.reshape(Y_mesh, m1.shape)
# 对其进行维度变化,让他和 m1 和 m2 具有相同的形状
# 这是 pcolormesh 函数对参数的要求

cm_pt = mpl.colors.ListedColormap(["blue", "red"])
cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0"])

plt.xlim(-1.2, 1.7)
plt.ylim(-1.2, 1.5)
plt.pcolormesh(m1, m2, n, cmap=cm_bg)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train, cmap=cm_pt)
# 需要注意的是,plt 中的绘图是有层次的,这里要首先绘制分区图作为背景。
# 然后,在它的上面绘制散点图。否则散点图会被分区图遮盖住。

plt.show()

运行结果如下:

这里分类边界的位置和之前绘制的决策边界相同,需要注意的是,plt 中的绘图是有层次的,这里要首先绘制分区图作为背景。然后,在它的上面绘制散点图。否则散点图会被分区图遮盖住。

从上面的点可以看出,有些点没有显示完全,是因为坐标轴的原因,因此,可以调整下坐标轴的范围。

plt.xlim(-1.2, 1.7)
plt.ylim(-1.2, 1.5)

运行结果如下:

5、根据鸢尾花分类模型,绘制测试集的分类图

采用同样的方法,也可以绘制出测试集的分区图。

import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib as mpl
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = "SimHei"
plt.rcParams['axes.unicode_minus'] = False

# 第一步:加载测试集数据集
TEST_URL = "http://download.tensorflow.org/data/iris_test.csv"
test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL)
df_iris_test = pd.read_csv(test_path, header=0)

# 第二步:数据处理
# 2.1 转化为NumPy数组
iris_test = np.array(df_iris_test)  # 将二维数据表转换为 Numpy 数组, (30, 5), iris的测试集中有30条样本

# 2.2 提取属性和标签
test_x = iris_test[:, 0:2]  # 取出鸢尾花训练数据集的前两列属性值
test_y = iris_test[:, 4]  # 取出最后一列作为标签值, (30, )

# 2.3 提取指定标签的样本
x_test = test_x[test_y < 2]  # 提取出标签值为 01 的样本
y_test = test_y[test_y < 2]
print(x_test.shape, y_test.shape)  # (22, 2) (22,)

# 2.4 数据归一化
# 可以看出这两个属性的尺寸相同,因此不需要进行归一化,可以直接对其进行中心化处理
# 对每个属性进行中心化, 也就是按列中心化, 所以使用下面这种方式
x_test = x_test-np.mean(x_test, axis=0)
# 此时样本点的横坐标和纵坐标的均值都是0

# 第三步:加载训练集得到的模型参数 (这是迭代510次后的结果)
W = np.array([[-0.04554737],
              [4.1817145],
              [-4.330421]], dtype='float32')

# 第四步:绘制分类图
M = 300

x1_min, x2_min = x_test.min(axis=0)
x1_max, x2_max = x_test.max(axis=0)

# linspace的功能是创建等差数列
t1 = np.linspace(x1_min, x1_max, M)  # 使用花萼的长度的取值范围作为横坐标的取值范围
t2 = np.linspace(x2_min, x2_max, M)  # 使用花萼的宽度的取值范围作为纵坐标的取值范围

m1, m2 = np.meshgrid(t1, t2)  # 使用它们来生成网格坐标矩阵
print(m1.shape)  # (300, 300)
print(m2.shape)  # (300, 300)

# 生成多元线性模型需要的属性矩阵
m0 = np.ones(M * M)  # 生成元素全为1的一位数组

# 在TensorFlow中使用tf.stack函数来实现张量的堆叠。
# 函数:tf.stack(values,axis)
# 合并张量时,创建一个新的维度。
X_mesh = tf.cast(np.stack((m0, m1.reshape(-1), m2.reshape(-1)), axis=1), dtype=tf.float32)
print(X_mesh.shape)  # (90000, 3)

# 根据Sigmoid公式计算所有网格点对应的函数值
Y_mesh = tf.cast(1 / (1 + tf.exp(-tf.matmul(X_mesh, W))), dtype=tf.float32)  # (90000, 1)

Y_mesh = tf.where(Y_mesh < 0.5, 0, 1)
# 把它们转化为分类结果01,作为填充依据

n = tf.reshape(Y_mesh, m1.shape)
# 对其进行维度变化,让它和 m1 和 m2 具有相同的形状
# 这是 pcolormesh 函数对参数的要求

cm_pt = mpl.colors.ListedColormap(["blue", "red"])
cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0"])

plt.xlim(-1.4, 1.2)
plt.ylim(-0.95, 1.2)
plt.pcolormesh(m1, m2, n, cmap=cm_bg)
plt.scatter(x_test[:, 0], x_test[:, 1], c=y_test, cmap=cm_pt)
# 需要注意的是,plt 中的绘图是有层次的,这里要首先绘制分区图作为背景。
# 然后,在它的上面绘制散点图。否则散点图会被分区图遮盖住。

plt.show()

运行结果如下:

需要注意的是,

1、样本集中有22个点,但是散点图中只有21个点,那是因为有相同点重叠的情况。

2、使用绘制训练集和测试集分类图的代码有大部分是相同的,因此,可将其封装成一个函数,然后进行调用,在实现多分类一节的笔记中实现。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/904560.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-15
下一篇 2022-05-15

发表评论

登录后才能评论

评论列表(0条)

保存