lucid-dl 2.7.5__tar.gz → 2.7.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/PKG-INFO +1 -1
  2. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/__init__.py +1 -0
  3. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/module.py +4 -6
  4. lucid_dl-2.7.7/lucid/nn/util.py +60 -0
  5. lucid_dl-2.7.7/lucid/optim/_base.py +142 -0
  6. lucid_dl-2.7.7/lucid/optim/lr_scheduler/_base.py +85 -0
  7. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/port.py +13 -12
  8. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid_dl.egg-info/PKG-INFO +1 -1
  9. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid_dl.egg-info/SOURCES.txt +1 -0
  10. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/setup.py +1 -1
  11. lucid_dl-2.7.5/lucid/optim/_base.py +0 -57
  12. lucid_dl-2.7.5/lucid/optim/lr_scheduler/_base.py +0 -63
  13. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/LICENSE +0 -0
  14. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/README.md +0 -0
  15. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/__init__.py +0 -0
  16. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_backend/__init__.py +0 -0
  17. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_backend/core.py +0 -0
  18. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_backend/metal.py +0 -0
  19. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_func/__init__.py +0 -0
  20. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_func/bfunc.py +0 -0
  21. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_func/gfunc.py +0 -0
  22. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_func/ufunc.py +0 -0
  23. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_tensor/__init__.py +0 -0
  24. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_tensor/tensor.py +0 -0
  25. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_tensor/tensor_ops.py +0 -0
  26. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_util/__init__.py +0 -0
  27. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/_util/func.py +0 -0
  28. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/data/__init__.py +0 -0
  29. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/data/_base.py +0 -0
  30. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/data/_util.py +0 -0
  31. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/datasets/__init__.py +0 -0
  32. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/datasets/_base.py +0 -0
  33. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/datasets/cifar.py +0 -0
  34. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/datasets/mnist.py +0 -0
  35. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/einops/__init__.py +0 -0
  36. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/einops/_func.py +0 -0
  37. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/error.py +0 -0
  38. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/linalg/__init__.py +0 -0
  39. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/linalg/_func.py +0 -0
  40. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/__init__.py +0 -0
  41. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/__init__.py +0 -0
  42. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/alex.py +0 -0
  43. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/coatnet.py +0 -0
  44. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/convnext.py +0 -0
  45. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/crossvit.py +0 -0
  46. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/cspnet.py +0 -0
  47. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/cvt.py +0 -0
  48. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/dense.py +0 -0
  49. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/efficient.py +0 -0
  50. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/efficientformer.py +0 -0
  51. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/inception.py +0 -0
  52. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/inception_next.py +0 -0
  53. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/inception_res.py +0 -0
  54. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/lenet.py +0 -0
  55. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/maxvit.py +0 -0
  56. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/mobile.py +0 -0
  57. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/pvt.py +0 -0
  58. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/resnest.py +0 -0
  59. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/resnet.py +0 -0
  60. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/resnext.py +0 -0
  61. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/senet.py +0 -0
  62. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/sknet.py +0 -0
  63. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/swin.py +0 -0
  64. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/vgg.py +0 -0
  65. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/vit.py +0 -0
  66. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/xception.py +0 -0
  67. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imgclf/zfnet.py +0 -0
  68. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imggen/__init__.py +0 -0
  69. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imggen/ddpm.py +0 -0
  70. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/imggen/vae.py +0 -0
  71. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/__init__.py +0 -0
  72. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/detr.py +0 -0
  73. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/efficientdet.py +0 -0
  74. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/fast_rcnn.py +0 -0
  75. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/faster_rcnn.py +0 -0
  76. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/rcnn.py +0 -0
  77. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/util.py +0 -0
  78. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/yolo/__init__.py +0 -0
  79. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/yolo/yolo_v1.py +0 -0
  80. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/yolo/yolo_v2.py +0 -0
  81. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/yolo/yolo_v3.py +0 -0
  82. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/objdet/yolo/yolo_v4.py +0 -0
  83. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/seq2seq/__init__.py +0 -0
  84. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/seq2seq/transformer.py +0 -0
  85. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/models/util.py +0 -0
  86. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/__init__.py +0 -0
  87. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_activation.py +0 -0
  88. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_attention.py +0 -0
  89. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_conv.py +0 -0
  90. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_drop.py +0 -0
  91. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_linear.py +0 -0
  92. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_loss.py +0 -0
  93. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_norm.py +0 -0
  94. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_pool.py +0 -0
  95. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_spatial.py +0 -0
  96. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/functional/_util.py +0 -0
  97. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/fused.py +0 -0
  98. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/init/__init__.py +0 -0
  99. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/init/_dist.py +0 -0
  100. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/__init__.py +0 -0
  101. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/activation.py +0 -0
  102. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/attention.py +0 -0
  103. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/conv.py +0 -0
  104. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/drop.py +0 -0
  105. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/einops.py +0 -0
  106. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/linear.py +0 -0
  107. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/loss.py +0 -0
  108. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/norm.py +0 -0
  109. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/pool.py +0 -0
  110. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/sparse.py +0 -0
  111. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/transformer.py +0 -0
  112. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/modules/vision.py +0 -0
  113. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/nn/parameter.py +0 -0
  114. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/optim/__init__.py +0 -0
  115. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/optim/ada.py +0 -0
  116. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/optim/adam.py +0 -0
  117. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/optim/lr_scheduler/__init__.py +0 -0
  118. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/optim/lr_scheduler/_schedulers.py +0 -0
  119. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/optim/prop.py +0 -0
  120. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/optim/sgd.py +0 -0
  121. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/random/__init__.py +0 -0
  122. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/random/_func.py +0 -0
  123. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/transforms/__init__.py +0 -0
  124. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/transforms/_base.py +0 -0
  125. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/transforms/image.py +0 -0
  126. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/types.py +0 -0
  127. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/visual/__init__.py +0 -0
  128. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/visual/graph.py +0 -0
  129. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/weights/__init__.py +0 -0
  130. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid/weights/__init__.pyi +0 -0
  131. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid_dl.egg-info/dependency_links.txt +0 -0
  132. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid_dl.egg-info/requires.txt +0 -0
  133. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/lucid_dl.egg-info/top_level.txt +0 -0
  134. {lucid_dl-2.7.5 → lucid_dl-2.7.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lucid-dl
3
- Version: 2.7.5
3
+ Version: 2.7.7
4
4
  Summary: Lumerico's Comprehensive Interface for Deep Learning
5
5
  Home-page: https://github.com/ChanLumerico/lucid
6
6
  Author: ChanLumerico
@@ -4,3 +4,4 @@ from lucid.nn.modules import *
4
4
  from lucid.nn.fused import *
5
5
 
6
6
  import lucid.nn.init as init
7
+ import lucid.nn.util as util
@@ -118,7 +118,7 @@ class Module:
118
118
  for param in self.parameters():
119
119
  param.zero()
120
120
 
121
- def forward(self, *args: Any, **kwargs: Any) -> Tensor | tuple[Tensor, ...]:
121
+ def forward(self) -> Tensor | tuple[Tensor, ...]:
122
122
  raise NotImplementedError(
123
123
  "The forward method must be implemented by the subclass."
124
124
  )
@@ -204,7 +204,7 @@ class Module:
204
204
  destination=destination, prefix=prefix + name + ".", keep_vars=keep_vars
205
205
  )
206
206
 
207
- for key in destination.keys():
207
+ for key in list(destination.keys()):
208
208
  if key in self._state_dict_pass_attr:
209
209
  del destination[key]
210
210
 
@@ -229,10 +229,8 @@ class Module:
229
229
  if key in own_state:
230
230
  attr = own_state[key]
231
231
  if isinstance(attr, (nn.Parameter, nn.Buffer)):
232
- if isinstance(value, Tensor):
233
- attr.data = value.data
234
- else:
235
- attr.data = value
232
+ value_t = Tensor(value, device=self.device)
233
+ attr.data = value_t.data
236
234
  else:
237
235
  setattr(self, key, value)
238
236
  elif strict:
@@ -0,0 +1,60 @@
1
+ from typing import Iterable
2
+
3
+ import lucid
4
+
5
+ from lucid._tensor import Tensor
6
+ from lucid.types import _Scalar
7
+
8
+
9
+ __all__ = ["grad_norm", "clip_grad_norm", "clip_grad_value"]
10
+
11
+
12
+ def _as_iter(parameters: Iterable[Tensor] | Tensor) -> list[Tensor]:
13
+ if isinstance(parameters, Tensor):
14
+ return [parameters]
15
+ return list(parameters)
16
+
17
+
18
+ def grad_norm(parameters: Iterable[Tensor] | Tensor, norm_type: int = 2) -> Tensor:
19
+ parameters = _as_iter(parameters)
20
+ device = parameters[0].device
21
+
22
+ params: list[Tensor] = [p for p in parameters if p.grad is not None]
23
+ if not params:
24
+ return Tensor(0.0, device=device)
25
+
26
+ norm_pow_sum = 0.0
27
+ for p in params:
28
+ param_norm = lucid.linalg.norm(lucid.ravel(p.grad), ord=norm_type).item()
29
+ norm_pow_sum += param_norm**norm_type
30
+
31
+ total_norm = norm_pow_sum ** (1.0 / norm_type)
32
+ return Tensor(total_norm, device=device)
33
+
34
+
35
+ def clip_grad_norm(
36
+ parameters: Iterable[Tensor] | Tensor,
37
+ max_norm: _Scalar,
38
+ norm_type: int = 2,
39
+ eps: float = 1e-7,
40
+ ) -> float:
41
+ params: list[Tensor] = [p for p in _as_iter(parameters) if p.grad is not None]
42
+ total_norm = grad_norm(params, norm_type=norm_type)
43
+
44
+ clip_coef = float(max_norm) / (total_norm.item() + eps)
45
+ if clip_coef < 1.0:
46
+ for p in params:
47
+ p.grad = p.grad * clip_coef
48
+
49
+ return total_norm
50
+
51
+
52
+ def clip_grad_value(parameters: Iterable[Tensor] | Tensor, clip_value: _Scalar) -> None:
53
+ params = [p for p in _as_iter(parameters) if p.grad is not None]
54
+ if not params:
55
+ return
56
+
57
+ lo, hi = -float(clip_value), float(clip_value)
58
+ for p in params:
59
+ g_clip = lucid.clip(p.grad, lo, hi).data
60
+ p.grad = g_clip
@@ -0,0 +1,142 @@
1
+ from collections import defaultdict
2
+ from typing import Any, Iterable
3
+ from abc import ABC, abstractmethod
4
+ import copy
5
+
6
+ import lucid.nn as nn
7
+
8
+ from lucid.types import _OptimClosure
9
+
10
+
11
+ class Optimizer(ABC):
12
+ def __init__(
13
+ self, params: Iterable[nn.Parameter], defaults: dict[str, Any]
14
+ ) -> None:
15
+ if not isinstance(params, Iterable):
16
+ raise TypeError("params should be an iterable of Parameters.")
17
+
18
+ param_list = list(params)
19
+ for p in param_list:
20
+ if not isinstance(p, nn.Parameter):
21
+ raise TypeError(f"Expected nn.Parameter, got {type(p).__name__}.")
22
+
23
+ self.defaults: dict[str, Any] = dict(defaults)
24
+ self.param_groups: list[dict[str, Any]] = self.param_groups_setup(
25
+ param_list, self.defaults
26
+ )
27
+ self.state: dict[nn.Parameter, dict[str, Any]] = defaultdict(dict)
28
+
29
+ @abstractmethod
30
+ def step(self, closure: _OptimClosure | None = None) -> Any | None:
31
+ raise NotImplementedError("The step method must be implemented by subclasses.")
32
+
33
+ def zero_grad(self) -> None:
34
+ for group in self.param_groups:
35
+ for param in group["params"]:
36
+ if isinstance(param, nn.Parameter):
37
+ param.zero_grad()
38
+
39
+ def param_groups_setup(
40
+ self, params: list[nn.Parameter], defaults: dict[str, Any]
41
+ ) -> list[dict[str, Any]]:
42
+ return [{"params": list(params), **defaults}]
43
+
44
+ def add_param_group(self, param_group: dict[str, Any]) -> None:
45
+ if "params" not in param_group:
46
+ raise ValueError("param_group must have a 'params' key.")
47
+
48
+ params = list(param_group["params"])
49
+ if len(params) == 0:
50
+ raise ValueError("param_group['params'] must be non-empty.")
51
+
52
+ for p in params:
53
+ if not isinstance(p, nn.Parameter):
54
+ raise TypeError(
55
+ f"Expected nn.Parameter in param_group, got {type(p).__name__}."
56
+ )
57
+
58
+ existing = set()
59
+ for g in self.param_groups:
60
+ existing.update(g["params"])
61
+
62
+ if any(p in existing for p in params):
63
+ raise ValueError("Some parameters appear in more than one parameter group.")
64
+
65
+ filled = {
66
+ **self.defaults,
67
+ **{k: v for k, v in param_group.items() if k != "params"},
68
+ }
69
+ filled["params"] = params
70
+ self.param_groups.append(filled)
71
+
72
+ def _flat_params(self) -> list[nn.Parameter]:
73
+ flat: list[nn.Parameter] = []
74
+ for g in self.param_groups:
75
+ flat.extend(g["params"])
76
+
77
+ return flat
78
+
79
+ def state_dict(self) -> dict:
80
+ param_to_idx: dict[nn.Parameter, int] = {}
81
+ for idx, p in enumerate(self._flat_params()):
82
+ if p not in param_to_idx:
83
+ param_to_idx[p] = idx
84
+
85
+ packed_state: dict[int, dict[str, Any]] = {}
86
+ for p, st in self.state.items():
87
+ if p in param_to_idx:
88
+ packed_state[param_to_idx[p]] = copy.deepcopy(st)
89
+
90
+ packed_groups: list[dict[str, Any]] = []
91
+ for g in self.param_groups:
92
+ new_g: dict[str, Any] = {}
93
+
94
+ for k, v in g.items():
95
+ if k == "params":
96
+ new_g[k] = [param_to_idx[p] for p in v]
97
+ else:
98
+ new_g[k] = copy.deepcopy(v)
99
+
100
+ packed_groups.append(new_g)
101
+
102
+ return {"state": packed_state, "param_groups": packed_groups}
103
+
104
+ def load_state_dict(self, state_dict: dict) -> None:
105
+ if (
106
+ not isinstance(state_dict, dict)
107
+ or "state" not in state_dict
108
+ or "param_groups" not in state_dict
109
+ ):
110
+ raise TypeError("Invalid state_dict format for Optimizer.")
111
+
112
+ saved_groups = state_dict["param_groups"]
113
+ saved_state = state_dict["state"]
114
+
115
+ current_params = self._flat_params()
116
+ n_current = len(current_params)
117
+
118
+ new_groups: list[dict[str, Any]] = []
119
+ for sg in saved_groups:
120
+ if "params" not in sg:
121
+ raise KeyError("Saved param_group missing 'params'.")
122
+ indices: list[int] = list(sg["params"])
123
+
124
+ if any(i < 0 or i >= n_current for i in indices):
125
+ raise IndexError("Saved state refers to parameter index out of range.")
126
+
127
+ params = [current_params[i] for i in indices]
128
+ ng = {
129
+ k: (params if k == "params" else copy.deepcopy(v))
130
+ for k, v in sg.items()
131
+ }
132
+ new_groups.append(ng)
133
+
134
+ self.param_groups = new_groups
135
+
136
+ self.state = defaultdict(dict)
137
+ for i, p in enumerate(self._flat_params()):
138
+ if i in saved_state:
139
+ self.state[p] = copy.deepcopy(saved_state[i])
140
+
141
+ def __repr__(self) -> str:
142
+ return f"{type(self).__name__}({self.defaults})"
@@ -0,0 +1,85 @@
1
+ from typing import Any
2
+ from abc import ABC, abstractmethod
3
+
4
+ from lucid.optim import Optimizer
5
+
6
+
7
+ class LRScheduler(ABC):
8
+ def __init__(
9
+ self, optimizer: Optimizer, last_epoch: int = -1, verbose: bool = False
10
+ ) -> None:
11
+ if not hasattr(optimizer, "param_groups"):
12
+ raise TypeError(f"{type(optimizer).__name__} is not a valid optimizer.")
13
+
14
+ self.optimizer = optimizer
15
+ self.last_epoch = last_epoch
16
+ self.verbose = verbose
17
+ self.base_lrs: list[float] = [float(g["lr"]) for g in optimizer.param_groups]
18
+
19
+ self._step_count = 0
20
+ self._last_lr: list[float] = [float(g["lr"]) for g in optimizer.param_groups]
21
+
22
+ @abstractmethod
23
+ def get_lr(self) -> list[float]:
24
+ raise NotImplementedError
25
+
26
+ def step(self, epoch: int | None = None) -> None:
27
+ if epoch is None:
28
+ self.last_epoch += 1
29
+ else:
30
+ self.last_epoch = int(epoch)
31
+ self._step_count += 1
32
+
33
+ new_lrs = self.get_lr()
34
+ if len(new_lrs) != len(self.optimizer.param_groups):
35
+ raise ValueError(
36
+ f"get_lr returned {len(new_lrs)} values, "
37
+ f"but optimizer has {len(self.optimizer.param_groups)} param groups."
38
+ )
39
+
40
+ for group, lr in zip(self.optimizer.param_groups, new_lrs):
41
+ group["lr"] = float(lr)
42
+
43
+ self._last_lr = [float(g["lr"]) for g in self.optimizer.param_groups]
44
+
45
+ if self.verbose:
46
+ print(
47
+ f"Epoch {self.last_epoch}: setting learning rates to {self._last_lr}."
48
+ )
49
+
50
+ def state_dict(self) -> dict[str, Any]:
51
+ return {
52
+ "last_epoch": int(self.last_epoch),
53
+ "base_lrs": [float(x) for x in self.base_lrs],
54
+ "_step_count": int(self._step_count),
55
+ "_last_lr": [float(x) for x in self._last_lr],
56
+ "_group_count": len(self.optimizer.param_groups),
57
+ }
58
+
59
+ def load_state_dict(self, state_dict: dict[str, Any]) -> None:
60
+ required = {"last_epoch", "base_lrs", "_step_count", "_last_lr"}
61
+ missing = required - set(state_dict)
62
+ if missing:
63
+ raise KeyError(f"Missing keys in scheduler state_dict: {missing}")
64
+
65
+ saved_group_count = int(
66
+ state_dict.get("_group_count", len(state_dict["_last_lr"]))
67
+ )
68
+ current_group_count = len(self.optimizer.param_groups)
69
+ if saved_group_count != current_group_count:
70
+ raise ValueError(
71
+ "Cannot load scheduler state: param group count mismatch "
72
+ f"(saved={saved_group_count}, current={current_group_count})."
73
+ )
74
+
75
+ self.last_epoch = int(state_dict["last_epoch"])
76
+ self.base_lrs = [float(x) for x in state_dict["base_lrs"]]
77
+ self._step_count = int(state_dict["_step_count"])
78
+ self._last_lr = [float(x) for x in state_dict["_last_lr"]]
79
+
80
+ for group, lr in zip(self.optimizer.param_groups, self._last_lr):
81
+ group["lr"] = float(lr)
82
+
83
+ @property
84
+ def last_lr(self) -> list[float]:
85
+ return list(self._last_lr)
@@ -5,14 +5,13 @@ from typing import Literal
5
5
 
6
6
  from lucid._tensor import Tensor
7
7
  from lucid.nn import Module
8
- from lucid.types import _NumPyArray
9
8
 
10
9
 
11
10
  __all__ = ["save", "load"]
12
11
 
13
- _LucidPortable = Tensor | Module | OrderedDict
12
+ _LucidPortable = Tensor | Module | OrderedDict | dict
14
13
 
15
- FORMAT_VERSION: float = 1.0
14
+ FORMAT_VERSION: float = 1.1
16
15
 
17
16
  EXTENSIONS = Literal[".lct", ".lcd", ".safetensors"]
18
17
 
@@ -30,7 +29,7 @@ def save(obj: _LucidPortable, path: Path | str, safetensors: bool = False) -> Pa
30
29
  if path.suffix == "":
31
30
  if isinstance(obj, Tensor):
32
31
  path = path.with_suffix(".lct")
33
- elif isinstance(obj, (Module, OrderedDict)):
32
+ elif isinstance(obj, (Module, OrderedDict, dict)):
34
33
  path = (
35
34
  path.with_suffix(".safetensors")
36
35
  if safetensors
@@ -56,10 +55,14 @@ def save(obj: _LucidPortable, path: Path | str, safetensors: bool = False) -> Pa
56
55
  elif suffix == ".lcd":
57
56
  if isinstance(obj, Module):
58
57
  obj = obj.state_dict()
59
- if not isinstance(obj, OrderedDict):
60
- raise TypeError("Expected a state_dict (OrderedDict) for .lcd file.")
58
+ if not isinstance(obj, (OrderedDict, dict)):
59
+ raise TypeError("Expected a state_dict for .lcd file.")
61
60
 
62
- data = {"type": "OrderedDict", "format_version": FORMAT_VERSION, "content": obj}
61
+ data = {
62
+ "type": type(obj).__name__,
63
+ "format_version": FORMAT_VERSION,
64
+ "content": obj,
65
+ }
63
66
 
64
67
  elif suffix == ".safetensors":
65
68
  try:
@@ -72,10 +75,8 @@ def save(obj: _LucidPortable, path: Path | str, safetensors: bool = False) -> Pa
72
75
 
73
76
  if isinstance(obj, Module):
74
77
  obj = obj.state_dict()
75
- if not isinstance(obj, OrderedDict):
76
- raise TypeError(
77
- "Expected a state_dict (OrderedDict) for .safetensors file."
78
- )
78
+ if not isinstance(obj, (OrderedDict, dict)):
79
+ raise TypeError("Expected a state_dict for .safetensors file.")
79
80
 
80
81
  save_file(obj, str(path))
81
82
  return path.resolve()
@@ -122,7 +123,7 @@ def load(path: Path | str) -> _LucidPortable:
122
123
  array = data["content"]
123
124
  return Tensor(array)
124
125
 
125
- elif file_type == "OrderedDict":
126
+ elif file_type in {"OrderedDict", "dict"}:
126
127
  return data["content"]
127
128
 
128
129
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lucid-dl
3
- Version: 2.7.5
3
+ Version: 2.7.7
4
4
  Summary: Lumerico's Comprehensive Interface for Deep Learning
5
5
  Home-page: https://github.com/ChanLumerico/lucid
6
6
  Author: ChanLumerico
@@ -78,6 +78,7 @@ lucid/nn/__init__.py
78
78
  lucid/nn/fused.py
79
79
  lucid/nn/module.py
80
80
  lucid/nn/parameter.py
81
+ lucid/nn/util.py
81
82
  lucid/nn/functional/__init__.py
82
83
  lucid/nn/functional/_activation.py
83
84
  lucid/nn/functional/_attention.py
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="lucid-dl",
8
- version="2.7.5",
8
+ version="2.7.7",
9
9
  author="ChanLumerico",
10
10
  author_email="greensox284@gmail.com",
11
11
  description="Lumerico's Comprehensive Interface for Deep Learning",
@@ -1,57 +0,0 @@
1
- from collections import defaultdict
2
- from typing import Any, Iterable, OrderedDict
3
- from abc import ABC, abstractmethod
4
- import copy
5
-
6
- import lucid.nn as nn
7
-
8
- from lucid.types import _OptimClosure
9
-
10
-
11
- class Optimizer(ABC):
12
- def __init__(
13
- self, params: Iterable[nn.Parameter], defaults: dict[str, Any]
14
- ) -> None:
15
- super().__init__()
16
- if not isinstance(params, Iterable):
17
- raise TypeError("params should be an iterable of Parameters.")
18
-
19
- params = list(params)
20
- self.param_groups = self.param_groups_setup(params, defaults)
21
- self.defaults = defaults
22
- self.state: dict[nn.Parameter, dict[str, Any]] = defaultdict(dict)
23
-
24
- def param_groups_setup(
25
- self, params: list[nn.Parameter], defaults: dict[str, Any]
26
- ) -> list[dict[str, Any]]:
27
- return [{"params": list(params), **defaults}]
28
-
29
- @abstractmethod
30
- def step(self, closure: _OptimClosure | None = None) -> Any | None:
31
- raise NotImplementedError("The step method must be implemented by subclasses.")
32
-
33
- def zero_grad(self) -> None:
34
- for group in self.param_groups:
35
- for param in group["params"]:
36
- param.zero_grad()
37
-
38
- def add_param_group(self, param_group: dict[str, Any]) -> None:
39
- for group in self.param_groups:
40
- if set(group["params"]) & set(param_group["params"]):
41
- raise ValueError(
42
- "Some parameters appear in more than one parameter group."
43
- )
44
- self.param_groups.append(param_group)
45
-
46
- def state_dict(self) -> OrderedDict:
47
- return {
48
- "state": copy.deepcopy(self.state),
49
- "param_groups": copy.deepcopy(self.param_groups),
50
- }
51
-
52
- def load_state_dict(self, state_dict: OrderedDict) -> None:
53
- self.state = defaultdict(dict, copy.deepcopy(state_dict["state"]))
54
- self.param_groups = copy.deepcopy(state_dict["param_groups"])
55
-
56
- def __repr__(self) -> str:
57
- return f"{type(self).__name__}({self.param_groups})"
@@ -1,63 +0,0 @@
1
- from typing import Any
2
- from abc import ABC, abstractmethod
3
-
4
- from lucid.optim import Optimizer
5
-
6
-
7
- class LRScheduler(ABC):
8
- def __init__(
9
- self, optimizer: Optimizer, last_epoch: int = -1, verbose: bool = False
10
- ) -> None:
11
- super().__init__()
12
- if not hasattr(optimizer, "param_groups"):
13
- raise TypeError(f"{type(optimizer).__name__} is not a valid optimizer.")
14
-
15
- self.optimizer = optimizer
16
- self.last_epoch = last_epoch
17
- self.verbose = verbose
18
- self.base_lrs = [group["lr"] for group in optimizer.param_groups]
19
-
20
- self._step_count = 0
21
- self._last_lr = [group["lr"] for group in optimizer.param_groups]
22
-
23
- @abstractmethod
24
- def get_lr(self) -> list[float]:
25
- raise NotImplementedError("get_lr must be implemented in subclasses.")
26
-
27
- def step(self, epoch: int | None = None) -> None:
28
- if epoch is not None:
29
- self.last_epoch = epoch
30
- else:
31
- self._step_count += 1
32
- self.last_epoch = self._step_count
33
-
34
- new_lrs = self.get_lr()
35
- for param_group, lr in zip(self.optimizer.param_groups, new_lrs):
36
- param_group["lr"] = lr
37
-
38
- self._last_lr = new_lrs
39
-
40
- if self.verbose:
41
- print(f"Epoch {self.last_epoch}: setting learning rates to {new_lrs}.")
42
-
43
- def state_dict(self) -> dict[str, Any]:
44
- return {
45
- "last_epoch": self.last_epoch,
46
- "base_lrs": self.base_lrs,
47
- "_step_count": self._step_count,
48
- "_last_lr": self._last_lr,
49
- }
50
-
51
- def load_state_dict(self, state_dict: dict[str, Any]) -> None:
52
- self.last_epoch = state_dict["last_epoch"]
53
- self.base_lrs = state_dict["base_lrs"]
54
-
55
- self._step_count = state_dict["_step_count"]
56
- self._last_lr = state_dict["_last_lr"]
57
-
58
- for param_group, lr in zip(self.optimizer.param_groups, self._last_lr):
59
- param_group["lr"] = lr
60
-
61
- @property
62
- def last_lr(self) -> list[float]:
63
- return self._last_lr
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes