逻辑回归是一种线性分类器,能够把线性可分的数据集划分为两类。
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 的值平均分为三部分。
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 函数。
在机器学习的分类任务中,经常使用下面这些函数以可视化的形式来展示分类的效果。
生成网格坐标矩阵: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 的转化为 0 或 1
plt.pcolormesh(X, Y, Z, cmap=cm_bg)
plt.show()
运行结果如下:
在模型训练好后,根据鸢尾花分类模型,绘制训练集的分类图。
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] # 提取出标签值为 0 和 1 的样本
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)
# 把它们转化为分类结果0和1,作为填充依据
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)
运行结果如下:
采用同样的方法,也可以绘制出测试集的分区图。
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] # 提取出标签值为 0 和 1 的样本
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)
# 把它们转化为分类结果0和1,作为填充依据
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、使用绘制训练集和测试集分类图的代码有大部分是相同的,因此,可将其封装成一个函数,然后进行调用,在实现多分类一节的笔记中实现。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)