比如将一张图片按尺寸识别分类为横向或者纵向两类就是二分类问题
设x轴为图像的宽、y轴为图像的高,那么把训练数据展现在图上就是这样的:
若增加更多的数据集有:
如果只用一条线将图中白色的点和黑色的点分开,那么:
分类的目的就是找到这条线,就可以根据点在线的哪一边来判断该数据属于哪个类别,比如图像是横向还是纵向的了
而这条线是使权重向量成为法线向量的直线。设权重向量为w,那么那条直线的表达式就是:
解释:
权重向量就是待求的未知参数,w是权重一词的英文——weight的首字母。
上次学习回归时,我们为了求未知参数θ 做了很多事情,而w和θ是一样的。
向量内积为零表示两向量垂直:
比如我们设权重向量为w=(1,1),那么刚才的内积表达式会变成:
w·x=w1x1+w2x2=1·x1 +1·x2=x1 +x2 =0
即x2=-x1,一条斜率为-1的直线,像这样与w成直角的向量有很多,它们连成了一条直线
内积还可以以向量夹角余弦表示:
表达式中的|w| 和|x| 是向量的长,因此必定是正数。
所以要想使内积为0,只能使cosθ=0。
要想使cosθ=0,也就意味着θ=90◦或θ =270◦。这两种情况也是直角。
所以我们的目标是最终找到与能够精准分类的直线成直角的权重向量
一开始并不存在画的那种直线,而是要通过训练找到权重向量,然后才能得到与这个向量垂直的直线,最后根据这条直线就可以对数据进行分类了。
对于cosθ图像:
不难得出在90◦ <θ<270◦的时候cosθ为负
而与权重向量w之间的夹角为θ,在90◦<θ<270◦范围内的所有向量都符合条件
即在这条直线下面、与权重向量方向相反的这个区域内任意找出一个向量,都满足与w内积为负
而内积为正,则说明两个向量大致属于一个方向,即绝对不可能反向,那么这些向量在:
内积是衡量向量之间相似程度的指标。
结果为正,说明二者相似;为0则二者垂直;为负则说明二者不相似
求出权重向量的基本做法和回归时相同:将权重向量用作参数,创建更新表达式来更新参数。
感知机(perceptron)模型(神经网络和深度学习的基础模型):
接受多个输入后将每个值与各自的权重相乘,最后输出总和的模型:
实质是向量间的内积
准备训练数据:
设表示宽的轴为x1、表示高的轴为x2,用y来表示图像是横向还是纵向的,横向的值为1、纵向为−1。
定义判别函数fw(x):根据参数向量x来判断图像是横向还是纵向(返回1或者−1产出分类结果)的函数:
即根据内积的符号来给出不同返回值的函数,以此判断图像是横向还是纵向的
那么定义权重向量的更新表达式来处理所有训练数据,更新权重向量:
若通过判别函数对宽和高的向量x进行分类的结果f与实际的标签y不同,即判别函数的分类结果不正确时就会更新权重,反之则不会更新
更新的逻辑:
先任意确定一个初始权重向量w(通过随机值来初始化的,类似于回归时随意确定初始值):
假设第一个训练数据是x(1)=(125,30)
现在权重向量w和训练数据的向量x(1)二者的方向几乎相反,w和x(1)之间的夹角θ的范围是90◦<θ<270◦,内积为负。也就是说,判别函数fw(x(1))的分类结果为−1,而训练数据x(1)的标签y(1)是1,所以fw(x(1))≠y(1)分类失败。
现在y(1)=1,所以更新表达式:w+y(1)x(1) = w +x(1)
这个w+x(1)就是下一个新的w,画一条与新的权重向量垂直的直线,相当于把原来的直线旋转了很多:
刚才x(1)与权重向量分居直线两侧,现在它们在同一侧了,即这次θ<90◦,所以内积为正,判别函数fw(x)的分类结果为1。而且x(1)的标签也为1,说明分类成功了。
本质就是分类失败时更新权重向量,使得直线旋转相应的角度
像这样重复更新所有的参数,就是感知机的学习方法((简单感知机或单层感知机),而多层感知机就是神经网络)。
但是感知机只能解决线性可分的问题,对于此类非线性问题失败了,找不到一条直线完全分类:
线性可分指的就是能够使用直线分类的情况,像这样不能用直线分类的就不是线性可分。
由此引出解决线性不可分的逻辑回归:
与感知机的不同之处在于,它是把分类作为概率来考虑的,比如x(1)是A类的概率为10%…
这里设横向的值为1、纵向的值为0(不是之前的-1)(这些是可以任意设定的,一般以简化表达式为目标)。
先回顾一个通过最速下降法或随机梯度下降法来学习参数θ的表达式。使用这个θ能够求出对未知数据x的输出值:
此处定义一个能够将未知数据分类为某个类别的函数fθ(x)(和感知机的判别函数fw(x)类似的东西)
使用与回归时同样的参数θ,函数的形式就是:
exp 的全称是exponential,即指数函数。
exp(x)与 e^x含义相同,只是写法不同。e是自然常数,具体的值为2.7182…
这个函数的名字叫sigmoid函数,设θ^Tx为横轴,fθ(x)为纵轴,那么它的图形是这样的:
θTx =0时fθ(x)=0.5,以及0<fθ(x)<1(所以sigmoid函数可以作为概率来使用)是sigmoid函数的两个特征
接下来我们就把未知数据x是横向图像的概率作为fθ(x),那么:
该条件概率表示在给出x数据时y=1,即图像为横向的概率。
假如fθ(x)的计算结果是0.7,表示是图像为横向的概率是70%
若是以0.5为阈值,然后把fθ(x)的结果与它相比较,从而分类横向或纵向,则有
而这个0.5很特殊:在θTx=0时,fθ(x)=0.5且在fθ(x)⩾0.5时,θTx⩾0。
所以有(因为单调,所以反过来,若θTx⩾0则有fθ(x)⩾0.5,可以归类为横向)
那么可以改写上述表达式:
设横轴为图像的宽(x1)、纵轴为图像的高(x2),随便确定θ再具体地去考虑,先画出下θTx⩾0的图像图来:
这个不等式表示的范围也就是图像被分类为横向的范围:
也就是说,将θTx=0这条直线作为边界线,就可以把这条线两侧的数据分类为横向和纵向了
这样用于数据分类的直线称为决策边界
但是初始值往往是错误的:
所以和回归的时候一样,是因为我们随意决定了参数。那么为了求得正确的参数θ则定义目标函数,进行微分,然后求参数的更新表达式,这种算法就称为逻辑回归
一开始我们把x为横向的概率P(y=1|x)定义为fθ(x)了。
基于此(P(y=1|x)是图像为横向的概率,P(y=0|x)是图像为纵向的概率),训练数据的标签y和fθ(x)是什么样的关系会比较理想呢:
既然fθ(x)是x为横向时的概率……那么在y=1时fθ(x)=1,y=0时fθ(x)=0的关系就是理想的:
y =1的时候,我们希望概率P(y=1|x)是最大的
y =0的时候,我们希望概率P(y=0|x)是最大的
所以对于训练数据的分类概率,有"希望":
假定所有的训练数据都是互不影响、独立发生的,这种情况下整体的概率就可以用联合概率:
联合概率的表达式一般化:
这里的目标函数L(θ)也被称为似然,函数的名字L取自似然的英文单词Likelihood的首字母。
才发现,代入y(i)=0/=1时,最终可简化为表示数据集为横向的概率/纵向的概率的条件概率
考虑一下使这个目标函数最大化的参数θ(回归的时候处理的是误差,所以要最小化,而现在考虑的是联合概率,我们希望概率尽可能大,所以要最大化)
我们可以认为似然函数L(θ)中,使其值最大的参数θ能够最近似地说明训练数据。
对似然函数进行取对微分,求出参数θ
取对是因为好微分,且对数单增不影响单调性是在L(θ1)<L(θ2)时,有logL(θ1) < logL(θ2)成立。
也就是说,使L(θ) 最大化等价于使logL(θ) 最大化
第2行是log(ab)=loga+logb
第3行是logab =bloga
第4行是P(y(i)=0|x(i)) = 1 − P(y(i)=1|x(i))
所以有:
那么对参数θ微分有:
但对sigmoid函数微分麻烦:
直接有其微分结论(fθ(x)本身就是sigmoid函数,所以这个微分表达式可以直接使用):
也可也设z=θTx,然后再一次使用复合函数的微分:
现在代入各个结果,然后通过展开、约分,使表达式更简洁
接下来要做的就是从这个表达式导出参数更新表达式。不过现在是以最大化为目标,所以必须按照与最小化问题时相反的方向移动参数。
也就是说,最小化时要按照与微分结果的符号相反的方向移动,而最大化时要与微分结果的符号同向移动。
当然也可再变形
线性不可分:
增加次数,之前的决策边界是直线,现在则是曲线了,通过随意地增加次数,就可以得到复杂形状的决策边界了.
实现:
感知机:
训练数据 images1.csv
x1,x2,y153,432,-1220,262,-1118,214,-1474,384,1485,411,1233,430,-1396,361,1484,349,1429,259,1286,220,1399,433,-1403,340,1252,34,1497,472,1379,416,-176,163,-1263,112,126,193,-161,473,-1420,253,1
在图中用圆点表示y=1的数据、用叉号表示y=−1的数据:
# 绘图
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('Agg')
# 读入训练数据
train = np.loadtxt('images1.csv', delimiter=',', skiprows=1)
train_x = train[:,0:2]
train_y = train[:,2]
# # 绘图
# plt.plot(train_x[train_y == 1, 0], train_x[train_y == 1, 1], 'o')
# plt.plot(train_x[train_y == -1, 0], train_x[train_y == -1, 1], 'x')
# plt.axis('scaled')
# plt.savefig('AI7.png') # 保存为图片,而非 plt.show()
初始化权重w,并实现函数:
# 权重的初始化
w = np.random.rand(2)
# 判别函数
def f(x):if np.dot(w, x) >= 0:return 1else:return -1
实现权重的更新表达式,感知机停止学习的标准是精度(但代码中以重复十次为例)
# 重复次数
epoch = 10# 更新次数
count = 0# 学习权重
for _ in range(epoch):for x, y in zip(train_x, train_y):if f(x) != y:w = w + y * x# 输出日志count += 1print(' 第 {} 次 : w = {}'.format(count, w))
使权重向量成为法线向量的直线方程是内积为0的x的集合。
所以对它进行移项变形
x1 = np.arange(0, 500)
plt.plot(train_x[train_y == 1, 0], train_x[train_y == 1, 1], 'o')
plt.plot(train_x[train_y == -1, 0], train_x[train_y == -1, 1], 'x')
plt.plot(x1, -w[0] / w[1] * x1, linestyle='dashed')
plt.savefig('AI8.png')
最终绘出以下表达式的图形:
值得说明的是,此处没有对训练数据进行标准化环节也成功了
完整代码:
# 绘图
import numpy as np
import matplotlib
import matplotlib.pyplot as pltmatplotlib.use('Agg')
# 读入训练数据
train = np.loadtxt('images1.csv', delimiter=',', skiprows=1)
train_x = train[:, 0:2]
train_y = train[:, 2]
# # 绘图
# plt.plot(train_x[train_y == 1, 0], train_x[train_y == 1, 1], 'o')
# plt.plot(train_x[train_y == -1, 0], train_x[train_y == -1, 1], 'x')
# plt.axis('scaled')
# plt.savefig('AI7.png') # 保存为图片,而非 plt.show()# 权重的初始化
w = np.random.rand(2)# 判别函数
def f(x):if np.dot(w, x) >= 0:return 1else:return -1# 重复次数
epoch = 10# 更新次数
count = 0# 学习权重
for _ in range(epoch):for x, y in zip(train_x, train_y):if f(x) != y:w = w + y * x# 输出日志count += 1print(' 第 {} 次 : w = {}'.format(count, w))x1 = np.arange(0, 500)
plt.plot(train_x[train_y == 1, 0], train_x[train_y == 1, 1], 'o')
plt.plot(train_x[train_y == -1, 0], train_x[train_y == -1, 1], 'x')
plt.plot(x1, -w[0] / w[1] * x1, linestyle='dashed')
plt.savefig('AI8.png')
验证:
# 200×100 的横向图像
f([200, 100])
应该输出1
# 100×200 的纵向图像
f([100, 200])
应该输出-1
刚才试验的都是二维数据,如果增加训练数据和w的维度,那么模型也可以处理三维以上的数据。
不过模型依然只能解决线性可分的问题。
接下来实现解决逻辑回归:
数据中的x1和x2可以不变,但y需要变一下。因为在逻辑回归中,我们需要把横向分配为1、纵向分配为0。
images2.csv
x1,x2,y153,432,0220,262,0118,214,0474,384,1485,411,1233,430,0396,361,1484,349,1429,259,1286,220,1399,433,0403,340,1252,34,1497,472,1379,416,076,163,0263,112,126,193,061,473,0420,253,1
实现逻辑回归首先初始化参数,然后对训练数据标准化。
对x1和x2分别取平均值和标准差,最后标准化。另外不要忘了加一个x0列。
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('Agg')# 读入训练数据
train = np.loadtxt('images2.csv', delimiter=',', skiprows=1)
train_x = train[:,0:2]
train_y = train[:,2]
# 初始化参数
theta = np.random.rand(3)# 标准化
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)
def standardize(x):return (x - mu) / sigmatrain_z = standardize(train_x)# 增加x0
def to_matrix(x):x0 = np.ones([x.shape[0], 1])return np.hstack([x0, x])X = to_matrix(train_z)# 将标准化后的训练数据画成图
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.savefig('AI9.png')
轴的刻度变了,说明标准化成功了
下一个要做的是预测函数(sigmoid)的实现:
# sigmoid 函数
def f(x):return 1 / (1 + np.exp(-np.dot(x, theta)))
参数更新部分的实现:
学习逻辑回归的时候,我们进行了定义逻辑回归的似然函数,对对数似然函数进行微分等一系列操作,然后最终得到的参数更新表达式
现在与回归时一样,将fθ(x(i))−y(i)当作向量来处理,将它与训练数据的矩阵相乘:
# 学习率
ETA = 1e-3
# 重复次数
epoch = 5000
# 重复学习
for _ in range(epoch):theta = theta - ETA * np.dot(f(X) - train_y, X)
过在逻辑回归中,θTx=0这条直线是决策边界.θTx⩾0时图像是横向的,θTx<0时图像是纵向的
将θTx=0变形并加以整理,得到:
完整代码:
x0 = np.linspace(-2, 2, 100)
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.plot(x0, -(theta[0] + theta[1] * x0) / theta[2],linestyle='dashed')
plt.savefig('AI10.png')
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('Agg')# 读入训练数据
train = np.loadtxt('images2.csv', delimiter=',', skiprows=1)
train_x = train[:,0:2]
train_y = train[:,2]
# 初始化参数
theta = np.random.rand(3)# 标准化
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)
def standardize(x):return (x - mu) / sigmatrain_z = standardize(train_x)# 增加x0
def to_matrix(x):x0 = np.ones([x.shape[0], 1])return np.hstack([x0, x])X = to_matrix(train_z)# # 将标准化后的训练数据画成图
# plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
# plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
# plt.savefig('AI9.png')# sigmoid 函数
def f(x):return 1 / (1 + np.exp(-np.dot(x, theta)))
# 学习率
ETA = 1e-3
# 重复次数
epoch = 5000
# 重复学习
for _ in range(epoch):theta = theta - ETA * np.dot(f(X) - train_y, X)
#整理
x0 = np.linspace(-2, 2, 100)
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.plot(x0, -(theta[0] + theta[1] * x0) / theta[2],linestyle='dashed')
plt.savefig('AI10.png')
验证
>>> f(to_matrix(standardize([... [200,100], # 200×100 的横向图像
... [100,200] # 100×200 的纵向图像
... ])))#fθ(x)返回的是x为横向的概率array([ 0.91740319, 0.02955752])
说明第一个有87%的概率为横向,另一个有26%的概率为横向(即可以认定为纵向)
直接看概率可能不够直观,我们可以确定一个阈值,然后定义一个根据阈值返回1或0的函数
添加代码:
# sigmoid 函数
def f(x):return 1 / (1 + np.exp(-np.dot(x, theta)))
#改进版本sigmoid,不返回概率而是返回阈值情况下的答案
def classify(x):#return (f(x) >= 0.5).astype(np.int)#报错return (f(x) >= 0.5).astype(int)
现在直接表示为是否是横向
线性不可分问题:
x1,x2,y
0.54508775,2.34541183,0
0.32769134,13.43066561,0
4.42748117,14.74150395,0
2.98189041,-1.81818172,1
4.02286274,8.90695686,1
2.26722613,-6.61287392,1
-2.66447221,5.05453871,1
-1.03482441,-1.95643469,14.06331548,1.70892541,12.89053966,6.07174283,02.26929206,10.59789814,04.68096051,13.01153161,11.27884366,-9.83826738,1-0.1485496,12.99605136,0-0.65113893,10.59417745,03.69145079,3.25209182,1-0.63429623,11.6135625,00.17589959,5.84139826,00.98204409,-9.41271559,1-0.11094911,6.27900499,0
按照之前的标准做法,将它们画成图看看
import numpy as np
import matplotlibmatplotlib.use('Agg')
import matplotlib.pyplot as plttrain = np.loadtxt('data3.csv', delimiter=',', skiprows=1)
train_x = train[:, 0:2]
train_y = train[:, 2]
plt.plot(train_x[train_y == 1, 0], train_x[train_y == 1, 1], 'o')
plt.plot(train_x[train_y == 0, 0], train_x[train_y == 0, 1], 'x')
plt.plot(train_x[train_y == 0, 0], train_x[train_y == 0, 1], 'x')plt.savefig('AI11.png')
这个数据看上去确实不能用一条直线来分类,要用二次函数(在训练数据里加上x1的平方,增加一个θ3参数,参数总数达到四个)
# 参数初始化
theta = np.random.rand(4)# 标准化
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)def standardize(x):return (x - mu) / sigmatrain_z = standardize(train_x)# 增加x0和x3
def to_matrix(x):x0 = np.ones([x.shape[0], 1])x3 = x[:, 0, np.newaxis] ** 2return np.hstack([x0, x, x3])X = to_matrix(train_z)
而sigmoid函数和学习部分与前面线性可分部分完全一样
# sigmoid 函数
def f(x):return 1 / (1 + np.exp(-np.dot(x, theta)))
# 学习率
ETA = 1e-3
# 重复次数
epoch = 5000
# 重复学习
for _ in range(epoch):theta = theta - ETA * np.dot(f(X) - train_y, X)
考虑画成图,则需要对有四个参数的θTx=0方程变形
x1 = np.linspace(-2, 2, 100)
x2 = -(theta[0] + theta[1] * x1 + theta[3] * x1 ** 2) / theta[2]
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.plot(x1, x2, linestyle='dashed')
plt.savefig('AI12.png')
将前面绘图部分代码注释,不然异常
import numpy as np
import matplotlibmatplotlib.use('Agg')
import matplotlib.pyplot as plttrain = np.loadtxt('data3.csv', delimiter=',', skiprows=1)
train_x = train[:, 0:2]
train_y = train[:, 2]
# plt.plot(train_x[train_y == 1, 0], train_x[train_y == 1, 1], 'o')
# plt.plot(train_x[train_y == 0, 0], train_x[train_y == 0, 1], 'x')
# plt.plot(train_x[train_y == 0, 0], train_x[train_y == 0, 1], 'x')
# plt.savefig('AI11.png')# 参数初始化
theta = np.random.rand(4)# 标准化
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)def standardize(x):return (x - mu) / sigmatrain_z = standardize(train_x)# 增加x0和x3
def to_matrix(x):x0 = np.ones([x.shape[0], 1])x3 = x[:, 0, np.newaxis] ** 2return np.hstack([x0, x, x3])X = to_matrix(train_z)
# sigmoid 函数
def f(x):return 1 / (1 + np.exp(-np.dot(x, theta)))
# 学习率
ETA = 1e-3
# 重复次数
epoch = 5000
# 重复学习
for _ in range(epoch):theta = theta - ETA * np.dot(f(X) - train_y, X)x1 = np.linspace(-2, 2, 100)
x2 = -(theta[0] + theta[1] * x1 + theta[3] * x1 ** 2) / theta[2]
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.plot(x1, x2, linestyle='dashed')
plt.savefig('AI12.png')
和回归时一样,将重复次数作为横轴、精度作为纵轴来绘图,这次应该会看到精度上升的样子
过精度的计算方法(这个值是被正确分类的数据个数占全部个数的比例):
# 精度的历史记录
accuracies = []# 重复学习
for _ in range(epoch):theta = theta - ETA * np.dot(f(X) - train_y, X)# 计算现在的精度result = classify(X) == train_yaccuracy = len(result[result == True]) / len(result)accuracies.append(accuracy)# 将精度画成图
x = np.arange(len(accuracies))plt.plot(x, accuracies)
plt.savefig('AI13.png')
完整
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plttrain = np.loadtxt('data3.csv', delimiter=',', skiprows=1)
train_x = train[:, 0:2]
train_y = train[:, 2]# 参数初始化
theta = np.random.rand(4)# 精度的历史记录
accuracies = []# 标准化
mu = train_x.mean(axis=0)
sigma = train_x.std(axis=0)def standardize(x):return (x - mu) / sigmatrain_z = standardize(train_x)# 增加x0和x3
def to_matrix(x):x0 = np.ones([x.shape[0], 1])x3 = x[:, 0, np.newaxis] ** 2return np.hstack([x0, x, x3])X = to_matrix(train_z)# sigmoid 函数
def f(x):return 1 / (1 + np.exp(-np.dot(x, theta)))def classify(x):return (f(x) >= 0.5).astype(int)# 学习率
ETA = 1e-3
# 重复次数
epoch = 5000# 重复学习
for _ in range(epoch):theta = theta - ETA * np.dot(f(X) - train_y, X)# 计算现在的精度result = classify(X) == train_yaccuracy = len(result[result == True]) / len(result)accuracies.append(accuracy)# 将精度画成图
x = np.arange(len(accuracies))plt.plot(x, accuracies)
plt.savefig('AI13.png')
随着次数的增加,精度的确变好了(线有棱有角是因为是训练数据只有20个,精度值只能为0.05的整数倍)
从图中可以看出,在重复满5000次之前,精度已经到1.0了。刚才随便定义了个重复学习5000次,也可以像这样,每次学习后都计算精度,当精度达到满意的程度后就停止学习,而不是一开始就大额初值。即根据精度来决定是否停止学习.
修改重复次数为1000发现效果仍然能达到1的精度(并且实则更准确得出只要700次)
随机梯度下降法
把学习部分稍稍修改一下:
# 重复学习
for _ in range(epoch):# 使用随机梯度下降法更新参数p = np.random.permutation(X.shape[0])for x, y in zip(X[p,:], train_y[p]):theta = theta - ETA * (f(x) - y) * xx1 = np.linspace(-2, 2, 100)
x2 = -(theta[0] + theta[1] * x1 + theta[3] * x1 ** 2) / theta[2]
plt.plot(train_z[train_y == 1, 0], train_z[train_y == 1, 1], 'o')
plt.plot(train_z[train_y == 0, 0], train_z[train_y == 0, 1], 'x')
plt.plot(x1, x2, linestyle='dashed')
plt.savefig('AI14.png')
后续更新:分类应用Iris数据集