S'S ALGORITHM

机器学习算法之分类问题:对数变换,逻辑回归

时间宝贵,只总结关键。

一个充满分类的世界

从某种程度上来说,回归也是一种分类。这个世界在无限小的量子维度中,也是离散的,分类就是指,对离散值结果的预测。

分类问题有哪些类型

名字迷惑的逻辑回归

逻辑回归是用于分类算法的基本算法,Logit回归,最大熵分类或对数几率回归,线性分类器,它有很多名字,但是回归两个字让很多初学者迷惑。

Logit图像是那条有名的S曲线:Sigmoid function。

它主要用于解决二分类问题,通过估计一个样本属于某个类别的概率来进行分类。毕竟这是一个概率的世界。

对数几率变换

首先有一个几率 odds = p / 1 - p

对数几率是指事件发生的概率和事件不发生的概率之比的自然对数。也就是:Logit(p) = log(p / 1 - p)

p 是指事件发生的概率,对数几率可以取任意实数值范围,而不受原始概率值范围的限制。

为什么会有这个公式呢,它的实际意义其实在于将事件发生的概率转换为一个线性形式,从而可以更容易地进行建模和分析。

sigmoid

假设我们要求的实际值为z(W^T*X +b),这个z就是我们拟合了机器学习模型后得到的那个看不太懂的值,对数几率,此处就是z = Logits(p)

由于Logits(p) = log(p / 1 - p) = z,关注后半部分,推导出 p / 1 - p = e^z

p = 1 / (1 + e^(-z))

也就是sigmoid函数。

sigmoid函数的逆函数就是对数几率函数,这意味着,在逻辑回归中,我们通常使用对数几率函数来获得模型的原始输出,然后用sigmoid函数来将输入的线性组合映射到一个0到1之间的概率值。在sigmoid函数的图像中,x轴就是logits,y轴映射的0到1就是logits的逆运算,得到最终我们想要的概率,进而得到分类结果。

使用一对多策略和softmax策略,就可以用逻辑回归解决多分类问题,二元逻辑回归模型还可以扩展到多标签输出。

在scikit learn中使用如下简单代码就可以实现逻辑回归training:

from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(random_state=0).fit(X_train, y_train)

一些再次学习的笔记

巩固贝叶斯定理

贝叶斯定理是概率论中的一项基本定理,它描述了在已知先验信息的情况下,如何通过新的证据来更新我们对事件的信念。该定理以托马斯·贝叶斯(Thomas Bayes)的名字命名,他首次提出了一种通过观察到的数据来更新对事件概率的信念的方法。

贝叶斯定理的表达式如下:

P(A B) = P(B A) x P(A) / P(B)

其中:

贝叶斯定理的核心思想是通过先验概率和似然度来计算后验概率,从而更新我们对事件的信念。这种更新是基于观测到的新证据,而不是依赖于大量的先验信息。

贝叶斯定理在许多领域中都有广泛的应用,特别是在机器学习和统计推断中。例如,在贝叶斯统计中,我们可以使用贝叶斯定理来估计参数的后验分布,进而进行参数推断;在贝叶斯分类中,我们可以使用贝叶斯定理来计算给定类别下观测数据的后验概率,从而进行分类决策。

这个算法在NLP和垃圾邮件检测中非常有用。

再谈过拟合和欠拟合

偏差-方差(Bias-Variance)权衡是机器学习中一个重要的概念,用于描述模型的泛化误差(generalization error)与其复杂性之间的关系。

偏差-方差权衡可以用来解释模型的泛化能力。在实际应用中,我们希望模型具有足够的拟合能力(低偏差),同时又能对训练数据的变化具有一定的鲁棒性(低方差)。然而,通常情况下,降低偏差会增加方差,反之亦然,这就构成了偏差-方差权衡。

在实践中,通过调整模型的复杂度(例如调整模型的参数、增加或减少特征、使用正则化等方法),我们可以尝试找到一个合适的平衡点,以最小化模型的总体泛化误差。

关于不平衡分类问题

分类的世界不一定类别总是平衡,比如欺诈检测就是小众类别的检测。有一些应对方法值得了解:

增加小众数据的数量,比如复制,随机过采样,或者通过其他的算法进行合成。

SMOTE(Synthetic Minority Over-sampling Technique)算法是一种用于处理类别不平衡问题的过采样技术。它通过合成新的少数类样本来增加少数类样本的数量,从而平衡类别分布。

SMOTE 算法的基本思想是对于少数类样本中的每个样本,找到它的 k 个最近邻的样本(通常是欧氏距离最近的样本),然后从这些邻居样本中随机选择一些样本点,并沿着这些样本点之间的线段生成新的合成样本。

Scikit-learn社区中有整个开源的代码:https://github.com/scikit-learn-contrib/imbalanced-learn

它选择或者删除多数类别的数据,但是删除数据总是不太好的,所以随机删除就不是一个好的选择。

Tomek Links 欠采样是最广泛使用的欠采样技术之一。它涉及仔细删除多数类样本。训练数据集中最近邻但属于不同类的那些数据对,然后删除多数类的那个。 它通常倾向于删除错误分类的实例,沿着类边界的那些,属于大多数类的实例会删除。

我不知道怎么翻译直接用英文了,是一种处理类别不平衡问题的机器学习方法,它考虑了不同类别的分类错误所造成的不同代价。在现实世界的许多应用中,不同类别的分类错误可能会导致不同程度的后果,因此考虑这种代价可以更好地适应真实场景。

它的基本思想是通过修改模型的损失函数,引入类别权重或代价矩阵,以考虑不同类别的分类代价。

超参优化技巧

交叉验证,网格搜索,随机搜索都是用于超参优化的技巧。

网格搜索是指通过尝试网格中存在的值来查找最佳的超参数值。它尝试网格中指定的所有可能的值组合,并返回那些给我们带来良好结果的组合。

Scikit Learn 提供了GridSearchCV使用网格中指定的每个参数组合来实现交叉验证的类。对于给定值,GridSearchCV详尽考虑所有参数组合。它提供了超参数的最佳组合以及与这些超参数相对应的最佳分数。

相对的随机搜索在调整超参数时尝试随机值或不同超参数的随机组合。这个一听就是一个相对网格搜索比较节省资源的方法。

贝叶斯优化方法

贝叶斯优化是一种用于优化黑箱函数的方法,它通过建立一个代理模型来估计目标函数的表现,并根据代理模型的预测结果来选择下一个要探索的超参数值。贝叶斯优化的目标是在尽可能少的实验次数下找到最优的超参数组合。这也是我第一次听的方法。

基本步骤:

遗传算法

遗传算法的灵感来自于人类进化以及染色体如何代代相传。

它们基于进化论,即具有最佳生存能力和对环境适应能力的个体更有可能生存并将其能力传递给后代。

超参数被视为染色体。然后进行进一步的基本操作,如交叉和变异,以尝试不同的超参数值,并选择给我们最好结果的一个。

听起来很抽象但是我个人也很看好这些正在研究的领域。

其他的开源调参工具

Hyperopt 是一个开源 Python 模块,它提供了寻找最佳超参数的自动化方法。这个在Databrick的学习中也遇到过了,不日就要再次学习了。

Optuna 是另一个著名的超参数优化工具,它有助于高效地找到正确的超参数。这个我不是很了解。但是名字还挺好听的。

一些代码

支持向量机的sklearn实现:

from sklearn.svm import SVC
clf = SVC(C = 1, kernel = "linear").fit(X_train, y_train)

决策树算法:

from sklearn import tree
clf = tree.DecisionTreeClassifier().fit(X_train, y_train)

贝叶斯算法:

from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
clf = gnb.fit(X_train, y_train)

k邻近算法:

算法参数可以使用如下选择:

from sklearn.neighbors import KNeighborsClassifier
neigh = KNeighborsClassifier(n_neighbors=3,algorithm="kd_tree")
clf = neigh.fit(X_train, y_train)

XgBoost算法参数:

from xgboost import XGBClassifier
clf = XGBClassifier(random_state=1,learning_rate=0.01, n_jobs=-1).fit(X_train, y_train)
print("The accuracy on test set is {0:.2f}".format(clf.score(X_test, y_test)))

Light GBM算法参数:

import lightgbm as lgb
d_train = lgb.Dataset(X_train, label=y_train)
params = {}
params['learning_rate'] = 0.02
params['objective'] = 'multiclass'
params['num_class'] = '3'
params['metric'] = 'multiclass'
params['max_depth'] = 10
clf = lgb.train(params, d_train, 100)  # 100是迭代次数
y_pred = clf.predict(X_test)

import numpy as np
from sklearn.metrics import accuracy_score
y_pred = [np.argmax(line) for line in y_pred]
print("The accuracy on test set is {0:.2f}".format(accuracy_score(y_test, y_pred)))

CatBoost参数:

from catboost import CatBoostClassifier
clf = CatBoostClassifier(
    iterations=2,
    max_depth=2,
    learning_rate=1,
    verbose=True
)
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
y_pred = y_pred.flatten().tolist()
print("The accuracy on test set is {0:.2f}".format(clf.score(X_test, y_test)))

和回归问题一样,分类也有虚拟估计器,作为一个基线标准。

# Fitting the BaseLine DummyEstimator
from sklearn.dummy import DummyClassifier
clf = DummyClassifier(strategy='most_frequent', random_state=0)
clf.fit(X_train, y_train)