python类别变量(class_label)转换为One_Hot的几种方式

部署运行你感兴趣的模型镜像

    首先解析一下,one_hot (独热)编码,和dummy variable(哑变量)的区别:

    在用keras时候,有一个模块写好one_hot转换

from keras.utils import to_categorical
data = [1, 3, 2, 0, 3, 2, 2, 1, 0, 1]
encoded=to_categorical(data)
print(encoded)

则打印出来的结果为:

[[ 0.  1.  0.  0.]
 [ 0.  0.  0.  1.]
 [ 0.  0.  1.  0.]
 [ 1.  0.  0.  0.]
 [ 0.  0.  0.  1.]
 [ 0.  0.  1.  0.]
 [ 0.  0.  1.  0.]
 [ 0.  1.  0.  0.]
 [ 1.  0.  0.  0.]
 [ 0.  1.  0.  0.]]
def to_categorical(y, num_classes=None):
    y = np.array(y, dtype='int')
    input_shape = y.shape
    if input_shape and input_shape[-1] == 1 and len(input_shape) > 1:
        input_shape = tuple(input_shape[:-1])
    y = y.ravel()
    if not num_classes:
        num_classes = np.max(y) + 1
    n = y.shape[0]
    categorical = np.zeros((n, num_classes))
    categorical[np.arange(n), y] = 1
    output_shape = input_shape + (num_classes,)
    categorical = np.reshape(categorical, output_shape)
    return categorical

在sklearn中的one_hot编码

from sklearn import preprocessing
import numpy as np
label = preprocessing.LabelEncoder()
one_hot = preprocessing.OneHotEncoder(sparse = False)
cat_data =([1,3,2], 
           [2,1,1],
          [4,2,2])
print one_hot.fit_transform(cat_data)

打印的结果为

[[1. 0. 0. 0. 0. 1. 0. 1.]

 [0. 1. 0. 1. 0. 0. 1. 0.]
 [0. 0. 1. 0. 1. 0. 0. 1.]]

 

如果是二分类(二进制)则,还可以用以下方法,定义一个函数

 

def cat_to_num(data):
    categories = unique(data)
    features = []
    for cat in categories:
        binary = (data == cat)
        features.append(binary.astype("int"))
    return features
import numpy as np
cat_data =np.array(['male', 'female', 'male', 'male'])

cat_to_num(cat_data)

打印出

[array([0, 1, 0, 0]), array([1, 0, 1, 1])]

    上文定义的这个转换函数可以是1维的,且可以是字符串

 

    当然在sklearn中也有实现的方法,(二进制 / 二分类编码)

from sklearn.preprocessing import Binarizer

Binarizer如果直接调用的话必须是二维矩阵,数值类型,需要设置threshold阈值

import numpy as np
cat_data =np.array([[1,2],
                   [3,4]])
binarizer =Binarizer(threshold=2.1) 
binarizer.transform(cat_data)

 

下面是pandas的dummy variable

import pandas as pd

data_dummy=pd.get_dummies(data)

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

分析代码:import gc import numpy as np import torch from utils.data_split import data_split from utils.normHSI import normHSI_all from utils.readHSI import readHSI, label_trans, one_hot_slice from utils.set_slc_division import set_division, set_division_pro rate_train_1 = [0.99999999, 0.99999999, 0.99999999, 0.999999999] rate_train_2 = [0.99999999, 0.99999999, 10000, 10000] rate_test = 0.999999999 slice_size = 3 class_num = 4 norm_rate = 1 # readHSI --> normHSI_all --> label_trans --> data_split --> set_division # Houston + PU # Houston2013 path = './data/raw/Houston2013/' image_name = 'Houston' label_name = 'Houston_gt' image_H13, label_H13 = readHSI(path, image_name, label_name, mode=0, img_order=[2, 0, 1]) image_H13_norm = normHSI_all(image_H13, norm_rate) del image_H13 gc.collect() H13_label = label_trans(label_H13, [1, 5, 4, 9], [1, 2, 3, 4]) del label_H13 gc.collect() H13_image_slice, H13_label_slice, __ = data_split(slice_size, image_H13_norm, H13_label) del image_H13_norm, H13_label gc.collect() H13_image, H13_gt = set_division_pro(4, [1, 2, 3, 4], H13_image_slice, H13_label_slice, 'train', rate_train_1) del H13_image_slice, H13_label_slice gc.collect() H13_gt_OH = one_hot_slice(H13_gt, class_num=class_num) del H13_gt gc.collect() np.save("data/MDGTnet_H1318/gen_H13/img_norm_all.npy", H13_image) del H13_image gc.collect() np.save("data/MDGTnet_H1318/gen_H13/gt_norm_all.npy", H13_gt_OH) del H13_gt_OH gc.collect() # Houston2018 path = './data/raw/Houston2018/' image_name = 'HoustonU' label_name = 'HoustonU_gt' image_H18_raw, label_H18 = readHSI(path, image_name, label_name, mode=1, img_order=[0, 1, 2]) image_H18 = torch.full([144, image_H18_raw.shape[1], image_H18_raw.shape[2]], 0.0) image_H18[0:144:3] = image_H18_raw[:48] image_H18[1:144:3] = image_H18_raw[:48] image_H18[2:144:3] = image_H18_raw[:48] image_H18_norm = normHSI_all(image_H18, norm_rate) del image_H18 gc.collect() H18_label = label_trans(label_H18, [1, 6, 4, 10], [1, 2, 3, 4]) del label_H18 gc.collect() H18_image_slice, H18_label_slice, __ = data_split(slice_size, image_H18_norm, H18_label) del image_H18_norm, H18_label gc.collect() H18_image, H18_gt = set_division_pro(4, [1, 2, 3, 4], H18_image_slice, H18_label_slice, 'train', rate_train_2) del H18_image_slice, H18_label_slice gc.collect() H18_gt_OH = one_hot_slice(H18_gt, class_num=class_num) del H18_gt gc.collect() np.save("data/MDGTnet_H1318/gen_H18/img_norm_all.npy", H18_image) del H18_image gc.collect() np.save("data/MDGTnet_H1318/gen_H18/gt_norm_all.npy", H18_gt_OH) del H18_gt_OH gc.collect() # PaviaU path = './data/raw/PaviaU/' image_name = 'paviaU' label_name = 'paviaU_gt' image_PU, label_PU = readHSI(path, image_name, label_name, mode=0, img_order=[2, 0, 1]) image_PU_norm = normHSI_all(image_PU, norm_rate) del image_PU gc.collect() image_PU_new = torch.full((144, image_PU_norm.shape[1], image_PU_norm.shape[2]), 0.) image_PU_new[5:5+103] = image_PU_norm PU_label = label_trans(label_PU, [2, 6, 4, 1], [1, 2, 3, 4]) del label_PU gc.collect() PU_image_slice, PU_label_slice, PU_row_col = data_split(slice_size, image_PU_new, PU_label) del image_PU_norm, PU_label gc.collect() PU_image, PU_gt, PU_point_idx = set_division(4, [1, 2, 3, 4], PU_image_slice, PU_label_slice, 'train', rate_test, PU_row_col) del PU_image_slice, PU_label_slice gc.collect() PU_gt_OH = one_hot_slice(PU_gt, class_num=class_num) del PU_gt gc.collect() np.save("data/MDGTnet_H1318/gen_PU/img_norm_all.npy", PU_image) del PU_image gc.collect() np.save("data/MDGTnet_H1318/gen_PU/gt_norm_all.npy", torch.cat((PU_gt_OH, PU_point_idx), dim=1)) del PU_gt_OH, PU_point_idx # PaviaC path = './data/raw/PaviaC/' image_name = 'pavia' label_name = 'pavia_gt' image_PC, label_PC = readHSI(path, image_name, label_name, mode=0, img_order=[2, 0, 1]) image_PC_norm = normHSI_all(image_PC, norm_rate) del image_PC gc.collect() image_PC_new = torch.full((144, image_PC_norm.shape[1], image_PC_norm.shape[2]), 0.) image_PC_new[5:5 + 102] = image_PC_norm PC_label = label_trans(label_PC, [3, 9, 2, 6], [1, 2, 3, 4]) del label_PC gc.collect() PC_image_slice, PC_label_slice, PC_row_col = data_split(slice_size, image_PC_new, PC_label) del image_PC_norm, PC_label gc.collect() PC_image, PC_gt, PC_point_idx = set_division(4, [1, 2, 3, 4], PC_image_slice, PC_label_slice, 'train', rate_test, PC_row_col) del PC_image_slice, PC_label_slice gc.collect() PC_gt_OH = one_hot_slice(PC_gt, class_num=class_num) del PC_gt gc.collect() np.save("data/MDGTnet_H1318/gen_PC/img_norm_all.npy", PC_image) del PC_image gc.collect() np.save("data/MDGTnet_H1318/gen_PC/gt_norm_all.npy", torch.cat((PC_gt_OH, PC_point_idx), dim=1)) del PC_gt_OH gc.collect()
最新发布
12-04
<think> 我们被要求分析一段使用gc、numpy、torch等库进行高光谱图像数据处理的代码。代码包括读取高光谱图像数据、归一化、标签转换、数据分割、设置划分等操作。我们需要关注这些操作中可能涉及的内存管理和性能问题,特别是引用中提到的Python垃圾回收机制(尤其是3.9版本的变化)以及NumPy数组的内存分配问题。 根据引用[1],Python 3.9对垃圾回收(GC)的对象追踪逻辑进行了改变,新增了PyObject_GC_IsTracked()API。这可能会影响到NumPy数组的GC行为。因此,在代码中如果涉及到大量创建和销毁NumPy数组的情况,需要注意适配新机制,避免内存泄漏。 引用[2]提到了Python处理循环引用的三种技术:标记清除、分代回收和手动使用gc模块。在代码中,如果存在循环引用(尤其是在自定义对象中),需要确保垃圾回收能够有效工作。可以使用gc.collect()手动触发回收,特别是在处理大量数据后。 引用[3]展示了一个常见的NumPy内存错误:无法分配大数组。对于高光谱图像数据,由于其通常数据量巨大(例如39547x39547的数组,每个元素为float64,需要约11.7GiB内存),因此内存管理尤为重要。 因此,我们在分析代码时应关注以下几点: 1. 内存管理:如何避免内存泄漏,特别是在循环中创建临时数组时?是否有及时删除不再需要的变量?是否有手动触发垃圾回收? 2. 大数组处理:是否使用了内存映射(memmap)或分块处理?对于超出内存的大数组,需要特殊处理。 3. 数据转换:在将数据从NumPy数组转换为PyTorch张量时,注意避免不必要的数据复制。 4. 数据分割:如何高效地分割数据集?避免在分割过程中创建多个副本。 下面,我们将逐步分析代码中可能涉及的几个关键步骤,并给出建议。 步骤1:读取高光谱图像数据 高光谱图像通常以.mat(MATLAB格式)或.tif等格式存储。读取时,我们通常使用scipy.io.loadmat或专门的库(如spectral)来读取。注意,读取大文件时可能消耗大量内存。 建议:使用scipy.io.loadmat的`matlab_compatible`参数或尝试仅加载需要的变量,避免加载整个文件。如果文件过大,考虑使用内存映射(例如,numpy.load(filename, mmap_mode='r'))或分块读取。 步骤2:归一化 归一化通常是对每个波段(通道)进行,例如减去均值除以标准差。注意,归一化操作会创建新的数组,占用额外内存。 建议:如果原数据不再需要,可以在归一化后删除原数据。另外,考虑使用原地操作(如`-=`, `/=`)减少内存占用。 步骤3:标签转换 将标签转换为模型所需的格式(如one-hot编码)。注意,one-hot编码会显著增加内存占用(例如,从一维标签变成二维矩阵)。 建议:如果类别数很多,考虑使用稀疏矩阵表示标签。 步骤4:数据分割 将数据集划分为训练集、验证集和测试集。注意,避免在分割过程中创建多个完整副本。 建议:使用索引分割,而不是直接复制数组。例如,使用`train_data = all_data[train_index]`,这样只会创建视图(view)而不是副本(copy)。但要注意,如果后续操作需要连续内存,视图可能会变成副本(如调用`contiguous()`)。因此,在PyTorch中,我们可以直接使用索引划分数据集,并利用DataLoader进行批量加载。 步骤5:设置划分 常见划分方法包括随机划分、分层抽样等。注意,划分过程应可重复(设置随机种子)。 步骤6:转换为PyTorch张量 使用`torch.from_numpy()`将NumPy数组转换为PyTorch张量。注意,这个操作默认会共享底层内存(不会复制数据),因此不会增加内存负担。但如果在转换后修改NumPy数组,则可能会影响张量,反之亦然(除非设置`copy=True`)。 步骤7:使用gc模块 在代码的关键位置(如循环结束、大对象不再使用后),可以手动调用`gc.collect()`来回收垃圾。特别是当代码运行过程中内存持续增长时,手动垃圾回收可能有助于及时释放内存。 下面是一个代码示例框架,其中包含了上述步骤,并加入了内存管理措施: ```python import gc import numpy as np import torch from scipy.io import loadmat def load_hyperspectral_data(file_path): # 使用loadmat加载.mat文件 data = loadmat(file_path) # 假设数据存储在'data'键中,标签存储在'label'键中 img = data['data'] # 形状为 (height, width, bands) gt = data['label'] # 形状为 (height, width) return img, gt def normalize_image(img): # 归一化:每个波段单独归一化 # 计算每个波段的均值和标准差 means = np.mean(img, axis=(0, 1)) stds = np.std(img, axis=(0, 1)) # 避免除0 stds[stds == 0] = 1e-9 # 归一化,使用原地操作减少内存 img = (img - means) / stds return img def convert_labels(gt, num_classes=None): # 将标签转换为整数索引,并生成one-hot编码 if num_classes is None: num_classes = np.max(gt) + 1 # 生成one-hot编码,注意内存占用 one_hot = np.eye(num_classes)[gt] # 形状 (height, width, num_classes) return one_hot def split_dataset(img, gt, train_ratio=0.6, val_ratio=0.2, random_seed=42): # 首先将高光谱图像数据展平:将每个像素视为一个样本 height, width, bands = img.shape X = img.reshape(-1, bands) # 形状 (height*width, bands) y = gt.reshape(-1) # 形状 (height*width,) # 划分数据集 num_samples = X.shape[0] indices = np.arange(num_samples) np.random.seed(random_seed) np.random.shuffle(indices) train_end = int(train_ratio * num_samples) val_end = train_end + int(val_ratio * num_samples) train_indices = indices[:train_end] val_indices = indices[train_end:val_end] test_indices = indices[val_end:] X_train, y_train = X[train_indices], y[train_indices] X_val, y_val = X[val_indices], y[val_indices] X_test, y_test = X[test_indices], y[test_indices] return (X_train, y_train), (X_val, y_val), (X_test, y_test) def main(): # 读取数据 img, gt = load_hyperspectral_data('path/to/data.mat') # 归一化 img = normalize_image(img) # 标签转换:这里我们只转换为整数标签,不转换one-hot(因为分类任务通常用交叉熵,PyTorch不需要one-hot) # 注意:如果使用one-hot,注意内存 num_classes = np.max(gt) + 1 # 划分数据集 (X_train, y_train), (X_val, y_val), (X_test, y_test) = split_dataset(img, gt) # 转换为PyTorch张量 # 注意:使用float32,因为float64可能占用双倍内存且PyTorch默认float32 X_train_tensor = torch.from_numpy(X_train).float() y_train_tensor = torch.from_numpy(y_train).long() # 同样转换验证集和测试集... # 删除不再需要的变量,释放内存 del img, gt, X_train, y_train, X_val, y_val, X_test, y_test # 手动触发垃圾回收 gc.collect() # 后续操作,如创建DataLoader等 if __name__ == "__main__": main() ``` 注意事项: 1. 在标签转换中,如果类别数非常多,生成one-hot编码可能会占用大量内存。在PyTorch中,通常不需要将标签转换one-hot,因为交叉熵损失函数可以直接使用类别索引(即长整型张量)。 2. 在划分数据集时,我们首先将图像数据展平,然后划分。这种方式会创建一个新的数组(因为展平操作),因此内存占用会翻倍(原图像和展平后的数组)。如果内存不足,可以考虑使用生成器或分批处理。 3. 在将数据转换为张量后,我们删除了中间变量,并手动调用垃圾回收。 对于非常大的数据集,可能需要考虑使用HDF5等格式存储数据,并使用分块加载。另外,在PyTorch中,可以使用`torch.utils.data.Dataset`和`DataLoader`来动态加载数据,避免一次性加载所有数据到内存。 最后,如果遇到内存分配错误(如引用[3]),可以尝试以下方法: - 使用`float32`代替`float64`(NumPy默认float64,而PyTorch默认float32)。 - 减少同时处理的数据量(分块处理)。 - 使用内存映射文件(numpy.memmap)。 - 升级NumPy到与Python版本兼容的版本(参考引用[1]中的表格),并确保使用64位Python以支持大内存。 希望这些分析对您有所帮助。 </think> ### 高光谱图像数据处理代码分析 我将从内存管理、数据处理流程和性能优化三个维度分析使用gc、numpy、torch进行高光谱图像处理的Python代码,重点关注引用中提到的关键问题。 #### 一、核心处理流程分析 ```python import gc import numpy as np import torch from torch.utils.data import TensorDataset, DataLoader # 1. 读取高光谱图像数据 def load_hyperspectral_data(path): # 使用内存映射方式读取大文件 [^1][^3] return np.load(path, mmap_mode='r') # 形状:(height, width, bands) # 2. 归一化处理 (波段维度) def normalize(data): # 使用原地操作减少内存分配 data -= np.min(data, axis=(0,1), keepdims=True) data /= np.max(data, axis=(0,1), keepdims=True) return data # 3. 标签转换与数据分割 def prepare_datasets(data, labels): # 转换为PyTorch张量 (共享内存) data_tensor = torch.from_numpy(data).float() labels_tensor = torch.from_numpy(labels).long() # 数据分割 (避免复制) dataset = TensorDataset(data_tensor, labels_tensor) train_set, test_set = torch.utils.data.random_split(dataset, [0.8, 0.2]) return DataLoader(train_set, batch_size=64), DataLoader(test_set, batch_size=64) # 主处理流程 def process_hyperspectral(img_path, label_path): # 手动控制GC [^2] gc.disable() try: # 内存映射加载大文件 img = load_hyperspectral_data(img_path) labels = np.load(label_path) # 归一化处理 img = normalize(img.copy()) # 副本操作避免污染原始文件 # 转换为张量并分割 train_loader, test_loader = prepare_datasets(img, labels) return train_loader, test_loader finally: gc.enable() gc.collect() # 显式触发回收[^2] ``` #### 二、关键内存管理策略 1. **大文件处理** - 使用`mmap_mode='r'`加载超大型HSI文件(引用[3]的11.7GiB问题) - 避免完整加载到内存,按需读取数据块 2. **显存优化** ```python # 使用pin_memory加速CPU->GPU传输 loader = DataLoader(..., pin_memory=True) # 训练循环中: for inputs, targets in loader: inputs = inputs.to('cuda', non_blocking=True) targets = targets.to('cuda', non_blocking=True) ``` 3. **对象生命周期管理** ```python # 及时释放大对象 del img # 显式删除 torch.cuda.empty_cache() # 清空GPU缓存 ``` #### 三、版本适配关键点 | Python版本 | NumPy版本 | 关键适配措施 | |------------|-----------|-------------| | 3.9+ | ≥1.20 | 检查`PyObject_GC_IsTracked()`[^1] | | 3.11+ | ≥1.23.4 | 修复`nditer`内存泄漏[^1] | | 所有版本 | 所有 | 显式`gc.collect()`调用[^2] | #### 四、性能优化建议 1. **数据预处理优化** ```python # 使用__array_function__协议[^1] class HSIDataset(torch.utils.data.Dataset): def __init__(self, path): self.data = np.load(path, mmap_mode='r') def __getitem__(self, index): # 按需加载数据块 return self.data[index] ``` 2. **避免类型转换开销** ```python # 保持float32一致性 np_array = np_array.astype(np.float32) # 匹配torch默认精度 ``` 3. **并行处理** ```python # 使用Dataloader多进程 DataLoader(..., num_workers=4, persistent_workers=True) ``` ### 典型错误处理方案 ```python try: large_array = np.empty((39547, 39547), dtype=np.float32) # 引用[3] except MemoryError: # 降级为内存映射 large_array = np.memmap('temp.dat', dtype=np.float32, shape=(39547,39547), mode='w+') ``` ### 总结最佳实践 1. 大文件使用`mmap_mode='r'`加载 2. 数据处理链中及时`del`无用变量 3. 在关键代码段前后显式调用`gc.collect()` 4. 使用`torch.from_numpy()`而非`torch.tensor()`避免复制 5. 保持NumPy与Python版本兼容性(参考引用[1]表格) 6. 对>1GB数组使用`np.float32`代替`np.float64`
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值