引言
在进行光谱数据建模前,通常需要将数据集划分为训练集和测试集,以评估模型的性能。本文总结了三种常用的高光谱数据集划分方法:随机划分、SPXY算法划分和Kennard-Stone算法划分,并给出一个整合好的Python代码。
数据集准备
在进行数据集划分之前,我们需要先准备好待划分的数据集。这里我们使用一个包含光谱数据和对应标签的CSV文件作为示例数据集。数据集的格式如下:
波段1, 波段2, ..., 波段n, 标签
接着,使用Pandas库读取CSV文件,并将数据转换为NumPy数组的形式:
import pandas as pd import numpy as np # 读取CSV文件 data = pd.read_csv(r"数据集.csv") # 转换为NumPy数组 data = np.array(data) # 提取特征和标签 data_x = data[0:, :-1] data_y = data[0:, -1]
随机划分
随机划分是最简单的数据集划分方法,它按照指定的比例随机将样本划分为训练集和测试集。以下是随机划分的Python实现:
from sklearn.model_selection import train_test_split def random(data, label, test_ratio=0.3, random_state=123): """ 随机划分数据集 参数: data: 特征数据,shape为(n_samples, n_features) label: 标签数据,shape为(n_samples,) test_ratio: 测试集比例,默认为0.3 random_state: 随机种子,默认为123 返回: X_train: 训练集特征,shape为(n_samples, n_features) X_test: 测试集特征,shape为(n_samples, n_features) y_train: 训练集标签,shape为(n_samples,) y_test: 测试集标签,shape为(n_samples,) """ X_train, X_test, y_train, y_test = train_test_split(data, label, test_size=test_ratio, random_state=random_state) return X_train, X_test, y_train, y_test
在上述代码中,我们使用了sklearn.model_selection模块提供的train_test_split函数实现随机划分。该函数的主要参数包括:
- data: 特征数据,shape为(n_samples, n_features)
- label: 标签数据,shape为(n_samples,)
- test_size: 测试集样本数量的比例,默认为0.25
- random_state: 随机种子,用于控制样本洗牌的随机性
SPXY算法划分
SPXY算法是一种基于样本相似度和标签相似度的数据集划分方法。它通过最小化训练集样本之间的距离和标签差异来选择具有代表性的样本作为训练集。以下是SPXY算法的Python实现:
import numpy as np def spxy(data, label, test_size=0.3): """ SPXY算法划分数据集 参数: data: 特征数据,shape为(n_samples, n_features) label: 标签数据,shape为(n_samples,) test_size: 测试集比例,默认为0.3 返回: X_train: 训练集特征,shape为(n_samples, n_features) X_test: 测试集特征,shape为(n_samples, n_features) y_train: 训练集标签,shape为(n_samples,) y_test: 测试集标签,shape为(n_samples,) """ # 备份数据 x_backup = data y_backup = label # 样本数量 M = data.shape[0] # 训练样本数量 N = round((1 - test_size) * M) # 样本索引 samples = np.arange(M) # 标准化标签 label = (label - np.mean(label)) / np.std(label) # 初始化距离矩阵 D = np.zeros((M, M)) Dy = np.zeros((M, M)) # 计算样本之间的欧氏距离和标签差异 for i in range(M - 1): xa = data[i, :] ya = label[i] for j in range((i + 1), M): xb = data[j, :] yb = label[j] D[i, j] = np.linalg.norm(xa - xb) Dy[i, j] = np.linalg.norm(ya - yb) # 归一化距离矩阵 Dmax = np.max(D) Dymax = np.max(Dy) D = D / Dmax + Dy / Dymax # 选择距离最远的两个样本作为初始训练样本 maxD = D.max(axis=0) index_row = D.argmax(axis=0) index_column = maxD.argmax() m = np.zeros(N) m[0] = index_row[index_column] m[1] = index_column m = m.astype(int) dminmax = np.zeros(N) dminmax[1] = D[m[0], m[1]] # 迭代选择训练样本 for i in range(2, N): pool = np.delete(samples, m[:i]) dmin = np.zeros(M - i) for j in range(M - i): indexa = pool[j] d = np.zeros(i) for k in range(i): indexb = m[k] if indexa < indexb: d[k] = D[indexa, indexb] else: d[k] = D[indexb, indexa] dmin[j] = np.min(d) dminmax[i] = np.max(dmin) index = np.argmax(dmin) m[i] = pool[index] # 测试集索引 m_complement = np.delete(np.arange(data.shape[0]), m) # 划分训练集和测试集 X_train = data[m, :] y_train = y_backup[m] X_test = data[m_complement, :] y_test = y_backup[m_complement] return X_train, X_test, y_train, y_test
SPXY算法的主要步骤如下:
- 计算样本之间的欧氏距离和标签差异,构建距离矩阵D和标签差异矩阵Dy。
- 对距离矩阵进行归一化处理。
- 选择距离最远的两个样本作为初始训练样本。
- 迭代选择距离已选训练样本最远的样本作为新的训练样本,直到达到指定的训练样本数量。
- 将未被选为训练样本的样本作为测试样本。
Kennard-Stone算法划分
Kennard-Stone算法是另一种经典的数据集划分方法。它的思想是通过最大化训练样本之间的最小距离来选择具有代表性的样本作为训练集。以下是Kennard-Stone算法的Python实现:
def ks(data, label, test_size=0.3): """ Kennard-Stone算法划分数据集 参数: data: 特征数据,shape为(n_samples, n_features) label: 标签数据,shape为(n_samples,) test_size: 测试集比例,默认为0.3 返回: X_train: 训练集特征,shape为(n_samples, n_features) X_test: 测试集特征,shape为(n_samples, n_features) y_train: 训练集标签,shape为(n_samples,) y_test: 测试集标签,shape为(n_samples,) """ # 样本数量 M = data.shape[0] # 训练样本数量 N = round((1 - test_size) * M) # 样本索引 samples = np.arange(M) # 初始化距离矩阵 D = np.zeros((M, M)) # 计算样本之间的欧氏距离 for i in range((M - 1)): xa = data[i, :] for j in range((i + 1), M): xb = data[j, :] D[i, j] = np.linalg.norm(xa - xb) # 选择距离最远的两个样本作为初始训练样本 maxD = np.max(D, axis=0) index_row = np.argmax(D, axis=0) index_column = np.argmax(maxD) m = np.zeros(N) m[0] = np.array(index_row[index_column]) m[1] = np.array(index_column) m = m.astype(int) dminmax = np.zeros(N) dminmax[1] = D[m[0], m[1]] # 迭代选择训练样本 for i in range(2, N): pool = np.delete(samples, m[:i]) dmin = np.zeros((M - i)) for j in range((M - i)): indexa = pool[j] d = np.zeros(i) for k in range(i): indexb = m[k] if indexa < indexb: d[k] = D[indexa, indexb] else: d[k] = D[indexb, indexa] dmin[j] = np.min(d) dminmax[i] = np.max(dmin) index = np.argmax(dmin) m[i] = pool[index] # 测试集索引 m_complement = np.delete(np.arange(data.shape[0]), m) # 划分训练集和测试集 X_train = data[m, :] y_train = label[m] X_test = data[m_complement, :] y_test = label[m_complement] return X_train, X_test, y_train, y_test
Kennard-Stone算法的主要步骤如下:
- 计算样本之间的欧氏距离,构建距离矩阵D。
- 选择距离最远的两个样本作为初始训练样本。
- 迭代选择距离已选训练样本最近的样本作为新的训练样本,直到达到指定的训练样本数量。
- 将未被选为训练样本的样本作为测试样本。
示例应用
下面我们使用一个示例数据集演示如何应用上述三种数据集划分方法:
# 随机划分 X_train, X_test, y_train, y_test = random(data_x, data_y, test_ratio=0.3) print("随机划分结果:") print("X_train shape:", X_train.shape) print("X_test shape:", X_test.shape) print("y_train shape:", y_train.shape) print("y_test shape:", y_test.shape) # SPXY算法划分 X_train, X_test, y_train, y_test = spxy(data_x, data_y, test_size=0.3) print("SPXY算法划分结果:") print("X_train shape:", X_train.shape) print("X_test shape:", X_test.shape) print("y_train shape:", y_train.shape) print("y_test shape:", y_test.shape) # Kennard-Stone算法划分 X_train, X_test, y_train, y_test = ks(data_x, data_y, test_size=0.3) print("Kennard-Stone算法划分结果:") print("X_train shape:", X_train.shape) print("X_test shape:", X_test.shape) print("y_train shape:", y_train.shape) print("y_test shape:", y_test.shape)
运行后输出结果如下:
随机划分结果: X_train shape: (700, 200) X_test shape: (300, 200) y_train shape: (700,) y_test shape: (300,) SPXY算法划分结果: X_train shape: (700, 200) X_test shape: (300, 200) y_train shape: (700,) y_test shape: (300,) Kennard-Stone算法划分结果: X_train shape: (700, 200) X_test shape: (300, 200) y_train shape: (700,) y_test shape: (300,)
从输出结果可以看出,三种方法都成功将数据集划分为了训练集和测试集,且样本数量符合预期。
结语
本文介绍了光谱数据集划分的三种常用方法:随机划分、SPXY算法划分和Kennard-Stone算法划分,并给出了相应的Python实现代码。这些方法可以帮助我们合理地划分数据集,有助后续的模型训练、建立和评估。在实际应用中,我们可以根据具体数据集来选择适合的划分方法。