我有一个函数,用于可视化由某些分类器(如逻辑回归或简单感知器模型)进行的分类。但我有几个问题不明白:
X有n个样本,只有2个特征。
- 为什么我必须使用xx1.ravel()和xx2.ravel(),然后对整个数组进行转置才能用于classifier.predict?为什么不能直接使用原始维度来预测结果?
2.为什么需要将Z重新调整回原始的xx1形状?
-
为什么需要创建一个网格用于绘制散点图?网格中的特定点是否像代表网格上某一点的“像素”?为什么需要这样做?
-
在idx, cl in enumerate(np.unique(y))中,idx值是什么意思?当我使用unique时,我得到的只是结果的唯一标识符?
-
在散点函数中,c = cmap(idx)的作用是什么?为什么cmap可以接受一个参数?
我为后面的问题可能与主题问题不符而道歉。代码来自《Python机器学习》一书。
def plot_decision_regions(X, y, classifier, test_idx = None, resolution = 0.002): #设置标记生成器和颜色映射 markers = ('s', 'x', 'o', '^', 'v') colors = ('red','blue','green','gray','cyan') cmap = ListedColormap(colors[:len(np.unique(y))]) #MESHGRID - 绘制决策面 x1_min, x1_max = X[:, 0].min(), X[:, 0].max() x2_min, x2_max = X[:, 1].min(), X[:, 1].max() xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution)) # print 'meshgrid:', xx1, xx2 #CLASSIFIER PREDICT Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T) Z = Z.reshape(xx1.shape) plt.contourf(xx1, xx2, Z, alpha = 0.4, cmap=cmap) plt.xlim(xx1.min(), xx1.max()) plt.ylim(xx2.min(), xx2.max()) for idx, cl in enumerate(np.unique(y)): plt.scatter(x = X[y == cl, 0], y = X[y == cl, 1], alpha = 0.8, c = cmap(idx), marker = markers[idx], label =cl) #高亮测试样本 if test_idx: XTest, yTest = X[test_idx, :], y[test_idx] plt.scatter(XTest[:,0], XTest[:,1], c = '', alpha = 1.0, linewidth = 1, marker = 'o', s = 55, label = 'test set')
回答:
使用网格和ravel的操作只是为了获取坐标范围的笛卡尔积,从而得到一组代表区域中各个点的(x, y)坐标对。
分类器期望其输入是一个Nx2的数组,其中N是样本数(即你想要预测其类别的案例数)。它需要两列,因为有两个特征。
网格生成两个数组,一个包含指定矩形区域中点的X坐标,另一个包含这些点的Y坐标。通过使用.ravel()
,你将这些数组展开成坐标列表。这只是以一种有些令人困惑的方式获取所需坐标范围的笛卡尔积。换句话说,这段代码:
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution))coord1, coord2 = xx1.ravel(), xx2.ravel()
与这段代码效果相同:
coord1, coord2 = zip(*itertools.product(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution)))
你可以通过一个简单的例子看到这一点:
>>> xx1, xx2 = np.meshgrid(np.arange(3), np.arange(2))>>> coord1, coord2 = xx1.ravel(), xx2.ravel()>>> coord1array([0, 1, 2, 0, 1, 2])>>> coord2array([0, 0, 0, 1, 1, 1])>>> coord1, coord2 = zip(*itertools.product(np.arange(3), np.arange(2)))>>> coord1(0, 0, 1, 1, 2, 2)>>> coord2(0, 1, 0, 1, 0, 1)
你可以看到生成的x/y对是相同的(尽管生成的顺序不同)。
这里选择网格方法可能是为了满足contourf的需要。contourf
基本上是将一个“XY平面”作为输入(由X和Y坐标的数组组成),以及该平面上每个点的Z值数组。
总的来说,分类器和轮廓图期望的输入格式不同。分类器接受两个单独的值(两个输入特征)并返回一个单一的值(它预测的类别)。contourf
需要一个矩形点的网格。换句话说,粗略地说,predict
一次想要一个X坐标和一个Y坐标,但contourf
想要先得到所有的X坐标,然后是所有的Y坐标。你发布的代码正在进行一些重塑操作,以在这些两种格式之间转换。你以contourf
想要的格式生成X和Y,并将其重塑成predict
想要的格式,以便你可以将其传递给predict
。predict
以predict
喜欢的形状给你Z数据,然后你将其重塑回contourf
想要的格式。