机器学习模型要求所有输入和输出变量都是数字。
这意味着如果您的数据包含分类数据,您必须在拟合和评估模型之前将其编码为数字。
两种最流行的技术是序数编码和独热编码。
在本教程中,您将学习如何对分类机器学习数据使用编码方案。
完成本教程后,您将了解:
- 对于机器学习算法的分类数据,编码是必需的预处理步骤。
- 如何对具有自然排序的分类变量使用序数编码。
- 如何对没有自然排序的分类变量使用独热编码。
通过我的新书《机器学习数据准备》启动您的项目,其中包括分步教程和所有示例的Python源代码文件。
让我们开始吧。

机器学习的序数和独热编码转换
照片由Felipe Valduga拍摄,保留部分权利。
教程概述
本教程分为六个部分;它们是:
- 名义变量和序数变量
- 分类数据编码
- 序数编码
- 独热编码
- 虚拟变量编码
- 乳腺癌数据集
- OrdinalEncoder 转换
- OneHotEncoder 转换
- 常见问题
名义变量和序数变量
顾名思义,数值数据涉及仅由数字(例如整数或浮点值)组成的特征。
分类数据是包含标签值而不是数值的变量。
可能值的数量通常限于固定集。
分类变量通常被称为名义变量。
一些例子包括:
- 一个“宠物”变量,值为:“狗”和“猫”。
- 一个“颜色”变量,值为:“红色”、“绿色”和“蓝色”。
- 一个“位置”变量,值为:“第一”、“第二”和“第三”。
每个值代表一个不同的类别。
某些类别之间可能存在自然关系,例如自然排序。
上面的“位置”变量确实具有值的自然排序。这种类型的分类变量称为序数变量,因为这些值可以排序或分级。
通过将数值变量的范围划分为区间并为每个区间分配值,可以将数值变量转换为序数变量。例如,一个介于1和10之间的数值变量可以分为具有5个标签的序数变量,这些标签具有序数关系:1-2、3-4、5-6、7-8、9-10。这称为离散化。
- 名义变量(分类)。变量包含一组有限的离散值,值之间没有关系。
- 序数变量。变量包含一组有限的离散值,值之间存在排序关系。
有些算法可以直接处理分类数据。
例如,决策树可以直接从分类数据中学习,无需数据转换(这取决于具体的实现)。
许多机器学习算法无法直接对标签数据进行操作。它们要求所有输入变量和输出变量都是数字。
通常,这主要是机器学习算法高效实现的限制,而不是算法本身的硬性限制。
机器学习算法的一些实现要求所有数据都是数值型的。例如,scikit-learn就有这个要求。
这意味着分类数据必须转换为数值形式。如果分类变量是输出变量,您可能还需要将模型预测转换回分类形式,以便呈现或在某些应用程序中使用它们。
想开始学习数据准备吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
分类数据编码
有三种常见的方法将序数变量和分类变量转换为数值。它们是
- 序数编码
- 独热编码
- 虚拟变量编码
让我们依次仔细看看每一个。
序数编码
在序数编码中,每个唯一的类别值都被赋予一个整数值。
例如,“红色”是1,“绿色”是2,“蓝色”是3。
这称为序数编码或整数编码,并且易于反转。通常使用从零开始的整数值。
对于某些变量,序数编码可能就足够了。整数值之间具有自然的有序关系,机器学习算法可能能够理解并利用这种关系。
这是一种自然的序数变量编码。对于分类变量,它在可能不存在这种关系的情况下强加了一种序数关系。这可能会导致问题,因此可以使用独热编码代替。
这种序数编码转换在scikit-learn Python机器学习库中通过OrdinalEncoder类提供。
默认情况下,它将按照数据中观察到的顺序将整数分配给标签。如果需要特定顺序,可以通过“categories”参数指定为一个包含所有预期标签排序的列表。
我们可以通过将“红”、“绿”、“蓝”颜色类别转换为整数来演示此类的用法。首先对类别进行排序,然后应用数字。对于字符串,这意味着标签按字母顺序排序,蓝色=0,绿色=1,红色=2。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 |
# 序数编码示例 from numpy import asarray from sklearn.preprocessing import OrdinalEncoder # 定义数据 data = asarray([['red'], ['green'], ['blue']]) print(data) # 定义序数编码 encoder = OrdinalEncoder() # 转换数据 result = encoder.fit_transform(data) print(result) |
运行该示例首先报告3行标签数据,然后是序数编码。
我们可以看到数字按照我们预期的方式分配给了标签。
1 2 3 4 5 6 |
[['red'] ['green'] ['blue']] [[2.] [1.] [0.]] |
这个OrdinalEncoder类用于组织成行和列的输入变量,例如矩阵。
如果需要对分类预测建模问题中的分类目标变量进行编码,可以使用LabelEncoder类。它与OrdinalEncoder的功能相同,尽管它期望单个目标变量的一维输入。
独热编码
对于不存在序数关系的分类变量,整数编码充其量可能不足,最坏的情况可能对模型产生误导。
通过序数编码强制建立序数关系,并允许模型假设类别之间存在自然排序,可能会导致性能不佳或意外结果(例如,预测值介于类别之间)。
在这种情况下,可以将独热编码应用于序数表示。这意味着整数编码变量被移除,并为变量中每个唯一的整数值添加一个新的二进制变量。
每个位代表一个可能的类别。如果变量不能同时属于多个类别,那么组中只有一个位可以是“开启”的。这被称为独热编码……
— 第78页,《机器学习的特征工程》,2018年。
在“颜色”变量示例中,有三个类别,因此需要三个二进制变量。在颜色对应的二进制变量中放置“1”值,其他颜色则为“0”值。
此独热编码转换可通过scikit-learn Python机器学习库中的OneHotEncoder类获得。
我们可以演示 OneHotEncoder 在颜色类别上的用法。首先对类别进行排序,在本例中按字母顺序排序,因为它们是字符串,然后依次为每个类别创建二进制变量。这意味着蓝色将表示为 [1, 0, 0],其中第一个二进制变量为“1”,然后是绿色,最后是红色。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 |
# 独热编码示例 from numpy import asarray from sklearn.preprocessing import OneHotEncoder # 定义数据 data = asarray([['red'], ['green'], ['blue']]) print(data) # 定义独热编码 encoder = OneHotEncoder(sparse=False) # 转换数据 onehot = encoder.fit_transform(data) print(onehot) |
运行该示例首先列出三行标签数据,然后是独热编码,与我们预期的“蓝色”、“绿色”和“红色”顺序的3个二进制变量相符。
1 2 3 4 5 6 |
[['red'] ['green'] ['blue']] [[0. 0. 1.] [0. 1. 0.] [1. 0. 0.]] |
如果您知道数据中所有预期标签,可以通过“categories”参数以列表形式指定它们。
编码器在训练数据集上进行拟合,如果未指定标签列表,训练数据集可能包含每个分类变量所有预期标签的至少一个示例。如果新数据包含训练数据集中未见的类别,可以将“handle_unknown”参数设置为“ignore”以避免引发错误,这将导致每个标签的值为零。
虚拟变量编码
独热编码为每个类别创建一个二进制变量。
问题在于这种表示包含冗余。例如,如果我们知道 [1, 0, 0] 代表“蓝色”,[0, 1, 0] 代表“绿色”,我们就不需要另一个二进制变量来代表“红色”,相反,我们可以只使用“蓝色”和“绿色”的 0 值,例如 [0, 0]。
这被称为虚拟变量编码,并且总是用C-1个二进制变量表示C个类别。
当预测变量有C个可能值,并且只使用C-1个虚拟变量时,可以计算矩阵逆,并且对比方法被称为全秩参数化。
— 第95页,《特征工程与选择》,2019年。
除了冗余度稍低之外,某些模型还需要虚拟变量表示。
例如,对于线性回归模型(以及其他带有偏置项的回归模型),独热编码会导致输入数据矩阵变为奇异,这意味着它无法求逆,并且无法使用线性代数计算线性回归系数。对于这些类型的模型,必须使用虚拟变量编码。
如果模型包含截距并包含虚拟变量 [...],那么 [...] 列将(按行)加起来等于截距,这种线性组合将阻止计算矩阵逆(因为它奇异)。
— 第95页,《特征工程与选择》,2019年。
在评估机器学习算法时,我们很少遇到这个问题,除非我们使用线性回归。
… 在某些情况下,一整套虚拟变量是有用的。例如,当虚拟变量编码了该预测变量的所有信息时,基于树的模型的拆分更具可解释性。我们建议在使用基于树的模型时使用全套虚拟变量。
— 第56页,《应用预测建模》,2013年。
我们可以使用 OneHotEncoder 类来实现虚拟编码和独热编码。
“drop”参数可以设置,以指示哪个类别将被分配所有零值,称为“基线”。我们可以将其设置为“first”,以便使用第一个类别。当标签按字母顺序排序时,第一个“蓝色”标签将是第一个,并将成为基线。
虚拟变量的数量总是比级别数少一个。没有虚拟变量的级别 [...] 称为基线。
— 第86页,《R语言统计学习导论与应用》,2014年。
我们可以用我们的颜色类别来演示这一点。完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 |
# 虚拟变量编码示例 from numpy import asarray from sklearn.preprocessing import OneHotEncoder # 定义数据 data = asarray([['red'], ['green'], ['blue']]) print(data) # 定义独热编码 encoder = OneHotEncoder(drop='first', sparse=False) # 转换数据 onehot = encoder.fit_transform(data) print(onehot) |
运行该示例首先列出分类变量的三行,然后是虚拟变量编码,显示绿色被“编码”为[1, 0],“红色”被编码为[0, 1],“蓝色”被编码为[0, 0],正如我们所指定的。
1 2 3 4 5 6 |
[['red'] ['green'] ['blue']] [[0. 1.] [1. 0.] [0. 0.]] |
现在我们熟悉了分类变量的三种编码方法,接下来让我们看一个包含分类变量的数据集。
乳腺癌数据集
本教程的基础是自20世纪80年代以来在机器学习领域广泛研究的“乳腺癌”数据集。
该数据集将乳腺癌患者数据分类为复发或未复发。共有286个样本和9个输入变量。这是一个二元分类问题。
该数据集上合理的分类准确率在68%到73%之间。我们将以这个区域为目标,但请注意,本教程中的模型并未优化:它们旨在演示编码方案。
无需下载数据集,我们将直接从代码示例中访问它。
查看数据,我们可以看到所有九个输入变量都是分类变量。
具体来说,所有变量都是带引号的字符串。有些变量显示出明显的序数关系(如年龄范围),有些则没有。
1 2 3 4 5 6 |
'40-49','premeno','15-19','0-2','yes','3','right','left_up','no','recurrence-events' '50-59','ge40','15-19','0-2','no','1','right','central','no','no-recurrence-events' '50-59','ge40','35-39','0-2','no','2','left','left_low','no','recurrence-events' '40-49','premeno','35-39','0-2','yes','3','right','left_low','yes','no-recurrence-events' '40-49','premeno','30-34','3-5','yes','2','left','right_up','no','recurrence-events' ... |
请注意,此数据集的缺失值标记为“nan”。
在本教程中,我们将保留这些值不变,并使用编码方案将“nan”编码为另一个值。这是处理分类变量缺失值的一种可能且相当合理的方法。
我们可以使用 Pandas 库将此数据集加载到内存中。
1 2 3 4 5 |
... # 加载数据集 dataset = read_csv(url, header=None) # 检索数据数组 data = dataset.values |
加载后,我们可以将列分成输入 (X) 和输出 (y) 用于建模。
1 2 3 4 |
... # 分割为输入和输出列 X = data[:, :-1].astype(str) y = data[:, -1].astype(str) |
利用此函数,下面列出了加载和汇总原始分类数据集的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 加载并汇总数据集 from pandas import read_csv # 定义数据集位置 url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/breast-cancer.csv" # 加载数据集 dataset = read_csv(url, header=None) # 检索数据数组 data = dataset.values # 分割为输入和输出列 X = data[:, :-1].astype(str) y = data[:, -1].astype(str) # 总结 print('Input', X.shape) print('Output', y.shape) |
运行该示例报告了数据集输入和输出元素的尺寸。
我们可以看到我们有286个样本和9个输入变量。
1 2 |
输入 (286, 9) 输出 (286,) |
现在我们熟悉了数据集,接下来看看如何将其编码以进行建模。
OrdinalEncoder 转换
序数编码涉及将每个唯一标签映射到一个整数值。
这种编码类型只适用于类别之间存在已知关系的情况。在我们的数据集中,一些变量确实存在这种关系,理想情况下,在准备数据时应该利用这种关系。
在这种情况下,我们将忽略任何可能存在的序数关系,并假设所有变量都是分类变量。使用序数编码仍然有帮助,至少可以作为与其他编码方案的参考点。
我们可以使用scikit-learn中的OrdinalEncoder将每个变量编码为整数。这是一个灵活的类,如果已知任何此类顺序,确实允许将类别的顺序指定为参数。
注意:我将此留给您作为练习,尝试更新下面的示例,为那些具有自然排序的变量指定顺序,并查看其是否对模型性能产生影响。
定义后,我们可以调用 fit_transform() 函数并将其传递给我们的数据集,以创建我们数据集的量化转换版本。
1 2 3 4 |
... # 序数编码输入变量 ordinal = OrdinalEncoder() X = ordinal.fit_transform(X) |
我们也可以以相同的方式准备目标。
1 2 3 4 |
... # 序数编码目标变量 label_encoder = LabelEncoder() y = label_encoder.fit_transform(y) |
让我们在乳腺癌数据集上尝试一下。
下面列出了创建乳腺癌数据集的序数编码转换并汇总结果的完整示例。
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 pandas import read_csv from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OrdinalEncoder # 定义数据集位置 url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/breast-cancer.csv" # 加载数据集 dataset = read_csv(url, header=None) # 检索数据数组 data = dataset.values # 分割为输入和输出列 X = data[:, :-1].astype(str) y = data[:, -1].astype(str) # 序数编码输入变量 ordinal_encoder = OrdinalEncoder() X = ordinal_encoder.fit_transform(X) # 序数编码目标变量 label_encoder = LabelEncoder() y = label_encoder.fit_transform(y) # 汇总转换后的数据 print('Input', X.shape) print(X[:5, :]) print('Output', y.shape) print(y[:5]) |
运行该示例将转换数据集并报告结果数据集的形状。
我们预期行数(在此情况下,列数)不变,只是所有字符串值现在都变为整数值。
正如预期的那样,在这种情况下,我们可以看到变量的数量没有变化,但所有值现在都已进行序数编码为整数。
1 2 3 4 5 6 7 8 |
输入 (286, 9) [[2. 2. 2. 0. 1. 2. 1. 2. 0.] [3. 0. 2. 0. 0. 0. 1. 0. 0.] [3. 0. 6. 0. 0. 1. 0. 1. 0.] [2. 2. 6. 0. 1. 2. 1. 1. 1.] [2. 2. 5. 4. 1. 1. 0. 4. 0.]] 输出 (286,) [1 0 1 0 1] |
接下来,让我们使用这种编码评估该数据集上的机器学习。
编码变量的最佳实践是,在训练数据集上拟合编码,然后将其应用于训练集和测试集。
我们将首先分割数据集,然后在训练集上准备编码,并将其应用于测试集。
1 2 3 |
... # 将数据集分割为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) |
然后,我们可以在训练数据集上拟合 OrdinalEncoder,并用它来转换训练集和测试集。
1 2 3 4 5 6 |
... # 序数编码输入变量 ordinal_encoder = OrdinalEncoder() ordinal_encoder.fit(X_train) X_train = ordinal_encoder.transform(X_train) X_test = ordinal_encoder.transform(X_test) |
同样的方法可以用于准备目标变量。然后,我们可以在训练数据集上拟合逻辑回归算法,并在测试数据集上评估它。
完整的示例如下所示。
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 |
# 使用序数编码评估乳腺癌数据集上的逻辑回归 from numpy import mean from numpy import std from pandas import read_csv from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OrdinalEncoder from sklearn.metrics import accuracy_score # 定义数据集位置 url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/breast-cancer.csv" # 加载数据集 dataset = read_csv(url, header=None) # 检索数据数组 data = dataset.values # 分割为输入和输出列 X = data[:, :-1].astype(str) y = data[:, -1].astype(str) # 将数据集分割为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 序数编码输入变量 ordinal_encoder = OrdinalEncoder() ordinal_encoder.fit(X_train) X_train = ordinal_encoder.transform(X_train) X_test = ordinal_encoder.transform(X_test) # 序数编码目标变量 label_encoder = LabelEncoder() label_encoder.fit(y_train) y_train = label_encoder.transform(y_train) y_test = label_encoder.transform(y_test) # 定义模型 model = LogisticRegression() # 在训练集上拟合 model.fit(X_train, y_train) # 在测试集上预测 yhat = model.predict(X_test) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.2f' % (accuracy*100)) |
运行该示例以正确的方式准备数据集,然后评估拟合在转换数据上的模型。
注意:由于算法或评估过程的随机性,或者数值精度差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,模型实现了约75.79%的分类准确率,这是一个合理的分数。
1 |
准确率:75.79 |
接下来,让我们仔细看看独热编码。
OneHotEncoder 转换
独热编码适用于类别之间没有关系的分类数据。
scikit-learn 库提供了 OneHotEncoder 类来自动对一个或多个变量进行独热编码。
默认情况下,OneHotEncoder 将输出稀疏表示的数据,这很高效,因为在编码表示中大多数值为0。我们将通过将“sparse”参数设置为False来禁用此功能,以便我们可以查看编码效果。
定义后,我们可以调用 fit_transform() 函数并将其传递给我们的数据集,以创建我们数据集的量化转换版本。
1 2 3 4 |
... # 独热编码输入变量 onehot_encoder = OneHotEncoder(sparse=False) X = onehot_encoder.fit_transform(X) |
和之前一样,我们必须对目标变量进行标签编码。
下面列出了创建乳腺癌数据集的独热编码转换并汇总结果的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# 对乳腺癌数据集进行独热编码 from pandas import read_csv from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OneHotEncoder # 定义数据集位置 url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/breast-cancer.csv" # 加载数据集 dataset = read_csv(url, header=None) # 检索数据数组 data = dataset.values # 分割为输入和输出列 X = data[:, :-1].astype(str) y = data[:, -1].astype(str) # 独热编码输入变量 onehot_encoder = OneHotEncoder(sparse=False) X = onehot_encoder.fit_transform(X) # 序数编码目标变量 label_encoder = LabelEncoder() y = label_encoder.fit_transform(y) # 汇总转换后的数据 print('Input', X.shape) print(X[:5, :]) |
运行该示例将转换数据集并报告结果数据集的形状。
我们预计行数保持不变,但列数会大幅增加。
正如预期的那样,在这种情况下,我们可以看到变量数量从9个猛增到43个,并且所有值现在都是二进制值0或1。
1 2 3 4 5 6 7 8 9 10 11 |
输入 (286, 43) [[0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 0. 1. 0. 0. 1. 0. 0. 0. 1. 0.] [0. 0. 0. 1. 0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 1. 0. 0. 1. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 1. 0.] [0. 0. 0. 1. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 0. 1. 0. 0. 1. 0. 0. 0. 0. 1. 0.] [0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 0. 1. 0. 1. 0. 0. 0. 0. 0. 1.] [0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 0. 0. 1. 0. 1. 0. 0. 0. 0. 0. 1. 0. 1. 0.]] |
接下来,我们将像上一节一样,使用这种编码评估此数据集上的机器学习。
编码在训练集上拟合,然后像之前一样应用于训练集和测试集。
1 2 3 4 5 6 |
... # 独热编码输入变量 onehot_encoder = OneHotEncoder() onehot_encoder.fit(X_train) X_train = onehot_encoder.transform(X_train) X_test = onehot_encoder.transform(X_test) |
将这些结合起来,完整的示例列在下面。
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 |
# 使用独热编码评估乳腺癌数据集上的逻辑回归 from numpy import mean from numpy import std from pandas import read_csv from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import OneHotEncoder from sklearn.metrics import accuracy_score # 定义数据集位置 url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/breast-cancer.csv" # 加载数据集 dataset = read_csv(url, header=None) # 检索数据数组 data = dataset.values # 分割为输入和输出列 X = data[:, :-1].astype(str) y = data[:, -1].astype(str) # 将数据集分割为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 独热编码输入变量 onehot_encoder = OneHotEncoder() onehot_encoder.fit(X_train) X_train = onehot_encoder.transform(X_train) X_test = onehot_encoder.transform(X_test) # 序数编码目标变量 label_encoder = LabelEncoder() label_encoder.fit(y_train) y_train = label_encoder.transform(y_train) y_test = label_encoder.transform(y_test) # 定义模型 model = LogisticRegression() # 在训练集上拟合 model.fit(X_train, y_train) # 在测试集上预测 yhat = model.predict(X_test) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.2f' % (accuracy*100)) |
运行该示例以正确的方式准备数据集,然后评估拟合在转换数据上的模型。
注意:由于算法或评估过程的随机性,或者数值精度差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,模型的分类准确率约为70.53%,略低于上一节的序数编码。
1 |
准确率:70.53 |
常见问题
本节列出了一些在编码分类数据时常见的问题和答案。
问:如果我同时有数值数据和分类数据怎么办?
或者,如果我同时有分类数据和序数数据怎么办?
您需要单独准备或编码数据集中的每个变量(列),然后将所有准备好的变量重新合并成一个数组,以便拟合或评估模型。
或者,您可以使用ColumnTransformer根据条件对不同的输入变量应用不同的数据转换。
问:如果我有数百个类别怎么办?
或者,如果我连接了许多独热编码向量以创建成千上万个元素的输入向量怎么办?
独热编码可以支持多达数千甚至上万个类别。此外,拥有大型向量作为输入听起来令人生畏,但模型通常可以处理它们。
问:哪种编码技术最好?
这是未知的。
在您的数据集上使用您选择的模型测试每种技术(以及更多),并找出最适合您情况的方法。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
教程
书籍
- 机器学习的特征工程, 2018.
- 应用预测建模, 2013.
- R语言统计学习应用导论, 2014。
API
- sklearn.preprocessing.OneHotEncoder API.
- sklearn.preprocessing.LabelEncoder API.
- sklearn.preprocessing.OrdinalEncoder API.
文章
总结
在本教程中,您学习了如何对分类机器学习数据使用编码方案。
具体来说,你学到了:
- 对于机器学习算法的分类数据,编码是必需的预处理步骤。
- 如何对具有自然排序的分类变量使用序数编码。
- 如何对没有自然排序的分类变量使用独热编码。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
“这种分类变量被称为名义变量,因为其值可以排序或分级。”这里应该是序数变量而不是名义变量。
谢谢,已修复!
你好,
“上面的“位置”变量确实具有值的自然排序。这种类型的分类变量称为名义变量,因为这些值可以排序或分级。”
你是指“序数变量”而不是“名义变量”吗?
是的,已修复。感谢您的发现。
关于sklearn中“为什么使用OneHotEncoder而不是get_dummies?”的有趣讨论可以在这里找到:https://stackoverflow.com/questions/36631163/what-are-the-pros-and-cons-between-get-dummies-pandas-and-onehotencoder-sciki
感谢分享。
在OrdinalEncoding的情况下,我观察到在引入新值后编码值发生了偏移。例如,data = asarray([['orange'], ['red'], ['green'], ['blue']])。在实际场景中,附加值不应该打乱训练集和测试集之间现有编码值的顺序。我们能否使用某种哈希技术来生成一致的编码值?
您可以预先指定预期值的完整列表,并保存对象以供将来所有数据使用。
或者您可以编写一些自定义代码来处理映射。
你好,
当我尝试使用参数 drop='first' 进行虚拟编码时,我收到此错误:__init__() got an unexpected keyword argument 'drop'。
我在这里查看了语法文档:https://scikit-learn.cn/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html?highlight=onehotencoder#sklearn.preprocessing.OneHotEncoder
代码似乎是正确的,但我仍然收到错误。请您帮忙吗?
非常感谢您的教程——它们太棒了!
也许可以确认您的scikit-learn版本是否最新?
唉。非常抱歉。我以为我是最新版本。我更新了,现在虚拟编码代码可以工作了。感谢您抽出时间回复我!
干得好!
没问题。
请问,我正在尝试在训练数据集上拟合OrdinalEncoder并使用它来转换训练集和测试集,如下所示;
ordinal_encoder = OrdinalEncoder()
ordinal_encoder.fit(X_train)
X_train = ordinal_encoder.transform(X_train)
X_test = ordinal_encoder.transform(X_test)
我遇到这个错误
——————————————————————————
ValueError 回溯 (最近一次调用)
in ()
2 ordinal_encoder.fit(x_train)
3 x_train=ordinal_encoder.transform(x_train)
—-> 4 x_test = ordinal_encoder.transform (x_test)
1 帧
/usr/local/lib/python3.6/dist-packages/sklearn/preprocessing/_encoders.py in _transform(self, X, handle_unknown)
122 msg = (“在转换过程中,在第 {1} 列中发现未知类别 {0}”
123 ” “.format(diff, i))
–> 124 raise ValueError(msg)
125 else
126 # 将有问题行设置为可接受的值
ValueError: 在转换过程中,在第0列中发现未知类别 [69.0, 70.0, 71.0, 72.0]
有什么办法解决吗?我感谢您的时间。
很抱歉您遇到麻烦,这可能会有所帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
这个问题的一个原因是测试数据集中存在训练集中没有的类别值。因此,编码器在拟合过程中没有见过这个值,也就不知如何编码。处理这种情况有几种不同的选择:
1. 使用 handle_unknown 参数,请参阅 OrdinalEncoder 文档。
2. 使用 categories 参数,请参阅 OrdinalEncoder 文档。我个人更喜欢这种方法,因为它提供了完全的控制,并且允许训练集和测试集之间的一致性。
然而,在您的案例中,错误消息是 ValueError: Found unknown categories [69.0, 70.0, 71.0, 72.0] in column 0 during transform
这让我想到您数据集中第一列可能是序列号,检查一下这是否真的应该作为特征集的一部分?如果这是一个简单的序列号,您绝对无法将其视为分类数据。
通常未见的类别被标记为全零。
嗨!
我正在处理的一个分类变量是就业时长,它有511193个不同的类别。我收到内存错误“无法为形状为(511193, 511193)的数组分配243.GiB”。如何处理这个问题。我遇到过一些文章说独热编码无法处理高基数,并且会给出误导性结果。请帮忙!谢谢
也许尝试序数编码。
另外,也许尝试将结果与嵌入和哈希进行比较。
更多建议在这里
https://machinelearning.org.cn/faq/single-faq/how-do-i-handle-a-large-number-of-categories
嗨,Jason,
既然独热编码可能会导致线性回归的问题。我可以将数据处理为虚拟变量,这样就不会影响我是否将其用于基于树的方法或线性模型吗?
是的,我相信是这样。如果您担心,请进行测试以确认。
你好,Jason。
我是一个机器学习预测的初学者。
我已经按照您的代码对乳腺癌数据集使用 OrdinalEncoder。在使用 OrdinalEncoder 编码后,如何输入新数据进行预测(除了测试数据集)?
这可以帮助您使用新数据进行模型/管道预测
https://machinelearning.org.cn/make-predictions-scikit-learn/
你好,Jason。
我是一个机器学习预测的初学者。
我已经按照您的代码对乳腺癌数据集使用 OrdinalEncoder。在使用 OrdinalEncoder 编码后,如何获得数据集未知的新数据(即与测试数据集不同的数据)的预测?
好问题,请看这个
https://machinelearning.org.cn/make-predictions-scikit-learn/
谢谢你的回复。
我明白了,但我的问题是如何对新的分类数据集进行预测。您提供的链接中的示例都是关于数字的。
但是,假设我正在处理 breast-cancer.csv 数据集,如何预测数据集中未知的新分类数据的结果?
无论数据类型如何,进行预测的方法都是相同的。例如,调用 model.predict()
如果您使用编码,则将它们封装到一个管道中,然后调用 pipeline.predict() 并将原始数据作为输入传递。
非常感谢。我搞清楚了。
干得好。
如果您需要一个默认值来覆盖未预见的分类级别怎么办?虚拟编码对此非常适用,但我认为应该讨论一下这个问题。
您可以设置handle_unknown参数并指定如何处理训练期间未见的标签。
“在这种情况下,我们将忽略任何可能存在的序数关系,并假设所有变量都是分类的。使用序数编码仍然有帮助,‘至少可以作为与其他编码方案的参考点。’”你能详细说明这句话吗?
是的,如果您愿意,您可以选择指定某些变量中标签之间的序数关系。这可能会提高拟合在序数编码上的模型的性能。
多次运行“使用序数编码评估乳腺癌数据集上的逻辑回归”代码会得到相同的准确率,因为在将数据集分割为训练集和测试集时使用了 random_state=1。在设置 random_state=None 后,您可能会收到如下错误:
ValueError: 在转换过程中,在第3列中发现未知类别 ["'24-26'"]
我假设我们应该在分割之前对输入变量进行序数编码。修改后的代码
for i in range(10)
# 序数编码输入变量
ordinal_encoder = OrdinalEncoder()
ordinal_encoder.fit(X)
X = ordinal_encoder.transform(X)
# 将数据集分割为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=None)
# 序数编码目标变量
label_encoder = LabelEncoder()
label_encoder.fit(y_train)
y_train = label_encoder.transform(y_train)
y_test = label_encoder.transform(y_test)
# 定义模型
model = LogisticRegression()
# 在训练集上拟合
model.fit(X_train, y_train)
# 在测试集上预测
yhat = model.predict(X_test)
# 评估预测
accuracy = accuracy_score(y_test, yhat)
print(f'{i + 1}. Accuracy: {accuracy*100:.2f} %’)
运行良好,在后续运行中给出不同的结果,例如
1. 准确率:64.21 %
2. 准确率:77.89 %
3. 准确率:75.79 %
4. 准确率:77.89 %
5. 准确率:76.84 %
6. 准确率:68.42 %
7. 准确率:75.79 %
8. 准确率:73.68 %
9. 准确率:71.58 %
10. 准确率:80.00 %
感谢分享。
您使用序数编码器转换目标,然后拟合逻辑回归模型。
这个过程是序数逻辑回归还是有什么不同?
提前感谢
不,只是“逻辑回归”。
嗨,请问在使用了独热编码并使用逻辑回归或其他分类算法构建模型后,我们如何计算分类数据的系数?因为在使用独热编码后,一个分类变量会变成例如3列,我们应该分别计算每个依赖于分类数据的列的系数然后相加,还是不相加?这是怎么回事?我有不同类型的变量,我的目标是构建分类模型后,找出哪个变量对我的目标变量(输出)影响最大。
什么是“分类数据系数”?
ValueError 回溯 (最近一次调用位于)
in ()
59 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
60 # 准备输入数据
—> 61 X_train_enc, X_test_enc = prepare_inputs(X_train, X_test)
62 # 准备输出数据
63 y_train_enc, y_test_enc = prepare_targets(y_train, y_test)
在 prepare_inputs(X_train, X_test)
38 # 编码
39 train_enc = le.transform(X_train[:, i])
—> 40 test_enc = le.transform(X_test[:, i])
41 # 存储
42 X_train_enc.append(train_enc)
~\Anaconda3\lib\python3.6\dist-packages\sklearn\preprocessing\label.py 中的 transform(self, y)
131 if len(np.intersect1d(classes, self.classes_)) 133 raise ValueError(“y 包含新标签: %s” % str(diff))
134 return np.searchsorted(self.classes_, y)
135
ValueError: y 包含新标签: ['102' '120' '137' '145' '147' '15' '159' '174' '195' '208' '214' '222'
'223' '23' '24' '247' '248' '259' '264' '286' '289' '290' '291' '298'
'301' '325' '328' '331' '34' '341' '354' '361' '367' '378' '401' '402'
'410' '426' '441' '446' '452' '46' '468' '485' '489' '5' '510' '512'
'513' '520' '533' '54' '550' '579' '586' '587' '59' '603' '605' '73' '79'
'89' '90' '99']
###################
我有多分类标签
很抱歉您遇到问题,这些提示可能会有所帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
如何处理独热编码中的缺失值?
我正在尝试对包含缺失值“N”的二维 numpy 数组应用独热编码。我目前的这段代码,在没有缺失值的情况下工作良好。
import numpy as np
X = [['A', 'G'], ['N', 'C'], ['T', 'A']]
X = np.array(X)
print('数据独热编码前的形状:', X.shape)
classes = np.array(['A', 'C', 'G', 'T'])
X = np.searchsorted(classes, X)
eye = np.eye(classes.shape[0])
onehotlabels = np.concatenate([eye[i] for i in X.T], axis=1)
print('数据预处理后的形状:', onehotlabels.shape)
print(onehotlabels)
输出
数据独热编码前的形状:(3, 2)
数据预处理后的形状:(3, 8)
[[1. 0. 0. 0. 0. 0. 1. 0.]
[0. 0. 0. 1. 0. 1. 0. 0.]
[0. 0. 0. 1. 1. 0. 0. 0.]]
是否有可能通过这种方法将“N”替换为0 0 0 0?输出将如下所示
[[1. 0. 0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 1. 0. 0.]
[0. 0. 0. 1. 1. 0. 0. 0.]]
您可以将“handle_unknown”设置为“ignore”。
https://scikit-learn.cn/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
嗨,Jason,
感谢您精彩的文章。
我有一个疑问,在任何地方都找不到答案。
假设我的目标变量是类别水果——苹果、橙子、芒果、香蕉。
它们之间没有序数关系。我可以使用独热编码还是序数编码?
到处(包括其他网站),我都可以找到将目标变量转换为苹果-1,橙子-2,芒果-3,香蕉-4。有时会创建带有标签1、2、3、4的列。
我的疑问——1)如果苹果、橙子、芒果、香蕉之间不存在关系。那么我们应该使用独热编码/虚拟变量编码还是序数编码来转换目标变量。我发现大多数情况下都使用序数编码。
如果我使用独热编码/虚拟变量编码对目标变量苹果、橙子、芒果、香蕉进行编码。那么会创建许多列。我认为这不合适。
这是为目标创建单列的原因——使用序数编码。
2) 那么如果我们对带有类别的目标变量使用独热编码/虚拟变量创建,那么哪种算法适合预测呢?
独热编码听起来很合适。
是的,会创建许多列,这通常不是问题,即使有数万个新列。
你好,
据我理解,标签编码器是用于分类目标变量(即y)。将其用于独立特征(即X)有意义吗?
**我刚买了你的书《机器学习的数据准备》**
这是一本很棒的书
是的,您可以在输入上使用标签编码器,但我们现在有“序数编码器”,它做相同的工作,并且旨在处理输入。
谢谢!!!
好的,谢谢。我使用标签编码器是因为该技术不需要二维数据。当我尝试使用序数编码器时,我必须重塑数据。
是的,这是一个关键区别。
很棒的技术写作!!!
我想知道应该先做哪一步:数据清洗还是数据转换?
我认为数据清洗包括异常值检测等子任务,其中列的值预期为数值。
谢谢。
通常两者都会随着您对数据/问题的深入了解而迭代,但通常是先清洗后转换。
你好,
如果我同时有分类变量和名义变量,我可以对分类变量使用独热编码,对名义变量使用标签编码吗?
当然。使用最适合您数据的方法。
你好,
引用您的文章”
“如果新数据包含训练数据集中未见的类别,可以将“handle_unknown”参数设置为“ignore”以不引发错误,这将导致每个标签的值为零。”
我猜是因为我们使用某个OneHotEncoder在训练集上进行拟合和转换,所以可能会出现测试集具有未进行OneHotEncoded的特征的情况,这正是您在博客中写到的。
在这种情况下,将“handle_unknow”设置为“ignore”是否会导致预测/分类的准确性损失?
是否有其他方法可以处理该问题,而不会导致数据泄露?
此致
它可能会或可能不会影响模型性能,具体取决于模型和数据集。
更好的方法是拥有一个更完整的训练数据集。
嗨,Jason,
您的文章总是我解决机器学习/人工智能相关问题的首选!感谢您对社区的贡献。
我的问题是,对于基于树的模型,是使用OHE还是虚拟编码更好?基于树的算法根据所有可用变量构建树,如果我们删除一个级别,树将永远看不到该变量,也不会在拆分中使用它。我很想知道您的想法。
谢谢。
也许可以尝试这两种方法,看看哪种方法最适合您的特定数据集和模型。
非常有趣的文章。
我仍然有些困惑。我目前正在尝试从头开始在骨折数据集上进行逻辑回归,并且有几个分类列需要我自己编码。
你说虚拟编码避免了独热编码带来的奇异矩阵问题(在线性回归中是这样,但在逻辑回归中可能不那么重要?),并且它适用于 n-1 列。
但是如果我想对性别列进行编码,唯一的可能性是男性为1,女性为0(或反之)。如果我应用虚拟编码的逻辑,我们又回到了序数关系问题(这里分类变量的不同值之间不存在这种关系),而我们正是为了避免这个问题才使用独热编码的?
那么,我有一个“药物”列,包含“无药物”、“药物 #1”、“药物 #2”等值,在这里我完全可以像您对颜色那样进行独热编码,对吗?
最后,我可以在哪里找到更多关于当不存在序数关系时,为什么序数编码会很糟糕,以及相反地,为什么独热编码会很好工作的确切数学原因?
非常感谢,
Geoffrey (来自法国)
为什么在没有序数关系时序数编码会很糟糕:测量级别按信息量递增的顺序排列为名义、序数、区间和比率。如果你在没有序数关系时使用序数数字,你就是在给机器引入不存在的信息进行学习,这本质上是噪音,会混淆模型。想想如果我们引入电话号码(名义数据)和学生学习的小时数(比率数据)来预测他们的考试分数会发生什么。
我忘了,你能详细说明为什么矩阵在独热编码后会变成奇异矩阵吗?问题是否发生在 n >= 3 时,其中 n 是分类特征中不同值的数量?因为当 n = 2 时,一列为 0,另一列为 1,但当 n = 3 时,两列可以具有相同的 0 值,我想问题就从这里开始?
然而,我仍然很难理解为什么矩阵会变成奇异矩阵。例如,在您给出的颜色示例中,矩阵不是奇异的,尽管您没有使用虚拟编码。我能想到的唯一使它奇异的方法是如果有一列全是零,但这将意味着该值从未出现过,所以这有点奇怪……
更广泛地说,在线性回归中,我们可能有两个相同的样本(例如,两个年龄和身高相同的人,体重目标也相同),我们的矩阵会是奇异的,对吗?
如果您不介意,请您扩展这些观点,我发现它们非常有趣,但我很难理解。
再次感谢
Geoffrey
从数学上讲,如果一个矩阵无法求逆,那么它就是奇异矩阵。一个包含大量零(独热编码就是这种情况)的矩阵很难求逆(什么数字乘以零会等于一?),因此很容易成为奇异矩阵。线性代数是一个非常广泛的话题,但在机器学习的理论方面非常有用。希望这个简短的回答能帮助您进一步理解。
您好,我有一个包含6个不同值的目标向量,经过独热编码后,我将得到6个目标向量。我想用核函数机训练模型,并且每次训练只能有一个目标向量。所以我会针对这6个目标向量进行6次训练。现在,我该如何组合这6个结果来获得预测和准确性?
谢谢。
在您的方法中,可能需要6个模型,每个模型对每个目标值进行二元(输出采用 Sigmoid 激活函数,范围为0到1)分类,然后比较哪个模型给出最高的浮点分数并将其用作预测。
或者更常见的方法是,一个模型有6个输出,每个输出都是 Sigmoid 激活,然后应用 Softmax 来为每个目标提供概率。在这种情况下,您的输出不是6个目标向量,而是一个包含6列的矩阵。
首先,非常感谢您。我从您的网站学到了很多东西。
我想问一个关于概念的问题。序数数据是名义数据的一种有顺序的类型,还是它们都是分类数据的分支?
名义数据可以认为是分类数据。但序数数据不是,因为顺序意味着一些东西,而不仅仅是名称。
你好 Jason,我正在用一系列模型进行二元分类:一些 scikit-learn 模型,一些 Transformer 模型和一些深度学习神经网络。
对于我的 scikit-learn 和 Transformer 模型,当使用 get_dummies 时,它们都运行良好。对于我的深度学习模型,当它输出形状为 [3625, ] 的数组时,模型的准确性和损失(只有一个输出神经元)都失败了。当 get_dummies 生成 [3625,2] 的数组时,模型运行良好,你知道为什么会这样吗?
非常感谢
抱歉,您描述的内容不太清楚。您是说模型失败是因为您的预测输出和训练输出形状不同吗?
在进行 PCA 或 PLS 之前应用独热编码或虚拟编码是否明智?
我有一个混合数据集,其中包含2个分类变量,分别有3个和2个类别。
PCA 是探索性步骤的一部分,旨在尝试理解数据。在这种情况下,您可能需要运行独热编码,将一个名义特征转换为多个特征,以帮助 PCA。
你好 Adrian,
感谢您精彩而详细的文章!一个快速的问题,您认为我们需要将原始编码变量(0, 1, 2, 3)标准化到0到1的范围吗?我之所以这样问,是因为我有大量级的连续变量,需要先将其标准化到0到1的范围,然后再输入模型。在对具有序数关系的类别进行序数编码后,是否也应该进行相同的标准化?
我建议您学习测量级别:https://en.wikipedia.org/wiki/Level_of_measurement
我们进行独热编码是因为数据是名义的,我们进行归一化是因为数据是区间或比率的。如果您尝试一下,错误的预处理可能会给数据注入噪声,并混淆您的机器学习模型。
我的分类变量确实有序数关系(例如,低、中、高)。在序数编码后,它被转换为 0、1 和 2),我的问题是,我们是否应该进一步将其标准化到 0 到 1 的范围(例如,0、0.5、1)
我认为 0 到 2 和 0 到 1 之间没有太大区别。所以在这种情况下,它应该是可选的。
你好 Jason,非常感谢您的系列文章,内容非常有帮助和信息量大。
我有一个关于您上面关于何时进行编码的声明的问题,其中提到
“编码变量的最佳实践是在训练数据集上拟合编码器,然后将其应用于训练和测试数据集。”
为什么会这样呢?似乎有些人认为,如果在将完整数据集分割成训练集和测试集之前进行编码,存在“数据泄露”的风险。但是编码与插补不同。插补使用数据集中的统计数据,因此肯定存在数据泄露的风险。但是编码仅仅是特征可能值空间的另一种表达。它不关心训练集和测试集中实际发现的值,它只关心可能值空间,对吗?因此,我认为编码中可能不存在数据泄露的风险,因为它不关心训练集和测试集中发现的特定值。它只关心可能值空间。
这种区别很重要,因为如果一个人尝试,例如,仅在训练数据上拟合 OrdinalEncode,而训练数据恰好缺少一个可能的值,并且这个缺失值出现在测试数据中,我们就会得到一个异常。但这可以通过在分割之前对完整数据集进行编码来避免。
也许我没有清楚地考虑这个问题?我欢迎您的想法和观点。
谢谢,mark
你好 Mark...请将您的疑问缩小到一个问题,以便我们更好地协助您。
你好。
如果我做一个线性回归模型,我可以将年份作为分类变量(而不是数值变量)纳入其中,以便估计每年的不同系数。当变量不是线性或者你想检测特定年份的异常情况时,通常会这样做……
当您使用R中的常见回归包进行操作时,它们会自动将其中一个级别(类别)作为参考,并根据此基础级别计算系数。也可以将所有级别的总平均值作为参考(我认为这包含在名为“对比”的包中,但这与我们编码虚拟变量的方式有关)。
我的问题是…
如果我想计算模型时,将每年的系数参考前一年而不是共同的系数,我该如何编码?
你好 Amparo...我们的内容基于 Python。以下资源提供了从头开始实现回归的示例。
https://machinelearning.org.cn/implement-linear-regression-stochastic-gradient-descent-scratch-python/
这是什么,它有什么作用——我为什么要将其分为输入和输出列?它在问题中突然出现,没有任何解释。您能澄清一下吗?
# 分割为输入和输出列
X = data[:, :-1].astype(str)
y = data[:, -1].astype(str)
你好 Jason...您可能会发现以下内容有帮助
https://machinelearning.org.cn/start-here/#dataprep
我有一个问题。为什么我的模型在使用序数编码器时能给出更好的结果,即使该特征不是序数数据?这是怎么回事?我应该继续使用独热编码,即使模型表现更差吗?
先谢谢了
你好 Dani… 序数编码是机器学习中用于将分类数据(大多数算法无法直接处理)转换为数值格式的技术。这种方法特别适用于处理类别具有自然有序关系的分类变量。以下是序数编码特别有用的几种情况:
1. **有序类别**:当分类变量表示固有的顺序或排名时。例如,教育水平(例如,高中 < 本科 < 硕士 < 博士)、评分等级(例如,差 < 一般 < 好 < 优秀)或尺寸(例如,小 < 中 < 大)。序数编码保留了顺序信息,可以被机器学习模型利用。2. **效率**:序数编码将类别转换为简单的数字代码,与独热编码等其他编码方法相比,在内存和计算成本方面更有效,尤其是在类别数量较多但其顺序很重要的情况下。3. **模型兼容性**:某些机器学习模型可以利用编码特征的序数性质来提高性能。例如,基于树的模型(如决策树和梯度提升机)可以从序数编码中受益,因为它们可以做出尊重编码类别顺序的决策。4. **降维**:与独热编码不同,独热编码会随着每个附加类别增加特征空间维度(在某些情况下会导致“维度诅咒”),而序数编码使特征空间更紧凑。这在处理具有明确排名的基数较高的分类变量时尤其有利。5. **简单性和可解释性**:在某些情况下,序数编码可以使模型的决策更具可解释性,因为数字表示直接对应于分类变量中的顺序。这在模型可解释性至关重要的领域(如金融和医疗保健)中可能具有优势。然而,重要的是要谨慎使用序数编码,因为它引入了可能不总是合适或可能过度简化数据内部关系的顺序假设。对于没有自然顺序的分类变量,其他编码技术,如独热编码、目标编码或二元编码可能更适合。此外,在使用序数编码时,数值的选择可能会影响模型的性能,因此确保编码准确反映类别之间的底层顺序至关重要。
你好,好文章,有没有办法在 GridSearchCV 或 RandomizedSearchCV 中将这些 Column Transformers 用作替代步骤?我的意思是,我们如何只将它们拟合到训练数据上,因为超参数调优器会应用交叉验证?或者我们如何用不同的数据来喂给 Column Transformers?
谢谢。
你好 Petros…是的,你绝对可以在
GridSearchCV
或RandomizedSearchCV
中使用ColumnTransformer
,并且它只会在交叉验证的每次折叠中拟合训练数据。关键是结合ColumnTransformer
和管道,以确保转换器(以及任何预处理步骤)在每次交叉验证拆分中仅正确应用于训练数据。它是这样工作的:
### 步骤
1. **创建管道**:使用
sklearn.pipeline
中的Pipeline
将你的预处理步骤(例如,ColumnTransformer
)和模型组合成一个对象。2. **使用 GridSearchCV/RandomizedSearchCV**:将
GridSearchCV
或RandomizedSearchCV
应用于此管道,它将自动处理ColumnTransformer
在每个交叉验证折叠内的拟合过程,只在训练数据上拟合它。### 示例
python
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
# 加载样本数据
data = fetch_openml(name="adult", version=2)
X, y = data.data, data.target
# 不同数据类型的示例
numeric_features = ['age', 'hours-per-week']
categorical_features = ['workclass', 'education', 'marital-status']
# ColumnTransformer 对数值和分类数据应用不同的预处理
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), numeric_features),
('cat', OneHotEncoder(), categorical_features)
])
# 管道:首先应用 ColumnTransformer,然后拟合分类器
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
('classifier', RandomForestClassifier())])
# 为 GridSearchCV 定义参数网格
param_grid = {
'classifier__n_estimators': [100, 200],
'classifier__max_depth': [5, 10]
}
# GridSearchCV 调优超参数并自动处理预处理
grid_search = GridSearchCV(pipeline, param_grid, cv=5)
# 拆分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 拟合模型
grid_search.fit(X_train, y_train)
# 检查最佳参数和分数
print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳交叉验证分数: {grid_search.best_score_}")
# 在测试数据上评估
print(f"测试集分数: {grid_search.score(X_test, y_test)}")
### 关键点
1. **管道**:通过将
ColumnTransformer
与模型结合在管道中,您可以确保:– 转换器(
preprocessor
)仅应用于交叉验证的每个折叠中的训练数据。– 转换器在每个折叠中都从头开始重新拟合。
2. **GridSearchCV**:它将搜索不同的超参数,每次都将
ColumnTransformer
拟合到训练数据,并将模型应用于验证集,从而确保正确的交叉验证。### 针对不同数据进行定制
如果您需要将
ColumnTransformer
应用于不同的数据子集(例如,将一个转换器应用于某些特征,将另一个转换器应用于其他特征),您可以调整ColumnTransformer
中的转换器,甚至为每种情况动态创建不同的管道。如果您想了解特定用例的更多详细信息,请告诉我!