Definir o que o carregador deve entregar
Escreva o contrato de saída antes de otimizar: número de elementos, tipo de cada campo, dimensões, intervalo dos alvos e regra para entradas incompletas. Distinga o identificador do exemplo de sua posição em um batch. Uma transformação pode alterar uma forma ou filtrar uma entrada; o programa de treinamento precisa saber se isso é permitido.
Pegue uma amostra representativa incluindo um arquivo comum, um caso limite e o último elemento do conjunto. Abra cada elemento com exatamente a mesma preparação que o Dataset. Depois examine a montagem deles. Um acesso individual bem-sucedido não prova que vários resultados podem ser empilhados. Para texto, documente padding e máscara; para imagem, canais, dimensões e ordem dos eixos.
Fixe o escopo: dados locais ou remotos, decodificação incluída ou não, transformações fixas ou aleatórias. Mantenha-o entre dois ajustes; um ganho aparente pode vir de um trabalho removido.
Voltar a um processo para ler o erro
Reproduza primeiro com num_workers=0, shuffle=False e um batch pequeno. O carregamento então ocorre no processo principal e o trace do erro geralmente fica mais legível. A documentação do DataLoader recomenda essa possibilidade para depurar. Registre o identificador do elemento que falha antes de sua decodificação, sem copiar seu conteúdo sensível nos logs.
Proceda por separação: acesso bruto, transformação, collate_fn e, por fim, transferência. Se o percurso falha antes da transferência, mexer no CUDA não é a primeira pista. Se trava apenas com vários workers, examine os objetos e recursos transmitidos a esses processos. Compare a primeira iteração e as seguintes: a inicialização dos workers pode explicar uma espera inicial sem caracterizar um problema recorrente.
Um timeout pode tornar uma espera visível, mas não conserta nem uma fonte indisponível nem um worker travado. Guarde a última etapa conhecida e reduza o número de entradas em vez de aumentar esse prazo indefinidamente.
Exemplo trabalhado: três canais esperados, uma imagem diferente
Considere quatro registros didáticos. Os três primeiros fornecem um tensor de forma [3, 16, 16], o quarto [1, 16, 16]. Com um contrato que exige três canais, o quarto elemento deve ser identificado antes do empilhamento. Esse cenário não foi executado aqui; ele descreve um resultado esperado a partir das formas escolhidas.
A função abaixo supõe que cada registro possui os campos id, x e y, que x é um tensor CPU e que y é um índice inteiro. Ela recusa a inconsistência em vez de descartar discretamente a imagem. Para o seu projeto, decida explicitamente se uma imagem monocromática deve ser convertida em três canais ou rejeitada na importação. Essa decisão depende do significado dos dados e do pré-processamento esperado pelo modelo.
Após a correção, os quatro identificadores devem permanecer presentes e o tensor montado deve ter a forma [4, 3, 16, 16]. Adicione uma verificação adaptada aos alvos: uma imagem corretamente dimensionada ainda pode carregar uma anotação inválida.
import torch
from torch.utils.data import DataLoader
def assemble(records):
for item in records:
if tuple(item["x"].shape) != (3, 16, 16):
raise ValueError(f"Forma inesperada para {item['id']}")
return {
"ids": [item["id"] for item in records],
"x": torch.stack([item["x"] for item in records]),
"y": torch.tensor([item["y"] for item in records],
dtype=torch.long),
}
# dataset é o seu Dataset que produz os registros descritos.
# Em um script multiprocessado, crie o loader sob a guarda main.
if __name__ == "__main__":
loader = DataLoader(dataset, batch_size=4, num_workers=0,
shuffle=False, collate_fn=assemble)
iterator = iter(loader)
batch = next(iterator)Reintroduzir os workers sem alterar os dados
Passe de zero para um pequeno número de workers mantendo batch, ordem e transformações. Teste uma época completa e depois uma segunda: alguns erros só aparecem ao reiniciar um iterador ou quando recursos já foram consumidos. Aumentar o paralelismo só é útil se o trabalho de preparação puder de fato avançar em paralelo.
Os métodos de inicialização dependem do sistema e da versão do Python. Com spawn, proteja a entrada do programa com if __name__ == '__main__' e defina Dataset, collate_fn e funções de workers no nível do módulo em vez de em lambdas locais. A documentação dos processos também explica por que locks ou threads herdados podem causar travamentos. Mantenha a inicialização dos acessos própria de cada processo quando a biblioteca exigir.
Para um IterableDataset, verifique a partição entre workers por meio de identificadores: vários trabalhadores não devem consumir cada um todo o mesmo fluxo. Não avalie apenas a quantidade de batches; procure também duplicatas e elementos faltantes.
Medir a espera e a vazão com uma unidade clara
Use duas observações complementares. Um percurso do loader sozinho conta os exemplos preparados durante um intervalo definido. Um percurso integrado examina o que acontece quando o modelo consome esses dados. O primeiro ajuda a isolar a preparação; ele não representa automaticamente a vazão do treinamento.
No seu protocolo, conte os exemplos efetivamente entregues e depois divida pelos segundos decorridos. Declare os trechos excluídos para inicialização, o cache de dados, as transformações e o número de repetições. Guarde os valores de cada passagem em vez de selecionar apenas o melhor. A tabela abaixo é uma folha de registro: nenhum desempenho está preenchido.
Se as formas variarem, um número de exemplos por segundo pode mascarar uma mudança de carga. Adicione a unidade pertinente, como pixels decodificados ou tokens efetivamente preparados, mantendo também os exemplos. Para localizar as esperas no loop completo, nomeie a leitura do próximo batch separadamente do cálculo.
Role a tabela para ler todas as colunas.| Ajuste | Elementos verificados | Duração observada | Conclusão esperada |
|---|---|---|---|
| workers=0 | Identificadores, formas, alvos | A medir em segundos | Referência correta |
| Pequeno número de workers | Mesmo conjunto de entradas | A medir em segundos | Ganho ou custo adicional real |
| Mesmo ajuste, segunda época | Nenhuma perda nem duplicação | A medir em segundos | Efeito da inicialização e dos caches |
Tratar memória, pré-carregamento e transferências separadamente
Os workers e os batches em espera consomem memória do host. Monitore-a durante seu teste antes de concluir que só a VRAM importa. Um pré-carregamento mais profundo pode deslocar a espera enquanto aumenta a ocupação; ele não garante mais resultados por segundo. Reduza primeiro a variável suspeita e compare o mesmo escopo.
pin_memory e as transferências não bloqueantes dizem respeito à passagem de dados para um acelerador. A receita de otimização do PyTorch os apresenta como alavancas a examinar junto com o hardware e a carga. Eles não corrigem uma decodificação errada. Comece com dados na CPU nos workers e depois organize a transferência no processo que comanda o cálculo. O benefício e a sobreposição efetiva devem ser observados, não presumidos.
Se persistent_workers for usado, pense nos recursos e estados mantidos entre duas épocas. Um ajuste satisfatório em um único batch não basta para verificar o fechamento de arquivos ou a renovação da fonte.
Aceitar um ajuste somente se os dados permanecerem corretos
O resultado esperado é um loop que recebe todas as entradas previstas, no escopo escolhido, sem erro silencioso. Compare os identificadores e os alvos antes e depois da otimização. Explique drop_last se você descartar o último batch incompleto. Se as transformações forem aleatórias, controle sua política em vez de exigir uma igualdade de pixels que contradiria essa política.
Mantenha o ajuste mais simples que atende à necessidade medida. Um aumento do número de workers pode não melhorar nada se o armazenamento, a decodificação ou o modelo já impõe um limite. Os recursos de CPU, RAM e armazenamento de um servidor não se deduzem do nome de sua GPU: especifique essas necessidades separadamente ao preparar seu ambiente Kernodeck.