ilovetools 0.2.18__tar.gz → 0.2.19__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.
- {ilovetools-0.2.18/ilovetools.egg-info → ilovetools-0.2.19}/PKG-INFO +2 -2
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/__init__.py +2 -2
- ilovetools-0.2.19/ilovetools/ml/cnn.py +619 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19/ilovetools.egg-info}/PKG-INFO +2 -2
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools.egg-info/SOURCES.txt +2 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/pyproject.toml +2 -2
- {ilovetools-0.2.18 → ilovetools-0.2.19}/setup.py +2 -2
- ilovetools-0.2.19/tests/test_cnn.py +394 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/LICENSE +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/MANIFEST.in +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/README.md +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ai/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ai/embeddings.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ai/inference.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ai/llm_helpers.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/audio/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/automation/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/automation/file_organizer.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/conversion/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/conversion/config_converter.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/conversion/config_converter_fixed_header.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/data/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/data/feature_engineering.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/data/preprocessing.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/database/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/datetime/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/email/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/email/template_engine.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/files/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/image/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/activations.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/anomaly_detection.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/attention.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/clustering.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/cross_validation.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/dimensionality.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/ensemble.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/feature_selection.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/gradient_descent.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/imbalanced.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/interpretation.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/loss_functions.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/metrics.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/neural_network.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/normalization.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/optimizers.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/pipeline.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/regularization.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/timeseries.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/ml/tuning.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/security/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/security/password_checker.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/text/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/utils/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/utils/cache_system.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/utils/logger.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/utils/rate_limiter.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/utils/retry.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/validation/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/validation/data_validator.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/web/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/web/scraper.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools/web/url_shortener.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools.egg-info/dependency_links.txt +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools.egg-info/requires.txt +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/ilovetools.egg-info/top_level.txt +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/requirements.txt +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/setup.cfg +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/__init__.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/test_activations.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/test_attention.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/test_gradient_descent.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/test_loss_functions.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/test_neural_network.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/test_normalization.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/test_optimizers.py +0 -0
- {ilovetools-0.2.18 → ilovetools-0.2.19}/tests/test_regularization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ilovetools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.19
|
|
4
4
|
Summary: A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs
|
|
5
5
|
Home-page: https://github.com/AliMehdi512/ilovetools
|
|
6
6
|
Author: Ali Mehdi
|
|
@@ -11,7 +11,7 @@ Project-URL: Repository, https://github.com/AliMehdi512/ilovetools
|
|
|
11
11
|
Project-URL: Issues, https://github.com/AliMehdi512/ilovetools/issues
|
|
12
12
|
Project-URL: Bug Reports, https://github.com/AliMehdi512/ilovetools/issues
|
|
13
13
|
Project-URL: Source, https://github.com/AliMehdi512/ilovetools
|
|
14
|
-
Keywords: utilities,tools,ai,ml,data-processing,automation,
|
|
14
|
+
Keywords: utilities,tools,ai,ml,data-processing,automation,cnn,convolutional-neural-networks,conv2d,pooling,computer-vision,image-processing
|
|
15
15
|
Classifier: Development Status :: 3 - Alpha
|
|
16
16
|
Classifier: Intended Audience :: Developers
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Convolutional Neural Network Operations
|
|
3
|
+
|
|
4
|
+
This module provides core CNN operations:
|
|
5
|
+
- 2D Convolution (Conv2D)
|
|
6
|
+
- Pooling Operations (Max, Average, Global)
|
|
7
|
+
- Padding Strategies (Same, Valid, Custom)
|
|
8
|
+
- Im2Col and Col2Im transformations
|
|
9
|
+
- Depthwise and Separable Convolutions
|
|
10
|
+
- Dilated Convolutions
|
|
11
|
+
|
|
12
|
+
All operations support batched inputs and are optimized for performance.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from typing import Tuple, Union, Optional
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# 2D CONVOLUTION
|
|
21
|
+
# ============================================================================
|
|
22
|
+
|
|
23
|
+
def conv2d(
|
|
24
|
+
input: np.ndarray,
|
|
25
|
+
kernel: np.ndarray,
|
|
26
|
+
stride: Union[int, Tuple[int, int]] = 1,
|
|
27
|
+
padding: Union[str, int, Tuple[int, int]] = 0,
|
|
28
|
+
dilation: Union[int, Tuple[int, int]] = 1
|
|
29
|
+
) -> np.ndarray:
|
|
30
|
+
"""
|
|
31
|
+
2D Convolution Operation
|
|
32
|
+
|
|
33
|
+
Applies a 2D convolution over an input signal composed of several input planes.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
input: Input tensor of shape (batch, in_channels, height, width)
|
|
37
|
+
kernel: Kernel tensor of shape (out_channels, in_channels, kernel_h, kernel_w)
|
|
38
|
+
stride: Stride of the convolution (default: 1)
|
|
39
|
+
padding: Padding added to input ('same', 'valid', or int/tuple)
|
|
40
|
+
dilation: Spacing between kernel elements (default: 1)
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Output tensor of shape (batch, out_channels, out_height, out_width)
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
>>> # Single channel convolution
|
|
47
|
+
>>> input = np.random.randn(1, 1, 28, 28) # (batch, channels, H, W)
|
|
48
|
+
>>> kernel = np.random.randn(32, 1, 3, 3) # (out_ch, in_ch, kH, kW)
|
|
49
|
+
>>> output = conv2d(input, kernel, stride=1, padding='same')
|
|
50
|
+
>>> print(output.shape) # (1, 32, 28, 28)
|
|
51
|
+
|
|
52
|
+
>>> # RGB image convolution
|
|
53
|
+
>>> input = np.random.randn(8, 3, 224, 224) # RGB images
|
|
54
|
+
>>> kernel = np.random.randn(64, 3, 3, 3) # 64 filters
|
|
55
|
+
>>> output = conv2d(input, kernel, stride=2, padding=1)
|
|
56
|
+
>>> print(output.shape) # (8, 64, 112, 112)
|
|
57
|
+
"""
|
|
58
|
+
# Parse stride
|
|
59
|
+
if isinstance(stride, int):
|
|
60
|
+
stride_h, stride_w = stride, stride
|
|
61
|
+
else:
|
|
62
|
+
stride_h, stride_w = stride
|
|
63
|
+
|
|
64
|
+
# Parse dilation
|
|
65
|
+
if isinstance(dilation, int):
|
|
66
|
+
dilation_h, dilation_w = dilation, dilation
|
|
67
|
+
else:
|
|
68
|
+
dilation_h, dilation_w = dilation
|
|
69
|
+
|
|
70
|
+
# Get dimensions
|
|
71
|
+
batch_size, in_channels, in_h, in_w = input.shape
|
|
72
|
+
out_channels, _, kernel_h, kernel_w = kernel.shape
|
|
73
|
+
|
|
74
|
+
# Apply padding
|
|
75
|
+
if isinstance(padding, str):
|
|
76
|
+
if padding == 'same':
|
|
77
|
+
# Calculate padding for 'same' output size
|
|
78
|
+
pad_h = ((in_h - 1) * stride_h + dilation_h * (kernel_h - 1) + 1 - in_h) // 2
|
|
79
|
+
pad_w = ((in_w - 1) * stride_w + dilation_w * (kernel_w - 1) + 1 - in_w) // 2
|
|
80
|
+
pad_h, pad_w = max(0, pad_h), max(0, pad_w)
|
|
81
|
+
elif padding == 'valid':
|
|
82
|
+
pad_h, pad_w = 0, 0
|
|
83
|
+
else:
|
|
84
|
+
raise ValueError(f"Unknown padding mode: {padding}")
|
|
85
|
+
elif isinstance(padding, int):
|
|
86
|
+
pad_h, pad_w = padding, padding
|
|
87
|
+
else:
|
|
88
|
+
pad_h, pad_w = padding
|
|
89
|
+
|
|
90
|
+
# Pad input
|
|
91
|
+
if pad_h > 0 or pad_w > 0:
|
|
92
|
+
input = np.pad(input, ((0, 0), (0, 0), (pad_h, pad_h), (pad_w, pad_w)), mode='constant')
|
|
93
|
+
in_h, in_w = input.shape[2], input.shape[3]
|
|
94
|
+
|
|
95
|
+
# Calculate output dimensions
|
|
96
|
+
out_h = (in_h - dilation_h * (kernel_h - 1) - 1) // stride_h + 1
|
|
97
|
+
out_w = (in_w - dilation_w * (kernel_w - 1) - 1) // stride_w + 1
|
|
98
|
+
|
|
99
|
+
# Initialize output
|
|
100
|
+
output = np.zeros((batch_size, out_channels, out_h, out_w))
|
|
101
|
+
|
|
102
|
+
# Perform convolution
|
|
103
|
+
for b in range(batch_size):
|
|
104
|
+
for oc in range(out_channels):
|
|
105
|
+
for i in range(out_h):
|
|
106
|
+
for j in range(out_w):
|
|
107
|
+
# Calculate input region
|
|
108
|
+
h_start = i * stride_h
|
|
109
|
+
w_start = j * stride_w
|
|
110
|
+
|
|
111
|
+
# Extract region with dilation
|
|
112
|
+
region = input[b, :,
|
|
113
|
+
h_start:h_start + dilation_h * kernel_h:dilation_h,
|
|
114
|
+
w_start:w_start + dilation_w * kernel_w:dilation_w]
|
|
115
|
+
|
|
116
|
+
# Convolve
|
|
117
|
+
output[b, oc, i, j] = np.sum(region * kernel[oc])
|
|
118
|
+
|
|
119
|
+
return output
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def conv2d_fast(
|
|
123
|
+
input: np.ndarray,
|
|
124
|
+
kernel: np.ndarray,
|
|
125
|
+
stride: Union[int, Tuple[int, int]] = 1,
|
|
126
|
+
padding: Union[str, int, Tuple[int, int]] = 0
|
|
127
|
+
) -> np.ndarray:
|
|
128
|
+
"""
|
|
129
|
+
Fast 2D Convolution using im2col
|
|
130
|
+
|
|
131
|
+
More efficient implementation using matrix multiplication.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
input: Input tensor of shape (batch, in_channels, height, width)
|
|
135
|
+
kernel: Kernel tensor of shape (out_channels, in_channels, kernel_h, kernel_w)
|
|
136
|
+
stride: Stride of the convolution
|
|
137
|
+
padding: Padding added to input
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Output tensor of shape (batch, out_channels, out_height, out_width)
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
>>> input = np.random.randn(8, 3, 224, 224)
|
|
144
|
+
>>> kernel = np.random.randn(64, 3, 3, 3)
|
|
145
|
+
>>> output = conv2d_fast(input, kernel, stride=1, padding='same')
|
|
146
|
+
>>> print(output.shape) # (8, 64, 224, 224)
|
|
147
|
+
"""
|
|
148
|
+
# Parse stride
|
|
149
|
+
if isinstance(stride, int):
|
|
150
|
+
stride_h, stride_w = stride, stride
|
|
151
|
+
else:
|
|
152
|
+
stride_h, stride_w = stride
|
|
153
|
+
|
|
154
|
+
# Get dimensions
|
|
155
|
+
batch_size, in_channels, in_h, in_w = input.shape
|
|
156
|
+
out_channels, _, kernel_h, kernel_w = kernel.shape
|
|
157
|
+
|
|
158
|
+
# Apply padding
|
|
159
|
+
if isinstance(padding, str):
|
|
160
|
+
if padding == 'same':
|
|
161
|
+
pad_h = ((in_h - 1) * stride_h + kernel_h - in_h) // 2
|
|
162
|
+
pad_w = ((in_w - 1) * stride_w + kernel_w - in_w) // 2
|
|
163
|
+
pad_h, pad_w = max(0, pad_h), max(0, pad_w)
|
|
164
|
+
elif padding == 'valid':
|
|
165
|
+
pad_h, pad_w = 0, 0
|
|
166
|
+
elif isinstance(padding, int):
|
|
167
|
+
pad_h, pad_w = padding, padding
|
|
168
|
+
else:
|
|
169
|
+
pad_h, pad_w = padding
|
|
170
|
+
|
|
171
|
+
# Pad input
|
|
172
|
+
if pad_h > 0 or pad_w > 0:
|
|
173
|
+
input = np.pad(input, ((0, 0), (0, 0), (pad_h, pad_h), (pad_w, pad_w)), mode='constant')
|
|
174
|
+
in_h, in_w = input.shape[2], input.shape[3]
|
|
175
|
+
|
|
176
|
+
# Calculate output dimensions
|
|
177
|
+
out_h = (in_h - kernel_h) // stride_h + 1
|
|
178
|
+
out_w = (in_w - kernel_w) // stride_w + 1
|
|
179
|
+
|
|
180
|
+
# Im2col transformation
|
|
181
|
+
col = im2col(input, kernel_h, kernel_w, stride_h, stride_w)
|
|
182
|
+
|
|
183
|
+
# Reshape kernel
|
|
184
|
+
kernel_col = kernel.reshape(out_channels, -1)
|
|
185
|
+
|
|
186
|
+
# Matrix multiplication
|
|
187
|
+
output = np.dot(kernel_col, col)
|
|
188
|
+
|
|
189
|
+
# Reshape output
|
|
190
|
+
output = output.reshape(out_channels, out_h, out_w, batch_size)
|
|
191
|
+
output = output.transpose(3, 0, 1, 2)
|
|
192
|
+
|
|
193
|
+
return output
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ============================================================================
|
|
197
|
+
# POOLING OPERATIONS
|
|
198
|
+
# ============================================================================
|
|
199
|
+
|
|
200
|
+
def max_pool2d(
|
|
201
|
+
input: np.ndarray,
|
|
202
|
+
pool_size: Union[int, Tuple[int, int]] = 2,
|
|
203
|
+
stride: Optional[Union[int, Tuple[int, int]]] = None,
|
|
204
|
+
padding: Union[int, Tuple[int, int]] = 0
|
|
205
|
+
) -> np.ndarray:
|
|
206
|
+
"""
|
|
207
|
+
2D Max Pooling
|
|
208
|
+
|
|
209
|
+
Applies a 2D max pooling over an input signal.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
input: Input tensor of shape (batch, channels, height, width)
|
|
213
|
+
pool_size: Size of the pooling window (default: 2)
|
|
214
|
+
stride: Stride of the pooling (default: same as pool_size)
|
|
215
|
+
padding: Padding added to input (default: 0)
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Output tensor after max pooling
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
>>> input = np.random.randn(8, 64, 28, 28)
|
|
222
|
+
>>> output = max_pool2d(input, pool_size=2, stride=2)
|
|
223
|
+
>>> print(output.shape) # (8, 64, 14, 14)
|
|
224
|
+
"""
|
|
225
|
+
# Parse pool_size
|
|
226
|
+
if isinstance(pool_size, int):
|
|
227
|
+
pool_h, pool_w = pool_size, pool_size
|
|
228
|
+
else:
|
|
229
|
+
pool_h, pool_w = pool_size
|
|
230
|
+
|
|
231
|
+
# Parse stride (default to pool_size)
|
|
232
|
+
if stride is None:
|
|
233
|
+
stride_h, stride_w = pool_h, pool_w
|
|
234
|
+
elif isinstance(stride, int):
|
|
235
|
+
stride_h, stride_w = stride, stride
|
|
236
|
+
else:
|
|
237
|
+
stride_h, stride_w = stride
|
|
238
|
+
|
|
239
|
+
# Parse padding
|
|
240
|
+
if isinstance(padding, int):
|
|
241
|
+
pad_h, pad_w = padding, padding
|
|
242
|
+
else:
|
|
243
|
+
pad_h, pad_w = padding
|
|
244
|
+
|
|
245
|
+
# Get dimensions
|
|
246
|
+
batch_size, channels, in_h, in_w = input.shape
|
|
247
|
+
|
|
248
|
+
# Apply padding
|
|
249
|
+
if pad_h > 0 or pad_w > 0:
|
|
250
|
+
input = np.pad(input, ((0, 0), (0, 0), (pad_h, pad_h), (pad_w, pad_w)),
|
|
251
|
+
mode='constant', constant_values=-np.inf)
|
|
252
|
+
in_h, in_w = input.shape[2], input.shape[3]
|
|
253
|
+
|
|
254
|
+
# Calculate output dimensions
|
|
255
|
+
out_h = (in_h - pool_h) // stride_h + 1
|
|
256
|
+
out_w = (in_w - pool_w) // stride_w + 1
|
|
257
|
+
|
|
258
|
+
# Initialize output
|
|
259
|
+
output = np.zeros((batch_size, channels, out_h, out_w))
|
|
260
|
+
|
|
261
|
+
# Perform max pooling
|
|
262
|
+
for b in range(batch_size):
|
|
263
|
+
for c in range(channels):
|
|
264
|
+
for i in range(out_h):
|
|
265
|
+
for j in range(out_w):
|
|
266
|
+
h_start = i * stride_h
|
|
267
|
+
w_start = j * stride_w
|
|
268
|
+
|
|
269
|
+
# Extract region
|
|
270
|
+
region = input[b, c, h_start:h_start + pool_h, w_start:w_start + pool_w]
|
|
271
|
+
|
|
272
|
+
# Take maximum
|
|
273
|
+
output[b, c, i, j] = np.max(region)
|
|
274
|
+
|
|
275
|
+
return output
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def avg_pool2d(
|
|
279
|
+
input: np.ndarray,
|
|
280
|
+
pool_size: Union[int, Tuple[int, int]] = 2,
|
|
281
|
+
stride: Optional[Union[int, Tuple[int, int]]] = None,
|
|
282
|
+
padding: Union[int, Tuple[int, int]] = 0
|
|
283
|
+
) -> np.ndarray:
|
|
284
|
+
"""
|
|
285
|
+
2D Average Pooling
|
|
286
|
+
|
|
287
|
+
Applies a 2D average pooling over an input signal.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
input: Input tensor of shape (batch, channels, height, width)
|
|
291
|
+
pool_size: Size of the pooling window (default: 2)
|
|
292
|
+
stride: Stride of the pooling (default: same as pool_size)
|
|
293
|
+
padding: Padding added to input (default: 0)
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Output tensor after average pooling
|
|
297
|
+
|
|
298
|
+
Example:
|
|
299
|
+
>>> input = np.random.randn(8, 64, 28, 28)
|
|
300
|
+
>>> output = avg_pool2d(input, pool_size=2, stride=2)
|
|
301
|
+
>>> print(output.shape) # (8, 64, 14, 14)
|
|
302
|
+
"""
|
|
303
|
+
# Parse pool_size
|
|
304
|
+
if isinstance(pool_size, int):
|
|
305
|
+
pool_h, pool_w = pool_size, pool_size
|
|
306
|
+
else:
|
|
307
|
+
pool_h, pool_w = pool_size
|
|
308
|
+
|
|
309
|
+
# Parse stride
|
|
310
|
+
if stride is None:
|
|
311
|
+
stride_h, stride_w = pool_h, pool_w
|
|
312
|
+
elif isinstance(stride, int):
|
|
313
|
+
stride_h, stride_w = stride, stride
|
|
314
|
+
else:
|
|
315
|
+
stride_h, stride_w = stride
|
|
316
|
+
|
|
317
|
+
# Parse padding
|
|
318
|
+
if isinstance(padding, int):
|
|
319
|
+
pad_h, pad_w = padding, padding
|
|
320
|
+
else:
|
|
321
|
+
pad_h, pad_w = padding
|
|
322
|
+
|
|
323
|
+
# Get dimensions
|
|
324
|
+
batch_size, channels, in_h, in_w = input.shape
|
|
325
|
+
|
|
326
|
+
# Apply padding
|
|
327
|
+
if pad_h > 0 or pad_w > 0:
|
|
328
|
+
input = np.pad(input, ((0, 0), (0, 0), (pad_h, pad_h), (pad_w, pad_w)), mode='constant')
|
|
329
|
+
in_h, in_w = input.shape[2], input.shape[3]
|
|
330
|
+
|
|
331
|
+
# Calculate output dimensions
|
|
332
|
+
out_h = (in_h - pool_h) // stride_h + 1
|
|
333
|
+
out_w = (in_w - pool_w) // stride_w + 1
|
|
334
|
+
|
|
335
|
+
# Initialize output
|
|
336
|
+
output = np.zeros((batch_size, channels, out_h, out_w))
|
|
337
|
+
|
|
338
|
+
# Perform average pooling
|
|
339
|
+
for b in range(batch_size):
|
|
340
|
+
for c in range(channels):
|
|
341
|
+
for i in range(out_h):
|
|
342
|
+
for j in range(out_w):
|
|
343
|
+
h_start = i * stride_h
|
|
344
|
+
w_start = j * stride_w
|
|
345
|
+
|
|
346
|
+
# Extract region
|
|
347
|
+
region = input[b, c, h_start:h_start + pool_h, w_start:w_start + pool_w]
|
|
348
|
+
|
|
349
|
+
# Take average
|
|
350
|
+
output[b, c, i, j] = np.mean(region)
|
|
351
|
+
|
|
352
|
+
return output
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def global_avg_pool2d(input: np.ndarray) -> np.ndarray:
|
|
356
|
+
"""
|
|
357
|
+
Global Average Pooling
|
|
358
|
+
|
|
359
|
+
Averages each feature map to a single value.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
input: Input tensor of shape (batch, channels, height, width)
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Output tensor of shape (batch, channels, 1, 1)
|
|
366
|
+
|
|
367
|
+
Example:
|
|
368
|
+
>>> input = np.random.randn(8, 512, 7, 7)
|
|
369
|
+
>>> output = global_avg_pool2d(input)
|
|
370
|
+
>>> print(output.shape) # (8, 512, 1, 1)
|
|
371
|
+
"""
|
|
372
|
+
return np.mean(input, axis=(2, 3), keepdims=True)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def global_max_pool2d(input: np.ndarray) -> np.ndarray:
|
|
376
|
+
"""
|
|
377
|
+
Global Max Pooling
|
|
378
|
+
|
|
379
|
+
Takes maximum of each feature map.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
input: Input tensor of shape (batch, channels, height, width)
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
Output tensor of shape (batch, channels, 1, 1)
|
|
386
|
+
|
|
387
|
+
Example:
|
|
388
|
+
>>> input = np.random.randn(8, 512, 7, 7)
|
|
389
|
+
>>> output = global_max_pool2d(input)
|
|
390
|
+
>>> print(output.shape) # (8, 512, 1, 1)
|
|
391
|
+
"""
|
|
392
|
+
return np.max(input, axis=(2, 3), keepdims=True)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
# ============================================================================
|
|
396
|
+
# IM2COL TRANSFORMATION
|
|
397
|
+
# ============================================================================
|
|
398
|
+
|
|
399
|
+
def im2col(
|
|
400
|
+
input: np.ndarray,
|
|
401
|
+
kernel_h: int,
|
|
402
|
+
kernel_w: int,
|
|
403
|
+
stride_h: int = 1,
|
|
404
|
+
stride_w: int = 1
|
|
405
|
+
) -> np.ndarray:
|
|
406
|
+
"""
|
|
407
|
+
Im2Col transformation for efficient convolution
|
|
408
|
+
|
|
409
|
+
Transforms image into column matrix for matrix multiplication.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
input: Input tensor of shape (batch, channels, height, width)
|
|
413
|
+
kernel_h: Kernel height
|
|
414
|
+
kernel_w: Kernel width
|
|
415
|
+
stride_h: Vertical stride
|
|
416
|
+
stride_w: Horizontal stride
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
Column matrix of shape (kernel_h * kernel_w * channels, out_h * out_w * batch)
|
|
420
|
+
|
|
421
|
+
Example:
|
|
422
|
+
>>> input = np.random.randn(8, 3, 32, 32)
|
|
423
|
+
>>> col = im2col(input, kernel_h=3, kernel_w=3, stride_h=1, stride_w=1)
|
|
424
|
+
>>> print(col.shape) # (27, 7200)
|
|
425
|
+
"""
|
|
426
|
+
batch_size, channels, in_h, in_w = input.shape
|
|
427
|
+
|
|
428
|
+
# Calculate output dimensions
|
|
429
|
+
out_h = (in_h - kernel_h) // stride_h + 1
|
|
430
|
+
out_w = (in_w - kernel_w) // stride_w + 1
|
|
431
|
+
|
|
432
|
+
# Initialize column matrix
|
|
433
|
+
col = np.zeros((batch_size, channels, kernel_h, kernel_w, out_h, out_w))
|
|
434
|
+
|
|
435
|
+
# Fill column matrix
|
|
436
|
+
for i in range(kernel_h):
|
|
437
|
+
i_max = i + stride_h * out_h
|
|
438
|
+
for j in range(kernel_w):
|
|
439
|
+
j_max = j + stride_w * out_w
|
|
440
|
+
col[:, :, i, j, :, :] = input[:, :, i:i_max:stride_h, j:j_max:stride_w]
|
|
441
|
+
|
|
442
|
+
# Reshape
|
|
443
|
+
col = col.transpose(0, 4, 5, 1, 2, 3).reshape(batch_size * out_h * out_w, -1)
|
|
444
|
+
|
|
445
|
+
return col.T
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def col2im(
|
|
449
|
+
col: np.ndarray,
|
|
450
|
+
input_shape: Tuple[int, int, int, int],
|
|
451
|
+
kernel_h: int,
|
|
452
|
+
kernel_w: int,
|
|
453
|
+
stride_h: int = 1,
|
|
454
|
+
stride_w: int = 1
|
|
455
|
+
) -> np.ndarray:
|
|
456
|
+
"""
|
|
457
|
+
Col2Im transformation (inverse of im2col)
|
|
458
|
+
|
|
459
|
+
Transforms column matrix back to image format.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
col: Column matrix
|
|
463
|
+
input_shape: Original input shape (batch, channels, height, width)
|
|
464
|
+
kernel_h: Kernel height
|
|
465
|
+
kernel_w: Kernel width
|
|
466
|
+
stride_h: Vertical stride
|
|
467
|
+
stride_w: Horizontal stride
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
Image tensor of shape input_shape
|
|
471
|
+
|
|
472
|
+
Example:
|
|
473
|
+
>>> col = np.random.randn(27, 7200)
|
|
474
|
+
>>> img = col2im(col, (8, 3, 32, 32), kernel_h=3, kernel_w=3)
|
|
475
|
+
>>> print(img.shape) # (8, 3, 32, 32)
|
|
476
|
+
"""
|
|
477
|
+
batch_size, channels, in_h, in_w = input_shape
|
|
478
|
+
|
|
479
|
+
# Calculate output dimensions
|
|
480
|
+
out_h = (in_h - kernel_h) // stride_h + 1
|
|
481
|
+
out_w = (in_w - kernel_w) // stride_w + 1
|
|
482
|
+
|
|
483
|
+
# Reshape column
|
|
484
|
+
col = col.T.reshape(batch_size, out_h, out_w, channels, kernel_h, kernel_w)
|
|
485
|
+
col = col.transpose(0, 3, 4, 5, 1, 2)
|
|
486
|
+
|
|
487
|
+
# Initialize image
|
|
488
|
+
img = np.zeros(input_shape)
|
|
489
|
+
|
|
490
|
+
# Fill image
|
|
491
|
+
for i in range(kernel_h):
|
|
492
|
+
i_max = i + stride_h * out_h
|
|
493
|
+
for j in range(kernel_w):
|
|
494
|
+
j_max = j + stride_w * out_w
|
|
495
|
+
img[:, :, i:i_max:stride_h, j:j_max:stride_w] += col[:, :, i, j, :, :]
|
|
496
|
+
|
|
497
|
+
return img
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
# ============================================================================
|
|
501
|
+
# DEPTHWISE AND SEPARABLE CONVOLUTIONS
|
|
502
|
+
# ============================================================================
|
|
503
|
+
|
|
504
|
+
def depthwise_conv2d(
|
|
505
|
+
input: np.ndarray,
|
|
506
|
+
kernel: np.ndarray,
|
|
507
|
+
stride: Union[int, Tuple[int, int]] = 1,
|
|
508
|
+
padding: Union[str, int, Tuple[int, int]] = 0
|
|
509
|
+
) -> np.ndarray:
|
|
510
|
+
"""
|
|
511
|
+
Depthwise 2D Convolution
|
|
512
|
+
|
|
513
|
+
Applies a separate filter to each input channel.
|
|
514
|
+
Used in MobileNets for efficiency.
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
input: Input tensor of shape (batch, in_channels, height, width)
|
|
518
|
+
kernel: Kernel tensor of shape (in_channels, 1, kernel_h, kernel_w)
|
|
519
|
+
stride: Stride of the convolution
|
|
520
|
+
padding: Padding added to input
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
Output tensor of shape (batch, in_channels, out_height, out_width)
|
|
524
|
+
|
|
525
|
+
Example:
|
|
526
|
+
>>> input = np.random.randn(8, 32, 56, 56)
|
|
527
|
+
>>> kernel = np.random.randn(32, 1, 3, 3) # One filter per channel
|
|
528
|
+
>>> output = depthwise_conv2d(input, kernel, stride=1, padding='same')
|
|
529
|
+
>>> print(output.shape) # (8, 32, 56, 56)
|
|
530
|
+
"""
|
|
531
|
+
batch_size, in_channels, _, _ = input.shape
|
|
532
|
+
|
|
533
|
+
# Apply convolution to each channel separately
|
|
534
|
+
outputs = []
|
|
535
|
+
for c in range(in_channels):
|
|
536
|
+
channel_input = input[:, c:c+1, :, :]
|
|
537
|
+
channel_kernel = kernel[c:c+1, :, :, :]
|
|
538
|
+
channel_output = conv2d(channel_input, channel_kernel, stride, padding)
|
|
539
|
+
outputs.append(channel_output)
|
|
540
|
+
|
|
541
|
+
return np.concatenate(outputs, axis=1)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def separable_conv2d(
|
|
545
|
+
input: np.ndarray,
|
|
546
|
+
depthwise_kernel: np.ndarray,
|
|
547
|
+
pointwise_kernel: np.ndarray,
|
|
548
|
+
stride: Union[int, Tuple[int, int]] = 1,
|
|
549
|
+
padding: Union[str, int, Tuple[int, int]] = 0
|
|
550
|
+
) -> np.ndarray:
|
|
551
|
+
"""
|
|
552
|
+
Separable 2D Convolution (Depthwise + Pointwise)
|
|
553
|
+
|
|
554
|
+
Efficient convolution used in MobileNets.
|
|
555
|
+
|
|
556
|
+
Args:
|
|
557
|
+
input: Input tensor of shape (batch, in_channels, height, width)
|
|
558
|
+
depthwise_kernel: Depthwise kernel (in_channels, 1, kernel_h, kernel_w)
|
|
559
|
+
pointwise_kernel: Pointwise kernel (out_channels, in_channels, 1, 1)
|
|
560
|
+
stride: Stride of the depthwise convolution
|
|
561
|
+
padding: Padding for depthwise convolution
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
Output tensor of shape (batch, out_channels, out_height, out_width)
|
|
565
|
+
|
|
566
|
+
Example:
|
|
567
|
+
>>> input = np.random.randn(8, 32, 56, 56)
|
|
568
|
+
>>> dw_kernel = np.random.randn(32, 1, 3, 3)
|
|
569
|
+
>>> pw_kernel = np.random.randn(64, 32, 1, 1)
|
|
570
|
+
>>> output = separable_conv2d(input, dw_kernel, pw_kernel)
|
|
571
|
+
>>> print(output.shape) # (8, 64, 56, 56)
|
|
572
|
+
"""
|
|
573
|
+
# Depthwise convolution
|
|
574
|
+
depthwise_output = depthwise_conv2d(input, depthwise_kernel, stride, padding)
|
|
575
|
+
|
|
576
|
+
# Pointwise convolution (1x1)
|
|
577
|
+
pointwise_output = conv2d(depthwise_output, pointwise_kernel, stride=1, padding=0)
|
|
578
|
+
|
|
579
|
+
return pointwise_output
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
# ============================================================================
|
|
583
|
+
# UTILITY FUNCTIONS
|
|
584
|
+
# ============================================================================
|
|
585
|
+
|
|
586
|
+
def calculate_output_size(
|
|
587
|
+
input_size: int,
|
|
588
|
+
kernel_size: int,
|
|
589
|
+
stride: int = 1,
|
|
590
|
+
padding: int = 0,
|
|
591
|
+
dilation: int = 1
|
|
592
|
+
) -> int:
|
|
593
|
+
"""
|
|
594
|
+
Calculate output size after convolution or pooling
|
|
595
|
+
|
|
596
|
+
Args:
|
|
597
|
+
input_size: Input dimension size
|
|
598
|
+
kernel_size: Kernel dimension size
|
|
599
|
+
stride: Stride
|
|
600
|
+
padding: Padding
|
|
601
|
+
dilation: Dilation
|
|
602
|
+
|
|
603
|
+
Returns:
|
|
604
|
+
Output dimension size
|
|
605
|
+
|
|
606
|
+
Example:
|
|
607
|
+
>>> out_size = calculate_output_size(224, 3, stride=2, padding=1)
|
|
608
|
+
>>> print(out_size) # 112
|
|
609
|
+
"""
|
|
610
|
+
return (input_size + 2 * padding - dilation * (kernel_size - 1) - 1) // stride + 1
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
# Aliases for convenience
|
|
614
|
+
maxpool2d = max_pool2d
|
|
615
|
+
avgpool2d = avg_pool2d
|
|
616
|
+
global_avgpool = global_avg_pool2d
|
|
617
|
+
global_maxpool = global_max_pool2d
|
|
618
|
+
depthwise_conv = depthwise_conv2d
|
|
619
|
+
separable_conv = separable_conv2d
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ilovetools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.19
|
|
4
4
|
Summary: A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs
|
|
5
5
|
Home-page: https://github.com/AliMehdi512/ilovetools
|
|
6
6
|
Author: Ali Mehdi
|
|
@@ -11,7 +11,7 @@ Project-URL: Repository, https://github.com/AliMehdi512/ilovetools
|
|
|
11
11
|
Project-URL: Issues, https://github.com/AliMehdi512/ilovetools/issues
|
|
12
12
|
Project-URL: Bug Reports, https://github.com/AliMehdi512/ilovetools/issues
|
|
13
13
|
Project-URL: Source, https://github.com/AliMehdi512/ilovetools
|
|
14
|
-
Keywords: utilities,tools,ai,ml,data-processing,automation,
|
|
14
|
+
Keywords: utilities,tools,ai,ml,data-processing,automation,cnn,convolutional-neural-networks,conv2d,pooling,computer-vision,image-processing
|
|
15
15
|
Classifier: Development Status :: 3 - Alpha
|
|
16
16
|
Classifier: Intended Audience :: Developers
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
@@ -34,6 +34,7 @@ ilovetools/ml/activations.py
|
|
|
34
34
|
ilovetools/ml/anomaly_detection.py
|
|
35
35
|
ilovetools/ml/attention.py
|
|
36
36
|
ilovetools/ml/clustering.py
|
|
37
|
+
ilovetools/ml/cnn.py
|
|
37
38
|
ilovetools/ml/cross_validation.py
|
|
38
39
|
ilovetools/ml/dimensionality.py
|
|
39
40
|
ilovetools/ml/ensemble.py
|
|
@@ -66,6 +67,7 @@ ilovetools/web/url_shortener.py
|
|
|
66
67
|
tests/__init__.py
|
|
67
68
|
tests/test_activations.py
|
|
68
69
|
tests/test_attention.py
|
|
70
|
+
tests/test_cnn.py
|
|
69
71
|
tests/test_gradient_descent.py
|
|
70
72
|
tests/test_loss_functions.py
|
|
71
73
|
tests/test_neural_network.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ilovetools"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.19"
|
|
8
8
|
description = "A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -12,7 +12,7 @@ license = "MIT"
|
|
|
12
12
|
authors = [
|
|
13
13
|
{name = "Ali Mehdi", email = "ali.mehdi.dev579@gmail.com"}
|
|
14
14
|
]
|
|
15
|
-
keywords = ["utilities", "tools", "ai", "ml", "data-processing", "automation", "
|
|
15
|
+
keywords = ["utilities", "tools", "ai", "ml", "data-processing", "automation", "cnn", "convolutional-neural-networks", "conv2d", "pooling", "computer-vision", "image-processing"]
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Development Status :: 3 - Alpha",
|
|
18
18
|
"Intended Audience :: Developers",
|