绘制分类图

逻辑回归:鸢尾花二分类

在上节课中我们编程实现了逻辑回归,通过它能够识别出鸢尾花的种类。

线性分类器

决策边界

逻辑回归是一种线性分类器,能够把线性可分的数据集划分为两类,这条绿色的直线称为决策边界。
在这里插入图片描述
也可以通过这种分区图更加清晰的展现分类的结果。
在这里插入图片描述
绘制这个图的方法很简单,把这个平面分成很多小的网格。
在这里插入图片描述
分类直线上面的网格都使用粉色填充,直线下面的网格使用绿色填充就可以得到这样的图。

绘制分类图

生成网格坐标矩阵plt.meshgrid()

填充网格plt.pcolormesh()

生成网格坐标矩阵,可以使用numpy中的plt.meshgrid()的函数,填充网格颜色使用plt.pcolormesh()函数。

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

n = 10
"""首先生成两个一维数组,X和Y"""
# 在指定的间隔内返回均匀间隔的数字
x = np.linspace(-10, 10, n)
y = np.linspace(-10, 10, n)

"""利用它们生成二维网格坐标矩阵"""
X, Y = np.meshgrid(x, y)
"""利用每个网格点的坐标计算Z"""
Z = X + Y
"""使用pcolormesh()函数填充网格"""
# 这个函数中的前两个参数确定网格位置,第3个参数的值决定网格的颜色,第4个参数指定所使用的颜色方案
plt.pcolormesh(X, Y, Z, cmap="rainbow")

plt.show()

这是运行的结果
在这里插入图片描述
他通过色彩的变化展现出Z的值的变化。如果划分出更多的网格,就可以得到更加细腻的色彩过度。
这是将n改为200的效果。
在这里插入图片描述
这个rainbow的色彩方案不仅包括了彩虹中的7种基本颜色,还有各种颜色之间的过度颜色,因此看起来色彩过度非常的平滑自然。

也可以使用自己定义的色彩方案,在这里定义一个背景色色彩方案,其中只有两种颜色,就按照Z的值把绘图区域平均划分为两个部分。

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

n = 200
"""首先生成两个一维数组,X和Y"""
# 在指定的间隔内返回均匀间隔的数字
x = np.linspace(-10, 10, n)
y = np.linspace(-10, 10, n)

"""利用它们生成二维网格坐标矩阵"""
X, Y = np.meshgrid(x, y)
"""利用每个网格点的坐标计算Z"""
Z = X + Y
"""使用pcolormesh()函数填充网格"""
# 这个函数中的前两个参数确定网格位置,第3个参数的值决定网格的颜色,第4个参数指定所使用的颜色方案
cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0"])
plt.pcolormesh(X, Y, Z, cmap=cm_bg)

plt.show()

在这里插入图片描述
方案中定义了三种颜色,就按照Z的值平均划分为三部分。

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

n = 200
"""首先生成两个一维数组,X和Y"""
# 在指定的间隔内返回均匀间隔的数字
x = np.linspace(-10, 10, n)
y = np.linspace(-10, 10, n)

"""利用它们生成二维网格坐标矩阵"""
X, Y = np.meshgrid(x, y)
"""利用每个网格点的坐标计算Z"""
Z = X + Y
"""使用pcolormesh()函数填充网格"""
# 这个函数中的前两个参数确定网格位置,第3个参数的值决定网格的颜色,第4个参数指定所使用的颜色方案
# cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0"])
cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0", "#A0A0FF"])
plt.pcolormesh(X, Y, Z, cmap=cm_bg)

plt.show()

在这里插入图片描述

绘制轮廓线plt.contour()

另外拍pyplot中还提供了contour()函数,它可以根据Z的取值绘制出相同取值的边界。

例如这里把pcolormesh()函数,改为contour()函数。

import numpy as np
import matplotlib.pyplot as plt

n = 200
"""首先生成两个一维数组,X和Y"""
# 在指定的间隔内返回均匀间隔的数字
x = np.linspace(-10, 10, n)
y = np.linspace(-10, 10, n)

"""利用它们生成二维网格坐标矩阵"""
X, Y = np.meshgrid(x, y)
"""利用每个网格点的坐标计算Z"""
Z = X + Y
"""使用pcolormesh()函数填充网格"""
# 这个函数中的前两个参数确定网格位置,第3个参数的值决定网格的颜色,第4个参数指定所使用的颜色方案
plt.contour(X, Y, Z, cmap="rainbow")

plt.show()

这是运行的结果。
在这里插入图片描述
在每一条线上Z的取值是相同的,可以理解为三维模型的等高线。

例如这里把Z的值修改为X的平方加Y的平方。

import numpy as np
import matplotlib.pyplot as plt

n = 200
"""首先生成两个一维数组,X和Y"""
# 在指定的间隔内返回均匀间隔的数字
x = np.linspace(-10, 10, n)
y = np.linspace(-10, 10, n)

"""利用它们生成二维网格坐标矩阵"""
X, Y = np.meshgrid(x, y)
"""利用每个网格点的坐标计算Z"""
Z = X**2 + Y**2
"""使用pcolormesh()函数填充网格"""
# 这个函数中的前两个参数确定网格位置,第3个参数的值决定网格的颜色,第4个参数指定所使用的颜色方案
plt.contour(X, Y, Z, cmap="rainbow")

plt.show()

这是运行的结果
在这里插入图片描述

plt.contourf()函数

如果要给等高线之间的区域填充颜色可以使用contourf函数。

import numpy as np
import matplotlib.pyplot as plt

n = 200
"""首先生成两个一维数组,X和Y"""
# 在指定的间隔内返回均匀间隔的数字
x = np.linspace(-10, 10, n)
y = np.linspace(-10, 10, n)

"""利用它们生成二维网格坐标矩阵"""
X, Y = np.meshgrid(x, y)
"""利用每个网格点的坐标计算Z"""
Z = X**2 + Y**2
"""使用pcolormesh()函数填充网格"""
# 这个函数中的前两个参数确定网格位置,第3个参数的值决定网格的颜色,第4个参数指定所使用的颜色方案
# plt.contour(X, Y, Z, cmap="rainbow")
plt.contourf(X, Y, Z, cmap="rainbow")

plt.show()

在这里插入图片描述
这个增加的f,表示fill,填充的意思。可以在这里添加参数plt.contourf(X, Y, Z, 20, cmap="rainbow"),指定颜色细分的数量,这是运行的结果。
在这里插入图片描述
可以看到,图中分区的数量增加了。

可以使用contourf()函数代替pcolourmesh()函数绘制分区图。在机器学习的分类任务中,经常使用这些函数以可视化的方式展示分类的效果。

比如我们要根据z的取值把样本分为两类,首先获得网格坐标矩阵,然后计算所有网格的内值,自定义一个背景颜色方案,包括两种颜色。根据阈值把z的取值转化为类别0和类别1。作为网格填充色的依据。

import tensorflow as tf
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
n = 200
"""首先生成两个一维数组,X和Y"""
# 在指定的间隔内返回均匀间隔的数字
x = np.linspace(-10, 10, n)
y = np.linspace(-10, 10, n)

"""利用它们生成二维网格坐标矩阵"""
X, Y = np.meshgrid(x, y)
"""利用每个网格点的坐标计算Z"""
Z = X + Y

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

plt.show()

在绘制分区图就可以得到这样的效果,改变Z的阈值就会得到不同的划分,非常的灵活方便。
在这里插入图片描述

根据鸢尾花分类模型,绘制分类图

现在我们就根据上节课中得到的鸢尾花分类的逻辑回归模型,绘制分类图。

在模型训练好之后,首先根据鸢尾花花萼长度和花萼宽度取值范围,确定坐标范围。找到每一列中的最小值和最大值,使用花萼长度的取值范围作为横坐标的范围,使用花萼宽度的取值范围作为纵坐标的范围。

然后使用它们生成网格点坐标矩阵,生成多元线性模型需要的属性矩阵。使用训练得到的模型参数w根据sigmoid公式计算所有网格点对应的函数值,把它们转化为分类结果0和1作为填充粉色还是绿色的依据,并对他进行维度变换,让他和m1,m2具有相同的形状,这是pcolormesh()函数对参数的要求。

定义绘制散点的颜色方案,定义背景颜色方案,绘制分区图,绘制散点图。

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

TRAIN_URL = "https://download.tensorflow.org/data/iris_training.csv"
train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL)
TEST_URL = "https://download.tensorflow.org/data/iris_test.csv"
test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL)

df_iris_train = pd.read_csv(train_path, header=0)
df_iris_test = pd.read_csv(test_path, header=0)
# 第2步处理数据
""""别读取训练集和测试集中的数据,并把它们转化为numpy数组"""
iris_train = np.array(df_iris_train)
iris_test = np.array(df_iris_test)
"""iris的训练集中有120条样本"""
# (120, 5)
"""可以看到测试集中有30个样本"""
# (30, 5)

"""每条样本中有5列,前4列是属性,最后一列是标签。
我们只取出前两列属性花萼的长度和宽度来训练模型"""
train_x = iris_train[:, 0:2]
test_x = iris_test[:, 0:2]
"""取出最后一列作为标签值"""
train_y = iris_train[:, 4]
test_y = iris_test[:, 4]

# (120, 2) (120,)
# (30, 2) (30,)

"""从训练集中提取出标签值为0和1的样本,也就是山鸢尾和变色鸢尾"""
x_train = train_x[train_y < 2]
y_train = train_y[train_y < 2]
"""可以训练集中有78条样本"""
# (78, 2) (78,)

"""从测试集中提取出标签值为0和1的样本,也就是山鸢尾和变色鸢尾"""
x_test = test_x[test_y < 2]
y_test = test_y[test_y < 2]
"""可以测试集中有22条样本"""
# (22, 2) (22,)


"""分别记录训练集合测试集中的样本数"""
num_train = len(x_train)
num_test = len(x_test)
# 78,22

# 按列中心化
x_train = x_train - np.mean(x_train, axis=0)
x_test = x_test - np.mean(x_test, axis=0)

# 构造多元线性模型需要的属性矩阵和标签列向量。
x0_train = np.ones(num_train).reshape(-1, 1)

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)

x0_test = np.ones(num_test).reshape(-1, 1)

X_test = tf.cast(tf.concat((x0_test, x_test), axis=1), tf.float32)
Y_test = tf.cast(y_test.reshape(-1, 1), tf.float32)


# 第3步设置超参数,设置模型参数初始值
learn_rate = 0.2
iter = 120
display_step = 30

np.random.seed(612)
W = tf.Variable(np.random.randn(3, 1), dtype=tf.float32)

# 第4步训练模型
"""列表ce用来保存每一次迭代的交叉熵损失"""
ce_train = []
"""acc用来保存准确率"""
acc_train = []
ce_test = []
acc_test = []
for i in range(0, iter + 1):
    with tf.GradientTape() as tape:
        """这是多元模型的sigmoid()函数
        属性矩阵X和参数向量W相乘的结果是一个列向量,
        因此计算得到的PRED的也是一个列向量,是每个样本的预测概率"""
        PRED_train = 1 / (1 + tf.exp(-tf.matmul(X_train, W)))
        """这是计算交叉熵损失"""
        """Y*tf.math.log(PRED)+(1-Y)*tf.math.log(1-PRED)
        这一部分的结果是一个列向量,是每个样本的损失
        使用reduce_mean()函数求它们的平均值,得到平均交叉熵损失"""
        LOSS_train = -tf.reduce_mean(Y_train * tf.math.log(PRED_train) + (1 - Y_train) * tf.math.log(1 - PRED_train))

        PRED_test = 1 / (1 + tf.exp(-tf.matmul(X_test, W)))
        LOSS_test = -tf.reduce_mean(Y_test * tf.math.log(PRED_test) + (1 - Y_test) * tf.math.log(1 - PRED_test))
    """这是准确率也是一个数字,因为不需要对它求导,所以把它放在with语句的外面"""
    accuracy_train = tf.reduce_mean(tf.cast(tf.equal(tf.where(PRED_train < 0.5, 0., 1.), Y_train), tf.float32))
    accuracy_test = tf.reduce_mean(tf.cast(tf.equal(tf.where(PRED_test < 0.5, 0., 1.), Y_test), tf.float32))

    """记录每一次迭代的损失和准确率"""
    ce_train.append(LOSS_train)
    acc_train.append(accuracy_train)
    ce_test.append(LOSS_test)
    acc_test.append(accuracy_test)

    """只使用训练集来更新模型参数"""
    dL_dw = tape.gradient(LOSS_train, W)
    W.assign_sub(learn_rate * dL_dw)

    """输出准确率和损失"""
    if i % display_step == 0:
        print("i: %i, TrainAcc: %f, TrainLoss: %f, TestAcc: %f, TestLoss: %f" % (i, accuracy_train, LOSS_train,
                                                                                 accuracy_test, LOSS_test))

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)

m0 = np.ones(M * M)
"""生成多元线性模型需要的属性矩阵"""
X_mesh = tf.cast(np.stack((m0, m1.reshape(-1), m2.reshape(-1)), axis=1), dtype=tf.float32)
"""使用训练得到的模型参数w根据sigmoid公式计算所有网格点对应的函数值"""
Y_mesh = tf.cast(1 / (1 + tf.exp(-tf.matmul(X_mesh, W))), dtype=tf.float32)
"""把它们转化为分类结果0和1作为填充粉色还是绿色的依据"""
Y_mesh = tf.where(Y_mesh < 0.5, 0, 1)
"""并对他进行维度变换,让他和m1,m2具有相同的形状,这是`pcolormesh()`函数对参数的要求"""
n = tf.reshape(Y_mesh, m1.shape)

"""定义绘制散点的颜色方案"""
cm_pt = mpl.colors.ListedColormap(["blue", "red"])
"""定义背景颜色方案"""
cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0"])

"""绘制分区图"""
plt.pcolormesh(m1, m2, n, cmap=cm_bg)
"""绘制散点图"""
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train, cmap=cm_pt)
plt.show()

这是得到的结果。
在这里插入图片描述
可以看到,这个分类线的位置和上节课我们绘制的决策边界相同。要注意的是,pyplot()函数根中的绘图是有层次的。这里要首先绘制分区图作为背景,然后在它的上面绘制散点图,否则散点图会被分区图遮盖住。

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

M = 300
"""首先根据鸢尾花花萼长度和花萼宽度取值范围,确定坐标范围。找到每一列中的最小值和最大值"""
x1_min, x2_min = x_test.min(axis=0)
x1_max, x2_max = x_test.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)

m0 = np.ones(M * M)
"""生成多元线性模型需要的属性矩阵"""
X_mesh = tf.cast(np.stack((m0, m1.reshape(-1), m2.reshape(-1)), axis=1), dtype=tf.float32)
"""使用训练得到的模型参数w根据sigmoid公式计算所有网格点对应的函数值"""
Y_mesh = tf.cast(1 / (1 + tf.exp(-tf.matmul(X_mesh, W))), dtype=tf.float32)
"""把它们转化为分类结果0和1作为填充粉色还是绿色的依据"""
Y_mesh = tf.where(Y_mesh < 0.5, 0, 1)
"""并对他进行维度变换,让他和m1,m2具有相同的形状,这是`pcolormesh()`函数对参数的要求"""
n = tf.reshape(Y_mesh, m1.shape)

"""定义绘制散点的颜色方案"""
cm_pt = mpl.colors.ListedColormap(["blue", "red"])
"""定义背景颜色方案"""
cm_bg = mpl.colors.ListedColormap(["#FFA0A0", "#A0FFA0"])

"""绘制分区图"""
plt.pcolormesh(m1, m2, n, cmap=cm_bg)
"""绘制散点图"""
plt.scatter(x_test[:, 0], x_test[:, 1], c=y_test, cmap=cm_pt)
plt.show()

在这里插入图片描述
可以看到,在测试集中有两个点划分错误,但是根据准确率来看有三个点错误。
这是因为在这个测试集中恰好有两个样本的前两列属性完全相同,他们在散点图中重合在了一起。
在这里插入图片描述
也可以把训练集和测试集的分区图放在同一个图中的不同子图中,更便于对照观察。
在这里插入图片描述
在使用测试及测试模型和绘制测试题的分类图时有大量的代码和使用训练集是重复的,聪明的你一定是把训练级的代码拷贝过来,再把这些地方的train修改成test,而没有重新敲一遍代码吧,更聪明的方法是创建函数把需要重复写的代码放在函数中,把每次拷贝之后需要修改的部分作为参数传递进来就可以了,在使用函数之后程序会更加简洁紧凑。
在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值