from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import SGDClassifier
from sklearn.metrics import precision_score, recall_score
from sklearn.model_selection import cross_val_score
from sklearn import svm

%matplotlib inline
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

iris = load_iris()

# sklearn提供的数据集有相同的字典结构
# DESCR 数据集描述信息
# data 数组,一行表示一个样例,一列表示一个特征
# target 标签数组
iris['DESCR']
iris['data']
iris['target']

# 我们先来看下数据集的各种信息
datas = pd.DataFrame(data=iris['data'],
                     columns=['sepal length','sepal width','petal length','petal width'])
labels = pd.DataFrame(data=iris['target'], columns=['label'])
iris_df = datas.join(labels)

# 数据集有150个样例,每个样例4个特征
iris_df.shape
# 观察下前5个,发现第四个特征和前三个特征数值差距比较大
iris_df.head(5)
# 整体观察下,第四个特征数据集前后差距比较大
iris_df.values
# 看下标签,怀疑不同类别的样例,第四个特征差距比较大
iris['target']
# 发现都是数字,没有文本特征,而且没有缺失特征
# 第一二个特征比较稳定,第三四个特征值差距比较大
# 一共三个分类
print(iris_df.info())
print(iris_df.describe())

# 我们可视化数据集看一下
# 我们这里是要做分类,我们看下散点图
# 前面观察数据集的时候,发现第三四个特征值差距比较大,我们看下第三四个特征和标签的关系
#iris_df.plot.scatter(x='petal length', y='label')
#iris_df.plot.scatter(x='petal width', y='label')

# 再来看下一二个特征的散点图
#iris_df.plot.scatter(x='sepal length', y='label')
#iris_df.plot.scatter(x='sepal width', y='label')

# 通过四个散点图我们能够看到,第三四个特征确实相对比较好的能够区别三个分类,第一个二个特征比较差
# 但是单纯靠第三四个特征貌似还不够,因为还有一些没有完全区分开的数据

# 上面我们是用单个特征来观察的分类可能性,下面,我们用两个特征来观察下,两个特征可视化是二维,也就是一个平面,可以进行观察
#iris_df.plot.scatter(x='petal length', y='petal width', c='label', cmap='hsv')
#iris_df.plot.scatter(x='sepal length', y='sepal width', c='label', cmap='hsv')

# 我们通过上面两个图像可以看到,petal length 和 petal width两个特征,几乎可以很好的把三个类别的花给分开
# sepal length 和 septal width效果就不是很好,三个类别的数据都掺杂在一块了

# 我们再来看下三维特征,三维的是一个空间,也就是3D,我们仍然可以直接进行可视化
#x, y, z = iris_df['sepal width'], iris_df['sepal length'], iris_df['petal length']
#ax = plt.subplot(111, projection='3d')  # 创建一个三维的绘图工程
#ax.scatter(x,y,z,c=iris_df['label'])  # 绘制数据点
#ax.set_zlabel('Z')  # 坐标轴
#ax.set_ylabel('Y')
#ax.set_xlabel('X')
#plt.show()

# 可以看到,在使用sepal width和sepal length的基础上,再加上petal length组成三维特征
# 在三维可视化图中,我们可以观察到使用这三个特征可以把大部分三个类别的数据给分隔开

# 我们总结一下数据可视化得出来的结论,然后根据结论推导模型来进行分类
# 1. 使用petal length和petal width可以有效的对三个类型的数据进行分类
# 2. 虽然使用sepal width和sepal length两个属性分类比较差,但是加上petal length属性组成三维属性分类效果应该是比较好的

# 首先,我们根据 1 得出的结论,来进行分类,1中有两个特征,我们可以使用线性模型来进行分类
# 我们这里使用线性sgd进行分类看下效果怎么样

shuffle_index = np.random.permutation(150)
datas = np.insert(iris.data, 4, values=iris.target, axis=1)[shuffle_index]

X = datas[:,2:4]
Y = datas[:,4]

x_trains, y_trains, x_labels, y_labels = train_test_split(X, Y, random_state=0)

sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(x_trains, x_labels)
print(cross_val_score(sgd_clf, x_trains, x_labels, cv=3, scoring='accuracy'))
# 我们使用交叉验证看到上面的精度十分差,和我们刚刚可视化看到的感觉不太一样
# 根据那会我们可视化看到的,通过 petal width和petal length两个特征
# 几乎可以很好的区分开三个类别,那为啥有这么差的精度呢?
# 下面,我们把sgd训练的参数可视化,看看是怎么回事
print(sgd_clf.coef_)

plt.figure(1)
plt.scatter(x=x_trains[:,0],y=x_trains[:,1],c=x_labels)
x=np.linspace(0, 7, 5)
y=sgd_clf.coef_[0][0]*x + sgd_clf.coef_[0][1]*x + sgd_clf.intercept_[0]
plt.plot(x,y,c='r')
y=sgd_clf.coef_[1][0]*x + sgd_clf.coef_[1][1]*x + sgd_clf.intercept_[1]
plt.plot(x,y,c='r')
y=sgd_clf.coef_[2][0]*x + sgd_clf.coef_[2][1]*x + sgd_clf.intercept_[2]
plt.plot(x,y,c='r')

# 因为我们这是一个多类分类,但是线性sgd只能做二类分类
# 如果我们要想用线性sgd去做多类分类,就只能把每两个目标之间都做一个分类器
# 然后执行所有的分类器,选一个最好的
# 这个数据有3个类别,1-2 1-3 2-3,所以有三个分类器
# sklearn的线性sgd分类器,会自动给我们完成多类分类

# 通过可视化,我们可以看到,我们训练的参数貌似有以下问题
# 1. 有两条直线勉强可以区分开两个类别,但是有一条直线三不沾。。。
# 2. 硬间隔分类,不是软间隔,不好

# 我们先看看硬间隔问题,我们使用线性svm试试怎么样
svc_clf = svm.SVC(kernel='linear')
svc_clf.fit(x_trains, x_labels)
print("svc两个特征:", cross_val_score(svc_clf, x_trains, x_labels, cv=3, scoring='accuracy'))
# 乖乖,这次我们可以看到,交叉验证的效果接近100%
# 这和我们可视化预测的效果一致
print(svc_clf.coef_)
print(svc_clf.intercept_)
plt.figure(2,figsize=(12,8))
plt.scatter(x=x_trains[:,0],y=x_trains[:,1],c=x_labels)

# 我们把训练好的方程可视化看一下
x=np.linspace(0, 8, 5)
y=svc_clf.coef_[0][0]*x + svc_clf.coef_[0][1]*x + svc_clf.intercept_[0]
plt.plot(x,y,c='r')
y=svc_clf.coef_[1][0]*x + svc_clf.coef_[1][1]*x + svc_clf.intercept_[1]
plt.plot(x,y,c='r')
y=svc_clf.coef_[2][0]*x + svc_clf.coef_[2][1]*x + svc_clf.intercept_[2]
plt.plot(x,y,c='r')

# 很奇怪,三条线为啥都集中在了一个地方,没有隔开的那两堆数据是怎么分类的呢
# 我猜测是通过距离的远近来分类的,这个问题先保留

# 简单总结一下,我们用我们可视化感觉效果不错的两个特征进行分类,果然取得了很好的效果
# 下面,我们再用之前看的三个特征来试试
shuffle_index = np.random.permutation(150)
datas = np.insert(iris.data, 4, values=iris.target, axis=1)[shuffle_index]

X = datas[:,0:3]
Y = datas[:,4]

x_trains, y_trains, x_labels, y_labels = train_test_split(X, Y, random_state=0)
svc_clf = svm.SVC(kernel='linear')
svc_clf.fit(x_trains, x_labels)
print("svc三个特征:", cross_val_score(svc_clf, x_trains, x_labels, cv=3, scoring='accuracy'))

# 三个特征效果一样不错,如果换成只有前两个特征,效果就比较差了
# 这个数据集先看到这里

原创文章,转载请注明地址: 文章地址