【机器学习 第二周】数据拆分&评价结果&超参数调整

梳理大纲: 数据拆分&评价结果
【1】数据拆分:训练数据集&测试数据集
【2】评价分类结果:精准度、混淆矩阵、精准率、召回率、F1 Score、ROC曲线等
【3】评价回归结果:MSE、RMSE、MAE、R Squared
【4】超参数调整 (引入调参器概念)

参考资料:
机器学习 西瓜书
微信文章 From【木东居士】 公众号

From 机器学习 小组:由【木东居士】公众号 定期发起
对数据感兴趣的伙伴们 可一同在此交流学习

【1】数据拆分

将数据集分隔为训练集测试集

——手动实现 随机拆分数据集:

# 方法1:使用concatenate函数进行拼接

# 因为传入的矩阵必须具有相同的形状,因此需要对label进行reshape操作,#reshape(-1,1)表示行数自动计算,1列。axis=1表示纵向拼接。
tempConcat = np.concatenate((X, y.reshape(-1,1)), axis=1)

# 拼接好后,直接进行乱序操作
np.random.shuffle(tempConcat)

# 再将shuffle后的数组使用split方法拆分
shuffle_X,shuffle_y = np.split(tempConcat, [4,], axis=1)

#设置划分的比例
test_ratio = 0.2
test_size = int(len(X) * test_ratio)
X_train = shuffle_X[test_size:]
y_train = shuffle_y[test_size:]
X_test = shuffle_X[:test_size]
y_test = shuffle_y[:test_size]

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
# 方法2:将索引随机打乱

# 将x长度这么多的数,返回一个新的打乱顺序的数组,注意,数组中的元素不是原来的数据,而是混乱的索引
shuffle_index = np.random.permutation(len(X))

# 指定测试数据的比例
test_ratio = 0.2
test_size = int(len(X) * test_ratio)

test_index = shuffle_index[:test_size]
train_index = shuffle_index[test_size:]
X_train = X[train_index]
X_test = X[test_index]
y_train = y[train_index]
y_test = y[test_index]

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)

——手动 起草函数-调用

import numpy as np

定义函数部分:
def train_test_split(X, y, test_ratio=0.2, seed=None):
    """将矩阵X和标签y按照test_ration分割成X_train, X_test, y_train, y_test"""
    
    # assert断言语句:用于规范输入
    assert X.shape[0] == y.shape[0], "the size of X must be equal to the size of y"
    assert 0.0 <= test_ratio <= 1.0, "test_train must be valid"

    if seed: # 是否使用随机种子,使随机结果相同,方便debug
        np.random.seed(seed)    
    shuffle_index = np.random.permutation(len(X))
	# permutation(n) 可直接生成一个随机排列的数组,含有n个元素
    test_size = int(len(X) * test_ratio)
    test_index = shuffle_index[:test_size]
    train_index = shuffle_index[test_size:]
    
    X_train = X[train_index]
    X_test = X[test_index]
    y_train = y[train_index]
    y_test = y[test_index]    
    
    return X_train, X_test, y_train, y_test

调用函数部分:
X_train, X_test, y_train, y_test = train_test_split(X, y)

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)

——Sklearn中的分隔数据集功能

# 引入sklearn中的train_test_split
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=666)

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)

【2】评价分类结果

先引入概念:混淆矩阵

对于二分类问题来说,所有的问题被分为0和1两类,混淆矩阵是2*2的矩阵:
在这里插入图片描述

  • TN:真实值是0,预测值也是0,即我们预测是negative,预测正确了。
  • FP:真实值是0,预测值是1,即我们预测是positive,但是预测错误了。
  • FN:真实值是1,预测值是0,即我们预测是negative,但预测错误了。
  • TP:真实值是1,预测值是1,即我们预测是positive,预测正确了。

现在假设有1万人进行预测,填入混淆矩阵如下:
在这里插入图片描述
假设1万个人中,有9978个人本身并没有癌症
我们如果清一色预测所有人都没有得病,那最常规的accuray也能达到99.78%
所以这种正负样本严重失衡的情况下,使用accuracy判断 容易出现问题


因此 需要引入其他的评估指标

[1] 准确率 accuracy
accuracy_score:函数计算分类准确率,返回被正确分类的样本比例(default)或者是数量(normalize=False)

[2] 精准率 precision & 召回率 recall

根据混淆矩阵可以求得指标:
在这里插入图片描述

——手动代码实现:
( 使用逻辑回归 - 验证像素集结果是否为9 )

# 引入数据集&数据拆分
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

digits = datasets.load_digits()
X = digits.data
y = digits.target.copy()

# 要构造偏斜数据,将数字9的对应索引的元素设置为108设置为0
y[digits.target==9]=1
y[digits.target!=9]=0

# 使用逻辑回归做一个分类
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)

log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
# 得到X_test所对应的预测值
y_log_predict = log_reg.predict(X_test)
# 定义混淆矩阵
# TN
def TN(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
    # 向量与向量按位与,结果还是布尔向量
    # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
    return np.sum((y_true == 0) & (y_predict == 0))  
    # 向量与向量按位与,结果还是向量
TN(y_test, y_log_predict)

# FP
def FP(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
    # 向量与向量按位与,结果还是布尔向量
    # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
    return np.sum((y_true == 0) & (y_predict == 1))  # 向量与向量按位与,结果还是向量
FP(y_test, y_log_predict)

# FN
def FN(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
    # 向量与向量按位与,结果还是布尔向量
    # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
    return np.sum((y_true == 1) & (y_predict == 0))  
    # 向量与向量按位与,结果还是向量
FN(y_test, y_log_predict)

# TP
def TP(y_true, y_predict):
    assert len(y_true) == len(y_predict)
    # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
    # 向量与向量按位与,结果还是布尔向量
    # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
    return np.sum((y_true == 1) & (y_predict == 1))  # 向量与向量按位与,结果还是向量
TP(y_test, y_log_predict)

# 混淆矩阵
def confusion_matrix(y_true, y_predict):
    return np.array([
        [TN(y_true, y_predict), FP(y_true, y_predict)],
        [FN(y_true, y_predict), TP(y_true, y_predict)]
    ])
confusion_matrix(y_test, y_log_predict)
# 准确率计算
log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
# 得到X_test所对应的预测值
y_log_predict = log_reg.predict(X_test)
log_reg.score(X_test, y_test)

# 计算精准率
def precision_score(y_true, y_predict):
    tp = TP(y_true, y_predict)
    fp = FP(y_true, y_predict)
    try:
        return tp / (tp + fp)
    except:
        return 0.0
precision_score(y_test, y_log_predict)

# 计算召回率
def recall_score(y_true, y_predict):
    tp = TP(y_true, y_predict)
    fn = FN(y_true, y_predict)
    try:
        return tp / (tp + fn)
    except:
        return 0.0
recall_score(y_test, y_log_predict)

——使用Sklearn内置函数 实现

# 混淆矩阵
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, y_log_predict)
# 输出:
# array([[403,   2],
#       [  9,  36]])

# 精准率
from sklearn.metrics import precision_score
precision_score(y_test, y_log_predict)
# 输出:
# 0.9473684210526315

# 召回率
from sklearn.metrics import recall_score
recall_score(y_test, y_log_predict)
# 输出:
# 0.8

【3】评估回归结果

首先,以线性回归模型的三个常用评价方法为例

简单线性回归的目标是:
在这里插入图片描述
在这里插入图片描述
【1】均方误差MSE

测试集中的数据量m不同,因为有累加操作,所以随着数据的增加 ,误差会逐渐积累;因此衡量标准和 m 相关
为了抵消掉数据量的形象,可以除去数据量,抵消误差。通过这种处理方式得到的结果叫做 均方误差MSE(Mean Squared Error):
在这里插入图片描述
【2】 均方根误差RMSE

但是使用均方误差MSE收到量纲的影响
例如在衡量房产时,y的单位是(万元),那么衡量标准得到的结果是(万元平方)
为了解决量纲的问题,可以将其开方(为了解决方差的量纲问题,将其开方得到平方差)得到均方根误差RMSE(Root Mean Squarde Error):
在这里插入图片描述

【3】平均绝对误差MAE

对于线性回归算法还有另外一种非常朴素评测标准
要求真实值 与 预测结果 之间的距离最小,可以直接相减做绝对值,加m次再除以m,即可求出平均距离,被称作平均绝对误差MAE(Mean Absolute Error):
在这里插入图片描述
在之前确定损失函数时,由于绝对值函数不是处处可导的,因此没有使用绝对值。但是在评价模型时不影响。因此模型的评价方法可以和损失函数不同

【4】R Square
之前的分类准确率,就是在【0-1】之间取值。
但RMSE和MAE没有这样的性质,存在不同量纲之间无法比较的局限性
(比如我们在预测波士顿方差,RMSE值是4.9(万美元) 我们再去预测身高,可能得到的误差是10(厘米),我们不能说后者比前者更准确)

所以引入了R Square:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

————手动代码实现:

# MSE 均方误差
mse_test = np.sum((y_predict - y_test) ** 2) / len(y_test)
mse_test

# RMSE 均方根误差
from math import sqrt
rmse_test = sqrt(mse_test)
rmse_test

# MAE 平均绝对误差
mae_test = np.sum(np.absolute(y_predict - y_test)) / len(y_test)
mae_test

# R Square
mean_squared_error(y_test, y_predict) / np.var(y_test)

——Sklearn中的MSE和MAE


from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error

# MSE
mean_squared_error(y_test, y_predict)
# 输出:24.156602134387438

# MAE
mean_absolute_error(y_test, y_predict)
# 输出:3.5430974409463873

# R Square
from sklearn.metrics import r2_score
r2_score(y_test, y_predict)

【4】超参数调整 (引入调参器概念)

所谓超参数,就是在机器学习算法模型执行之前需要指定的参数。
(调参调的就是超参数) 如kNN算法中的k

如何选择最佳的超参数?
以KNN模型为例:
KNN的超参数除了k,还有weights,weights默认是uniform即不考虑距离,也可以写distance来考虑距离权重(默认是欧拉距离,如果要是曼哈顿距离,则可以写参数p(明可夫斯基距离的参数)

—— 手动代码实现:

# 遍历循环参数的可能性
best_method = ""
best_score = 0.0
best_k = -1
for method in ["uniform","distance"]:    
	for k in range(1, 11):
        knn_clf = KNeighborsClassifier(n_neighbors=k, weights=method, p=2)
        knn_clf.fit(X_train, y_train)
        score = knn_clf.score(X_test, y_test)        
        if score > best_score:
            best_k = k
            best_score = score
            best_method = method
print("best_method = ", method)
print("best_k = ", best_k)
print("best_score = ", best_score)

输出:best_method =  distance
best_k =  4best_score =  0.9916666666666667

—— Sklearn中的超参数网格搜索方法Grid Search:

param_search = [
    {"weights":["uniform"],"n_neighbors":[i for i in range(1,11)]
    },
    {"weights":["distance"],"n_neighbors":[i for i in range(1,11)],"p":[i for i in range(1,6)]
    }
]

knn_clf = KNeighborsClassifier()
from sklearn.model_selection import GridSearchCV

grid_search = GridSearchCV(knn_clf, param_search)
grid_search.fit(X_train, y_train)
——————————————————————————————————————
输出:CPU times: user 2min 21s, sys: 628 ms, total: 2min 21s
Wall time: 2min 23s
GridSearchCV(cv=None, error_score='raise',
       estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=1, n_neighbors=5, p=2,
           weights='uniform'),
       fit_params=None, iid=True, n_jobs=1,
       param_grid=[{'weights': ['uniform'], 'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}, {'weights': ['distance'], 'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'p': [1, 2, 3, 4, 5]}],
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=0)
——————————————————————————————————————

# 返回的是网格搜索搜索到的最佳的分类器对应的参数 
grid_search.best_estimator_
——————————————————————————————————————
输出:KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=1, n_neighbors=3, p=3,
           weights='distance')
——————————————————————————————————————

grid_search.best_score_
输出:0.9853862212943633

grid_search.beat_params_
输出:{'n_neighbors':3, 'p':3, 'weights':'distance'}

knn_clf = grid_search.beat_estimator_
knn_clf.score(X_test, y_test)
输出:0.9833333333328

注:best_estimator_和best_score_参数后面有一个_。这是一种常见的语法规范,不是用户传入的参数,而是根据用户传入的规则,自己计算出来的结果,参数名字后面接_

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值