首页 > 技术文章 > 数据挖掘学习与实践

Serenaxy 2020-12-14 09:02 原文

学习

数据挖掘的流程

数据预处理->数据探索->模型训练->模型选择->模型评估

模型选择

模型选择是对超参数的选择,通过校验集,来看看模型那一组超参数有更好的效果

模型评估

参考资料
参考资料

分类

分类问题的常用评估指标有准确率(accuracy)、精确率(precision)、召回率(recall)、F1_score、ROC曲线()等等,它们都可以基于混淆矩阵(confusion matrix)来进行计算
  (1)混淆矩阵:二分类问题混淆矩阵如图所示:

P为Positive正类的意思,N为Negative负类的意思,T为True真的意思、F为False假的意思,举个例子,TP就是预测结果为Positive,同时预测结果为真(真实结果也为positive)。下面为混淆矩阵的代码实现:

#二分类问题
from sklearn.metrics import confusion_matrix
y_pre  = np.array([0, 1, 1, 1, 0])
y_tru = np.array([1, 1, 0, 0, 0])
confusion_mat = confusion_matrix(y_pre, y_tru) # predict_value为行,true_value为列
print(confusion_mat)
[[1 1]
 [2 1]]
\ 实际类别0 实际类别1
预测类别0 1 1
预测类别1 2 1
#多分类问题 
y_pre = np.array([1, 1, 0, 2, 1, 0, 1, 3, 3])
y_tru = np.array([1, 0, 0, 2, 1, 0, 3, 3, 3])

confusion_mat = confusion_matrix(y_pre, y_tru)
print(confusion_mat)
[[2 0 0 0]
 [1 2 0 1]
 [0 0 1 0]
 [0 0 0 2]]
\ 实际类别0 实际类别1 实际类别2 实际类别3
预测类别0 2 0 0 0
预测类别1 1 2 0 1
预测类别2 0 0 1 0
预测类别3 0 0 0 2

  (2)准确率、精确率、召回率:有了混淆矩阵后,就可以计算一些指标了。准确率公式为\(Accuracy = \frac{TP+TN}{TP+TN+FP+FN}\); 精确率公式为\(Precision = \frac{TP}{TP+FP}\); 召回率的公式为\(Recall = \frac{TP}{TP+FN}\)。下面为具体的代码实现:

#准确率、精确率、召回率
y_pre = [0,  1, 1, 0, 1]
y_tru = [0, 0, 0, 0, 1]
confusion_mat = confusion_matrix(y_pre, y_tru)
accuracy = sum([confusion_mat[i, i] for i in range(len(confusion_mat))])  / np.sum(confusion_mat)
precision = confusion_mat[1, 1] / np.sum(confusion_mat[1]) #对于类别而言
recall = confusion_mat[1,1] / np.sum(confusion_mat[: 1])
print('准确率Accuracy={}'.format(accuracy))
print('精确率Precision={}'.format(precision))
print('召回率Recall={}'.format(recall))

准确率Accuracy=0.6
精确率Precision=0.3333333333333333
召回率Recall=0.5
  (3)F1_score:往往精确率和召回率是不可兼得的事情,所以我们需要一个综合两者的指标F1_score,为什么选择F1_score,而不选择mean呢?原因。其公式为\(F1_score = \frac{2\cdot p\cdot r}{p + r}\)。下面为具体的代码实现:

#F1_score
f1_score = 2 * precision * recall / (precision + recall)
print('F1_score={}'.format(f1_score))

F1_score=0.4
其实上面的指标都可以用一个函数实现

from sklearn.metrics import classification_report
y_pre = [0,  1, 1, 0, 1]
y_tru = [0, 0, 0, 0, 1]
print(classification_report(y_tru, y_pre))
              precision    recall  f1-score   support

           0       1.00      0.50      0.67         4
           1       0.33      1.00      0.50         1

    accuracy                           0.60         5
   macro avg       0.67      0.75      0.58         5
weighted avg       0.87      0.60      0.63         5

  (4)ROC(Receiver Operating Characteristic Curve)曲线和AUC(Area under roc Curve):上面的的评价指标适合与输出为具体分类标签的分类问题(或者已经给定概率阀值),可是有些分类问题输出的是每种分类的结果的概率,所以我们需要引入ROC曲线和AUC值。
首先我们先规定分类中的正类,其余为负类,然后我们定义真阳性率\(TPR = \frac{TP}{P}\),假阳性率\(FPR = \frac{FP}{N}\)。下面举个例子来理解真阳性率和假阳性率:一个人被给予若干张动物然后进行分类,我们先规定猫为正类,那么真阳性率在这里就指的是在所有的猫图片中被分类为猫的概率,假阳性率中所有不是猫的图片被分类为猫的概率,简单的来说就是正确判断率和误判率。理解了真阳性率和加阳性率后,下面就可以构建ROC曲线。假设通过分类模型,我们得到了测试集中正类的概率序列([0.1, 0.2, 0.5, 0.8]),那么取不同的概率阀值,就会对应不同的预测值,比如当阀值=0.5时,大于等于阀值的会被预测为正类,小于阀值则预测为负类,预测序列为[非正,非正,正,正], 那么根据真实结果序列就可以计算出对应的(FPR, TPR)。我们让阀值去遍不同的值,就会得到一系列的(FPR, TPR)点,在坐标轴上绘制就得到了ROC曲线。

那么如何利用ROC曲线进行模型评估呢?那么则需要AUC,即ROC曲线包围的面积值。显然,AUC值越高,所对应的模型越好。
下面为具体的代码实现:

from sklearn.metrics import roc_curve, auc
y_true = np.array([0, 0, 1, 1])
y_scores = np.array([0.1, 0.4, 0.35, 0.8])
fpr, tpr, thresholds = roc_curve(y_true, y_scores, pos_label=0)
print('roc曲线为')
print('fpr={}'.format(fpr))
print('tpr={}'.format(tpr))
print('thresholds={}'.format(thresholds))
auc_value = auc(fpr, tpr)
print('auc={}'.format(auc_value))
roc曲线为
fpr=[0.  0.5 0.5 1.  1. ]
tpr=[0.  0.  0.5 0.5 1. ]
thresholds=[1.8  0.8  0.4  0.35 0.1 ]
auc=0.25

回归

sklearn

sklearn是做数据挖掘经常使用的python库,在此记录一些用法。当然最好的参考资料永远是官方文档

转换器

为什么要转换器

  sklearn中的转换器是什么的呢?假设目前这个场景,你想对测试集上输入部分做同训练集一样归一化的处理(\(x' = \frac{x-min}{max-min}\)),那么一般会这样做。

import numpy as np
#自定义训练集和测试集
X_train = np.array([[1, 2], [3, 4]], dtype='float')
X_test = np.array([[2, 2], [3, 3]], dtype='float')
#训练集每列的最大最小值
min0, max0 = min(X_train[:, 0]), max(X_train[:, 0])
min1, max1 = min(X_train[:, 1]), max(X_train[:, 1])
print('训练集X_train=')
print(X_train)
print('训练集X_train的第0列的最大值为{}, 最小值为{}'.format(max0, min0))
print('训练集X_train的第1列的最大值为{}, 最小值为{}'.format(max1, min1))
#对训练集进行归一化处理
for i in range(X_train.shape[0]):
    for j in range(X_train.shape[1]):
        if j == 0:
            X_train[i][j] = (X_train[i][j] - min0) / (max0 - min0)
        else:
            X_train[i][j] = (X_train[i][j] - min1) / (max1 - min1)
print('归一化后的训练集为X_train=')
print(X_train)
训练集X_train=
[[1. 2.]
 [3. 4.]]
训练集X_train的第0列的最大值为3.0, 最小值为1.0
训练集X_train的第1列的最大值为4.0, 最小值为2.0
归一化后的训练集为X_train=
[[0. 0.]
 [1. 1.]]
#再对测试集做相同的处理
print('原测试集X_test=')
print(X_test)
for i in range(X_test.shape[0]):
    for j in range(X_test.shape[1]):
        if j == 0:
            X_test[i][j] = (X_test[i][j] - min0) / (max0 - min0)
        else:
            print(X_test[i][j])
            X_test[i][j] = (X_test[i][j] - min1)  / (max1 - min1) 
print('归一化后的测试集X_test=')
print(X_test)
原测试集X_test=
[[2. 2.]
 [3. 3.]]
2.0
3.0
归一化后的测试集X_test=
[[0.5 0. ]
 [1.  0.5]]

可以看到,上面对测试集归一化非常的麻烦,并且我们还需要记录下训练集的最大最小值,所以sklearn中使用了转换器来简略操作。

import numpy as np
from sklearn.preprocessing import MinMaxScaler
#自定义训练集和测试集
X_train = np.array([[1, 2], [3, 4]], dtype='float')
X_test = np.array([[2, 2], [3, 3]], dtype='float')
ms = MinMaxScaler()
ms.fit(X_train)
X_test = ms.transform(X_test)
print(X_test)
[[0.5 0. ]
 [1.  0.5]]

  在这里其中转换器的fit操作相当于记录训练集中每一列的极值并保存起来,transform函数就相当于用保存的极值对测试集进行归一化
  一般来说,fit就是用来保存通过训练集得到的关键信息,transform就是用fit得到的关键信息来处理测试集,fit_transform则是合并2个函数

自定义转换器

虽然sklearn提供转换器可以满足大多数问题的需要,但是总会遇到已有转换器无法解决的问题,所以我们需要自定义转换器。
下面我们来制作和MinMaxScaler转换器一样功能的转换器

import numpy as np
import pandas as pd
#导入自定义转换器所需要的基类
from sklearn.base import BaseEstimator, TransformerMixin
#定义自定义转换器,重写fit和transform函数
class MyMinMaxScaler(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        self.min = np.min(X, axis=0)
        self.max = np.max(X, axis=0)
        return self
    def transform(self, X, y=None):
        X = X.copy()
        return (X-self.min) / (self.max-self.min)
    
X_train = np.array([[1, 2], [3, 4]], dtype='float')
X_test = np.array([[2, 2], [3, 3]], dtype='float')

ms = MyMinMaxScaler()
ms.fit(X_train)
X_test = ms.transform(X_test)
print(X_test)
[[0.5 0. ]
 [1.  0.5]]

估计器

  估计器是sklearn提供的应一个强大的类,它封装了某个模型,比如决策树模型、贝叶斯模型,使用者可以创建估计器对象创建模型,调用fit方法训练模型,调用predict或者predict_proba来预测结果。例如:

import sklearn.tree as tree
#创建模型
clf = tree.DecisionTreeClassifier()
#训练模型
clf.fit(X_train)
#预测结果
clf.predict(X_test)

管道

连接n个转换器

注意:连接n个转换器后得到的管道视为一个转换器
举个例子:假设目前我们有两个转换器,我们会这样使用

import numpy as np
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import MinMaxScaler
from sklearn.pipeline import Pipeline

#自定义训练集、测试集
X_train = np.array([[1, np.nan, 3],[np.nan, 2, 3],[3, 5, np.nan]])
X_test = np.array([[2, np.nan, 3],[np.nan, 2, 3],[3, 4, np.nan]])

#定义转换器
imputer = SimpleImputer(strategy='most_frequent')
scaler = MinMaxScaler()

#通过训练集获取转换器关键参数
X_train_1 = imputer.fit_transform(X_train)
scaler.fit(X_train_1)

#对测试集做相同处理
X_test_1 = imputer.transform(X_test)
X_test_2 = scaler.transform(X_test_1)
print(X_test_2)
[[0.5        0.         0.        ]
 [0.         0.         0.        ]
 [1.         0.66666667 0.        ]]

同样这里比较麻烦,可以用Pipeline对象将多个转换器连接起来,起到一个转换器的效果

# 自定义训练集、测试集
X_train = np.array([[1, np.nan, 3],[np.nan, 2, 3],[3, 5, np.nan]])
X_test = np.array([[2, np.nan, 3],[np.nan, 2, 3],[3, 4, np.nan]])

pipeline = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('scaler', MinMaxScaler())
])

pipeline.fit(X_train)
X_test_ = pipeline.transform(X_test)
print(X_test_)
[[0.5        0.         0.        ]
 [0.         0.         0.        ]
 [1.         0.66666667 0.        ]]

连接n个转换器和1个评估器

注意:连接n个转换器和1个评估器得到的管道视为一个评估器

调用fit函数会依次调用转换器的fit_transform函数,最后在调用估计器的fit函数进行训练
调用predict或者predict_proba函数则是会依次调用转换器的transform函数对测试集做和训练集相同的处理,最后在调用估计器的predict或predict_proba函数进行预测

numpy

pandas

concat函数

该函数用于DataFrame对象的链接,下面举具体的例子:

# axis=0 行合并
import pandas as pd

df1 = pd.DataFrame({'name': ['小明', '小红'], 'color': ['蓝', '红']})
df2 = pd.DataFrame({'name': ['小黑', '小白'], 'color': ['黑', '白']})
df_concat = pd.concat([df1, df2], axis=0)

print('df1=')
print(df2)
print()
print('df2=')
print(df2)
print()
print('行合并结果为')
print('df_concat=')
print(df_concat)
df1=
  name color
0   小黑     黑
1   小白     白

df2=
  name color
0   小黑     黑
1   小白     白

行合并结果为
df_concat=
  name color
0   小明     蓝
1   小红     红
0   小黑     黑
1   小白     白
# axis=1 列合并
import pandas as pd

df1 = pd.DataFrame({'name': ['小黑', '小白'], 'color': ['黑', '白']})
df2 = pd.DataFrame({'Age': [10, 10]})
df_concat = pd.concat([df1, df2], axis=1)

print('df1=')
print(df2)
print()
print('df2=')
print(df2)
print()
print('列合并结果为')
print('df_concat=')
print(df_concat)
df1=
   Age
0   10
1   10

df2=
   Age
0   10
1   10

列合并结果为
df_concat=
  name color  Age
0   小黑     黑   10
1   小白     白   10

copy函数

copy函数的参数deep=False时为浅复制,deep=True时为深复制。原来网上是说deep=False为默认值,但是个人实践发现,在python3.8版本下,deep=True为默认值。

import pandas as pd
df = pd.DataFrame({'a':[1, 2], 'b':[3, 4]})
print('原来df=')
print(df)
print('然后用不同方式复制df....然后更改df.....')
df_True = df.copy(deep=True)
df_False = df.copy(deep=False)
df_Default = df.copy()
df['a'][0] = 10
print('更改后的df=')
print(df)
print('深复制得到df_True=')
print(df_True)
print('浅复制得到df_False=')
print(df_False)
print('默认复制(深复制)的得到的df_Default=')
print(df_Default)
原来df=
   a  b
0  1  3
1  2  4
然后用不同方式复制df....然后更改df.....
更改后的df=
    a  b
0  10  3
1   2  4
深复制得到df_True=
   a  b
0  1  3
1  2  4
浅复制得到df_False=
    a  b
0  10  3
1   2  4
默认复制(深复制)的得到的df_Default=
   a  b
0  1  3
1  2  4

实践

泰坦尼克号分类

项目存放地址

推荐阅读