机器学习之逻辑回归(正则化惩罚、交叉验证、上下采样)

#技术栈深潜计划:原理解析&编程技巧深度探索征文活动#

正则化惩罚

目的:为了防止模型过拟合

过拟合

模型只能拟合训练数据的状态被称为过拟合,英文是 overfitting。也就是说过拟合是指我们在训练集上的误差较小,但在测试集上的误差较大。在做回归模型的时候,过度增加函数 fθ(x)的次数会导致过拟合。过拟合不止在回归时出现,在分类时也经常发生,我们要时常留意它。

 正则化的方法

以回归模型的目标损失函数为例:

我们要向这个目标函数增加下面这样的正则化项:

那么现在的E ( θ ) 就变为:

我们要对这个新的目标函数进行最小化,这种方法就称为正则化。

m 是参数的个数,不过一般来说不对 θ0 应用正则化。所以仔细看会发现 j 的取值是从 1 开始的。也就是说,假如预测函数的表达式为 fθ(x) = θ0 + θ1x + θ2x2,那么 m = 2 就意味着正则化的对象参数为 θ1 和 θ2,θ0 这种只有参数的项称为偏置项,一般不对它进行正则化。λ 是决定正则化项影响程度的正的常数。这个值需要我们自己来定。

(1)L1正则
L1是指向量中各个元素绝对值之和, L1正则化偏向于稀疏,它会自动进行特征选择,去掉一些没用的特征,也就是将这些特 征对应的权重置为0.

(2)L2正则
L2是指各个元素平方之和,L2主要功能是为了防止过拟合,当要求参数越小时,说明模型越简单,而模型越简单则,越趋向于平滑,从而防止过拟合。

 正则化的效果

光看表达式可能不容易理解。我们结合图来想象一下吧:首先把目标函数分成两个部分。

C(θ) 是本来就有的目标函数项,R(θ) 是正则化项。C(θ) 和 R(θ) 相加之后就是新的目标函数,所以我们实际地把这两个函数的图形画出来,加起来看看。不过参数太多就画不出图来了,所以这里我们只关注 θ1。而且为了更加易懂,先不考虑 λ。

交叉验证

交叉验证是什么?
在模型建立中,通常有两个数据集:训练集(train)和测试集(test)。训练集用来训练模型;测试集是完全不参与训练的数据,仅仅用来观测测试效果的数据。

一般情况下,训练的结果对于训练集的拟合程度通常还是挺好的,但是在测试集总的表现却可能不行。比如下面的例子:


图一的模型是一条线型方程。 可以看到,所有的红点都不在蓝线上,所以导致了错误率很高,这是典型的不拟合的情况
图二 的蓝线则更加贴近实际的红点,虽然没有完全重合,但是可以看出模型表示的关系是正确的。
图三,所有点都在蓝线上,这时候模型计算出的错误率很低,(甚至将噪音都考虑进去了)。这个模型只在训练集中表现很好,在测试集中的表现就不行。 这是典型的‘过拟合’情况。
所以,训练的模型需要找出数据之间‘真正’的关系,避免‘过拟合’的情况发生。

交叉验证:就是在训练集中选一部分样本用于测试模型。
保留一部分的训练集数据作为验证集/评估集,对训练集生成的参数进行测试,相对客观的判断这些参数对训练集之外的数据的符合程度。

验证集方法
交叉验证的步骤如下:

保留一个样本数据集, (取出训练集中20%的样本不用)
使用数据集的剩余部分训练模型 (使用另外的80%样本训练模型)
使用验证集的保留样本。(完成模型后,在20%的样本中测试)
如果模型在验证数据上提供了一个肯定的结果,那么继续使用当前的模型。
优点: 简单方便。直接将训练集按比例拆分成训练集和验证集,比如50:50。

缺点: 没有充分利用数据, 结果具有偶然性。如果按50:50分,会损失掉另外50%的数据信息,因为我们没有利用着50%的数据来训练模型。

K折交叉验证(k-fold cross validation)


针对上面通过train_test_split划分,从而进行模型评估方式存在的弊端,提出Cross Validation 交叉验证。

Cross Validation:简言之,就是进行多次train_test_split划分;每次划分时,在不同的数据集上进行训练、测试评估,从而得出一个评价结果;如果是5折交叉验证,意思就是在原始数据集上,进行5次划分,每次划分进行一次训练、评估,最后得到5次划分后的评估结果,一般在这几次评估结果上取平均得到最后的 评分。k-fold cross-validation ,其中,k一般取5或10。


训练模型需要在大量的数据集基础上,否则就不能够识别数据中的趋势,导致错误产生
同样需要适量的验证数据点。 验证集太小容易导致误差
多次训练和验证模型。需要改变训练集和验证集的划分,有助于验证模型。
步骤:
随机将整个数据集分成k折;
如图中所示,依次取每一折的数据集作验证集,剩余部分作为训练集
算出每一折测试的错误率
取这里K次的记录平均值 作为最终结果
优点:

适合大样本的数据集
经过多次划分,大大降低了结果的偶然性,从而提高了模型的准确性。
对数据的使用效率更高。train_test_split,默认训练集、测试集比例为3:1。如果是5折交叉验证,训练集比测试集为4:1;10折交叉验证训练集比测试集为9:1。数据量越大,模型准确率越高。
缺点:

对数据随机均等划分,不适合包含不同类别的数据集。比如:数据集有5类数据(ABCDE各占20%),抽取出来的也正好是按照类别划分的5类,第一折全是A,第二折全是B……这样就会导致,模型学习到测试集中数据的特点,用BCDE训练的模型去测试A类数据、ACDE的模型测试B类数据,这样准确率就会很低。
如何确定K值?

一般情况下3、5是默认选项,常建议用K=10。


k值越低,就越有偏差;K值越高偏差就越小,但是会受到很大的变化。
k值越小,就越类似于验证集方法;而k值越大,则越接近LOOCV方法。
 

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
matplotlib.rcParams['axes.unicode_minus'] = False
plt.rcParams['axes.unicode_minus'] = False
data = pd.read_csv(r'F:\机器学习\逻辑回归\creditcard.csv')
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
data['Amount'] = scaler.fit_transform(data['Amount'].values.reshape(-1,1))
data = data.drop(['Time'],axis=1)
# plt.figure(figsize=(8, 6))
# class_counts = data['Class'].value_counts()
# plt.bar(['正常交易 (Class 0)', '欺诈交易 (Class 1)'], class_counts.values,
#         color=['skyblue', 'salmon'])
# plt.ylabel('样本数量')
# plt.title('总人数')
# plt.yscale('log')  # 使用对数刻度,因为两类样本数量差异很大
# for i, v in enumerate(class_counts.values):
#     plt.text(i, v, str(v), ha='center', va='bottom')
# plt.tight_layout()
# plt.show()
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = \
    train_test_split(data.drop(['Class'],axis=1),data['Class'],test_size=0.25,random_state=1000)
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
scores = []
c_ranges = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
for c in c_ranges:
    lr = LogisticRegression(C=c,penalty='l2',solver='lbfgs',max_iter=1000)
    score = cross_val_score(lr,x_train,y_train,cv=10,scoring='recall')#交叉验证
    score_mean = sum(score)/len(score)#验证交叉验证后的召回率
    scores.append(score_mean)
    print("C={},正确率:{}".format(c,score_mean))
best_C = c_ranges[scores.index(max(scores))]#选取最好的C
print("最佳惩罚因子:",best_C)

下采样和过采样

机器学习中的过采样和欠采样


过采样


机器学习中的过采样和欠采样是两种常见的数据处理技术,用于解决不平衡数据集的问题。

过采样(Oversampling)是指增加少数类样本的数量,以使其与多数类样本数量相当。这样可以帮助模型更好地学习少数类的特征,提高分类器对少数类的预测性能。过采样的方法包括复制样本、生成合成样本等。

复制样本:简单地复制少数类样本,使其数量增加到与多数类相当。这种方法简单但可能导致过拟合,因为复制的样本没有提供新的信息。
合成样本:使用一些生成算法,如SMOTE(Synthetic Minority Oversampling Technique),通过对少数类样本进行插值来生成新的合成样本。
举个例子:

假设我们有一个二分类问题,原始数据集如下:

正例(少数类):100个样本
负例(多数类):200个样本

我们可使用SMOTE方法来生成合成样本,SMOTE的基本思想是通过对少数类样本之间的插值来生成新的合成样本。

步骤如下:
1. 确定少数类样本。我们选择正例(少数类)样本作为少数类样本集合。
2. 选择合成样本。对于每个正例样本,我们选择其最近的K个邻居样本作为合成样本的生成候选。假设我们选择K=5。
3. 合成新样本。对于每个正例样本,我们从其5个最近邻居中随机选择一个样本,并使用线性插值生成新的合成样本。例如,假设我们从5个邻居中选择了一个邻居样本,并假设该邻居样本的特征向量为[1, 2, 3],而当前正例样本的特征向量为[4, 5, 6],则我们可以使用线性插值生成一个新的合成样本,例如**[2.5, 3.5, 4.5]**。
4. 添加合成样本。将生成的合成样本添加到原始数据集的正例样本中。
重复步骤2到步骤4,直到我们生成足够数量的合成样本。在这个例子中,我们可能选择生成100个合成样本,使得正例(少数类)的样本数量与负例(多数类)相当。

最终,我们得到平衡的数据集如下:

正例(少数类):200个样本(包括原始样本和合成样本)。
负例(多数类):200个样本。

使用平衡的数据集来训练机器学习模型可以更好地处理正例(少数类)的分类问题。

可以使用第三方库imbalanced-learn来实现过采样,可以使用以下函数:

RandomOverSampler:这个函数实现了随机过采样方法,通过复制少数类样本来增加其数量,以平衡类别分布。
SMOTE:这个函数实现了SMOTE方法,通过合成新的少数类样本来增加其数量。

下面是一个使用imbalanced-learn库中的过采样函数的示例:

from imblearn.over_sampling import RandomOverSampler, SMOTE
import torch
from torch.utils.data import DataLoader

# 假设我们有一个不平衡的数据集
train_dataset = '数据集'
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)

# 创建 RandomOverSampler 对象
over_sampler = RandomOverSampler()

# 使用 RandomOverSampler 进行过采样
oversampled_data, oversampled_labels = over_sampler.fit_resample(train_dataset.data, train_dataset.targets)

# 使用过采样后的数据训练模型
oversampled_dataset = torch.utils.data.TensorDataset(torch.tensor(oversampled_data), torch.tensor(oversampled_labels))
oversampled_loader = DataLoader(oversampled_dataset, batch_size=32, shuffle=True)
model.train()
for inputs, labels in oversampled_loader:
    # 模型训练步骤
   	pass

# 或者使用SMOTE进行过采样
smote = SMOTE()
oversampled_data, oversampled_labels = smote.fit_resample(train_dataset.data, train_dataset.targets)

 

from sklearn import metrics
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
data = pd.read_csv(r'F:\机器学习\逻辑回归\creditcard.csv')
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
data['Amount'] = scaler.fit_transform(data['Amount'].values.reshape(-1,1))
data = data.drop(['Time'],axis=1)
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = \
    train_test_split(data.drop(['Class'],axis=1),data['Class'],test_size=0.25,random_state=1000)

from imblearn.over_sampling import SMOTE
oversampler = SMOTE(random_state=1000)
os_x_train,os_y_train = oversampler.fit_resample(x_test,y_test)

os_x_train_new,os_x_test_new,os_y_train_new,os_y_test_new = \
    train_test_split(os_x_train,os_y_train,test_size=0.25,random_state=1000)

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
scores = []
c_ranges = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
for c in c_ranges:
    lr = LogisticRegression(C=c,penalty='l2',solver='lbfgs',max_iter=1000)
    score = cross_val_score(lr,x_train,y_train,cv=10,scoring='recall')
    score_mean = sum(score)/len(score)
    scores.append(score_mean)
    print("C={},正确率:{}".format(c,score_mean))
best_C = c_ranges[scores.index(max(scores))]
print("最佳惩罚因子:",best_C)
lr = LogisticRegression(C=best_C,penalty='l2',solver='lbfgs',max_iter=1000)
lr.fit(os_x_train_new,os_y_train_new)
test_predicted = lr.predict(os_x_test_new)
print("测试集正确率:",lr.score(os_x_test_new,os_y_test_new))
print("测试集召回率:",metrics.recall_score(os_y_test_new,test_predicted))
from sklearn import metrics
YZ = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]
recalls = []
for i in YZ:
    y_pp = lr.predict_proba(x_test)
    y_pp = pd.DataFrame(y_pp)
    y_pp = y_pp.drop([0],axis=1)
    y_pp[y_pp[[1]] > i] = 1
    y_pp[y_pp[[1]] <= i] = 0
    recall = metrics.recall_score(y_test,y_pp[1])
    recalls.append(recall)
print(f"最优阈值为:{recall}")
from sklearn.metrics import classification_report
print("报告:",classification_report(y_test,lr.predict(x_test)))

过采样可能会导致过拟合问题

下采样

欠采样
欠采样(Undersampling)是指减少多数类样本的数量,以使其与少数类样本数量相当。这样可以减少多数类样本对模型的影响,提高对少数类的分类性能。欠采样的方法包括随机删除样本、聚类方法等。

随机删除样本:从多数类中随机选择一部分样本进行删除,以降低多数类的数量。这种方法简单快速,但可能会丢失一些重要信息。
聚类方法:使用聚类算法(如K-means)将多数类样本聚类为较小的子集,然后从每个聚类中选择一个样本作为代表性样本。这样可以减少多数类样本数量,同时保留一些代表性样本。
可以使用imbalanced-learn第三方库来实现欠采样,可以使用以下函数:

RandomUnderSampler:这个函数实现了随机欠采样方法,通过随机选择多数类样本来减少其数量,以平衡类别分布。
NearMiss:这个函数实现了基于距离的欠采样方法,根据少数类样本与多数类样本之间的距离进行样本选择。
使用imbalanced-learn库中的欠采样函数的示例:

 

from imblearn.under_sampling import RandomUnderSampler, NearMiss
import torch
from torch.utils.data import DataLoader

# 假设我们有一个不平衡的数据集
train_dataset = '数据集'
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)

# 创建 RandomUnderSampler 对象
under_sampler = RandomUnderSampler()

# 使用 RandomUnderSampler 进行欠采样
undersampled_data, undersampled_labels = under_sampler.fit_resample(train_dataset.data, train_dataset.targets)

# 使用欠采样后的数据训练模型
undersampled_dataset = torch.utils.data.TensorDataset(torch.tensor(undersampled_data), torch.tensor(undersampled_labels))
undersampled_loader = DataLoader(undersampled_dataset, batch_size=32, shuffle=True)
model.train()
for inputs, labels in undersampled_loader:
    # 模型训练步骤
    pass

# 或者使用NearMiss进行欠采样
nearmiss = NearMiss()
undersampled_data, undersampled_labels = nearmiss.fit_resample(train_dataset.data, train_dataset.targets)

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
data = pd.read_csv(r'F:\机器学习\逻辑回归\creditcard.csv')
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
data['Amount'] = scaler.fit_transform(data['Amount'].values.reshape(-1,1))
data = data.drop(['Time'],axis=1)
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = \
    train_test_split(data.drop(['Class'],axis=1),data['Class'],test_size=0.25,random_state=1000)

x_train['Class'] = y_train
train_data = x_train
positive_data = train_data[train_data['Class']==0]
negative_data = train_data[train_data['Class']==1]
data_concat = pd.concat([positive_data,negative_data])

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
scores = []
c_ranges = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
for c in c_ranges:
    lr = LogisticRegression(C=c,penalty='l2',solver='lbfgs',max_iter=1000)
    score = cross_val_score(lr,x_train,y_train,cv=10,scoring='recall')
    score_mean = sum(score)/len(score)
    scores.append(score_mean)
    print("C={},正确率:{}".format(c,score_mean))
best_C = c_ranges[scores.index(max(scores))]
print("最佳惩罚因子:",best_C)
x = data_concat.drop(['Class'],axis=1)
y = data_concat['Class']
lr = LogisticRegression(C=best_C,penalty='l2',solver='lbfgs',max_iter=1000)
lr.fit(x, y)
print("逻辑回归的准确率:",lr.score(x_test,y_test))
test_train_data = pd.concat([x_test,y_test],axis=1)
test_train_data['Predict'] = lr.predict(x_test)
print("逻辑回归的召回率:",sum(test_train_data[test_train_data['Class']==test_train_data['Predict']]['Class'])/len(test_train_data))
from sklearn.metrics import classification_report
print("报告:",classification_report(y_test,lr.predict(x_test)))

欠采样可能会导致丢失多数类样本的信息。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值