0%

交叉验证(Cross-Validation)是一种评估机器学习模型性能的方法,通过将数据集分成训练集和验证集来进行多次训练和测试。它有助于更准确地估计模型在未见过数据上的性能,并避免对单一验证集的过度拟合。

常见的交叉验证方法包括K折交叉验证和留一交叉验证。其中,K折交叉验证将数据集分成K个子集,依次将每个子集作为验证集,其余作为训练集,重复K次;留一交叉验证是K折交叉验证的特例,其中K等于数据集大小。交叉验证的结果通常是各次验证的平均性能。

以下是一个使用Python实现K折交叉验证的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score
from sklearn.svm import SVC

# 加载鸢尾花数据集
iris = load_iris()
X, y = iris.data, iris.target

# 创建SVM分类器
svm_model = SVC(kernel='linear', C=1.0, random_state=42)

# 进行5折交叉验证
scores = cross_val_score(svm_model, X, y, cv=5) # cv参数指定折数

print("Cross-Validation Scores:", scores)
print("Mean Score:", scores.mean())

在上述代码中,我们使用load_iris函数加载鸢尾花数据集,并创建了一个线性SVM分类器。然后,使用cross_val_score函数进行5折交叉验证,得到每次验证的性能分数。最后,我们打印出各次分数和平均分数。

通过交叉验证,可以更准确地估计模型的性能,并对模型进行调参。

交叉熵(Cross-Entropy)是一种常用的损失函数,用于衡量实际概率分布与预测概率分布之间的差异。在分类问题中,交叉熵可以用于衡量模型预测结果与真实标签之间的距离,从而进行模型的优化。

以下是一个使用Python实现交叉熵计算的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np

def cross_entropy(y_true, y_pred):
epsilon = 1e-15 # 用于避免取对数时出现无限大
y_pred = np.clip(y_pred, epsilon, 1 - epsilon) # 对预测结果进行裁剪
ce = -np.sum(y_true * np.log(y_pred))
return ce

# 真实标签和预测概率分布
y_true = np.array([0, 1, 0, 0])
y_pred = np.array([0.1, 0.6, 0.2, 0.1])

# 计算交叉熵
ce = cross_entropy(y_true, y_pred)
print("Cross-Entropy:", ce)

在上述代码中,我们定义了一个cross_entropy函数,接受真实标签和预测概率分布作为输入,并计算交叉熵。为了避免取对数时出现无限大,我们使用了一个小的epsilon值进行裁剪。然后,通过对真实标签与对数化后的预测概率分布进行元素级相乘,最后对结果取负数并求和,得到交叉熵值。

请注意,上述代码示例只是一个简单的交叉熵计算实现。在实际应用中,您可能需要处理更多情况,例如多类别问题、批量计算等。在深度学习中,交叉熵常作为损失函数,在优化过程中用于衡量预测结果与真实标签之间的误差。

分类

  • 基于字典、词库匹配
  • 基于词频度统计
  • 基于知识理解

字典、词库匹配

中文复杂、丰富,词典、词库匹配无法有效处理大规模文本分词处理

  1. 逐词遍历法:全字典遍历一遍,效率低,不适用于大系统
  2. 机械分词法

最大正向匹配法

假定词典词最大长度为i,被处理文档的当前字串中的前i个字作为匹配字段,匹配成功,则切分出来,匹配失败,则i-1,继续匹配,若成功则i+1,知道文档处理完成

逆向最大匹配法

文档逆序处理,通过逆序词典匹配,汉语中偏正结构较多,若从后向前匹配,可以适当提高精确度,逆向最大匹配法比正向最大匹配法的误差要小

最少切分法

使每一句中切出的词数最小

双向匹配法

将正向最大匹配法与逆向最大匹配法组合,如果两种分词方法得到的匹配结果相同,则认为分词正确,否则,按最小集处理

词频统计

考虑到相邻字词关系、词频、共现信息,有较好的实用性

全切分和基于词的频度统计的分词方法

基于词的频度统计的分词方法是一种全切分方法

全切分要求获得输入序列的所有可接受的切分形式,而部分切分只取得一种或几种可接受的切分形式,由于部分切分忽略了可能的其他切分形式,所以建立在部分切分基础上的分词方法不管采取何种歧义纠正策略,都可能会遗漏正确的切分,造成分词错误或失败。而建立在全切分基础上的分词方法,由于全切分取得了所有可能的切分形式,因而从根本上避免了可能切分形式的遗漏,克服了部分切分方法的缺陷。

问题:

  1. 全切分算法只是能获得正确分词的前提,因为全切分不具有歧义检测功能,最终分词结果的正确性和完全性依赖于独立的歧义处理方法,如果评测有误,也会造成错误的结果。

  2. 全切分的切分结果个数随句子长度的增长呈指数增长,一方面将导致庞大的无用数据充斥于存储数据库;另一方面当句长达到一定长度后,由于切分形式过多,造成分词效率严重下降。

解决方案:

基于词的频度统计的分词方法:

这是一种全切分方法。它不依靠词典,而是将文章中任意两个字同时出现的频率进行统计,次数越高的就可能是一个词。它首先切分出与词表匹配的所有可能的词,运用统计语言模型和决策算法决定最优的切分结果。它的优点在于可以发现所有的切分歧义并且容易将新词提取出来。

基于知识理解

基于句法、语法分析,并结合语义分析,通过对上下文内容所提供信息的分析对词进行定界,它通常包括三个部分:分词子系统、句法语义子系统、总控部分。在总控部分的协调下,分词子系统可以获得有关词、句子等的句法和语义信息来对分词歧义进行判断。这类方法试图让机器具有人类的理解能力,需要使用大量的语言知识和信息。由于汉语语言知识的笼统、复杂性,难以将各种语言信息组织成机器可直接读取的形式。因此目前基于知识的分词系统还处在试验阶段。

并行分词方法

并行分词方法:这种分词方法借助于一个含有分词词库的管道进行 ,比较匹配过程是分步进行的 ,每一步可以对进入管道中的词同时与词库中相应的词进行比较 ,由于同时有多个词进行比较匹配 ,因而分词速度可以大幅度提高。这种方法涉及到多级内码理论和管道的词典数据结构。(详细算法可以参考吴胜远的《并行分词方法的研究》。)

在Python中,有许多库可以用于中文分词,其中最常用的是jieba库。以下是一个使用jieba库进行中文分词的示例代码:

首先,确保已安装 jieba 库,可以通过以下命令安装:

1
pip install jieba

接下来,使用下面的代码示例:

1
2
3
4
5
6
7
8
9
10
import jieba

# 输入文本
text = "今天天气不错,适合出去玩。"

# 使用jieba分词
seg_list = jieba.cut(text, cut_all=False) # cut_all=False表示精确模式分词
seg_result = " ".join(seg_list)

print("分词结果:", seg_result)

在上述代码中,我们使用了jieba.cut函数对输入文本进行中文分词。设置cut_all=False表示使用精确模式进行分词。然后,将分词结果用空格连接起来并打印出来。

jieba库支持更多的分词模式、自定义词典、停用词过滤等功能,以及支持并行分词,适用于不同的应用场景。在实际应用中,您可以根据需求对分词结果进行后续处理,比如进行文本分析、情感分析等。

XGBoost(eXtreme Gradient Boosting)是一种梯度提升树算法,被广泛应用于机器学习竞赛和实际项目中。它在GBDT的基础上进行了改进,引入了正则化项、并行处理、稀疏数据优化等特性,以提高模型的性能和泛化能力。

以下是一个使用Python的xgboost库实现XGBoost的示例代码:

首先,确保已安装 xgboost 库,可以通过以下命令安装:

1
pip install xgboost

接下来,使用下面的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import xgboost as xgb
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 加载鸢尾花数据集
iris = load_iris()
X, y = iris.data, iris.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 构建XGBoost分类器
xgb_model = xgb.XGBClassifier(objective='multi:softmax', num_class=3, random_state=42)

# 训练模型
xgb_model.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = xgb_model.predict(X_test)

# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print("XGBoost Accuracy:", accuracy)

在上述代码中,我们使用了load_iris函数加载鸢尾花数据集,并将数据集划分为训练集和测试集。然后,我们创建了一个XGBoost分类器(XGBClassifier),并使用fit方法训练模型。接着,我们在测试集上进行预测并计算准确率。

XGBoost有很多参数可以调整,包括树的深度、学习率、子样本比例、正则化项等。您可以根据实际问题进行调参,以达到更好的性能。请注意,XGBoost还支持回归问题,以及对稀疏数据和缺失值的处理。

参考: [博客 xgboost原理]( https://www.cnblogs.com/zhouxiaohui888/p/6008368.html “xgboost”)

参考: github xgboost学习

在特征空间上找到最佳的分离超平面使得训练集上正负样本间隔最大

用来解决二分类问题的有监督学习算法

引入了核方法之后SVM也可以用来解决非线性问题

类型

  • 硬间隔支持向量机(线性可分支持向量机):当训练数据线性可分时,可通过硬间隔最大化学得一个线性可分支持向量机。
  • 软间隔支持向量机:当训练数据近似线性可分时,可通过软间隔最大化学得一个线性支持向量机。
  • 非线性支持向量机:当训练数据线性不可分时,可通过核方法以及软间隔最大化学得一个非线性支持向量机。

支持向量机(Support Vector Machine,SVM)是一种常用的机器学习算法,主要用于分类和回归问题。SVM的核心思想是通过找到一个超平面(或者更一般地说,一个线性决策边界),将不同类别的数据点尽可能分开,同时最大化支持向量与超平面的距离(即间隔)。在分类问题中,SVM的目标是找到一个最优的超平面,使得不同类别的数据点都尽可能远离这个超平面。

以下是一个使用Python和scikit-learn库实现SVM的示例代码:

首先,确保已安装 scikit-learn 库,可以通过以下命令安装:

1
pip install scikit-learn

接下来,使用下面的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score

# 加载鸢尾花数据集
iris = datasets.load_iris()
X, y = iris.data, iris.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建SVM分类器
svm_model = SVC(kernel='linear', C=1.0, random_state=42)

# 训练模型
svm_model.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = svm_model.predict(X_test)

# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print("SVM Accuracy:", accuracy)

在上述代码中,我们使用了datasets.load_iris函数加载鸢尾花数据集,并将数据集划分为训练集和测试集。然后,我们创建了一个线性SVM分类器(SVC),使用fit方法训练模型。接着,我们在测试集上进行预测并计算准确率。

您可以通过调整参数(例如核函数、惩罚参数C等)来进一步优化SVM模型的性能。注意,SVM算法不仅适用于二元分类问题,还可以扩展到多类别分类和回归问题。

参考: svm

Softmax回归是一种用于多类别分类的线性模型。它在输出层使用了Softmax函数,将线性组合的原始分数转化为类别的概率分布,从而进行多类别分类。Softmax回归常用于机器学习中的分类任务,尤其是在神经网络中作为输出层来处理多类别分类问题。

以下是一个使用Python和TensorFlow库实现Softmax回归的简单示例代码:

首先,确保已安装 tensorflow 库,可以通过以下命令安装:

1
pip install tensorflow

接下来,使用下面的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import tensorflow as tf
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score

# 加载鸢尾花数据集
iris = load_iris()
X, y = iris.data, iris.target

# 数据预处理
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 构建Softmax回归模型
model = tf.keras.models.Sequential([
tf.keras.layers.Input(shape=(4,)), # 输入层
tf.keras.layers.Dense(3, activation='softmax') # 输出层,3个类别
])

# 编译模型
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

# 训练模型
model.fit(X_train, y_train, epochs=100, batch_size=16, verbose=1)

# 在测试集上进行预测
y_pred = model.predict(X_test)
y_pred_classes = y_pred.argmax(axis=1)

# 计算准确率
accuracy = accuracy_score(y_test, y_pred_classes)
print("Accuracy:", accuracy)

在上述代码中,我们使用load_iris函数加载鸢尾花数据集,并进行数据预处理。然后,我们构建了一个包含输入层和输出层的Softmax回归模型。通过compile方法来指定优化器和损失函数。接着,使用fit方法来训练模型。最后,我们在测试集上进行预测并计算准确率。

请注意,这只是一个简单的Softmax回归实现示例。在实际应用中,您可能需要考虑更复杂的模型结构、超参数调整以及更多的优化。

ResNet(Residual Network)是一个深度卷积神经网络架构,由Kaiming He等人在2015年提出。它通过使用残差块(Residual Block)来解决深层网络的退化问题,允许在训练更深的神经网络时仍然能够获得更好的性能。

ResNet引入了”跳跃连接”或”残差连接”的概念,即通过将前一层的输出直接添加到后续层的输出中,以便网络能够学习残差(剩余部分)。这种结构有助于避免梯度消失和梯度爆炸问题,使网络更容易优化。ResNet的核心思想是:通过层与层之间的直接捷径,网络可以学习原始输入和残差之间的映射,从而更轻松地适应更深的层次。

深度网络的退化问题至少说明深度网络不容易训练。但是我们考虑这样一个事实:现在你有一个浅层网络,你想通过向上堆积新层来建立深层网络,一个极端情况是这些增加的层什么也不学习,仅仅复制浅层网络的特征,即这样新层是恒等映射(Identity mapping)。在这种情况下,深层网络应该至少和浅层网络性能一样,也不应该出现退化现象。好吧,你不得不承认肯定是目前的训练方法有问题,才使得深层网络很难去找到一个好的参数。

这个有趣的假设让何博士灵感爆发,他提出了残差学习来解决退化问题。对于一个堆积层结构(几层堆积而成)当输入为时其学习到的特征记为,现在我们希望其可以学习到残差,这样其实原始的学习特征是。之所以这样是因为残差学习相比原始特征直接学习更容易。当残差为0时,此时堆积层仅仅做了恒等映射,至少网络性能不会下降,实际上残差不会为0,这也会使得堆积层在输入特征基础上学习到新的特征,从而拥有更好的性能。残差学习的结构如图4所示。这有点类似与电路中的“短路”,所以是一种短路连接(shortcutconnection)。

以下是一个使用Python和TensorFlow库实现ResNet的简化版本的示例代码:

首先,确保已安装 tensorflow 库,可以通过以下命令安装:

1
pip install tensorflow

接下来,使用下面的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import tensorflow as tf
from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, ReLU, Add, GlobalAveragePooling2D, Dense
from tensorflow.keras.models import Model

def residual_block(x, filters, kernel_size=3, stride=1):
shortcut = x
# 第一个卷积层
x = Conv2D(filters, kernel_size, strides=stride, padding='same')(x)
x = BatchNormalization()(x)
x = ReLU()(x)
# 第二个卷积层
x = Conv2D(filters, kernel_size, strides=1, padding='same')(x)
x = BatchNormalization()(x)
# 添加残差连接
if stride != 1 or shortcut.shape[-1] != filters:
shortcut = Conv2D(filters, kernel_size=1, strides=stride, padding='valid')(shortcut)
shortcut = BatchNormalization()(shortcut)
x = Add()([x, shortcut])
x = ReLU()(x)
return x

def build_resnet(input_shape, num_classes, num_blocks_list):
input_layer = Input(shape=input_shape)
x = Conv2D(64, 7, strides=2, padding='same')(input_layer)
x = BatchNormalization()(x)
x = ReLU()(x)
x = tf.keras.layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(x)

for num_blocks in num_blocks_list:
for _ in range(num_blocks):
x = residual_block(x, 64)

x = GlobalAveragePooling2D()(x)
x = Dense(num_classes, activation='softmax')(x)

model = Model(inputs=input_layer, outputs=x)
return model

# 构建ResNet模型
input_shape = (224, 224, 3) # 输入图像大小
num_classes = 1000 # 分类类别数
num_blocks_list = [2, 2, 2, 2] # 每个stage的残差块数量
resnet_model = build_resnet(input_shape, num_classes, num_blocks_list)

# 打印模型结构
resnet_model.summary()

在上述代码中,我们定义了一个residual_block函数来实现残差块的构建,然后使用build_resnet函数来构建简化版本的ResNet模型。通过设置不同的num_blocks_list,可以调整每个stage中的残差块数量。最终,我们打印出ResNet模型的结构概要。

高斯混合模型是一种混合模型,混合的基本分布是高斯分布

参考《统计学习方法》

高斯混合模型(Gaussian Mixture Model,GMM)是一种用于对数据进行建模的概率模型。它假设数据是由多个高斯分布(正态分布)组成的混合体,每个分布代表一个潜在的子群。GMM 在聚类、密度估计和异常检测等领域具有广泛的应用。

以下是一个使用 Python 的 scikit-learn 库实现 GMM 的示例代码:

首先,确保已安装 scikit-learn 库,可以通过以下命令安装:

1
pip install scikit-learn

接下来,使用下面的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
import matplotlib.pyplot as plt
from sklearn.mixture import GaussianMixture
from sklearn.datasets import make_blobs

# 生成示例数据
n_samples = 300
X, y_true = make_blobs(n_samples=n_samples, centers=3, cluster_std=1.0, random_state=42)

# 创建 GMM 模型
gmm = GaussianMixture(n_components=3, random_state=42)

# 训练模型
gmm.fit(X)

# 预测每个样本所属的分布
y_pred = gmm.predict(X)

# 绘制原始数据和 GMM 聚类结果
plt.scatter(X[:, 0], X[:, 1], c=y_pred, s=40, cmap='viridis')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.title('GMM Clustering')
plt.show()

在上述代码中,我们首先使用 make_blobs 函数生成一个示例数据集,用于演示 GMM 的聚类效果。然后,我们创建了一个 GMM 模型,指定了期望的分布数量(n_components),并使用 fit 方法训练模型。最后,我们根据 GMM 模型的预测结果绘制了原始数据和聚类效果。

链接:浅显易懂的GMM模型及其训练过程

Gradient Boosting Decision Tree 迭代决策树,泛化能力较强

梯度提升决策树(Gradient Boosting Decision Tree,GBDT)是一种集成学习算法,通过迭代地训练弱学习器(通常是决策树),每一轮都试图修正前一轮的错误。这些弱学习器的预测结果被组合起来,形成最终的集成模型。 几乎可用于所有回归问题(线性/非线性),相对logistic regression仅能用于线性回归,GBDT的适用面非常广。亦可用于二分类问题(设定阈值,大于阈值为正例,反之为负例)。
可用于搜索排序

Regression Decistion Tree

分类树

C4.5分类树:在每次分枝时,是穷举每一个feature的每一个阈值,找到使得按照feature<=阈值,和feature>阈值分成的两个分枝的熵最大的feature和阈值(熵最大的概念可理解成尽可能每个分枝的男女比例都远离1:1),按照该标准分枝得到两个新节点,用同样方法继续分枝直到所有人都被分入性别唯一的叶子节点,或达到预设的终止条件,若最终叶子节点中的性别不唯一,则以多数人的性别作为该叶子节点的性别。

回归树

在每个节点(不一定是叶子节点)都会得一个预测值,以年龄为例,该预测值等于属于这个节点的所有人年龄的平均值。分枝时穷举每一个feature的每个阈值找最好的分割点,但衡量最好的标准不再是最大熵,而是最小化均方差–即(每个人的年龄-预测年龄)^2 的总和 / N,或者说是每个人的预测误差平方和 除以 N。这很好理解,被预测出错的人数越多,错的越离谱,均方差就越大,通过最小化均方差能够找到最靠谱的分枝依据。分枝直到每个叶子节点上人的年龄都唯一(这太难了)或者达到预设的终止条件(如叶子个数上限),若最终叶子节点上人的年龄不唯一,则以该节点上所有人的平均年龄做为该叶子节点的预测年龄。

参考:http://www.schonlau.net/publication/05stata_boosting.pdf

Gradient Boosting

多棵树来共同决策。GBDT的核心就在于,每一棵树学的是之前所有树结论和的残差,这个残差就是一个加预测值后能得真实值的累加量。比如A的真实年龄是18岁,但第一棵树的预测年龄是12岁,差了6岁,即残差为6岁。那么在第二棵树里我们把A的年龄设为6岁去学习,如果第二棵树真的能把A分到6岁的叶子节点,那累加两棵树的结论就是A的真实年龄;如果第二棵树的结论是5岁,则A仍然存在1岁的残差,第三棵树里A的年龄就变成1岁,继续学。这就是Gradient Boosting在GBDT中的意义,简单吧。

残差: A的预测值 + A的残差 = A的实际值 ,残差为0,极为真实值,(过拟合问题)

优点:Boosting的最大好处在于,每一步的残差计算其实变相地增大了分错instance的权重,而已经分对的instance则都趋向于0。这样后面的树就能越来越专注那些前面被分错的instance。

Adaboost是另一种boost方法,它按分类对错,分配不同的weight,计算cost function时使用这些weight,从而让“错分的样本权重越来越大,使它们更被重视”。Bootstrap也有类似思想,它在每一步迭代时不改变模型本身,也不计算残差,而是从N个instance训练集中按一定概率重新抽取N个instance出来(单个instance可以被重复sample),对着这N个新的instance再训练一轮。由于数据集变了迭代模型训练结果也不一样,而一个instance被前面分错的越厉害,它的概率就被设的越高,这样就能同样达到逐步关注被分错的instance,逐步完善的效果。Adaboost的方法被实践证明是一种很好的防止过拟合的方法,但至于为什么则至今没从理论上被证明。GBDT也可以在使用残差的同时引入Bootstrap re-sampling,GBDT多数实现版本中也增加的这个选项,但是否一定使用则有不同看法。re-sampling一个缺点是它的随机性,即同样的数据集合训练两遍结果是不一样的,也就是模型不可稳定复现,这对评估是很大挑战,比如很难说一个模型变好是因为你选用了更好的feature,还是由于这次sample的随机因素。

参考:http://en.wikipedia.org/wiki/Gradient_boosted_trees#Gradient_tree_boosting

Shrinkage

每次走一小步逐渐逼近结果的效果,要比每次迈一大步很快逼近结果的方式更容易避免过拟合。即它不完全信任每一个棵残差树,它认为每棵树只学到了真理的一小部分,累加的时候只累加一小部分,通过多学几棵树弥补不足。

没用Shrinkage时:(yi表示第i棵树上y的预测值, y(1~i)表示前i棵树y的综合预测值)

y(i+1) = 残差(y1yi), 其中: 残差(y1yi) = y真实值 - y(1 ~ i)

y(1 ~ i) = SUM(y1, …, yi)

Shrinkage不改变第一个方程,只把第二个方程改为:

y(1 ~ i) = y(1 ~ i-1) + step * yi

即Shrinkage仍然以残差作为学习目标,但对于残差学习出来的结果,只累加一小部分(step*残差)逐步逼近目标,step一般都比较小,如0.01~0.001(注意该step非gradient的step),导致各个树的残差是渐变的而不是陡变的。直觉上这也很好理解,不像直接用残差一步修复误差,而是只修复一点点,其实就是把大步切成了很多小步。本质上,Shrinkage为每棵树设置了一个weight,累加时要乘以这个weight,但和Gradient并没有关系。这个weight就是step。就像Adaboost一样,Shrinkage能减少过拟合发生也是经验证明的,目前还没有看到从理论的证明。

RankNet

实际的搜索排序使用的是LambdaMART算法,必须指出的是由于这里要使用排序需要的cost function,LambdaMART迭代用的并不是残差。Lambda在这里充当替代残差的计算方法,它使用了一种类似Gradient*步长模拟残差的方法。这里的MART在求解方法上和之前说的残差略有不同,其区别描述见这里。

就像所有的机器学习一样,搜索排序的学习也需要训练集,这里一般是用人工标注实现,即对每一个(query,doc) pair给定一个分值(如1,2,3,4),分值越高表示越相关,越应该排到前面。然而这些绝对的分值本身意义不大,例如你很难说1分和2分文档的相关程度差异是1分和3分文档差距的一半。相关度本身就是一个很主观的评判,标注人员无法做到这种定量标注,这种标准也无法制定。但标注人员很容易做到的是”AB都不错,但文档A比文档B更相关,所以A是4分,B是3分“。RankNet就是基于此制定了一个学习误差衡量方法,即cost function。具体而言,RankNet对任意两个文档A,B,通过它们的人工标注分差,用sigmoid函数估计两者顺序和逆序的概率P1。然后同理用机器学习到的分差计算概率P2(sigmoid的好处在于它允许机器学习得到的分值是任意实数值,只要它们的分差和标准分的分差一致,P2就趋近于P1)。这时利用P1和P2求的两者的交叉熵,该交叉熵就是cost function。它越低说明机器学得的当前排序越趋近于标注排序。为了体现NDCG的作用(NDCG是搜索排序业界最常用的评判标准),RankNet还在cost function中乘以了NDCG。

好,现在我们有了cost function,而且它是和各个文档的当前分值yi相关的,那么虽然我们不知道它的全局最优方向,但可以求导求Gradient,Gradient即每个文档得分的一个下降方向组成的N维向量,N为文档个数(应该说是query-doc pair个数)。这里仅仅是把”求残差“的逻辑替换为”求梯度“,可以这样想:梯度方向为每一步最优方向,累加的步数多了,总能走到局部最优点,若该点恰好为全局最优点,那和用残差的效果是一样的。这时套到之前讲的逻辑,GDBT就已经可以上了。那么最终排序怎么产生呢?很简单,每个样本通过Shrinkage累加都会得到一个最终得分,直接按分数从大到小排序就可以了(因为机器学习产生的是实数域的预测分,极少会出现在人工标注中常见的两文档分数相等的情况,几乎不同考虑同分文档的排序方式)

另外,如果feature个数太多,每一棵回归树都要耗费大量时间,这时每个分支时可以随机抽一部分feature来遍历求最优(ELF源码实现方式)。

在 Python 中,可以使用 scikit-learn 库来实现 GBDT。

首先,确保已安装 scikit-learn 库,可以通过以下命令安装:

1
pip install scikit-learn

接下来,使用下面的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score

# 加载鸢尾花数据集
iris = load_iris()
X, y = iris.data, iris.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建 GBDT 分类器
gbdt_model = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, random_state=42)

# 训练模型
gbdt_model.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = gbdt_model.predict(X_test)

# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print("GBDT Accuracy:", accuracy)

在上述代码中,我们使用了 load_iris 函数加载鸢尾花数据集,并将数据集划分为训练集和测试集。然后,我们创建了一个 GBDT 分类器,并使用 fit 方法训练模型。最后,我们在测试集上进行预测,并计算了预测的准确率。

参考:https://www.cnblogs.com/pinard/p/6140514.html