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.
- pytinytensor-0.1.2/LICENSE +21 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/PKG-INFO +3 -1
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/PKG-INFO +3 -1
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/SOURCES.txt +24 -0
- pytinytensor-0.1.2/setup.py +133 -0
- pytinytensor-0.1.2/tinytensor/nn/__init__.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2/tinytensor}/setup.py +10 -15
- pytinytensor-0.1.2/tinytensor/tinytensor/__init__.py +0 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/backends/__init__.py +0 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/backends/cpu_numpy.py +0 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/config.py +20 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/core/__init__.py +0 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/core/autograd.py +22 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/core/ops.py +1 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/core/tensor.py +288 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/data/__init__.py +5 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/data/dataloader.py +41 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/data/dataset.py +19 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/nn/__init__.py +0 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/nn/activations.py +36 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/nn/linear.py +18 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/nn/losses.py +12 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/nn/modules.py +64 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/optim/__init__.py +11 -0
- pytinytensor-0.1.2/tinytensor/tinytensor/optim/optimizer.py +72 -0
- pytinytensor-0.1.2/tinytensor/utils/__init__.py +4 -0
- pytinytensor-0.1.2/tinytensor/utils/bar.py +26 -0
- pytinytensor-0.1.2/tinytensor/utils/early_stopping.py +64 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/README.md +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pyproject.toml +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/dependency_links.txt +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/requires.txt +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/pytinytensor.egg-info/top_level.txt +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/setup.cfg +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_autograd.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_data.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_losses.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_nn.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_optim.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tests/test_tensor_math.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/__init__.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/backends/__init__.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/backends/cpu_numpy.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/backends/cuda_binding.cpp +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/backends/cuda_gpu.cu +0 -0
- /pytinytensor-0.1.1/tinytensor/core/__init__.py → /pytinytensor-0.1.2/tinytensor/backends/cuda_gpu.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/config.py +0 -0
- {pytinytensor-0.1.1/tinytensor/nn → pytinytensor-0.1.2/tinytensor/core}/__init__.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/core/autograd.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/core/ops.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/core/tensor.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/data/__init__.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/data/dataloader.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/data/dataset.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/nn/activations.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/nn/linear.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/nn/losses.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/nn/modules.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/optim/__init__.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2}/tinytensor/optim/optimizer.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2/tinytensor}/tinytensor/utils/__init__.py +0 -0
- {pytinytensor-0.1.1 → pytinytensor-0.1.2/tinytensor}/tinytensor/utils/bar.py +0 -0
- {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.
|
|
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.
|
|
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="
|
|
69
|
-
version="0.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
|
|
File without changes
|
|
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)
|
|
File without changes
|
|
@@ -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,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]
|
|
File without changes
|
|
@@ -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,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
|
|
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
|
|
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
|