机器学习笔记

机器学习DAY01

机器学习

概述

什么是机器学习

机器学习是一门能够让编程计算机从数据中学习的计算机科学。
一个计算机程序在完成任务T之后,获得经验E,其表现效果为P,如果任务T的性能表现,也就是用以衡量的P,随着E增加而增加,那么这样计算机程序就被称为机器学习系统。
自我完善,自我增进,自我适应。

为什么需要机器学习
  • 自动化的升级和维护
  • 解决那些算法过于复杂甚至跟本就没有已知算法的问题
  • 在机器学习的过程中协助人类获得对事物的洞见
机器学习的问题
  1. 建模问题
    所谓机器学习,在形式上可这样理解:在数据对象中通过统计或推理的方法,寻找一个接受特定输入X,并给出预期输出Y的功能函数f,即Y=f(X)。
  2. 评估问题
    针对已知的输入,函数给出的输出(预测值)与实际输出(目标值)之间存在一定的误差,因此需要构建一个评估体系,根据误差的大小判定函数的优劣。
  3. 优化问题
    学习的核心在于改善性能,通过数据对算法的反复锤炼,不断提升函数预测的准确性,直至获得能够满足实际需求的最优解,这个过程就是机器学习。
机器学习的种类

监督学习、无监督学习、半监督学习、强化学习

  1. 有监督学习:用已知输出评估模型的性能。
  2. 无监督学习:在没有已知输出的情况下,仅仅根据输入信息的相关性,进行类别的划分。
  3. 半监督学习:先通过无监督学习划分类别,再根据人工标记通过有监督学习预测输出。
  4. 强化学习:通过对不同决策结果的奖励和惩罚,使机器学习系统在经过足够长时间的训练以后,越来越倾向于给出接近期望结果的输出。

批量学习和增量学习

  1. 批量学习:将学习的过程和应用的过程截然分开,用全部的训练数据训练模型,然后再在应用场景中实现预测,当预测结果不够理想时,重新回到学习过程,如此循环。
  2. 增量学习:将学习的过程和应用的过程统一起来,在应用的同时以增量的方式,不断学习新的内容,边训练边预测。

基于实例的学习和基于模型的学习

  1. 根据以往的经验,寻找与待预测输入最接近的样本,以其输出作为预测结果。

    年龄 学历 经验 性别 月薪
    25 硕士 2 10000
    20 本科 3 8000
    20 本科 3
  2. 基于模型的学习:根据以往的经验,建立用于联系输出和输入的某种数学模型,将待预测输入代入该模型,预测其结果。
    输入 -> 输出
    1 2
    2 4
    3 6 Y = 2 * X

    9 ? -> 18

机器学习的一般过程

数据处理

  1. 数据收集 (数据检索、数据挖掘、爬虫)
  2. 数据清洗

机器学习

  1. 选择模型 (算法)
  2. 训练模型 (算法)
  3. 评估模型 (工具、框架、算法知识)
  4. 测试模型

业务运维

  1. 应用模型
  2. 维护模型
机器学习的典型应用

股价预测、推荐引擎、自然语言识别、语音识别、图像识别、人脸识别

机器学习的基本问题

1)回归问题:根据已知的输入和输出寻找某种性能最佳的模型,将未知输出的输入代入模型,得到连续的输出。

2)分类问题:根据已知的输入和输出寻找某种性能最佳的模型,将未知输出的输入代入模型,得到离散的输出。

3)聚类问题:根据已知输入的相似程度,将其划分为不同的群落。

4)降维问题:在性能损失尽可能小的前提下,降低数据的复杂度。

数据预处理

数据预处理的过程: 输入数据 -> 模型 -> 输出数据

数据样本矩阵

年龄 学历 经验 性别 月薪
25 硕士 2 10000
20 本科 3 8000

一行一样本,一列一特征。

数据预处理相关库

# 解决机器学习问题的科学计算工具包
import sklearn.preprocessing as sp
均值移除(标准化)

由于一个样本的不同特征值差异较大,不利于使用现有机器学习算法进行样本处理。均值移除可以让样本矩阵中的每一列的平均值为0,标准差为1。

如何使样本矩阵中的每一列的平均值为0呢?

例如有一列特征值表示年龄: 17, 20, 23
mean = (17 + 20 + 23)/3 = 20
a' = -3
b' =  0
c' =  3
完成!

如何使样本矩阵中的每一列的标准差为1呢?

a' = -3
b' =  0
c' =  3
s' = std(a', b', c') 
[a'/s',  b'/s',  c'/s']

均值移除API:

import sklearn.preprocessing as sp
# scale函数用于对函数进行预处理,实现均值移除。
# array为原数组,返回A为均值移除后的结果。
A = sp.scale(array)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])

std_samples = sp.scale(raw_samples)
print(std_samples)
print(std_samples.mean(axis=0))
print(std_samples.std(axis=0))
范围缩放

将样本矩阵中的每一列的最小值和最大值设定为相同的区间,统一各列特征值的范围。一般情况下会把特征值缩放至[0, 1]区间。

如何使一组特征值的最小值为0呢?

例如有一列特征值表示年龄: [17, 20, 23]
每个元素减去特征值数组所有元素的最小值即可:[0, 3, 6]

如何使一组特征值的最大值为1呢?

[0, 3, 6]
把特征值数组的每个元素除以最大值即可:[0, 1/2, 1]

范围缩放API:

# 创建MinMax缩放器
mms = sp.MinMaxScaler(feature_range=(0, 1))
# 调用mms对象的方法执行缩放操作, 返回缩放过后的结果
result = mms.fit_transform(原始样本矩阵)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])
print(raw_samples)
mms_samples = raw_samples.copy()
for col in mms_samples.T:
    col_min = col.min()
    col_max = col.max()
    a = np.array([
        [col_min, 1],
        [col_max, 1]])
    b = np.array([0, 1])
    x = np.linalg.solve(a, b)
    col *= x[0]
    col += x[1]
print(mms_samples)
# 根据给定范围创建一个范围缩放器
mms = sp.MinMaxScaler(feature_range=(0, 1))
# 用范围缩放器实现特征值的范围缩放
mms_samples = mms.fit_transform(raw_samples)
print(mms_samples)
归一化

有些情况每个样本的每个特征值具体的值并不重要,但是每个样本特征值的占比更加重要。

Python Java PHP
2017 10 20 5
2018 8 5 0

所以归一化即是用每个样本的每个特征值除以该样本各个特征值绝对值的总和。变换后的样本矩阵,每个样本的特征值绝对值之和为1。

归一化相关API:

# array 原始样本矩阵
# norm  范数
#    l1 - l1范数,向量中个元素绝对值之和
#    l2 - l2范数,向量中个元素平方之和
# 返回归一化预处理后的样本矩阵
sp.normalize(array, norm='l1')

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])
print(raw_samples)
nor_samples = raw_samples.copy()
for row in nor_samples:
    row /= abs(row).sum()
print(nor_samples)
print(abs(nor_samples).sum(axis=1))
# 归一化预处理
nor_samples = sp.normalize(raw_samples, norm='l1')
print(nor_samples)
print(abs(nor_samples).sum(axis=1))
二值化

有些业务并不需要分析矩阵的详细完整数据(比如图像边缘识别只需要分析出图像边缘即可),可以根据一个事先给定的阈值,用0和1表示特征值不高于或高于阈值。二值化后的数组中每个元素非0即1,达到简化数学模型的目的。

二值化相关API:

# 给出阈值, 获取二值化器
bin = sp.Binarizer(threshold=阈值)
# 调用transform方法对原始样本矩阵进行二值化预处理操作
result = bin.transform(原始样本矩阵)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])
print(raw_samples)
bin_samples = raw_samples.copy()
bin_samples[bin_samples <= 80] = 0
bin_samples[bin_samples > 80] = 1
print(bin_samples)
# 根据给定的阈值创建一个二值化器
bin = sp.Binarizer(threshold=80)
# 通过二值化器进行二值化预处理
bin_samples = bin.transform(raw_samples)
print(bin_samples)
独热编码

为样本特征的每个值建立一个由一个1和若干个0组成的序列,用该序列对所有的特征值进行编码。

两个数   三个数	四个数
1		3		2
7		5		4
1		8		6  
7		3		9
为每一个数字进行独热编码:
1-10    3-100	2-1000
7-01    5-010   4-0100
        8-001   6-0010
                9-0001
编码完毕后得到最终经过独热编码后的样本矩阵:
101001000
010100100
100010010
011000001

独热编码相关API:

# 创建一个独热编码器
# sparse: 是否使用紧缩格式(稀疏矩阵)
# dtyle:  数据类型
ohe = sp.OneHotEncoder(sparse=是否采用紧缩格式, dtype=数据类型)
# 对原始样本矩阵进行处理,返回独热编码后的样本矩阵。
result = ohe.fit_transform(原始样本矩阵)
ohe = sp.OneHotEncoder(sparse=是否采用紧缩格式, dtype=数据类型)
# 对原始样本矩阵进行训练,得到编码字典
encode_dict = ohe.fit(原始样本矩阵)
# 调用encode_dict字典的transform方法 对数据样本矩阵进行独热编码
result = encode_dict.transform(原始样本矩阵)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    [17., 100., 4000],
    [20., 80., 5000],
    [23., 75., 5500]])
# 创建独热编码器
ohe = sp.OneHotEncoder(sparse=False, dtype=int)
# 用独特编码器对原始样本矩阵做独热编码
ohe_dict = ohe.fit(raw_samples)
ohe_samples = ohe_dict.transform(raw_samples)

ohe_samples = ohe.fit_transform(raw_samples)
print(ohe_samples)
标签编码

根据字符串形式的特征值在特征序列中的位置,为其指定一个数字标签,用于提供给基于数值算法的学习模型。

标签编码相关API:

# 获取标签编码器
lbe = sp.LabelEncoder()
# 调用标签编码器的fit_transform方法训练并且为原始样本矩阵进行标签编码
result = lbe.fit_transform(原始样本矩阵)
# 根据标签编码的结果矩阵反查字典 得到原始数据矩阵
samples = lbe.inverse_transform(result)

案例:

import numpy as np
import sklearn.preprocessing as sp
raw_samples = np.array([
    'audi', 'ford', 'audi', 'toyota',
    'ford', 'bmw', 'toyota', 'ford',
    'audi'])
print(raw_samples)
lbe = sp.LabelEncoder()
lbe_samples = lbe.fit_transform(raw_samples)
print(lbe_samples)
inv_samples = lbe.inverse_transform(lbe_samples)
print(inv_samples)

线性回归

输入		输出
0.5      5.0
0.6      5.5
0.8      6.0
1.1      6.8
1.4      7.0
...
y = f(x)

预测函数:y = w0+w1x
x: 输入
y: 输出
w0和w1: 模型参数

所谓模型训练,就是根据已知的x和y,找到最佳的模型参数w0 和 w1,尽可能精确地描述出输入和输出的关系。

5.0 = w0 + w1 × 0.5
5.5 = w0 + w1 × 0.6

单样本误差:

根据预测函数求出输入为x时的预测值:y’ = w0 + w1x,单样本误差为1/2(y’ - y)2

总样本误差:

把所有单样本误差相加即是总样本误差:1/2 Σ(y’ - y)2

损失函数:

loss = 1/2 Σ(w0 + w1x - y)2

所以损失函数就是总样本误差关于模型参数的函数,该函数属于三维数学模型,即需要找到一组w0 w1使得loss取极小值。

案例:画图模拟梯度下降的过程

  1. 整理训练集数据,自定义梯度下降算法规则,求出w0 , w1 ,绘制回归线。
import numpy as np
import matplotlib.pyplot as mp
train_x = np.array([0.5, 0.6, 0.8, 1.1, 1.4])
train_y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])
test_x = np.array([0.45, 0.55, 1.0, 1.3, 1.5])
test_y = np.array([4.8, 5.3, 6.4, 6.9, 7.3])

times = 1000	# 定义梯度下降次数
lrate = 0.01	# 记录每次梯度下降参数变化率
epoches = []	# 记录每次梯度下降的索引
w0, w1, losses = [1], [1], []
for i in range(1, times + 1):
    epoches.append(i)
    loss = (((w0[-1] + w1[-1] * train_x) - train_y) ** 2).sum() / 2
    losses.append(loss)
    d0 = ((w0[-1] + w1[-1] * train_x) - train_y).sum()
    d1 = (((w0[-1] + w1[-1] * train_x) - train_y) * train_x).sum()
    print('{:4}> w0={:.8f}, w1={:.8f}, loss={:.8f}'.format(epoches[-1], w0[-1], w1[-1], losses[-1]))
    w0.append(w0[-1] - lrate * d0)
    w1.append(w1[-1] - lrate * d1)

pred_test_y = w0[-1] + w1[-1] * test_x
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(train_x, train_y, marker='s', c='dodgerblue', alpha=0.5, s=80, label='Training')
mp.scatter(test_x, test_y, marker='D', c='orangered', alpha=0.5, s=60, label='Testing')
mp.scatter(test_x, pred_test_y, c='orangered', alpha=0.5, s=80, label='Predicted')
mp.plot(test_x, pred_test_y, '--', c='limegreen', label='Regression', linewidth=1)
mp.legend()
mp.show()
  1. 绘制随着每次梯度下降,w0,w1,loss的变化曲线。
w0 = w0[:-1]
w1 = w1[:-1]

mp.figure('Training Progress', facecolor='lightgray')
mp.subplot(311)
mp.title('Training Progress', fontsize=20)
mp.ylabel('w0', fontsize=14)
mp.gca().xaxis.set_major_locator(mp.MultipleLocator(100))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(epoches, w0, c='dodgerblue', label='w0')
mp.legend()
mp.subplot(312)
mp.ylabel('w1', fontsize=14)
mp.gca().xaxis.set_major_locator(mp.MultipleLocator(100))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(epoches, w1, c='limegreen', label='w1')
mp.legend()

mp.subplot(313)
mp.xlabel('epoch', fontsize=14)
mp.ylabel('loss', fontsize=14)
mp.gca().xaxis.set_major_locator(mp.MultipleLocator(100))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(epoches, losses, c='orangered', label='loss')
mp.legend()
  1. 基于三维曲面绘制梯度下降过程中的每一个点。
import mpl_toolkits.mplot3d as axes3d

grid_w0, grid_w1 = np.meshgrid(
    np.linspace(0, 9, 500),
    np.linspace(0, 3.5, 500))

grid_loss = np.zeros_like(grid_w0)
for x, y in zip(train_x, train_y):
    grid_loss += ((grid_w0 + x*grid_w1 - y) ** 2) / 2

mp.figure('Loss Function')
ax = mp.gca(projection='3d')
mp.title('Loss Function', fontsize=20)
ax.set_xlabel('w0', fontsize=14)
ax.set_ylabel('w1', fontsize=14)
ax.set_zlabel('loss', fontsize=14)
ax.plot_surface(grid_w0, grid_w1, grid_loss, rstride=10, cstride=10, cmap='jet')
ax.plot(w0, w1, losses, 'o-', c='orangered', label='BGD')
mp.legend()
  1. 以等高线的方式绘制梯度下降的过程。
mp.figure('Batch Gradient Descent', facecolor='lightgray')
mp.title('Batch Gradient Descent', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.contourf(grid_w0, grid_w1, grid_loss, 10, cmap='jet')
cntr = mp.contour(grid_w0, grid_w1, grid_loss, 10,
                  colors='black', linewidths=0.5)
mp.clabel(cntr, inline_spacing=0.1, fmt='%.2f',
          fontsize=8)
mp.plot(w0, w1, 'o-', c='orangered', label='BGD')
mp.legend()
mp.show()

线性回归

线性回归相关API:

import sklearn.linear_model as lm
# 创建模型
model = lm.LinearRegression()
# 训练模型
# 输入为一个二维数组表示的样本矩阵
# 输出为每个样本最终的结果
model.fit(输入, 输出) # 通过梯度下降法计算模型参数
# 预测输出  
# 输入array是一个二维数组,每一行是一个样本,每一列是一个特征。
result = model.predict(array)

案例:基于线性回归训练single.txt中的训练样本,使用模型预测测试样本。

import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as mp
# 采集数据
x, y = np.loadtxt('../data/single.txt', delimiter=',', usecols=(0,1), unpack=True)
x = x.reshape(-1, 1)
# 创建模型
model = lm.LinearRegression()  # 线性回归
# 训练模型
model.fit(x, y)
# 根据输入预测输出
pred_y = model.predict(x)
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(x, y, c='dodgerblue', alpha=0.75, s=60, label='Sample')
mp.plot(x, pred_y, c='orangered', label='Regression')
mp.legend()
mp.show()
评估训练结果误差(metrics)

线性回归模型训练完毕后,可以利用测试集评估训练结果误差。sklearn.metrics提供了计算模型误差的几个常用算法:

import sklearn.metrics as sm

# 平均绝对值误差:1/m∑|实际输出-预测输出|
sm.mean_absolute_error(y, pred_y)
# 平均平方误差:SQRT(1/mΣ(实际输出-预测输出)^2)
sm.mean_squared_error(y, pred_y)
# 中位绝对值误差:MEDIAN(|实际输出-预测输出|)
sm.median_absolute_error(y, pred_y)
# R2得分,(0,1]区间的分值。分数越高,误差越小。
sm.r2_score(y, pred_y)

案例:在上一个案例中使用sm评估模型误差。

# 平均绝对值误差:1/m∑|实际输出-预测输出|
print(sm.mean_absolute_error(y, pred_y))
# 平均平方误差:SQRT(1/mΣ(实际输出-预测输 出)^2)
print(sm.mean_squared_error(y, pred_y))
# 中位绝对值误差:MEDIAN(|实际输出-预测输出|)
print(sm.median_absolute_error(y, pred_y))
# R2得分,(0,1]区间的分值。分数越高,误差越小。
print(sm.r2_score(y, pred_y))
模型的保存和加载

模型训练是一个耗时的过程,一个优秀的机器学习是非常宝贵的。可以模型保存到磁盘中,也可以在需要使用的时候从磁盘中重新加载模型即可。不需要重新训练。

模型保存和加载相关API:

import pickle
pickle.dump(内存对象, 磁盘文件) # 保存模型
model = pickle.load(磁盘文件)  # 加载模型

案例:把训练好的模型保存到磁盘中。

# 将训练好的模型对象保存到磁盘文件中
with open('../../data/linear.pkl', 'wb') as f:
    pickle.dump(model, f)
    
# 从磁盘文件中加载模型对象
with open('../../data/linear.pkl', 'rb') as f:
    model = pickle.load(f)
# 根据输入预测输出
pred_y = model.predict(x)
岭回归

普通线性回归模型使用基于梯度下降的最小二乘法,在最小化损失函数的前提下,寻找最优模型参数,于此过程中,包括少数异常样本在内的全部训练数据都会对最终模型参数造成程度相等的影响,异常值对模型所带来影响无法在训练过程中被识别出来。为此,岭回归在模型迭代过程所依据的损失函数中增加了正则项,以限制模型参数对异常样本的匹配程度,进而提高模型面对多数正常样本的拟合精度。

import sklearn.linear_model as lm
# 创建模型
model = lm.Ridge(正则强度,fit_intercept=是否训练截距, max_iter=最大迭代次数)
# 训练模型
# 输入为一个二维数组表示的样本矩阵
# 输出为每个样本最终的结果
model.fit(输入, 输出)
# 预测输出  
# 输入array是一个二维数组,每一行是一个样本,每一列是一个特征。
result = model.predict(array)

案例:加载abnormal.txt文件中的数据,基于岭回归算法训练回归模型。

import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as mp
# 采集数据
x, y = np.loadtxt('../data/single.txt', delimiter=',', usecols=(0,1), unpack=True)
x = x.reshape(-1, 1)
# 创建线性回归模型
model = lm.LinearRegression() 
# 训练模型
model.fit(x, y)
# 根据输入预测输出
pred_y1 = model.predict(x)
# 创建岭回归模型
model = lm.Ridge(150, fit_intercept=True, max_iter=10000) 
# 训练模型
model.fit(x, y)
# 根据输入预测输出
pred_y2 = model.predict(x)

mp.figure('Linear & Ridge', facecolor='lightgray')
mp.title('Linear & Ridge', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(x, y, c='dodgerblue', alpha=0.75,
           s=60, label='Sample')
sorted_indices = x.T[0].argsort()
mp.plot(x[sorted_indices], pred_y1[sorted_indices],
        c='orangered', label='Linear')
mp.plot(x[sorted_indices], pred_y2[sorted_indices],
        c='limegreen', label='Ridge')
mp.legend()
mp.show()

多项式回归

若希望回归模型更好的拟合训练样本数据,可以使用多项式回归器。

一元多项式回归

y=w0 + w1 x + w2 x2 + w3 x3 + … + wd xd

将高次项看做对一次项特征的扩展得到:

y=w0 + w1 x1 + w2 x2 + w3 x3 + … + wd xd

那么一元多项式回归即可以看做为多元线性回归,可以使用LinearRegression模型对样本数据进行模型训练。

所以一元多项式回归的实现需要两个步骤:

  1. 将一元多项式回归问题转换为多元线性回归问题(只需给出多项式最高次数即可)。
  2. 将1步骤得到多项式的结果中 w1 w2 … 当做样本特征,交给线性回归器训练多元线性模型。

使用sklearn提供的数据管线实现两个步骤的顺序执行:

import sklearn.pipeline as pl
import sklearn.preprocessing as sp
import sklearn.linear_model as lm

model = pl.make_pipeline(
    sp.PolynomialFeatures(10),  # 多项式特征扩展器
    lm.LinearRegression())      # 线性回归器

案例:

import numpy as np
import sklearn.pipeline as pl
import sklearn.preprocessing as sp
import sklearn.linear_model as lm
import sklearn.metrics as sm
import matplotlib.pyplot as mp
# 采集数据
x, y = np.loadtxt('../data/single.txt', delimiter=',', usecols=(0,1), unpack=True)
x = x.reshape(-1, 1)
# 创建模型(管线)
model = pl.make_pipeline(
    sp.PolynomialFeatures(10),  # 多项式特征扩展器
    lm.LinearRegression())      # 线性回归器
# 训练模型
model.fit(x, y)
# 根据输入预测输出
pred_y = model.predict(x)
test_x = np.linspace(x.min(), x.max(), 1000).reshape(-1, 1)
pred_test_y = model.predict(test_x)
mp.figure('Polynomial Regression', facecolor='lightgray')
mp.title('Polynomial Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(x, y, c='dodgerblue', alpha=0.75, s=60, label='Sample')
mp.plot(test_x, pred_test_y, c='orangered', label='Regression')
mp.legend()
mp.show()

过于简单的模型,无论对于训练数据还是测试数据都无法给出足够高的预测精度,这种现象叫做欠拟合。

过于复杂的模型,对于训练数据可以得到较高的预测精度,但对于测试数据通常精度较低,这种现象叫做过拟合。

一个性能可以接受的学习模型应该对训练数据和测试数据都有接近的预测精度,而且精度不能太低。

训练集R2   测试集R2
0.3        0.4        欠拟合:过于简单,无法反映数据的规则
0.9        0.2        过拟合:过于复杂,太特殊,缺乏一般性
0.7        0.6        可接受:复杂度适中,既反映数据的规则,同时又不失一般性

决策树

基本算法原理

核心思想:相似的输入必会产生相似的输出。例如预测某人薪资:

年龄:1-青年,2-中年,3-老年
学历:1-本科,2-硕士,3-博士
经历:1-出道,2-一般,3-老手,4-骨灰
性别:1-男性,2-女性

年龄 学历 经历 性别 ==> 薪资
1 1 1 1 ==> 6000(低)
2 1 3 1 ==> 10000(中)
3 3 4 1 ==> 50000(高)
==>
1 3 2 2 ==> ?

为了提高搜索效率,使用树形数据结构处理样本数据:
年 龄 = 1 { 学 历 1 学 历 2 学 历 3 年 龄 = 2 { 学 历 1 学 历 2 学 历 3 年 龄 = 3 { 学 历 1 学 历 2 学 历 3 年龄=1\left\{ \begin{aligned} 学历1 \\ 学历2 \\ 学历3 \\ \end{aligned} \right. \quad\quad 年龄=2\left\{ \begin{aligned} 学历1 \\ 学历2 \\ 学历3 \\ \end{aligned} \right. \quad\quad 年龄=3\left\{ \begin{aligned} 学历1 \\ 学历2 \\ 学历3 \\ \end{aligned} \right. =1123=2123=3123
首先从训练样本矩阵中选择第一个特征进行子表划分,使每个子表中该特征的值全部相同,然后再在每个子表中选择下一个特征按照同样的规则继续划分更小的子表,不断重复直到所有的特征全部使用完为止,此时便得到叶级子表,其中所有样本的特征值全部相同。对于待预测样本,根据其每一个特征的值,选择对应的子表,逐一匹配,直到找到与之完全匹配的叶级子表,用该子表中样本的输出,通过平均(回归)或者投票(分类)为待预测样本提供输出。

随着子表的划分,信息熵(信息的混乱程度)越来越小,信息越来越纯,数据越来越有序。

决策树回归器模型相关API:

import sklearn.tree as st

# 创建决策树回归器模型  决策树的最大深度为4
model = st.DecisionTreeRegressor(max_depth=4)
# 训练模型  
# train_x: 二维数组样本数据
# train_y: 训练集中对应每行样本的结果
model.fit(train_x, train_y)
# 测试模型
pred_test_y = model.predict(test_x)

案例:预测波士顿地区房屋价格。

  1. 读取数据,打断原始数据集。 划分训练集和测试集。
import sklearn.datasets as sd
import sklearn.utils as su
# 加载波士顿地区房价数据集
boston = sd.load_boston()
print(boston.feature_names)
# |CRIM|ZN|INDUS|CHAS|NOX|RM|AGE|DIS|RAD|TAX|PTRATIO|B|LSTAT|
# 犯罪率|住宅用地比例|商业用地比例|是否靠河|空气质量|房间数|年限|距中心区距离|路网密度|房产税|师生比|黑人比例|低地位人口比例|
# 打乱原始数据集的输入和输出
x, y = su.shuffle(boston.data, boston.target, random_state=7)
# 划分训练集和测试集
train_size = int(len(x) * 0.8)
train_x, test_x, train_y, test_y = \
    x[:train_size], x[train_size:], \
    y[:train_size], y[train_size:]
  1. 创建决策树回归器模型,使用训练集训练模型。使用测试集测试模型。
import sklearn.tree as st
import sklearn.metrics as sm

# 创建决策树回归模型
model = st.DecisionTreeRegressor(max_depth=4)
# 训练模型
model.fit(train_x, train_y)
# 测试模型
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))
工程优化

不必用尽所有的特征,叶级子表中允许混杂不同的特征值,以此降低决策树的层数,在精度牺牲可接受的前提下,提高模型的性能。通常情况下,可以优先选择使信息熵减少量最大的特征作为划分子表的依据。

集合算法

根据多个不同模型给出的预测结果,利用平均(回归)或者投票(分类)的方法,得出最终预测结果。

基于决策树的集合算法,就是按照某种规则,构建多棵彼此不同的决策树模型,分别给出针对未知样本的预测结果,最后通过平均或投票得到相对综合的结论。

正向激励

首先为样本矩阵中的样本随机分配初始权重,由此构建一棵带有权重的决策树,在由该决策树提供预测输出时,通过加权平均或者加权投票的方式产生预测值。将训练样本代入模型,预测其输出,对那些预测值与实际值不同的样本,提高其权重,由此形成第二棵决策树。重复以上过程,构建出不同权重的若干棵决策树。

正向激励相关API:

import sklearn.tree as st
import sklearn.ensemble as se
# model: 决策树模型(一颗)
model = st.DecisionTreeRegressor(max_depth=4)
# 自适应增强决策树回归模型	
# n_estimators:构建400棵不同权重的决策树,训练模型
model = se.AdaBoostRegressor(model, n_estimators=400, random_state=7)
# 训练模型
model.fit(train_x, train_y)
# 测试模型
pred_test_y = model.predict(test_x)

案例:基于正向激励训练预测波士顿地区房屋价格的模型。

# 创建基于决策树的正向激励回归器模型
model = se.AdaBoostRegressor(
	st.DecisionTreeRegressor(max_depth=4), n_estimators=400, random_state=7)
# 训练模型
model.fit(train_x, train_y)
# 测试模型
pred_test_y = model.predict(test_x)
print(sm.r2_score(test_y, pred_test_y))

特征重要性

作为决策树模型训练过程的副产品,根据每个特征划分子表前后的信息熵减少量就标志了该特征的重要程度,此即为该特征重要性指标。训练得到的模型对象提供了属性:feature_importances_来存储每个特征的重要性。

获取样本矩阵特征重要性属性:

model.fit(train_x, train_y)
fi = model.feature_importances_

案例:获取普通决策树与正向激励决策树训练的两个模型的特征重要性值,按照从大到小顺序输出绘图。

import matplotlib.pyplot as mp

model = st.DecisionTreeRegressor(max_depth=4)
model.fit(train_x, train_y)
# 决策树回归器给出的特征重要性
fi_dt = model.feature_importances_
model = se.AdaBoostRegressor(
    st.DecisionTreeRegressor(max_depth=4), n_estimators=400, random_state=7)
model.fit(train_x, train_y)
# 基于决策树的正向激励回归器给出的特征重要性
fi_ab = model.feature_importances_

mp.figure('Feature Importance', facecolor='lightgray')
mp.subplot(211)
mp.title('Decision Tree', fontsize=16)
mp.ylabel('Importance', fontsize=12)
mp.tick_params(labelsize=
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值