You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Keras训练UNet时大内存数据分批加载问题求助

Keras/PyTorch分批加载大尺寸Bin数据训练UNet解决方案

问题原因

Keras无法直接识别RaggedMmap类型的数据结构,需要将其转换为Keras兼容的数据加载方式,同时实现分批加载以节省内存。


优先方案:Keras兼容的分批加载实现

方案1:用tf.data.Dataset包装RaggedMmap

直接将RaggedMmap的迭代逻辑包装成TensorFlow数据集,Keras可直接识别,自动分批加载并释放内存:

import tensorflow as tf
from mmap_ninja.ragged import RaggedMmap

# 加载内存映射数据
X_train_mmp = RaggedMmap('/kaggle/working/xTreino_mmap')
y_train_cat_mmp = RaggedMmap('/kaggle/working/yTreino_mmap')
X_test_mmp = RaggedMmap('/kaggle/working/xTeste_mmap')
y_test_cat_mmp = RaggedMmap('/kaggle/working/yTeste_mmap')

# 定义训练/验证数据生成器
def train_gen():
    for idx in range(len(X_train_mmp)):
        yield X_train_mmp[idx], y_train_cat_mmp[idx]

def val_gen():
    for idx in range(len(X_test_mmp)):
        yield X_test_mmp[idx], y_test_cat_mmp[idx]

# 转换为tf.data.Dataset,指定输出张量签名
batch_size = 8
train_dataset = tf.data.Dataset.from_generator(
    train_gen,
    output_signature=(
        tf.TensorSpec(shape=X_train_mmp[0].shape, dtype=tf.float32),
        tf.TensorSpec(shape=y_train_cat_mmp[0].shape, dtype=tf.float32)  # 标签类型按需调整
    )
).batch(batch_size).prefetch(tf.data.AUTOTUNE)

val_dataset = tf.data.Dataset.from_generator(
    val_gen,
    output_signature=(
        tf.TensorSpec(shape=X_test_mmp[0].shape, dtype=tf.float32),
        tf.TensorSpec(shape=y_test_cat_mmp[0].shape, dtype=tf.float32)
    )
).batch(batch_size).prefetch(tf.data.AUTOTUNE)

# 启动训练
history = model.fit(
    train_dataset,
    verbose=1,
    epochs=10,
    validation_data=val_dataset,
    shuffle=False  # 如需洗牌,可在generator中添加逻辑或使用dataset.shuffle
)

方案2:自定义Sequence类(更灵活)

如果需要更精细的加载控制(比如直接操作bin文件指针),继承Keras的Sequence类实现自定义分批加载:

from keras.utils import Sequence
import numpy as np

class BinDataLoader(Sequence):
    def __init__(self, x_bin_path, y_bin_path, batch_size, total_samples, sample_shape, dtype=np.float32):
        self.x_path = x_bin_path
        self.y_path = y_bin_path
        self.batch_size = batch_size
        self.total_samples = total_samples
        self.sample_shape = sample_shape
        self.dtype = dtype
        self.sample_bytes = np.prod(sample_shape) * dtype.itemsize
        self.indices = np.arange(total_samples)

    def __len__(self):
        # 计算总批次数
        return int(np.ceil(self.total_samples / self.batch_size))

    def __getitem__(self, idx):
        # 加载当前批次样本
        batch_idx = self.indices[idx*self.batch_size : (idx+1)*self.batch_size]
        batch_x, batch_y = [], []
        
        with open(self.x_path, 'rb') as f_x, open(self.y_path, 'rb') as f_y:
            for i in batch_idx:
                # 定位到样本位置并读取
                f_x.seek(i * self.sample_bytes)
                x = np.frombuffer(f_x.read(self.sample_bytes), dtype=self.dtype).reshape(self.sample_shape)
                batch_x.append(x)
                
                f_y.seek(i * self.sample_bytes)  # 标签字节数不同需调整
                y = np.frombuffer(f_y.read(self.sample_bytes), dtype=self.dtype).reshape(self.sample_shape)
                batch_y.append(y)
        
        return np.array(batch_x), np.array(batch_y)

    def on_epoch_end(self):
        # 每个epoch后洗牌样本(可选)
        np.random.shuffle(self.indices)

# 初始化加载器
batch_size = 8
train_loader = BinDataLoader(
    x_bin_path='/kaggle/working/xTreino.bin',
    y_bin_path='/kaggle/working/yTreino.bin',
    batch_size=batch_size,
    total_samples=10000,  # 替换为实际训练样本数
    sample_shape=(256,256,3)  # 替换为你的样本形状
)

val_loader = BinDataLoader(
    x_bin_path='/kaggle/working/xTeste.bin',
    y_bin_path='/kaggle/working/yTeste.bin',
    batch_size=batch_size,
    total_samples=2000,  # 替换为实际验证样本数
    sample_shape=(256,256,3)
)

# 训练模型
history = model.fit(
    train_loader,
    verbose=1,
    epochs=10,
    validation_data=val_loader
)

备选方案:PyTorch分批加载

如果切换到PyTorch,使用Dataset+DataLoader实现分批加载:

import torch
from torch.utils.data import Dataset, DataLoader
from mmap_ninja.ragged import RaggedMmap

class MmapDataset(Dataset):
    def __init__(self, x_mmap, y_mmap):
        self.x_data = x_mmap
        self.y_data = y_mmap

    def __len__(self):
        return len(self.x_data)

    def __getitem__(self, idx):
        x = torch.tensor(self.x_data[idx], dtype=torch.float32)
        y = torch.tensor(self.y_data[idx], dtype=torch.float32)
        return x, y

# 加载数据
X_train_mmp = RaggedMmap('/kaggle/working/xTreino_mmap')
y_train_cat_mmp = RaggedMmap('/kaggle/working/yTreino_mmap')
X_test_mmp = RaggedMmap('/kaggle/working/xTeste_mmap')
y_test_cat_mmp = RaggedMmap('/kaggle/working/yTeste_mmap')

# 创建DataLoader
batch_size = 8
train_loader = DataLoader(MmapDataset(X_train_mmp, y_train_cat_mmp), batch_size=batch_size, shuffle=True)
val_loader = DataLoader(MmapDataset(X_test_mmp, y_test_cat_mmp), batch_size=batch_size)

# 训练循环示例
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)
optimizer = torch.optim.Adam(model.parameters())
criterion = torch.nn.CrossEntropyLoss()

for epoch in range(10):
    model.train()
    total_loss = 0.0
    for batch_x, batch_y in train_loader:
        batch_x, batch_y = batch_x.to(device), batch_y.to(device)
        optimizer.zero_grad()
        outputs = model(batch_x)
        loss = criterion(outputs, batch_y)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    
    model.eval()
    val_loss = 0.0
    with torch.no_grad():
        for batch_x, batch_y in val_loader:
            batch_x, batch_y = batch_x.to(device), batch_y.to(device)
            outputs = model(batch_x)
            val_loss += criterion(outputs, batch_y).item()
    
    print(f"Epoch {epoch+1} | Train Loss: {total_loss/len(train_loader):.4f} | Val Loss: {val_loss/len(val_loader):.4f}")

内容的提问来源于stack exchange,提问作者Lud Dias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:02:34