发布时间:2023-04-01 11:00
更新日期 | 更新次数 | 更新章节 |
---|---|---|
2020-03-19 | 第1次更新 | 1.0 |
2020-04-12 | 第2次更新 | 1.0-5.0 |
本文不涉及公式推导,从纯代码的角度告诉你如何入门使用scikit-learn包。适合非计算机专业的小白0基础体验机器学习,帮助大家消除“公式恐惧症”。
sklearn包是一个pyton的机器学习包,基于numpy和scipy建立,遵守BSD许可(这个许可的开放度最高,程序员可以为所欲为,包括运用于商业软件)。
sklearn的官网在这里
scikit-learn的api中涉及到的类绝大多数都有 obj.fit(),或者obj.fit_transform 方法,这两种方法的专业名词叫估计器(.fit)和转换器(.fit+.transform 或者 .fit_transform)
.fit + .transform
和 .fit_transform
的区别我们用一个实例来解释这个问题。
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
data1= np.random.randint(3,size=(3,3))
data1
array([[0, 1, 1],
[0, 0, 1],
[1, 1, 1]])
然后我们使用.fit_transform
的方法对data1
进行归一化
s=MinMaxScaler()
result=s.fit_transform(data1)
result
array([[0., 1., 0.],
[0., 0., 0.],
[1., 1., 0.]])
然后我们使用.fit+.transform
的方法再来一遍
>>s=MinMaxScaler()
>>result=s.fit(data1)
>>result=s.transform(data1)
result
array([[0., 1., 0.],
[0., 0., 0.],
[1., 1., 0.]])
结果是一样的,所以区别在哪里呢?我们看下面的例子:
>data2= np.random.randint(3,size=(3,3))
>data2
array([[1, 2, 2],
[2, 2, 0],
[2, 0, 1]])
>s=MinMaxScaler()
>result=s.fit(data2)
>result=s.transform(data2)
result
array([[0. , 1. , 1. ],
[1. , 1. , 0. ],
[1. , 0. , 0.5]])
>s=MinMaxScaler()
>result=s.fit(data2)
>result=s.transform(data1)
result
array([[-1. , 0.5, 0.5],
[-1. , 0. , 0.5],
[ 0. , 0.5, 0.5]])
.fit
是用来计算.transform
所需要的数据,例如均值、方差等。上面的最后一个例子中,最后一段代码在对data1
进行归一化的时候使用的是data2
的min和max。
总结一下转换器的使用套路,就三行
#第一步,实例化
object_name= class_name(param)
#第二步,拟合计算 .fit()
result=object_name.fit(data) 拟合数据,意思就是训练了
#第三步,转换.transform()
result=object_name.transform(data)
#当然,如果计算和转换用的是同一个数据集,那么第二步和第三步也可以合并成
result=object_name.fit_transform(data)
我们用鸢尾花数据来做两个机器学习模型,分别是KNN、随机森林(先不考虑超参数的优化)
from sklearn import neighbors, datasets
from sklearn.ensemble import RandomForestClassifier
import numpy as np
n_neighbors = 11
# 导入数据
iris = datasets.load_iris()
x = iris.data[:, :2] # 我们只采用前两个feature,方便画图在二维平面显示
y = iris.target
#上面数据准备好了,我们来拟合(训练)KNN
KNN_clf = neighbors.KNeighborsClassifier(n_neighbors)
KNN_clf.fit(x, y)
#然后我们来拟合(训练)随机森林
forest_clf = RandomForestClassifier(random_state=42)
forest_clf.fit()
#然后我们要预测的话。。
KNN_clf.predict(x, y)
forest_clf.predict(x, y)
综上所述,这个包是非常友好的,即便是你对机器学习算法完全不懂,也可以迅速拟合各种机器学习模型。所以,机器学习模型到底难在哪呢?是超参数的优化!是超参数的优化!是超参数的优化! 这是进阶学习的重点内容!当然,如果你想水非计算机专业的论文(比如交通、土木之类的),你也可以用暴力搜的方法。
但是事实上,sklearn提供了这种暴力搜超参数的机制。后续章节会有详细的说明。总结一下,训练机器学习模型的一般步骤,也就是估计器的使用方法。
#第一步,实例化
object_name= class_name(param)
#第二步,训练
object_name.fit(X, y) 拟合数据,意思就是训练了。
#第三步,预测
object_name.predict(x, y)
这种语法跟R语言很像,另外statsmodels 也是使用了这种语法。这种方法可以在我们在实际应用中专注于超参数的优化。
这一节是对sklearn用户手册中6.3节的要点的整理。 用户手册中的6.3节介绍了sklearn.preprocessing
模块。这个模块致力于将原始数据集中的各个特征向量(你可以理解成数据表格中的行、列),转化为更适合sklearn中各种算法的表示形式。主要包括了标准化,去中心化以及方差归一化
归一化就是把各列的数据限定在0~1之间,公式如下:
X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
X_scaled = X_std * (max - min) + min
这样做的理由是,有些数据的特征scale非常小,如果不将他们统一到同一个尺度,scale大的特征将会占主导地位,归一化的假设就是:假设所有的特征同等重要!
注意:但是这种方法对离群点特别敏感,因为离群点会拉大数据特征的最大值和最小值,所以这个方法的用途并不广泛。它的使用方法可以参看3.1节
数据的标准化是所有的估计算法的共同要求,换句话说在使用sklearn的各种算法进行拟合之前,这一步是必须要做的。官方的解释是:假如一个变量是服从正态分布的,但如果不是标准正态分布(mean:0,sd:1)的话,他们的表现会很差。
实际应用中,不可能所有的变量都是服从正态分布的,但是我们可以用相似的方法将均值和方差进行处理。某一列数据逐个减去他们的方差,然后再除以标准差。类似于将正态分布转化为标准正态分布的方法。公式如下:
z = (x - u) / s
这么做的理由是,很多学习算法的目标函数都假设特征是标准化的,如果一个变量方差很大,它会支配整个计算结果,说白了就是其他变量的影响将会弱化,所以所有变量都在同一尺度上进行比较的话,就会得到更可靠的结果。
preprocessing模块提供了一个叫StandardScale的类,它能很轻易的完成数据的转换。
from sklearn import preprocessing
import numpy as np
#首先任意设置一个二维的数据,行代表观测,列代表变量
X_train = np.array([[ 1., -1., 2.],
[ 2., 0., 0.],
[ 0., 1., -1.]])
scaler = preprocessing.StandardScaler().fit_transform(X_train)
print(scaler)
[[ 0. -1.22474487 1.33630621]
[ 1.22474487 0. -0.26726124]
[-1.22474487 1.22474487 -1.06904497]]
#当然你也可以按照下面的方法写:
scaler = preprocessing.StandardScaler() #实例化
scaler=scaler.fit_transform(X_train) #拟合
print(scaler)
[[ 0. -1.22474487 1.33630621]
[ 1.22474487 0. -0.26726124]
[-1.22474487 1.22474487 -1.06904497]]
这一节主要涉及字典数据的提取和文本数据的提取。api是sklearn.feature_extraction
字典数据提取的api是sklearn.feature_extraction.DictVectorizer
from sklearn.feature_extraction import DictVectorizer
import pandas as pd
import numpy as np
D=[{\'city\':\'北京\', \'temprature\': 34}, {\'city\':\'深圳\', \'temprature\': 27}, {\'city\':\'上海\', \'temprature\': 29}]
print(D)
[{\'city\': \'北京\', \'temprature\': 34}, {\'city\': \'深圳\', \'temprature\': 27}, {\'city\': \'上海\', \'temprature\': 29}]
dict=DictVectorizer(sparse=False,sort=True) #实例化
data=dict.fit_transform(D)
print(data)
[[ 0. 1. 0. 34.]
[ 0. 0. 1. 27.]
[ 1. 0. 0. 29.]]
#下面这个注意,只有实例里有get_feature_names
names=dict.get_feature_names()
print(names)
[\'city=上海\', \'city=北京\', \'city=深圳\', \'temprature\']
#如果想把数据转到字典状态
data_=dict.inverse_transform(data)
print(data_)
[{\'city=北京\': 1.0, \'temprature\': 34.0}, {\'city=深圳\': 1.0, \'temprature\': 27.0}, {\'city=上海\': 1.0, \'temprature\': 29.0}
#然后我们可以生成pandas的DataFrame
pd.DataFrame(columns=dict.get_feature_names(),data=data)
city=上海 city=北京 city=深圳 temprature
0 0.0 1.0 0.0 34.0
1 0.0 0.0 1.0 27.0
2 1.0 0.0 0.0 29.0
我们可以看到,特征提取之后,对于类别变量采用的是one-hot编码。类似于统计学里的哑变量(dummy)。意思是当这个样本是某一个类别的时候就是1,不是这个类别的时候就是0。一般来说,同一个变量大类的各个哑变量是互斥的。
这里注意一下dict=DictVectorizer(sparse=False,sort=True) #实例化
这个语句,里面有一个参数叫sparse,这个参数等于True
的时候是生成一个sparse矩阵,这个是scipy的标准数据格式之一。如果这个地方改成True
看一下结果:
(0, 1) 1.0
(0, 3) 34.0
(1, 2) 1.0
(1, 3) 27.0
(2, 0) 1.0
(2, 3) 29.0
对比下面这个结果:
[[ 0. 1. 0. 34.]
[ 0. 0. 1. 27.]
[ 1. 0. 0. 29.]]
sparse矩阵的含义是:从第0行开始看,(0,1)的含义是第0行,第1列的这个元素是1,以此类推。
如果要处理中文文档,需要把文章里的各个词汇中间打上空格,否则会把整个一句话当做一个词汇。这个就需要借助一个包叫jieba。打开anaconda prompt:
pip install jieba
文档特征提取的api是sklearn.feature_extraction.text.CountVectorizer
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
import jieba as jb
cont1=list(jb.cut(\"我有一头小毛驴我从来也不骑\"))
cont2=list(jb.cut(\"门前大桥下游过一群鸭,快来快来数一数,二四六七八\"))
print(cont1)
[\'我\', \'有\', \'一头\', \'小毛驴\', \'我\', \'从来\', \'也\', \'不\', \'骑\']
print(cont2)
[\'门前\', \'大桥\', \'下游\', \'过\', \'一群\', \'鸭\', \',\', \'快来\', \'快来\', \'数一数\', \',\', \'二四六\', \'七八\']
c=CountVectorizer()
data=c.fit_transform(cont2)
print(data)
(0, 7) 1
(1, 4) 1
(2, 2) 1
(4, 0) 1
(7, 5) 1
(8, 5) 1
(9, 6) 1
(11, 3) 1
(12, 1) 1
#转化成DataFrame
df=pd.DataFrame(columns=c.get_feature_names(),data=data.toarray())
print(df)
一群 七八 下游 二四六 大桥 快来 数一数 门前
0 0 0 0 0 0 0 0 1
1 0 0 0 0 1 0 0 0
2 0 0 1 0 0 0 0 0
3 0 0 0 0 0 0 0 0
4 1 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0
7 0 0 0 0 0 1 0 0
8 0 0 0 0 0 1 0 0
9 0 0 0 0 0 0 1 0
10 0 0 0 0 0 0 0 0
11 0 0 0 1 0 0 0 0
12 0 1 0 0 0 0 0 0
特征提取之后,它会统计每个词出现的次数。这里注意,\"门前大桥下游过一群鸭,快来快来数一数,二四六七八\"这句话里分解出13个部分:
[\'门前\', \'大桥\', \'下游\', \'过\', \'一群\', \'鸭\', \',\', \'快来\', \'快来\', \'数一数\', \',\', \'二四六\', \'七八\']
所以矩阵有13行,但是仅统计字符数目大于1的词汇。这一点务必牢记。
另外有一个坑CountVectorizer是没有sparse这个参数的,但是sparse矩阵可以用.toarray()
方法转化成numpy的ndarray类型数据。
data.toarray()
array([[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0]], dtype=int64)
入门机器学习并不难,sklearn包非常友好,初学者可以运用各种api的估计器拟合自己的数据进行体验。我把这种方法叫做.fit()大法。但愿这篇文章可以让你体验机器学习的乐趣。限于本人水平,文中的错误还希望读者指出。谢谢!