PowerMachine commited on
Commit
ba9b564
·
verified ·
1 Parent(s): a00f08d

Upload cnn_bigru/utils/memory_optimizer.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. cnn_bigru/utils/memory_optimizer.py +139 -0
cnn_bigru/utils/memory_optimizer.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """memory_optimizer.py — Otimização central de memória (CPU + GPU).
2
+
3
+ Adaptado de xavante_work/xavante/utils/memory_optimizer.py, com:
4
+ 1. gc.collect() explícito em checkpoints
5
+ 2. torch.cuda.empty_cache() (se CUDA)
6
+ 3. AMP (bfloat16/fp16) para reduzir VRAM
7
+ 4. Gradient checkpointing
8
+ 5. CPU offload de parâmetros congelados
9
+ 6. Pinned memory para transferência async
10
+ 7. set_per_process_memory_fraction (CUDA)
11
+
12
+ Matemática:
13
+ M_total = M_params + M_grads + M_activations + M_optimizer_state
14
+ Com AMP: M_params *= 0.5, M_grads *= 0.5, M_activations *= 0.5
15
+ Com gradient checkpointing: M_activations *= 1/sqrt(L)
16
+ """
17
+ from __future__ import annotations
18
+
19
+ import gc
20
+ import logging
21
+ from contextlib import contextmanager
22
+ from typing import Iterator
23
+
24
+ import torch
25
+ import torch.nn as nn
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class MemoryOptimizer:
31
+ """Centraliza otimização de memória para treino e inferência."""
32
+
33
+ def __init__(self, vram_fraction: float = 0.85, enable_amp: bool = True):
34
+ self.vram_fraction = vram_fraction
35
+ self.enable_amp = enable_amp
36
+ self._peak_memory_mb: float = 0.0
37
+
38
+ def configure(self) -> None:
39
+ """Configura PyTorch para uso otimizado de memória."""
40
+ try:
41
+ torch.backends.cudnn.benchmark = True
42
+ except Exception:
43
+ pass
44
+ try:
45
+ torch.set_float32_matmul_precision("high")
46
+ except Exception:
47
+ pass
48
+ if torch.cuda.is_available():
49
+ try:
50
+ torch.cuda.set_per_process_memory_fraction(self.vram_fraction)
51
+ logger.info("VRAM limitada a %.0f%%", self.vram_fraction * 100)
52
+ except Exception as e:
53
+ logger.warning("set_per_process_memory_fraction falhou: %s", e)
54
+
55
+ @staticmethod
56
+ def cleanup() -> None:
57
+ """Limpa memória agressivamente (gc + empty_cache)."""
58
+ gc.collect()
59
+ if torch.cuda.is_available():
60
+ torch.cuda.empty_cache()
61
+ torch.cuda.synchronize()
62
+
63
+ @staticmethod
64
+ def get_memory_mb() -> dict:
65
+ """Retorna uso de memória em MB."""
66
+ if torch.cuda.is_available():
67
+ return {
68
+ "device": "cuda",
69
+ "allocated_mb": torch.cuda.memory_allocated() / 1024**2,
70
+ "cached_mb": torch.cuda.memory_reserved() / 1024**2,
71
+ "max_allocated_mb": torch.cuda.max_memory_allocated() / 1024**2,
72
+ }
73
+ try:
74
+ import psutil
75
+ mem = psutil.virtual_memory()
76
+ return {
77
+ "device": "cpu",
78
+ "total_mb": mem.total / 1024**2,
79
+ "available_mb": mem.available / 1024**2,
80
+ "used_mb": mem.used / 1024**2,
81
+ "percent": mem.percent,
82
+ }
83
+ except ImportError:
84
+ return {"device": "cpu", "info": "psutil not available"}
85
+
86
+ @contextmanager
87
+ def zero_grad_context(self, model: nn.Module) -> Iterator[None]:
88
+ """Context manager que limpa gradientes ao sair."""
89
+ try:
90
+ yield
91
+ finally:
92
+ model.zero_grad(set_to_none=True)
93
+ self.cleanup()
94
+
95
+ @staticmethod
96
+ def enable_gradient_checkpointing(model: nn.Module) -> None:
97
+ """Tenta ativar gradient checkpointing."""
98
+ if hasattr(model, "gradient_checkpointing_enable"):
99
+ try:
100
+ model.gradient_checkpointing_enable()
101
+ logger.info("Gradient checkpointing ativado")
102
+ return
103
+ except Exception as e:
104
+ logger.warning("gradient_checkpointing_enable falhou: %s", e)
105
+ logger.info("Modelo não suporta gradient checkpointing nativo")
106
+
107
+ @staticmethod
108
+ def cpu_offload_constrained(model: nn.Module) -> int:
109
+ """Move parâmetros sem requires_grad para CPU."""
110
+ n_offloaded = 0
111
+ for p in model.parameters():
112
+ if not p.requires_grad and p.device.type != "cpu":
113
+ p.data = p.data.cpu()
114
+ n_offloaded += p.numel()
115
+ if n_offloaded > 0:
116
+ logger.info("Offloaded %d params para CPU", n_offloaded)
117
+ if torch.cuda.is_available():
118
+ torch.cuda.empty_cache()
119
+ return n_offloaded
120
+
121
+ def amp_context(self, device_type: str = "cpu"):
122
+ """Context manager para mixed precision."""
123
+ if not self.enable_amp or device_type == "cpu":
124
+ from contextlib import nullcontext
125
+ return nullcontext()
126
+ try:
127
+ return torch.amp.autocast(device_type=device_type, dtype=torch.bfloat16)
128
+ except Exception:
129
+ from contextlib import nullcontext
130
+ return nullcontext()
131
+
132
+ @staticmethod
133
+ def report_peak() -> float:
134
+ if torch.cuda.is_available():
135
+ return torch.cuda.max_memory_allocated() / 1024**2
136
+ return 0.0
137
+
138
+
139
+ __all__ = ["MemoryOptimizer"]