dibo机器学习

"深入浅出Python机器学习" 学习笔记-

2019-07-07  本文已影响12人  爱折腾的大懒猪

深入浅出Python机器学习_学习笔记

前80多页可以参考这里,这本书在出版社的介绍和源代码

第一章 概述

第二章 基于Python语言的环境配置

该书主要依赖库为 numpy,scipy, matplotlib, pandas, ipython, scikit-learn.


pandas使用小示例(包括过滤数据)

#%%
import pandas

#先创建一个小数据集
data = {"Name":["小芋","小菡","小榆","小梒"],
        "City":["北京","上海","广州","深圳"],
        "Age":["18","20","22","24"],
        "Height":["162","161","165","166"]}
data_frame = pandas.DataFrame(data)
display(data_frame)

#%%
display(data_frame[data_frame.City != "北京"])
#显示所有不在北京的同学信息
pandas使用小示例

利用scipy产生稀疏矩阵

import numpy as np
from scipy import sparse
# 构造6*6对角矩阵, 对角1,其余0
matrix = np.eye(6)
# 将矩阵转为CSR 格式的系数矩阵.
sparse_matrix = sparse.csc_matrix(matrix)
print(sparse_matrix)
稀疏矩阵示例

等差数列变量和曲线

import matplotlib.pyplot as plt
%matplotlib inline
# 生成-20到20, 共10个元素的等差数列
x = np.linspace(-20, 20, 10)
# y函数构造
y = x**3 + 2*x**2 + 6*x + 5
# 画出曲线
plt.plot(x, y, marker="o")
等差数列变量和曲线

第三章 K最近邻算法-近朱者赤,近墨者黑

3.1 kNN原理

TODO: 如果分类时, k个点最多的类有2甚至3种, 是根据最近原则来确定类么?

3.2 kNN的用法

这里作者用了一个自建数据集和红酒数据集做测试, 针对分类和回归的应用都做了展示.

3.2.1 kNN在简单分类的应用

sklearn.datasets子模块是针对数据集的一个模块, 内含几个toy datasets, 另外, 也可以下载更大的数据集, 或者自己产生数据集. 这章书使用make_blobs来产生分类点, 使用make_regression来产生回归使用的点.

make_blobs 产生数据集

使用datasets.make_blobs 可以产生分类的数据点, 见下面例子.

# 导入数据集, 分类器, 画图工具, 数据集拆分工具
from sklearn.datasets import make_blobs
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

# 生成样本数200, 分类为2类的数据集,
data = make_blobs(n_samples=200, n_features=2, centers=2, random_state=8)
X, y = data
# 将生成的数据集进行可视化
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.spring, edgecolors='k')
plt.show()
make_blobs产生二分类的数据集
  • 该例子产生的是两个类, 为了便于可视化, 使用2个特征.
  • 产生的返回值是(features, labels)两个数组, 分别给了X和y.
  • 一般地, 大写X是多维数据(矩阵), 特征时用; y是一维数组(向量), 一般标签用.
  • scatter作散点图, 前两个是X轴和Y轴坐标(用的是特征1和特征2), c=y是指定颜色对应的是标签(0/1)
创建kNN分类模型
# 拟合
import numpy as np
clf = KNeighborsClassifier()
clf.fit(X, y)

# 画图
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
                     np.arange(y_min, y_max, 0.02))
# 空间虚拟点的预测值
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Pastel1)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.spring, edgecolor='k')
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("Classifier:KNN")
plt.show()
建立kNN模型并可视化
预测新点的分类

输入一个数据点的特征([[6.75,4.82]],注意两层中括号, 一层是元素, 一层是特征), 肉眼可见再灰色一类(类1). 进行预测, 其结果和观察一致.

# 加入新数据点, 并五星标记
plt.scatter(6.75, 4.82, marker='*', c='red', s=200)
plt.show()

# 对新点进行预测, 并显示其分类
print('新数据点的分类是:',clf.predict([[6.75,4.82]]))
新数据点显示和预测

新数据点的分类是: [1]

3.2.2 kNN在多元分类的应用

后续测试了复杂一点的5类点的例子(用下面的代码, 去掉random_state, 可以产生各色各样的点分类图), 下面选的这个分类有点麻烦, 因为有两个类粘在一起了. 如果是分得比较开的类, 后续的测试结果会好很多.

# 样品量500, 5类, 2特征, 还是设定种子8
data2 = make_blobs(n_samples=500,n_features=2, centers=5, random_state=8)
X2,y2 = data2
# 散点图可视化
plt.scatter(X2[:,0],X2[:,1],c=y2, cmap=plt.cm.spring,edgecolor='k')
plt.show()
分类数量为5

类似地, 进行拟合和模型显示. 可以看出中间两个类太接近了.

# kNN算法建立模型
clf = KNeighborsClassifier()
clf.fit(X2,y2)

#下面的代码用于画图
x_min, x_max = X2[:, 0].min() - 1, X2[:, 0].max() + 1
y_min, y_max = X2[:, 1].min() - 1, X2[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, .02),
                     np.arange(y_min, y_max, .02))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Pastel1)
plt.scatter(X2[:, 0], X2[:, 1], c=y2, cmap=plt.cm.spring, edgecolor='k')
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("Classifier:KNN")
plt.show()
对5类数据的kNN分类
print('模型正确率:{:.2f}'.format(clf.score(X2,y2)))
# 模型正确率:0.96

这里的正确率(score)实际是正确样本数/总样本数, 500点的话, 就是错了20个点. 如果样本分类分离较好, 则正确率会上升, 例如:

随机到分离更好的一族数据点,结果更好

TODO: 怎么知道此时的随机种子是多少??

整体而言, kNN在分类时的效果还是不错的. 不过这里没有做训练集和测试集分离的测试.

如果想分割成训练集和测试集, 可以:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X2,y2,test_size=0.2)
clf2 = KNeighborsClassifier()
clf2.fit(X_train, y_train)
print('模型正确率:{:.2f}'.format(clf2.score(X_test, y_test)))
# 模型正确率:0.92
### 在不设随机值时, 正确率在0.91~0.97间波动

3.2.3 kNN用于回归分析

首先使用make_regression 创建一个数据集, 然后用KNeighborsRegressor 构建回归模型. 最后看看影响因素. 对比kNN用于分类, 回归的效果要差些.

这里构建了100样本(默认), 特征数1, 有效特征1, 噪音标准差50的数据集.

# 使用make_regression 生成特征1,噪音50
from sklearn.datasets import make_regression
X, y = make_regression(n_features=1,n_informative=1,noise=50,random_state=8)
plt.scatter(X,y,c='orange',edgecolor='k')
plt.show()
make_regression生成数据集
# 导入kNN回归分析的模型
from sklearn.neighbors import KNeighborsRegressor
reg = KNeighborsRegressor()
# 拟合数据并评分
reg.fit(X,y)
print('模型评分:{:.2f}'.format(reg.score(X,y)))

# 预测结果可视化.
z = np.linspace(-3,3,200).reshape(-1,1)
plt.scatter(X,y,c='orange',edgecolor='k')
plt.plot(z, reg.predict(z),c='k',linewidth=3)
plt.title('KNN Regressor')
plt.show()
kNN回归分析(k=5)
# 导入kNN回归分析的模型
from sklearn.neighbors import KNeighborsRegressor
reg2 = KNeighborsRegressor(n_neighbors=20)
# 拟合数据并评分
reg2.fit(X,y)
print('模型评分:{:.2f}'.format(reg2.score(X,y)))

# 预测结果可视化.
z = np.linspace(-3,3,200).reshape(-1,1)
plt.scatter(X,y,c='orange',edgecolor='k')
plt.plot(z, reg2.predict(z),c='k',linewidth=3)
plt.title('KNN Regressor: n_neighbors=20')
plt.show()
# k = 2: 模型评分:0.86
# k = 5: 模型评分:0.77 (上面默认的5)
# k = 10: 模型评分:0.75
# k = 20: 模型评分:0.67
# k = 50: 模型评分:0.54
kNN回归分析(k=2) kNN回归分析(k=5) kNN回归分析(k=10) kNN回归分析(k=20) kNN回归分析(k=50)

从上述可以看出, 随着k的增大, 评分下降. 但k太小的时候, 显然存在一定过拟合的情况.

# 尝试使用线性回归求解
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(X,y)
print('模型评分:{:.2f}'.format(lr.score(X,y)))
# 预测结果可视化.
z = np.linspace(-3,3,200).reshape(-1,1)
plt.scatter(X,y,c='orange',edgecolor='k')
plt.plot(z, lr.predict(z),c='k',linewidth=3)
plt.title('Linear Regressor')
plt.show()

# 模型评分: 0.69
线性回归分析

对比线性回归, kNN=20时较为接近. 太大时拟合不好, 太小时过拟合.

那究竟分开训练集和测试集后是不是能明显看出这种差异呢?

# 重新测试分割训练集合测试集后的效果
# 点数越多, 拟合越稳定, 效果更稳定
X, y = make_regression(n_samples=200, n_features=1,n_informative=1,noise=50,random_state=8)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,random_state=4)
# 喜欢的可以把下面的random_state=4去掉,看多个效果

# 测试分割训练集和测试集
for n in [2,5,10,20,50]:
    reg3 = KNeighborsRegressor(n_neighbors=n)
    reg3.fit(X_train, y_train)
    print('训练集评分:{:.2f}'.format(reg3.score(X_train, y_train)))
    print('测试集评分:{:.2f}'.format(reg3.score(X_test, y_test)))
    # 预测结果可视化.
    z = np.linspace(-3,3,200).reshape(-1,1)
    plt.scatter(X_train, y_train,c='orange',edgecolor='k')
    plt.scatter(X_test, y_test,c='green',edgecolor='k')
    plt.plot(z, reg3.predict(z),c='k',linewidth=2)
    plt.title('KNN Regressor: n_neighbors=2')
    plt.show()

lr2 = LinearRegression()
lr2.fit(X_train, y_train)
print('训练集评分:{:.2f}'.format(lr2.score(X_train, y_train)))
print('测试集评分:{:.2f}'.format(lr2.score(X_test, y_test)))
# 预测结果可视化.
z = np.linspace(-3,3,200).reshape(-1,1)
plt.scatter(X_train, y_train,c='orange',edgecolor='k')
plt.scatter(X_test, y_test,c='green',edgecolor='k')
plt.plot(z, lr2.predict(z),c='k',linewidth=2)
plt.title('Linear Regressor')
plt.show()

从上面结果来看, 的确存在过拟合现象, 尤其当k=2时最严重. k=5和10差不多, k=20时尽管训练集评分低些, 但测试集评分最高. 当k=50时, 出现欠拟合现象, 训练集和测试集评分均不高.

# 重新测试分割训练集合测试集后的效果
# 点数越多, 拟合越稳定, 效果更稳定
import pandas as pd
kv1 = [100, 200, 500, 1000, 2000]
kv2 = [2, 5, 10, 20, 50]

final_out = []
for sn in kv1:
    X, y = make_regression(n_samples=sn, n_features=1,
                           n_informative=1, noise=50, random_state=8)
    output = []
    for i in range(100):
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,random_state=i)
        scores = []

        # 测试分割训练集和测试集
        for n in kv2:
            reg3 = KNeighborsRegressor(n_neighbors=n)
            reg3.fit(X_train, y_train)
            scores += [reg3.score(X_train, y_train), reg3.score(X_test, y_test)]

        lr2 = LinearRegression()
        lr2.fit(X_train, y_train)
        scores += [lr2.score(X_train, y_train), lr2.score(X_test, y_test)]
        output.append(scores)
    final_out.append(pd.DataFrame(output).mean().tolist())
pd.DataFrame(final_out,
             columns=pd.MultiIndex.from_product([['k=2', 'k=5', 'k=10','k=20','k=50','LR']
                     ,['train', 'test']]),
             index = kv1).round(decimals=3)
多次测试

上述做了个多次测试, 主要对采样训练和测试做了随机100次, 而不同的k, 以及不同的样本数的情况. 从上表中可以看到一个比较奇怪的是500样本时比较反常, 因为此时(随机为8)时, 样本点比较分散, 因此回归和kNN的效果都不甚好, 甚至在kNN中的测试集出现了负数.

从上表也可以看出, 当k较小时, 容易出现过拟合, 当k=5或10时, 其实已经比较稳定了, 继续增大时(k=50)部分就出现欠拟合情况(点多的情况, k=50也开始稳定.)在多次的随机训练可以看出, 线性回归在这里的测试表现最佳, 训练和测试的成功率相当.

500次样本时的点分布

书中说, k=2时覆盖更多点, 评分比k=5时显著提高. 这实在说得不科学. 上面测试做了更丰富的结果供参考.

3.3 kNN实战 - 酒的分类

其实这部分也没啥.. 和前面大同小异. 主要的差异就是, 继续学习使用sklearn的datasets模块读取已有的数据集.

3.3.1 数据集基本分析

#从sklearn的datasets模块载入数据集
from sklearn.datasets import load_wine
wine_dataset = load_wine()

print("红酒数据集中的键:\n{}".format(wine_dataset.keys()))
# 红酒数据集中的键:
# dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])

print('数据概况:{}'.format(wine_dataset['data'].shape))
# 数据概况:(178, 13)

print(wine_dataset['DESCR'])
# .. _wine_dataset:
# Wine recognition dataset
# ------------------------
# **Data Set Characteristics:**
# 后面接了一堆的说明. 只放出有意义的部分
    :Summary Statistics:
    
    ============================= ==== ===== ======= =====
                                   Min   Max   Mean     SD
    ============================= ==== ===== ======= =====
    Alcohol:                      11.0  14.8    13.0   0.8
    Malic Acid:                   0.74  5.80    2.34  1.12
    Ash:                          1.36  3.23    2.36  0.27
    Alcalinity of Ash:            10.6  30.0    19.5   3.3
    Magnesium:                    70.0 162.0    99.7  14.3
    Total Phenols:                0.98  3.88    2.29  0.63
    Flavanoids:                   0.34  5.08    2.03  1.00
    Nonflavanoid Phenols:         0.13  0.66    0.36  0.12
    Proanthocyanins:              0.41  3.58    1.59  0.57
    Colour Intensity:              1.3  13.0     5.1   2.3
    Hue:                          0.48  1.71    0.96  0.23
    OD280/OD315 of diluted wines: 1.27  4.00    2.61  0.71
    Proline:                       278  1680     746   315
    ============================= ==== ===== ======= =====

    :Missing Attribute Values: None
    :Class Distribution: class_0 (59), class_1 (71), class_2 (48)

使用 load_xxxx的方法就可以简单地加载该数据集, 其实数据集是一种Bunch对象, 用于数据集的一种容器, 类似于字典, 有key和value. 可使用对象.key 来将键名作为属性来调用. 属性有: 'DESCR', 'data', 'feature_names', 'target', 'target_names', 部分数据集还有如'filename'等.

此外, datasets 模块还有不少小数据集:

datasets中已有的数据集

除此以外, 还能简单地获取更大的一些数据集:


datasets使用fetch获取的数据集

3.3.2 生成训练数据集和测试数据集

使用model_selection模块的train_test_split方法可以将数据集随机分成一定大小的子集. 从而实现训练和测试两步. 前面已经说了, 一般使用大写X来储存多维数据的训练集, 而用小写y储存一位数组(向量).

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(wine_dataset['data'], 
                                      wine_dataset['target'], random_state=0)

print('X_train shape:{}'.format(X_train.shape))
print('X_test shape:{}'.format(X_test.shape))
print('y_train shape:{}'.format(y_train.shape))
print('y_test shape:{}'.format(y_test.shape))
# X_train shape:(133, 13)
# X_test shape:(45, 13)
# y_train shape:(133,)
# y_test shape:(45,)

3.3.3 kNN进行建模和预测

和前面类似, 包括了使用fit来拟合(使用训练集X_train, y_train), 使用predict来预测(使用测试集X_test, y_test), 使用score也可以预测并直接评分.

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors = 1)
knn.fit(X_train, y_train)

print('训练数据集得分:{:.2f}'.format(knn.score(X_train, y_train)))
# 训练数据集得分:1.00

print('测试数据集得分:{:.2f}'.format(knn.score(X_test, y_test)))
# 测试数据集得分:0.76

这里的得分其实是预测正确样本数/总输入样本数.

假设有一瓶新酒, 经测试各种指标后(如酒精含量,某某成分含量等), 可以建立其特征. 利用新特征, 就可以对其进行分类预测:

import numpy as np
X_new = np.array([[13.2,2.77,2.51,18.5,96.6,1.04,2.55,0.57,1.47,6.2,1.05,
                  3.33,820]])
prediction = knn.predict(X_new)

print("预测新红酒的分类为:{}".format(wine_dataset['target_names'][prediction]))
# 预测新红酒的分类为:['class_2']

kNN在实际应用时, 还要做好特征的处理, 有时特征的权重也很重要, 对高维数据容易拟合欠佳, 对稀疏数据束手无策.

上一篇下一篇

猜你喜欢

热点阅读