pytinytensor 0.1.1__tar.gz → 0.1.2__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 (63) hide show
  1. pytinytensor-0.1.2/LICENSE +21 -0
  2. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/PKG-INFO +3 -1
  3. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/PKG-INFO +3 -1
  4. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/SOURCES.txt +24 -0
  5. pytinytensor-0.1.2/setup.py +133 -0
  6. pytinytensor-0.1.2/tinytensor/nn/__init__.py +0 -0
  7. {pytinytensor-0.1.1 → pytinytensor-0.1.2/tinytensor}/setup.py +10 -15
  8. pytinytensor-0.1.2/tinytensor/tinytensor/__init__.py +0 -0
  9. pytinytensor-0.1.2/tinytensor/tinytensor/backends/__init__.py +0 -0
  10. pytinytensor-0.1.2/tinytensor/tinytensor/backends/cpu_numpy.py +0 -0
  11. pytinytensor-0.1.2/tinytensor/tinytensor/config.py +20 -0
  12. pytinytensor-0.1.2/tinytensor/tinytensor/core/__init__.py +0 -0
  13. pytinytensor-0.1.2/tinytensor/tinytensor/core/autograd.py +22 -0
  14. pytinytensor-0.1.2/tinytensor/tinytensor/core/ops.py +1 -0
  15. pytinytensor-0.1.2/tinytensor/tinytensor/core/tensor.py +288 -0
  16. pytinytensor-0.1.2/tinytensor/tinytensor/data/__init__.py +5 -0
  17. pytinytensor-0.1.2/tinytensor/tinytensor/data/dataloader.py +41 -0
  18. pytinytensor-0.1.2/tinytensor/tinytensor/data/dataset.py +19 -0
  19. pytinytensor-0.1.2/tinytensor/tinytensor/nn/__init__.py +0 -0
  20. pytinytensor-0.1.2/tinytensor/tinytensor/nn/activations.py +36 -0
  21. pytinytensor-0.1.2/tinytensor/tinytensor/nn/linear.py +18 -0
  22. pytinytensor-0.1.2/tinytensor/tinytensor/nn/losses.py +12 -0
  23. pytinytensor-0.1.2/tinytensor/tinytensor/nn/modules.py +64 -0
  24. pytinytensor-0.1.2/tinytensor/tinytensor/optim/__init__.py +11 -0
  25. pytinytensor-0.1.2/tinytensor/tinytensor/optim/optimizer.py +72 -0
  26. pytinytensor-0.1.2/tinytensor/utils/__init__.py +4 -0
  27. pytinytensor-0.1.2/tinytensor/utils/bar.py +26 -0
  28. pytinytensor-0.1.2/tinytensor/utils/early_stopping.py +64 -0
  29. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/README.md +0 -0
  30. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pyproject.toml +0 -0
  31. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/dependency_links.txt +0 -0
  32. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/requires.txt +0 -0
  33. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/top_level.txt +0 -0
  34. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/setup.cfg +0 -0
  35. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_autograd.py +0 -0
  36. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_data.py +0 -0
  37. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_losses.py +0 -0
  38. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_nn.py +0 -0
  39. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_optim.py +0 -0
  40. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_tensor_math.py +0 -0
  41. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/__init__.py +0 -0
  42. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/backends/__init__.py +0 -0
  43. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/backends/cpu_numpy.py +0 -0
  44. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/backends/cuda_binding.cpp +0 -0
  45. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/backends/cuda_gpu.cu +0 -0
  46. /pytinytensor-0.1.1/tinytensor/core/__init__.py → /pytinytensor-0.1.2/tinytensor/backends/cuda_gpu.py +0 -0
  47. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/config.py +0 -0
  48. {pytinytensor-0.1.1/tinytensor/nn → pytinytensor-0.1.2/tinytensor/core}/__init__.py +0 -0
  49. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/core/autograd.py +0 -0
  50. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/core/ops.py +0 -0
  51. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/core/tensor.py +0 -0
  52. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/data/__init__.py +0 -0
  53. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/data/dataloader.py +0 -0
  54. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/data/dataset.py +0 -0
  55. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/nn/activations.py +0 -0
  56. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/nn/linear.py +0 -0
  57. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/nn/losses.py +0 -0
  58. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/nn/modules.py +0 -0
  59. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/optim/__init__.py +0 -0
  60. {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/optim/optimizer.py +0 -0
  61. {pytinytensor-0.1.1 → pytinytensor-0.1.2/tinytensor}/tinytensor/utils/__init__.py +0 -0
  62. {pytinytensor-0.1.1 → pytinytensor-0.1.2/tinytensor}/tinytensor/utils/bar.py +0 -0
  63. {pytinytensor-0.1.1 → pytinytensor-0.1.2/tinytensor}/tinytensor/utils/early_stopping.py +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 IbrokhimN
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytinytensor
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: мини ИИ фреймворк от IbrokimN ( github/IbrokhimN )
5
5
  Home-page: https://github.com/IbrokhimN/tinytensor
6
6
  Author: IbrokhimN
7
7
  Requires-Python: >=3.9
8
8
  Description-Content-Type: text/markdown
9
+ License-File: LICENSE
9
10
  Requires-Dist: numpy>=1.23
10
11
  Requires-Dist: pybind11>=2.10
11
12
  Provides-Extra: dev
@@ -14,6 +15,7 @@ Dynamic: author
14
15
  Dynamic: description
15
16
  Dynamic: description-content-type
16
17
  Dynamic: home-page
18
+ Dynamic: license-file
17
19
  Dynamic: provides-extra
18
20
  Dynamic: requires-dist
19
21
  Dynamic: requires-python
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytinytensor
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: мини ИИ фреймворк от IbrokimN ( github/IbrokhimN )
5
5
  Home-page: https://github.com/IbrokhimN/tinytensor
6
6
  Author: IbrokhimN
7
7
  Requires-Python: >=3.9
8
8
  Description-Content-Type: text/markdown
9
+ License-File: LICENSE
9
10
  Requires-Dist: numpy>=1.23
10
11
  Requires-Dist: pybind11>=2.10
11
12
  Provides-Extra: dev
@@ -14,6 +15,7 @@ Dynamic: author
14
15
  Dynamic: description
15
16
  Dynamic: description-content-type
16
17
  Dynamic: home-page
18
+ Dynamic: license-file
17
19
  Dynamic: provides-extra
18
20
  Dynamic: requires-dist
19
21
  Dynamic: requires-python
@@ -1,3 +1,4 @@
1
+ LICENSE
1
2
  README.md
2
3
  pyproject.toml
3
4
  setup.py
@@ -14,10 +15,12 @@ tests/test_optim.py
14
15
  tests/test_tensor_math.py
15
16
  tinytensor/__init__.py
16
17
  tinytensor/config.py
18
+ tinytensor/setup.py
17
19
  tinytensor/backends/__init__.py
18
20
  tinytensor/backends/cpu_numpy.py
19
21
  tinytensor/backends/cuda_binding.cpp
20
22
  tinytensor/backends/cuda_gpu.cu
23
+ tinytensor/backends/cuda_gpu.py
21
24
  tinytensor/core/__init__.py
22
25
  tinytensor/core/autograd.py
23
26
  tinytensor/core/ops.py
@@ -32,6 +35,27 @@ tinytensor/nn/losses.py
32
35
  tinytensor/nn/modules.py
33
36
  tinytensor/optim/__init__.py
34
37
  tinytensor/optim/optimizer.py
38
+ tinytensor/tinytensor/__init__.py
39
+ tinytensor/tinytensor/config.py
40
+ tinytensor/tinytensor/backends/__init__.py
41
+ tinytensor/tinytensor/backends/cpu_numpy.py
42
+ tinytensor/tinytensor/core/__init__.py
43
+ tinytensor/tinytensor/core/autograd.py
44
+ tinytensor/tinytensor/core/ops.py
45
+ tinytensor/tinytensor/core/tensor.py
46
+ tinytensor/tinytensor/data/__init__.py
47
+ tinytensor/tinytensor/data/dataloader.py
48
+ tinytensor/tinytensor/data/dataset.py
49
+ tinytensor/tinytensor/nn/__init__.py
50
+ tinytensor/tinytensor/nn/activations.py
51
+ tinytensor/tinytensor/nn/linear.py
52
+ tinytensor/tinytensor/nn/losses.py
53
+ tinytensor/tinytensor/nn/modules.py
54
+ tinytensor/tinytensor/optim/__init__.py
55
+ tinytensor/tinytensor/optim/optimizer.py
56
+ tinytensor/tinytensor/utils/__init__.py
57
+ tinytensor/tinytensor/utils/bar.py
58
+ tinytensor/tinytensor/utils/early_stopping.py
35
59
  tinytensor/utils/__init__.py
36
60
  tinytensor/utils/bar.py
37
61
  tinytensor/utils/early_stopping.py
@@ -0,0 +1,133 @@
1
+ import os
2
+ import subprocess
3
+ import pybind11
4
+ from pathlib import Path
5
+ from setuptools import setup, find_packages, Extension
6
+ from setuptools.command.build_ext import build_ext
7
+
8
+ long_description = Path(__file__).parent / "README.md"
9
+ long_description = long_description.read_text(encoding="utf-8") if long_description.exists() else ""
10
+
11
+ def find_cuda_lib_dirs():
12
+ candidates = [
13
+ os.environ.get("CUDA_HOME"),
14
+ os.environ.get("CUDA_PATH"),
15
+ "/usr/local/cuda",
16
+ ]
17
+ conda_prefix = os.environ.get("CONDA_PREFIX")
18
+ if conda_prefix:
19
+ candidates.append(conda_prefix)
20
+
21
+ found = []
22
+ for base in candidates:
23
+ if not base:
24
+ continue
25
+ lib_dir = os.path.join(base, "lib64")
26
+ if not os.path.isdir(lib_dir):
27
+ lib_dir = os.path.join(base, "lib")
28
+ has_cudart = any(
29
+ f.startswith("libcudart.so") for f in os.listdir(lib_dir)
30
+ ) if os.path.isdir(lib_dir) else False
31
+ has_cublas = any(
32
+ f.startswith("libcublas.so") for f in os.listdir(lib_dir)
33
+ ) if os.path.isdir(lib_dir) else False
34
+ if has_cudart and has_cublas:
35
+ found.append(lib_dir)
36
+ return found
37
+
38
+
39
+ def check_cuda():
40
+ try:
41
+ subprocess.check_output(["nvcc", "--version"])
42
+ except (FileNotFoundError, subprocess.CalledProcessError):
43
+ return False, []
44
+
45
+ lib_dirs = find_cuda_lib_dirs()
46
+ if not lib_dirs:
47
+ print("nvcc есть но libcudart/libcublas не найдены рядом. Сборка пойдет на CPU.")
48
+ return False, []
49
+ return True, lib_dirs
50
+
51
+
52
+ class CUDABuildExt(build_ext):
53
+ # если cuda-сборка все равно упадет (например линковка) - не роняем весь pip install,
54
+ # а просто откатываемся на чистый cpu-пакет
55
+ def build_extensions(self):
56
+ self.compiler.src_extensions.append('.cu')
57
+ original_compile = self.compiler._compile
58
+
59
+ def custom_compile(obj, src, ext, cc_args, extra_postargs, pp_opts):
60
+ # extra_postargs может приходить как список или словарь — обрабатываем оба случая
61
+ if isinstance(extra_postargs, dict):
62
+ gcc_postargs = extra_postargs.get('gcc', [])
63
+ nvcc_postargs = extra_postargs.get('nvcc', [])
64
+ else:
65
+ gcc_postargs = extra_postargs or []
66
+ nvcc_postargs = []
67
+
68
+ if os.path.splitext(src)[1] == '.cu':
69
+ # nvcc собирает .cu файл
70
+ inc_flags = [f"-I{inc}" for inc in self.compiler.include_dirs]
71
+ cmd = ['nvcc', '-c', src, '-o', obj] + inc_flags + nvcc_postargs
72
+ self.spawn(cmd)
73
+ else:
74
+ # g++ собирает .cpp файл (с pybind11)
75
+ original_compile(obj, src, ext, cc_args, gcc_postargs, pp_opts)
76
+
77
+ self.compiler._compile = custom_compile
78
+
79
+ try:
80
+ super().build_extensions()
81
+ except Exception as e:
82
+ print(f"сборка cuda-расширения не удалась ({e}), продолжаем без него (cpu-only).")
83
+ self.extensions = []
84
+
85
+ ext_modules = []
86
+
87
+ cuda_ok, cuda_lib_dirs = check_cuda()
88
+
89
+ if cuda_ok:
90
+ cuda_module = Extension(
91
+ 'tinytensor.cuda_ops',
92
+ sources=[
93
+ 'tinytensor/backends/cuda_gpu.cu', # nvcc
94
+ 'tinytensor/backends/cuda_binding.cpp' # g++
95
+ ],
96
+ include_dirs=[
97
+ pybind11.get_include(),
98
+ '/usr/local/cuda/include',
99
+ os.path.expanduser('~/.local/include')
100
+ ],
101
+ library_dirs=cuda_lib_dirs,
102
+ libraries=['cudart', 'cublas'],
103
+ extra_compile_args={
104
+ 'gcc': ['-O3', '-fPIC', '-std=c++17'],
105
+ 'nvcc': ['-O3', '-Xcompiler', '-fPIC']
106
+ },
107
+ optional=True,
108
+ )
109
+ ext_modules.append(cuda_module)
110
+ print("cublas")
111
+ else:
112
+ print("cpu")
113
+
114
+ setup(
115
+ name="pytinytensor",
116
+ version="0.1.2",
117
+ description="мини ИИ фреймворк от IbrokimN ( github/IbrokhimN )",
118
+ long_description=long_description,
119
+ long_description_content_type="text/markdown",
120
+ author="IbrokhimN",
121
+ url="https://github.com/IbrokhimN/tinytensor",
122
+ packages=find_packages(include=["tinytensor", "tinytensor.*"]),
123
+ ext_modules=ext_modules,
124
+ cmdclass={'build_ext': CUDABuildExt},
125
+ install_requires=[
126
+ "numpy>=1.23",
127
+ "pybind11>=2.10",
128
+ ],
129
+ extras_require={
130
+ "dev": ["pytest>=7.0"],
131
+ },
132
+ python_requires=">=3.9",
133
+ )
File without changes
@@ -1,13 +1,9 @@
1
1
  import os
2
2
  import subprocess
3
3
  import pybind11
4
- from pathlib import Path
5
4
  from setuptools import setup, find_packages, Extension
6
5
  from setuptools.command.build_ext import build_ext
7
6
 
8
- long_description = Path(__file__).parent / "README.md"
9
- long_description = long_description.read_text(encoding="utf-8") if long_description.exists() else ""
10
-
11
7
  def check_cuda():
12
8
  try:
13
9
  subprocess.check_output(["nvcc", "--version"])
@@ -21,6 +17,7 @@ class CUDABuildExt(build_ext):
21
17
  original_compile = self.compiler._compile
22
18
 
23
19
  def custom_compile(obj, src, ext, cc_args, extra_postargs, pp_opts):
20
+ # extra_postargs может приходить как список или словарь — обрабатываем оба случая
24
21
  if isinstance(extra_postargs, dict):
25
22
  gcc_postargs = extra_postargs.get('gcc', [])
26
23
  nvcc_postargs = extra_postargs.get('nvcc', [])
@@ -29,10 +26,12 @@ class CUDABuildExt(build_ext):
29
26
  nvcc_postargs = []
30
27
 
31
28
  if os.path.splitext(src)[1] == '.cu':
29
+ # nvcc собирает .cu файл
32
30
  inc_flags = [f"-I{inc}" for inc in self.compiler.include_dirs]
33
31
  cmd = ['nvcc', '-c', src, '-o', obj] + inc_flags + nvcc_postargs
34
32
  self.spawn(cmd)
35
33
  else:
34
+ # g++ собирает .cpp файл (с pybind11)
36
35
  original_compile(obj, src, ext, cc_args, gcc_postargs, pp_opts)
37
36
 
38
37
  self.compiler._compile = custom_compile
@@ -44,8 +43,8 @@ if check_cuda():
44
43
  cuda_module = Extension(
45
44
  'tinytensor.cuda_ops',
46
45
  sources=[
47
- 'tinytensor/backends/cuda_gpu.cu',
48
- 'tinytensor/backends/cuda_binding.cpp'
46
+ 'tinytensor/backends/cuda_gpu.cu', # Компилируется nvcc
47
+ 'tinytensor/backends/cuda_binding.cpp' # Компилируется g++
49
48
  ],
50
49
  include_dirs=[
51
50
  pybind11.get_include(),
@@ -60,18 +59,14 @@ if check_cuda():
60
59
  }
61
60
  )
62
61
  ext_modules.append(cuda_module)
63
- print("CUDA найдена! Собираем cuBLAS бэкенд...")
62
+ print("🔥 CUDA найдена! Собираем cuBLAS бэкенд...")
64
63
  else:
65
- print("CUDA не найдена. Сборка продолжится на CPU (NumPy).")
64
+ print("⚠️ CUDA не найдена. Сборка продолжится на CPU (NumPy).")
66
65
 
67
66
  setup(
68
- name="pytinytensor",
69
- version="0.1.1",
67
+ name="tinytensor",
68
+ version="0.1.0",
70
69
  description="мини ИИ фреймворк от IbrokimN ( github/IbrokhimN )",
71
- long_description=long_description,
72
- long_description_content_type="text/markdown",
73
- author="IbrokhimN",
74
- url="https://github.com/IbrokhimN/tinytensor",
75
70
  packages=find_packages(include=["tinytensor", "tinytensor.*"]),
76
71
  ext_modules=ext_modules,
77
72
  cmdclass={'build_ext': CUDABuildExt},
@@ -83,4 +78,4 @@ setup(
83
78
  "dev": ["pytest>=7.0"],
84
79
  },
85
80
  python_requires=">=3.9",
86
- )
81
+ )
File without changes
@@ -0,0 +1,20 @@
1
+ from dataclasses import dataclass
2
+ import numpy as np
3
+
4
+
5
+ @dataclass
6
+ class Config:
7
+ # базовый сид будет 67
8
+ seed: int = 67
9
+
10
+
11
+ config = Config()
12
+
13
+
14
+ def set_seed(seed: int):
15
+ # автоматически устанавливаем сид для воспроизведения и тд
16
+ config.seed = seed
17
+ np.random.seed(seed)
18
+
19
+ # ну все при импорте будет устанавливаться
20
+ np.random.seed(config.seed)
@@ -0,0 +1,22 @@
1
+ import numpy as np
2
+
3
+
4
+ def backward(target_tensor):
5
+ # эт я вообще сделал чтоб файл не пустовал, но это архитектура micrograd как у Андрея Карпатова,
6
+ # если кто то шарит за нормальный движок autograd, то PRните
7
+ topo = []
8
+ visited = set()
9
+
10
+ def build_topo(v):
11
+ if v not in visited:
12
+ visited.add(v)
13
+ for child in v._prev:
14
+ build_topo(child)
15
+ topo.append(v)
16
+
17
+ build_topo(target_tensor)
18
+
19
+ target_tensor.grad = np.ones_like(target_tensor.data, dtype=np.float32)
20
+
21
+ for v in reversed(topo):
22
+ v._backward()
@@ -0,0 +1 @@
1
+ # пока что побудет пустым
@@ -0,0 +1,288 @@
1
+ import numpy as np
2
+
3
+ from tinytensor.core.autograd import backward as run_backward
4
+
5
+ #чекаем ес можно на cuda матричные умножения сделать
6
+ try:
7
+ from tinytensor import cuda_ops
8
+ HAS_CUDA = True
9
+ except ImportError:
10
+ HAS_CUDA = False
11
+
12
+
13
+ def _unbroadcast(grad, target):
14
+ while grad.ndim > len(target):
15
+ grad = grad.sum(axis=0)
16
+
17
+ for i, dim in enumerate(target):
18
+ if dim == 1:
19
+ grad = grad.sum(axis=i, keepdims=True)
20
+ return grad
21
+
22
+
23
+ # этот мир жесток
24
+ class Tensor:
25
+ def __init__(self, data, requires_grad=False, device="cpu"):
26
+ # кароч проверка если нампай массив то оставляем а если нет то делаем нампаем
27
+ if isinstance(data, np.ndarray):
28
+ self.data = data.astype(np.float32)
29
+ else:
30
+ self.data = np.array(data, dtype=np.float32)
31
+
32
+ self.grad = None
33
+ self._backward = lambda: None
34
+ self._prev = set()
35
+ self.requires_grad = requires_grad
36
+ self.device = device
37
+
38
+ def to(self, device):
39
+ # перекидываем тензор на cuda или cpu
40
+ self.device = device
41
+ return self
42
+
43
+ def __repr__(self):
44
+ return f"tensor({self.data}, device='{self.device}', requires_grad={self.requires_grad})"
45
+
46
+ @property
47
+ def shape(self):
48
+ return self.data.shape
49
+
50
+ # сложение
51
+ def __add__(self, other):
52
+ other = other if isinstance(other, Tensor) else Tensor(other, device=self.device)
53
+ out = Tensor(
54
+ self.data + other.data,
55
+ requires_grad=self.requires_grad or other.requires_grad,
56
+ device=self.device,
57
+ )
58
+
59
+ if out.requires_grad:
60
+ out._prev = {self, other}
61
+
62
+ def _backward():
63
+ if self.requires_grad:
64
+ if self.grad is None:
65
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
66
+
67
+ grad_self = out.grad * 1.0
68
+ self.grad += _unbroadcast(grad_self, self.data.shape)
69
+
70
+ # градиент для 2 параметра
71
+ if other.requires_grad:
72
+ if other.grad is None:
73
+ other.grad = np.zeros_like(other.data, dtype=np.float32)
74
+
75
+ grad_other = out.grad * 1.0
76
+ other.grad += _unbroadcast(grad_other, other.data.shape)
77
+
78
+ out._backward = _backward
79
+
80
+ return out
81
+
82
+ def __radd__(self, other):
83
+ # эт если пользователь решит написать не Tensor + 5 а 5 + Tensor
84
+ return self + other
85
+
86
+ # умножение
87
+ def __mul__(self, other):
88
+ other = other if isinstance(other, Tensor) else Tensor(other, device=self.device)
89
+ out = Tensor(
90
+ self.data * other.data,
91
+ requires_grad=self.requires_grad or other.requires_grad,
92
+ device=self.device,
93
+ )
94
+
95
+ if out.requires_grad:
96
+ out._prev = {self, other}
97
+
98
+ def _backward():
99
+ if self.requires_grad:
100
+ if self.grad is None:
101
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
102
+ grad_self = out.grad * other.data
103
+ self.grad += _unbroadcast(grad_self, self.data.shape)
104
+
105
+ if other.requires_grad:
106
+ if other.grad is None:
107
+ other.grad = np.zeros_like(other.data, dtype=np.float32)
108
+ grad_other = out.grad * self.data
109
+ other.grad += _unbroadcast(grad_other, other.data.shape)
110
+
111
+ out._backward = _backward
112
+
113
+ return out
114
+
115
+ def __rmul__(self, other):
116
+ return self * other
117
+
118
+ # матричное умножение
119
+ def __matmul__(self, other):
120
+ other = other if isinstance(other, Tensor) else Tensor(other, device=self.device)
121
+
122
+ # если девайс куда и бэкенд собрался то гоним через кублас а если нет то обычный нампай
123
+ if (self.device == "cuda" or other.device == "cuda") and HAS_CUDA:
124
+ res_data = cuda_ops.matmul(self.data, other.data)
125
+ else:
126
+ res_data = np.matmul(self.data, other.data)
127
+
128
+ out = Tensor(
129
+ res_data,
130
+ requires_grad=self.requires_grad or other.requires_grad,
131
+ device=self.device,
132
+ )
133
+
134
+ if out.requires_grad:
135
+ out._prev = {self, other}
136
+
137
+ def _backward():
138
+ if self.requires_grad:
139
+ if self.grad is None:
140
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
141
+ self.grad += np.matmul(out.grad, other.data.T)
142
+
143
+ if other.requires_grad:
144
+ if other.grad is None:
145
+ other.grad = np.zeros_like(other.data, dtype=np.float32)
146
+ other.grad += np.matmul(self.data.T, out.grad)
147
+
148
+ out._backward = _backward
149
+
150
+ return out
151
+
152
+ # вычитание
153
+ def __sub__(self, other):
154
+ return self + (other * -1)
155
+
156
+ def __rsub__(self, other):
157
+ return (self * -1) + other
158
+
159
+ def __pow__(self, power):
160
+ out = Tensor(self.data ** power, requires_grad=self.requires_grad, device=self.device)
161
+ if out.requires_grad:
162
+ out._prev = {self}
163
+
164
+ def _backward():
165
+ if self.requires_grad:
166
+ if self.grad is None:
167
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
168
+ self.grad += out.grad * (power * (self.data ** (power - 1)))
169
+
170
+ out._backward = _backward
171
+ return out
172
+
173
+ def sum(self):
174
+ out = Tensor(self.data.sum(), requires_grad=self.requires_grad, device=self.device)
175
+ if out.requires_grad:
176
+ out._prev = {self}
177
+
178
+ def _backward():
179
+ if self.requires_grad:
180
+ if self.grad is None:
181
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
182
+ self.grad += out.grad * np.ones_like(self.data)
183
+
184
+ out._backward = _backward
185
+ return out
186
+
187
+ def relu(self):
188
+ out = Tensor(
189
+ np.maximum(0, self.data),
190
+ requires_grad=self.requires_grad,
191
+ device=self.device,
192
+ )
193
+
194
+ if out.requires_grad:
195
+ out._prev = {self}
196
+
197
+ def _backward():
198
+ if self.requires_grad:
199
+ if self.grad is None:
200
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
201
+ self.grad += out.grad * (self.data > 0)
202
+
203
+ out._backward = _backward
204
+
205
+ return out
206
+
207
+ def leaky_relu(self, alpha=0.01):
208
+ out_data = np.where(self.data > 0, self.data, self.data * alpha)
209
+ out = Tensor(out_data, requires_grad=self.requires_grad, device=self.device)
210
+ if out.requires_grad:
211
+ out._prev = {self}
212
+
213
+ def _backward():
214
+ if self.requires_grad:
215
+ if self.grad is None:
216
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
217
+ dx = np.where(self.data > 0, 1.0, alpha)
218
+ self.grad += out.grad * dx
219
+
220
+ out._backward = _backward
221
+ return out
222
+
223
+ def sigmoid(self):
224
+ sig = 1.0 / (1.0 + np.exp(-np.clip(self.data, -50, 50)))
225
+ out = Tensor(sig, requires_grad=self.requires_grad, device=self.device)
226
+ if out.requires_grad:
227
+ out._prev = {self}
228
+
229
+ def _backward():
230
+ if self.requires_grad:
231
+ if self.grad is None:
232
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
233
+ self.grad += out.grad * (sig * (1.0 - sig))
234
+
235
+ out._backward = _backward
236
+ return out
237
+
238
+ def tanh(self):
239
+ t = np.tanh(self.data)
240
+ out = Tensor(t, requires_grad=self.requires_grad, device=self.device)
241
+ if out.requires_grad:
242
+ out._prev = {self}
243
+
244
+ def _backward():
245
+ if self.requires_grad:
246
+ if self.grad is None:
247
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
248
+ self.grad += out.grad * (1.0 - t ** 2)
249
+
250
+ out._backward = _backward
251
+ return out
252
+
253
+ def gelu(self):
254
+ x = self.data
255
+ cdf = 0.5 * (1.0 + np.tanh(np.sqrt(2.0 / np.pi) * (x + 0.044715 * (x ** 3))))
256
+ out = Tensor(x * cdf, requires_grad=self.requires_grad, device=self.device)
257
+ if out.requires_grad:
258
+ out._prev = {self}
259
+
260
+ def _backward():
261
+ if self.requires_grad:
262
+ if self.grad is None:
263
+ self.grad = np.zeros_like(self.data, dtype=np.float32)
264
+ pdf = np.exp(-0.5 * (x ** 2)) / np.sqrt(2.0 * np.pi)
265
+ d_gelu = cdf + x * pdf
266
+ self.grad += out.grad * d_gelu
267
+
268
+ out._backward = _backward
269
+ return out
270
+
271
+ def backward(self):
272
+ run_backward(self)
273
+
274
+
275
+ # ⣇⣿⠘⣿⣿⣿⡿⡿⣟⣟⢟⢟⢝⠵⡝⣿⡿⢂⣼⣿⣷⣌⠩⡫⡻⣝⠹⢿⣿⣷
276
+ # ⡆⣿⣆⠱⣝⡵⣝⢅⠙⣿⢕⢕⢕⢕⢝⣥⢒⠅⣿⣿⣿⡿⣳⣌⠪⡪⣡⢑⢝⣇
277
+ # ⡆⣿⣿⣦⠹⣳⣳⣕⢅⠈⢗⢕⢕⢕⢕⢕⢈⢆⠟⠋⠉⠁⠉⠉⠁⠈⠼⢐⢕⢽
278
+ # ⡗⢰⣶⣶⣦⣝⢝⢕⢕⠅⡆⢕⢕⢕⢕⢕⣴⠏⣠⡶⠛⡉⡉⡛⢶⣦⡀⠐⣕⢕
279
+ # ⡝⡄⢻⢟⣿⣿⣷⣕⣕⣅⣿⣔⣕⣵⣵⣿⣿⢠⣿⢠⣮⡈⣌⠨⠅⠹⣷⡀⢱⢕
280
+ # ⡝⡵⠟⠈⢀⣀⣀⡀⠉⢿⣿⣿⣿⣿⣿⣿⣿⣼⣿⢈⡋⠴⢿⡟⣡⡇⣿⡇⡀⢕
281
+ # ⡝⠁⣠⣾⠟⡉⡉⡉⠻⣦⣻⣿⣿⣿⣿⣿⣿⣿⣿⣧⠸⣿⣦⣥⣿⡇⡿⣰⢗⢄
282
+ # ⠁⢰⣿⡏⣴⣌⠈⣌⠡⠈⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣬⣉⣉⣁⣄⢖⢕⢕⢕
283
+ # ⡀⢻⣿⡇⢙⠁⠴⢿⡟⣡⡆⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣵⣵⣿
284
+ # ⡻⣄⣻⣿⣌⠘⢿⣷⣥⣿⠇⣿⣿⣿⣿⣿⣿⠛⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
285
+ # ⣷⢄⠻⣿⣟⠿⠦⠍⠉⣡⣾⣿⣿⣿⣿⣿⣿⢸⣿⣦⠙⣿⣿⣿⣿⣿⣿⣿⣿⠟
286
+ # ⡕⡑⣑⣈⣻⢗⢟⢞⢝⣻⣿⣿⣿⣿⣿⣿⣿⠸⣿⠿⠃⣿⣿⣿⣿⣿⣿⡿⠁⣠
287
+ # ⡝⡵⢟⢕⢕⢕⢕⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣿⣿⣿⣿⣿⠿⠋⣀⣈⠙
288
+ # ⡝⡵⡕⡀⠑⠳⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⢉⡠⡲⡫⡪⡪⡣
@@ -0,0 +1,5 @@
1
+ from tinytensor.data.dataset import Dataset, TensorDataset
2
+ from tinytensor.data.dataloader import DataLoader
3
+
4
+ __all__ = ["Dataset", "TensorDataset", "DataLoader"]
5
+ # для крастоы :3
@@ -0,0 +1,41 @@
1
+ import numpy as np
2
+ from tinytensor.core.tensor import Tensor
3
+
4
+ class DataLoader:
5
+ def __init__(self, dataset, batch_size=32, shuffle=True):
6
+ self.dataset = dataset
7
+ self.batch_size = batch_size
8
+ self.shuffle = shuffle
9
+
10
+ def __len__(self):
11
+ #колво батчей за 1 эпоху
12
+ return (len(self.dataset) + self.batch_size - 1) // self.batch_size
13
+
14
+ def __iter__(self):
15
+ self.indices = np.arange(len(self.dataset))
16
+
17
+ # перемешкиваем если шафл есть
18
+ if self.shuffle:
19
+ np.random.shuffle(self.indices)
20
+
21
+ self.current_idx = 0
22
+ return self
23
+
24
+ def __next__(self):
25
+ # выдает след батч
26
+ # останавливаемся если дошли до конца датасета
27
+ if self.current_idx >= len(self.dataset):
28
+ raise StopIteration
29
+
30
+ batch_indices = self.indices[self.current_idx : self.current_idx + self.batch_size]
31
+ self.current_idx += self.batch_size
32
+
33
+ #cбор
34
+ batch_x, batch_y = [], []
35
+ for i in batch_indices:
36
+ x, y = self.dataset[i]
37
+ batch_x.append(x.data if isinstance(x, Tensor) else x)
38
+ batch_y.append(y.data if isinstance(y, Tensor) else y)
39
+
40
+ # батч в нампай и потом в тензо
41
+ return Tensor(np.array(batch_x)), Tensor(np.array(batch_y))
@@ -0,0 +1,19 @@
1
+ #влом коменты ставить тут и так все понятно
2
+ class Dataset:
3
+ def __len__(self):
4
+ raise NotImplementedError
5
+
6
+ def __getitem__(self, idx):
7
+ raise NotImplementedError
8
+
9
+ class TensorDataset(Dataset):
10
+ def __init__(self, x, y):
11
+ assert len(x) == len(y)
12
+ self.x = x
13
+ self.y = y
14
+
15
+ def __len__(self):
16
+ return len(self.x)
17
+
18
+ def __getitem__(self, idx):
19
+ return self.x[idx], self.y[idx]
@@ -0,0 +1,36 @@
1
+ from tinytensor.nn.modules import Module
2
+
3
+ class ReLU(Module):
4
+ def forward(self, x):
5
+ return x.relu()
6
+
7
+ class LeReLU(Module):
8
+ # у нас есть тут альфа
9
+ def __init__(self, alpha=0.01):
10
+ self.alpha = alpha
11
+
12
+ def forward(self, x):
13
+ return x.leaky_relu(alpha=self.alpha)
14
+
15
+ class Sigmod(Module):
16
+ def forward(self, x):
17
+ return x.sigmoid()
18
+
19
+ class Tanh(Module):
20
+ def forward(self, x):
21
+ return x.tanh()
22
+
23
+ class GELU(Module):
24
+ def forward(self, x):
25
+ return x.gelu()
26
+
27
+
28
+
29
+
30
+ # ToDo:
31
+ # ReLU [x]
32
+ # LeReLU [x]
33
+ # Sigmoid [x]
34
+ # Tanh [x]
35
+ # GELU [x]
36
+ # kama soska
@@ -0,0 +1,18 @@
1
+ import numpy as np
2
+
3
+ from tinytensor.core.tensor import Tensor
4
+ from tinytensor.nn.modules import Module
5
+
6
+ class Linear(Module):
7
+ def __init__(self,in_features, out_features):
8
+ super().__init__()
9
+ std = np.sqrt(2.0 / in_features)
10
+ weightd = np.random.randn(in_features, out_features).astype(np.float32) * std
11
+ self.weight = Tensor(weightd, requires_grad=True)
12
+
13
+ bias_d = np.zeros((1, out_features), dtype=np.float32)
14
+ self.bias = Tensor(bias_d, requires_grad=True)
15
+
16
+ def forward(self, x):
17
+ # кароч обычная формула y = x@w + b
18
+ return (x @ self.weight ) + self.bias
@@ -0,0 +1,12 @@
1
+ import numpy as np
2
+ from tinytensor.nn.modules import Module
3
+ # 1/n * sum((n, i = 1), yi - y`i)^2
4
+ class MSELoss(Module):
5
+ def forward(self, y_pred, y_true):
6
+ diff = y_pred - y_true
7
+ sq_diff = diff ** 2
8
+ return sq_diff.sum() * (1.0/y_pred.data.size)
9
+
10
+
11
+
12
+ # мб в будущем добавлю cross entropy loss
@@ -0,0 +1,64 @@
1
+ from tinytensor.core.tensor import Tensor
2
+ import pickle
3
+
4
+ #кароч модуль от которого будут наследоваться все слои
5
+ class Module:
6
+ def __init__(self):
7
+ self.training = True
8
+
9
+ def __call__(self, *args, **kwargs):
10
+ return self.forward(*args, **kwargs)
11
+
12
+ #щас это просто заглушка для прямого вызова, а в реале у всех должны быть свои реализации forward, так что это роль не играет
13
+ def forward(self, *args, **kwargs):
14
+ raise NotImplementedError(...)
15
+
16
+ # сохраняет обучаемые веса, и если в модуле есть еще какие то модули то он их тоже открывает
17
+ def parameters(self):
18
+ params = []
19
+ for atr in self.__dict__.values():
20
+ if isinstance(atr, Tensor) and atr.requires_grad == True:
21
+ params.append(atr)
22
+
23
+ elif isinstance(atr, Module):
24
+ params.extend(atr.parameters())
25
+ return params
26
+
27
+ #обнуляет градиенты
28
+ def zero_grad(self):
29
+ for atr in self.parameters():
30
+ atr.grad = None
31
+
32
+ def state_dict(self):
33
+ #вытащим все веса параметров
34
+ sdict = {}
35
+ for name, param in self._get_named_params().items():
36
+ sdict[name] = param.data
37
+ return sdict
38
+
39
+ def load_state_dict(self, sdict):
40
+ #запихиваем обратно
41
+ for name, param in self._get_named_params().items():
42
+ if name in sdict:
43
+ param.data = sdict[name]
44
+
45
+ def _get_named_params(self, prefix=""):
46
+ named = {}
47
+ for attr_name, attr in self.__dict__.items():
48
+ key = f"{prefix}.{attr_name}" if prefix else attr_name
49
+ if isinstance(attr, Tensor):
50
+ named[key] = attr
51
+ elif isinstance(attr, Module):
52
+ named.update(attr._get_named_params(prefix=key))
53
+ return named
54
+
55
+ #сохранение модели
56
+ def save(self,filepath):
57
+ with open(filepath, "wb") as f:
58
+ pickle.dump(self.state_dict(), f)
59
+
60
+ # загрузка весов обратно
61
+ def load(self, filepath):
62
+ with open(filepath, "rb") as f:
63
+ sd = pickle.load(f)
64
+ self.load_state_dict(sd)
@@ -0,0 +1,11 @@
1
+ from tinytensor.optim.optimizer import Optimizer, SGD, AdamW
2
+
3
+ __all__ = ["Optimizer", "SGD", "AdamW"]
4
+
5
+ #эт для удобства
6
+ #чтоб можно было писать вот так:
7
+ """
8
+ from tinytensor.optim import AdamW
9
+
10
+ optimizer = AdamW(model.parameters(), lr=1e-3)
11
+ """
@@ -0,0 +1,72 @@
1
+ # я допил свой чай, теперь хочу еще но мне лень вставать
2
+ import numpy as np
3
+
4
+ class Optimizer:
5
+ def __init__ (self, parameters):
6
+ # в лист если через генератор model параметры выдано
7
+ self.parameters = list(parameters)
8
+
9
+ def step(self):
10
+ #у каждого свой
11
+ raise NotImplementedError
12
+
13
+ def zero_grad(self):
14
+ for p in self.parameters:
15
+ p.grad = None
16
+
17
+ # SGD
18
+ # w = w - lr*dw
19
+ class SGD(Optimizer):
20
+ def __init__(self, parameters, lr = 0.01, momentum = 0.0):
21
+ super().__init__(parameters)
22
+ self.lr = lr
23
+ self.momentum = momentum
24
+ # буфера
25
+ self.velocities = [np.zeros_like(p.data) for p in self.parameters] if momentum > 0 else None
26
+
27
+ def step(self):
28
+ for i,p in enumerate(self.parameters):
29
+ if p.grad is None:
30
+ continue
31
+ grad = p.grad
32
+
33
+ if self.momentum>0:
34
+ self.velocities[i] = self.momentum * self.velocities[i] + grad
35
+ grad = self.velocities[i]
36
+ # шаг
37
+ p.data -= self.lr * grad
38
+
39
+
40
+
41
+ #Adamw
42
+ # слишком много формул мне впадлу писать
43
+ class AdamW(Optimizer):
44
+ def __init__(self, parameters, lr=0.001, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.01):
45
+ super().__init__(parameters)
46
+ self.lr = lr
47
+ self.beta1, self.beta2 = betas
48
+ self.eps = eps
49
+ self.weight_decay = weight_decay
50
+
51
+ self.t = 0
52
+
53
+ self.m = [np.zeros_like(p.data) for p in self.parameters]
54
+ self.v = [np.zeros_like(p.data) for p in self.parameters]
55
+
56
+ def step(self):
57
+ self.t += 1
58
+
59
+ for i, p in enumerate(self.parameters):
60
+ if p.grad is None:
61
+ continue
62
+
63
+ grad = p.grad
64
+ # обновка m и v мометы
65
+ self.m[i] = self.beta1 * self.m[i] + (1.0 - self.beta1) * grad
66
+ self.v[i] = self.beta2 * self.v[i] + (1.0 - self.beta2) * (grad ** 2)
67
+ # корекция баяса
68
+ m_hat = self.m[i] / (1.0 - self.beta1 ** self.t)
69
+ v_hat = self.v[i] / (1.0 - self.beta2 ** self.t)
70
+ # адапт шаг и разделение затухания весов
71
+ denom = np.sqrt(v_hat) + self.eps
72
+ p.data -= self.lr * (m_hat / denom + self.weight_decay * p.data)
@@ -0,0 +1,4 @@
1
+ from tinytensor.utils.bar import progress_bar, train_bar
2
+ from tinytensor.utils.early_stopping import EarlyStopping
3
+ __all__ = ["progress_bar", "train_bar",
4
+ "EarlyStopping"]
@@ -0,0 +1,26 @@
1
+ import time
2
+
3
+
4
+ def progress_bar(iterable, prefix="", length=30):
5
+ total = len(iterable)
6
+ start_time = time.time()
7
+ for i, item in enumerate(iterable):
8
+ yield item # ИСПРАВЛЕНО: возвращаем элемент, а не модуль time
9
+ percent = (i + 1) / total
10
+ filled = int(length * percent)
11
+ bar = "█" * filled + "-" * (length - filled)
12
+ elapsed = time.time() - start_time
13
+
14
+ print(f"\r{prefix} |{bar}| {percent*100:.1f}% [{elapsed:.1f}s]", end="")
15
+ print()
16
+
17
+
18
+ #алиас для тех кто привык train_bar писать
19
+ train_bar = progress_bar
20
+
21
+
22
+ """
23
+ пользоваться надо крч вот так:
24
+ for epoch in train_bar(range(100), prefix="обучение"):
25
+ ...
26
+ """
@@ -0,0 +1,64 @@
1
+ import copy
2
+
3
+
4
+ class EarlyStopping:
5
+ def __init__(self, model, patience=5, min_delta=0.0, restore_best_weights=True):
6
+ self.model = model
7
+ self.patience = patience
8
+ self.min_delta = min_delta
9
+ self.restore_best_weights = restore_best_weights
10
+
11
+ self.counter = 0
12
+ self.best_loss = None
13
+ self.early_stop = False
14
+ self.best_state_dict = None
15
+
16
+ def __call__(self, val_loss):
17
+ if hasattr(val_loss, "data"):
18
+ val_loss = float(val_loss.data)
19
+
20
+ #если это была первая эпоха
21
+ if self.best_loss is None:
22
+ self.best_loss = val_loss
23
+ self._save_checkpoint()
24
+
25
+ elif val_loss < self.best_loss - self.min_delta:
26
+ self.best_loss = val_loss
27
+ self.counter = 0
28
+ self._save_checkpoint()
29
+
30
+ else:
31
+ self.counter += 1
32
+ if self.counter >= self.patience:
33
+ self.early_stop = True
34
+ if self.restore_best_weights and self.best_state_dict is not None:
35
+ # восстанавливаем лучшие веса
36
+ self.model.load_state_dict(self.best_state_dict)
37
+
38
+ return self.early_stop
39
+
40
+ def _save_checkpoint(self):
41
+ if self.restore_best_weights and hasattr(self.model, "state_dict"):
42
+ self.best_state_dict = copy.deepcopy(self.model.state_dict())
43
+
44
+
45
+ '''пример кода
46
+
47
+ from tinytensor.utils import EarlyStopping
48
+
49
+ model = MLP()
50
+ early_stopping = EarlyStopping(model, patience=7, min_delta=0.01)
51
+
52
+ for epoch in range(100):
53
+ #обучение
54
+ train_loss = train_one_epoch(model)
55
+
56
+ #валидация
57
+ val_loss = evaluate(model)
58
+
59
+ #проверка остановки
60
+ if early_stopping(val_loss):
61
+ print(f"обучение прекратилось на эпохе {epoch+1}")
62
+ break
63
+
64
+ '''
File without changes
File without changes