数据处理
- 数据收集:原始列表和标签
- 数据划分:训练集,验证集,测试集
- 数据读取:DataLoader
- Samper(生成索引,也就是序号)→Index
- DataSet(根据索引来读取标签)→Img,Label
- 数据预处理:transforms/中心化/标准化/归一化等
数据集的一般类型:
CSV文件是指逗号分隔值文件格式,分别有expression和annotation两个数据文件中,expression文件中每一列为一个样本,每一行为一个基因标签;annotation文件中每一行是一个样本,标签只有两个值.CSV格式的expression文件是生物信息学研究中用于存储和分析基因表达数据的重要工具。这些文件的结构相对简单,但它们包含的信息对于理解基因如何在不同条件下调控生物过程至关重要
OVC格式的数据集:一张图片对应一个xml文件,xml是一种可扩展标记语言,里面记录了图片中的详细信息,其中最重要的是object标签记录的内容,记录了划分标签的重点,比如dog或者person. 数据集中分别游annotation文件夹和imageset文件夹
*文件夹操作:
1.os.walk()
是 Python 中用于遍历文件夹及其子文件夹的一个非常方便的函数。它返回一个生成器,允许你递归地遍历目录结构,并获取每个目录中的文件和子目录的信息。
import os
dir = 'your dir'
for root,dir,_ in os.walk(dir): # 对于os.walk的for循环命名的参数变量分别是:根目录,子目录(列表),文件(列表)
for files in _: # 对文件的操作
...
for dirs in dir: # 对子目录的操作
...
2.os.listdir()
是 Python 标准库中的一个函数,用于返回指定目录中的文件和子目录的列表。这个函数不会递归地遍历子目录,它只会返回指定目录下的直接子目录和文件的名称列表,不包含完整的路径信息。
3.快速筛选出你需要的文件名或元素
filted = list(filter(lambda x:x.endwith('jpg'),your_dir))
"""
filter()函数接受两个参数:一个函数和一个可迭代对象(在这里是img_names列表)。
lambda x: x.endswith('.jpg')是一个匿名函数,它接受一个参数x,并返回True或False,表示x是否以'.jpg'结尾.
filter()函数会遍历 img_names列表中的每个元素,并将每个元素传递给lambda函数.只有 lambda 函数返回True的元素会被保留在结果中.
最后,list() 函数将filter()函数的结果转换为一个列表,即只包含以 '.jpg' 结尾的元素的新列表。
"""
4.append
list.append((your data))
"""
.append() 方法可以用于向列表(list)中添加各种数据类型的元素,包括数字、字符串、列表、元组、字典等等。没有特定的数据类型限制。
"""
DataLoader和Dataset
Dataloader是pytorch数据读取的核心
DataLoader:
DataLoader(dataset, # Dataset类,决定数据从哪读取以及如何读取
batch_size=1, # 批大小(每次进行一个循环都是从dataset里获取一个batchsize的数据)
shuffle=False, # 每个epoch是否乱序
sampler=None, # 定义了从数据集中抽取样本的策略。如果指定了sampler,shuffle 必须为 False
batch_sampler=None, # 类似于 sampler,但它一次性返回一个批次的索引。如果 batch_sampler 被指定,那么 batch_size、shuffle、sampler 和 drop_last 将不会被使用
num_workers=0, # 是否多进程读取数据(减少时间,加速训练)
collate_fn=None, # 用于把多个数据样本拼接成一个批次数据的函数。可以自定义以处理复杂的数据组合逻辑。
pin_memory=False, # 如果设置为 True,数据加载器会在返回之前将数据复制到 CUDA 的固定内存中。这样做有助于更快地将数据移动到 GPU
drop_last=Flase, # 当样本数不能被batchsize整除时,是否舍弃最后一批数据
timeout=0, # 用于收集一个批次数据的超时时间。这只在 num_workers > 0 时有用
worker_init_fn=None, # 如果提供,每个 worker 子进程在开始处理之前会调用这个函数。这可以用于设置每个 worker 的随机种子或进行其他初始化。
multiprocessing_context=None)
"""功能:构建可迭代的数据装载器"""
Epoch:所有训练样本都已输入到模型中,称为一个Epoch
Iteration:一批样本输入到模型中,称之为一个Iteration
Batchsize:决定一个Epoch有多少个Iteration,称之为批大小
也就是说在每次epoch循环时,都会循环加载完成一次batch
Dataset类:
class Dataset(object):
def __getitem__(self,index):
raise NotImplementedError
def __add__(self,other):
return ConcatDataset([self,other])
"""Dataset抽象类,所有自定义的Dataset需要继承它,并且复写__getitem__()
getitem:接受一个索引,返回一个样本"""
pytorch的数据读取步骤:
1.读取数据
import os
"""
定义一个得到文件和标签的函数
"""
root_dir = r'C:\Users\10477\Desktop\DataLoader\RMB_data' # 定义根目录
rmb_label = {"1": 0, "100": 1} # 定义一个字典,用来二分类
def get_data_info():
data_info = list() # 新建一个列表用来保存data_info
for root, dirs, file in os.walk(root_dir): # os.walk函数来遍历文件夹下的每一个文件
for sub_dir in dirs: # 在子文件夹中进行图像清理,确保文件都是图像文件
img_name = os.listdir(os.path.join(root, sub_dir))
img_names = list(filter(lambda x: x.endswith('.jpg'), img_name)) # 注意filter的用法
for i in range(len(img_names)): # 在每个子文件夹下将文件的路径和标签进行拼接
imgs_name = img_names[i]
path = os.path.join(root, sub_dir, imgs_name)
label = rmb_label[sub_dir]
data_info.append((path, int(label))) # 这里label最好转换为整形,因为字典传出的默认是字符串
return(data_info)
2.创建dataset类
__init__:读取数据
__getitem__:按索引返回值
__len__:返回样本数量
import os # 导入os模块,用于文件和目录操作
from torch.utils.data import Dataset # 从torch.utils.data导入Dataset类,用于创建自定义数据集
from PIL import Image # 导入PIL库中的Image模块,用于图像处理
# 定义一个名为RMB_dataset的类,继承自Dataset
class RMB_dataset(Dataset):
# 构造函数,初始化数据集
def __init__(self, data_dir, transform=None):
self.label_name = {"1": 100, "0": 1} # 定义标签名字典,将字符串标签映射到数值标签
self.data_info = self.get_info(data_dir) # 调用get_info方法获取数据集的路径和标签信息
self.transform = transform # 存储传入的图像转换方法
# 重写__getitem__方法,用于按索引获取数据
def __getitem__(self, index):
img_path, label = self.data_info[index] # 获取对应索引的图像路径和标签
img = Image.open(img_path).convert('RGB') # 打开图像并转换为RGB格式
print(label) # 打印标签
return img, label # 返回图像和标签
# 重写__len__方法,返回数据集的长度
def __len__(self):
return len(self.data_info) # 返回数据信息列表的长度
# 定义一个静态方法get_info,用于从给定的根目录中获取所有图像的信息
@staticmethod
def get_info(root_dir):
rmb_label = {"1": 0, "100": 1} # 定义标签映射字典
data_info = list() # 初始化一个空列表,用于存储图像信息
for root, dirs, file in os.walk(root_dir): # 遍历根目录
for sub_dir in dirs: # 遍历子目录
img_name = os.listdir(os.path.join(root, sub_dir)) # 获取子目录中的文件列表
img_names = list(filter(lambda x: x.endswith('.jpg'), img_name)) # 筛选出jpg格式的图像
for i in range(len(img_names)): # 遍历所有jpg图像
imgs_name = img_names[i] # 获取图像名称
path = os.path.join(root, sub_dir, imgs_name) # 构造图像的完整路径
label = rmb_label[sub_dir] # 获取图像的标签
data_info.append((path, int(label))) # 将路径和标签作为元组添加到列表中
return data_info # 返回包含图像路径和标签的列表
# 测试代码
data_path = r'C:\Users\10477\Desktop\DataLoader\RMB_data' # 定义数据集路径
RMB = RMB_dataset(data_path) # 创建RMB_dataset实例
item = RMB.__getitem__(1) # 获取第二个数据项
print(item) # 打印该数据项
length = RMB.__len__() # 获取数据集长度
print(length) # 打印数据集长度
3.数据集的切分
`os.path.join(上一级目录,下一级目录)
import os
import shutil
import random
# ------------------------------------------------------------------------------
# 1.set your data name
your_data_name = "RMB_data"
# 2.Set the percentage for each of your datasets
train_percent = 0.8
val_percent = 0.1
test_percent = 0.1
# ------------------------------------------------------------------------------
def make_a_new_dir(new_dir):
# 检查目录是否存在
if not os.path.exists(new_dir):
# 如果不存在,创建新目录
os.makedirs(new_dir)
print(f"create {new_dir} success")
# 定义源数据路径
source = os.path.join("data", your_data_name)
# 定义分割数据的路径
split_path = os.path.join("data", "split_data")
# 定义训练集、测试集和验证集的路径
train = os.path.join(split_path, "train")
test = os.path.join(split_path, "test")
val = os.path.join(split_path, "val")
# 设置随机种子,确保结果可复现
random.seed(1)
# 遍历源数据目录
for root, dirs, files in os.walk(source):
for sub_dirs in dirs:
# 获取子目录中的图片
img = os.path.join(root, sub_dirs)
img = os.listdir(img)
# 过滤出jpg图片
img = list(filter(lambda x: x.endswith('.jpg'), img))
# 随机打乱图片
random.shuffle(img)
# 计算图片数量
img_length = len(img)
# 根据比例计算训练集数量
train_num = int(img_length * train_percent)
# 计算验证集数量
val_num = int(img_length * val_percent + train_num)
# 分配图片到不同的集合
for i in range(img_length):
if i <= train_num:
destination = os.path.join(train, str(sub_dirs))
elif i <= val_num:
destination = os.path.join(val, str(sub_dirs))
else:
destination = os.path.join(test, str(sub_dirs))
# 创建目标目录
make_a_new_dir(destination)
# 构建源图片和目标图片的路径
source_img = os.path.join(source, sub_dirs, img[i])
destination_img = os.path.join(destination, img[i])
print(f"source:{source_img},destination:{destination_img}")
# 复制图片到目标目录
shutil.copy(source_img, destination)
4.dataloader的载入
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
from DATASETS import RMBDataset
test_path = r"data\split_data\test"
val_path = r"data\split_data\val"
train_path = r"data\split_data\train"
batch_size = 16
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]
train_transform = transforms.Compose([
transforms.Resize((32, 32)),
transforms.RandomCrop(32, padding=4),
# 这个转换首先在图像的边缘添加4个像素的填充,然后从填充后的图像中随机裁剪出32x32像素的区域。
# 这种随机裁剪是一种数据增强技术,可以提高模型对位置变化的鲁棒性
transforms.ToTensor(),
# 将图像转换为PyTorch张量。这通常涉及改变维度顺序(比如从HWC到CHW,即从高度x宽度x通道数到
# 通道数x高度x宽度)并且将数据类型从PIL.Image或者numpy数组转换为torch.Tensor
transforms.Normalize(norm_mean, norm_std)
# 这个转换对图像进行标准化,使用指定的均值(norm_mean)和标准差(norm_std)。标准化是通过减
# 去均值并除以标准差来实现的。这种操作通常用于帮助神经网络更好地学习和收敛。
])
val_transform = transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Normalize(norm_mean, norm_std)
])
"""
问:对图像进行标准化后不就改变图像原本的样子了吗?
答:对图像进行标准化确实会改变图像的原始像素值,但这是为了促进神经网络的训练和性能。下面解释了为
什么这样做以及它如何影响图像:
1.统一数据范围:未标准化的图像数据可能会有非常不同的值范围,这可能导致训练过程中的不稳定性
和收敛问题。通过标准化,我们确保网络在所有输入上工作
在相似的数值范围。
2.加速训练:标准化后的数据有助于加速梯度下降的收敛过程,因为它确保了特征在不同尺度上的一致
性。这意味着更少的训练周期可能就能达到较好的性能。
3.消除偏差:如果某个颜色通道普遍比其他通道高或低,未经处理的数据可能会引入偏差。标准化有助
于消除这种偏差,使得模型不会因为输入数据的绝对大小而
受到影响。
虽然标准化改变了图像的像素值,但这种改变对于人类视觉可能是显著的(例如,颜色可能会看起来不同),
但对于模型来说并不重要。模型关心的是能从数据中学习到
的模式和特征,而不是原始像素值。实际上,标准化后的数据更适合模型处理和学习,因此通常会提高模型
的性能。
"""
train_data = RMBDataset(train_path, transform=train_transform)
val_data = RMBDataset(test_path, transform=val_transform)
train_Loader = DataLoader(train_data, batch_size, shuffle=True, num_workers=2, )
val_Loader = DataLoader(val_data, batch_size)