机器学习

1 机器学习概述

1.1 介绍
1.1.1 什么是机器学习

机器学习是从数据中自动分析获得规律(模型),并利用规律对未知数据进行预测或分类

1.1.2 机器学习与人工智能的关系

机器学习是实现人工智能的一种技术手段。

1.1.3 模型与样本数据
1.1.3.1 模型

模型可以理解为特殊的对象,在对象内部集成或封装了某种形式的方程,不过这些方程还无法进行求解。
模型的作用是用于对未知数据进行预测或分类。

1.1.3.2 样本数据

样本数据是整体数据的一部分,在一定程度上可以反映出整体数据所蕴含的某些规律。
样本数据包括特征标签
特征是方程输入的自变量,标签是方程输出的因变量。

样本数据的载体
样本数据一般存储于文件中,例如csv,而不会存储于数据库中。
原因:

  1. 性能瓶颈
    对于数据量级较大的数据,不便于使用数据库进行存储和高效读写;
  2. 数据库使用的数据格式可能不适用于机器学习的需求。
1.1.3.3 样本数据与模型的关系

训练模型是将样本数据输入到模型的方程中,获得方程的参数,使得方程可以求解。
模型实现的预测或分类可以理解为向模型的方程中输入数据进行计算的结果。

1.1.3.4 模型分类

有监督学习
向模型输入的样本数据中包含数据和标签,获得能够将数据映射到标签的方程。

无监督学习
向模型输入的样本数据中只需要包含特征数据,没有任何标签。

1.1.3.5 样本数据的获取途径
  1. Kaggle数据科学竞赛平台:https://www.kaggle.com/
  2. UCI数据库:https://archive.ics.uci.edu/ml/index.php
  3. sklearn模块提供的数据集。

2 特征工程

2.1 什么是特征工程

数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。

特征工程是指将原始数据转变为模型可以直接使用的训练数据的过程,纯净的训练数据能够更好地反映出模型的潜在规律。
特征工程的目的是最大限度地从原始数据中提取出有价值的特征以供模型使用,使得模型能够逼近机器学习的上限,因此特征工程就是对特征的处理,直接影响模型的性能和输出结果。

2.2 特征抽取
2.2.1 定义

特征抽取是对数据进行某种变换以突出代表性特征的操作。
对于非数值型特征的特征抽取称为特征值化

2.2.2 特征值化

特征值化是将非数值型特征转换成数值型特征。

2.2.3 文本特征抽取

特征值化过程中将训练文本中的词汇单独视为一列特征,只考虑每个词的出现次数,不考虑其出现顺序,每一行表示一个训练文本的词频统计结果。
CountVectorizer实例化的对象用于对文本数据进行特征值化处理。

from sklearn.feature_extraction.text import CountVectorizer

demo_list = ['Lift is short, Ben love python.', 'Lift is too long, Ben hate python.']
cv = CountVectorizer()
cv_fit = cv.fit_transform(demo_list)

print(cv.get_feature_names())  # 获取特征列名,重复的词汇只统计1次。
'''
['ben', 'hate', 'is', 'lift', 'long', 'love', 'python', 'short', 'too']
'''

print(cv_fit)  # Sparse矩阵
'''
  (0, 3)	1
  (0, 2)	1
  (0, 7)	1
  (0, 0)	1
  (0, 5)	1
  (0, 6)	1
  (1, 3)	1
  (1, 2)	1
  (1, 0)	1
  (1, 6)	1
  (1, 8)	1
  (1, 4)	1
  (1, 1)	1
'''

print(cv_fit.toarray())  # 将结果转化为数组
'''
[[1 0 1 1 0 1 1 1 0]
 [1 1 1 1 1 0 1 0 1]]
'''

print(cv_fit.toarray().sum(axis=0))  # 统计每个词出现的次数
'''
[2 1 2 2 1 1 2 1 1]
'''

print(cv.inverse_transform(cv_fit.toarray()))  # 将特征矩阵还原成原始数据
'''
[array(['ben', 'is', 'lift', 'love', 'python', 'short'], dtype='<U6'), array(['ben', 'hate', 'is', 'lift', 'long', 'python', 'too'], dtype='<U6')]
'''
2.2.4 字典特征抽取

对字典数据进行特征值化,将由特征与值的映射字典组成的列表转换成向量。

from sklearn.feature_extraction import DictVectorizer

demo_list = [
    {'city': 'BJ', 'temp': 33},
    {'city': 'GZ', 'temp': 42},
    {'city': 'SH', 'temp': 40}
]
dv = DictVectorizer(sparse=False)
dv_fit = dv.fit_transform(demo_list)  # 进行特征值化

print(type(dv_fit))  # <class 'numpy.ndarray'>
print(dv_fit)  # 返回sparse矩阵
'''
[[ 1.  0.  0. 33.]
 [ 0.  1.  0. 42.]
 [ 0.  0.  1. 40.]]
'''

print(dv.get_feature_names())  # 获取特征列名
'''
['city=BJ', 'city=GZ', 'city=SH', 'temp']
'''

print(dv.inverse_transform(dv_fit))  # 将特征矩阵还原成原始数据
'''
[{'city=BJ': 1.0, 'temp': 33.0}, {'city=GZ': 1.0, 'temp': 42.0}, {'city=SH': 1.0, 'temp': 40.0}]
'''
2.2.5 稀疏矩阵

稀疏矩阵(sparse matrix),0元素的数量远多于非0元素的数量,且非0元素的分布没有规律。

dv = DictVectorizer()
dv = DictVectorizer(sparse=True)  # 默认值
from sklearn.feature_extraction import DictVectorizer

demo_list = [
    {'city': 'BJ', 'temp': 33},
    {'city': 'GZ', 'temp': 42},
    {'city': 'SH', 'temp': 40}
]
dv = DictVectorizer()
# dv = DictVectorizer(sparse=True)
dv_fit = dv.fit_transform(demo_list)  # 进行特征值化

print(type(dv_fit))  # <class 'scipy.sparse.csr.csr_matrix'>
print(dv_fit)  # 返回的是sparse矩阵
'''
  (0, 0)	1.0
  (0, 3)	33.0
  (1, 1)	1.0
  (1, 3)	42.0
  (2, 2)	1.0
  (2, 3)	40.0
'''

print(dv.get_feature_names())  # 获取特征列名
'''
['city=BJ', 'city=GZ', 'city=SH', 'temp']
'''

print(dv.inverse_transform(dv_fit))  # 将特征矩阵还原成原始数据
'''
[{'city=BJ': 1.0, 'temp': 33.0}, {'city=GZ': 1.0, 'temp': 42.0}, {'city=SH': 1.0, 'temp': 40.0}]
'''
2.2.6 One-Hot编码

One-Hot编码,又称为一位有效编码,主要是采用N位状态寄存器来对N个状态进行编码,每个状态都拥有独立的寄存器位,并且在任意时候只有一位有效。
Sparse矩阵中的0和1就是One-Hot编码。

One-Hot编码,主要用于对非数值型的数据进行特征值化处理。
优点:处理的结果中特征之间都是独立的,没有权重或优先级的差别。

import pandas as pd

df = pd.DataFrame([
    ['green', 'M', 20, 'class1'],
    ['red', 'L', 21, 'class2'],
    ['blue', 'XL', 30, 'class3']])
df.columns = ['color', 'size', 'weight', 'class label']
'''
 	color 	size 	weight 	class label
0 	green 	M 		20 		class1
1 	red 	L 		21 		class2
2 	blue 	XL 		30 		class3
'''

# One-Hot编码
pd.get_dummies(df['color'])
'''
	blue 	green 	red
0 	0 		1 		0
1 	0 		0 		1
2 	1 		0 		0
'''

pd.concat((pd.get_dummies(df['color']), df), axis = 1)
'''
	blue 	green 	red 	color 	size 	weight 	class label
0 	0 		1 		0 		green 	M 		20 		class1
1 	0 		0 		1 		red 	L 		21 		class2
2 	1 		0 		0 		blue 	XL 		30 		class3
'''
2.2.2.7 中文文本特征抽取 - 结巴分词
  1. CountVectorizer
from sklearn.feature_extraction.text import CountVectorizer

demo_list = ['人生苦短,我用Python。','人生漫长,不用Python。']
cv = CountVectorizer()
cv_fit = cv.fit_transform(demo_list)

print(cv.get_feature_names())
'''
['不用python', '人生漫长', '人生苦短', '我用python']
'''
print(cv_fit.toarray())
'''
[[0 0 1 1]
 [1 1 0 0]]
'''

手动加入空格

from sklearn.feature_extraction.text import CountVectorizer

demo_list = ['人生 苦短,我用 Python。','人生 漫长,不用 Python。']
cv = CountVectorizer()
cv_fit = cv.fit_transform(demo_list)

print(cv.get_feature_names())
'''
['python', '不用', '人生', '我用', '漫长', '苦短']
'''
print(cv_fit.toarray())
'''
[[1 0 1 1 0 1]
 [1 1 1 0 1 0]]
'''

可以看出,CountVectorizer在对中文的特征提取过程中无法正确识别中文词汇,仅通过标点符号或空格等分隔符对文本数据进行特征抽取。

  1. 结巴分词
    结巴分词是Python的中文分词模块。
    结巴分词:https://github.com/fxsjy/jieba
import jieba

demo_str = '“拳愿竞技”是企业、商人们赌上巨额利益,雇用斗技者并令其对打之空手格斗竞技。'
seg_list = list(jieba.cut(demo_str))
print(seg_list)
'''
['“', '拳愿', '竞技', '”', '是', '企业', '、', '商人', '们', '赌', '上', '巨额', '利益', ',', '雇用', '斗技', '者', '并', '令', '其', '对', '打', '之', '空手', '格斗', '竞技', '。']
'''

print('/'.join(seg_list))
'''
“/拳愿/竞技/”/是/企业/、/商人/们/赌/上/巨额/利益/,/雇用/斗技/者/并/令/其/对/打/之/空手/格斗/竞技/。
'''
data1_str = '“拳愿竞技”是企业、商人们赌上巨额利益,雇用斗技者并令其对打之空手格斗竞技。'
data2_str = '原为平息商人斗争之手段,始于江户中期,并传承至现代。'

data1_seg_list = list(jieba.cut(data1_str))
data2_seg_list = list(jieba.cut(data2_str))

data1_seg_str = ' '.join(data1_seg_list)
data2_seg_str = ' '.join(data2_seg_list)

data_total_seg_list = [data1_seg_str, data2_seg_str]
# 特征值化处理
cv = CountVectorizer()
cv_fit = cv.fit_transform(data_total_seg_list)

print(cv.get_feature_names())
'''
['中期', '企业', '传承', '利益', '商人', '始于', '巨额', '平息', '手段', '拳愿', '斗争', '斗技', '格斗', '江户', '现代', '空手', '竞技', '雇用']
'''

print(cv_fit.toarray())
'''
[[0 1 0 1 1 0 1 0 0 1 0 1 1 0 0 1 2 1]
 [1 0 1 0 1 1 0 1 1 0 1 0 0 1 1 0 0 0]]
'''
2.3 特征预处理

通过特征提取可以得到数据原始的特征,此时特征可能存在以下问题:

  1. 不属于同一量纲,即特征的规格不同,无法放在一起进行比较,需要进行无量纲化处理;
  2. 信息冗余,对于某些特征,其包含的数据为区间划分状态,例如考试结果[‘及格’, ‘不及格’],此时需要对区间划分与数值(0, 1)进行映射(二值化);
  3. 模型只接受定量数据,无法直接使用定性特征,使用哑编码的方式将定性特征转换为定量特征;
  4. 存在缺失值。
2.3.1 准备数据集

数据集使用sklearn提供的的IRIS(鸢尾花)数据集。
IRIS数据集由Fisher在1936年整理,包含4个特征:

Sepal.Length - 花萼长度
Sepal.Width - 花萼宽度
Petal.Length - 花瓣长度
Petal.Width - 花瓣宽度

特征值均为正浮点数,单位为厘米。
目标值为鸢尾花的分类,包括

Iris Setosa - 山鸢尾
Iris Versicolour - 杂色鸢尾
Iris Virginica - 维吉尼亚鸢尾
from sklearn.datasets import load_iris

iris = load_iris()  # 导入IRIS数据集
iris.data  # 特征矩阵
iris.target  # 目标向量
2.3.2 无量纲化

无量纲化用于使不同规格的数据转换到同一规格,包括标准化和区间缩放法。

2.3.2.1 标准化

标准化的前提是特征值服从正态分布,标准化后,其转换成标准正态分布。
标准化需要计算特征的均值样本数据存储中心架构 样本数据是什么_机器学习和标准差样本数据存储中心架构 样本数据是什么_python_02
样本数据存储中心架构 样本数据是什么_机器学习_03

import numpy as np
from sklearn.preprocessing import StandardScaler

feature = np.random.randint(50,100,size=(3, 5))
'''
array([[70, 79, 72, 59, 50],
       [67, 94, 52, 86, 91],
       [51, 80, 82, 66, 85]])
'''

# 标准化操作,返回的是标准化处理后的数据。
StandardScaler().fit_transform(feature)
'''
array([[ 1.37699742,  1.25491161,  0.57289595, -1.35244738,  0.20203051],
       [-0.40937761, -0.06274558, -1.40619914,  1.03422447, -1.31319831],
       [-0.96761981, -1.19216603,  0.83330319,  0.31822291,  1.1111678 ]])
'''

使用IRIS数据集

from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler

iris = load_iris()
# 标准化操作,返回的是标准化处理后的数据。
StandardScaler().fit_transform(iris.data)
2.3.2.2 区间缩放法、归一化

区间缩放法利用边界值(即数据集中的最大值和最小值),将特征的取值区间缩放到某个指定范围。
范围为[0, 1]的区间缩放法为归一化。

标准化
样本数据存储中心架构 样本数据是什么_机器学习_04

区间缩放法
样本数据存储中心架构 样本数据是什么_数据_05

import numpy as np
from sklearn.preprocessing import MinMaxScaler

feature = np.random.randint(50,100,size=(3, 5))
'''
array([[50, 92, 76, 72, 98],
       [50, 58, 87, 86, 57],
       [90, 90, 77, 82, 84]])
'''

# 范围默认为[0, 1]
MinMaxScaler().fit_transform(feature)
'''
array([[0.        , 1.        , 0.        , 0.        , 1.        ],
       [0.        , 0.        , 1.        , 1.        , 0.        ],
       [1.        , 0.94117647, 0.09090909, 0.71428571, 0.65853659]])
'''

# 指定范围为[1, 10]
MinMaxScaler((1, 10)).fit_transform(feature)
'''
array([[ 1.        , 10.        ,  1.        ,  1.        , 10.        ],
       [ 1.        ,  1.        , 10.        , 10.        ,  1.        ],
       [10.        ,  9.47058824,  1.81818182,  7.42857143,  6.92682927]])
'''

使用IRIS数据集

from sklearn.datasets import load_iris
from sklearn.preprocessing import MinMaxScaler

iris = load_iris()
# 区间缩放,返回值为缩放到[0, 1]区间的数据。
MinMaxScaler().fit_transform(iris.data)
2.3.2.3 标准化和归一化总结

如果数据中存在的异常值比较多,例如存在极大的异常值,对于归一化,异常值会影响特征的最大值或最小值,因此归一化受异常值的影响较大,此时建议使用标准化。

2.4 特征选择

当数据预处理完成后,需要选择有意义的特征输入模型中进行训练。

在做特征选择前,需要与数据提供者进行联系、沟通、开会等。一定要与提供数据的人沟通,尤其是理解业务和数据含义的人,目的是理解业务
技术能够让模型起飞,前提是开发者能够做到和业务人员一样理解数据,理解每个特征代表的意义。所以说特征选择的第一步,其实是根据我们的目标,用业务常识来选择特征。

2.4.1 特征选择考虑因素
  1. 特征是否发散:如果某个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本没有差异,这个特征在对于样本区分方面作用很小,需要舍弃这个特征。
  2. 特征与目标的相关性:应该优先选择与目标相关性高的特征。
2.4.2 特征选择方法
  1. Filter过滤法,按照发散性或者相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,选择特征;
  2. Wrapper包装法,根据目标函数(一般是预测效果的评分),每次选择或排除若干特征;
  3. Embedded集成法,先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。与Filter法相比,Embedded法是通过训练来确定特征的优劣。
2.4.3 Filter过滤法 - 方差选择法

Filter过滤法的主要服务对象是需要遍历特征的算法模型,主要目的是在维持算法表现的前提下,降低计算成本。
Filter过滤法包括方差选择法、相关系数法、卡方检验等,这里主要介绍方差选择法。

方差选择法是通过计算各个特征的方差来筛选特征。首先计算各个特征的方差,然后根据阈值选择方差大于阈值的特征。

feature = np.random.randint(0,100,size=(3, 5))
'''
array([[18, 48, 48, 70, 90],
       [ 1, 64, 88, 96, 15],
       [71, 81,  0, 84, 54]])
'''
feature.var(axis=0)  # 方差
'''
array([ 888.66666667, 181.55555556, 1294.22222222, 112.88888889,938. ])
'''
from sklearn.feature_selection import VarianceThreshold

# 方差选择法,返回值为特征选择后的数据。
VarianceThreshold(threshold=800).fit_transform(feature)  # 指定阈值为800
'''
array([[18, 48, 90],
       [ 1, 88, 15],
       [71,  0, 54]])
'''

如果将方差为0或者方差极低的特征去除后,剩余的特征还有很多,同时模型的效果没有得到显著改善,则可以尝试留下一半的特征,设定一个让特征总数减半的方差阈值,即将特征方差的中位数设定为阈值。

from sklearn.feature_selection import VarianceThreshold

vt = VarianceThreshold(threshold=np.median(feature.var(axis=0)))
vt.fit_transform(feature)
'''
[[48 90]
 [88 15]
 [ 0 54]]
'''
2.5 降维

当特征选择完成后,可以直接训练模型了,但是可能由于特征矩阵过大,使得计算量巨大,导致训练时间非常长,因此需要进一步降低特征矩阵维度,即降维。

降维的维度值指的是特征的种类。对于特征数量巨大的特征矩阵,需要压缩数据维度,在尽量减少损失信息的前提下,尽可能降低数据维度。
常见的降维方法包括主成分分析法(PCA)和线性判别分析(LDA)等,这里主要介绍主成分分析法(PCA)。

2.5.1 主成分分析法(PCA)

主成分分析法,PCA(Principal Component Analysis),本质是要将原始的样本映射到维度更低的样本空间中,PCA的目标是为了让映射后的样本具有最大的发散性,因此PCA是一种无监督的降维方法。

from sklearn.decomposition import PCA

# 主成分分析法
pca = PCA(n_components=2)
# 对于参数n_components,
# 如果是小数,表示保留特征的百分比,
# 如果是整数,表示减少后的特征数量。

pca.fit_transform([[0, 2, 4, 3], [0, 3, 7, 3], [0, 9, 6, 3]])
'''
array([[-2.88362421, -1.25443227],
       [-1.45140588,  1.56492061],
       [ 4.33503009, -0.31048834]])
'''

3 数据集

机器学习是从数据中自动分析获得规律,并利用规律对未知数据进行预测。因此机器学习的前提是使用样本数据对模型进行训练,使模型学习到数据中的规律。

3.1 sklearn提供的数据集

sklearn提供的数据集分为Toy和Real World两种,
Toy表示轻量级的数据集,适用于学习和理解算法,
Real World表示真实的数据集,适用于模拟真实环境下的工作。

  1. Toy数据集
sklearn.datasets.load_<数据集名称>

导入鸢尾花数据集
The Iris Dataset

import sklearn.datasets as datasets

iris = datasets.load_iris()  # 导入鸢尾花数据集
dir(iris)  # 属性一览
'''
['DESCR', 'data', 'feature_names', 'filename', 'target', 'target_names']
'''

数据集属性介绍

DESCR - describe,数据集的完整描述
data - 所有数据
feature_names - 特征名称
target - 标签
target_names - 标签名称
  1. Real World数据集
sklearn.datasets.fetch_<数据集名称>

导入带标签的人脸数据集
Faces recognition example using eigenfaces and SVMs

import sklearn.datasets as datasets

iris = datasets.fetch_lfw_people()  # 导入带标签的人脸数据集
3.2 数据集切分

数据集需要按照一定比利划分为训练集和测试集。
训练集用于训练模型,测试集用于评估模型。

from sklearn.model_selection import train_test_split

x_train, x_test,  y_train,  y_test = train_test_split(train_data, train_target, test_size=None, random_state=None)

参数:
train_data - 待划分样本数据
train_target - 待划分样本数据的结果,即标签
test_size - 测试数据占样本数据的比例,若是整数则表示样本数量
random_state - 设置随机数种子,保证每次都是同一个随机数,若为0或不填,则每次得到数据都不一样。

返回值:
x_train - 特征数据的训练集
y_train - 标签数据的训练集
x_test - 特征数据的测试集
y_test - 标签数据的测试集

切分样本数据

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2020)

# 测试数据占样本数据的比例设定为0.2。
x_test.shape  # (30, 4) 特征数据的测试集 150*0.2=30
x_train.shape  # (120, 4) 特征数据的训练集 150*(1-0.2)=120

4 机器学习基础

机器学习模型的最终预测结果都是通过相关的算法计算出来的,所以说在机器学习中算法是核心,数据是基础

4.1 对机器学习从业者的基本要求
  1. 学会分析问题,使用机器学习相关算法实现需求;
  2. 掌握算法的基本思想,学会对不同问题选择合适的算法去解决;
  3. 学会使用框架和库解决问题。
4.2 数据类型分类与算法分类

机器学习中的数据类型分为两大类:离散型数据和连续型数据。
机器学习中的算法主要分为两大类:分类问题和回归问题。
分类问题主要的任务是将一个未知归类的样本归属到某一个已知的类群中;
回归问题主要的任务是根据数据的规律计算出一个未知的数据。

分类算法基于的是【标签数据】为【离散型】数据;
回归算法基于的是【标签数据】为【连续型】数据。

4.3 机器学习开发流程
  1. 数据采集
    公司内部产生的数据、与其他公司合作获取的数据、购买的数据等;
  2. 了解要解决的需求、问题和期望目标,根据标签数据推断问题是属于回归问题还是属于分类问题。
  3. 数据的基本处理
    数据清洗、合并/级联等;
  4. 特征工程
    对数据特征进行处理,包括特征抽取、特征预处理、降维等;
  5. 选择合适的模型,对模型进行训练;
  6. 模型评估
  7. 上线使用

开发流程中前4步可以统称为探索性数据分析EDA (Exploratory Data Analysis),是整个开发流程中的核心。